diff options
Diffstat (limited to 'src/test/test_circuitbuild.c')
-rw-r--r-- | src/test/test_circuitbuild.c | 1828 |
1 files changed, 1817 insertions, 11 deletions
diff --git a/src/test/test_circuitbuild.c b/src/test/test_circuitbuild.c index 538f20781f..299908ce82 100644 --- a/src/test/test_circuitbuild.c +++ b/src/test/test_circuitbuild.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define CIRCUITBUILD_PRIVATE @@ -8,18 +8,35 @@ #define ENTRYNODES_PRIVATE #include "core/or/or.h" + #include "test/test.h" #include "test/test_helpers.h" #include "test/log_test_helpers.h" + +#define CONFIG_PRIVATE #include "app/config/config.h" + +#include "core/or/channel.h" #include "core/or/circuitbuild.h" #include "core/or/circuitlist.h" +#include "core/or/circuituse.h" +#include "core/or/onion.h" +#include "core/or/cell_st.h" #include "core/or/cpath_build_state_st.h" #include "core/or/extend_info_st.h" #include "core/or/origin_circuit_st.h" +#include "core/or/or_circuit_st.h" #include "feature/client/entrynodes.h" +#include "feature/nodelist/nodelist.h" +#include "feature/nodelist/node_select.h" +#include "feature/relay/circuitbuild_relay.h" +#include "feature/relay/router.h" +#include "feature/relay/routermode.h" + +#include "feature/nodelist/node_st.h" +#include "feature/nodelist/routerinfo_st.h" /* Dummy nodes smartlist for testing */ static smartlist_t dummy_nodes; @@ -27,11 +44,11 @@ static smartlist_t dummy_nodes; static extend_info_t dummy_ei; static int -mock_count_acceptable_nodes(smartlist_t *nodes) +mock_count_acceptable_nodes(const smartlist_t *nodes, int direct) { (void)nodes; - return DEFAULT_ROUTE_LEN + 1; + return direct ? 1 : DEFAULT_ROUTE_LEN + 1; } /* Test route lengths when the caller of new_route_len() doesn't @@ -114,6 +131,14 @@ test_new_route_len_unhandled_exit(void *arg) int r; (void)arg; +#ifdef ALL_BUGS_ARE_FATAL + /* Coverity (and maybe clang analyser) complain that the code following + * tt_skip() is unconditionally unreachable. */ +#if !defined(__COVERITY__) && !defined(__clang_analyzer__) + tt_skip(); +#endif +#endif /* defined(ALL_BUGS_ARE_FATAL) */ + MOCK(count_acceptable_nodes, mock_count_acceptable_nodes); tor_capture_bugs_(1); @@ -125,10 +150,10 @@ test_new_route_len_unhandled_exit(void *arg) "!(exit_ei && !known_purpose)"); expect_single_log_msg_containing("Unhandled purpose"); expect_single_log_msg_containing("with a chosen exit; assuming routelen"); - teardown_capture_of_logs(); - tor_end_capture_bugs_(); done: + teardown_capture_of_logs(); + tor_end_capture_bugs_(); UNMOCK(count_acceptable_nodes); } @@ -167,16 +192,1797 @@ test_upgrade_from_guard_wait(void *arg) tt_assert(!list); done: + smartlist_free(list); circuit_free(circ); entry_guard_free_(guard); } +static int server = 0; +static int +mock_server_mode(const or_options_t *options) +{ + (void)options; + return server; +} + +/* Test the different cases in circuit_extend_state_valid_helper(). */ +static void +test_circuit_extend_state_valid(void *arg) +{ + (void)arg; + circuit_t *circ = tor_malloc_zero(sizeof(circuit_t)); + + server = 0; + MOCK(server_mode, mock_server_mode); + + setup_full_capture_of_logs(LOG_INFO); + + /* Clients can't extend */ + server = 0; + tt_int_op(circuit_extend_state_valid_helper(NULL), OP_EQ, -1); + expect_log_msg("Got an extend cell, but running as a client. Closing.\n"); + mock_clean_saved_logs(); + +#ifndef ALL_BUGS_ARE_FATAL + /* Circuit must be non-NULL */ + tor_capture_bugs_(1); + server = 1; + tt_int_op(circuit_extend_state_valid_helper(NULL), OP_EQ, -1); + tt_int_op(smartlist_len(tor_get_captured_bug_log_()), OP_EQ, 1); + tt_str_op(smartlist_get(tor_get_captured_bug_log_(), 0), OP_EQ, + "!(ASSERT_PREDICT_UNLIKELY_(!circ))"); + tor_end_capture_bugs_(); + mock_clean_saved_logs(); +#endif /* !defined(ALL_BUGS_ARE_FATAL) */ + + /* n_chan and n_hop are NULL, this should succeed */ + server = 1; + tt_int_op(circuit_extend_state_valid_helper(circ), OP_EQ, 0); + mock_clean_saved_logs(); + + /* But clients still can't extend */ + server = 0; + tt_int_op(circuit_extend_state_valid_helper(circ), OP_EQ, -1); + expect_log_msg("Got an extend cell, but running as a client. Closing.\n"); + mock_clean_saved_logs(); + + /* n_chan must be NULL */ + circ->n_chan = tor_malloc_zero(sizeof(channel_t)); + server = 1; + tt_int_op(circuit_extend_state_valid_helper(circ), OP_EQ, -1); + expect_log_msg("n_chan already set. Bug/attack. Closing.\n"); + mock_clean_saved_logs(); + tor_free(circ->n_chan); + + /* n_hop must be NULL */ + circ->n_hop = tor_malloc_zero(sizeof(extend_info_t)); + server = 1; + tt_int_op(circuit_extend_state_valid_helper(circ), OP_EQ, -1); + expect_log_msg("conn to next hop already launched. Bug/attack. Closing.\n"); + mock_clean_saved_logs(); + tor_free(circ->n_hop); + + done: + tor_end_capture_bugs_(); + teardown_capture_of_logs(); + + UNMOCK(server_mode); + server = 0; + + tor_free(circ->n_chan); + tor_free(circ->n_hop); + tor_free(circ); +} + +static node_t *mocked_node = NULL; +static const node_t * +mock_node_get_by_id(const char *identity_digest) +{ + (void)identity_digest; + return mocked_node; +} + +static bool mocked_supports_ed25519_link_authentication = 0; +static bool +mock_node_supports_ed25519_link_authentication(const node_t *node, + bool compatible_with_us) +{ + (void)node; + (void)compatible_with_us; + return mocked_supports_ed25519_link_authentication; +} + +static ed25519_public_key_t * mocked_ed25519_id = NULL; +static const ed25519_public_key_t * +mock_node_get_ed25519_id(const node_t *node) +{ + (void)node; + return mocked_ed25519_id; +} + +/* Test the different cases in circuit_extend_add_ed25519_helper(). */ +static void +test_circuit_extend_add_ed25519(void *arg) +{ + (void)arg; + extend_cell_t *ec = tor_malloc_zero(sizeof(extend_cell_t)); + extend_cell_t *old_ec = tor_malloc_zero(sizeof(extend_cell_t)); + extend_cell_t *zero_ec = tor_malloc_zero(sizeof(extend_cell_t)); + + node_t *fake_node = tor_malloc_zero(sizeof(node_t)); + ed25519_public_key_t *fake_ed25519_id = NULL; + fake_ed25519_id = tor_malloc_zero(sizeof(ed25519_public_key_t)); + + MOCK(node_get_by_id, mock_node_get_by_id); + MOCK(node_supports_ed25519_link_authentication, + mock_node_supports_ed25519_link_authentication); + MOCK(node_get_ed25519_id, mock_node_get_ed25519_id); + + setup_full_capture_of_logs(LOG_INFO); + +#ifndef ALL_BUGS_ARE_FATAL + /* The extend cell must be non-NULL */ + tor_capture_bugs_(1); + tt_int_op(circuit_extend_add_ed25519_helper(NULL), OP_EQ, -1); + tt_int_op(smartlist_len(tor_get_captured_bug_log_()), OP_EQ, 1); + tt_str_op(smartlist_get(tor_get_captured_bug_log_(), 0), OP_EQ, + "!(ASSERT_PREDICT_UNLIKELY_(!ec))"); + tor_end_capture_bugs_(); + mock_clean_saved_logs(); +#endif /* !defined(ALL_BUGS_ARE_FATAL) */ + + /* The node id must be non-zero */ + memcpy(old_ec, ec, sizeof(extend_cell_t)); + tt_int_op(circuit_extend_add_ed25519_helper(ec), OP_EQ, -1); + expect_log_msg( + "Client asked me to extend without specifying an id_digest.\n"); + /* And nothing should have changed */ + tt_mem_op(ec, OP_EQ, old_ec, sizeof(extend_cell_t)); + mock_clean_saved_logs(); + + /* Fill in fake node_id, and try again */ + memset(ec->node_id, 0xAA, sizeof(ec->node_id)); + memcpy(old_ec, ec, sizeof(extend_cell_t)); + tt_int_op(circuit_extend_add_ed25519_helper(ec), OP_EQ, 0); + /* There's no node with that id, so the ed pubkey should still be zeroed */ + tt_mem_op(&ec->ed_pubkey, OP_EQ, &zero_ec->ed_pubkey, sizeof(ec->ed_pubkey)); + /* In fact, nothing should have changed */ + tt_mem_op(ec, OP_EQ, old_ec, sizeof(extend_cell_t)); + mock_clean_saved_logs(); + + /* Provide 2 out of 3 of node, supports link auth, and ed_id. + * The ed_id should remain zeroed. */ + + /* Provide node and supports link auth */ + memset(ec->node_id, 0xAA, sizeof(ec->node_id)); + memcpy(old_ec, ec, sizeof(extend_cell_t)); + /* Set up the fake variables */ + mocked_node = fake_node; + mocked_supports_ed25519_link_authentication = 1; + /* Do the test */ + tt_int_op(circuit_extend_add_ed25519_helper(ec), OP_EQ, 0); + /* The ed pubkey should still be zeroed */ + tt_mem_op(&ec->ed_pubkey, OP_EQ, &zero_ec->ed_pubkey, sizeof(ec->ed_pubkey)); + /* In fact, nothing should have changed */ + tt_mem_op(ec, OP_EQ, old_ec, sizeof(extend_cell_t)); + /* Cleanup */ + mock_clean_saved_logs(); + mocked_node = NULL; + mocked_supports_ed25519_link_authentication = 0; + mocked_ed25519_id = NULL; + memset(fake_ed25519_id, 0x00, sizeof(ed25519_public_key_t)); + + /* Provide supports link auth and ed id */ + memset(ec->node_id, 0xAA, sizeof(ec->node_id)); + memcpy(old_ec, ec, sizeof(extend_cell_t)); + /* Set up the fake variables */ + mocked_supports_ed25519_link_authentication = 1; + memset(fake_ed25519_id, 0xEE, sizeof(ed25519_public_key_t)); + mocked_ed25519_id = fake_ed25519_id; + /* Do the test */ + tt_int_op(circuit_extend_add_ed25519_helper(ec), OP_EQ, 0); + /* The ed pubkey should still be zeroed */ + tt_mem_op(&ec->ed_pubkey, OP_EQ, &zero_ec->ed_pubkey, sizeof(ec->ed_pubkey)); + /* In fact, nothing should have changed */ + tt_mem_op(ec, OP_EQ, old_ec, sizeof(extend_cell_t)); + /* Cleanup */ + mock_clean_saved_logs(); + mocked_node = NULL; + mocked_supports_ed25519_link_authentication = 0; + mocked_ed25519_id = NULL; + memset(fake_ed25519_id, 0x00, sizeof(ed25519_public_key_t)); + + /* Provide node and ed id */ + memset(ec->node_id, 0xAA, sizeof(ec->node_id)); + memcpy(old_ec, ec, sizeof(extend_cell_t)); + /* Set up the fake variables */ + mocked_node = fake_node; + memset(fake_ed25519_id, 0xEE, sizeof(ed25519_public_key_t)); + mocked_ed25519_id = fake_ed25519_id; + /* Do the test */ + tt_int_op(circuit_extend_add_ed25519_helper(ec), OP_EQ, 0); + /* The ed pubkey should still be zeroed */ + tt_mem_op(&ec->ed_pubkey, OP_EQ, &zero_ec->ed_pubkey, sizeof(ec->ed_pubkey)); + /* In fact, nothing should have changed */ + tt_mem_op(ec, OP_EQ, old_ec, sizeof(extend_cell_t)); + /* Cleanup */ + mock_clean_saved_logs(); + mocked_node = NULL; + mocked_supports_ed25519_link_authentication = 0; + mocked_ed25519_id = NULL; + memset(fake_ed25519_id, 0x00, sizeof(ed25519_public_key_t)); + + /* Now do the real lookup */ + memset(ec->node_id, 0xAA, sizeof(ec->node_id)); + memcpy(old_ec, ec, sizeof(extend_cell_t)); + /* Set up the fake variables */ + mocked_node = fake_node; + mocked_supports_ed25519_link_authentication = 1; + memset(fake_ed25519_id, 0xEE, sizeof(ed25519_public_key_t)); + mocked_ed25519_id = fake_ed25519_id; + /* Do the test */ + tt_int_op(circuit_extend_add_ed25519_helper(ec), OP_EQ, 0); + /* The ed pubkey should match */ + tt_mem_op(&ec->ed_pubkey, OP_EQ, fake_ed25519_id, sizeof(ec->ed_pubkey)); + /* Nothing else should have changed */ + memcpy(&ec->ed_pubkey, &old_ec->ed_pubkey, sizeof(ec->ed_pubkey)); + tt_mem_op(ec, OP_EQ, old_ec, sizeof(extend_cell_t)); + /* Cleanup */ + mock_clean_saved_logs(); + mocked_node = NULL; + mocked_supports_ed25519_link_authentication = 0; + mocked_ed25519_id = NULL; + memset(fake_ed25519_id, 0x00, sizeof(ed25519_public_key_t)); + + /* Now do the real lookup, but with a zeroed ed id */ + memset(ec->node_id, 0xAA, sizeof(ec->node_id)); + memcpy(old_ec, ec, sizeof(extend_cell_t)); + /* Set up the fake variables */ + mocked_node = fake_node; + mocked_supports_ed25519_link_authentication = 1; + memset(fake_ed25519_id, 0x00, sizeof(ed25519_public_key_t)); + mocked_ed25519_id = fake_ed25519_id; + /* Do the test */ + tt_int_op(circuit_extend_add_ed25519_helper(ec), OP_EQ, 0); + /* The ed pubkey should match */ + tt_mem_op(&ec->ed_pubkey, OP_EQ, fake_ed25519_id, sizeof(ec->ed_pubkey)); + /* Nothing else should have changed */ + memcpy(&ec->ed_pubkey, &old_ec->ed_pubkey, sizeof(ec->ed_pubkey)); + tt_mem_op(ec, OP_EQ, old_ec, sizeof(extend_cell_t)); + /* Cleanup */ + mock_clean_saved_logs(); + mocked_node = NULL; + mocked_supports_ed25519_link_authentication = 0; + mocked_ed25519_id = NULL; + memset(fake_ed25519_id, 0x00, sizeof(ed25519_public_key_t)); + + done: + UNMOCK(node_get_by_id); + UNMOCK(node_supports_ed25519_link_authentication); + UNMOCK(node_get_ed25519_id); + + tor_end_capture_bugs_(); + teardown_capture_of_logs(); + + tor_free(ec); + tor_free(old_ec); + tor_free(zero_ec); + + tor_free(fake_ed25519_id); + tor_free(fake_node); +} + +static or_options_t *mocked_options = NULL; +static const or_options_t * +mock_get_options(void) +{ + return mocked_options; +} + +#define PUBLIC_IPV4 "1.2.3.4" +#define INTERNAL_IPV4 "0.0.0.1" + +#define PUBLIC_IPV6 "1234::cdef" +#define INTERNAL_IPV6 "::1" + +#define VALID_PORT 0x1234 + +/* Test the different cases in circuit_extend_lspec_valid_helper(). */ +static void +test_circuit_extend_lspec_valid(void *arg) +{ + (void)arg; + extend_cell_t *ec = tor_malloc_zero(sizeof(extend_cell_t)); + channel_t *p_chan = tor_malloc_zero(sizeof(channel_t)); + or_circuit_t *or_circ = tor_malloc_zero(sizeof(or_circuit_t)); + circuit_t *circ = TO_CIRCUIT(or_circ); + + or_options_t *fake_options = options_new(); + MOCK(get_options, mock_get_options); + mocked_options = fake_options; + + setup_full_capture_of_logs(LOG_INFO); + +#ifndef ALL_BUGS_ARE_FATAL + /* Extend cell must be non-NULL */ + tor_capture_bugs_(1); + tt_int_op(circuit_extend_lspec_valid_helper(NULL, circ), OP_EQ, -1); + tt_int_op(smartlist_len(tor_get_captured_bug_log_()), OP_EQ, 1); + tt_str_op(smartlist_get(tor_get_captured_bug_log_(), 0), OP_EQ, + "!(ASSERT_PREDICT_UNLIKELY_(!ec))"); + tor_end_capture_bugs_(); + mock_clean_saved_logs(); + + /* Circuit must be non-NULL */ + tor_capture_bugs_(1); + tt_int_op(circuit_extend_lspec_valid_helper(ec, NULL), OP_EQ, -1); + tt_int_op(smartlist_len(tor_get_captured_bug_log_()), OP_EQ, 1); + tt_str_op(smartlist_get(tor_get_captured_bug_log_(), 0), OP_EQ, + "!(ASSERT_PREDICT_UNLIKELY_(!circ))"); + tor_end_capture_bugs_(); + mock_clean_saved_logs(); + + /* Extend cell and circuit must be non-NULL */ + tor_capture_bugs_(1); + tt_int_op(circuit_extend_lspec_valid_helper(NULL, NULL), OP_EQ, -1); + /* Since we're using IF_BUG_ONCE(), we might not log any bugs */ + tt_int_op(smartlist_len(tor_get_captured_bug_log_()), OP_GE, 0); + tt_int_op(smartlist_len(tor_get_captured_bug_log_()), OP_LE, 2); + tor_end_capture_bugs_(); + mock_clean_saved_logs(); +#endif /* !defined(ALL_BUGS_ARE_FATAL) */ + + /* IPv4 and IPv6 addr and port are all zero, this should fail */ + tt_int_op(circuit_extend_lspec_valid_helper(ec, circ), OP_EQ, -1); + expect_log_msg("Client asked me to extend to a zero destination port " + "or unspecified address '[scrubbed]'.\n"); + mock_clean_saved_logs(); + + /* Now ask for the actual address in the logs */ + fake_options->SafeLogging_ = SAFELOG_SCRUB_NONE; + + /* IPv4 port is 0, IPv6 addr and port are both zero, this should fail */ + tor_addr_parse(&ec->orport_ipv4.addr, PUBLIC_IPV4); + tt_int_op(circuit_extend_lspec_valid_helper(ec, circ), OP_EQ, -1); + expect_log_msg("Client asked me to extend to a zero destination port " + "or IPv4 address '1.2.3.4:0'.\n"); + mock_clean_saved_logs(); + tor_addr_port_make_null_ap(&ec->orport_ipv4, AF_INET); + tor_addr_port_make_null_ap(&ec->orport_ipv6, AF_INET6); + + /* IPv4 addr is 0, IPv6 addr and port are both zero, this should fail */ + ec->orport_ipv4.port = VALID_PORT; + tt_int_op(circuit_extend_lspec_valid_helper(ec, circ), OP_EQ, -1); + expect_log_msg("Client asked me to extend to a zero destination port " + "or IPv4 address '0.0.0.0:4660'.\n"); + mock_clean_saved_logs(); + ec->orport_ipv4.port = 0; + tor_addr_port_make_null_ap(&ec->orport_ipv4, AF_INET); + tor_addr_port_make_null_ap(&ec->orport_ipv6, AF_INET6); + + /* IPv4 addr is internal, and port is valid. + * (IPv6 addr and port are both zero.) + * Result depends on ExtendAllowPrivateAddresses. */ + tor_addr_parse(&ec->orport_ipv4.addr, INTERNAL_IPV4); + ec->orport_ipv4.port = VALID_PORT; + + fake_options->ExtendAllowPrivateAddresses = 0; + tt_int_op(circuit_extend_lspec_valid_helper(ec, circ), OP_EQ, -1); + expect_log_msg("Client asked me to extend " + "to a private IPv4 address '0.0.0.1'.\n"); + mock_clean_saved_logs(); + fake_options->ExtendAllowPrivateAddresses = 0; + tor_addr_port_make_null_ap(&ec->orport_ipv4, AF_INET); + tor_addr_port_make_null_ap(&ec->orport_ipv6, AF_INET6); + + /* Now do the same tests, but for IPv6 */ + + /* IPv6 port is 0, IPv4 addr and port are both zero, this should fail */ + tor_addr_parse(&ec->orport_ipv6.addr, PUBLIC_IPV6); + tt_int_op(circuit_extend_lspec_valid_helper(ec, circ), OP_EQ, -1); + expect_log_msg("Client asked me to extend to a zero destination port " + "or IPv6 address '[1234::cdef]:0'.\n"); + mock_clean_saved_logs(); + tor_addr_port_make_null_ap(&ec->orport_ipv4, AF_INET); + tor_addr_port_make_null_ap(&ec->orport_ipv6, AF_INET6); + + /* IPv6 addr is 0, IPv4 addr and port are both zero, this should fail */ + ec->orport_ipv6.port = VALID_PORT; + tt_int_op(circuit_extend_lspec_valid_helper(ec, circ), OP_EQ, -1); + expect_log_msg("Client asked me to extend to a zero destination port " + "or IPv6 address '[::]:4660'.\n"); + mock_clean_saved_logs(); + ec->orport_ipv4.port = 0; + tor_addr_port_make_null_ap(&ec->orport_ipv4, AF_INET); + tor_addr_port_make_null_ap(&ec->orport_ipv6, AF_INET6); + + /* IPv6 addr is internal, and port is valid. + * (IPv4 addr and port are both zero.) + * Result depends on ExtendAllowPrivateAddresses. */ + tor_addr_parse(&ec->orport_ipv6.addr, INTERNAL_IPV6); + ec->orport_ipv6.port = VALID_PORT; + + fake_options->ExtendAllowPrivateAddresses = 0; + tt_int_op(circuit_extend_lspec_valid_helper(ec, circ), OP_EQ, -1); + expect_log_msg("Client asked me to extend " + "to a private IPv6 address '[::1]'.\n"); + mock_clean_saved_logs(); + fake_options->ExtendAllowPrivateAddresses = 0; + tor_addr_port_make_null_ap(&ec->orport_ipv4, AF_INET); + tor_addr_port_make_null_ap(&ec->orport_ipv6, AF_INET6); + + /* Both addresses are internal. + * Result depends on ExtendAllowPrivateAddresses. */ + tor_addr_parse(&ec->orport_ipv4.addr, INTERNAL_IPV4); + ec->orport_ipv4.port = VALID_PORT; + tor_addr_parse(&ec->orport_ipv6.addr, INTERNAL_IPV6); + ec->orport_ipv6.port = VALID_PORT; + + fake_options->ExtendAllowPrivateAddresses = 0; + tt_int_op(circuit_extend_lspec_valid_helper(ec, circ), OP_EQ, -1); + expect_log_msg("Client asked me to extend " + "to a private IPv4 address '0.0.0.1'.\n"); + expect_log_msg("Client asked me to extend " + "to a private IPv6 address '[::1]'.\n"); + mock_clean_saved_logs(); + fake_options->ExtendAllowPrivateAddresses = 0; + tor_addr_port_make_null_ap(&ec->orport_ipv4, AF_INET); + tor_addr_port_make_null_ap(&ec->orport_ipv6, AF_INET6); + +#ifndef ALL_BUGS_ARE_FATAL + /* If we pass the private address check, but don't have the right + * OR circuit magic number, we trigger another bug */ + tor_addr_parse(&ec->orport_ipv4.addr, INTERNAL_IPV4); + ec->orport_ipv4.port = VALID_PORT; + tor_addr_parse(&ec->orport_ipv6.addr, INTERNAL_IPV6); + ec->orport_ipv6.port = VALID_PORT; + fake_options->ExtendAllowPrivateAddresses = 1; + + tor_capture_bugs_(1); + tt_int_op(circuit_extend_lspec_valid_helper(ec, circ), OP_EQ, -1); + tt_int_op(smartlist_len(tor_get_captured_bug_log_()), OP_EQ, 1); + tt_str_op(smartlist_get(tor_get_captured_bug_log_(), 0), OP_EQ, + "!(ASSERT_PREDICT_UNLIKELY_(circ->magic != 0x98ABC04Fu))"); + tor_end_capture_bugs_(); + mock_clean_saved_logs(); + fake_options->ExtendAllowPrivateAddresses = 0; + tor_addr_port_make_null_ap(&ec->orport_ipv4, AF_INET); + tor_addr_port_make_null_ap(&ec->orport_ipv6, AF_INET6); + + /* Fail again, but this time only set an IPv4 address. */ + tor_addr_parse(&ec->orport_ipv4.addr, INTERNAL_IPV4); + ec->orport_ipv4.port = VALID_PORT; + fake_options->ExtendAllowPrivateAddresses = 1; + tor_capture_bugs_(1); + tt_int_op(circuit_extend_lspec_valid_helper(ec, circ), OP_EQ, -1); + /* Since we're using IF_BUG_ONCE(), expect 0-1 bug logs */ + tt_int_op(smartlist_len(tor_get_captured_bug_log_()), OP_GE, 0); + tt_int_op(smartlist_len(tor_get_captured_bug_log_()), OP_LE, 1); + tor_end_capture_bugs_(); + mock_clean_saved_logs(); + fake_options->ExtendAllowPrivateAddresses = 0; +#endif /* !defined(ALL_BUGS_ARE_FATAL) */ + + /* Now set the right magic */ + or_circ->base_.magic = OR_CIRCUIT_MAGIC; + +#ifndef ALL_BUGS_ARE_FATAL + /* If we pass the OR circuit magic check, but don't have p_chan, + * we trigger another bug */ + fake_options->ExtendAllowPrivateAddresses = 1; + tor_capture_bugs_(1); + tt_int_op(circuit_extend_lspec_valid_helper(ec, circ), OP_EQ, -1); + tt_int_op(smartlist_len(tor_get_captured_bug_log_()), OP_EQ, 1); + tt_str_op(smartlist_get(tor_get_captured_bug_log_(), 0), OP_EQ, + "!(ASSERT_PREDICT_UNLIKELY_(!p_chan))"); + tor_end_capture_bugs_(); + mock_clean_saved_logs(); + fake_options->ExtendAllowPrivateAddresses = 0; + + /* We can also pass the OR circuit magic check with a public address */ + tor_addr_parse(&ec->orport_ipv4.addr, PUBLIC_IPV4); + fake_options->ExtendAllowPrivateAddresses = 0; + tor_capture_bugs_(1); + tt_int_op(circuit_extend_lspec_valid_helper(ec, circ), OP_EQ, -1); + /* Since we're using IF_BUG_ONCE(), expect 0-1 bug logs */ + tt_int_op(smartlist_len(tor_get_captured_bug_log_()), OP_GE, 0); + tt_int_op(smartlist_len(tor_get_captured_bug_log_()), OP_LE, 1); + tor_end_capture_bugs_(); + mock_clean_saved_logs(); + fake_options->ExtendAllowPrivateAddresses = 0; + + tor_addr_make_null(&ec->orport_ipv4.addr, AF_INET); + ec->orport_ipv4.port = 0x0000; +#endif /* !defined(ALL_BUGS_ARE_FATAL) */ + + /* Now let's fake a p_chan and the addresses */ + tor_addr_parse(&ec->orport_ipv4.addr, PUBLIC_IPV4); + ec->orport_ipv4.port = VALID_PORT; + or_circ->p_chan = p_chan; + + /* This is a trivial failure: node_id and p_chan->identity_digest are both + * zeroed */ + tt_int_op(circuit_extend_lspec_valid_helper(ec, circ), OP_EQ, -1); + expect_log_msg("Client asked me to extend back to the previous hop.\n"); + mock_clean_saved_logs(); + + /* Let's check with non-zero identities as well */ + memset(ec->node_id, 0xAA, sizeof(ec->node_id)); + memset(p_chan->identity_digest, 0xAA, sizeof(p_chan->identity_digest)); + + tt_int_op(circuit_extend_lspec_valid_helper(ec, circ), OP_EQ, -1); + expect_log_msg("Client asked me to extend back to the previous hop.\n"); + mock_clean_saved_logs(); + + memset(ec->node_id, 0, sizeof(ec->node_id)); + memset(p_chan->identity_digest, 0, sizeof(p_chan->identity_digest)); + + /* Let's pass the node_id test */ + memset(ec->node_id, 0xAA, sizeof(ec->node_id)); + memset(p_chan->identity_digest, 0xBB, sizeof(p_chan->identity_digest)); + + /* ed_pubkey is zero, and that's allowed, so we should succeed */ + tt_int_op(circuit_extend_lspec_valid_helper(ec, circ), OP_EQ, 0); + mock_clean_saved_logs(); + + /* Now let's check that we warn, but succeed, when only one address is + * private */ + tor_addr_parse(&ec->orport_ipv4.addr, INTERNAL_IPV4); + ec->orport_ipv4.port = VALID_PORT; + tor_addr_parse(&ec->orport_ipv6.addr, PUBLIC_IPV6); + ec->orport_ipv6.port = VALID_PORT; + fake_options->ExtendAllowPrivateAddresses = 0; + + tt_int_op(circuit_extend_lspec_valid_helper(ec, circ), OP_EQ, 0); + expect_log_msg("Client asked me to extend " + "to a private IPv4 address '0.0.0.1'.\n"); + mock_clean_saved_logs(); + tor_addr_port_make_null_ap(&ec->orport_ipv4, AF_INET); + tor_addr_port_make_null_ap(&ec->orport_ipv6, AF_INET6); + + /* Now with private IPv6 */ + tor_addr_parse(&ec->orport_ipv4.addr, PUBLIC_IPV4); + ec->orport_ipv4.port = VALID_PORT; + tor_addr_parse(&ec->orport_ipv6.addr, INTERNAL_IPV6); + ec->orport_ipv6.port = VALID_PORT; + fake_options->ExtendAllowPrivateAddresses = 0; + + tt_int_op(circuit_extend_lspec_valid_helper(ec, circ), OP_EQ, 0); + expect_log_msg("Client asked me to extend " + "to a private IPv6 address '[::1]'.\n"); + mock_clean_saved_logs(); + tor_addr_port_make_null_ap(&ec->orport_ipv4, AF_INET); + tor_addr_port_make_null_ap(&ec->orport_ipv6, AF_INET6); + + /* Now reset to public IPv4 and IPv6 */ + tor_addr_parse(&ec->orport_ipv4.addr, PUBLIC_IPV4); + ec->orport_ipv4.port = VALID_PORT; + tor_addr_parse(&ec->orport_ipv6.addr, PUBLIC_IPV6); + ec->orport_ipv6.port = VALID_PORT; + + /* Fail on matching non-zero identities */ + memset(&ec->ed_pubkey, 0xEE, sizeof(ec->ed_pubkey)); + memset(&p_chan->ed25519_identity, 0xEE, sizeof(p_chan->ed25519_identity)); + + tt_int_op(circuit_extend_lspec_valid_helper(ec, circ), OP_EQ, -1); + expect_log_msg("Client asked me to extend back to the previous hop " + "(by Ed25519 ID).\n"); + mock_clean_saved_logs(); + + memset(&ec->ed_pubkey, 0, sizeof(ec->ed_pubkey)); + memset(&p_chan->ed25519_identity, 0, sizeof(p_chan->ed25519_identity)); + + /* Succeed on different, non-zero identities */ + memset(&ec->ed_pubkey, 0xDD, sizeof(ec->ed_pubkey)); + memset(&p_chan->ed25519_identity, 0xEE, sizeof(p_chan->ed25519_identity)); + + tt_int_op(circuit_extend_lspec_valid_helper(ec, circ), OP_EQ, 0); + mock_clean_saved_logs(); + + memset(&ec->ed_pubkey, 0, sizeof(ec->ed_pubkey)); + memset(&p_chan->ed25519_identity, 0, sizeof(p_chan->ed25519_identity)); + + /* Succeed if the client knows the identity, but we don't */ + memset(&ec->ed_pubkey, 0xDD, sizeof(ec->ed_pubkey)); + memset(&p_chan->ed25519_identity, 0x00, sizeof(p_chan->ed25519_identity)); + + tt_int_op(circuit_extend_lspec_valid_helper(ec, circ), OP_EQ, 0); + mock_clean_saved_logs(); + + memset(&ec->ed_pubkey, 0, sizeof(ec->ed_pubkey)); + memset(&p_chan->ed25519_identity, 0, sizeof(p_chan->ed25519_identity)); + + /* Succeed if we know the identity, but the client doesn't */ + memset(&ec->ed_pubkey, 0x00, sizeof(ec->ed_pubkey)); + memset(&p_chan->ed25519_identity, 0xEE, sizeof(p_chan->ed25519_identity)); + + tt_int_op(circuit_extend_lspec_valid_helper(ec, circ), OP_EQ, 0); + mock_clean_saved_logs(); + + memset(&ec->ed_pubkey, 0, sizeof(ec->ed_pubkey)); + memset(&p_chan->ed25519_identity, 0, sizeof(p_chan->ed25519_identity)); + + /* Cleanup the node ids */ + memset(ec->node_id, 0, sizeof(ec->node_id)); + memset(p_chan->identity_digest, 0, sizeof(p_chan->identity_digest)); + + /* Cleanup the p_chan and the addresses */ + tor_addr_make_null(&ec->orport_ipv4.addr, AF_UNSPEC); + ec->orport_ipv4.port = 0; + or_circ->p_chan = NULL; + + done: + tor_end_capture_bugs_(); + teardown_capture_of_logs(); + + UNMOCK(get_options); + or_options_free(fake_options); + mocked_options = NULL; + + tor_free(ec); + tor_free(or_circ); + tor_free(p_chan); +} + +#define NODE_SET_IPV4(node, ipv4_addr_str, ipv4_port) { \ + tor_addr_parse(&node->ri->ipv4_addr, ipv4_addr_str); \ + node->ri->ipv4_orport = ipv4_port; \ + } + +#define NODE_CLEAR_IPV4(node) { \ + tor_addr_make_unspec(&node->ri->ipv4_addr); \ + node->ri->ipv4_orport = 0; \ + } + +#define NODE_SET_IPV6(node, ipv6_addr_str, ipv6_port) { \ + tor_addr_parse(&node->ri->ipv6_addr, ipv6_addr_str); \ + node->ri->ipv6_orport = ipv6_port; \ + } + +/* Test the different cases in circuit_extend_add_ed25519_helper(). */ +static void +test_circuit_extend_add_ip(void *arg) +{ + (void) arg; + tor_addr_t ipv4_tmp; + extend_cell_t *ec = tor_malloc_zero(sizeof(extend_cell_t)); + extend_cell_t *old_ec = tor_malloc_zero(sizeof(extend_cell_t)); + + node_t *fake_node = tor_malloc_zero(sizeof(node_t)); + routerinfo_t *ri = tor_malloc_zero(sizeof(routerinfo_t)); + + MOCK(node_get_by_id, mock_node_get_by_id); + + /* Set up the fake variables for the IPv4 test */ + fake_node->ri = ri; + mocked_node = fake_node; + memset(ec->node_id, 0xAA, sizeof(ec->node_id)); + memcpy(old_ec, ec, sizeof(extend_cell_t)); + NODE_SET_IPV4(fake_node, PUBLIC_IPV4, VALID_PORT); + + /* Do the IPv4 test */ + tt_int_op(circuit_extend_add_ipv4_helper(ec), OP_EQ, 0); + tor_addr_copy(&ipv4_tmp, &fake_node->ri->ipv4_addr); + /* The IPv4 should match */ + tt_int_op(tor_addr_compare(&ec->orport_ipv4.addr, &ipv4_tmp, CMP_SEMANTIC), + OP_EQ, 0); + tt_int_op(ec->orport_ipv4.port, OP_EQ, VALID_PORT); + + /* Set up the fake variables for the IPv6 test */ + memcpy(ec, old_ec, sizeof(extend_cell_t)); + NODE_CLEAR_IPV4(fake_node); + NODE_SET_IPV6(fake_node, PUBLIC_IPV6, VALID_PORT); + + /* Do the IPv6 test */ + tt_int_op(circuit_extend_add_ipv6_helper(ec), OP_EQ, 0); + /* The IPv6 should match */ + tt_int_op(tor_addr_compare(&ec->orport_ipv6.addr, &fake_node->ri->ipv6_addr, + CMP_SEMANTIC), OP_EQ, 0); + tt_int_op(ec->orport_ipv6.port, OP_EQ, VALID_PORT); + + /* Cleanup */ + mocked_node = NULL; + + done: + UNMOCK(node_get_by_id); + + tor_free(ec); + tor_free(old_ec); + + tor_free(ri); + tor_free(fake_node); +} + +static bool can_extend_over_ipv6_result = false; +static int mock_router_can_extend_over_ipv6_calls = 0; +static bool +mock_router_can_extend_over_ipv6(const or_options_t *options) +{ + (void)options; + mock_router_can_extend_over_ipv6_calls++; + return can_extend_over_ipv6_result; +} + +/* Test the different cases in circuit_choose_ip_ap_for_extend(). */ +static void +test_circuit_choose_ip_ap_for_extend(void *arg) +{ + (void)arg; + tor_addr_port_t ipv4_ap; + tor_addr_port_t ipv6_ap; + + /* Set up valid addresses */ + tor_addr_parse(&ipv4_ap.addr, PUBLIC_IPV4); + ipv4_ap.port = VALID_PORT; + tor_addr_parse(&ipv6_ap.addr, PUBLIC_IPV6); + ipv6_ap.port = VALID_PORT; + + or_options_t *fake_options = options_new(); + MOCK(get_options, mock_get_options); + mocked_options = fake_options; + + MOCK(router_can_extend_over_ipv6, + mock_router_can_extend_over_ipv6); + can_extend_over_ipv6_result = true; + mock_router_can_extend_over_ipv6_calls = 0; + + /* No valid addresses */ + can_extend_over_ipv6_result = true; + mock_router_can_extend_over_ipv6_calls = 0; + tt_ptr_op(circuit_choose_ip_ap_for_extend(NULL, NULL), OP_EQ, NULL); + tt_int_op(mock_router_can_extend_over_ipv6_calls, OP_EQ, 1); + + can_extend_over_ipv6_result = false; + mock_router_can_extend_over_ipv6_calls = 0; + tt_ptr_op(circuit_choose_ip_ap_for_extend(NULL, NULL), OP_EQ, NULL); + tt_int_op(mock_router_can_extend_over_ipv6_calls, OP_EQ, 1); + + /* One valid address: IPv4 */ + can_extend_over_ipv6_result = true; + mock_router_can_extend_over_ipv6_calls = 0; + tt_ptr_op(circuit_choose_ip_ap_for_extend(&ipv4_ap, NULL), OP_EQ, &ipv4_ap); + tt_int_op(mock_router_can_extend_over_ipv6_calls, OP_EQ, 1); + + can_extend_over_ipv6_result = false; + mock_router_can_extend_over_ipv6_calls = 0; + tt_ptr_op(circuit_choose_ip_ap_for_extend(&ipv4_ap, NULL), OP_EQ, &ipv4_ap); + tt_int_op(mock_router_can_extend_over_ipv6_calls, OP_EQ, 1); + + /* One valid address: IPv6 */ + can_extend_over_ipv6_result = true; + mock_router_can_extend_over_ipv6_calls = 0; + tt_ptr_op(circuit_choose_ip_ap_for_extend(NULL, &ipv6_ap), OP_EQ, &ipv6_ap); + tt_int_op(mock_router_can_extend_over_ipv6_calls, OP_EQ, 1); + + can_extend_over_ipv6_result = false; + mock_router_can_extend_over_ipv6_calls = 0; + tt_ptr_op(circuit_choose_ip_ap_for_extend(NULL, &ipv6_ap), OP_EQ, NULL); + tt_int_op(mock_router_can_extend_over_ipv6_calls, OP_EQ, 1); + + /* Two valid addresses */ + const tor_addr_port_t *chosen_addr = NULL; + + can_extend_over_ipv6_result = true; + mock_router_can_extend_over_ipv6_calls = 0; + chosen_addr = circuit_choose_ip_ap_for_extend(&ipv4_ap, &ipv6_ap); + tt_assert(chosen_addr == &ipv4_ap || chosen_addr == &ipv6_ap); + tt_int_op(mock_router_can_extend_over_ipv6_calls, OP_EQ, 1); + + can_extend_over_ipv6_result = false; + mock_router_can_extend_over_ipv6_calls = 0; + tt_ptr_op(circuit_choose_ip_ap_for_extend(&ipv4_ap, &ipv6_ap), + OP_EQ, &ipv4_ap); + tt_int_op(mock_router_can_extend_over_ipv6_calls, OP_EQ, 1); + + done: + UNMOCK(get_options); + or_options_free(fake_options); + mocked_options = NULL; + + UNMOCK(router_can_extend_over_ipv6); + + tor_free(fake_options); +} + +static int mock_circuit_close_calls = 0; +static void +mock_circuit_mark_for_close_(circuit_t *circ, int reason, + int line, const char *cfile) +{ + (void)circ; + (void)reason; + (void)line; + (void)cfile; + mock_circuit_close_calls++; +} + +static int mock_channel_connect_calls = 0; +static channel_t *mock_channel_connect_nchan = NULL; +static channel_t * +mock_channel_connect_for_circuit(const extend_info_t *ei) +{ + (void)ei; + mock_channel_connect_calls++; + return mock_channel_connect_nchan; +} + +/* Test the different cases in circuit_open_connection_for_extend(). + * Chooses different IP addresses depending on the first character in arg: + * - 4: IPv4 + * - 6: IPv6 + * - d: IPv4 and IPv6 (dual-stack) + */ +static void +test_circuit_open_connection_for_extend(void *arg) +{ + const char ip_version = ((const char *)arg)[0]; + const bool use_ipv4 = (ip_version == '4' || ip_version == 'd'); + const bool use_ipv6 = (ip_version == '6' || ip_version == 'd'); + tor_assert(use_ipv4 || use_ipv6); + + extend_cell_t *ec = tor_malloc_zero(sizeof(extend_cell_t)); + circuit_t *circ = tor_malloc_zero(sizeof(circuit_t)); + channel_t *fake_n_chan = tor_malloc_zero(sizeof(channel_t)); + + or_options_t *fake_options = options_new(); + MOCK(get_options, mock_get_options); + mocked_options = fake_options; + + MOCK(circuit_mark_for_close_, mock_circuit_mark_for_close_); + mock_circuit_close_calls = 0; + MOCK(channel_connect_for_circuit, mock_channel_connect_for_circuit); + mock_channel_connect_calls = 0; + mock_channel_connect_nchan = NULL; + + MOCK(router_can_extend_over_ipv6, + mock_router_can_extend_over_ipv6); + can_extend_over_ipv6_result = true; + + setup_full_capture_of_logs(LOG_INFO); + +#ifndef ALL_BUGS_ARE_FATAL + /* Circuit must be non-NULL */ + mock_circuit_close_calls = 0; + mock_channel_connect_calls = 0; + tor_capture_bugs_(1); + circuit_open_connection_for_extend(ec, NULL, 0); + /* We can't close a NULL circuit */ + tt_int_op(mock_circuit_close_calls, OP_EQ, 0); + tt_int_op(mock_channel_connect_calls, OP_EQ, 0); + tt_int_op(smartlist_len(tor_get_captured_bug_log_()), OP_EQ, 1); + tt_str_op(smartlist_get(tor_get_captured_bug_log_(), 0), OP_EQ, + "!(ASSERT_PREDICT_UNLIKELY_(!circ))"); + tor_end_capture_bugs_(); + mock_clean_saved_logs(); + + /* Extend cell must be non-NULL */ + mock_circuit_close_calls = 0; + mock_channel_connect_calls = 0; + tor_capture_bugs_(1); + circuit_open_connection_for_extend(NULL, circ, 0); + tt_int_op(mock_circuit_close_calls, OP_EQ, 1); + tt_int_op(mock_channel_connect_calls, OP_EQ, 0); + tt_int_op(smartlist_len(tor_get_captured_bug_log_()), OP_EQ, 1); + tt_str_op(smartlist_get(tor_get_captured_bug_log_(), 0), OP_EQ, + "!(ASSERT_PREDICT_UNLIKELY_(!ec))"); + tor_end_capture_bugs_(); + mock_clean_saved_logs(); + + /* Extend cell and circuit must be non-NULL */ + mock_circuit_close_calls = 0; + mock_channel_connect_calls = 0; + tor_capture_bugs_(1); + circuit_open_connection_for_extend(NULL, NULL, 0); + /* We can't close a NULL circuit */ + tt_int_op(mock_circuit_close_calls, OP_EQ, 0); + tt_int_op(mock_channel_connect_calls, OP_EQ, 0); + /* Since we're using IF_BUG_ONCE(), we might not log any bugs */ + tt_int_op(smartlist_len(tor_get_captured_bug_log_()), OP_GE, 0); + tt_int_op(smartlist_len(tor_get_captured_bug_log_()), OP_LE, 2); + tor_end_capture_bugs_(); + mock_clean_saved_logs(); + + /* Fail, because neither address is valid */ + mock_circuit_close_calls = 0; + mock_channel_connect_calls = 0; + tor_capture_bugs_(1); + circuit_open_connection_for_extend(ec, circ, 0); + /* Close the circuit, don't connect */ + tt_int_op(mock_circuit_close_calls, OP_EQ, 1); + tt_int_op(mock_channel_connect_calls, OP_EQ, 0); + /* Check state */ + tt_ptr_op(circ->n_hop, OP_EQ, NULL); + tt_ptr_op(circ->n_chan_create_cell, OP_EQ, NULL); + tt_int_op(circ->state, OP_EQ, 0); + /* Cleanup */ + tor_end_capture_bugs_(); + mock_clean_saved_logs(); +#endif /* !defined(ALL_BUGS_ARE_FATAL) */ + + /* Set up valid addresses */ + if (use_ipv4) { + tor_addr_parse(&ec->orport_ipv4.addr, PUBLIC_IPV4); + ec->orport_ipv4.port = VALID_PORT; + } + if (use_ipv6) { + tor_addr_parse(&ec->orport_ipv6.addr, PUBLIC_IPV6); + ec->orport_ipv6.port = VALID_PORT; + } + + /* Succeed, but don't try to open a connection */ + mock_circuit_close_calls = 0; + mock_channel_connect_calls = 0; + circuit_open_connection_for_extend(ec, circ, 0); + /* If we haven't closed the circuit, that's success */ + tt_int_op(mock_circuit_close_calls, OP_EQ, 0); + tt_int_op(mock_channel_connect_calls, OP_EQ, 0); + /* Check state */ + tt_ptr_op(circ->n_hop, OP_NE, NULL); + tt_ptr_op(circ->n_chan_create_cell, OP_NE, NULL); + tt_int_op(circ->state, OP_EQ, CIRCUIT_STATE_CHAN_WAIT); + /* Cleanup */ + mock_clean_saved_logs(); + tor_free(circ->n_hop); + tor_free(circ->n_chan_create_cell); + circ->state = 0; + + /* Try to open a connection, but fail with a NULL n_chan */ + mock_circuit_close_calls = 0; + mock_channel_connect_calls = 0; + circuit_open_connection_for_extend(ec, circ, 1); + /* Try to connect, but fail, and close the circuit */ + tt_int_op(mock_circuit_close_calls, OP_EQ, 1); + tt_int_op(mock_channel_connect_calls, OP_EQ, 1); + expect_log_msg("Launching n_chan failed. Closing circuit.\n"); + /* Check state */ + tt_ptr_op(circ->n_hop, OP_NE, NULL); + tt_ptr_op(circ->n_chan_create_cell, OP_NE, NULL); + tt_int_op(circ->state, OP_EQ, CIRCUIT_STATE_CHAN_WAIT); + /* Cleanup */ + mock_clean_saved_logs(); + tor_free(circ->n_hop); + tor_free(circ->n_chan_create_cell); + circ->state = 0; + + /* Try to open a connection, and succeed, because n_chan is not NULL */ + mock_channel_connect_nchan = fake_n_chan; + mock_circuit_close_calls = 0; + mock_channel_connect_calls = 0; + circuit_open_connection_for_extend(ec, circ, 1); + /* Connection attempt succeeded, leaving the circuit open */ + tt_int_op(mock_circuit_close_calls, OP_EQ, 0); + tt_int_op(mock_channel_connect_calls, OP_EQ, 1); + /* Check state */ + tt_ptr_op(circ->n_hop, OP_NE, NULL); + tt_ptr_op(circ->n_chan_create_cell, OP_NE, NULL); + tt_int_op(circ->state, OP_EQ, CIRCUIT_STATE_CHAN_WAIT); + /* Cleanup */ + mock_clean_saved_logs(); + tor_free(circ->n_hop); + tor_free(circ->n_chan_create_cell); + circ->state = 0; + mock_channel_connect_nchan = NULL; + + done: + tor_end_capture_bugs_(); + teardown_capture_of_logs(); + + UNMOCK(circuit_mark_for_close_); + mock_circuit_close_calls = 0; + UNMOCK(channel_connect_for_circuit); + mock_channel_connect_calls = 0; + + UNMOCK(get_options); + or_options_free(fake_options); + mocked_options = NULL; + + UNMOCK(router_can_extend_over_ipv6); + + tor_free(ec); + tor_free(circ->n_hop); + tor_free(circ->n_chan_create_cell); + tor_free(circ); + tor_free(fake_n_chan); +} + +/* Guaranteed to be initialised to zero. */ +static extend_cell_t mock_extend_cell_parse_cell_out; +static int mock_extend_cell_parse_result = 0; +static int mock_extend_cell_parse_calls = 0; + +static int +mock_extend_cell_parse(extend_cell_t *cell_out, + const uint8_t command, + const uint8_t *payload_in, + size_t payload_len) +{ + (void)command; + (void)payload_in; + (void)payload_len; + + mock_extend_cell_parse_calls++; + memcpy(cell_out, &mock_extend_cell_parse_cell_out, + sizeof(extend_cell_t)); + return mock_extend_cell_parse_result; +} + +static int mock_channel_get_for_extend_calls = 0; +static int mock_channel_get_for_extend_launch_out = 0; +static channel_t *mock_channel_get_for_extend_nchan = NULL; +static channel_t * +mock_channel_get_for_extend(const char *rsa_id_digest, + const ed25519_public_key_t *ed_id, + const tor_addr_t *target_ipv4_addr, + const tor_addr_t *target_ipv6_addr, + bool for_origin_circ, + const char **msg_out, + int *launch_out) +{ + (void)rsa_id_digest; + (void)ed_id; + (void)target_ipv4_addr; + (void)target_ipv6_addr; + (void)for_origin_circ; + + /* channel_get_for_extend() requires non-NULL arguments */ + tt_ptr_op(msg_out, OP_NE, NULL); + tt_ptr_op(launch_out, OP_NE, NULL); + + mock_channel_get_for_extend_calls++; + *msg_out = NULL; + *launch_out = mock_channel_get_for_extend_launch_out; + return mock_channel_get_for_extend_nchan; + + done: + return NULL; +} + +static const char * +mock_channel_get_canonical_remote_descr(channel_t *chan) +{ + (void)chan; + return "mock_channel_get_canonical_remote_descr()"; +} + +/* Should mock_circuit_deliver_create_cell() expect a direct connection? */ +static bool mock_circuit_deliver_create_cell_expect_direct = false; +static int mock_circuit_deliver_create_cell_calls = 0; +static int mock_circuit_deliver_create_cell_result = 0; +static int +mock_circuit_deliver_create_cell(circuit_t *circ, + const struct create_cell_t *create_cell, + int relayed) +{ + (void)create_cell; + + /* circuit_deliver_create_cell() requires non-NULL arguments, + * but we only check circ and circ->n_chan here. */ + tt_ptr_op(circ, OP_NE, NULL); + /* We expect n_chan for relayed cells. But should we also expect it for + * direct connections? */ + if (!mock_circuit_deliver_create_cell_expect_direct) + tt_ptr_op(circ->n_chan, OP_NE, NULL); + + /* We should only ever get relayed cells from extends */ + tt_int_op(relayed, OP_EQ, !mock_circuit_deliver_create_cell_expect_direct); + + mock_circuit_deliver_create_cell_calls++; + return mock_circuit_deliver_create_cell_result; + + done: + return -1; +} + +/* Test the different cases in circuit_extend(). */ +static void +test_circuit_extend(void *arg) +{ + (void)arg; + cell_t *cell = tor_malloc_zero(sizeof(cell_t)); + channel_t *p_chan = tor_malloc_zero(sizeof(channel_t)); + or_circuit_t *or_circ = tor_malloc_zero(sizeof(or_circuit_t)); + circuit_t *circ = TO_CIRCUIT(or_circ); + channel_t *fake_n_chan = tor_malloc_zero(sizeof(channel_t)); + + server = 0; + MOCK(server_mode, mock_server_mode); + + /* Mock a debug function, but otherwise ignore it */ + MOCK(channel_describe_peer, + mock_channel_get_canonical_remote_descr); + + setup_full_capture_of_logs(LOG_INFO); + +#ifndef ALL_BUGS_ARE_FATAL + /* Circuit must be non-NULL */ + tor_capture_bugs_(1); + tt_int_op(circuit_extend(cell, NULL), OP_EQ, -1); + tt_int_op(smartlist_len(tor_get_captured_bug_log_()), OP_EQ, 1); + tt_str_op(smartlist_get(tor_get_captured_bug_log_(), 0), OP_EQ, + "!(ASSERT_PREDICT_UNLIKELY_(!circ))"); + tor_end_capture_bugs_(); + mock_clean_saved_logs(); + + /* Cell must be non-NULL */ + tor_capture_bugs_(1); + tt_int_op(circuit_extend(NULL, circ), OP_EQ, -1); + tt_int_op(smartlist_len(tor_get_captured_bug_log_()), OP_EQ, 1); + tt_str_op(smartlist_get(tor_get_captured_bug_log_(), 0), OP_EQ, + "!(ASSERT_PREDICT_UNLIKELY_(!cell))"); + tor_end_capture_bugs_(); + mock_clean_saved_logs(); + + /* Extend cell and circuit must be non-NULL */ + tor_capture_bugs_(1); + tt_int_op(circuit_extend(NULL, NULL), OP_EQ, -1); + /* Since we're using IF_BUG_ONCE(), we might not log any bugs */ + tt_int_op(smartlist_len(tor_get_captured_bug_log_()), OP_GE, 0); + tt_int_op(smartlist_len(tor_get_captured_bug_log_()), OP_LE, 2); + tor_end_capture_bugs_(); + mock_clean_saved_logs(); +#endif /* !defined(ALL_BUGS_ARE_FATAL) */ + + /* Clients can't extend */ + server = 0; + tt_int_op(circuit_extend(cell, circ), OP_EQ, -1); + expect_log_msg("Got an extend cell, but running as a client. Closing.\n"); + mock_clean_saved_logs(); + + /* But servers can. Unpack the cell, but fail parsing. */ + server = 1; + tt_int_op(circuit_extend(cell, circ), OP_EQ, -1); + expect_log_msg("Can't parse extend cell. Closing circuit.\n"); + mock_clean_saved_logs(); + + /* Now mock parsing */ + MOCK(extend_cell_parse, mock_extend_cell_parse); + + /* And make parsing succeed, but fail on adding ed25519 */ + memset(&mock_extend_cell_parse_cell_out, 0, + sizeof(mock_extend_cell_parse_cell_out)); + mock_extend_cell_parse_result = 0; + mock_extend_cell_parse_calls = 0; + + tt_int_op(circuit_extend(cell, circ), OP_EQ, -1); + tt_int_op(mock_extend_cell_parse_calls, OP_EQ, 1); + expect_log_msg( + "Client asked me to extend without specifying an id_digest.\n"); + mock_clean_saved_logs(); + mock_extend_cell_parse_calls = 0; + + /* Now add a node_id. Fail the lspec check because IPv4 and port are zero. */ + memset(&mock_extend_cell_parse_cell_out.node_id, 0xAA, + sizeof(mock_extend_cell_parse_cell_out.node_id)); + + tt_int_op(circuit_extend(cell, circ), OP_EQ, -1); + tt_int_op(mock_extend_cell_parse_calls, OP_EQ, 1); + expect_log_msg("Client asked me to extend to a zero destination port " + "or unspecified address '[scrubbed]'.\n"); + mock_clean_saved_logs(); + mock_extend_cell_parse_calls = 0; + + /* Now add a valid IPv4 and port. Fail the OR circuit magic check. */ + tor_addr_parse(&mock_extend_cell_parse_cell_out.orport_ipv4.addr, + PUBLIC_IPV4); + mock_extend_cell_parse_cell_out.orport_ipv4.port = VALID_PORT; + +#ifndef ALL_BUGS_ARE_FATAL + tor_capture_bugs_(1); + tt_int_op(circuit_extend(cell, circ), OP_EQ, -1); + tt_int_op(mock_extend_cell_parse_calls, OP_EQ, 1); + tt_int_op(smartlist_len(tor_get_captured_bug_log_()), OP_EQ, 1); + tt_str_op(smartlist_get(tor_get_captured_bug_log_(), 0), OP_EQ, + "!(ASSERT_PREDICT_UNLIKELY_(circ->magic != 0x98ABC04Fu))"); + tor_end_capture_bugs_(); + mock_clean_saved_logs(); + mock_extend_cell_parse_calls = 0; +#endif /* !defined(ALL_BUGS_ARE_FATAL) */ + + /* Now add the right magic and a p_chan. */ + or_circ->base_.magic = OR_CIRCUIT_MAGIC; + or_circ->p_chan = p_chan; + + /* Mock channel_get_for_extend(), so it doesn't crash. */ + mock_channel_get_for_extend_calls = 0; + MOCK(channel_get_for_extend, mock_channel_get_for_extend); + + /* Test circuit not established, but don't launch another one */ + mock_channel_get_for_extend_launch_out = 0; + mock_channel_get_for_extend_nchan = NULL; + tt_int_op(circuit_extend(cell, circ), OP_EQ, 0); + tt_int_op(mock_extend_cell_parse_calls, OP_EQ, 1); + tt_int_op(mock_channel_get_for_extend_calls, OP_EQ, 1); + + /* cleanup */ + mock_clean_saved_logs(); + mock_extend_cell_parse_calls = 0; + mock_channel_get_for_extend_calls = 0; + /* circ and or_circ are the same object */ + tor_free(circ->n_hop); + tor_free(circ->n_chan_create_cell); + + /* Mock channel_connect_for_circuit(), so we don't crash */ + mock_channel_connect_calls = 0; + MOCK(channel_connect_for_circuit, mock_channel_connect_for_circuit); + + /* Test circuit not established, and successful launch of a channel */ + mock_channel_get_for_extend_launch_out = 1; + mock_channel_get_for_extend_nchan = NULL; + mock_channel_connect_nchan = fake_n_chan; + tt_int_op(circuit_extend(cell, circ), OP_EQ, 0); + tt_int_op(mock_extend_cell_parse_calls, OP_EQ, 1); + tt_int_op(mock_channel_get_for_extend_calls, OP_EQ, 1); + tt_int_op(mock_channel_connect_calls, OP_EQ, 1); + + /* cleanup */ + mock_clean_saved_logs(); + mock_extend_cell_parse_calls = 0; + mock_channel_get_for_extend_calls = 0; + mock_channel_connect_calls = 0; + /* circ and or_circ are the same object */ + tor_free(circ->n_hop); + tor_free(circ->n_chan_create_cell); + + /* Mock circuit_deliver_create_cell(), so it doesn't crash */ + mock_circuit_deliver_create_cell_calls = 0; + mock_circuit_deliver_create_cell_expect_direct = false; + MOCK(circuit_deliver_create_cell, mock_circuit_deliver_create_cell); + + /* Test circuit established, re-using channel, successful delivery */ + mock_channel_get_for_extend_launch_out = 0; + mock_channel_get_for_extend_nchan = fake_n_chan; + mock_channel_connect_nchan = NULL; + mock_circuit_deliver_create_cell_result = 0; + tt_int_op(circuit_extend(cell, circ), OP_EQ, 0); + tt_int_op(mock_extend_cell_parse_calls, OP_EQ, 1); + tt_int_op(mock_channel_get_for_extend_calls, OP_EQ, 1); + tt_int_op(mock_channel_connect_calls, OP_EQ, 0); + tt_int_op(mock_circuit_deliver_create_cell_calls, OP_EQ, 1); + tt_ptr_op(circ->n_chan, OP_EQ, fake_n_chan); + + /* cleanup */ + circ->n_chan = NULL; + mock_clean_saved_logs(); + mock_extend_cell_parse_calls = 0; + mock_channel_get_for_extend_calls = 0; + mock_channel_connect_calls = 0; + mock_circuit_deliver_create_cell_calls = 0; + /* circ and or_circ are the same object */ + tor_free(circ->n_hop); + tor_free(circ->n_chan_create_cell); + + /* Test circuit established, re-using channel, failed delivery */ + mock_channel_get_for_extend_launch_out = 0; + mock_channel_get_for_extend_nchan = fake_n_chan; + mock_channel_connect_nchan = NULL; + mock_circuit_deliver_create_cell_result = -1; + tt_int_op(circuit_extend(cell, circ), OP_EQ, -1); + tt_int_op(mock_extend_cell_parse_calls, OP_EQ, 1); + tt_int_op(mock_channel_get_for_extend_calls, OP_EQ, 1); + tt_int_op(mock_channel_connect_calls, OP_EQ, 0); + tt_int_op(mock_circuit_deliver_create_cell_calls, OP_EQ, 1); + tt_ptr_op(circ->n_chan, OP_EQ, fake_n_chan); + + /* cleanup */ + circ->n_chan = NULL; + mock_clean_saved_logs(); + mock_extend_cell_parse_calls = 0; + mock_channel_get_for_extend_calls = 0; + mock_channel_connect_calls = 0; + mock_circuit_deliver_create_cell_calls = 0; + /* circ and or_circ are the same object */ + tor_free(circ->n_hop); + tor_free(circ->n_chan_create_cell); + + done: + tor_end_capture_bugs_(); + teardown_capture_of_logs(); + + UNMOCK(server_mode); + server = 0; + + UNMOCK(channel_describe_peer); + + UNMOCK(extend_cell_parse); + memset(&mock_extend_cell_parse_cell_out, 0, + sizeof(mock_extend_cell_parse_cell_out)); + mock_extend_cell_parse_result = 0; + mock_extend_cell_parse_calls = 0; + + UNMOCK(channel_get_for_extend); + mock_channel_get_for_extend_calls = 0; + mock_channel_get_for_extend_launch_out = 0; + mock_channel_get_for_extend_nchan = NULL; + + UNMOCK(channel_connect_for_circuit); + mock_channel_connect_calls = 0; + mock_channel_connect_nchan = NULL; + + UNMOCK(circuit_deliver_create_cell); + mock_circuit_deliver_create_cell_calls = 0; + mock_circuit_deliver_create_cell_result = 0; + + tor_free(cell); + /* circ and or_circ are the same object */ + tor_free(circ->n_hop); + tor_free(circ->n_chan_create_cell); + tor_free(or_circ); + tor_free(p_chan); + tor_free(fake_n_chan); +} + +/* Test the different cases in onionskin_answer(). */ +static void +test_onionskin_answer(void *arg) +{ + (void)arg; + created_cell_t *created_cell = tor_malloc_zero(sizeof(created_cell_t)); + or_circuit_t *or_circ = tor_malloc_zero(sizeof(or_circuit_t)); + char keys[CPATH_KEY_MATERIAL_LEN] = {0}; + uint8_t rend_circ_nonce[DIGEST_LEN] = {0}; + + setup_full_capture_of_logs(LOG_INFO); + +#ifndef ALL_BUGS_ARE_FATAL + /* Circuit must be non-NULL */ + tor_capture_bugs_(1); + tt_int_op(onionskin_answer(NULL, created_cell, + keys, CPATH_KEY_MATERIAL_LEN, + rend_circ_nonce), OP_EQ, -1); + tt_int_op(smartlist_len(tor_get_captured_bug_log_()), OP_EQ, 1); + tt_str_op(smartlist_get(tor_get_captured_bug_log_(), 0), OP_EQ, + "!(ASSERT_PREDICT_UNLIKELY_(!circ))"); + tor_end_capture_bugs_(); + mock_clean_saved_logs(); + + /* Created cell must be non-NULL */ + tor_capture_bugs_(1); + tt_int_op(onionskin_answer(or_circ, NULL, + keys, CPATH_KEY_MATERIAL_LEN, + rend_circ_nonce), OP_EQ, -1); + tt_int_op(smartlist_len(tor_get_captured_bug_log_()), OP_EQ, 1); + tt_str_op(smartlist_get(tor_get_captured_bug_log_(), 0), OP_EQ, + "!(ASSERT_PREDICT_UNLIKELY_(!created_cell))"); + tor_end_capture_bugs_(); + mock_clean_saved_logs(); + + /* Keys must be non-NULL */ + tor_capture_bugs_(1); + tt_int_op(onionskin_answer(or_circ, created_cell, + NULL, CPATH_KEY_MATERIAL_LEN, + rend_circ_nonce), OP_EQ, -1); + tt_int_op(smartlist_len(tor_get_captured_bug_log_()), OP_EQ, 1); + tt_str_op(smartlist_get(tor_get_captured_bug_log_(), 0), OP_EQ, + "!(ASSERT_PREDICT_UNLIKELY_(!keys))"); + tor_end_capture_bugs_(); + mock_clean_saved_logs(); + + /* The rend circuit nonce must be non-NULL */ + tor_capture_bugs_(1); + tt_int_op(onionskin_answer(or_circ, created_cell, + keys, CPATH_KEY_MATERIAL_LEN, + NULL), OP_EQ, -1); + tt_int_op(smartlist_len(tor_get_captured_bug_log_()), OP_EQ, 1); + tt_str_op(smartlist_get(tor_get_captured_bug_log_(), 0), OP_EQ, + "!(ASSERT_PREDICT_UNLIKELY_(!rend_circ_nonce))"); + tor_end_capture_bugs_(); + mock_clean_saved_logs(); +#endif /* !defined(ALL_BUGS_ARE_FATAL) */ + + /* Also, the keys length must be CPATH_KEY_MATERIAL_LEN, but we can't catch + * asserts in unit tests. */ + + /* Fail when formatting the created cell */ + tt_int_op(onionskin_answer(or_circ, created_cell, + keys, CPATH_KEY_MATERIAL_LEN, + rend_circ_nonce), OP_EQ, -1); + expect_log_msg("couldn't format created cell (type=0, len=0).\n"); + mock_clean_saved_logs(); + + /* TODO: test the rest of onionskin_answer(), see #33860 */ + /* TODO: mock created_cell_format for the next test */ + + done: + tor_end_capture_bugs_(); + teardown_capture_of_logs(); + + tor_free(created_cell); + tor_free(or_circ); +} + +/* Test the different cases in origin_circuit_init(). */ +static void +test_origin_circuit_init(void *arg) +{ + (void)arg; + origin_circuit_t *origin_circ = NULL; + + /* Init with 0 purpose and 0 flags */ + origin_circ = origin_circuit_init(0, 0); + tt_int_op(origin_circ->base_.purpose, OP_EQ, 0); + tt_int_op(origin_circ->base_.state, OP_EQ, CIRCUIT_STATE_CHAN_WAIT); + tt_ptr_op(origin_circ->build_state, OP_NE, NULL); + tt_int_op(origin_circ->build_state->is_internal, OP_EQ, 0); + tt_int_op(origin_circ->build_state->is_ipv6_selftest, OP_EQ, 0); + tt_int_op(origin_circ->build_state->need_capacity, OP_EQ, 0); + tt_int_op(origin_circ->build_state->need_uptime, OP_EQ, 0); + tt_int_op(origin_circ->build_state->onehop_tunnel, OP_EQ, 0); + /* The circuits are automatically freed by the circuitlist. */ + + /* Init with a purpose */ + origin_circ = origin_circuit_init(CIRCUIT_PURPOSE_C_GENERAL, 0); + tt_int_op(origin_circ->base_.purpose, OP_EQ, CIRCUIT_PURPOSE_C_GENERAL); + + /* Init with each flag */ + origin_circ = origin_circuit_init(0, CIRCLAUNCH_IS_INTERNAL); + tt_ptr_op(origin_circ->build_state, OP_NE, NULL); + tt_int_op(origin_circ->build_state->is_internal, OP_EQ, 1); + tt_int_op(origin_circ->build_state->is_ipv6_selftest, OP_EQ, 0); + tt_int_op(origin_circ->build_state->need_capacity, OP_EQ, 0); + tt_int_op(origin_circ->build_state->need_uptime, OP_EQ, 0); + tt_int_op(origin_circ->build_state->onehop_tunnel, OP_EQ, 0); + + origin_circ = origin_circuit_init(0, CIRCLAUNCH_IS_IPV6_SELFTEST); + tt_ptr_op(origin_circ->build_state, OP_NE, NULL); + tt_int_op(origin_circ->build_state->is_internal, OP_EQ, 0); + tt_int_op(origin_circ->build_state->is_ipv6_selftest, OP_EQ, 1); + tt_int_op(origin_circ->build_state->need_capacity, OP_EQ, 0); + tt_int_op(origin_circ->build_state->need_uptime, OP_EQ, 0); + tt_int_op(origin_circ->build_state->onehop_tunnel, OP_EQ, 0); + + origin_circ = origin_circuit_init(0, CIRCLAUNCH_NEED_CAPACITY); + tt_ptr_op(origin_circ->build_state, OP_NE, NULL); + tt_int_op(origin_circ->build_state->is_internal, OP_EQ, 0); + tt_int_op(origin_circ->build_state->is_ipv6_selftest, OP_EQ, 0); + tt_int_op(origin_circ->build_state->need_capacity, OP_EQ, 1); + tt_int_op(origin_circ->build_state->need_uptime, OP_EQ, 0); + tt_int_op(origin_circ->build_state->onehop_tunnel, OP_EQ, 0); + + origin_circ = origin_circuit_init(0, CIRCLAUNCH_NEED_UPTIME); + tt_ptr_op(origin_circ->build_state, OP_NE, NULL); + tt_int_op(origin_circ->build_state->is_internal, OP_EQ, 0); + tt_int_op(origin_circ->build_state->is_ipv6_selftest, OP_EQ, 0); + tt_int_op(origin_circ->build_state->need_capacity, OP_EQ, 0); + tt_int_op(origin_circ->build_state->need_uptime, OP_EQ, 1); + tt_int_op(origin_circ->build_state->onehop_tunnel, OP_EQ, 0); + + origin_circ = origin_circuit_init(0, CIRCLAUNCH_ONEHOP_TUNNEL); + tt_ptr_op(origin_circ->build_state, OP_NE, NULL); + tt_int_op(origin_circ->build_state->is_internal, OP_EQ, 0); + tt_int_op(origin_circ->build_state->is_ipv6_selftest, OP_EQ, 0); + tt_int_op(origin_circ->build_state->need_capacity, OP_EQ, 0); + tt_int_op(origin_circ->build_state->need_uptime, OP_EQ, 0); + tt_int_op(origin_circ->build_state->onehop_tunnel, OP_EQ, 1); + + done: + /* The circuits are automatically freed by the circuitlist. */ + ; +} + +/* Test the different cases in circuit_send_next_onion_skin(). */ +static void +test_circuit_send_next_onion_skin(void *arg) +{ + (void)arg; + origin_circuit_t *origin_circ = NULL; + struct timeval circ_start_time; + memset(&circ_start_time, 0, sizeof(circ_start_time)); + + extend_info_t fakehop; + memset(&fakehop, 0, sizeof(fakehop)); + extend_info_t *single_fakehop = &fakehop; + extend_info_t *multi_fakehop[DEFAULT_ROUTE_LEN] = {&fakehop, + &fakehop, + &fakehop}; + + extend_info_t ipv6_hop; + memset(&ipv6_hop, 0, sizeof(ipv6_hop)); + tor_addr_parse(&ipv6_hop.orports[0].addr, "1::2"); + extend_info_t *multi_ipv6_hop[DEFAULT_ROUTE_LEN] = {&ipv6_hop, + &ipv6_hop, + &ipv6_hop}; + + extend_info_t ipv4_hop; + memset(&ipv4_hop, 0, sizeof(ipv4_hop)); + tor_addr_from_ipv4h(&ipv4_hop.orports[0].addr, 0x20304050); + extend_info_t *multi_ipv4_hop[DEFAULT_ROUTE_LEN] = {&ipv4_hop, + &ipv4_hop, + &ipv4_hop}; + + mock_circuit_deliver_create_cell_expect_direct = false; + MOCK(circuit_deliver_create_cell, mock_circuit_deliver_create_cell); + server = 0; + MOCK(server_mode, mock_server_mode); + + /* Try a direct connection, and succeed on a client */ + server = 0; + origin_circ = new_test_origin_circuit(false, + circ_start_time, + 1, + &single_fakehop); + tt_ptr_op(origin_circ, OP_NE, NULL); + /* Skip some of the multi-hop checks */ + origin_circ->build_state->onehop_tunnel = 1; + /* This is a direct connection */ + mock_circuit_deliver_create_cell_expect_direct = true; + tt_int_op(circuit_send_next_onion_skin(origin_circ), OP_EQ, 0); + /* The circuits are automatically freed by the circuitlist. */ + + /* Try a direct connection, and succeed on a server */ + server = 1; + origin_circ = new_test_origin_circuit(false, + circ_start_time, + 1, + &single_fakehop); + tt_ptr_op(origin_circ, OP_NE, NULL); + origin_circ->build_state->onehop_tunnel = 1; + mock_circuit_deliver_create_cell_expect_direct = true; + tt_int_op(circuit_send_next_onion_skin(origin_circ), OP_EQ, 0); + + /* Start capturing bugs */ + setup_full_capture_of_logs(LOG_WARN); + tor_capture_bugs_(1); + + /* Try an extend, but fail the client valid address family check */ + server = 0; + origin_circ = new_test_origin_circuit(true, + circ_start_time, + ARRAY_LENGTH(multi_fakehop), + multi_fakehop); + tt_ptr_op(origin_circ, OP_NE, NULL); + /* Fix the state */ + origin_circ->base_.state = 0; + /* This is an indirect connection */ + mock_circuit_deliver_create_cell_expect_direct = false; + /* Fail because the address family is invalid */ + tt_int_op(circuit_send_next_onion_skin(origin_circ), OP_EQ, + -END_CIRC_REASON_INTERNAL); + expect_log_msg("No supported address family found in extend_info.\n"); + mock_clean_saved_logs(); + + /* Try an extend, but fail the server valid address check */ + server = 1; + origin_circ = new_test_origin_circuit(true, + circ_start_time, + ARRAY_LENGTH(multi_fakehop), + multi_fakehop); + tt_ptr_op(origin_circ, OP_NE, NULL); + origin_circ->base_.state = 0; + mock_circuit_deliver_create_cell_expect_direct = false; + tt_int_op(circuit_send_next_onion_skin(origin_circ), OP_EQ, + -END_CIRC_REASON_INTERNAL); + expect_log_msg("No supported address family found in extend_info.\n"); + mock_clean_saved_logs(); + + /* Try an extend, but fail in the client code, with an IPv6 address */ + server = 0; + origin_circ = new_test_origin_circuit(true, + circ_start_time, + ARRAY_LENGTH(multi_ipv6_hop), + multi_ipv6_hop); + tt_ptr_op(origin_circ, OP_NE, NULL); + origin_circ->base_.state = 0; + mock_circuit_deliver_create_cell_expect_direct = false; + tt_int_op(circuit_send_next_onion_skin(origin_circ), OP_EQ, + -END_CIRC_REASON_INTERNAL); + expect_log_msg("No supported address family found in extend_info.\n"); + mock_clean_saved_logs(); + + /* Stop capturing bugs, but keep capturing logs */ + tor_end_capture_bugs_(); + + /* Try an extend, pass the client IPv4 check, but fail later */ + server = 0; + origin_circ = new_test_origin_circuit(true, + circ_start_time, + ARRAY_LENGTH(multi_ipv4_hop), + multi_ipv4_hop); + tt_ptr_op(origin_circ, OP_NE, NULL); + origin_circ->base_.state = 0; + mock_circuit_deliver_create_cell_expect_direct = false; + /* Fail because the circuit data is invalid */ + tt_int_op(circuit_send_next_onion_skin(origin_circ), OP_EQ, + -END_CIRC_REASON_INTERNAL); + expect_log_msg("onion_skin_create failed.\n"); + mock_clean_saved_logs(); + + /* Try an extend, pass the server IPv4 check, but fail later */ + server = 1; + origin_circ = new_test_origin_circuit(true, + circ_start_time, + ARRAY_LENGTH(multi_ipv4_hop), + multi_ipv4_hop); + tt_ptr_op(origin_circ, OP_NE, NULL); + origin_circ->base_.state = 0; + mock_circuit_deliver_create_cell_expect_direct = false; + tt_int_op(circuit_send_next_onion_skin(origin_circ), OP_EQ, + -END_CIRC_REASON_INTERNAL); + expect_log_msg("onion_skin_create failed.\n"); + mock_clean_saved_logs(); + + /* Try an extend, pass the server IPv6 check, but fail later */ + server = 1; + origin_circ = new_test_origin_circuit(true, + circ_start_time, + ARRAY_LENGTH(multi_ipv6_hop), + multi_ipv6_hop); + tt_ptr_op(origin_circ, OP_NE, NULL); + origin_circ->base_.state = 0; + mock_circuit_deliver_create_cell_expect_direct = false; + tt_int_op(circuit_send_next_onion_skin(origin_circ), OP_EQ, + -END_CIRC_REASON_INTERNAL); + expect_log_msg("onion_skin_create failed.\n"); + mock_clean_saved_logs(); + + /* Things we're not testing right now: + * - the addresses in the extend cell inside + * circuit_send_intermediate_onion_skin() matches the address in the + * supplied extend_info. + * - valid circuit data. + * - actually extending the circuit to each hop. */ + + done: + tor_end_capture_bugs_(); + mock_clean_saved_logs(); + teardown_capture_of_logs(); + + UNMOCK(circuit_deliver_create_cell); + UNMOCK(server_mode); + server = 0; + + /* The circuits are automatically freed by the circuitlist. */ +} + +/* Test the different cases in cpath_build_state_to_crn_flags(). */ +static void +test_cpath_build_state_to_crn_flags(void *arg) +{ + (void)arg; + + cpath_build_state_t state; + memset(&state, 0, sizeof(state)); + + tt_int_op(cpath_build_state_to_crn_flags(&state), OP_EQ, + 0); + + memset(&state, 0, sizeof(state)); + state.need_uptime = 1; + tt_int_op(cpath_build_state_to_crn_flags(&state), OP_EQ, + CRN_NEED_UPTIME); + + memset(&state, 0, sizeof(state)); + state.need_capacity = 1; + tt_int_op(cpath_build_state_to_crn_flags(&state), OP_EQ, + CRN_NEED_CAPACITY); + + memset(&state, 0, sizeof(state)); + state.need_capacity = 1; + state.need_uptime = 1; + tt_int_op(cpath_build_state_to_crn_flags(&state), OP_EQ, + CRN_NEED_CAPACITY | CRN_NEED_UPTIME); + + /* Check that no other flags are handled */ + memset(&state, 0xff, sizeof(state)); + tt_int_op(cpath_build_state_to_crn_flags(&state), OP_EQ, + CRN_NEED_CAPACITY | CRN_NEED_UPTIME); + + done: + ; +} + +/* Test the different cases in cpath_build_state_to_crn_ipv6_extend_flag(). */ +static void +test_cpath_build_state_to_crn_ipv6_extend_flag(void *arg) +{ + (void)arg; + + cpath_build_state_t state; + + memset(&state, 0, sizeof(state)); + state.desired_path_len = DEFAULT_ROUTE_LEN; + tt_int_op(cpath_build_state_to_crn_ipv6_extend_flag(&state, 0), OP_EQ, + 0); + + /* Pass the state flag check, but not the length check */ + memset(&state, 0, sizeof(state)); + state.desired_path_len = DEFAULT_ROUTE_LEN; + state.is_ipv6_selftest = 1; + tt_int_op(cpath_build_state_to_crn_ipv6_extend_flag(&state, 0), OP_EQ, + 0); + + /* Pass the length check, but not the state flag check */ + memset(&state, 0, sizeof(state)); + state.desired_path_len = DEFAULT_ROUTE_LEN; + tt_int_op( + cpath_build_state_to_crn_ipv6_extend_flag(&state, + DEFAULT_ROUTE_LEN - 2), + OP_EQ, 0); + + /* Pass both checks */ + memset(&state, 0, sizeof(state)); + state.desired_path_len = DEFAULT_ROUTE_LEN; + state.is_ipv6_selftest = 1; + tt_int_op( + cpath_build_state_to_crn_ipv6_extend_flag(&state, + DEFAULT_ROUTE_LEN - 2), + OP_EQ, CRN_INITIATE_IPV6_EXTEND); + + /* Check that no other flags are handled */ + memset(&state, 0xff, sizeof(state)); + state.desired_path_len = INT_MAX; + tt_int_op(cpath_build_state_to_crn_ipv6_extend_flag(&state, INT_MAX), OP_EQ, + 0); + +#ifndef ALL_BUGS_ARE_FATAL + /* Start capturing bugs */ + setup_full_capture_of_logs(LOG_INFO); + tor_capture_bugs_(1); + + /* Now test the single hop circuit case */ +#define SINGLE_HOP_ROUTE_LEN 1 + memset(&state, 0, sizeof(state)); + state.desired_path_len = SINGLE_HOP_ROUTE_LEN; + state.is_ipv6_selftest = 1; + tt_int_op( + cpath_build_state_to_crn_ipv6_extend_flag(&state, + SINGLE_HOP_ROUTE_LEN - 2), + OP_EQ, 0); + tt_int_op(smartlist_len(tor_get_captured_bug_log_()), OP_EQ, 1); + tt_str_op(smartlist_get(tor_get_captured_bug_log_(), 0), OP_EQ, + "!(ASSERT_PREDICT_UNLIKELY_(state->desired_path_len < 2))"); + mock_clean_saved_logs(); +#endif /* !defined(ALL_BUGS_ARE_FATAL) */ + + done: + tor_end_capture_bugs_(); + mock_clean_saved_logs(); + teardown_capture_of_logs(); +} + +#define TEST(name, flags, setup, cleanup) \ + { #name, test_ ## name, flags, setup, cleanup } + +#define TEST_NEW_ROUTE_LEN(name, flags) \ + { #name, test_new_route_len_ ## name, flags, NULL, NULL } + +#define TEST_CIRCUIT(name, flags) \ + { #name, test_circuit_ ## name, flags, NULL, NULL } + +#define TEST_CPATH(name, flags) \ + { #name, test_cpath_ ## name, flags, NULL, NULL } + +#ifndef COCCI +#define TEST_CIRCUIT_PASSTHROUGH(name, flags, arg) \ + { #name "/" arg, test_circuit_ ## name, flags, \ + &passthrough_setup, (void *)(arg) } +#endif + struct testcase_t circuitbuild_tests[] = { - { "noexit", test_new_route_len_noexit, 0, NULL, NULL }, - { "safe_exit", test_new_route_len_safe_exit, 0, NULL, NULL }, - { "unsafe_exit", test_new_route_len_unsafe_exit, 0, NULL, NULL }, - { "unhandled_exit", test_new_route_len_unhandled_exit, 0, NULL, NULL }, - { "upgrade_from_guard_wait", test_upgrade_from_guard_wait, TT_FORK, - NULL, NULL }, + TEST_NEW_ROUTE_LEN(noexit, 0), + TEST_NEW_ROUTE_LEN(safe_exit, 0), + TEST_NEW_ROUTE_LEN(unsafe_exit, 0), + TEST_NEW_ROUTE_LEN(unhandled_exit, 0), + + TEST(upgrade_from_guard_wait, TT_FORK, &helper_pubsub_setup, NULL), + + TEST_CIRCUIT(extend_state_valid, TT_FORK), + TEST_CIRCUIT(extend_add_ed25519, TT_FORK), + TEST_CIRCUIT(extend_lspec_valid, TT_FORK), + TEST_CIRCUIT(extend_add_ip, TT_FORK), + TEST_CIRCUIT(choose_ip_ap_for_extend, 0), + + TEST_CIRCUIT_PASSTHROUGH(open_connection_for_extend, TT_FORK, "4"), + TEST_CIRCUIT_PASSTHROUGH(open_connection_for_extend, TT_FORK, "6"), + TEST_CIRCUIT_PASSTHROUGH(open_connection_for_extend, TT_FORK, "dual-stack"), + + TEST_CIRCUIT(extend, TT_FORK), + + TEST(onionskin_answer, TT_FORK, NULL, NULL), + + TEST(origin_circuit_init, TT_FORK, NULL, NULL), + TEST_CIRCUIT(send_next_onion_skin, TT_FORK), + TEST_CPATH(build_state_to_crn_flags, 0), + TEST_CPATH(build_state_to_crn_ipv6_extend_flag, TT_FORK), + END_OF_TESTCASES }; |