tor

The Tor anonymity network
git clone https://git.dasho.dev/tor.git
Log | Files | Refs | README | LICENSE

commit 49800cf5390bc1445a5fab9bbf346ca73c6b3a58
parent cbe9e56590b0ea354313667bf20e187dd5b1d556
Author: Nick Mathewson <nickm@torproject.org>
Date:   Wed, 29 Apr 2020 19:16:40 -0400

Merge remote-tracking branch 'tor-github/pr/1864/head'

Diffstat:
Achanges/bug33900 | 3+++
Achanges/bug33917 | 5+++++
Achanges/ticket33817 | 12++++++++++++
Achanges/ticket33901 | 4++++
Mscripts/maint/practracker/exceptions.txt | 2+-
Msrc/core/or/channel.c | 60++++++++++++++++++++++++++++++++++++++++++------------------
Msrc/core/or/channel.h | 5++---
Msrc/core/or/channeltls.c | 2+-
Msrc/core/or/circuitbuild.c | 16+++++++++++-----
Msrc/core/or/connection_edge.c | 2+-
Msrc/core/or/onion.c | 43+++++++++++++++++++++++++++++++++++--------
Msrc/core/or/policies.c | 4++--
Msrc/core/or/relay.c | 8++++----
Msrc/core/proto/proto_socks.c | 5++---
Msrc/feature/client/addressmap.c | 2+-
Msrc/feature/relay/circuitbuild_relay.c | 196+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------
Msrc/feature/relay/circuitbuild_relay.h | 3+++
Msrc/feature/relay/router.c | 20+++++++++++++++++++-
Msrc/feature/relay/router.h | 2++
Msrc/lib/log/util_bug.h | 23++++++++++++++++++-----
Msrc/lib/net/address.c | 62+++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
Msrc/lib/net/address.h | 15+++++++++++----
Msrc/test/test_address.c | 4++--
Msrc/test/test_cell_formats.c | 78+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
Msrc/test/test_channel.c | 37+++++++++++++++++++++++++------------
Msrc/test/test_circuitbuild.c | 328++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
Msrc/tools/tor-resolve.c | 2+-
27 files changed, 805 insertions(+), 138 deletions(-)

diff --git a/changes/bug33900 b/changes/bug33900 @@ -0,0 +1,3 @@ + o Minor bugfixes (IPv4, relay): + - Check for invalid zero IPv4 addresses and ports, when sending and + receiving extend cells. Fixes bug 33900; bugfix on 0.2.4.8-alpha. diff --git a/changes/bug33917 b/changes/bug33917 @@ -0,0 +1,5 @@ + o Minor bugfixes (logging, testing): + - Make all of tor's assertion macros support the ALL_BUGS_ARE_FATAL and + DISABLE_ASSERTS_IN_UNIT_TESTS debugging modes. Implements these modes + for IF_BUG_ONCE(). (It used to log a non-fatal warning, regardless of + the debugging mode.) Fixes bug 33917; bugfix on 0.2.9.1-alpha. diff --git a/changes/ticket33817 b/changes/ticket33817 @@ -0,0 +1,12 @@ + o Major features (IPv6, relay): + - Relays may extend circuits over IPv6, if the relay has an IPv6 ORPort, + and the client supplies the other relay's IPv6 ORPort in the EXTEND2 + cell. IPv6 extends will be used by the relay IPv6 ORPort self-tests in + 33222. Closes ticket 33817. + - Consider IPv6-only EXTEND2 cells valid on relays. Log a protocol warning + if the IPv4 or IPv6 address is an internal address, and internal + addresses are not allowed. But continue to use the other address, if it + is valid. Closes ticket 33817. + - If a relay can extend over IPv4 and IPv6, it chooses between them + uniformly at random. Closes ticket 33817. + - Re-use existing IPv6 connections for circuit extends. Closes ticket 33817. diff --git a/changes/ticket33901 b/changes/ticket33901 @@ -0,0 +1,4 @@ + o Minor features (IPv6, relay): + - Allow clients and relays to send dual-stack and IPv6-only EXTEND2 cells. + Parse dual-stack and IPv6-only EXTEND2 cells on relays. + Closes ticket 33901. diff --git a/scripts/maint/practracker/exceptions.txt b/scripts/maint/practracker/exceptions.txt @@ -305,7 +305,7 @@ problem function-size /src/lib/encoding/confline.c:parse_config_line_from_str_ve problem function-size /src/lib/encoding/cstring.c:unescape_string() 108 problem function-size /src/lib/fs/dir.c:check_private_dir() 230 problem function-size /src/lib/math/prob_distr.c:sample_uniform_interval() 145 -problem function-size /src/lib/net/address.c:tor_addr_parse_mask_ports() 194 +problem function-size /src/lib/net/address.c:tor_addr_parse_mask_ports() 195 problem function-size /src/lib/net/address.c:tor_addr_compare_masked() 110 problem function-size /src/lib/net/inaddr.c:tor_inet_pton() 107 problem function-size /src/lib/net/socketpair.c:tor_ersatz_socketpair() 102 diff --git a/src/core/or/channel.c b/src/core/or/channel.c @@ -83,6 +83,13 @@ #include "core/or/cell_queue_st.h" +/* Static function prototypes */ + +static bool channel_matches_target_addr_for_extend( + channel_t *chan, + const tor_addr_t *target_ipv4_addr, + const tor_addr_t *target_ipv6_addr); + /* Global lists of channels */ /* All channel_t instances */ @@ -2360,9 +2367,9 @@ channel_is_better(channel_t *a, channel_t *b) * Get a channel to extend a circuit. * * Given the desired relay identity, pick a suitable channel to extend a - * circuit to the target address requsted by the client. Search for an - * existing channel for the requested endpoint. Make sure the channel is - * usable for new circuits, and matches the target address. + * circuit to the target IPv4 or IPv6 address requsted by the client. Search + * for an existing channel for the requested endpoint. Make sure the channel + * is usable for new circuits, and matches one of the target addresses. * * Try to return the best channel. But if there is no good channel, set * *msg_out to a message describing the channel's state and our next action, @@ -2372,7 +2379,8 @@ channel_is_better(channel_t *a, channel_t *b) MOCK_IMPL(channel_t *, channel_get_for_extend,(const char *rsa_id_digest, const ed25519_public_key_t *ed_id, - const tor_addr_t *target_addr, + const tor_addr_t *target_ipv4_addr, + const tor_addr_t *target_ipv6_addr, const char **msg_out, int *launch_out)) { @@ -2404,11 +2412,15 @@ channel_get_for_extend,(const char *rsa_id_digest, continue; } + const bool matches_target = + channel_matches_target_addr_for_extend(chan, + target_ipv4_addr, + target_ipv6_addr); /* Never return a non-open connection. */ if (!CHANNEL_IS_OPEN(chan)) { /* If the address matches, don't launch a new connection for this * circuit. */ - if (channel_matches_target_addr_for_extend(chan, target_addr)) + if (matches_target) ++n_inprogress_goodaddr; continue; } @@ -2419,22 +2431,21 @@ channel_get_for_extend,(const char *rsa_id_digest, continue; } - /* Never return a non-canonical connection using a recent link protocol - * if the address is not what we wanted. + /* If the connection is using a recent link protocol, only return canonical + * connections, when the address is one of the addresses we wanted. * * The channel_is_canonical_is_reliable() function asks the lower layer - * if we should trust channel_is_canonical(). The below is from the - * comments of the old circuit_or_get_for_extend() and applies when + * if we should trust channel_is_canonical(). It only applies when * the lower-layer transport is channel_tls_t. * - * (For old link protocols, we can't rely on is_canonical getting + * For old link protocols, we can't rely on is_canonical getting * set properly if we're talking to the right address, since we might * have an out-of-date descriptor, and we will get no NETINFO cell to - * tell us about the right address.) + * tell us about the right address. */ if (!channel_is_canonical(chan) && channel_is_canonical_is_reliable(chan) && - !channel_matches_target_addr_for_extend(chan, target_addr)) { + !matches_target) { ++n_noncanonical; continue; } @@ -3297,20 +3308,33 @@ channel_matches_extend_info(channel_t *chan, extend_info_t *extend_info) } /** - * Check if a channel matches a given target address; return true iff we do. + * Check if a channel matches the given target IPv4 or IPv6 addresses. + * If either address matches, return true. If neither address matches, + * return false. + * + * Both addresses can't be NULL. * * This function calls into the lower layer and asks if this channel thinks - * it matches a given target address for circuit extension purposes. + * it matches the target addresses for circuit extension purposes. */ -int +static bool channel_matches_target_addr_for_extend(channel_t *chan, - const tor_addr_t *target) + const tor_addr_t *target_ipv4_addr, + const tor_addr_t *target_ipv6_addr) { tor_assert(chan); tor_assert(chan->matches_target); - tor_assert(target); - return chan->matches_target(chan, target); + IF_BUG_ONCE(!target_ipv4_addr && !target_ipv6_addr) + return false; + + if (target_ipv4_addr && chan->matches_target(chan, target_ipv4_addr)) + return true; + + if (target_ipv6_addr && chan->matches_target(chan, target_ipv6_addr)) + return true; + + return false; } /** diff --git a/src/core/or/channel.h b/src/core/or/channel.h @@ -661,7 +661,8 @@ channel_t * channel_connect(const tor_addr_t *addr, uint16_t port, MOCK_DECL(channel_t *, channel_get_for_extend,( const char *rsa_id_digest, const struct ed25519_public_key_t *ed_id, - const tor_addr_t *target_addr, + const tor_addr_t *target_ipv4_addr, + const tor_addr_t *target_ipv6_addr, const char **msg_out, int *launch_out)); @@ -737,8 +738,6 @@ int channel_is_outgoing(channel_t *chan); void channel_mark_client(channel_t *chan); void channel_clear_client(channel_t *chan); int channel_matches_extend_info(channel_t *chan, extend_info_t *extend_info); -int channel_matches_target_addr_for_extend(channel_t *chan, - const tor_addr_t *target); unsigned int channel_num_circuits(channel_t *chan); MOCK_DECL(void,channel_set_circid_type,(channel_t *chan, crypto_pk_t *identity_rcvd, diff --git a/src/core/or/channeltls.c b/src/core/or/channeltls.c @@ -1669,7 +1669,7 @@ tor_addr_from_netinfo_addr(tor_addr_t *tor_addr, } else if (type == NETINFO_ADDR_TYPE_IPV6 && len == 16) { const uint8_t *ipv6_bytes = netinfo_addr_getconstarray_addr_ipv6( netinfo_addr); - tor_addr_from_ipv6_bytes(tor_addr, (const char *)ipv6_bytes); + tor_addr_from_ipv6_bytes(tor_addr, ipv6_bytes); } else { log_fn(LOG_PROTOCOL_WARN, LD_OR, "Cannot read address from NETINFO " "- wrong type/length."); diff --git a/src/core/or/circuitbuild.c b/src/core/or/circuitbuild.c @@ -559,11 +559,17 @@ circuit_handle_first_hop(origin_circuit_t *circ) fmt_addrport(&firsthop->extend_info->addr, firsthop->extend_info->port)); - n_chan = channel_get_for_extend(firsthop->extend_info->identity_digest, - &firsthop->extend_info->ed_identity, - &firsthop->extend_info->addr, - &msg, - &should_launch); + /* We'll cleanup this code in #33220, when we add an IPv6 address to + * extend_info_t. */ + const bool addr_is_ipv4 = + (tor_addr_family(&firsthop->extend_info->addr) == AF_INET); + n_chan = channel_get_for_extend( + firsthop->extend_info->identity_digest, + &firsthop->extend_info->ed_identity, + addr_is_ipv4 ? &firsthop->extend_info->addr : NULL, + addr_is_ipv4 ? NULL : &firsthop->extend_info->addr, + &msg, + &should_launch); if (!n_chan) { /* not currently connected in a useful way. */ diff --git a/src/core/or/connection_edge.c b/src/core/or/connection_edge.c @@ -3531,7 +3531,7 @@ connection_ap_handshake_socks_resolved,(entry_connection_t *conn, } } else if (answer_type == RESOLVED_TYPE_IPV6 && answer_len == 16) { tor_addr_t a; - tor_addr_from_ipv6_bytes(&a, (char*)answer); + tor_addr_from_ipv6_bytes(&a, answer); if (! tor_addr_is_null(&a)) { client_dns_set_addressmap(conn, conn->socks_request->address, &a, diff --git a/src/core/or/onion.c b/src/core/or/onion.c @@ -240,11 +240,21 @@ created_cell_parse(created_cell_t *cell_out, const cell_t *cell_in) static int check_extend_cell(const extend_cell_t *cell) { + const bool is_extend2 = (cell->cell_type == RELAY_COMMAND_EXTEND2); + if (tor_digest_is_zero((const char*)cell->node_id)) return -1; - /* We don't currently allow EXTEND2 cells without an IPv4 address */ - if (tor_addr_family(&cell->orport_ipv4.addr) == AF_UNSPEC) - return -1; + if (!tor_addr_port_is_valid_ap(&cell->orport_ipv4, 0)) { + /* EXTEND cells must have an IPv4 address. */ + if (!is_extend2) { + return -1; + } + /* EXTEND2 cells must have at least one IP address. + * It can be IPv4 or IPv6. */ + if (!tor_addr_port_is_valid_ap(&cell->orport_ipv6, 0)) { + return -1; + } + } if (cell->create_cell.cell_type == CELL_CREATE) { if (cell->cell_type != RELAY_COMMAND_EXTEND) return -1; @@ -343,7 +353,7 @@ extend_cell_from_extend2_cell_body(extend_cell_t *cell_out, continue; found_ipv6 = 1; tor_addr_from_ipv6_bytes(&cell_out->orport_ipv6.addr, - (const char *)ls->un_ipv6_addr); + ls->un_ipv6_addr); cell_out->orport_ipv6.port = ls->un_ipv6_port; break; case LS_LEGACY_ID: @@ -364,7 +374,12 @@ extend_cell_from_extend2_cell_body(extend_cell_t *cell_out, } } - if (!found_rsa_id || !found_ipv4) /* These are mandatory */ + /* EXTEND2 cells must have an RSA ID */ + if (!found_rsa_id) + return -1; + + /* EXTEND2 cells must have at least one IP address */ + if (!found_ipv4 && !found_ipv6) return -1; return create_cell_from_create2_cell_body(&cell_out->create_cell, @@ -620,12 +635,13 @@ extend_cell_format(uint8_t *command_out, uint16_t *len_out, break; case RELAY_COMMAND_EXTEND2: { - uint8_t n_specifiers = 2; + uint8_t n_specifiers = 1; *command_out = RELAY_COMMAND_EXTEND2; extend2_cell_body_t *cell = extend2_cell_body_new(); link_specifier_t *ls; - { - /* IPv4 specifier first. */ + if (tor_addr_port_is_valid_ap(&cell_in->orport_ipv4, 0)) { + /* Maybe IPv4 specifier first. */ + ++n_specifiers; ls = link_specifier_new(); extend2_cell_body_add_ls(cell, ls); ls->ls_type = LS_IPV4; @@ -651,6 +667,17 @@ extend_cell_format(uint8_t *command_out, uint16_t *len_out, ls->ls_len = 32; memcpy(ls->un_ed25519_id, cell_in->ed_pubkey.pubkey, 32); } + if (tor_addr_port_is_valid_ap(&cell_in->orport_ipv6, 0)) { + /* Then maybe IPv6 specifier. */ + ++n_specifiers; + ls = link_specifier_new(); + extend2_cell_body_add_ls(cell, ls); + ls->ls_type = LS_IPV6; + ls->ls_len = 18; + tor_addr_copy_ipv6_bytes(ls->un_ipv6_addr, + &cell_in->orport_ipv6.addr); + ls->un_ipv6_port = cell_in->orport_ipv6.port; + } cell->n_spec = n_specifiers; /* Now, the handshake */ diff --git a/src/core/or/policies.c b/src/core/or/policies.c @@ -167,7 +167,7 @@ policy_expand_unspec(smartlist_t **policy) } tor_addr_from_ipv4h(&newpolicy_ipv4.addr, 0); tor_addr_from_ipv6_bytes(&newpolicy_ipv6.addr, - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"); + (const uint8_t *)"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"); smartlist_add(tmp, addr_policy_get_canonical_entry(&newpolicy_ipv4)); smartlist_add(tmp, addr_policy_get_canonical_entry(&newpolicy_ipv6)); addr_policy_free(p); @@ -1005,7 +1005,7 @@ fascist_firewall_choose_address_ls(const smartlist_t *lspecs, * direct connection. */ if (have_v6) continue; tor_addr_from_ipv6_bytes(&addr_v6, - (const char *) link_specifier_getconstarray_un_ipv6_addr(ls)); + link_specifier_getconstarray_un_ipv6_addr(ls)); port_v6 = link_specifier_get_un_ipv6_port(ls); have_v6 = 1; break; diff --git a/src/core/or/relay.c b/src/core/or/relay.c @@ -867,7 +867,7 @@ connection_ap_process_end_not_open( ttl = (int)ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE+5)); } else if (rh->length == 17 || rh->length == 21) { tor_addr_from_ipv6_bytes(&addr, - (char*)(cell->payload+RELAY_HEADER_SIZE+1)); + (cell->payload+RELAY_HEADER_SIZE+1)); if (rh->length == 21) ttl = (int)ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE+17)); } @@ -1092,7 +1092,7 @@ connected_cell_parse(const relay_header_t *rh, const cell_t *cell, return -1; if (get_uint8(payload + 4) != 6) return -1; - tor_addr_from_ipv6_bytes(addr_out, (char*)(payload + 5)); + tor_addr_from_ipv6_bytes(addr_out, (payload + 5)); bytes = ntohl(get_uint32(payload + 21)); if (bytes <= INT32_MAX) *ttl_out = (int) bytes; @@ -1165,7 +1165,7 @@ resolved_cell_parse(const cell_t *cell, const relay_header_t *rh, if (answer_len != 16) goto err; addr = tor_malloc_zero(sizeof(*addr)); - tor_addr_from_ipv6_bytes(&addr->addr, (const char*) cp); + tor_addr_from_ipv6_bytes(&addr->addr, cp); cp += 16; addr->ttl = ntohl(get_uint32(cp)); cp += 4; @@ -3217,7 +3217,7 @@ decode_address_from_payload(tor_addr_t *addr_out, const uint8_t *payload, case RESOLVED_TYPE_IPV6: if (payload[1] != 16) return NULL; - tor_addr_from_ipv6_bytes(addr_out, (char*)(payload+2)); + tor_addr_from_ipv6_bytes(addr_out, (payload+2)); break; default: tor_addr_make_unspec(addr_out); diff --git a/src/core/proto/proto_socks.c b/src/core/proto/proto_socks.c @@ -587,9 +587,8 @@ parse_socks5_client_request(const uint8_t *raw_data, socks_request_t *req, strlcpy(req->address, hostname, sizeof(req->address)); } break; case 4: { - const char *ipv6 = - (const char *)socks5_client_request_getarray_dest_addr_ipv6( - trunnel_req); + const uint8_t *ipv6 = + socks5_client_request_getarray_dest_addr_ipv6(trunnel_req); tor_addr_from_ipv6_bytes(&destaddr, ipv6); tor_addr_to_str(req->address, &destaddr, sizeof(req->address), 1); diff --git a/src/feature/client/addressmap.c b/src/feature/client/addressmap.c @@ -902,7 +902,7 @@ get_random_virtual_addr(const virtual_addr_conf_t *conf, tor_addr_t *addr_out) } if (ipv6) - tor_addr_from_ipv6_bytes(addr_out, (char*) bytes); + tor_addr_from_ipv6_bytes(addr_out, bytes); else tor_addr_from_ipv4n(addr_out, get_uint32(bytes)); diff --git a/src/feature/relay/circuitbuild_relay.c b/src/feature/relay/circuitbuild_relay.c @@ -18,6 +18,8 @@ #include "orconfig.h" #include "feature/relay/circuitbuild_relay.h" +#include "lib/crypt_ops/crypto_rand.h" + #include "core/or/or.h" #include "app/config/config.h" @@ -36,6 +38,7 @@ #include "feature/nodelist/nodelist.h" +#include "feature/relay/router.h" #include "feature/relay/routermode.h" #include "feature/relay/selftest.h" @@ -119,6 +122,49 @@ circuit_extend_add_ed25519_helper(struct extend_cell_t *ec) return 0; } +/* Check if the address and port in the tor_addr_port_t <b>ap</b> are valid, + * and are allowed by the current ExtendAllowPrivateAddresses config. + * + * If they are valid, return true. + * Otherwise, if they are invalid, return false. + * + * If <b>log_zero_addrs</b> is true, log warnings about zero addresses at + * <b>log_level</b>. If <b>log_internal_addrs</b> is true, log warnings about + * internal addresses at <b>log_level</b>. + */ +static bool +circuit_extend_addr_port_is_valid(const struct tor_addr_port_t *ap, + bool log_zero_addrs, bool log_internal_addrs, + int log_level) +{ + /* It's safe to print the family. But we don't want to print the address, + * unless specifically configured to do so. (Zero addresses aren't sensitive, + * But some internal addresses might be.)*/ + + if (!tor_addr_port_is_valid_ap(ap, 0)) { + if (log_zero_addrs) { + log_fn(log_level, LD_PROTOCOL, + "Client asked me to extend to a zero destination port or " + "%s address '%s'.", + fmt_addr_family(&ap->addr), safe_str(fmt_addrport_ap(ap))); + } + return false; + } + + if (tor_addr_is_internal(&ap->addr, 0) && + !get_options()->ExtendAllowPrivateAddresses) { + if (log_internal_addrs) { + log_fn(log_level, LD_PROTOCOL, + "Client asked me to extend to a private %s address '%s'.", + fmt_addr_family(&ap->addr), + safe_str(fmt_and_decorate_addr(&ap->addr))); + } + return false; + } + + return true; +} + /* Before replying to an extend cell, check the link specifiers in the extend * cell <b>ec</b>, which was received on the circuit <b>circ</b>. * @@ -139,17 +185,28 @@ circuit_extend_lspec_valid_helper(const struct extend_cell_t *ec, return -1; } - if (!ec->orport_ipv4.port || tor_addr_is_null(&ec->orport_ipv4.addr)) { - log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, - "Client asked me to extend to zero destination port or addr."); - return -1; - } - - if (tor_addr_is_internal(&ec->orport_ipv4.addr, 0) && - !get_options()->ExtendAllowPrivateAddresses) { - log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, - "Client asked me to extend to a private address."); + /* Check the addresses, without logging */ + const int ipv4_valid = circuit_extend_addr_port_is_valid(&ec->orport_ipv4, + false, false, 0); + const int ipv6_valid = circuit_extend_addr_port_is_valid(&ec->orport_ipv6, + false, false, 0); + /* We need at least one valid address */ + if (!ipv4_valid && !ipv6_valid) { + /* Now, log the invalid addresses at protocol warning level */ + circuit_extend_addr_port_is_valid(&ec->orport_ipv4, + true, true, LOG_PROTOCOL_WARN); + circuit_extend_addr_port_is_valid(&ec->orport_ipv6, + true, true, LOG_PROTOCOL_WARN); + /* And fail */ return -1; + } else if (!ipv4_valid) { + /* Always log unexpected internal addresses, but go on to use the other + * valid address */ + circuit_extend_addr_port_is_valid(&ec->orport_ipv4, + false, true, LOG_PROTOCOL_WARN); + } else if (!ipv6_valid) { + circuit_extend_addr_port_is_valid(&ec->orport_ipv6, + false, true, LOG_PROTOCOL_WARN); } IF_BUG_ONCE(circ->magic != OR_CIRCUIT_MAGIC) { @@ -183,10 +240,64 @@ circuit_extend_lspec_valid_helper(const struct extend_cell_t *ec, return 0; } +/* If possible, return a supported, non-NULL IP address. + * + * If both addresses are supported and non-NULL, choose one uniformly at + * random. + * + * If we have an IPv6-only extend, but IPv6 is not supported, returns NULL. + * If both addresses are NULL, also returns NULL. */ +STATIC const tor_addr_port_t * +circuit_choose_ip_ap_for_extend(const tor_addr_port_t *ipv4_ap, + const tor_addr_port_t *ipv6_ap) +{ + const bool ipv6_supported = router_can_extend_over_ipv6(get_options()); + + /* If IPv6 is not supported, we can't use the IPv6 address. */ + if (!ipv6_supported) { + ipv6_ap = NULL; + } + + /* If there is no IPv6 address, IPv4 is always supported. + * Until clients include IPv6 ORPorts, and most relays support IPv6, + * this is the most common case. */ + if (!ipv6_ap) { + return ipv4_ap; + } + + /* If there is no IPv4 address, return the (possibly NULL) IPv6 address. */ + if (!ipv4_ap) { + return ipv6_ap; + } + + /* Now we have an IPv4 and an IPv6 address, and IPv6 is supported. + * So make an IPv6 connection at random, with probability 1 in N. + * 1 means "always IPv6 (and no IPv4)" + * 2 means "equal probability of IPv4 or IPv6" + * ... (and so on) ... + * (UINT_MAX - 1) means "almost always IPv4 (and almost never IPv6)" + * To disable IPv6, set ipv6_supported to 0. + */ +#define IPV6_CONNECTION_ONE_IN_N 2 + + bool choose_ipv6 = crypto_fast_rng_one_in_n(get_thread_fast_rng(), + IPV6_CONNECTION_ONE_IN_N); + if (choose_ipv6) { + return ipv6_ap; + } else { + return ipv4_ap; + } +} + /* When there is no open channel for an extend cell <b>ec</b>, set up the - * circuit <b>circ</b> to wait for a new connection. If <b>should_launch</b> - * is true, open a new connection. (Otherwise, we are already waiting for a - * new connection to the same relay.) + * circuit <b>circ</b> to wait for a new connection. + * + * If <b>should_launch</b> is true, open a new connection. (Otherwise, we are + * already waiting for a new connection to the same relay.) + * + * Check if IPv6 extends are supported by our current configuration. If they + * are, new connections may be made over IPv4 or IPv6. (IPv4 connections are + * always supported.) */ STATIC void circuit_open_connection_for_extend(const struct extend_cell_t *ec, @@ -205,13 +316,36 @@ circuit_open_connection_for_extend(const struct extend_cell_t *ec, return; } + /* Check the addresses, without logging */ + const int ipv4_valid = circuit_extend_addr_port_is_valid(&ec->orport_ipv4, + false, false, 0); + const int ipv6_valid = circuit_extend_addr_port_is_valid(&ec->orport_ipv6, + false, false, 0); + + IF_BUG_ONCE(!ipv4_valid && !ipv6_valid) { + /* circuit_extend_lspec_valid_helper() should have caught this */ + circuit_mark_for_close(circ, END_CIRC_REASON_CONNECTFAILED); + return; + } + + const tor_addr_port_t *chosen_ap = circuit_choose_ip_ap_for_extend( + ipv4_valid ? &ec->orport_ipv4 : NULL, + ipv6_valid ? &ec->orport_ipv6 : NULL); + if (!chosen_ap) { + /* An IPv6-only extend, but IPv6 is not supported */ + log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, + "Received IPv6-only extend, but we don't have an IPv6 ORPort."); + circuit_mark_for_close(circ, END_CIRC_REASON_CONNECTFAILED); + return; + } + circ->n_hop = extend_info_new(NULL /*nickname*/, (const char*)ec->node_id, &ec->ed_pubkey, NULL, /*onion_key*/ NULL, /*curve25519_key*/ - &ec->orport_ipv4.addr, - ec->orport_ipv4.port); + &chosen_ap->addr, + chosen_ap->port); circ->n_chan_create_cell = tor_memdup(&ec->create_cell, sizeof(ec->create_cell)); @@ -220,10 +354,11 @@ circuit_open_connection_for_extend(const struct extend_cell_t *ec, if (should_launch) { /* we should try to open a connection */ - channel_t *n_chan = channel_connect_for_circuit(&ec->orport_ipv4.addr, - ec->orport_ipv4.port, - (const char*)ec->node_id, - &ec->ed_pubkey); + channel_t *n_chan = channel_connect_for_circuit( + &circ->n_hop->addr, + circ->n_hop->port, + circ->n_hop->identity_digest, + &circ->n_hop->ed_identity); if (!n_chan) { log_info(LD_CIRC,"Launching n_chan failed. Closing circuit."); circuit_mark_for_close(circ, END_CIRC_REASON_CONNECTFAILED); @@ -277,16 +412,31 @@ circuit_extend(struct cell_t *cell, struct circuit_t *circ) if (circuit_extend_lspec_valid_helper(&ec, circ) < 0) return -1; + /* Check the addresses, without logging */ + const int ipv4_valid = circuit_extend_addr_port_is_valid(&ec.orport_ipv4, + false, false, 0); + const int ipv6_valid = circuit_extend_addr_port_is_valid(&ec.orport_ipv6, + false, false, 0); + IF_BUG_ONCE(!ipv4_valid && !ipv6_valid) { + /* circuit_extend_lspec_valid_helper() should have caught this */ + return -1; + } + n_chan = channel_get_for_extend((const char*)ec.node_id, &ec.ed_pubkey, - &ec.orport_ipv4.addr, + ipv4_valid ? &ec.orport_ipv4.addr : NULL, + ipv6_valid ? &ec.orport_ipv6.addr : NULL, &msg, &should_launch); if (!n_chan) { - log_debug(LD_CIRC|LD_OR,"Next router (%s): %s.", - fmt_addrport(&ec.orport_ipv4.addr,ec.orport_ipv4.port), - msg?msg:"????"); + /* We can't use fmt_addr*() twice in the same function call, + * because it uses a static buffer. */ + log_debug(LD_CIRC|LD_OR, "Next router IPv4 (%s): %s.", + fmt_addrport_ap(&ec.orport_ipv4), + msg ? msg : "????"); + log_debug(LD_CIRC|LD_OR, "Next router IPv6 (%s).", + fmt_addrport_ap(&ec.orport_ipv6)); circuit_open_connection_for_extend(&ec, circ, should_launch); diff --git a/src/feature/relay/circuitbuild_relay.h b/src/feature/relay/circuitbuild_relay.h @@ -75,6 +75,9 @@ STATIC int circuit_extend_state_valid_helper(const struct circuit_t *circ); STATIC int circuit_extend_add_ed25519_helper(struct extend_cell_t *ec); STATIC int circuit_extend_lspec_valid_helper(const struct extend_cell_t *ec, const struct circuit_t *circ); +STATIC const tor_addr_port_t * circuit_choose_ip_ap_for_extend( + const tor_addr_port_t *ipv4_ap, + const tor_addr_port_t *ipv6_ap); STATIC void circuit_open_connection_for_extend(const struct extend_cell_t *ec, struct circuit_t *circ, int should_launch); diff --git a/src/feature/relay/router.c b/src/feature/relay/router.c @@ -1469,7 +1469,7 @@ router_get_advertised_ipv6_or_ap(const or_options_t *options, AF_INET6); if (!addr || port == 0) { - log_info(LD_CONFIG, "There is no advertised IPv6 ORPort."); + log_debug(LD_CONFIG, "There is no advertised IPv6 ORPort."); return; } @@ -1490,6 +1490,24 @@ router_get_advertised_ipv6_or_ap(const or_options_t *options, ipv6_ap_out->port = port; } +/** Returns true if this router has an advertised IPv6 ORPort. */ +bool +router_has_advertised_ipv6_orport(const or_options_t *options) +{ + tor_addr_port_t ipv6_ap; + router_get_advertised_ipv6_or_ap(options, &ipv6_ap); + return tor_addr_port_is_valid_ap(&ipv6_ap, 0); +} + +/** Returns true if this router has an advertised IPv6 ORPort. */ +MOCK_IMPL(bool, +router_can_extend_over_ipv6,(const or_options_t *options)) +{ + /* We might add some extra checks here, such as ExtendAllowIPv6Addresses + * from ticket 33818. */ + return router_has_advertised_ipv6_orport(options); +} + /** Return the port that we should advertise as our DirPort; * this is one of three possibilities: * The one that is passed as <b>dirport</b> if the DirPort option is 0, or diff --git a/src/feature/relay/router.h b/src/feature/relay/router.h @@ -68,6 +68,8 @@ uint16_t router_get_active_listener_port_by_type_af(int listener_type, uint16_t router_get_advertised_or_port(const or_options_t *options); void router_get_advertised_ipv6_or_ap(const or_options_t *options, tor_addr_port_t *ipv6_ap_out); +bool router_has_advertised_ipv6_orport(const or_options_t *options); +MOCK_DECL(bool, router_can_extend_over_ipv6,(const or_options_t *options)); uint16_t router_get_advertised_or_port_by_af(const or_options_t *options, sa_family_t family); uint16_t router_get_advertised_dir_port(const or_options_t *options, diff --git a/src/lib/log/util_bug.h b/src/lib/log/util_bug.h @@ -142,6 +142,8 @@ #define ALL_BUGS_ARE_FATAL #endif +/** Define ALL_BUGS_ARE_FATAL if you want Tor to crash when any problem comes + * up, so you can get a coredump and track things down. */ #ifdef ALL_BUGS_ARE_FATAL #define tor_assert_nonfatal_unreached() tor_assert(0) #define tor_assert_nonfatal(cond) tor_assert((cond)) @@ -154,6 +156,9 @@ (tor_assertion_failed_(SHORT_FILE__,__LINE__,__func__,"!("#cond")",NULL), \ tor_abort_(), 1) \ : 0) +#ifndef COCCI +#define IF_BUG_ONCE(cond) if (BUG(cond)) +#endif #elif defined(TOR_UNIT_TESTS) && defined(DISABLE_ASSERTS_IN_UNIT_TESTS) #define tor_assert_nonfatal_unreached() STMT_NIL #define tor_assert_nonfatal(cond) ((void)(cond)) @@ -164,6 +169,9 @@ #define tor_assert_nonfatal_unreached_once() STMT_NIL #define tor_assert_nonfatal_once(cond) ((void)(cond)) #define BUG(cond) (ASSERT_PREDICT_UNLIKELY_(cond) ? 1 : 0) +#ifndef COCCI +#define IF_BUG_ONCE(cond) if (BUG(cond)) +#endif #else /* Normal case, !ALL_BUGS_ARE_FATAL, !DISABLE_ASSERTS_IN_UNIT_TESTS */ #define tor_assert_nonfatal_unreached() STMT_BEGIN \ tor_bug_occurred_(SHORT_FILE__, __LINE__, __func__, NULL, 0, NULL); \ @@ -200,7 +208,6 @@ (ASSERT_PREDICT_UNLIKELY_(cond) ? \ (tor_bug_occurred_(SHORT_FILE__,__LINE__,__func__,"!("#cond")",0,NULL),1) \ : 0) -#endif /* defined(ALL_BUGS_ARE_FATAL) || ... */ #ifndef COCCI #ifdef __GNUC__ @@ -232,7 +239,7 @@ #define IF_BUG_ONCE_VARNAME__(a) \ IF_BUG_ONCE_VARNAME_(a) -/** This macro behaves as 'if (bug(x))', except that it only logs its +/** This macro behaves as 'if (BUG(x))', except that it only logs its * warning once, no matter how many times it triggers. */ @@ -240,9 +247,15 @@ IF_BUG_ONCE__(ASSERT_PREDICT_UNLIKELY_(cond), \ IF_BUG_ONCE_VARNAME__(__LINE__)) -/** Define this if you want Tor to crash when any problem comes up, - * so you can get a coredump and track things down. */ -// #define tor_fragile_assert() tor_assert_unreached(0) +#endif /* defined(ALL_BUGS_ARE_FATAL) || ... */ + +/** In older code, we used tor_fragile_assert() to mark optional failure + * points. At these points, we could make some debug builds fail. + * (But release builds would continue.) + * + * To get the same behaviour in recent tor versions, define + * ALL_BUGS_ARE_FATAL, and use any non-fatal assertion or *BUG() macro. + */ #define tor_fragile_assert() tor_assert_nonfatal_unreached_once() void tor_assertion_failed_(const char *fname, unsigned int line, diff --git a/src/lib/net/address.c b/src/lib/net/address.c @@ -608,7 +608,8 @@ tor_addr_parse_mask_ports(const char *s, family = AF_INET; tor_addr_from_ipv4h(addr_out, 0); } else if (flags & TAPMP_STAR_IPV6_ONLY) { - static char nil_bytes[16] = { [0]=0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 }; + static uint8_t nil_bytes[16] = + { [0]=0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 }; family = AF_INET6; tor_addr_from_ipv6_bytes(addr_out, nil_bytes); } else { @@ -629,7 +630,7 @@ tor_addr_parse_mask_ports(const char *s, tor_addr_from_ipv4h(addr_out, 0); any_flag = 1; } else if (!strcmp(address, "*6") && (flags & TAPMP_EXTENDED_STAR)) { - static char nil_bytes[16] = { [0]=0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 }; + static uint8_t nil_bytes[16] = { [0]=0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 }; family = AF_INET6; tor_addr_from_ipv6_bytes(addr_out, nil_bytes); any_flag = 1; @@ -887,7 +888,7 @@ tor_addr_from_ipv4n(tor_addr_t *dest, uint32_t v4addr) /** Set <b>dest</b> to equal the IPv6 address in the 16 bytes at * <b>ipv6_bytes</b>. */ void -tor_addr_from_ipv6_bytes(tor_addr_t *dest, const char *ipv6_bytes) +tor_addr_from_ipv6_bytes(tor_addr_t *dest, const uint8_t *ipv6_bytes) { tor_assert(dest); tor_assert(ipv6_bytes); @@ -900,7 +901,21 @@ tor_addr_from_ipv6_bytes(tor_addr_t *dest, const char *ipv6_bytes) void tor_addr_from_in6(tor_addr_t *dest, const struct in6_addr *in6) { - tor_addr_from_ipv6_bytes(dest, (const char*)in6->s6_addr); + tor_addr_from_ipv6_bytes(dest, in6->s6_addr); +} + +/** Set the 16 bytes at <b>dest</b> to equal the IPv6 address <b>src</b>. + * <b>src</b> must be an IPv6 address, if it is not, log a warning, and clear + * <b>dest</b>. */ +void +tor_addr_copy_ipv6_bytes(uint8_t *dest, const tor_addr_t *src) +{ + tor_assert(dest); + tor_assert(src); + memset(dest, 0, 16); + IF_BUG_ONCE(src->family != AF_INET6) + return; + memcpy(dest, src->addr.in6_addr.s6_addr, 16); } /** Copy a tor_addr_t from <b>src</b> to <b>dest</b>. @@ -1192,6 +1207,39 @@ fmt_addr32(uint32_t addr) return buf; } +/** Return a string representing the family of <b>addr</b>. + * + * This string is a string constant, and must not be freed. + * This function is thread-safe. + */ +const char * +fmt_addr_family(const tor_addr_t *addr) +{ + static int default_bug_once = 0; + + IF_BUG_ONCE(!addr) + return "NULL pointer"; + + switch (tor_addr_family(addr)) { + case AF_INET6: + return "IPv6"; + case AF_INET: + return "IPv4"; + case AF_UNIX: + return "UNIX socket"; + case AF_UNSPEC: + return "unspecified"; + default: + if (!default_bug_once) { + log_warn(LD_BUG, "Called with unknown address family %d", + (int)tor_addr_family(addr)); + default_bug_once = 1; + } + return "unknown"; + } + //return "(unreachable code)"; +} + /** Convert the string in <b>src</b> to a tor_addr_t <b>addr</b>. The string * may be an IPv4 address, or an IPv6 address surrounded by square brackets. * @@ -1416,10 +1464,10 @@ ifconf_free_ifc_buf(struct ifconf *ifc) * into smartlist of <b>tor_addr_t</b> structures. */ STATIC smartlist_t * -ifreq_to_smartlist(char *buf, size_t buflen) +ifreq_to_smartlist(const uint8_t *buf, size_t buflen) { smartlist_t *result = smartlist_new(); - char *end = buf + buflen; + const uint8_t *end = buf + buflen; /* These acrobatics are due to alignment issues which trigger * undefined behaviour traps on OSX. */ @@ -1493,7 +1541,7 @@ get_interface_addresses_ioctl(int severity, sa_family_t family) /* Ensure we have least IFREQ_SIZE bytes unused at the end. Otherwise, we * don't know if we got everything during ioctl. */ } while (mult * IFREQ_SIZE - ifc.ifc_len <= IFREQ_SIZE); - result = ifreq_to_smartlist(ifc.ifc_buf, ifc.ifc_len); + result = ifreq_to_smartlist((const uint8_t *)ifc.ifc_buf, ifc.ifc_len); done: if (fd >= 0) diff --git a/src/lib/net/address.h b/src/lib/net/address.h @@ -104,6 +104,10 @@ int tor_addr_from_sockaddr(tor_addr_t *a, const struct sockaddr *sa, uint16_t *port_out); void tor_addr_make_unspec(tor_addr_t *a); void tor_addr_make_null(tor_addr_t *a, sa_family_t family); +#define tor_addr_port_make_null(addr, port, family) \ + (void)(tor_addr_make_null(addr, family), (port) = 0) +#define tor_addr_port_make_null_ap(ap, family) \ + tor_addr_port_make_null(&(ap)->addr, (ap)->port, family) char *tor_sockaddr_to_str(const struct sockaddr *sa); /** Return an in6_addr* equivalent to <b>a</b>, or NULL if <b>a</b> is not @@ -221,7 +225,9 @@ char *tor_addr_to_str_dup(const tor_addr_t *addr) ATTR_MALLOC; const char *fmt_addr_impl(const tor_addr_t *addr, int decorate); const char *fmt_addrport(const tor_addr_t *addr, uint16_t port); -const char * fmt_addr32(uint32_t addr); +#define fmt_addrport_ap(ap) fmt_addrport(&(ap)->addr, (ap)->port) +const char *fmt_addr32(uint32_t addr); +const char *fmt_addr_family(const tor_addr_t *addr); MOCK_DECL(int,get_interface_address6,(int severity, sa_family_t family, tor_addr_t *addr)); @@ -298,11 +304,12 @@ void tor_addr_from_ipv4n(tor_addr_t *dest, uint32_t v4addr); * order. */ #define tor_addr_from_ipv4h(dest, v4addr) \ tor_addr_from_ipv4n((dest), htonl(v4addr)) -void tor_addr_from_ipv6_bytes(tor_addr_t *dest, const char *bytes); +void tor_addr_from_ipv6_bytes(tor_addr_t *dest, const uint8_t *bytes); /** Set <b>dest</b> to the IPv4 address incoded in <b>in</b>. */ #define tor_addr_from_in(dest, in) \ tor_addr_from_ipv4n((dest), (in)->s_addr); void tor_addr_from_in6(tor_addr_t *dest, const struct in6_addr *in6); +void tor_addr_copy_ipv6_bytes(uint8_t *dest, const tor_addr_t *src); int tor_addr_is_null(const tor_addr_t *addr); int tor_addr_is_loopback(const tor_addr_t *addr); @@ -393,8 +400,8 @@ STATIC struct smartlist_t *get_interface_addresses_win32(int severity, #endif /* defined(HAVE_IP_ADAPTER_TO_SMARTLIST) */ #ifdef HAVE_IFCONF_TO_SMARTLIST -STATIC struct smartlist_t *ifreq_to_smartlist(char *ifr, - size_t buflen); +STATIC struct smartlist_t *ifreq_to_smartlist(const uint8_t *ifr, + size_t buflen); STATIC struct smartlist_t *get_interface_addresses_ioctl(int severity, sa_family_t family); #endif /* defined(HAVE_IFCONF_TO_SMARTLIST) */ diff --git a/src/test/test_address.c b/src/test/test_address.c @@ -460,7 +460,7 @@ test_address_ifreq_to_smartlist(void *arg) ifc->ifc_len = sizeof(struct ifreq); ifc->ifc_ifcu.ifcu_req = ifr; - results = ifreq_to_smartlist(ifc->ifc_buf,ifc->ifc_len); + results = ifreq_to_smartlist((const uint8_t *)ifc->ifc_buf,ifc->ifc_len); tt_int_op(smartlist_len(results),OP_EQ,1); tor_addr = smartlist_get(results, 0); @@ -483,7 +483,7 @@ test_address_ifreq_to_smartlist(void *arg) SMARTLIST_FOREACH(results, tor_addr_t *, t, tor_free(t)); smartlist_free(results); - results = ifreq_to_smartlist(ifc->ifc_buf,ifc->ifc_len); + results = ifreq_to_smartlist((const uint8_t *)ifc->ifc_buf,ifc->ifc_len); tt_int_op(smartlist_len(results),OP_EQ,2); tor_addr = smartlist_get(results, 0); diff --git a/src/test/test_cell_formats.c b/src/test/test_cell_formats.c @@ -713,16 +713,20 @@ test_cfmt_extend_cells(void *arg) tt_mem_op(cc->onionskin,OP_EQ, b, 99+20); tt_int_op(0, OP_EQ, extend_cell_format(&p2_cmd, &p2_len, p2, &ec)); tt_int_op(p2_cmd, OP_EQ, RELAY_COMMAND_EXTEND2); - /* We'll generate it minus the IPv6 address and minus the konami code */ - tt_int_op(p2_len, OP_EQ, 89+99-34-20); + /* We'll generate it minus the konami code */ + tt_int_op(p2_len, OP_EQ, 89+99-34); test_memeq_hex(p2, - /* Two items: one that same darn IP address. */ - "02000612F40001F0F1" - /* The next is a digest : anthropomorphization */ - "0214616e7468726f706f6d6f727068697a6174696f6e" + /* Three items */ + "03" + /* IPv4 address */ + "0006" "12F40001" "F0F1" + /* The next is an RSA digest: anthropomorphization */ + "0214" "616e7468726f706f6d6f727068697a6174696f6e" + /*IPv6 address */ + "0112" "20020000000000000000000000f0c51e" "1112" /* Now the handshake prologue */ "01050063"); - tt_mem_op(p2+1+8+22+4,OP_EQ, b, 99+20); + tt_mem_op(p2+1+8+22+20+4, OP_EQ, b, 99+20); tt_int_op(0, OP_EQ, create_cell_format_relayed(&cell, cc)); /* Now let's add an ed25519 key to that extend2 cell. */ @@ -732,22 +736,31 @@ test_cfmt_extend_cells(void *arg) /* As before, since we aren't extending by ed25519. */ get_options_mutable()->ExtendByEd25519ID = 0; tt_int_op(0, OP_EQ, extend_cell_format(&p2_cmd, &p2_len, p2, &ec)); - tt_int_op(p2_len, OP_EQ, 89+99-34-20); + tt_int_op(p2_len, OP_EQ, 89+99-34); test_memeq_hex(p2, - "02000612F40001F0F1" + "03" + "000612F40001F0F1" "0214616e7468726f706f6d6f727068697a6174696f6e" + "011220020000000000000000000000f0c51e1112" "01050063"); /* Now try with the ed25519 ID. */ get_options_mutable()->ExtendByEd25519ID = 1; tt_int_op(0, OP_EQ, extend_cell_format(&p2_cmd, &p2_len, p2, &ec)); - tt_int_op(p2_len, OP_EQ, 89+99-34-20 + 34); + tt_int_op(p2_len, OP_EQ, 89+99); test_memeq_hex(p2, - "03000612F40001F0F1" + /* Four items */ + "04" + /* IPv4 address */ + "0006" "12F40001" "F0F1" + /* The next is an RSA digest: anthropomorphization */ "0214616e7468726f706f6d6f727068697a6174696f6e" - // ed digest follows: + /* Then an ed public key: brownshoesdontmakeit/brownshoesd */ "0320" "62726f776e73686f6573646f6e746d616b656" "9742f62726f776e73686f657364" + /*IPv6 address */ + "0112" "20020000000000000000000000f0c51e" "1112" + /* Now the handshake prologue */ "01050063"); /* Can we parse that? Did the key come through right? */ memset(&ec, 0, sizeof(ec)); @@ -756,6 +769,40 @@ test_cfmt_extend_cells(void *arg) tt_mem_op("brownshoesdontmakeit/brownshoesd", OP_EQ, ec.ed_pubkey.pubkey, 32); + /* Now try IPv6 without IPv4 */ + memset(p, 0, sizeof(p)); + memcpy(p, "\x02", 1); + memcpy(p+1, "\x02\x14" "anthropomorphization", 22); + memcpy(p+23, "\x01\x12" "xxxxxxxxxxxxxxxxYY", 20); + memcpy(p+43, "\xff\xff\x00\x20", 4); + tt_int_op(0, OP_EQ, extend_cell_parse(&ec, RELAY_COMMAND_EXTEND2, + p, sizeof(p))); + tt_int_op(RELAY_COMMAND_EXTEND2, OP_EQ, ec.cell_type); + tt_assert(fast_mem_is_zero((const char *)&ec.orport_ipv4.addr, + sizeof(tor_addr_t))); + tt_int_op(0, OP_EQ, ec.orport_ipv4.port); + tt_str_op("7878:7878:7878:7878:7878:7878:7878:7878", + OP_EQ, fmt_addr(&ec.orport_ipv6.addr)); + tt_int_op(22873, OP_EQ, ec.orport_ipv6.port); + tt_assert(ed25519_public_key_is_zero(&ec.ed_pubkey)); + tt_mem_op(ec.node_id,OP_EQ, "anthropomorphization", 20); + tt_int_op(cc->cell_type, OP_EQ, CELL_CREATE2); + tt_int_op(cc->handshake_type, OP_EQ, 0xffff); + tt_int_op(cc->handshake_len, OP_EQ, 32); + tt_int_op(0, OP_EQ, extend_cell_format(&p2_cmd, &p2_len, p2, &ec)); + tt_int_op(p2_cmd, OP_EQ, RELAY_COMMAND_EXTEND2); + tt_int_op(p2_len, OP_EQ, 47+32); + test_memeq_hex(p2, + /* Two items */ + "02" + /* The next is an RSA digest: anthropomorphization */ + "0214" "616e7468726f706f6d6f727068697a6174696f6e" + /*IPv6 address */ + "0112" "78787878787878787878787878787878" "5959" + /* Now the handshake prologue */ + "ffff0020"); + tt_int_op(0, OP_EQ, create_cell_format_relayed(&cell, cc)); + /* == Now try parsing some junk */ /* Try a too-long handshake */ @@ -811,13 +858,6 @@ test_cfmt_extend_cells(void *arg) memcpy(p+48, "\xff\xff\x00\x20", 4); tt_int_op(-1, OP_EQ, extend_cell_parse(&ec, RELAY_COMMAND_EXTEND2, p, sizeof(p))); - memset(p, 0, sizeof(p)); - memcpy(p, "\x02", 1); - memcpy(p+1, "\x02\x14" "anarchoindividualist", 22); - memcpy(p+23, "\x01\x12" "xxxxxxxxxxxxxxxxYY", 18); - memcpy(p+41, "\xff\xff\x00\x20", 4); - tt_int_op(-1, OP_EQ, extend_cell_parse(&ec, RELAY_COMMAND_EXTEND2, - p, sizeof(p))); /* Running out of space in specifiers */ memset(p,0,sizeof(p)); diff --git a/src/test/test_channel.c b/src/test/test_channel.c @@ -1326,7 +1326,7 @@ test_channel_for_extend(void *arg) channel_t *ret_chan = NULL; char digest[DIGEST_LEN]; ed25519_public_key_t ed_id; - tor_addr_t addr; + tor_addr_t ipv4_addr, ipv6_addr; const char *msg; int launch; time_t now = time(NULL); @@ -1336,6 +1336,9 @@ test_channel_for_extend(void *arg) memset(digest, 'A', sizeof(digest)); memset(&ed_id, 'B', sizeof(ed_id)); + tor_addr_make_null(&ipv4_addr, AF_INET); + tor_addr_make_null(&ipv6_addr, AF_INET6); + chan1 = new_fake_channel(); tt_assert(chan1); /* Need to be registered to get added to the id map. */ @@ -1366,7 +1369,8 @@ test_channel_for_extend(void *arg) tt_ptr_op(channel_find_by_remote_identity(digest, &ed_id), OP_EQ, chan1); /* The expected result is chan2 because it is older than chan1. */ - ret_chan = channel_get_for_extend(digest, &ed_id, &addr, &msg, &launch); + ret_chan = channel_get_for_extend(digest, &ed_id, &ipv4_addr, &ipv6_addr, + &msg, &launch); tt_assert(ret_chan); tt_ptr_op(ret_chan, OP_EQ, chan2); tt_int_op(launch, OP_EQ, 0); @@ -1374,7 +1378,8 @@ test_channel_for_extend(void *arg) /* Switch that around from previous test. */ chan2->timestamp_created = chan1->timestamp_created + 1; - ret_chan = channel_get_for_extend(digest, &ed_id, &addr, &msg, &launch); + ret_chan = channel_get_for_extend(digest, &ed_id, &ipv4_addr, &ipv6_addr, + &msg, &launch); tt_assert(ret_chan); tt_ptr_op(ret_chan, OP_EQ, chan1); tt_int_op(launch, OP_EQ, 0); @@ -1383,7 +1388,8 @@ test_channel_for_extend(void *arg) /* Same creation time, num circuits will be used and they both have 0 so the * channel 2 should be picked due to how channel_is_better() works. */ chan2->timestamp_created = chan1->timestamp_created; - ret_chan = channel_get_for_extend(digest, &ed_id, &addr, &msg, &launch); + ret_chan = channel_get_for_extend(digest, &ed_id, &ipv4_addr, &ipv6_addr, + &msg, &launch); tt_assert(ret_chan); tt_ptr_op(ret_chan, OP_EQ, chan1); tt_int_op(launch, OP_EQ, 0); @@ -1394,7 +1400,8 @@ test_channel_for_extend(void *arg) /* Condemned the older channel. */ chan1->state = CHANNEL_STATE_CLOSING; - ret_chan = channel_get_for_extend(digest, &ed_id, &addr, &msg, &launch); + ret_chan = channel_get_for_extend(digest, &ed_id, &ipv4_addr, &ipv6_addr, + &msg, &launch); tt_assert(ret_chan); tt_ptr_op(ret_chan, OP_EQ, chan2); tt_int_op(launch, OP_EQ, 0); @@ -1403,7 +1410,8 @@ test_channel_for_extend(void *arg) /* Make the older channel a client one. */ channel_mark_client(chan1); - ret_chan = channel_get_for_extend(digest, &ed_id, &addr, &msg, &launch); + ret_chan = channel_get_for_extend(digest, &ed_id, &ipv4_addr, &ipv6_addr, + &msg, &launch); tt_assert(ret_chan); tt_ptr_op(ret_chan, OP_EQ, chan2); tt_int_op(launch, OP_EQ, 0); @@ -1413,8 +1421,9 @@ test_channel_for_extend(void *arg) /* Non matching ed identity with valid digest. */ ed25519_public_key_t dumb_ed_id; memset(&dumb_ed_id, 0, sizeof(dumb_ed_id)); - ret_chan = channel_get_for_extend(digest, &dumb_ed_id, &addr, &msg, - &launch); + ret_chan = channel_get_for_extend(digest, &dumb_ed_id, + &ipv4_addr, &ipv6_addr, + &msg, &launch); tt_assert(!ret_chan); tt_str_op(msg, OP_EQ, "Not connected. Connecting."); tt_int_op(launch, OP_EQ, 1); @@ -1423,7 +1432,8 @@ test_channel_for_extend(void *arg) test_chan_should_match_target = 1; chan1->state = CHANNEL_STATE_OPENING; chan2->state = CHANNEL_STATE_OPENING; - ret_chan = channel_get_for_extend(digest, &ed_id, &addr, &msg, &launch); + ret_chan = channel_get_for_extend(digest, &ed_id, &ipv4_addr, &ipv6_addr, + &msg, &launch); tt_assert(!ret_chan); tt_str_op(msg, OP_EQ, "Connection in progress; waiting."); tt_int_op(launch, OP_EQ, 0); @@ -1432,7 +1442,8 @@ test_channel_for_extend(void *arg) /* Mark channel 1 as bad for circuits. */ channel_mark_bad_for_new_circs(chan1); - ret_chan = channel_get_for_extend(digest, &ed_id, &addr, &msg, &launch); + ret_chan = channel_get_for_extend(digest, &ed_id, &ipv4_addr, &ipv6_addr, + &msg, &launch); tt_assert(ret_chan); tt_ptr_op(ret_chan, OP_EQ, chan2); tt_int_op(launch, OP_EQ, 0); @@ -1442,7 +1453,8 @@ test_channel_for_extend(void *arg) /* Mark both channels as unusable. */ channel_mark_bad_for_new_circs(chan1); channel_mark_bad_for_new_circs(chan2); - ret_chan = channel_get_for_extend(digest, &ed_id, &addr, &msg, &launch); + ret_chan = channel_get_for_extend(digest, &ed_id, &ipv4_addr, &ipv6_addr, + &msg, &launch); tt_assert(!ret_chan); tt_str_op(msg, OP_EQ, "Connections all too old, or too non-canonical. " " Launching a new one."); @@ -1453,7 +1465,8 @@ test_channel_for_extend(void *arg) /* Non canonical channels. */ test_chan_should_match_target = 0; test_chan_canonical_should_be_reliable = 1; - ret_chan = channel_get_for_extend(digest, &ed_id, &addr, &msg, &launch); + ret_chan = channel_get_for_extend(digest, &ed_id, &ipv4_addr, &ipv6_addr, + &msg, &launch); tt_assert(!ret_chan); tt_str_op(msg, OP_EQ, "Connections all too old, or too non-canonical. " " Launching a new one."); diff --git a/src/test/test_circuitbuild.c b/src/test/test_circuitbuild.c @@ -30,6 +30,7 @@ #include "feature/client/entrynodes.h" #include "feature/nodelist/nodelist.h" #include "feature/relay/circuitbuild_relay.h" +#include "feature/relay/router.h" #include "feature/relay/routermode.h" #include "feature/nodelist/node_st.h" @@ -219,6 +220,7 @@ test_circuit_extend_state_valid(void *arg) 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; @@ -228,6 +230,7 @@ test_circuit_extend_state_valid(void *arg) "!(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; @@ -314,6 +317,7 @@ test_circuit_extend_add_ed25519(void *arg) 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); @@ -322,6 +326,7 @@ test_circuit_extend_add_ed25519(void *arg) "!(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)); @@ -474,6 +479,9 @@ mock_get_options(void) #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(). */ @@ -492,6 +500,7 @@ test_circuit_extend_lspec_valid(void *arg) 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); @@ -518,41 +527,114 @@ test_circuit_extend_lspec_valid(void *arg) 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 addr or port are 0, these should fail */ + /* 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 " - "zero destination port or addr.\n"); + 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 " - "zero destination port or addr.\n"); + 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_make_null(&ec->orport_ipv4.addr, AF_INET); + 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 " - "zero destination port or addr.\n"); + 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 address.\n"); + 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); @@ -561,10 +643,27 @@ test_circuit_extend_lspec_valid(void *arg) 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; @@ -591,6 +690,7 @@ test_circuit_extend_lspec_valid(void *arg) 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); @@ -622,6 +722,41 @@ test_circuit_extend_lspec_valid(void *arg) 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)); @@ -686,6 +821,97 @@ test_circuit_extend_lspec_valid(void *arg) tor_free(p_chan); } +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, @@ -714,23 +940,41 @@ mock_channel_connect_for_circuit(const tor_addr_t *addr, return mock_channel_connect_nchan; } -/* Test the different cases in circuit_open_connection_for_extend(). */ +/* 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) { - (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; @@ -772,6 +1016,33 @@ test_circuit_open_connection_for_extend(void *arg) 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; @@ -812,7 +1083,7 @@ test_circuit_open_connection_for_extend(void *arg) mock_circuit_close_calls = 0; mock_channel_connect_calls = 0; circuit_open_connection_for_extend(ec, circ, 1); - /* Try to connect, and succeed, leaving the circuit open */ + /* 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 */ @@ -835,6 +1106,12 @@ test_circuit_open_connection_for_extend(void *arg) 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); @@ -869,13 +1146,15 @@ 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_addr, + const tor_addr_t *target_ipv4_addr, + const tor_addr_t *target_ipv6_addr, const char **msg_out, int *launch_out) { (void)rsa_id_digest; (void)ed_id; - (void)target_addr; + (void)target_ipv4_addr; + (void)target_ipv6_addr; /* channel_get_for_extend() requires non-NULL arguments */ tt_ptr_op(msg_out, OP_NE, NULL); @@ -941,6 +1220,7 @@ test_circuit_extend(void *arg) 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); @@ -967,6 +1247,7 @@ test_circuit_extend(void *arg) 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; @@ -1002,8 +1283,8 @@ test_circuit_extend(void *arg) 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 " - "zero destination port or addr.\n"); + 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; @@ -1012,6 +1293,7 @@ test_circuit_extend(void *arg) 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); @@ -1021,6 +1303,7 @@ test_circuit_extend(void *arg) 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; @@ -1166,6 +1449,7 @@ test_onionskin_answer(void *arg) 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, @@ -1209,6 +1493,7 @@ test_onionskin_answer(void *arg) "!(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. */ @@ -1240,6 +1525,12 @@ test_onionskin_answer(void *arg) #define TEST_CIRCUIT(name, flags) \ { #name, test_circuit_ ## 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[] = { TEST_NEW_ROUTE_LEN(noexit, 0), TEST_NEW_ROUTE_LEN(safe_exit, 0), @@ -1251,7 +1542,10 @@ struct testcase_t circuitbuild_tests[] = { TEST_CIRCUIT(extend_state_valid, TT_FORK), TEST_CIRCUIT(extend_add_ed25519, TT_FORK), TEST_CIRCUIT(extend_lspec_valid, TT_FORK), - TEST_CIRCUIT(open_connection_for_extend, 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), diff --git a/src/tools/tor-resolve.c b/src/tools/tor-resolve.c @@ -509,7 +509,7 @@ do_resolve(const char *hostname, } else if (atype == SOCKS5_ATYPE_IPV6) { /* IPv6 address */ tor_addr_from_ipv6_bytes(result_addr, - (const char *)socks5_server_reply_getarray_bind_addr_ipv6(reply)); + socks5_server_reply_getarray_bind_addr_ipv6(reply)); } else if (atype == SOCKS5_ATYPE_HOSTNAME) { /* Domain name */ domainname_t *dn =