diff options
Diffstat (limited to 'src/test')
39 files changed, 9276 insertions, 1456 deletions
diff --git a/src/test/Makefile.nmake b/src/test/Makefile.nmake index 562c8df8b5..822431f3b8 100644 --- a/src/test/Makefile.nmake +++ b/src/test/Makefile.nmake @@ -12,9 +12,10 @@ LIBS = ..\..\..\build-alpha\lib\libevent.lib \ crypt32.lib gdi32.lib user32.lib TEST_OBJECTS = test.obj test_addr.obj test_containers.obj \ - test_crypto.obj test_data.obj test_dir.obj test_microdesc.obj \ - test_pt.obj test_util.obj test_config.obj test_cell_formats.obj \ - test_replay.obj test_introduce.obj tinytest.obj + test_controller_events.ogj test_crypto.obj test_data.obj test_dir.obj \ + test_microdesc.obj test_pt.obj test_util.obj test_config.obj \ + test_cell_formats.obj test_replay.obj test_introduce.obj tinytest.obj \ + test_hs.obj tinytest.obj: ..\ext\tinytest.c $(CC) $(CFLAGS) /D snprintf=_snprintf /c ..\ext\tinytest.c diff --git a/src/test/bench.c b/src/test/bench.c index 706b8bc7fb..f6c33626f2 100644 --- a/src/test/bench.c +++ b/src/test/bench.c @@ -14,9 +14,6 @@ const char tor_git_revision[] = ""; #include "orconfig.h" -#define RELAY_PRIVATE -#define CONFIG_PRIVATE - #include "or.h" #include "onion_tap.h" #include "relay.h" @@ -204,6 +201,7 @@ bench_onion_ntor(void) for (i = 0; i < iters; ++i) { onion_skin_ntor_create(nodeid, &keypair1.pubkey, &state, os); ntor_handshake_state_free(state); + state = NULL; } end = perftime(); printf("Client-side, part 1: %f usec.\n", NANOCOUNT(start, end, iters)/1e3); @@ -340,6 +338,28 @@ bench_dmap(void) } static void +bench_siphash(void) +{ + char buf[128]; + int lens[] = { 7, 8, 15, 16, 20, 32, 111, 128, -1 }; + int i, j; + uint64_t start, end; + const int N = 300000; + crypto_rand(buf, sizeof(buf)); + + for (i = 0; lens[i] > 0; ++i) { + reset_perftime(); + start = perftime(); + for (j = 0; j < N; ++j) { + siphash24g(buf, lens[i]); + } + end = perftime(); + printf("siphash24g(%d): %.2f ns per call\n", + lens[i], NANOCOUNT(start,end,N)); + } +} + +static void bench_cell_ops(void) { const int iters = 1<<16; @@ -489,6 +509,7 @@ typedef struct benchmark_t { static struct benchmark_t benchmarks[] = { ENT(dmap), + ENT(siphash), ENT(aes), ENT(onion_TAP), #ifdef CURVE25519_ENABLED @@ -546,6 +567,7 @@ main(int argc, const char **argv) reset_perftime(); crypto_seed_rng(1); + crypto_init_siphash_key(); options = options_new(); init_logging(); options->command = CMD_RUN_UNITTESTS; diff --git a/src/test/bt_test.py b/src/test/bt_test.py new file mode 100755 index 0000000000..8290509fa7 --- /dev/null +++ b/src/test/bt_test.py @@ -0,0 +1,42 @@ +# Copyright 2013, The Tor Project, Inc +# See LICENSE for licensing information + +""" +bt_test.py + +This file tests the output from test-bt-cl to make sure it's as expected. + +Example usage: + +$ ./src/test/test-bt-cl crash | ./src/test/bt_test.py +OK +$ ./src/test/test-bt-cl assert | ./src/test/bt_test.py +OK + +""" + +import sys + + +def matches(lines, funcs): + if len(lines) < len(funcs): + return False + try: + for l, f in zip(lines, funcs): + l.index(f) + except ValueError: + return False + else: + return True + +FUNCNAMES = "crash oh_what a_tangled_web we_weave main".split() + +LINES = sys.stdin.readlines() + +for I in range(len(LINES)): + if matches(LINES[I:], FUNCNAMES): + print("OK") + break +else: + print("BAD") + diff --git a/src/test/include.am b/src/test/include.am index 112d1a79d8..fba439a616 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -1,11 +1,15 @@ -TESTS+= src/test/test +TESTS += src/test/test -noinst_PROGRAMS+= src/test/test src/test/test-child src/test/bench +noinst_PROGRAMS+= src/test/bench +if UNITTESTS_ENABLED +noinst_PROGRAMS+= src/test/test src/test/test-child +endif src_test_AM_CPPFLAGS = -DSHARE_DATADIR="\"$(datadir)\"" \ -DLOCALSTATEDIR="\"$(localstatedir)\"" \ -DBINDIR="\"$(bindir)\"" \ - -I"$(top_srcdir)/src/or" -I"$(top_srcdir)/src/ext" + -I"$(top_srcdir)/src/or" -I"$(top_srcdir)/src/ext" \ + -DTOR_UNIT_TESTS # -L flags need to go in LDFLAGS. -l flags need to go in LDADD. # This seems to matter nowhere but on Windows, but I assure you that it @@ -14,31 +18,47 @@ src_test_AM_CPPFLAGS = -DSHARE_DATADIR="\"$(datadir)\"" \ src_test_test_SOURCES = \ src/test/test.c \ src/test/test_addr.c \ + src/test/test_buffers.c \ src/test/test_cell_formats.c \ + src/test/test_circuitlist.c \ + src/test/test_circuitmux.c \ src/test/test_containers.c \ + src/test/test_controller_events.c \ src/test/test_crypto.c \ + src/test/test_cell_queue.c \ src/test/test_data.c \ src/test/test_dir.c \ + src/test/test_extorport.c \ src/test/test_introduce.c \ + src/test/test_logging.c \ src/test/test_microdesc.c \ + src/test/test_oom.c \ + src/test/test_options.c \ src/test/test_pt.c \ + src/test/test_relaycell.c \ src/test/test_replay.c \ + src/test/test_routerkeys.c \ + src/test/test_socks.c \ src/test/test_util.c \ src/test/test_config.c \ + src/test/test_hs.c \ + src/test/test_nodelist.c \ + src/test/test_policy.c \ + src/test/test_status.c \ src/ext/tinytest.c +src_test_test_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) + src_test_test_CPPFLAGS= $(src_test_AM_CPPFLAGS) src_test_bench_SOURCES = \ src/test/bench.c -src_test_bench_CPPFLAGS= $(src_test_AM_CPPFLAGS) - src_test_test_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \ @TOR_LDFLAGS_libevent@ -src_test_test_LDADD = src/or/libtor.a src/common/libor.a \ - src/common/libor-crypto.a $(LIBDONNA) \ - src/common/libor-event.a \ +src_test_test_LDADD = src/or/libtor-testing.a src/common/libor-testing.a \ + src/common/libor-crypto-testing.a $(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@ @@ -63,6 +83,39 @@ src_test_test_ntor_cl_LDADD = src/or/libtor.a src/common/libor.a \ @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ src_test_test_ntor_cl_AM_CPPFLAGS = \ -I"$(top_srcdir)/src/or" +NTOR_TEST_DEPS=src/test/test-ntor-cl +else +NTOR_TEST_DEPS= +endif +if COVERAGE_ENABLED +CMDLINE_TEST_TOR = ./src/or/tor-cov +else +CMDLINE_TEST_TOR = ./src/or/tor +endif + +noinst_PROGRAMS += src/test/test-bt-cl +src_test_test_bt_cl_SOURCES = src/test/test_bt_cl.c +src_test_test_bt_cl_LDADD = src/common/libor-testing.a \ + @TOR_LIB_MATH@ \ + @TOR_LIB_WS32@ @TOR_LIB_GDI@ +src_test_test_bt_cl_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) +src_test_test_bt_cl_CPPFLAGS= $(src_test_AM_CPPFLAGS) + + +check-local: $(NTOR_TEST_DEPS) $(CMDLINE_TEST_TOR) +if USEPYTHON + $(PYTHON) $(top_srcdir)/src/test/test_cmdline_args.py $(CMDLINE_TEST_TOR) "${top_srcdir}" +if CURVE25519_ENABLED + $(PYTHON) $(top_srcdir)/src/test/ntor_ref.py test-tor + $(PYTHON) $(top_srcdir)/src/test/ntor_ref.py self-test +endif + ./src/test/test-bt-cl assert | $(PYTHON) $(top_srcdir)/src/test/bt_test.py + ./src/test/test-bt-cl crash | $(PYTHON) $(top_srcdir)/src/test/bt_test.py endif +EXTRA_DIST += \ + src/test/bt_test.py \ + src/test/ntor_ref.py \ + src/test/slownacl_curve25519.py \ + src/test/test_cmdline_args.py diff --git a/src/test/ntor_ref.py b/src/test/ntor_ref.py index ade468da7d..7d6e43e716 100644..100755 --- a/src/test/ntor_ref.py +++ b/src/test/ntor_ref.py @@ -1,3 +1,4 @@ +#!/usr/bin/python # Copyright 2012-2013, The Tor Project, Inc # See LICENSE for licensing information @@ -27,17 +28,25 @@ commands: """ import binascii -import curve25519 +try: + import curve25519 + curve25519mod = curve25519.keys +except ImportError: + curve25519 = None + import slownacl_curve25519 + curve25519mod = slownacl_curve25519 + import hashlib import hmac import subprocess +import sys # ********************************************************************** # Helpers and constants def HMAC(key,msg): "Return the HMAC-SHA256 of 'msg' using the key 'key'." - H = hmac.new(key, "", hashlib.sha256) + H = hmac.new(key, b"", hashlib.sha256) H.update(msg) return H.digest() @@ -59,31 +68,38 @@ G_LENGTH = 32 H_LENGTH = 32 PROTOID = b"ntor-curve25519-sha256-1" -M_EXPAND = PROTOID + ":key_expand" -T_MAC = PROTOID + ":mac" -T_KEY = PROTOID + ":key_extract" -T_VERIFY = PROTOID + ":verify" +M_EXPAND = PROTOID + b":key_expand" +T_MAC = PROTOID + b":mac" +T_KEY = PROTOID + b":key_extract" +T_VERIFY = PROTOID + b":verify" def H_mac(msg): return H(msg, tweak=T_MAC) def H_verify(msg): return H(msg, tweak=T_VERIFY) -class PrivateKey(curve25519.keys.Private): - """As curve25519.keys.Private, but doesn't regenerate its public key +class PrivateKey(curve25519mod.Private): + """As curve25519mod.Private, but doesn't regenerate its public key every time you ask for it. """ def __init__(self): - curve25519.keys.Private.__init__(self) + curve25519mod.Private.__init__(self) self._memo_public = None def get_public(self): if self._memo_public is None: - self._memo_public = curve25519.keys.Private.get_public(self) + self._memo_public = curve25519mod.Private.get_public(self) return self._memo_public # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -def kdf_rfc5869(key, salt, info, n): +if sys.version < '3': + def int2byte(i): + return chr(i) +else: + def int2byte(i): + return bytes([i]) + +def kdf_rfc5869(key, salt, info, n): prk = HMAC(key=salt, msg=key) @@ -91,7 +107,7 @@ def kdf_rfc5869(key, salt, info, n): last = b"" i = 1 while len(out) < n: - m = last + info + chr(i) + m = last + info + int2byte(i) last = h = HMAC(key=prk, msg=m) out += h i = i + 1 @@ -177,7 +193,7 @@ def server(seckey_b, my_node_id, message, keyBytes=72): badness = (keyid(seckey_b.get_public()) != message[NODE_ID_LENGTH:NODE_ID_LENGTH+H_LENGTH]) - pubkey_X = curve25519.keys.Public(message[NODE_ID_LENGTH+H_LENGTH:]) + pubkey_X = curve25519mod.Public(message[NODE_ID_LENGTH+H_LENGTH:]) seckey_y = PrivateKey() pubkey_Y = seckey_y.get_public() pubkey_B = seckey_b.get_public() @@ -200,7 +216,7 @@ def server(seckey_b, my_node_id, message, keyBytes=72): pubkey_Y.serialize() + pubkey_X.serialize() + PROTOID + - "Server") + b"Server") msg = pubkey_Y.serialize() + H_mac(auth_input) @@ -240,7 +256,7 @@ def client_part2(seckey_x, msg, node_id, pubkey_B, keyBytes=72): """ assert len(msg) == G_LENGTH + H_LENGTH - pubkey_Y = curve25519.keys.Public(msg[:G_LENGTH]) + pubkey_Y = curve25519mod.Public(msg[:G_LENGTH]) their_auth = msg[G_LENGTH:] pubkey_X = seckey_x.get_public() @@ -262,7 +278,7 @@ def client_part2(seckey_x, msg, node_id, pubkey_B, keyBytes=72): pubkey_B.serialize() + pubkey_Y.serialize() + pubkey_X.serialize() + PROTOID + - "Server") + b"Server") my_auth = H_mac(auth_input) @@ -276,7 +292,7 @@ def client_part2(seckey_x, msg, node_id, pubkey_B, keyBytes=72): # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -def demo(node_id="iToldYouAboutStairs.", server_key=PrivateKey()): +def demo(node_id=b"iToldYouAboutStairs.", server_key=PrivateKey()): """ Try to handshake with ourself. """ @@ -286,6 +302,7 @@ def demo(node_id="iToldYouAboutStairs.", server_key=PrivateKey()): assert len(skeys) == 72 assert len(ckeys) == 72 assert skeys == ckeys + print("OK") # ====================================================================== def timing(): @@ -295,7 +312,7 @@ def timing(): import timeit t = timeit.Timer(stmt="ntor_ref.demo(N,SK)", setup="import ntor_ref,curve25519;N='ABCD'*5;SK=ntor_ref.PrivateKey()") - print t.timeit(number=1000) + print(t.timeit(number=1000)) # ====================================================================== @@ -306,7 +323,7 @@ def kdf_vectors(): import binascii def kdf_vec(inp): k = kdf(inp, T_KEY, M_EXPAND, 100) - print repr(inp), "\n\""+ binascii.b2a_hex(k)+ "\"" + print(repr(inp), "\n\""+ binascii.b2a_hex(k)+ "\"") kdf_vec("") kdf_vec("Tor") kdf_vec("AN ALARMING ITEM TO FIND ON YOUR CREDIT-RATING STATEMENT") @@ -319,13 +336,13 @@ def test_tor(): Call the test-ntor-cl command-line program to make sure we can interoperate with Tor's ntor program """ - enhex=binascii.b2a_hex + enhex=lambda s: binascii.b2a_hex(s) dehex=lambda s: binascii.a2b_hex(s.strip()) - PROG = "./src/test/test-ntor-cl" + PROG = b"./src/test/test-ntor-cl" def tor_client1(node_id, pubkey_B): " returns (msg, state) " - p = subprocess.Popen([PROG, "client1", enhex(node_id), + p = subprocess.Popen([PROG, b"client1", enhex(node_id), enhex(pubkey_B.serialize())], stdout=subprocess.PIPE) return map(dehex, p.stdout.readlines()) @@ -343,7 +360,7 @@ def test_tor(): return map(dehex, p.stdout.readlines()) - node_id = "thisisatornodeid$#%^" + node_id = b"thisisatornodeid$#%^" seckey_b = PrivateKey() pubkey_B = seckey_b.get_public() @@ -368,13 +385,14 @@ def test_tor(): assert c_keys == s_keys assert len(c_keys) == 90 - print "We just interoperated." + print("OK") # ====================================================================== if __name__ == '__main__': - import sys - if sys.argv[1] == 'gen_kdf_vectors': + if len(sys.argv) < 2: + print(__doc__) + elif sys.argv[1] == 'gen_kdf_vectors': kdf_vectors() elif sys.argv[1] == 'timing': timing() @@ -384,4 +402,4 @@ if __name__ == '__main__': test_tor() else: - print __doc__ + print(__doc__) diff --git a/src/test/slownacl_curve25519.py b/src/test/slownacl_curve25519.py new file mode 100644 index 0000000000..4dabab61b6 --- /dev/null +++ b/src/test/slownacl_curve25519.py @@ -0,0 +1,117 @@ +# This is the curve25519 implementation from Matthew Dempsky's "Slownacl" +# library. It is in the public domain. +# +# It isn't constant-time. Don't use it except for testing. +# +# Nick got the slownacl source from: +# https://github.com/mdempsky/dnscurve/tree/master/slownacl + +__all__ = ['smult_curve25519_base', 'smult_curve25519'] + +import sys + +P = 2 ** 255 - 19 +A = 486662 + +def expmod(b, e, m): + if e == 0: return 1 + t = expmod(b, e // 2, m) ** 2 % m + if e & 1: t = (t * b) % m + return t + +def inv(x): + return expmod(x, P - 2, P) + +# Addition and doubling formulas taken from Appendix D of "Curve25519: +# new Diffie-Hellman speed records". + +def add(n,m,d): + (xn,zn), (xm,zm), (xd,zd) = n, m, d + x = 4 * (xm * xn - zm * zn) ** 2 * zd + z = 4 * (xm * zn - zm * xn) ** 2 * xd + return (x % P, z % P) + +def double(n): + (xn,zn) = n + x = (xn ** 2 - zn ** 2) ** 2 + z = 4 * xn * zn * (xn ** 2 + A * xn * zn + zn ** 2) + return (x % P, z % P) + +def curve25519(n, base): + one = (base,1) + two = double(one) + # f(m) evaluates to a tuple containing the mth multiple and the + # (m+1)th multiple of base. + def f(m): + if m == 1: return (one, two) + (pm, pm1) = f(m // 2) + if (m & 1): + return (add(pm, pm1, one), double(pm1)) + return (double(pm), add(pm, pm1, one)) + ((x,z), _) = f(n) + return (x * inv(z)) % P + +if sys.version < '3': + def b2i(c): + return ord(c) + def i2b(i): + return chr(i) + def ba2bs(ba): + return "".join(ba) +else: + def b2i(c): + return c + def i2b(i): + return i + def ba2bs(ba): + return bytes(ba) + +def unpack(s): + if len(s) != 32: raise ValueError('Invalid Curve25519 argument') + return sum(b2i(s[i]) << (8 * i) for i in range(32)) + +def pack(n): + return ba2bs([i2b((n >> (8 * i)) & 255) for i in range(32)]) + +def clamp(n): + n &= ~7 + n &= ~(128 << 8 * 31) + n |= 64 << 8 * 31 + return n + +def smult_curve25519(n, p): + n = clamp(unpack(n)) + p = unpack(p) + return pack(curve25519(n, p)) + +def smult_curve25519_base(n): + n = clamp(unpack(n)) + return pack(curve25519(n, 9)) + + +# +# This part I'm adding in for compatibility with the curve25519 python +# module. -Nick +# +import os + +class Private: + def __init__(self, secret=None, seed=None): + self.private = pack(clamp(unpack(os.urandom(32)))) + + def get_public(self): + return Public(smult_curve25519_base(self.private)) + + def get_shared_key(self, public, hashfn): + return hashfn(smult_curve25519(self.private, public.public)) + + def serialize(self): + return self.private + +class Public: + def __init__(self, public): + self.public = public + + def serialize(self): + return self.public + diff --git a/src/test/test-child.c b/src/test/test-child.c index ef10fbb922..756782e70b 100644 --- a/src/test/test-child.c +++ b/src/test/test-child.c @@ -9,6 +9,13 @@ #else #include <unistd.h> #endif +#include <string.h> + +#ifdef _WIN32 +#define SLEEP(sec) Sleep((sec)*1000) +#else +#define SLEEP(sec) sleep(sec) +#endif /** Trivial test program which prints out its command line arguments so we can * check if tor_spawn_background() works */ @@ -16,27 +23,38 @@ int main(int argc, char **argv) { int i; + int delay = 1; + int fast = 0; + + if (argc > 1) { + if (!strcmp(argv[1], "--hang")) { + delay = 60; + } else if (!strcmp(argv[1], "--fast")) { + fast = 1; + delay = 0; + } + } fprintf(stdout, "OUT\n"); fprintf(stderr, "ERR\n"); for (i = 1; i < argc; i++) fprintf(stdout, "%s\n", argv[i]); - fprintf(stdout, "SLEEPING\n"); + if (!fast) + fprintf(stdout, "SLEEPING\n"); /* We need to flush stdout so that test_util_spawn_background_partial_read() succeed. Otherwise ReadFile() will get the entire output in one */ // XXX: Can we make stdio flush on newline? fflush(stdout); -#ifdef _WIN32 - Sleep(1000); -#else - sleep(1); -#endif + if (!fast) + SLEEP(1); fprintf(stdout, "DONE\n"); -#ifdef _WIN32 - Sleep(1000); -#else - sleep(1); -#endif + fflush(stdout); + if (fast) + return 0; + + while (--delay) { + SLEEP(1); + } return 0; } diff --git a/src/test/test-network.sh b/src/test/test-network.sh new file mode 100755 index 0000000000..7b59864166 --- /dev/null +++ b/src/test/test-network.sh @@ -0,0 +1,47 @@ +#! /bin/sh + +until [ -z $1 ] +do + case $1 in + --chutney-path) + export CHUTNEY_PATH="$2" + shift + ;; + --tor-path) + export TOR_DIR="$2" + shift + ;; + --flavo?r|--network-flavo?r) + export NETWORK_FLAVOUR="$2" + shift + ;; + *) + echo "Sorry, I don't know what to do with '$1'." + exit 2 + ;; + esac + shift +done + +TOR_DIR="${TOR_DIR:-$PWD}" +NETWORK_FLAVOUR=${NETWORK_FLAVOUR:-basic} +CHUTNEY_NETWORK=networks/$NETWORK_FLAVOUR +myname=$(basename $0) + +[ -d "$CHUTNEY_PATH" ] && [ -x "$CHUTNEY_PATH/chutney" ] || { + echo "$myname: missing 'chutney' in CHUTNEY_PATH ($CHUTNEY_PATH)" + exit 1 +} +cd "$CHUTNEY_PATH" +# For picking up the right tor binaries. +PATH="$TOR_DIR/src/or:$TOR_DIR/src/tools:$PATH" +./tools/bootstrap-network.sh $NETWORK_FLAVOUR || exit 2 + +# Sleep some, waiting for the network to bootstrap. +# TODO: Add chutney command 'bootstrap-status' and use that instead. +BOOTSTRAP_TIME=18 +echo -n "$myname: sleeping for $BOOTSTRAP_TIME seconds" +n=$BOOTSTRAP_TIME; while [ $n -gt 0 ]; do + sleep 1; n=$(expr $n - 1); echo -n . +done; echo "" +./chutney verify $CHUTNEY_NETWORK diff --git a/src/test/test.c b/src/test/test.c index c2911d842c..8bce9c91f4 100644 --- a/src/test/test.c +++ b/src/test/test.c @@ -28,11 +28,11 @@ const char tor_git_revision[] = ""; /* These macros pull in declarations for some functions and structures that * are typically file-private. */ -#define BUFFERS_PRIVATE -#define CONFIG_PRIVATE #define GEOIP_PRIVATE #define ROUTER_PRIVATE #define CIRCUITSTATS_PRIVATE +#define CIRCUITLIST_PRIVATE +#define STATEFILE_PRIVATE /* * Linux doesn't provide lround in math.h by default, but mac os does... @@ -52,16 +52,20 @@ double fabs(double x); #include "rendcommon.h" #include "test.h" #include "torgzip.h" +#ifdef ENABLE_MEMPOOLS #include "mempool.h" +#endif #include "memarea.h" #include "onion.h" -#include "onion_tap.h" #include "onion_ntor.h" +#include "onion_tap.h" #include "policies.h" #include "rephist.h" #include "routerparse.h" +#include "statefile.h" #ifdef CURVE25519_ENABLED #include "crypto_curve25519.h" +#include "onion_ntor.h" #endif #ifdef USE_DMALLOC @@ -218,667 +222,138 @@ free_pregenerated_keys(void) } } -typedef struct socks_test_data_t { - socks_request_t *req; - buf_t *buf; -} socks_test_data_t; - -static void * -socks_test_setup(const struct testcase_t *testcase) -{ - socks_test_data_t *data = tor_malloc(sizeof(socks_test_data_t)); - (void)testcase; - data->buf = buf_new_with_capacity(256); - data->req = socks_request_new(); - config_register_addressmaps(get_options()); - return data; -} -static int -socks_test_cleanup(const struct testcase_t *testcase, void *ptr) -{ - socks_test_data_t *data = ptr; - (void)testcase; - buf_free(data->buf); - socks_request_free(data->req); - tor_free(data); - return 1; -} - -const struct testcase_setup_t socks_setup = { - socks_test_setup, socks_test_cleanup -}; - -#define SOCKS_TEST_INIT() \ - socks_test_data_t *testdata = ptr; \ - buf_t *buf = testdata->buf; \ - socks_request_t *socks = testdata->req; -#define ADD_DATA(buf, s) \ - write_to_buf(s, sizeof(s)-1, buf) - -static void -socks_request_clear(socks_request_t *socks) -{ - tor_free(socks->username); - tor_free(socks->password); - memset(socks, 0, sizeof(socks_request_t)); -} - -/** Perform unsupported SOCKS 4 commands */ -static void -test_socks_4_unsupported_commands(void *ptr) -{ - SOCKS_TEST_INIT(); - - /* SOCKS 4 Send BIND [02] to IP address 2.2.2.2:4369 */ - ADD_DATA(buf, "\x04\x02\x11\x11\x02\x02\x02\x02\x00"); - test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks, - get_options()->SafeSocks) == -1); - test_eq(4, socks->socks_version); - test_eq(0, socks->replylen); /* XXX: shouldn't tor reply? */ - - done: - ; -} - -/** Perform supported SOCKS 4 commands */ -static void -test_socks_4_supported_commands(void *ptr) -{ - SOCKS_TEST_INIT(); - - test_eq(0, buf_datalen(buf)); - - /* SOCKS 4 Send CONNECT [01] to IP address 2.2.2.2:4370 */ - ADD_DATA(buf, "\x04\x01\x11\x12\x02\x02\x02\x03\x00"); - test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks, - get_options()->SafeSocks) == 1); - test_eq(4, socks->socks_version); - test_eq(0, socks->replylen); /* XXX: shouldn't tor reply? */ - test_eq(SOCKS_COMMAND_CONNECT, socks->command); - test_streq("2.2.2.3", socks->address); - test_eq(4370, socks->port); - test_assert(socks->got_auth == 0); - test_assert(! socks->username); - - test_eq(0, buf_datalen(buf)); - socks_request_clear(socks); - - /* SOCKS 4 Send CONNECT [01] to IP address 2.2.2.2:4369 with userid*/ - ADD_DATA(buf, "\x04\x01\x11\x12\x02\x02\x02\x04me\x00"); - test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks, - get_options()->SafeSocks) == 1); - test_eq(4, socks->socks_version); - test_eq(0, socks->replylen); /* XXX: shouldn't tor reply? */ - test_eq(SOCKS_COMMAND_CONNECT, socks->command); - test_streq("2.2.2.4", socks->address); - test_eq(4370, socks->port); - test_assert(socks->got_auth == 1); - test_assert(socks->username); - test_eq(2, socks->usernamelen); - test_memeq("me", socks->username, 2); - - test_eq(0, buf_datalen(buf)); - socks_request_clear(socks); - - /* SOCKS 4a Send RESOLVE [F0] request for torproject.org */ - ADD_DATA(buf, "\x04\xF0\x01\x01\x00\x00\x00\x02me\x00torproject.org\x00"); - test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks, - get_options()->SafeSocks) == 1); - test_eq(4, socks->socks_version); - test_eq(0, socks->replylen); /* XXX: shouldn't tor reply? */ - test_streq("torproject.org", socks->address); - - test_eq(0, buf_datalen(buf)); - - done: - ; -} - -/** Perform unsupported SOCKS 5 commands */ -static void -test_socks_5_unsupported_commands(void *ptr) -{ - SOCKS_TEST_INIT(); - - /* SOCKS 5 Send unsupported BIND [02] command */ - ADD_DATA(buf, "\x05\x02\x00\x01"); - - test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks, - get_options()->SafeSocks), 0); - test_eq(0, buf_datalen(buf)); - test_eq(5, socks->socks_version); - test_eq(2, socks->replylen); - test_eq(5, socks->reply[0]); - test_eq(0, socks->reply[1]); - ADD_DATA(buf, "\x05\x02\x00\x01\x02\x02\x02\x01\x01\x01"); - test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks, - get_options()->SafeSocks), -1); - /* XXX: shouldn't tor reply 'command not supported' [07]? */ - - buf_clear(buf); - socks_request_clear(socks); - - /* SOCKS 5 Send unsupported UDP_ASSOCIATE [03] command */ - ADD_DATA(buf, "\x05\x03\x00\x01\x02"); - test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks, - get_options()->SafeSocks), 0); - test_eq(5, socks->socks_version); - test_eq(2, socks->replylen); - test_eq(5, socks->reply[0]); - test_eq(2, socks->reply[1]); - ADD_DATA(buf, "\x05\x03\x00\x01\x02\x02\x02\x01\x01\x01"); - test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks, - get_options()->SafeSocks), -1); - /* XXX: shouldn't tor reply 'command not supported' [07]? */ - - done: - ; -} - -/** Perform supported SOCKS 5 commands */ -static void -test_socks_5_supported_commands(void *ptr) -{ - SOCKS_TEST_INIT(); - - /* SOCKS 5 Send CONNECT [01] to IP address 2.2.2.2:4369 */ - ADD_DATA(buf, "\x05\x01\x00"); - test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks, - get_options()->SafeSocks), 0); - test_eq(5, socks->socks_version); - test_eq(2, socks->replylen); - test_eq(5, socks->reply[0]); - test_eq(0, socks->reply[1]); - - ADD_DATA(buf, "\x05\x01\x00\x01\x02\x02\x02\x02\x11\x11"); - test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks, - get_options()->SafeSocks), 1); - test_streq("2.2.2.2", socks->address); - test_eq(4369, socks->port); - - test_eq(0, buf_datalen(buf)); - socks_request_clear(socks); - - /* SOCKS 5 Send CONNECT [01] to FQDN torproject.org:4369 */ - ADD_DATA(buf, "\x05\x01\x00"); - ADD_DATA(buf, "\x05\x01\x00\x03\x0Etorproject.org\x11\x11"); - test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks, - get_options()->SafeSocks), 1); - - test_eq(5, socks->socks_version); - test_eq(2, socks->replylen); - test_eq(5, socks->reply[0]); - test_eq(0, socks->reply[1]); - test_streq("torproject.org", socks->address); - test_eq(4369, socks->port); - - test_eq(0, buf_datalen(buf)); - socks_request_clear(socks); - - /* SOCKS 5 Send RESOLVE [F0] request for torproject.org:4369 */ - ADD_DATA(buf, "\x05\x01\x00"); - ADD_DATA(buf, "\x05\xF0\x00\x03\x0Etorproject.org\x01\x02"); - test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks, - get_options()->SafeSocks) == 1); - test_eq(5, socks->socks_version); - test_eq(2, socks->replylen); - test_eq(5, socks->reply[0]); - test_eq(0, socks->reply[1]); - test_streq("torproject.org", socks->address); - - test_eq(0, buf_datalen(buf)); - socks_request_clear(socks); - - /* SOCKS 5 Send RESOLVE_PTR [F1] for IP address 2.2.2.5 */ - ADD_DATA(buf, "\x05\x01\x00"); - ADD_DATA(buf, "\x05\xF1\x00\x01\x02\x02\x02\x05\x01\x03"); - test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks, - get_options()->SafeSocks) == 1); - test_eq(5, socks->socks_version); - test_eq(2, socks->replylen); - test_eq(5, socks->reply[0]); - test_eq(0, socks->reply[1]); - test_streq("2.2.2.5", socks->address); - - test_eq(0, buf_datalen(buf)); - - done: - ; -} - -/** Perform SOCKS 5 authentication */ -static void -test_socks_5_no_authenticate(void *ptr) -{ - SOCKS_TEST_INIT(); - - /*SOCKS 5 No Authentication */ - ADD_DATA(buf,"\x05\x01\x00"); - test_assert(!fetch_from_buf_socks(buf, socks, - get_options()->TestSocks, - get_options()->SafeSocks)); - test_eq(2, socks->replylen); - test_eq(5, socks->reply[0]); - test_eq(SOCKS_NO_AUTH, socks->reply[1]); - - test_eq(0, buf_datalen(buf)); - - /*SOCKS 5 Send username/password anyway - pretend to be broken */ - ADD_DATA(buf,"\x01\x02\x01\x01\x02\x01\x01"); - test_assert(!fetch_from_buf_socks(buf, socks, - get_options()->TestSocks, - get_options()->SafeSocks)); - test_eq(5, socks->socks_version); - test_eq(2, socks->replylen); - test_eq(1, socks->reply[0]); - test_eq(0, socks->reply[1]); - - test_eq(2, socks->usernamelen); - test_eq(2, socks->passwordlen); - - test_memeq("\x01\x01", socks->username, 2); - test_memeq("\x01\x01", socks->password, 2); - - done: - ; -} - -/** Perform SOCKS 5 authentication */ -static void -test_socks_5_authenticate(void *ptr) -{ - SOCKS_TEST_INIT(); - - /* SOCKS 5 Negotiate username/password authentication */ - ADD_DATA(buf, "\x05\x01\x02"); - - test_assert(!fetch_from_buf_socks(buf, socks, - get_options()->TestSocks, - get_options()->SafeSocks)); - test_eq(2, socks->replylen); - test_eq(5, socks->reply[0]); - test_eq(SOCKS_USER_PASS, socks->reply[1]); - test_eq(5, socks->socks_version); - - test_eq(0, buf_datalen(buf)); - - /* SOCKS 5 Send username/password */ - ADD_DATA(buf, "\x01\x02me\x08mypasswd"); - test_assert(!fetch_from_buf_socks(buf, socks, - get_options()->TestSocks, - get_options()->SafeSocks)); - test_eq(5, socks->socks_version); - test_eq(2, socks->replylen); - test_eq(1, socks->reply[0]); - test_eq(0, socks->reply[1]); - - test_eq(2, socks->usernamelen); - test_eq(8, socks->passwordlen); - - test_memeq("me", socks->username, 2); - test_memeq("mypasswd", socks->password, 8); - - done: - ; -} - -/** Perform SOCKS 5 authentication and send data all in one go */ -static void -test_socks_5_authenticate_with_data(void *ptr) -{ - SOCKS_TEST_INIT(); - - /* SOCKS 5 Negotiate username/password authentication */ - ADD_DATA(buf, "\x05\x01\x02"); - - test_assert(!fetch_from_buf_socks(buf, socks, - get_options()->TestSocks, - get_options()->SafeSocks)); - test_eq(2, socks->replylen); - test_eq(5, socks->reply[0]); - test_eq(SOCKS_USER_PASS, socks->reply[1]); - test_eq(5, socks->socks_version); - - test_eq(0, buf_datalen(buf)); - - /* SOCKS 5 Send username/password */ - /* SOCKS 5 Send CONNECT [01] to IP address 2.2.2.2:4369 */ - ADD_DATA(buf, "\x01\x02me\x03you\x05\x01\x00\x01\x02\x02\x02\x02\x11\x11"); - test_assert(fetch_from_buf_socks(buf, socks, - get_options()->TestSocks, - get_options()->SafeSocks) == 1); - test_eq(5, socks->socks_version); - test_eq(2, socks->replylen); - test_eq(1, socks->reply[0]); - test_eq(0, socks->reply[1]); - - test_streq("2.2.2.2", socks->address); - test_eq(4369, socks->port); - - test_eq(2, socks->usernamelen); - test_eq(3, socks->passwordlen); - test_memeq("me", socks->username, 2); - test_memeq("you", socks->password, 3); - - done: - ; -} - -/** Perform SOCKS 5 authentication before method negotiated */ -static void -test_socks_5_auth_before_negotiation(void *ptr) -{ - SOCKS_TEST_INIT(); - - /* SOCKS 5 Send username/password */ - ADD_DATA(buf, "\x01\x02me\x02me"); - test_assert(fetch_from_buf_socks(buf, socks, - get_options()->TestSocks, - get_options()->SafeSocks) == -1); - test_eq(0, socks->socks_version); - test_eq(0, socks->replylen); - test_eq(0, socks->reply[0]); - test_eq(0, socks->reply[1]); - - done: - ; -} - +/** Run unit tests for the onion handshake code. */ static void -test_buffer_copy(void *arg) +test_onion_handshake(void) { - generic_buffer_t *buf=NULL, *buf2=NULL; - const char *s; - size_t len; - char b[256]; + /* client-side */ + crypto_dh_t *c_dh = NULL; + char c_buf[TAP_ONIONSKIN_CHALLENGE_LEN]; + char c_keys[40]; + /* server-side */ + char s_buf[TAP_ONIONSKIN_REPLY_LEN]; + char s_keys[40]; int i; - (void)arg; - - buf = generic_buffer_new(); - tt_assert(buf); - - /* Copy an empty buffer. */ - tt_int_op(0, ==, generic_buffer_set_to_copy(&buf2, buf)); - tt_assert(buf2); - tt_int_op(0, ==, generic_buffer_len(buf2)); - - /* Now try with a short buffer. */ - s = "And now comes an act of enormous enormance!"; - len = strlen(s); - generic_buffer_add(buf, s, len); - tt_int_op(len, ==, generic_buffer_len(buf)); - /* Add junk to buf2 so we can test replacing.*/ - generic_buffer_add(buf2, "BLARG", 5); - tt_int_op(0, ==, generic_buffer_set_to_copy(&buf2, buf)); - tt_int_op(len, ==, generic_buffer_len(buf2)); - generic_buffer_get(buf2, b, len); - test_mem_op(b, ==, s, len); - /* Now free buf2 and retry so we can test allocating */ - generic_buffer_free(buf2); - buf2 = NULL; - tt_int_op(0, ==, generic_buffer_set_to_copy(&buf2, buf)); - tt_int_op(len, ==, generic_buffer_len(buf2)); - generic_buffer_get(buf2, b, len); - test_mem_op(b, ==, s, len); - /* Clear buf for next test */ - generic_buffer_get(buf, b, len); - tt_int_op(generic_buffer_len(buf),==,0); - - /* Okay, now let's try a bigger buffer. */ - s = "Quis autem vel eum iure reprehenderit qui in ea voluptate velit " - "esse quam nihil molestiae consequatur, vel illum qui dolorem eum " - "fugiat quo voluptas nulla pariatur?"; - len = strlen(s); - for (i = 0; i < 256; ++i) { - b[0]=i; - generic_buffer_add(buf, b, 1); - generic_buffer_add(buf, s, len); - } - tt_int_op(0, ==, generic_buffer_set_to_copy(&buf2, buf)); - tt_int_op(generic_buffer_len(buf2), ==, generic_buffer_len(buf)); - for (i = 0; i < 256; ++i) { - generic_buffer_get(buf2, b, len+1); - tt_int_op((unsigned char)b[0],==,i); - test_mem_op(b+1, ==, s, len); - } - - done: - if (buf) - generic_buffer_free(buf); - if (buf2) - generic_buffer_free(buf2); -} - -/** Run unit tests for buffers.c */ -static void -test_buffers(void) -{ - char str[256]; - char str2[256]; - - buf_t *buf = NULL, *buf2 = NULL; - const char *cp; + /* shared */ + crypto_pk_t *pk = NULL, *pk2 = NULL; - int j; - size_t r; + pk = pk_generate(0); + pk2 = pk_generate(1); - /**** - * buf_new - ****/ - if (!(buf = buf_new())) - test_fail(); + /* client handshake 1. */ + memset(c_buf, 0, TAP_ONIONSKIN_CHALLENGE_LEN); + test_assert(! onion_skin_TAP_create(pk, &c_dh, c_buf)); - //test_eq(buf_capacity(buf), 4096); - test_eq(buf_datalen(buf), 0); + for (i = 1; i <= 3; ++i) { + crypto_pk_t *k1, *k2; + if (i==1) { + /* server handshake: only one key known. */ + k1 = pk; k2 = NULL; + } else if (i==2) { + /* server handshake: try the right key first. */ + k1 = pk; k2 = pk2; + } else { + /* server handshake: try the right key second. */ + k1 = pk2; k2 = pk; + } - /**** - * General pointer frobbing - */ - for (j=0;j<256;++j) { - str[j] = (char)j; - } - write_to_buf(str, 256, buf); - write_to_buf(str, 256, buf); - test_eq(buf_datalen(buf), 512); - fetch_from_buf(str2, 200, buf); - test_memeq(str, str2, 200); - test_eq(buf_datalen(buf), 312); - memset(str2, 0, sizeof(str2)); - - fetch_from_buf(str2, 256, buf); - test_memeq(str+200, str2, 56); - test_memeq(str, str2+56, 200); - test_eq(buf_datalen(buf), 56); - memset(str2, 0, sizeof(str2)); - /* Okay, now we should be 512 bytes into the 4096-byte buffer. If we add - * another 3584 bytes, we hit the end. */ - for (j=0;j<15;++j) { - write_to_buf(str, 256, buf); - } - assert_buf_ok(buf); - test_eq(buf_datalen(buf), 3896); - fetch_from_buf(str2, 56, buf); - test_eq(buf_datalen(buf), 3840); - test_memeq(str+200, str2, 56); - for (j=0;j<15;++j) { - memset(str2, 0, sizeof(str2)); - fetch_from_buf(str2, 256, buf); - test_memeq(str, str2, 256); - } - test_eq(buf_datalen(buf), 0); - buf_free(buf); - buf = NULL; - - /* Okay, now make sure growing can work. */ - buf = buf_new_with_capacity(16); - //test_eq(buf_capacity(buf), 16); - write_to_buf(str+1, 255, buf); - //test_eq(buf_capacity(buf), 256); - fetch_from_buf(str2, 254, buf); - test_memeq(str+1, str2, 254); - //test_eq(buf_capacity(buf), 256); - assert_buf_ok(buf); - write_to_buf(str, 32, buf); - //test_eq(buf_capacity(buf), 256); - assert_buf_ok(buf); - write_to_buf(str, 256, buf); - assert_buf_ok(buf); - //test_eq(buf_capacity(buf), 512); - test_eq(buf_datalen(buf), 33+256); - fetch_from_buf(str2, 33, buf); - test_eq(*str2, str[255]); - - test_memeq(str2+1, str, 32); - //test_eq(buf_capacity(buf), 512); - test_eq(buf_datalen(buf), 256); - fetch_from_buf(str2, 256, buf); - test_memeq(str, str2, 256); - - /* now try shrinking: case 1. */ - buf_free(buf); - buf = buf_new_with_capacity(33668); - for (j=0;j<67;++j) { - write_to_buf(str,255, buf); - } - //test_eq(buf_capacity(buf), 33668); - test_eq(buf_datalen(buf), 17085); - for (j=0; j < 40; ++j) { - fetch_from_buf(str2, 255,buf); - test_memeq(str2, str, 255); - } + memset(s_buf, 0, TAP_ONIONSKIN_REPLY_LEN); + memset(s_keys, 0, 40); + test_assert(! onion_skin_TAP_server_handshake(c_buf, k1, k2, + s_buf, s_keys, 40)); - /* now try shrinking: case 2. */ - buf_free(buf); - buf = buf_new_with_capacity(33668); - for (j=0;j<67;++j) { - write_to_buf(str,255, buf); - } - for (j=0; j < 20; ++j) { - fetch_from_buf(str2, 255,buf); - test_memeq(str2, str, 255); - } - for (j=0;j<80;++j) { - write_to_buf(str,255, buf); - } - //test_eq(buf_capacity(buf),33668); - for (j=0; j < 120; ++j) { - fetch_from_buf(str2, 255,buf); - test_memeq(str2, str, 255); - } + /* client handshake 2 */ + memset(c_keys, 0, 40); + test_assert(! onion_skin_TAP_client_handshake(c_dh, s_buf, c_keys, 40)); - /* Move from buf to buf. */ - buf_free(buf); - buf = buf_new_with_capacity(4096); - buf2 = buf_new_with_capacity(4096); - for (j=0;j<100;++j) - write_to_buf(str, 255, buf); - test_eq(buf_datalen(buf), 25500); - for (j=0;j<100;++j) { - r = 10; - move_buf_to_buf(buf2, buf, &r); - test_eq(r, 0); - } - test_eq(buf_datalen(buf), 24500); - test_eq(buf_datalen(buf2), 1000); - for (j=0;j<3;++j) { - fetch_from_buf(str2, 255, buf2); - test_memeq(str2, str, 255); - } - r = 8192; /*big move*/ - move_buf_to_buf(buf2, buf, &r); - test_eq(r, 0); - r = 30000; /* incomplete move */ - move_buf_to_buf(buf2, buf, &r); - test_eq(r, 13692); - for (j=0;j<97;++j) { - fetch_from_buf(str2, 255, buf2); - test_memeq(str2, str, 255); + test_memeq(c_keys, s_keys, 40); + memset(s_buf, 0, 40); + test_memneq(c_keys, s_buf, 40); } - buf_free(buf); - buf_free(buf2); - buf = buf2 = NULL; - - buf = buf_new_with_capacity(5); - cp = "Testing. This is a moderately long Testing string."; - for (j = 0; cp[j]; j++) - write_to_buf(cp+j, 1, buf); - test_eq(0, buf_find_string_offset(buf, "Testing", 7)); - test_eq(1, buf_find_string_offset(buf, "esting", 6)); - test_eq(1, buf_find_string_offset(buf, "est", 3)); - test_eq(39, buf_find_string_offset(buf, "ing str", 7)); - test_eq(35, buf_find_string_offset(buf, "Testing str", 11)); - test_eq(32, buf_find_string_offset(buf, "ng ", 3)); - test_eq(43, buf_find_string_offset(buf, "string.", 7)); - test_eq(-1, buf_find_string_offset(buf, "shrdlu", 6)); - test_eq(-1, buf_find_string_offset(buf, "Testing thing", 13)); - test_eq(-1, buf_find_string_offset(buf, "ngx", 3)); - buf_free(buf); - buf = NULL; - - /* Try adding a string too long for any freelist. */ - { - char *cp = tor_malloc_zero(65536); - buf = buf_new(); - write_to_buf(cp, 65536, buf); - tor_free(cp); - - tt_int_op(buf_datalen(buf), ==, 65536); - buf_free(buf); - buf = NULL; - } - done: - if (buf) - buf_free(buf); - if (buf2) - buf_free(buf2); + crypto_dh_free(c_dh); + crypto_pk_free(pk); + crypto_pk_free(pk2); } -/** Run unit tests for the onion handshake code. */ static void -test_onion_handshake(void) +test_bad_onion_handshake(void *arg) { + char junk_buf[TAP_ONIONSKIN_CHALLENGE_LEN]; + char junk_buf2[TAP_ONIONSKIN_CHALLENGE_LEN]; /* client-side */ crypto_dh_t *c_dh = NULL; char c_buf[TAP_ONIONSKIN_CHALLENGE_LEN]; char c_keys[40]; - /* server-side */ char s_buf[TAP_ONIONSKIN_REPLY_LEN]; char s_keys[40]; - /* shared */ - crypto_pk_t *pk = NULL; + crypto_pk_t *pk = NULL, *pk2 = NULL; + + (void)arg; pk = pk_generate(0); + pk2 = pk_generate(1); - /* client handshake 1. */ + /* Server: Case 1: the encrypted data is degenerate. */ + memset(junk_buf, 0, sizeof(junk_buf)); + crypto_pk_public_hybrid_encrypt(pk, junk_buf2, TAP_ONIONSKIN_CHALLENGE_LEN, + junk_buf, DH_KEY_LEN, PK_PKCS1_OAEP_PADDING, 1); + tt_int_op(-1, ==, + onion_skin_TAP_server_handshake(junk_buf2, pk, NULL, + s_buf, s_keys, 40)); + + /* Server: Case 2: the encrypted data is not long enough. */ + memset(junk_buf, 0, sizeof(junk_buf)); + memset(junk_buf2, 0, sizeof(junk_buf2)); + crypto_pk_public_encrypt(pk, junk_buf2, sizeof(junk_buf2), + junk_buf, 48, PK_PKCS1_OAEP_PADDING); + tt_int_op(-1, ==, + onion_skin_TAP_server_handshake(junk_buf2, pk, NULL, + s_buf, s_keys, 40)); + + /* client handshake 1: do it straight. */ memset(c_buf, 0, TAP_ONIONSKIN_CHALLENGE_LEN); test_assert(! onion_skin_TAP_create(pk, &c_dh, c_buf)); - /* server handshake */ - memset(s_buf, 0, TAP_ONIONSKIN_REPLY_LEN); - memset(s_keys, 0, 40); - test_assert(! onion_skin_TAP_server_handshake(c_buf, pk, NULL, + /* Server: Case 3: we just don't have the right key. */ + tt_int_op(-1, ==, + onion_skin_TAP_server_handshake(c_buf, pk2, NULL, s_buf, s_keys, 40)); - /* client handshake 2 */ - memset(c_keys, 0, 40); - test_assert(! onion_skin_TAP_client_handshake(c_dh, s_buf, c_keys, 40)); + /* Server: Case 4: The RSA-encrypted portion is corrupt. */ + c_buf[64] ^= 33; + tt_int_op(-1, ==, + onion_skin_TAP_server_handshake(c_buf, pk, NULL, + s_buf, s_keys, 40)); + c_buf[64] ^= 33; - if (memcmp(c_keys, s_keys, 40)) { - puts("Aiiiie"); - exit(1); - } - test_memeq(c_keys, s_keys, 40); - memset(s_buf, 0, 40); - test_memneq(c_keys, s_buf, 40); + /* (Let the server procede) */ + tt_int_op(0, ==, + onion_skin_TAP_server_handshake(c_buf, pk, NULL, + s_buf, s_keys, 40)); + + /* Client: Case 1: The server sent back junk. */ + s_buf[64] ^= 33; + tt_int_op(-1, ==, + onion_skin_TAP_client_handshake(c_dh, s_buf, c_keys, 40)); + s_buf[64] ^= 33; + + /* Let the client finish; make sure it can. */ + tt_int_op(0, ==, + onion_skin_TAP_client_handshake(c_dh, s_buf, c_keys, 40)); + test_memeq(s_keys, c_keys, 40); + + /* Client: Case 2: The server sent back a degenerate DH. */ + memset(s_buf, 0, sizeof(s_buf)); + tt_int_op(-1, ==, + onion_skin_TAP_client_handshake(c_dh, s_buf, c_keys, 40)); done: - if (c_dh) - crypto_dh_free(c_dh); - if (pk) - crypto_pk_free(pk); + crypto_dh_free(c_dh); + crypto_pk_free(pk); + crypto_pk_free(pk2); } #ifdef CURVE25519_ENABLED @@ -945,9 +420,10 @@ test_onion_queues(void) or_circuit_t *circ1 = or_circuit_new(0, NULL); or_circuit_t *circ2 = or_circuit_new(0, NULL); - create_cell_t *onionskin = NULL; + create_cell_t *onionskin = NULL, *create2_ptr; create_cell_t *create1 = tor_malloc_zero(sizeof(create_cell_t)); create_cell_t *create2 = tor_malloc_zero(sizeof(create_cell_t)); + create2_ptr = create2; /* remember, but do not free */ create_cell_init(create1, CELL_CREATE, ONION_HANDSHAKE_TYPE_TAP, TAP_ONIONSKIN_CHALLENGE_LEN, buf1); @@ -956,26 +432,29 @@ test_onion_queues(void) test_eq(0, onion_num_pending(ONION_HANDSHAKE_TYPE_TAP)); test_eq(0, onion_pending_add(circ1, create1)); + create1 = NULL; test_eq(1, onion_num_pending(ONION_HANDSHAKE_TYPE_TAP)); test_eq(0, onion_num_pending(ONION_HANDSHAKE_TYPE_NTOR)); test_eq(0, onion_pending_add(circ2, create2)); + create2 = NULL; test_eq(1, onion_num_pending(ONION_HANDSHAKE_TYPE_NTOR)); test_eq_ptr(circ2, onion_next_task(&onionskin)); test_eq(1, onion_num_pending(ONION_HANDSHAKE_TYPE_TAP)); test_eq(0, onion_num_pending(ONION_HANDSHAKE_TYPE_NTOR)); + tt_ptr_op(onionskin, ==, create2_ptr); clear_pending_onions(); test_eq(0, onion_num_pending(ONION_HANDSHAKE_TYPE_TAP)); test_eq(0, onion_num_pending(ONION_HANDSHAKE_TYPE_NTOR)); done: - ; -// circuit_free(circ1); -// circuit_free(circ2); - /* and free create1 and create2 */ - /* XXX leaks everything here */ + circuit_free(TO_CIRCUIT(circ1)); + circuit_free(TO_CIRCUIT(circ2)); + tor_free(create1); + tor_free(create2); + tor_free(onionskin); } static void @@ -994,14 +473,14 @@ test_circuit_timeout(void) circuit_build_times_t estimate; circuit_build_times_t final; double timeout1, timeout2; - or_state_t state; + or_state_t *state=NULL; int i, runs; double close_ms; circuit_build_times_init(&initial); circuit_build_times_init(&estimate); circuit_build_times_init(&final); - memset(&state, 0, sizeof(or_state_t)); + state = or_state_new(); circuitbuild_running_unit_tests(); #define timeout0 (build_time_t)(30*1000.0) @@ -1033,8 +512,9 @@ test_circuit_timeout(void) test_assert(estimate.total_build_times <= CBT_NCIRCUITS_TO_OBSERVE); - circuit_build_times_update_state(&estimate, &state); - test_assert(circuit_build_times_parse_state(&final, &state) == 0); + circuit_build_times_update_state(&estimate, state); + circuit_build_times_free_timeouts(&final); + test_assert(circuit_build_times_parse_state(&final, state) == 0); circuit_build_times_update_alpha(&final); timeout2 = circuit_build_times_calculate_timeout(&final, @@ -1123,336 +603,10 @@ test_circuit_timeout(void) } done: - return; -} - -/* Helper: assert that short_policy parses and writes back out as itself, - or as <b>expected</b> if that's provided. */ -static void -test_short_policy_parse(const char *input, - const char *expected) -{ - short_policy_t *short_policy = NULL; - char *out = NULL; - - if (expected == NULL) - expected = input; - - short_policy = parse_short_policy(input); - tt_assert(short_policy); - out = write_short_policy(short_policy); - tt_str_op(out, ==, expected); - - done: - tor_free(out); - short_policy_free(short_policy); -} - -/** Helper: Parse the exit policy string in <b>policy_str</b>, and make sure - * that policies_summarize() produces the string <b>expected_summary</b> from - * it. */ -static void -test_policy_summary_helper(const char *policy_str, - const char *expected_summary) -{ - config_line_t line; - smartlist_t *policy = smartlist_new(); - char *summary = NULL; - char *summary_after = NULL; - int r; - short_policy_t *short_policy = NULL; - - line.key = (char*)"foo"; - line.value = (char *)policy_str; - line.next = NULL; - - r = policies_parse_exit_policy(&line, &policy, 1, 0, NULL, 1); - test_eq(r, 0); - summary = policy_summarize(policy, AF_INET); - - test_assert(summary != NULL); - test_streq(summary, expected_summary); - - short_policy = parse_short_policy(summary); - tt_assert(short_policy); - summary_after = write_short_policy(short_policy); - test_streq(summary, summary_after); - - done: - tor_free(summary_after); - tor_free(summary); - if (policy) - addr_policy_list_free(policy); - short_policy_free(short_policy); -} - -/** Run unit tests for generating summary lines of exit policies */ -static void -test_policies(void) -{ - int i; - smartlist_t *policy = NULL, *policy2 = NULL, *policy3 = NULL, - *policy4 = NULL, *policy5 = NULL, *policy6 = NULL, - *policy7 = NULL; - addr_policy_t *p; - tor_addr_t tar; - config_line_t line; - smartlist_t *sm = NULL; - char *policy_str = NULL; - - policy = smartlist_new(); - - p = router_parse_addr_policy_item_from_string("reject 192.168.0.0/16:*",-1); - test_assert(p != NULL); - test_eq(ADDR_POLICY_REJECT, p->policy_type); - tor_addr_from_ipv4h(&tar, 0xc0a80000u); - test_eq(0, tor_addr_compare(&p->addr, &tar, CMP_EXACT)); - test_eq(16, p->maskbits); - test_eq(1, p->prt_min); - test_eq(65535, p->prt_max); - - smartlist_add(policy, p); - - tor_addr_from_ipv4h(&tar, 0x01020304u); - test_assert(ADDR_POLICY_ACCEPTED == - compare_tor_addr_to_addr_policy(&tar, 2, policy)); - tor_addr_make_unspec(&tar); - test_assert(ADDR_POLICY_PROBABLY_ACCEPTED == - compare_tor_addr_to_addr_policy(&tar, 2, policy)); - tor_addr_from_ipv4h(&tar, 0xc0a80102); - test_assert(ADDR_POLICY_REJECTED == - compare_tor_addr_to_addr_policy(&tar, 2, policy)); - - test_assert(0 == policies_parse_exit_policy(NULL, &policy2, 1, 1, NULL, 1)); - test_assert(policy2); - - policy3 = smartlist_new(); - p = router_parse_addr_policy_item_from_string("reject *:*",-1); - test_assert(p != NULL); - smartlist_add(policy3, p); - p = router_parse_addr_policy_item_from_string("accept *:*",-1); - test_assert(p != NULL); - smartlist_add(policy3, p); - - policy4 = smartlist_new(); - p = router_parse_addr_policy_item_from_string("accept *:443",-1); - test_assert(p != NULL); - smartlist_add(policy4, p); - p = router_parse_addr_policy_item_from_string("accept *:443",-1); - test_assert(p != NULL); - smartlist_add(policy4, p); - - policy5 = smartlist_new(); - p = router_parse_addr_policy_item_from_string("reject 0.0.0.0/8:*",-1); - test_assert(p != NULL); - smartlist_add(policy5, p); - p = router_parse_addr_policy_item_from_string("reject 169.254.0.0/16:*",-1); - test_assert(p != NULL); - smartlist_add(policy5, p); - p = router_parse_addr_policy_item_from_string("reject 127.0.0.0/8:*",-1); - test_assert(p != NULL); - smartlist_add(policy5, p); - p = router_parse_addr_policy_item_from_string("reject 192.168.0.0/16:*",-1); - test_assert(p != NULL); - smartlist_add(policy5, p); - p = router_parse_addr_policy_item_from_string("reject 10.0.0.0/8:*",-1); - test_assert(p != NULL); - smartlist_add(policy5, p); - p = router_parse_addr_policy_item_from_string("reject 172.16.0.0/12:*",-1); - test_assert(p != NULL); - smartlist_add(policy5, p); - p = router_parse_addr_policy_item_from_string("reject 80.190.250.90:*",-1); - test_assert(p != NULL); - smartlist_add(policy5, p); - p = router_parse_addr_policy_item_from_string("reject *:1-65534",-1); - test_assert(p != NULL); - smartlist_add(policy5, p); - p = router_parse_addr_policy_item_from_string("reject *:65535",-1); - test_assert(p != NULL); - smartlist_add(policy5, p); - p = router_parse_addr_policy_item_from_string("accept *:1-65535",-1); - test_assert(p != NULL); - smartlist_add(policy5, p); - - policy6 = smartlist_new(); - p = router_parse_addr_policy_item_from_string("accept 43.3.0.0/9:*",-1); - test_assert(p != NULL); - smartlist_add(policy6, p); - - policy7 = smartlist_new(); - p = router_parse_addr_policy_item_from_string("accept 0.0.0.0/8:*",-1); - test_assert(p != NULL); - smartlist_add(policy7, p); - - test_assert(!exit_policy_is_general_exit(policy)); - test_assert(exit_policy_is_general_exit(policy2)); - test_assert(!exit_policy_is_general_exit(NULL)); - test_assert(!exit_policy_is_general_exit(policy3)); - test_assert(!exit_policy_is_general_exit(policy4)); - test_assert(!exit_policy_is_general_exit(policy5)); - test_assert(!exit_policy_is_general_exit(policy6)); - test_assert(!exit_policy_is_general_exit(policy7)); - - test_assert(cmp_addr_policies(policy, policy2)); - test_assert(cmp_addr_policies(policy, NULL)); - test_assert(!cmp_addr_policies(policy2, policy2)); - test_assert(!cmp_addr_policies(NULL, NULL)); - - test_assert(!policy_is_reject_star(policy2, AF_INET)); - test_assert(policy_is_reject_star(policy, AF_INET)); - test_assert(policy_is_reject_star(NULL, AF_INET)); - - addr_policy_list_free(policy); - policy = NULL; - - /* make sure compacting logic works. */ - policy = NULL; - line.key = (char*)"foo"; - line.value = (char*)"accept *:80,reject private:*,reject *:*"; - line.next = NULL; - test_assert(0 == policies_parse_exit_policy(&line, &policy, 1, 0, NULL, 1)); - test_assert(policy); - //test_streq(policy->string, "accept *:80"); - //test_streq(policy->next->string, "reject *:*"); - test_eq(smartlist_len(policy), 4); - - /* test policy summaries */ - /* check if we properly ignore private IP addresses */ - test_policy_summary_helper("reject 192.168.0.0/16:*," - "reject 0.0.0.0/8:*," - "reject 10.0.0.0/8:*," - "accept *:10-30," - "accept *:90," - "reject *:*", - "accept 10-30,90"); - /* check all accept policies, and proper counting of rejects */ - test_policy_summary_helper("reject 11.0.0.0/9:80," - "reject 12.0.0.0/9:80," - "reject 13.0.0.0/9:80," - "reject 14.0.0.0/9:80," - "accept *:*", "accept 1-65535"); - test_policy_summary_helper("reject 11.0.0.0/9:80," - "reject 12.0.0.0/9:80," - "reject 13.0.0.0/9:80," - "reject 14.0.0.0/9:80," - "reject 15.0.0.0:81," - "accept *:*", "accept 1-65535"); - test_policy_summary_helper("reject 11.0.0.0/9:80," - "reject 12.0.0.0/9:80," - "reject 13.0.0.0/9:80," - "reject 14.0.0.0/9:80," - "reject 15.0.0.0:80," - "accept *:*", - "reject 80"); - /* no exits */ - test_policy_summary_helper("accept 11.0.0.0/9:80," - "reject *:*", - "reject 1-65535"); - /* port merging */ - test_policy_summary_helper("accept *:80," - "accept *:81," - "accept *:100-110," - "accept *:111," - "reject *:*", - "accept 80-81,100-111"); - /* border ports */ - test_policy_summary_helper("accept *:1," - "accept *:3," - "accept *:65535," - "reject *:*", - "accept 1,3,65535"); - /* holes */ - test_policy_summary_helper("accept *:1," - "accept *:3," - "accept *:5," - "accept *:7," - "reject *:*", - "accept 1,3,5,7"); - test_policy_summary_helper("reject *:1," - "reject *:3," - "reject *:5," - "reject *:7," - "accept *:*", - "reject 1,3,5,7"); - - /* Short policies with unrecognized formats should get accepted. */ - test_short_policy_parse("accept fred,2,3-5", "accept 2,3-5"); - test_short_policy_parse("accept 2,fred,3", "accept 2,3"); - test_short_policy_parse("accept 2,fred,3,bob", "accept 2,3"); - test_short_policy_parse("accept 2,-3,500-600", "accept 2,500-600"); - /* Short policies with nil entries are accepted too. */ - test_short_policy_parse("accept 1,,3", "accept 1,3"); - test_short_policy_parse("accept 100-200,,", "accept 100-200"); - test_short_policy_parse("reject ,1-10,,,,30-40", "reject 1-10,30-40"); - - /* Try parsing various broken short policies */ - tt_ptr_op(NULL, ==, parse_short_policy("accept 200-199")); - tt_ptr_op(NULL, ==, parse_short_policy("")); - tt_ptr_op(NULL, ==, parse_short_policy("rejekt 1,2,3")); - tt_ptr_op(NULL, ==, parse_short_policy("reject ")); - tt_ptr_op(NULL, ==, parse_short_policy("reject")); - tt_ptr_op(NULL, ==, parse_short_policy("rej")); - tt_ptr_op(NULL, ==, parse_short_policy("accept 2,3,100000")); - tt_ptr_op(NULL, ==, parse_short_policy("accept 2,3x,4")); - tt_ptr_op(NULL, ==, parse_short_policy("accept 2,3x,4")); - tt_ptr_op(NULL, ==, parse_short_policy("accept 2-")); - tt_ptr_op(NULL, ==, parse_short_policy("accept 2-x")); - tt_ptr_op(NULL, ==, parse_short_policy("accept 1-,3")); - tt_ptr_op(NULL, ==, parse_short_policy("accept 1-,3")); - /* Test a too-long policy. */ - { - int i; - char *policy = NULL; - smartlist_t *chunks = smartlist_new(); - smartlist_add(chunks, tor_strdup("accept ")); - for (i=1; i<10000; ++i) - smartlist_add_asprintf(chunks, "%d,", i); - smartlist_add(chunks, tor_strdup("20000")); - policy = smartlist_join_strings(chunks, "", 0, NULL); - SMARTLIST_FOREACH(chunks, char *, ch, tor_free(ch)); - smartlist_free(chunks); - tt_ptr_op(NULL, ==, parse_short_policy(policy));/* shouldn't be accepted */ - tor_free(policy); /* could leak. */ - } - - /* truncation ports */ - sm = smartlist_new(); - for (i=1; i<2000; i+=2) { - char buf[POLICY_BUF_LEN]; - tor_snprintf(buf, sizeof(buf), "reject *:%d", i); - smartlist_add(sm, tor_strdup(buf)); - } - smartlist_add(sm, tor_strdup("accept *:*")); - policy_str = smartlist_join_strings(sm, ",", 0, NULL); - test_policy_summary_helper( policy_str, - "accept 2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44," - "46,48,50,52,54,56,58,60,62,64,66,68,70,72,74,76,78,80,82,84,86,88,90," - "92,94,96,98,100,102,104,106,108,110,112,114,116,118,120,122,124,126,128," - "130,132,134,136,138,140,142,144,146,148,150,152,154,156,158,160,162,164," - "166,168,170,172,174,176,178,180,182,184,186,188,190,192,194,196,198,200," - "202,204,206,208,210,212,214,216,218,220,222,224,226,228,230,232,234,236," - "238,240,242,244,246,248,250,252,254,256,258,260,262,264,266,268,270,272," - "274,276,278,280,282,284,286,288,290,292,294,296,298,300,302,304,306,308," - "310,312,314,316,318,320,322,324,326,328,330,332,334,336,338,340,342,344," - "346,348,350,352,354,356,358,360,362,364,366,368,370,372,374,376,378,380," - "382,384,386,388,390,392,394,396,398,400,402,404,406,408,410,412,414,416," - "418,420,422,424,426,428,430,432,434,436,438,440,442,444,446,448,450,452," - "454,456,458,460,462,464,466,468,470,472,474,476,478,480,482,484,486,488," - "490,492,494,496,498,500,502,504,506,508,510,512,514,516,518,520,522"); - - done: - addr_policy_list_free(policy); - addr_policy_list_free(policy2); - addr_policy_list_free(policy3); - addr_policy_list_free(policy4); - addr_policy_list_free(policy5); - addr_policy_list_free(policy6); - addr_policy_list_free(policy7); - tor_free(policy_str); - if (sm) { - SMARTLIST_FOREACH(sm, char *, s, tor_free(s)); - smartlist_free(sm); - } + circuit_build_times_free_timeouts(&initial); + circuit_build_times_free_timeouts(&estimate); + circuit_build_times_free_timeouts(&final); + or_state_free(state); } /** Test encoding and parsing of rendezvous service descriptors. */ @@ -1579,6 +733,34 @@ test_rend_fns(void) tor_free(intro_points_encrypted); } + /* Record odd numbered fake-IPs using ipv6, even numbered fake-IPs + * using ipv4. Since our fake geoip database is the same between + * ipv4 and ipv6, we should get the same result no matter which + * address family we pick for each IP. */ +#define SET_TEST_ADDRESS(i) do { \ + if ((i) & 1) { \ + SET_TEST_IPV6(i); \ + tor_addr_from_in6(&addr, &in6); \ + } else { \ + tor_addr_from_ipv4h(&addr, (uint32_t) i); \ + } \ + } while (0) + + /* Make sure that country ID actually works. */ +#define SET_TEST_IPV6(i) \ + do { \ + set_uint32(in6.s6_addr + 12, htonl((uint32_t) (i))); \ + } while (0) +#define CHECK_COUNTRY(country, val) do { \ + /* test ipv4 country lookup */ \ + test_streq(country, \ + geoip_get_country_name(geoip_get_country_by_ipv4(val))); \ + /* test ipv6 country lookup */ \ + SET_TEST_IPV6(val); \ + test_streq(country, \ + geoip_get_country_name(geoip_get_country_by_ipv6(&in6))); \ + } while (0) + /** Run unit tests for GeoIP code. */ static void test_geoip(void) @@ -1589,7 +771,8 @@ test_geoip(void) const char *bridge_stats_1 = "bridge-stats-end 2010-08-12 13:27:30 (86400 s)\n" "bridge-ips zz=24,xy=8\n" - "bridge-ip-versions v4=16,v6=16\n", + "bridge-ip-versions v4=16,v6=16\n" + "bridge-ip-transports <OR>=24\n", *dirreq_stats_1 = "dirreq-stats-end 2010-08-12 13:27:30 (86400 s)\n" "dirreq-v3-ips ab=8\n" @@ -1653,21 +836,6 @@ test_geoip(void) test_eq(4, geoip_get_n_countries()); memset(&in6, 0, sizeof(in6)); - /* Make sure that country ID actually works. */ -#define SET_TEST_IPV6(i) \ - do { \ - set_uint32(in6.s6_addr + 12, htonl((uint32_t) (i))); \ - } while (0) -#define CHECK_COUNTRY(country, val) do { \ - /* test ipv4 country lookup */ \ - test_streq(country, \ - geoip_get_country_name(geoip_get_country_by_ipv4(val))); \ - /* test ipv6 country lookup */ \ - SET_TEST_IPV6(val); \ - test_streq(country, \ - geoip_get_country_name(geoip_get_country_by_ipv6(&in6))); \ - } while (0) - CHECK_COUNTRY("??", 3); CHECK_COUNTRY("ab", 32); CHECK_COUNTRY("??", 5); @@ -1680,40 +848,25 @@ test_geoip(void) SET_TEST_IPV6(3); test_eq(0, geoip_get_country_by_ipv6(&in6)); -#undef CHECK_COUNTRY - - /* Record odd numbered fake-IPs using ipv6, even numbered fake-IPs - * using ipv4. Since our fake geoip database is the same between - * ipv4 and ipv6, we should get the same result no matter which - * address family we pick for each IP. */ -#define SET_TEST_ADDRESS(i) do { \ - if ((i) & 1) { \ - SET_TEST_IPV6(i); \ - tor_addr_from_in6(&addr, &in6); \ - } else { \ - tor_addr_from_ipv4h(&addr, (uint32_t) i); \ - } \ - } while (0) - get_options_mutable()->BridgeRelay = 1; get_options_mutable()->BridgeRecordUsageByCountry = 1; /* Put 9 observations in AB... */ for (i=32; i < 40; ++i) { SET_TEST_ADDRESS(i); - geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, now-7200); + geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, NULL, now-7200); } SET_TEST_ADDRESS(225); - geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, now-7200); + geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, NULL, now-7200); /* and 3 observations in XY, several times. */ for (j=0; j < 10; ++j) for (i=52; i < 55; ++i) { SET_TEST_ADDRESS(i); - geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, now-3600); + geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, NULL, now-3600); } /* and 17 observations in ZZ... */ for (i=110; i < 127; ++i) { SET_TEST_ADDRESS(i); - geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, now); + geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, NULL, now); } geoip_get_client_history(GEOIP_CLIENT_CONNECT, &s, &v); test_assert(s); @@ -1762,7 +915,7 @@ test_geoip(void) /* Start testing dirreq statistics by making sure that we don't collect * dirreq stats without initializing them. */ SET_TEST_ADDRESS(100); - geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS, &addr, now); + geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS, &addr, NULL, now); s = geoip_format_dirreq_stats(now + 86400); test_assert(!s); @@ -1770,7 +923,7 @@ test_geoip(void) * dirreq-stats history string. */ geoip_dirreq_stats_init(now); SET_TEST_ADDRESS(100); - geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS, &addr, now); + geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS, &addr, NULL, now); s = geoip_format_dirreq_stats(now + 86400); test_streq(dirreq_stats_1, s); tor_free(s); @@ -1779,7 +932,7 @@ test_geoip(void) * don't generate a history string. */ geoip_dirreq_stats_term(); SET_TEST_ADDRESS(101); - geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS, &addr, now); + geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS, &addr, NULL, now); s = geoip_format_dirreq_stats(now + 86400); test_assert(!s); @@ -1787,7 +940,7 @@ test_geoip(void) * that we get an all empty history string. */ geoip_dirreq_stats_init(now); SET_TEST_ADDRESS(100); - geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS, &addr, now); + geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS, &addr, NULL, now); geoip_reset_dirreq_stats(now); s = geoip_format_dirreq_stats(now + 86400); test_streq(dirreq_stats_2, s); @@ -1804,6 +957,7 @@ test_geoip(void) geoip_start_dirreq((uint64_t) 1, 1024, DIRREQ_TUNNELED); s = geoip_format_dirreq_stats(now + 86400); test_streq(dirreq_stats_4, s); + tor_free(s); /* Stop collecting directory request statistics and start gathering * entry stats. */ @@ -1814,7 +968,7 @@ test_geoip(void) /* Start testing entry statistics by making sure that we don't collect * anything without initializing entry stats. */ SET_TEST_ADDRESS(100); - geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, now); + geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, NULL, now); s = geoip_format_entry_stats(now + 86400); test_assert(!s); @@ -1822,7 +976,7 @@ test_geoip(void) * entry-stats history string. */ geoip_entry_stats_init(now); SET_TEST_ADDRESS(100); - geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, now); + geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, NULL, now); s = geoip_format_entry_stats(now + 86400); test_streq(entry_stats_1, s); tor_free(s); @@ -1831,7 +985,7 @@ test_geoip(void) * don't generate a history string. */ geoip_entry_stats_term(); SET_TEST_ADDRESS(101); - geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, now); + geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, NULL, now); s = geoip_format_entry_stats(now + 86400); test_assert(!s); @@ -1839,15 +993,12 @@ test_geoip(void) * that we get an all empty history string. */ geoip_entry_stats_init(now); SET_TEST_ADDRESS(100); - geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, now); + geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, NULL, now); geoip_reset_entry_stats(now); s = geoip_format_entry_stats(now + 86400); test_streq(entry_stats_2, s); tor_free(s); -#undef SET_TEST_ADDRESS -#undef SET_TEST_IPV6 - /* Stop collecting entry statistics. */ geoip_entry_stats_term(); get_options_mutable()->EntryStatistics = 0; @@ -1857,6 +1008,81 @@ test_geoip(void) tor_free(v); } +static void +test_geoip_with_pt(void) +{ + time_t now = 1281533250; /* 2010-08-11 13:27:30 UTC */ + char *s = NULL; + int i; + tor_addr_t addr; + struct in6_addr in6; + + get_options_mutable()->BridgeRelay = 1; + get_options_mutable()->BridgeRecordUsageByCountry = 1; + + memset(&in6, 0, sizeof(in6)); + + /* No clients seen yet. */ + s = geoip_get_transport_history(); + tor_assert(!s); + + /* 4 connections without a pluggable transport */ + for (i=0; i < 4; ++i) { + SET_TEST_ADDRESS(i); + geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, NULL, now-7200); + } + + /* 9 connections with "alpha" */ + for (i=4; i < 13; ++i) { + SET_TEST_ADDRESS(i); + geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, "alpha", now-7200); + } + + /* one connection with "beta" */ + SET_TEST_ADDRESS(13); + geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, "beta", now-7200); + + /* 14 connections with "charlie" */ + for (i=14; i < 28; ++i) { + SET_TEST_ADDRESS(i); + geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, "charlie", now-7200); + } + + /* 131 connections with "ddr" */ + for (i=28; i < 159; ++i) { + SET_TEST_ADDRESS(i); + geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, "ddr", now-7200); + } + + /* 8 connections with "entropy" */ + for (i=159; i < 167; ++i) { + SET_TEST_ADDRESS(i); + geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, "entropy", now-7200); + } + + /* 2 connections from the same IP with two different transports. */ + SET_TEST_ADDRESS(++i); + geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, "fire", now-7200); + geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, "google", now-7200); + + /* Test the transport history string. */ + s = geoip_get_transport_history(); + tor_assert(s); + test_streq(s, "<OR>=8,alpha=16,beta=8,charlie=16,ddr=136," + "entropy=8,fire=8,google=8"); + + /* Stop collecting entry statistics. */ + geoip_entry_stats_term(); + get_options_mutable()->EntryStatistics = 0; + + done: + tor_free(s); +} + +#undef SET_TEST_ADDRESS +#undef SET_TEST_IPV6 +#undef CHECK_COUNTRY + /** Run unit tests for stats code. */ static void test_stats(void) @@ -2047,40 +1273,23 @@ const struct testcase_setup_t legacy_setup = { { #name, legacy_test_helper, TT_FORK, &legacy_setup, test_ ## name } static struct testcase_t test_array[] = { - ENT(buffers), - { "buffer_copy", test_buffer_copy, 0, NULL, NULL }, ENT(onion_handshake), + { "bad_onion_handshake", test_bad_onion_handshake, 0, NULL, NULL }, ENT(onion_queues), #ifdef CURVE25519_ENABLED { "ntor_handshake", test_ntor_handshake, 0, NULL, NULL }, #endif ENT(circuit_timeout), - ENT(policies), ENT(rend_fns), ENT(geoip), + FORK(geoip_with_pt), FORK(stats), END_OF_TESTCASES }; -#define SOCKSENT(name) \ - { #name, test_socks_##name, TT_FORK, &socks_setup, NULL } - -static struct testcase_t socks_tests[] = { - SOCKSENT(4_unsupported_commands), - SOCKSENT(4_supported_commands), - - SOCKSENT(5_unsupported_commands), - SOCKSENT(5_supported_commands), - SOCKSENT(5_no_authenticate), - SOCKSENT(5_auth_before_negotiation), - SOCKSENT(5_authenticate), - SOCKSENT(5_authenticate_with_data), - - END_OF_TESTCASES -}; - extern struct testcase_t addr_tests[]; +extern struct testcase_t buffer_tests[]; extern struct testcase_t crypto_tests[]; extern struct testcase_t container_tests[]; extern struct testcase_t util_tests[]; @@ -2090,22 +1299,52 @@ extern struct testcase_t pt_tests[]; extern struct testcase_t config_tests[]; extern struct testcase_t introduce_tests[]; extern struct testcase_t replaycache_tests[]; +extern struct testcase_t relaycell_tests[]; extern struct testcase_t cell_format_tests[]; +extern struct testcase_t circuitlist_tests[]; +extern struct testcase_t circuitmux_tests[]; +extern struct testcase_t cell_queue_tests[]; +extern struct testcase_t options_tests[]; +extern struct testcase_t socks_tests[]; +extern struct testcase_t extorport_tests[]; +extern struct testcase_t controller_event_tests[]; +extern struct testcase_t logging_tests[]; +extern struct testcase_t hs_tests[]; +extern struct testcase_t nodelist_tests[]; +extern struct testcase_t routerkeys_tests[]; +extern struct testcase_t oom_tests[]; +extern struct testcase_t policy_tests[]; +extern struct testcase_t status_tests[]; static struct testgroup_t testgroups[] = { { "", test_array }, + { "buffer/", buffer_tests }, { "socks/", socks_tests }, { "addr/", addr_tests }, { "crypto/", crypto_tests }, { "container/", container_tests }, { "util/", util_tests }, + { "util/logging/", logging_tests }, { "cellfmt/", cell_format_tests }, + { "cellqueue/", cell_queue_tests }, { "dir/", dir_tests }, { "dir/md/", microdesc_tests }, { "pt/", pt_tests }, { "config/", config_tests }, { "replaycache/", replaycache_tests }, + { "relaycell/", relaycell_tests }, { "introduce/", introduce_tests }, + { "circuitlist/", circuitlist_tests }, + { "circuitmux/", circuitmux_tests }, + { "options/", options_tests }, + { "extorport/", extorport_tests }, + { "control/", controller_event_tests }, + { "hs/", hs_tests }, + { "nodelist/", nodelist_tests }, + { "routerkeys/", routerkeys_tests }, + { "oom/", oom_tests }, + { "policy/" , policy_tests }, + { "status/" , status_tests }, END_OF_GROUPS }; @@ -2118,6 +1357,7 @@ main(int c, const char **v) char *errmsg = NULL; int i, i_out; int loglevel = LOG_ERR; + int accel_crypto = 0; #ifdef USE_DMALLOC { @@ -2140,6 +1380,8 @@ main(int c, const char **v) loglevel = LOG_INFO; } else if (!strcmp(v[i], "--debug")) { loglevel = LOG_DEBUG; + } else if (!strcmp(v[i], "--accel")) { + accel_crypto = 1; } else { v[i_out++] = v[i]; } @@ -2154,7 +1396,7 @@ main(int c, const char **v) } options->command = CMD_RUN_UNITTESTS; - if (crypto_global_init(0, NULL, NULL)) { + if (crypto_global_init(accel_crypto, NULL, NULL)) { printf("Can't initialize crypto subsystem; exiting.\n"); return 1; } diff --git a/src/test/test.h b/src/test/test.h index a89b558e5a..b9e4d5bdb4 100644 --- a/src/test/test.h +++ b/src/test/test.h @@ -36,17 +36,7 @@ #define test_strneq(expr1, expr2) tt_str_op((expr1), !=, (expr2)) #define test_mem_op(expr1, op, expr2, len) \ - tt_assert_test_fmt_type(expr1,expr2,#expr1" "#op" "#expr2, \ - const char *, \ - (memcmp(val1_, val2_, len) op 0), \ - char *, "%s", \ - { size_t printlen = (len)*2+1; \ - print_ = tor_malloc(printlen); \ - base16_encode(print_, printlen, value_, \ - (len)); }, \ - { tor_free(print_); }, \ - TT_EXIT_TEST_FUNCTION \ - ); + tt_mem_op((expr1), op, (expr2), (len)) #define test_memeq(expr1, expr2, len) test_mem_op((expr1), ==, (expr2), len) #define test_memneq(expr1, expr2, len) test_mem_op((expr1), !=, (expr2), len) @@ -69,11 +59,126 @@ tt_assert_test_type(a,b,#a" "#op" "#b,double,(val1_ op val2_),"%f", \ TT_EXIT_TEST_FUNCTION) +#ifdef _MSC_VER +#define U64_PRINTF_TYPE uint64_t +#define I64_PRINTF_TYPE int64_t +#else +#define U64_PRINTF_TYPE unsigned long long +#define I64_PRINTF_TYPE long long +#endif + +#define tt_size_op(a,op,b) \ + tt_assert_test_fmt_type(a,b,#a" "#op" "#b,size_t,(val1_ op val2_), \ + U64_PRINTF_TYPE, U64_FORMAT, \ + {print_ = (U64_PRINTF_TYPE) value_;}, {}, TT_EXIT_TEST_FUNCTION) + +#define tt_u64_op(a,op,b) \ + tt_assert_test_fmt_type(a,b,#a" "#op" "#b,uint64_t,(val1_ op val2_), \ + U64_PRINTF_TYPE, U64_FORMAT, \ + {print_ = (U64_PRINTF_TYPE) value_;}, {}, TT_EXIT_TEST_FUNCTION) + +#define tt_i64_op(a,op,b) \ + tt_assert_test_fmt_type(a,b,#a" "#op" "#b,int64_t,(val1_ op val2_), \ + I64_PRINTF_TYPE, I64_FORMAT, \ + {print_ = (I64_PRINTF_TYPE) value_;}, {}, TT_EXIT_TEST_FUNCTION) + const char *get_fname(const char *name); crypto_pk_t *pk_generate(int idx); void legacy_test_helper(void *data); extern const struct testcase_setup_t legacy_setup; +#define US2_CONCAT_2__(a, b) a ## __ ## b +#define US_CONCAT_2__(a, b) a ## _ ## b +#define US_CONCAT_3__(a, b, c) a ## _ ## b ## _ ## c +#define US_CONCAT_2_(a, b) US_CONCAT_2__(a, b) +#define US_CONCAT_3_(a, b, c) US_CONCAT_3__(a, b, c) + +/* + * These macros are helpful for streamlining the authorship of several test + * cases that use mocks. + * + * The pattern is as follows. + * * Declare a top level namespace: + * #define NS_MODULE foo + * + * * For each test case you want to write, create a new submodule in the + * namespace. All mocks and other information should belong to a single + * submodule to avoid interference with other test cases. + * You can simply name the submodule after the function in the module you + * are testing: + * #define NS_SUBMODULE some_function + * or, if you're wanting to write several tests against the same function, + * ie., you are testing an aspect of that function, you can use: + * #define NS_SUBMODULE ASPECT(some_function, behavior) + * + * * Declare all the mocks you will use. The NS_DECL macro serves to declare + * the mock in the current namespace (defined by NS_MODULE and NS_SUBMODULE). + * It behaves like MOCK_DECL: + * NS_DECL(int, dependent_function, (void *)); + * Here, dependent_function must be declared and implemented with the + * MOCK_DECL and MOCK_IMPL macros. The NS_DECL macro also defines an integer + * global for use for tracking how many times a mock was called, and can be + * accessed by CALLED(mock_name). For example, you might put + * CALLED(dependent_function)++; + * in your mock body. + * + * * Define a function called NS(main) that will contain the body of the + * test case. The NS macro can be used to reference a name in the current + * namespace. + * + * * In NS(main), indicate that a mock function in the current namespace, + * declared with NS_DECL is to override that in the global namespace, + * with the NS_MOCK macro: + * NS_MOCK(dependent_function) + * Unmock with: + * NS_UNMOCK(dependent_function) + * + * * Define the mocks with the NS macro, eg., + * int + * NS(dependent_function)(void *) + * { + * CALLED(dependent_function)++; + * } + * + * * In the struct testcase_t array, you can use the TEST_CASE and + * TEST_CASE_ASPECT macros to define the cases without having to do so + * explicitly nor without having to reset NS_SUBMODULE, eg., + * struct testcase_t foo_tests[] = { + * TEST_CASE_ASPECT(some_function, behavior), + * ... + * END_OF_TESTCASES + * which will define a test case named "some_function__behavior". + */ + +#define NAME_TEST_(name) #name +#define NAME_TEST(name) NAME_TEST_(name) +#define ASPECT(test_module, test_name) US2_CONCAT_2__(test_module, test_name) +#define TEST_CASE(function) \ + { \ + NAME_TEST(function), \ + NS_FULL(NS_MODULE, function, test_main), \ + TT_FORK, \ + NULL, \ + NULL, \ + } +#define TEST_CASE_ASPECT(function, aspect) \ + { \ + NAME_TEST(ASPECT(function, aspect)), \ + NS_FULL(NS_MODULE, ASPECT(function, aspect), test_main), \ + TT_FORK, \ + NULL, \ + NULL, \ + } + +#define NS(name) US_CONCAT_3_(NS_MODULE, NS_SUBMODULE, name) +#define NS_FULL(module, submodule, name) US_CONCAT_3_(module, submodule, name) + +#define CALLED(mock_name) US_CONCAT_2_(NS(mock_name), called) +#define NS_DECL(retval, mock_fn, args) \ + static retval NS(mock_fn) args; int CALLED(mock_fn) = 0 +#define NS_MOCK(name) MOCK(name, NS(name)) +#define NS_UNMOCK(name) UNMOCK(name) + #endif diff --git a/src/test/test_addr.c b/src/test/test_addr.c index 645fc643db..2c126e6d14 100644 --- a/src/test/test_addr.c +++ b/src/test/test_addr.c @@ -44,6 +44,10 @@ test_addr_basic(void) test_eq(u32, 0x7f000001u); test_eq(u16, 0); tor_free(cp); + + test_assert(addr_port_lookup(LOG_WARN, "localhost:3", &cp, &u32, NULL)); + tor_free(cp); + test_eq(0, addr_mask_get_bits(0x0u)); test_eq(32, addr_mask_get_bits(0xFFFFFFFFu)); test_eq(16, addr_mask_get_bits(0xFFFF0000u)); @@ -69,7 +73,7 @@ test_addr_basic(void) } done: - ; + tor_free(cp); } #define test_op_ip6_(a,op,b,e1,e2) \ @@ -217,11 +221,12 @@ test_addr_ip6_helpers(void) /* ==== Converting to and from sockaddr_t. */ sin = (struct sockaddr_in *)&sa_storage; sin->sin_family = AF_INET; - sin->sin_port = 9090; + sin->sin_port = htons(9090); sin->sin_addr.s_addr = htonl(0x7f7f0102); /*127.127.1.2*/ - tor_addr_from_sockaddr(&t1, (struct sockaddr *)sin, NULL); + tor_addr_from_sockaddr(&t1, (struct sockaddr *)sin, &port1); test_eq(tor_addr_family(&t1), AF_INET); test_eq(tor_addr_to_ipv4h(&t1), 0x7f7f0102); + tt_int_op(port1, ==, 9090); memset(&sa_storage, 0, sizeof(sa_storage)); test_eq(sizeof(struct sockaddr_in), @@ -235,8 +240,9 @@ test_addr_ip6_helpers(void) sin6->sin6_family = AF_INET6; sin6->sin6_port = htons(7070); sin6->sin6_addr.s6_addr[0] = 128; - tor_addr_from_sockaddr(&t1, (struct sockaddr *)sin6, NULL); + tor_addr_from_sockaddr(&t1, (struct sockaddr *)sin6, &port1); test_eq(tor_addr_family(&t1), AF_INET6); + tt_int_op(port1, ==, 7070); p1 = tor_addr_to_str(buf, &t1, sizeof(buf), 0); test_streq(p1, "8000::"); @@ -340,6 +346,9 @@ test_addr_ip6_helpers(void) test_pton6_bad("a:::b:c"); test_pton6_bad(":::a:b:c"); test_pton6_bad("a:b:c:::"); + test_pton6_bad("1.2.3.4"); + test_pton6_bad(":1.2.3.4"); + test_pton6_bad(".2.3.4"); /* Regression tests for 22789. */ test_pton6_bad("0xfoo"); test_pton6_bad("0x88"); @@ -405,7 +414,6 @@ test_addr_ip6_helpers(void) test_internal_ip("::ffff:169.254.0.0", 0); test_internal_ip("::ffff:169.254.255.255", 0); test_external_ip("::ffff:169.255.0.0", 0); - test_assert(is_internal_IP(0x7f000001, 0)); /* tor_addr_compare(tor_addr_t x2) */ test_addr_compare("ffff::", ==, "ffff::0"); @@ -473,6 +481,9 @@ test_addr_ip6_helpers(void) test_eq(0, i); i = tor_addr_parse_PTR_name(&t1, "Foobar.baz", AF_UNSPEC, 1); test_eq(0, i); + i = tor_addr_parse_PTR_name(&t1, "9999999999999999999999999999.in-addr.arpa", + AF_UNSPEC, 1); + test_eq(-1, i); i = tor_addr_parse_PTR_name(&t1, "1.0.168.192.in-addr.arpa", AF_UNSPEC, 1); test_eq(1, i); @@ -744,42 +755,89 @@ test_addr_parse(void) /* Correct call. */ r= tor_addr_port_parse(LOG_DEBUG, "192.0.2.1:1234", - &addr, &port); + &addr, &port, -1); test_assert(r == 0); tor_addr_to_str(buf, &addr, sizeof(buf), 0); test_streq(buf, "192.0.2.1"); test_eq(port, 1234); + r= tor_addr_port_parse(LOG_DEBUG, + "[::1]:1234", + &addr, &port, -1); + test_assert(r == 0); + tor_addr_to_str(buf, &addr, sizeof(buf), 0); + test_streq(buf, "::1"); + test_eq(port, 1234); + /* Domain name. */ r= tor_addr_port_parse(LOG_DEBUG, "torproject.org:1234", - &addr, &port); + &addr, &port, -1); test_assert(r == -1); /* Only IP. */ r= tor_addr_port_parse(LOG_DEBUG, "192.0.2.2", - &addr, &port); + &addr, &port, -1); + test_assert(r == -1); + + r= tor_addr_port_parse(LOG_DEBUG, + "192.0.2.2", + &addr, &port, 200); + test_assert(r == 0); + tt_int_op(port,==,200); + + r= tor_addr_port_parse(LOG_DEBUG, + "[::1]", + &addr, &port, -1); test_assert(r == -1); + r= tor_addr_port_parse(LOG_DEBUG, + "[::1]", + &addr, &port, 400); + test_assert(r == 0); + tt_int_op(port,==,400); + /* Bad port. */ r= tor_addr_port_parse(LOG_DEBUG, "192.0.2.2:66666", - &addr, &port); + &addr, &port, -1); + test_assert(r == -1); + r= tor_addr_port_parse(LOG_DEBUG, + "192.0.2.2:66666", + &addr, &port, 200); test_assert(r == -1); /* Only domain name */ r= tor_addr_port_parse(LOG_DEBUG, "torproject.org", - &addr, &port); + &addr, &port, -1); + test_assert(r == -1); + r= tor_addr_port_parse(LOG_DEBUG, + "torproject.org", + &addr, &port, 200); test_assert(r == -1); /* Bad IP address */ r= tor_addr_port_parse(LOG_DEBUG, "192.0.2:1234", - &addr, &port); + &addr, &port, -1); test_assert(r == -1); + /* Make sure that the default port has lower priority than the real + one */ + r= tor_addr_port_parse(LOG_DEBUG, + "192.0.2.2:1337", + &addr, &port, 200); + test_assert(r == 0); + tt_int_op(port,==,1337); + + r= tor_addr_port_parse(LOG_DEBUG, + "[::1]:1369", + &addr, &port, 200); + test_assert(r == 0); + tt_int_op(port,==,1369); + done: ; } @@ -853,6 +911,90 @@ test_virtaddrmap(void *data) } static void +test_addr_localname(void *arg) +{ + (void)arg; + tt_assert(tor_addr_hostname_is_local("localhost")); + tt_assert(tor_addr_hostname_is_local("LOCALHOST")); + tt_assert(tor_addr_hostname_is_local("LocalHost")); + tt_assert(tor_addr_hostname_is_local("local")); + tt_assert(tor_addr_hostname_is_local("LOCAL")); + tt_assert(tor_addr_hostname_is_local("here.now.local")); + tt_assert(tor_addr_hostname_is_local("here.now.LOCAL")); + + tt_assert(!tor_addr_hostname_is_local(" localhost")); + tt_assert(!tor_addr_hostname_is_local("www.torproject.org")); + done: + ; +} + +static void +test_addr_dup_ip(void *arg) +{ + char *v = NULL; + (void)arg; +#define CHECK(ip, s) do { \ + v = tor_dup_ip(ip); \ + tt_str_op(v,==,(s)); \ + tor_free(v); \ + } while (0) + + CHECK(0xffffffff, "255.255.255.255"); + CHECK(0x00000000, "0.0.0.0"); + CHECK(0x7f000001, "127.0.0.1"); + CHECK(0x01020304, "1.2.3.4"); + +#undef CHECK + done: + tor_free(v); +} + +static void +test_addr_sockaddr_to_str(void *arg) +{ + char *v = NULL; + struct sockaddr_in sin; + struct sockaddr_in6 sin6; + struct sockaddr_storage ss; +#ifdef HAVE_SYS_UN_H + struct sockaddr_un s_un; +#endif +#define CHECK(sa, s) do { \ + v = tor_sockaddr_to_str((const struct sockaddr*) &(sa)); \ + tt_str_op(v,==,(s)); \ + tor_free(v); \ + } while (0) + (void)arg; + + memset(&ss,0,sizeof(ss)); + ss.ss_family = AF_UNSPEC; + CHECK(ss, "unspec"); + + memset(&sin,0,sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = htonl(0x7f808001); + sin.sin_port = htons(1234); + CHECK(sin, "127.128.128.1:1234"); + +#ifdef HAVE_SYS_UN_H + memset(&s_un,0,sizeof(s_un)); + s_un.sun_family = AF_UNIX; + strlcpy(s_un.sun_path, "/here/is/a/path", sizeof(s_un.sun_path)); + CHECK(s_un, "unix:/here/is/a/path"); +#endif + + memset(&sin6,0,sizeof(sin6)); + sin6.sin6_family = AF_INET6; + memcpy(sin6.sin6_addr.s6_addr, "\x20\x00\x00\x00\x00\x00\x00\x00" + "\x00\x1a\x2b\x3c\x4d\x5e\x00\x01", 16); + sin6.sin6_port = htons(1234); + CHECK(sin6, "[2000::1a:2b3c:4d5e:1]:1234"); + + done: + tor_free(v); +} + +static void test_addr_is_loopback(void *data) { static const struct loopback_item { @@ -887,6 +1029,32 @@ test_addr_is_loopback(void *data) ; } +static void +test_addr_make_null(void *data) +{ + tor_addr_t *addr = tor_malloc(sizeof(*addr)); + tor_addr_t *zeros = tor_malloc_zero(sizeof(*addr)); + char buf[TOR_ADDR_BUF_LEN]; + (void) data; + /* Ensure that before tor_addr_make_null, addr != 0's */ + memset(addr, 1, sizeof(*addr)); + tt_int_op(memcmp(addr, zeros, sizeof(*addr)), !=, 0); + /* Test with AF == AF_INET */ + zeros->family = AF_INET; + tor_addr_make_null(addr, AF_INET); + tt_int_op(memcmp(addr, zeros, sizeof(*addr)), ==, 0); + tt_str_op(tor_addr_to_str(buf, addr, sizeof(buf), 0), ==, "0.0.0.0"); + /* Test with AF == AF_INET6 */ + memset(addr, 1, sizeof(*addr)); + zeros->family = AF_INET6; + tor_addr_make_null(addr, AF_INET6); + tt_int_op(memcmp(addr, zeros, sizeof(*addr)), ==, 0); + tt_str_op(tor_addr_to_str(buf, addr, sizeof(buf), 0), ==, "::"); + done: + tor_free(addr); + tor_free(zeros); +} + #define ADDR_LEGACY(name) \ { #name, legacy_test_helper, 0, &legacy_setup, test_addr_ ## name } @@ -895,7 +1063,11 @@ struct testcase_t addr_tests[] = { ADDR_LEGACY(ip6_helpers), ADDR_LEGACY(parse), { "virtaddr", test_virtaddrmap, 0, NULL, NULL }, + { "localname", test_addr_localname, 0, NULL, NULL }, + { "dup_ip", test_addr_dup_ip, 0, NULL, NULL }, + { "sockaddr_to_str", test_addr_sockaddr_to_str, 0, NULL, NULL }, { "is_loopback", test_addr_is_loopback, 0, NULL, NULL }, + { "make_null", test_addr_make_null, 0, NULL, NULL }, END_OF_TESTCASES }; diff --git a/src/test/test_bt_cl.c b/src/test/test_bt_cl.c new file mode 100644 index 0000000000..45ae82fb85 --- /dev/null +++ b/src/test/test_bt_cl.c @@ -0,0 +1,109 @@ +/* Copyright (c) 2012-2013, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "orconfig.h" +#include <stdio.h> +#include <stdlib.h> + +#include "or.h" +#include "util.h" +#include "backtrace.h" +#include "torlog.h" + +/* -1: no crash. + * 0: crash with a segmentation fault. + * 1x: crash with an assertion failure. */ +static int crashtype = 0; + +#ifdef __GNUC__ +#define NOINLINE __attribute__((noinline)) +#define NORETURN __attribute__((noreturn)) +#endif + +int crash(int x) NOINLINE; +int oh_what(int x) NOINLINE; +int a_tangled_web(int x) NOINLINE; +int we_weave(int x) NOINLINE; +static void abort_handler(int s) NORETURN; + +int +crash(int x) +{ + if (crashtype == 0) { + *(volatile int *)0 = 0; + } else if (crashtype == 1) { + tor_assert(1 == 0); + } else if (crashtype == -1) { + ; + } + + crashtype *= x; + return crashtype; +} + +int +oh_what(int x) +{ + /* We call crash() twice here, so that the compiler won't try to do a + * tail-call optimization. Only the first call will actually happen, but + * telling the compiler to maybe do the second call will prevent it from + * replacing the first call with a jump. */ + return crash(x) + crash(x*2); +} + +int +a_tangled_web(int x) +{ + return oh_what(x) * 99 + oh_what(x); +} + +int +we_weave(int x) +{ + return a_tangled_web(x) + a_tangled_web(x+1); +} + +static void +abort_handler(int s) +{ + (void)s; + exit(0); +} + +int +main(int argc, char **argv) +{ + log_severity_list_t severity; + + if (argc < 2) { + puts("I take an argument. It should be \"assert\" or \"crash\" or " + "\"none\""); + return 1; + } + if (!strcmp(argv[1], "assert")) { + crashtype = 1; + } else if (!strcmp(argv[1], "crash")) { + crashtype = 0; + } else if (!strcmp(argv[1], "none")) { + crashtype = -1; + } else { + puts("Argument should be \"assert\" or \"crash\" or \"none\""); + return 1; + } + + init_logging(); + set_log_severity_config(LOG_WARN, LOG_ERR, &severity); + add_stream_log(&severity, "stdout", STDOUT_FILENO); + tor_log_update_sigsafe_err_fds(); + + configure_backtrace_handler(NULL); + + signal(SIGABRT, abort_handler); + + printf("%d\n", we_weave(2)); + + clean_up_backtrace_handler(); + + return 0; +} + diff --git a/src/test/test_buffers.c b/src/test/test_buffers.c new file mode 100644 index 0000000000..f24b80f0b0 --- /dev/null +++ b/src/test/test_buffers.c @@ -0,0 +1,729 @@ +/* Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2013, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#define BUFFERS_PRIVATE +#include "or.h" +#include "buffers.h" +#include "ext_orport.h" +#include "test.h" + +/** Run unit tests for buffers.c */ +static void +test_buffers_basic(void *arg) +{ + char str[256]; + char str2[256]; + + buf_t *buf = NULL, *buf2 = NULL; + const char *cp; + + int j; + size_t r; + (void) arg; + + /**** + * buf_new + ****/ + if (!(buf = buf_new())) + test_fail(); + + //test_eq(buf_capacity(buf), 4096); + test_eq(buf_datalen(buf), 0); + + /**** + * General pointer frobbing + */ + for (j=0;j<256;++j) { + str[j] = (char)j; + } + write_to_buf(str, 256, buf); + write_to_buf(str, 256, buf); + test_eq(buf_datalen(buf), 512); + fetch_from_buf(str2, 200, buf); + test_memeq(str, str2, 200); + test_eq(buf_datalen(buf), 312); + memset(str2, 0, sizeof(str2)); + + fetch_from_buf(str2, 256, buf); + test_memeq(str+200, str2, 56); + test_memeq(str, str2+56, 200); + test_eq(buf_datalen(buf), 56); + memset(str2, 0, sizeof(str2)); + /* Okay, now we should be 512 bytes into the 4096-byte buffer. If we add + * another 3584 bytes, we hit the end. */ + for (j=0;j<15;++j) { + write_to_buf(str, 256, buf); + } + assert_buf_ok(buf); + test_eq(buf_datalen(buf), 3896); + fetch_from_buf(str2, 56, buf); + test_eq(buf_datalen(buf), 3840); + test_memeq(str+200, str2, 56); + for (j=0;j<15;++j) { + memset(str2, 0, sizeof(str2)); + fetch_from_buf(str2, 256, buf); + test_memeq(str, str2, 256); + } + test_eq(buf_datalen(buf), 0); + buf_free(buf); + buf = NULL; + + /* Okay, now make sure growing can work. */ + buf = buf_new_with_capacity(16); + //test_eq(buf_capacity(buf), 16); + write_to_buf(str+1, 255, buf); + //test_eq(buf_capacity(buf), 256); + fetch_from_buf(str2, 254, buf); + test_memeq(str+1, str2, 254); + //test_eq(buf_capacity(buf), 256); + assert_buf_ok(buf); + write_to_buf(str, 32, buf); + //test_eq(buf_capacity(buf), 256); + assert_buf_ok(buf); + write_to_buf(str, 256, buf); + assert_buf_ok(buf); + //test_eq(buf_capacity(buf), 512); + test_eq(buf_datalen(buf), 33+256); + fetch_from_buf(str2, 33, buf); + test_eq(*str2, str[255]); + + test_memeq(str2+1, str, 32); + //test_eq(buf_capacity(buf), 512); + test_eq(buf_datalen(buf), 256); + fetch_from_buf(str2, 256, buf); + test_memeq(str, str2, 256); + + /* now try shrinking: case 1. */ + buf_free(buf); + buf = buf_new_with_capacity(33668); + for (j=0;j<67;++j) { + write_to_buf(str,255, buf); + } + //test_eq(buf_capacity(buf), 33668); + test_eq(buf_datalen(buf), 17085); + for (j=0; j < 40; ++j) { + fetch_from_buf(str2, 255,buf); + test_memeq(str2, str, 255); + } + + /* now try shrinking: case 2. */ + buf_free(buf); + buf = buf_new_with_capacity(33668); + for (j=0;j<67;++j) { + write_to_buf(str,255, buf); + } + for (j=0; j < 20; ++j) { + fetch_from_buf(str2, 255,buf); + test_memeq(str2, str, 255); + } + for (j=0;j<80;++j) { + write_to_buf(str,255, buf); + } + //test_eq(buf_capacity(buf),33668); + for (j=0; j < 120; ++j) { + fetch_from_buf(str2, 255,buf); + test_memeq(str2, str, 255); + } + + /* Move from buf to buf. */ + buf_free(buf); + buf = buf_new_with_capacity(4096); + buf2 = buf_new_with_capacity(4096); + for (j=0;j<100;++j) + write_to_buf(str, 255, buf); + test_eq(buf_datalen(buf), 25500); + for (j=0;j<100;++j) { + r = 10; + move_buf_to_buf(buf2, buf, &r); + test_eq(r, 0); + } + test_eq(buf_datalen(buf), 24500); + test_eq(buf_datalen(buf2), 1000); + for (j=0;j<3;++j) { + fetch_from_buf(str2, 255, buf2); + test_memeq(str2, str, 255); + } + r = 8192; /*big move*/ + move_buf_to_buf(buf2, buf, &r); + test_eq(r, 0); + r = 30000; /* incomplete move */ + move_buf_to_buf(buf2, buf, &r); + test_eq(r, 13692); + for (j=0;j<97;++j) { + fetch_from_buf(str2, 255, buf2); + test_memeq(str2, str, 255); + } + buf_free(buf); + buf_free(buf2); + buf = buf2 = NULL; + + buf = buf_new_with_capacity(5); + cp = "Testing. This is a moderately long Testing string."; + for (j = 0; cp[j]; j++) + write_to_buf(cp+j, 1, buf); + test_eq(0, buf_find_string_offset(buf, "Testing", 7)); + test_eq(1, buf_find_string_offset(buf, "esting", 6)); + test_eq(1, buf_find_string_offset(buf, "est", 3)); + test_eq(39, buf_find_string_offset(buf, "ing str", 7)); + test_eq(35, buf_find_string_offset(buf, "Testing str", 11)); + test_eq(32, buf_find_string_offset(buf, "ng ", 3)); + test_eq(43, buf_find_string_offset(buf, "string.", 7)); + test_eq(-1, buf_find_string_offset(buf, "shrdlu", 6)); + test_eq(-1, buf_find_string_offset(buf, "Testing thing", 13)); + test_eq(-1, buf_find_string_offset(buf, "ngx", 3)); + buf_free(buf); + buf = NULL; + + /* Try adding a string too long for any freelist. */ + { + char *cp = tor_malloc_zero(65536); + buf = buf_new(); + write_to_buf(cp, 65536, buf); + tor_free(cp); + + tt_int_op(buf_datalen(buf), ==, 65536); + buf_free(buf); + buf = NULL; + } + + done: + if (buf) + buf_free(buf); + if (buf2) + buf_free(buf2); + buf_shrink_freelists(1); +} + +static void +test_buffer_pullup(void *arg) +{ + buf_t *buf; + char *stuff, *tmp; + const char *cp; + size_t sz; + (void)arg; + stuff = tor_malloc(16384); + tmp = tor_malloc(16384); + + /* Note: this test doesn't check the nulterminate argument to buf_pullup, + since nothing actually uses it. We should remove it some time. */ + + buf = buf_new_with_capacity(3000); /* rounds up to next power of 2. */ + + tt_assert(buf); + tt_int_op(buf_get_default_chunk_size(buf), ==, 4096); + + tt_int_op(buf_get_total_allocation(), ==, 0); + + /* There are a bunch of cases for pullup. One is the trivial case. Let's + mess around with an empty buffer. */ + buf_pullup(buf, 16, 1); + buf_get_first_chunk_data(buf, &cp, &sz); + tt_ptr_op(cp, ==, NULL); + tt_uint_op(sz, ==, 0); + + /* Let's make sure nothing got allocated */ + tt_int_op(buf_get_total_allocation(), ==, 0); + + /* Case 1: everything puts into the first chunk with some moving. */ + + /* Let's add some data. */ + crypto_rand(stuff, 16384); + write_to_buf(stuff, 3000, buf); + write_to_buf(stuff+3000, 3000, buf); + buf_get_first_chunk_data(buf, &cp, &sz); + tt_ptr_op(cp, !=, NULL); + tt_int_op(sz, <=, 4096); + + /* Make room for 3000 bytes in the first chunk, so that the pullup-move code + * can get tested. */ + tt_int_op(fetch_from_buf(tmp, 3000, buf), ==, 3000); + test_memeq(tmp, stuff, 3000); + buf_pullup(buf, 2048, 0); + assert_buf_ok(buf); + buf_get_first_chunk_data(buf, &cp, &sz); + tt_ptr_op(cp, !=, NULL); + tt_int_op(sz, >=, 2048); + test_memeq(cp, stuff+3000, 2048); + tt_int_op(3000, ==, buf_datalen(buf)); + tt_int_op(fetch_from_buf(tmp, 3000, buf), ==, 0); + test_memeq(tmp, stuff+3000, 2048); + + buf_free(buf); + + /* Now try the large-chunk case. */ + buf = buf_new_with_capacity(3000); /* rounds up to next power of 2. */ + write_to_buf(stuff, 4000, buf); + write_to_buf(stuff+4000, 4000, buf); + write_to_buf(stuff+8000, 4000, buf); + write_to_buf(stuff+12000, 4000, buf); + tt_int_op(buf_datalen(buf), ==, 16000); + buf_get_first_chunk_data(buf, &cp, &sz); + tt_ptr_op(cp, !=, NULL); + tt_int_op(sz, <=, 4096); + + buf_pullup(buf, 12500, 0); + assert_buf_ok(buf); + buf_get_first_chunk_data(buf, &cp, &sz); + tt_ptr_op(cp, !=, NULL); + tt_int_op(sz, >=, 12500); + test_memeq(cp, stuff, 12500); + tt_int_op(buf_datalen(buf), ==, 16000); + + fetch_from_buf(tmp, 12400, buf); + test_memeq(tmp, stuff, 12400); + tt_int_op(buf_datalen(buf), ==, 3600); + fetch_from_buf(tmp, 3500, buf); + test_memeq(tmp, stuff+12400, 3500); + fetch_from_buf(tmp, 100, buf); + test_memeq(tmp, stuff+15900, 10); + + buf_free(buf); + + /* Make sure that the pull-up-whole-buffer case works */ + buf = buf_new_with_capacity(3000); /* rounds up to next power of 2. */ + write_to_buf(stuff, 4000, buf); + write_to_buf(stuff+4000, 4000, buf); + fetch_from_buf(tmp, 100, buf); /* dump 100 bytes from first chunk */ + buf_pullup(buf, 16000, 0); /* Way too much. */ + assert_buf_ok(buf); + buf_get_first_chunk_data(buf, &cp, &sz); + tt_ptr_op(cp, !=, NULL); + tt_int_op(sz, ==, 7900); + test_memeq(cp, stuff+100, 7900); + + buf_free(buf); + buf = NULL; + + buf_shrink_freelists(1); + + tt_int_op(buf_get_total_allocation(), ==, 0); + done: + buf_free(buf); + buf_shrink_freelists(1); + tor_free(stuff); + tor_free(tmp); +} + +static void +test_buffer_copy(void *arg) +{ + generic_buffer_t *buf=NULL, *buf2=NULL; + const char *s; + size_t len; + char b[256]; + int i; + (void)arg; + + buf = generic_buffer_new(); + tt_assert(buf); + + /* Copy an empty buffer. */ + tt_int_op(0, ==, generic_buffer_set_to_copy(&buf2, buf)); + tt_assert(buf2); + tt_int_op(0, ==, generic_buffer_len(buf2)); + + /* Now try with a short buffer. */ + s = "And now comes an act of enormous enormance!"; + len = strlen(s); + generic_buffer_add(buf, s, len); + tt_int_op(len, ==, generic_buffer_len(buf)); + /* Add junk to buf2 so we can test replacing.*/ + generic_buffer_add(buf2, "BLARG", 5); + tt_int_op(0, ==, generic_buffer_set_to_copy(&buf2, buf)); + tt_int_op(len, ==, generic_buffer_len(buf2)); + generic_buffer_get(buf2, b, len); + test_mem_op(b, ==, s, len); + /* Now free buf2 and retry so we can test allocating */ + generic_buffer_free(buf2); + buf2 = NULL; + tt_int_op(0, ==, generic_buffer_set_to_copy(&buf2, buf)); + tt_int_op(len, ==, generic_buffer_len(buf2)); + generic_buffer_get(buf2, b, len); + test_mem_op(b, ==, s, len); + /* Clear buf for next test */ + generic_buffer_get(buf, b, len); + tt_int_op(generic_buffer_len(buf),==,0); + + /* Okay, now let's try a bigger buffer. */ + s = "Quis autem vel eum iure reprehenderit qui in ea voluptate velit " + "esse quam nihil molestiae consequatur, vel illum qui dolorem eum " + "fugiat quo voluptas nulla pariatur?"; + len = strlen(s); + for (i = 0; i < 256; ++i) { + b[0]=i; + generic_buffer_add(buf, b, 1); + generic_buffer_add(buf, s, len); + } + tt_int_op(0, ==, generic_buffer_set_to_copy(&buf2, buf)); + tt_int_op(generic_buffer_len(buf2), ==, generic_buffer_len(buf)); + for (i = 0; i < 256; ++i) { + generic_buffer_get(buf2, b, len+1); + tt_int_op((unsigned char)b[0],==,i); + test_mem_op(b+1, ==, s, len); + } + + done: + if (buf) + generic_buffer_free(buf); + if (buf2) + generic_buffer_free(buf2); + buf_shrink_freelists(1); +} + +static void +test_buffer_ext_or_cmd(void *arg) +{ + ext_or_cmd_t *cmd = NULL; + generic_buffer_t *buf = generic_buffer_new(); + char *tmp = NULL; + (void) arg; + + /* Empty -- should give "not there. */ + tt_int_op(0, ==, generic_buffer_fetch_ext_or_cmd(buf, &cmd)); + tt_ptr_op(NULL, ==, cmd); + + /* Three bytes: shouldn't work. */ + generic_buffer_add(buf, "\x00\x20\x00", 3); + tt_int_op(0, ==, generic_buffer_fetch_ext_or_cmd(buf, &cmd)); + tt_ptr_op(NULL, ==, cmd); + tt_int_op(3, ==, generic_buffer_len(buf)); + + /* 0020 0000: That's a nil command. It should work. */ + generic_buffer_add(buf, "\x00", 1); + tt_int_op(1, ==, generic_buffer_fetch_ext_or_cmd(buf, &cmd)); + tt_ptr_op(NULL, !=, cmd); + tt_int_op(0x20, ==, cmd->cmd); + tt_int_op(0, ==, cmd->len); + tt_int_op(0, ==, generic_buffer_len(buf)); + ext_or_cmd_free(cmd); + cmd = NULL; + + /* Now try a length-6 command with one byte missing. */ + generic_buffer_add(buf, "\x10\x21\x00\x06""abcde", 9); + tt_int_op(0, ==, generic_buffer_fetch_ext_or_cmd(buf, &cmd)); + tt_ptr_op(NULL, ==, cmd); + generic_buffer_add(buf, "f", 1); + tt_int_op(1, ==, generic_buffer_fetch_ext_or_cmd(buf, &cmd)); + tt_ptr_op(NULL, !=, cmd); + tt_int_op(0x1021, ==, cmd->cmd); + tt_int_op(6, ==, cmd->len); + test_mem_op("abcdef", ==, cmd->body, 6); + tt_int_op(0, ==, generic_buffer_len(buf)); + ext_or_cmd_free(cmd); + cmd = NULL; + + /* Now try a length-10 command with 4 extra bytes. */ + generic_buffer_add(buf, "\xff\xff\x00\x0a" + "loremipsum\x10\x00\xff\xff", 18); + tt_int_op(1, ==, generic_buffer_fetch_ext_or_cmd(buf, &cmd)); + tt_ptr_op(NULL, !=, cmd); + tt_int_op(0xffff, ==, cmd->cmd); + tt_int_op(10, ==, cmd->len); + test_mem_op("loremipsum", ==, cmd->body, 10); + tt_int_op(4, ==, generic_buffer_len(buf)); + ext_or_cmd_free(cmd); + cmd = NULL; + + /* Finally, let's try a maximum-length command. We already have the header + * waiting. */ + tt_int_op(0, ==, generic_buffer_fetch_ext_or_cmd(buf, &cmd)); + tmp = tor_malloc_zero(65535); + generic_buffer_add(buf, tmp, 65535); + tt_int_op(1, ==, generic_buffer_fetch_ext_or_cmd(buf, &cmd)); + tt_ptr_op(NULL, !=, cmd); + tt_int_op(0x1000, ==, cmd->cmd); + tt_int_op(0xffff, ==, cmd->len); + test_mem_op(tmp, ==, cmd->body, 65535); + tt_int_op(0, ==, generic_buffer_len(buf)); + ext_or_cmd_free(cmd); + cmd = NULL; + + done: + ext_or_cmd_free(cmd); + generic_buffer_free(buf); + tor_free(tmp); + buf_shrink_freelists(1); +} + +static void +test_buffer_allocation_tracking(void *arg) +{ + char *junk = tor_malloc(16384); + buf_t *buf1 = NULL, *buf2 = NULL; + int i; + + (void)arg; + + crypto_rand(junk, 16384); + tt_int_op(buf_get_total_allocation(), ==, 0); + + buf1 = buf_new(); + tt_assert(buf1); + buf2 = buf_new(); + tt_assert(buf2); + + tt_int_op(buf_allocation(buf1), ==, 0); + tt_int_op(buf_get_total_allocation(), ==, 0); + + write_to_buf(junk, 4000, buf1); + write_to_buf(junk, 4000, buf1); + write_to_buf(junk, 4000, buf1); + write_to_buf(junk, 4000, buf1); + tt_int_op(buf_allocation(buf1), ==, 16384); + fetch_from_buf(junk, 100, buf1); + tt_int_op(buf_allocation(buf1), ==, 16384); /* still 4 4k chunks */ + + tt_int_op(buf_get_total_allocation(), ==, 16384); + + fetch_from_buf(junk, 4096, buf1); /* drop a 1k chunk... */ + tt_int_op(buf_allocation(buf1), ==, 3*4096); /* now 3 4k chunks */ + +#ifdef ENABLE_BUF_FREELISTS + tt_int_op(buf_get_total_allocation(), ==, 16384); /* that chunk went onto + the freelist. */ +#else + tt_int_op(buf_get_total_allocation(), ==, 12288); /* that chunk was really + freed. */ +#endif + + write_to_buf(junk, 4000, buf2); + tt_int_op(buf_allocation(buf2), ==, 4096); /* another 4k chunk. */ + /* + * If we're using freelists, size stays at 16384 because we just pulled a + * chunk from the freelist. If we aren't, we bounce back up to 16384 by + * allocating a new chunk. + */ + tt_int_op(buf_get_total_allocation(), ==, 16384); + write_to_buf(junk, 4000, buf2); + tt_int_op(buf_allocation(buf2), ==, 8192); /* another 4k chunk. */ + tt_int_op(buf_get_total_allocation(), ==, 5*4096); /* that chunk was new. */ + + /* Make a really huge buffer */ + for (i = 0; i < 1000; ++i) { + write_to_buf(junk, 4000, buf2); + } + tt_int_op(buf_allocation(buf2), >=, 4008000); + tt_int_op(buf_get_total_allocation(), >=, 4008000); + buf_free(buf2); + buf2 = NULL; + + tt_int_op(buf_get_total_allocation(), <, 4008000); + buf_shrink_freelists(1); + tt_int_op(buf_get_total_allocation(), ==, buf_allocation(buf1)); + buf_free(buf1); + buf1 = NULL; + buf_shrink_freelists(1); + tt_int_op(buf_get_total_allocation(), ==, 0); + + done: + buf_free(buf1); + buf_free(buf2); + buf_shrink_freelists(1); + tor_free(junk); +} + +static void +test_buffer_time_tracking(void *arg) +{ + buf_t *buf=NULL, *buf2=NULL; + struct timeval tv0; + const time_t START = 1389288246; + const uint32_t START_MSEC = (uint32_t) ((uint64_t)START * 1000); + int i; + char tmp[4096]; + (void)arg; + + crypto_rand(tmp, sizeof(tmp)); + + tv0.tv_sec = START; + tv0.tv_usec = 0; + + buf = buf_new_with_capacity(3000); /* rounds up to next power of 2. */ + tt_assert(buf); + + /* Empty buffer means the timestamp is 0. */ + tt_int_op(0, ==, buf_get_oldest_chunk_timestamp(buf, START_MSEC)); + tt_int_op(0, ==, buf_get_oldest_chunk_timestamp(buf, START_MSEC+1000)); + + tor_gettimeofday_cache_set(&tv0); + write_to_buf("ABCDEFG", 7, buf); + tt_int_op(1000, ==, buf_get_oldest_chunk_timestamp(buf, START_MSEC+1000)); + + buf2 = buf_copy(buf); + tt_assert(buf2); + tt_int_op(1234, ==, buf_get_oldest_chunk_timestamp(buf2, START_MSEC+1234)); + + /* Now add more bytes; enough to overflow the first chunk. */ + tv0.tv_usec += 123 * 1000; + tor_gettimeofday_cache_set(&tv0); + for (i = 0; i < 600; ++i) + write_to_buf("ABCDEFG", 7, buf); + tt_int_op(4207, ==, buf_datalen(buf)); + + /* The oldest bytes are still in the front. */ + tt_int_op(2000, ==, buf_get_oldest_chunk_timestamp(buf, START_MSEC+2000)); + + /* Once those bytes are dropped, the chunk is still on the first + * timestamp. */ + fetch_from_buf(tmp, 100, buf); + tt_int_op(2000, ==, buf_get_oldest_chunk_timestamp(buf, START_MSEC+2000)); + + /* But once we discard the whole first chunk, we get the data in the second + * chunk. */ + fetch_from_buf(tmp, 4000, buf); + tt_int_op(107, ==, buf_datalen(buf)); + tt_int_op(2000, ==, buf_get_oldest_chunk_timestamp(buf, START_MSEC+2123)); + + /* This time we'll be grabbing a chunk from the freelist, and making sure + its time gets updated */ + tv0.tv_sec += 5; + tv0.tv_usec = 617*1000; + tor_gettimeofday_cache_set(&tv0); + for (i = 0; i < 600; ++i) + write_to_buf("ABCDEFG", 7, buf); + tt_int_op(4307, ==, buf_datalen(buf)); + + tt_int_op(2000, ==, buf_get_oldest_chunk_timestamp(buf, START_MSEC+2123)); + fetch_from_buf(tmp, 4000, buf); + fetch_from_buf(tmp, 306, buf); + tt_int_op(0, ==, buf_get_oldest_chunk_timestamp(buf, START_MSEC+5617)); + tt_int_op(383, ==, buf_get_oldest_chunk_timestamp(buf, START_MSEC+6000)); + + done: + buf_free(buf); + buf_free(buf2); +} + +static void +test_buffers_zlib_impl(int finalize_with_nil) +{ + char *msg = NULL; + char *contents = NULL; + char *expanded = NULL; + buf_t *buf = NULL; + tor_zlib_state_t *zlib_state = NULL; + size_t out_len, in_len; + int done; + + buf = buf_new_with_capacity(128); /* will round up */ + zlib_state = tor_zlib_new(1, ZLIB_METHOD); + + msg = tor_malloc(512); + crypto_rand(msg, 512); + tt_int_op(write_to_buf_zlib(buf, zlib_state, msg, 128, 0), ==, 0); + tt_int_op(write_to_buf_zlib(buf, zlib_state, msg+128, 128, 0), ==, 0); + tt_int_op(write_to_buf_zlib(buf, zlib_state, msg+256, 256, 0), ==, 0); + done = !finalize_with_nil; + tt_int_op(write_to_buf_zlib(buf, zlib_state, "all done", 9, done), ==, 0); + if (finalize_with_nil) { + tt_int_op(write_to_buf_zlib(buf, zlib_state, "", 0, 1), ==, 0); + } + + in_len = buf_datalen(buf); + contents = tor_malloc(in_len); + + tt_int_op(fetch_from_buf(contents, in_len, buf), ==, 0); + + tt_int_op(0, ==, tor_gzip_uncompress(&expanded, &out_len, + contents, in_len, + ZLIB_METHOD, 1, + LOG_WARN)); + + tt_int_op(out_len, >=, 128); + tt_mem_op(msg, ==, expanded, 128); + tt_int_op(out_len, >=, 512); + tt_mem_op(msg, ==, expanded, 512); + tt_int_op(out_len, ==, 512+9); + tt_mem_op("all done", ==, expanded+512, 9); + + done: + buf_free(buf); + tor_zlib_free(zlib_state); + tor_free(contents); + tor_free(expanded); + tor_free(msg); +} + +static void +test_buffers_zlib(void *arg) +{ + (void) arg; + test_buffers_zlib_impl(0); +} +static void +test_buffers_zlib_fin_with_nil(void *arg) +{ + (void) arg; + test_buffers_zlib_impl(1); +} + +static void +test_buffers_zlib_fin_at_chunk_end(void *arg) +{ + char *msg = NULL; + char *contents = NULL; + char *expanded = NULL; + buf_t *buf = NULL; + tor_zlib_state_t *zlib_state = NULL; + size_t out_len, in_len; + size_t sz, headerjunk; + (void) arg; + + buf = buf_new_with_capacity(128); /* will round up */ + sz = buf_get_default_chunk_size(buf); + msg = tor_malloc_zero(sz); + + write_to_buf(msg, 1, buf); + tt_assert(buf->head); + + /* Fill up the chunk so the zlib stuff won't fit in one chunk. */ + tt_uint_op(buf->head->memlen, <, sz); + headerjunk = buf->head->memlen - 7; + write_to_buf(msg, headerjunk-1, buf); + tt_uint_op(buf->head->datalen, ==, headerjunk); + tt_uint_op(buf_datalen(buf), ==, headerjunk); + /* Write an empty string, with finalization on. */ + zlib_state = tor_zlib_new(1, ZLIB_METHOD); + tt_int_op(write_to_buf_zlib(buf, zlib_state, "", 0, 1), ==, 0); + + in_len = buf_datalen(buf); + contents = tor_malloc(in_len); + + tt_int_op(fetch_from_buf(contents, in_len, buf), ==, 0); + + tt_uint_op(in_len, >, headerjunk); + + tt_int_op(0, ==, tor_gzip_uncompress(&expanded, &out_len, + contents + headerjunk, in_len - headerjunk, + ZLIB_METHOD, 1, + LOG_WARN)); + + tt_int_op(out_len, ==, 0); + tt_assert(expanded); + + done: + buf_free(buf); + tor_zlib_free(zlib_state); + tor_free(contents); + tor_free(expanded); + tor_free(msg); +} + +struct testcase_t buffer_tests[] = { + { "basic", test_buffers_basic, TT_FORK, NULL, NULL }, + { "copy", test_buffer_copy, TT_FORK, NULL, NULL }, + { "pullup", test_buffer_pullup, TT_FORK, NULL, NULL }, + { "ext_or_cmd", test_buffer_ext_or_cmd, TT_FORK, NULL, NULL }, + { "allocation_tracking", test_buffer_allocation_tracking, TT_FORK, + NULL, NULL }, + { "time_tracking", test_buffer_time_tracking, TT_FORK, NULL, NULL }, + { "zlib", test_buffers_zlib, TT_FORK, NULL, NULL }, + { "zlib_fin_with_nil", test_buffers_zlib_fin_with_nil, TT_FORK, NULL, NULL }, + { "zlib_fin_at_chunk_end", test_buffers_zlib_fin_at_chunk_end, TT_FORK, + NULL, NULL}, + END_OF_TESTCASES +}; + diff --git a/src/test/test_cell_formats.c b/src/test/test_cell_formats.c index 55d8d0f00f..d7f60680c2 100644 --- a/src/test/test_cell_formats.c +++ b/src/test/test_cell_formats.c @@ -8,7 +8,9 @@ #define CONNECTION_EDGE_PRIVATE #define RELAY_PRIVATE #include "or.h" +#include "channel.h" #include "connection_edge.h" +#include "connection_or.h" #include "onion.h" #include "onion_tap.h" #include "onion_fast.h" @@ -872,6 +874,387 @@ test_cfmt_extended_cells(void *arg) tor_free(mem_op_hex_tmp); } +static void +test_cfmt_resolved_cells(void *arg) +{ + smartlist_t *addrs = smartlist_new(); + relay_header_t rh; + cell_t cell; + int r, errcode; + address_ttl_t *a; + + (void)arg; +#define CLEAR_CELL() do { \ + memset(&cell, 0, sizeof(cell)); \ + memset(&rh, 0, sizeof(rh)); \ + } while (0) +#define CLEAR_ADDRS() do { \ + SMARTLIST_FOREACH(addrs, address_ttl_t *, a, \ + address_ttl_free(a); ); \ + smartlist_clear(addrs); \ + } while (0) +#define SET_CELL(s) do { \ + CLEAR_CELL(); \ + memcpy(cell.payload + RELAY_HEADER_SIZE, (s), sizeof((s))-1); \ + rh.length = sizeof((s))-1; \ + rh.command = RELAY_COMMAND_RESOLVED; \ + errcode = -1; \ + } while (0) + + /* The cell format is one or more answers; each of the form + * type [1 byte---0:hostname, 4:ipv4, 6:ipv6, f0:err-transient, f1:err] + * length [1 byte] + * body [length bytes] + * ttl [4 bytes] + */ + + /* Let's try an empty cell */ + SET_CELL(""); + r = resolved_cell_parse(&cell, &rh, addrs, &errcode); + tt_int_op(errcode, ==, 0); + tt_int_op(r, ==, 0); + tt_int_op(smartlist_len(addrs), ==, 0); + CLEAR_ADDRS(); /* redundant but let's be consistent */ + + /* Cell with one ipv4 addr */ + SET_CELL("\x04\x04" "\x7f\x00\x02\x0a" "\x00\00\x01\x00"); + tt_int_op(rh.length, ==, 10); + r = resolved_cell_parse(&cell, &rh, addrs, &errcode); + tt_int_op(errcode, ==, 0); + tt_int_op(r, ==, 0); + tt_int_op(smartlist_len(addrs), ==, 1); + a = smartlist_get(addrs, 0); + tt_str_op(fmt_addr(&a->addr), ==, "127.0.2.10"); + tt_ptr_op(a->hostname, ==, NULL); + tt_int_op(a->ttl, ==, 256); + CLEAR_ADDRS(); + + /* Cell with one ipv6 addr */ + SET_CELL("\x06\x10" + "\x20\x02\x90\x90\x00\x00\x00\x00" + "\x00\x00\x00\x00\xf0\xf0\xab\xcd" + "\x02\00\x00\x01"); + tt_int_op(rh.length, ==, 22); + r = resolved_cell_parse(&cell, &rh, addrs, &errcode); + tt_int_op(errcode, ==, 0); + tt_int_op(r, ==, 0); + tt_int_op(smartlist_len(addrs), ==, 1); + a = smartlist_get(addrs, 0); + tt_str_op(fmt_addr(&a->addr), ==, "2002:9090::f0f0:abcd"); + tt_ptr_op(a->hostname, ==, NULL); + tt_int_op(a->ttl, ==, 0x2000001); + CLEAR_ADDRS(); + + /* Cell with one hostname */ + SET_CELL("\x00\x11" + "motherbrain.zebes" + "\x00\00\x00\x00"); + tt_int_op(rh.length, ==, 23); + r = resolved_cell_parse(&cell, &rh, addrs, &errcode); + tt_int_op(errcode, ==, 0); + tt_int_op(r, ==, 0); + tt_int_op(smartlist_len(addrs), ==, 1); + a = smartlist_get(addrs, 0); + tt_assert(tor_addr_is_null(&a->addr)); + tt_str_op(a->hostname, ==, "motherbrain.zebes"); + tt_int_op(a->ttl, ==, 0); + CLEAR_ADDRS(); + +#define LONG_NAME \ + "this-hostname-has-255-characters.in-order-to-test-whether-very-long.ho" \ + "stnames-are-accepted.i-am-putting-it-in-a-macro-because-although.this-" \ + "function-is-already-very-full.of-copy-and-pasted-stuff.having-this-app" \ + "ear-more-than-once-would-bother-me-somehow.is" + + tt_int_op(strlen(LONG_NAME), ==, 255); + SET_CELL("\x00\xff" + LONG_NAME + "\x00\01\x00\x00"); + tt_int_op(rh.length, ==, 261); + r = resolved_cell_parse(&cell, &rh, addrs, &errcode); + tt_int_op(errcode, ==, 0); + tt_int_op(r, ==, 0); + tt_int_op(smartlist_len(addrs), ==, 1); + a = smartlist_get(addrs, 0); + tt_assert(tor_addr_is_null(&a->addr)); + tt_str_op(a->hostname, ==, LONG_NAME); + tt_int_op(a->ttl, ==, 65536); + CLEAR_ADDRS(); + + /* Cells with an error */ + SET_CELL("\xf0\x2b" + "I'm sorry, Dave. I'm afraid I can't do that" + "\x00\x11\x22\x33"); + tt_int_op(rh.length, ==, 49); + r = resolved_cell_parse(&cell, &rh, addrs, &errcode); + tt_int_op(errcode, ==, RESOLVED_TYPE_ERROR_TRANSIENT); + tt_int_op(r, ==, 0); + tt_int_op(smartlist_len(addrs), ==, 0); + CLEAR_ADDRS(); + + SET_CELL("\xf1\x40" + "This hostname is too important for me to allow you to resolve it" + "\x00\x00\x00\x00"); + tt_int_op(rh.length, ==, 70); + r = resolved_cell_parse(&cell, &rh, addrs, &errcode); + tt_int_op(errcode, ==, RESOLVED_TYPE_ERROR); + tt_int_op(r, ==, 0); + tt_int_op(smartlist_len(addrs), ==, 0); + CLEAR_ADDRS(); + + /* Cell with an unrecognized type */ + SET_CELL("\xee\x16" + "fault in the AE35 unit" + "\x09\x09\x01\x01"); + tt_int_op(rh.length, ==, 28); + r = resolved_cell_parse(&cell, &rh, addrs, &errcode); + tt_int_op(errcode, ==, 0); + tt_int_op(r, ==, 0); + tt_int_op(smartlist_len(addrs), ==, 0); + CLEAR_ADDRS(); + + /* Cell with one of each */ + SET_CELL(/* unrecognized: */ + "\xee\x16" + "fault in the AE35 unit" + "\x09\x09\x01\x01" + /* error: */ + "\xf0\x2b" + "I'm sorry, Dave. I'm afraid I can't do that" + "\x00\x11\x22\x33" + /* IPv6: */ + "\x06\x10" + "\x20\x02\x90\x90\x00\x00\x00\x00" + "\x00\x00\x00\x00\xf0\xf0\xab\xcd" + "\x02\00\x00\x01" + /* IPv4: */ + "\x04\x04" "\x7f\x00\x02\x0a" "\x00\00\x01\x00" + /* Hostname: */ + "\x00\x11" + "motherbrain.zebes" + "\x00\00\x00\x00" + ); + r = resolved_cell_parse(&cell, &rh, addrs, &errcode); + tt_int_op(errcode, ==, 0); /* no error reported; we got answers */ + tt_int_op(r, ==, 0); + tt_int_op(smartlist_len(addrs), ==, 3); + a = smartlist_get(addrs, 0); + tt_str_op(fmt_addr(&a->addr), ==, "2002:9090::f0f0:abcd"); + tt_ptr_op(a->hostname, ==, NULL); + tt_int_op(a->ttl, ==, 0x2000001); + a = smartlist_get(addrs, 1); + tt_str_op(fmt_addr(&a->addr), ==, "127.0.2.10"); + tt_ptr_op(a->hostname, ==, NULL); + tt_int_op(a->ttl, ==, 256); + a = smartlist_get(addrs, 2); + tt_assert(tor_addr_is_null(&a->addr)); + tt_str_op(a->hostname, ==, "motherbrain.zebes"); + tt_int_op(a->ttl, ==, 0); + CLEAR_ADDRS(); + + /* Cell with several of similar type */ + SET_CELL(/* IPv4 */ + "\x04\x04" "\x7f\x00\x02\x0a" "\x00\00\x01\x00" + "\x04\x04" "\x08\x08\x08\x08" "\x00\00\x01\x05" + "\x04\x04" "\x7f\xb0\x02\xb0" "\x00\01\xff\xff" + /* IPv6 */ + "\x06\x10" + "\x20\x02\x90\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\xca\xfe\xf0\x0d" + "\x00\00\x00\x01" + "\x06\x10" + "\x20\x02\x90\x01\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\xfa\xca\xde" + "\x00\00\x00\x03"); + r = resolved_cell_parse(&cell, &rh, addrs, &errcode); + tt_int_op(errcode, ==, 0); + tt_int_op(r, ==, 0); + tt_int_op(smartlist_len(addrs), ==, 5); + a = smartlist_get(addrs, 0); + tt_str_op(fmt_addr(&a->addr), ==, "127.0.2.10"); + tt_ptr_op(a->hostname, ==, NULL); + tt_int_op(a->ttl, ==, 256); + a = smartlist_get(addrs, 1); + tt_str_op(fmt_addr(&a->addr), ==, "8.8.8.8"); + tt_ptr_op(a->hostname, ==, NULL); + tt_int_op(a->ttl, ==, 261); + a = smartlist_get(addrs, 2); + tt_str_op(fmt_addr(&a->addr), ==, "127.176.2.176"); + tt_ptr_op(a->hostname, ==, NULL); + tt_int_op(a->ttl, ==, 131071); + a = smartlist_get(addrs, 3); + tt_str_op(fmt_addr(&a->addr), ==, "2002:9000::cafe:f00d"); + tt_ptr_op(a->hostname, ==, NULL); + tt_int_op(a->ttl, ==, 1); + a = smartlist_get(addrs, 4); + tt_str_op(fmt_addr(&a->addr), ==, "2002:9001::fa:cade"); + tt_ptr_op(a->hostname, ==, NULL); + tt_int_op(a->ttl, ==, 3); + CLEAR_ADDRS(); + + /* Full cell */ +#define LONG_NAME2 \ + "this-name-has-231-characters.so-that-it-plus-LONG_NAME-can-completely-" \ + "fill-up-the-payload-of-a-cell.its-important-to-check-for-the-full-thin" \ + "g-case.to-avoid-off-by-one-errors.where-full-things-are-misreported-as" \ + ".overflowing-by-one.z" + + tt_int_op(strlen(LONG_NAME2), ==, 231); + SET_CELL("\x00\xff" + LONG_NAME + "\x00\01\x00\x00" + "\x00\xe7" + LONG_NAME2 + "\x00\01\x00\x00"); + tt_int_op(rh.length, ==, RELAY_PAYLOAD_SIZE); + r = resolved_cell_parse(&cell, &rh, addrs, &errcode); + tt_int_op(errcode, ==, 0); + tt_int_op(r, ==, 0); + tt_int_op(smartlist_len(addrs), ==, 2); + a = smartlist_get(addrs, 0); + tt_str_op(a->hostname, ==, LONG_NAME); + a = smartlist_get(addrs, 1); + tt_str_op(a->hostname, ==, LONG_NAME2); + CLEAR_ADDRS(); + + /* BAD CELLS */ + + /* Invalid length on an IPv4 */ + SET_CELL("\x04\x03zzz1234"); + r = resolved_cell_parse(&cell, &rh, addrs, &errcode); + tt_int_op(errcode, ==, 0); + tt_int_op(r, ==, -1); + tt_int_op(smartlist_len(addrs), ==, 0); + SET_CELL("\x04\x04" "\x7f\x00\x02\x0a" "\x00\00\x01\x00" + "\x04\x05zzzzz1234"); + r = resolved_cell_parse(&cell, &rh, addrs, &errcode); + tt_int_op(errcode, ==, 0); + tt_int_op(r, ==, -1); + tt_int_op(smartlist_len(addrs), ==, 0); + + /* Invalid length on an IPv6 */ + SET_CELL("\x06\x03zzz1234"); + r = resolved_cell_parse(&cell, &rh, addrs, &errcode); + tt_int_op(errcode, ==, 0); + tt_int_op(r, ==, -1); + tt_int_op(smartlist_len(addrs), ==, 0); + SET_CELL("\x04\x04" "\x7f\x00\x02\x0a" "\x00\00\x01\x00" + "\x06\x17wwwwwwwwwwwwwwwww1234"); + r = resolved_cell_parse(&cell, &rh, addrs, &errcode); + tt_int_op(errcode, ==, 0); + tt_int_op(r, ==, -1); + tt_int_op(smartlist_len(addrs), ==, 0); + SET_CELL("\x04\x04" "\x7f\x00\x02\x0a" "\x00\00\x01\x00" + "\x06\x10xxxx"); + r = resolved_cell_parse(&cell, &rh, addrs, &errcode); + tt_int_op(errcode, ==, 0); + tt_int_op(r, ==, -1); + tt_int_op(smartlist_len(addrs), ==, 0); + + /* Empty hostname */ + SET_CELL("\x00\x00xxxx"); + r = resolved_cell_parse(&cell, &rh, addrs, &errcode); + tt_int_op(errcode, ==, 0); + tt_int_op(r, ==, -1); + tt_int_op(smartlist_len(addrs), ==, 0); + + /* rh.length out of range */ + CLEAR_CELL(); + rh.length = 499; + r = resolved_cell_parse(&cell, &rh, addrs, &errcode); + tt_int_op(errcode, ==, 0); + tt_int_op(r, ==, -1); + tt_int_op(smartlist_len(addrs), ==, 0); + + /* Item length extends beyond rh.length */ + CLEAR_CELL(); + SET_CELL("\x00\xff" + LONG_NAME + "\x00\01\x00\x00"); + rh.length -= 1; + r = resolved_cell_parse(&cell, &rh, addrs, &errcode); + tt_int_op(r, ==, -1); + tt_int_op(smartlist_len(addrs), ==, 0); + rh.length -= 5; + r = resolved_cell_parse(&cell, &rh, addrs, &errcode); + tt_int_op(r, ==, -1); + tt_int_op(smartlist_len(addrs), ==, 0); + + SET_CELL("\x04\x04" "\x7f\x00\x02\x0a" "\x00\00\x01\x00"); + rh.length -= 1; + r = resolved_cell_parse(&cell, &rh, addrs, &errcode); + tt_int_op(r, ==, -1); + tt_int_op(smartlist_len(addrs), ==, 0); + + SET_CELL("\xee\x10" + "\x20\x02\x90\x01\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\xfa\xca\xde" + "\x00\00\x00\x03"); + rh.length -= 1; + r = resolved_cell_parse(&cell, &rh, addrs, &errcode); + tt_int_op(r, ==, -1); + tt_int_op(smartlist_len(addrs), ==, 0); + + /* Truncated item after first character */ + SET_CELL("\x04"); + r = resolved_cell_parse(&cell, &rh, addrs, &errcode); + tt_int_op(r, ==, -1); + tt_int_op(smartlist_len(addrs), ==, 0); + + SET_CELL("\xee"); + r = resolved_cell_parse(&cell, &rh, addrs, &errcode); + tt_int_op(r, ==, -1); + tt_int_op(smartlist_len(addrs), ==, 0); + + done: + CLEAR_ADDRS(); + CLEAR_CELL(); + smartlist_free(addrs); +#undef CLEAR_ADDRS +#undef CLEAR_CELL +} + +static void +test_cfmt_is_destroy(void *arg) +{ + cell_t cell; + packed_cell_t packed; + circid_t circid = 0; + channel_t *chan; + (void)arg; + + chan = tor_malloc_zero(sizeof(channel_t)); + + memset(&cell, 0xff, sizeof(cell)); + cell.circ_id = 3003; + cell.command = CELL_RELAY; + + cell_pack(&packed, &cell, 0); + chan->wide_circ_ids = 0; + tt_assert(! packed_cell_is_destroy(chan, &packed, &circid)); + tt_int_op(circid, ==, 0); + + cell_pack(&packed, &cell, 1); + chan->wide_circ_ids = 1; + tt_assert(! packed_cell_is_destroy(chan, &packed, &circid)); + tt_int_op(circid, ==, 0); + + cell.command = CELL_DESTROY; + + cell_pack(&packed, &cell, 0); + chan->wide_circ_ids = 0; + tt_assert(packed_cell_is_destroy(chan, &packed, &circid)); + tt_int_op(circid, ==, 3003); + + circid = 0; + cell_pack(&packed, &cell, 1); + chan->wide_circ_ids = 1; + tt_assert(packed_cell_is_destroy(chan, &packed, &circid)); + + done: + tor_free(chan); +} + #define TEST(name, flags) \ { #name, test_cfmt_ ## name, flags, 0, NULL } @@ -883,6 +1266,8 @@ struct testcase_t cell_format_tests[] = { TEST(created_cells, 0), TEST(extend_cells, 0), TEST(extended_cells, 0), + TEST(resolved_cells, 0), + TEST(is_destroy, 0), END_OF_TESTCASES }; diff --git a/src/test/test_cell_queue.c b/src/test/test_cell_queue.c new file mode 100644 index 0000000000..92629823ec --- /dev/null +++ b/src/test/test_cell_queue.c @@ -0,0 +1,158 @@ +/* Copyright (c) 2013, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#define CIRCUITLIST_PRIVATE +#define RELAY_PRIVATE +#include "or.h" +#include "circuitlist.h" +#include "relay.h" +#include "test.h" + +static void +test_cq_manip(void *arg) +{ + packed_cell_t *pc1=NULL, *pc2=NULL, *pc3=NULL, *pc4=NULL, *pc_tmp=NULL; + cell_queue_t cq; + cell_t cell; + (void) arg; + +#ifdef ENABLE_MEMPOOLS + init_cell_pool(); +#endif /* ENABLE_MEMPOOLS */ + + cell_queue_init(&cq); + tt_int_op(cq.n, ==, 0); + + pc1 = packed_cell_new(); + pc2 = packed_cell_new(); + pc3 = packed_cell_new(); + pc4 = packed_cell_new(); + tt_assert(pc1 && pc2 && pc3 && pc4); + + tt_ptr_op(NULL, ==, cell_queue_pop(&cq)); + + /* Add and remove a singleton. */ + cell_queue_append(&cq, pc1); + tt_int_op(cq.n, ==, 1); + tt_ptr_op(pc1, ==, cell_queue_pop(&cq)); + tt_int_op(cq.n, ==, 0); + + /* Add and remove four items */ + cell_queue_append(&cq, pc4); + cell_queue_append(&cq, pc3); + cell_queue_append(&cq, pc2); + cell_queue_append(&cq, pc1); + tt_int_op(cq.n, ==, 4); + tt_ptr_op(pc4, ==, cell_queue_pop(&cq)); + tt_ptr_op(pc3, ==, cell_queue_pop(&cq)); + tt_ptr_op(pc2, ==, cell_queue_pop(&cq)); + tt_ptr_op(pc1, ==, cell_queue_pop(&cq)); + tt_int_op(cq.n, ==, 0); + tt_ptr_op(NULL, ==, cell_queue_pop(&cq)); + + /* Try a packed copy (wide, then narrow, which is a bit of a cheat, since a + * real cell queue has only one type.) */ + memset(&cell, 0, sizeof(cell)); + cell.circ_id = 0x12345678; + cell.command = 10; + strlcpy((char*)cell.payload, "Lorax ipsum gruvvulus thneed amet, snergelly " + "once-ler lerkim, sed do barbaloot tempor gluppitus ut labore et " + "truffula magna aliqua.", + sizeof(cell.payload)); + cell_queue_append_packed_copy(NULL /*circ*/, &cq, 0 /*exitward*/, &cell, + 1 /*wide*/, 0 /*stats*/); + cell.circ_id = 0x2013; + cell_queue_append_packed_copy(NULL /*circ*/, &cq, 0 /*exitward*/, &cell, + 0 /*wide*/, 0 /*stats*/); + tt_int_op(cq.n, ==, 2); + + pc_tmp = cell_queue_pop(&cq); + tt_int_op(cq.n, ==, 1); + tt_ptr_op(pc_tmp, !=, NULL); + test_mem_op(pc_tmp->body, ==, "\x12\x34\x56\x78\x0a", 5); + test_mem_op(pc_tmp->body+5, ==, cell.payload, sizeof(cell.payload)); + packed_cell_free(pc_tmp); + + pc_tmp = cell_queue_pop(&cq); + tt_int_op(cq.n, ==, 0); + tt_ptr_op(pc_tmp, !=, NULL); + test_mem_op(pc_tmp->body, ==, "\x20\x13\x0a", 3); + test_mem_op(pc_tmp->body+3, ==, cell.payload, sizeof(cell.payload)); + packed_cell_free(pc_tmp); + pc_tmp = NULL; + + tt_ptr_op(NULL, ==, cell_queue_pop(&cq)); + + /* Now make sure cell_queue_clear works. */ + cell_queue_append(&cq, pc2); + cell_queue_append(&cq, pc1); + tt_int_op(cq.n, ==, 2); + cell_queue_clear(&cq); + pc2 = pc1 = NULL; /* prevent double-free */ + tt_int_op(cq.n, ==, 0); + + done: + packed_cell_free(pc1); + packed_cell_free(pc2); + packed_cell_free(pc3); + packed_cell_free(pc4); + packed_cell_free(pc_tmp); + + cell_queue_clear(&cq); + +#ifdef ENABLE_MEMPOOLS + free_cell_pool(); +#endif /* ENABLE_MEMPOOLS */ +} + +static void +test_circuit_n_cells(void *arg) +{ + packed_cell_t *pc1=NULL, *pc2=NULL, *pc3=NULL, *pc4=NULL, *pc5=NULL; + origin_circuit_t *origin_c=NULL; + or_circuit_t *or_c=NULL; + + (void)arg; + +#ifdef ENABLE_MEMPOOLS + init_cell_pool(); +#endif /* ENABLE_MEMPOOLS */ + + pc1 = packed_cell_new(); + pc2 = packed_cell_new(); + pc3 = packed_cell_new(); + pc4 = packed_cell_new(); + pc5 = packed_cell_new(); + tt_assert(pc1 && pc2 && pc3 && pc4 && pc5); + + or_c = or_circuit_new(0, NULL); + origin_c = origin_circuit_new(); + origin_c->base_.purpose = CIRCUIT_PURPOSE_C_GENERAL; + + tt_int_op(n_cells_in_circ_queues(TO_CIRCUIT(or_c)), ==, 0); + cell_queue_append(&or_c->p_chan_cells, pc1); + tt_int_op(n_cells_in_circ_queues(TO_CIRCUIT(or_c)), ==, 1); + cell_queue_append(&or_c->base_.n_chan_cells, pc2); + cell_queue_append(&or_c->base_.n_chan_cells, pc3); + tt_int_op(n_cells_in_circ_queues(TO_CIRCUIT(or_c)), ==, 3); + + tt_int_op(n_cells_in_circ_queues(TO_CIRCUIT(origin_c)), ==, 0); + cell_queue_append(&origin_c->base_.n_chan_cells, pc4); + cell_queue_append(&origin_c->base_.n_chan_cells, pc5); + tt_int_op(n_cells_in_circ_queues(TO_CIRCUIT(origin_c)), ==, 2); + + done: + circuit_free(TO_CIRCUIT(or_c)); + circuit_free(TO_CIRCUIT(origin_c)); + +#ifdef ENABLE_MEMPOOLS + free_cell_pool(); +#endif /* ENABLE_MEMPOOLS */ +} + +struct testcase_t cell_queue_tests[] = { + { "basic", test_cq_manip, TT_FORK, NULL, NULL, }, + { "circ_n_cells", test_circuit_n_cells, TT_FORK, NULL, NULL }, + END_OF_TESTCASES +}; + diff --git a/src/test/test_circuitlist.c b/src/test/test_circuitlist.c new file mode 100644 index 0000000000..b19edd1fd4 --- /dev/null +++ b/src/test/test_circuitlist.c @@ -0,0 +1,342 @@ +/* Copyright (c) 2013, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#define TOR_CHANNEL_INTERNAL_ +#define CIRCUITBUILD_PRIVATE +#define CIRCUITLIST_PRIVATE +#include "or.h" +#include "channel.h" +#include "circuitbuild.h" +#include "circuitlist.h" +#include "test.h" + +static channel_t * +new_fake_channel(void) +{ + channel_t *chan = tor_malloc_zero(sizeof(channel_t)); + channel_init(chan); + return chan; +} + +static struct { + int ncalls; + void *cmux; + void *circ; + cell_direction_t dir; +} cam; + +static void +circuitmux_attach_mock(circuitmux_t *cmux, circuit_t *circ, + cell_direction_t dir) +{ + ++cam.ncalls; + cam.cmux = cmux; + cam.circ = circ; + cam.dir = dir; +} + +static struct { + int ncalls; + void *cmux; + void *circ; +} cdm; + +static void +circuitmux_detach_mock(circuitmux_t *cmux, circuit_t *circ) +{ + ++cdm.ncalls; + cdm.cmux = cmux; + cdm.circ = circ; +} + +#define GOT_CMUX_ATTACH(mux_, circ_, dir_) do { \ + tt_int_op(cam.ncalls, ==, 1); \ + tt_ptr_op(cam.cmux, ==, (mux_)); \ + tt_ptr_op(cam.circ, ==, (circ_)); \ + tt_int_op(cam.dir, ==, (dir_)); \ + memset(&cam, 0, sizeof(cam)); \ + } while (0) + +#define GOT_CMUX_DETACH(mux_, circ_) do { \ + tt_int_op(cdm.ncalls, ==, 1); \ + tt_ptr_op(cdm.cmux, ==, (mux_)); \ + tt_ptr_op(cdm.circ, ==, (circ_)); \ + memset(&cdm, 0, sizeof(cdm)); \ + } while (0) + +static void +test_clist_maps(void *arg) +{ + channel_t *ch1 = new_fake_channel(); + channel_t *ch2 = new_fake_channel(); + channel_t *ch3 = new_fake_channel(); + or_circuit_t *or_c1=NULL, *or_c2=NULL; + + (void) arg; + + MOCK(circuitmux_attach_circuit, circuitmux_attach_mock); + MOCK(circuitmux_detach_circuit, circuitmux_detach_mock); + memset(&cam, 0, sizeof(cam)); + memset(&cdm, 0, sizeof(cdm)); + + ch1->cmux = (void*)0x1001; + ch2->cmux = (void*)0x1002; + ch3->cmux = (void*)0x1003; + + or_c1 = or_circuit_new(100, ch2); + tt_assert(or_c1); + GOT_CMUX_ATTACH(ch2->cmux, or_c1, CELL_DIRECTION_IN); + tt_int_op(or_c1->p_circ_id, ==, 100); + tt_ptr_op(or_c1->p_chan, ==, ch2); + + or_c2 = or_circuit_new(100, ch1); + tt_assert(or_c2); + GOT_CMUX_ATTACH(ch1->cmux, or_c2, CELL_DIRECTION_IN); + tt_int_op(or_c2->p_circ_id, ==, 100); + tt_ptr_op(or_c2->p_chan, ==, ch1); + + circuit_set_n_circid_chan(TO_CIRCUIT(or_c1), 200, ch1); + GOT_CMUX_ATTACH(ch1->cmux, or_c1, CELL_DIRECTION_OUT); + + circuit_set_n_circid_chan(TO_CIRCUIT(or_c2), 200, ch2); + GOT_CMUX_ATTACH(ch2->cmux, or_c2, CELL_DIRECTION_OUT); + + tt_ptr_op(circuit_get_by_circid_channel(200, ch1), ==, TO_CIRCUIT(or_c1)); + tt_ptr_op(circuit_get_by_circid_channel(200, ch2), ==, TO_CIRCUIT(or_c2)); + tt_ptr_op(circuit_get_by_circid_channel(100, ch2), ==, TO_CIRCUIT(or_c1)); + /* Try the same thing again, to test the "fast" path. */ + tt_ptr_op(circuit_get_by_circid_channel(100, ch2), ==, TO_CIRCUIT(or_c1)); + tt_assert(circuit_id_in_use_on_channel(100, ch2)); + tt_assert(! circuit_id_in_use_on_channel(101, ch2)); + + /* Try changing the circuitid and channel of that circuit. */ + circuit_set_p_circid_chan(or_c1, 500, ch3); + GOT_CMUX_DETACH(ch2->cmux, TO_CIRCUIT(or_c1)); + GOT_CMUX_ATTACH(ch3->cmux, TO_CIRCUIT(or_c1), CELL_DIRECTION_IN); + tt_ptr_op(circuit_get_by_circid_channel(100, ch2), ==, NULL); + tt_assert(! circuit_id_in_use_on_channel(100, ch2)); + tt_ptr_op(circuit_get_by_circid_channel(500, ch3), ==, TO_CIRCUIT(or_c1)); + + /* Now let's see about destroy handling. */ + tt_assert(! circuit_id_in_use_on_channel(205, ch2)); + tt_assert(circuit_id_in_use_on_channel(200, ch2)); + channel_note_destroy_pending(ch2, 200); + channel_note_destroy_pending(ch2, 205); + channel_note_destroy_pending(ch1, 100); + tt_assert(circuit_id_in_use_on_channel(205, ch2)) + tt_assert(circuit_id_in_use_on_channel(200, ch2)); + tt_assert(circuit_id_in_use_on_channel(100, ch1)); + + tt_assert(TO_CIRCUIT(or_c2)->n_delete_pending != 0); + tt_ptr_op(circuit_get_by_circid_channel(200, ch2), ==, TO_CIRCUIT(or_c2)); + tt_ptr_op(circuit_get_by_circid_channel(100, ch1), ==, TO_CIRCUIT(or_c2)); + + /* Okay, now free ch2 and make sure that the circuit ID is STILL not + * usable, because we haven't declared the destroy to be nonpending */ + tt_int_op(cdm.ncalls, ==, 0); + circuit_free(TO_CIRCUIT(or_c2)); + or_c2 = NULL; /* prevent free */ + tt_int_op(cdm.ncalls, ==, 2); + memset(&cdm, 0, sizeof(cdm)); + tt_assert(circuit_id_in_use_on_channel(200, ch2)); + tt_assert(circuit_id_in_use_on_channel(100, ch1)); + tt_ptr_op(circuit_get_by_circid_channel(200, ch2), ==, NULL); + tt_ptr_op(circuit_get_by_circid_channel(100, ch1), ==, NULL); + + /* Now say that the destroy is nonpending */ + channel_note_destroy_not_pending(ch2, 200); + tt_ptr_op(circuit_get_by_circid_channel(200, ch2), ==, NULL); + channel_note_destroy_not_pending(ch1, 100); + tt_ptr_op(circuit_get_by_circid_channel(100, ch1), ==, NULL); + tt_assert(! circuit_id_in_use_on_channel(200, ch2)); + tt_assert(! circuit_id_in_use_on_channel(100, ch1)); + + done: + if (or_c1) + circuit_free(TO_CIRCUIT(or_c1)); + if (or_c2) + circuit_free(TO_CIRCUIT(or_c2)); + tor_free(ch1); + tor_free(ch2); + tor_free(ch3); + UNMOCK(circuitmux_attach_circuit); + UNMOCK(circuitmux_detach_circuit); +} + +static void +test_rend_token_maps(void *arg) +{ + or_circuit_t *c1, *c2, *c3, *c4; + const uint8_t tok1[REND_TOKEN_LEN] = "The cat can't tell y"; + const uint8_t tok2[REND_TOKEN_LEN] = "ou its name, and it "; + const uint8_t tok3[REND_TOKEN_LEN] = "doesn't really care."; + /* -- Adapted from a quote by Fredrik Lundh. */ + + (void)arg; + (void)tok1; //xxxx + c1 = or_circuit_new(0, NULL); + c2 = or_circuit_new(0, NULL); + c3 = or_circuit_new(0, NULL); + c4 = or_circuit_new(0, NULL); + + /* Make sure we really filled up the tok* variables */ + tt_int_op(tok1[REND_TOKEN_LEN-1], ==, 'y'); + tt_int_op(tok2[REND_TOKEN_LEN-1], ==, ' '); + tt_int_op(tok3[REND_TOKEN_LEN-1], ==, '.'); + + /* No maps; nothing there. */ + tt_ptr_op(NULL, ==, circuit_get_rendezvous(tok1)); + tt_ptr_op(NULL, ==, circuit_get_intro_point(tok1)); + + circuit_set_rendezvous_cookie(c1, tok1); + circuit_set_intro_point_digest(c2, tok2); + + tt_ptr_op(NULL, ==, circuit_get_rendezvous(tok3)); + tt_ptr_op(NULL, ==, circuit_get_intro_point(tok3)); + tt_ptr_op(NULL, ==, circuit_get_rendezvous(tok2)); + tt_ptr_op(NULL, ==, circuit_get_intro_point(tok1)); + + /* Without purpose set, we don't get the circuits */ + tt_ptr_op(NULL, ==, circuit_get_rendezvous(tok1)); + tt_ptr_op(NULL, ==, circuit_get_intro_point(tok2)); + + c1->base_.purpose = CIRCUIT_PURPOSE_REND_POINT_WAITING; + c2->base_.purpose = CIRCUIT_PURPOSE_INTRO_POINT; + + /* Okay, make sure they show up now. */ + tt_ptr_op(c1, ==, circuit_get_rendezvous(tok1)); + tt_ptr_op(c2, ==, circuit_get_intro_point(tok2)); + + /* Two items at the same place with the same token. */ + c3->base_.purpose = CIRCUIT_PURPOSE_REND_POINT_WAITING; + circuit_set_rendezvous_cookie(c3, tok2); + tt_ptr_op(c2, ==, circuit_get_intro_point(tok2)); + tt_ptr_op(c3, ==, circuit_get_rendezvous(tok2)); + + /* Marking a circuit makes it not get returned any more */ + circuit_mark_for_close(TO_CIRCUIT(c1), END_CIRC_REASON_FINISHED); + tt_ptr_op(NULL, ==, circuit_get_rendezvous(tok1)); + circuit_free(TO_CIRCUIT(c1)); + c1 = NULL; + + /* Freeing a circuit makes it not get returned any more. */ + circuit_free(TO_CIRCUIT(c2)); + c2 = NULL; + tt_ptr_op(NULL, ==, circuit_get_intro_point(tok2)); + + /* c3 -- are you still there? */ + tt_ptr_op(c3, ==, circuit_get_rendezvous(tok2)); + /* Change its cookie. This never happens in Tor per se, but hey. */ + c3->base_.purpose = CIRCUIT_PURPOSE_INTRO_POINT; + circuit_set_intro_point_digest(c3, tok3); + + tt_ptr_op(NULL, ==, circuit_get_rendezvous(tok2)); + tt_ptr_op(c3, ==, circuit_get_intro_point(tok3)); + + /* Now replace c3 with c4. */ + c4->base_.purpose = CIRCUIT_PURPOSE_INTRO_POINT; + circuit_set_intro_point_digest(c4, tok3); + + tt_ptr_op(c4, ==, circuit_get_intro_point(tok3)); + + tt_ptr_op(c3->rendinfo, ==, NULL); + tt_ptr_op(c4->rendinfo, !=, NULL); + test_mem_op(c4->rendinfo, ==, tok3, REND_TOKEN_LEN); + + /* Now clear c4's cookie. */ + circuit_set_intro_point_digest(c4, NULL); + tt_ptr_op(c4->rendinfo, ==, NULL); + tt_ptr_op(NULL, ==, circuit_get_intro_point(tok3)); + + done: + if (c1) + circuit_free(TO_CIRCUIT(c1)); + if (c2) + circuit_free(TO_CIRCUIT(c2)); + if (c3) + circuit_free(TO_CIRCUIT(c3)); + if (c4) + circuit_free(TO_CIRCUIT(c4)); +} + +static void +test_pick_circid(void *arg) +{ + bitarray_t *ba = NULL; + channel_t *chan1, *chan2; + circid_t circid; + int i; + (void) arg; + + chan1 = tor_malloc_zero(sizeof(channel_t)); + chan2 = tor_malloc_zero(sizeof(channel_t)); + chan2->wide_circ_ids = 1; + + chan1->circ_id_type = CIRC_ID_TYPE_NEITHER; + tt_int_op(0, ==, get_unique_circ_id_by_chan(chan1)); + + /* Basic tests, with no collisions */ + chan1->circ_id_type = CIRC_ID_TYPE_LOWER; + for (i = 0; i < 50; ++i) { + circid = get_unique_circ_id_by_chan(chan1); + tt_uint_op(0, <, circid); + tt_uint_op(circid, <, (1<<15)); + } + chan1->circ_id_type = CIRC_ID_TYPE_HIGHER; + for (i = 0; i < 50; ++i) { + circid = get_unique_circ_id_by_chan(chan1); + tt_uint_op((1<<15), <, circid); + tt_uint_op(circid, <, (1<<16)); + } + + chan2->circ_id_type = CIRC_ID_TYPE_LOWER; + for (i = 0; i < 50; ++i) { + circid = get_unique_circ_id_by_chan(chan2); + tt_uint_op(0, <, circid); + tt_uint_op(circid, <, (1u<<31)); + } + chan2->circ_id_type = CIRC_ID_TYPE_HIGHER; + for (i = 0; i < 50; ++i) { + circid = get_unique_circ_id_by_chan(chan2); + tt_uint_op((1u<<31), <, circid); + } + + /* Now make sure that we can behave well when we are full up on circuits */ + chan1->circ_id_type = CIRC_ID_TYPE_LOWER; + chan2->circ_id_type = CIRC_ID_TYPE_LOWER; + chan1->wide_circ_ids = chan2->wide_circ_ids = 0; + ba = bitarray_init_zero((1<<15)); + for (i = 0; i < (1<<15); ++i) { + circid = get_unique_circ_id_by_chan(chan1); + if (circid == 0) { + tt_int_op(i, >, (1<<14)); + break; + } + tt_uint_op(circid, <, (1<<15)); + tt_assert(! bitarray_is_set(ba, circid)); + bitarray_set(ba, circid); + channel_mark_circid_unusable(chan1, circid); + } + tt_int_op(i, <, (1<<15)); + /* Make sure that being full on chan1 does not interfere with chan2 */ + for (i = 0; i < 100; ++i) { + circid = get_unique_circ_id_by_chan(chan2); + tt_uint_op(circid, >, 0); + tt_uint_op(circid, <, (1<<15)); + channel_mark_circid_unusable(chan2, circid); + } + + done: + tor_free(chan1); + tor_free(chan2); + bitarray_free(ba); + circuit_free_all(); +} + +struct testcase_t circuitlist_tests[] = { + { "maps", test_clist_maps, TT_FORK, NULL, NULL }, + { "rend_token_maps", test_rend_token_maps, TT_FORK, NULL, NULL }, + { "pick_circid", test_pick_circid, TT_FORK, NULL, NULL }, + END_OF_TESTCASES +}; + diff --git a/src/test/test_circuitmux.c b/src/test/test_circuitmux.c new file mode 100644 index 0000000000..b9c0436ebf --- /dev/null +++ b/src/test/test_circuitmux.c @@ -0,0 +1,88 @@ +/* Copyright (c) 2013, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#define TOR_CHANNEL_INTERNAL_ +#define CIRCUITMUX_PRIVATE +#define RELAY_PRIVATE +#include "or.h" +#include "channel.h" +#include "circuitmux.h" +#include "relay.h" +#include "test.h" + +/* XXXX duplicated function from test_circuitlist.c */ +static channel_t * +new_fake_channel(void) +{ + channel_t *chan = tor_malloc_zero(sizeof(channel_t)); + channel_init(chan); + return chan; +} + +static int +has_queued_writes(channel_t *c) +{ + (void) c; + return 1; +} + +/** Test destroy cell queue with no interference from other queues. */ +static void +test_cmux_destroy_cell_queue(void *arg) +{ + circuitmux_t *cmux = NULL; + channel_t *ch = NULL; + circuit_t *circ = NULL; + cell_queue_t *cq = NULL; + packed_cell_t *pc = NULL; + +#ifdef ENABLE_MEMPOOLS + init_cell_pool(); +#endif /* ENABLE_MEMPOOLS */ + (void) arg; + + cmux = circuitmux_alloc(); + tt_assert(cmux); + ch = new_fake_channel(); + ch->has_queued_writes = has_queued_writes; + ch->wide_circ_ids = 1; + + circ = circuitmux_get_first_active_circuit(cmux, &cq); + tt_assert(!circ); + tt_assert(!cq); + + circuitmux_append_destroy_cell(ch, cmux, 100, 10); + circuitmux_append_destroy_cell(ch, cmux, 190, 6); + circuitmux_append_destroy_cell(ch, cmux, 30, 1); + + tt_int_op(circuitmux_num_cells(cmux), ==, 3); + + circ = circuitmux_get_first_active_circuit(cmux, &cq); + tt_assert(!circ); + tt_assert(cq); + + tt_int_op(cq->n, ==, 3); + + pc = cell_queue_pop(cq); + tt_assert(pc); + test_mem_op(pc->body, ==, "\x00\x00\x00\x64\x04\x0a\x00\x00\x00", 9); + packed_cell_free(pc); + pc = NULL; + + tt_int_op(circuitmux_num_cells(cmux), ==, 2); + + done: + circuitmux_free(cmux); + channel_free(ch); + packed_cell_free(pc); + +#ifdef ENABLE_MEMPOOLS + free_cell_pool(); +#endif /* ENABLE_MEMPOOLS */ +} + +struct testcase_t circuitmux_tests[] = { + { "destroy_cell_queue", test_cmux_destroy_cell_queue, TT_FORK, NULL, NULL }, + END_OF_TESTCASES +}; + diff --git a/src/test/test_cmdline_args.py b/src/test/test_cmdline_args.py new file mode 100755 index 0000000000..55d1cdb805 --- /dev/null +++ b/src/test/test_cmdline_args.py @@ -0,0 +1,292 @@ +#!/usr/bin/python + +import binascii +import hashlib +import os +import re +import shutil +import subprocess +import sys +import tempfile +import unittest + +TOR = "./src/or/tor" +TOP_SRCDIR = "." + +if len(sys.argv) > 1: + TOR = sys.argv[1] + del sys.argv[1] + +if len(sys.argv) > 1: + TOP_SRCDIR = sys.argv[1] + del sys.argv[1] + +class UnexpectedSuccess(Exception): + pass + +class UnexpectedFailure(Exception): + pass + +if sys.version < '3': + def b2s(b): + return b + def s2b(s): + return s + def NamedTemporaryFile(): + return tempfile.NamedTemporaryFile(delete=False) +else: + def b2s(b): + return str(b, 'ascii') + def s2b(s): + return s.encode('ascii') + def NamedTemporaryFile(): + return tempfile.NamedTemporaryFile(mode="w",delete=False,encoding="ascii") + +def contents(fn): + f = open(fn) + try: + return f.read() + finally: + f.close() + +def run_tor(args, failure=False): + p = subprocess.Popen([TOR] + args, stdout=subprocess.PIPE) + output, _ = p.communicate() + result = p.poll() + if result and not failure: + raise UnexpectedFailure() + elif not result and failure: + raise UnexpectedSuccess() + return b2s(output) + +def spaceify_fp(fp): + for i in range(0, len(fp), 4): + yield fp[i:i+4] + +def lines(s): + out = s.split("\n") + if out and out[-1] == '': + del out[-1] + return out + +def strip_log_junk(line): + m = re.match(r'([^\[]+\[[a-z]*\] *)(.*)', line) + if not m: + return ""+line + return m.group(2).strip() + +def randstring(entropy_bytes): + s = os.urandom(entropy_bytes) + return b2s(binascii.b2a_hex(s)) + +def findLineContaining(lines, s): + for ln in lines: + if s in ln: + return True + return False + +class CmdlineTests(unittest.TestCase): + + def test_version(self): + out = run_tor(["--version"]) + self.assertTrue(out.startswith("Tor version ")) + self.assertEqual(len(lines(out)), 1) + + def test_quiet(self): + out = run_tor(["--quiet", "--quumblebluffin", "1"], failure=True) + self.assertEqual(out, "") + + def test_help(self): + out = run_tor(["--help"], failure=False) + out2 = run_tor(["-h"], failure=False) + self.assertTrue(out.startswith("Copyright (c) 2001")) + self.assertTrue(out.endswith( + "tor -f <torrc> [args]\n" + "See man page for options, or https://www.torproject.org/ for documentation.\n")) + self.assertTrue(out == out2) + + def test_hush(self): + torrc = NamedTemporaryFile() + torrc.close() + try: + out = run_tor(["--hush", "-f", torrc.name, + "--quumblebluffin", "1"], failure=True) + finally: + os.unlink(torrc.name) + self.assertEqual(len(lines(out)), 2) + ln = [ strip_log_junk(l) for l in lines(out) ] + self.assertEqual(ln[0], "Failed to parse/validate config: Unknown option 'quumblebluffin'. Failing.") + self.assertEqual(ln[1], "Reading config failed--see warnings above.") + + def test_missing_argument(self): + out = run_tor(["--hush", "--hash-password"], failure=True) + self.assertEqual(len(lines(out)), 2) + ln = [ strip_log_junk(l) for l in lines(out) ] + self.assertEqual(ln[0], "Command-line option '--hash-password' with no value. Failing.") + + def test_hash_password(self): + out = run_tor(["--hash-password", "woodwose"]) + result = lines(out)[-1] + self.assertEqual(result[:3], "16:") + self.assertEqual(len(result), 61) + r = binascii.a2b_hex(result[3:]) + self.assertEqual(len(r), 29) + + salt, how, hashed = r[:8], r[8], r[9:] + self.assertEqual(len(hashed), 20) + if type(how) == type("A"): + how = ord(how) + + count = (16 + (how & 15)) << ((how >> 4) + 6) + stuff = salt + s2b("woodwose") + repetitions = count // len(stuff) + 1 + inp = stuff * repetitions + inp = inp[:count] + + self.assertEqual(hashlib.sha1(inp).digest(), hashed) + + def test_digests(self): + main_c = os.path.join(TOP_SRCDIR, "src", "or", "main.c") + + if os.stat(TOR).st_mtime < os.stat(main_c).st_mtime: + self.skipTest(TOR+" not up to date") + out = run_tor(["--digests"]) + main_line = [ l for l in lines(out) if l.endswith("/main.c") ] + digest, name = main_line[0].split() + f = open(main_c, 'rb') + actual = hashlib.sha1(f.read()).hexdigest() + f.close() + self.assertEqual(digest, actual) + + def test_dump_options(self): + default_torrc = NamedTemporaryFile() + torrc = NamedTemporaryFile() + torrc.write("SocksPort 9999") + torrc.close() + default_torrc.write("SafeLogging 0") + default_torrc.close() + out_sh = out_nb = out_fl = None + opts = [ "-f", torrc.name, + "--defaults-torrc", default_torrc.name ] + try: + out_sh = run_tor(["--dump-config", "short"]+opts) + out_nb = run_tor(["--dump-config", "non-builtin"]+opts) + out_fl = run_tor(["--dump-config", "full"]+opts) + out_nr = run_tor(["--dump-config", "bliznert"]+opts, + failure=True) + + out_verif = run_tor(["--verify-config"]+opts) + finally: + os.unlink(torrc.name) + os.unlink(default_torrc.name) + + self.assertEqual(len(lines(out_sh)), 2) + self.assertTrue(lines(out_sh)[0].startswith("DataDirectory ")) + self.assertEqual(lines(out_sh)[1:], + [ "SocksPort 9999" ]) + + self.assertEqual(len(lines(out_nb)), 2) + self.assertEqual(lines(out_nb), + [ "SafeLogging 0", + "SocksPort 9999" ]) + + out_fl = lines(out_fl) + self.assertTrue(len(out_fl) > 100) + self.assertTrue("SocksPort 9999" in out_fl) + self.assertTrue("SafeLogging 0" in out_fl) + self.assertTrue("ClientOnly 0" in out_fl) + + self.assertTrue(out_verif.endswith("Configuration was valid\n")) + + def test_list_fingerprint(self): + tmpdir = tempfile.mkdtemp(prefix='ttca_') + torrc = NamedTemporaryFile() + torrc.write("ORPort 9999\n") + torrc.write("DataDirectory %s\n"%tmpdir) + torrc.write("Nickname tippi") + torrc.close() + opts = ["-f", torrc.name] + try: + out = run_tor(["--list-fingerprint"]+opts) + fp = contents(os.path.join(tmpdir, "fingerprint")) + finally: + os.unlink(torrc.name) + shutil.rmtree(tmpdir) + + out = lines(out) + lastlog = strip_log_junk(out[-2]) + lastline = out[-1] + fp = fp.strip() + nn_fp = fp.split()[0] + space_fp = " ".join(spaceify_fp(fp.split()[1])) + self.assertEqual(lastlog, + "Your Tor server's identity key fingerprint is '%s'"%fp) + self.assertEqual(lastline, "tippi %s"%space_fp) + self.assertEqual(nn_fp, "tippi") + + def test_list_options(self): + out = lines(run_tor(["--list-torrc-options"])) + self.assertTrue(len(out)>100) + self.assertTrue(out[0] <= 'AccountingMax') + self.assertTrue("UseBridges" in out) + self.assertTrue("SocksPort" in out) + + def test_cmdline_args(self): + default_torrc = NamedTemporaryFile() + torrc = NamedTemporaryFile() + torrc.write("SocksPort 9999\n") + torrc.write("SocksPort 9998\n") + torrc.write("ORPort 9000\n") + torrc.write("ORPort 9001\n") + torrc.write("Nickname eleventeen\n") + torrc.write("ControlPort 9500\n") + torrc.close() + default_torrc.write("") + default_torrc.close() + out_sh = out_nb = out_fl = None + opts = [ "-f", torrc.name, + "--defaults-torrc", default_torrc.name, + "--dump-config", "short" ] + try: + out_1 = run_tor(opts) + out_2 = run_tor(opts+["+ORPort", "9003", + "SocksPort", "9090", + "/ControlPort", + "/TransPort", + "+ExtORPort", "9005"]) + finally: + os.unlink(torrc.name) + os.unlink(default_torrc.name) + + out_1 = [ l for l in lines(out_1) if not l.startswith("DataDir") ] + out_2 = [ l for l in lines(out_2) if not l.startswith("DataDir") ] + + self.assertEqual(out_1, + ["ControlPort 9500", + "Nickname eleventeen", + "ORPort 9000", + "ORPort 9001", + "SocksPort 9999", + "SocksPort 9998"]) + self.assertEqual(out_2, + ["ExtORPort 9005", + "Nickname eleventeen", + "ORPort 9000", + "ORPort 9001", + "ORPort 9003", + "SocksPort 9090"]) + + def test_missing_torrc(self): + fname = "nonexistent_file_"+randstring(8) + out = run_tor(["-f", fname, "--verify-config"], failure=True) + ln = [ strip_log_junk(l) for l in lines(out) ] + self.assertTrue("Unable to open configuration file" in ln[-2]) + self.assertTrue("Reading config failed" in ln[-1]) + + out = run_tor(["-f", fname, "--verify-config", "--ignore-missing-torrc"]) + ln = [ strip_log_junk(l) for l in lines(out) ] + self.assertTrue(findLineContaining(ln, ", using reasonable defaults")) + self.assertTrue("Configuration was valid" in ln[-1]) + +if __name__ == '__main__': + unittest.main() diff --git a/src/test/test_config.c b/src/test/test_config.c index e20fe73295..94ac4dca13 100644 --- a/src/test/test_config.c +++ b/src/test/test_config.c @@ -4,12 +4,16 @@ /* See LICENSE for licensing information */ #include "orconfig.h" + +#define CONFIG_PRIVATE #include "or.h" #include "addressmap.h" #include "config.h" #include "confparse.h" #include "connection_edge.h" #include "test.h" +#include "util.h" +#include "address.h" static void test_config_addressmap(void *arg) @@ -120,6 +124,7 @@ test_config_addressmap(void *arg) test_assert(!addressmap_rewrite(address, sizeof(address), &expires, NULL)); /* Test top-level-domain matching a bit harder */ + config_free_lines(get_options_mutable()->AddressMap); addressmap_clear_configured(); strlcpy(buf, "MapAddress *.com *.torserver.exit\n" "MapAddress *.torproject.org 1.1.1.1\n" @@ -149,6 +154,7 @@ test_config_addressmap(void *arg) test_streq(address, "2.2.2.2"); /* We don't support '*' as a mapping directive */ + config_free_lines(get_options_mutable()->AddressMap); addressmap_clear_configured(); strlcpy(buf, "MapAddress * *.torserver.exit\n", sizeof(buf)); config_get_lines(buf, &(get_options_mutable()->AddressMap), 0); @@ -166,7 +172,421 @@ test_config_addressmap(void *arg) #undef addressmap_rewrite done: - ; + config_free_lines(get_options_mutable()->AddressMap); + get_options_mutable()->AddressMap = NULL; +} + +static int +is_private_dir(const char* path) +{ + struct stat st; + int r = stat(path, &st); + if (r) { + return 0; + } +#if !defined (_WIN32) || defined (WINCE) + if ((st.st_mode & (S_IFDIR | 0777)) != (S_IFDIR | 0700)) { + return 0; + } +#endif + return 1; +} + +static void +test_config_check_or_create_data_subdir(void *arg) +{ + or_options_t *options = get_options_mutable(); + char *datadir; + const char *subdir = "test_stats"; + char *subpath; + struct stat st; + int r; +#if !defined (_WIN32) || defined (WINCE) + unsigned group_permission; +#endif + (void)arg; + + tor_free(options->DataDirectory); + datadir = options->DataDirectory = tor_strdup(get_fname("datadir-0")); + subpath = get_datadir_fname(subdir); + +#if defined (_WIN32) && !defined (WINCE) + tt_int_op(mkdir(options->DataDirectory), ==, 0); +#else + tt_int_op(mkdir(options->DataDirectory, 0700), ==, 0); +#endif + + r = stat(subpath, &st); + + // The subdirectory shouldn't exist yet, + // but should be created by the call to check_or_create_data_subdir. + test_assert(r && (errno == ENOENT)); + test_assert(!check_or_create_data_subdir(subdir)); + test_assert(is_private_dir(subpath)); + + // The check should return 0, if the directory already exists + // and is private to the user. + test_assert(!check_or_create_data_subdir(subdir)); + + r = stat(subpath, &st); + if (r) { + tt_abort_perror("stat"); + } + +#if !defined (_WIN32) || defined (WINCE) + group_permission = st.st_mode | 0070; + r = chmod(subpath, group_permission); + + if (r) { + tt_abort_perror("chmod"); + } + + // If the directory exists, but its mode is too permissive + // a call to check_or_create_data_subdir should reset the mode. + test_assert(!is_private_dir(subpath)); + test_assert(!check_or_create_data_subdir(subdir)); + test_assert(is_private_dir(subpath)); +#endif + + done: + rmdir(subpath); + tor_free(datadir); + tor_free(subpath); +} + +static void +test_config_write_to_data_subdir(void *arg) +{ + or_options_t* options = get_options_mutable(); + char *datadir; + char *cp = NULL; + const char* subdir = "test_stats"; + const char* fname = "test_file"; + const char* str = + "Lorem ipsum dolor sit amet, consetetur sadipscing\n" + "elitr, sed diam nonumy eirmod\n" + "tempor invidunt ut labore et dolore magna aliquyam\n" + "erat, sed diam voluptua.\n" + "At vero eos et accusam et justo duo dolores et ea\n" + "rebum. Stet clita kasd gubergren,\n" + "no sea takimata sanctus est Lorem ipsum dolor sit amet.\n" + "Lorem ipsum dolor sit amet,\n" + "consetetur sadipscing elitr, sed diam nonumy eirmod\n" + "tempor invidunt ut labore et dolore\n" + "magna aliquyam erat, sed diam voluptua. At vero eos et\n" + "accusam et justo duo dolores et\n" + "ea rebum. Stet clita kasd gubergren, no sea takimata\n" + "sanctus est Lorem ipsum dolor sit amet."; + char* filepath = NULL; + (void)arg; + + tor_free(options->DataDirectory); + datadir = options->DataDirectory = tor_strdup(get_fname("datadir-1")); + filepath = get_datadir_fname2(subdir, fname); + +#if defined (_WIN32) && !defined (WINCE) + tt_int_op(mkdir(options->DataDirectory), ==, 0); +#else + tt_int_op(mkdir(options->DataDirectory, 0700), ==, 0); +#endif + + // Write attempt shoudl fail, if subdirectory doesn't exist. + test_assert(write_to_data_subdir(subdir, fname, str, NULL)); + test_assert(! check_or_create_data_subdir(subdir)); + + // Content of file after write attempt should be + // equal to the original string. + test_assert(!write_to_data_subdir(subdir, fname, str, NULL)); + cp = read_file_to_str(filepath, 0, NULL); + test_streq(cp, str); + tor_free(cp); + + // A second write operation should overwrite the old content. + test_assert(!write_to_data_subdir(subdir, fname, str, NULL)); + cp = read_file_to_str(filepath, 0, NULL); + test_streq(cp, str); + tor_free(cp); + + done: + (void) unlink(filepath); + rmdir(options->DataDirectory); + tor_free(datadir); + tor_free(filepath); + tor_free(cp); +} + +/* Test helper function: Make sure that a bridge line gets parsed + * properly. Also make sure that the resulting bridge_line_t structure + * has its fields set correctly. */ +static void +good_bridge_line_test(const char *string, const char *test_addrport, + const char *test_digest, const char *test_transport, + const smartlist_t *test_socks_args) +{ + char *tmp = NULL; + bridge_line_t *bridge_line = parse_bridge_line(string); + test_assert(bridge_line); + + /* test addrport */ + tmp = tor_strdup(fmt_addrport(&bridge_line->addr, bridge_line->port)); + test_streq(test_addrport, tmp); + tor_free(tmp); + + /* If we were asked to validate a digest, but we did not get a + digest after parsing, we failed. */ + if (test_digest && tor_digest_is_zero(bridge_line->digest)) + test_assert(0); + + /* If we were not asked to validate a digest, and we got a digest + after parsing, we failed again. */ + if (!test_digest && !tor_digest_is_zero(bridge_line->digest)) + test_assert(0); + + /* If we were asked to validate a digest, and we got a digest after + parsing, make sure it's correct. */ + if (test_digest) { + tmp = tor_strdup(hex_str(bridge_line->digest, DIGEST_LEN)); + tor_strlower(tmp); + test_streq(test_digest, tmp); + tor_free(tmp); + } + + /* If we were asked to validate a transport name, make sure tha it + matches with the transport name that was parsed. */ + if (test_transport && !bridge_line->transport_name) + test_assert(0); + if (!test_transport && bridge_line->transport_name) + test_assert(0); + if (test_transport) + test_streq(test_transport, bridge_line->transport_name); + + /* Validate the SOCKS argument smartlist. */ + if (test_socks_args && !bridge_line->socks_args) + test_assert(0); + if (!test_socks_args && bridge_line->socks_args) + test_assert(0); + if (test_socks_args) + test_assert(smartlist_strings_eq(test_socks_args, + bridge_line->socks_args)); + + done: + tor_free(tmp); + bridge_line_free(bridge_line); +} + +/* Test helper function: Make sure that a bridge line is + * unparseable. */ +static void +bad_bridge_line_test(const char *string) +{ + bridge_line_t *bridge_line = parse_bridge_line(string); + if (bridge_line) + TT_FAIL(("%s was supposed to fail, but it didn't.", string)); + test_assert(!bridge_line); + + done: + bridge_line_free(bridge_line); +} + +static void +test_config_parse_bridge_line(void *arg) +{ + (void) arg; + good_bridge_line_test("192.0.2.1:4123", + "192.0.2.1:4123", NULL, NULL, NULL); + + good_bridge_line_test("192.0.2.1", + "192.0.2.1:443", NULL, NULL, NULL); + + good_bridge_line_test("transport [::1]", + "[::1]:443", NULL, "transport", NULL); + + good_bridge_line_test("transport 192.0.2.1:12 " + "4352e58420e68f5e40bf7c74faddccd9d1349413", + "192.0.2.1:12", + "4352e58420e68f5e40bf7c74faddccd9d1349413", + "transport", NULL); + + { + smartlist_t *sl_tmp = smartlist_new(); + smartlist_add_asprintf(sl_tmp, "twoandtwo=five"); + + good_bridge_line_test("transport 192.0.2.1:12 " + "4352e58420e68f5e40bf7c74faddccd9d1349413 twoandtwo=five", + "192.0.2.1:12", "4352e58420e68f5e40bf7c74faddccd9d1349413", + "transport", sl_tmp); + + SMARTLIST_FOREACH(sl_tmp, char *, s, tor_free(s)); + smartlist_free(sl_tmp); + } + + { + smartlist_t *sl_tmp = smartlist_new(); + smartlist_add_asprintf(sl_tmp, "twoandtwo=five"); + smartlist_add_asprintf(sl_tmp, "z=z"); + + good_bridge_line_test("transport 192.0.2.1:12 twoandtwo=five z=z", + "192.0.2.1:12", NULL, "transport", sl_tmp); + + SMARTLIST_FOREACH(sl_tmp, char *, s, tor_free(s)); + smartlist_free(sl_tmp); + } + + { + smartlist_t *sl_tmp = smartlist_new(); + smartlist_add_asprintf(sl_tmp, "dub=come"); + smartlist_add_asprintf(sl_tmp, "save=me"); + + good_bridge_line_test("transport 192.0.2.1:12 " + "4352e58420e68f5e40bf7c74faddccd9d1349666 " + "dub=come save=me", + + "192.0.2.1:12", + "4352e58420e68f5e40bf7c74faddccd9d1349666", + "transport", sl_tmp); + + SMARTLIST_FOREACH(sl_tmp, char *, s, tor_free(s)); + smartlist_free(sl_tmp); + } + + good_bridge_line_test("192.0.2.1:1231 " + "4352e58420e68f5e40bf7c74faddccd9d1349413", + "192.0.2.1:1231", + "4352e58420e68f5e40bf7c74faddccd9d1349413", + NULL, NULL); + + /* Empty line */ + bad_bridge_line_test(""); + /* bad transport name */ + bad_bridge_line_test("tr$n_sp0r7 190.20.2.2"); + /* weird ip address */ + bad_bridge_line_test("a.b.c.d"); + /* invalid fpr */ + bad_bridge_line_test("2.2.2.2:1231 4352e58420e68f5e40bf7c74faddccd9d1349"); + /* no k=v in the end */ + bad_bridge_line_test("obfs2 2.2.2.2:1231 " + "4352e58420e68f5e40bf7c74faddccd9d1349413 what"); + /* no addrport */ + bad_bridge_line_test("asdw"); + /* huge k=v value that can't fit in SOCKS fields */ + bad_bridge_line_test( + "obfs2 2.2.2.2:1231 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aa=b"); +} + +static void +test_config_parse_transport_options_line(void *arg) +{ + smartlist_t *options_sl = NULL, *sl_tmp = NULL; + + (void) arg; + + { /* too small line */ + options_sl = get_options_from_transport_options_line("valley", NULL); + test_assert(!options_sl); + } + + { /* no k=v values */ + options_sl = get_options_from_transport_options_line("hit it!", NULL); + test_assert(!options_sl); + } + + { /* correct line, but wrong transport specified */ + options_sl = + get_options_from_transport_options_line("trebuchet k=v", "rook"); + test_assert(!options_sl); + } + + { /* correct -- no transport specified */ + sl_tmp = smartlist_new(); + smartlist_add_asprintf(sl_tmp, "ladi=dadi"); + smartlist_add_asprintf(sl_tmp, "weliketo=party"); + + options_sl = + get_options_from_transport_options_line("rook ladi=dadi weliketo=party", + NULL); + test_assert(options_sl); + test_assert(smartlist_strings_eq(options_sl, sl_tmp)); + + SMARTLIST_FOREACH(sl_tmp, char *, s, tor_free(s)); + smartlist_free(sl_tmp); + sl_tmp = NULL; + SMARTLIST_FOREACH(options_sl, char *, s, tor_free(s)); + smartlist_free(options_sl); + options_sl = NULL; + } + + { /* correct -- correct transport specified */ + sl_tmp = smartlist_new(); + smartlist_add_asprintf(sl_tmp, "ladi=dadi"); + smartlist_add_asprintf(sl_tmp, "weliketo=party"); + + options_sl = + get_options_from_transport_options_line("rook ladi=dadi weliketo=party", + "rook"); + test_assert(options_sl); + test_assert(smartlist_strings_eq(options_sl, sl_tmp)); + SMARTLIST_FOREACH(sl_tmp, char *, s, tor_free(s)); + smartlist_free(sl_tmp); + sl_tmp = NULL; + SMARTLIST_FOREACH(options_sl, char *, s, tor_free(s)); + smartlist_free(options_sl); + options_sl = NULL; + } + + done: + if (options_sl) { + SMARTLIST_FOREACH(options_sl, char *, s, tor_free(s)); + smartlist_free(options_sl); + } + if (sl_tmp) { + SMARTLIST_FOREACH(sl_tmp, char *, s, tor_free(s)); + smartlist_free(sl_tmp); + } +} + +// Tests if an options with MyFamily fingerprints missing '$' normalises +// them correctly and also ensure it also works with multiple fingerprints +static void +test_config_fix_my_family(void *arg) +{ + char *err = NULL; + const char *family = "$1111111111111111111111111111111111111111, " + "1111111111111111111111111111111111111112, " + "$1111111111111111111111111111111111111113"; + + or_options_t* options = options_new(); + or_options_t* defaults = options_new(); + (void) arg; + + options_init(options); + options_init(defaults); + options->MyFamily = tor_strdup(family); + + options_validate(NULL, options, defaults, 0, &err) ; + + if (err != NULL) { + TT_FAIL(("options_validate failed: %s", err)); + } + + test_streq(options->MyFamily, "$1111111111111111111111111111111111111111, " + "$1111111111111111111111111111111111111112, " + "$1111111111111111111111111111111111111113"); + + done: + if (err != NULL) { + tor_free(err); + } + + or_options_free(options); + or_options_free(defaults); } #define CONFIG_TEST(name, flags) \ @@ -174,6 +594,11 @@ test_config_addressmap(void *arg) struct testcase_t config_tests[] = { CONFIG_TEST(addressmap, 0), + CONFIG_TEST(parse_bridge_line, 0), + CONFIG_TEST(parse_transport_options_line, 0), + CONFIG_TEST(check_or_create_data_subdir, TT_FORK), + CONFIG_TEST(write_to_data_subdir, TT_FORK), + CONFIG_TEST(fix_my_family, 0), END_OF_TESTCASES }; diff --git a/src/test/test_containers.c b/src/test/test_containers.c index 005e102e25..067c4c1907 100644 --- a/src/test/test_containers.c +++ b/src/test/test_containers.c @@ -469,6 +469,51 @@ test_container_smartlist_join(void) tor_free(joined); } +static void +test_container_smartlist_ints_eq(void *arg) +{ + smartlist_t *sl1 = NULL, *sl2 = NULL; + int x; + (void)arg; + + tt_assert(smartlist_ints_eq(NULL, NULL)); + + sl1 = smartlist_new(); + tt_assert(!smartlist_ints_eq(sl1, NULL)); + tt_assert(!smartlist_ints_eq(NULL, sl1)); + + sl2 = smartlist_new(); + tt_assert(smartlist_ints_eq(sl1, sl2)); + + x = 5; + smartlist_add(sl1, tor_memdup(&x, sizeof(int))); + smartlist_add(sl2, tor_memdup(&x, sizeof(int))); + x = 90; + smartlist_add(sl1, tor_memdup(&x, sizeof(int))); + smartlist_add(sl2, tor_memdup(&x, sizeof(int))); + tt_assert(smartlist_ints_eq(sl1, sl2)); + + x = -50; + smartlist_add(sl1, tor_memdup(&x, sizeof(int))); + tt_assert(! smartlist_ints_eq(sl1, sl2)); + tt_assert(! smartlist_ints_eq(sl2, sl1)); + smartlist_add(sl2, tor_memdup(&x, sizeof(int))); + tt_assert(smartlist_ints_eq(sl1, sl2)); + + *(int*)smartlist_get(sl1, 1) = 101010; + tt_assert(! smartlist_ints_eq(sl2, sl1)); + *(int*)smartlist_get(sl2, 1) = 101010; + tt_assert(smartlist_ints_eq(sl1, sl2)); + + done: + if (sl1) + SMARTLIST_FOREACH(sl1, int *, ip, tor_free(ip)); + if (sl2) + SMARTLIST_FOREACH(sl2, int *, ip, tor_free(ip)); + smartlist_free(sl1); + smartlist_free(sl2); +} + /** Run unit tests for bitarray code */ static void test_container_bitarray(void) @@ -784,7 +829,7 @@ test_container_order_functions(void) } static void -test_di_map(void *arg) +test_container_di_map(void *arg) { di_digest256_map_t *map = NULL; const uint8_t key1[] = "In view of the fact that it was "; @@ -856,12 +901,12 @@ test_container_fp_pair_map(void) memset(fp6.second, 0x62, DIGEST_LEN); v = fp_pair_map_set(map, &fp1, (void*)99); - test_eq(v, NULL); + tt_ptr_op(v, ==, NULL); test_assert(!fp_pair_map_isempty(map)); v = fp_pair_map_set(map, &fp2, (void*)101); - test_eq(v, NULL); + tt_ptr_op(v, ==, NULL); v = fp_pair_map_set(map, &fp1, (void*)100); - test_eq(v, (void*)99); + tt_ptr_op(v, ==, (void*)99); test_eq_ptr(fp_pair_map_get(map, &fp1), (void*)100); test_eq_ptr(fp_pair_map_get(map, &fp2), (void*)101); test_eq_ptr(fp_pair_map_get(map, &fp3), NULL); @@ -912,18 +957,22 @@ test_container_fp_pair_map(void) #define CONTAINER_LEGACY(name) \ { #name, legacy_test_helper, 0, &legacy_setup, test_container_ ## name } +#define CONTAINER(name, flags) \ + { #name, test_container_ ## name, (flags), NULL, NULL } + struct testcase_t container_tests[] = { CONTAINER_LEGACY(smartlist_basic), CONTAINER_LEGACY(smartlist_strings), CONTAINER_LEGACY(smartlist_overlap), CONTAINER_LEGACY(smartlist_digests), CONTAINER_LEGACY(smartlist_join), + CONTAINER(smartlist_ints_eq, 0), CONTAINER_LEGACY(bitarray), CONTAINER_LEGACY(digestset), CONTAINER_LEGACY(strmap), CONTAINER_LEGACY(pqueue), CONTAINER_LEGACY(order_functions), - { "di_map", test_di_map, 0, NULL, NULL }, + CONTAINER(di_map, 0), CONTAINER_LEGACY(fp_pair_map), END_OF_TESTCASES }; diff --git a/src/test/test_controller_events.c b/src/test/test_controller_events.c new file mode 100644 index 0000000000..b45e97a417 --- /dev/null +++ b/src/test/test_controller_events.c @@ -0,0 +1,307 @@ +/* Copyright (c) 2013, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#define CONNECTION_PRIVATE +#define TOR_CHANNEL_INTERNAL_ +#define CONTROL_PRIVATE +#include "or.h" +#include "channel.h" +#include "channeltls.h" +#include "connection.h" +#include "control.h" +#include "test.h" + +static void +help_test_bucket_note_empty(uint32_t expected_msec_since_midnight, + int tokens_before, size_t tokens_removed, + uint32_t msec_since_epoch) +{ + uint32_t timestamp_var = 0; + struct timeval tvnow; + tvnow.tv_sec = msec_since_epoch / 1000; + tvnow.tv_usec = (msec_since_epoch % 1000) * 1000; + connection_buckets_note_empty_ts(×tamp_var, tokens_before, + tokens_removed, &tvnow); + tt_int_op(expected_msec_since_midnight, ==, timestamp_var); + + done: + ; +} + +static void +test_cntev_bucket_note_empty(void *arg) +{ + (void)arg; + + /* Two cases with nothing to note, because bucket was empty before; + * 86442200 == 1970-01-02 00:00:42.200000 */ + help_test_bucket_note_empty(0, 0, 0, 86442200); + help_test_bucket_note_empty(0, -100, 100, 86442200); + + /* Nothing to note, because bucket has not been emptied. */ + help_test_bucket_note_empty(0, 101, 100, 86442200); + + /* Bucket was emptied, note 42200 msec since midnight. */ + help_test_bucket_note_empty(42200, 101, 101, 86442200); + help_test_bucket_note_empty(42200, 101, 102, 86442200); +} + +static void +test_cntev_bucket_millis_empty(void *arg) +{ + struct timeval tvnow; + (void)arg; + + /* 1970-01-02 00:00:42.200000 */ + tvnow.tv_sec = 86400 + 42; + tvnow.tv_usec = 200000; + + /* Bucket has not been refilled. */ + tt_int_op(0, ==, bucket_millis_empty(0, 42120, 0, 100, &tvnow)); + tt_int_op(0, ==, bucket_millis_empty(-10, 42120, -10, 100, &tvnow)); + + /* Bucket was not empty. */ + tt_int_op(0, ==, bucket_millis_empty(10, 42120, 20, 100, &tvnow)); + + /* Bucket has been emptied 80 msec ago and has just been refilled. */ + tt_int_op(80, ==, bucket_millis_empty(-20, 42120, -10, 100, &tvnow)); + tt_int_op(80, ==, bucket_millis_empty(-10, 42120, 0, 100, &tvnow)); + tt_int_op(80, ==, bucket_millis_empty(0, 42120, 10, 100, &tvnow)); + + /* Bucket has been emptied 180 msec ago, last refill was 100 msec ago + * which was insufficient to make it positive, so cap msec at 100. */ + tt_int_op(100, ==, bucket_millis_empty(0, 42020, 1, 100, &tvnow)); + + /* 1970-01-02 00:00:00:050000 */ + tvnow.tv_sec = 86400; + tvnow.tv_usec = 50000; + + /* Last emptied 30 msec before midnight, tvnow is 50 msec after + * midnight, that's 80 msec in total. */ + tt_int_op(80, ==, bucket_millis_empty(0, 86400000 - 30, 1, 100, &tvnow)); + + done: + ; +} + +static void +add_testing_cell_stats_entry(circuit_t *circ, uint8_t command, + unsigned int waiting_time, + unsigned int removed, unsigned int exitward) +{ + testing_cell_stats_entry_t *ent = tor_malloc_zero( + sizeof(testing_cell_stats_entry_t)); + ent->command = command; + ent->waiting_time = waiting_time; + ent->removed = removed; + ent->exitward = exitward; + if (!circ->testing_cell_stats) + circ->testing_cell_stats = smartlist_new(); + smartlist_add(circ->testing_cell_stats, ent); +} + +static void +test_cntev_sum_up_cell_stats(void *arg) +{ + or_circuit_t *or_circ; + circuit_t *circ; + cell_stats_t *cell_stats = NULL; + (void)arg; + + /* This circuit is fake. */ + or_circ = tor_malloc_zero(sizeof(or_circuit_t)); + or_circ->base_.magic = OR_CIRCUIT_MAGIC; + or_circ->base_.purpose = CIRCUIT_PURPOSE_OR; + circ = TO_CIRCUIT(or_circ); + + /* A single RELAY cell was added to the appward queue. */ + cell_stats = tor_malloc_zero(sizeof(cell_stats_t)); + add_testing_cell_stats_entry(circ, CELL_RELAY, 0, 0, 0); + sum_up_cell_stats_by_command(circ, cell_stats); + tt_u64_op(1, ==, cell_stats->added_cells_appward[CELL_RELAY]); + + /* A single RELAY cell was added to the exitward queue. */ + add_testing_cell_stats_entry(circ, CELL_RELAY, 0, 0, 1); + sum_up_cell_stats_by_command(circ, cell_stats); + tt_u64_op(1, ==, cell_stats->added_cells_exitward[CELL_RELAY]); + + /* A single RELAY cell was removed from the appward queue where it spent + * 20 msec. */ + add_testing_cell_stats_entry(circ, CELL_RELAY, 2, 1, 0); + sum_up_cell_stats_by_command(circ, cell_stats); + tt_u64_op(20, ==, cell_stats->total_time_appward[CELL_RELAY]); + tt_u64_op(1, ==, cell_stats->removed_cells_appward[CELL_RELAY]); + + /* A single RELAY cell was removed from the exitward queue where it + * spent 30 msec. */ + add_testing_cell_stats_entry(circ, CELL_RELAY, 3, 1, 1); + sum_up_cell_stats_by_command(circ, cell_stats); + tt_u64_op(30, ==, cell_stats->total_time_exitward[CELL_RELAY]); + tt_u64_op(1, ==, cell_stats->removed_cells_exitward[CELL_RELAY]); + + done: + tor_free(cell_stats); + tor_free(or_circ); +} + +static void +test_cntev_append_cell_stats(void *arg) +{ + smartlist_t *event_parts; + char *cp = NULL; + const char *key = "Z"; + uint64_t include_if_non_zero[CELL_COMMAND_MAX_ + 1], + number_to_include[CELL_COMMAND_MAX_ + 1]; + (void)arg; + + event_parts = smartlist_new(); + memset(include_if_non_zero, 0, + (CELL_COMMAND_MAX_ + 1) * sizeof(uint64_t)); + memset(number_to_include, 0, + (CELL_COMMAND_MAX_ + 1) * sizeof(uint64_t)); + + /* All array entries empty. */ + append_cell_stats_by_command(event_parts, key, + include_if_non_zero, + number_to_include); + tt_int_op(0, ==, smartlist_len(event_parts)); + + /* There's a RELAY cell to include, but the corresponding field in + * include_if_non_zero is still zero. */ + number_to_include[CELL_RELAY] = 1; + append_cell_stats_by_command(event_parts, key, + include_if_non_zero, + number_to_include); + tt_int_op(0, ==, smartlist_len(event_parts)); + + /* Now include single RELAY cell. */ + include_if_non_zero[CELL_RELAY] = 2; + append_cell_stats_by_command(event_parts, key, + include_if_non_zero, + number_to_include); + cp = smartlist_pop_last(event_parts); + tt_str_op("Z=relay:1", ==, cp); + tor_free(cp); + + /* Add four CREATE cells. */ + include_if_non_zero[CELL_CREATE] = 3; + number_to_include[CELL_CREATE] = 4; + append_cell_stats_by_command(event_parts, key, + include_if_non_zero, + number_to_include); + cp = smartlist_pop_last(event_parts); + tt_str_op("Z=create:4,relay:1", ==, cp); + + done: + tor_free(cp); + smartlist_free(event_parts); +} + +static void +test_cntev_format_cell_stats(void *arg) +{ + char *event_string = NULL; + origin_circuit_t *ocirc = NULL; + or_circuit_t *or_circ = NULL; + cell_stats_t *cell_stats = NULL; + channel_tls_t *n_chan=NULL, *p_chan=NULL; + (void)arg; + + n_chan = tor_malloc_zero(sizeof(channel_tls_t)); + n_chan->base_.global_identifier = 1; + + ocirc = tor_malloc_zero(sizeof(origin_circuit_t)); + ocirc->base_.magic = ORIGIN_CIRCUIT_MAGIC; + ocirc->base_.purpose = CIRCUIT_PURPOSE_C_GENERAL; + ocirc->global_identifier = 2; + ocirc->base_.n_circ_id = 3; + ocirc->base_.n_chan = &(n_chan->base_); + + /* Origin circuit was completely idle. */ + cell_stats = tor_malloc_zero(sizeof(cell_stats_t)); + format_cell_stats(&event_string, TO_CIRCUIT(ocirc), cell_stats); + tt_str_op("ID=2 OutboundQueue=3 OutboundConn=1", ==, event_string); + tor_free(event_string); + + /* Origin circuit had 4 RELAY cells added to its exitward queue. */ + cell_stats->added_cells_exitward[CELL_RELAY] = 4; + format_cell_stats(&event_string, TO_CIRCUIT(ocirc), cell_stats); + tt_str_op("ID=2 OutboundQueue=3 OutboundConn=1 OutboundAdded=relay:4", + ==, event_string); + tor_free(event_string); + + /* Origin circuit also had 5 CREATE2 cells added to its exitward + * queue. */ + cell_stats->added_cells_exitward[CELL_CREATE2] = 5; + format_cell_stats(&event_string, TO_CIRCUIT(ocirc), cell_stats); + tt_str_op("ID=2 OutboundQueue=3 OutboundConn=1 OutboundAdded=relay:4," + "create2:5", ==, event_string); + tor_free(event_string); + + /* Origin circuit also had 7 RELAY cells removed from its exitward queue + * which together spent 6 msec in the queue. */ + cell_stats->total_time_exitward[CELL_RELAY] = 6; + cell_stats->removed_cells_exitward[CELL_RELAY] = 7; + format_cell_stats(&event_string, TO_CIRCUIT(ocirc), cell_stats); + tt_str_op("ID=2 OutboundQueue=3 OutboundConn=1 OutboundAdded=relay:4," + "create2:5 OutboundRemoved=relay:7 OutboundTime=relay:6", + ==, event_string); + tor_free(event_string); + + p_chan = tor_malloc_zero(sizeof(channel_tls_t)); + p_chan->base_.global_identifier = 2; + + or_circ = tor_malloc_zero(sizeof(or_circuit_t)); + or_circ->base_.magic = OR_CIRCUIT_MAGIC; + or_circ->base_.purpose = CIRCUIT_PURPOSE_OR; + or_circ->p_circ_id = 8; + or_circ->p_chan = &(p_chan->base_); + or_circ->base_.n_circ_id = 9; + or_circ->base_.n_chan = &(n_chan->base_); + + tor_free(cell_stats); + + /* OR circuit was idle. */ + cell_stats = tor_malloc_zero(sizeof(cell_stats_t)); + format_cell_stats(&event_string, TO_CIRCUIT(or_circ), cell_stats); + tt_str_op("InboundQueue=8 InboundConn=2 OutboundQueue=9 OutboundConn=1", + ==, event_string); + tor_free(event_string); + + /* OR circuit had 3 RELAY cells added to its appward queue. */ + cell_stats->added_cells_appward[CELL_RELAY] = 3; + format_cell_stats(&event_string, TO_CIRCUIT(or_circ), cell_stats); + tt_str_op("InboundQueue=8 InboundConn=2 InboundAdded=relay:3 " + "OutboundQueue=9 OutboundConn=1", ==, event_string); + tor_free(event_string); + + /* OR circuit had 7 RELAY cells removed from its appward queue which + * together spent 6 msec in the queue. */ + cell_stats->total_time_appward[CELL_RELAY] = 6; + cell_stats->removed_cells_appward[CELL_RELAY] = 7; + format_cell_stats(&event_string, TO_CIRCUIT(or_circ), cell_stats); + tt_str_op("InboundQueue=8 InboundConn=2 InboundAdded=relay:3 " + "InboundRemoved=relay:7 InboundTime=relay:6 " + "OutboundQueue=9 OutboundConn=1", ==, event_string); + + done: + tor_free(cell_stats); + tor_free(event_string); + tor_free(or_circ); + tor_free(ocirc); + tor_free(p_chan); + tor_free(n_chan); +} + +#define TEST(name, flags) \ + { #name, test_cntev_ ## name, flags, 0, NULL } + +struct testcase_t controller_event_tests[] = { + TEST(bucket_note_empty, 0), + TEST(bucket_millis_empty, 0), + TEST(sum_up_cell_stats, 0), + TEST(append_cell_stats, 0), + TEST(format_cell_stats, 0), + END_OF_TESTCASES +}; + diff --git a/src/test/test_crypto.c b/src/test/test_crypto.c index f92bfd673e..5d8edb6550 100644 --- a/src/test/test_crypto.c +++ b/src/test/test_crypto.c @@ -4,16 +4,20 @@ /* See LICENSE for licensing information */ #include "orconfig.h" -#define CRYPTO_PRIVATE #define CRYPTO_CURVE25519_PRIVATE #include "or.h" #include "test.h" #include "aes.h" #include "util.h" +#include "siphash.h" #ifdef CURVE25519_ENABLED #include "crypto_curve25519.h" #endif +extern const char AUTHORITY_SIGNKEY_3[]; +extern const char AUTHORITY_SIGNKEY_A_DIGEST[]; +extern const char AUTHORITY_SIGNKEY_A_DIGEST256[]; + /** Run unit tests for Diffie-Hellman functionality. */ static void test_crypto_dh(void) @@ -269,34 +273,6 @@ test_crypto_sha(void) "96177A9CB410FF61F20015AD"); tt_int_op(i, ==, 0); - /* Test HMAC-SHA-1 with test cases from RFC2202. */ - - /* Case 1. */ - memset(key, 0x0b, 20); - crypto_hmac_sha1(digest, key, 20, "Hi There", 8); - test_streq(hex_str(digest, 20), - "B617318655057264E28BC0B6FB378C8EF146BE00"); - /* Case 2. */ - crypto_hmac_sha1(digest, "Jefe", 4, "what do ya want for nothing?", 28); - test_streq(hex_str(digest, 20), - "EFFCDF6AE5EB2FA2D27416D5F184DF9C259A7C79"); - - /* Case 4. */ - base16_decode(key, 25, - "0102030405060708090a0b0c0d0e0f10111213141516171819", 50); - memset(data, 0xcd, 50); - crypto_hmac_sha1(digest, key, 25, data, 50); - test_streq(hex_str(digest, 20), - "4C9007F4026250C6BC8414F9BF50C86C2D7235DA"); - - /* Case 5. */ - memset(key, 0xaa, 80); - crypto_hmac_sha1(digest, key, 80, - "Test Using Larger Than Block-Size Key - Hash Key First", - 54); - test_streq(hex_str(digest, 20), - "AA4AE5E15272D00E95705637CE8A3B55ED402112"); - /* Test HMAC-SHA256 with test cases from wikipedia and RFC 4231 */ /* Case empty (wikipedia) */ @@ -422,7 +398,7 @@ test_crypto_pk(void) char *encoded = NULL; char data1[1024], data2[1024], data3[1024]; size_t size; - int i, j, p, len; + int i, len; /* Public-key ciphers */ pk1 = pk_generate(0); @@ -506,19 +482,16 @@ test_crypto_pk(void) /* Try with hybrid encryption wrappers. */ crypto_rand(data1, 1024); - for (i = 0; i < 2; ++i) { - for (j = 85; j < 140; ++j) { - memset(data2,0,1024); - memset(data3,0,1024); - p = (i==0)?PK_PKCS1_PADDING:PK_PKCS1_OAEP_PADDING; - len = crypto_pk_public_hybrid_encrypt(pk1,data2,sizeof(data2), - data1,j,p,0); - test_assert(len>=0); - len = crypto_pk_private_hybrid_decrypt(pk1,data3,sizeof(data3), - data2,len,p,1); - test_eq(len,j); - test_memeq(data1,data3,j); - } + for (i = 85; i < 140; ++i) { + memset(data2,0,1024); + memset(data3,0,1024); + len = crypto_pk_public_hybrid_encrypt(pk1,data2,sizeof(data2), + data1,i,PK_PKCS1_OAEP_PADDING,0); + test_assert(len>=0); + len = crypto_pk_private_hybrid_decrypt(pk1,data3,sizeof(data3), + data2,len,PK_PKCS1_OAEP_PADDING,1); + test_eq(len,i); + test_memeq(data1,data3,i); } /* Try copy_full */ @@ -536,6 +509,85 @@ test_crypto_pk(void) tor_free(encoded); } +static void +test_crypto_pk_fingerprints(void *arg) +{ + crypto_pk_t *pk = NULL; + char encoded[512]; + char d[DIGEST_LEN], d2[DIGEST_LEN]; + char fingerprint[FINGERPRINT_LEN+1]; + int n; + unsigned i; + char *mem_op_hex_tmp=NULL; + + (void)arg; + + pk = pk_generate(1); + tt_assert(pk); + n = crypto_pk_asn1_encode(pk, encoded, sizeof(encoded)); + tt_int_op(n, >, 0); + tt_int_op(n, >, 128); + tt_int_op(n, <, 256); + + /* Is digest as expected? */ + crypto_digest(d, encoded, n); + tt_int_op(0, ==, crypto_pk_get_digest(pk, d2)); + test_memeq(d, d2, DIGEST_LEN); + + /* Is fingerprint right? */ + tt_int_op(0, ==, crypto_pk_get_fingerprint(pk, fingerprint, 0)); + tt_int_op(strlen(fingerprint), ==, DIGEST_LEN * 2); + test_memeq_hex(d, fingerprint); + + /* Are spaces right? */ + tt_int_op(0, ==, crypto_pk_get_fingerprint(pk, fingerprint, 1)); + for (i = 4; i < strlen(fingerprint); i += 5) { + tt_int_op(fingerprint[i], ==, ' '); + } + tor_strstrip(fingerprint, " "); + tt_int_op(strlen(fingerprint), ==, DIGEST_LEN * 2); + test_memeq_hex(d, fingerprint); + + /* Now hash again and check crypto_pk_get_hashed_fingerprint. */ + crypto_digest(d2, d, sizeof(d)); + tt_int_op(0, ==, crypto_pk_get_hashed_fingerprint(pk, fingerprint)); + tt_int_op(strlen(fingerprint), ==, DIGEST_LEN * 2); + test_memeq_hex(d2, fingerprint); + + done: + crypto_pk_free(pk); + tor_free(mem_op_hex_tmp); +} + +/** Sanity check for crypto pk digests */ +static void +test_crypto_digests(void) +{ + crypto_pk_t *k = NULL; + ssize_t r; + digests_t pkey_digests; + char digest[DIGEST_LEN]; + + k = crypto_pk_new(); + test_assert(k); + r = crypto_pk_read_private_key_from_string(k, AUTHORITY_SIGNKEY_3, -1); + test_assert(!r); + + r = crypto_pk_get_digest(k, digest); + test_assert(r == 0); + test_memeq(hex_str(digest, DIGEST_LEN), + AUTHORITY_SIGNKEY_A_DIGEST, HEX_DIGEST_LEN); + + r = crypto_pk_get_all_digests(k, &pkey_digests); + + test_memeq(hex_str(pkey_digests.d[DIGEST_SHA1], DIGEST_LEN), + AUTHORITY_SIGNKEY_A_DIGEST, HEX_DIGEST_LEN); + test_memeq(hex_str(pkey_digests.d[DIGEST_SHA256], DIGEST256_LEN), + AUTHORITY_SIGNKEY_A_DIGEST256, HEX_DIGEST256_LEN); + done: + crypto_pk_free(k); +} + /** Run unit tests for misc crypto formatting functionality (base64, base32, * fingerprints, etc) */ static void @@ -630,7 +682,7 @@ test_crypto_formats(void) data1 = tor_strdup("ABCD1234ABCD56780000ABCD1234ABCD56780000"); test_eq(strlen(data1), 40); data2 = tor_malloc(FINGERPRINT_LEN+1); - add_spaces_to_fp(data2, FINGERPRINT_LEN+1, data1); + crypto_add_spaces_to_fp(data2, FINGERPRINT_LEN+1, data1); test_streq(data2, "ABCD 1234 ABCD 5678 0000 ABCD 1234 ABCD 5678 0000"); tor_free(data1); tor_free(data2); @@ -730,11 +782,13 @@ test_crypto_aes_iv(void *arg) /* Decrypt with the wrong key. */ decrypted_size = crypto_cipher_decrypt_with_iv(key2, decrypted2, 4095, encrypted1, encrypted_size); + test_eq(decrypted_size, 4095); test_memneq(plain, decrypted2, decrypted_size); /* Alter the initialization vector. */ encrypted1[0] += 42; decrypted_size = crypto_cipher_decrypt_with_iv(key1, decrypted1, 4095, encrypted1, encrypted_size); + test_eq(decrypted_size, 4095); test_memneq(plain, decrypted2, 4095); /* Special length case: 1. */ encrypted_size = crypto_cipher_encrypt_with_iv(key1, encrypted1, 16 + 1, @@ -1078,7 +1132,8 @@ test_crypto_curve25519_persist(void *arg) content = read_file_to_str(fname, RFTS_BIN, &st); tt_assert(content); taglen = strlen("== c25519v1: testing =="); - tt_int_op(st.st_size, ==, 32+CURVE25519_PUBKEY_LEN+CURVE25519_SECKEY_LEN); + tt_u64_op((uint64_t)st.st_size, ==, + 32+CURVE25519_PUBKEY_LEN+CURVE25519_SECKEY_LEN); tt_assert(fast_memeq(content, "== c25519v1: testing ==", taglen)); tt_assert(tor_mem_is_zero(content+taglen, 32-taglen)); cp = content + 32; @@ -1108,6 +1163,102 @@ test_crypto_curve25519_persist(void *arg) #endif +static void +test_crypto_siphash(void *arg) +{ + /* From the reference implementation, taking + k = 00 01 02 ... 0f + and in = 00; 00 01; 00 01 02; ... + */ + const uint8_t VECTORS[64][8] = + { + { 0x31, 0x0e, 0x0e, 0xdd, 0x47, 0xdb, 0x6f, 0x72, }, + { 0xfd, 0x67, 0xdc, 0x93, 0xc5, 0x39, 0xf8, 0x74, }, + { 0x5a, 0x4f, 0xa9, 0xd9, 0x09, 0x80, 0x6c, 0x0d, }, + { 0x2d, 0x7e, 0xfb, 0xd7, 0x96, 0x66, 0x67, 0x85, }, + { 0xb7, 0x87, 0x71, 0x27, 0xe0, 0x94, 0x27, 0xcf, }, + { 0x8d, 0xa6, 0x99, 0xcd, 0x64, 0x55, 0x76, 0x18, }, + { 0xce, 0xe3, 0xfe, 0x58, 0x6e, 0x46, 0xc9, 0xcb, }, + { 0x37, 0xd1, 0x01, 0x8b, 0xf5, 0x00, 0x02, 0xab, }, + { 0x62, 0x24, 0x93, 0x9a, 0x79, 0xf5, 0xf5, 0x93, }, + { 0xb0, 0xe4, 0xa9, 0x0b, 0xdf, 0x82, 0x00, 0x9e, }, + { 0xf3, 0xb9, 0xdd, 0x94, 0xc5, 0xbb, 0x5d, 0x7a, }, + { 0xa7, 0xad, 0x6b, 0x22, 0x46, 0x2f, 0xb3, 0xf4, }, + { 0xfb, 0xe5, 0x0e, 0x86, 0xbc, 0x8f, 0x1e, 0x75, }, + { 0x90, 0x3d, 0x84, 0xc0, 0x27, 0x56, 0xea, 0x14, }, + { 0xee, 0xf2, 0x7a, 0x8e, 0x90, 0xca, 0x23, 0xf7, }, + { 0xe5, 0x45, 0xbe, 0x49, 0x61, 0xca, 0x29, 0xa1, }, + { 0xdb, 0x9b, 0xc2, 0x57, 0x7f, 0xcc, 0x2a, 0x3f, }, + { 0x94, 0x47, 0xbe, 0x2c, 0xf5, 0xe9, 0x9a, 0x69, }, + { 0x9c, 0xd3, 0x8d, 0x96, 0xf0, 0xb3, 0xc1, 0x4b, }, + { 0xbd, 0x61, 0x79, 0xa7, 0x1d, 0xc9, 0x6d, 0xbb, }, + { 0x98, 0xee, 0xa2, 0x1a, 0xf2, 0x5c, 0xd6, 0xbe, }, + { 0xc7, 0x67, 0x3b, 0x2e, 0xb0, 0xcb, 0xf2, 0xd0, }, + { 0x88, 0x3e, 0xa3, 0xe3, 0x95, 0x67, 0x53, 0x93, }, + { 0xc8, 0xce, 0x5c, 0xcd, 0x8c, 0x03, 0x0c, 0xa8, }, + { 0x94, 0xaf, 0x49, 0xf6, 0xc6, 0x50, 0xad, 0xb8, }, + { 0xea, 0xb8, 0x85, 0x8a, 0xde, 0x92, 0xe1, 0xbc, }, + { 0xf3, 0x15, 0xbb, 0x5b, 0xb8, 0x35, 0xd8, 0x17, }, + { 0xad, 0xcf, 0x6b, 0x07, 0x63, 0x61, 0x2e, 0x2f, }, + { 0xa5, 0xc9, 0x1d, 0xa7, 0xac, 0xaa, 0x4d, 0xde, }, + { 0x71, 0x65, 0x95, 0x87, 0x66, 0x50, 0xa2, 0xa6, }, + { 0x28, 0xef, 0x49, 0x5c, 0x53, 0xa3, 0x87, 0xad, }, + { 0x42, 0xc3, 0x41, 0xd8, 0xfa, 0x92, 0xd8, 0x32, }, + { 0xce, 0x7c, 0xf2, 0x72, 0x2f, 0x51, 0x27, 0x71, }, + { 0xe3, 0x78, 0x59, 0xf9, 0x46, 0x23, 0xf3, 0xa7, }, + { 0x38, 0x12, 0x05, 0xbb, 0x1a, 0xb0, 0xe0, 0x12, }, + { 0xae, 0x97, 0xa1, 0x0f, 0xd4, 0x34, 0xe0, 0x15, }, + { 0xb4, 0xa3, 0x15, 0x08, 0xbe, 0xff, 0x4d, 0x31, }, + { 0x81, 0x39, 0x62, 0x29, 0xf0, 0x90, 0x79, 0x02, }, + { 0x4d, 0x0c, 0xf4, 0x9e, 0xe5, 0xd4, 0xdc, 0xca, }, + { 0x5c, 0x73, 0x33, 0x6a, 0x76, 0xd8, 0xbf, 0x9a, }, + { 0xd0, 0xa7, 0x04, 0x53, 0x6b, 0xa9, 0x3e, 0x0e, }, + { 0x92, 0x59, 0x58, 0xfc, 0xd6, 0x42, 0x0c, 0xad, }, + { 0xa9, 0x15, 0xc2, 0x9b, 0xc8, 0x06, 0x73, 0x18, }, + { 0x95, 0x2b, 0x79, 0xf3, 0xbc, 0x0a, 0xa6, 0xd4, }, + { 0xf2, 0x1d, 0xf2, 0xe4, 0x1d, 0x45, 0x35, 0xf9, }, + { 0x87, 0x57, 0x75, 0x19, 0x04, 0x8f, 0x53, 0xa9, }, + { 0x10, 0xa5, 0x6c, 0xf5, 0xdf, 0xcd, 0x9a, 0xdb, }, + { 0xeb, 0x75, 0x09, 0x5c, 0xcd, 0x98, 0x6c, 0xd0, }, + { 0x51, 0xa9, 0xcb, 0x9e, 0xcb, 0xa3, 0x12, 0xe6, }, + { 0x96, 0xaf, 0xad, 0xfc, 0x2c, 0xe6, 0x66, 0xc7, }, + { 0x72, 0xfe, 0x52, 0x97, 0x5a, 0x43, 0x64, 0xee, }, + { 0x5a, 0x16, 0x45, 0xb2, 0x76, 0xd5, 0x92, 0xa1, }, + { 0xb2, 0x74, 0xcb, 0x8e, 0xbf, 0x87, 0x87, 0x0a, }, + { 0x6f, 0x9b, 0xb4, 0x20, 0x3d, 0xe7, 0xb3, 0x81, }, + { 0xea, 0xec, 0xb2, 0xa3, 0x0b, 0x22, 0xa8, 0x7f, }, + { 0x99, 0x24, 0xa4, 0x3c, 0xc1, 0x31, 0x57, 0x24, }, + { 0xbd, 0x83, 0x8d, 0x3a, 0xaf, 0xbf, 0x8d, 0xb7, }, + { 0x0b, 0x1a, 0x2a, 0x32, 0x65, 0xd5, 0x1a, 0xea, }, + { 0x13, 0x50, 0x79, 0xa3, 0x23, 0x1c, 0xe6, 0x60, }, + { 0x93, 0x2b, 0x28, 0x46, 0xe4, 0xd7, 0x06, 0x66, }, + { 0xe1, 0x91, 0x5f, 0x5c, 0xb1, 0xec, 0xa4, 0x6c, }, + { 0xf3, 0x25, 0x96, 0x5c, 0xa1, 0x6d, 0x62, 0x9f, }, + { 0x57, 0x5f, 0xf2, 0x8e, 0x60, 0x38, 0x1b, 0xe5, }, + { 0x72, 0x45, 0x06, 0xeb, 0x4c, 0x32, 0x8a, 0x95, } + }; + + const struct sipkey K = { U64_LITERAL(0x0706050403020100), + U64_LITERAL(0x0f0e0d0c0b0a0908) }; + uint8_t input[64]; + int i, j; + + (void)arg; + + for (i = 0; i < 64; ++i) + input[i] = i; + + for (i = 0; i < 64; ++i) { + uint64_t r = siphash24(input, i, &K); + for (j = 0; j < 8; ++j) { + tt_int_op( (r >> (j*8)) & 0xff, ==, VECTORS[i][j]); + } + } + + done: + ; +} + static void * pass_data_setup_fn(const struct testcase_t *testcase) { @@ -1134,6 +1285,8 @@ struct testcase_t crypto_tests[] = { { "aes_EVP", test_crypto_aes, TT_FORK, &pass_data, (void*)"evp" }, CRYPTO_LEGACY(sha), CRYPTO_LEGACY(pk), + { "pk_fingerprints", test_crypto_pk_fingerprints, TT_FORK, NULL, NULL }, + CRYPTO_LEGACY(digests), CRYPTO_LEGACY(dh), CRYPTO_LEGACY(s2k), { "aes_iv_AES", test_crypto_aes_iv, TT_FORK, &pass_data, (void*)"aes" }, @@ -1148,6 +1301,7 @@ struct testcase_t crypto_tests[] = { { "curve25519_encode", test_crypto_curve25519_encode, 0, NULL, NULL }, { "curve25519_persist", test_crypto_curve25519_persist, 0, NULL, NULL }, #endif + { "siphash", test_crypto_siphash, 0, NULL, NULL }, END_OF_TESTCASES }; diff --git a/src/test/test_data.c b/src/test/test_data.c index 5f0f7cba01..0c51c98f1e 100644 --- a/src/test/test_data.c +++ b/src/test/test_data.c @@ -3,8 +3,18 @@ * Copyright (c) 2007-2013, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/* Our unit test expect that the AUTHORITY_CERT_* public keys will sort + * in this order. */ +#define AUTHORITY_CERT_A AUTHORITY_CERT_3 +#define AUTHORITY_CERT_B AUTHORITY_CERT_1 +#define AUTHORITY_CERT_C AUTHORITY_CERT_2 + +#define AUTHORITY_SIGNKEY_A AUTHORITY_SIGNKEY_3 +#define AUTHORITY_SIGNKEY_B AUTHORITY_SIGNKEY_1 +#define AUTHORITY_SIGNKEY_C AUTHORITY_SIGNKEY_2 + /** First of 3 example authority certificates for unit testing. */ -const char AUTHORITY_CERT_1[] = +const char AUTHORITY_CERT_A[] = "dir-key-certificate-version 3\n" "fingerprint D867ACF56A9D229B35C25F0090BC9867E906BE69\n" "dir-key-published 2008-12-12 18:07:24\n" @@ -46,7 +56,7 @@ const char AUTHORITY_CERT_1[] = "-----END SIGNATURE-----\n"; /** The private signing key for AUTHORITY_CERT_1 */ -const char AUTHORITY_SIGNKEY_1[] = +const char AUTHORITY_SIGNKEY_A[] = "-----BEGIN RSA PRIVATE KEY-----\n" "MIICWwIBAAKBgQCz0lCJ8rhLujVdzY6M6ZWp4iBAc0FxI79cff/pqp8GQAaWFZrs\n" "vQPJ8XqMmN7GRbJ2MDVvyGYwIBtt6RJnr7txfi+JsjI42mujkZdzIEWEOIJrhaqX\n" @@ -63,111 +73,128 @@ const char AUTHORITY_SIGNKEY_1[] = "Yx4lqK0ca5IkTp3HevwnlWaJgbaOTUspCVshzJBhDA==\n" "-----END RSA PRIVATE KEY-----\n"; +const char AUTHORITY_SIGNKEY_A_DIGEST[] = + "CBF56A83368A5150F1A9AAADAFB4D77F8C4170E2"; +const char AUTHORITY_SIGNKEY_A_DIGEST256[] = + "AF7C5468DBE3BA54A052726038D7F15F3C4CA511B1952645B3D96D83A8DFB51C"; + /** Second of 3 example authority certificates for unit testing. */ -const char AUTHORITY_CERT_2[] = +const char AUTHORITY_CERT_B[] = "dir-key-certificate-version 3\n" -"fingerprint 4D44AE0470B9E88FD4558EFEC82698FB33715400\n" -"dir-key-published 2007-06-13 16:52:32\n" -"dir-key-expires 2008-06-13 16:52:32\n" +"fingerprint AD011E25302925A9D39A80E0E32576442E956467\n" +"dir-key-published 2013-11-14 14:12:05\n" +"dir-key-expires 2014-11-14 14:12:05\n" "dir-identity-key\n" "-----BEGIN RSA PUBLIC KEY-----\n" -"MIIBigKCAYEAqukDwQRm1Oy1pPY+7GNRnRNFJzEVPUBfJwC4tBH19tkvdRQPuIGI\n" -"2jiTy/rmZ6CLcl1G0oulSgxfKEX75QdptOasZu+rKUrRRSxx0QrXhs9a7up0rpXh\n" -"13fw3mh1Vl/As3rJYF30Hjk01BTOJMxi/HY2y0ALQytFWjiMGY74A9Y6+uDcHkB2\n" -"KflBjxIl8zpCsXsTTnUhN5kXqaOOnK46XaUShSpXsyOxTMJXuJEtgLz9XCyA8XjW\n" -"d75QLHucEnlTqxUAdI5YSN2KIlIJiySCVnAorDpJey2mE9VncpHQWMCv/FPFdnSU\n" -"EMMPUc4bBShcoNFf0mMJeV2sv+dBkgKAL0GLM19PuJIThJhfN/B6+YQTxw4HEpPV\n" -"plfUqYRN0fYC+5hCTS6rroO/uCfDR7NBtoeDNm9dQrvjfk3b/Mywah1rdWNjnVqs\n" -"tPJaz3fc/CVBOUUexhmyktgLuwSNEYIQilQ+BydkWN/4RObhV+YSV5BgekEDVaoS\n" -"RHw4IbYBDHVxAgMBAAE=\n" +"MIIBigKCAYEAyXYEMlGNRAixXdg65xf2WPkskYj2Wo8ysKMTls1JCXdIOAPvC2k2\n" +"+AC6i3x9JHzUgCjWr4Jd5PSi7ODGyFC543igYl4wzkxNTU2L+SQ+hMe9qbEuUNhH\n" +"sRR0xofdoH//3UuKj+HXEiMhhHbRWQGtWFuJqtGBruJqjZqIGOrp5nFjdlP0R98n\n" +"Rx5wWlPgdJzifkXjKouu4mV+KzLl7f0gAtngA9DkSjt1wzga5IlL/lxDciD0SyJU\n" +"tKMmls056omrZNbTnBxnY2pOlq9nx/zFrt/KQm1fTAQMjMBCf9KnDIV7NhaaHx7F\n" +"7Nk8L7Hha353SvR+bsOFpiu05/EMZFTTIhO3MhUxZiCVZ0hKXvW1xe0HoGC5wbB+\n" +"NyXu8oa4fIKLJ+WJ8Z60BNc0DcxJiQOf1eolGM/qrBul1lFZznds5/7182d+nF2W\n" +"+bEjSm0fgXIxPfSD/7hB0FvgtmB3TXybHGBfPZgX0sTzFB6LNtP0BHicRoMXKdLF\n" +"hM3tgIjEAsoZAgMBAAE=\n" "-----END RSA PUBLIC KEY-----\n" "dir-signing-key\n" "-----BEGIN RSA PUBLIC KEY-----\n" -"MIGJAoGBAOu3dgrQth3iqvi/UzfywaANw0bBUuMOBhnMBeiLEcRLneJHUJkVvrpR\n" -"/EDQkdMov1e7CX6aqBKygVnbDNYjJ+bcQej8MKpuuW+zIknnz5lfnAVZO5uAmo3Y\n" -"DpG574oQ2FFMdkWHSBloIRxSj/E4Jn1M2qJjElBXP0E33Ka/Noo7AgMBAAE=\n" +"MIGJAoGBAJ567PZIGG/mYWEY4szYi/C5XXvf0BkquzKTHKrqVjysZEys9giz56Gv\n" +"B08kIRxsxYKEWkq60rv0xtTc1WyEMcDpV1WLU0KSTQSVXzLu7BT8jbTsWzGsxdTV\n" +"TdeyOirwHh8Cyyon5lppuMH5twUHrL5O7pWWbxjjrQjAHCn3gd+NAgMBAAE=\n" "-----END RSA PUBLIC KEY-----\n" +"dir-key-crosscert\n" +"-----BEGIN ID SIGNATURE-----\n" +"OC+gaukd4K7xJOsgTPbRhacf5mDUGxsu3ho/J1oJdtni4CK9WscVs6/Goj1o5Lot\n" +"H1nCAMaR96Jnqq5c63Aaj1sEXdeYHlu5cI7YHgtGI5MmtjiUNXUCWMjCwSQYwGKe\n" +"2YDYGAKAGt97n7XMKhJWGjAmv1TgmK3DvL1jt/aazL8=\n" +"-----END ID SIGNATURE-----\n" "dir-key-certification\n" "-----BEGIN SIGNATURE-----\n" -"Fv0Li68QUdAiChY3OklZOakHzwXAUfCzDNxkqe+HLC0n6ZECE9ZCvLVo69XmgVhH\n" -"L5qYr2rxT6QpF+9yuOHbN9gWn8EsDcli06MlhX9TUt/IYVxHa/9tJwNoTfEw2w2D\n" -"tyHhWm94IfOK7/Sea6jHnjckl80X+kk0ZNtAGs3/6fP4iltKNGXnvBwfgLpEgW7X\n" -"NpDl0OLeDuA79zem2GogwQZQdoDbePByU0TJVx9jYi2Bzx2Nb2H0hRTPP6+dY0HQ\n" -"MHb7yyyTQRad5iAUnExKhhyt22p7X3a6lgkAhq4YrNn/zVPkpnT2dzjsOydTHOW8\n" -"2BQs33QlGNe095i47pJBDYsUgmJaXfqB/RG6dFg7jwIsc3/7dZcvcqfxY7wKcD/T\n" -"wtogCIKxDvWbZn7f0hqYkT6uQC8Zom8bcnedmyzufOZCyA2SqQ2wvio6lznR4RIB\n" -"a8qDHR0tPS9/VkqTPcvUWCZeY3UiDeWPjoK1nea1pz6DHDWglKPx86a0amjjayZQ\n" -"-----END SIGNATURE-----\n"; +"BddmCKsvS6VoFXIf9Aj9OZnfyVCx527517QtsQHN+NaVm20LzUkJ5MWGXYx4wgh3\n" +"ExsHvVQguiVfnonkQpEHHKg+TbldlkuDhIdlb9f7dL7V3HLCsEdmS1c3A+TEyrPH\n" +"i44p6QB5IMFAdgUMV/9ueKMh7pMoam6VNakMOd+Axx9BSJTrCRzcepjtM4Z0cPsj\n" +"nmDgZi0df1+ca1t+HnuWyt3trxlqoUxRcPZKz28kEFDJsgnRNvoHrIvNTuy9qY4x\n" +"rONnPuLr5kTO7VQVVZxgxt6WX3p6d8tj+WYHubydr2pG0dwu2vGDTy4qXvDIm/I4\n" +"Gyo6OAoPbYV8fl0584EgiEbAWcX/Pze8mXr9lmXbf73xbSBHqveAs0UfB+4sBI98\n" +"v4ax4NZkGs8cCIfugtAOLgZE0WCh/TQYnQ3PFcrUtj0RW+tM1z7S8P3UfEVBHVkJ\n" +"8SqSB+pbsY6PwMuy6TC3WujW7gmjVanbwkbW19El9l9jRzteFerz7grG/WQkshqF\n" + "-----END SIGNATURE-----\n"; /** The private signing key for AUTHORITY_CERT_2 */ -const char AUTHORITY_SIGNKEY_2[] = +const char AUTHORITY_SIGNKEY_B[] = "-----BEGIN RSA PRIVATE KEY-----\n" -"MIICXgIBAAKBgQDrt3YK0LYd4qr4v1M38sGgDcNGwVLjDgYZzAXoixHES53iR1CZ\n" -"Fb66UfxA0JHTKL9Xuwl+mqgSsoFZ2wzWIyfm3EHo/DCqbrlvsyJJ58+ZX5wFWTub\n" -"gJqN2A6Rue+KENhRTHZFh0gZaCEcUo/xOCZ9TNqiYxJQVz9BN9ymvzaKOwIDAQAB\n" -"AoGAJ+I9/ex8tCfTSA2PdisEKiHKBeHWNYb870Z/RW6qje1BhLUOZSixwfL3XLwt\n" -"wG3nml+SZrKid69uhZaz4FPIf0tqCgURf6dDrF5vuzzr7VLVqkZHYSBp0vE6bu0R\n" -"Sgc5QNxI2talgc4bsp0O0C+Zd4n3Yto0pXl/I6NHVAxlFBECQQD2mahkY+QEHWPV\n" -"yRY3w3HhRmWBcrkY2zVyvPpqfn/sdHRPYW/yj4Xr/d1CO9VyFmEs4k324lIvu6LT\n" -"WDdpPlcJAkEA9LOZv5aNeAm8ckvvXH7iv8KiONiSz0n9wlisxMhNYTEkOCo1g7jG\n" -"AX5ZknRC9s4sWCPOBpMhloUvemdQ5FCEIwJBAMqCFwoSCf7jD8hRcUBr7QodoF/0\n" -"kVJ7OeI2lMJ9jZnlbFp/3snn2Qeam2e38SnWfQi582KKKwnt4eIDMMXpntkCQQDI\n" -"v1Lh11wl3y7nQZ6T7lCNatp08k+2mQgCWYcbRQweMRd6sD4I2xwt+372ZETPfyLo\n" -"CC+sOyYx+v+RVpMJS3irAkEA6l98nMteZKmhOgyKSjdolP+ahpZunb+WnCdAtP97\n" -"rjZyXmEZS3oe7TRCDD28GAGMmxSDvNfOOpyn14ishEs5AQ==\n" +"MIICWwIBAAKBgQCeeuz2SBhv5mFhGOLM2IvwuV1739AZKrsykxyq6lY8rGRMrPYI\n" +"s+ehrwdPJCEcbMWChFpKutK79MbU3NVshDHA6VdVi1NCkk0ElV8y7uwU/I207Fsx\n" +"rMXU1U3Xsjoq8B4fAssqJ+ZaabjB+bcFB6y+Tu6Vlm8Y460IwBwp94HfjQIDAQAB\n" +"AoGAfHQ4ZmfTmPyoeGHcqdVcgBxxh3gJqdnezCavGqGQO3F+CqDBTbBKNLSI3uOW\n" +"hQX+TTK23Xy9RRFCm6MYj3F4x7OOrSHSFyhMmzRnAZi3zGbtQZn30XoqTwCmVevY\n" +"p5JbVvhP2BJcvdsyQhiIG23FRQ7MMHWtksAxmovTto1h/hkCQQDNCfMqSztgJZDn\n" +"JSf5ASHBOw8QzfZBeYi3hqfiDtAN1RxT1uQnEiFQFJqwCz5lCbcwVrfQbrrk5M+h\n" +"ooYrX7tTAkEAxd6Tl0N0WM3zCKz+3/Hoiyty6olnnpzNoPCg7LLBJcetABQi0KUv\n" +"swYWlKP3eOFZkiBzTqa9nBK7eYLKV3d9nwJAKNM3WI98Nguky3FJgTnpd6kDuevY\n" +"gXbqcuhb2xXp9Sceqc7axLDGc0R2/GBwvvttPzG1DcpOai7o7J0Iq/A2wwJAYuKI\n" +"/99GFdtWyc8q0OAkRui/1VY14p6aZQPcaG4s+KSBYLivbXYgEGfKgR4wXsi/6rcs\n" +"6PGLcKQr7N3gITYmIQJAaQn6djUWygCn1noKyWU+Sa7G5qqU2GWkLq9dMaRLm1/I\n" +"nqi+2K1mN15rra0QtFVqSH4JXr8h3KAGyU45voGM7A==\n" "-----END RSA PRIVATE KEY-----\n"; /** Third of 3 example authority certificates for unit testing. */ -const char AUTHORITY_CERT_3[] = +const char AUTHORITY_CERT_C[] = "dir-key-certificate-version 3\n" -"fingerprint ED3719BF554DE9D7D59F5CA5A4F5AD121D020ED9\n" -"dir-key-published 2007-06-13 16:52:40\n" -"dir-key-expires 2008-06-13 16:52:40\n" +"fingerprint 628C2086EC29C9D26E638C5A8B2065BFBD35829B\n" +"dir-key-published 2013-11-14 14:12:18\n" +"dir-key-expires 2014-11-14 14:12:18\n" "dir-identity-key\n" "-----BEGIN RSA PUBLIC KEY-----\n" -"MIIBigKCAYEAtB+yw4BNxtZAG4cPaedkhWNmeij7IuNWmXjh58ZYEGurvGyHs1w4\n" -"QlwNYI2UftSIeIGdWZ5fJ17h9P3xvO6eeJuOt4KPrNOxUbSGrELEx1Lje1fDAJ1X\n" -"SvN+dvptusxtyFUr8afgTPrFIvYuazQ6q/Rw+NDagjmDx3h/A/enihpBnjwzeH8j\n" -"Xzu7b+HKnzFnNfveTDdvSy0NSC6tCOnrfXo31XbXRXtlesnMIpbJClUcAv55eyai\n" -"/PrVPCCUz8mk0sQnn2Xhv1YJmwOlQTGMfg0a0kWLmh+UWcHsGQ4VWxBZJcuzgFHG\n" -"hu2/Fz6DXSpX5Q6B9HKoGmnH1oBh24l0kUW1jL8BxPY4YDU1Lt5t3qgcDn9dXYcI\n" -"o8VvyI0ecSc26Q2PYFWX1hpN4VIBZ8uGaW3IpyTdNiRq0g3iMGRFEXcDlWuyMB9E\n" -"EbSM7m/79V/z7SjDd75EP8Z0qDPESEVB8a8LbuSJtzFVE0KHd7RzkIEN5sorXspZ\n" -"/THukftSmkIvAgMBAAE=\n" +"MIIBigKCAYEAuzPA82lRVUAc1uZgfDehhK0rBU5xt+qhJXUSH0DxsuocYCLW//q+\n" +"7+L7q9SochqZK3R5+SxJaZRlVK4rAeIHsxXFxsnGvuqasGM3he80EV1RpVRkvLaO\n" +"2dDmHcfEjYBadft2DEq811yvqSRqbFXmK0hLucA6LI6NnEw9VNWlguaV6ACVLyKQ\n" +"iYVFz2JOJIAi0Zz57WZg7eHypUAGoyXjtYTJPsh6pUe/0NLFJVd3JHcJX+bNqU2a\n" +"QU37r+CQ9f3T+8fZGJQ/CXNnYUNHa0j+toOFuPEiZBBh8C4PE7FJWjidvhe9uI7T\n" +"Py41RZhy8e05MAQmUBNRKBHWPKHoy2zWZZxTkcfWFdJJz/dzsNrIjrqf2fYId9To\n" +"fDpHzYd/UjzZaaVYRVS/Oyf3pN8DKw8LMhEArS0X9pblPVkWWjmYMU6f0VR7pelc\n" +"gGYuML3gOiKdNbeMWgAv3HNRsVsuW0HZLrhXUGYzTRPJ/GxVCwA/NmYgMTNVWRwF\n" +"7M78YHpayyEPAgMBAAE=\n" "-----END RSA PUBLIC KEY-----\n" "dir-signing-key\n" "-----BEGIN RSA PUBLIC KEY-----\n" -"MIGJAoGBANrSZlUq38Boz3iuUOydYTJV57rTbq1bz805FP2QG2Z+2bwpgKIOZag/\n" -"gN2A1ySJaIYLgZIg9irxrLkqlY/UAjC23y6V9fJXP1S3TXoqLmHleW8PsaDLuwTo\n" -"hCWaR61Mx9WG7IXcodn2Z7RiCfZpSW4Rztbk5WtjQa5jPXSFOuBJAgMBAAE=\n" +"MIGJAoGBANESf/hRRWCK3TLQyNb9Y42tYedCORUc8Rl+Q4wrvdz3R0TNr6rztE9N\n" +"u8v3Wbvjtiqm1xL1I5PaOObFQQj61QZxKiCm1yU4eFH15dNmcvBEy5BjEXVYiDgy\n" +"zKRyePzjHYQIZF3ZaQTABUplkXVpY0YvAurluhEy+dKEvZMwWFZTAgMBAAE=\n" "-----END RSA PUBLIC KEY-----\n" +"dir-key-crosscert\n" +"-----BEGIN ID SIGNATURE-----\n" +"NHNBya6Dt7Ww3qSGA0DBEl6pZFBzmYXM+QdqF+ESpdyYCQ54EYimaxl4VcXoGaxy\n" +"xk8/VOXPC6h7hVnTWDTsC86G6eXug1yzpd/uhQbcDJMH5q8/Yg5WXGOnGhMWNCBh\n" +"u2UmbtAjdjLrObQaB50FfOpuOV9kdG4SEzaPUBR2ayU=\n" +"-----END ID SIGNATURE-----\n" "dir-key-certification\n" "-----BEGIN SIGNATURE-----\n" -"UNXZy+4OQ8iat+gw+vg2ynvKj2BYbqZt+EAZAV3rmw6gux44U9TLRECRd6LsA08N\n" -"4+Vz01TU81xqMgfrUy94ei2YvcfpO8art9/muWHTP9SmOX8S1uqDqLWA+n723C9A\n" -"HyVXn4aINncO2081gJcIW5+Ul8WTCeZe/n3LVPTCKbTdqxvmrPUdCWlJTQUmb19M\n" -"T+kcCjaEfgQGLC+Y2MHqYe/nxz+aBKqpjiWUDdjc35va6r/2e3c0jGi1B1xRZxN1\n" -"xThPZ+CifjDoWBxJdDGlIfZRK1lMnOCJY9w9ibTXQ1UnvE4whFvmB55/t9/XLq4q\n" -"3pnZz0H7funey3+ilmTxDohoAYT1GX+4a+3xYH07UmAFqlTzqKClj84XEHn+Cer7\n" -"Nun9kJlJFuBgUpQjwCkzedFZKKLOHgB2h7trJfnqcBpAM8Rup1Bb5u/RcBx9gy1q\n" -"pMc65FviIrc/Q5TUku6NNbCbnGll1599PvWuUzkG42lJ17V6psKHIsqGtVdHlCUc\n" +"NocTkLl9iKglVo+yrpY0slsqgPviuScMyEfOJ3i65KeJb4Dr1huIs0Fip40zFD8D\n" +"cz/SYu09FbANuRwBJIRdVWZLLwVFLBj5F8U65iJRAPBw/O/xgSVBvWoOhBUZqmJA\n" +"Jp1IUutQHYFfnAOO9za4r8Ox6yPaOWF9Ks5gL0kU/fI8Bdi5E9p3e9fMtoM7hROg\n" +"oX1AoV/za3LcM0oMsGsdXQ7B8vRqY0eUX523kpRpF1fUDyvBUvvMsXdZDN6anCV6\n" +"NtSq2UaM/msTX1oQ8gzyD1gMXH0Ek26YMhd+6WZE6KUeb1x5HJgXtKtYzMLB6nQM\n" +"4Q/OA4NND/Veflofy6xx8uzXe8H+MoUHK9WiORtwqvBl0E9qk6SVCuo4ipR4Ybgk\n" +"PAFOXA58j80dlNYYEVgV8MXF1Y/g/thuXlf2dWiLAExdHTtE0AzC4quWshegaImC\n" +"4aziHeA43TRDszAXcJorREAM0AhSxp3aWDde4Jt46ODOJR8t+gHreks29eDttEIn\n" "-----END SIGNATURE-----\n"; /** The private signing key for AUTHORITY_CERT_3 */ -const char AUTHORITY_SIGNKEY_3[] = +const char AUTHORITY_SIGNKEY_C[] = "-----BEGIN RSA PRIVATE KEY-----\n" -"MIICXgIBAAKBgQDa0mZVKt/AaM94rlDsnWEyVee6026tW8/NORT9kBtmftm8KYCi\n" -"DmWoP4DdgNckiWiGC4GSIPYq8ay5KpWP1AIwtt8ulfXyVz9Ut016Ki5h5XlvD7Gg\n" -"y7sE6IQlmketTMfVhuyF3KHZ9me0Ygn2aUluEc7W5OVrY0GuYz10hTrgSQIDAQAB\n" -"AoGBAIyoeG1AnQmildKeQpiGZackf0uhg2BeRwpFKg//5Q0Sd0Wza+M/2+q1v1Ei\n" -"86ihxxV7KfPTykk6hmuUSwVkI28Z+5J9NYTr35EzPiUlqpo0iclTkFqrlbqSPULx\n" -"9fQhvcOGv1c0m5CnYrHsM8eu3tagLg+6OE4abLOYX4Az5pkxAkEA/NwHhVaVJrXH\n" -"lGDrRAfGtaD5Tzeeg1H9DNZi5lmFiSNR0O11sgDLkiZNP5oM8knyqo8Gq08hwxEb\n" -"yqMXM3XtJQJBAN2KJbFhOjDIkvJyYvbmcP6P7vV2c9j+oUTKkFMF7vvfWunxMi9j\n" -"ghbdUKgl7tU0VFpw7ufDDD0pkN6sua3gp1UCQQCvNzTK861U7p/GtMYyFQVf9JTt\n" -"jMf9jYHBNInBvwTme6AFG5bz6tMlif77dJ9GAXHzODrR2Hq3thJA/3RjR3M1AkBg\n" -"+6M4ncmtpYC+5lhwob0Bk90WU/6vFflfdhXsYoKWfNb95vsDR9qhS82Nbt25NClh\n" -"VmMfzoFDHTkwYgj/F4PpAkEA+RaaSRP7BmbvFNqvlm8J/m0RVdAH4+p/Q5Z5u6Yo\n" -"N7xC/gFi0qFPGKsDvD2CncAYmt+KNsd8S0JGDN4eieKn+Q==\n" +"MIICXAIBAAKBgQDREn/4UUVgit0y0MjW/WONrWHnQjkVHPEZfkOMK73c90dEza+q\n" +"87RPTbvL91m747YqptcS9SOT2jjmxUEI+tUGcSogptclOHhR9eXTZnLwRMuQYxF1\n" +"WIg4Msykcnj84x2ECGRd2WkEwAVKZZF1aWNGLwLq5boRMvnShL2TMFhWUwIDAQAB\n" +"AoGAU68L+eDN3C65CzX2rdcOmg7kOSSQpJrJBmM7tkdr3546sJeD0PFrIrMCkEmZ\n" +"aVNj/v545+WnL+8RB4280lNUIF4AMNaMZUL+4FAtwekqWua3QvvqgRMjCdG3/h/d\n" +"bOAUiiKKEimflTaIVHNVSCvOIntftOu3PhebctuabnZzg0ECQQD9i+FX7M9UXT1A\n" +"bVm+bRIJuQtG+u9jD3VxrvHsmh0QnOAL3oa/ofTCwoTJLZs8Qy0GeAoJNf28rY1q\n" +"AgNMEeEXAkEA0xhxNX2fDQ2yvKwPkPMrRycJVWry+KHvSZG2+XYh+V5sVGQ5H7Gu\n" +"krc6IzRZlIKQhEGktkw8ih0DEHQbAihiJQJBAKi/SnFcePjrPXL91Hb63MB/2dOZ\n" +"+21wwnexOe6A+8ssvajop8IvJlnhYMMMiX7oLrVZe0R6HLBQyge94zfjxm0CQGye\n" +"dRIrE34qAEBo4JGbLjesdHcJUwBwgqn+WoI+MPkZhvBdqa8PRF6l/TpEI5vxGt+S\n" +"z2gmDjia+QqsU4FmuikCQDDOs85uwNSKJFax9XMzd1qd1QwX20F8lvnOsWErXiDw\n" +"Fy2+rmIRHoSxn4D+rE5ivqkO99E9jAlz+uuQz/6WqwE=\n" "-----END RSA PRIVATE KEY-----\n"; diff --git a/src/test/test_dir.c b/src/test/test_dir.c index 56ac3b34c7..c03b63be27 100644 --- a/src/test/test_dir.c +++ b/src/test/test_dir.c @@ -11,6 +11,7 @@ #define ROUTER_PRIVATE #define ROUTERLIST_PRIVATE #define HIBERNATE_PRIVATE +#define NETWORKSTATUS_PRIVATE #include "or.h" #include "config.h" #include "directory.h" @@ -97,7 +98,6 @@ test_dir_formats(void) get_platform_str(platform, sizeof(platform)); r1 = tor_malloc_zero(sizeof(routerinfo_t)); - r1->address = tor_strdup("18.244.0.1"); r1->addr = 0xc0a80001u; /* 192.168.0.1 */ r1->cache_info.published_on = 0; r1->or_port = 9000; @@ -124,7 +124,6 @@ test_dir_formats(void) ex2->maskbits = 8; ex2->prt_min = ex2->prt_max = 24; r2 = tor_malloc_zero(sizeof(routerinfo_t)); - r2->address = tor_strdup("1.1.1.1"); r2->addr = 0x0a030201u; /* 10.3.2.1 */ r2->platform = tor_strdup(platform); r2->cache_info.published_on = 5; @@ -153,7 +152,7 @@ test_dir_formats(void) tor_free(options->ContactInfo); test_assert(buf); - strlcpy(buf2, "router Magri 18.244.0.1 9000 0 9003\n" + strlcpy(buf2, "router Magri 192.168.0.1 9000 0 9003\n" "or-address [1:2:3:4::]:9999\n" "platform Tor "VERSION" on ", sizeof(buf2)); strlcat(buf2, get_uname(), sizeof(buf2)); @@ -187,7 +186,7 @@ test_dir_formats(void) cp = buf; rp1 = router_parse_entry_from_string((const char*)cp,NULL,1,0,NULL); test_assert(rp1); - test_streq(rp1->address, r1->address); + test_eq(rp1->addr, r1->addr); test_eq(rp1->or_port, r1->or_port); //test_eq(rp1->dir_port, r1->dir_port); test_eq(rp1->bandwidthrate, r1->bandwidthrate); @@ -196,9 +195,10 @@ test_dir_formats(void) test_assert(crypto_pk_cmp_keys(rp1->onion_pkey, pk1) == 0); test_assert(crypto_pk_cmp_keys(rp1->identity_pkey, pk2) == 0); //test_assert(rp1->exit_policy == NULL); + tor_free(buf); strlcpy(buf2, - "router Fred 1.1.1.1 9005 0 0\n" + "router Fred 10.3.2.1 9005 0 0\n" "platform Tor "VERSION" on ", sizeof(buf2)); strlcat(buf2, get_uname(), sizeof(buf2)); strlcat(buf2, "\n" @@ -214,8 +214,10 @@ test_dir_formats(void) strlcat(buf2, "signing-key\n", sizeof(buf2)); strlcat(buf2, pk1_str, sizeof(buf2)); strlcat(buf2, "hidden-service-dir\n", sizeof(buf2)); +#ifdef CURVE25519_ENABLED strlcat(buf2, "ntor-onion-key " "skyinAnvardNostarsNomoonNowindormistsorsnow=\n", sizeof(buf2)); +#endif strlcat(buf2, "accept *:80\nreject 18.0.0.0/8:24\n", sizeof(buf2)); strlcat(buf2, "router-signature\n", sizeof(buf2)); @@ -229,15 +231,17 @@ test_dir_formats(void) cp = buf; rp2 = router_parse_entry_from_string((const char*)cp,NULL,1,0,NULL); test_assert(rp2); - test_streq(rp2->address, r2->address); + test_eq(rp2->addr, r2->addr); test_eq(rp2->or_port, r2->or_port); test_eq(rp2->dir_port, r2->dir_port); test_eq(rp2->bandwidthrate, r2->bandwidthrate); test_eq(rp2->bandwidthburst, r2->bandwidthburst); test_eq(rp2->bandwidthcapacity, r2->bandwidthcapacity); +#ifdef CURVE25519_ENABLED test_memeq(rp2->onion_curve25519_pkey->public_key, r2->onion_curve25519_pkey->public_key, CURVE25519_PUBKEY_LEN); +#endif test_assert(crypto_pk_cmp_keys(rp2->onion_pkey, pk2) == 0); test_assert(crypto_pk_cmp_keys(rp2->identity_pkey, pk1) == 0); @@ -275,6 +279,8 @@ test_dir_formats(void) routerinfo_free(r1); if (r2) routerinfo_free(r2); + if (rp2) + routerinfo_free(rp2); tor_free(buf); tor_free(pk1_str); @@ -937,7 +943,7 @@ gen_routerstatus_for_v3ns(int idx, time_t now) 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_v2_dir = rs->is_possible_guard = 1; + rs->is_valid = rs->is_possible_guard = 1; break; case 2: /* Generate the third routerstatus. */ @@ -952,7 +958,7 @@ gen_routerstatus_for_v3ns(int idx, time_t now) 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_flagged_running = rs->is_valid = rs->is_possible_guard = 1; break; case 3: @@ -1009,16 +1015,14 @@ vote_tweaks_for_v3ns(networkstatus_t *v, int voter, time_t now) /* Monkey around with the list a bit */ vrs = smartlist_get(v->routerstatus_list, 2); smartlist_del_keeporder(v->routerstatus_list, 2); - tor_free(vrs->version); - tor_free(vrs); + vote_routerstatus_free(vrs); vrs = smartlist_get(v->routerstatus_list, 0); vrs->status.is_fast = 1; if (voter == 3) { vrs = smartlist_get(v->routerstatus_list, 0); smartlist_del_keeporder(v->routerstatus_list, 0); - tor_free(vrs->version); - tor_free(vrs); + vote_routerstatus_free(vrs); vrs = smartlist_get(v->routerstatus_list, 0); memset(vrs->status.descriptor_digest, (int)'Z', DIGEST_LEN); test_assert(router_add_to_routerlist( @@ -1061,7 +1065,8 @@ test_vrs_for_v3ns(vote_routerstatus_t *vrs, int voter, time_t now) test_eq(rs->addr, 0x99008801); test_eq(rs->or_port, 443); test_eq(rs->dir_port, 8000); - test_eq(vrs->flags, U64_LITERAL(16)); // no flags except "running" + /* no flags except "running" (16) and "v2dir" (64) */ + tt_u64_op(vrs->flags, ==, U64_LITERAL(80)); } else if (tor_memeq(rs->identity_digest, "\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5" "\x5\x5\x5\x5", @@ -1086,10 +1091,11 @@ test_vrs_for_v3ns(vote_routerstatus_t *vrs, int voter, time_t now) test_assert(tor_addr_eq(&rs->ipv6_addr, &addr_ipv6)); test_eq(rs->ipv6_orport, 4711); if (voter == 1) { - test_eq(vrs->flags, U64_LITERAL(254)); // all flags except "authority." + /* all except "authority" (1) and "v2dir" (64) */ + tt_u64_op(vrs->flags, ==, U64_LITERAL(190)); } else { - /* 1023 - authority(1) - madeofcheese(16) - madeoftin(32) */ - test_eq(vrs->flags, U64_LITERAL(974)); + /* 1023 - authority(1) - madeofcheese(16) - madeoftin(32) - v2dir(256) */ + tt_u64_op(vrs->flags, ==, U64_LITERAL(718)); } } else if (tor_memeq(rs->identity_digest, "\x33\x33\x33\x33\x33\x33\x33\x33\x33\x33" @@ -1157,7 +1163,6 @@ test_routerstatus_for_v3ns(routerstatus_t *rs, time_t now) test_assert(!rs->is_stable); /* (If it wasn't running it wouldn't be here) */ test_assert(rs->is_flagged_running); - test_assert(!rs->is_v2_dir); test_assert(!rs->is_valid); test_assert(!rs->is_named); /* XXXX check version */ @@ -1184,7 +1189,6 @@ test_routerstatus_for_v3ns(routerstatus_t *rs, time_t now) test_assert(rs->is_possible_guard); test_assert(rs->is_stable); test_assert(rs->is_flagged_running); - test_assert(rs->is_v2_dir); test_assert(rs->is_valid); test_assert(!rs->is_named); /* XXXX check version */ @@ -1226,7 +1230,8 @@ 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; + char *v1_text=NULL, *v2_text=NULL, *v3_text=NULL, *consensus_text=NULL, + *cp=NULL; smartlist_t *votes = smartlist_new(); /* For generating the two other consensuses. */ @@ -1244,7 +1249,6 @@ test_a_networkstatus( /* Parse certificates and keys. */ cert1 = authority_cert_parse_from_string(AUTHORITY_CERT_1, NULL); test_assert(cert1); - test_assert(cert1->is_cross_certified); cert2 = authority_cert_parse_from_string(AUTHORITY_CERT_2, NULL); test_assert(cert2); cert3 = authority_cert_parse_from_string(AUTHORITY_CERT_3, NULL); @@ -1358,7 +1362,8 @@ test_a_networkstatus( vote->dist_seconds = 300; authority_cert_free(vote->cert); vote->cert = authority_cert_dup(cert2); - vote->net_params = smartlist_new(); + 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); @@ -1402,7 +1407,8 @@ test_a_networkstatus( vote->dist_seconds = 250; authority_cert_free(vote->cert); vote->cert = authority_cert_dup(cert3); - vote->net_params = smartlist_new(); + 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")); @@ -1645,6 +1651,7 @@ test_a_networkstatus( } done: + tor_free(cp); smartlist_free(votes); tor_free(v1_text); tor_free(v2_text); @@ -1768,7 +1775,7 @@ test_dir_random_weighted(void *testdata) inp[i].u64 = vals[i]; total += vals[i]; } - tt_int_op(total, ==, 45); + tt_u64_op(total, ==, 45); for (i=0; i<n; ++i) { choice = choose_array_element_by_weight(inp, 10); tt_int_op(choice, >=, 0); @@ -1886,7 +1893,7 @@ gen_routerstatus_for_umbw(int idx, time_t now) 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_v2_dir = rs->is_possible_guard = 1; + rs->is_valid = rs->is_possible_guard = 1; /* * This one has measured bandwidth above the clip cutoff, and * so shouldn't be clipped; we'll have to test that it isn't @@ -1909,7 +1916,7 @@ gen_routerstatus_for_umbw(int idx, time_t now) 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_flagged_running = rs->is_valid = rs->is_possible_guard = 1; /* * This one has unmeasured bandwidth above the clip cutoff, and @@ -1978,6 +1985,7 @@ vote_tweaks_for_umbw(networkstatus_t *v, int voter, time_t now) (void)now; test_assert(v->supported_methods); + SMARTLIST_FOREACH(v->supported_methods, char *, c, tor_free(c)); smartlist_clear(v->supported_methods); /* Method 17 is MIN_METHOD_TO_CLIP_UNMEASURED_BW_KB */ smartlist_split_string(v->supported_methods, @@ -2145,7 +2153,6 @@ test_routerstatus_for_umbw(routerstatus_t *rs, time_t now) test_assert(!rs->is_stable); /* (If it wasn't running it wouldn't be here) */ test_assert(rs->is_flagged_running); - test_assert(!rs->is_v2_dir); test_assert(!rs->is_valid); test_assert(!rs->is_named); /* This one should have measured bandwidth below the clip cutoff */ @@ -2176,7 +2183,6 @@ test_routerstatus_for_umbw(routerstatus_t *rs, time_t now) test_assert(rs->is_possible_guard); test_assert(rs->is_stable); test_assert(rs->is_flagged_running); - test_assert(rs->is_v2_dir); test_assert(rs->is_valid); test_assert(!rs->is_named); /* This one should have measured bandwidth above the clip cutoff */ @@ -2254,82 +2260,6 @@ test_dir_clip_unmeasured_bw_kb_alt(void) test_routerstatus_for_umbw); } -extern time_t time_of_process_start; /* from main.c */ - -static void -test_dir_v2_dir(void *arg) -{ - /* Runs in a forked process: acts like a v2 directory just enough to make and - * sign a v2 networkstatus opinion */ - - cached_dir_t *v2 = NULL; - or_options_t *options = get_options_mutable(); - crypto_pk_t *id_key = pk_generate(4); - (void) arg; - - options->ORPort_set = 1; /* So we believe we're a server. */ - options->DirPort_set = 1; - options->Address = tor_strdup("99.99.99.99"); - options->Nickname = tor_strdup("TestV2Auth"); - options->ContactInfo = tor_strdup("TestV2Auth <testv2auth@example.com>"); - { - /* Give it a DirPort */ - smartlist_t *ports = (smartlist_t *)get_configured_ports(); - port_cfg_t *port = tor_malloc_zero(sizeof(port_cfg_t)); - port->type = CONN_TYPE_DIR_LISTENER; - port->port = 9999; - smartlist_add(ports, port); - } - set_server_identity_key(id_key); - set_client_identity_key(id_key); - - /* Add a router. */ - { - was_router_added_t wra; - const char *msg = NULL; - routerinfo_t *r1 = tor_malloc_zero(sizeof(routerinfo_t)); - r1->address = tor_strdup("18.244.0.1"); - r1->addr = 0xc0a80001u; /* 192.168.0.1 */ - r1->cache_info.published_on = time(NULL)-60; - r1->or_port = 9000; - r1->dir_port = 9003; - tor_addr_parse(&r1->ipv6_addr, "1:2:3:4::"); - r1->ipv6_orport = 9999; - r1->onion_pkey = pk_generate(1); - r1->identity_pkey = pk_generate(2); - r1->bandwidthrate = 1000; - r1->bandwidthburst = 5000; - r1->bandwidthcapacity = 10000; - r1->exit_policy = NULL; - r1->nickname = tor_strdup("Magri"); - r1->platform = tor_strdup("Tor 0.2.7.7-gamma"); - r1->cache_info.routerlist_index = -1; - r1->cache_info.signed_descriptor_body = - router_dump_router_to_string(r1, r1->identity_pkey); - r1->cache_info.signed_descriptor_len = - strlen(r1->cache_info.signed_descriptor_body); - wra = router_add_to_routerlist(r1, &msg, 0, 0); - tt_int_op(wra, ==, ROUTER_ADDED_SUCCESSFULLY); - } - - /* Prevent call of rep_hist_note_router_unreachable(). */ - time_of_process_start = time(NULL); - - /* Make a directory so there's somewhere to store the thing */ -#ifdef _WIN32 - mkdir(get_fname("cached-status")); -#else - mkdir(get_fname("cached-status"), 0700); -#endif - - v2 = generate_v2_networkstatus_opinion(); - tt_assert(v2); - - done: - crypto_pk_free(id_key); - cached_dir_decref(v2); -} - static void test_dir_fmt_control_ns(void *arg) { @@ -2357,13 +2287,81 @@ test_dir_fmt_control_ns(void *arg) "r TetsuoMilk U3RhdGVseSwgcGx1bXAgQnVjayA " "TXVsbGlnYW4gY2FtZSB1cCBmcm8 2013-04-02 17:53:18 " "32.48.64.80 9001 9002\n" - "s Exit Fast Running\n" + "s Exit Fast Running V2Dir\n" "w Bandwidth=1000\n"); done: tor_free(s); } +static void +test_dir_http_handling(void *args) +{ + char *url = NULL; + (void)args; + + /* Parse http url tests: */ + /* Good headers */ + test_eq(parse_http_url("GET /tor/a/b/c.txt HTTP/1.1\r\n" + "Host: example.com\r\n" + "User-Agent: Mozilla/5.0 (Windows;" + " U; Windows NT 6.1; en-US; rv:1.9.1.5)\r\n", + &url), 0); + test_streq(url, "/tor/a/b/c.txt"); + tor_free(url); + + test_eq(parse_http_url("GET /tor/a/b/c.txt HTTP/1.0\r\n", &url), 0); + test_streq(url, "/tor/a/b/c.txt"); + tor_free(url); + + test_eq(parse_http_url("GET /tor/a/b/c.txt HTTP/1.600\r\n", &url), 0); + test_streq(url, "/tor/a/b/c.txt"); + tor_free(url); + + /* Should prepend '/tor/' to url if required */ + test_eq(parse_http_url("GET /a/b/c.txt HTTP/1.1\r\n" + "Host: example.com\r\n" + "User-Agent: Mozilla/5.0 (Windows;" + " U; Windows NT 6.1; en-US; rv:1.9.1.5)\r\n", + &url), 0); + test_streq(url, "/tor/a/b/c.txt"); + tor_free(url); + + /* Bad headers -- no HTTP/1.x*/ + test_eq(parse_http_url("GET /a/b/c.txt\r\n" + "Host: example.com\r\n" + "User-Agent: Mozilla/5.0 (Windows;" + " U; Windows NT 6.1; en-US; rv:1.9.1.5)\r\n", + &url), -1); + tt_assert(!url); + + /* Bad headers */ + test_eq(parse_http_url("GET /a/b/c.txt\r\n" + "Host: example.com\r\n" + "User-Agent: Mozilla/5.0 (Windows;" + " U; Windows NT 6.1; en-US; rv:1.9.1.5)\r\n", + &url), -1); + tt_assert(!url); + + test_eq(parse_http_url("GET /tor/a/b/c.txt", &url), -1); + tt_assert(!url); + + test_eq(parse_http_url("GET /tor/a/b/c.txt HTTP/1.1", &url), -1); + tt_assert(!url); + + test_eq(parse_http_url("GET /tor/a/b/c.txt HTTP/1.1x\r\n", &url), -1); + tt_assert(!url); + + test_eq(parse_http_url("GET /tor/a/b/c.txt HTTP/1.", &url), -1); + tt_assert(!url); + + test_eq(parse_http_url("GET /tor/a/b/c.txt HTTP/1.\r", &url), -1); + tt_assert(!url); + + done: + tor_free(url); +} + #define DIR_LEGACY(name) \ { #name, legacy_test_helper, TT_FORK, &legacy_setup, test_dir_ ## name } @@ -2384,8 +2382,8 @@ struct testcase_t dir_tests[] = { DIR(scale_bw, 0), DIR_LEGACY(clip_unmeasured_bw_kb), DIR_LEGACY(clip_unmeasured_bw_kb_alt), - DIR(v2_dir, TT_FORK), DIR(fmt_control_ns, 0), + DIR(http_handling, 0), END_OF_TESTCASES }; diff --git a/src/test/test_extorport.c b/src/test/test_extorport.c new file mode 100644 index 0000000000..93c8f77d5b --- /dev/null +++ b/src/test/test_extorport.c @@ -0,0 +1,607 @@ +/* Copyright (c) 2013, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#define CONNECTION_PRIVATE +#define EXT_ORPORT_PRIVATE +#define MAIN_PRIVATE +#include "or.h" +#include "buffers.h" +#include "connection.h" +#include "connection_or.h" +#include "config.h" +#include "control.h" +#include "ext_orport.h" +#include "main.h" +#include "test.h" + +/* Test connection_or_remove_from_ext_or_id_map and + * connection_or_set_ext_or_identifier */ +static void +test_ext_or_id_map(void *arg) +{ + or_connection_t *c1 = NULL, *c2 = NULL, *c3 = NULL; + char *idp = NULL, *idp2 = NULL; + (void)arg; + + /* pre-initialization */ + tt_ptr_op(NULL, ==, connection_or_get_by_ext_or_id("xxxxxxxxxxxxxxxxxxxx")); + + c1 = or_connection_new(CONN_TYPE_EXT_OR, AF_INET); + c2 = or_connection_new(CONN_TYPE_EXT_OR, AF_INET); + c3 = or_connection_new(CONN_TYPE_OR, AF_INET); + + tt_ptr_op(c1->ext_or_conn_id, !=, NULL); + tt_ptr_op(c2->ext_or_conn_id, !=, NULL); + tt_ptr_op(c3->ext_or_conn_id, ==, NULL); + + tt_ptr_op(c1, ==, connection_or_get_by_ext_or_id(c1->ext_or_conn_id)); + tt_ptr_op(c2, ==, connection_or_get_by_ext_or_id(c2->ext_or_conn_id)); + tt_ptr_op(NULL, ==, connection_or_get_by_ext_or_id("xxxxxxxxxxxxxxxxxxxx")); + + idp = tor_memdup(c2->ext_or_conn_id, EXT_OR_CONN_ID_LEN); + + /* Give c2 a new ID. */ + connection_or_set_ext_or_identifier(c2); + test_mem_op(idp, !=, c2->ext_or_conn_id, EXT_OR_CONN_ID_LEN); + idp2 = tor_memdup(c2->ext_or_conn_id, EXT_OR_CONN_ID_LEN); + tt_assert(!tor_digest_is_zero(idp2)); + + tt_ptr_op(NULL, ==, connection_or_get_by_ext_or_id(idp)); + tt_ptr_op(c2, ==, connection_or_get_by_ext_or_id(idp2)); + + /* Now remove it. */ + connection_or_remove_from_ext_or_id_map(c2); + tt_ptr_op(NULL, ==, connection_or_get_by_ext_or_id(idp)); + tt_ptr_op(NULL, ==, connection_or_get_by_ext_or_id(idp2)); + + done: + if (c1) + connection_free_(TO_CONN(c1)); + if (c2) + connection_free_(TO_CONN(c2)); + if (c3) + connection_free_(TO_CONN(c3)); + tor_free(idp); + tor_free(idp2); + connection_or_clear_ext_or_id_map(); +} + +/* Simple connection_write_to_buf_impl_ replacement that unconditionally + * writes to outbuf. */ +static void +connection_write_to_buf_impl_replacement(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); +} + +static char * +buf_get_contents(buf_t *buf, size_t *sz_out) +{ + char *out; + *sz_out = buf_datalen(buf); + if (*sz_out >= ULONG_MAX) + return NULL; /* C'mon, really? */ + out = tor_malloc(*sz_out + 1); + if (fetch_from_buf(out, (unsigned long)*sz_out, buf) != 0) { + tor_free(out); + return NULL; + } + out[*sz_out] = '\0'; /* Hopefully gratuitous. */ + return out; +} + +static void +test_ext_or_write_command(void *arg) +{ + or_connection_t *c1; + char *cp = NULL; + char *buf = NULL; + size_t sz; + + (void) arg; + MOCK(connection_write_to_buf_impl_, + connection_write_to_buf_impl_replacement); + + c1 = or_connection_new(CONN_TYPE_EXT_OR, AF_INET); + tt_assert(c1); + + /* Length too long */ + tt_int_op(connection_write_ext_or_command(TO_CONN(c1), 100, "X", 100000), + <, 0); + + /* Empty command */ + tt_int_op(connection_write_ext_or_command(TO_CONN(c1), 0x99, NULL, 0), + ==, 0); + cp = buf_get_contents(TO_CONN(c1)->outbuf, &sz); + tt_int_op(sz, ==, 4); + test_mem_op(cp, ==, "\x00\x99\x00\x00", 4); + tor_free(cp); + + /* Medium command. */ + tt_int_op(connection_write_ext_or_command(TO_CONN(c1), 0x99, + "Wai\0Hello", 9), ==, 0); + cp = buf_get_contents(TO_CONN(c1)->outbuf, &sz); + tt_int_op(sz, ==, 13); + test_mem_op(cp, ==, "\x00\x99\x00\x09Wai\x00Hello", 13); + tor_free(cp); + + /* Long command */ + buf = tor_malloc(65535); + memset(buf, 'x', 65535); + tt_int_op(connection_write_ext_or_command(TO_CONN(c1), 0xf00d, + buf, 65535), ==, 0); + cp = buf_get_contents(TO_CONN(c1)->outbuf, &sz); + tt_int_op(sz, ==, 65539); + test_mem_op(cp, ==, "\xf0\x0d\xff\xff", 4); + test_mem_op(cp+4, ==, buf, 65535); + tor_free(cp); + + done: + if (c1) + connection_free_(TO_CONN(c1)); + tor_free(cp); + tor_free(buf); + UNMOCK(connection_write_to_buf_impl_); +} + +static int +write_bytes_to_file_fail(const char *fname, const char *str, size_t len, + int bin) +{ + (void) fname; + (void) str; + (void) len; + (void) bin; + + return -1; +} + +static void +test_ext_or_init_auth(void *arg) +{ + or_options_t *options = get_options_mutable(); + const char *fn; + char *cp = NULL; + struct stat st; + char cookie0[32]; + (void)arg; + + /* Check default filename location */ + tor_free(options->DataDirectory); + options->DataDirectory = tor_strdup("foo"); + cp = get_ext_or_auth_cookie_file_name(); + tt_str_op(cp, ==, "foo"PATH_SEPARATOR"extended_orport_auth_cookie"); + tor_free(cp); + + /* Shouldn't be initialized already, or our tests will be a bit + * meaningless */ + ext_or_auth_cookie = tor_malloc_zero(32); + test_assert(tor_mem_is_zero((char*)ext_or_auth_cookie, 32)); + + /* Now make sure we use a temporary file */ + fn = get_fname("ext_cookie_file"); + options->ExtORPortCookieAuthFile = tor_strdup(fn); + cp = get_ext_or_auth_cookie_file_name(); + tt_str_op(cp, ==, fn); + tor_free(cp); + + /* Test the initialization function with a broken + write_bytes_to_file(). See if the problem is handled properly. */ + MOCK(write_bytes_to_file, write_bytes_to_file_fail); + tt_int_op(-1, ==, init_ext_or_cookie_authentication(1)); + tt_int_op(ext_or_auth_cookie_is_set, ==, 0); + UNMOCK(write_bytes_to_file); + + /* Now do the actual initialization. */ + tt_int_op(0, ==, init_ext_or_cookie_authentication(1)); + tt_int_op(ext_or_auth_cookie_is_set, ==, 1); + cp = read_file_to_str(fn, RFTS_BIN, &st); + tt_ptr_op(cp, !=, NULL); + tt_u64_op((uint64_t)st.st_size, ==, 64); + test_memeq(cp, "! Extended ORPort Auth Cookie !\x0a", 32); + test_memeq(cp+32, ext_or_auth_cookie, 32); + memcpy(cookie0, ext_or_auth_cookie, 32); + test_assert(!tor_mem_is_zero((char*)ext_or_auth_cookie, 32)); + + /* Operation should be idempotent. */ + tt_int_op(0, ==, init_ext_or_cookie_authentication(1)); + test_memeq(cookie0, ext_or_auth_cookie, 32); + + done: + tor_free(cp); + ext_orport_free_all(); +} + +static void +test_ext_or_cookie_auth(void *arg) +{ + char *reply=NULL, *reply2=NULL, *client_hash=NULL, *client_hash2=NULL; + size_t reply_len=0; + char hmac1[32], hmac2[32]; + + const char client_nonce[32] = + "Who is the third who walks alway"; + char server_hash_input[] = + "ExtORPort authentication server-to-client hash" + "Who is the third who walks alway" + "................................"; + char client_hash_input[] = + "ExtORPort authentication client-to-server hash" + "Who is the third who walks alway" + "................................"; + + (void)arg; + + tt_int_op(strlen(client_hash_input), ==, 46+32+32); + tt_int_op(strlen(server_hash_input), ==, 46+32+32); + + ext_or_auth_cookie = tor_malloc_zero(32); + memcpy(ext_or_auth_cookie, "s beside you? When I count, ther", 32); + ext_or_auth_cookie_is_set = 1; + + /* For this authentication, the client sends 32 random bytes (ClientNonce) + * The server replies with 32 byte ServerHash and 32 byte ServerNonce, + * where ServerHash is: + * HMAC-SHA256(CookieString, + * "ExtORPort authentication server-to-client hash" | ClientNonce | + * ServerNonce)" + * The client must reply with 32-byte ClientHash, which we compute as: + * ClientHash is computed as: + * HMAC-SHA256(CookieString, + * "ExtORPort authentication client-to-server hash" | ClientNonce | + * ServerNonce) + */ + + /* Wrong length */ + tt_int_op(-1, ==, + handle_client_auth_nonce(client_nonce, 33, &client_hash, &reply, + &reply_len)); + tt_int_op(-1, ==, + handle_client_auth_nonce(client_nonce, 31, &client_hash, &reply, + &reply_len)); + + /* Now let's try this for real! */ + tt_int_op(0, ==, + handle_client_auth_nonce(client_nonce, 32, &client_hash, &reply, + &reply_len)); + tt_int_op(reply_len, ==, 64); + tt_ptr_op(reply, !=, NULL); + tt_ptr_op(client_hash, !=, NULL); + /* Fill in the server nonce into the hash inputs... */ + memcpy(server_hash_input+46+32, reply+32, 32); + memcpy(client_hash_input+46+32, reply+32, 32); + /* Check the HMACs are correct... */ + crypto_hmac_sha256(hmac1, (char*)ext_or_auth_cookie, 32, server_hash_input, + 46+32+32); + crypto_hmac_sha256(hmac2, (char*)ext_or_auth_cookie, 32, client_hash_input, + 46+32+32); + test_memeq(hmac1, reply, 32); + test_memeq(hmac2, client_hash, 32); + + /* Now do it again and make sure that the results are *different* */ + tt_int_op(0, ==, + handle_client_auth_nonce(client_nonce, 32, &client_hash2, &reply2, + &reply_len)); + test_memneq(reply2, reply, reply_len); + test_memneq(client_hash2, client_hash, 32); + /* But that this one checks out too. */ + memcpy(server_hash_input+46+32, reply2+32, 32); + memcpy(client_hash_input+46+32, reply2+32, 32); + /* Check the HMACs are correct... */ + crypto_hmac_sha256(hmac1, (char*)ext_or_auth_cookie, 32, server_hash_input, + 46+32+32); + crypto_hmac_sha256(hmac2, (char*)ext_or_auth_cookie, 32, client_hash_input, + 46+32+32); + test_memeq(hmac1, reply2, 32); + test_memeq(hmac2, client_hash2, 32); + + done: + tor_free(reply); + tor_free(client_hash); + tor_free(reply2); + tor_free(client_hash2); +} + +static int +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; + } + memcpy(to, "te road There is always another ", 32); + return 0; +} + +static void +test_ext_or_cookie_auth_testvec(void *arg) +{ + char *reply=NULL, *client_hash=NULL; + size_t reply_len; + char *mem_op_hex_tmp=NULL; + + const char client_nonce[] = "But when I look ahead up the whi"; + (void)arg; + + ext_or_auth_cookie = tor_malloc_zero(32); + memcpy(ext_or_auth_cookie, "Gliding wrapt in a brown mantle," , 32); + ext_or_auth_cookie_is_set = 1; + + MOCK(crypto_rand, crypto_rand_return_tse_str); + + tt_int_op(0, ==, + handle_client_auth_nonce(client_nonce, 32, &client_hash, &reply, + &reply_len)); + tt_ptr_op(reply, !=, NULL ); + tt_uint_op(reply_len, ==, 64); + test_memeq(reply+32, "te road There is always another ", 32); + /* HMACSHA256("Gliding wrapt in a brown mantle," + * "ExtORPort authentication server-to-client hash" + * "But when I look ahead up the write road There is always another "); + */ + test_memeq_hex(reply, + "ec80ed6e546d3b36fdfc22fe1315416b" + "029f1ade7610d910878b62eeb7403821"); + /* HMACSHA256("Gliding wrapt in a brown mantle," + * "ExtORPort authentication client-to-server hash" + * "But when I look ahead up the write road There is always another "); + * (Both values computed using Python CLI.) + */ + test_memeq_hex(client_hash, + "ab391732dd2ed968cd40c087d1b1f25b" + "33b3cd77ff79bd80c2074bbf438119a2"); + + done: + UNMOCK(crypto_rand); + tor_free(reply); + tor_free(client_hash); + tor_free(mem_op_hex_tmp); +} + +static void +ignore_bootstrap_problem(const char *warn, int reason, + or_connection_t *conn) +{ + (void)warn; + (void)reason; + (void)conn; +} + +static int is_reading = 1; +static int handshake_start_called = 0; + +static void +note_read_stopped(connection_t *conn) +{ + (void)conn; + is_reading=0; +} +static void +note_read_started(connection_t *conn) +{ + (void)conn; + is_reading=1; +} +static int +handshake_start(or_connection_t *conn, int receiving) +{ + if (!conn || !receiving) + TT_FAIL(("Bad arguments to handshake_start")); + handshake_start_called = 1; + return 0; +} + +#define WRITE(s,n) \ + do { \ + write_to_buf((s), (n), TO_CONN(conn)->inbuf); \ + } while (0) +#define CONTAINS(s,n) \ + do { \ + tt_int_op((n), <=, sizeof(b)); \ + tt_int_op(buf_datalen(TO_CONN(conn)->outbuf), ==, (n)); \ + if ((n)) { \ + fetch_from_buf(b, (n), TO_CONN(conn)->outbuf); \ + test_memeq(b, (s), (n)); \ + } \ + } while (0) + +/* Helper: Do a successful Extended ORPort authentication handshake. */ +static void +do_ext_or_handshake(or_connection_t *conn) +{ + char b[256]; + + tt_int_op(0, ==, connection_ext_or_start_auth(conn)); + CONTAINS("\x01\x00", 2); + WRITE("\x01", 1); + WRITE("But when I look ahead up the whi", 32); + MOCK(crypto_rand, crypto_rand_return_tse_str); + tt_int_op(0, ==, connection_ext_or_process_inbuf(conn)); + UNMOCK(crypto_rand); + tt_int_op(TO_CONN(conn)->state, ==, EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_HASH); + CONTAINS("\xec\x80\xed\x6e\x54\x6d\x3b\x36\xfd\xfc\x22\xfe\x13\x15\x41\x6b" + "\x02\x9f\x1a\xde\x76\x10\xd9\x10\x87\x8b\x62\xee\xb7\x40\x38\x21" + "te road There is always another ", 64); + /* Send the right response this time. */ + WRITE("\xab\x39\x17\x32\xdd\x2e\xd9\x68\xcd\x40\xc0\x87\xd1\xb1\xf2\x5b" + "\x33\xb3\xcd\x77\xff\x79\xbd\x80\xc2\x07\x4b\xbf\x43\x81\x19\xa2", + 32); + tt_int_op(0, ==, connection_ext_or_process_inbuf(conn)); + CONTAINS("\x01", 1); + tt_assert(! TO_CONN(conn)->marked_for_close); + tt_int_op(TO_CONN(conn)->state, ==, EXT_OR_CONN_STATE_OPEN); + + done: ; +} + +static void +test_ext_or_handshake(void *arg) +{ + or_connection_t *conn=NULL; + char b[256]; + + (void) arg; + MOCK(connection_write_to_buf_impl_, + connection_write_to_buf_impl_replacement); + /* Use same authenticators as for test_ext_or_cookie_auth_testvec */ + ext_or_auth_cookie = tor_malloc_zero(32); + memcpy(ext_or_auth_cookie, "Gliding wrapt in a brown mantle," , 32); + ext_or_auth_cookie_is_set = 1; + + init_connection_lists(); + + conn = or_connection_new(CONN_TYPE_EXT_OR, AF_INET); + tt_int_op(0, ==, connection_ext_or_start_auth(conn)); + /* The server starts by telling us about the one supported authtype. */ + CONTAINS("\x01\x00", 2); + /* Say the client hasn't responded yet. */ + tt_int_op(0, ==, connection_ext_or_process_inbuf(conn)); + /* Let's say the client replies badly. */ + WRITE("\x99", 1); + tt_int_op(-1, ==, connection_ext_or_process_inbuf(conn)); + CONTAINS("", 0); + tt_assert(TO_CONN(conn)->marked_for_close); + close_closeable_connections(); + conn = NULL; + + /* Okay, try again. */ + conn = or_connection_new(CONN_TYPE_EXT_OR, AF_INET); + tt_int_op(0, ==, connection_ext_or_start_auth(conn)); + CONTAINS("\x01\x00", 2); + /* Let's say the client replies sensibly this time. "Yes, AUTHTYPE_COOKIE + * sounds delicious. Let's have some of that!" */ + WRITE("\x01", 1); + /* Let's say that the client also sends part of a nonce. */ + WRITE("But when I look ", 16); + tt_int_op(0, ==, connection_ext_or_process_inbuf(conn)); + CONTAINS("", 0); + tt_int_op(TO_CONN(conn)->state, ==, + EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_NONCE); + /* Pump it again. Nothing should happen. */ + tt_int_op(0, ==, connection_ext_or_process_inbuf(conn)); + /* send the rest of the nonce. */ + WRITE("ahead up the whi", 16); + MOCK(crypto_rand, crypto_rand_return_tse_str); + tt_int_op(0, ==, connection_ext_or_process_inbuf(conn)); + UNMOCK(crypto_rand); + /* We should get the right reply from the server. */ + CONTAINS("\xec\x80\xed\x6e\x54\x6d\x3b\x36\xfd\xfc\x22\xfe\x13\x15\x41\x6b" + "\x02\x9f\x1a\xde\x76\x10\xd9\x10\x87\x8b\x62\xee\xb7\x40\x38\x21" + "te road There is always another ", 64); + /* Send the wrong response. */ + WRITE("not with a bang but a whimper...", 32); + MOCK(control_event_bootstrap_problem, ignore_bootstrap_problem); + tt_int_op(-1, ==, connection_ext_or_process_inbuf(conn)); + CONTAINS("\x00", 1); + tt_assert(TO_CONN(conn)->marked_for_close); + /* XXXX Hold-open-until-flushed. */ + close_closeable_connections(); + conn = NULL; + UNMOCK(control_event_bootstrap_problem); + + MOCK(connection_start_reading, note_read_started); + MOCK(connection_stop_reading, note_read_stopped); + MOCK(connection_tls_start_handshake, handshake_start); + + /* Okay, this time let's succeed. */ + conn = or_connection_new(CONN_TYPE_EXT_OR, AF_INET); + do_ext_or_handshake(conn); + + /* Now let's run through some messages. */ + /* First let's send some junk and make sure it's ignored. */ + WRITE("\xff\xf0\x00\x03""ABC", 7); + tt_int_op(0, ==, connection_ext_or_process_inbuf(conn)); + CONTAINS("", 0); + /* Now let's send a USERADDR command. */ + WRITE("\x00\x01\x00\x0c""1.2.3.4:5678", 16); + tt_int_op(0, ==, connection_ext_or_process_inbuf(conn)); + tt_int_op(TO_CONN(conn)->port, ==, 5678); + tt_int_op(tor_addr_to_ipv4h(&TO_CONN(conn)->addr), ==, 0x01020304); + /* Now let's send a TRANSPORT command. */ + WRITE("\x00\x02\x00\x07""rfc1149", 11); + tt_int_op(0, ==, connection_ext_or_process_inbuf(conn)); + tt_ptr_op(NULL, !=, conn->ext_or_transport); + tt_str_op("rfc1149", ==, conn->ext_or_transport); + tt_int_op(is_reading,==,1); + tt_int_op(TO_CONN(conn)->state, ==, EXT_OR_CONN_STATE_OPEN); + /* DONE */ + WRITE("\x00\x00\x00\x00", 4); + tt_int_op(0, ==, connection_ext_or_process_inbuf(conn)); + tt_int_op(TO_CONN(conn)->state, ==, EXT_OR_CONN_STATE_FLUSHING); + tt_int_op(is_reading,==,0); + CONTAINS("\x10\x00\x00\x00", 4); + tt_int_op(handshake_start_called,==,0); + tt_int_op(0, ==, connection_ext_or_finished_flushing(conn)); + tt_int_op(is_reading,==,1); + tt_int_op(handshake_start_called,==,1); + tt_int_op(TO_CONN(conn)->type, ==, CONN_TYPE_OR); + tt_int_op(TO_CONN(conn)->state, ==, 0); + close_closeable_connections(); + conn = NULL; + + /* Okay, this time let's succeed the handshake but fail the USERADDR + command. */ + conn = or_connection_new(CONN_TYPE_EXT_OR, AF_INET); + do_ext_or_handshake(conn); + /* USERADDR command with an extra NUL byte */ + WRITE("\x00\x01\x00\x0d""1.2.3.4:5678\x00", 17); + MOCK(control_event_bootstrap_problem, ignore_bootstrap_problem); + tt_int_op(-1, ==, connection_ext_or_process_inbuf(conn)); + CONTAINS("", 0); + tt_assert(TO_CONN(conn)->marked_for_close); + close_closeable_connections(); + conn = NULL; + UNMOCK(control_event_bootstrap_problem); + + /* Now fail the TRANSPORT command. */ + conn = or_connection_new(CONN_TYPE_EXT_OR, AF_INET); + do_ext_or_handshake(conn); + /* TRANSPORT command with an extra NUL byte */ + WRITE("\x00\x02\x00\x08""rfc1149\x00", 12); + MOCK(control_event_bootstrap_problem, ignore_bootstrap_problem); + tt_int_op(-1, ==, connection_ext_or_process_inbuf(conn)); + CONTAINS("", 0); + tt_assert(TO_CONN(conn)->marked_for_close); + close_closeable_connections(); + conn = NULL; + UNMOCK(control_event_bootstrap_problem); + + /* Now fail the TRANSPORT command. */ + conn = or_connection_new(CONN_TYPE_EXT_OR, AF_INET); + do_ext_or_handshake(conn); + /* TRANSPORT command with transport name with symbols (not a + C-identifier) */ + WRITE("\x00\x02\x00\x07""rf*1149", 11); + MOCK(control_event_bootstrap_problem, ignore_bootstrap_problem); + tt_int_op(-1, ==, connection_ext_or_process_inbuf(conn)); + CONTAINS("", 0); + tt_assert(TO_CONN(conn)->marked_for_close); + close_closeable_connections(); + conn = NULL; + UNMOCK(control_event_bootstrap_problem); + + done: + UNMOCK(connection_write_to_buf_impl_); + UNMOCK(crypto_rand); + if (conn) + connection_free_(TO_CONN(conn)); +#undef CONTAINS +#undef WRITE +} + +struct testcase_t extorport_tests[] = { + { "id_map", test_ext_or_id_map, TT_FORK, NULL, NULL }, + { "write_command", test_ext_or_write_command, TT_FORK, NULL, NULL }, + { "init_auth", test_ext_or_init_auth, TT_FORK, NULL, NULL }, + { "cookie_auth", test_ext_or_cookie_auth, TT_FORK, NULL, NULL }, + { "cookie_auth_testvec", test_ext_or_cookie_auth_testvec, TT_FORK, + NULL, NULL }, + { "handshake", test_ext_or_handshake, TT_FORK, NULL, NULL }, + END_OF_TESTCASES +}; + diff --git a/src/test/test_hs.c b/src/test/test_hs.c new file mode 100644 index 0000000000..99ef7dd570 --- /dev/null +++ b/src/test/test_hs.c @@ -0,0 +1,129 @@ +/* Copyright (c) 2007-2013, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file test_hs.c + * \brief Unit tests for hidden service. + **/ + +#define CONTROL_PRIVATE +#include "or.h" +#include "test.h" +#include "control.h" + +/* mock ID digest and longname for node that's in nodelist */ +#define HSDIR_EXIST_ID "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" \ + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" +#define STR_HSDIR_EXIST_LONGNAME \ + "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=TestDir" +/* mock ID digest and longname for node that's not in nodelist */ +#define HSDIR_NONE_EXIST_ID "\xBB\xBB\xBB\xBB\xBB\xBB\xBB\xBB\xBB\xBB" \ + "\xBB\xBB\xBB\xBB\xBB\xBB\xBB\xBB\xBB\xBB" +#define STR_HSDIR_NONE_EXIST_LONGNAME \ + "$BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" + +/* Helper global variable for hidden service descriptor event test. + * It's used as a pointer to dynamically created message buffer in + * send_control_event_string_replacement function, which mocks + * send_control_event_string function. + * + * Always free it after use! */ +static char *received_msg = NULL; + +/** Mock function for send_control_event_string + */ +static void +send_control_event_string_replacement(uint16_t event, event_format_t which, + const char *msg) +{ + (void) event; + (void) which; + tor_free(received_msg); + received_msg = tor_strdup(msg); +} + +/** Mock function for node_describe_longname_by_id, it returns either + * STR_HSDIR_EXIST_LONGNAME or STR_HSDIR_NONE_EXIST_LONGNAME + */ +static const char * +node_describe_longname_by_id_replacement(const char *id_digest) +{ + if (!strcmp(id_digest, HSDIR_EXIST_ID)) { + return STR_HSDIR_EXIST_LONGNAME; + } else { + return STR_HSDIR_NONE_EXIST_LONGNAME; + } +} + +/** Make sure each hidden service descriptor async event generation + * + * function generates the message in expected format. + */ +static void +test_hs_desc_event(void *arg) +{ + #define STR_HS_ADDR "ajhb7kljbiru65qo" + #define STR_HS_ID "b3oeducbhjmbqmgw2i3jtz4fekkrinwj" + + rend_data_t rend_query; + const char *expected_msg; + + (void) arg; + MOCK(send_control_event_string, + send_control_event_string_replacement); + MOCK(node_describe_longname_by_id, + node_describe_longname_by_id_replacement); + + /* setup rend_query struct */ + strncpy(rend_query.onion_address, STR_HS_ADDR, + REND_SERVICE_ID_LEN_BASE32+1); + rend_query.auth_type = 0; + + /* test request event */ + control_event_hs_descriptor_requested(&rend_query, HSDIR_EXIST_ID, + STR_HS_ID); + expected_msg = "650 HS_DESC REQUESTED "STR_HS_ADDR" NO_AUTH "\ + STR_HSDIR_EXIST_LONGNAME" "STR_HS_ID"\r\n"; + test_assert(received_msg); + test_streq(received_msg, expected_msg); + tor_free(received_msg); + + /* test received event */ + rend_query.auth_type = 1; + control_event_hs_descriptor_received(&rend_query, HSDIR_EXIST_ID); + expected_msg = "650 HS_DESC RECEIVED "STR_HS_ADDR" BASIC_AUTH "\ + STR_HSDIR_EXIST_LONGNAME"\r\n"; + test_assert(received_msg); + test_streq(received_msg, expected_msg); + tor_free(received_msg); + + /* test failed event */ + rend_query.auth_type = 2; + control_event_hs_descriptor_failed(&rend_query, HSDIR_NONE_EXIST_ID); + expected_msg = "650 HS_DESC FAILED "STR_HS_ADDR" STEALTH_AUTH "\ + STR_HSDIR_NONE_EXIST_LONGNAME"\r\n"; + test_assert(received_msg); + test_streq(received_msg, expected_msg); + tor_free(received_msg); + + /* test invalid auth type */ + rend_query.auth_type = 999; + control_event_hs_descriptor_failed(&rend_query, HSDIR_EXIST_ID); + expected_msg = "650 HS_DESC FAILED "STR_HS_ADDR" UNKNOWN "\ + STR_HSDIR_EXIST_LONGNAME"\r\n"; + test_assert(received_msg); + test_streq(received_msg, expected_msg); + tor_free(received_msg); + + done: + UNMOCK(send_control_event_string); + UNMOCK(node_describe_longname_by_id); + tor_free(received_msg); +} + +struct testcase_t hs_tests[] = { + { "hs_desc_event", test_hs_desc_event, TT_FORK, + NULL, NULL }, + END_OF_TESTCASES +}; + diff --git a/src/test/test_logging.c b/src/test/test_logging.c new file mode 100644 index 0000000000..7e558f83b1 --- /dev/null +++ b/src/test/test_logging.c @@ -0,0 +1,135 @@ +/* Copyright (c) 2013, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "orconfig.h" +#include "or.h" +#include "torlog.h" +#include "test.h" + +static void +dummy_cb_fn(int severity, uint32_t domain, const char *msg) +{ + (void)severity; (void)domain; (void)msg; +} + +static void +test_get_sigsafe_err_fds(void *arg) +{ + const int *fds; + int n; + log_severity_list_t include_bug, no_bug, no_bug2; + (void) arg; + init_logging(); + + n = tor_log_get_sigsafe_err_fds(&fds); + tt_int_op(n, ==, 1); + tt_int_op(fds[0], ==, STDERR_FILENO); + + set_log_severity_config(LOG_WARN, LOG_ERR, &include_bug); + set_log_severity_config(LOG_WARN, LOG_ERR, &no_bug); + no_bug.masks[0] &= ~(LD_BUG|LD_GENERAL); + set_log_severity_config(LOG_INFO, LOG_NOTICE, &no_bug2); + + /* Add some logs; make sure the output is as expected. */ + mark_logs_temp(); + add_stream_log(&include_bug, "dummy-1", 3); + add_stream_log(&no_bug, "dummy-2", 4); + add_stream_log(&no_bug2, "dummy-3", 5); + add_callback_log(&include_bug, dummy_cb_fn); + close_temp_logs(); + tor_log_update_sigsafe_err_fds(); + + n = tor_log_get_sigsafe_err_fds(&fds); + tt_int_op(n, ==, 2); + tt_int_op(fds[0], ==, STDERR_FILENO); + tt_int_op(fds[1], ==, 3); + + /* Allow STDOUT to replace STDERR. */ + add_stream_log(&include_bug, "dummy-4", STDOUT_FILENO); + tor_log_update_sigsafe_err_fds(); + n = tor_log_get_sigsafe_err_fds(&fds); + tt_int_op(n, ==, 2); + tt_int_op(fds[0], ==, 3); + tt_int_op(fds[1], ==, STDOUT_FILENO); + + /* But don't allow it to replace explicit STDERR. */ + add_stream_log(&include_bug, "dummy-5", STDERR_FILENO); + tor_log_update_sigsafe_err_fds(); + n = tor_log_get_sigsafe_err_fds(&fds); + tt_int_op(n, ==, 3); + tt_int_op(fds[0], ==, STDERR_FILENO); + tt_int_op(fds[1], ==, STDOUT_FILENO); + tt_int_op(fds[2], ==, 3); + + /* Don't overflow the array. */ + { + int i; + for (i=5; i<20; ++i) { + add_stream_log(&include_bug, "x-dummy", i); + } + } + tor_log_update_sigsafe_err_fds(); + n = tor_log_get_sigsafe_err_fds(&fds); + tt_int_op(n, ==, 8); + + done: + ; +} + +static void +test_sigsafe_err(void *arg) +{ + const char *fn=get_fname("sigsafe_err_log"); + char *content=NULL; + log_severity_list_t include_bug; + smartlist_t *lines = smartlist_new(); + (void)arg; + + set_log_severity_config(LOG_WARN, LOG_ERR, &include_bug); + + init_logging(); + mark_logs_temp(); + add_file_log(&include_bug, fn); + tor_log_update_sigsafe_err_fds(); + close_temp_logs(); + + close(STDERR_FILENO); + log_err(LD_BUG, "Say, this isn't too cool."); + tor_log_err_sigsafe("Minimal.\n", NULL); + + set_log_time_granularity(100*1000); + tor_log_err_sigsafe("Testing any ", + "attempt to manually log ", + "from a signal.\n", + NULL); + mark_logs_temp(); + close_temp_logs(); + close(STDERR_FILENO); + content = read_file_to_str(fn, 0, NULL); + + tt_assert(content != NULL); + tor_split_lines(lines, content, (int)strlen(content)); + tt_int_op(smartlist_len(lines), >=, 5); + + if (strstr(smartlist_get(lines, 0), "opening new log file")) + smartlist_del_keeporder(lines, 0); + tt_assert(strstr(smartlist_get(lines, 0), "Say, this isn't too cool")); + /* Next line is blank. */ + tt_assert(!strcmpstart(smartlist_get(lines, 1), "==============")); + tt_assert(!strcmpstart(smartlist_get(lines, 2), "Minimal.")); + /* Next line is blank. */ + tt_assert(!strcmpstart(smartlist_get(lines, 3), "==============")); + tt_str_op(smartlist_get(lines, 4), ==, + "Testing any attempt to manually log from a signal."); + + done: + tor_free(content); + smartlist_free(lines); +} + +struct testcase_t logging_tests[] = { + { "sigsafe_err_fds", test_get_sigsafe_err_fds, TT_FORK, NULL, NULL }, + { "sigsafe_err", test_sigsafe_err, TT_FORK, NULL, NULL }, + END_OF_TESTCASES +}; + diff --git a/src/test/test_microdesc.c b/src/test/test_microdesc.c index 53a03a48ad..78f4823b87 100644 --- a/src/test/test_microdesc.c +++ b/src/test/test_microdesc.c @@ -5,7 +5,10 @@ #include "or.h" #include "config.h" +#include "dirvote.h" #include "microdesc.h" +#include "routerlist.h" +#include "routerparse.h" #include "test.h" @@ -261,6 +264,7 @@ test_md_cache_broken(void *data) options = get_options_mutable(); tt_assert(options); + tor_free(options->DataDirectory); options->DataDirectory = tor_strdup(get_fname("md_datadir_test2")); #ifdef _WIN32 @@ -284,9 +288,113 @@ test_md_cache_broken(void *data) microdesc_free_all(); } +/* Generated by chutney. */ +static const char test_ri[] = + "router test005r 127.0.0.1 5005 0 7005\n" + "platform Tor 0.2.5.4-alpha-dev on Linux\n" + "protocols Link 1 2 Circuit 1\n" + "published 2014-05-06 22:57:55\n" + "fingerprint 09DE 3BA2 48C2 1C3F 3760 6CD3 8460 43A6 D5EC F59E\n" + "uptime 0\n" + "bandwidth 1073741824 1073741824 0\n" + "extra-info-digest 361F9428F9FA4DD854C03DDBCC159D0D9FA996C9\n" + "onion-key\n" + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBANBJz8Vldl12aFeSMPLiA4nOetLDN0oxU8bB1SDhO7Uu2zdWYVYAF5J0\n" + "st7WvrVy/jA9v/fsezNAPskBanecHRSkdMTpkcgRPMHE7CTGEwIy1Yp1X4bPgDlC\n" + "VCnbs5Pcts5HnWEYNK7qHDAUn+IlmjOO+pTUY8uyq+GQVz6H9wFlAgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + "signing-key\n" + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBANbGUC4802Ke6C3nOVxN0U0HhIRrs32cQFEL4v+UUMJPgjbistHBvOax\n" + "CWVR/sMXM2kKJeGThJ9ZUs2p9dDG4WHPUXgkMqzTTEeeFa7pQKU0brgbmLaJq0Pi\n" + "mxmqC5RkTHa5bQvq6QlSFprAEoovV27cWqBM9jVdV9hyc//6kwPzAgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + "hidden-service-dir\n" + "ntor-onion-key Gg73xH7+kTfT6bi1uNVx9gwQdQas9pROIfmc4NpAdC4=\n" + "reject *:25\n" + "reject *:119\n" + "reject *:135-139\n" + "reject *:445\n" + "reject *:563\n" + "reject *:1214\n" + "reject *:4661-4666\n" + "reject *:6346-6429\n" + "reject *:6699\n" + "reject *:6881-6999\n" + "accept *:*\n" + "router-signature\n" + "-----BEGIN SIGNATURE-----\n" + "ImzX5PF2vRCrG1YzGToyjoxYhgh1vtHEDjmP+tIS/iil1DSnHZNpHSuHp0L1jE9S\n" + "yZyrtKaqpBE/aecAM3j4CWCn/ipnAAQkHcyRLin1bYvqBtRzyopVCRlUhF+uWrLq\n" + "t0xkIE39ss/EwmQr7iIgkdVH4oRIMsjYnFFJBG26nYY=\n" + "-----END SIGNATURE-----\n"; + +static const char test_md_8[] = + "onion-key\n" + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBANBJz8Vldl12aFeSMPLiA4nOetLDN0oxU8bB1SDhO7Uu2zdWYVYAF5J0\n" + "st7WvrVy/jA9v/fsezNAPskBanecHRSkdMTpkcgRPMHE7CTGEwIy1Yp1X4bPgDlC\n" + "VCnbs5Pcts5HnWEYNK7qHDAUn+IlmjOO+pTUY8uyq+GQVz6H9wFlAgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + "p reject 25,119,135-139,445,563,1214,4661-4666,6346-6429,6699,6881-6999\n"; + +static const char test_md_16[] = + "onion-key\n" + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBANBJz8Vldl12aFeSMPLiA4nOetLDN0oxU8bB1SDhO7Uu2zdWYVYAF5J0\n" + "st7WvrVy/jA9v/fsezNAPskBanecHRSkdMTpkcgRPMHE7CTGEwIy1Yp1X4bPgDlC\n" + "VCnbs5Pcts5HnWEYNK7qHDAUn+IlmjOO+pTUY8uyq+GQVz6H9wFlAgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + "ntor-onion-key Gg73xH7+kTfT6bi1uNVx9gwQdQas9pROIfmc4NpAdC4=\n" + "p reject 25,119,135-139,445,563,1214,4661-4666,6346-6429,6699,6881-6999\n"; + +static const char test_md_18[] = + "onion-key\n" + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBANBJz8Vldl12aFeSMPLiA4nOetLDN0oxU8bB1SDhO7Uu2zdWYVYAF5J0\n" + "st7WvrVy/jA9v/fsezNAPskBanecHRSkdMTpkcgRPMHE7CTGEwIy1Yp1X4bPgDlC\n" + "VCnbs5Pcts5HnWEYNK7qHDAUn+IlmjOO+pTUY8uyq+GQVz6H9wFlAgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + "ntor-onion-key Gg73xH7+kTfT6bi1uNVx9gwQdQas9pROIfmc4NpAdC4=\n" + "p reject 25,119,135-139,445,563,1214,4661-4666,6346-6429,6699,6881-6999\n" + "id rsa1024 Cd47okjCHD83YGzThGBDptXs9Z4\n"; + +static void +test_md_generate(void *arg) +{ + routerinfo_t *ri; + microdesc_t *md = NULL; + (void)arg; + + ri = router_parse_entry_from_string(test_ri, NULL, 0, 0, NULL); + tt_assert(ri); + md = dirvote_create_microdescriptor(ri, 8); + tt_str_op(md->body, ==, test_md_8); + + /* XXXX test family lines. */ + /* XXXX test method 14 for A lines. */ + /* XXXX test method 15 for P6 lines. */ + + microdesc_free(md); + md = NULL; + md = dirvote_create_microdescriptor(ri, 16); + tt_str_op(md->body, ==, test_md_16); + + microdesc_free(md); + md = NULL; + md = dirvote_create_microdescriptor(ri, 18); + tt_str_op(md->body, ==, test_md_18); + + done: + microdesc_free(md); + routerinfo_free(ri); +} + struct testcase_t microdesc_tests[] = { { "cache", test_md_cache, TT_FORK, NULL, NULL }, { "broken_cache", test_md_cache_broken, TT_FORK, NULL, NULL }, + { "generate", test_md_generate, 0, NULL, NULL }, END_OF_TESTCASES }; diff --git a/src/test/test_nodelist.c b/src/test/test_nodelist.c new file mode 100644 index 0000000000..600e6a89d4 --- /dev/null +++ b/src/test/test_nodelist.c @@ -0,0 +1,71 @@ +/* Copyright (c) 2007-2013, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file test_nodelist.c + * \brief Unit tests for nodelist related functions. + **/ + +#include "or.h" +#include "nodelist.h" +#include "test.h" + +/** Tese the case when node_get_by_id() returns NULL, + * node_get_verbose_nickname_by_id should return the base 16 encoding + * of the id. + */ +static void +test_nodelist_node_get_verbose_nickname_by_id_null_node(void *arg) +{ + char vname[MAX_VERBOSE_NICKNAME_LEN+1]; + const char ID[] = "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA"; + (void) arg; + + /* make sure node_get_by_id returns NULL */ + test_assert(!node_get_by_id(ID)); + node_get_verbose_nickname_by_id(ID, vname); + test_streq(vname, "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"); + done: + return; +} + +/** For routers without named flag, get_verbose_nickname should return + * "Fingerprint~Nickname" + */ +static void +test_nodelist_node_get_verbose_nickname_not_named(void *arg) +{ + node_t mock_node; + routerstatus_t mock_rs; + + char vname[MAX_VERBOSE_NICKNAME_LEN+1]; + + (void) arg; + + memset(&mock_node, 0, sizeof(node_t)); + memset(&mock_rs, 0, sizeof(routerstatus_t)); + + /* verbose nickname should use ~ instead of = for unnamed routers */ + strlcpy(mock_rs.nickname, "TestOR", sizeof(mock_rs.nickname)); + mock_node.rs = &mock_rs; + memcpy(mock_node.identity, + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA", + DIGEST_LEN); + node_get_verbose_nickname(&mock_node, vname); + test_streq(vname, "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~TestOR"); + + 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), + END_OF_TESTCASES +}; + diff --git a/src/test/test_oom.c b/src/test/test_oom.c new file mode 100644 index 0000000000..2726056b80 --- /dev/null +++ b/src/test/test_oom.c @@ -0,0 +1,381 @@ +/* Copyright (c) 2014, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/* Unit tests for OOM handling logic */ + +#define RELAY_PRIVATE +#define BUFFERS_PRIVATE +#define CIRCUITLIST_PRIVATE +#define CONNECTION_PRIVATE +#include "or.h" +#include "buffers.h" +#include "circuitlist.h" +#include "compat_libevent.h" +#include "connection.h" +#include "config.h" +#ifdef ENABLE_MEMPOOLS +#include "mempool.h" +#endif +#include "relay.h" +#include "test.h" + +/* small replacement mock for circuit_mark_for_close_ to avoid doing all + * the other bookkeeping that comes with marking circuits. */ +static void +circuit_mark_for_close_dummy_(circuit_t *circ, int reason, int line, + const char *file) +{ + (void) reason; + if (circ->marked_for_close) { + TT_FAIL(("Circuit already marked for close at %s:%d, but we are marking " + "it again at %s:%d", + circ->marked_for_close_file, (int)circ->marked_for_close, + file, line)); + } + + circ->marked_for_close = line; + circ->marked_for_close_file = file; +} + +static circuit_t * +dummy_or_circuit_new(int n_p_cells, int n_n_cells) +{ + or_circuit_t *circ = or_circuit_new(0, NULL); + int i; + cell_t cell; + + for (i=0; i < n_p_cells; ++i) { + crypto_rand((void*)&cell, sizeof(cell)); + cell_queue_append_packed_copy(TO_CIRCUIT(circ), &circ->p_chan_cells, + 0, &cell, 1, 0); + } + + for (i=0; i < n_n_cells; ++i) { + crypto_rand((void*)&cell, sizeof(cell)); + cell_queue_append_packed_copy(TO_CIRCUIT(circ), + &TO_CIRCUIT(circ)->n_chan_cells, + 1, &cell, 1, 0); + } + + TO_CIRCUIT(circ)->purpose = CIRCUIT_PURPOSE_OR; + return TO_CIRCUIT(circ); +} + +static circuit_t * +dummy_origin_circuit_new(int n_cells) +{ + origin_circuit_t *circ = origin_circuit_new(); + int i; + cell_t cell; + + for (i=0; i < n_cells; ++i) { + crypto_rand((void*)&cell, sizeof(cell)); + cell_queue_append_packed_copy(TO_CIRCUIT(circ), + &TO_CIRCUIT(circ)->n_chan_cells, + 1, &cell, 1, 0); + } + + TO_CIRCUIT(circ)->purpose = CIRCUIT_PURPOSE_C_GENERAL; + return TO_CIRCUIT(circ); +} + +static void +add_bytes_to_buf(generic_buffer_t *buf, size_t n_bytes) +{ + char b[3000]; + + while (n_bytes) { + size_t this_add = n_bytes > sizeof(b) ? sizeof(b) : n_bytes; + crypto_rand(b, this_add); + generic_buffer_add(buf, b, this_add); + n_bytes -= this_add; + } +} + +static edge_connection_t * +dummy_edge_conn_new(circuit_t *circ, + int type, size_t in_bytes, size_t out_bytes) +{ + edge_connection_t *conn; + generic_buffer_t *inbuf, *outbuf; + + if (type == CONN_TYPE_EXIT) + conn = edge_connection_new(type, AF_INET); + else + conn = ENTRY_TO_EDGE_CONN(entry_connection_new(type, AF_INET)); + +#ifdef USE_BUFFEREVENTS + inbuf = bufferevent_get_input(TO_CONN(conn)->bufev); + outbuf = bufferevent_get_output(TO_CONN(conn)->bufev); +#else + inbuf = TO_CONN(conn)->inbuf; + outbuf = TO_CONN(conn)->outbuf; +#endif + + /* We add these bytes directly to the buffers, to avoid all the + * edge connection read/write machinery. */ + add_bytes_to_buf(inbuf, in_bytes); + add_bytes_to_buf(outbuf, out_bytes); + + conn->on_circuit = circ; + if (type == CONN_TYPE_EXIT) { + or_circuit_t *oc = TO_OR_CIRCUIT(circ); + conn->next_stream = oc->n_streams; + oc->n_streams = conn; + } else { + origin_circuit_t *oc = TO_ORIGIN_CIRCUIT(circ); + conn->next_stream = oc->p_streams; + oc->p_streams = conn; + } + + return conn; +} + +/** Run unit tests for buffers.c */ +static void +test_oom_circbuf(void *arg) +{ + or_options_t *options = get_options_mutable(); + circuit_t *c1 = NULL, *c2 = NULL, *c3 = NULL, *c4 = NULL; + struct timeval tv = { 1389631048, 0 }; + + (void) arg; + + MOCK(circuit_mark_for_close_, circuit_mark_for_close_dummy_); + +#ifdef ENABLE_MEMPOOLS + init_cell_pool(); +#endif /* ENABLE_MEMPOOLS */ + + /* Far too low for real life. */ + options->MaxMemInQueues = 256*packed_cell_mem_cost(); + options->CellStatistics = 0; + + tt_int_op(cell_queues_check_size(), ==, 0); /* We don't start out OOM. */ + tt_int_op(cell_queues_get_total_allocation(), ==, 0); + tt_int_op(buf_get_total_allocation(), ==, 0); + + /* Now we're going to fake up some circuits and get them added to the global + circuit list. */ + tv.tv_usec = 0; + tor_gettimeofday_cache_set(&tv); + c1 = dummy_origin_circuit_new(30); + tv.tv_usec = 10*1000; + tor_gettimeofday_cache_set(&tv); + c2 = dummy_or_circuit_new(20, 20); + +#ifdef ENABLE_MEMPOOLS + tt_int_op(packed_cell_mem_cost(), ==, + sizeof(packed_cell_t) + MP_POOL_ITEM_OVERHEAD); +#else + tt_int_op(packed_cell_mem_cost(), ==, + sizeof(packed_cell_t)); +#endif /* ENABLE_MEMPOOLS */ + tt_int_op(cell_queues_get_total_allocation(), ==, + packed_cell_mem_cost() * 70); + tt_int_op(cell_queues_check_size(), ==, 0); /* We are still not OOM */ + + tv.tv_usec = 20*1000; + tor_gettimeofday_cache_set(&tv); + c3 = dummy_or_circuit_new(100, 85); + tt_int_op(cell_queues_check_size(), ==, 0); /* We are still not OOM */ + tt_int_op(cell_queues_get_total_allocation(), ==, + packed_cell_mem_cost() * 255); + + tv.tv_usec = 30*1000; + tor_gettimeofday_cache_set(&tv); + /* Adding this cell will trigger our OOM handler. */ + c4 = dummy_or_circuit_new(2, 0); + + tt_int_op(cell_queues_get_total_allocation(), ==, + packed_cell_mem_cost() * 257); + + tt_int_op(cell_queues_check_size(), ==, 1); /* We are now OOM */ + + tt_assert(c1->marked_for_close); + tt_assert(! c2->marked_for_close); + tt_assert(! c3->marked_for_close); + tt_assert(! c4->marked_for_close); + + tt_int_op(cell_queues_get_total_allocation(), ==, + packed_cell_mem_cost() * (257 - 30)); + + circuit_free(c1); + tv.tv_usec = 0; + tor_gettimeofday_cache_set(&tv); /* go back in time */ + c1 = dummy_or_circuit_new(90, 0); + + tv.tv_usec = 40*1000; /* go back to the future */ + tor_gettimeofday_cache_set(&tv); + + tt_int_op(cell_queues_check_size(), ==, 1); /* We are now OOM */ + + tt_assert(c1->marked_for_close); + tt_assert(! c2->marked_for_close); + tt_assert(! c3->marked_for_close); + tt_assert(! c4->marked_for_close); + + tt_int_op(cell_queues_get_total_allocation(), ==, + packed_cell_mem_cost() * (257 - 30)); + + done: + circuit_free(c1); + circuit_free(c2); + circuit_free(c3); + circuit_free(c4); + + UNMOCK(circuit_mark_for_close_); +} + +/** Run unit tests for buffers.c */ +static void +test_oom_streambuf(void *arg) +{ + or_options_t *options = get_options_mutable(); + circuit_t *c1 = NULL, *c2 = NULL, *c3 = NULL, *c4 = NULL, *c5 = NULL; + struct timeval tv = { 1389641159, 0 }; + uint32_t tvms; + int i; + smartlist_t *edgeconns = smartlist_new(); + + (void) arg; + + MOCK(circuit_mark_for_close_, circuit_mark_for_close_dummy_); + +#ifdef ENABLE_MEMPOOLS + init_cell_pool(); +#endif /* ENABLE_MEMPOOLS */ + + /* Far too low for real life. */ + options->MaxMemInQueues = 81*packed_cell_mem_cost() + 4096 * 34; + options->CellStatistics = 0; + + tt_int_op(cell_queues_check_size(), ==, 0); /* We don't start out OOM. */ + tt_int_op(cell_queues_get_total_allocation(), ==, 0); + tt_int_op(buf_get_total_allocation(), ==, 0); + + /* Start all circuits with a bit of data queued in cells */ + tv.tv_usec = 500*1000; /* go halfway into the second. */ + tor_gettimeofday_cache_set(&tv); + c1 = dummy_or_circuit_new(10,10); + tv.tv_usec = 510*1000; + tor_gettimeofday_cache_set(&tv); + c2 = dummy_origin_circuit_new(20); + tv.tv_usec = 520*1000; + tor_gettimeofday_cache_set(&tv); + c3 = dummy_or_circuit_new(20,20); + tv.tv_usec = 530*1000; + tor_gettimeofday_cache_set(&tv); + c4 = dummy_or_circuit_new(0,0); + tt_int_op(cell_queues_get_total_allocation(), ==, + packed_cell_mem_cost() * 80); + + tv.tv_usec = 600*1000; + tor_gettimeofday_cache_set(&tv); + + /* Add some connections to c1...c4. */ + for (i = 0; i < 4; ++i) { + edge_connection_t *ec; + /* link it to a circuit */ + tv.tv_usec += 10*1000; + tor_gettimeofday_cache_set(&tv); + ec = dummy_edge_conn_new(c1, CONN_TYPE_EXIT, 1000, 1000); + tt_assert(ec); + smartlist_add(edgeconns, ec); + tv.tv_usec += 10*1000; + tor_gettimeofday_cache_set(&tv); + ec = dummy_edge_conn_new(c2, CONN_TYPE_AP, 1000, 1000); + tt_assert(ec); + smartlist_add(edgeconns, ec); + tv.tv_usec += 10*1000; + tor_gettimeofday_cache_set(&tv); + ec = dummy_edge_conn_new(c4, CONN_TYPE_EXIT, 1000, 1000); /* Yes, 4 twice*/ + tt_assert(ec); + smartlist_add(edgeconns, ec); + tv.tv_usec += 10*1000; + tor_gettimeofday_cache_set(&tv); + ec = dummy_edge_conn_new(c4, CONN_TYPE_EXIT, 1000, 1000); + smartlist_add(edgeconns, ec); + tt_assert(ec); + } + + tv.tv_sec += 1; + tv.tv_usec = 0; + tvms = (uint32_t) tv_to_msec(&tv); + + tt_int_op(circuit_max_queued_cell_age(c1, tvms), ==, 500); + tt_int_op(circuit_max_queued_cell_age(c2, tvms), ==, 490); + tt_int_op(circuit_max_queued_cell_age(c3, tvms), ==, 480); + tt_int_op(circuit_max_queued_cell_age(c4, tvms), ==, 0); + + tt_int_op(circuit_max_queued_data_age(c1, tvms), ==, 390); + tt_int_op(circuit_max_queued_data_age(c2, tvms), ==, 380); + tt_int_op(circuit_max_queued_data_age(c3, tvms), ==, 0); + tt_int_op(circuit_max_queued_data_age(c4, tvms), ==, 370); + + tt_int_op(circuit_max_queued_item_age(c1, tvms), ==, 500); + tt_int_op(circuit_max_queued_item_age(c2, tvms), ==, 490); + tt_int_op(circuit_max_queued_item_age(c3, tvms), ==, 480); + tt_int_op(circuit_max_queued_item_age(c4, tvms), ==, 370); + + tt_int_op(cell_queues_get_total_allocation(), ==, + packed_cell_mem_cost() * 80); + tt_int_op(buf_get_total_allocation(), ==, 4096*16*2); + + /* Now give c4 a very old buffer of modest size */ + { + edge_connection_t *ec; + tv.tv_sec -= 1; + tv.tv_usec = 0; + tor_gettimeofday_cache_set(&tv); + ec = dummy_edge_conn_new(c4, CONN_TYPE_EXIT, 1000, 1000); + tt_assert(ec); + smartlist_add(edgeconns, ec); + } + tt_int_op(buf_get_total_allocation(), ==, 4096*17*2); + tt_int_op(circuit_max_queued_item_age(c4, tvms), ==, 1000); + + tt_int_op(cell_queues_check_size(), ==, 0); + + /* And run over the limit. */ + tv.tv_usec = 800*1000; + tor_gettimeofday_cache_set(&tv); + c5 = dummy_or_circuit_new(0,5); + + tt_int_op(cell_queues_get_total_allocation(), ==, + packed_cell_mem_cost() * 85); + tt_int_op(buf_get_total_allocation(), ==, 4096*17*2); + + tt_int_op(cell_queues_check_size(), ==, 1); /* We are now OOM */ + + /* C4 should have died. */ + tt_assert(! c1->marked_for_close); + tt_assert(! c2->marked_for_close); + tt_assert(! c3->marked_for_close); + tt_assert(c4->marked_for_close); + tt_assert(! c5->marked_for_close); + + tt_int_op(cell_queues_get_total_allocation(), ==, + packed_cell_mem_cost() * 85); + tt_int_op(buf_get_total_allocation(), ==, 4096*8*2); + + done: + circuit_free(c1); + circuit_free(c2); + circuit_free(c3); + circuit_free(c4); + circuit_free(c5); + + SMARTLIST_FOREACH(edgeconns, edge_connection_t *, ec, + connection_free_(TO_CONN(ec))); + smartlist_free(edgeconns); + + UNMOCK(circuit_mark_for_close_); +} + +struct testcase_t oom_tests[] = { + { "circbuf", test_oom_circbuf, TT_FORK, NULL, NULL }, + { "streambuf", test_oom_streambuf, TT_FORK, NULL, NULL }, + END_OF_TESTCASES +}; + diff --git a/src/test/test_options.c b/src/test/test_options.c new file mode 100644 index 0000000000..737f658e2c --- /dev/null +++ b/src/test/test_options.c @@ -0,0 +1,170 @@ +/* Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2013, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#define CONFIG_PRIVATE +#include "or.h" +#include "confparse.h" +#include "config.h" +#include "test.h" + +typedef struct { + int severity; + uint32_t domain; + char *msg; +} logmsg_t; + +static smartlist_t *messages = NULL; + +static void +log_cback(int severity, uint32_t domain, const char *msg) +{ + logmsg_t *x = tor_malloc(sizeof(*x)); + x->severity = severity; + x->domain = domain; + x->msg = tor_strdup(msg); + if (!messages) + messages = smartlist_new(); + smartlist_add(messages, x); +} + +static void +setup_log_callback(void) +{ + log_severity_list_t lst; + memset(&lst, 0, sizeof(lst)); + lst.masks[LOG_ERR - LOG_ERR] = ~0; + lst.masks[LOG_WARN - LOG_ERR] = ~0; + lst.masks[LOG_NOTICE - LOG_ERR] = ~0; + add_callback_log(&lst, log_cback); +} + +static char * +dump_logs(void) +{ + smartlist_t *msgs; + char *out; + if (! messages) + return tor_strdup(""); + msgs = smartlist_new(); + SMARTLIST_FOREACH_BEGIN(messages, logmsg_t *, x) { + smartlist_add_asprintf(msgs, "[%s] %s", + log_level_to_string(x->severity), x->msg); + } SMARTLIST_FOREACH_END(x); + out = smartlist_join_strings(msgs, "", 0, NULL); + SMARTLIST_FOREACH(msgs, char *, cp, tor_free(cp)); + smartlist_free(msgs); + return out; +} + +static void +clear_log_messages(void) +{ + if (!messages) + return; + SMARTLIST_FOREACH(messages, logmsg_t *, m, + { tor_free(m->msg); tor_free(m); }); + smartlist_free(messages); + messages = NULL; +} + +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 *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(); + + r = config_get_lines(configuration, &cl, 1); + tt_int_op(r, ==, 0); + + r = config_assign(&options_format, opt, cl, 0, 0, &msg); + tt_int_op(r, ==, 0); + + r = options_validate(NULL, opt, dflt, 0, &msg); + if (expect_errmsg && !msg) { + TT_DIE(("Expected error message <%s> from <%s>, but got none.", + expect_errmsg, configuration)); + } else if (expect_errmsg && !strstr(msg, expect_errmsg)) { + TT_DIE(("Expected error message <%s> from <%s>, but got <%s>.", + expect_errmsg, configuration, msg)); + } else if (!expect_errmsg && msg) { + TT_DIE(("Expected no error message from <%s> but got <%s>.", + configuration, msg)); + } + tt_int_op((r == 0), ==, (msg == NULL)); + + if (expect_log) { + int found = 0; + if (messages) { + SMARTLIST_FOREACH_BEGIN(messages, logmsg_t *, m) { + if (m->severity == expect_log_severity && + strstr(m->msg, expect_log)) { + found = 1; + break; + } + } SMARTLIST_FOREACH_END(m); + } + if (!found) { + tor_free(msg); + msg = dump_logs(); + TT_DIE(("Expected log message [%s] %s from <%s>, but got <%s>.", + log_level_to_string(expect_log_severity), expect_log, + configuration, msg)); + } + } + + done: + config_free_lines(cl); + or_options_free(opt); + or_options_free(dflt); + tor_free(msg); + clear_log_messages(); +} + +#define WANT_ERR(config, msg) \ + test_options_validate_impl((config), (msg), 0, NULL) +#define WANT_LOG(config, severity, msg) \ + test_options_validate_impl((config), NULL, (severity), (msg)) +#define WANT_ERR_LOG(config, msg, severity, logmsg) \ + test_options_validate_impl((config), (msg), (severity), (logmsg)) +#define OK(config) \ + test_options_validate_impl((config), NULL, 0, NULL) + +static void +test_options_validate(void *arg) +{ + (void)arg; + setup_log_callback(); + + WANT_ERR("ExtORPort 500000", "Invalid ExtORPort"); + + WANT_ERR_LOG("ServerTransportOptions trebuchet", + "ServerTransportOptions did not parse", + LOG_WARN, "Too few arguments"); + OK("ServerTransportOptions trebuchet sling=snappy"); + OK("ServerTransportOptions trebuchet sling="); + WANT_ERR_LOG("ServerTransportOptions trebuchet slingsnappy", + "ServerTransportOptions did not parse", + LOG_WARN, "\"slingsnappy\" is not a k=v"); + + clear_log_messages(); + return; +} + +struct testcase_t options_tests[] = { + { "validate", test_options_validate, TT_FORK, NULL, NULL }, + END_OF_TESTCASES +}; + diff --git a/src/test/test_policy.c b/src/test/test_policy.c new file mode 100644 index 0000000000..4cdcd034bb --- /dev/null +++ b/src/test/test_policy.c @@ -0,0 +1,437 @@ +/* Copyright (c) 2013, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "or.h" +#include "router.h" +#include "routerparse.h" +#include "policies.h" +#include "test.h" + +/* Helper: assert that short_policy parses and writes back out as itself, + or as <b>expected</b> if that's provided. */ +static void +test_short_policy_parse(const char *input, + const char *expected) +{ + short_policy_t *short_policy = NULL; + char *out = NULL; + + if (expected == NULL) + expected = input; + + short_policy = parse_short_policy(input); + tt_assert(short_policy); + out = write_short_policy(short_policy); + tt_str_op(out, ==, expected); + + done: + tor_free(out); + short_policy_free(short_policy); +} + +/** Helper: Parse the exit policy string in <b>policy_str</b>, and make sure + * that policies_summarize() produces the string <b>expected_summary</b> from + * it. */ +static void +test_policy_summary_helper(const char *policy_str, + const char *expected_summary) +{ + config_line_t line; + smartlist_t *policy = smartlist_new(); + char *summary = NULL; + char *summary_after = NULL; + int r; + short_policy_t *short_policy = NULL; + + line.key = (char*)"foo"; + line.value = (char *)policy_str; + line.next = NULL; + + r = policies_parse_exit_policy(&line, &policy, 1, 0, 0, 1); + test_eq(r, 0); + summary = policy_summarize(policy, AF_INET); + + test_assert(summary != NULL); + test_streq(summary, expected_summary); + + short_policy = parse_short_policy(summary); + tt_assert(short_policy); + summary_after = write_short_policy(short_policy); + test_streq(summary, summary_after); + + done: + tor_free(summary_after); + tor_free(summary); + if (policy) + addr_policy_list_free(policy); + short_policy_free(short_policy); +} + +/** Run unit tests for generating summary lines of exit policies */ +static void +test_policies_general(void *arg) +{ + int i; + smartlist_t *policy = NULL, *policy2 = NULL, *policy3 = NULL, + *policy4 = NULL, *policy5 = NULL, *policy6 = NULL, + *policy7 = NULL; + addr_policy_t *p; + tor_addr_t tar; + config_line_t line; + smartlist_t *sm = NULL; + char *policy_str = NULL; + short_policy_t *short_parsed = NULL; + (void)arg; + + policy = smartlist_new(); + + p = router_parse_addr_policy_item_from_string("reject 192.168.0.0/16:*",-1); + test_assert(p != NULL); + test_eq(ADDR_POLICY_REJECT, p->policy_type); + tor_addr_from_ipv4h(&tar, 0xc0a80000u); + test_eq(0, tor_addr_compare(&p->addr, &tar, CMP_EXACT)); + test_eq(16, p->maskbits); + test_eq(1, p->prt_min); + test_eq(65535, p->prt_max); + + smartlist_add(policy, p); + + tor_addr_from_ipv4h(&tar, 0x01020304u); + test_assert(ADDR_POLICY_ACCEPTED == + compare_tor_addr_to_addr_policy(&tar, 2, policy)); + tor_addr_make_unspec(&tar); + test_assert(ADDR_POLICY_PROBABLY_ACCEPTED == + compare_tor_addr_to_addr_policy(&tar, 2, policy)); + tor_addr_from_ipv4h(&tar, 0xc0a80102); + test_assert(ADDR_POLICY_REJECTED == + compare_tor_addr_to_addr_policy(&tar, 2, policy)); + + test_assert(0 == policies_parse_exit_policy(NULL, &policy2, 1, 1, 0, 1)); + test_assert(policy2); + + policy3 = smartlist_new(); + p = router_parse_addr_policy_item_from_string("reject *:*",-1); + test_assert(p != NULL); + smartlist_add(policy3, p); + p = router_parse_addr_policy_item_from_string("accept *:*",-1); + test_assert(p != NULL); + smartlist_add(policy3, p); + + policy4 = smartlist_new(); + p = router_parse_addr_policy_item_from_string("accept *:443",-1); + test_assert(p != NULL); + smartlist_add(policy4, p); + p = router_parse_addr_policy_item_from_string("accept *:443",-1); + test_assert(p != NULL); + smartlist_add(policy4, p); + + policy5 = smartlist_new(); + p = router_parse_addr_policy_item_from_string("reject 0.0.0.0/8:*",-1); + test_assert(p != NULL); + smartlist_add(policy5, p); + p = router_parse_addr_policy_item_from_string("reject 169.254.0.0/16:*",-1); + test_assert(p != NULL); + smartlist_add(policy5, p); + p = router_parse_addr_policy_item_from_string("reject 127.0.0.0/8:*",-1); + test_assert(p != NULL); + smartlist_add(policy5, p); + p = router_parse_addr_policy_item_from_string("reject 192.168.0.0/16:*",-1); + test_assert(p != NULL); + smartlist_add(policy5, p); + p = router_parse_addr_policy_item_from_string("reject 10.0.0.0/8:*",-1); + test_assert(p != NULL); + smartlist_add(policy5, p); + p = router_parse_addr_policy_item_from_string("reject 172.16.0.0/12:*",-1); + test_assert(p != NULL); + smartlist_add(policy5, p); + p = router_parse_addr_policy_item_from_string("reject 80.190.250.90:*",-1); + test_assert(p != NULL); + smartlist_add(policy5, p); + p = router_parse_addr_policy_item_from_string("reject *:1-65534",-1); + test_assert(p != NULL); + smartlist_add(policy5, p); + p = router_parse_addr_policy_item_from_string("reject *:65535",-1); + test_assert(p != NULL); + smartlist_add(policy5, p); + p = router_parse_addr_policy_item_from_string("accept *:1-65535",-1); + test_assert(p != NULL); + smartlist_add(policy5, p); + + policy6 = smartlist_new(); + p = router_parse_addr_policy_item_from_string("accept 43.3.0.0/9:*",-1); + test_assert(p != NULL); + smartlist_add(policy6, p); + + policy7 = smartlist_new(); + p = router_parse_addr_policy_item_from_string("accept 0.0.0.0/8:*",-1); + test_assert(p != NULL); + smartlist_add(policy7, p); + + test_assert(!exit_policy_is_general_exit(policy)); + test_assert(exit_policy_is_general_exit(policy2)); + test_assert(!exit_policy_is_general_exit(NULL)); + test_assert(!exit_policy_is_general_exit(policy3)); + test_assert(!exit_policy_is_general_exit(policy4)); + test_assert(!exit_policy_is_general_exit(policy5)); + test_assert(!exit_policy_is_general_exit(policy6)); + test_assert(!exit_policy_is_general_exit(policy7)); + + test_assert(cmp_addr_policies(policy, policy2)); + test_assert(cmp_addr_policies(policy, NULL)); + test_assert(!cmp_addr_policies(policy2, policy2)); + test_assert(!cmp_addr_policies(NULL, NULL)); + + test_assert(!policy_is_reject_star(policy2, AF_INET)); + test_assert(policy_is_reject_star(policy, AF_INET)); + test_assert(policy_is_reject_star(NULL, AF_INET)); + + addr_policy_list_free(policy); + policy = NULL; + + /* make sure compacting logic works. */ + policy = NULL; + line.key = (char*)"foo"; + line.value = (char*)"accept *:80,reject private:*,reject *:*"; + line.next = NULL; + test_assert(0 == policies_parse_exit_policy(&line, &policy, 1, 0, 0, 1)); + test_assert(policy); + //test_streq(policy->string, "accept *:80"); + //test_streq(policy->next->string, "reject *:*"); + test_eq(smartlist_len(policy), 4); + + /* test policy summaries */ + /* check if we properly ignore private IP addresses */ + test_policy_summary_helper("reject 192.168.0.0/16:*," + "reject 0.0.0.0/8:*," + "reject 10.0.0.0/8:*," + "accept *:10-30," + "accept *:90," + "reject *:*", + "accept 10-30,90"); + /* check all accept policies, and proper counting of rejects */ + test_policy_summary_helper("reject 11.0.0.0/9:80," + "reject 12.0.0.0/9:80," + "reject 13.0.0.0/9:80," + "reject 14.0.0.0/9:80," + "accept *:*", "accept 1-65535"); + test_policy_summary_helper("reject 11.0.0.0/9:80," + "reject 12.0.0.0/9:80," + "reject 13.0.0.0/9:80," + "reject 14.0.0.0/9:80," + "reject 15.0.0.0:81," + "accept *:*", "accept 1-65535"); + test_policy_summary_helper("reject 11.0.0.0/9:80," + "reject 12.0.0.0/9:80," + "reject 13.0.0.0/9:80," + "reject 14.0.0.0/9:80," + "reject 15.0.0.0:80," + "accept *:*", + "reject 80"); + /* no exits */ + test_policy_summary_helper("accept 11.0.0.0/9:80," + "reject *:*", + "reject 1-65535"); + /* port merging */ + test_policy_summary_helper("accept *:80," + "accept *:81," + "accept *:100-110," + "accept *:111," + "reject *:*", + "accept 80-81,100-111"); + /* border ports */ + test_policy_summary_helper("accept *:1," + "accept *:3," + "accept *:65535," + "reject *:*", + "accept 1,3,65535"); + /* holes */ + test_policy_summary_helper("accept *:1," + "accept *:3," + "accept *:5," + "accept *:7," + "reject *:*", + "accept 1,3,5,7"); + test_policy_summary_helper("reject *:1," + "reject *:3," + "reject *:5," + "reject *:7," + "accept *:*", + "reject 1,3,5,7"); + + /* Short policies with unrecognized formats should get accepted. */ + test_short_policy_parse("accept fred,2,3-5", "accept 2,3-5"); + test_short_policy_parse("accept 2,fred,3", "accept 2,3"); + test_short_policy_parse("accept 2,fred,3,bob", "accept 2,3"); + test_short_policy_parse("accept 2,-3,500-600", "accept 2,500-600"); + /* Short policies with nil entries are accepted too. */ + test_short_policy_parse("accept 1,,3", "accept 1,3"); + test_short_policy_parse("accept 100-200,,", "accept 100-200"); + test_short_policy_parse("reject ,1-10,,,,30-40", "reject 1-10,30-40"); + + /* Try parsing various broken short policies */ +#define TT_BAD_SHORT_POLICY(s) \ + do { \ + tt_ptr_op(NULL, ==, (short_parsed = parse_short_policy((s)))); \ + } while (0) + TT_BAD_SHORT_POLICY("accept 200-199"); + TT_BAD_SHORT_POLICY(""); + TT_BAD_SHORT_POLICY("rejekt 1,2,3"); + TT_BAD_SHORT_POLICY("reject "); + TT_BAD_SHORT_POLICY("reject"); + TT_BAD_SHORT_POLICY("rej"); + TT_BAD_SHORT_POLICY("accept 2,3,100000"); + TT_BAD_SHORT_POLICY("accept 2,3x,4"); + TT_BAD_SHORT_POLICY("accept 2,3x,4"); + TT_BAD_SHORT_POLICY("accept 2-"); + TT_BAD_SHORT_POLICY("accept 2-x"); + TT_BAD_SHORT_POLICY("accept 1-,3"); + TT_BAD_SHORT_POLICY("accept 1-,3"); + + /* Test a too-long policy. */ + { + int i; + char *policy = NULL; + smartlist_t *chunks = smartlist_new(); + smartlist_add(chunks, tor_strdup("accept ")); + for (i=1; i<10000; ++i) + smartlist_add_asprintf(chunks, "%d,", i); + smartlist_add(chunks, tor_strdup("20000")); + policy = smartlist_join_strings(chunks, "", 0, NULL); + SMARTLIST_FOREACH(chunks, char *, ch, tor_free(ch)); + smartlist_free(chunks); + short_parsed = parse_short_policy(policy);/* shouldn't be accepted */ + tor_free(policy); + tt_ptr_op(NULL, ==, short_parsed); + } + + /* truncation ports */ + sm = smartlist_new(); + for (i=1; i<2000; i+=2) { + char buf[POLICY_BUF_LEN]; + tor_snprintf(buf, sizeof(buf), "reject *:%d", i); + smartlist_add(sm, tor_strdup(buf)); + } + smartlist_add(sm, tor_strdup("accept *:*")); + policy_str = smartlist_join_strings(sm, ",", 0, NULL); + test_policy_summary_helper( policy_str, + "accept 2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44," + "46,48,50,52,54,56,58,60,62,64,66,68,70,72,74,76,78,80,82,84,86,88,90," + "92,94,96,98,100,102,104,106,108,110,112,114,116,118,120,122,124,126,128," + "130,132,134,136,138,140,142,144,146,148,150,152,154,156,158,160,162,164," + "166,168,170,172,174,176,178,180,182,184,186,188,190,192,194,196,198,200," + "202,204,206,208,210,212,214,216,218,220,222,224,226,228,230,232,234,236," + "238,240,242,244,246,248,250,252,254,256,258,260,262,264,266,268,270,272," + "274,276,278,280,282,284,286,288,290,292,294,296,298,300,302,304,306,308," + "310,312,314,316,318,320,322,324,326,328,330,332,334,336,338,340,342,344," + "346,348,350,352,354,356,358,360,362,364,366,368,370,372,374,376,378,380," + "382,384,386,388,390,392,394,396,398,400,402,404,406,408,410,412,414,416," + "418,420,422,424,426,428,430,432,434,436,438,440,442,444,446,448,450,452," + "454,456,458,460,462,464,466,468,470,472,474,476,478,480,482,484,486,488," + "490,492,494,496,498,500,502,504,506,508,510,512,514,516,518,520,522"); + + done: + addr_policy_list_free(policy); + addr_policy_list_free(policy2); + addr_policy_list_free(policy3); + addr_policy_list_free(policy4); + addr_policy_list_free(policy5); + addr_policy_list_free(policy6); + addr_policy_list_free(policy7); + tor_free(policy_str); + if (sm) { + SMARTLIST_FOREACH(sm, char *, s, tor_free(s)); + smartlist_free(sm); + } + short_policy_free(short_parsed); +} + +static void +test_dump_exit_policy_to_string(void *arg) +{ + char *ep; + addr_policy_t *policy_entry; + + routerinfo_t *ri = tor_malloc_zero(sizeof(routerinfo_t)); + + (void)arg; + + ri->policy_is_reject_star = 1; + ri->exit_policy = NULL; // expecting "reject *:*" + ep = router_dump_exit_policy_to_string(ri,1,1); + + test_streq("reject *:*",ep); + + tor_free(ep); + + ri->exit_policy = smartlist_new(); + ri->policy_is_reject_star = 0; + + policy_entry = router_parse_addr_policy_item_from_string("accept *:*",-1); + + smartlist_add(ri->exit_policy,policy_entry); + + ep = router_dump_exit_policy_to_string(ri,1,1); + + test_streq("accept *:*",ep); + + tor_free(ep); + + policy_entry = router_parse_addr_policy_item_from_string("reject *:25",-1); + + smartlist_add(ri->exit_policy,policy_entry); + + ep = router_dump_exit_policy_to_string(ri,1,1); + + test_streq("accept *:*\nreject *:25",ep); + + tor_free(ep); + + policy_entry = + router_parse_addr_policy_item_from_string("reject 8.8.8.8:*",-1); + + smartlist_add(ri->exit_policy,policy_entry); + + ep = router_dump_exit_policy_to_string(ri,1,1); + + test_streq("accept *:*\nreject *:25\nreject 8.8.8.8:*",ep); + tor_free(ep); + + policy_entry = + router_parse_addr_policy_item_from_string("reject6 [FC00::]/7:*",-1); + + smartlist_add(ri->exit_policy,policy_entry); + + ep = router_dump_exit_policy_to_string(ri,1,1); + + test_streq("accept *:*\nreject *:25\nreject 8.8.8.8:*\n" + "reject6 [fc00::]/7:*",ep); + tor_free(ep); + + policy_entry = + router_parse_addr_policy_item_from_string("accept6 [c000::]/3:*",-1); + + smartlist_add(ri->exit_policy,policy_entry); + + ep = router_dump_exit_policy_to_string(ri,1,1); + + test_streq("accept *:*\nreject *:25\nreject 8.8.8.8:*\n" + "reject6 [fc00::]/7:*\naccept6 [c000::]/3:*",ep); + + done: + + if (ri->exit_policy) { + SMARTLIST_FOREACH(ri->exit_policy, addr_policy_t *, + entry, addr_policy_free(entry)); + smartlist_free(ri->exit_policy); + } + tor_free(ri); + tor_free(ep); +} + +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 }, + END_OF_TESTCASES +}; + diff --git a/src/test/test_pt.c b/src/test/test_pt.c index 80707f4379..f71627df1e 100644 --- a/src/test/test_pt.c +++ b/src/test/test_pt.c @@ -5,9 +5,17 @@ #include "orconfig.h" #define PT_PRIVATE +#define UTIL_PRIVATE +#define STATEFILE_PRIVATE +#define CONTROL_PRIVATE #include "or.h" +#include "config.h" +#include "confparse.h" +#include "control.h" #include "transports.h" #include "circuitbuild.h" +#include "util.h" +#include "statefile.h" #include "test.h" static void @@ -22,71 +30,163 @@ static void test_pt_parsing(void) { char line[200]; + transport_t *transport = NULL; + tor_addr_t test_addr; managed_proxy_t *mp = tor_malloc(sizeof(managed_proxy_t)); mp->conf_state = PT_PROTO_INFANT; mp->transports = smartlist_new(); /* incomplete cmethod */ - strcpy(line,"CMETHOD trebuchet"); + strlcpy(line,"CMETHOD trebuchet",sizeof(line)); test_assert(parse_cmethod_line(line, mp) < 0); reset_mp(mp); /* wrong proxy type */ - strcpy(line,"CMETHOD trebuchet dog 127.0.0.1:1999"); + strlcpy(line,"CMETHOD trebuchet dog 127.0.0.1:1999",sizeof(line)); test_assert(parse_cmethod_line(line, mp) < 0); reset_mp(mp); /* wrong addrport */ - strcpy(line,"CMETHOD trebuchet socks4 abcd"); + strlcpy(line,"CMETHOD trebuchet socks4 abcd",sizeof(line)); test_assert(parse_cmethod_line(line, mp) < 0); reset_mp(mp); /* correct line */ - strcpy(line,"CMETHOD trebuchet socks5 127.0.0.1:1999"); + strlcpy(line,"CMETHOD trebuchet socks5 127.0.0.1:1999",sizeof(line)); test_assert(parse_cmethod_line(line, mp) == 0); - test_assert(smartlist_len(mp->transports)); + test_assert(smartlist_len(mp->transports) == 1); + transport = smartlist_get(mp->transports, 0); + /* test registered address of transport */ + tor_addr_parse(&test_addr, "127.0.0.1"); + test_assert(tor_addr_eq(&test_addr, &transport->addr)); + /* test registered port of transport */ + test_assert(transport->port == 1999); + /* test registered SOCKS version of transport */ + test_assert(transport->socks_version == PROXY_SOCKS5); + /* test registered name of transport */ + test_streq(transport->name, "trebuchet"); reset_mp(mp); /* incomplete smethod */ - strcpy(line,"SMETHOD trebuchet"); + strlcpy(line,"SMETHOD trebuchet",sizeof(line)); test_assert(parse_smethod_line(line, mp) < 0); reset_mp(mp); /* wrong addr type */ - strcpy(line,"SMETHOD trebuchet abcd"); + strlcpy(line,"SMETHOD trebuchet abcd",sizeof(line)); test_assert(parse_smethod_line(line, mp) < 0); reset_mp(mp); /* cowwect */ - strcpy(line,"SMETHOD trebuchy 127.0.0.1:1999"); + strlcpy(line,"SMETHOD trebuchy 127.0.0.2:2999",sizeof(line)); test_assert(parse_smethod_line(line, mp) == 0); + test_assert(smartlist_len(mp->transports) == 1); + transport = smartlist_get(mp->transports, 0); + /* test registered address of transport */ + tor_addr_parse(&test_addr, "127.0.0.2"); + test_assert(tor_addr_eq(&test_addr, &transport->addr)); + /* test registered port of transport */ + test_assert(transport->port == 2999); + /* test registered name of transport */ + test_streq(transport->name, "trebuchy"); reset_mp(mp); + /* Include some arguments. Good ones. */ + strlcpy(line,"SMETHOD trebuchet 127.0.0.1:9999 " + "ARGS:counterweight=3,sling=snappy", + sizeof(line)); + test_assert(parse_smethod_line(line, mp) == 0); + tt_int_op(1, ==, smartlist_len(mp->transports)); + { + const transport_t *transport = smartlist_get(mp->transports, 0); + tt_assert(transport); + tt_str_op(transport->name, ==, "trebuchet"); + tt_int_op(transport->port, ==, 9999); + tt_str_op(fmt_addr(&transport->addr), ==, "127.0.0.1"); + tt_str_op(transport->extra_info_args, ==, + "counterweight=3,sling=snappy"); + } + reset_mp(mp); + /* unsupported version */ - strcpy(line,"VERSION 666"); + strlcpy(line,"VERSION 666",sizeof(line)); test_assert(parse_version(line, mp) < 0); /* incomplete VERSION */ - strcpy(line,"VERSION "); + strlcpy(line,"VERSION ",sizeof(line)); test_assert(parse_version(line, mp) < 0); /* correct VERSION */ - strcpy(line,"VERSION 1"); + strlcpy(line,"VERSION 1",sizeof(line)); test_assert(parse_version(line, mp) == 0); done: + reset_mp(mp); + smartlist_free(mp->transports); tor_free(mp); } static void +test_pt_get_transport_options(void *arg) +{ + char **execve_args; + smartlist_t *transport_list = smartlist_new(); + managed_proxy_t *mp; + or_options_t *options = get_options_mutable(); + char *opt_str = NULL; + config_line_t *cl = NULL; + (void)arg; + + execve_args = tor_malloc(sizeof(char*)*2); + execve_args[0] = tor_strdup("cheeseshop"); + execve_args[1] = NULL; + + mp = managed_proxy_create(transport_list, execve_args, 1); + tt_ptr_op(mp, !=, NULL); + opt_str = get_transport_options_for_server_proxy(mp); + tt_ptr_op(opt_str, ==, NULL); + + smartlist_add(mp->transports_to_launch, tor_strdup("gruyere")); + smartlist_add(mp->transports_to_launch, tor_strdup("roquefort")); + smartlist_add(mp->transports_to_launch, tor_strdup("stnectaire")); + + tt_assert(options); + + cl = tor_malloc_zero(sizeof(config_line_t)); + cl->value = tor_strdup("gruyere melty=10 hardness=se;ven"); + options->ServerTransportOptions = cl; + + cl = tor_malloc_zero(sizeof(config_line_t)); + cl->value = tor_strdup("stnectaire melty=4 hardness=three"); + cl->next = options->ServerTransportOptions; + options->ServerTransportOptions = cl; + + cl = tor_malloc_zero(sizeof(config_line_t)); + cl->value = tor_strdup("pepperjack melty=12 hardness=five"); + cl->next = options->ServerTransportOptions; + options->ServerTransportOptions = cl; + + opt_str = get_transport_options_for_server_proxy(mp); + tt_str_op(opt_str, ==, + "gruyere:melty=10;gruyere:hardness=se\\;ven;" + "stnectaire:melty=4;stnectaire:hardness=three"); + + done: + tor_free(opt_str); + config_free_lines(cl); + managed_proxy_destroy(mp, 0); + smartlist_free(transport_list); +} + +static void test_pt_protocol(void) { char line[200]; @@ -99,36 +199,254 @@ test_pt_protocol(void) /* various wrong protocol runs: */ - strcpy(line,"VERSION 1"); + strlcpy(line,"VERSION 1",sizeof(line)); handle_proxy_line(line, mp); test_assert(mp->conf_state == PT_PROTO_ACCEPTING_METHODS); - strcpy(line,"VERSION 1"); + strlcpy(line,"VERSION 1",sizeof(line)); handle_proxy_line(line, mp); test_assert(mp->conf_state == PT_PROTO_BROKEN); reset_mp(mp); - strcpy(line,"CMETHOD trebuchet socks5 127.0.0.1:1999"); + strlcpy(line,"CMETHOD trebuchet socks5 127.0.0.1:1999",sizeof(line)); handle_proxy_line(line, mp); test_assert(mp->conf_state == PT_PROTO_BROKEN); reset_mp(mp); /* correct protocol run: */ - strcpy(line,"VERSION 1"); + strlcpy(line,"VERSION 1",sizeof(line)); handle_proxy_line(line, mp); test_assert(mp->conf_state == PT_PROTO_ACCEPTING_METHODS); - strcpy(line,"CMETHOD trebuchet socks5 127.0.0.1:1999"); + strlcpy(line,"CMETHOD trebuchet socks5 127.0.0.1:1999",sizeof(line)); handle_proxy_line(line, mp); test_assert(mp->conf_state == PT_PROTO_ACCEPTING_METHODS); - strcpy(line,"CMETHODS DONE"); + strlcpy(line,"CMETHODS DONE",sizeof(line)); handle_proxy_line(line, mp); test_assert(mp->conf_state == PT_PROTO_CONFIGURED); done: + reset_mp(mp); + smartlist_free(mp->transports); + tor_free(mp->argv[0]); + tor_free(mp->argv); + tor_free(mp); +} + +static void +test_pt_get_extrainfo_string(void *arg) +{ + managed_proxy_t *mp1 = NULL, *mp2 = NULL; + char **argv1, **argv2; + smartlist_t *t1 = smartlist_new(), *t2 = smartlist_new(); + int r; + char *s = NULL; + (void) arg; + + argv1 = tor_malloc_zero(sizeof(char*)*3); + argv1[0] = tor_strdup("ewige"); + argv1[1] = tor_strdup("Blumenkraft"); + argv1[2] = NULL; + argv2 = tor_malloc_zero(sizeof(char*)*4); + argv2[0] = tor_strdup("und"); + argv2[1] = tor_strdup("ewige"); + argv2[2] = tor_strdup("Schlangenkraft"); + argv2[3] = NULL; + + mp1 = managed_proxy_create(t1, argv1, 1); + mp2 = managed_proxy_create(t2, argv2, 1); + + r = parse_smethod_line("SMETHOD hagbard 127.0.0.1:5555", mp1); + tt_int_op(r, ==, 0); + r = parse_smethod_line("SMETHOD celine 127.0.0.1:1723 ARGS:card=no-enemy", + mp2); + tt_int_op(r, ==, 0); + + /* Force these proxies to look "completed" or they won't generate output. */ + mp1->conf_state = mp2->conf_state = PT_PROTO_COMPLETED; + + s = pt_get_extra_info_descriptor_string(); + tt_assert(s); + tt_str_op(s, ==, + "transport hagbard 127.0.0.1:5555\n" + "transport celine 127.0.0.1:1723 card=no-enemy\n"); + + done: + /* XXXX clean up better */ + smartlist_free(t1); + smartlist_free(t2); + tor_free(s); +} + +#ifdef _WIN32 +#define STDIN_HANDLE HANDLE +#else +#define STDIN_HANDLE FILE +#endif + +static smartlist_t * +tor_get_lines_from_handle_replacement(STDIN_HANDLE *handle, + enum stream_status *stream_status_out) +{ + static int times_called = 0; + smartlist_t *retval_sl = smartlist_new(); + + (void) handle; + (void) stream_status_out; + + /* Generate some dummy CMETHOD lines the first 5 times. The 6th + time, send 'CMETHODS DONE' to finish configuring the proxy. */ + if (times_called++ != 5) { + smartlist_add_asprintf(retval_sl, "SMETHOD mock%d 127.0.0.1:555%d", + times_called, times_called); + } else { + smartlist_add(retval_sl, tor_strdup("SMETHODS DONE")); + } + + return retval_sl; +} + +/* NOP mock */ +static void +tor_process_handle_destroy_replacement(process_handle_t *process_handle, + int also_terminate_process) +{ + (void) process_handle; + (void) also_terminate_process; +} + +static or_state_t *dummy_state = NULL; + +static or_state_t * +get_or_state_replacement(void) +{ + return dummy_state; +} + +static int controlevent_n = 0; +static uint16_t controlevent_event = 0; +static smartlist_t *controlevent_msgs = NULL; + +static void +send_control_event_string_replacement(uint16_t event, event_format_t which, + const char *msg) +{ + (void) which; + ++controlevent_n; + controlevent_event = event; + if (!controlevent_msgs) + controlevent_msgs = smartlist_new(); + smartlist_add(controlevent_msgs, tor_strdup(msg)); +} + +/* Test the configure_proxy() function. */ +static void +test_pt_configure_proxy(void *arg) +{ + int i, retval; + managed_proxy_t *mp = NULL; + (void) arg; + + dummy_state = tor_malloc_zero(sizeof(or_state_t)); + + MOCK(tor_get_lines_from_handle, + tor_get_lines_from_handle_replacement); + MOCK(tor_process_handle_destroy, + tor_process_handle_destroy_replacement); + MOCK(get_or_state, + get_or_state_replacement); + MOCK(send_control_event_string, + send_control_event_string_replacement); + + control_testing_set_global_event_mask(EVENT_TRANSPORT_LAUNCHED); + + mp = tor_malloc(sizeof(managed_proxy_t)); + mp->conf_state = PT_PROTO_ACCEPTING_METHODS; + mp->transports = smartlist_new(); + mp->transports_to_launch = smartlist_new(); + mp->process_handle = tor_malloc_zero(sizeof(process_handle_t)); + mp->argv = tor_malloc_zero(sizeof(char*)*2); + mp->argv[0] = tor_strdup("<testcase>"); + mp->is_server = 1; + + /* Test the return value of configure_proxy() by calling it some + times while it is uninitialized and then finally finalizing its + configuration. */ + for (i = 0 ; i < 5 ; i++) { + retval = configure_proxy(mp); + /* retval should be zero because proxy hasn't finished configuring yet */ + test_assert(retval == 0); + /* check the number of registered transports */ + test_assert(smartlist_len(mp->transports) == i+1); + /* check that the mp is still waiting for transports */ + test_assert(mp->conf_state == PT_PROTO_ACCEPTING_METHODS); + } + + /* this last configure_proxy() should finalize the proxy configuration. */ + retval = configure_proxy(mp); + /* retval should be 1 since the proxy finished configuring */ + test_assert(retval == 1); + /* check the mp state */ + test_assert(mp->conf_state == PT_PROTO_COMPLETED); + + tt_int_op(controlevent_n, ==, 5); + tt_int_op(controlevent_event, ==, EVENT_TRANSPORT_LAUNCHED); + tt_int_op(smartlist_len(controlevent_msgs), ==, 5); + smartlist_sort_strings(controlevent_msgs); + tt_str_op(smartlist_get(controlevent_msgs, 0), ==, + "650 TRANSPORT_LAUNCHED server mock1 127.0.0.1 5551\r\n"); + tt_str_op(smartlist_get(controlevent_msgs, 1), ==, + "650 TRANSPORT_LAUNCHED server mock2 127.0.0.1 5552\r\n"); + tt_str_op(smartlist_get(controlevent_msgs, 2), ==, + "650 TRANSPORT_LAUNCHED server mock3 127.0.0.1 5553\r\n"); + tt_str_op(smartlist_get(controlevent_msgs, 3), ==, + "650 TRANSPORT_LAUNCHED server mock4 127.0.0.1 5554\r\n"); + tt_str_op(smartlist_get(controlevent_msgs, 4), ==, + "650 TRANSPORT_LAUNCHED server mock5 127.0.0.1 5555\r\n"); + + { /* check that the transport info were saved properly in the tor state */ + config_line_t *transport_in_state = NULL; + smartlist_t *transport_info_sl = smartlist_new(); + char *name_of_transport = NULL; + char *bindaddr = NULL; + + /* Get the bindaddr for "mock1" and check it against the bindaddr + that the mocked tor_get_lines_from_handle() generated. */ + transport_in_state = get_transport_in_state_by_name("mock1"); + test_assert(transport_in_state); + smartlist_split_string(transport_info_sl, transport_in_state->value, + NULL, 0, 0); + name_of_transport = smartlist_get(transport_info_sl, 0); + bindaddr = smartlist_get(transport_info_sl, 1); + tt_str_op(name_of_transport, ==, "mock1"); + tt_str_op(bindaddr, ==, "127.0.0.1:5551"); + + SMARTLIST_FOREACH(transport_info_sl, char *, cp, tor_free(cp)); + smartlist_free(transport_info_sl); + } + + done: + or_state_free(dummy_state); + UNMOCK(tor_get_lines_from_handle); + UNMOCK(tor_process_handle_destroy); + UNMOCK(get_or_state); + UNMOCK(send_control_event_string); + if (controlevent_msgs) { + SMARTLIST_FOREACH(controlevent_msgs, char *, cp, tor_free(cp)); + smartlist_free(controlevent_msgs); + controlevent_msgs = NULL; + } + if (mp->transports) { + SMARTLIST_FOREACH(mp->transports, transport_t *, t, transport_free(t)); + smartlist_free(mp->transports); + } + smartlist_free(mp->transports_to_launch); + tor_free(mp->process_handle); + tor_free(mp->argv[0]); + tor_free(mp->argv); tor_free(mp); } @@ -138,6 +456,12 @@ test_pt_protocol(void) struct testcase_t pt_tests[] = { PT_LEGACY(parsing), PT_LEGACY(protocol), + { "get_transport_options", test_pt_get_transport_options, TT_FORK, + NULL, NULL }, + { "get_extrainfo_string", test_pt_get_extrainfo_string, TT_FORK, + NULL, NULL }, + { "configure_proxy",test_pt_configure_proxy, TT_FORK, + NULL, NULL }, END_OF_TESTCASES }; diff --git a/src/test/test_relaycell.c b/src/test/test_relaycell.c new file mode 100644 index 0000000000..9aff6ab49e --- /dev/null +++ b/src/test/test_relaycell.c @@ -0,0 +1,249 @@ +/* Copyright (c) 2014, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/* Unit tests for handling different kinds of relay cell */ + +#define RELAY_PRIVATE +#include "or.h" +#include "config.h" +#include "connection.h" +#include "connection_edge.h" +#include "relay.h" +#include "test.h" + +static int srm_ncalls; +static entry_connection_t *srm_conn; +static int srm_atype; +static size_t srm_alen; +static int srm_answer_is_set; +static uint8_t srm_answer[512]; +static int srm_ttl; +static time_t srm_expires; + +/* Mock replacement for connection_ap_hannshake_socks_resolved() */ +static void +socks_resolved_mock(entry_connection_t *conn, + int answer_type, + size_t answer_len, + const uint8_t *answer, + int ttl, + time_t expires) +{ + srm_ncalls++; + srm_conn = conn; + srm_atype = answer_type; + srm_alen = answer_len; + if (answer) { + memset(srm_answer, 0, sizeof(srm_answer)); + memcpy(srm_answer, answer, answer_len < 512 ? answer_len : 512); + srm_answer_is_set = 1; + } else { + srm_answer_is_set = 0; + } + srm_ttl = ttl; + srm_expires = expires; +} + +static int mum_ncalls; +static entry_connection_t *mum_conn; +static int mum_endreason; + +/* Mock replacement for connection_mark_unattached_ap_() */ +static void +mark_unattached_mock(entry_connection_t *conn, int endreason, + int line, const char *file) +{ + ++mum_ncalls; + mum_conn = conn; + mum_endreason = endreason; + (void) line; + (void) file; +} + +/* Tests for connection_edge_process_resolved_cell(). + + The point of ..process_resolved_cell() is to handle an incoming cell + on an entry connection, and call connection_mark_unattached_ap() and/or + connection_ap_handshake_socks_resolved(). + */ +static void +test_relaycell_resolved(void *arg) +{ + entry_connection_t *entryconn; + edge_connection_t *edgeconn; + cell_t cell; + relay_header_t rh; + int r; + or_options_t *options = get_options_mutable(); + +#define SET_CELL(s) do { \ + memset(&cell, 0, sizeof(cell)); \ + memset(&rh, 0, sizeof(rh)); \ + memcpy(cell.payload + RELAY_HEADER_SIZE, (s), sizeof((s))-1); \ + rh.length = sizeof((s))-1; \ + rh.command = RELAY_COMMAND_RESOLVED; \ + } while (0) +#define MOCK_RESET() do { \ + srm_ncalls = mum_ncalls = 0; \ + } while (0) +#define ASSERT_MARK_CALLED(reason) do { \ + tt_int_op(mum_ncalls, ==, 1); \ + tt_ptr_op(mum_conn, ==, entryconn); \ + tt_int_op(mum_endreason, ==, (reason)); \ + } while (0) +#define ASSERT_RESOLVED_CALLED(atype, answer, ttl, expires) do { \ + tt_int_op(srm_ncalls, ==, 1); \ + tt_ptr_op(srm_conn, ==, entryconn); \ + tt_int_op(srm_atype, ==, (atype)); \ + if (answer) { \ + tt_int_op(srm_alen, ==, sizeof(answer)-1); \ + tt_int_op(srm_alen, <, 512); \ + tt_int_op(srm_answer_is_set, ==, 1); \ + tt_mem_op(srm_answer, ==, answer, sizeof(answer)-1); \ + } else { \ + tt_int_op(srm_answer_is_set, ==, 0); \ + } \ + tt_int_op(srm_ttl, ==, ttl); \ + tt_i64_op((int64_t)srm_expires, ==, (int64_t)expires); \ + } while (0) + + (void)arg; + + MOCK(connection_mark_unattached_ap_, mark_unattached_mock); + MOCK(connection_ap_handshake_socks_resolved, socks_resolved_mock); + + options->ClientDNSRejectInternalAddresses = 0; + + SET_CELL(/* IPv4: 127.0.1.2, ttl 256 */ + "\x04\x04\x7f\x00\x01\x02\x00\x00\x01\x00" + /* IPv4: 18.0.0.1, ttl 512 */ + "\x04\x04\x12\x00\x00\x01\x00\x00\x02\x00" + /* IPv6: 2003::3, ttl 1024 */ + "\x06\x10" + "\x20\x02\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x03" + "\x00\x00\x04\x00"); + + entryconn = entry_connection_new(CONN_TYPE_AP, AF_INET); + edgeconn = ENTRY_TO_EDGE_CONN(entryconn); + + /* Try with connection in non-RESOLVE_WAIT state: cell gets ignored */ + MOCK_RESET(); + r = connection_edge_process_resolved_cell(edgeconn, &cell, &rh); + tt_int_op(r, ==, 0); + tt_int_op(srm_ncalls, ==, 0); + tt_int_op(mum_ncalls, ==, 0); + + /* Now put it in the right state. */ + ENTRY_TO_CONN(entryconn)->state = AP_CONN_STATE_RESOLVE_WAIT; + entryconn->socks_request->command = SOCKS_COMMAND_RESOLVE; + entryconn->ipv4_traffic_ok = 1; + entryconn->ipv6_traffic_ok = 1; + entryconn->prefer_ipv6_traffic = 0; + + /* We prefer ipv4, so we should get the first ipv4 answer */ + MOCK_RESET(); + r = connection_edge_process_resolved_cell(edgeconn, &cell, &rh); + tt_int_op(r, ==, 0); + ASSERT_MARK_CALLED(END_STREAM_REASON_DONE| + END_STREAM_REASON_FLAG_ALREADY_SOCKS_REPLIED); + ASSERT_RESOLVED_CALLED(RESOLVED_TYPE_IPV4, "\x7f\x00\x01\x02", 256, -1); + + /* But we may be discarding private answers. */ + MOCK_RESET(); + options->ClientDNSRejectInternalAddresses = 1; + r = connection_edge_process_resolved_cell(edgeconn, &cell, &rh); + tt_int_op(r, ==, 0); + ASSERT_MARK_CALLED(END_STREAM_REASON_DONE| + END_STREAM_REASON_FLAG_ALREADY_SOCKS_REPLIED); + ASSERT_RESOLVED_CALLED(RESOLVED_TYPE_IPV4, "\x12\x00\x00\x01", 512, -1); + + /* now prefer ipv6, and get the first ipv6 answer */ + entryconn->prefer_ipv6_traffic = 1; + MOCK_RESET(); + r = connection_edge_process_resolved_cell(edgeconn, &cell, &rh); + tt_int_op(r, ==, 0); + ASSERT_MARK_CALLED(END_STREAM_REASON_DONE| + END_STREAM_REASON_FLAG_ALREADY_SOCKS_REPLIED); + ASSERT_RESOLVED_CALLED(RESOLVED_TYPE_IPV6, + "\x20\x02\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x03", + 1024, -1); + + /* With a cell that only has IPv4, we report IPv4 even if we prefer IPv6 */ + MOCK_RESET(); + SET_CELL("\x04\x04\x12\x00\x00\x01\x00\x00\x02\x00"); + r = connection_edge_process_resolved_cell(edgeconn, &cell, &rh); + tt_int_op(r, ==, 0); + ASSERT_MARK_CALLED(END_STREAM_REASON_DONE| + END_STREAM_REASON_FLAG_ALREADY_SOCKS_REPLIED); + ASSERT_RESOLVED_CALLED(RESOLVED_TYPE_IPV4, "\x12\x00\x00\x01", 512, -1); + + /* But if we don't allow IPv4, we report nothing if the cell contains only + * ipv4 */ + MOCK_RESET(); + entryconn->ipv4_traffic_ok = 0; + r = connection_edge_process_resolved_cell(edgeconn, &cell, &rh); + tt_int_op(r, ==, 0); + ASSERT_MARK_CALLED(END_STREAM_REASON_DONE| + END_STREAM_REASON_FLAG_ALREADY_SOCKS_REPLIED); + ASSERT_RESOLVED_CALLED(RESOLVED_TYPE_ERROR, NULL, -1, -1); + + /* If we wanted hostnames, we report nothing, since we only had IPs. */ + MOCK_RESET(); + entryconn->ipv4_traffic_ok = 1; + entryconn->socks_request->command = SOCKS_COMMAND_RESOLVE_PTR; + r = connection_edge_process_resolved_cell(edgeconn, &cell, &rh); + tt_int_op(r, ==, 0); + ASSERT_MARK_CALLED(END_STREAM_REASON_DONE| + END_STREAM_REASON_FLAG_ALREADY_SOCKS_REPLIED); + ASSERT_RESOLVED_CALLED(RESOLVED_TYPE_ERROR, NULL, -1, -1); + + /* A hostname cell is fine though. */ + MOCK_RESET(); + SET_CELL("\x00\x0fwww.example.com\x00\x01\x00\x00"); + r = connection_edge_process_resolved_cell(edgeconn, &cell, &rh); + tt_int_op(r, ==, 0); + ASSERT_MARK_CALLED(END_STREAM_REASON_DONE| + END_STREAM_REASON_FLAG_ALREADY_SOCKS_REPLIED); + ASSERT_RESOLVED_CALLED(RESOLVED_TYPE_HOSTNAME, "www.example.com", 65536, -1); + + /* error on malformed cell */ + MOCK_RESET(); + entryconn->socks_request->command = SOCKS_COMMAND_RESOLVE; + SET_CELL("\x04\x04\x01\x02\x03\x04"); /* no ttl */ + r = connection_edge_process_resolved_cell(edgeconn, &cell, &rh); + tt_int_op(r, ==, 0); + ASSERT_MARK_CALLED(END_STREAM_REASON_TORPROTOCOL); + tt_int_op(srm_ncalls, ==, 0); + + /* error on all addresses private */ + MOCK_RESET(); + SET_CELL(/* IPv4: 127.0.1.2, ttl 256 */ + "\x04\x04\x7f\x00\x01\x02\x00\x00\x01\x00" + /* IPv4: 192.168.1.1, ttl 256 */ + "\x04\x04\xc0\xa8\x01\x01\x00\x00\x01\x00"); + r = connection_edge_process_resolved_cell(edgeconn, &cell, &rh); + tt_int_op(r, ==, 0); + ASSERT_MARK_CALLED(END_STREAM_REASON_TORPROTOCOL); + ASSERT_RESOLVED_CALLED(RESOLVED_TYPE_ERROR_TRANSIENT, NULL, 0, TIME_MAX); + + /* Legit error code */ + MOCK_RESET(); + SET_CELL("\xf0\x15" "quiet and meaningless" "\x00\x00\x0f\xff"); + r = connection_edge_process_resolved_cell(edgeconn, &cell, &rh); + tt_int_op(r, ==, 0); + ASSERT_MARK_CALLED(END_STREAM_REASON_DONE| + END_STREAM_REASON_FLAG_ALREADY_SOCKS_REPLIED); + ASSERT_RESOLVED_CALLED(RESOLVED_TYPE_ERROR_TRANSIENT, NULL, -1, -1); + + done: + UNMOCK(connection_mark_unattached_ap_); + UNMOCK(connection_ap_handshake_socks_resolved); +} + +struct testcase_t relaycell_tests[] = { + { "resolved", test_relaycell_resolved, TT_FORK, NULL, NULL }, + END_OF_TESTCASES +}; + diff --git a/src/test/test_replay.c b/src/test/test_replay.c index de841ad594..b48f582f5e 100644 --- a/src/test/test_replay.c +++ b/src/test/test_replay.c @@ -32,6 +32,40 @@ test_replaycache_alloc(void) } static void +test_replaycache_badalloc(void) +{ + replaycache_t *r = NULL; + + /* Negative horizon should fail */ + r = replaycache_new(-600, 300); + test_assert(r == NULL); + /* Negative interval should get adjusted to zero */ + r = replaycache_new(600, -300); + test_assert(r != NULL); + test_eq(r->scrub_interval, 0); + replaycache_free(r); + /* Negative horizon and negative interval should still fail */ + r = replaycache_new(-600, -300); + test_assert(r == NULL); + + done: + if (r) replaycache_free(r); + + return; +} + +static void +test_replaycache_free_null(void) +{ + replaycache_free(NULL); + /* Assert that we're here without horrible death */ + test_assert(1); + + done: + return; +} + +static void test_replaycache_miss(void) { replaycache_t *r = NULL; @@ -42,7 +76,13 @@ test_replaycache_miss(void) result = replaycache_add_and_test_internal(1200, r, test_buffer, - (int)strlen(test_buffer), NULL); + strlen(test_buffer), NULL); + test_eq(result, 0); + + /* poke the bad-parameter error case too */ + result = + replaycache_add_and_test_internal(1200, NULL, test_buffer, + strlen(test_buffer), NULL); test_eq(result, 0); done: @@ -62,12 +102,12 @@ test_replaycache_hit(void) result = replaycache_add_and_test_internal(1200, r, test_buffer, - (int)strlen(test_buffer), NULL); + strlen(test_buffer), NULL); test_eq(result, 0); result = replaycache_add_and_test_internal(1300, r, test_buffer, - (int)strlen(test_buffer), NULL); + strlen(test_buffer), NULL); test_eq(result, 1); done: @@ -87,17 +127,17 @@ test_replaycache_age(void) result = replaycache_add_and_test_internal(1200, r, test_buffer, - (int)strlen(test_buffer), NULL); + strlen(test_buffer), NULL); test_eq(result, 0); result = replaycache_add_and_test_internal(1300, r, test_buffer, - (int)strlen(test_buffer), NULL); + strlen(test_buffer), NULL); test_eq(result, 1); result = replaycache_add_and_test_internal(3000, r, test_buffer, - (int)strlen(test_buffer), NULL); + strlen(test_buffer), NULL); test_eq(result, 0); done: @@ -118,12 +158,12 @@ test_replaycache_elapsed(void) result = replaycache_add_and_test_internal(1200, r, test_buffer, - (int)strlen(test_buffer), NULL); + strlen(test_buffer), NULL); test_eq(result, 0); result = replaycache_add_and_test_internal(1300, r, test_buffer, - (int)strlen(test_buffer), &elapsed); + strlen(test_buffer), &elapsed); test_eq(result, 1); test_eq(elapsed, 100); @@ -144,18 +184,102 @@ test_replaycache_noexpire(void) result = replaycache_add_and_test_internal(1200, r, test_buffer, - (int)strlen(test_buffer), NULL); + strlen(test_buffer), NULL); test_eq(result, 0); result = replaycache_add_and_test_internal(1300, r, test_buffer, - (int)strlen(test_buffer), NULL); + strlen(test_buffer), NULL); test_eq(result, 1); result = replaycache_add_and_test_internal(3000, r, test_buffer, - (int)strlen(test_buffer), NULL); + strlen(test_buffer), NULL); + test_eq(result, 1); + + done: + if (r) replaycache_free(r); + + return; +} + +static void +test_replaycache_scrub(void) +{ + replaycache_t *r = NULL; + int result; + + r = replaycache_new(600, 300); + test_assert(r != NULL); + + /* Set up like in test_replaycache_hit() */ + result = + replaycache_add_and_test_internal(100, r, test_buffer, + strlen(test_buffer), NULL); + test_eq(result, 0); + + result = + replaycache_add_and_test_internal(200, r, test_buffer, + strlen(test_buffer), NULL); + test_eq(result, 1); + + /* + * Poke a few replaycache_scrub_if_needed_internal() error cases that + * can't happen through replaycache_add_and_test_internal() + */ + + /* Null cache */ + replaycache_scrub_if_needed_internal(300, NULL); + /* Assert we're still here */ + test_assert(1); + + /* Make sure we hit the aging-out case too */ + replaycache_scrub_if_needed_internal(1500, r); + /* Assert that we aged it */ + test_eq(digestmap_size(r->digests_seen), 0); + + done: + if (r) replaycache_free(r); + + return; +} + +static void +test_replaycache_future(void) +{ + replaycache_t *r = NULL; + int result; + time_t elapsed = 0; + + r = replaycache_new(600, 300); + test_assert(r != NULL); + + /* Set up like in test_replaycache_hit() */ + result = + replaycache_add_and_test_internal(100, r, test_buffer, + strlen(test_buffer), &elapsed); + test_eq(result, 0); + /* elapsed should still be 0, since it wasn't written */ + test_eq(elapsed, 0); + + result = + replaycache_add_and_test_internal(200, r, test_buffer, + strlen(test_buffer), &elapsed); + test_eq(result, 1); + /* elapsed should be the time since the last hit */ + test_eq(elapsed, 100); + + /* + * Now let's turn the clock back to get coverage on the cache entry from the + * future not-supposed-to-happen case. + */ + result = + replaycache_add_and_test_internal(150, r, test_buffer, + strlen(test_buffer), &elapsed); + /* We should still get a hit */ test_eq(result, 1); + /* ...but it shouldn't let us see a negative elapsed time */ + test_eq(elapsed, 0); done: if (r) replaycache_free(r); @@ -163,16 +287,62 @@ test_replaycache_noexpire(void) return; } +static void +test_replaycache_realtime(void) +{ + replaycache_t *r = NULL; + /* + * Negative so we fail if replaycache_add_test_and_elapsed() doesn't + * write to elapsed. + */ + time_t elapsed = -1; + int result; + + /* Test the realtime as well as *_internal() entry points */ + r = replaycache_new(600, 300); + test_assert(r != NULL); + + /* This should miss */ + result = + replaycache_add_and_test(r, test_buffer, strlen(test_buffer)); + test_eq(result, 0); + + /* This should hit */ + result = + replaycache_add_and_test(r, test_buffer, strlen(test_buffer)); + test_eq(result, 1); + + /* This should hit and return a small elapsed time */ + result = + replaycache_add_test_and_elapsed(r, test_buffer, + strlen(test_buffer), &elapsed); + test_eq(result, 1); + test_assert(elapsed >= 0); + test_assert(elapsed <= 5); + + /* Scrub it to exercise that entry point too */ + replaycache_scrub_if_needed(r); + + done: + if (r) replaycache_free(r); + return; +} + #define REPLAYCACHE_LEGACY(name) \ { #name, legacy_test_helper, 0, &legacy_setup, test_replaycache_ ## name } struct testcase_t replaycache_tests[] = { REPLAYCACHE_LEGACY(alloc), + REPLAYCACHE_LEGACY(badalloc), + REPLAYCACHE_LEGACY(free_null), REPLAYCACHE_LEGACY(miss), REPLAYCACHE_LEGACY(hit), REPLAYCACHE_LEGACY(age), REPLAYCACHE_LEGACY(elapsed), REPLAYCACHE_LEGACY(noexpire), + REPLAYCACHE_LEGACY(scrub), + REPLAYCACHE_LEGACY(future), + REPLAYCACHE_LEGACY(realtime), END_OF_TESTCASES }; diff --git a/src/test/test_routerkeys.c b/src/test/test_routerkeys.c new file mode 100644 index 0000000000..182e0f6f87 --- /dev/null +++ b/src/test/test_routerkeys.c @@ -0,0 +1,85 @@ +/* Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2013, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "orconfig.h" +#define ROUTER_PRIVATE +#include "or.h" +#include "config.h" +#include "router.h" +#include "util.h" +#include "crypto.h" + +#include "test.h" + +static void +test_routerkeys_write_fingerprint(void *arg) +{ + crypto_pk_t *key = pk_generate(2); + or_options_t *options = get_options_mutable(); + const char *ddir = get_fname("write_fingerprint"); + char *cp = NULL, *cp2 = NULL; + char fp[FINGERPRINT_LEN+1]; + + (void)arg; + + tt_assert(key); + + options->ORPort_set = 1; /* So that we can get the server ID key */ + tor_free(options->DataDirectory); + options->DataDirectory = tor_strdup(ddir); + options->Nickname = tor_strdup("haflinger"); + set_server_identity_key(key); + set_client_identity_key(crypto_pk_dup_key(key)); + + tt_int_op(0, ==, check_private_dir(ddir, CPD_CREATE, NULL)); + tt_int_op(crypto_pk_cmp_keys(get_server_identity_key(),key),==,0); + + /* Write fingerprint file */ + tt_int_op(0, ==, router_write_fingerprint(0)); + cp = read_file_to_str(get_fname("write_fingerprint/fingerprint"), + 0, NULL); + crypto_pk_get_fingerprint(key, fp, 0); + tor_asprintf(&cp2, "haflinger %s\n", fp); + tt_str_op(cp, ==, cp2); + tor_free(cp); + tor_free(cp2); + + /* Write hashed-fingerprint file */ + tt_int_op(0, ==, router_write_fingerprint(1)); + cp = read_file_to_str(get_fname("write_fingerprint/hashed-fingerprint"), + 0, NULL); + crypto_pk_get_hashed_fingerprint(key, fp); + tor_asprintf(&cp2, "haflinger %s\n", fp); + tt_str_op(cp, ==, cp2); + tor_free(cp); + tor_free(cp2); + + /* Replace outdated file */ + write_str_to_file(get_fname("write_fingerprint/hashed-fingerprint"), + "junk goes here", 0); + tt_int_op(0, ==, router_write_fingerprint(1)); + cp = read_file_to_str(get_fname("write_fingerprint/hashed-fingerprint"), + 0, NULL); + crypto_pk_get_hashed_fingerprint(key, fp); + tor_asprintf(&cp2, "haflinger %s\n", fp); + tt_str_op(cp, ==, cp2); + tor_free(cp); + tor_free(cp2); + + done: + crypto_pk_free(key); + set_client_identity_key(NULL); + tor_free(cp); + tor_free(cp2); +} + +#define TEST(name, flags) \ + { #name , test_routerkeys_ ## name, (flags), NULL, NULL } + +struct testcase_t routerkeys_tests[] = { + TEST(write_fingerprint, TT_FORK), + END_OF_TESTCASES +}; + diff --git a/src/test/test_socks.c b/src/test/test_socks.c new file mode 100644 index 0000000000..4ce61e068b --- /dev/null +++ b/src/test/test_socks.c @@ -0,0 +1,393 @@ +/* Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2013, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "or.h" +#include "buffers.h" +#include "config.h" +#include "test.h" + +typedef struct socks_test_data_t { + socks_request_t *req; + buf_t *buf; +} socks_test_data_t; + +static void * +socks_test_setup(const struct testcase_t *testcase) +{ + socks_test_data_t *data = tor_malloc(sizeof(socks_test_data_t)); + (void)testcase; + data->buf = buf_new_with_capacity(256); + data->req = socks_request_new(); + config_register_addressmaps(get_options()); + return data; +} +static int +socks_test_cleanup(const struct testcase_t *testcase, void *ptr) +{ + socks_test_data_t *data = ptr; + (void)testcase; + buf_free(data->buf); + socks_request_free(data->req); + tor_free(data); + return 1; +} + +const struct testcase_setup_t socks_setup = { + socks_test_setup, socks_test_cleanup +}; + +#define SOCKS_TEST_INIT() \ + socks_test_data_t *testdata = ptr; \ + buf_t *buf = testdata->buf; \ + socks_request_t *socks = testdata->req; +#define ADD_DATA(buf, s) \ + write_to_buf(s, sizeof(s)-1, buf) + +static void +socks_request_clear(socks_request_t *socks) +{ + tor_free(socks->username); + tor_free(socks->password); + memset(socks, 0, sizeof(socks_request_t)); +} + +/** Perform unsupported SOCKS 4 commands */ +static void +test_socks_4_unsupported_commands(void *ptr) +{ + SOCKS_TEST_INIT(); + + /* SOCKS 4 Send BIND [02] to IP address 2.2.2.2:4369 */ + ADD_DATA(buf, "\x04\x02\x11\x11\x02\x02\x02\x02\x00"); + test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks, + get_options()->SafeSocks) == -1); + test_eq(4, socks->socks_version); + test_eq(0, socks->replylen); /* XXX: shouldn't tor reply? */ + + done: + ; +} + +/** Perform supported SOCKS 4 commands */ +static void +test_socks_4_supported_commands(void *ptr) +{ + SOCKS_TEST_INIT(); + + test_eq(0, buf_datalen(buf)); + + /* SOCKS 4 Send CONNECT [01] to IP address 2.2.2.2:4370 */ + ADD_DATA(buf, "\x04\x01\x11\x12\x02\x02\x02\x03\x00"); + test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks, + get_options()->SafeSocks) == 1); + test_eq(4, socks->socks_version); + test_eq(0, socks->replylen); /* XXX: shouldn't tor reply? */ + test_eq(SOCKS_COMMAND_CONNECT, socks->command); + test_streq("2.2.2.3", socks->address); + test_eq(4370, socks->port); + test_assert(socks->got_auth == 0); + test_assert(! socks->username); + + test_eq(0, buf_datalen(buf)); + socks_request_clear(socks); + + /* SOCKS 4 Send CONNECT [01] to IP address 2.2.2.2:4369 with userid*/ + ADD_DATA(buf, "\x04\x01\x11\x12\x02\x02\x02\x04me\x00"); + test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks, + get_options()->SafeSocks) == 1); + test_eq(4, socks->socks_version); + test_eq(0, socks->replylen); /* XXX: shouldn't tor reply? */ + test_eq(SOCKS_COMMAND_CONNECT, socks->command); + test_streq("2.2.2.4", socks->address); + test_eq(4370, socks->port); + test_assert(socks->got_auth == 1); + test_assert(socks->username); + test_eq(2, socks->usernamelen); + test_memeq("me", socks->username, 2); + + test_eq(0, buf_datalen(buf)); + socks_request_clear(socks); + + /* SOCKS 4a Send RESOLVE [F0] request for torproject.org */ + ADD_DATA(buf, "\x04\xF0\x01\x01\x00\x00\x00\x02me\x00torproject.org\x00"); + test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks, + get_options()->SafeSocks) == 1); + test_eq(4, socks->socks_version); + test_eq(0, socks->replylen); /* XXX: shouldn't tor reply? */ + test_streq("torproject.org", socks->address); + + test_eq(0, buf_datalen(buf)); + + done: + ; +} + +/** Perform unsupported SOCKS 5 commands */ +static void +test_socks_5_unsupported_commands(void *ptr) +{ + SOCKS_TEST_INIT(); + + /* SOCKS 5 Send unsupported BIND [02] command */ + ADD_DATA(buf, "\x05\x02\x00\x01"); + + test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks, + get_options()->SafeSocks), 0); + test_eq(0, buf_datalen(buf)); + test_eq(5, socks->socks_version); + test_eq(2, socks->replylen); + test_eq(5, socks->reply[0]); + test_eq(0, socks->reply[1]); + ADD_DATA(buf, "\x05\x02\x00\x01\x02\x02\x02\x01\x01\x01"); + test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks, + get_options()->SafeSocks), -1); + /* XXX: shouldn't tor reply 'command not supported' [07]? */ + + buf_clear(buf); + socks_request_clear(socks); + + /* SOCKS 5 Send unsupported UDP_ASSOCIATE [03] command */ + ADD_DATA(buf, "\x05\x03\x00\x01\x02"); + test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks, + get_options()->SafeSocks), 0); + test_eq(5, socks->socks_version); + test_eq(2, socks->replylen); + test_eq(5, socks->reply[0]); + test_eq(2, socks->reply[1]); + ADD_DATA(buf, "\x05\x03\x00\x01\x02\x02\x02\x01\x01\x01"); + test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks, + get_options()->SafeSocks), -1); + /* XXX: shouldn't tor reply 'command not supported' [07]? */ + + done: + ; +} + +/** Perform supported SOCKS 5 commands */ +static void +test_socks_5_supported_commands(void *ptr) +{ + SOCKS_TEST_INIT(); + + /* SOCKS 5 Send CONNECT [01] to IP address 2.2.2.2:4369 */ + ADD_DATA(buf, "\x05\x01\x00"); + test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks, + get_options()->SafeSocks), 0); + test_eq(5, socks->socks_version); + test_eq(2, socks->replylen); + test_eq(5, socks->reply[0]); + test_eq(0, socks->reply[1]); + + ADD_DATA(buf, "\x05\x01\x00\x01\x02\x02\x02\x02\x11\x11"); + test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks, + get_options()->SafeSocks), 1); + test_streq("2.2.2.2", socks->address); + test_eq(4369, socks->port); + + test_eq(0, buf_datalen(buf)); + socks_request_clear(socks); + + /* SOCKS 5 Send CONNECT [01] to FQDN torproject.org:4369 */ + ADD_DATA(buf, "\x05\x01\x00"); + ADD_DATA(buf, "\x05\x01\x00\x03\x0Etorproject.org\x11\x11"); + test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks, + get_options()->SafeSocks), 1); + + test_eq(5, socks->socks_version); + test_eq(2, socks->replylen); + test_eq(5, socks->reply[0]); + test_eq(0, socks->reply[1]); + test_streq("torproject.org", socks->address); + test_eq(4369, socks->port); + + test_eq(0, buf_datalen(buf)); + socks_request_clear(socks); + + /* SOCKS 5 Send RESOLVE [F0] request for torproject.org:4369 */ + ADD_DATA(buf, "\x05\x01\x00"); + ADD_DATA(buf, "\x05\xF0\x00\x03\x0Etorproject.org\x01\x02"); + test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks, + get_options()->SafeSocks) == 1); + test_eq(5, socks->socks_version); + test_eq(2, socks->replylen); + test_eq(5, socks->reply[0]); + test_eq(0, socks->reply[1]); + test_streq("torproject.org", socks->address); + + test_eq(0, buf_datalen(buf)); + socks_request_clear(socks); + + /* SOCKS 5 Send RESOLVE_PTR [F1] for IP address 2.2.2.5 */ + ADD_DATA(buf, "\x05\x01\x00"); + ADD_DATA(buf, "\x05\xF1\x00\x01\x02\x02\x02\x05\x01\x03"); + test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks, + get_options()->SafeSocks) == 1); + test_eq(5, socks->socks_version); + test_eq(2, socks->replylen); + test_eq(5, socks->reply[0]); + test_eq(0, socks->reply[1]); + test_streq("2.2.2.5", socks->address); + + test_eq(0, buf_datalen(buf)); + + done: + ; +} + +/** Perform SOCKS 5 authentication */ +static void +test_socks_5_no_authenticate(void *ptr) +{ + SOCKS_TEST_INIT(); + + /*SOCKS 5 No Authentication */ + ADD_DATA(buf,"\x05\x01\x00"); + test_assert(!fetch_from_buf_socks(buf, socks, + get_options()->TestSocks, + get_options()->SafeSocks)); + test_eq(2, socks->replylen); + test_eq(5, socks->reply[0]); + test_eq(SOCKS_NO_AUTH, socks->reply[1]); + + test_eq(0, buf_datalen(buf)); + + /*SOCKS 5 Send username/password anyway - pretend to be broken */ + ADD_DATA(buf,"\x01\x02\x01\x01\x02\x01\x01"); + test_assert(!fetch_from_buf_socks(buf, socks, + get_options()->TestSocks, + get_options()->SafeSocks)); + test_eq(5, socks->socks_version); + test_eq(2, socks->replylen); + test_eq(1, socks->reply[0]); + test_eq(0, socks->reply[1]); + + test_eq(2, socks->usernamelen); + test_eq(2, socks->passwordlen); + + test_memeq("\x01\x01", socks->username, 2); + test_memeq("\x01\x01", socks->password, 2); + + done: + ; +} + +/** Perform SOCKS 5 authentication */ +static void +test_socks_5_authenticate(void *ptr) +{ + SOCKS_TEST_INIT(); + + /* SOCKS 5 Negotiate username/password authentication */ + ADD_DATA(buf, "\x05\x01\x02"); + + test_assert(!fetch_from_buf_socks(buf, socks, + get_options()->TestSocks, + get_options()->SafeSocks)); + test_eq(2, socks->replylen); + test_eq(5, socks->reply[0]); + test_eq(SOCKS_USER_PASS, socks->reply[1]); + test_eq(5, socks->socks_version); + + test_eq(0, buf_datalen(buf)); + + /* SOCKS 5 Send username/password */ + ADD_DATA(buf, "\x01\x02me\x08mypasswd"); + test_assert(!fetch_from_buf_socks(buf, socks, + get_options()->TestSocks, + get_options()->SafeSocks)); + test_eq(5, socks->socks_version); + test_eq(2, socks->replylen); + test_eq(1, socks->reply[0]); + test_eq(0, socks->reply[1]); + + test_eq(2, socks->usernamelen); + test_eq(8, socks->passwordlen); + + test_memeq("me", socks->username, 2); + test_memeq("mypasswd", socks->password, 8); + + done: + ; +} + +/** Perform SOCKS 5 authentication and send data all in one go */ +static void +test_socks_5_authenticate_with_data(void *ptr) +{ + SOCKS_TEST_INIT(); + + /* SOCKS 5 Negotiate username/password authentication */ + ADD_DATA(buf, "\x05\x01\x02"); + + test_assert(!fetch_from_buf_socks(buf, socks, + get_options()->TestSocks, + get_options()->SafeSocks)); + test_eq(2, socks->replylen); + test_eq(5, socks->reply[0]); + test_eq(SOCKS_USER_PASS, socks->reply[1]); + test_eq(5, socks->socks_version); + + test_eq(0, buf_datalen(buf)); + + /* SOCKS 5 Send username/password */ + /* SOCKS 5 Send CONNECT [01] to IP address 2.2.2.2:4369 */ + ADD_DATA(buf, "\x01\x02me\x03you\x05\x01\x00\x01\x02\x02\x02\x02\x11\x11"); + test_assert(fetch_from_buf_socks(buf, socks, + get_options()->TestSocks, + get_options()->SafeSocks) == 1); + test_eq(5, socks->socks_version); + test_eq(2, socks->replylen); + test_eq(1, socks->reply[0]); + test_eq(0, socks->reply[1]); + + test_streq("2.2.2.2", socks->address); + test_eq(4369, socks->port); + + test_eq(2, socks->usernamelen); + test_eq(3, socks->passwordlen); + test_memeq("me", socks->username, 2); + test_memeq("you", socks->password, 3); + + done: + ; +} + +/** Perform SOCKS 5 authentication before method negotiated */ +static void +test_socks_5_auth_before_negotiation(void *ptr) +{ + SOCKS_TEST_INIT(); + + /* SOCKS 5 Send username/password */ + ADD_DATA(buf, "\x01\x02me\x02me"); + test_assert(fetch_from_buf_socks(buf, socks, + get_options()->TestSocks, + get_options()->SafeSocks) == -1); + test_eq(0, socks->socks_version); + test_eq(0, socks->replylen); + test_eq(0, socks->reply[0]); + test_eq(0, socks->reply[1]); + + done: + ; +} + +#define SOCKSENT(name) \ + { #name, test_socks_##name, TT_FORK, &socks_setup, NULL } + +struct testcase_t socks_tests[] = { + SOCKSENT(4_unsupported_commands), + SOCKSENT(4_supported_commands), + + SOCKSENT(5_unsupported_commands), + SOCKSENT(5_supported_commands), + SOCKSENT(5_no_authenticate), + SOCKSENT(5_auth_before_negotiation), + SOCKSENT(5_authenticate), + SOCKSENT(5_authenticate_with_data), + + END_OF_TESTCASES +}; + diff --git a/src/test/test_status.c b/src/test/test_status.c new file mode 100644 index 0000000000..46dd473132 --- /dev/null +++ b/src/test/test_status.c @@ -0,0 +1,1114 @@ +#define STATUS_PRIVATE +#define HIBERNATE_PRIVATE +#define LOG_PRIVATE +#define REPHIST_PRIVATE + +#include <float.h> +#include <math.h> + +#include "or.h" +#include "torlog.h" +#include "tor_queue.h" +#include "status.h" +#include "circuitlist.h" +#include "config.h" +#include "hibernate.h" +#include "rephist.h" +#include "relay.h" +#include "router.h" +#include "main.h" +#include "nodelist.h" +#include "statefile.h" +#include "test.h" + +#define NS_MODULE status + +#define NS_SUBMODULE count_circuits + +/* + * Test that count_circuits() is correctly counting the number of + * global circuits. + */ + +struct global_circuitlist_s mock_global_circuitlist = + TOR_LIST_HEAD_INITIALIZER(global_circuitlist); + +NS_DECL(struct global_circuitlist_s *, circuit_get_global_list, (void)); + +static void +NS(test_main)(void *arg) +{ + /* Choose origin_circuit_t wlog. */ + origin_circuit_t *mock_circuit1, *mock_circuit2; + circuit_t *circ, *tmp; + int expected_circuits = 2, actual_circuits; + + (void)arg; + + mock_circuit1 = tor_malloc_zero(sizeof(origin_circuit_t)); + mock_circuit2 = tor_malloc_zero(sizeof(origin_circuit_t)); + TOR_LIST_INSERT_HEAD( + &mock_global_circuitlist, TO_CIRCUIT(mock_circuit1), head); + TOR_LIST_INSERT_HEAD( + &mock_global_circuitlist, TO_CIRCUIT(mock_circuit2), head); + + NS_MOCK(circuit_get_global_list); + + actual_circuits = count_circuits(); + + tt_assert(expected_circuits == actual_circuits); + + done: + TOR_LIST_FOREACH_SAFE( + circ, NS(circuit_get_global_list)(), head, tmp); + tor_free(circ); + NS_UNMOCK(circuit_get_global_list); +} + +static struct global_circuitlist_s * +NS(circuit_get_global_list)(void) +{ + return &mock_global_circuitlist; +} + +#undef NS_SUBMODULE +#define NS_SUBMODULE secs_to_uptime + +/* + * Test that secs_to_uptime() is converting the number of seconds that + * Tor is up for into the appropriate string form containing hours and minutes. + */ + +static void +NS(test_main)(void *arg) +{ + const char *expected; + char *actual; + (void)arg; + + expected = "0:00 hours"; + actual = secs_to_uptime(0); + tt_str_op(actual, ==, expected); + tor_free(actual); + + expected = "0:00 hours"; + actual = secs_to_uptime(1); + tt_str_op(actual, ==, expected); + tor_free(actual); + + expected = "0:01 hours"; + actual = secs_to_uptime(60); + tt_str_op(actual, ==, expected); + tor_free(actual); + + expected = "0:59 hours"; + actual = secs_to_uptime(60 * 59); + tt_str_op(actual, ==, expected); + tor_free(actual); + + expected = "1:00 hours"; + actual = secs_to_uptime(60 * 60); + tt_str_op(actual, ==, expected); + tor_free(actual); + + expected = "23:59 hours"; + actual = secs_to_uptime(60 * 60 * 23 + 60 * 59); + tt_str_op(actual, ==, expected); + tor_free(actual); + + expected = "1 day 0:00 hours"; + actual = secs_to_uptime(60 * 60 * 23 + 60 * 60); + tt_str_op(actual, ==, expected); + tor_free(actual); + + expected = "1 day 0:00 hours"; + actual = secs_to_uptime(86400 + 1); + tt_str_op(actual, ==, expected); + tor_free(actual); + + expected = "1 day 0:01 hours"; + actual = secs_to_uptime(86400 + 60); + tt_str_op(actual, ==, expected); + tor_free(actual); + + expected = "10 days 0:00 hours"; + actual = secs_to_uptime(86400 * 10); + tt_str_op(actual, ==, expected); + tor_free(actual); + + expected = "10 days 0:00 hours"; + actual = secs_to_uptime(864000 + 1); + tt_str_op(actual, ==, expected); + tor_free(actual); + + expected = "10 days 0:01 hours"; + actual = secs_to_uptime(864000 + 60); + tt_str_op(actual, ==, expected); + tor_free(actual); + + done: + if (actual != NULL) + tor_free(actual); +} + +#undef NS_SUBMODULE +#define NS_SUBMODULE bytes_to_usage + +/* + * Test that bytes_to_usage() is correctly converting the number of bytes that + * Tor has read/written into the appropriate string form containing kilobytes, + * megabytes, or gigabytes. + */ + +static void +NS(test_main)(void *arg) +{ + const char *expected; + char *actual; + (void)arg; + + expected = "0 kB"; + actual = bytes_to_usage(0); + tt_str_op(actual, ==, expected); + tor_free(actual); + + expected = "0 kB"; + actual = bytes_to_usage(1); + tt_str_op(actual, ==, expected); + tor_free(actual); + + expected = "1 kB"; + actual = bytes_to_usage(1024); + tt_str_op(actual, ==, expected); + tor_free(actual); + + expected = "1023 kB"; + actual = bytes_to_usage((1 << 20) - 1); + tt_str_op(actual, ==, expected); + tor_free(actual); + + expected = "1.00 MB"; + actual = bytes_to_usage((1 << 20)); + tt_str_op(actual, ==, expected); + tor_free(actual); + + expected = "1.00 MB"; + actual = bytes_to_usage((1 << 20) + 5242); + tt_str_op(actual, ==, expected); + tor_free(actual); + + expected = "1.01 MB"; + actual = bytes_to_usage((1 << 20) + 5243); + tt_str_op(actual, ==, expected); + tor_free(actual); + + expected = "1024.00 MB"; + actual = bytes_to_usage((1 << 30) - 1); + tt_str_op(actual, ==, expected); + tor_free(actual); + + expected = "1.00 GB"; + actual = bytes_to_usage((1 << 30)); + tt_str_op(actual, ==, expected); + tor_free(actual); + + expected = "1.00 GB"; + actual = bytes_to_usage((1 << 30) + 5368709); + tt_str_op(actual, ==, expected); + tor_free(actual); + + expected = "1.01 GB"; + actual = bytes_to_usage((1 << 30) + 5368710); + tt_str_op(actual, ==, expected); + tor_free(actual); + + expected = "10.00 GB"; + actual = bytes_to_usage((U64_LITERAL(1) << 30) * 10L); + tt_str_op(actual, ==, expected); + tor_free(actual); + + done: + if (actual != NULL) + tor_free(actual); +} + +#undef NS_SUBMODULE +#define NS_SUBMODULE ASPECT(log_heartbeat, fails) + +/* + * Tests that log_heartbeat() fails when in the public server mode, + * not hibernating, and we couldn't get the current routerinfo. + */ + +NS_DECL(double, tls_get_write_overhead_ratio, (void)); +NS_DECL(int, we_are_hibernating, (void)); +NS_DECL(const or_options_t *, get_options, (void)); +NS_DECL(int, public_server_mode, (const or_options_t *options)); +NS_DECL(const routerinfo_t *, router_get_my_routerinfo, (void)); + +static void +NS(test_main)(void *arg) +{ + int expected, actual; + (void)arg; + + NS_MOCK(tls_get_write_overhead_ratio); + NS_MOCK(we_are_hibernating); + NS_MOCK(get_options); + NS_MOCK(public_server_mode); + NS_MOCK(router_get_my_routerinfo); + + expected = -1; + actual = log_heartbeat(0); + + tt_int_op(actual, ==, expected); + + done: + NS_UNMOCK(tls_get_write_overhead_ratio); + NS_UNMOCK(we_are_hibernating); + NS_UNMOCK(get_options); + NS_UNMOCK(public_server_mode); + NS_UNMOCK(router_get_my_routerinfo); +} + +static double +NS(tls_get_write_overhead_ratio)(void) +{ + return 2.0; +} + +static int +NS(we_are_hibernating)(void) +{ + return 0; +} + +static const or_options_t * +NS(get_options)(void) +{ + return NULL; +} + +static int +NS(public_server_mode)(const or_options_t *options) +{ + (void)options; + + return 1; +} + +static const routerinfo_t * +NS(router_get_my_routerinfo)(void) +{ + return NULL; +} + +#undef NS_SUBMODULE +#define NS_SUBMODULE ASPECT(log_heartbeat, not_in_consensus) + +/* + * Tests that log_heartbeat() logs appropriately if we are not in the cached + * consensus. + */ + +NS_DECL(double, tls_get_write_overhead_ratio, (void)); +NS_DECL(int, we_are_hibernating, (void)); +NS_DECL(const or_options_t *, get_options, (void)); +NS_DECL(int, public_server_mode, (const or_options_t *options)); +NS_DECL(const routerinfo_t *, router_get_my_routerinfo, (void)); +NS_DECL(const node_t *, node_get_by_id, (const char *identity_digest)); +NS_DECL(void, logv, (int severity, log_domain_mask_t domain, + const char *funcname, const char *suffix, const char *format, va_list ap)); +NS_DECL(int, server_mode, (const or_options_t *options)); + +static routerinfo_t *mock_routerinfo; +extern int onion_handshakes_requested[MAX_ONION_HANDSHAKE_TYPE+1]; +extern int onion_handshakes_assigned[MAX_ONION_HANDSHAKE_TYPE+1]; + +static void +NS(test_main)(void *arg) +{ + int expected, actual; + (void)arg; + + NS_MOCK(tls_get_write_overhead_ratio); + NS_MOCK(we_are_hibernating); + NS_MOCK(get_options); + NS_MOCK(public_server_mode); + NS_MOCK(router_get_my_routerinfo); + NS_MOCK(node_get_by_id); + NS_MOCK(logv); + NS_MOCK(server_mode); + + log_global_min_severity_ = LOG_DEBUG; + onion_handshakes_requested[ONION_HANDSHAKE_TYPE_TAP] = 1; + onion_handshakes_assigned[ONION_HANDSHAKE_TYPE_TAP] = 1; + onion_handshakes_requested[ONION_HANDSHAKE_TYPE_NTOR] = 1; + onion_handshakes_assigned[ONION_HANDSHAKE_TYPE_NTOR] = 1; + + expected = 0; + actual = log_heartbeat(0); + + tt_int_op(actual, ==, expected); + tt_int_op(CALLED(logv), ==, 3); + + done: + NS_UNMOCK(tls_get_write_overhead_ratio); + NS_UNMOCK(we_are_hibernating); + NS_UNMOCK(get_options); + NS_UNMOCK(public_server_mode); + NS_UNMOCK(router_get_my_routerinfo); + NS_UNMOCK(node_get_by_id); + NS_UNMOCK(logv); + NS_UNMOCK(server_mode); + tor_free(mock_routerinfo); +} + +static double +NS(tls_get_write_overhead_ratio)(void) +{ + return 1.0; +} + +static int +NS(we_are_hibernating)(void) +{ + return 0; +} + +static const or_options_t * +NS(get_options)(void) +{ + return NULL; +} + +static int +NS(public_server_mode)(const or_options_t *options) +{ + (void)options; + + return 1; +} + +static const routerinfo_t * +NS(router_get_my_routerinfo)(void) +{ + mock_routerinfo = tor_malloc(sizeof(routerinfo_t)); + + return mock_routerinfo; +} + +static const node_t * +NS(node_get_by_id)(const char *identity_digest) +{ + (void)identity_digest; + + return NULL; +} + +static void +NS(logv)(int severity, log_domain_mask_t domain, + const char *funcname, const char *suffix, const char *format, va_list ap) +{ + switch (CALLED(logv)) + { + case 0: + tt_int_op(severity, ==, LOG_NOTICE); + tt_int_op(domain, ==, LD_HEARTBEAT); + tt_ptr_op(strstr(funcname, "log_heartbeat"), !=, NULL); + tt_ptr_op(suffix, ==, NULL); + tt_str_op(format, ==, + "Heartbeat: It seems like we are not in the cached consensus."); + break; + case 1: + tt_int_op(severity, ==, LOG_NOTICE); + tt_int_op(domain, ==, LD_HEARTBEAT); + tt_ptr_op(strstr(funcname, "log_heartbeat"), !=, NULL); + tt_ptr_op(suffix, ==, NULL); + tt_str_op(format, ==, + "Heartbeat: Tor's uptime is %s, with %d circuits open. " + "I've sent %s and received %s.%s"); + tt_str_op(va_arg(ap, char *), ==, "0:00 hours"); /* uptime */ + tt_int_op(va_arg(ap, int), ==, 0); /* count_circuits() */ + tt_str_op(va_arg(ap, char *), ==, "0 kB"); /* bw_sent */ + tt_str_op(va_arg(ap, char *), ==, "0 kB"); /* bw_rcvd */ + tt_str_op(va_arg(ap, char *), ==, ""); /* hibernating */ + break; + case 2: + tt_int_op(severity, ==, LOG_NOTICE); + tt_int_op(domain, ==, LD_HEARTBEAT); + tt_ptr_op( + strstr(funcname, "rep_hist_log_circuit_handshake_stats"), !=, NULL); + tt_ptr_op(suffix, ==, NULL); + tt_str_op(format, ==, + "Circuit handshake stats since last time: %d/%d TAP, %d/%d NTor."); + tt_int_op(va_arg(ap, int), ==, 1); /* handshakes assigned (TAP) */ + tt_int_op(va_arg(ap, int), ==, 1); /* handshakes requested (TAP) */ + tt_int_op(va_arg(ap, int), ==, 1); /* handshakes assigned (NTOR) */ + tt_int_op(va_arg(ap, int), ==, 1); /* handshakes requested (NTOR) */ + break; + default: + tt_abort_msg("unexpected call to logv()"); // TODO: prettyprint args + break; + } + + done: + CALLED(logv)++; +} + +static int +NS(server_mode)(const or_options_t *options) +{ + (void)options; + + return 0; +} + +#undef NS_SUBMODULE +#define NS_SUBMODULE ASPECT(log_heartbeat, simple) + +/* + * Tests that log_heartbeat() correctly logs heartbeat information + * normally. + */ + +NS_DECL(double, tls_get_write_overhead_ratio, (void)); +NS_DECL(int, we_are_hibernating, (void)); +NS_DECL(const or_options_t *, get_options, (void)); +NS_DECL(int, public_server_mode, (const or_options_t *options)); +NS_DECL(long, get_uptime, (void)); +NS_DECL(uint64_t, get_bytes_read, (void)); +NS_DECL(uint64_t, get_bytes_written, (void)); +NS_DECL(void, logv, (int severity, log_domain_mask_t domain, + const char *funcname, const char *suffix, const char *format, va_list ap)); +NS_DECL(int, server_mode, (const or_options_t *options)); + +static void +NS(test_main)(void *arg) +{ + int expected, actual; + (void)arg; + + NS_MOCK(tls_get_write_overhead_ratio); + NS_MOCK(we_are_hibernating); + NS_MOCK(get_options); + NS_MOCK(public_server_mode); + NS_MOCK(get_uptime); + NS_MOCK(get_bytes_read); + NS_MOCK(get_bytes_written); + NS_MOCK(logv); + NS_MOCK(server_mode); + + log_global_min_severity_ = LOG_DEBUG; + + expected = 0; + actual = log_heartbeat(0); + + tt_int_op(actual, ==, expected); + + done: + NS_UNMOCK(tls_get_write_overhead_ratio); + NS_UNMOCK(we_are_hibernating); + NS_UNMOCK(get_options); + NS_UNMOCK(public_server_mode); + NS_UNMOCK(get_uptime); + NS_UNMOCK(get_bytes_read); + NS_UNMOCK(get_bytes_written); + NS_UNMOCK(logv); + NS_UNMOCK(server_mode); +} + +static double +NS(tls_get_write_overhead_ratio)(void) +{ + return 1.0; +} + +static int +NS(we_are_hibernating)(void) +{ + return 1; +} + +static const or_options_t * +NS(get_options)(void) +{ + return NULL; +} + +static int +NS(public_server_mode)(const or_options_t *options) +{ + (void)options; + + return 0; +} + +static long +NS(get_uptime)(void) +{ + return 0; +} + +static uint64_t +NS(get_bytes_read)(void) +{ + return 0; +} + +static uint64_t +NS(get_bytes_written)(void) +{ + return 0; +} + +static void +NS(logv)(int severity, log_domain_mask_t domain, const char *funcname, + const char *suffix, const char *format, va_list ap) +{ + tt_int_op(severity, ==, LOG_NOTICE); + tt_int_op(domain, ==, LD_HEARTBEAT); + tt_ptr_op(strstr(funcname, "log_heartbeat"), !=, NULL); + tt_ptr_op(suffix, ==, NULL); + tt_str_op(format, ==, + "Heartbeat: Tor's uptime is %s, with %d circuits open. " + "I've sent %s and received %s.%s"); + tt_str_op(va_arg(ap, char *), ==, "0:00 hours"); /* uptime */ + tt_int_op(va_arg(ap, int), ==, 0); /* count_circuits() */ + tt_str_op(va_arg(ap, char *), ==, "0 kB"); /* bw_sent */ + tt_str_op(va_arg(ap, char *), ==, "0 kB"); /* bw_rcvd */ + tt_str_op(va_arg(ap, char *), ==, " We are currently hibernating."); + + done: + ; +} + +static int +NS(server_mode)(const or_options_t *options) +{ + (void)options; + + return 0; +} + +#undef NS_SUBMODULE +#define NS_SUBMODULE ASPECT(log_heartbeat, calls_log_accounting) + +/* + * Tests that log_heartbeat() correctly logs heartbeat information + * and accounting information when configured. + */ + +NS_DECL(double, tls_get_write_overhead_ratio, (void)); +NS_DECL(int, we_are_hibernating, (void)); +NS_DECL(const or_options_t *, get_options, (void)); +NS_DECL(int, public_server_mode, (const or_options_t *options)); +NS_DECL(long, get_uptime, (void)); +NS_DECL(uint64_t, get_bytes_read, (void)); +NS_DECL(uint64_t, get_bytes_written, (void)); +NS_DECL(void, logv, (int severity, log_domain_mask_t domain, + const char *funcname, const char *suffix, const char *format, va_list ap)); +NS_DECL(int, server_mode, (const or_options_t *options)); +NS_DECL(or_state_t *, get_or_state, (void)); +NS_DECL(int, accounting_is_enabled, (const or_options_t *options)); +NS_DECL(time_t, accounting_get_end_time, (void)); + +static or_state_t * NS(mock_state) = NULL; +static or_options_t * NS(mock_options) = NULL; + +static void +NS(test_main)(void *arg) +{ + int expected, actual; + (void)arg; + + NS_MOCK(tls_get_write_overhead_ratio); + NS_MOCK(we_are_hibernating); + NS_MOCK(get_options); + NS_MOCK(public_server_mode); + NS_MOCK(get_uptime); + NS_MOCK(get_bytes_read); + NS_MOCK(get_bytes_written); + NS_MOCK(logv); + NS_MOCK(server_mode); + NS_MOCK(get_or_state); + NS_MOCK(accounting_is_enabled); + NS_MOCK(accounting_get_end_time); + + log_global_min_severity_ = LOG_DEBUG; + + expected = 0; + actual = log_heartbeat(0); + + tt_int_op(actual, ==, expected); + tt_int_op(CALLED(logv), ==, 2); + + done: + NS_UNMOCK(tls_get_write_overhead_ratio); + NS_UNMOCK(we_are_hibernating); + NS_UNMOCK(get_options); + NS_UNMOCK(public_server_mode); + NS_UNMOCK(get_uptime); + NS_UNMOCK(get_bytes_read); + NS_UNMOCK(get_bytes_written); + NS_UNMOCK(logv); + NS_UNMOCK(server_mode); + NS_UNMOCK(accounting_is_enabled); + NS_UNMOCK(accounting_get_end_time); + tor_free_(NS(mock_state)); + tor_free_(NS(mock_options)); +} + +static double +NS(tls_get_write_overhead_ratio)(void) +{ + return 1.0; +} + +static int +NS(we_are_hibernating)(void) +{ + return 0; +} + +static const or_options_t * +NS(get_options)(void) +{ + NS(mock_options) = tor_malloc_zero(sizeof(or_options_t)); + NS(mock_options)->AccountingMax = 0; + + return NS(mock_options); +} + +static int +NS(public_server_mode)(const or_options_t *options) +{ + (void)options; + + return 0; +} + +static long +NS(get_uptime)(void) +{ + return 0; +} + +static uint64_t +NS(get_bytes_read)(void) +{ + return 0; +} + +static uint64_t +NS(get_bytes_written)(void) +{ + return 0; +} + +static void +NS(logv)(int severity, log_domain_mask_t domain, + const char *funcname, const char *suffix, const char *format, va_list ap) +{ + switch (CALLED(logv)) + { + case 0: + tt_int_op(severity, ==, LOG_NOTICE); + tt_int_op(domain, ==, LD_HEARTBEAT); + tt_ptr_op(strstr(funcname, "log_heartbeat"), !=, NULL); + tt_ptr_op(suffix, ==, NULL); + tt_str_op(format, ==, + "Heartbeat: Tor's uptime is %s, with %d circuits open. " + "I've sent %s and received %s.%s"); + tt_str_op(va_arg(ap, char *), ==, "0:00 hours"); /* uptime */ + tt_int_op(va_arg(ap, int), ==, 0); /* count_circuits() */ + tt_str_op(va_arg(ap, char *), ==, "0 kB"); /* bw_sent */ + tt_str_op(va_arg(ap, char *), ==, "0 kB"); /* bw_rcvd */ + tt_str_op(va_arg(ap, char *), ==, ""); /* hibernating */ + break; + case 1: + tt_int_op(severity, ==, LOG_NOTICE); + tt_int_op(domain, ==, LD_HEARTBEAT); + tt_ptr_op(strstr(funcname, "log_accounting"), !=, NULL); + tt_ptr_op(suffix, ==, NULL); + tt_str_op(format, ==, + "Heartbeat: Accounting enabled. Sent: %s / %s, Received: %s / %s. " + "The current accounting interval ends on %s, in %s."); + tt_str_op(va_arg(ap, char *), ==, "0 kB"); /* acc_sent */ + tt_str_op(va_arg(ap, char *), ==, "0 kB"); /* acc_max */ + tt_str_op(va_arg(ap, char *), ==, "0 kB"); /* acc_rcvd */ + tt_str_op(va_arg(ap, char *), ==, "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"), !=, NULL); /* end_buf */ + tt_str_op(va_arg(ap, char *), ==, "0:01 hours"); /* remaining */ + break; + default: + tt_abort_msg("unexpected call to logv()"); // TODO: prettyprint args + break; + } + + done: + CALLED(logv)++; +} + +static int +NS(server_mode)(const or_options_t *options) +{ + (void)options; + + return 1; +} + +static int +NS(accounting_is_enabled)(const or_options_t *options) +{ + (void)options; + + return 1; +} + +static time_t +NS(accounting_get_end_time)(void) +{ + return 60; +} + +static or_state_t * +NS(get_or_state)(void) +{ + NS(mock_state) = tor_malloc_zero(sizeof(or_state_t)); + NS(mock_state)->AccountingBytesReadInInterval = 0; + NS(mock_state)->AccountingBytesWrittenInInterval = 0; + + return NS(mock_state); +} + +#undef NS_SUBMODULE +#define NS_SUBMODULE ASPECT(log_heartbeat, packaged_cell_fullness) + +/* + * Tests that log_heartbeat() correctly logs packaged cell + * fullness information. + */ + +NS_DECL(double, tls_get_write_overhead_ratio, (void)); +NS_DECL(int, we_are_hibernating, (void)); +NS_DECL(const or_options_t *, get_options, (void)); +NS_DECL(int, public_server_mode, (const or_options_t *options)); +NS_DECL(long, get_uptime, (void)); +NS_DECL(uint64_t, get_bytes_read, (void)); +NS_DECL(uint64_t, get_bytes_written, (void)); +NS_DECL(void, logv, (int severity, log_domain_mask_t domain, + const char *funcname, const char *suffix, const char *format, va_list ap)); +NS_DECL(int, server_mode, (const or_options_t *options)); +NS_DECL(int, accounting_is_enabled, (const or_options_t *options)); + +static void +NS(test_main)(void *arg) +{ + int expected, actual; + (void)arg; + + NS_MOCK(tls_get_write_overhead_ratio); + NS_MOCK(we_are_hibernating); + NS_MOCK(get_options); + NS_MOCK(public_server_mode); + NS_MOCK(get_uptime); + NS_MOCK(get_bytes_read); + NS_MOCK(get_bytes_written); + NS_MOCK(logv); + NS_MOCK(server_mode); + NS_MOCK(accounting_is_enabled); + log_global_min_severity_ = LOG_DEBUG; + + stats_n_data_bytes_packaged = RELAY_PAYLOAD_SIZE; + stats_n_data_cells_packaged = 1; + expected = 0; + actual = log_heartbeat(0); + + tt_int_op(actual, ==, expected); + tt_int_op(CALLED(logv), ==, 2); + + done: + stats_n_data_bytes_packaged = 0; + stats_n_data_cells_packaged = 0; + NS_UNMOCK(tls_get_write_overhead_ratio); + NS_UNMOCK(we_are_hibernating); + NS_UNMOCK(get_options); + NS_UNMOCK(public_server_mode); + NS_UNMOCK(get_uptime); + NS_UNMOCK(get_bytes_read); + NS_UNMOCK(get_bytes_written); + NS_UNMOCK(logv); + NS_UNMOCK(server_mode); + NS_UNMOCK(accounting_is_enabled); +} + +static double +NS(tls_get_write_overhead_ratio)(void) +{ + return 1.0; +} + +static int +NS(we_are_hibernating)(void) +{ + return 0; +} + +static const or_options_t * +NS(get_options)(void) +{ + return NULL; +} + +static int +NS(public_server_mode)(const or_options_t *options) +{ + (void)options; + + return 0; +} + +static long +NS(get_uptime)(void) +{ + return 0; +} + +static uint64_t +NS(get_bytes_read)(void) +{ + return 0; +} + +static uint64_t +NS(get_bytes_written)(void) +{ + return 0; +} + +static void +NS(logv)(int severity, log_domain_mask_t domain, const char *funcname, + const char *suffix, const char *format, va_list ap) +{ + switch (CALLED(logv)) + { + case 0: + tt_int_op(severity, ==, LOG_NOTICE); + tt_int_op(domain, ==, LD_HEARTBEAT); + tt_ptr_op(strstr(funcname, "log_heartbeat"), !=, NULL); + tt_ptr_op(suffix, ==, NULL); + tt_str_op(format, ==, + "Heartbeat: Tor's uptime is %s, with %d circuits open. " + "I've sent %s and received %s.%s"); + tt_str_op(va_arg(ap, char *), ==, "0:00 hours"); /* uptime */ + tt_int_op(va_arg(ap, int), ==, 0); /* count_circuits() */ + tt_str_op(va_arg(ap, char *), ==, "0 kB"); /* bw_sent */ + tt_str_op(va_arg(ap, char *), ==, "0 kB"); /* bw_rcvd */ + tt_str_op(va_arg(ap, char *), ==, ""); /* hibernating */ + break; + case 1: + tt_int_op(severity, ==, LOG_NOTICE); + tt_int_op(domain, ==, LD_HEARTBEAT); + tt_ptr_op(strstr(funcname, "log_heartbeat"), !=, NULL); + tt_ptr_op(suffix, ==, NULL); + tt_str_op(format, ==, + "Average packaged cell fullness: %2.3f%%"); + tt_int_op(fabs(va_arg(ap, double) - 100.0) <= DBL_EPSILON, ==, 1); + break; + default: + tt_abort_msg("unexpected call to logv()"); // TODO: prettyprint args + break; + } + + done: + CALLED(logv)++; +} + +static int +NS(server_mode)(const or_options_t *options) +{ + (void)options; + + return 0; +} + +static int +NS(accounting_is_enabled)(const or_options_t *options) +{ + (void)options; + + return 0; +} + +#undef NS_SUBMODULE +#define NS_SUBMODULE ASPECT(log_heartbeat, tls_write_overhead) + +/* + * Tests that log_heartbeat() correctly logs the TLS write overhead information + * when the TLS write overhead ratio exceeds 1. + */ + +NS_DECL(double, tls_get_write_overhead_ratio, (void)); +NS_DECL(int, we_are_hibernating, (void)); +NS_DECL(const or_options_t *, get_options, (void)); +NS_DECL(int, public_server_mode, (const or_options_t *options)); +NS_DECL(long, get_uptime, (void)); +NS_DECL(uint64_t, get_bytes_read, (void)); +NS_DECL(uint64_t, get_bytes_written, (void)); +NS_DECL(void, logv, (int severity, log_domain_mask_t domain, + const char *funcname, const char *suffix, const char *format, va_list ap)); +NS_DECL(int, server_mode, (const or_options_t *options)); +NS_DECL(int, accounting_is_enabled, (const or_options_t *options)); + +static void +NS(test_main)(void *arg) +{ + int expected, actual; + (void)arg; + + NS_MOCK(tls_get_write_overhead_ratio); + NS_MOCK(we_are_hibernating); + NS_MOCK(get_options); + NS_MOCK(public_server_mode); + NS_MOCK(get_uptime); + NS_MOCK(get_bytes_read); + NS_MOCK(get_bytes_written); + NS_MOCK(logv); + NS_MOCK(server_mode); + NS_MOCK(accounting_is_enabled); + stats_n_data_cells_packaged = 0; + log_global_min_severity_ = LOG_DEBUG; + + expected = 0; + actual = log_heartbeat(0); + + tt_int_op(actual, ==, expected); + tt_int_op(CALLED(logv), ==, 2); + + done: + NS_UNMOCK(tls_get_write_overhead_ratio); + NS_UNMOCK(we_are_hibernating); + NS_UNMOCK(get_options); + NS_UNMOCK(public_server_mode); + NS_UNMOCK(get_uptime); + NS_UNMOCK(get_bytes_read); + NS_UNMOCK(get_bytes_written); + NS_UNMOCK(logv); + NS_UNMOCK(server_mode); + NS_UNMOCK(accounting_is_enabled); +} + +static double +NS(tls_get_write_overhead_ratio)(void) +{ + return 2.0; +} + +static int +NS(we_are_hibernating)(void) +{ + return 0; +} + +static const or_options_t * +NS(get_options)(void) +{ + return NULL; +} + +static int +NS(public_server_mode)(const or_options_t *options) +{ + (void)options; + + return 0; +} + +static long +NS(get_uptime)(void) +{ + return 0; +} + +static uint64_t +NS(get_bytes_read)(void) +{ + return 0; +} + +static uint64_t +NS(get_bytes_written)(void) +{ + return 0; +} + +static void +NS(logv)(int severity, log_domain_mask_t domain, + const char *funcname, const char *suffix, const char *format, va_list ap) +{ + switch (CALLED(logv)) + { + case 0: + tt_int_op(severity, ==, LOG_NOTICE); + tt_int_op(domain, ==, LD_HEARTBEAT); + tt_ptr_op(strstr(funcname, "log_heartbeat"), !=, NULL); + tt_ptr_op(suffix, ==, NULL); + tt_str_op(format, ==, + "Heartbeat: Tor's uptime is %s, with %d circuits open. " + "I've sent %s and received %s.%s"); + tt_str_op(va_arg(ap, char *), ==, "0:00 hours"); /* uptime */ + tt_int_op(va_arg(ap, int), ==, 0); /* count_circuits() */ + tt_str_op(va_arg(ap, char *), ==, "0 kB"); /* bw_sent */ + tt_str_op(va_arg(ap, char *), ==, "0 kB"); /* bw_rcvd */ + tt_str_op(va_arg(ap, char *), ==, ""); /* hibernating */ + break; + case 1: + tt_int_op(severity, ==, LOG_NOTICE); + tt_int_op(domain, ==, LD_HEARTBEAT); + tt_ptr_op(strstr(funcname, "log_heartbeat"), !=, NULL); + tt_ptr_op(suffix, ==, NULL); + tt_str_op(format, ==, "TLS write overhead: %.f%%"); + tt_int_op(fabs(va_arg(ap, double) - 100.0) <= DBL_EPSILON, ==, 1); + break; + default: + tt_abort_msg("unexpected call to logv()"); // TODO: prettyprint args + break; + } + + done: + CALLED(logv)++; +} + +static int +NS(server_mode)(const or_options_t *options) +{ + (void)options; + + return 0; +} + +static int +NS(accounting_is_enabled)(const or_options_t *options) +{ + (void)options; + + return 0; +} + +#undef NS_SUBMODULE + +struct testcase_t status_tests[] = { + TEST_CASE(count_circuits), + TEST_CASE(secs_to_uptime), + TEST_CASE(bytes_to_usage), + TEST_CASE_ASPECT(log_heartbeat, fails), + TEST_CASE_ASPECT(log_heartbeat, simple), + TEST_CASE_ASPECT(log_heartbeat, not_in_consensus), + TEST_CASE_ASPECT(log_heartbeat, calls_log_accounting), + TEST_CASE_ASPECT(log_heartbeat, packaged_cell_fullness), + TEST_CASE_ASPECT(log_heartbeat, tls_write_overhead), + END_OF_TESTCASES +}; + diff --git a/src/test/test_util.c b/src/test/test_util.c index 65d9d2f878..225fb790f9 100644 --- a/src/test/test_util.c +++ b/src/test/test_util.c @@ -4,6 +4,7 @@ /* See LICENSE for licensing information */ #include "orconfig.h" +#define COMPAT_PRIVATE #define CONTROL_PRIVATE #define MEMPOOL_PRIVATE #define UTIL_PRIVATE @@ -11,8 +12,11 @@ #include "config.h" #include "control.h" #include "test.h" +#ifdef ENABLE_MEMPOOLS #include "mempool.h" +#endif /* ENABLE_MEMPOOLS */ #include "memarea.h" +#include "util_process.h" #ifdef _WIN32 #include <tchar.h> @@ -101,6 +105,107 @@ test_util_read_file_eof_zero_bytes(void *arg) test_util_read_until_eof_impl("tor_test_fifo_empty", 0, 10000); } +/* Test the basic expected behaviour for write_chunks_to_file. + * NOTE: This will need to be updated if we ever change the tempfile location + * or extension */ +static void +test_util_write_chunks_to_file(void *arg) +{ + char *fname = NULL; + char *tempname = NULL; + char *str = NULL; + int r; + struct stat st; + + /* These should be two different sizes to ensure the data is different + * between the data file and the temp file's 'known string' */ + int temp_str_len = 1024; + int data_str_len = 512; + char *data_str = tor_malloc(data_str_len); + char *temp_str = tor_malloc(temp_str_len); + + smartlist_t *chunks = smartlist_new(); + sized_chunk_t c = {data_str, data_str_len/2}; + sized_chunk_t c2 = {data_str + data_str_len/2, data_str_len/2}; + (void)arg; + + crypto_rand(temp_str, temp_str_len); + crypto_rand(data_str, data_str_len); + + // Ensure it can write multiple chunks + + smartlist_add(chunks, &c); + smartlist_add(chunks, &c2); + + /* + * Check if it writes using a tempfile + */ + fname = tor_strdup(get_fname("write_chunks_with_tempfile")); + tor_asprintf(&tempname, "%s.tmp", fname); + + // write a known string to a file where the tempfile will be + r = write_bytes_to_file(tempname, temp_str, temp_str_len, 1); + tt_int_op(r, ==, 0); + + // call write_chunks_to_file + r = write_chunks_to_file(fname, chunks, 1, 0); + tt_int_op(r, ==, 0); + + // assert the file has been written (expected size) + str = read_file_to_str(fname, RFTS_BIN, &st); + tt_assert(str != NULL); + tt_u64_op((uint64_t)st.st_size, ==, data_str_len); + test_mem_op(data_str, ==, str, data_str_len); + tor_free(str); + + // assert that the tempfile is removed (should not leave artifacts) + str = read_file_to_str(tempname, RFTS_BIN|RFTS_IGNORE_MISSING, &st); + tt_assert(str == NULL); + + // Remove old testfile for second test + r = unlink(fname); + tt_int_op(r, ==, 0); + tor_free(fname); + tor_free(tempname); + + /* + * Check if it skips using a tempfile with flags + */ + fname = tor_strdup(get_fname("write_chunks_with_no_tempfile")); + tor_asprintf(&tempname, "%s.tmp", fname); + + // write a known string to a file where the tempfile will be + r = write_bytes_to_file(tempname, temp_str, temp_str_len, 1); + tt_int_op(r, ==, 0); + + // call write_chunks_to_file with no_tempfile = true + r = write_chunks_to_file(fname, chunks, 1, 1); + tt_int_op(r, ==, 0); + + // assert the file has been written (expected size) + str = read_file_to_str(fname, RFTS_BIN, &st); + tt_assert(str != NULL); + tt_u64_op((uint64_t)st.st_size, ==, data_str_len); + test_mem_op(data_str, ==, str, data_str_len); + tor_free(str); + + // assert the tempfile still contains the known string + str = read_file_to_str(tempname, RFTS_BIN, &st); + tt_assert(str != NULL); + tt_u64_op((uint64_t)st.st_size, ==, temp_str_len); + test_mem_op(temp_str, ==, str, temp_str_len); + + done: + unlink(fname); + unlink(tempname); + smartlist_free(chunks); + tor_free(fname); + tor_free(tempname); + tor_free(str); + tor_free(data_str); + tor_free(temp_str); +} + static void test_util_time(void) { @@ -242,7 +347,7 @@ test_util_time(void) tv.tv_sec = (time_t)1326296338; tv.tv_usec = 3060; - format_iso_time(timestr, tv.tv_sec); + format_iso_time(timestr, (time_t)tv.tv_sec); test_streq("2012-01-11 15:38:58", timestr); /* The output of format_local_iso_time will vary by timezone, and setting our timezone for testing purposes would be a nontrivial flaky pain. @@ -250,7 +355,7 @@ test_util_time(void) format_local_iso_time(timestr, tv.tv_sec); test_streq("2012-01-11 10:38:58", timestr); */ - format_iso_time_nospace(timestr, tv.tv_sec); + format_iso_time_nospace(timestr, (time_t)tv.tv_sec); test_streq("2012-01-11T15:38:58", timestr); test_eq(strlen(timestr), ISO_TIME_LEN); format_iso_time_nospace_usec(timestr, &tv); @@ -796,6 +901,64 @@ test_util_expand_filename(void) } #endif +/** Test tor_escape_str_for_pt_args(). */ +static void +test_util_escape_string_socks(void) +{ + char *escaped_string = NULL; + + /** Simple backslash escape. */ + escaped_string = tor_escape_str_for_pt_args("This is a backslash: \\",";\\"); + test_assert(escaped_string); + test_streq(escaped_string, "This is a backslash: \\\\"); + tor_free(escaped_string); + + /** Simple semicolon escape. */ + escaped_string = tor_escape_str_for_pt_args("First rule:Do not use ;",";\\"); + test_assert(escaped_string); + test_streq(escaped_string, "First rule:Do not use \\;"); + tor_free(escaped_string); + + /** Empty string. */ + escaped_string = tor_escape_str_for_pt_args("", ";\\"); + test_assert(escaped_string); + test_streq(escaped_string, ""); + tor_free(escaped_string); + + /** Escape all characters. */ + escaped_string = tor_escape_str_for_pt_args(";\\;\\", ";\\"); + test_assert(escaped_string); + test_streq(escaped_string, "\\;\\\\\\;\\\\"); + tor_free(escaped_string); + + escaped_string = tor_escape_str_for_pt_args(";", ";\\"); + test_assert(escaped_string); + test_streq(escaped_string, "\\;"); + tor_free(escaped_string); + + done: + tor_free(escaped_string); +} + +static void +test_util_string_is_key_value(void *ptr) +{ + (void)ptr; + test_assert(string_is_key_value(LOG_WARN, "key=value")); + test_assert(string_is_key_value(LOG_WARN, "k=v")); + test_assert(string_is_key_value(LOG_WARN, "key=")); + test_assert(string_is_key_value(LOG_WARN, "x=")); + test_assert(string_is_key_value(LOG_WARN, "xx=")); + test_assert(!string_is_key_value(LOG_WARN, "=value")); + test_assert(!string_is_key_value(LOG_WARN, "=x")); + test_assert(!string_is_key_value(LOG_WARN, "=")); + + /* ??? */ + /* test_assert(!string_is_key_value(LOG_WARN, "===")); */ + done: + ; +} + /** Test basic string functionality. */ static void test_util_strmisc(void) @@ -867,6 +1030,8 @@ test_util_strmisc(void) test_eq(0L, tor_parse_long("10",-2,0,100,NULL,NULL)); test_eq(68284L, tor_parse_long("10abc",16,0,70000,NULL,NULL)); test_eq(68284L, tor_parse_long("10ABC",16,0,70000,NULL,NULL)); + test_eq(0, tor_parse_long("10ABC",-1,0,70000,&i,NULL)); + test_eq(i, 0); /* Test parse_ulong */ test_eq(0UL, tor_parse_ulong("",10,0,100,NULL,NULL)); @@ -878,6 +1043,8 @@ test_util_strmisc(void) test_eq(0UL, tor_parse_ulong("8",8,0,100,NULL,NULL)); test_eq(50UL, tor_parse_ulong("50",10,50,100,NULL,NULL)); test_eq(0UL, tor_parse_ulong("-50",10,-100,100,NULL,NULL)); + test_eq(0UL, tor_parse_ulong("50",-1,50,100,&i,NULL)); + test_eq(0, i); /* Test parse_uint64 */ test_assert(U64_LITERAL(10) == tor_parse_uint64("10 x",10,0,100, &i, &cp)); @@ -890,6 +1057,9 @@ test_util_strmisc(void) test_assert(U64_LITERAL(0) == tor_parse_uint64("12345678901",10,500,INT32_MAX, &i, &cp)); test_eq(0, i); + test_assert(U64_LITERAL(0) == + tor_parse_uint64("123",-1,0,INT32_MAX, &i, &cp)); + test_eq(0, i); { /* Test parse_double */ @@ -923,7 +1093,7 @@ test_util_strmisc(void) test_eq(i, 0); test_eq(0UL, tor_parse_ulong(TOOBIG, 10, 0, ULONG_MAX, &i, NULL)); test_eq(i, 0); - test_eq(U64_LITERAL(0), tor_parse_uint64(TOOBIG, 10, + tt_u64_op(U64_LITERAL(0), ==, tor_parse_uint64(TOOBIG, 10, 0, UINT64_MAX, &i, NULL)); test_eq(i, 0); } @@ -1022,19 +1192,19 @@ test_util_strmisc(void) } /* Test str-foo functions */ - cp = tor_strdup("abcdef"); - test_assert(tor_strisnonupper(cp)); - cp[3] = 'D'; - test_assert(!tor_strisnonupper(cp)); - tor_strupper(cp); - test_streq(cp, "ABCDEF"); - tor_strlower(cp); - test_streq(cp, "abcdef"); - test_assert(tor_strisnonupper(cp)); - test_assert(tor_strisprint(cp)); - cp[3] = 3; - test_assert(!tor_strisprint(cp)); - tor_free(cp); + cp_tmp = tor_strdup("abcdef"); + test_assert(tor_strisnonupper(cp_tmp)); + cp_tmp[3] = 'D'; + test_assert(!tor_strisnonupper(cp_tmp)); + tor_strupper(cp_tmp); + test_streq(cp_tmp, "ABCDEF"); + tor_strlower(cp_tmp); + test_streq(cp_tmp, "abcdef"); + test_assert(tor_strisnonupper(cp_tmp)); + test_assert(tor_strisprint(cp_tmp)); + cp_tmp[3] = 3; + test_assert(!tor_strisprint(cp_tmp)); + tor_free(cp_tmp); /* Test memmem and memstr */ { @@ -1045,6 +1215,10 @@ test_util_strmisc(void) test_assert(!tor_memmem(haystack, 4, "cde", 3)); haystack = "ababcad"; test_eq_ptr(tor_memmem(haystack, 7, "abc", 3), haystack + 2); + test_eq_ptr(tor_memmem(haystack, 7, "ad", 2), haystack + 5); + test_eq_ptr(tor_memmem(haystack, 7, "cad", 3), haystack + 4); + test_assert(!tor_memmem(haystack, 7, "dadad", 5)); + test_assert(!tor_memmem(haystack, 7, "abcdefghij", 10)); /* memstr */ test_eq_ptr(tor_memstr(haystack, 7, "abc"), haystack + 2); test_eq_ptr(tor_memstr(haystack, 7, "cad"), haystack + 4); @@ -1117,21 +1291,21 @@ test_util_pow2(void) test_eq(tor_log2(UINT64_MAX), 63); /* Test round_to_power_of_2 */ - test_eq(round_to_power_of_2(120), 128); - test_eq(round_to_power_of_2(128), 128); - test_eq(round_to_power_of_2(130), 128); - test_eq(round_to_power_of_2(U64_LITERAL(40000000000000000)), - U64_LITERAL(1)<<55); - test_eq(round_to_power_of_2(U64_LITERAL(0xffffffffffffffff)), + tt_u64_op(round_to_power_of_2(120), ==, 128); + tt_u64_op(round_to_power_of_2(128), ==, 128); + tt_u64_op(round_to_power_of_2(130), ==, 128); + tt_u64_op(round_to_power_of_2(U64_LITERAL(40000000000000000)), ==, + U64_LITERAL(1)<<55); + tt_u64_op(round_to_power_of_2(U64_LITERAL(0xffffffffffffffff)), ==, U64_LITERAL(1)<<63); - test_eq(round_to_power_of_2(0), 1); - test_eq(round_to_power_of_2(1), 1); - test_eq(round_to_power_of_2(2), 2); - test_eq(round_to_power_of_2(3), 2); - test_eq(round_to_power_of_2(4), 4); - test_eq(round_to_power_of_2(5), 4); - test_eq(round_to_power_of_2(6), 4); - test_eq(round_to_power_of_2(7), 8); + tt_u64_op(round_to_power_of_2(0), ==, 1); + tt_u64_op(round_to_power_of_2(1), ==, 1); + tt_u64_op(round_to_power_of_2(2), ==, 2); + tt_u64_op(round_to_power_of_2(3), ==, 2); + tt_u64_op(round_to_power_of_2(4), ==, 4); + tt_u64_op(round_to_power_of_2(5), ==, 4); + tt_u64_op(round_to_power_of_2(6), ==, 4); + tt_u64_op(round_to_power_of_2(7), ==, 8); done: ; @@ -1410,14 +1584,14 @@ test_util_mmap(void) test_eq(mapping->size, strlen("Short file.")); test_streq(mapping->data, "Short file."); #ifdef _WIN32 - tor_munmap_file(mapping); + tt_int_op(0, ==, tor_munmap_file(mapping)); mapping = NULL; test_assert(unlink(fname1) == 0); #else /* make sure we can unlink. */ test_assert(unlink(fname1) == 0); test_streq(mapping->data, "Short file."); - tor_munmap_file(mapping); + tt_int_op(0, ==, tor_munmap_file(mapping)); mapping = NULL; #endif @@ -1438,7 +1612,7 @@ test_util_mmap(void) test_assert(mapping); test_eq(mapping->size, buflen); test_memeq(mapping->data, buf, buflen); - tor_munmap_file(mapping); + tt_int_op(0, ==, tor_munmap_file(mapping)); mapping = NULL; /* Now try a big aligned file. */ @@ -1447,7 +1621,7 @@ test_util_mmap(void) test_assert(mapping); test_eq(mapping->size, 16384); test_memeq(mapping->data, buf, 16384); - tor_munmap_file(mapping); + tt_int_op(0, ==, tor_munmap_file(mapping)); mapping = NULL; done: @@ -1460,8 +1634,7 @@ test_util_mmap(void) tor_free(fname3); tor_free(buf); - if (mapping) - tor_munmap_file(mapping); + tor_munmap_file(mapping); } /** Run unit tests for escaping/unescaping data for use by controllers. */ @@ -1729,6 +1902,8 @@ test_util_path_is_relative(void) ; } +#ifdef ENABLE_MEMPOOLS + /** Run unittests for memory pool allocator */ static void test_util_mempool(void) @@ -1787,6 +1962,8 @@ test_util_mempool(void) mp_pool_destroy(pool); } +#endif /* ENABLE_MEMPOOLS */ + /** Run unittests for memory area allocator */ static void test_util_memarea(void) @@ -2071,18 +2248,21 @@ test_util_asprintf(void *ptr) test_assert(cp); test_streq("simple string 100% safe", cp); test_eq(strlen(cp), r); + tor_free(cp); /* empty string */ r = tor_asprintf(&cp, "%s", ""); test_assert(cp); test_streq("", cp); test_eq(strlen(cp), r); + tor_free(cp); /* numbers (%i) */ r = tor_asprintf(&cp, "I like numbers-%2i, %i, etc.", -1, 2); test_assert(cp); test_streq("I like numbers--1, 2, etc.", cp); test_eq(strlen(cp), r); + /* don't free cp; next test uses it. */ /* numbers (%d) */ r = tor_asprintf(&cp2, "First=%d, Second=%d", 101, 202); @@ -2155,6 +2335,8 @@ test_util_listdir(void *ptr) done: tor_free(fname1); tor_free(fname2); + tor_free(fname3); + tor_free(dir1); tor_free(dirname); if (dir_contents) { SMARTLIST_FOREACH(dir_contents, char *, cp, tor_free(cp)); @@ -2223,6 +2405,7 @@ test_util_load_win_lib(void *ptr) } #endif +#ifndef _WIN32 static void clear_hex_errno(char *hex_errno) { @@ -2267,6 +2450,7 @@ test_util_exit_status(void *ptr) done: ; } +#endif #ifndef _WIN32 /** Check that fgets waits until a full line, and not return a partial line, on @@ -2355,6 +2539,19 @@ test_util_fgets_eagain(void *ptr) } #endif +#ifndef BUILDDIR +#define BUILDDIR "." +#endif + +#ifdef _WIN32 +#define notify_pending_waitpid_callbacks() STMT_NIL +#define TEST_CHILD "test-child.exe" +#define EOL "\r\n" +#else +#define TEST_CHILD (BUILDDIR "/src/test/test-child") +#define EOL "\n" +#endif + /** Helper function for testing tor_spawn_background */ static void run_util_spawn_background(const char *argv[], const char *expected_out, @@ -2374,19 +2571,28 @@ run_util_spawn_background(const char *argv[], const char *expected_out, status = tor_spawn_background(argv[0], argv, NULL, &process_handle); #endif + notify_pending_waitpid_callbacks(); + test_eq(expected_status, status); - if (status == PROCESS_STATUS_ERROR) + if (status == PROCESS_STATUS_ERROR) { + tt_ptr_op(process_handle, ==, NULL); return; + } test_assert(process_handle != NULL); test_eq(expected_status, process_handle->status); +#ifndef _WIN32 + notify_pending_waitpid_callbacks(); + tt_ptr_op(process_handle->waitpid_cb, !=, NULL); +#endif + #ifdef _WIN32 test_assert(process_handle->stdout_pipe != INVALID_HANDLE_VALUE); test_assert(process_handle->stderr_pipe != INVALID_HANDLE_VALUE); #else - test_assert(process_handle->stdout_pipe > 0); - test_assert(process_handle->stderr_pipe > 0); + test_assert(process_handle->stdout_pipe >= 0); + test_assert(process_handle->stderr_pipe >= 0); #endif /* Check stdout */ @@ -2397,12 +2603,19 @@ run_util_spawn_background(const char *argv[], const char *expected_out, test_eq(strlen(expected_out), pos); test_streq(expected_out, stdout_buf); + notify_pending_waitpid_callbacks(); + /* Check it terminated correctly */ retval = tor_get_exit_code(process_handle, 1, &exit_code); test_eq(PROCESS_EXIT_EXITED, retval); test_eq(expected_exit, exit_code); // TODO: Make test-child exit with something other than 0 +#ifndef _WIN32 + notify_pending_waitpid_callbacks(); + tt_ptr_op(process_handle->waitpid_cb, ==, NULL); +#endif + /* Check stderr */ pos = tor_read_all_from_process_stderr(process_handle, stderr_buf, sizeof(stderr_buf) - 1); @@ -2411,6 +2624,8 @@ run_util_spawn_background(const char *argv[], const char *expected_out, test_streq(expected_err, stderr_buf); test_eq(strlen(expected_err), pos); + notify_pending_waitpid_callbacks(); + done: if (process_handle) tor_process_handle_destroy(process_handle, 1); @@ -2420,29 +2635,20 @@ run_util_spawn_background(const char *argv[], const char *expected_out, static void test_util_spawn_background_ok(void *ptr) { -#ifdef _WIN32 - const char *argv[] = {"test-child.exe", "--test", NULL}; - const char *expected_out = "OUT\r\n--test\r\nSLEEPING\r\nDONE\r\n"; - const char *expected_err = "ERR\r\n"; -#else - const char *argv[] = {BUILDDIR "/src/test/test-child", "--test", NULL}; - const char *expected_out = "OUT\n--test\nSLEEPING\nDONE\n"; - const char *expected_err = "ERR\n"; -#endif + const char *argv[] = {TEST_CHILD, "--test", NULL}; + const char *expected_out = "OUT"EOL "--test"EOL "SLEEPING"EOL "DONE" EOL; + const char *expected_err = "ERR"EOL; (void)ptr; run_util_spawn_background(argv, expected_out, expected_err, 0, - PROCESS_STATUS_RUNNING); + PROCESS_STATUS_RUNNING); } /** Check that failing to find the executable works as expected */ static void test_util_spawn_background_fail(void *ptr) { -#ifndef BUILDDIR -#define BUILDDIR "." -#endif const char *argv[] = {BUILDDIR "/src/test/no-such-file", "--test", NULL}; const char *expected_err = ""; char expected_out[1024]; @@ -2463,13 +2669,13 @@ test_util_spawn_background_fail(void *ptr) "ERR: Failed to spawn background process - code %s\n", code); run_util_spawn_background(argv, expected_out, expected_err, 255, - expected_status); + expected_status); } /** Test that reading from a handle returns a partial read rather than * blocking */ static void -test_util_spawn_background_partial_read(void *ptr) +test_util_spawn_background_partial_read_impl(int exit_early) { const int expected_exit = 0; const int expected_status = PROCESS_STATUS_RUNNING; @@ -2479,22 +2685,22 @@ test_util_spawn_background_partial_read(void *ptr) process_handle_t *process_handle=NULL; int status; char stdout_buf[100], stderr_buf[100]; -#ifdef _WIN32 - const char *argv[] = {"test-child.exe", "--test", NULL}; - const char *expected_out[] = { "OUT\r\n--test\r\nSLEEPING\r\n", - "DONE\r\n", - NULL }; - const char *expected_err = "ERR\r\n"; -#else - const char *argv[] = {BUILDDIR "/src/test/test-child", "--test", NULL}; - const char *expected_out[] = { "OUT\n--test\nSLEEPING\n", - "DONE\n", + + const char *argv[] = {TEST_CHILD, "--test", NULL}; + const char *expected_out[] = { "OUT" EOL "--test" EOL "SLEEPING" EOL, + "DONE" EOL, NULL }; - const char *expected_err = "ERR\n"; + const char *expected_err = "ERR" EOL; + +#ifndef _WIN32 int eof = 0; #endif int expected_out_ctr; - (void)ptr; + + if (exit_early) { + argv[1] = "--hang"; + expected_out[0] = "OUT"EOL "--hang"EOL "SLEEPING" EOL; + } /* Start the program */ #ifdef _WIN32 @@ -2530,6 +2736,12 @@ test_util_spawn_background_partial_read(void *ptr) expected_out_ctr++; } + if (exit_early) { + tor_process_handle_destroy(process_handle, 1); + process_handle = NULL; + goto done; + } + /* The process should have exited without writing more */ #ifdef _WIN32 pos = tor_read_all_handle(process_handle->stdout_pipe, stdout_buf, @@ -2567,15 +2779,84 @@ test_util_spawn_background_partial_read(void *ptr) tor_process_handle_destroy(process_handle, 1); } +static void +test_util_spawn_background_partial_read(void *arg) +{ + (void)arg; + test_util_spawn_background_partial_read_impl(0); +} + +static void +test_util_spawn_background_exit_early(void *arg) +{ + (void)arg; + test_util_spawn_background_partial_read_impl(1); +} + +static void +test_util_spawn_background_waitpid_notify(void *arg) +{ + int retval, exit_code; + process_handle_t *process_handle=NULL; + int status; + int ms_timer; + + const char *argv[] = {TEST_CHILD, "--fast", NULL}; + + (void) arg; + +#ifdef _WIN32 + status = tor_spawn_background(NULL, argv, NULL, &process_handle); +#else + status = tor_spawn_background(argv[0], argv, NULL, &process_handle); +#endif + + tt_int_op(status, ==, PROCESS_STATUS_RUNNING); + tt_ptr_op(process_handle, !=, NULL); + + /* We're not going to look at the stdout/stderr output this time. Instead, + * we're testing whether notify_pending_waitpid_calbacks() can report the + * process exit (on unix) and/or whether tor_get_exit_code() can notice it + * (on windows) */ + +#ifndef _WIN32 + ms_timer = 30*1000; + tt_ptr_op(process_handle->waitpid_cb, !=, NULL); + while (process_handle->waitpid_cb && ms_timer > 0) { + tor_sleep_msec(100); + ms_timer -= 100; + notify_pending_waitpid_callbacks(); + } + tt_int_op(ms_timer, >, 0); + tt_ptr_op(process_handle->waitpid_cb, ==, NULL); +#endif + + ms_timer = 30*1000; + while (((retval = tor_get_exit_code(process_handle, 0, &exit_code)) + == PROCESS_EXIT_RUNNING) && ms_timer > 0) { + tor_sleep_msec(100); + ms_timer -= 100; + } + tt_int_op(ms_timer, >, 0); + + tt_int_op(retval, ==, PROCESS_EXIT_EXITED); + + done: + tor_process_handle_destroy(process_handle, 1); +} + +#undef TEST_CHILD +#undef EOL + /** - * Test for format_hex_number_for_helper_exit_status() + * Test for format_hex_number_sigsafe() */ static void test_util_format_hex_number(void *ptr) { int i, len; - char buf[HEX_ERRNO_SIZE + 1]; + char buf[33]; const struct { const char *str; unsigned int x; @@ -2584,6 +2865,8 @@ test_util_format_hex_number(void *ptr) {"1", 1}, {"273A", 0x273a}, {"FFFF", 0xffff}, + {"7FFFFFFF", 0x7fffffff}, + {"FFFFFFFF", 0xffffffff}, #if UINT_MAX >= 0xffffffff {"31BC421D", 0x31bc421d}, {"FFFFFFFF", 0xffffffff}, @@ -2594,19 +2877,73 @@ test_util_format_hex_number(void *ptr) (void)ptr; for (i = 0; test_data[i].str != NULL; ++i) { - len = format_hex_number_for_helper_exit_status(test_data[i].x, - buf, HEX_ERRNO_SIZE); + len = format_hex_number_sigsafe(test_data[i].x, buf, sizeof(buf)); + test_neq(len, 0); + test_eq(len, strlen(buf)); + test_streq(buf, test_data[i].str); + } + + test_eq(4, format_hex_number_sigsafe(0xffff, buf, 5)); + test_streq(buf, "FFFF"); + test_eq(0, format_hex_number_sigsafe(0xffff, buf, 4)); + test_eq(0, format_hex_number_sigsafe(0, buf, 1)); + + done: + return; +} + +/** + * Test for format_hex_number_sigsafe() + */ + +static void +test_util_format_dec_number(void *ptr) +{ + int i, len; + char buf[33]; + const struct { + const char *str; + unsigned int x; + } test_data[] = { + {"0", 0}, + {"1", 1}, + {"1234", 1234}, + {"12345678", 12345678}, + {"99999999", 99999999}, + {"100000000", 100000000}, + {"4294967295", 4294967295u}, +#if UINT_MAX > 0xffffffff + {"18446744073709551615", 18446744073709551615u }, +#endif + {NULL, 0} + }; + + (void)ptr; + + for (i = 0; test_data[i].str != NULL; ++i) { + len = format_dec_number_sigsafe(test_data[i].x, buf, sizeof(buf)); test_neq(len, 0); - buf[len] = '\0'; + test_eq(len, strlen(buf)); + test_streq(buf, test_data[i].str); + + len = format_dec_number_sigsafe(test_data[i].x, buf, + (int)(strlen(test_data[i].str) + 1)); + test_eq(len, strlen(buf)); test_streq(buf, test_data[i].str); } + test_eq(4, format_dec_number_sigsafe(7331, buf, 5)); + test_streq(buf, "7331"); + test_eq(0, format_dec_number_sigsafe(7331, buf, 4)); + test_eq(1, format_dec_number_sigsafe(0, buf, 2)); + test_eq(0, format_dec_number_sigsafe(0, buf, 1)); + done: return; } /** - * Test that we can properly format q Windows command line + * Test that we can properly format a Windows command line */ static void test_util_join_win_cmdline(void *ptr) @@ -2817,7 +3154,7 @@ test_util_eat_whitespace(void *ptr) (void)ptr; /* Try one leading ws */ - strcpy(str, "fuubaar"); + strlcpy(str, "fuubaar", sizeof(str)); for (i = 0; i < sizeof(ws); ++i) { str[0] = ws[i]; test_eq_ptr(str + 1, eat_whitespace(str)); @@ -2832,14 +3169,14 @@ test_util_eat_whitespace(void *ptr) test_eq_ptr(str, eat_whitespace_eos_no_nl(str, str + strlen(str))); /* Empty string */ - strcpy(str, ""); + strlcpy(str, "", sizeof(str)); test_eq_ptr(str, eat_whitespace(str)); test_eq_ptr(str, eat_whitespace_eos(str, str)); test_eq_ptr(str, eat_whitespace_no_nl(str)); test_eq_ptr(str, eat_whitespace_eos_no_nl(str, str)); /* Only ws */ - strcpy(str, " \t\r\n"); + strlcpy(str, " \t\r\n", sizeof(str)); test_eq_ptr(str + strlen(str), eat_whitespace(str)); test_eq_ptr(str + strlen(str), eat_whitespace_eos(str, str + strlen(str))); test_eq_ptr(str + strlen(str) - 1, @@ -2847,7 +3184,7 @@ test_util_eat_whitespace(void *ptr) test_eq_ptr(str + strlen(str) - 1, eat_whitespace_eos_no_nl(str, str + strlen(str))); - strcpy(str, " \t\r "); + strlcpy(str, " \t\r ", sizeof(str)); test_eq_ptr(str + strlen(str), eat_whitespace(str)); test_eq_ptr(str + strlen(str), eat_whitespace_eos(str, str + strlen(str))); @@ -2856,7 +3193,7 @@ test_util_eat_whitespace(void *ptr) eat_whitespace_eos_no_nl(str, str + strlen(str))); /* Multiple ws */ - strcpy(str, "fuubaar"); + strlcpy(str, "fuubaar", sizeof(str)); for (i = 0; i < sizeof(ws); ++i) str[i] = ws[i]; test_eq_ptr(str + sizeof(ws), eat_whitespace(str)); @@ -2866,28 +3203,28 @@ test_util_eat_whitespace(void *ptr) eat_whitespace_eos_no_nl(str, str + strlen(str))); /* Eat comment */ - strcpy(str, "# Comment \n No Comment"); + strlcpy(str, "# Comment \n No Comment", sizeof(str)); test_streq("No Comment", eat_whitespace(str)); test_streq("No Comment", eat_whitespace_eos(str, str + strlen(str))); test_eq_ptr(str, eat_whitespace_no_nl(str)); test_eq_ptr(str, eat_whitespace_eos_no_nl(str, str + strlen(str))); /* Eat comment & ws mix */ - strcpy(str, " # \t Comment \n\t\nNo Comment"); + strlcpy(str, " # \t Comment \n\t\nNo Comment", sizeof(str)); test_streq("No Comment", eat_whitespace(str)); test_streq("No Comment", eat_whitespace_eos(str, str + strlen(str))); test_eq_ptr(str + 1, eat_whitespace_no_nl(str)); test_eq_ptr(str + 1, eat_whitespace_eos_no_nl(str, str + strlen(str))); /* Eat entire comment */ - strcpy(str, "#Comment"); + strlcpy(str, "#Comment", sizeof(str)); test_eq_ptr(str + strlen(str), eat_whitespace(str)); test_eq_ptr(str + strlen(str), eat_whitespace_eos(str, str + strlen(str))); test_eq_ptr(str, eat_whitespace_no_nl(str)); test_eq_ptr(str, eat_whitespace_eos_no_nl(str, str + strlen(str))); /* Blank line, then comment */ - strcpy(str, " \t\n # Comment"); + strlcpy(str, " \t\n # Comment", sizeof(str)); test_eq_ptr(str + strlen(str), eat_whitespace(str)); test_eq_ptr(str + strlen(str), eat_whitespace_eos(str, str + strlen(str))); test_eq_ptr(str + 2, eat_whitespace_no_nl(str)); @@ -2913,6 +3250,8 @@ smartlist_new_from_text_lines(const char *lines) last_line = smartlist_pop_last(sl); if (last_line != NULL && *last_line != '\0') { smartlist_add(sl, last_line); + } else { + tor_free(last_line); } return sl; @@ -3212,12 +3551,204 @@ test_util_mathlog(void *arg) ; } +static void +test_util_round_to_next_multiple_of(void *arg) +{ + (void)arg; + + test_assert(round_uint64_to_next_multiple_of(0,1) == 0); + test_assert(round_uint64_to_next_multiple_of(0,7) == 0); + + test_assert(round_uint64_to_next_multiple_of(99,1) == 99); + test_assert(round_uint64_to_next_multiple_of(99,7) == 105); + test_assert(round_uint64_to_next_multiple_of(99,9) == 99); + + done: + ; +} + +static void +test_util_strclear(void *arg) +{ + static const char *vals[] = { "", "a", "abcdef", "abcdefgh", NULL }; + int i; + char *v = NULL; + (void)arg; + + for (i = 0; vals[i]; ++i) { + size_t n; + v = tor_strdup(vals[i]); + n = strlen(v); + tor_strclear(v); + tt_assert(tor_mem_is_zero(v, n+1)); + tor_free(v); + } + done: + tor_free(v); +} + #define UTIL_LEGACY(name) \ { #name, legacy_test_helper, 0, &legacy_setup, test_util_ ## name } #define UTIL_TEST(name, flags) \ { #name, test_util_ ## name, flags, NULL, NULL } +#ifdef FD_CLOEXEC +#define CAN_CHECK_CLOEXEC +static int +fd_is_cloexec(tor_socket_t fd) +{ + int flags = fcntl(fd, F_GETFD, 0); + return (flags & FD_CLOEXEC) == FD_CLOEXEC; +} +#endif + +#ifndef _WIN32 +#define CAN_CHECK_NONBLOCK +static int +fd_is_nonblocking(tor_socket_t fd) +{ + int flags = fcntl(fd, F_GETFL, 0); + return (flags & O_NONBLOCK) == O_NONBLOCK; +} +#endif + +static void +test_util_socket(void *arg) +{ + tor_socket_t fd1 = TOR_INVALID_SOCKET; + tor_socket_t fd2 = TOR_INVALID_SOCKET; + tor_socket_t fd3 = TOR_INVALID_SOCKET; + tor_socket_t fd4 = TOR_INVALID_SOCKET; + int n = get_n_open_sockets(); + + TT_BLATHER(("Starting with %d open sockets.", n)); + + (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); + tt_assert(SOCKET_OK(fd1)); + tt_assert(SOCKET_OK(fd2)); + tt_int_op(get_n_open_sockets(), ==, 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); + tt_assert(SOCKET_OK(fd3)); + tt_assert(SOCKET_OK(fd4)); + tt_int_op(get_n_open_sockets(), ==, n + 4); + +#ifdef CAN_CHECK_CLOEXEC + tt_int_op(fd_is_cloexec(fd1), ==, 0); + tt_int_op(fd_is_cloexec(fd2), ==, 0); + tt_int_op(fd_is_cloexec(fd3), ==, 1); + tt_int_op(fd_is_cloexec(fd4), ==, 1); +#endif +#ifdef CAN_CHECK_NONBLOCK + tt_int_op(fd_is_nonblocking(fd1), ==, 0); + tt_int_op(fd_is_nonblocking(fd2), ==, 1); + tt_int_op(fd_is_nonblocking(fd3), ==, 0); + tt_int_op(fd_is_nonblocking(fd4), ==, 1); +#endif + + tor_close_socket(fd1); + tor_close_socket(fd2); + fd1 = fd2 = TOR_INVALID_SOCKET; + tt_int_op(get_n_open_sockets(), ==, n + 2); + tor_close_socket(fd3); + tor_close_socket(fd4); + fd3 = fd4 = TOR_INVALID_SOCKET; + tt_int_op(get_n_open_sockets(), ==, n); + + done: + if (SOCKET_OK(fd1)) + tor_close_socket(fd1); + if (SOCKET_OK(fd2)) + tor_close_socket(fd2); + if (SOCKET_OK(fd3)) + tor_close_socket(fd3); + if (SOCKET_OK(fd4)) + tor_close_socket(fd4); +} + +static void * +socketpair_test_setup(const struct testcase_t *testcase) +{ + return testcase->setup_data; +} +static int +socketpair_test_cleanup(const struct testcase_t *testcase, void *ptr) +{ + (void)testcase; + (void)ptr; + return 1; +} + +static const struct testcase_setup_t socketpair_setup = { + socketpair_test_setup, socketpair_test_cleanup +}; + +/* Test for socketpair and ersatz_socketpair(). We test them both, since + * the latter is a tolerably good way to exersize tor_accept_socket(). */ +static void +test_util_socketpair(void *arg) +{ + const int ersatz = !strcmp(arg, "1"); + int (*const tor_socketpair_fn)(int, int, int, tor_socket_t[2]) = + ersatz ? tor_ersatz_socketpair : tor_socketpair; + int n = get_n_open_sockets(); + tor_socket_t fds[2] = {TOR_INVALID_SOCKET, TOR_INVALID_SOCKET}; + const int family = AF_UNIX; + + tt_int_op(0, ==, 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(), ==, n + 2); +#ifdef CAN_CHECK_CLOEXEC + tt_int_op(fd_is_cloexec(fds[0]), ==, 1); + tt_int_op(fd_is_cloexec(fds[1]), ==, 1); +#endif +#ifdef CAN_CHECK_NONBLOCK + tt_int_op(fd_is_nonblocking(fds[0]), ==, 0); + tt_int_op(fd_is_nonblocking(fds[1]), ==, 0); +#endif + + done: + if (SOCKET_OK(fds[0])) + tor_close_socket(fds[0]); + if (SOCKET_OK(fds[1])) + tor_close_socket(fds[1]); +} + +static void +test_util_max_mem(void *arg) +{ + size_t memory1, memory2; + int r, r2; + (void) arg; + + r = get_total_system_memory(&memory1); + r2 = get_total_system_memory(&memory2); + tt_int_op(r, ==, r2); + tt_uint_op(memory2, ==, memory1); + + TT_BLATHER(("System memory: "U64_FORMAT, U64_PRINTF_ARG(memory1))); + + if (r==0) { + /* You have at least a megabyte. */ + tt_uint_op(memory1, >, (1<<20)); + } else { + /* You do not have a petabyte. */ +#if SIZEOF_SIZE_T == SIZEOF_UINT64_T + tt_u64_op(memory1, <, (U64_LITERAL(1)<<50)); +#endif + } + + done: + ; +} + struct testcase_t util_tests[] = { UTIL_LEGACY(time), UTIL_TEST(parse_http_time, 0), @@ -3228,11 +3759,15 @@ struct testcase_t util_tests[] = { #ifndef _WIN32 UTIL_LEGACY(expand_filename), #endif + UTIL_LEGACY(escape_string_socks), + UTIL_LEGACY(string_is_key_value), UTIL_LEGACY(strmisc), UTIL_LEGACY(pow2), UTIL_LEGACY(gzip), UTIL_LEGACY(datadir), +#ifdef ENABLE_MEMPOOLS UTIL_LEGACY(mempool), +#endif UTIL_LEGACY(memarea), UTIL_LEGACY(control_formats), UTIL_LEGACY(mmap), @@ -3241,6 +3776,8 @@ struct testcase_t util_tests[] = { UTIL_LEGACY(path_is_relative), UTIL_LEGACY(strtok), UTIL_LEGACY(di_ops), + UTIL_TEST(round_to_next_multiple_of, 0), + UTIL_TEST(strclear, 0), UTIL_TEST(find_str_at_start_of_line, 0), UTIL_TEST(string_is_C_identifier, 0), UTIL_TEST(asprintf, 0), @@ -3249,14 +3786,17 @@ struct testcase_t util_tests[] = { #ifdef _WIN32 UTIL_TEST(load_win_lib, 0), #endif - UTIL_TEST(exit_status, 0), #ifndef _WIN32 + UTIL_TEST(exit_status, 0), UTIL_TEST(fgets_eagain, TT_SKIP), #endif UTIL_TEST(spawn_background_ok, 0), UTIL_TEST(spawn_background_fail, 0), UTIL_TEST(spawn_background_partial_read, 0), + UTIL_TEST(spawn_background_exit_early, 0), + UTIL_TEST(spawn_background_waitpid_notify, 0), UTIL_TEST(format_hex_number, 0), + UTIL_TEST(format_dec_number, 0), UTIL_TEST(join_win_cmdline, 0), UTIL_TEST(split_lines, 0), UTIL_TEST(n_bits_set, 0), @@ -3268,8 +3808,15 @@ struct testcase_t util_tests[] = { UTIL_TEST(read_file_eof_tiny_limit, 0), UTIL_TEST(read_file_eof_two_loops, 0), UTIL_TEST(read_file_eof_zero_bytes, 0), + UTIL_TEST(write_chunks_to_file, 0), UTIL_TEST(mathlog, 0), UTIL_TEST(weak_random, 0), + UTIL_TEST(socket, TT_FORK), + { "socketpair", test_util_socketpair, TT_FORK, &socketpair_setup, + (void*)"0" }, + { "socketpair_ersatz", test_util_socketpair, TT_FORK, + &socketpair_setup, (void*)"1" }, + UTIL_TEST(max_mem, 0), END_OF_TESTCASES }; |