From 16486662038de53c482cd6f50a30505f2bf20453 Mon Sep 17 00:00:00 2001 From: "teor (Tim Wilson-Brown)" Date: Sun, 3 Jan 2016 18:20:37 +1100 Subject: [PATCH] Choose bridge addresses by IPv4/IPv6 preferences --- src/or/directory.c | 176 ++++++++++++++++++++++++++++++++-------------------- src/or/directory.h | 4 +- src/or/entrynodes.c | 13 +++- src/or/router.c | 5 +- 4 files changed, 123 insertions(+), 75 deletions(-) diff --git a/src/or/directory.c b/src/or/directory.c index 20ffcee8dd..665ba27767 100644 --- a/src/or/directory.c +++ b/src/or/directory.c @@ -497,11 +497,14 @@ MOCK_IMPL(void, directory_get_from_dirserver, ( const node_t *node = choose_random_dirguard(type); if (node && node->ri) { /* every bridge has a routerinfo. */ - tor_addr_t addr; routerinfo_t *ri = node->ri; - node_get_addr(node, &addr); - directory_initiate_command(&addr, - ri->or_port, 0/*no dirport*/, + /* clients always make OR connections to bridges */ + tor_addr_port_t or_ap; + /* we are willing to use a non-preferred address if we need to */ + fascist_firewall_choose_address_node(node, FIREWALL_OR_CONNECTION, 0, + &or_ap); + directory_initiate_command(&or_ap.addr, or_ap.port, + NULL, 0, /*no dirport*/ ri->cache_info.identity_digest, dir_purpose, router_purpose, @@ -610,6 +613,80 @@ dirind_is_anon(dir_indirection_t ind) return ind == DIRIND_ANON_DIRPORT || ind == DIRIND_ANONYMOUS; } +/* Choose reachable OR and Dir addresses and ports from status, copying them + * into use_or_ap and use_dir_ap. If indirection is anonymous, then we're + * connecting via another relay, so choose the primary IPv4 address and ports. + * + * status should have at least one reachable address, if we can't choose a + * reachable address, warn and return -1. Otherwise, return 0. + */ +static int +directory_choose_address_routerstatus(const routerstatus_t *status, + dir_indirection_t indirection, + tor_addr_port_t *use_or_ap, + tor_addr_port_t *use_dir_ap) +{ + tor_assert(status != NULL); + tor_assert(use_or_ap != NULL); + tor_assert(use_dir_ap != NULL); + + const int anonymized_connection = dirind_is_anon(indirection); + int have_or = 0, have_dir = 0; + + /* We expect status to have at least one reachable address if we're + * connecting to it directly. + * + * Therefore, we can simply use the other address if the one we want isn't + * allowed by the firewall. + * + * (When Tor uploads and downloads a hidden service descriptor, it uses + * DIRIND_ANONYMOUS, except for Tor2Web, which uses DIRIND_ONEHOP. + * So this code will only modify the address for Tor2Web's HS descriptor + * fetches. Even Single Onion Servers (NYI) use DIRIND_ANONYMOUS, to avoid + * HSDirs denying service by rejecting descriptors.) + */ + + /* Initialise the OR / Dir addresses */ + tor_addr_make_null(&use_or_ap->addr, AF_UNSPEC); + use_or_ap->port = 0; + tor_addr_make_null(&use_dir_ap->addr, AF_UNSPEC); + use_dir_ap->port = 0; + + if (anonymized_connection) { + /* Use the primary (IPv4) OR address if we're making an indirect + * connection. */ + tor_addr_from_ipv4h(&use_or_ap->addr, status->addr); + use_or_ap->port = status->or_port; + have_or = 1; + } else { + /* We use an IPv6 address if we have one and we prefer it. + * Use the preferred address and port if they are reachable, otherwise, + * use the alternate address and port (if any). + */ + have_or = fascist_firewall_choose_address_rs(status, + FIREWALL_OR_CONNECTION, 0, + use_or_ap); + } + + have_dir = fascist_firewall_choose_address_rs(status, + FIREWALL_DIR_CONNECTION, 0, + use_dir_ap); + + /* We rejected both addresses. This isn't great. */ + if (!have_or && !have_dir) { + log_warn(LD_BUG, "Rejected all OR and Dir addresses from %s when " + "launching a directory connection to: IPv4 %s OR %d Dir %d " + "IPv6 %s OR %d Dir %d", routerstatus_describe(status), + fmt_addr32(status->addr), status->or_port, + status->dir_port, fmt_addr(&status->ipv6_addr), + status->ipv6_orport, status->dir_port); + log_backtrace(LOG_WARN, LD_BUG, "Addresses came from"); + return -1; + } + + return 0; +} + /** Same as directory_initiate_command_routerstatus(), but accepts * rendezvous data to fetch a hidden service descriptor. */ void @@ -627,7 +704,8 @@ directory_initiate_command_routerstatus_rend(const routerstatus_t *status, const node_t *node; tor_addr_port_t use_or_ap, use_dir_ap; const int anonymized_connection = dirind_is_anon(indirection); - int have_or = 0, have_dir = 0; + + tor_assert(status != NULL); node = node_get_by_id(status->identity_digest); @@ -654,57 +732,16 @@ directory_initiate_command_routerstatus_rend(const routerstatus_t *status, * selection uses the preference in ClientPreferIPv6{OR,Dir}Port, if * possible. (If UseBridges is set, clients ignore all these settings.) * - * Now we use a similar process to select an address for the relay, - * but simply use the other address if the one we want isn't allowed by - * the firewall. - * - * (When Tor uploads and downloads a hidden service descriptor, it uses - * DIRIND_ANONYMOUS, except for Tor2Web, which uses DIRIND_ONEHOP. - * So this code will only modify the address for Tor2Web's HS descriptor - * fetches. Even Single Onion Servers (NYI) use DIRIND_ANONYMOUS, to avoid - * HSDirs denying service by rejecting descriptors.) + * Now choose an address that we can use to connect to the directory server. */ - - /* Initialise the OR / Dir addresses */ - tor_addr_make_null(&use_or_ap.addr, AF_UNSPEC); - use_or_ap.port = 0; - tor_addr_make_null(&use_dir_ap.addr, AF_UNSPEC); - use_dir_ap.port = 0; - - if (anonymized_connection) { - /* Use the primary (IPv4) OR address if we're making an indirect - * connection. */ - tor_addr_from_ipv4h(&use_or_ap.addr, status->addr); - use_or_ap.port = status->or_port; - have_or = 1; - } else { - /* We use an IPv6 address if we have one and we prefer it. - * Use the preferred address and port if they are reachable, otherwise, - * use the alternate address and port (if any). - */ - have_or = fascist_firewall_choose_address_rs(status, - FIREWALL_OR_CONNECTION, 0, - &use_or_ap); - } - - have_dir = fascist_firewall_choose_address_rs(status, - FIREWALL_DIR_CONNECTION, 0, - &use_dir_ap); - - /* We rejected both addresses. This isn't great. */ - if (!have_or && !have_dir) { - log_warn(LD_BUG, "Rejected all OR and Dir addresses from %s when " - "launching a directory connection to: IPv4 %s OR %d Dir %d " - "IPv6 %s OR %d Dir %d", routerstatus_describe(status), - fmt_addr32(status->addr), status->or_port, - status->dir_port, fmt_addr(&status->ipv6_addr), - status->ipv6_orport, status->dir_port); - log_backtrace(LOG_WARN, LD_BUG, "Addresses came from"); + if (directory_choose_address_routerstatus(status, indirection, &use_or_ap, + &use_dir_ap) < 0) { return; } - /* XX/teor - we don't retry the alternate OR/Dir address if this one fails. - * (See #6772.) Instead, we'll retry another directory on failure. */ + /* We don't retry the alternate OR/Dir address for the same directory if + * the address we choose fails (#6772). + * Instead, we'll retry another directory on failure. */ directory_initiate_command_rend(&use_or_ap, &use_dir_ap, status->identity_digest, @@ -941,41 +978,42 @@ directory_command_should_use_begindir(const or_options_t *options, } /** Helper for directory_initiate_command_rend: send the - * command to a server whose address is _addr, whose OR port is - * or_port, whose directory port is dir_port, whose identity key - * digest is digest, with purposes dir_purpose and + * command to a server whose OR address/port is or_addr/or_port, + * whose directory address/port is dir_addr/dir_port, whose + * identity key digest is digest, with purposes dir_purpose and * router_purpose, making an (in)direct connection as specified in * indirection, with command resource, payload of * payload_len, and asking for a result only if_modified_since. */ void -directory_initiate_command(const tor_addr_t *_addr, - uint16_t or_port, uint16_t dir_port, +directory_initiate_command(const tor_addr_t *or_addr, uint16_t or_port, + const tor_addr_t *dir_addr, uint16_t dir_port, const char *digest, uint8_t dir_purpose, uint8_t router_purpose, dir_indirection_t indirection, const char *resource, const char *payload, size_t payload_len, time_t if_modified_since) { - /* Assume _addr applies to both the ORPort and the DirPort, but use the - * null tor_addr if ORPort or DirPort are 0. */ tor_addr_port_t or_ap, dir_ap; - if (or_port) { - tor_addr_copy(&or_ap.addr, _addr); + /* Use the null tor_addr and 0 port if the address or port isn't valid. */ + if (tor_addr_port_is_valid(or_addr, or_port, 0)) { + tor_addr_copy(&or_ap.addr, or_addr); + or_ap.port = or_port; } else { - /* the family doesn't matter here, so make it the same as _addr */ - tor_addr_make_null(&or_ap.addr, tor_addr_family(_addr)); + /* the family doesn't matter here, so make it IPv4 */ + tor_addr_make_null(&or_ap.addr, AF_INET); + or_port = 0; } - or_ap.port = or_port; - if (dir_port) { - tor_addr_copy(&dir_ap.addr, _addr); + if (tor_addr_port_is_valid(dir_addr, dir_port, 0)) { + tor_addr_copy(&dir_ap.addr, dir_addr); + dir_ap.port = dir_port; } else { - /* the family doesn't matter here, so make it the same as _addr */ - tor_addr_make_null(&dir_ap.addr, tor_addr_family(_addr)); + /* the family doesn't matter here, so make it IPv4 */ + tor_addr_make_null(&dir_ap.addr, AF_INET); + dir_port = 0; } - dir_ap.port = dir_port; directory_initiate_command_rend(&or_ap, &dir_ap, digest, dir_purpose, diff --git a/src/or/directory.h b/src/or/directory.h index 2644e5703e..61c29baf06 100644 --- a/src/or/directory.h +++ b/src/or/directory.h @@ -66,8 +66,8 @@ int connection_dir_process_inbuf(dir_connection_t *conn); int connection_dir_finished_flushing(dir_connection_t *conn); int connection_dir_finished_connecting(dir_connection_t *conn); void connection_dir_about_to_close(dir_connection_t *dir_conn); -void directory_initiate_command(const tor_addr_t *addr, - uint16_t or_port, uint16_t dir_port, +void directory_initiate_command(const tor_addr_t *or_addr, uint16_t or_port, + const tor_addr_t *dir_addr, uint16_t dir_port, const char *digest, uint8_t dir_purpose, uint8_t router_purpose, dir_indirection_t indirection, diff --git a/src/or/entrynodes.c b/src/or/entrynodes.c index e358e92ccd..583b7efa8b 100644 --- a/src/or/entrynodes.c +++ b/src/or/entrynodes.c @@ -2117,8 +2117,17 @@ launch_direct_bridge_descriptor_fetch(bridge_info_t *bridge) return; } - directory_initiate_command(&bridge->addr, - bridge->port, 0/*no dirport*/, + /* Until we get a descriptor for the bridge, we only know one address for + * it. If we */ + if (!fascist_firewall_allows_address_addr(&bridge->addr, bridge->port, + FIREWALL_OR_CONNECTION, 0)) { + log_notice(LD_CONFIG, "Tried to fetch a descriptor directly from a bridge, " + "but that bridge is not reachable through our firewall."); + return; + } + + directory_initiate_command(&bridge->addr, bridge->port, + NULL, 0, /*no dirport*/ bridge->identity, DIR_PURPOSE_FETCH_SERVERDESC, ROUTER_PURPOSE_BRIDGE, diff --git a/src/or/router.c b/src/or/router.c index ba32d77fd1..d4131992aa 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -1244,14 +1244,15 @@ consider_testing_reachability(int test_or, int test_dir) extend_info_free(ei); } + /* XXX IPv6 self testing */ tor_addr_from_ipv4h(&addr, me->addr); if (test_dir && !check_whether_dirport_reachable() && !connection_get_by_type_addr_port_purpose( CONN_TYPE_DIR, &addr, me->dir_port, DIR_PURPOSE_FETCH_SERVERDESC)) { /* ask myself, via tor, for my server descriptor. */ - directory_initiate_command(&addr, - me->or_port, me->dir_port, + directory_initiate_command(&addr, me->or_port, + &addr, me->dir_port, me->cache_info.identity_digest, DIR_PURPOSE_FETCH_SERVERDESC, ROUTER_PURPOSE_GENERAL, -- 2.11.4.GIT