diff options
Diffstat (limited to 'src/or')
63 files changed, 7537 insertions, 2172 deletions
diff --git a/src/or/bridges.c b/src/or/bridges.c new file mode 100644 index 0000000000..7d1acdfeaa --- /dev/null +++ b/src/or/bridges.c @@ -0,0 +1,866 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file bridges.c + * \brief Code to manage bridges and bridge selection. + * + * Bridges are fixed entry nodes, used for censorship circumvention. + **/ + +#include "or.h" +#include "bridges.h" +#include "circuitbuild.h" +#include "config.h" +#include "connection.h" +#include "directory.h" +#include "entrynodes.h" +#include "nodelist.h" +#include "policies.h" +#include "router.h" +#include "routerlist.h" +#include "routerset.h" +#include "transports.h" + +/** Information about a configured bridge. Currently this just matches the + * ones in the torrc file, but one day we may be able to learn about new + * bridges on our own, and remember them in the state file. */ +struct bridge_info_t { + /** Address and port of the bridge, as configured by the user.*/ + tor_addr_port_t addrport_configured; + /** Address of the bridge. */ + tor_addr_t addr; + /** TLS port for the bridge. */ + uint16_t port; + /** Boolean: We are re-parsing our bridge list, and we are going to remove + * this one if we don't find it in the list of configured bridges. */ + unsigned marked_for_removal : 1; + /** Expected identity digest, or all zero bytes if we don't know what the + * digest should be. */ + char identity[DIGEST_LEN]; + + /** Name of pluggable transport protocol taken from its config line. */ + char *transport_name; + + /** When should we next try to fetch a descriptor for this bridge? */ + download_status_t fetch_status; + + /** A smartlist of k=v values to be passed to the SOCKS proxy, if + transports are used for this bridge. */ + smartlist_t *socks_args; +}; + +static void bridge_free(bridge_info_t *bridge); + +/** A list of configured bridges. Whenever we actually get a descriptor + * for one, we add it as an entry guard. Note that the order of bridges + * in this list does not necessarily correspond to the order of bridges + * in the torrc. */ +static smartlist_t *bridge_list = NULL; + +/** Mark every entry of the bridge list to be removed on our next call to + * sweep_bridge_list unless it has first been un-marked. */ +void +mark_bridge_list(void) +{ + if (!bridge_list) + bridge_list = smartlist_new(); + SMARTLIST_FOREACH(bridge_list, bridge_info_t *, b, + b->marked_for_removal = 1); +} + +/** Remove every entry of the bridge list that was marked with + * mark_bridge_list if it has not subsequently been un-marked. */ +void +sweep_bridge_list(void) +{ + if (!bridge_list) + bridge_list = smartlist_new(); + SMARTLIST_FOREACH_BEGIN(bridge_list, bridge_info_t *, b) { + if (b->marked_for_removal) { + SMARTLIST_DEL_CURRENT(bridge_list, b); + bridge_free(b); + } + } SMARTLIST_FOREACH_END(b); +} + +/** Initialize the bridge list to empty, creating it if needed. */ +static void +clear_bridge_list(void) +{ + if (!bridge_list) + bridge_list = smartlist_new(); + SMARTLIST_FOREACH(bridge_list, bridge_info_t *, b, bridge_free(b)); + smartlist_clear(bridge_list); +} + +/** Free the bridge <b>bridge</b>. */ +static void +bridge_free(bridge_info_t *bridge) +{ + if (!bridge) + return; + + tor_free(bridge->transport_name); + if (bridge->socks_args) { + SMARTLIST_FOREACH(bridge->socks_args, char*, s, tor_free(s)); + smartlist_free(bridge->socks_args); + } + + tor_free(bridge); +} + +/** Return a list of all the configured bridges, as bridge_info_t pointers. */ +const smartlist_t * +bridge_list_get(void) +{ + if (!bridge_list) + bridge_list = smartlist_new(); + return bridge_list; +} + +/** + * Given a <b>bridge</b>, return a pointer to its RSA identity digest, or + * NULL if we don't know one for it. + */ +const uint8_t * +bridge_get_rsa_id_digest(const bridge_info_t *bridge) +{ + tor_assert(bridge); + if (tor_digest_is_zero(bridge->identity)) + return NULL; + else + return (const uint8_t *) bridge->identity; +} + +/** + * Given a <b>bridge</b>, return a pointer to its configured addr:port + * combination. + */ +const tor_addr_port_t * +bridge_get_addr_port(const bridge_info_t *bridge) +{ + tor_assert(bridge); + return &bridge->addrport_configured; +} + +/** If we have a bridge configured whose digest matches <b>digest</b>, or a + * bridge with no known digest whose address matches any of the + * tor_addr_port_t's in <b>orports</b>, return that bridge. Else return + * NULL. */ +static bridge_info_t * +get_configured_bridge_by_orports_digest(const char *digest, + const smartlist_t *orports) +{ + if (!bridge_list) + return NULL; + SMARTLIST_FOREACH_BEGIN(bridge_list, bridge_info_t *, bridge) + { + if (tor_digest_is_zero(bridge->identity)) { + SMARTLIST_FOREACH_BEGIN(orports, tor_addr_port_t *, ap) + { + if (tor_addr_compare(&bridge->addr, &ap->addr, CMP_EXACT) == 0 && + bridge->port == ap->port) + return bridge; + } + SMARTLIST_FOREACH_END(ap); + } + if (digest && tor_memeq(bridge->identity, digest, DIGEST_LEN)) + return bridge; + } + SMARTLIST_FOREACH_END(bridge); + return NULL; +} + +/** If we have a bridge configured whose digest matches <b>digest</b>, or a + * bridge with no known digest whose address matches <b>addr</b>:<b>port</b>, + * return that bridge. Else return NULL. If <b>digest</b> is NULL, check for + * address/port matches only. */ +bridge_info_t * +get_configured_bridge_by_addr_port_digest(const tor_addr_t *addr, + uint16_t port, + const char *digest) +{ + if (!bridge_list) + return NULL; + SMARTLIST_FOREACH_BEGIN(bridge_list, bridge_info_t *, bridge) + { + if ((tor_digest_is_zero(bridge->identity) || digest == NULL) && + !tor_addr_compare(&bridge->addr, addr, CMP_EXACT) && + bridge->port == port) + return bridge; + if (digest && tor_memeq(bridge->identity, digest, DIGEST_LEN)) + return bridge; + } + SMARTLIST_FOREACH_END(bridge); + return NULL; +} + +/** If we have a bridge configured whose digest matches <b>digest</b>, or a + * bridge with no known digest whose address matches <b>addr</b>:<b>port</b>, + * return 1. Else return 0. If <b>digest</b> is NULL, check for + * address/port matches only. */ +int +addr_is_a_configured_bridge(const tor_addr_t *addr, + uint16_t port, + const char *digest) +{ + tor_assert(addr); + return get_configured_bridge_by_addr_port_digest(addr, port, digest) ? 1 : 0; +} + +/** If we have a bridge configured whose digest matches + * <b>ei->identity_digest</b>, or a bridge with no known digest whose address + * matches <b>ei->addr</b>:<b>ei->port</b>, return 1. Else return 0. + * If <b>ei->onion_key</b> is NULL, check for address/port matches only. */ +int +extend_info_is_a_configured_bridge(const extend_info_t *ei) +{ + const char *digest = ei->onion_key ? ei->identity_digest : NULL; + return addr_is_a_configured_bridge(&ei->addr, ei->port, digest); +} + +/** Wrapper around get_configured_bridge_by_addr_port_digest() to look + * it up via router descriptor <b>ri</b>. */ +static bridge_info_t * +get_configured_bridge_by_routerinfo(const routerinfo_t *ri) +{ + bridge_info_t *bi = NULL; + smartlist_t *orports = router_get_all_orports(ri); + bi = get_configured_bridge_by_orports_digest(ri->cache_info.identity_digest, + orports); + SMARTLIST_FOREACH(orports, tor_addr_port_t *, p, tor_free(p)); + smartlist_free(orports); + return bi; +} + +/** Return 1 if <b>ri</b> is one of our known bridges, else 0. */ +int +routerinfo_is_a_configured_bridge(const routerinfo_t *ri) +{ + return get_configured_bridge_by_routerinfo(ri) ? 1 : 0; +} + +/** Return 1 if <b>node</b> is one of our configured bridges, else 0. */ +int +node_is_a_configured_bridge(const node_t *node) +{ + int retval = 0; + smartlist_t *orports = node_get_all_orports(node); + retval = get_configured_bridge_by_orports_digest(node->identity, + orports) != NULL; + SMARTLIST_FOREACH(orports, tor_addr_port_t *, p, tor_free(p)); + smartlist_free(orports); + return retval; +} + +/** We made a connection to a router at <b>addr</b>:<b>port</b> + * without knowing its digest. Its digest turned out to be <b>digest</b>. + * If it was a bridge, and we still don't know its digest, record it. + */ +void +learned_router_identity(const tor_addr_t *addr, uint16_t port, + const char *digest, + const ed25519_public_key_t *ed_id) +{ + // XXXX prop220 use ed_id here, once there is some way to specify + (void)ed_id; + int learned = 0; + bridge_info_t *bridge = + get_configured_bridge_by_addr_port_digest(addr, port, digest); + if (bridge && tor_digest_is_zero(bridge->identity)) { + memcpy(bridge->identity, digest, DIGEST_LEN); + learned = 1; + } + /* XXXX prop220 remember bridge ed25519 identities -- add a field */ +#if 0 + if (bridge && ed_id && + ed25519_public_key_is_zero(&bridge->ed25519_identity) && + !ed25519_public_key_is_zero(ed_id)) { + memcpy(&bridge->ed25519_identity, ed_id, sizeof(*ed_id)); + learned = 1; + } +#endif + if (learned) { + char *transport_info = NULL; + const char *transport_name = + find_transport_name_by_bridge_addrport(addr, port); + if (transport_name) + tor_asprintf(&transport_info, " (with transport '%s')", transport_name); + + // XXXX prop220 log both fingerprints. + log_notice(LD_DIR, "Learned fingerprint %s for bridge %s%s.", + hex_str(digest, DIGEST_LEN), fmt_addrport(addr, port), + transport_info ? transport_info : ""); + tor_free(transport_info); + entry_guard_learned_bridge_identity(&bridge->addrport_configured, + (const uint8_t *)digest); + } +} + +/** Return true if <b>bridge</b> has the same identity digest as + * <b>digest</b>. If <b>digest</b> is NULL, it matches + * bridges with unspecified identity digests. */ +static int +bridge_has_digest(const bridge_info_t *bridge, const char *digest) +{ + if (digest) + return tor_memeq(digest, bridge->identity, DIGEST_LEN); + else + return tor_digest_is_zero(bridge->identity); +} + +/** We are about to add a new bridge at <b>addr</b>:<b>port</b>, with optional + * <b>digest</b> and <b>transport_name</b>. Mark for removal any previously + * existing bridge with the same address and port, and warn the user as + * appropriate. + */ +static void +bridge_resolve_conflicts(const tor_addr_t *addr, uint16_t port, + const char *digest, const char *transport_name) +{ + /* Iterate the already-registered bridge list: + + If you find a bridge with the same adress and port, mark it for + removal. It doesn't make sense to have two active bridges with + the same IP:PORT. If the bridge in question has a different + digest or transport than <b>digest</b>/<b>transport_name</b>, + it's probably a misconfiguration and we should warn the user. + */ + SMARTLIST_FOREACH_BEGIN(bridge_list, bridge_info_t *, bridge) { + if (bridge->marked_for_removal) + continue; + + if (tor_addr_eq(&bridge->addr, addr) && (bridge->port == port)) { + + bridge->marked_for_removal = 1; + + if (!bridge_has_digest(bridge, digest) || + strcmp_opt(bridge->transport_name, transport_name)) { + /* warn the user */ + char *bridge_description_new, *bridge_description_old; + tor_asprintf(&bridge_description_new, "%s:%s:%s", + fmt_addrport(addr, port), + digest ? hex_str(digest, DIGEST_LEN) : "", + transport_name ? transport_name : ""); + tor_asprintf(&bridge_description_old, "%s:%s:%s", + fmt_addrport(&bridge->addr, bridge->port), + tor_digest_is_zero(bridge->identity) ? + "" : hex_str(bridge->identity,DIGEST_LEN), + bridge->transport_name ? bridge->transport_name : ""); + + log_warn(LD_GENERAL,"Tried to add bridge '%s', but we found a conflict" + " with the already registered bridge '%s'. We will discard" + " the old bridge and keep '%s'. If this is not what you" + " wanted, please change your configuration file accordingly.", + bridge_description_new, bridge_description_old, + bridge_description_new); + + tor_free(bridge_description_new); + tor_free(bridge_description_old); + } + } + } SMARTLIST_FOREACH_END(bridge); +} + +/** Return True if we have a bridge that uses a transport with name + * <b>transport_name</b>. */ +MOCK_IMPL(int, +transport_is_needed, (const char *transport_name)) +{ + if (!bridge_list) + return 0; + + SMARTLIST_FOREACH_BEGIN(bridge_list, const bridge_info_t *, bridge) { + if (bridge->transport_name && + !strcmp(bridge->transport_name, transport_name)) + return 1; + } SMARTLIST_FOREACH_END(bridge); + + return 0; +} + +/** Register the bridge information in <b>bridge_line</b> to the + * bridge subsystem. Steals reference of <b>bridge_line</b>. */ +void +bridge_add_from_config(bridge_line_t *bridge_line) +{ + bridge_info_t *b; + + // XXXX prop220 add a way to specify ed25519 ID to bridge_line_t. + + { /* Log the bridge we are about to register: */ + log_debug(LD_GENERAL, "Registering bridge at %s (transport: %s) (%s)", + fmt_addrport(&bridge_line->addr, bridge_line->port), + bridge_line->transport_name ? + bridge_line->transport_name : "no transport", + tor_digest_is_zero(bridge_line->digest) ? + "no key listed" : hex_str(bridge_line->digest, DIGEST_LEN)); + + if (bridge_line->socks_args) { /* print socks arguments */ + int i = 0; + + tor_assert(smartlist_len(bridge_line->socks_args) > 0); + + log_debug(LD_GENERAL, "Bridge uses %d SOCKS arguments:", + smartlist_len(bridge_line->socks_args)); + SMARTLIST_FOREACH(bridge_line->socks_args, const char *, arg, + log_debug(LD_CONFIG, "%d: %s", ++i, arg)); + } + } + + bridge_resolve_conflicts(&bridge_line->addr, + bridge_line->port, + bridge_line->digest, + bridge_line->transport_name); + + b = tor_malloc_zero(sizeof(bridge_info_t)); + tor_addr_copy(&b->addrport_configured.addr, &bridge_line->addr); + b->addrport_configured.port = bridge_line->port; + tor_addr_copy(&b->addr, &bridge_line->addr); + b->port = bridge_line->port; + memcpy(b->identity, bridge_line->digest, DIGEST_LEN); + if (bridge_line->transport_name) + b->transport_name = bridge_line->transport_name; + b->fetch_status.schedule = DL_SCHED_BRIDGE; + b->fetch_status.backoff = DL_SCHED_RANDOM_EXPONENTIAL; + b->socks_args = bridge_line->socks_args; + if (!bridge_list) + bridge_list = smartlist_new(); + + tor_free(bridge_line); /* Deallocate bridge_line now. */ + + smartlist_add(bridge_list, b); +} + +/** If <b>digest</b> is one of our known bridges, return it. */ +bridge_info_t * +find_bridge_by_digest(const char *digest) +{ + if (! bridge_list) + return NULL; + SMARTLIST_FOREACH(bridge_list, bridge_info_t *, bridge, + { + if (tor_memeq(bridge->identity, digest, DIGEST_LEN)) + return bridge; + }); + return NULL; +} + +/** Given the <b>addr</b> and <b>port</b> of a bridge, if that bridge + * supports a pluggable transport, return its name. Otherwise, return + * NULL. */ +const char * +find_transport_name_by_bridge_addrport(const tor_addr_t *addr, uint16_t port) +{ + if (!bridge_list) + return NULL; + + SMARTLIST_FOREACH_BEGIN(bridge_list, const bridge_info_t *, bridge) { + if (tor_addr_eq(&bridge->addr, addr) && + (bridge->port == port)) + return bridge->transport_name; + } SMARTLIST_FOREACH_END(bridge); + + return NULL; +} + +/** If <b>addr</b> and <b>port</b> match the address and port of a + * bridge of ours that uses pluggable transports, place its transport + * in <b>transport</b>. + * + * Return 0 on success (found a transport, or found a bridge with no + * transport, or found no bridge); return -1 if we should be using a + * transport, but the transport could not be found. + */ +int +get_transport_by_bridge_addrport(const tor_addr_t *addr, uint16_t port, + const transport_t **transport) +{ + *transport = NULL; + if (!bridge_list) + return 0; + + SMARTLIST_FOREACH_BEGIN(bridge_list, const bridge_info_t *, bridge) { + if (tor_addr_eq(&bridge->addr, addr) && + (bridge->port == port)) { /* bridge matched */ + if (bridge->transport_name) { /* it also uses pluggable transports */ + *transport = transport_get_by_name(bridge->transport_name); + if (*transport == NULL) { /* it uses pluggable transports, but + the transport could not be found! */ + return -1; + } + return 0; + } else { /* bridge matched, but it doesn't use transports. */ + break; + } + } + } SMARTLIST_FOREACH_END(bridge); + + *transport = NULL; + return 0; +} + +/** Return a smartlist containing all the SOCKS arguments that we + * should pass to the SOCKS proxy. */ +const smartlist_t * +get_socks_args_by_bridge_addrport(const tor_addr_t *addr, uint16_t port) +{ + bridge_info_t *bridge = get_configured_bridge_by_addr_port_digest(addr, + port, + NULL); + return bridge ? bridge->socks_args : NULL; +} + +/** We need to ask <b>bridge</b> for its server descriptor. */ +static void +launch_direct_bridge_descriptor_fetch(bridge_info_t *bridge) +{ + const or_options_t *options = get_options(); + + if (connection_get_by_type_addr_port_purpose( + CONN_TYPE_DIR, &bridge->addr, bridge->port, + DIR_PURPOSE_FETCH_SERVERDESC)) + return; /* it's already on the way */ + + if (routerset_contains_bridge(options->ExcludeNodes, bridge)) { + download_status_mark_impossible(&bridge->fetch_status); + log_warn(LD_APP, "Not using bridge at %s: it is in ExcludeNodes.", + safe_str_client(fmt_and_decorate_addr(&bridge->addr))); + return; + } + + /* Until we get a descriptor for the bridge, we only know one address for + * it. */ + if (!fascist_firewall_allows_address_addr(&bridge->addr, bridge->port, + FIREWALL_OR_CONNECTION, 0, 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, + DIRIND_ONEHOP, "authority.z", NULL, 0, 0); +} + +/** Fetching the bridge descriptor from the bridge authority returned a + * "not found". Fall back to trying a direct fetch. */ +void +retry_bridge_descriptor_fetch_directly(const char *digest) +{ + bridge_info_t *bridge = find_bridge_by_digest(digest); + if (!bridge) + return; /* not found? oh well. */ + + launch_direct_bridge_descriptor_fetch(bridge); +} + +/** For each bridge in our list for which we don't currently have a + * descriptor, fetch a new copy of its descriptor -- either directly + * from the bridge or via a bridge authority. */ +void +fetch_bridge_descriptors(const or_options_t *options, time_t now) +{ + int num_bridge_auths = get_n_authorities(BRIDGE_DIRINFO); + int ask_bridge_directly; + int can_use_bridge_authority; + + if (!bridge_list) + return; + + /* If we still have unconfigured managed proxies, don't go and + connect to a bridge. */ + if (pt_proxies_configuration_pending()) + return; + + SMARTLIST_FOREACH_BEGIN(bridge_list, bridge_info_t *, bridge) + { + if (!download_status_is_ready(&bridge->fetch_status, now, + IMPOSSIBLE_TO_DOWNLOAD)) + continue; /* don't bother, no need to retry yet */ + if (routerset_contains_bridge(options->ExcludeNodes, bridge)) { + download_status_mark_impossible(&bridge->fetch_status); + log_warn(LD_APP, "Not using bridge at %s: it is in ExcludeNodes.", + safe_str_client(fmt_and_decorate_addr(&bridge->addr))); + continue; + } + + /* schedule another fetch as if this one will fail, in case it does */ + download_status_failed(&bridge->fetch_status, 0); + + can_use_bridge_authority = !tor_digest_is_zero(bridge->identity) && + num_bridge_auths; + ask_bridge_directly = !can_use_bridge_authority || + !options->UpdateBridgesFromAuthority; + log_debug(LD_DIR, "ask_bridge_directly=%d (%d, %d, %d)", + ask_bridge_directly, tor_digest_is_zero(bridge->identity), + !options->UpdateBridgesFromAuthority, !num_bridge_auths); + + if (ask_bridge_directly && + !fascist_firewall_allows_address_addr(&bridge->addr, bridge->port, + FIREWALL_OR_CONNECTION, 0, + 0)) { + log_notice(LD_DIR, "Bridge at '%s' isn't reachable by our " + "firewall policy. %s.", + fmt_addrport(&bridge->addr, bridge->port), + can_use_bridge_authority ? + "Asking bridge authority instead" : "Skipping"); + if (can_use_bridge_authority) + ask_bridge_directly = 0; + else + continue; + } + + if (ask_bridge_directly) { + /* we need to ask the bridge itself for its descriptor. */ + launch_direct_bridge_descriptor_fetch(bridge); + } else { + /* We have a digest and we want to ask an authority. We could + * combine all the requests into one, but that may give more + * hints to the bridge authority than we want to give. */ + char resource[10 + HEX_DIGEST_LEN]; + memcpy(resource, "fp/", 3); + base16_encode(resource+3, HEX_DIGEST_LEN+1, + bridge->identity, DIGEST_LEN); + memcpy(resource+3+HEX_DIGEST_LEN, ".z", 3); + log_info(LD_DIR, "Fetching bridge info '%s' from bridge authority.", + resource); + directory_get_from_dirserver(DIR_PURPOSE_FETCH_SERVERDESC, + ROUTER_PURPOSE_BRIDGE, resource, 0, DL_WANT_AUTHORITY); + } + } + SMARTLIST_FOREACH_END(bridge); +} + +/** If our <b>bridge</b> is configured to be a different address than + * the bridge gives in <b>node</b>, rewrite the routerinfo + * we received to use the address we meant to use. Now we handle + * multihomed bridges better. + */ +static void +rewrite_node_address_for_bridge(const bridge_info_t *bridge, node_t *node) +{ + /* XXXX move this function. */ + /* XXXX overridden addresses should really live in the node_t, so that the + * routerinfo_t and the microdesc_t can be immutable. But we can only + * do that safely if we know that no function that connects to an OR + * does so through an address from any source other than node_get_addr(). + */ + tor_addr_t addr; + const or_options_t *options = get_options(); + + if (node->ri) { + routerinfo_t *ri = node->ri; + tor_addr_from_ipv4h(&addr, ri->addr); + + if ((!tor_addr_compare(&bridge->addr, &addr, CMP_EXACT) && + bridge->port == ri->or_port) || + (!tor_addr_compare(&bridge->addr, &ri->ipv6_addr, CMP_EXACT) && + bridge->port == ri->ipv6_orport)) { + /* they match, so no need to do anything */ + } else { + if (tor_addr_family(&bridge->addr) == AF_INET) { + ri->addr = tor_addr_to_ipv4h(&bridge->addr); + ri->or_port = bridge->port; + log_info(LD_DIR, + "Adjusted bridge routerinfo for '%s' to match configured " + "address %s:%d.", + ri->nickname, fmt_addr32(ri->addr), ri->or_port); + } else if (tor_addr_family(&bridge->addr) == AF_INET6) { + tor_addr_copy(&ri->ipv6_addr, &bridge->addr); + ri->ipv6_orport = bridge->port; + log_info(LD_DIR, + "Adjusted bridge routerinfo for '%s' to match configured " + "address %s.", + ri->nickname, fmt_addrport(&ri->ipv6_addr, ri->ipv6_orport)); + } else { + log_err(LD_BUG, "Address family not supported: %d.", + tor_addr_family(&bridge->addr)); + return; + } + } + + if (options->ClientPreferIPv6ORPort == -1) { + /* Mark which address to use based on which bridge_t we got. */ + node->ipv6_preferred = (tor_addr_family(&bridge->addr) == AF_INET6 && + !tor_addr_is_null(&node->ri->ipv6_addr)); + } else { + /* Mark which address to use based on user preference */ + node->ipv6_preferred = (fascist_firewall_prefer_ipv6_orport(options) && + !tor_addr_is_null(&node->ri->ipv6_addr)); + } + + /* XXXipv6 we lack support for falling back to another address for + the same relay, warn the user */ + if (!tor_addr_is_null(&ri->ipv6_addr)) { + tor_addr_port_t ap; + node_get_pref_orport(node, &ap); + log_notice(LD_CONFIG, + "Bridge '%s' has both an IPv4 and an IPv6 address. " + "Will prefer using its %s address (%s) based on %s.", + ri->nickname, + node->ipv6_preferred ? "IPv6" : "IPv4", + fmt_addrport(&ap.addr, ap.port), + options->ClientPreferIPv6ORPort == -1 ? + "the configured Bridge address" : + "ClientPreferIPv6ORPort"); + } + } + if (node->rs) { + routerstatus_t *rs = node->rs; + tor_addr_from_ipv4h(&addr, rs->addr); + + if (!tor_addr_compare(&bridge->addr, &addr, CMP_EXACT) && + bridge->port == rs->or_port) { + /* they match, so no need to do anything */ + } else { + rs->addr = tor_addr_to_ipv4h(&bridge->addr); + rs->or_port = bridge->port; + log_info(LD_DIR, + "Adjusted bridge routerstatus for '%s' to match " + "configured address %s.", + rs->nickname, fmt_addrport(&bridge->addr, rs->or_port)); + } + } +} + +/** We just learned a descriptor for a bridge. See if that + * digest is in our entry guard list, and add it if not. */ +void +learned_bridge_descriptor(routerinfo_t *ri, int from_cache) +{ + tor_assert(ri); + tor_assert(ri->purpose == ROUTER_PURPOSE_BRIDGE); + if (get_options()->UseBridges) { + int first = num_bridges_usable() <= 1; + bridge_info_t *bridge = get_configured_bridge_by_routerinfo(ri); + time_t now = time(NULL); + router_set_status(ri->cache_info.identity_digest, 1); + + if (bridge) { /* if we actually want to use this one */ + node_t *node; + /* it's here; schedule its re-fetch for a long time from now. */ + if (!from_cache) + download_status_reset(&bridge->fetch_status); + + node = node_get_mutable_by_id(ri->cache_info.identity_digest); + tor_assert(node); + rewrite_node_address_for_bridge(bridge, node); + if (tor_digest_is_zero(bridge->identity)) { + memcpy(bridge->identity,ri->cache_info.identity_digest, DIGEST_LEN); + log_notice(LD_DIR, "Learned identity %s for bridge at %s:%d", + hex_str(bridge->identity, DIGEST_LEN), + fmt_and_decorate_addr(&bridge->addr), + (int) bridge->port); + } + if (get_options()->UseDeprecatedGuardAlgorithm) { +#ifdef ENABLE_LEGACY_GUARD_ALGORITHM + add_bridge_as_entry_guard(get_guard_selection_info(), node); +#else + tor_assert_nonfatal_unreached(); +#endif + } else { + entry_guard_learned_bridge_identity(&bridge->addrport_configured, + (const uint8_t*)ri->cache_info.identity_digest); + } + + log_notice(LD_DIR, "new bridge descriptor '%s' (%s): %s", ri->nickname, + from_cache ? "cached" : "fresh", router_describe(ri)); + /* set entry->made_contact so if it goes down we don't drop it from + * our entry node list */ + if (get_options()->UseDeprecatedGuardAlgorithm) { +#ifdef ENABLE_LEGACY_GUARD_ALGORITHM + entry_guard_register_connect_status(ri->cache_info.identity_digest, + 1, 0, now); +#else + tor_assert_nonfatal_unreached(); +#endif + } + if (first) { + routerlist_retry_directory_downloads(now); + } + } + } +} + +/** Return the number of bridges that have descriptors that + * are marked with purpose 'bridge' and are running. + * + * We use this function to decide if we're ready to start building + * circuits through our bridges, or if we need to wait until the + * directory "server/authority" requests finish. */ +int +any_bridge_descriptors_known(void) +{ + tor_assert(get_options()->UseBridges); + + if (!bridge_list) + return 0; + + SMARTLIST_FOREACH_BEGIN(bridge_list, bridge_info_t *, bridge) { + const node_t *node; + if (!tor_digest_is_zero(bridge->identity) && + (node = node_get_by_id(bridge->identity)) != NULL && + node->ri) { + return 1; + } + } SMARTLIST_FOREACH_END(bridge); + + return 0; +} + +/** Return a smartlist containing all bridge identity digests */ +MOCK_IMPL(smartlist_t *, +list_bridge_identities, (void)) +{ + smartlist_t *result = NULL; + char *digest_tmp; + + if (get_options()->UseBridges && bridge_list) { + result = smartlist_new(); + + SMARTLIST_FOREACH_BEGIN(bridge_list, bridge_info_t *, b) { + digest_tmp = tor_malloc(DIGEST_LEN); + memcpy(digest_tmp, b->identity, DIGEST_LEN); + smartlist_add(result, digest_tmp); + } SMARTLIST_FOREACH_END(b); + } + + return result; +} + +/** Get the download status for a bridge descriptor given its identity */ +MOCK_IMPL(download_status_t *, +get_bridge_dl_status_by_id, (const char *digest)) +{ + download_status_t *dl = NULL; + + if (digest && get_options()->UseBridges && bridge_list) { + SMARTLIST_FOREACH_BEGIN(bridge_list, bridge_info_t *, b) { + if (tor_memeq(digest, b->identity, DIGEST_LEN)) { + dl = &(b->fetch_status); + break; + } + } SMARTLIST_FOREACH_END(b); + } + + return dl; +} + +/** Release all storage held in bridges.c */ +void +bridges_free_all(void) +{ + clear_bridge_list(); + smartlist_free(bridge_list); + bridge_list = NULL; +} + diff --git a/src/or/bridges.h b/src/or/bridges.h new file mode 100644 index 0000000000..de23fe6eeb --- /dev/null +++ b/src/or/bridges.h @@ -0,0 +1,66 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file bridges.h + * \brief Header file for circuitbuild.c. + **/ + +#ifndef TOR_BRIDGES_H +#define TOR_BRIDGES_H + +struct bridge_line_t; + +/* Opaque handle to a configured bridge */ +typedef struct bridge_info_t bridge_info_t; + +void mark_bridge_list(void); +void sweep_bridge_list(void); +const smartlist_t *bridge_list_get(void); +bridge_info_t *find_bridge_by_digest(const char *digest); +const uint8_t *bridge_get_rsa_id_digest(const bridge_info_t *bridge); +const tor_addr_port_t * bridge_get_addr_port(const bridge_info_t *bridge); +bridge_info_t *get_configured_bridge_by_addr_port_digest( + const tor_addr_t *addr, + uint16_t port, + const char *digest); + +int addr_is_a_configured_bridge(const tor_addr_t *addr, uint16_t port, + const char *digest); +int extend_info_is_a_configured_bridge(const extend_info_t *ei); +int routerinfo_is_a_configured_bridge(const routerinfo_t *ri); +int node_is_a_configured_bridge(const node_t *node); +void learned_router_identity(const tor_addr_t *addr, uint16_t port, + const char *digest, + const ed25519_public_key_t *ed_id); + +void bridge_add_from_config(struct bridge_line_t *bridge_line); +void retry_bridge_descriptor_fetch_directly(const char *digest); +void fetch_bridge_descriptors(const or_options_t *options, time_t now); +void learned_bridge_descriptor(routerinfo_t *ri, int from_cache); +int any_bridge_descriptors_known(void); +const smartlist_t *get_socks_args_by_bridge_addrport(const tor_addr_t *addr, + uint16_t port); + +int any_bridges_dont_support_microdescriptors(void); + +const char *find_transport_name_by_bridge_addrport(const tor_addr_t *addr, + uint16_t port); +struct transport_t; +int get_transport_by_bridge_addrport(const tor_addr_t *addr, uint16_t port, + const struct transport_t **transport); + +MOCK_DECL(int, transport_is_needed, (const char *transport_name)); +int validate_pluggable_transports_config(void); + +MOCK_DECL(smartlist_t *, list_bridge_identities, (void)); +MOCK_DECL(download_status_t *, get_bridge_dl_status_by_id, + (const char *digest)); + +void bridges_free_all(void); + +#endif + diff --git a/src/or/channel.c b/src/or/channel.c index 7984558b40..dbf442eca0 100644 --- a/src/or/channel.c +++ b/src/or/channel.c @@ -2318,121 +2318,120 @@ channel_flush_some_cells_from_outgoing_queue(channel_t *chan, free_q = 0; handed_off = 0; - if (1) { - /* Figure out how big it is for statistical purposes */ - cell_size = channel_get_cell_queue_entry_size(chan, q); - /* - * Okay, we have a good queue entry, try to give it to the lower - * layer. - */ - switch (q->type) { - case CELL_QUEUE_FIXED: - if (q->u.fixed.cell) { - if (chan->write_cell(chan, - q->u.fixed.cell)) { - ++flushed; - channel_timestamp_xmit(chan); - ++(chan->n_cells_xmitted); - chan->n_bytes_xmitted += cell_size; - free_q = 1; - handed_off = 1; - } - /* Else couldn't write it; leave it on the queue */ - } else { - /* This shouldn't happen */ - log_info(LD_CHANNEL, - "Saw broken cell queue entry of type CELL_QUEUE_FIXED " - "with no cell on channel %p " - "(global ID " U64_FORMAT ").", - chan, U64_PRINTF_ARG(chan->global_identifier)); - /* Throw it away */ - free_q = 1; - handed_off = 0; - } - break; - case CELL_QUEUE_PACKED: - if (q->u.packed.packed_cell) { - if (chan->write_packed_cell(chan, - q->u.packed.packed_cell)) { - ++flushed; - channel_timestamp_xmit(chan); - ++(chan->n_cells_xmitted); - chan->n_bytes_xmitted += cell_size; - free_q = 1; - handed_off = 1; - } - /* Else couldn't write it; leave it on the queue */ - } else { - /* This shouldn't happen */ - log_info(LD_CHANNEL, - "Saw broken cell queue entry of type CELL_QUEUE_PACKED " - "with no cell on channel %p " - "(global ID " U64_FORMAT ").", - chan, U64_PRINTF_ARG(chan->global_identifier)); - /* Throw it away */ - free_q = 1; - handed_off = 0; - } - break; - case CELL_QUEUE_VAR: - if (q->u.var.var_cell) { - if (chan->write_var_cell(chan, - q->u.var.var_cell)) { - ++flushed; - channel_timestamp_xmit(chan); - ++(chan->n_cells_xmitted); - chan->n_bytes_xmitted += cell_size; - free_q = 1; - handed_off = 1; - } - /* Else couldn't write it; leave it on the queue */ - } else { - /* This shouldn't happen */ - log_info(LD_CHANNEL, - "Saw broken cell queue entry of type CELL_QUEUE_VAR " - "with no cell on channel %p " - "(global ID " U64_FORMAT ").", - chan, U64_PRINTF_ARG(chan->global_identifier)); - /* Throw it away */ - free_q = 1; - handed_off = 0; - } - break; - default: - /* Unknown type, log and free it */ - log_info(LD_CHANNEL, - "Saw an unknown cell queue entry type %d on channel %p " - "(global ID " U64_FORMAT "; ignoring it." - " Someone should fix this.", - q->type, chan, U64_PRINTF_ARG(chan->global_identifier)); + /* Figure out how big it is for statistical purposes */ + cell_size = channel_get_cell_queue_entry_size(chan, q); + /* + * Okay, we have a good queue entry, try to give it to the lower + * layer. + */ + switch (q->type) { + case CELL_QUEUE_FIXED: + if (q->u.fixed.cell) { + if (chan->write_cell(chan, + q->u.fixed.cell)) { + ++flushed; + channel_timestamp_xmit(chan); + ++(chan->n_cells_xmitted); + chan->n_bytes_xmitted += cell_size; + free_q = 1; + handed_off = 1; + } + /* Else couldn't write it; leave it on the queue */ + } else { + /* This shouldn't happen */ + log_info(LD_CHANNEL, + "Saw broken cell queue entry of type CELL_QUEUE_FIXED " + "with no cell on channel %p " + "(global ID " U64_FORMAT ").", + chan, U64_PRINTF_ARG(chan->global_identifier)); + /* Throw it away */ + free_q = 1; + handed_off = 0; + } + break; + case CELL_QUEUE_PACKED: + if (q->u.packed.packed_cell) { + if (chan->write_packed_cell(chan, + q->u.packed.packed_cell)) { + ++flushed; + channel_timestamp_xmit(chan); + ++(chan->n_cells_xmitted); + chan->n_bytes_xmitted += cell_size; free_q = 1; - handed_off = 0; + handed_off = 1; + } + /* Else couldn't write it; leave it on the queue */ + } else { + /* This shouldn't happen */ + log_info(LD_CHANNEL, + "Saw broken cell queue entry of type CELL_QUEUE_PACKED " + "with no cell on channel %p " + "(global ID " U64_FORMAT ").", + chan, U64_PRINTF_ARG(chan->global_identifier)); + /* Throw it away */ + free_q = 1; + handed_off = 0; } + break; + case CELL_QUEUE_VAR: + if (q->u.var.var_cell) { + if (chan->write_var_cell(chan, + q->u.var.var_cell)) { + ++flushed; + channel_timestamp_xmit(chan); + ++(chan->n_cells_xmitted); + chan->n_bytes_xmitted += cell_size; + free_q = 1; + handed_off = 1; + } + /* Else couldn't write it; leave it on the queue */ + } else { + /* This shouldn't happen */ + log_info(LD_CHANNEL, + "Saw broken cell queue entry of type CELL_QUEUE_VAR " + "with no cell on channel %p " + "(global ID " U64_FORMAT ").", + chan, U64_PRINTF_ARG(chan->global_identifier)); + /* Throw it away */ + free_q = 1; + handed_off = 0; + } + break; + default: + /* Unknown type, log and free it */ + log_info(LD_CHANNEL, + "Saw an unknown cell queue entry type %d on channel %p " + "(global ID " U64_FORMAT "; ignoring it." + " Someone should fix this.", + q->type, chan, U64_PRINTF_ARG(chan->global_identifier)); + free_q = 1; + handed_off = 0; + } + /* + * if free_q is set, we used it and should remove the queue entry; + * we have to do the free down here so TOR_SIMPLEQ_REMOVE_HEAD isn't + * accessing freed memory + */ + if (free_q) { + TOR_SIMPLEQ_REMOVE_HEAD(&chan->outgoing_queue, next); /* - * if free_q is set, we used it and should remove the queue entry; - * we have to do the free down here so TOR_SIMPLEQ_REMOVE_HEAD isn't - * accessing freed memory + * ...and we handed a cell off to the lower layer, so we should + * update the counters. */ - if (free_q) { - TOR_SIMPLEQ_REMOVE_HEAD(&chan->outgoing_queue, next); - /* - * ...and we handed a cell off to the lower layer, so we should - * update the counters. - */ - ++n_channel_cells_passed_to_lower_layer; - --n_channel_cells_in_queues; - n_channel_bytes_passed_to_lower_layer += cell_size; - n_channel_bytes_in_queues -= cell_size; - channel_assert_counter_consistency(); - /* Update the channel's queue size too */ - chan->bytes_in_queue -= cell_size; - /* Finally, free q */ - cell_queue_entry_free(q, handed_off); - q = NULL; - } + ++n_channel_cells_passed_to_lower_layer; + --n_channel_cells_in_queues; + n_channel_bytes_passed_to_lower_layer += cell_size; + n_channel_bytes_in_queues -= cell_size; + channel_assert_counter_consistency(); + /* Update the channel's queue size too */ + chan->bytes_in_queue -= cell_size; + /* Finally, free q */ + cell_queue_entry_free(q, handed_off); + q = NULL; + } else { /* No cell removed from list, so we can't go on any further */ - else break; + break; } } } @@ -2578,6 +2577,8 @@ channel_do_open_actions(channel_t *chan) if (started_here) { circuit_build_times_network_is_live(get_circuit_build_times_mutable()); rep_hist_note_connect_succeeded(chan->identity_digest, now); +#ifdef ENABLE_LEGACY_GUARD_ALGORITHM + // XXXX prop271 this call is no longer useful with the new algorithm. if (entry_guard_register_connect_status( chan->identity_digest, 1, 0, now) < 0) { /* Close any circuits pending on this channel. We leave it in state @@ -2588,6 +2589,7 @@ channel_do_open_actions(channel_t *chan) "connection so we can retry the earlier entry guards."); close_origin_circuits = 1; } +#endif router_set_status(chan->identity_digest, 1); } else { /* only report it to the geoip module if it's not a known router */ diff --git a/src/or/channeltls.c b/src/or/channeltls.c index aef0143c9d..dbed95fb43 100644 --- a/src/or/channeltls.c +++ b/src/or/channeltls.c @@ -49,6 +49,7 @@ #include "connection.h" #include "connection_or.h" #include "control.h" +#include "entrynodes.h" #include "link_handshake.h" #include "relay.h" #include "rephist.h" @@ -1094,6 +1095,10 @@ channel_tls_handle_cell(cell_t *cell, or_connection_t *conn) if (conn->base_.state == OR_CONN_STATE_OR_HANDSHAKING_V3) or_handshake_state_record_cell(conn, conn->handshake_state, cell, 1); + /* We note that we're on the internet whenever we read a cell. This is + * a fast operation. */ + entry_guards_note_internet_connectivity(get_guard_selection_info()); + switch (cell->command) { case CELL_PADDING: ++stats_n_padding_cells_processed; @@ -1272,6 +1277,10 @@ channel_tls_handle_var_cell(var_cell_t *var_cell, or_connection_t *conn) return; } + /* We note that we're on the internet whenever we read a cell. This is + * a fast operation. */ + entry_guards_note_internet_connectivity(get_guard_selection_info()); + /* Now handle the cell */ switch (var_cell->command) { diff --git a/src/or/circpathbias.c b/src/or/circpathbias.c index 6ee69aac1e..d86d70f1ff 100644 --- a/src/or/circpathbias.c +++ b/src/or/circpathbias.c @@ -51,19 +51,21 @@ static int entry_guard_inc_circ_attempt_count(entry_guard_t *guard); static int entry_guard_inc_circ_attempt_count(entry_guard_t *guard) { + guard_pathbias_t *pb = entry_guard_get_pathbias_state(guard); + entry_guards_changed(); pathbias_measure_close_rate(guard); - if (guard->path_bias_disabled) + if (pb->path_bias_disabled) return -1; pathbias_scale_close_rates(guard); - guard->circ_attempts++; + pb->circ_attempts++; - log_info(LD_CIRC, "Got success count %f/%f for guard %s ($%s)", - guard->circ_successes, guard->circ_attempts, guard->nickname, - hex_str(guard->identity, DIGEST_LEN)); + log_info(LD_CIRC, "Got success count %f/%f for guard %s", + pb->circ_successes, pb->circ_attempts, + entry_guard_describe(guard)); return 0; } @@ -513,14 +515,16 @@ pathbias_count_build_success(origin_circuit_t *circ) } if (guard) { + guard_pathbias_t *pb = entry_guard_get_pathbias_state(guard); + if (circ->path_state == PATH_STATE_BUILD_ATTEMPTED) { circ->path_state = PATH_STATE_BUILD_SUCCEEDED; - guard->circ_successes++; + pb->circ_successes++; entry_guards_changed(); - log_info(LD_CIRC, "Got success count %f/%f for guard %s ($%s)", - guard->circ_successes, guard->circ_attempts, - guard->nickname, hex_str(guard->identity, DIGEST_LEN)); + log_info(LD_CIRC, "Got success count %f/%f for guard %s", + pb->circ_successes, pb->circ_attempts, + entry_guard_describe(guard)); } else { if ((rate_msg = rate_limit_log(&success_notice_limit, approx_time()))) { @@ -535,11 +539,11 @@ pathbias_count_build_success(origin_circuit_t *circ) } } - if (guard->circ_attempts < guard->circ_successes) { + if (pb->circ_attempts < pb->circ_successes) { log_notice(LD_BUG, "Unexpectedly high successes counts (%f/%f) " - "for guard %s ($%s)", - guard->circ_successes, guard->circ_attempts, - guard->nickname, hex_str(guard->identity, DIGEST_LEN)); + "for guard %s", + pb->circ_successes, pb->circ_attempts, + entry_guard_describe(guard)); } /* In rare cases, CIRCUIT_PURPOSE_TESTING can get converted to * CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT and have no guards here. @@ -582,8 +586,6 @@ pathbias_count_build_success(origin_circuit_t *circ) void pathbias_count_use_attempt(origin_circuit_t *circ) { - entry_guard_t *guard; - if (!pathbias_should_count(circ)) { return; } @@ -596,19 +598,21 @@ pathbias_count_use_attempt(origin_circuit_t *circ) circuit_purpose_to_string(circ->base_.purpose), circuit_state_to_string(circ->base_.state)); } else if (circ->path_state < PATH_STATE_USE_ATTEMPTED) { - guard = entry_guard_get_by_id_digest( + entry_guard_t *guard = entry_guard_get_by_id_digest( circ->cpath->extend_info->identity_digest); if (guard) { + guard_pathbias_t *pb = entry_guard_get_pathbias_state(guard); + pathbias_measure_use_rate(guard); pathbias_scale_use_rates(guard); - guard->use_attempts++; + pb->use_attempts++; entry_guards_changed(); log_debug(LD_CIRC, - "Marked circuit %d (%f/%f) as used for guard %s ($%s).", + "Marked circuit %d (%f/%f) as used for guard %s.", circ->global_identifier, - guard->use_successes, guard->use_attempts, - guard->nickname, hex_str(guard->identity, DIGEST_LEN)); + pb->use_successes, pb->use_attempts, + entry_guard_describe(guard)); } circ->path_state = PATH_STATE_USE_ATTEMPTED; @@ -710,22 +714,23 @@ pathbias_count_use_success(origin_circuit_t *circ) guard = entry_guard_get_by_id_digest( circ->cpath->extend_info->identity_digest); if (guard) { - guard->use_successes++; + guard_pathbias_t *pb = entry_guard_get_pathbias_state(guard); + + pb->use_successes++; entry_guards_changed(); - if (guard->use_attempts < guard->use_successes) { + if (pb->use_attempts < pb->use_successes) { log_notice(LD_BUG, "Unexpectedly high use successes counts (%f/%f) " - "for guard %s=%s", - guard->use_successes, guard->use_attempts, - guard->nickname, hex_str(guard->identity, DIGEST_LEN)); + "for guard %s", + pb->use_successes, pb->use_attempts, + entry_guard_describe(guard)); } log_debug(LD_CIRC, - "Marked circuit %d (%f/%f) as used successfully for guard " - "%s ($%s).", - circ->global_identifier, guard->use_successes, - guard->use_attempts, guard->nickname, - hex_str(guard->identity, DIGEST_LEN)); + "Marked circuit %d (%f/%f) as used successfully for guard %s", + circ->global_identifier, pb->use_successes, + pb->use_attempts, + entry_guard_describe(guard)); } } @@ -1026,9 +1031,11 @@ pathbias_count_successful_close(origin_circuit_t *circ) } if (guard) { + guard_pathbias_t *pb = entry_guard_get_pathbias_state(guard); + /* In the long run: circuit_success ~= successful_circuit_close + * circ_failure + stream_failure */ - guard->successful_circuits_closed++; + pb->successful_circuits_closed++; entry_guards_changed(); } else if (circ->base_.purpose != CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT) { /* In rare cases, CIRCUIT_PURPOSE_TESTING can get converted to @@ -1065,7 +1072,9 @@ pathbias_count_collapse(origin_circuit_t *circ) } if (guard) { - guard->collapsed_circuits++; + guard_pathbias_t *pb = entry_guard_get_pathbias_state(guard); + + pb->collapsed_circuits++; entry_guards_changed(); } else if (circ->base_.purpose != CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT) { /* In rare cases, CIRCUIT_PURPOSE_TESTING can get converted to @@ -1098,7 +1107,9 @@ pathbias_count_use_failed(origin_circuit_t *circ) } if (guard) { - guard->unusable_circuits++; + guard_pathbias_t *pb = entry_guard_get_pathbias_state(guard); + + pb->unusable_circuits++; entry_guards_changed(); } else if (circ->base_.purpose != CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT) { /* In rare cases, CIRCUIT_PURPOSE_TESTING can get converted to @@ -1141,7 +1152,9 @@ pathbias_count_timeout(origin_circuit_t *circ) } if (guard) { - guard->timeouts++; + guard_pathbias_t *pb = entry_guard_get_pathbias_state(guard); + + pb->timeouts++; entry_guards_changed(); } } @@ -1173,7 +1186,7 @@ pathbias_count_circs_in_states(entry_guard_t *guard, if (ocirc->path_state >= from && ocirc->path_state <= to && pathbias_should_count(ocirc) && - fast_memeq(guard->identity, + fast_memeq(entry_guard_get_rsa_id_digest(guard), ocirc->cpath->extend_info->identity_digest, DIGEST_LEN)) { log_debug(LD_CIRC, "Found opened circuit %d in path_state %s", @@ -1197,7 +1210,9 @@ pathbias_count_circs_in_states(entry_guard_t *guard, double pathbias_get_close_success_count(entry_guard_t *guard) { - return guard->successful_circuits_closed + + guard_pathbias_t *pb = entry_guard_get_pathbias_state(guard); + + return pb->successful_circuits_closed + pathbias_count_circs_in_states(guard, PATH_STATE_BUILD_SUCCEEDED, PATH_STATE_USE_SUCCEEDED); @@ -1213,7 +1228,9 @@ pathbias_get_close_success_count(entry_guard_t *guard) double pathbias_get_use_success_count(entry_guard_t *guard) { - return guard->use_successes + + guard_pathbias_t *pb = entry_guard_get_pathbias_state(guard); + + return pb->use_successes + pathbias_count_circs_in_states(guard, PATH_STATE_USE_ATTEMPTED, PATH_STATE_USE_SUCCEEDED); @@ -1231,18 +1248,19 @@ static void pathbias_measure_use_rate(entry_guard_t *guard) { const or_options_t *options = get_options(); + guard_pathbias_t *pb = entry_guard_get_pathbias_state(guard); - if (guard->use_attempts > pathbias_get_min_use(options)) { + if (pb->use_attempts > pathbias_get_min_use(options)) { /* Note: We rely on the < comparison here to allow us to set a 0 * rate and disable the feature entirely. If refactoring, don't * change to <= */ - if (pathbias_get_use_success_count(guard)/guard->use_attempts + if (pathbias_get_use_success_count(guard)/pb->use_attempts < pathbias_get_extreme_use_rate(options)) { /* Dropping is currently disabled by default. */ if (pathbias_get_dropguards(options)) { - if (!guard->path_bias_disabled) { + if (!pb->path_bias_disabled) { log_warn(LD_CIRC, - "Your Guard %s ($%s) is failing to carry an extremely large " + "Your Guard %s is failing to carry an extremely large " "amount of stream on its circuits. " "To avoid potential route manipulation attacks, Tor has " "disabled use of this guard. " @@ -1250,25 +1268,27 @@ pathbias_measure_use_rate(entry_guard_t *guard) "%ld circuits completed, %ld were unusable, %ld collapsed, " "and %ld timed out. " "For reference, your timeout cutoff is %ld seconds.", - guard->nickname, hex_str(guard->identity, DIGEST_LEN), + entry_guard_describe(guard), tor_lround(pathbias_get_use_success_count(guard)), - tor_lround(guard->use_attempts), + tor_lround(pb->use_attempts), tor_lround(pathbias_get_close_success_count(guard)), - tor_lround(guard->circ_attempts), - tor_lround(guard->circ_successes), - tor_lround(guard->unusable_circuits), - tor_lround(guard->collapsed_circuits), - tor_lround(guard->timeouts), + tor_lround(pb->circ_attempts), + tor_lround(pb->circ_successes), + tor_lround(pb->unusable_circuits), + tor_lround(pb->collapsed_circuits), + tor_lround(pb->timeouts), tor_lround(get_circuit_build_close_time_ms()/1000)); - guard->path_bias_disabled = 1; - guard->bad_since = approx_time(); - entry_guards_changed(); + pb->path_bias_disabled = 1; +#ifdef ENABLE_LEGACY_GUARD_ALGORITHM + // XXXX + entry_guard_mark_bad(guard); +#endif return; } - } else if (!guard->path_bias_use_extreme) { - guard->path_bias_use_extreme = 1; + } else if (!pb->path_bias_use_extreme) { + pb->path_bias_use_extreme = 1; log_warn(LD_CIRC, - "Your Guard %s ($%s) is failing to carry an extremely large " + "Your Guard %s is failing to carry an extremely large " "amount of streams on its circuits. " "This could indicate a route manipulation attack, network " "overload, bad local network connectivity, or a bug. " @@ -1276,23 +1296,23 @@ pathbias_measure_use_rate(entry_guard_t *guard) "%ld circuits completed, %ld were unusable, %ld collapsed, " "and %ld timed out. " "For reference, your timeout cutoff is %ld seconds.", - guard->nickname, hex_str(guard->identity, DIGEST_LEN), + entry_guard_describe(guard), tor_lround(pathbias_get_use_success_count(guard)), - tor_lround(guard->use_attempts), + tor_lround(pb->use_attempts), tor_lround(pathbias_get_close_success_count(guard)), - tor_lround(guard->circ_attempts), - tor_lround(guard->circ_successes), - tor_lround(guard->unusable_circuits), - tor_lround(guard->collapsed_circuits), - tor_lround(guard->timeouts), + tor_lround(pb->circ_attempts), + tor_lround(pb->circ_successes), + tor_lround(pb->unusable_circuits), + tor_lround(pb->collapsed_circuits), + tor_lround(pb->timeouts), tor_lround(get_circuit_build_close_time_ms()/1000)); } - } else if (pathbias_get_use_success_count(guard)/guard->use_attempts + } else if (pathbias_get_use_success_count(guard)/pb->use_attempts < pathbias_get_notice_use_rate(options)) { - if (!guard->path_bias_use_noticed) { - guard->path_bias_use_noticed = 1; + if (!pb->path_bias_use_noticed) { + pb->path_bias_use_noticed = 1; log_notice(LD_CIRC, - "Your Guard %s ($%s) is failing to carry more streams on its " + "Your Guard %s is failing to carry more streams on its " "circuits than usual. " "Most likely this means the Tor network is overloaded " "or your network connection is poor. " @@ -1300,15 +1320,15 @@ pathbias_measure_use_rate(entry_guard_t *guard) "%ld circuits completed, %ld were unusable, %ld collapsed, " "and %ld timed out. " "For reference, your timeout cutoff is %ld seconds.", - guard->nickname, hex_str(guard->identity, DIGEST_LEN), + entry_guard_describe(guard), tor_lround(pathbias_get_use_success_count(guard)), - tor_lround(guard->use_attempts), + tor_lround(pb->use_attempts), tor_lround(pathbias_get_close_success_count(guard)), - tor_lround(guard->circ_attempts), - tor_lround(guard->circ_successes), - tor_lround(guard->unusable_circuits), - tor_lround(guard->collapsed_circuits), - tor_lround(guard->timeouts), + tor_lround(pb->circ_attempts), + tor_lround(pb->circ_successes), + tor_lround(pb->unusable_circuits), + tor_lround(pb->collapsed_circuits), + tor_lround(pb->timeouts), tor_lround(get_circuit_build_close_time_ms()/1000)); } } @@ -1337,18 +1357,19 @@ static void pathbias_measure_close_rate(entry_guard_t *guard) { const or_options_t *options = get_options(); + guard_pathbias_t *pb = entry_guard_get_pathbias_state(guard); - if (guard->circ_attempts > pathbias_get_min_circs(options)) { + if (pb->circ_attempts > pathbias_get_min_circs(options)) { /* Note: We rely on the < comparison here to allow us to set a 0 * rate and disable the feature entirely. If refactoring, don't * change to <= */ - if (pathbias_get_close_success_count(guard)/guard->circ_attempts + if (pathbias_get_close_success_count(guard)/pb->circ_attempts < pathbias_get_extreme_rate(options)) { /* Dropping is currently disabled by default. */ if (pathbias_get_dropguards(options)) { - if (!guard->path_bias_disabled) { + if (!pb->path_bias_disabled) { log_warn(LD_CIRC, - "Your Guard %s ($%s) is failing an extremely large " + "Your Guard %s is failing an extremely large " "amount of circuits. " "To avoid potential route manipulation attacks, Tor has " "disabled use of this guard. " @@ -1356,25 +1377,27 @@ pathbias_measure_close_rate(entry_guard_t *guard) "%ld circuits completed, %ld were unusable, %ld collapsed, " "and %ld timed out. " "For reference, your timeout cutoff is %ld seconds.", - guard->nickname, hex_str(guard->identity, DIGEST_LEN), + entry_guard_describe(guard), tor_lround(pathbias_get_close_success_count(guard)), - tor_lround(guard->circ_attempts), + tor_lround(pb->circ_attempts), tor_lround(pathbias_get_use_success_count(guard)), - tor_lround(guard->use_attempts), - tor_lround(guard->circ_successes), - tor_lround(guard->unusable_circuits), - tor_lround(guard->collapsed_circuits), - tor_lround(guard->timeouts), + tor_lround(pb->use_attempts), + tor_lround(pb->circ_successes), + tor_lround(pb->unusable_circuits), + tor_lround(pb->collapsed_circuits), + tor_lround(pb->timeouts), tor_lround(get_circuit_build_close_time_ms()/1000)); - guard->path_bias_disabled = 1; - guard->bad_since = approx_time(); - entry_guards_changed(); + pb->path_bias_disabled = 1; +#ifdef ENABLE_LEGACY_GUARD_ALGORITHM + // XXXX + entry_guard_mark_bad(guard); +#endif return; } - } else if (!guard->path_bias_extreme) { - guard->path_bias_extreme = 1; + } else if (!pb->path_bias_extreme) { + pb->path_bias_extreme = 1; log_warn(LD_CIRC, - "Your Guard %s ($%s) is failing an extremely large " + "Your Guard %s is failing an extremely large " "amount of circuits. " "This could indicate a route manipulation attack, " "extreme network overload, or a bug. " @@ -1382,23 +1405,23 @@ pathbias_measure_close_rate(entry_guard_t *guard) "%ld circuits completed, %ld were unusable, %ld collapsed, " "and %ld timed out. " "For reference, your timeout cutoff is %ld seconds.", - guard->nickname, hex_str(guard->identity, DIGEST_LEN), + entry_guard_describe(guard), tor_lround(pathbias_get_close_success_count(guard)), - tor_lround(guard->circ_attempts), + tor_lround(pb->circ_attempts), tor_lround(pathbias_get_use_success_count(guard)), - tor_lround(guard->use_attempts), - tor_lround(guard->circ_successes), - tor_lround(guard->unusable_circuits), - tor_lround(guard->collapsed_circuits), - tor_lround(guard->timeouts), + tor_lround(pb->use_attempts), + tor_lround(pb->circ_successes), + tor_lround(pb->unusable_circuits), + tor_lround(pb->collapsed_circuits), + tor_lround(pb->timeouts), tor_lround(get_circuit_build_close_time_ms()/1000)); } - } else if (pathbias_get_close_success_count(guard)/guard->circ_attempts + } else if (pathbias_get_close_success_count(guard)/pb->circ_attempts < pathbias_get_warn_rate(options)) { - if (!guard->path_bias_warned) { - guard->path_bias_warned = 1; + if (!pb->path_bias_warned) { + pb->path_bias_warned = 1; log_warn(LD_CIRC, - "Your Guard %s ($%s) is failing a very large " + "Your Guard %s is failing a very large " "amount of circuits. " "Most likely this means the Tor network is " "overloaded, but it could also mean an attack against " @@ -1407,38 +1430,38 @@ pathbias_measure_close_rate(entry_guard_t *guard) "%ld circuits completed, %ld were unusable, %ld collapsed, " "and %ld timed out. " "For reference, your timeout cutoff is %ld seconds.", - guard->nickname, hex_str(guard->identity, DIGEST_LEN), + entry_guard_describe(guard), tor_lround(pathbias_get_close_success_count(guard)), - tor_lround(guard->circ_attempts), + tor_lround(pb->circ_attempts), tor_lround(pathbias_get_use_success_count(guard)), - tor_lround(guard->use_attempts), - tor_lround(guard->circ_successes), - tor_lround(guard->unusable_circuits), - tor_lround(guard->collapsed_circuits), - tor_lround(guard->timeouts), + tor_lround(pb->use_attempts), + tor_lround(pb->circ_successes), + tor_lround(pb->unusable_circuits), + tor_lround(pb->collapsed_circuits), + tor_lround(pb->timeouts), tor_lround(get_circuit_build_close_time_ms()/1000)); } - } else if (pathbias_get_close_success_count(guard)/guard->circ_attempts + } else if (pathbias_get_close_success_count(guard)/pb->circ_attempts < pathbias_get_notice_rate(options)) { - if (!guard->path_bias_noticed) { - guard->path_bias_noticed = 1; + if (!pb->path_bias_noticed) { + pb->path_bias_noticed = 1; log_notice(LD_CIRC, - "Your Guard %s ($%s) is failing more circuits than " + "Your Guard %s is failing more circuits than " "usual. " "Most likely this means the Tor network is overloaded. " "Success counts are %ld/%ld. Use counts are %ld/%ld. " "%ld circuits completed, %ld were unusable, %ld collapsed, " "and %ld timed out. " "For reference, your timeout cutoff is %ld seconds.", - guard->nickname, hex_str(guard->identity, DIGEST_LEN), + entry_guard_describe(guard), tor_lround(pathbias_get_close_success_count(guard)), - tor_lround(guard->circ_attempts), + tor_lround(pb->circ_attempts), tor_lround(pathbias_get_use_success_count(guard)), - tor_lround(guard->use_attempts), - tor_lround(guard->circ_successes), - tor_lround(guard->unusable_circuits), - tor_lround(guard->collapsed_circuits), - tor_lround(guard->timeouts), + tor_lround(pb->use_attempts), + tor_lround(pb->circ_successes), + tor_lround(pb->unusable_circuits), + tor_lround(pb->collapsed_circuits), + tor_lround(pb->timeouts), tor_lround(get_circuit_build_close_time_ms()/1000)); } } @@ -1458,9 +1481,10 @@ static void pathbias_scale_close_rates(entry_guard_t *guard) { const or_options_t *options = get_options(); + guard_pathbias_t *pb = entry_guard_get_pathbias_state(guard); /* If we get a ton of circuits, just scale everything down */ - if (guard->circ_attempts > pathbias_get_scale_threshold(options)) { + if (pb->circ_attempts > pathbias_get_scale_threshold(options)) { double scale_ratio = pathbias_get_scale_ratio(options); int opened_attempts = pathbias_count_circs_in_states(guard, PATH_STATE_BUILD_ATTEMPTED, PATH_STATE_BUILD_ATTEMPTED); @@ -1468,38 +1492,38 @@ pathbias_scale_close_rates(entry_guard_t *guard) PATH_STATE_BUILD_SUCCEEDED, PATH_STATE_USE_FAILED); /* Verify that the counts are sane before and after scaling */ - int counts_are_sane = (guard->circ_attempts >= guard->circ_successes); + int counts_are_sane = (pb->circ_attempts >= pb->circ_successes); - guard->circ_attempts -= (opened_attempts+opened_built); - guard->circ_successes -= opened_built; + pb->circ_attempts -= (opened_attempts+opened_built); + pb->circ_successes -= opened_built; - guard->circ_attempts *= scale_ratio; - guard->circ_successes *= scale_ratio; - guard->timeouts *= scale_ratio; - guard->successful_circuits_closed *= scale_ratio; - guard->collapsed_circuits *= scale_ratio; - guard->unusable_circuits *= scale_ratio; + pb->circ_attempts *= scale_ratio; + pb->circ_successes *= scale_ratio; + pb->timeouts *= scale_ratio; + pb->successful_circuits_closed *= scale_ratio; + pb->collapsed_circuits *= scale_ratio; + pb->unusable_circuits *= scale_ratio; - guard->circ_attempts += (opened_attempts+opened_built); - guard->circ_successes += opened_built; + pb->circ_attempts += (opened_attempts+opened_built); + pb->circ_successes += opened_built; entry_guards_changed(); log_info(LD_CIRC, "Scaled pathbias counts to (%f,%f)/%f (%d/%d open) for guard " - "%s ($%s)", - guard->circ_successes, guard->successful_circuits_closed, - guard->circ_attempts, opened_built, opened_attempts, - guard->nickname, hex_str(guard->identity, DIGEST_LEN)); + "%s", + pb->circ_successes, pb->successful_circuits_closed, + pb->circ_attempts, opened_built, opened_attempts, + entry_guard_describe(guard)); /* Have the counts just become invalid by this scaling attempt? */ - if (counts_are_sane && guard->circ_attempts < guard->circ_successes) { + if (counts_are_sane && pb->circ_attempts < pb->circ_successes) { log_notice(LD_BUG, "Scaling has mangled pathbias counts to %f/%f (%d/%d open) " - "for guard %s ($%s)", - guard->circ_successes, guard->circ_attempts, opened_built, - opened_attempts, guard->nickname, - hex_str(guard->identity, DIGEST_LEN)); + "for guard %s", + pb->circ_successes, pb->circ_attempts, opened_built, + opened_attempts, + entry_guard_describe(guard)); } } } @@ -1517,35 +1541,35 @@ void pathbias_scale_use_rates(entry_guard_t *guard) { const or_options_t *options = get_options(); + guard_pathbias_t *pb = entry_guard_get_pathbias_state(guard); /* If we get a ton of circuits, just scale everything down */ - if (guard->use_attempts > pathbias_get_scale_use_threshold(options)) { + if (pb->use_attempts > pathbias_get_scale_use_threshold(options)) { double scale_ratio = pathbias_get_scale_ratio(options); int opened_attempts = pathbias_count_circs_in_states(guard, PATH_STATE_USE_ATTEMPTED, PATH_STATE_USE_SUCCEEDED); /* Verify that the counts are sane before and after scaling */ - int counts_are_sane = (guard->use_attempts >= guard->use_successes); + int counts_are_sane = (pb->use_attempts >= pb->use_successes); - guard->use_attempts -= opened_attempts; + pb->use_attempts -= opened_attempts; - guard->use_attempts *= scale_ratio; - guard->use_successes *= scale_ratio; + pb->use_attempts *= scale_ratio; + pb->use_successes *= scale_ratio; - guard->use_attempts += opened_attempts; + pb->use_attempts += opened_attempts; log_info(LD_CIRC, - "Scaled pathbias use counts to %f/%f (%d open) for guard %s ($%s)", - guard->use_successes, guard->use_attempts, opened_attempts, - guard->nickname, hex_str(guard->identity, DIGEST_LEN)); + "Scaled pathbias use counts to %f/%f (%d open) for guard %s", + pb->use_successes, pb->use_attempts, opened_attempts, + entry_guard_describe(guard)); /* Have the counts just become invalid by this scaling attempt? */ - if (counts_are_sane && guard->use_attempts < guard->use_successes) { + if (counts_are_sane && pb->use_attempts < pb->use_successes) { log_notice(LD_BUG, "Scaling has mangled pathbias usage counts to %f/%f " - "(%d open) for guard %s ($%s)", - guard->circ_successes, guard->circ_attempts, - opened_attempts, guard->nickname, - hex_str(guard->identity, DIGEST_LEN)); + "(%d open) for guard %s", + pb->circ_successes, pb->circ_attempts, + opened_attempts, entry_guard_describe(guard)); } entry_guards_changed(); diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c index f60a8bfa89..55929c1666 100644 --- a/src/or/circuitbuild.c +++ b/src/or/circuitbuild.c @@ -28,6 +28,7 @@ #define CIRCUITBUILD_PRIVATE #include "or.h" +#include "bridges.h" #include "channel.h" #include "circpathbias.h" #define CIRCUITBUILD_PRIVATE @@ -518,6 +519,13 @@ circuit_establish_circuit(uint8_t purpose, extend_info_t *exit_ei, int flags) return circ; } +/** Return the guard state associated with <b>circ</b>, which may be NULL. */ +circuit_guard_state_t * +origin_circuit_get_guard_state(origin_circuit_t *circ) +{ + return circ->guard_state; +} + /** Start establishing the first hop of our circuit. Figure out what * OR we should connect to, and if necessary start the connection to * it. If we're already connected, then send the 'create' cell. @@ -958,7 +966,40 @@ circuit_send_next_onion_skin(origin_circuit_t *circ) memset(&ec, 0, sizeof(ec)); if (!hop) { /* done building the circuit. whew. */ - circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_OPEN); + guard_usable_t r; + if (get_options()->UseDeprecatedGuardAlgorithm) { + // The circuit is usable; we already marked the guard as okay. + r = GUARD_USABLE_NOW; + } else if (! circ->guard_state) { + if (circuit_get_cpath_len(circ) != 1 && + circ->base_.purpose != CIRCUIT_PURPOSE_TESTING && + get_options()->UseEntryGuards) { + log_warn(LD_BUG, "%d-hop circuit %p with purpose %d has no " + "guard state", + circuit_get_cpath_len(circ), circ, circ->base_.purpose); + } + r = GUARD_USABLE_NOW; + } else { + r = entry_guard_succeeded(&circ->guard_state); + } + const int is_usable_for_streams = (r == GUARD_USABLE_NOW); + if (r == GUARD_USABLE_NOW) { + circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_OPEN); + } else if (r == GUARD_MAYBE_USABLE_LATER) { + // XXXX prop271 we might want to probe for whether this + // XXXX one is ready even before the next second rolls over. + circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_GUARD_WAIT); + } else { + tor_assert_nonfatal(r == GUARD_USABLE_NEVER); + return - END_CIRC_REASON_INTERNAL; + } + + /* XXXX prop271 -- the rest of this branch needs careful thought! + * Some of the things here need to happen when a circuit becomes + * mechanically open; some need to happen when it is actually usable. + * I think I got them right, but more checking would be wise. -NM + */ + if (circuit_timeout_want_to_count_circ(circ)) { struct timeval end; long timediff; @@ -1000,7 +1041,8 @@ circuit_send_next_onion_skin(origin_circuit_t *circ) pathbias_count_build_success(circ); circuit_rep_hist_note_result(circ); - circuit_has_opened(circ); /* do other actions as necessary */ + if (is_usable_for_streams) + circuit_has_opened(circ); /* do other actions as necessary */ if (!have_completed_a_circuit() && !circ->build_state->onehop_tunnel) { const or_options_t *options = get_options(); @@ -1164,7 +1206,7 @@ circuit_extend(cell_t *cell, circuit_t *circ) /* Check if they asked us for 0000..0000. We support using * an empty fingerprint for the first hop (e.g. for a bridge relay), - * but we don't want to let people send us extend cells for empty + * but we don't want to let clients send us extend cells for empty * fingerprints -- a) because it opens the user up to a mitm attack, * and b) because it lets an attacker force the relay to hold open a * new TLS connection for each extend request. */ @@ -2227,9 +2269,20 @@ choose_good_middle_server(uint8_t purpose, * * If <b>state</b> is NULL, we're choosing a router to serve as an entry * guard, not for any particular circuit. + * + * Set *<b>guard_state_out</b> to information about the guard that + * we're selecting, which we'll use later to remember whether the + * guard worked or not. + * + * XXXX prop271 this function is used in four ways: picking out guards for + * the old (pre-prop271) guard algorithm; picking out guards for circuits; + * picking out guards for testing circuits on non-bridgees; + * picking out entries when entry guards are disabled. These options + * should be disentangled. */ const node_t * -choose_good_entry_server(uint8_t purpose, cpath_build_state_t *state) +choose_good_entry_server(uint8_t purpose, cpath_build_state_t *state, + circuit_guard_state_t **guard_state_out) { const node_t *choice; smartlist_t *excluded; @@ -2244,7 +2297,8 @@ choose_good_entry_server(uint8_t purpose, cpath_build_state_t *state) (purpose != CIRCUIT_PURPOSE_TESTING || options->BridgeRelay)) { /* This request is for an entry server to use for a regular circuit, * and we use entry guard nodes. Just return one of the guard nodes. */ - return choose_random_entry(state); + tor_assert(guard_state_out); + return guards_choose_guard(state, guard_state_out); } excluded = smartlist_new(); @@ -2254,6 +2308,7 @@ choose_good_entry_server(uint8_t purpose, cpath_build_state_t *state) * family. */ nodelist_add_node_and_family(excluded, node); } +#ifdef ENABLE_LEGACY_GUARD_ALGORITHM /* and exclude current entry guards and their families, * unless we're in a test network, and excluding guards * would exclude all nodes (i.e. we're in an incredibly small tor network, @@ -2267,11 +2322,12 @@ choose_good_entry_server(uint8_t purpose, cpath_build_state_t *state) )) { SMARTLIST_FOREACH(get_entry_guards(), const entry_guard_t *, entry, { - if ((node = node_get_by_id(entry->identity))) { + if ((node = entry_guard_find_node(entry))) { nodelist_add_node_and_family(excluded, node); } }); } +#endif if (state) { if (state->need_uptime) @@ -2327,7 +2383,8 @@ onion_extend_cpath(origin_circuit_t *circ) if (cur_len == state->desired_path_len - 1) { /* Picking last node */ info = extend_info_dup(state->chosen_exit); } else if (cur_len == 0) { /* picking first node */ - const node_t *r = choose_good_entry_server(purpose, state); + const node_t *r = choose_good_entry_server(purpose, state, + &circ->guard_state); if (r) { /* If we're a client, use the preferred address rather than the primary address, for potentially connecting to an IPv6 OR @@ -2510,8 +2567,8 @@ extend_info_dup(extend_info_t *info) return newinfo; } -/** Return the routerinfo_t for the chosen exit router in <b>state</b>. - * If there is no chosen exit, or if we don't know the routerinfo_t for +/** Return the node_t for the chosen exit router in <b>state</b>. + * If there is no chosen exit, or if we don't know the node_t for * the chosen exit, return NULL. */ const node_t * @@ -2522,6 +2579,17 @@ build_state_get_exit_node(cpath_build_state_t *state) return node_get_by_id(state->chosen_exit->identity_digest); } +/** Return the RSA ID digest for the chosen exit router in <b>state</b>. + * If there is no chosen exit, return NULL. + */ +const uint8_t * +build_state_get_exit_rsa_id(cpath_build_state_t *state) +{ + if (!state || !state->chosen_exit) + return NULL; + return (const uint8_t *) state->chosen_exit->identity_digest; +} + /** Return the nickname for the chosen exit router in <b>state</b>. If * there is no chosen exit, or if we don't know the routerinfo_t for the * chosen exit, return NULL. @@ -2614,3 +2682,26 @@ extend_info_has_preferred_onion_key(const extend_info_t* ei) return extend_info_supports_ntor(ei); } +/** Find the circuits that are waiting to find out whether their guards are + * usable, and if any are ready to become usable, mark them open and try + * attaching streams as appropriate. */ +void +circuit_upgrade_circuits_from_guard_wait(void) +{ + smartlist_t *to_upgrade = + circuit_find_circuits_to_upgrade_from_guard_wait(); + + if (to_upgrade == NULL) + return; + + log_info(LD_GUARD, "Upgrading %d circuits from 'waiting for better guard' " + "to 'open'.", smartlist_len(to_upgrade)); + + SMARTLIST_FOREACH_BEGIN(to_upgrade, origin_circuit_t *, circ) { + circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_OPEN); + circuit_has_opened(circ); + } SMARTLIST_FOREACH_END(circ); + + smartlist_free(to_upgrade); +} + diff --git a/src/or/circuitbuild.h b/src/or/circuitbuild.h index 54d14bbc7f..ddb070b427 100644 --- a/src/or/circuitbuild.h +++ b/src/or/circuitbuild.h @@ -21,6 +21,8 @@ origin_circuit_t *origin_circuit_init(uint8_t purpose, int flags); origin_circuit_t *circuit_establish_circuit(uint8_t purpose, extend_info_t *exit, int flags); +struct circuit_guard_state_t *origin_circuit_get_guard_state( + origin_circuit_t *circ); int circuit_handle_first_hop(origin_circuit_t *circ); void circuit_n_chan_done(channel_t *chan, int status, int close_origin_circuits); @@ -62,11 +64,16 @@ int extend_info_supports_ntor(const extend_info_t* ei); int circuit_can_use_tap(const origin_circuit_t *circ); int circuit_has_usable_onion_key(const origin_circuit_t *circ); int extend_info_has_preferred_onion_key(const extend_info_t* ei); +const uint8_t *build_state_get_exit_rsa_id(cpath_build_state_t *state); const node_t *build_state_get_exit_node(cpath_build_state_t *state); const char *build_state_get_exit_nickname(cpath_build_state_t *state); +struct circuit_guard_state_t; + const node_t *choose_good_entry_server(uint8_t purpose, - cpath_build_state_t *state); + cpath_build_state_t *state, + struct circuit_guard_state_t **guard_state_out); +void circuit_upgrade_circuits_from_guard_wait(void); #ifdef CIRCUITBUILD_PRIVATE STATIC circid_t get_unique_circ_id_by_chan(channel_t *chan); diff --git a/src/or/circuitlist.c b/src/or/circuitlist.c index dee103e36a..4ff5eff1d2 100644 --- a/src/or/circuitlist.c +++ b/src/or/circuitlist.c @@ -63,7 +63,9 @@ #include "connection_edge.h" #include "connection_or.h" #include "control.h" +#include "entrynodes.h" #include "main.h" +#include "hs_circuitmap.h" #include "hs_common.h" #include "networkstatus.h" #include "nodelist.h" @@ -84,18 +86,23 @@ /** A global list of all circuits at this hop. */ static smartlist_t *global_circuitlist = NULL; +/** A global list of all origin circuits. Every element of this is also + * an element of global_circuitlist. */ +static smartlist_t *global_origin_circuit_list = NULL; + /** A list of all the circuits in CIRCUIT_STATE_CHAN_WAIT. */ static smartlist_t *circuits_pending_chans = NULL; +/** List of all the (origin) circuits whose state is + * CIRCUIT_STATE_GUARD_WAIT. */ +static smartlist_t *circuits_pending_other_guards = NULL; + /** A list of all the circuits that have been marked with * circuit_mark_for_close and which are waiting for circuit_about_to_free. */ static smartlist_t *circuits_pending_close = NULL; static void circuit_free_cpath_node(crypt_path_t *victim); static void cpath_ref_decref(crypt_path_reference_t *cpath_ref); -//static void circuit_set_rend_token(or_circuit_t *circ, int is_rend_circ, -// const uint8_t *token); -static void circuit_clear_rend_token(or_circuit_t *circ); static void circuit_about_to_free_atexit(circuit_t *circ); static void circuit_about_to_free(circuit_t *circ); @@ -428,8 +435,10 @@ circuit_set_state(circuit_t *circ, uint8_t state) tor_assert(circ); if (state == circ->state) return; - if (!circuits_pending_chans) + if (PREDICT_UNLIKELY(!circuits_pending_chans)) circuits_pending_chans = smartlist_new(); + if (PREDICT_UNLIKELY(!circuits_pending_other_guards)) + circuits_pending_other_guards = smartlist_new(); if (circ->state == CIRCUIT_STATE_CHAN_WAIT) { /* remove from waiting-circuit list. */ smartlist_remove(circuits_pending_chans, circ); @@ -438,7 +447,13 @@ circuit_set_state(circuit_t *circ, uint8_t state) /* add to waiting-circuit list. */ smartlist_add(circuits_pending_chans, circ); } - if (state == CIRCUIT_STATE_OPEN) + if (circ->state == CIRCUIT_STATE_GUARD_WAIT) { + smartlist_remove(circuits_pending_other_guards, circ); + } + if (state == CIRCUIT_STATE_GUARD_WAIT) { + smartlist_add(circuits_pending_other_guards, circ); + } + if (state == CIRCUIT_STATE_GUARD_WAIT || state == CIRCUIT_STATE_OPEN) tor_assert(!circ->n_chan_create_cell); circ->state = state; } @@ -516,6 +531,19 @@ circuit_close_all_marked(void) } circ->global_circuitlist_idx = -1; + /* Remove it from the origin circuit list, if appropriate. */ + if (CIRCUIT_IS_ORIGIN(circ)) { + origin_circuit_t *origin_circ = TO_ORIGIN_CIRCUIT(circ); + int origin_idx = origin_circ->global_origin_circuit_list_idx; + smartlist_del(global_origin_circuit_list, origin_idx); + if (origin_idx < smartlist_len(global_origin_circuit_list)) { + origin_circuit_t *replacement = + smartlist_get(global_origin_circuit_list, origin_idx); + replacement->global_origin_circuit_list_idx = origin_idx; + } + origin_circ->global_origin_circuit_list_idx = -1; + } + circuit_about_to_free(circ); circuit_free(circ); } SMARTLIST_FOREACH_END(circ); @@ -523,7 +551,7 @@ circuit_close_all_marked(void) smartlist_clear(circuits_pending_close); } -/** Return the head of the global linked list of circuits. */ +/** Return a pointer to the global list of circuits. */ MOCK_IMPL(smartlist_t *, circuit_get_global_list,(void)) { @@ -532,6 +560,15 @@ circuit_get_global_list,(void)) return global_circuitlist; } +/** Return a pointer to the global list of origin circuits. */ +smartlist_t * +circuit_get_global_origin_circuit_list(void) +{ + if (NULL == global_origin_circuit_list) + global_origin_circuit_list = smartlist_new(); + return global_circuitlist; +} + /** Function to make circ-\>state human-readable */ const char * circuit_state_to_string(int state) @@ -541,6 +578,8 @@ circuit_state_to_string(int state) case CIRCUIT_STATE_BUILDING: return "doing handshakes"; case CIRCUIT_STATE_ONIONSKIN_PENDING: return "processing the onion"; case CIRCUIT_STATE_CHAN_WAIT: return "connecting to server"; + case CIRCUIT_STATE_GUARD_WAIT: return "waiting to see how other " + "guards perform"; case CIRCUIT_STATE_OPEN: return "open"; default: log_warn(LD_BUG, "Unknown circuit state %d", state); @@ -771,6 +810,13 @@ origin_circuit_new(void) init_circuit_base(TO_CIRCUIT(circ)); + /* Add to origin-list. */ + if (!global_origin_circuit_list) + global_origin_circuit_list = smartlist_new(); + smartlist_add(global_origin_circuit_list, circ); + circ->global_origin_circuit_list_idx = + smartlist_len(global_origin_circuit_list) - 1; + circuit_build_times_update_last_circ(get_circuit_build_times_mutable()); return circ; @@ -828,6 +874,18 @@ circuit_free(circuit_t *circ) mem = ocirc; memlen = sizeof(origin_circuit_t); tor_assert(circ->magic == ORIGIN_CIRCUIT_MAGIC); + + if (ocirc->global_origin_circuit_list_idx != -1) { + int idx = ocirc->global_origin_circuit_list_idx; + origin_circuit_t *c2 = smartlist_get(global_origin_circuit_list, idx); + tor_assert(c2 == ocirc); + smartlist_del(global_origin_circuit_list, idx); + if (idx < smartlist_len(global_origin_circuit_list)) { + c2 = smartlist_get(global_origin_circuit_list, idx); + c2->global_origin_circuit_list_idx = idx; + } + } + if (ocirc->build_state) { extend_info_free(ocirc->build_state->chosen_exit); circuit_free_cpath_node(ocirc->build_state->pending_final_cpath); @@ -835,6 +893,12 @@ circuit_free(circuit_t *circ) } tor_free(ocirc->build_state); + /* Cancel before freeing, if we haven't already succeeded or failed. */ + if (ocirc->guard_state) { + entry_guard_cancel(ô->guard_state); + } + circuit_guard_state_free(ocirc->guard_state); + circuit_clear_cpath(ocirc); crypto_pk_free(ocirc->intro_key); @@ -866,7 +930,9 @@ circuit_free(circuit_t *circ) crypto_cipher_free(ocirc->n_crypto); crypto_digest_free(ocirc->n_digest); - circuit_clear_rend_token(ocirc); + if (ocirc->hs_token) { + hs_circuitmap_remove_circuit(ocirc); + } if (ocirc->rend_splice) { or_circuit_t *other = ocirc->rend_splice; @@ -967,12 +1033,18 @@ circuit_free_all(void) smartlist_free(lst); global_circuitlist = NULL; + smartlist_free(global_origin_circuit_list); + global_origin_circuit_list = NULL; + smartlist_free(circuits_pending_chans); circuits_pending_chans = NULL; smartlist_free(circuits_pending_close); circuits_pending_close = NULL; + smartlist_free(circuits_pending_other_guards); + circuits_pending_other_guards = NULL; + { chan_circid_circuit_map_t **elt, **next, *c; for (elt = HT_START(chan_circid_map, &chan_circid_map); @@ -1368,6 +1440,41 @@ circuit_get_ready_rend_circ_by_rend_data(const rend_data_t *rend_data) return NULL; } +/** Return the first service introduction circuit originating from the global + * circuit list after <b>start</b> or at the start of the list if <b>start</b> + * is NULL. Return NULL if no circuit is found. + * + * A service introduction point circuit has a purpose of either + * CIRCUIT_PURPOSE_S_ESTABLISH_INTRO or CIRCUIT_PURPOSE_S_INTRO. This does not + * return a circuit marked for close and its state must be open. */ +origin_circuit_t * +circuit_get_next_service_intro_circ(origin_circuit_t *start) +{ + int idx = 0; + smartlist_t *lst = circuit_get_global_list(); + + if (start) { + idx = TO_CIRCUIT(start)->global_circuitlist_idx + 1; + } + + for ( ; idx < smartlist_len(lst); ++idx) { + circuit_t *circ = smartlist_get(lst, idx); + + /* Ignore a marked for close circuit or purpose not matching a service + * intro point or if the state is not open. */ + if (circ->marked_for_close || circ->state != CIRCUIT_STATE_OPEN || + (circ->purpose != CIRCUIT_PURPOSE_S_ESTABLISH_INTRO && + circ->purpose != CIRCUIT_PURPOSE_S_INTRO)) { + continue; + } + /* The purposes we are looking for are only for origin circuits so the + * following is valid. */ + return TO_ORIGIN_CIRCUIT(circ); + } + /* Not found. */ + return NULL; +} + /** Return the first circuit originating here in global_circuitlist after * <b>start</b> whose purpose is <b>purpose</b>, and where <b>digest</b> (if * set) matches the private key digest of the rend data associated with the @@ -1409,177 +1516,6 @@ circuit_get_next_by_pk_and_purpose(origin_circuit_t *start, return NULL; } -/** Map from rendezvous cookie to or_circuit_t */ -static digestmap_t *rend_cookie_map = NULL; - -/** Map from introduction point digest to or_circuit_t */ -static digestmap_t *intro_digest_map = NULL; - -/** Return the OR circuit whose purpose is <b>purpose</b>, and whose - * rend_token is the REND_TOKEN_LEN-byte <b>token</b>. If <b>is_rend_circ</b>, - * look for rendezvous point circuits; otherwise look for introduction point - * circuits. */ -static or_circuit_t * -circuit_get_by_rend_token_and_purpose(uint8_t purpose, int is_rend_circ, - const char *token) -{ - or_circuit_t *circ; - digestmap_t *map = is_rend_circ ? rend_cookie_map : intro_digest_map; - - if (!map) - return NULL; - - circ = digestmap_get(map, token); - if (!circ || - circ->base_.purpose != purpose || - circ->base_.marked_for_close) - return NULL; - - if (!circ->rendinfo) { - char *t = tor_strdup(hex_str(token, REND_TOKEN_LEN)); - log_warn(LD_BUG, "Wanted a circuit with %s:%d, but lookup returned a " - "circuit with no rendinfo set.", - safe_str(t), is_rend_circ); - tor_free(t); - return NULL; - } - - if (! bool_eq(circ->rendinfo->is_rend_circ, is_rend_circ) || - tor_memneq(circ->rendinfo->rend_token, token, REND_TOKEN_LEN)) { - char *t = tor_strdup(hex_str(token, REND_TOKEN_LEN)); - log_warn(LD_BUG, "Wanted a circuit with %s:%d, but lookup returned %s:%d", - safe_str(t), is_rend_circ, - safe_str(hex_str(circ->rendinfo->rend_token, REND_TOKEN_LEN)), - (int)circ->rendinfo->is_rend_circ); - tor_free(t); - return NULL; - } - - return circ; -} - -/** Clear the rendezvous cookie or introduction point key digest that's - * configured on <b>circ</b>, if any, and remove it from any such maps. */ -static void -circuit_clear_rend_token(or_circuit_t *circ) -{ - or_circuit_t *found_circ; - digestmap_t *map; - - if (!circ || !circ->rendinfo) - return; - - map = circ->rendinfo->is_rend_circ ? rend_cookie_map : intro_digest_map; - - if (!map) { - log_warn(LD_BUG, "Tried to clear rend token on circuit, but found no map"); - return; - } - - found_circ = digestmap_get(map, circ->rendinfo->rend_token); - if (found_circ == circ) { - /* Great, this is the right one. */ - digestmap_remove(map, circ->rendinfo->rend_token); - } else if (found_circ) { - log_warn(LD_BUG, "Tried to clear rend token on circuit, but " - "it was already replaced in the map."); - } else { - log_warn(LD_BUG, "Tried to clear rend token on circuit, but " - "it not in the map at all."); - } - - tor_free(circ->rendinfo); /* Sets it to NULL too */ -} - -/** Set the rendezvous cookie (if is_rend_circ), or the introduction point - * digest (if ! is_rend_circ) of <b>circ</b> to the REND_TOKEN_LEN-byte value - * in <b>token</b>, and add it to the appropriate map. If it previously had a - * token, clear it. If another circuit previously had the same - * cookie/intro-digest, mark that circuit and remove it from the map. */ -static void -circuit_set_rend_token(or_circuit_t *circ, int is_rend_circ, - const uint8_t *token) -{ - digestmap_t **map_p, *map; - or_circuit_t *found_circ; - - /* Find the right map, creating it as needed */ - map_p = is_rend_circ ? &rend_cookie_map : &intro_digest_map; - - if (!*map_p) - *map_p = digestmap_new(); - - map = *map_p; - - /* If this circuit already has a token, we need to remove that. */ - if (circ->rendinfo) - circuit_clear_rend_token(circ); - - if (token == NULL) { - /* We were only trying to remove this token, not set a new one. */ - return; - } - - found_circ = digestmap_get(map, (const char *)token); - if (found_circ) { - tor_assert(found_circ != circ); - circuit_clear_rend_token(found_circ); - if (! found_circ->base_.marked_for_close) { - circuit_mark_for_close(TO_CIRCUIT(found_circ), END_CIRC_REASON_FINISHED); - if (is_rend_circ) { - log_fn(LOG_PROTOCOL_WARN, LD_REND, - "Duplicate rendezvous cookie (%s...) used on two circuits", - hex_str((const char*)token, 4)); /* only log first 4 chars */ - } - } - } - - /* Now set up the rendinfo */ - circ->rendinfo = tor_malloc(sizeof(*circ->rendinfo)); - memcpy(circ->rendinfo->rend_token, token, REND_TOKEN_LEN); - circ->rendinfo->is_rend_circ = is_rend_circ ? 1 : 0; - - digestmap_set(map, (const char *)token, circ); -} - -/** Return the circuit waiting for a rendezvous with the provided cookie. - * Return NULL if no such circuit is found. - */ -or_circuit_t * -circuit_get_rendezvous(const uint8_t *cookie) -{ - return circuit_get_by_rend_token_and_purpose( - CIRCUIT_PURPOSE_REND_POINT_WAITING, - 1, (const char*)cookie); -} - -/** Return the circuit waiting for intro cells of the given digest. - * Return NULL if no such circuit is found. - */ -or_circuit_t * -circuit_get_intro_point(const uint8_t *digest) -{ - return circuit_get_by_rend_token_and_purpose( - CIRCUIT_PURPOSE_INTRO_POINT, 0, - (const char *)digest); -} - -/** Set the rendezvous cookie of <b>circ</b> to <b>cookie</b>. If another - * circuit previously had that cookie, mark it. */ -void -circuit_set_rendezvous_cookie(or_circuit_t *circ, const uint8_t *cookie) -{ - circuit_set_rend_token(circ, 1, cookie); -} - -/** Set the intro point key digest of <b>circ</b> to <b>cookie</b>. If another - * circuit previously had that intro point digest, mark it. */ -void -circuit_set_intro_point_digest(or_circuit_t *circ, const uint8_t *digest) -{ - circuit_set_rend_token(circ, 0, digest); -} - /** Return a circuit that is open, is CIRCUIT_PURPOSE_C_GENERAL, * has a timestamp_dirty value of 0, has flags matching the CIRCLAUNCH_* * flags in <b>flags</b>, and if info is defined, does not already use info @@ -1672,6 +1608,37 @@ circuit_find_to_cannibalize(uint8_t purpose, extend_info_t *info, return best; } +/** + * Check whether any of the origin circuits that are waiting to see if + * their guard is good enough to use can be upgraded to "ready". If so, + * return a new smartlist containing them. Otherwise return NULL. + */ +smartlist_t * +circuit_find_circuits_to_upgrade_from_guard_wait(void) +{ + /* Only if some circuit is actually waiting on an upgrade should we + * run the algorithm. */ + if (! circuits_pending_other_guards || + smartlist_len(circuits_pending_other_guards)==0) + return NULL; + /* Only if we have some origin circuits should we run the algorithm. */ + if (!global_origin_circuit_list) + return NULL; + + /* Okay; we can pass our circuit list to entrynodes.c.*/ + smartlist_t *result = smartlist_new(); + int circuits_upgraded = entry_guards_upgrade_waiting_circuits( + get_guard_selection_info(), + global_origin_circuit_list, + result); + if (circuits_upgraded && smartlist_len(result)) { + return result; + } else { + smartlist_free(result); + return NULL; + } +} + /** Return the number of hops in circuit's path. If circ has no entries, * or is NULL, returns 0. */ int @@ -1866,7 +1833,8 @@ circuit_about_to_free(circuit_t *circ) * module then. If it isn't OPEN, we send it there now to remember which * links worked and which didn't. */ - if (circ->state != CIRCUIT_STATE_OPEN) { + if (circ->state != CIRCUIT_STATE_OPEN && + circ->state != CIRCUIT_STATE_GUARD_WAIT) { if (CIRCUIT_IS_ORIGIN(circ)) { origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ); circuit_build_failed(ocirc); /* take actions if necessary */ @@ -1879,7 +1847,9 @@ circuit_about_to_free(circuit_t *circ) } if (CIRCUIT_IS_ORIGIN(circ)) { control_event_circuit_status(TO_ORIGIN_CIRCUIT(circ), - (circ->state == CIRCUIT_STATE_OPEN)?CIRC_EVENT_CLOSED:CIRC_EVENT_FAILED, + (circ->state == CIRCUIT_STATE_OPEN || + circ->state == CIRCUIT_STATE_GUARD_WAIT) ? + CIRC_EVENT_CLOSED:CIRC_EVENT_FAILED, orig_reason); } @@ -2401,7 +2371,8 @@ assert_circuit_ok(const circuit_t *c) tor_assert(c->deliver_window >= 0); tor_assert(c->package_window >= 0); - if (c->state == CIRCUIT_STATE_OPEN) { + if (c->state == CIRCUIT_STATE_OPEN || + c->state == CIRCUIT_STATE_GUARD_WAIT) { tor_assert(!c->n_chan_create_cell); if (or_circ) { tor_assert(or_circ->n_crypto); diff --git a/src/or/circuitlist.h b/src/or/circuitlist.h index 989c02afd5..6abee37dc4 100644 --- a/src/or/circuitlist.h +++ b/src/or/circuitlist.h @@ -15,6 +15,7 @@ #include "testsupport.h" MOCK_DECL(smartlist_t *, circuit_get_global_list, (void)); +smartlist_t *circuit_get_global_origin_circuit_list(void); const char *circuit_state_to_string(int state); const char *circuit_purpose_to_controller_string(uint8_t purpose); const char *circuit_purpose_to_controller_hs_state_string(uint8_t purpose); @@ -46,10 +47,7 @@ origin_circuit_t *circuit_get_ready_rend_circ_by_rend_data( const rend_data_t *rend_data); origin_circuit_t *circuit_get_next_by_pk_and_purpose(origin_circuit_t *start, const uint8_t *digest, uint8_t purpose); -or_circuit_t *circuit_get_rendezvous(const uint8_t *cookie); -or_circuit_t *circuit_get_intro_point(const uint8_t *digest); -void circuit_set_rendezvous_cookie(or_circuit_t *circ, const uint8_t *cookie); -void circuit_set_intro_point_digest(or_circuit_t *circ, const uint8_t *digest); +origin_circuit_t *circuit_get_next_service_intro_circ(origin_circuit_t *start); origin_circuit_t *circuit_find_to_cannibalize(uint8_t purpose, extend_info_t *info, int flags); void circuit_mark_all_unused_circs(void); @@ -77,6 +75,8 @@ void channel_note_destroy_pending(channel_t *chan, circid_t id); MOCK_DECL(void, channel_note_destroy_not_pending, (channel_t *chan, circid_t id)); +smartlist_t *circuit_find_circuits_to_upgrade_from_guard_wait(void); + #ifdef CIRCUITLIST_PRIVATE STATIC void circuit_free(circuit_t *circ); STATIC size_t n_cells_in_circ_queues(const circuit_t *c); diff --git a/src/or/circuitstats.c b/src/or/circuitstats.c index 418acc0024..6cb99e4175 100644 --- a/src/or/circuitstats.c +++ b/src/or/circuitstats.c @@ -1431,7 +1431,7 @@ circuit_build_times_network_check_changed(circuit_build_times_t *cbt) #define MAX_TIMEOUT ((int32_t) (INT32_MAX/2)) /* Check to see if this has happened before. If so, double the timeout - * to give people on abysmally bad network connections a shot at access */ + * to give clients on abysmally bad network connections a shot at access */ if (cbt->timeout_ms >= circuit_build_times_get_initial_timeout()) { if (cbt->timeout_ms > MAX_TIMEOUT || cbt->close_ms > MAX_TIMEOUT) { log_warn(LD_CIRC, "Insanely large circuit build timeout value. " diff --git a/src/or/circuituse.c b/src/or/circuituse.c index 04c5af92e8..ecd35af9dd 100644 --- a/src/or/circuituse.c +++ b/src/or/circuituse.c @@ -29,6 +29,7 @@ #include "or.h" #include "addressmap.h" +#include "bridges.h" #include "channel.h" #include "circpathbias.h" #include "circuitbuild.h" @@ -549,16 +550,14 @@ circuit_expire_building(void) == CPATH_STATE_OPEN; log_info(LD_CIRC, "No circuits are opened. Relaxing timeout for circuit %d " - "(a %s %d-hop circuit in state %s with channel state %s). " - "%d guards are live.", + "(a %s %d-hop circuit in state %s with channel state %s).", TO_ORIGIN_CIRCUIT(victim)->global_identifier, circuit_purpose_to_string(victim->purpose), TO_ORIGIN_CIRCUIT(victim)->build_state ? TO_ORIGIN_CIRCUIT(victim)->build_state->desired_path_len : -1, circuit_state_to_string(victim->state), - channel_state_to_string(victim->n_chan->state), - num_live_entry_guards(0)); + channel_state_to_string(victim->n_chan->state)); /* We count the timeout here for CBT, because technically this * was a timeout, and the timeout value needs to reset if we @@ -576,7 +575,7 @@ circuit_expire_building(void) "No circuits are opened. Relaxed timeout for circuit %d " "(a %s %d-hop circuit in state %s with channel state %s) to " "%ldms. However, it appears the circuit has timed out " - "anyway. %d guards are live.", + "anyway.", TO_ORIGIN_CIRCUIT(victim)->global_identifier, circuit_purpose_to_string(victim->purpose), TO_ORIGIN_CIRCUIT(victim)->build_state ? @@ -584,8 +583,7 @@ circuit_expire_building(void) -1, circuit_state_to_string(victim->state), channel_state_to_string(victim->n_chan->state), - (long)build_close_ms, - num_live_entry_guards(0)); + (long)build_close_ms); } } @@ -799,6 +797,25 @@ circuit_expire_building(void) } SMARTLIST_FOREACH_END(victim); } +/** + * Mark for close all circuits that start here, that were built through a + * guard we weren't sure if we wanted to use, and that have been waiting + * around for way too long. + */ +void +circuit_expire_waiting_for_better_guard(void) +{ + SMARTLIST_FOREACH_BEGIN(circuit_get_global_origin_circuit_list(), + origin_circuit_t *, circ) { + if (TO_CIRCUIT(circ)->marked_for_close) + continue; + if (circ->guard_state == NULL) + continue; + if (entry_guard_state_should_expire(circ->guard_state)) + circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_NONE); + } SMARTLIST_FOREACH_END(circ); +} + /** For debugging #8387: track when we last called * circuit_expire_old_circuits_clientside. */ static time_t last_expired_clientside_circuits = 0; @@ -1712,7 +1729,13 @@ circuit_build_failed(origin_circuit_t *circ) "Our circuit died before the first hop with no connection"); } if (n_chan_id && !already_marked) { + /* New guard API: we failed. */ + if (circ->guard_state) + entry_guard_failed(&circ->guard_state); +#ifdef ENABLE_LEGACY_GUARD_ALGORITHM + /* Old guard API: we failed. */ entry_guard_register_connect_status(n_chan_id, 0, 1, time(NULL)); +#endif /* if there are any one-hop streams waiting on this circuit, fail * them now so they can retry elsewhere. */ connection_ap_fail_onehop(n_chan_id, circ->build_state); @@ -2022,7 +2045,7 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn, int severity = LOG_NOTICE; /* Retry some stuff that might help the connection work. */ if (entry_list_is_constrained(options) && - entries_known_but_down(options)) { + guards_retry_optimistic(options)) { log_fn(severity, LD_APP|LD_DIR, "Application request when we haven't %s. " "Optimistically trying known %s again.", @@ -2030,7 +2053,6 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn, "used client functionality lately" : "received a consensus with exits", options->UseBridges ? "bridges" : "entrynodes"); - entries_retry_all(options); } else if (!options->UseBridges || any_bridge_descriptors_known()) { log_fn(severity, LD_APP|LD_DIR, "Application request when we haven't %s. " @@ -2105,7 +2127,7 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn, * will have to launch one! */ - /* THe chosen exit node, if there is one. */ + /* The chosen exit node, if there is one. */ extend_info_t *extend_info=NULL; const int n_pending = count_pending_general_client_circuits(); @@ -2598,7 +2620,7 @@ connection_ap_handshake_attach_circuit(entry_connection_t *conn) log_debug(LD_APP|LD_CIRC, "Attaching apconn to circ %u (stream %d sec old).", (unsigned)circ->base_.n_circ_id, conn_age); - /* print the circ's path, so people can figure out which circs are + /* print the circ's path, so clients can figure out which circs are * sucking. */ circuit_log_path(LOG_INFO,LD_APP|LD_CIRC,circ); diff --git a/src/or/circuituse.h b/src/or/circuituse.h index d484be1986..e5f8700ea2 100644 --- a/src/or/circuituse.h +++ b/src/or/circuituse.h @@ -13,6 +13,7 @@ #define TOR_CIRCUITUSE_H void circuit_expire_building(void); +void circuit_expire_waiting_for_better_guard(void); void circuit_remove_handled_ports(smartlist_t *needed_ports); int circuit_stream_is_being_handled(entry_connection_t *conn, uint16_t port, int min); diff --git a/src/or/config.c b/src/or/config.c index bbe93a5ba1..b8ca14e85b 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -60,6 +60,7 @@ #define CONFIG_PRIVATE #include "or.h" +#include "bridges.h" #include "compat.h" #include "addressmap.h" #include "channel.h" @@ -218,7 +219,7 @@ static config_var_t option_vars_[] = { OBSOLETE("AuthDirListBadDirs"), V(AuthDirListBadExits, BOOL, "0"), V(AuthDirMaxServersPerAddr, UINT, "2"), - V(AuthDirMaxServersPerAuthAddr,UINT, "5"), + OBSOLETE("AuthDirMaxServersPerAuthAddr"), V(AuthDirHasIPv6Connectivity, BOOL, "0"), VAR("AuthoritativeDirectory", BOOL, AuthoritativeDir, "0"), V(AutomapHostsOnResolve, BOOL, "0"), @@ -307,6 +308,10 @@ static config_var_t option_vars_[] = { V(ExtraInfoStatistics, BOOL, "1"), V(ExtendByEd25519ID, AUTOBOOL, "auto"), V(FallbackDir, LINELIST, NULL), + /* XXXX prop271 -- this has an ugly name to remind us to remove it. */ + VAR("UseDeprecatedGuardAlgorithm_", BOOL, + UseDeprecatedGuardAlgorithm, "0"), + V(UseDefaultFallbackDirs, BOOL, "1"), OBSOLETE("FallbackNetworkstatusFile"), @@ -491,7 +496,7 @@ static config_var_t option_vars_[] = { V(UpdateBridgesFromAuthority, BOOL, "0"), V(UseBridges, BOOL, "0"), VAR("UseEntryGuards", BOOL, UseEntryGuards_option, "1"), - V(UseEntryGuardsAsDirGuards, BOOL, "1"), + OBSOLETE("UseEntryGuardsAsDirGuards"), V(UseGuardFraction, AUTOBOOL, "auto"), V(UseMicrodescriptors, AUTOBOOL, "auto"), OBSOLETE("UseNTorHandshake"), @@ -594,7 +599,6 @@ static const config_var_t testing_tor_network_defaults[] = { V(EnforceDistinctSubnets, BOOL, "0"), V(AssumeReachable, BOOL, "1"), V(AuthDirMaxServersPerAddr, UINT, "0"), - V(AuthDirMaxServersPerAuthAddr,UINT, "0"), V(ClientBootstrapConsensusAuthorityDownloadSchedule, CSV_INTERVAL, "0, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 16, 32, 60"), V(ClientBootstrapConsensusFallbackDownloadSchedule, CSV_INTERVAL, @@ -1558,6 +1562,36 @@ options_transition_requires_fresh_tls_context(const or_options_t *old_options, return 0; } +/** + * Return true if changing the configuration from <b>old</b> to <b>new</b> + * affects the guard susbsystem. + */ +static int +options_transition_affects_guards(const or_options_t *old, + const or_options_t *new) +{ + /* NOTE: Make sure this function stays in sync with + * entry_guards_set_filtered_flags */ + + tor_assert(old); + tor_assert(new); + + return + (old->UseEntryGuards != new->UseEntryGuards || + old->UseDeprecatedGuardAlgorithm != new->UseDeprecatedGuardAlgorithm || + old->UseBridges != new->UseBridges || + old->UseEntryGuards != new->UseEntryGuards || + old->ClientUseIPv4 != new->ClientUseIPv4 || + old->ClientUseIPv6 != new->ClientUseIPv6 || + old->FascistFirewall != new->FascistFirewall || + !routerset_equal(old->ExcludeNodes, new->ExcludeNodes) || + !routerset_equal(old->EntryNodes, new->EntryNodes) || + !smartlist_strings_eq(old->FirewallPorts, new->FirewallPorts) || + !config_lines_eq(old->Bridges, new->Bridges) || + !config_lines_eq(old->ReachableORAddresses, new->ReachableORAddresses) || + !config_lines_eq(old->ReachableDirAddresses, new->ReachableDirAddresses)); +} + /** Fetch the active option list, and take actions based on it. All of the * things we do should survive being done repeatedly. If present, * <b>old_options</b> contains the previous value of the options. @@ -1577,6 +1611,8 @@ options_act(const or_options_t *old_options) const int transition_affects_workers = old_options && options_transition_affects_workers(old_options, options); int old_ewma_enabled; + const int transition_affects_guards = + old_options && options_transition_affects_guards(old_options, options); /* disable ptrace and later, other basic debugging techniques */ { @@ -1853,6 +1889,7 @@ options_act(const or_options_t *old_options) if (old_options) { int revise_trackexithosts = 0; int revise_automap_entries = 0; + int abandon_circuits = 0; if ((options->UseEntryGuards && !old_options->UseEntryGuards) || options->UseBridges != old_options->UseBridges || (options->UseBridges && @@ -1869,6 +1906,16 @@ options_act(const or_options_t *old_options) "Changed to using entry guards or bridges, or changed " "preferred or excluded node lists. " "Abandoning previous circuits."); + abandon_circuits = 1; + } + + if (transition_affects_guards) { + if (guards_update_all()) { + abandon_circuits = 1; + } + } + + if (abandon_circuits) { circuit_mark_all_unused_circs(); circuit_mark_all_dirty_circs_as_unusable(); revise_trackexithosts = 1; @@ -1899,7 +1946,7 @@ options_act(const or_options_t *old_options) addressmap_clear_invalid_automaps(options); /* How long should we delay counting bridge stats after becoming a bridge? - * We use this so we don't count people who used our bridge thinking it is + * We use this so we don't count clients who used our bridge thinking it is * a relay. If you change this, don't forget to change the log message * below. It's 4 hours (the time it takes to stop being used by clients) * plus some extra time for clock skew. */ @@ -2051,11 +2098,13 @@ options_act(const or_options_t *old_options) rep_hist_desc_stats_term(); /* Check if we need to parse and add the EntryNodes config option. */ +#ifdef ENABLE_LEGACY_GUARD_ALGORITHM if (options->EntryNodes && (!old_options || !routerset_equal(old_options->EntryNodes,options->EntryNodes) || !routerset_equal(old_options->ExcludeNodes,options->ExcludeNodes))) entry_nodes_should_be_added(); +#endif /* Since our options changed, we might need to regenerate and upload our * server descriptor. @@ -2381,8 +2430,8 @@ using_default_dir_authorities(const or_options_t *options) * Fail if one or more of the following is true: * - DNS name in <b>options-\>Address</b> cannot be resolved. * - <b>options-\>Address</b> is a local host address. - * - Attempt to getting local hostname fails. - * - Attempt to getting network interface address fails. + * - Attempt at getting local hostname fails. + * - Attempt at getting network interface address fails. * * Return 0 if all is well, or -1 if we can't find a suitable * public IP address. @@ -2762,7 +2811,7 @@ compute_publishserverdescriptor(or_options_t *options) #define MIN_REND_POST_PERIOD (10*60) #define MIN_REND_POST_PERIOD_TESTING (5) -/** Higest allowable value for PredictedPortsRelevanceTime; if this is +/** Highest allowable value for PredictedPortsRelevanceTime; if this is * too high, our selection of exits will decrease for an extended * period of time to an uncomfortable level .*/ #define MAX_PREDICTED_CIRCS_RELEVANCE (60*60) @@ -2905,12 +2954,12 @@ options_validate_single_onion(or_options_t *options, char **msg) REJECT("Non-anonymous (Tor2web) mode is incompatible with using Tor as a " "hidden service. Please remove all HiddenServiceDir lines, or use " "a version of tor compiled without --enable-tor2web-mode, or use " - " HiddenServiceNonAnonymousMode."); + "HiddenServiceNonAnonymousMode."); } if (rend_service_allow_non_anonymous_connection(options) && options->UseEntryGuards) { - /* Single Onion services only use entry guards when uploading descriptors, + /* Single Onion services only use entry guards when uploading descriptors; * all other connections are one-hop. Further, Single Onions causes the * hidden service code to do things which break the path bias * detector, and it's far easier to turn off entry guards (and @@ -2953,13 +3002,20 @@ options_validate(or_options_t *old_options, or_options_t *options, *msg = NULL; /* Set UseEntryGuards from the configured value, before we check it below. - * We change UseEntryGuards whenn it's incompatible with other options, + * We change UseEntryGuards when it's incompatible with other options, * but leave UseEntryGuards_option with the original value. * Always use the value of UseEntryGuards, not UseEntryGuards_option. */ options->UseEntryGuards = options->UseEntryGuards_option; warn_about_relative_paths(options); +#ifndef ENABLE_LEGACY_GUARD_ALGORITHM + if (options->UseDeprecatedGuardAlgorithm) { + log_warn(LD_CONFIG, "DeprecatedGuardAlgorithm not supported."); + return -1; + } +#endif + if (server_mode(options) && (!strcmpstart(uname, "Windows 95") || !strcmpstart(uname, "Windows 98") || @@ -4449,7 +4505,6 @@ options_transition_allowed(const or_options_t *old, } while (0) SB_NOCHANGE_STR(Address); - SB_NOCHANGE_STR(PidFile); SB_NOCHANGE_STR(ServerDNSResolvConfFile); SB_NOCHANGE_STR(DirPortFrontPage); SB_NOCHANGE_STR(CookieAuthFile); @@ -5240,35 +5295,35 @@ addressmap_register_auto(const char *from, const char *to, int from_wildcard = 0, to_wildcard = 0; *msg = "whoops, forgot the error message"; - if (1) { - if (!strcmp(to, "*") || !strcmp(from, "*")) { - *msg = "can't remap from or to *"; - return -1; - } - /* Detect asterisks in expressions of type: '*.example.com' */ - if (!strncmp(from,"*.",2)) { - from += 2; - from_wildcard = 1; - } - if (!strncmp(to,"*.",2)) { - to += 2; - to_wildcard = 1; - } - if (to_wildcard && !from_wildcard) { - *msg = "can only use wildcard (i.e. '*.') if 'from' address " - "uses wildcard also"; - return -1; - } + if (!strcmp(to, "*") || !strcmp(from, "*")) { + *msg = "can't remap from or to *"; + return -1; + } + /* Detect asterisks in expressions of type: '*.example.com' */ + if (!strncmp(from,"*.",2)) { + from += 2; + from_wildcard = 1; + } + if (!strncmp(to,"*.",2)) { + to += 2; + to_wildcard = 1; + } - if (address_is_invalid_destination(to, 1)) { - *msg = "destination is invalid"; - return -1; - } + if (to_wildcard && !from_wildcard) { + *msg = "can only use wildcard (i.e. '*.') if 'from' address " + "uses wildcard also"; + return -1; + } - addressmap_register(from, tor_strdup(to), expires, addrmap_source, - from_wildcard, to_wildcard); + if (address_is_invalid_destination(to, 1)) { + *msg = "destination is invalid"; + return -1; } + + addressmap_register(from, tor_strdup(to), expires, addrmap_source, + from_wildcard, to_wildcard); + return 0; } @@ -7786,7 +7841,7 @@ getinfo_helper_config(control_connection_t *conn, case CONFIG_TYPE_CSV: type = "CommaList"; break; case CONFIG_TYPE_CSV_INTERVAL: type = "TimeIntervalCommaList"; break; case CONFIG_TYPE_LINELIST: type = "LineList"; break; - case CONFIG_TYPE_LINELIST_S: type = "Dependant"; break; + case CONFIG_TYPE_LINELIST_S: type = "Dependent"; break; case CONFIG_TYPE_LINELIST_V: type = "Virtual"; break; default: case CONFIG_TYPE_OBSOLETE: diff --git a/src/or/connection.c b/src/or/connection.c index ac3408a72e..7e0ee4551e 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -34,7 +34,7 @@ * they become able to read or write register the fact with the event main * loop by calling connection_watch_events(), connection_start_reading(), or * connection_start_writing(). When they no longer want to read or write, - * they call connection_stop_reading() or connection_start_writing(). + * they call connection_stop_reading() or connection_stop_writing(). * * To queue data to be written on a connection, call * connection_write_to_buf(). When data arrives, the @@ -56,6 +56,7 @@ #define CONNECTION_PRIVATE #include "or.h" +#include "bridges.h" #include "buffers.h" /* * Define this so we get channel internal functions, since we're implementing @@ -633,6 +634,11 @@ connection_free_(connection_t *conn) cached_dir_decref(dir_conn->cached_dir); rend_data_free(dir_conn->rend_data); + if (dir_conn->guard_state) { + /* Cancel before freeing, if it's still there. */ + entry_guard_cancel(&dir_conn->guard_state); + } + circuit_guard_state_free(dir_conn->guard_state); } if (SOCKET_OK(conn->s)) { diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c index 3874d52c23..82d5d3c5a2 100644 --- a/src/or/connection_edge.c +++ b/src/or/connection_edge.c @@ -329,6 +329,33 @@ relay_send_end_cell_from_edge(streamid_t stream_id, circuit_t *circ, payload, 1, cpath_layer); } +/* If the connection <b>conn</b> is attempting to connect to an external + * destination that is an hidden service and the reason is a connection + * refused or timeout, log it so the operator can take appropriate actions. + * The log statement is a rate limited warning. */ +static void +warn_if_hs_unreachable(const edge_connection_t *conn, uint8_t reason) +{ + tor_assert(conn); + + if (conn->base_.type == CONN_TYPE_EXIT && + connection_edge_is_rendezvous_stream(conn) && + (reason == END_STREAM_REASON_CONNECTREFUSED || + reason == END_STREAM_REASON_TIMEOUT)) { +#define WARN_FAILED_HS_CONNECTION 300 + static ratelim_t warn_limit = RATELIM_INIT(WARN_FAILED_HS_CONNECTION); + char *m; + if ((m = rate_limit_log(&warn_limit, approx_time()))) { + log_warn(LD_EDGE, "Onion service connection to %s failed (%s)", + (conn->base_.socket_family == AF_UNIX) ? + safe_str(conn->base_.address) : + safe_str(fmt_addrport(&conn->base_.addr, conn->base_.port)), + stream_end_reason_to_string(reason)); + tor_free(m); + } + } +} + /** Send a relay end cell from stream <b>conn</b> down conn's circuit, and * remember that we've done so. If this is not a client connection, set the * relay end cell's reason for closing as <b>reason</b>. @@ -386,6 +413,9 @@ connection_edge_end(edge_connection_t *conn, uint8_t reason) conn->base_.s); connection_edge_send_command(conn, RELAY_COMMAND_END, payload, payload_len); + /* We'll log warn if the connection was an hidden service and couldn't be + * made because the service wasn't available. */ + warn_if_hs_unreachable(conn, control_reason); } else { log_debug(LD_EDGE,"No circ to send end on conn " "(fd "TOR_SOCKET_T_FORMAT").", @@ -1300,7 +1330,7 @@ connection_ap_handshake_rewrite(entry_connection_t *conn, * an internal address? If so, we should reject it if we're configured to * do so. */ if (options->ClientDNSRejectInternalAddresses) { - /* Don't let people try to do a reverse lookup on 10.0.0.1. */ + /* Don't let clients try to do a reverse lookup on 10.0.0.1. */ tor_addr_t addr; int ok; ok = tor_addr_parse_PTR_name( @@ -1638,7 +1668,7 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn, return -1; } /* You can't make connections to internal addresses, by default. - * Exceptions are begindir requests (where the address is meaningless, + * Exceptions are begindir requests (where the address is meaningless), * or cases where you've hand-configured a particular exit, thereby * making the local address meaningful. */ if (options->ClientRejectInternalAddresses && @@ -3521,7 +3551,7 @@ connection_exit_connect_dir(edge_connection_t *exitconn) * it is a general stream. */ int -connection_edge_is_rendezvous_stream(edge_connection_t *conn) +connection_edge_is_rendezvous_stream(const edge_connection_t *conn) { tor_assert(conn); if (conn->rend_data) diff --git a/src/or/connection_edge.h b/src/or/connection_edge.h index 5dfc8af901..61b5752aed 100644 --- a/src/or/connection_edge.h +++ b/src/or/connection_edge.h @@ -60,7 +60,7 @@ void connection_ap_handshake_socks_resolved_addr(entry_connection_t *conn, int connection_exit_begin_conn(cell_t *cell, circuit_t *circ); int connection_exit_begin_resolve(cell_t *cell, or_circuit_t *circ); void connection_exit_connect(edge_connection_t *conn); -int connection_edge_is_rendezvous_stream(edge_connection_t *conn); +int connection_edge_is_rendezvous_stream(const edge_connection_t *conn); int connection_ap_can_use_exit(const entry_connection_t *conn, const node_t *exit); void connection_ap_expire_beginning(void); diff --git a/src/or/connection_or.c b/src/or/connection_or.c index 635d3e416a..b3ae291831 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -21,6 +21,7 @@ * This module also implements the client side of the v3 Tor link handshake, **/ #include "or.h" +#include "bridges.h" #include "buffers.h" /* * Define this so we get channel internal functions, since we're implementing @@ -715,8 +716,13 @@ connection_or_about_to_close(or_connection_t *or_conn) const or_options_t *options = get_options(); connection_or_note_state_when_broken(or_conn); rep_hist_note_connect_failed(or_conn->identity_digest, now); + /* Tell the new guard API about the channel failure */ + entry_guard_chan_failed(TLS_CHAN_TO_BASE(or_conn->chan)); +#ifdef ENABLE_LEGACY_GUARD_ALGORITHM + /* Tell the old guard API about the channel failure */ entry_guard_register_connect_status(or_conn->identity_digest,0, !options->HTTPSProxy, now); +#endif if (conn->state >= OR_CONN_STATE_TLS_HANDSHAKING) { int reason = tls_error_to_orconn_end_reason(or_conn->tls_error); control_event_or_conn_status(or_conn, OR_CONN_EVENT_FAILED, @@ -1720,8 +1726,13 @@ connection_or_client_learned_peer_id(or_connection_t *conn, conn->base_.address, conn->base_.port, expected_rsa, expected_ed, seen_rsa, seen_ed, extra_log); + /* Tell the new guard API about the channel failure */ + entry_guard_chan_failed(TLS_CHAN_TO_BASE(conn->chan)); +#ifdef ENABLE_LEGACY_GUARD_ALGORITHM + /* Tell the old guard API about the channel failure */ entry_guard_register_connect_status(conn->identity_digest, 0, 1, time(NULL)); +#endif control_event_or_conn_status(conn, OR_CONN_EVENT_FAILED, END_OR_CONN_REASON_OR_IDENTITY); if (!authdir_mode_tests_reachability(options)) diff --git a/src/or/control.c b/src/or/control.c index a22113174a..364d75c976 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -36,6 +36,7 @@ #include "or.h" #include "addressmap.h" +#include "bridges.h" #include "buffers.h" #include "channel.h" #include "channeltls.h" @@ -70,6 +71,7 @@ #include "router.h" #include "routerlist.h" #include "routerparse.h" +#include "shared_random.h" #ifndef _WIN32 #include <pwd.h> @@ -2595,6 +2597,8 @@ getinfo_helper_events(control_connection_t *control_conn, if (circ->base_.state == CIRCUIT_STATE_OPEN) state = "BUILT"; + else if (circ->base_.state == CIRCUIT_STATE_GUARD_WAIT) + state = "GUARD_WAIT"; // XXXX prop271 spec deviation-- specify this. else if (circ->cpath) state = "EXTENDED"; else @@ -2867,6 +2871,26 @@ getinfo_helper_liveness(control_connection_t *control_conn, return 0; } +/** Implementation helper for GETINFO: answers queries about shared random + * value. */ +static int +getinfo_helper_sr(control_connection_t *control_conn, + const char *question, char **answer, + const char **errmsg) +{ + (void) control_conn; + (void) errmsg; + + if (!strcmp(question, "sr/current")) { + *answer = sr_get_current_for_control(); + } else if (!strcmp(question, "sr/previous")) { + *answer = sr_get_previous_for_control(); + } + /* Else statement here is unrecognized key so do nothing. */ + + return 0; +} + /** Callback function for GETINFO: on a given control connection, try to * answer the question <b>q</b> and store the newly-allocated answer in * *<b>a</b>. If an internal error occurs, return -1 and optionally set @@ -3059,6 +3083,8 @@ static const getinfo_item_t getinfo_items[] = { "Onion services owned by the current control connection."), ITEM("onions/detached", onions, "Onion services detached from the control connection."), + ITEM("sr/current", sr, "Get current shared random value."), + ITEM("sr/previous", sr, "Get previous shared random value."), { NULL, NULL, NULL, 0 } }; @@ -3378,7 +3404,8 @@ handle_control_extendcircuit(control_connection_t *conn, uint32_t len, goto done; } } else { - if (circ->base_.state == CIRCUIT_STATE_OPEN) { + if (circ->base_.state == CIRCUIT_STATE_OPEN || + circ->base_.state == CIRCUIT_STATE_GUARD_WAIT) { int err_reason = 0; circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_BUILDING); if ((err_reason = circuit_send_next_onion_skin(circ)) < 0) { @@ -4037,12 +4064,17 @@ handle_control_dropguards(control_connection_t *conn, smartlist_split_string(args, body, " ", SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); +#ifdef ENABLE_LEGACY_GUARD_ALGORITHM if (smartlist_len(args)) { connection_printf_to_buf(conn, "512 Too many arguments to DROPGUARDS\r\n"); } else { remove_all_entry_guards(); send_control_done(conn); } +#else + // XXXX + connection_printf_to_buf(conn, "512 not supported\r\n"); +#endif SMARTLIST_FOREACH(args, char *, cp, tor_free(cp)); smartlist_free(args); diff --git a/src/or/directory.c b/src/or/directory.c index f0affbd395..9e41394673 100644 --- a/src/or/directory.c +++ b/src/or/directory.c @@ -7,6 +7,7 @@ #include "or.h" #include "backtrace.h" +#include "bridges.h" #include "buffers.h" #include "circuitbuild.h" #include "config.h" @@ -127,7 +128,8 @@ static void directory_initiate_command_rend( const char *payload, size_t payload_len, time_t if_modified_since, - const rend_data_t *rend_query); + const rend_data_t *rend_query, + circuit_guard_state_t *guard_state); static void connection_dir_close_consensus_fetches( dir_connection_t *except_this_one, const char *resource); @@ -421,7 +423,8 @@ directory_post_to_dirservers(uint8_t dir_purpose, uint8_t router_purpose, directory_initiate_command_routerstatus(rs, dir_purpose, router_purpose, indirection, - NULL, payload, upload_len, 0); + NULL, payload, upload_len, 0, + NULL); } SMARTLIST_FOREACH_END(ds); if (!found) { char *s = authdir_type_to_string(type); @@ -439,10 +442,9 @@ should_use_directory_guards(const or_options_t *options) /* Public (non-bridge) servers never use directory guards. */ if (public_server_mode(options)) return 0; - /* If guards are disabled, or directory guards are disabled, we can't - * use directory guards. + /* If guards are disabled, we can't use directory guards. */ - if (!options->UseEntryGuards || !options->UseEntryGuardsAsDirGuards) + if (!options->UseEntryGuards) return 0; /* If we're configured to fetch directory info aggressively or of a * nonstandard type, don't use directory guards. */ @@ -457,7 +459,8 @@ should_use_directory_guards(const or_options_t *options) * information of type <b>type</b>, and return its routerstatus. */ static const routerstatus_t * directory_pick_generic_dirserver(dirinfo_type_t type, int pds_flags, - uint8_t dir_purpose) + uint8_t dir_purpose, + circuit_guard_state_t **guard_state_out) { const routerstatus_t *rs = NULL; const or_options_t *options = get_options(); @@ -466,7 +469,7 @@ directory_pick_generic_dirserver(dirinfo_type_t type, int pds_flags, log_warn(LD_BUG, "Called when we have UseBridges set."); if (should_use_directory_guards(options)) { - const node_t *node = choose_random_dirguard(type); + const node_t *node = guards_choose_dirguard(type, guard_state_out); if (node) rs = node->rs; } else { @@ -547,6 +550,7 @@ MOCK_IMPL(void, directory_get_from_dirserver, ( if (!options->FetchServerDescriptors) return; + circuit_guard_state_t *guard_state = NULL; if (!get_via_tor) { if (options->UseBridges && !(type & BRIDGE_DIRINFO)) { /* We want to ask a running bridge for which we have a descriptor. @@ -555,25 +559,35 @@ MOCK_IMPL(void, directory_get_from_dirserver, ( * sort of dir fetch we'll be doing, so it won't return a bridge * that can't answer our question. */ - const node_t *node = choose_random_dirguard(type); + const node_t *node = guards_choose_dirguard(type, + &guard_state); if (node && node->ri) { /* every bridge has a routerinfo. */ routerinfo_t *ri = node->ri; /* clients always make OR connections to bridges */ tor_addr_port_t or_ap; + tor_addr_port_t nil_dir_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, - DIRIND_ONEHOP, - resource, NULL, 0, if_modified_since); - } else + tor_addr_make_null(&nil_dir_ap.addr, AF_INET); + nil_dir_ap.port = 0; + directory_initiate_command_rend(&or_ap, + &nil_dir_ap, + ri->cache_info.identity_digest, + dir_purpose, + router_purpose, + DIRIND_ONEHOP, + resource, NULL, 0, if_modified_since, + NULL, guard_state); + } else { + if (guard_state) { + entry_guard_cancel(&guard_state); + } log_notice(LD_DIR, "Ignoring directory request, since no bridge " "nodes are available yet."); + } + return; } else { if (prefer_authority || (type & BRIDGE_DIRINFO)) { @@ -604,9 +618,9 @@ MOCK_IMPL(void, directory_get_from_dirserver, ( } } if (!rs && !(type & BRIDGE_DIRINFO)) { - /* */ rs = directory_pick_generic_dirserver(type, pds_flags, - dir_purpose); + dir_purpose, + &guard_state); if (!rs) get_via_tor = 1; /* last resort: try routing it via Tor */ } @@ -629,7 +643,8 @@ MOCK_IMPL(void, directory_get_from_dirserver, ( router_purpose, indirection, resource, NULL, 0, - if_modified_since); + if_modified_since, + guard_state); } else { log_notice(LD_DIR, "While fetching directory info, " @@ -663,7 +678,7 @@ directory_get_from_all_authorities(uint8_t dir_purpose, rs = &ds->fake_status; directory_initiate_command_routerstatus(rs, dir_purpose, router_purpose, DIRIND_ONEHOP, resource, NULL, - 0, 0); + 0, 0, NULL); } SMARTLIST_FOREACH_END(ds); } @@ -774,7 +789,8 @@ directory_initiate_command_routerstatus_rend(const routerstatus_t *status, const char *payload, size_t payload_len, time_t if_modified_since, - const rend_data_t *rend_query) + const rend_data_t *rend_query, + circuit_guard_state_t *guard_state) { const or_options_t *options = get_options(); const node_t *node; @@ -829,7 +845,8 @@ directory_initiate_command_routerstatus_rend(const routerstatus_t *status, dir_purpose, router_purpose, indirection, resource, payload, payload_len, if_modified_since, - rend_query); + rend_query, + guard_state); } /** Launch a new connection to the directory server <b>status</b> to @@ -854,13 +871,15 @@ MOCK_IMPL(void, directory_initiate_command_routerstatus, const char *resource, const char *payload, size_t payload_len, - time_t if_modified_since)) + time_t if_modified_since, + circuit_guard_state_t *guard_state)) { directory_initiate_command_routerstatus_rend(status, dir_purpose, router_purpose, indirection, resource, payload, payload_len, - if_modified_since, NULL); + if_modified_since, NULL, + guard_state); } /** Return true iff <b>conn</b> is the client side of a directory connection @@ -888,6 +907,11 @@ directory_conn_is_self_reachability_test(dir_connection_t *conn) static void connection_dir_request_failed(dir_connection_t *conn) { + if (conn->guard_state) { + /* We haven't seen a success on this guard state, so consider it to have + * failed. */ + entry_guard_failed(&conn->guard_state); + } if (directory_conn_is_self_reachability_test(conn)) { return; /* this was a test fetch. don't retry. */ } @@ -1135,7 +1159,7 @@ directory_initiate_command(const tor_addr_t *or_addr, uint16_t or_port, digest, dir_purpose, router_purpose, indirection, resource, payload, payload_len, - if_modified_since, NULL); + if_modified_since, NULL, NULL); } /** Same as directory_initiate_command(), but accepts rendezvous data to @@ -1150,7 +1174,8 @@ directory_initiate_command_rend(const tor_addr_port_t *or_addr_port, const char *resource, const char *payload, size_t payload_len, time_t if_modified_since, - const rend_data_t *rend_query) + const rend_data_t *rend_query, + circuit_guard_state_t *guard_state) { tor_assert(or_addr_port); tor_assert(dir_addr_port); @@ -1245,12 +1270,18 @@ directory_initiate_command_rend(const tor_addr_port_t *or_addr_port, if (!anonymized_connection && !use_begindir) { /* then we want to connect to dirport directly */ + // XXXX prop271 I think that we never use guards in this case. if (options->HTTPProxy) { tor_addr_copy(&addr, &options->HTTPProxyAddr); port = options->HTTPProxyPort; } + // In this case we should not have picked a directory guard. + if (BUG(guard_state)) { + entry_guard_cancel(&guard_state); + } + switch (connection_connect(TO_CONN(conn), conn->base_.address, &addr, port, &socket_error)) { case -1: @@ -1287,6 +1318,14 @@ directory_initiate_command_rend(const tor_addr_port_t *or_addr_port, else if (anonymized_connection && !use_begindir) rep_hist_note_used_port(time(NULL), conn->base_.port); + // In this case we should not have a directory guard; we'll + // get a regular guard later when we build the circuit. + if (BUG(anonymized_connection && guard_state)) { + entry_guard_cancel(&guard_state); + } + + conn->guard_state = guard_state; + /* make an AP connection * populate it and add it at the right state * hook up both sides @@ -1818,15 +1857,15 @@ body_is_plausible(const char *body, size_t len, int purpose) if (purpose == DIR_PURPOSE_FETCH_MICRODESC) { return (!strcmpstart(body,"onion-key")); } - if (1) { - if (!strcmpstart(body,"router") || - !strcmpstart(body,"network-status")) - return 1; - for (i=0;i<32;++i) { - if (!TOR_ISPRINT(body[i]) && !TOR_ISSPACE(body[i])) - return 0; - } + + if (!strcmpstart(body,"router") || + !strcmpstart(body,"network-status")) + return 1; + for (i=0;i<32;++i) { + if (!TOR_ISPRINT(body[i]) && !TOR_ISSPACE(body[i])) + return 0; } + return 1; } @@ -1928,6 +1967,21 @@ connection_dir_client_reached_eof(dir_connection_t *conn) escaped(reason), conn->base_.purpose); + if (conn->guard_state) { + /* we count the connection as successful once we can read from it. We do + * not, however, delay use of the circuit here, since it's just for a + * one-hop directory request. */ + /* XXXXprop271 note that this will not do the right thing for other + * waiting circuits that would be triggered by this circuit becoming + * complete/usable. But that's ok, I think. + */ + /* XXXXprop271 should we count this as only a partial success somehow? + */ + entry_guard_succeeded(&conn->guard_state); + circuit_guard_state_free(conn->guard_state); + conn->guard_state = NULL; + } + /* now check if it's got any hints for us about our IP address. */ if (conn->dirconn_direct) { char *guess = http_get_header(headers, X_ADDRESS_HEADER); @@ -2939,6 +2993,28 @@ handle_get_frontpage(dir_connection_t *conn, const get_handler_args_t *args) return 0; } +/** Warn that the consensus <b>v</b> of type <b>flavor</b> is too old and will + * not be served to clients. Rate-limit the warning to avoid logging an entry + * on every request. + */ +static void +warn_consensus_is_too_old(networkstatus_t *v, const char *flavor, time_t now) +{ +#define TOO_OLD_WARNING_INTERVAL (60*60) + static ratelim_t warned = RATELIM_INIT(TOO_OLD_WARNING_INTERVAL); + char timestamp[ISO_TIME_LEN+1]; + char *dupes; + + if ((dupes = rate_limit_log(&warned, now))) { + format_local_iso_time(timestamp, v->valid_until); + log_warn(LD_DIRSERV, "Our %s%sconsensus is too old, so we will not " + "serve it to clients. It was valid until %s local time and we " + "continued to serve it for up to 24 hours after it expired.%s", + flavor ? flavor : "", flavor ? " " : "", timestamp, dupes); + tor_free(dupes); + } +} + /** Helper function for GET /tor/status-vote/current/consensus */ static int @@ -2954,55 +3030,62 @@ handle_get_current_consensus(dir_connection_t *conn, smartlist_t *dir_fps = smartlist_new(); long lifetime = NETWORKSTATUS_CACHE_LIFETIME; - if (1) { - networkstatus_t *v; - time_t now = time(NULL); - const char *want_fps = NULL; - char *flavor = NULL; - int flav = FLAV_NS; - #define CONSENSUS_URL_PREFIX "/tor/status-vote/current/consensus/" - #define CONSENSUS_FLAVORED_PREFIX "/tor/status-vote/current/consensus-" - /* figure out the flavor if any, and who we wanted to sign the thing */ - if (!strcmpstart(url, CONSENSUS_FLAVORED_PREFIX)) { - const char *f, *cp; - f = url + strlen(CONSENSUS_FLAVORED_PREFIX); - cp = strchr(f, '/'); - if (cp) { - want_fps = cp+1; - flavor = tor_strndup(f, cp-f); - } else { - flavor = tor_strdup(f); - } - flav = networkstatus_parse_flavor_name(flavor); - if (flav < 0) - flav = FLAV_NS; + networkstatus_t *v; + time_t now = time(NULL); + const char *want_fps = NULL; + char *flavor = NULL; + int flav = FLAV_NS; +#define CONSENSUS_URL_PREFIX "/tor/status-vote/current/consensus/" +#define CONSENSUS_FLAVORED_PREFIX "/tor/status-vote/current/consensus-" + /* figure out the flavor if any, and who we wanted to sign the thing */ + if (!strcmpstart(url, CONSENSUS_FLAVORED_PREFIX)) { + const char *f, *cp; + f = url + strlen(CONSENSUS_FLAVORED_PREFIX); + cp = strchr(f, '/'); + if (cp) { + want_fps = cp+1; + flavor = tor_strndup(f, cp-f); } else { - if (!strcmpstart(url, CONSENSUS_URL_PREFIX)) - want_fps = url+strlen(CONSENSUS_URL_PREFIX); + flavor = tor_strdup(f); } + flav = networkstatus_parse_flavor_name(flavor); + if (flav < 0) + flav = FLAV_NS; + } else { + if (!strcmpstart(url, CONSENSUS_URL_PREFIX)) + want_fps = url+strlen(CONSENSUS_URL_PREFIX); + } - v = networkstatus_get_latest_consensus_by_flavor(flav); + v = networkstatus_get_latest_consensus_by_flavor(flav); - if (v && want_fps && - !client_likes_consensus(v, want_fps)) { - write_http_status_line(conn, 404, "Consensus not signed by sufficient " - "number of requested authorities"); - smartlist_free(dir_fps); - geoip_note_ns_response(GEOIP_REJECT_NOT_ENOUGH_SIGS); - tor_free(flavor); - goto done; - } + if (v && !networkstatus_consensus_reasonably_live(v, now)) { + write_http_status_line(conn, 404, "Consensus is too old"); + warn_consensus_is_too_old(v, flavor, now); + smartlist_free(dir_fps); + geoip_note_ns_response(GEOIP_REJECT_NOT_FOUND); + tor_free(flavor); + goto done; + } - { - char *fp = tor_malloc_zero(DIGEST_LEN); - if (flavor) - strlcpy(fp, flavor, DIGEST_LEN); - tor_free(flavor); - smartlist_add(dir_fps, fp); - } - lifetime = (v && v->fresh_until > now) ? v->fresh_until - now : 0; + if (v && want_fps && + !client_likes_consensus(v, want_fps)) { + write_http_status_line(conn, 404, "Consensus not signed by sufficient " + "number of requested authorities"); + smartlist_free(dir_fps); + geoip_note_ns_response(GEOIP_REJECT_NOT_ENOUGH_SIGS); + tor_free(flavor); + goto done; } + { + char *fp = tor_malloc_zero(DIGEST_LEN); + if (flavor) + strlcpy(fp, flavor, DIGEST_LEN); + tor_free(flavor); + smartlist_add(dir_fps, fp); + } + lifetime = (v && v->fresh_until > now) ? v->fresh_until - now : 0; + if (!smartlist_len(dir_fps)) { /* we failed to create/cache cp */ write_http_status_line(conn, 503, "Network status object unavailable"); smartlist_free(dir_fps); @@ -3037,21 +3120,19 @@ handle_get_current_consensus(dir_connection_t *conn, goto done; } - if (1) { - tor_addr_t addr; - if (tor_addr_parse(&addr, (TO_CONN(conn))->address) >= 0) { - geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS, - &addr, NULL, - time(NULL)); - geoip_note_ns_response(GEOIP_SUCCESS); - /* Note that a request for a network status has started, so that we - * can measure the download time later on. */ - if (conn->dirreq_id) - geoip_start_dirreq(conn->dirreq_id, dlen, DIRREQ_TUNNELED); - else - geoip_start_dirreq(TO_CONN(conn)->global_identifier, dlen, - DIRREQ_DIRECT); - } + tor_addr_t addr; + if (tor_addr_parse(&addr, (TO_CONN(conn))->address) >= 0) { + geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS, + &addr, NULL, + time(NULL)); + geoip_note_ns_response(GEOIP_SUCCESS); + /* Note that a request for a network status has started, so that we + * can measure the download time later on. */ + if (conn->dirreq_id) + geoip_start_dirreq(conn->dirreq_id, dlen, DIRREQ_TUNNELED); + else + geoip_start_dirreq(TO_CONN(conn)->global_identifier, dlen, + DIRREQ_DIRECT); } write_http_response_header(conn, -1, compressed, @@ -3443,13 +3524,6 @@ handle_get_hs_descriptor_v3(dir_connection_t *conn, const char *pubkey_str = NULL; const char *url = args->url; - /* Don't serve v3 descriptors if next gen onion service is disabled. */ - if (!hs_v3_protocol_is_enabled()) { - /* 404 is used for an unrecognized URL so send back the same. */ - write_http_status_line(conn, 404, "Not found"); - goto done; - } - /* Reject unencrypted dir connections */ if (!connection_dir_is_encrypted(conn)) { write_http_status_line(conn, 404, "Not found"); @@ -3664,13 +3738,6 @@ directory_handle_command_post(dir_connection_t *conn, const char *headers, * the prop224 be deployed and thus use. */ if (connection_dir_is_encrypted(conn) && !strcmpstart(url, "/tor/hs/")) { const char *msg = "HS descriptor stored successfully."; - /* Don't accept v3 and onward publish request if next gen onion service is - * disabled. */ - if (!hs_v3_protocol_is_enabled()) { - /* 404 is used for an unrecognized URL so send back the same. */ - write_http_status_line(conn, 404, "Not found"); - goto done; - } /* We most probably have a publish request for an HS descriptor. */ int code = handle_post_hs_descriptor(url, body); diff --git a/src/or/directory.h b/src/or/directory.h index 589df7b70d..ee0a198c52 100644 --- a/src/or/directory.h +++ b/src/or/directory.h @@ -49,7 +49,8 @@ MOCK_DECL(void, directory_initiate_command_routerstatus, const char *resource, const char *payload, size_t payload_len, - time_t if_modified_since)); + time_t if_modified_since, + struct circuit_guard_state_t *guard_state)); void directory_initiate_command_routerstatus_rend(const routerstatus_t *status, uint8_t dir_purpose, @@ -59,7 +60,8 @@ void directory_initiate_command_routerstatus_rend(const routerstatus_t *status, const char *payload, size_t payload_len, time_t if_modified_since, - const rend_data_t *rend_query); + const rend_data_t *rend_query, + struct circuit_guard_state_t *guard_state); int parse_http_response(const char *headers, int *code, time_t *date, compress_method_t *compression, char **response); diff --git a/src/or/dirserv.c b/src/or/dirserv.c index 399d5ea955..71dcbb6a9c 100644 --- a/src/or/dirserv.c +++ b/src/or/dirserv.c @@ -1102,7 +1102,7 @@ directory_caches_dir_info(const or_options_t *options) should_refuse_unknown_exits(options); } -/** Return 1 if we want to allow remote people to ask us directory +/** Return 1 if we want to allow remote clients to ask us directory * requests via the "begin_dir" interface, which doesn't require * having any separate port open. */ int @@ -2056,12 +2056,8 @@ get_possible_sybil_list(const smartlist_t *routers) int addr_count; /* Allow at most this number of Tor servers on a single IP address, ... */ int max_with_same_addr = options->AuthDirMaxServersPerAddr; - /* ... unless it's a directory authority, in which case allow more. */ - int max_with_same_addr_on_authority = options->AuthDirMaxServersPerAuthAddr; if (max_with_same_addr <= 0) max_with_same_addr = INT_MAX; - if (max_with_same_addr_on_authority <= 0) - max_with_same_addr_on_authority = INT_MAX; smartlist_add_all(routers_by_ip, routers); smartlist_sort(routers_by_ip, compare_routerinfo_by_ip_and_bw_); @@ -2074,9 +2070,7 @@ get_possible_sybil_list(const smartlist_t *routers) last_addr = ri->addr; addr_count = 1; } else if (++addr_count > max_with_same_addr) { - if (!router_addr_is_trusted_dir(ri->addr) || - addr_count > max_with_same_addr_on_authority) - digestmap_set(omit_as_sybil, ri->cache_info.identity_digest, ri); + digestmap_set(omit_as_sybil, ri->cache_info.identity_digest, ri); } } SMARTLIST_FOREACH_END(ri); @@ -2236,8 +2230,8 @@ dirserv_set_routerstatus_testing(routerstatus_t *rs) } /** Routerstatus <b>rs</b> is part of a group of routers that are on - * too narrow an IP-space. Clear out its flags: we don't want people - * using it. + * too narrow an IP-space. Clear out its flags since we don't want it be used + * because of its Sybil-like appearance. */ static void clear_status_flags_on_sybil(routerstatus_t *rs) @@ -3215,7 +3209,7 @@ dirserv_orconn_tls_done(const tor_addr_t *addr, if (!authdir_mode_bridge(get_options()) || ri->purpose == ROUTER_PURPOSE_BRIDGE) { char addrstr[TOR_ADDR_BUF_LEN]; - /* This is a bridge or we're not a bridge authorititative -- + /* This is a bridge or we're not a bridge authority -- mark it as reachable. */ log_info(LD_DIRSERV, "Found router %s to be reachable at %s:%d. Yay.", router_describe(ri), diff --git a/src/or/dnsserv.c b/src/or/dnsserv.c index c5c0a88b09..8768b2a1d1 100644 --- a/src/or/dnsserv.c +++ b/src/or/dnsserv.c @@ -284,7 +284,7 @@ dnsserv_reject_request(entry_connection_t *conn) } /** Look up the original name that corresponds to 'addr' in req. We use this - * to preserve case in order to facilitate people using 0x20-hacks to avoid + * to preserve case in order to facilitate clients using 0x20-hacks to avoid * DNS poisoning. */ static const char * evdns_get_orig_address(const struct evdns_server_request *req, diff --git a/src/or/entrynodes.c b/src/or/entrynodes.c index af1b1a39ab..ca3e57b064 100644 --- a/src/or/entrynodes.c +++ b/src/or/entrynodes.c @@ -10,14 +10,119 @@ * * Entry nodes can be guards (for general use) or bridges (for censorship * circumvention). + * + * XXXX prop271 This module is in flux, since I'm currently in the middle of + * implementation proposal 271. The module documentation here will describe + * the new algorithm and data structures; the old ones should get removed as + * proposal 271 is completed. + * + * In general, we use entry guards to prevent traffic-sampling attacks: + * if we chose every circuit independently, an adversary controlling + * some fraction of paths on the network would observe a sample of every + * user's traffic. Using guards gives users a chance of not being + * profiled. + * + * The current entry guard selection code is designed to try to avoid + * _ever_ trying every guard on the network, to try to stick to guards + * that we've used before, to handle hostile/broken networks, and + * to behave sanely when the network goes up and down. + * + * Our algorithm works as follows: First, we maintain a SAMPLE of guards + * we've seen in the networkstatus consensus. We maintain this sample + * over time, and store it persistently; it is chosen without reference + * to our configuration or firewall rules. Guards remain in the sample + * as they enter and leave the consensus. We expand this sample as + * needed, up to a maximum size. + * + * As a subset of the sample, we maintain a FILTERED SET of the guards + * that we would be willing to use if we could connect to them. The + * filter removes all the guards that we're excluding because they're + * bridges (or not bridges), because we have restrictive firewall rules, + * because of ExcludeNodes, because we of path bias restrictions, + * because they're absent from the network at present, and so on. + * + * As a subset of the filtered set, we keep a REACHABLE FILTERED SET + * (also called a "usable filtered set") of those guards that we call + * "reachable" or "maybe reachable". A guard is reachable if we've + * connected to it more recently than we've failed. A guard is "maybe + * reachable" if we have never tried to connect to it, or if we + * failed to connect to it so long ago that we no longer think our + * failure means it's down. + * + * As a persistent ordered list whose elements are taken from the + * sampled set, we track a CONFIRMED GUARDS LIST. A guard becomes + * confirmed when we successfully build a circuit through it, and decide + * to use that circuit. We order the guards on this list by the order + * in which they became confirmed. + * + * And as a final group, we have an ordered list of PRIMARY GUARDS, + * whose elements are taken from the filtered set. We prefer + * confirmed guards to non-confirmed guards for this list, and place + * other restrictions on it. The primary guards are the ones that we + * connect to "when nothing is wrong" -- circuits through them can be used + * immediately. + * + * To build circuits, we take a primary guard if possible -- or a + * reachable filtered confirmed guard if no primary guard is possible -- + * or a random reachable filtered guard otherwise. If the guard is + * primary, we can use the circuit immediately on success. Otherwise, + * the guard is now "pending" -- we won't use its circuit unless all + * of the circuits we're trying to build through better guards have + * definitely failed. + * + * While we're building circuits, we track a little "guard state" for + * each circuit. We use this to keep track of whether the circuit is + * one that we can use as soon as its done, or whether it's one that + * we should keep around to see if we can do better. In the latter case, + * a periodic call to entry_guards_upgrade_waiting_circuits() will + * eventually upgrade it. **/ +/* DOCDOC -- expand this. + * + * Information invariants: + * + * [x] whenever a guard becomes unreachable, clear its usable_filtered flag. + * + * [x] Whenever a guard becomes reachable or maybe-reachable, if its filtered + * flag is set, set its usable_filtered flag. + * + * [x] Whenever we get a new consensus, call update_from_consensus(). (LATER.) + * + * [x] Whenever the configuration changes in a relevant way, update the + * filtered/usable flags. (LATER.) + * + * [x] Whenever we add a guard to the sample, make sure its filtered/usable + * flags are set as possible. + * + * [x] Whenever we remove a guard from the sample, remove it from the primary + * and confirmed lists. + * + * [x] When we make a guard confirmed, update the primary list. + * + * [x] When we make a guard filtered or unfiltered, update the primary list. + * + * [x] When we are about to pick a guard, make sure that the primary list is + * full. + * + * [x] Before calling sample_reachable_filtered_entry_guards(), make sure + * that the filtered, primary, and confirmed flags are up-to-date. + * + * [x] Call entry_guard_consider_retry every time we are about to check + * is_usable_filtered or is_reachable, and every time we set + * is_filtered to 1. + * + * [x] Call entry_guards_changed_for_guard_selection() whenever we update + * a persistent field. + */ #define ENTRYNODES_PRIVATE #include "or.h" #include "channel.h" +#include "bridges.h" #include "circpathbias.h" #include "circuitbuild.h" +#include "circuitlist.h" #include "circuitstats.h" #include "config.h" #include "confparse.h" @@ -37,107 +142,169 @@ #include "transports.h" #include "statefile.h" -/** Information about a configured bridge. Currently this just matches the - * ones in the torrc file, but one day we may be able to learn about new - * bridges on our own, and remember them in the state file. */ -typedef struct { - /** Address of the bridge. */ - tor_addr_t addr; - /** TLS port for the bridge. */ - uint16_t port; - /** Boolean: We are re-parsing our bridge list, and we are going to remove - * this one if we don't find it in the list of configured bridges. */ - unsigned marked_for_removal : 1; - /** Expected identity digest, or all zero bytes if we don't know what the - * digest should be. */ - char identity[DIGEST_LEN]; - - /** Name of pluggable transport protocol taken from its config line. */ - char *transport_name; - - /** When should we next try to fetch a descriptor for this bridge? */ - download_status_t fetch_status; - - /** A smartlist of k=v values to be passed to the SOCKS proxy, if - transports are used for this bridge. */ - smartlist_t *socks_args; -} bridge_info_t; - -/** All the context for guard selection on a particular client */ - -struct guard_selection_s { - /** - * A value of 1 means that guard_selection_t structures have changed - * and those changes need to be flushed to disk. - * - * XXX we don't know how to flush multiple guard contexts to disk yet; - * fix that as soon as any way to change the default exists, or at least - * make sure this gets set on change. - */ - int dirty; - - /** - * A list of our chosen entry guards, as entry_guard_t structures; this - * preserves the pre-Prop271 behavior. - */ - smartlist_t *chosen_entry_guards; - - /** - * When we try to choose an entry guard, should we parse and add - * config's EntryNodes first? This was formerly a global. - */ - int should_add_entry_nodes; -}; - +/** A list of existing guard selection contexts. */ static smartlist_t *guard_contexts = NULL; +/** The currently enabled guard selection context. */ static guard_selection_t *curr_guard_context = NULL; -static void bridge_free(bridge_info_t *bridge); +/** A value of 1 means that at least one context has changed, + * and those changes need to be flushed to disk. */ +static int entry_guards_dirty = 0; + +#ifdef ENABLE_LEGACY_GUARD_ALGORITHM static const node_t *choose_random_entry_impl(guard_selection_t *gs, cpath_build_state_t *state, int for_directory, dirinfo_type_t dirtype, int *n_options_out); -static guard_selection_t * guard_selection_new(void); -static int num_bridges_usable(void); +#endif +static void entry_guard_set_filtered_flags(const or_options_t *options, + guard_selection_t *gs, + entry_guard_t *guard); +static void pathbias_check_use_success_count(entry_guard_t *guard); +static void pathbias_check_close_success_count(entry_guard_t *guard); +static int node_is_possible_guard(const node_t *node); +static int node_passes_guard_filter(const or_options_t *options, + const node_t *node); +static entry_guard_t *entry_guard_add_to_sample_impl(guard_selection_t *gs, + const uint8_t *rsa_id_digest, + const char *nickname, + const tor_addr_port_t *bridge_addrport); +static entry_guard_t *get_sampled_guard_by_bridge_addr(guard_selection_t *gs, + const tor_addr_port_t *addrport); +static int entry_guard_obeys_restriction(const entry_guard_t *guard, + const entry_guard_restriction_t *rst); -/* Default number of entry guards in the case where the NumEntryGuards - * consensus parameter is not set */ -#define DEFAULT_N_GUARDS 1 -/* Minimum and maximum number of entry guards (in case the NumEntryGuards - * consensus parameter is set). */ -#define MIN_N_GUARDS 1 -#define MAX_N_GUARDS 10 +/** Return 0 if we should apply guardfraction information found in the + * consensus. A specific consensus can be specified with the + * <b>ns</b> argument, if NULL the most recent one will be picked.*/ +int +should_apply_guardfraction(const networkstatus_t *ns) +{ + /* We need to check the corresponding torrc option and the consensus + * parameter if we need to. */ + const or_options_t *options = get_options(); -/** Allocate a new guard_selection_t */ + /* If UseGuardFraction is 'auto' then check the same-named consensus + * parameter. If the consensus parameter is not present, default to + * "off". */ + if (options->UseGuardFraction == -1) { + return networkstatus_get_param(ns, "UseGuardFraction", + 0, /* default to "off" */ + 0, 1); + } -static guard_selection_t * -guard_selection_new(void) + return options->UseGuardFraction; +} + +/** + * Try to determine the correct type for a selection named "name", + * if <b>type</b> is GS_TYPE_INFER. + */ +STATIC guard_selection_type_t +guard_selection_infer_type(guard_selection_type_t type, + const char *name) +{ + if (type == GS_TYPE_INFER) { + if (!strcmp(name, "legacy")) + type = GS_TYPE_LEGACY; + else if (!strcmp(name, "bridges")) + type = GS_TYPE_BRIDGE; + else if (!strcmp(name, "restricted")) + type = GS_TYPE_RESTRICTED; + else + type = GS_TYPE_NORMAL; + } + return type; +} + +/** + * Allocate and return a new guard_selection_t, with the name <b>name</b>. + */ +STATIC guard_selection_t * +guard_selection_new(const char *name, + guard_selection_type_t type) { guard_selection_t *gs; + type = guard_selection_infer_type(type, name); + gs = tor_malloc_zero(sizeof(*gs)); + gs->name = tor_strdup(name); + gs->type = type; +#ifdef ENABLE_LEGACY_GUARD_ALGORITHM gs->chosen_entry_guards = smartlist_new(); +#endif + gs->sampled_entry_guards = smartlist_new(); + gs->confirmed_entry_guards = smartlist_new(); + gs->primary_entry_guards = smartlist_new(); return gs; } -/** Get current default guard_selection_t, creating it if necessary */ -guard_selection_t * -get_guard_selection_info(void) +/** + * Return the guard selection called <b>name</b>. If there is none, and + * <b>create_if_absent</b> is true, then create and return it. If there + * is none, and <b>create_if_absent</b> is false, then return NULL. + */ +STATIC guard_selection_t * +get_guard_selection_by_name(const char *name, + guard_selection_type_t type, + int create_if_absent) +{ + if (!guard_contexts) { + guard_contexts = smartlist_new(); + } + SMARTLIST_FOREACH_BEGIN(guard_contexts, guard_selection_t *, gs) { + if (!strcmp(gs->name, name)) + return gs; + } SMARTLIST_FOREACH_END(gs); + + if (! create_if_absent) + return NULL; + + log_debug(LD_GUARD, "Creating a guard selection called %s", name); + guard_selection_t *new_selection = guard_selection_new(name, type); + smartlist_add(guard_contexts, new_selection); + + return new_selection; +} + +/** + * Allocate the first guard context that we're planning to use, + * and make it the current context. + */ +static void +create_initial_guard_context(void) { + tor_assert(! curr_guard_context); if (!guard_contexts) { guard_contexts = smartlist_new(); } + guard_selection_type_t type = GS_TYPE_INFER; + const char *name = choose_guard_selection( + get_options(), + networkstatus_get_live_consensus(approx_time()), + NULL, + &type); + tor_assert(name); // "name" can only be NULL if we had an old name. + tor_assert(type != GS_TYPE_INFER); + log_notice(LD_GUARD, "Starting with guard context \"%s\"", name); + curr_guard_context = get_guard_selection_by_name(name, type, 1); +} +/** Get current default guard_selection_t, creating it if necessary */ +guard_selection_t * +get_guard_selection_info(void) +{ if (!curr_guard_context) { - curr_guard_context = guard_selection_new(); - smartlist_add(guard_contexts, curr_guard_context); + create_initial_guard_context(); } return curr_guard_context; } +#ifdef ENABLE_LEGACY_GUARD_ALGORITHM /** Return the list of entry guards for a guard_selection_t, creating it * if necessary. */ const smartlist_t * @@ -157,6 +324,2661 @@ get_entry_guards(void) return get_entry_guards_for_guard_selection(get_guard_selection_info()); } +/** Helper: mark an entry guard as not usable. */ +void +entry_guard_mark_bad(entry_guard_t *guard) +{ + guard->bad_since = approx_time(); + entry_guards_changed(); +} +#endif + +/** Return a statically allocated human-readable description of <b>guard</b> + */ +const char * +entry_guard_describe(const entry_guard_t *guard) +{ + static char buf[256]; + tor_snprintf(buf, sizeof(buf), + "%s ($%s)", + strlen(guard->nickname) ? guard->nickname : "[bridge]", + hex_str(guard->identity, DIGEST_LEN)); + return buf; +} + +/** Return <b>guard</b>'s 20-byte RSA identity digest */ +const char * +entry_guard_get_rsa_id_digest(const entry_guard_t *guard) +{ + return guard->identity; +} + +/** Return the pathbias state associated with <b>guard</b>. */ +guard_pathbias_t * +entry_guard_get_pathbias_state(entry_guard_t *guard) +{ + return &guard->pb; +} + +HANDLE_IMPL(entry_guard, entry_guard_t, ATTR_UNUSED STATIC) + +/** Return an interval betweeen 'now' and 'max_backdate' seconds in the past, + * chosen uniformly at random. We use this before recording persistent + * dates, so that we aren't leaking exactly when we recorded it. + */ +MOCK_IMPL(STATIC time_t, +randomize_time,(time_t now, time_t max_backdate)) +{ + tor_assert(max_backdate > 0); + + time_t earliest = now - max_backdate; + time_t latest = now; + if (earliest <= 0) + earliest = 1; + if (latest <= earliest) + latest = earliest + 1; + + return crypto_rand_time_range(earliest, latest); +} + +/** + * @name parameters for networkstatus algorithm + * + * These parameters are taken from the consensus; some are overrideable in + * the torrc. + */ +/**@{*/ +/** + * We never let our sampled guard set grow larger than this fraction + * of the guards on the network. + */ +STATIC double +get_max_sample_threshold(void) +{ + int32_t pct = + networkstatus_get_param(NULL, "guard-max-sample-threshold-percent", + DFLT_MAX_SAMPLE_THRESHOLD_PERCENT, + 1, 100); + return pct / 100.0; +} +/** + * We never let our sampled guard set grow larger than this number. + */ +STATIC int +get_max_sample_size_absolute(void) +{ + return (int) networkstatus_get_param(NULL, "guard-max-sample-size", + DFLT_MAX_SAMPLE_SIZE, + 1, INT32_MAX); +} +/** + * We always try to make our sample contain at least this many guards. + * + * XXXX prop271 spec deviation There was a MIN_SAMPLE_THRESHOLD in the + * proposal, but I removed it in favor of MIN_FILTERED_SAMPLE_SIZE. -NM + */ +STATIC int +get_min_filtered_sample_size(void) +{ + return networkstatus_get_param(NULL, "guard-min-filtered-sample-size", + DFLT_MIN_FILTERED_SAMPLE_SIZE, + 1, INT32_MAX); +} +/** + * If a guard is unlisted for this many days in a row, we remove it. + */ +STATIC int +get_remove_unlisted_guards_after_days(void) +{ + return networkstatus_get_param(NULL, + "guard-remove-unlisted-guards-after-days", + DFLT_REMOVE_UNLISTED_GUARDS_AFTER_DAYS, + 1, 365*10); +} +/** + * We remove unconfirmed guards from the sample after this many days, + * regardless of whether they are listed or unlisted. + */ +STATIC int +get_guard_lifetime(void) +{ + if (get_options()->GuardLifetime >= 86400) + return get_options()->GuardLifetime; + int32_t days; + days = networkstatus_get_param(NULL, + "guard-lifetime-days", + DFLT_GUARD_LIFETIME_DAYS, 1, 365*10); + return days * 86400; +} +/** + * We remove confirmed guards from the sample if they were sampled + * GUARD_LIFETIME_DAYS ago and confirmed this many days ago. + */ +STATIC int +get_guard_confirmed_min_lifetime(void) +{ + if (get_options()->GuardLifetime >= 86400) + return get_options()->GuardLifetime; + int32_t days; + days = networkstatus_get_param(NULL, "guard-confirmed-min-lifetime-days", + DFLT_GUARD_CONFIRMED_MIN_LIFETIME_DAYS, + 1, 365*10); + return days * 86400; +} +/** + * How many guards do we try to keep on our primary guard list? + */ +STATIC int +get_n_primary_guards(void) +{ + const int n = get_options()->NumEntryGuards; + const int n_dir = get_options()->NumDirectoryGuards; + if (n > 5) { + return MAX(n_dir, n + n / 2); + } else if (n >= 1) { + return MAX(n_dir, n * 2); + } + + return networkstatus_get_param(NULL, + "guard-n-primary-guards", + DFLT_N_PRIMARY_GUARDS, 1, INT32_MAX); +} +/** + * Return the number of the live primary guards we should look at when + * making a circuit. + */ +STATIC int +get_n_primary_guards_to_use(guard_usage_t usage) +{ + int configured; + const char *param_name; + int param_default; + if (usage == GUARD_USAGE_DIRGUARD) { + configured = get_options()->NumDirectoryGuards; + param_name = "guard-n-primary-dir-guards-to-use"; + param_default = DFLT_N_PRIMARY_DIR_GUARDS_TO_USE; + } else { + configured = get_options()->NumEntryGuards; + param_name = "guard-n-primary-guards-to-use"; + param_default = DFLT_N_PRIMARY_GUARDS_TO_USE; + } + if (configured >= 1) { + return configured; + } + return networkstatus_get_param(NULL, + param_name, param_default, 1, INT32_MAX); +} +/** + * If we haven't successfully built or used a circuit in this long, then + * consider that the internet is probably down. + */ +STATIC int +get_internet_likely_down_interval(void) +{ + return networkstatus_get_param(NULL, "guard-internet-likely-down-interval", + DFLT_INTERNET_LIKELY_DOWN_INTERVAL, + 1, INT32_MAX); +} +/** + * If we're trying to connect to a nonprimary guard for at least this + * many seconds, and we haven't gotten the connection to work, we will treat + * lower-priority guards as usable. + */ +STATIC int +get_nonprimary_guard_connect_timeout(void) +{ + return networkstatus_get_param(NULL, + "guard-nonprimary-guard-connect-timeout", + DFLT_NONPRIMARY_GUARD_CONNECT_TIMEOUT, + 1, INT32_MAX); +} +/** + * If a circuit has been sitting around in 'waiting for better guard' state + * for at least this long, we'll expire it. + */ +STATIC int +get_nonprimary_guard_idle_timeout(void) +{ + return networkstatus_get_param(NULL, + "guard-nonprimary-guard-idle-timeout", + DFLT_NONPRIMARY_GUARD_IDLE_TIMEOUT, + 1, INT32_MAX); +} +/** + * If our configuration retains fewer than this fraction of guards from the + * torrc, we are in a restricted setting. + */ +STATIC double +get_meaningful_restriction_threshold(void) +{ + int32_t pct = networkstatus_get_param(NULL, + "guard-meaningful-restriction-percent", + DFLT_MEANINGFUL_RESTRICTION_PERCENT, + 1, INT32_MAX); + return pct / 100.0; +} +/** + * If our configuration retains fewer than this fraction of guards from the + * torrc, we are in an extremely restricted setting, and should warn. + */ +STATIC double +get_extreme_restriction_threshold(void) +{ + int32_t pct = networkstatus_get_param(NULL, + "guard-extreme-restriction-percent", + DFLT_EXTREME_RESTRICTION_PERCENT, + 1, INT32_MAX); + return pct / 100.0; +} +/**@}*/ + +/** + * Given our options and our list of nodes, return the name of the + * guard selection that we should use. Return NULL for "use the + * same selection you were using before. + */ +STATIC const char * +choose_guard_selection(const or_options_t *options, + const networkstatus_t *live_ns, + const guard_selection_t *old_selection, + guard_selection_type_t *type_out) +{ + tor_assert(options); + tor_assert(type_out); + if (options->UseDeprecatedGuardAlgorithm) { + *type_out = GS_TYPE_LEGACY; + return "legacy"; + } + + if (options->UseBridges) { + *type_out = GS_TYPE_BRIDGE; + return "bridges"; + } + + if (! live_ns) { + /* without a networkstatus, we can't tell any more than that. */ + *type_out = GS_TYPE_NORMAL; + return "default"; + } + + const smartlist_t *nodes = nodelist_get_list(); + int n_guards = 0, n_passing_filter = 0; + SMARTLIST_FOREACH_BEGIN(nodes, const node_t *, node) { + if (node_is_possible_guard(node)) { + ++n_guards; + if (node_passes_guard_filter(options, node)) { + ++n_passing_filter; + } + } + } SMARTLIST_FOREACH_END(node); + + /* XXXX prop271 spec deviation -- separate 'high' and 'low' thresholds + * to prevent flapping */ + const int meaningful_threshold_high = + (int)(n_guards * get_meaningful_restriction_threshold() * 1.05); + const int meaningful_threshold_mid = + (int)(n_guards * get_meaningful_restriction_threshold()); + const int meaningful_threshold_low = + (int)(n_guards * get_meaningful_restriction_threshold() * .95); + const int extreme_threshold = + (int)(n_guards * get_extreme_restriction_threshold()); + + /* + If we have no previous selection, then we're "restricted" iff we are + below the meaningful restriction threshold. That's easy enough. + + But if we _do_ have a previous selection, we make it a little + "sticky": we only move from "restricted" to "default" when we find + that we're above the threshold plus 5%, and we only move from + "default" to "restricted" when we're below the threshold minus 5%. + That should prevent us from flapping back and forth if we happen to + be hovering very close to the default. + + The extreme threshold is for warning only. + */ + + static int have_warned_extreme_threshold = 0; + if (n_guards && + n_passing_filter < extreme_threshold && + ! have_warned_extreme_threshold) { + have_warned_extreme_threshold = 1; + const double exclude_frac = + (n_guards - n_passing_filter) / (double)n_guards; + log_warn(LD_GUARD, "Your configuration excludes %d%% of all possible " + "guards. That's likely to make you stand out from the " + "rest of the world.", (int)(exclude_frac * 100)); + } + + /* Easy case: no previous selection. Just check if we are in restricted or + normal guard selection. */ + if (old_selection == NULL) { + if (n_passing_filter >= meaningful_threshold_mid) { + *type_out = GS_TYPE_NORMAL; + return "default"; + } else { + *type_out = GS_TYPE_RESTRICTED; + return "restricted"; + } + } + + /* Trickier case: we do have a previous guard selection context. */ + tor_assert(old_selection); + + /* Use high and low thresholds to decide guard selection, and if we fall in + the middle then keep the current guard selection context. */ + if (n_passing_filter >= meaningful_threshold_high) { + *type_out = GS_TYPE_NORMAL; + return "default"; + } else if (n_passing_filter < meaningful_threshold_low) { + *type_out = GS_TYPE_RESTRICTED; + return "restricted"; + } else { + /* we are in the middle: maintain previous guard selection */ + *type_out = old_selection->type; + return old_selection->name; + } +} + +/** + * Check whether we should switch from our current guard selection to a + * different one. If so, switch and return 1. Return 0 otherwise. + * + * On a 1 return, the caller should mark all currently live circuits unusable + * for new streams, by calling circuit_mark_all_unused_circs() and + * circuit_mark_all_dirty_circs_as_unusable(). + */ +int +update_guard_selection_choice(const or_options_t *options) +{ + if (!curr_guard_context) { + create_initial_guard_context(); + return 1; + } + + guard_selection_type_t type = GS_TYPE_INFER; + const char *new_name = choose_guard_selection( + options, + networkstatus_get_live_consensus(approx_time()), + curr_guard_context, + &type); + tor_assert(new_name); + tor_assert(type != GS_TYPE_INFER); + + const char *cur_name = curr_guard_context->name; + if (! strcmp(cur_name, new_name)) { + log_debug(LD_GUARD, + "Staying with guard context \"%s\" (no change)", new_name); + return 0; // No change + } + + log_notice(LD_GUARD, "Switching to guard context \"%s\" (was using \"%s\")", + new_name, cur_name); + guard_selection_t *new_guard_context; + new_guard_context = get_guard_selection_by_name(new_name, type, 1); + tor_assert(new_guard_context); + tor_assert(new_guard_context != curr_guard_context); + curr_guard_context = new_guard_context; + + return 1; +} + +/** + * Return true iff <b>node</b> has all the flags needed for us to consider it + * a possible guard when sampling guards. + */ +static int +node_is_possible_guard(const node_t *node) +{ + /* The "GUARDS" set is all nodes in the nodelist for which this predicate + * holds. */ + + /* XXXX -- prop271 spec deviation. We require node_is_dir() here. */ + tor_assert(node); + return (node->is_possible_guard && + node->is_stable && + node->is_fast && + node->is_valid && + node_is_dir(node)); +} + +/** + * Return the sampled guard with the RSA identity digest <b>rsa_id</b>, or + * NULL if we don't have one. */ +STATIC entry_guard_t * +get_sampled_guard_with_id(guard_selection_t *gs, + const uint8_t *rsa_id) +{ + tor_assert(gs); + tor_assert(rsa_id); + SMARTLIST_FOREACH_BEGIN(gs->sampled_entry_guards, entry_guard_t *, guard) { + if (tor_memeq(guard->identity, rsa_id, DIGEST_LEN)) + return guard; + } SMARTLIST_FOREACH_END(guard); + return NULL; +} + +/** If <b>gs</b> contains a sampled entry guard matching <b>bridge</b>, + * return that guard. Otherwise return NULL. */ +static entry_guard_t * +get_sampled_guard_for_bridge(guard_selection_t *gs, + const bridge_info_t *bridge) +{ + const uint8_t *id = bridge_get_rsa_id_digest(bridge); + const tor_addr_port_t *addrport = bridge_get_addr_port(bridge); + entry_guard_t *guard; + if (id) { + guard = get_sampled_guard_with_id(gs, id); + if (guard) + return guard; + } + if (BUG(!addrport)) + return NULL; // LCOV_EXCL_LINE + guard = get_sampled_guard_by_bridge_addr(gs, addrport); + if (! guard || (id && tor_memneq(id, guard->identity, DIGEST_LEN))) + return NULL; + else + return guard; +} + +/** If we know a bridge_info_t matching <b>guard</b>, return that + * bridge. Otherwise return NULL. */ +static bridge_info_t * +get_bridge_info_for_guard(const entry_guard_t *guard) +{ + if (! tor_digest_is_zero(guard->identity)) { + bridge_info_t *bridge = find_bridge_by_digest(guard->identity); + if (bridge) + return bridge; + } + if (BUG(guard->bridge_addr == NULL)) + return NULL; + return get_configured_bridge_by_addr_port_digest(&guard->bridge_addr->addr, + guard->bridge_addr->port, + NULL); +} + +/** + * Return true iff we have a sampled guard with the RSA identity digest + * <b>rsa_id</b>. */ +static inline int +have_sampled_guard_with_id(guard_selection_t *gs, const uint8_t *rsa_id) +{ + return get_sampled_guard_with_id(gs, rsa_id) != NULL; +} + +/** + * Allocate a new entry_guard_t object for <b>node</b>, add it to the + * sampled entry guards in <b>gs</b>, and return it. <b>node</b> must + * not currently be a sampled guard in <b>gs</b>. + */ +STATIC entry_guard_t * +entry_guard_add_to_sample(guard_selection_t *gs, + const node_t *node) +{ + log_info(LD_GUARD, "Adding %s as to the entry guard sample set.", + node_describe(node)); + + return entry_guard_add_to_sample_impl(gs, + (const uint8_t*)node->identity, + node_get_nickname(node), + NULL); +} + +/** + * Backend: adds a new sampled guard to <b>gs</b>, with given identity, + * nickname, and ORPort. rsa_id_digest and bridge_addrport are optional, but + * we need one of them. nickname is optional. The caller is responsible for + * maintaining the size limit of the SAMPLED_GUARDS set. + */ +static entry_guard_t * +entry_guard_add_to_sample_impl(guard_selection_t *gs, + const uint8_t *rsa_id_digest, + const char *nickname, + const tor_addr_port_t *bridge_addrport) +{ + const int GUARD_LIFETIME = get_guard_lifetime(); + tor_assert(gs); + + // XXXX prop271 take ed25519 identity here too. + + /* make sure that the guard is not already sampled. */ + if (rsa_id_digest && BUG(have_sampled_guard_with_id(gs, rsa_id_digest))) + return NULL; // LCOV_EXCL_LINE + /* Make sure we can actually identify the guard. */ + if (BUG(!rsa_id_digest && !bridge_addrport)) + return NULL; // LCOV_EXCL_LINE + + entry_guard_t *guard = tor_malloc_zero(sizeof(entry_guard_t)); + + /* persistent fields */ + guard->is_persistent = (rsa_id_digest != NULL); + guard->selection_name = tor_strdup(gs->name); + if (rsa_id_digest) + memcpy(guard->identity, rsa_id_digest, DIGEST_LEN); + if (nickname) + strlcpy(guard->nickname, nickname, sizeof(guard->nickname)); + guard->sampled_on_date = randomize_time(approx_time(), GUARD_LIFETIME/10); + tor_free(guard->sampled_by_version); + guard->sampled_by_version = tor_strdup(VERSION); + guard->currently_listed = 1; + guard->confirmed_idx = -1; + + /* non-persistent fields */ + guard->is_reachable = GUARD_REACHABLE_MAYBE; + if (bridge_addrport) + guard->bridge_addr = tor_memdup(bridge_addrport, sizeof(*bridge_addrport)); + + smartlist_add(gs->sampled_entry_guards, guard); + guard->in_selection = gs; + entry_guard_set_filtered_flags(get_options(), gs, guard); + entry_guards_changed_for_guard_selection(gs); + return guard; +} + +/** + * Add an entry guard to the "bridges" guard selection sample, with + * information taken from <b>bridge</b>. Return that entry guard. + */ +static entry_guard_t * +entry_guard_add_bridge_to_sample(guard_selection_t *gs, + const bridge_info_t *bridge) +{ + const uint8_t *id_digest = bridge_get_rsa_id_digest(bridge); + const tor_addr_port_t *addrport = bridge_get_addr_port(bridge); + + tor_assert(addrport); + + return entry_guard_add_to_sample_impl(gs, id_digest, NULL, addrport); +} + +/** + * Return the entry_guard_t in <b>gs</b> whose address is <b>addrport</b>, + * or NULL if none exists. +*/ +static entry_guard_t * +get_sampled_guard_by_bridge_addr(guard_selection_t *gs, + const tor_addr_port_t *addrport) +{ + if (! gs) + return NULL; + if (BUG(!addrport)) + return NULL; + SMARTLIST_FOREACH_BEGIN(gs->sampled_entry_guards, entry_guard_t *, g) { + if (g->bridge_addr && tor_addr_port_eq(addrport, g->bridge_addr)) + return g; + } SMARTLIST_FOREACH_END(g); + return NULL; +} + +/** Update the guard subsystem's knowledge of the identity of the bridge + * at <b>addrport</b>. Idempotent. + */ +void +entry_guard_learned_bridge_identity(const tor_addr_port_t *addrport, + const uint8_t *rsa_id_digest) +{ + guard_selection_t *gs = get_guard_selection_by_name("bridges", + GS_TYPE_BRIDGE, + 0); + if (!gs) + return; + + entry_guard_t *g = get_sampled_guard_by_bridge_addr(gs, addrport); + if (!g) + return; + + int make_persistent = 0; + + if (tor_digest_is_zero(g->identity)) { + memcpy(g->identity, rsa_id_digest, DIGEST_LEN); + make_persistent = 1; + } else if (tor_memeq(g->identity, rsa_id_digest, DIGEST_LEN)) { + /* Nothing to see here; we learned something we already knew. */ + if (BUG(! g->is_persistent)) + make_persistent = 1; + } else { + char old_id[HEX_DIGEST_LEN+1]; + base16_encode(old_id, sizeof(old_id), g->identity, sizeof(g->identity)); + log_warn(LD_BUG, "We 'learned' an identity %s for a bridge at %s:%d, but " + "we already knew a different one (%s). Ignoring the new info as " + "possibly bogus.", + hex_str((const char *)rsa_id_digest, DIGEST_LEN), + fmt_and_decorate_addr(&addrport->addr), addrport->port, + old_id); + return; // redundant, but let's be clear: we're not making this persistent. + } + + if (make_persistent) { + g->is_persistent = 1; + entry_guards_changed_for_guard_selection(gs); + } +} + +/** + * Return the number of sampled guards in <b>gs</b> that are "filtered" + * (that is, we're willing to connect to them) and that are "usable" + * (that is, either "reachable" or "maybe reachable"). + * + * If a restriction is provided in <b>rst</b>, do not count any guards that + * violate it. + */ +STATIC int +num_reachable_filtered_guards(guard_selection_t *gs, + const entry_guard_restriction_t *rst) +{ + int n_reachable_filtered_guards = 0; + SMARTLIST_FOREACH_BEGIN(gs->sampled_entry_guards, entry_guard_t *, guard) { + entry_guard_consider_retry(guard); + if (! entry_guard_obeys_restriction(guard, rst)) + continue; + if (guard->is_usable_filtered_guard) + ++n_reachable_filtered_guards; + } SMARTLIST_FOREACH_END(guard); + return n_reachable_filtered_guards; +} + +/** Return the actual maximum size for the sample in <b>gs</b>, + * given that we know about <b>n_guards</b> total. */ +static int +get_max_sample_size(guard_selection_t *gs, + int n_guards) +{ + const int using_bridges = (gs->type == GS_TYPE_BRIDGE); + const int min_sample = get_min_filtered_sample_size(); + + /* XXXX prop271 spec deviation with bridges, max_sample is "all of them" */ + if (using_bridges) + return n_guards; + + const int max_sample_by_pct = (int)(n_guards * get_max_sample_threshold()); + const int max_sample_absolute = get_max_sample_size_absolute(); + const int max_sample = MIN(max_sample_by_pct, max_sample_absolute); + if (max_sample < min_sample) // XXXX prop271 spec deviation + return min_sample; + else + return max_sample; +} + +/** + * Return a smartlist of the all the guards that are not currently + * members of the sample (GUARDS - SAMPLED_GUARDS). The elements of + * this list are node_t pointers in the non-bridge case, and + * bridge_info_t pointers in the bridge case. Set *<b>n_guards_out/b> + * to the number of guards that we found in GUARDS, including those + * that were already sampled. + */ +static smartlist_t * +get_eligible_guards(const or_options_t *options, + guard_selection_t *gs, + int *n_guards_out) +{ + /* Construct eligible_guards as GUARDS - SAMPLED_GUARDS */ + smartlist_t *eligible_guards = smartlist_new(); + int n_guards = 0; // total size of "GUARDS" + + if (gs->type == GS_TYPE_BRIDGE) { + const smartlist_t *bridges = bridge_list_get(); + SMARTLIST_FOREACH_BEGIN(bridges, bridge_info_t *, bridge) { + ++n_guards; + if (NULL != get_sampled_guard_for_bridge(gs, bridge)) { + continue; + } + smartlist_add(eligible_guards, bridge); + } SMARTLIST_FOREACH_END(bridge); + } else { + const smartlist_t *nodes = nodelist_get_list(); + const int n_sampled = smartlist_len(gs->sampled_entry_guards); + + /* Build a bloom filter of our current guards: let's keep this O(N). */ + digestset_t *sampled_guard_ids = digestset_new(n_sampled); + SMARTLIST_FOREACH_BEGIN(gs->sampled_entry_guards, const entry_guard_t *, + guard) { + digestset_add(sampled_guard_ids, guard->identity); + } SMARTLIST_FOREACH_END(guard); + + SMARTLIST_FOREACH_BEGIN(nodes, const node_t *, node) { + if (! node_is_possible_guard(node)) + continue; + if (gs->type == GS_TYPE_RESTRICTED) { + /* In restricted mode, we apply the filter BEFORE sampling, so + * that we are sampling from the nodes that we might actually + * select. If we sampled first, we might wind up with a sample + * that didn't include any EntryNodes at all. */ + if (! node_passes_guard_filter(options, node)) + continue; + } + ++n_guards; + if (digestset_contains(sampled_guard_ids, node->identity)) + continue; + smartlist_add(eligible_guards, (node_t*)node); + } SMARTLIST_FOREACH_END(node); + + /* Now we can free that bloom filter. */ + digestset_free(sampled_guard_ids); + } + + *n_guards_out = n_guards; + return eligible_guards; +} + +/** Helper: given a smartlist of either bridge_info_t (if gs->type is + * GS_TYPE_BRIDGE) or node_t (otherwise), pick one that can be a guard, + * add it as a guard, remove it from the list, and return a new + * entry_guard_t. Return NULL on failure. */ +static entry_guard_t * +select_and_add_guard_item_for_sample(guard_selection_t *gs, + smartlist_t *eligible_guards) +{ + entry_guard_t *added_guard; + if (gs->type == GS_TYPE_BRIDGE) { + const bridge_info_t *bridge = smartlist_choose(eligible_guards); + if (BUG(!bridge)) + return NULL; // LCOV_EXCL_LINE + smartlist_remove(eligible_guards, bridge); + added_guard = entry_guard_add_bridge_to_sample(gs, bridge); + } else { + const node_t *node = + node_sl_choose_by_bandwidth(eligible_guards, WEIGHT_FOR_GUARD); + if (BUG(!node)) + return NULL; // LCOV_EXCL_LINE + smartlist_remove(eligible_guards, node); + added_guard = entry_guard_add_to_sample(gs, node); + } + + return added_guard; +} + +/** + * Add new guards to the sampled guards in <b>gs</b> until there are + * enough usable filtered guards, but never grow the sample beyond its + * maximum size. Return the last guard added, or NULL if none were + * added. + */ +STATIC entry_guard_t * +entry_guards_expand_sample(guard_selection_t *gs) +{ + tor_assert(gs); + const or_options_t *options = get_options(); + int n_sampled = smartlist_len(gs->sampled_entry_guards); + entry_guard_t *added_guard = NULL; + int n_usable_filtered_guards = num_reachable_filtered_guards(gs, NULL); + int n_guards = 0; + smartlist_t *eligible_guards = get_eligible_guards(options, gs, &n_guards); + + const int max_sample = get_max_sample_size(gs, n_guards); + const int min_filtered_sample = get_min_filtered_sample_size(); + + log_info(LD_GUARD, "Expanding the sample guard set. We have %d guards " + "in the sample, and %d eligible guards to extend it with.", + n_sampled, smartlist_len(eligible_guards)); + + while (n_usable_filtered_guards < min_filtered_sample) { + /* Has our sample grown too large to expand? */ + if (n_sampled >= max_sample) { + log_info(LD_GUARD, "Not expanding the guard sample any further; " + "just hit the maximum sample threshold of %d", + max_sample); + goto done; + } + + /* Did we run out of guards? */ + if (smartlist_len(eligible_guards) == 0) { + /* LCOV_EXCL_START + As long as MAX_SAMPLE_THRESHOLD makes can't be adjusted to + allow all guards to be sampled, this can't be reached. + */ + log_info(LD_GUARD, "Not expanding the guard sample any further; " + "just ran out of eligible guards"); + goto done; + /* LCOV_EXCL_STOP */ + } + + /* Otherwise we can add at least one new guard. */ + added_guard = select_and_add_guard_item_for_sample(gs, eligible_guards); + if (!added_guard) + goto done; // LCOV_EXCL_LINE -- only fails on BUG. + + ++n_sampled; + + if (added_guard->is_usable_filtered_guard) + ++n_usable_filtered_guards; + } + + done: + smartlist_free(eligible_guards); + return added_guard; +} + +/** + * Helper: <b>guard</b> has just been removed from the sampled guards: + * also remove it from primary and confirmed. */ +static void +remove_guard_from_confirmed_and_primary_lists(guard_selection_t *gs, + entry_guard_t *guard) +{ + if (guard->is_primary) { + guard->is_primary = 0; + smartlist_remove_keeporder(gs->primary_entry_guards, guard); + } else { + if (BUG(smartlist_contains(gs->primary_entry_guards, guard))) { + smartlist_remove_keeporder(gs->primary_entry_guards, guard); + } + } + + if (guard->confirmed_idx >= 0) { + entry_guard_t *found_guard = NULL; + if (guard->confirmed_idx < smartlist_len(gs->confirmed_entry_guards)) + found_guard = smartlist_get(gs->confirmed_entry_guards, + guard->confirmed_idx); + if (BUG(guard != found_guard)) { + // LCOV_EXCL_START + smartlist_remove_keeporder(gs->confirmed_entry_guards, guard); + // LCOV_EXCL_STOP + } else { + smartlist_del_keeporder(gs->confirmed_entry_guards, + guard->confirmed_idx); + } + guard->confirmed_idx = -1; + guard->confirmed_on_date = 0; + } else { + if (BUG(smartlist_contains(gs->confirmed_entry_guards, guard))) { + // LCOV_EXCL_START + smartlist_remove_keeporder(gs->confirmed_entry_guards, guard); + // LCOV_EXCL_STOP + } + } +} + +/** Return true iff <b>guard</b> is currently "listed" -- that is, it + * appears in the consensus, or as a configured bridge (as + * appropriate) */ +MOCK_IMPL(STATIC int, +entry_guard_is_listed,(guard_selection_t *gs, const entry_guard_t *guard)) +{ + if (gs->type == GS_TYPE_BRIDGE) { + return NULL != get_bridge_info_for_guard(guard); + } else { + const node_t *node = node_get_by_id(guard->identity); + + return node && node_is_possible_guard(node); + } +} + +/** + * Update the status of all sampled guards based on the arrival of a + * new consensus networkstatus document. This will include marking + * some guards as listed or unlisted, and removing expired guards. */ +STATIC void +sampled_guards_update_from_consensus(guard_selection_t *gs) +{ + /*XXXX prop271 consider splitting this function up. */ + tor_assert(gs); + const int REMOVE_UNLISTED_GUARDS_AFTER = + (get_remove_unlisted_guards_after_days() * 86400); + const int unlisted_since_slop = REMOVE_UNLISTED_GUARDS_AFTER / 5; + + // It's important to use only a live consensus here; we don't want to + // make changes based on anything expired or old. + if (gs->type != GS_TYPE_BRIDGE) { + networkstatus_t *ns = networkstatus_get_live_consensus(approx_time()); + + if (! ns) { + log_info(LD_GUARD, "No live consensus; can't update " + "sampled entry guards."); + return; + } else { + log_info(LD_GUARD, "Updating sampled guard status based on received " + "consensus."); + } + } + + int n_changes = 0; + + /* First: Update listed/unlisted. */ + SMARTLIST_FOREACH_BEGIN(gs->sampled_entry_guards, entry_guard_t *, guard) { + /* XXXX prop271 check ed ID too */ + const int is_listed = entry_guard_is_listed(gs, guard); + + if (is_listed && ! guard->currently_listed) { + ++n_changes; + guard->currently_listed = 1; + guard->unlisted_since_date = 0; + log_info(LD_GUARD, "Sampled guard %s is now listed again.", + entry_guard_describe(guard)); + } else if (!is_listed && guard->currently_listed) { + ++n_changes; + guard->currently_listed = 0; + guard->unlisted_since_date = randomize_time(approx_time(), + unlisted_since_slop); + log_info(LD_GUARD, "Sampled guard %s is now unlisted.", + entry_guard_describe(guard)); + } else if (is_listed && guard->currently_listed) { + log_debug(LD_GUARD, "Sampled guard %s is still listed.", + entry_guard_describe(guard)); + } else { + tor_assert(! is_listed && ! guard->currently_listed); + log_debug(LD_GUARD, "Sampled guard %s is still unlisted.", + entry_guard_describe(guard)); + } + + /* Clean up unlisted_since_date, just in case. */ + if (guard->currently_listed && guard->unlisted_since_date) { + ++n_changes; + guard->unlisted_since_date = 0; + log_warn(LD_BUG, "Sampled guard %s was listed, but with " + "unlisted_since_date set. Fixing.", + entry_guard_describe(guard)); + } else if (!guard->currently_listed && ! guard->unlisted_since_date) { + ++n_changes; + guard->unlisted_since_date = randomize_time(approx_time(), + unlisted_since_slop); + log_warn(LD_BUG, "Sampled guard %s was unlisted, but with " + "unlisted_since_date unset. Fixing.", + entry_guard_describe(guard)); + } + } SMARTLIST_FOREACH_END(guard); + + const time_t remove_if_unlisted_since = + approx_time() - REMOVE_UNLISTED_GUARDS_AFTER; + const time_t maybe_remove_if_sampled_before = + approx_time() - get_guard_lifetime(); + const time_t remove_if_confirmed_before = + approx_time() - get_guard_confirmed_min_lifetime(); + + /* Then: remove the ones that have been junk for too long */ + SMARTLIST_FOREACH_BEGIN(gs->sampled_entry_guards, entry_guard_t *, guard) { + int rmv = 0; + + if (guard->currently_listed == 0 && + guard->unlisted_since_date < remove_if_unlisted_since) { + /* + "We have a live consensus, and {IS_LISTED} is false, and + {FIRST_UNLISTED_AT} is over {REMOVE_UNLISTED_GUARDS_AFTER} + days in the past." + */ + log_info(LD_GUARD, "Removing sampled guard %s: it has been unlisted " + "for over %d days", entry_guard_describe(guard), + get_remove_unlisted_guards_after_days()); + rmv = 1; + } else if (guard->sampled_on_date < maybe_remove_if_sampled_before) { + /* We have a live consensus, and {ADDED_ON_DATE} is over + {GUARD_LIFETIME} ago, *and* {CONFIRMED_ON_DATE} is either + "never", or over {GUARD_CONFIRMED_MIN_LIFETIME} ago. + */ + if (guard->confirmed_on_date == 0) { + rmv = 1; + log_info(LD_GUARD, "Removing sampled guard %s: it was sampled " + "over %d days ago, but never confirmed.", + entry_guard_describe(guard), + get_guard_lifetime() / 86400); + } else if (guard->confirmed_on_date < remove_if_confirmed_before) { + rmv = 1; + log_info(LD_GUARD, "Removing sampled guard %s: it was sampled " + "over %d days ago, and confirmed over %d days ago.", + entry_guard_describe(guard), + get_guard_lifetime() / 86400, + get_guard_confirmed_min_lifetime() / 86400); + } + } + + if (rmv) { + ++n_changes; + SMARTLIST_DEL_CURRENT(gs->sampled_entry_guards, guard); + remove_guard_from_confirmed_and_primary_lists(gs, guard); + entry_guard_free(guard); + } + } SMARTLIST_FOREACH_END(guard); + + if (n_changes) { + gs->primary_guards_up_to_date = 0; + entry_guards_update_filtered_sets(gs); + /* We don't need to rebuild the confirmed list right here -- we may have + * removed confirmed guards above, but we can't have added any new + * confirmed guards. + */ + entry_guards_changed_for_guard_selection(gs); + } +} + +/** + * Return true iff <b>node</b> is a Tor relay that we are configured to + * be able to connect to. */ +static int +node_passes_guard_filter(const or_options_t *options, + const node_t *node) +{ + /* NOTE: Make sure that this function stays in sync with + * options_transition_affects_entry_guards */ + if (routerset_contains_node(options->ExcludeNodes, node)) + return 0; + + /* XXXX -- prop271 spec deviation -- add entrynodes to spec. */ + if (options->EntryNodes && + !routerset_contains_node(options->EntryNodes, node)) + return 0; + + if (!fascist_firewall_allows_node(node, FIREWALL_OR_CONNECTION, 0)) + return 0; + + if (node_is_a_configured_bridge(node)) + return 0; + + return 1; +} + +/** Helper: Return true iff <b>bridge</b> passes our configuration + * filter-- if it is a relay that we are configured to be able to + * connect to. */ +static int +bridge_passes_guard_filter(const or_options_t *options, + const bridge_info_t *bridge) +{ + tor_assert(bridge); + if (!bridge) + return 0; + + if (routerset_contains_bridge(options->ExcludeNodes, bridge)) + return 0; + + /* Ignore entrynodes */ + const tor_addr_port_t *addrport = bridge_get_addr_port(bridge); + + if (!fascist_firewall_allows_address_addr(&addrport->addr, + addrport->port, + FIREWALL_OR_CONNECTION, + 0, 0)) + return 0; + + return 1; +} + +/** + * Return true iff <b>guard</b> is a Tor relay that we are configured to + * be able to connect to, and we haven't disabled it for omission from + * the consensus or path bias issues. */ +static int +entry_guard_passes_filter(const or_options_t *options, guard_selection_t *gs, + entry_guard_t *guard) +{ + if (guard->currently_listed == 0) + return 0; + if (guard->pb.path_bias_disabled) + return 0; + + if (gs->type == GS_TYPE_BRIDGE) { + const bridge_info_t *bridge = get_bridge_info_for_guard(guard); + if (bridge == NULL) + return 0; + return bridge_passes_guard_filter(options, bridge); + } else { + const node_t *node = node_get_by_id(guard->identity); + if (node == NULL) { + // This can happen when currently_listed is true, and we're not updating + // it because we don't have a live consensus. + return 0; + } + + return node_passes_guard_filter(options, node); + } +} + +/** + * Return true iff <b>guard</b> obeys the restrictions defined in <b>rst</b>. + * (If <b>rst</b> is NULL, there are no restrictions.) + */ +static int +entry_guard_obeys_restriction(const entry_guard_t *guard, + const entry_guard_restriction_t *rst) +{ + tor_assert(guard); + if (! rst) + return 1; // No restriction? No problem. + + // Only one kind of restriction exists right now + return tor_memneq(guard->identity, rst->exclude_id, DIGEST_LEN); +} + +/** + * Update the <b>is_filtered_guard</b> and <b>is_usable_filtered_guard</b> + * flags on <b>guard</b>. */ +void +entry_guard_set_filtered_flags(const or_options_t *options, + guard_selection_t *gs, + entry_guard_t *guard) +{ + unsigned was_filtered = guard->is_filtered_guard; + guard->is_filtered_guard = 0; + guard->is_usable_filtered_guard = 0; + + if (entry_guard_passes_filter(options, gs, guard)) { + guard->is_filtered_guard = 1; + + if (guard->is_reachable != GUARD_REACHABLE_NO) + guard->is_usable_filtered_guard = 1; + + entry_guard_consider_retry(guard); + } + log_debug(LD_GUARD, "Updated sampled guard %s: filtered=%d; " + "reachable_filtered=%d.", entry_guard_describe(guard), + guard->is_filtered_guard, guard->is_usable_filtered_guard); + + if (!bool_eq(was_filtered, guard->is_filtered_guard)) { + /* This guard might now be primary or nonprimary. */ + gs->primary_guards_up_to_date = 0; + } +} + +/** + * Update the <b>is_filtered_guard</b> and <b>is_usable_filtered_guard</b> + * flag on every guard in <b>gs</b>. */ +STATIC void +entry_guards_update_filtered_sets(guard_selection_t *gs) +{ + const or_options_t *options = get_options(); + + SMARTLIST_FOREACH_BEGIN(gs->sampled_entry_guards, entry_guard_t *, guard) { + entry_guard_set_filtered_flags(options, gs, guard); + } SMARTLIST_FOREACH_END(guard); +} + +/** + * Return a random guard from the reachable filtered sample guards + * in <b>gs</b>, subject to the exclusion rules listed in <b>flags</b>. + * Return NULL if no such guard can be found. + * + * Make sure that the sample is big enough, and that all the filter flags + * are set correctly, before calling this function. + * + * If a restriction is provided in <b>rst</b>, do not return any guards that + * violate it. + **/ +STATIC entry_guard_t * +sample_reachable_filtered_entry_guards(guard_selection_t *gs, + const entry_guard_restriction_t *rst, + unsigned flags) +{ + tor_assert(gs); + entry_guard_t *result = NULL; + const unsigned exclude_confirmed = flags & SAMPLE_EXCLUDE_CONFIRMED; + const unsigned exclude_primary = flags & SAMPLE_EXCLUDE_PRIMARY; + const unsigned exclude_pending = flags & SAMPLE_EXCLUDE_PENDING; + const unsigned no_update_primary = flags & SAMPLE_NO_UPDATE_PRIMARY; + + SMARTLIST_FOREACH_BEGIN(gs->sampled_entry_guards, entry_guard_t *, guard) { + entry_guard_consider_retry(guard); + } SMARTLIST_FOREACH_END(guard); + + const int n_reachable_filtered = num_reachable_filtered_guards(gs, rst); + + log_info(LD_GUARD, "Trying to sample a reachable guard: We know of %d " + "in the USABLE_FILTERED set.", n_reachable_filtered); + + const int min_filtered_sample = get_min_filtered_sample_size(); + if (n_reachable_filtered < min_filtered_sample) { + log_info(LD_GUARD, " (That isn't enough. Trying to expand the sample.)"); + entry_guards_expand_sample(gs); + } + + if (exclude_primary && !gs->primary_guards_up_to_date && !no_update_primary) + entry_guards_update_primary(gs); + + /* Build the set of reachable filtered guards. */ + smartlist_t *reachable_filtered_sample = smartlist_new(); + SMARTLIST_FOREACH_BEGIN(gs->sampled_entry_guards, entry_guard_t *, guard) { + entry_guard_consider_retry(guard);// redundant, but cheap. + if (! entry_guard_obeys_restriction(guard, rst)) + continue; + if (! guard->is_usable_filtered_guard) + continue; + if (exclude_confirmed && guard->confirmed_idx >= 0) + continue; + if (exclude_primary && guard->is_primary) + continue; + if (exclude_pending && guard->is_pending) + continue; + smartlist_add(reachable_filtered_sample, guard); + } SMARTLIST_FOREACH_END(guard); + + log_info(LD_GUARD, " (After filters [%x], we have %d guards to consider.)", + flags, smartlist_len(reachable_filtered_sample)); + + if (smartlist_len(reachable_filtered_sample)) { + result = smartlist_choose(reachable_filtered_sample); + log_info(LD_GUARD, " (Selected %s.)", + result ? entry_guard_describe(result) : "<null>"); + } + smartlist_free(reachable_filtered_sample); + + return result; +} + +/** + * Helper: compare two entry_guard_t by their confirmed_idx values. + * Used to sort the confirmed list. + */ +static int +compare_guards_by_confirmed_idx(const void **a_, const void **b_) +{ + const entry_guard_t *a = *a_, *b = *b_; + if (a->confirmed_idx < b->confirmed_idx) + return -1; + else if (a->confirmed_idx > b->confirmed_idx) + return 1; + else + return 0; +} + +/** + * Find the confirmed guards from among the sampled guards in <b>gs</b>, + * and put them in confirmed_entry_guards in the correct + * order. Recalculate their indices. + */ +STATIC void +entry_guards_update_confirmed(guard_selection_t *gs) +{ + smartlist_clear(gs->confirmed_entry_guards); + SMARTLIST_FOREACH_BEGIN(gs->sampled_entry_guards, entry_guard_t *, guard) { + if (guard->confirmed_idx >= 0) + smartlist_add(gs->confirmed_entry_guards, guard); + } SMARTLIST_FOREACH_END(guard); + + smartlist_sort(gs->confirmed_entry_guards, compare_guards_by_confirmed_idx); + + int any_changed = 0; + SMARTLIST_FOREACH_BEGIN(gs->confirmed_entry_guards, entry_guard_t *, guard) { + if (guard->confirmed_idx != guard_sl_idx) { + any_changed = 1; + guard->confirmed_idx = guard_sl_idx; + } + } SMARTLIST_FOREACH_END(guard); + + gs->next_confirmed_idx = smartlist_len(gs->confirmed_entry_guards); + + if (any_changed) { + entry_guards_changed_for_guard_selection(gs); + } +} + +/** + * Mark <b>guard</b> as a confirmed guard -- that is, one that we have + * connected to, and intend to use again. + */ +STATIC void +make_guard_confirmed(guard_selection_t *gs, entry_guard_t *guard) +{ + if (BUG(guard->confirmed_on_date && guard->confirmed_idx >= 0)) + return; // LCOV_EXCL_LINE + + if (BUG(smartlist_contains(gs->confirmed_entry_guards, guard))) + return; // LCOV_EXCL_LINE + + const int GUARD_LIFETIME = get_guard_lifetime(); + guard->confirmed_on_date = randomize_time(approx_time(), GUARD_LIFETIME/10); + + log_info(LD_GUARD, "Marking %s as a confirmed guard (index %d)", + entry_guard_describe(guard), + gs->next_confirmed_idx); + + guard->confirmed_idx = gs->next_confirmed_idx++; + smartlist_add(gs->confirmed_entry_guards, guard); + + // This confirmed guard might kick something else out of the primary + // guards. + gs->primary_guards_up_to_date = 0; + + entry_guards_changed_for_guard_selection(gs); +} + +/** + * Recalculate the list of primary guards (the ones we'd prefer to use) from + * the filtered sample and the confirmed list. + */ +STATIC void +entry_guards_update_primary(guard_selection_t *gs) +{ + /*XXXX prop271 consider splitting this function up. */ + tor_assert(gs); + + // prevent recursion. Recursion is potentially very bad here. + static int running = 0; + tor_assert(!running); + running = 1; + + const int N_PRIMARY_GUARDS = get_n_primary_guards(); + + smartlist_t *new_primary_guards = smartlist_new(); + smartlist_t *old_primary_guards = smartlist_new(); + smartlist_add_all(old_primary_guards, gs->primary_entry_guards); + + /* Set this flag now, to prevent the calls below from recursing. */ + gs->primary_guards_up_to_date = 1; + + /* First, can we fill it up with confirmed guards? */ + SMARTLIST_FOREACH_BEGIN(gs->confirmed_entry_guards, entry_guard_t *, guard) { + if (smartlist_len(new_primary_guards) >= N_PRIMARY_GUARDS) + break; + if (! guard->is_filtered_guard) + continue; + guard->is_primary = 1; + smartlist_add(new_primary_guards, guard); + } SMARTLIST_FOREACH_END(guard); + + /* Can we keep any older primary guards? First remove all the ones + * that we already kept. */ + SMARTLIST_FOREACH_BEGIN(old_primary_guards, entry_guard_t *, guard) { + if (smartlist_contains(new_primary_guards, guard)) { + SMARTLIST_DEL_CURRENT_KEEPORDER(old_primary_guards, guard); + } + } SMARTLIST_FOREACH_END(guard); + + /* Now add any that are still good. */ + SMARTLIST_FOREACH_BEGIN(old_primary_guards, entry_guard_t *, guard) { + if (smartlist_len(new_primary_guards) >= N_PRIMARY_GUARDS) + break; + if (! guard->is_filtered_guard) + continue; + guard->is_primary = 1; + smartlist_add(new_primary_guards, guard); + SMARTLIST_DEL_CURRENT_KEEPORDER(old_primary_guards, guard); + } SMARTLIST_FOREACH_END(guard); + + /* Mark the remaining previous primary guards as non-primary */ + SMARTLIST_FOREACH_BEGIN(old_primary_guards, entry_guard_t *, guard) { + guard->is_primary = 0; + } SMARTLIST_FOREACH_END(guard); + + /* Finally, fill out the list with sampled guards. */ + while (smartlist_len(new_primary_guards) < N_PRIMARY_GUARDS) { + entry_guard_t *guard = sample_reachable_filtered_entry_guards(gs, NULL, + SAMPLE_EXCLUDE_CONFIRMED| + SAMPLE_EXCLUDE_PRIMARY| + SAMPLE_NO_UPDATE_PRIMARY); + if (!guard) + break; + guard->is_primary = 1; + smartlist_add(new_primary_guards, guard); + } + +#if 1 + /* Debugging. */ + SMARTLIST_FOREACH(gs->sampled_entry_guards, entry_guard_t *, guard, { + tor_assert_nonfatal( + bool_eq(guard->is_primary, + smartlist_contains(new_primary_guards, guard))); + }); +#endif + + int any_change = 0; + if (smartlist_len(gs->primary_entry_guards) != + smartlist_len(new_primary_guards)) { + any_change = 1; + } else { + SMARTLIST_FOREACH_BEGIN(gs->primary_entry_guards, entry_guard_t *, g) { + if (g != smartlist_get(new_primary_guards, g_sl_idx)) { + any_change = 1; + } + } SMARTLIST_FOREACH_END(g); + } + + if (any_change) { + log_info(LD_GUARD, "Primary entry guards have changed. " + "New primary guard list is: "); + int n = smartlist_len(new_primary_guards); + SMARTLIST_FOREACH_BEGIN(new_primary_guards, entry_guard_t *, g) { + log_info(LD_GUARD, " %d/%d: %s%s%s", + g_sl_idx+1, n, entry_guard_describe(g), + g->confirmed_idx >= 0 ? " (confirmed)" : "", + g->is_filtered_guard ? "" : " (excluded by filter)"); + } SMARTLIST_FOREACH_END(g); + } + + smartlist_free(old_primary_guards); + smartlist_free(gs->primary_entry_guards); + gs->primary_entry_guards = new_primary_guards; + gs->primary_guards_up_to_date = 1; + running = 0; +} + +/** + * Return the number of seconds after the last attempt at which we should + * retry a guard that has been failing since <b>failing_since</b>. + */ +static int +get_retry_schedule(time_t failing_since, time_t now, + int is_primary) +{ + const unsigned SIX_HOURS = 6 * 3600; + const unsigned FOUR_DAYS = 4 * 86400; + const unsigned SEVEN_DAYS = 7 * 86400; + + time_t tdiff; + if (now > failing_since) { + tdiff = now - failing_since; + } else { + tdiff = 0; + } + + const struct { + time_t maximum; int primary_delay; int nonprimary_delay; + } delays[] = { + { SIX_HOURS, 10*60, 1*60*60 }, + { FOUR_DAYS, 90*60, 4*60*60 }, + { SEVEN_DAYS, 4*60*60, 18*60*60 }, + { TIME_MAX, 9*60*60, 36*60*60 } + }; + + unsigned i; + for (i = 0; i < ARRAY_LENGTH(delays); ++i) { + if (tdiff <= delays[i].maximum) { + return is_primary ? delays[i].primary_delay : delays[i].nonprimary_delay; + } + } + /* LCOV_EXCL_START -- can't reach, since delays ends with TIME_MAX. */ + tor_assert_nonfatal_unreached(); + return 36*60*60; + /* LCOV_EXCL_STOP */ +} + +/** + * If <b>guard</b> is unreachable, consider whether enough time has passed + * to consider it maybe-reachable again. + */ +STATIC void +entry_guard_consider_retry(entry_guard_t *guard) +{ + if (guard->is_reachable != GUARD_REACHABLE_NO) + return; /* No retry needed. */ + + const time_t now = approx_time(); + const int delay = + get_retry_schedule(guard->failing_since, now, guard->is_primary); + const time_t last_attempt = guard->last_tried_to_connect; + + if (BUG(last_attempt == 0) || + now >= last_attempt + delay) { + /* We should mark this retriable. */ + char tbuf[ISO_TIME_LEN+1]; + format_local_iso_time(tbuf, last_attempt); + log_info(LD_GUARD, "Marked %s%sguard %s for possible retry, since we " + "haven't tried to use it since %s.", + guard->is_primary?"primary ":"", + guard->confirmed_idx>=0?"confirmed ":"", + entry_guard_describe(guard), + tbuf); + + guard->is_reachable = GUARD_REACHABLE_MAYBE; + if (guard->is_filtered_guard) + guard->is_usable_filtered_guard = 1; + } +} + +/** Tell the entry guards subsystem that we have confirmed that as of + * just now, we're on the internet. */ +void +entry_guards_note_internet_connectivity(guard_selection_t *gs) +{ + gs->last_time_on_internet = approx_time(); +} + +/** + * Get a guard for use with a circuit. Prefer to pick a running primary + * guard; then a non-pending running filtered confirmed guard; then a + * non-pending runnable filtered guard. Update the + * <b>last_tried_to_connect</b> time and the <b>is_pending</b> fields of the + * guard as appropriate. Set <b>state_out</b> to the new guard-state + * of the circuit. + */ +STATIC entry_guard_t * +select_entry_guard_for_circuit(guard_selection_t *gs, + guard_usage_t usage, + const entry_guard_restriction_t *rst, + unsigned *state_out) +{ + /*XXXX prop271 consider splitting this function up. */ + tor_assert(gs); + tor_assert(state_out); + + if (!gs->primary_guards_up_to_date) + entry_guards_update_primary(gs); + + int num_entry_guards = get_n_primary_guards_to_use(usage); + smartlist_t *usable_primary_guards = smartlist_new(); + + /* "If any entry in PRIMARY_GUARDS has {is_reachable} status of + <maybe> or <yes>, return the first such guard." */ + SMARTLIST_FOREACH_BEGIN(gs->primary_entry_guards, entry_guard_t *, guard) { + entry_guard_consider_retry(guard); + if (! entry_guard_obeys_restriction(guard, rst)) + continue; + if (guard->is_reachable != GUARD_REACHABLE_NO) { + *state_out = GUARD_CIRC_STATE_USABLE_ON_COMPLETION; + guard->last_tried_to_connect = approx_time(); + smartlist_add(usable_primary_guards, guard); + if (smartlist_len(usable_primary_guards) >= num_entry_guards) + break; + } + } SMARTLIST_FOREACH_END(guard); + + if (smartlist_len(usable_primary_guards)) { + entry_guard_t *guard = smartlist_choose(usable_primary_guards); + smartlist_free(usable_primary_guards); + log_info(LD_GUARD, "Selected primary guard %s for circuit.", + entry_guard_describe(guard)); + return guard; + } + smartlist_free(usable_primary_guards); + + /* "Otherwise, if the ordered intersection of {CONFIRMED_GUARDS} + and {USABLE_FILTERED_GUARDS} is nonempty, return the first + entry in that intersection that has {is_pending} set to + false." */ + SMARTLIST_FOREACH_BEGIN(gs->confirmed_entry_guards, entry_guard_t *, guard) { + if (guard->is_primary) + continue; /* we already considered this one. */ + if (! entry_guard_obeys_restriction(guard, rst)) + continue; + entry_guard_consider_retry(guard); + if (guard->is_usable_filtered_guard && ! guard->is_pending) { + guard->is_pending = 1; + guard->last_tried_to_connect = approx_time(); + *state_out = GUARD_CIRC_STATE_USABLE_IF_NO_BETTER_GUARD; + log_info(LD_GUARD, "No primary guards available. Selected confirmed " + "guard %s for circuit. Will try other guards before using " + "this circuit.", + entry_guard_describe(guard)); + return guard; + } + } SMARTLIST_FOREACH_END(guard); + + /* "Otherwise, if there is no such entry, select a member at + random from {USABLE_FILTERED_GUARDS}." */ + { + entry_guard_t *guard; + guard = sample_reachable_filtered_entry_guards(gs, + rst, + SAMPLE_EXCLUDE_CONFIRMED | + SAMPLE_EXCLUDE_PRIMARY | + SAMPLE_EXCLUDE_PENDING); + if (guard == NULL) { + log_info(LD_GUARD, "Absolutely no sampled guards were available."); + return NULL; + } + guard->is_pending = 1; + guard->last_tried_to_connect = approx_time(); + *state_out = GUARD_CIRC_STATE_USABLE_IF_NO_BETTER_GUARD; + log_info(LD_GUARD, "No primary or confirmed guards available. Selected " + "random guard %s for circuit. Will try other guards before " + "using this circuit.", + entry_guard_describe(guard)); + return guard; + } +} + +/** + * Note that we failed to connect to or build circuits through <b>guard</b>. + * Use with a guard returned by select_entry_guard_for_circuit(). + */ +STATIC void +entry_guards_note_guard_failure(guard_selection_t *gs, + entry_guard_t *guard) +{ + tor_assert(gs); + + guard->is_reachable = GUARD_REACHABLE_NO; + guard->is_usable_filtered_guard = 0; + + guard->is_pending = 0; + if (guard->failing_since == 0) + guard->failing_since = approx_time(); + + log_info(LD_GUARD, "Recorded failure for %s%sguard %s", + guard->is_primary?"primary ":"", + guard->confirmed_idx>=0?"confirmed ":"", + entry_guard_describe(guard)); +} + +/** + * Called when the network comes up after having seemed to be down for + * a while: Mark the primary guards as maybe-reachable so that we'll + * try them again. + */ +STATIC void +mark_primary_guards_maybe_reachable(guard_selection_t *gs) +{ + if (!gs->primary_guards_up_to_date) + entry_guards_update_primary(gs); + + SMARTLIST_FOREACH_BEGIN(gs->primary_entry_guards, entry_guard_t *, guard) { + if (guard->is_reachable != GUARD_REACHABLE_NO) + continue; + + /* Note that we do not clear failing_since: this guard is now only + * _maybe-reachable_. */ + guard->is_reachable = GUARD_REACHABLE_MAYBE; + if (guard->is_filtered_guard) + guard->is_usable_filtered_guard = 1; + + } SMARTLIST_FOREACH_END(guard); +} + +/** + * Note that we successfully connected to, and built a circuit through + * <b>guard</b>. Given the old guard-state of the circuit in <b>old_state</b>, + * return the new guard-state of the circuit. + * + * Be aware: the circuit is only usable when its guard-state becomes + * GUARD_CIRC_STATE_COMPLETE. + **/ +STATIC unsigned +entry_guards_note_guard_success(guard_selection_t *gs, + entry_guard_t *guard, + unsigned old_state) +{ + tor_assert(gs); + + /* Save this, since we're about to overwrite it. */ + const time_t last_time_on_internet = gs->last_time_on_internet; + gs->last_time_on_internet = approx_time(); + + guard->is_reachable = GUARD_REACHABLE_YES; + guard->failing_since = 0; + guard->is_pending = 0; + if (guard->is_filtered_guard) + guard->is_usable_filtered_guard = 1; + + if (guard->confirmed_idx < 0) { + make_guard_confirmed(gs, guard); + if (!gs->primary_guards_up_to_date) + entry_guards_update_primary(gs); + } + + unsigned new_state; + switch (old_state) { + case GUARD_CIRC_STATE_COMPLETE: + case GUARD_CIRC_STATE_USABLE_ON_COMPLETION: + new_state = GUARD_CIRC_STATE_COMPLETE; + break; + default: + tor_assert_nonfatal_unreached(); + /* Fall through. */ + case GUARD_CIRC_STATE_USABLE_IF_NO_BETTER_GUARD: + if (guard->is_primary) { + /* XXXX prop271 -- I don't actually like this logic. It seems to make + * us a little more susceptible to evil-ISP attacks. The mitigations + * I'm thinking of, however, aren't local to this point, so I'll leave + * it alone. */ + /* This guard may have become primary by virtue of being confirmed. + * If so, the circuit for it is now complete. + */ + new_state = GUARD_CIRC_STATE_COMPLETE; + } else { + new_state = GUARD_CIRC_STATE_WAITING_FOR_BETTER_GUARD; + } + break; + } + + if (! guard->is_primary) { + if (last_time_on_internet + get_internet_likely_down_interval() + < approx_time()) { + mark_primary_guards_maybe_reachable(gs); + } + } + + log_info(LD_GUARD, "Recorded success for %s%sguard %s", + guard->is_primary?"primary ":"", + guard->confirmed_idx>=0?"confirmed ":"", + entry_guard_describe(guard)); + + return new_state; +} + +/** + * Helper: Return true iff <b>a</b> has higher priority than <b>b</b>. + */ +STATIC int +entry_guard_has_higher_priority(entry_guard_t *a, entry_guard_t *b) +{ + tor_assert(a && b); + if (a == b) + return 0; + + /* Confirmed is always better than unconfirmed; lower index better + than higher */ + if (a->confirmed_idx < 0) { + if (b->confirmed_idx >= 0) + return 0; + } else { + if (b->confirmed_idx < 0) + return 1; + + /* Lower confirmed_idx is better than higher. */ + return (a->confirmed_idx < b->confirmed_idx); + } + + /* If we reach this point, both are unconfirmed. If one is pending, it + * has higher priority. */ + if (a->is_pending) { + if (! b->is_pending) + return 1; + + /* Both are pending: earlier last_tried_connect wins. */ + return a->last_tried_to_connect < b->last_tried_to_connect; + } else { + if (b->is_pending) + return 0; + + /* Neither is pending: priorities are equal. */ + return 0; // XXXX prop271 return a tristate instead? + } +} + +/** Release all storage held in <b>restriction</b> */ +static void +entry_guard_restriction_free(entry_guard_restriction_t *rst) +{ + tor_free(rst); +} + +/** + * Release all storage held in <b>state</b>. + */ +void +circuit_guard_state_free(circuit_guard_state_t *state) +{ + if (!state) + return; + entry_guard_restriction_free(state->restrictions); + entry_guard_handle_free(state->guard); + tor_free(state); +} + +/** + * Pick a suitable entry guard for a circuit in, and place that guard + * in *<b>chosen_node_out</b>. Set *<b>guard_state_out</b> to an opaque + * state object that will record whether the circuit is ready to be used + * or not. Return 0 on success; on failure, return -1. + * + * If a restriction is provided in <b>rst</b>, do not return any guards that + * violate it, and remember that restriction in <b>guard_state_out</b> for + * later use. (Takes ownership of the <b>rst</b> object.) + */ +int +entry_guard_pick_for_circuit(guard_selection_t *gs, + guard_usage_t usage, + entry_guard_restriction_t *rst, + const node_t **chosen_node_out, + circuit_guard_state_t **guard_state_out) +{ + tor_assert(gs); + tor_assert(chosen_node_out); + tor_assert(guard_state_out); + *chosen_node_out = NULL; + *guard_state_out = NULL; + + unsigned state = 0; + entry_guard_t *guard = + select_entry_guard_for_circuit(gs, usage, rst, &state); + if (! guard) + goto fail; + if (BUG(state == 0)) + goto fail; + const node_t *node = node_get_by_id(guard->identity); + // XXXX prop271 check Ed ID. + if (! node) + goto fail; + + *chosen_node_out = node; + *guard_state_out = tor_malloc_zero(sizeof(circuit_guard_state_t)); + (*guard_state_out)->guard = entry_guard_handle_new(guard); + (*guard_state_out)->state = state; + (*guard_state_out)->state_set_at = approx_time(); + (*guard_state_out)->restrictions = rst; + + return 0; + fail: + entry_guard_restriction_free(rst); + return -1; +} + +/** + * Called by the circuit building module when a circuit has succeeded: informs + * the guards code that the guard in *<b>guard_state_p</b> is working, and + * advances the state of the guard module. On a GUARD_USABLE_NEVER return + * value, the circuit is broken and should not be used. On a GUARD_USABLE_NOW + * return value, the circuit is ready to use. On a GUARD_MAYBE_USABLE_LATER + * return value, the circuit should not be used until we find out whether + * preferred guards will work for us. + */ +guard_usable_t +entry_guard_succeeded(circuit_guard_state_t **guard_state_p) +{ + if (get_options()->UseDeprecatedGuardAlgorithm) + return GUARD_USABLE_NOW; + + if (BUG(*guard_state_p == NULL)) + return GUARD_USABLE_NEVER; + + entry_guard_t *guard = entry_guard_handle_get((*guard_state_p)->guard); + if (! guard || BUG(guard->in_selection == NULL)) + return GUARD_USABLE_NEVER; + + unsigned newstate = + entry_guards_note_guard_success(guard->in_selection, guard, + (*guard_state_p)->state); + + (*guard_state_p)->state = newstate; + (*guard_state_p)->state_set_at = approx_time(); + + if (newstate == GUARD_CIRC_STATE_COMPLETE) { + return GUARD_USABLE_NOW; + } else { + return GUARD_MAYBE_USABLE_LATER; + } +} + +/** Cancel the selection of *<b>guard_state_p</b> without declaring + * success or failure. It is safe to call this function if success or + * failure _has_ already been declared. */ +void +entry_guard_cancel(circuit_guard_state_t **guard_state_p) +{ + if (get_options()->UseDeprecatedGuardAlgorithm) + return; + if (BUG(*guard_state_p == NULL)) + return; + entry_guard_t *guard = entry_guard_handle_get((*guard_state_p)->guard); + if (! guard) + return; + + /* XXXX prop271 -- last_tried_to_connect_at will be erroneous here, but this + * function will only get called in "bug" cases anyway. */ + guard->is_pending = 0; + circuit_guard_state_free(*guard_state_p); + *guard_state_p = NULL; +} + +/** + * Called by the circuit building module when a circuit has succeeded: + * informs the guards code that the guard in *<b>guard_state_p</b> is + * not working, and advances the state of the guard module. + */ +void +entry_guard_failed(circuit_guard_state_t **guard_state_p) +{ + if (get_options()->UseDeprecatedGuardAlgorithm) + return; + + if (BUG(*guard_state_p == NULL)) + return; + + entry_guard_t *guard = entry_guard_handle_get((*guard_state_p)->guard); + if (! guard || BUG(guard->in_selection == NULL)) + return; + + entry_guards_note_guard_failure(guard->in_selection, guard); + + (*guard_state_p)->state = GUARD_CIRC_STATE_DEAD; + (*guard_state_p)->state_set_at = approx_time(); +} + +/** + * Run the entry_guard_failed() function on every circuit that is + * pending on <b>chan</b>. + */ +void +entry_guard_chan_failed(channel_t *chan) +{ + if (!chan) + return; + if (get_options()->UseDeprecatedGuardAlgorithm) + return; + + smartlist_t *pending = smartlist_new(); + circuit_get_all_pending_on_channel(pending, chan); + SMARTLIST_FOREACH_BEGIN(pending, circuit_t *, circ) { + if (!CIRCUIT_IS_ORIGIN(circ)) + continue; + + origin_circuit_t *origin_circ = TO_ORIGIN_CIRCUIT(circ); + entry_guard_failed(&origin_circ->guard_state); + } SMARTLIST_FOREACH_END(circ); + smartlist_free(pending); +} + +/** + * Return true iff every primary guard in <b>gs</b> is believed to + * be unreachable. + */ +STATIC int +entry_guards_all_primary_guards_are_down(guard_selection_t *gs) +{ + tor_assert(gs); + if (!gs->primary_guards_up_to_date) + entry_guards_update_primary(gs); + SMARTLIST_FOREACH_BEGIN(gs->primary_entry_guards, entry_guard_t *, guard) { + entry_guard_consider_retry(guard); + if (guard->is_reachable != GUARD_REACHABLE_NO) + return 0; + } SMARTLIST_FOREACH_END(guard); + return 1; +} + +/** Wrapper for entry_guard_has_higher_priority that compares the + * guard-priorities of a pair of circuits. Return 1 if <b>a</b> has higher + * priority than <b>b</b>. + * + * If a restriction is provided in <b>rst</b>, then do not consider + * <b>a</b> to have higher priority if it violates the restriction. + */ +static int +circ_state_has_higher_priority(origin_circuit_t *a, + const entry_guard_restriction_t *rst, + origin_circuit_t *b) +{ + circuit_guard_state_t *state_a = origin_circuit_get_guard_state(a); + circuit_guard_state_t *state_b = origin_circuit_get_guard_state(b); + + tor_assert(state_a); + tor_assert(state_b); + + entry_guard_t *guard_a = entry_guard_handle_get(state_a->guard); + entry_guard_t *guard_b = entry_guard_handle_get(state_b->guard); + + if (! guard_a) { + /* Unknown guard -- never higher priority. */ + return 0; + } else if (! guard_b) { + /* Known guard -- higher priority than any unknown guard. */ + return 1; + } else if (! entry_guard_obeys_restriction(guard_a, rst)) { + /* Restriction violated; guard_a cannot have higher priority. */ + return 0; + } else { + /* Both known -- compare.*/ + return entry_guard_has_higher_priority(guard_a, guard_b); + } +} + +/** + * Look at all of the origin_circuit_t * objects in <b>all_circuits_in</b>, + * and see if any of them that were previously not ready to use for + * guard-related reasons are now ready to use. Place those circuits + * in <b>newly_complete_out</b>, and mark them COMPLETE. + * + * Return 1 if we upgraded any circuits, and 0 otherwise. + */ +int +entry_guards_upgrade_waiting_circuits(guard_selection_t *gs, + const smartlist_t *all_circuits_in, + smartlist_t *newly_complete_out) +{ + tor_assert(gs); + tor_assert(all_circuits_in); + tor_assert(newly_complete_out); + + if (! entry_guards_all_primary_guards_are_down(gs)) { + /* We only upgrade a waiting circuit if the primary guards are all + * down. */ + log_debug(LD_GUARD, "Considered upgrading guard-stalled circuits, " + "but not all primary guards were definitely down."); + return 0; + } + + int n_waiting = 0; + int n_complete = 0; + int n_complete_blocking = 0; + origin_circuit_t *best_waiting_circuit = NULL; + smartlist_t *all_circuits = smartlist_new(); + SMARTLIST_FOREACH_BEGIN(all_circuits_in, origin_circuit_t *, circ) { + // We filter out circuits that aren't ours, or which we can't + // reason about. + circuit_guard_state_t *state = origin_circuit_get_guard_state(circ); + if (state == NULL) + continue; + entry_guard_t *guard = entry_guard_handle_get(state->guard); + if (!guard || guard->in_selection != gs) + continue; + + smartlist_add(all_circuits, circ); + } SMARTLIST_FOREACH_END(circ); + + SMARTLIST_FOREACH_BEGIN(all_circuits, origin_circuit_t *, circ) { + circuit_guard_state_t *state = origin_circuit_get_guard_state(circ); + if BUG((state == NULL)) + continue; + + if (state->state == GUARD_CIRC_STATE_WAITING_FOR_BETTER_GUARD) { + ++n_waiting; + if (! best_waiting_circuit || + circ_state_has_higher_priority(circ, NULL, best_waiting_circuit)) { + best_waiting_circuit = circ; + } + } + } SMARTLIST_FOREACH_END(circ); + + if (! best_waiting_circuit) { + log_debug(LD_GUARD, "Considered upgrading guard-stalled circuits, " + "but didn't find any."); + goto no_change; + } + + /* We'll need to keep track of what restrictions were used when picking this + * circuit, so that we don't allow any circuit without those restrictions to + * block it. */ + const entry_guard_restriction_t *rst_on_best_waiting = + origin_circuit_get_guard_state(best_waiting_circuit)->restrictions; + + /* First look at the complete circuits: Do any block this circuit? */ + SMARTLIST_FOREACH_BEGIN(all_circuits, origin_circuit_t *, circ) { + circuit_guard_state_t *state = origin_circuit_get_guard_state(circ); + if BUG((state == NULL)) + continue; + if (state->state != GUARD_CIRC_STATE_COMPLETE) + continue; + ++n_complete; + if (circ_state_has_higher_priority(circ, rst_on_best_waiting, + best_waiting_circuit)) + ++n_complete_blocking; + } SMARTLIST_FOREACH_END(circ); + + if (n_complete_blocking) { + /* "If any circuit is <complete>, then do not use any + <waiting_for_better_guard> or <usable_if_no_better_guard> circuits + circuits whose guards have lower priority." */ + log_debug(LD_GUARD, "Considered upgrading guard-stalled circuits: found " + "%d complete and %d guard-stalled. At least one complete " + "circuit had higher priority, so not upgrading.", + n_complete, n_waiting); + goto no_change; + } + + /* "If any circuit is <waiting_for_better_guard>, and every currently + {is_pending} circuit whose guard has higher priority has been in + state <usable_if_no_better_guard> for at least + {NONPRIMARY_GUARD_CONNECT_TIMEOUT} seconds, and all primary guards + have reachable status of <no>, then call that circuit <complete>." + + XXXX --- prop271 deviation. there's no such thing in the spec as + an {is_pending circuit}; fix the spec. + */ + int n_blockers_found = 0; + const time_t state_set_at_cutoff = + approx_time() - get_nonprimary_guard_connect_timeout(); + SMARTLIST_FOREACH_BEGIN(all_circuits, origin_circuit_t *, circ) { + circuit_guard_state_t *state = origin_circuit_get_guard_state(circ); + if (BUG(state == NULL)) + continue; + if (state->state != GUARD_CIRC_STATE_USABLE_IF_NO_BETTER_GUARD) + continue; + if (state->state_set_at <= state_set_at_cutoff) + continue; + if (circ_state_has_higher_priority(circ, rst_on_best_waiting, + best_waiting_circuit)) + ++n_blockers_found; + } SMARTLIST_FOREACH_END(circ); + + if (n_blockers_found) { + log_debug(LD_GUARD, "Considered upgrading guard-stalled circuits: found " + "%d guard-stalled, but %d pending circuit(s) had higher " + "guard priority, so not upgrading.", + n_waiting, n_blockers_found); + goto no_change; + } + + /* Okay. We have a best waiting circuit, and we aren't waiting for + anything better. Add all circuits with that priority to the + list, and call them COMPLETE. */ + int n_succeeded = 0; + SMARTLIST_FOREACH_BEGIN(all_circuits, origin_circuit_t *, circ) { + circuit_guard_state_t *state = origin_circuit_get_guard_state(circ); + if (BUG(state == NULL)) + continue; + if (circ != best_waiting_circuit && rst_on_best_waiting) { + /* Can't upgrade other circ with same priority as best; might + be blocked. */ + continue; + } + if (state->state != GUARD_CIRC_STATE_WAITING_FOR_BETTER_GUARD) + continue; + if (circ_state_has_higher_priority(best_waiting_circuit, NULL, circ)) + continue; + + state->state = GUARD_CIRC_STATE_COMPLETE; + state->state_set_at = approx_time(); + smartlist_add(newly_complete_out, circ); + ++n_succeeded; + } SMARTLIST_FOREACH_END(circ); + + log_info(LD_GUARD, "Considered upgrading guard-stalled circuits: found " + "%d guard-stalled, %d complete. %d of the guard-stalled " + "circuit(s) had high enough priority to upgrade.", + n_waiting, n_complete, n_succeeded); + + tor_assert_nonfatal(n_succeeded >= 1); + smartlist_free(all_circuits); + return 1; + + no_change: + smartlist_free(all_circuits); + return 0; +} + +/** + * Return true iff the circuit whose state is <b>guard_state</b> should + * expire. + */ +int +entry_guard_state_should_expire(circuit_guard_state_t *guard_state) +{ + if (guard_state == NULL) + return 0; + const time_t expire_if_waiting_since = + approx_time() - get_nonprimary_guard_idle_timeout(); + return (guard_state->state == GUARD_CIRC_STATE_WAITING_FOR_BETTER_GUARD + && guard_state->state_set_at < expire_if_waiting_since); +} + +/** + * Update all derived pieces of the guard selection state in <b>gs</b>. + * Return true iff we should stop using all previously generated circuits. + */ +int +entry_guards_update_all(guard_selection_t *gs) +{ + sampled_guards_update_from_consensus(gs); + entry_guards_update_filtered_sets(gs); + entry_guards_update_confirmed(gs); + entry_guards_update_primary(gs); + return 0; +} + +/** + * Return a newly allocated string for encoding the persistent parts of + * <b>guard</b> to the state file. + */ +STATIC char * +entry_guard_encode_for_state(entry_guard_t *guard) +{ + /* + * The meta-format we use is K=V K=V K=V... where K can be any + * characters excepts space and =, and V can be any characters except + * space. The order of entries is not allowed to matter. + * Unrecognized K=V entries are persisted; recognized but erroneous + * entries are corrected. + */ + + smartlist_t *result = smartlist_new(); + char tbuf[ISO_TIME_LEN+1]; + + tor_assert(guard); + + smartlist_add_asprintf(result, "in=%s", guard->selection_name); + smartlist_add_asprintf(result, "rsa_id=%s", + hex_str(guard->identity, DIGEST_LEN)); + if (guard->bridge_addr) { + smartlist_add_asprintf(result, "bridge_addr=%s:%d", + fmt_and_decorate_addr(&guard->bridge_addr->addr), + guard->bridge_addr->port); + } + if (strlen(guard->nickname) && is_legal_nickname(guard->nickname)) { + smartlist_add_asprintf(result, "nickname=%s", guard->nickname); + } + + format_iso_time_nospace(tbuf, guard->sampled_on_date); + smartlist_add_asprintf(result, "sampled_on=%s", tbuf); + + if (guard->sampled_by_version) { + smartlist_add_asprintf(result, "sampled_by=%s", + guard->sampled_by_version); + } + + if (guard->unlisted_since_date > 0) { + format_iso_time_nospace(tbuf, guard->unlisted_since_date); + smartlist_add_asprintf(result, "unlisted_since=%s", tbuf); + } + + smartlist_add_asprintf(result, "listed=%d", + (int)guard->currently_listed); + + if (guard->confirmed_idx >= 0) { + format_iso_time_nospace(tbuf, guard->confirmed_on_date); + smartlist_add_asprintf(result, "confirmed_on=%s", tbuf); + + smartlist_add_asprintf(result, "confirmed_idx=%d", guard->confirmed_idx); + } + + const double EPSILON = 1.0e-6; + + /* Make a copy of the pathbias object, since we will want to update + some of them */ + guard_pathbias_t *pb = tor_memdup(&guard->pb, sizeof(*pb)); + pb->use_successes = pathbias_get_use_success_count(guard); + pb->successful_circuits_closed = pathbias_get_close_success_count(guard); + + #define PB_FIELD(field) do { \ + if (pb->field >= EPSILON) { \ + smartlist_add_asprintf(result, "pb_" #field "=%f", pb->field); \ + } \ + } while (0) + PB_FIELD(use_attempts); + PB_FIELD(use_successes); + PB_FIELD(circ_attempts); + PB_FIELD(circ_successes); + PB_FIELD(successful_circuits_closed); + PB_FIELD(collapsed_circuits); + PB_FIELD(unusable_circuits); + PB_FIELD(timeouts); + tor_free(pb); +#undef PB_FIELD + + if (guard->extra_state_fields) + smartlist_add_strdup(result, guard->extra_state_fields); + + char *joined = smartlist_join_strings(result, " ", 0, NULL); + SMARTLIST_FOREACH(result, char *, cp, tor_free(cp)); + smartlist_free(result); + + return joined; +} + +/** + * Given a string generated by entry_guard_encode_for_state(), parse it + * (if possible) and return an entry_guard_t object for it. Return NULL + * on complete failure. + */ +STATIC entry_guard_t * +entry_guard_parse_from_state(const char *s) +{ + /* Unrecognized entries get put in here. */ + smartlist_t *extra = smartlist_new(); + + /* These fields get parsed from the string. */ + char *in = NULL; + char *rsa_id = NULL; + char *nickname = NULL; + char *sampled_on = NULL; + char *sampled_by = NULL; + char *unlisted_since = NULL; + char *listed = NULL; + char *confirmed_on = NULL; + char *confirmed_idx = NULL; + char *bridge_addr = NULL; + + // pathbias + char *pb_use_attempts = NULL; + char *pb_use_successes = NULL; + char *pb_circ_attempts = NULL; + char *pb_circ_successes = NULL; + char *pb_successful_circuits_closed = NULL; + char *pb_collapsed_circuits = NULL; + char *pb_unusable_circuits = NULL; + char *pb_timeouts = NULL; + + /* Split up the entries. Put the ones we know about in strings and the + * rest in "extra". */ + { + smartlist_t *entries = smartlist_new(); + + strmap_t *vals = strmap_new(); // Maps keyword to location +#define FIELD(f) \ + strmap_set(vals, #f, &f); + FIELD(in); + FIELD(rsa_id); + FIELD(nickname); + FIELD(sampled_on); + FIELD(sampled_by); + FIELD(unlisted_since); + FIELD(listed); + FIELD(confirmed_on); + FIELD(confirmed_idx); + FIELD(bridge_addr); + FIELD(pb_use_attempts); + FIELD(pb_use_successes); + FIELD(pb_circ_attempts); + FIELD(pb_circ_successes); + FIELD(pb_successful_circuits_closed); + FIELD(pb_collapsed_circuits); + FIELD(pb_unusable_circuits); + FIELD(pb_timeouts); +#undef FIELD + + smartlist_split_string(entries, s, " ", + SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); + + SMARTLIST_FOREACH_BEGIN(entries, char *, entry) { + const char *eq = strchr(entry, '='); + if (!eq) { + smartlist_add(extra, entry); + continue; + } + char *key = tor_strndup(entry, eq-entry); + char **target = strmap_get(vals, key); + if (target == NULL || *target != NULL) { + /* unrecognized or already set */ + smartlist_add(extra, entry); + tor_free(key); + continue; + } + + *target = tor_strdup(eq+1); + tor_free(key); + tor_free(entry); + } SMARTLIST_FOREACH_END(entry); + + smartlist_free(entries); + strmap_free(vals, NULL); + } + + entry_guard_t *guard = tor_malloc_zero(sizeof(entry_guard_t)); + guard->is_persistent = 1; + + if (in == NULL) { + log_warn(LD_CIRC, "Guard missing 'in' field"); + goto err; + } + + guard->selection_name = in; + in = NULL; + + if (rsa_id == NULL) { + log_warn(LD_CIRC, "Guard missing RSA ID field"); + goto err; + } + + /* Process the identity and nickname. */ + if (base16_decode(guard->identity, sizeof(guard->identity), + rsa_id, strlen(rsa_id)) != DIGEST_LEN) { + log_warn(LD_CIRC, "Unable to decode guard identity %s", escaped(rsa_id)); + goto err; + } + + if (nickname) { + strlcpy(guard->nickname, nickname, sizeof(guard->nickname)); + } else { + guard->nickname[0]='$'; + base16_encode(guard->nickname+1, sizeof(guard->nickname)-1, + guard->identity, DIGEST_LEN); + } + + if (bridge_addr) { + tor_addr_port_t res; + memset(&res, 0, sizeof(res)); + int r = tor_addr_port_parse(LOG_WARN, bridge_addr, + &res.addr, &res.port, -1); + if (r == 0) + guard->bridge_addr = tor_memdup(&res, sizeof(res)); + /* On error, we already warned. */ + } + + /* Process the various time fields. */ + +#define HANDLE_TIME(field) do { \ + if (field) { \ + int r = parse_iso_time_nospace(field, &field ## _time); \ + if (r < 0) { \ + log_warn(LD_CIRC, "Unable to parse %s %s from guard", \ + #field, escaped(field)); \ + field##_time = -1; \ + } \ + } \ + } while (0) + + time_t sampled_on_time = 0; + time_t unlisted_since_time = 0; + time_t confirmed_on_time = 0; + + HANDLE_TIME(sampled_on); + HANDLE_TIME(unlisted_since); + HANDLE_TIME(confirmed_on); + + if (sampled_on_time <= 0) + sampled_on_time = approx_time(); + if (unlisted_since_time < 0) + unlisted_since_time = 0; + if (confirmed_on_time < 0) + confirmed_on_time = 0; + + #undef HANDLE_TIME + + guard->sampled_on_date = sampled_on_time; + guard->unlisted_since_date = unlisted_since_time; + guard->confirmed_on_date = confirmed_on_time; + + /* Take sampled_by_version verbatim. */ + guard->sampled_by_version = sampled_by; + sampled_by = NULL; /* prevent free */ + // XXXX -- prop271 spec deviation -- we do not require sampled_by_version + + /* Listed is a boolean */ + if (listed && strcmp(listed, "0")) + guard->currently_listed = 1; + + /* The index is a nonnegative integer. */ + guard->confirmed_idx = -1; + if (confirmed_idx) { + int ok=1; + long idx = tor_parse_long(confirmed_idx, 10, 0, INT_MAX, &ok, NULL); + if (! ok) { + log_warn(LD_GUARD, "Guard has invalid confirmed_idx %s", + escaped(confirmed_idx)); + } else { + guard->confirmed_idx = (int)idx; + } + } + + /* Anything we didn't recognize gets crammed together */ + if (smartlist_len(extra) > 0) { + guard->extra_state_fields = smartlist_join_strings(extra, " ", 0, NULL); + } + + /* initialize non-persistent fields */ + guard->is_reachable = GUARD_REACHABLE_MAYBE; + +#define PB_FIELD(field) \ + do { \ + if (pb_ ## field) { \ + int ok = 1; \ + double r = tor_parse_double(pb_ ## field, 0.0, 1e9, &ok, NULL); \ + if (! ok) { \ + log_warn(LD_CIRC, "Guard has invalid pb_%s %s", \ + #field, pb_ ## field); \ + } else { \ + guard->pb.field = r; \ + } \ + } \ + } while (0) + PB_FIELD(use_attempts); + PB_FIELD(use_successes); + PB_FIELD(circ_attempts); + PB_FIELD(circ_successes); + PB_FIELD(successful_circuits_closed); + PB_FIELD(collapsed_circuits); + PB_FIELD(unusable_circuits); + PB_FIELD(timeouts); +#undef PB_FIELD + + pathbias_check_use_success_count(guard); + pathbias_check_close_success_count(guard); + + /* We update everything on this guard later, after we've parsed + * everything. */ + + goto done; + + err: + // only consider it an error if the guard state was totally unparseable. + entry_guard_free(guard); + guard = NULL; + + done: + tor_free(in); + tor_free(rsa_id); + tor_free(nickname); + tor_free(sampled_on); + tor_free(sampled_by); + tor_free(unlisted_since); + tor_free(listed); + tor_free(confirmed_on); + tor_free(confirmed_idx); + tor_free(bridge_addr); + tor_free(pb_use_attempts); + tor_free(pb_use_successes); + tor_free(pb_circ_attempts); + tor_free(pb_circ_successes); + tor_free(pb_successful_circuits_closed); + tor_free(pb_collapsed_circuits); + tor_free(pb_unusable_circuits); + tor_free(pb_timeouts); + + SMARTLIST_FOREACH(extra, char *, cp, tor_free(cp)); + smartlist_free(extra); + + return guard; +} + +/** + * Replace the Guards entries in <b>state</b> with a list of all our + * non-legacy sampled guards. + */ +static void +entry_guards_update_guards_in_state(or_state_t *state) +{ + if (!guard_contexts) + return; + config_line_t *lines = NULL; + config_line_t **nextline = &lines; + + SMARTLIST_FOREACH_BEGIN(guard_contexts, guard_selection_t *, gs) { + if (!strcmp(gs->name, "legacy")) + continue; /* This is encoded differently. */ + SMARTLIST_FOREACH_BEGIN(gs->sampled_entry_guards, entry_guard_t *, guard) { + if (guard->is_persistent == 0) + continue; + *nextline = tor_malloc_zero(sizeof(config_line_t)); + (*nextline)->key = tor_strdup("Guard"); + (*nextline)->value = entry_guard_encode_for_state(guard); + nextline = &(*nextline)->next; + } SMARTLIST_FOREACH_END(guard); + } SMARTLIST_FOREACH_END(gs); + + config_free_lines(state->Guard); + state->Guard = lines; +} + +/** + * Replace our non-legacy sampled guards from the Guards entries in + * <b>state</b>. Return 0 on success, -1 on failure. (If <b>set</b> is + * true, replace nothing -- only check whether replacing would work.) + */ +static int +entry_guards_load_guards_from_state(or_state_t *state, int set) +{ + const config_line_t *line = state->Guard; + int n_errors = 0; + + if (!guard_contexts) + guard_contexts = smartlist_new(); + + /* Wipe all our existing guard info. (we shouldn't have any, but + * let's be safe.) */ + if (set) { + SMARTLIST_FOREACH_BEGIN(guard_contexts, guard_selection_t *, gs) { + if (!strcmp(gs->name, "legacy")) + continue; + guard_selection_free(gs); + if (curr_guard_context == gs) + curr_guard_context = NULL; + SMARTLIST_DEL_CURRENT(guard_contexts, gs); + } SMARTLIST_FOREACH_END(gs); + } + + for ( ; line != NULL; line = line->next) { + entry_guard_t *guard = entry_guard_parse_from_state(line->value); + if (guard == NULL) { + ++n_errors; + continue; + } + tor_assert(guard->selection_name); + if (!strcmp(guard->selection_name, "legacy")) { + ++n_errors; + entry_guard_free(guard); + continue; + } + + if (set) { + guard_selection_t *gs; + gs = get_guard_selection_by_name(guard->selection_name, + GS_TYPE_INFER, 1); + tor_assert(gs); + smartlist_add(gs->sampled_entry_guards, guard); + guard->in_selection = gs; + } else { + entry_guard_free(guard); + } + } + + if (set) { + SMARTLIST_FOREACH_BEGIN(guard_contexts, guard_selection_t *, gs) { + if (!strcmp(gs->name, "legacy")) + continue; + entry_guards_update_all(gs); + } SMARTLIST_FOREACH_END(gs); + } + return n_errors ? -1 : 0; +} + +/* XXXXX ----------------------------------------------- */ +/* XXXXX prop271 ----- end of new-for-prop271 code ----- */ +/* XXXXX ----------------------------------------------- */ + +#ifdef ENABLE_LEGACY_GUARD_ALGORITHM +/** + * @name Constants for old (pre-prop271) guard selection algorithm. + */ + +/**@{*/ + +/* Default number of entry guards in the case where the NumEntryGuards + * consensus parameter is not set */ +#define DEFAULT_N_GUARDS 1 +/* Minimum and maximum number of entry guards (in case the NumEntryGuards + * consensus parameter is set). */ +#define MIN_N_GUARDS 1 +#define MAX_N_GUARDS 10 +/** Largest amount that we'll backdate chosen_on_date */ +#define CHOSEN_ON_DATE_SLOP (30*86400) +/** How long (in seconds) do we allow an entry guard to be nonfunctional, + * unlisted, excluded, or otherwise nonusable before we give up on it? */ +#define ENTRY_GUARD_REMOVE_AFTER (30*24*60*60) +/**}@*/ + +/** + * @name Networkstatus parameters for old (pre-prop271) guard selection + */ +/**@}*/ +/** Choose how many entry guards or directory guards we'll use. If + * <b>for_directory</b> is true, we return how many directory guards to + * use; else we return how many entry guards to use. */ +STATIC int +decide_num_guards(const or_options_t *options, int for_directory) +{ + if (for_directory) { + int answer; + if (options->NumDirectoryGuards != 0) + return options->NumDirectoryGuards; + answer = networkstatus_get_param(NULL, "NumDirectoryGuards", 0, 0, 10); + if (answer) /* non-zero means use the consensus value */ + return answer; + } + + if (options->NumEntryGuards) + return options->NumEntryGuards; + + /* Use the value from the consensus, or 3 if no guidance. */ + return networkstatus_get_param(NULL, "NumEntryGuards", DEFAULT_N_GUARDS, + MIN_N_GUARDS, MAX_N_GUARDS); +} + /** Check whether the entry guard <b>e</b> is usable, given the directory * authorities' opinion about the router (stored in <b>ri</b>) and the user's * configuration (in <b>options</b>). Set <b>e</b>->bad_since @@ -192,7 +3014,7 @@ entry_guard_set_status(entry_guard_t *e, const node_t *node, /* We only care about OR connection connectivity for entry guards. */ else if (!fascist_firewall_allows_node(node, FIREWALL_OR_CONNECTION, 0)) *reason = "unreachable by config"; - else if (e->path_bias_disabled) + else if (e->pb.path_bias_disabled) *reason = "path-biased"; if (*reason && ! e->bad_since) { @@ -297,7 +3119,7 @@ entry_is_live(const entry_guard_t *e, entry_is_live_flags_t flags, tor_assert(msg); - if (e->path_bias_disabled) { + if (e->pb.path_bias_disabled) { *msg = "path-biased"; return NULL; } @@ -388,6 +3210,7 @@ num_live_entry_guards(int for_directory) return num_live_entry_guards_for_guard_selection( get_guard_selection_info(), for_directory); } +#endif /** If <b>digest</b> matches the identity of any node in the * entry_guards list for the provided guard selection state, @@ -398,13 +3221,28 @@ entry_guard_get_by_id_digest_for_guard_selection(guard_selection_t *gs, { tor_assert(gs != NULL); + SMARTLIST_FOREACH(gs->sampled_entry_guards, entry_guard_t *, entry, + if (tor_memeq(digest, entry->identity, DIGEST_LEN)) + return entry; + ); +#ifdef ENABLE_LEGACY_GUARD_ALGORITHM SMARTLIST_FOREACH(gs->chosen_entry_guards, entry_guard_t *, entry, if (tor_memeq(digest, entry->identity, DIGEST_LEN)) return entry; ); +#endif return NULL; } +/** Return the node_t associated with a single entry_guard_t. May + * return NULL if the guard is not currently in the consensus. */ +const node_t * +entry_guard_find_node(const entry_guard_t *guard) +{ + tor_assert(guard); + return node_get_by_id(guard->identity); +} + /** If <b>digest</b> matches the identity of any node in the * entry_guards list for the default guard selection state, return that node. Else return NULL. */ @@ -415,6 +3253,7 @@ entry_guard_get_by_id_digest(const char *digest) get_guard_selection_info(), digest); } +#ifdef ENABLE_LEGACY_GUARD_ALGORITHM /** Dump a description of our list of entry guards in the given guard * selection context to the log at level <b>severity</b>. */ static void @@ -484,9 +3323,6 @@ control_event_guard_deferred(void) #endif } -/** Largest amount that we'll backdate chosen_on_date */ -#define CHOSEN_ON_DATE_SLOP (30*86400) - /** Add a new (preferably stable and fast) router to our chosen_entry_guards * list for the supplied guard selection. Return a pointer to the router if * we succeed, or NULL if we can't find any more suitable entries. @@ -521,7 +3357,7 @@ add_an_entry_guard(guard_selection_t *gs, return NULL; } } else if (!for_directory) { - node = choose_good_entry_server(CIRCUIT_PURPOSE_C_GENERAL, NULL); + node = choose_good_entry_server(CIRCUIT_PURPOSE_C_GENERAL, NULL, NULL); if (!node) return NULL; } else { @@ -542,6 +3378,7 @@ add_an_entry_guard(guard_selection_t *gs, return NULL; } entry = tor_malloc_zero(sizeof(entry_guard_t)); + entry->is_persistent = 1; log_info(LD_CIRC, "Chose %s as new entry guard.", node_describe(node)); strlcpy(entry->nickname, node_get_nickname(node), sizeof(entry->nickname)); @@ -574,6 +3411,7 @@ add_an_entry_guard(guard_selection_t *gs, smartlist_insert(gs->chosen_entry_guards, 0, entry); else smartlist_add(gs->chosen_entry_guards, entry); + entry->in_selection = gs; control_event_guard(entry->nickname, entry->identity, "NEW"); control_event_guard_deferred(); @@ -582,27 +3420,40 @@ add_an_entry_guard(guard_selection_t *gs, return node; } -/** Choose how many entry guards or directory guards we'll use. If - * <b>for_directory</b> is true, we return how many directory guards to - * use; else we return how many entry guards to use. */ -STATIC int -decide_num_guards(const or_options_t *options, int for_directory) +/** Entry point for bridges.c to add a bridge as guard. + * + * XXXX prop271 refactor, bridge.*/ +void +add_bridge_as_entry_guard(guard_selection_t *gs, + const node_t *chosen) { - if (for_directory) { - int answer; - if (options->NumDirectoryGuards != 0) - return options->NumDirectoryGuards; - answer = networkstatus_get_param(NULL, "NumDirectoryGuards", 0, 0, 10); - if (answer) /* non-zero means use the consensus value */ - return answer; - } + add_an_entry_guard(gs, chosen, 1, 1, 0, 0); +} - if (options->NumEntryGuards) - return options->NumEntryGuards; +/** + * Return the minimum lifetime of working entry guard, in seconds, + * as given in the consensus networkstatus. (Plus CHOSEN_ON_DATE_SLOP, + * so that we can do the chosen_on_date randomization while achieving the + * desired minimum lifetime.) + */ +static int32_t +guards_get_lifetime(void) +{ + const or_options_t *options = get_options(); +#define DFLT_GUARD_LIFETIME (86400 * 60) /* Two months. */ +#define MIN_GUARD_LIFETIME (86400 * 30) /* One months. */ +#define MAX_GUARD_LIFETIME (86400 * 1826) /* Five years. */ - /* Use the value from the consensus, or 3 if no guidance. */ - return networkstatus_get_param(NULL, "NumEntryGuards", DEFAULT_N_GUARDS, - MIN_N_GUARDS, MAX_N_GUARDS); + if (options->GuardLifetime >= 1) { + return CLAMP(MIN_GUARD_LIFETIME, + options->GuardLifetime, + MAX_GUARD_LIFETIME) + CHOSEN_ON_DATE_SLOP; + } + + return networkstatus_get_param(NULL, "GuardLifetime", + DFLT_GUARD_LIFETIME, + MIN_GUARD_LIFETIME, + MAX_GUARD_LIFETIME) + CHOSEN_ON_DATE_SLOP; } /** If the use of entry guards is configured, choose more entry guards @@ -628,47 +3479,26 @@ pick_entry_guards(guard_selection_t *gs, if (changed) entry_guards_changed_for_guard_selection(gs); } - -/** How long (in seconds) do we allow an entry guard to be nonfunctional, - * unlisted, excluded, or otherwise nonusable before we give up on it? */ -#define ENTRY_GUARD_REMOVE_AFTER (30*24*60*60) +#endif /** Release all storage held by <b>e</b>. */ -static void +STATIC void entry_guard_free(entry_guard_t *e) { if (!e) return; + entry_guard_handles_clear(e); +#ifdef ENABLE_LEGACY_GUARD_ALGORITHM tor_free(e->chosen_by_version); +#endif + tor_free(e->sampled_by_version); + tor_free(e->extra_state_fields); + tor_free(e->selection_name); + tor_free(e->bridge_addr); tor_free(e); } -/** - * Return the minimum lifetime of working entry guard, in seconds, - * as given in the consensus networkstatus. (Plus CHOSEN_ON_DATE_SLOP, - * so that we can do the chosen_on_date randomization while achieving the - * desired minimum lifetime.) - */ -static int32_t -guards_get_lifetime(void) -{ - const or_options_t *options = get_options(); -#define DFLT_GUARD_LIFETIME (86400 * 60) /* Two months. */ -#define MIN_GUARD_LIFETIME (86400 * 30) /* One months. */ -#define MAX_GUARD_LIFETIME (86400 * 1826) /* Five years. */ - - if (options->GuardLifetime >= 1) { - return CLAMP(MIN_GUARD_LIFETIME, - options->GuardLifetime, - MAX_GUARD_LIFETIME) + CHOSEN_ON_DATE_SLOP; - } - - return networkstatus_get_param(NULL, "GuardLifetime", - DFLT_GUARD_LIFETIME, - MIN_GUARD_LIFETIME, - MAX_GUARD_LIFETIME) + CHOSEN_ON_DATE_SLOP; -} - +#ifdef ENABLE_LEGACY_GUARD_ALGORITHM /** Remove from a guard selection context any entry guard which was selected * by an unknown version of Tor, or which was selected by a version of Tor * that's known to select entry guards badly, or which was selected more 2 @@ -739,7 +3569,7 @@ remove_dead_entry_guards(guard_selection_t *gs, time_t now) for (i = 0; i < smartlist_len(gs->chosen_entry_guards); ) { entry_guard_t *entry = smartlist_get(gs->chosen_entry_guards, i); if (entry->bad_since && - ! entry->path_bias_disabled && + ! entry->pb.path_bias_disabled && entry->bad_since + ENTRY_GUARD_REMOVE_AFTER < now) { base16_encode(dbuf, sizeof(dbuf), entry->identity, DIGEST_LEN); @@ -789,6 +3619,7 @@ remove_all_entry_guards_for_guard_selection(guard_selection_t *gs) void remove_all_entry_guards(void) { + // XXXX prop271 this function shouldn't exist, in the new order. remove_all_entry_guards_for_guard_selection(get_guard_selection_info()); } @@ -812,6 +3643,9 @@ entry_guards_compute_status_for_guard_selection(guard_selection_t *gs, if ((!gs) || !(gs->chosen_entry_guards)) return; + if (!get_options()->UseDeprecatedGuardAlgorithm) + return; + if (options->EntryNodes) /* reshuffle the entry guard list if needed */ entry_nodes_should_be_added(); @@ -904,6 +3738,10 @@ entry_guard_register_connect_status_for_guard_selection( return 0; } + if (! get_options()->UseDeprecatedGuardAlgorithm) { + return 0; + } + SMARTLIST_FOREACH_BEGIN(gs->chosen_entry_guards, entry_guard_t *, e) { tor_assert(e); if (tor_memeq(e->identity, digest, DIGEST_LEN)) { @@ -1139,6 +3977,7 @@ entry_guards_set_from_config(guard_selection_t *gs, smartlist_free(old_entry_guards_not_on_list); entry_guards_changed_for_guard_selection(gs); } +#endif /** Return 0 if we're fine adding arbitrary routers out of the * directory to our entry guard list, or return 1 if we have a @@ -1147,6 +3986,7 @@ entry_guards_set_from_config(guard_selection_t *gs, int entry_list_is_constrained(const or_options_t *options) { + // XXXX prop271 look at the current selection. if (options->EntryNodes) return 1; if (options->UseBridges) @@ -1154,6 +3994,7 @@ entry_list_is_constrained(const or_options_t *options) return 0; } +#ifdef ENABLE_LEGACY_GUARD_ALGORITHM /** Pick a live (up and listed) entry guard from entry_guards. If * <b>state</b> is non-NULL, this is for a specific circuit -- * make sure not to pick this circuit's exit or any node in the @@ -1164,6 +4005,8 @@ entry_list_is_constrained(const or_options_t *options) const node_t * choose_random_entry(cpath_build_state_t *state) { + tor_assert(get_options()->UseDeprecatedGuardAlgorithm); + return choose_random_entry_impl(get_guard_selection_info(), state, 0, NO_DIRINFO, NULL); } @@ -1173,10 +4016,50 @@ choose_random_entry(cpath_build_state_t *state) const node_t * choose_random_dirguard(dirinfo_type_t type) { + tor_assert(get_options()->UseDeprecatedGuardAlgorithm); + return choose_random_entry_impl(get_guard_selection_info(), NULL, 1, type, NULL); } +#endif + +/** Return the number of bridges that have descriptors that are marked with + * purpose 'bridge' and are running. + */ +int +num_bridges_usable(void) +{ + int n_options = 0; + + if (get_options()->UseDeprecatedGuardAlgorithm) { +#ifdef ENABLE_LEGACY_GUARD_ALGORITHM + tor_assert(get_options()->UseBridges); + (void) choose_random_entry_impl(get_guard_selection_info(), + NULL, 0, 0, &n_options); +#else + tor_assert_nonfatal_unreached(); +#endif + } else { + /* XXXX prop271 Is this quite right? */ + tor_assert(get_options()->UseBridges); + guard_selection_t *gs = get_guard_selection_info(); + tor_assert(gs->type == GS_TYPE_BRIDGE); + SMARTLIST_FOREACH_BEGIN(gs->sampled_entry_guards, entry_guard_t *, guard) { + if (guard->is_reachable == GUARD_REACHABLE_NO) + continue; + if (tor_digest_is_zero(guard->identity)) + continue; + const node_t *node = node_get_by_id(guard->identity); + if (node && node->ri) + ++n_options; + } SMARTLIST_FOREACH_END(guard); + } + + return n_options; +} + +#ifdef ENABLE_LEGACY_GUARD_ALGORITHM /** Filter <b>all_entry_guards</b> for usable entry guards and put them * in <b>live_entry_guards</b>. We filter based on whether the node is * currently alive, and on whether it satisfies the restrictions @@ -1389,7 +4272,55 @@ choose_random_entry_impl(guard_selection_t *gs, smartlist_free(live_entry_guards); return node; } +#endif + +/** Check the pathbias use success count of <b>node</b> and disable it if it + * goes over our thresholds. */ +static void +pathbias_check_use_success_count(entry_guard_t *node) +{ + const or_options_t *options = get_options(); + const double EPSILON = 1.0e-9; + + /* Note: We rely on the < comparison here to allow us to set a 0 + * rate and disable the feature entirely. If refactoring, don't + * change to <= */ + if (node->pb.use_attempts > EPSILON && + pathbias_get_use_success_count(node)/node->pb.use_attempts + < pathbias_get_extreme_use_rate(options) && + pathbias_get_dropguards(options)) { + node->pb.path_bias_disabled = 1; + log_info(LD_GENERAL, + "Path use bias is too high (%f/%f); disabling node %s", + node->pb.circ_successes, node->pb.circ_attempts, + node->nickname); + } +} + +/** Check the pathbias close count of <b>node</b> and disable it if it goes + * over our thresholds. */ +static void +pathbias_check_close_success_count(entry_guard_t *node) +{ + const or_options_t *options = get_options(); + const double EPSILON = 1.0e-9; + + /* Note: We rely on the < comparison here to allow us to set a 0 + * rate and disable the feature entirely. If refactoring, don't + * change to <= */ + if (node->pb.circ_attempts > EPSILON && + pathbias_get_close_success_count(node)/node->pb.circ_attempts + < pathbias_get_extreme_rate(options) && + pathbias_get_dropguards(options)) { + node->pb.path_bias_disabled = 1; + log_info(LD_GENERAL, + "Path bias is too high (%f/%f); disabling node %s", + node->pb.circ_successes, node->pb.circ_attempts, + node->nickname); + } +} +#ifdef ENABLE_LEGACY_GUARD_ALGORITHM /** Parse <b>state</b> and learn about the entry guards it describes. * If <b>set</b> is true, and there are no errors, replace the guard * list in the provided guard selection context with what we find. @@ -1417,6 +4348,7 @@ entry_guards_parse_state_for_guard_selection( node = tor_malloc_zero(sizeof(entry_guard_t)); /* all entry guards on disk have been contacted */ node->made_contact = 1; + node->is_persistent = 1; smartlist_add(new_entry_guards, node); smartlist_split_string(args, line->value, " ", SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); @@ -1458,7 +4390,7 @@ entry_guards_parse_state_for_guard_selection( "EntryGuardDownSince/UnlistedSince without EntryGuard"); break; } - if (parse_iso_time_(line->value, &when, 0)<0) { + if (parse_iso_time_(line->value, &when, 0, 0)<0) { *msg = tor_strdup("Unable to parse entry nodes: " "Bad time in EntryGuardDownSince/UnlistedSince"); break; @@ -1494,7 +4426,6 @@ entry_guards_parse_state_for_guard_selection( } digestmap_set(added_by, d, tor_strdup(line->value+HEX_DIGEST_LEN+1)); } else if (!strcasecmp(line->key, "EntryGuardPathUseBias")) { - const or_options_t *options = get_options(); double use_cnt, success_cnt; if (!node) { @@ -1525,25 +4456,15 @@ entry_guards_parse_state_for_guard_selection( success_cnt = use_cnt; } - node->use_attempts = use_cnt; - node->use_successes = success_cnt; + node->pb.use_attempts = use_cnt; + node->pb.use_successes = success_cnt; log_info(LD_GENERAL, "Read %f/%f path use bias for node %s", - node->use_successes, node->use_attempts, node->nickname); - - /* Note: We rely on the < comparison here to allow us to set a 0 - * rate and disable the feature entirely. If refactoring, don't - * change to <= */ - if (pathbias_get_use_success_count(node)/node->use_attempts - < pathbias_get_extreme_use_rate(options) && - pathbias_get_dropguards(options)) { - node->path_bias_disabled = 1; - log_info(LD_GENERAL, - "Path use bias is too high (%f/%f); disabling node %s", - node->circ_successes, node->circ_attempts, node->nickname); - } + node->pb.use_successes, node->pb.use_attempts, node->nickname); + + pathbias_check_use_success_count(node); + } else if (!strcasecmp(line->key, "EntryGuardPathBias")) { - const or_options_t *options = get_options(); double hop_cnt, success_cnt, timeouts, collapsed, successful_closed, unusable; @@ -1590,28 +4511,19 @@ entry_guards_parse_state_for_guard_selection( success_cnt = hop_cnt; } - node->circ_attempts = hop_cnt; - node->circ_successes = success_cnt; + node->pb.circ_attempts = hop_cnt; + node->pb.circ_successes = success_cnt; - node->successful_circuits_closed = successful_closed; - node->timeouts = timeouts; - node->collapsed_circuits = collapsed; - node->unusable_circuits = unusable; + node->pb.successful_circuits_closed = successful_closed; + node->pb.timeouts = timeouts; + node->pb.collapsed_circuits = collapsed; + node->pb.unusable_circuits = unusable; log_info(LD_GENERAL, "Read %f/%f path bias for node %s", - node->circ_successes, node->circ_attempts, node->nickname); - /* Note: We rely on the < comparison here to allow us to set a 0 - * rate and disable the feature entirely. If refactoring, don't - * change to <= */ - if (pathbias_get_close_success_count(node)/node->circ_attempts - < pathbias_get_extreme_rate(options) && - pathbias_get_dropguards(options)) { - node->path_bias_disabled = 1; - log_info(LD_GENERAL, - "Path bias is too high (%f/%f); disabling node %s", - node->circ_successes, node->circ_attempts, node->nickname); - } + node->pb.circ_successes, node->pb.circ_attempts, + node->nickname); + pathbias_check_close_success_count(node); } else { log_warn(LD_BUG, "Unexpected key %s", line->key); } @@ -1635,7 +4547,7 @@ entry_guards_parse_state_for_guard_selection( e->chosen_by_version = tor_strdup(state_version); } } - if (e->path_bias_disabled && !e->bad_since) + if (e->pb.path_bias_disabled && !e->bad_since) e->bad_since = time(NULL); } SMARTLIST_FOREACH_END(e); @@ -1651,15 +4563,18 @@ entry_guards_parse_state_for_guard_selection( smartlist_free(gs->chosen_entry_guards); } gs->chosen_entry_guards = new_entry_guards; - gs->dirty = 0; + SMARTLIST_FOREACH(new_entry_guards, entry_guard_t *, e, + e->in_selection = gs); + /* XXX hand new_entry_guards to this func, and move it up a * few lines, so we don't have to re-dirty it */ if (remove_obsolete_entry_guards(gs, now)) - gs->dirty = 1; + entry_guards_dirty = 1; } digestmap_free(added_by, tor_free_); return *msg ? -1 : 0; } +#endif /** Parse <b>state</b> and learn about the entry guards it describes. * If <b>set</b> is true, and there are no errors, replace the guard @@ -1670,9 +4585,27 @@ entry_guards_parse_state_for_guard_selection( int entry_guards_parse_state(or_state_t *state, int set, char **msg) { - return entry_guards_parse_state_for_guard_selection( - get_guard_selection_info(), + entry_guards_dirty = 0; + + int r1 = entry_guards_load_guards_from_state(state, set); + +#ifdef ENABLE_LEGACY_GUARD_ALGORITHM + int r2 = entry_guards_parse_state_for_guard_selection( + get_guard_selection_by_name("legacy", GS_TYPE_LEGACY, 1), state, set, msg); +#else + int r2 = 0; +#endif + + entry_guards_dirty = 0; + + if (r1 < 0 || r2 < 0) { + if (msg && *msg == NULL) { + *msg = tor_strdup("parsing error"); //xxxx prop271 should we try harder? + } + return -1; + } + return 0; } /** How long will we let a change in our guard nodes stay un-saved @@ -1693,14 +4626,16 @@ entry_guards_changed_for_guard_selection(guard_selection_t *gs) tor_assert(gs != NULL); - gs->dirty = 1; + entry_guards_dirty = 1; if (get_options()->AvoidDiskWrites) when = time(NULL) + SLOW_GUARD_STATE_FLUSH_TIME; else when = time(NULL) + FAST_GUARD_STATE_FLUSH_TIME; - /* or_state_save() will call entry_guards_update_state(). */ + /* or_state_save() will call entry_guards_update_state() and + entry_guards_update_guards_in_state() + */ or_state_mark_dirty(get_or_state(), when); } @@ -1718,24 +4653,26 @@ entry_guards_changed(void) * Otherwise, free the EntryGuards piece of <b>state</b> and create * a new one out of the global entry_guards list, and then mark * <b>state</b> dirty so it will get saved to disk. - * - * XXX this should get totally redesigned around storing multiple - * entry guard contexts. For the initial refactor we'll just - * always use the current default. Fix it as soon as we actually - * have any way that default can change. */ void entry_guards_update_state(or_state_t *state) { + entry_guards_dirty = 0; + + // Handles all non-legacy guard info. + entry_guards_update_guards_in_state(state); + + entry_guards_dirty = 0; + +#ifdef ENABLE_LEGACY_GUARD_ALGORITHM config_line_t **next, *line; - guard_selection_t *gs = get_guard_selection_info(); - tor_assert(gs != NULL); + guard_selection_t *gs; + gs = get_guard_selection_by_name("legacy", GS_TYPE_LEGACY, 0); + if (!gs) + return; // nothign to save. tor_assert(gs->chosen_entry_guards != NULL); - if (!gs->dirty) - return; - config_free_lines(state->EntryGuards); next = &state->EntryGuards; *next = NULL; @@ -1779,33 +4716,106 @@ entry_guards_update_state(or_state_t *state) d, e->chosen_by_version, t); next = &(line->next); } - if (e->circ_attempts > 0) { + if (e->pb.circ_attempts > 0) { *next = line = tor_malloc_zero(sizeof(config_line_t)); line->key = tor_strdup("EntryGuardPathBias"); /* In the long run: circuit_success ~= successful_circuit_close + * collapsed_circuits + * unusable_circuits */ tor_asprintf(&line->value, "%f %f %f %f %f %f", - e->circ_attempts, e->circ_successes, + e->pb.circ_attempts, e->pb.circ_successes, pathbias_get_close_success_count(e), - e->collapsed_circuits, - e->unusable_circuits, e->timeouts); + e->pb.collapsed_circuits, + e->pb.unusable_circuits, e->pb.timeouts); next = &(line->next); } - if (e->use_attempts > 0) { + if (e->pb.use_attempts > 0) { *next = line = tor_malloc_zero(sizeof(config_line_t)); line->key = tor_strdup("EntryGuardPathUseBias"); tor_asprintf(&line->value, "%f %f", - e->use_attempts, + e->pb.use_attempts, pathbias_get_use_success_count(e)); next = &(line->next); } } SMARTLIST_FOREACH_END(e); +#endif if (!get_options()->AvoidDiskWrites) or_state_mark_dirty(get_or_state(), 0); - gs->dirty = 0; + entry_guards_dirty = 0; +} + +/** + * Format a single entry guard in the format expected by the controller. + * Return a newly allocated string. + */ +STATIC char * +getinfo_helper_format_single_entry_guard(const entry_guard_t *e, + int legacy_guard) +{ + const char *status = NULL; + time_t when = 0; + const node_t *node; + char tbuf[ISO_TIME_LEN+1]; + char nbuf[MAX_VERBOSE_NICKNAME_LEN+1]; + + if (legacy_guard) { +#ifdef ENABLE_LEGACY_GUARD_ALGORITHM + if (!e->made_contact) { + status = "never-connected"; + } else if (e->bad_since) { + when = e->bad_since; + status = "unusable"; + } else if (e->unreachable_since) { + when = e->unreachable_since; + status = "down"; + } else { + status = "up"; + } +#else + tor_assert_nonfatal_unreached(); + status = "BUG"; +#endif + } else { + /* modern case. This is going to be a bit tricky, since the status + * codes above weren't really intended for prop271 guards. + * + * XXXX use a more appropriate format for exporting this information + */ + if (e->confirmed_idx < 0) { + status = "never-connected"; + } else if (! e->currently_listed) { + when = e->unlisted_since_date; + status = "unusable"; + } else if (! e->is_filtered_guard) { + status = "unusable"; + } else if (e->is_reachable == GUARD_REACHABLE_NO) { + when = e->failing_since; + status = "down"; + } else { + status = "up"; + } + } + + node = entry_guard_find_node(e); + if (node) { + node_get_verbose_nickname(node, nbuf); + } else { + nbuf[0] = '$'; + base16_encode(nbuf+1, sizeof(nbuf)-1, e->identity, DIGEST_LEN); + /* e->nickname field is not very reliable if we don't know about + * this router any longer; don't include it. */ + } + + char *result = NULL; + if (when) { + format_iso_time(tbuf, when); + tor_asprintf(&result, "%s %s %s\n", nbuf, status, tbuf); + } else { + tor_asprintf(&result, "%s %s\n", nbuf, status); + } + return result; } /** If <b>question</b> is the string "entry-guards", then dump @@ -1825,50 +4835,35 @@ getinfo_helper_entry_guards(control_connection_t *conn, guard_selection_t *gs = get_guard_selection_info(); tor_assert(gs != NULL); +#ifdef ENABLE_LEGACY_GUARD_ALGORITHM tor_assert(gs->chosen_entry_guards != NULL); +#endif (void) conn; (void) errmsg; if (!strcmp(question,"entry-guards") || !strcmp(question,"helper-nodes")) { - smartlist_t *sl = smartlist_new(); - char tbuf[ISO_TIME_LEN+1]; - char nbuf[MAX_VERBOSE_NICKNAME_LEN+1]; - - SMARTLIST_FOREACH_BEGIN(gs->chosen_entry_guards, entry_guard_t *, e) { - const char *status = NULL; - time_t when = 0; - const node_t *node; - - if (!e->made_contact) { - status = "never-connected"; - } else if (e->bad_since) { - when = e->bad_since; - status = "unusable"; - } else if (e->unreachable_since) { - when = e->unreachable_since; - status = "down"; - } else { - status = "up"; - } + const smartlist_t *guards; + int legacy_mode; + if (gs->type == GS_TYPE_LEGACY) { +#ifdef ENABLE_LEGACY_GUARD_ALGORITHM + guards = gs->chosen_entry_guards; + legacy_mode = 1; +#else + tor_assert_nonfatal_unreached(); + return 0; +#endif + } else { + guards = gs->sampled_entry_guards; + legacy_mode = 0; + } - node = node_get_by_id(e->identity); - if (node) { - node_get_verbose_nickname(node, nbuf); - } else { - nbuf[0] = '$'; - base16_encode(nbuf+1, sizeof(nbuf)-1, e->identity, DIGEST_LEN); - /* e->nickname field is not very reliable if we don't know about - * this router any longer; don't include it. */ - } + smartlist_t *sl = smartlist_new(); - if (when) { - format_iso_time(tbuf, when); - smartlist_add_asprintf(sl, "%s %s %s\n", nbuf, status, tbuf); - } else { - smartlist_add_asprintf(sl, "%s %s\n", nbuf, status); - } + SMARTLIST_FOREACH_BEGIN(guards, const entry_guard_t *, e) { + char *cp = getinfo_helper_format_single_entry_guard(e, legacy_mode); + smartlist_add(sl, cp); } SMARTLIST_FOREACH_END(e); *answer = smartlist_join_strings(sl, "", 0, NULL); SMARTLIST_FOREACH(sl, char *, c, tor_free(c)); @@ -1877,28 +4872,6 @@ getinfo_helper_entry_guards(control_connection_t *conn, return 0; } -/** Return 0 if we should apply guardfraction information found in the - * consensus. A specific consensus can be specified with the - * <b>ns</b> argument, if NULL the most recent one will be picked.*/ -int -should_apply_guardfraction(const networkstatus_t *ns) -{ - /* We need to check the corresponding torrc option and the consensus - * parameter if we need to. */ - const or_options_t *options = get_options(); - - /* If UseGuardFraction is 'auto' then check the same-named consensus - * parameter. If the consensus parameter is not present, default to - * "off". */ - if (options->UseGuardFraction == -1) { - return networkstatus_get_param(ns, "UseGuardFraction", - 0, /* default to "off" */ - 0, 1); - } - - return options->UseGuardFraction; -} - /* Given the original bandwidth of a guard and its guardfraction, * calculate how much bandwidth the guard should have as a guard and * as a non-guard. @@ -1934,348 +4907,7 @@ guard_get_guardfraction_bandwidth(guardfraction_bandwidth_t *guardfraction_bw, guardfraction_bw->non_guard_bw = orig_bandwidth - (int) guard_bw; } -/** A list of configured bridges. Whenever we actually get a descriptor - * for one, we add it as an entry guard. Note that the order of bridges - * in this list does not necessarily correspond to the order of bridges - * in the torrc. */ -static smartlist_t *bridge_list = NULL; - -/** Mark every entry of the bridge list to be removed on our next call to - * sweep_bridge_list unless it has first been un-marked. */ -void -mark_bridge_list(void) -{ - if (!bridge_list) - bridge_list = smartlist_new(); - SMARTLIST_FOREACH(bridge_list, bridge_info_t *, b, - b->marked_for_removal = 1); -} - -/** Remove every entry of the bridge list that was marked with - * mark_bridge_list if it has not subsequently been un-marked. */ -void -sweep_bridge_list(void) -{ - if (!bridge_list) - bridge_list = smartlist_new(); - SMARTLIST_FOREACH_BEGIN(bridge_list, bridge_info_t *, b) { - if (b->marked_for_removal) { - SMARTLIST_DEL_CURRENT(bridge_list, b); - bridge_free(b); - } - } SMARTLIST_FOREACH_END(b); -} - -/** Initialize the bridge list to empty, creating it if needed. */ -static void -clear_bridge_list(void) -{ - if (!bridge_list) - bridge_list = smartlist_new(); - SMARTLIST_FOREACH(bridge_list, bridge_info_t *, b, bridge_free(b)); - smartlist_clear(bridge_list); -} - -/** Free the bridge <b>bridge</b>. */ -static void -bridge_free(bridge_info_t *bridge) -{ - if (!bridge) - return; - - tor_free(bridge->transport_name); - if (bridge->socks_args) { - SMARTLIST_FOREACH(bridge->socks_args, char*, s, tor_free(s)); - smartlist_free(bridge->socks_args); - } - - tor_free(bridge); -} - -/** If we have a bridge configured whose digest matches <b>digest</b>, or a - * bridge with no known digest whose address matches any of the - * tor_addr_port_t's in <b>orports</b>, return that bridge. Else return - * NULL. */ -static bridge_info_t * -get_configured_bridge_by_orports_digest(const char *digest, - const smartlist_t *orports) -{ - if (!bridge_list) - return NULL; - SMARTLIST_FOREACH_BEGIN(bridge_list, bridge_info_t *, bridge) - { - if (tor_digest_is_zero(bridge->identity)) { - SMARTLIST_FOREACH_BEGIN(orports, tor_addr_port_t *, ap) - { - if (tor_addr_compare(&bridge->addr, &ap->addr, CMP_EXACT) == 0 && - bridge->port == ap->port) - return bridge; - } - SMARTLIST_FOREACH_END(ap); - } - if (digest && tor_memeq(bridge->identity, digest, DIGEST_LEN)) - return bridge; - } - SMARTLIST_FOREACH_END(bridge); - return NULL; -} - -/** If we have a bridge configured whose digest matches <b>digest</b>, or a - * bridge with no known digest whose address matches <b>addr</b>:<b>port</b>, - * return that bridge. Else return NULL. If <b>digest</b> is NULL, check for - * address/port matches only. */ -static bridge_info_t * -get_configured_bridge_by_addr_port_digest(const tor_addr_t *addr, - uint16_t port, - const char *digest) -{ - if (!bridge_list) - return NULL; - SMARTLIST_FOREACH_BEGIN(bridge_list, bridge_info_t *, bridge) - { - if ((tor_digest_is_zero(bridge->identity) || digest == NULL) && - !tor_addr_compare(&bridge->addr, addr, CMP_EXACT) && - bridge->port == port) - return bridge; - if (digest && tor_memeq(bridge->identity, digest, DIGEST_LEN)) - return bridge; - } - SMARTLIST_FOREACH_END(bridge); - return NULL; -} - -/** If we have a bridge configured whose digest matches <b>digest</b>, or a - * bridge with no known digest whose address matches <b>addr</b>:<b>port</b>, - * return 1. Else return 0. If <b>digest</b> is NULL, check for - * address/port matches only. */ -int -addr_is_a_configured_bridge(const tor_addr_t *addr, - uint16_t port, - const char *digest) -{ - tor_assert(addr); - return get_configured_bridge_by_addr_port_digest(addr, port, digest) ? 1 : 0; -} - -/** If we have a bridge configured whose digest matches - * <b>ei->identity_digest</b>, or a bridge with no known digest whose address - * matches <b>ei->addr</b>:<b>ei->port</b>, return 1. Else return 0. - * If <b>ei->onion_key</b> is NULL, check for address/port matches only. */ -int -extend_info_is_a_configured_bridge(const extend_info_t *ei) -{ - const char *digest = ei->onion_key ? ei->identity_digest : NULL; - return addr_is_a_configured_bridge(&ei->addr, ei->port, digest); -} - -/** Wrapper around get_configured_bridge_by_addr_port_digest() to look - * it up via router descriptor <b>ri</b>. */ -static bridge_info_t * -get_configured_bridge_by_routerinfo(const routerinfo_t *ri) -{ - bridge_info_t *bi = NULL; - smartlist_t *orports = router_get_all_orports(ri); - bi = get_configured_bridge_by_orports_digest(ri->cache_info.identity_digest, - orports); - SMARTLIST_FOREACH(orports, tor_addr_port_t *, p, tor_free(p)); - smartlist_free(orports); - return bi; -} - -/** Return 1 if <b>ri</b> is one of our known bridges, else 0. */ -int -routerinfo_is_a_configured_bridge(const routerinfo_t *ri) -{ - return get_configured_bridge_by_routerinfo(ri) ? 1 : 0; -} - -/** Return 1 if <b>node</b> is one of our configured bridges, else 0. */ -int -node_is_a_configured_bridge(const node_t *node) -{ - int retval = 0; - smartlist_t *orports = node_get_all_orports(node); - retval = get_configured_bridge_by_orports_digest(node->identity, - orports) != NULL; - SMARTLIST_FOREACH(orports, tor_addr_port_t *, p, tor_free(p)); - smartlist_free(orports); - return retval; -} - -/** We made a connection to a router at <b>addr</b>:<b>port</b> - * without knowing its digest. Its digest turned out to be <b>digest</b>. - * If it was a bridge, and we still don't know its digest, record it. - */ -void -learned_router_identity(const tor_addr_t *addr, uint16_t port, - const char *digest, - const ed25519_public_key_t *ed_id) -{ - // XXXX prop220 use ed_id here, once there is some way to specify - (void)ed_id; - int learned = 0; - bridge_info_t *bridge = - get_configured_bridge_by_addr_port_digest(addr, port, digest); - if (bridge && tor_digest_is_zero(bridge->identity)) { - memcpy(bridge->identity, digest, DIGEST_LEN); - learned = 1; - } - /* XXXX prop220 remember bridge ed25519 identities -- add a field */ -#if 0 - if (bridge && ed_id && - ed25519_public_key_is_zero(&bridge->ed25519_identity) && - !ed25519_public_key_is_zero(ed_id)) { - memcpy(&bridge->ed25519_identity, ed_id, sizeof(*ed_id)); - learned = 1; - } -#endif - if (learned) { - char *transport_info = NULL; - const char *transport_name = - find_transport_name_by_bridge_addrport(addr, port); - if (transport_name) - tor_asprintf(&transport_info, " (with transport '%s')", transport_name); - // XXXX prop220 log both fingerprints. - log_notice(LD_DIR, "Learned fingerprint %s for bridge %s%s.", - hex_str(digest, DIGEST_LEN), fmt_addrport(addr, port), - transport_info ? transport_info : ""); - tor_free(transport_info); - } -} - -/** Return true if <b>bridge</b> has the same identity digest as - * <b>digest</b>. If <b>digest</b> is NULL, it matches - * bridges with unspecified identity digests. */ -static int -bridge_has_digest(const bridge_info_t *bridge, const char *digest) -{ - if (digest) - return tor_memeq(digest, bridge->identity, DIGEST_LEN); - else - return tor_digest_is_zero(bridge->identity); -} - -/** We are about to add a new bridge at <b>addr</b>:<b>port</b>, with optional - * <b>digest</b> and <b>transport_name</b>. Mark for removal any previously - * existing bridge with the same address and port, and warn the user as - * appropriate. - */ -static void -bridge_resolve_conflicts(const tor_addr_t *addr, uint16_t port, - const char *digest, const char *transport_name) -{ - /* Iterate the already-registered bridge list: - - If you find a bridge with the same adress and port, mark it for - removal. It doesn't make sense to have two active bridges with - the same IP:PORT. If the bridge in question has a different - digest or transport than <b>digest</b>/<b>transport_name</b>, - it's probably a misconfiguration and we should warn the user. - */ - SMARTLIST_FOREACH_BEGIN(bridge_list, bridge_info_t *, bridge) { - if (bridge->marked_for_removal) - continue; - - if (tor_addr_eq(&bridge->addr, addr) && (bridge->port == port)) { - - bridge->marked_for_removal = 1; - - if (!bridge_has_digest(bridge, digest) || - strcmp_opt(bridge->transport_name, transport_name)) { - /* warn the user */ - char *bridge_description_new, *bridge_description_old; - tor_asprintf(&bridge_description_new, "%s:%s:%s", - fmt_addrport(addr, port), - digest ? hex_str(digest, DIGEST_LEN) : "", - transport_name ? transport_name : ""); - tor_asprintf(&bridge_description_old, "%s:%s:%s", - fmt_addrport(&bridge->addr, bridge->port), - tor_digest_is_zero(bridge->identity) ? - "" : hex_str(bridge->identity,DIGEST_LEN), - bridge->transport_name ? bridge->transport_name : ""); - - log_warn(LD_GENERAL,"Tried to add bridge '%s', but we found a conflict" - " with the already registered bridge '%s'. We will discard" - " the old bridge and keep '%s'. If this is not what you" - " wanted, please change your configuration file accordingly.", - bridge_description_new, bridge_description_old, - bridge_description_new); - - tor_free(bridge_description_new); - tor_free(bridge_description_old); - } - } - } SMARTLIST_FOREACH_END(bridge); -} - -/** Return True if we have a bridge that uses a transport with name - * <b>transport_name</b>. */ -MOCK_IMPL(int, -transport_is_needed, (const char *transport_name)) -{ - if (!bridge_list) - return 0; - - SMARTLIST_FOREACH_BEGIN(bridge_list, const bridge_info_t *, bridge) { - if (bridge->transport_name && - !strcmp(bridge->transport_name, transport_name)) - return 1; - } SMARTLIST_FOREACH_END(bridge); - - return 0; -} - -/** Register the bridge information in <b>bridge_line</b> to the - * bridge subsystem. Steals reference of <b>bridge_line</b>. */ -void -bridge_add_from_config(bridge_line_t *bridge_line) -{ - bridge_info_t *b; - - // XXXX prop220 add a way to specify ed25519 ID to bridge_line_t. - - { /* Log the bridge we are about to register: */ - log_debug(LD_GENERAL, "Registering bridge at %s (transport: %s) (%s)", - fmt_addrport(&bridge_line->addr, bridge_line->port), - bridge_line->transport_name ? - bridge_line->transport_name : "no transport", - tor_digest_is_zero(bridge_line->digest) ? - "no key listed" : hex_str(bridge_line->digest, DIGEST_LEN)); - - if (bridge_line->socks_args) { /* print socks arguments */ - int i = 0; - - tor_assert(smartlist_len(bridge_line->socks_args) > 0); - - log_debug(LD_GENERAL, "Bridge uses %d SOCKS arguments:", - smartlist_len(bridge_line->socks_args)); - SMARTLIST_FOREACH(bridge_line->socks_args, const char *, arg, - log_debug(LD_CONFIG, "%d: %s", ++i, arg)); - } - } - - bridge_resolve_conflicts(&bridge_line->addr, - bridge_line->port, - bridge_line->digest, - bridge_line->transport_name); - - b = tor_malloc_zero(sizeof(bridge_info_t)); - tor_addr_copy(&b->addr, &bridge_line->addr); - b->port = bridge_line->port; - memcpy(b->identity, bridge_line->digest, DIGEST_LEN); - if (bridge_line->transport_name) - b->transport_name = bridge_line->transport_name; - b->fetch_status.schedule = DL_SCHED_BRIDGE; - b->fetch_status.backoff = DL_SCHED_RANDOM_EXPONENTIAL; - b->socks_args = bridge_line->socks_args; - if (!bridge_list) - bridge_list = smartlist_new(); - - tor_free(bridge_line); /* Deallocate bridge_line now. */ - - smartlist_add(bridge_list, b); -} - +#ifdef ENABLE_LEGACY_GUARD_ALGORITHM /** Returns true iff the node is used as a guard in the specified guard * context */ int @@ -2312,429 +4944,6 @@ is_node_used_as_guard, (const node_t *node)) get_guard_selection_info(), node); } -/** Return true iff <b>routerset</b> contains the bridge <b>bridge</b>. */ -static int -routerset_contains_bridge(const routerset_t *routerset, - const bridge_info_t *bridge) -{ - int result; - extend_info_t *extinfo; - tor_assert(bridge); - if (!routerset) - return 0; - - extinfo = extend_info_new( - NULL, bridge->identity, - NULL, /* Ed25519 ID */ - NULL, NULL, /* onion keys */ - &bridge->addr, bridge->port); - result = routerset_contains_extendinfo(routerset, extinfo); - extend_info_free(extinfo); - return result; -} - -/** If <b>digest</b> is one of our known bridges, return it. */ -static bridge_info_t * -find_bridge_by_digest(const char *digest) -{ - SMARTLIST_FOREACH(bridge_list, bridge_info_t *, bridge, - { - if (tor_memeq(bridge->identity, digest, DIGEST_LEN)) - return bridge; - }); - return NULL; -} - -/** Given the <b>addr</b> and <b>port</b> of a bridge, if that bridge - * supports a pluggable transport, return its name. Otherwise, return - * NULL. */ -const char * -find_transport_name_by_bridge_addrport(const tor_addr_t *addr, uint16_t port) -{ - if (!bridge_list) - return NULL; - - SMARTLIST_FOREACH_BEGIN(bridge_list, const bridge_info_t *, bridge) { - if (tor_addr_eq(&bridge->addr, addr) && - (bridge->port == port)) - return bridge->transport_name; - } SMARTLIST_FOREACH_END(bridge); - - return NULL; -} - -/** If <b>addr</b> and <b>port</b> match the address and port of a - * bridge of ours that uses pluggable transports, place its transport - * in <b>transport</b>. - * - * Return 0 on success (found a transport, or found a bridge with no - * transport, or found no bridge); return -1 if we should be using a - * transport, but the transport could not be found. - */ -int -get_transport_by_bridge_addrport(const tor_addr_t *addr, uint16_t port, - const transport_t **transport) -{ - *transport = NULL; - if (!bridge_list) - return 0; - - SMARTLIST_FOREACH_BEGIN(bridge_list, const bridge_info_t *, bridge) { - if (tor_addr_eq(&bridge->addr, addr) && - (bridge->port == port)) { /* bridge matched */ - if (bridge->transport_name) { /* it also uses pluggable transports */ - *transport = transport_get_by_name(bridge->transport_name); - if (*transport == NULL) { /* it uses pluggable transports, but - the transport could not be found! */ - return -1; - } - return 0; - } else { /* bridge matched, but it doesn't use transports. */ - break; - } - } - } SMARTLIST_FOREACH_END(bridge); - - *transport = NULL; - return 0; -} - -/** Return a smartlist containing all the SOCKS arguments that we - * should pass to the SOCKS proxy. */ -const smartlist_t * -get_socks_args_by_bridge_addrport(const tor_addr_t *addr, uint16_t port) -{ - bridge_info_t *bridge = get_configured_bridge_by_addr_port_digest(addr, - port, - NULL); - return bridge ? bridge->socks_args : NULL; -} - -/** We need to ask <b>bridge</b> for its server descriptor. */ -static void -launch_direct_bridge_descriptor_fetch(bridge_info_t *bridge) -{ - const or_options_t *options = get_options(); - - if (connection_get_by_type_addr_port_purpose( - CONN_TYPE_DIR, &bridge->addr, bridge->port, - DIR_PURPOSE_FETCH_SERVERDESC)) - return; /* it's already on the way */ - - if (routerset_contains_bridge(options->ExcludeNodes, bridge)) { - download_status_mark_impossible(&bridge->fetch_status); - log_warn(LD_APP, "Not using bridge at %s: it is in ExcludeNodes.", - safe_str_client(fmt_and_decorate_addr(&bridge->addr))); - return; - } - - /* Until we get a descriptor for the bridge, we only know one address for - * it. */ - if (!fascist_firewall_allows_address_addr(&bridge->addr, bridge->port, - FIREWALL_OR_CONNECTION, 0, 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, - DIRIND_ONEHOP, "authority.z", NULL, 0, 0); -} - -/** Fetching the bridge descriptor from the bridge authority returned a - * "not found". Fall back to trying a direct fetch. */ -void -retry_bridge_descriptor_fetch_directly(const char *digest) -{ - bridge_info_t *bridge = find_bridge_by_digest(digest); - if (!bridge) - return; /* not found? oh well. */ - - launch_direct_bridge_descriptor_fetch(bridge); -} - -/** For each bridge in our list for which we don't currently have a - * descriptor, fetch a new copy of its descriptor -- either directly - * from the bridge or via a bridge authority. */ -void -fetch_bridge_descriptors(const or_options_t *options, time_t now) -{ - int num_bridge_auths = get_n_authorities(BRIDGE_DIRINFO); - int ask_bridge_directly; - int can_use_bridge_authority; - - if (!bridge_list) - return; - - /* If we still have unconfigured managed proxies, don't go and - connect to a bridge. */ - if (pt_proxies_configuration_pending()) - return; - - SMARTLIST_FOREACH_BEGIN(bridge_list, bridge_info_t *, bridge) - { - if (!download_status_is_ready(&bridge->fetch_status, now, - IMPOSSIBLE_TO_DOWNLOAD)) - continue; /* don't bother, no need to retry yet */ - if (routerset_contains_bridge(options->ExcludeNodes, bridge)) { - download_status_mark_impossible(&bridge->fetch_status); - log_warn(LD_APP, "Not using bridge at %s: it is in ExcludeNodes.", - safe_str_client(fmt_and_decorate_addr(&bridge->addr))); - continue; - } - - /* schedule another fetch as if this one will fail, in case it does */ - download_status_failed(&bridge->fetch_status, 0); - - can_use_bridge_authority = !tor_digest_is_zero(bridge->identity) && - num_bridge_auths; - ask_bridge_directly = !can_use_bridge_authority || - !options->UpdateBridgesFromAuthority; - log_debug(LD_DIR, "ask_bridge_directly=%d (%d, %d, %d)", - ask_bridge_directly, tor_digest_is_zero(bridge->identity), - !options->UpdateBridgesFromAuthority, !num_bridge_auths); - - if (ask_bridge_directly && - !fascist_firewall_allows_address_addr(&bridge->addr, bridge->port, - FIREWALL_OR_CONNECTION, 0, - 0)) { - log_notice(LD_DIR, "Bridge at '%s' isn't reachable by our " - "firewall policy. %s.", - fmt_addrport(&bridge->addr, bridge->port), - can_use_bridge_authority ? - "Asking bridge authority instead" : "Skipping"); - if (can_use_bridge_authority) - ask_bridge_directly = 0; - else - continue; - } - - if (ask_bridge_directly) { - /* we need to ask the bridge itself for its descriptor. */ - launch_direct_bridge_descriptor_fetch(bridge); - } else { - /* We have a digest and we want to ask an authority. We could - * combine all the requests into one, but that may give more - * hints to the bridge authority than we want to give. */ - char resource[10 + HEX_DIGEST_LEN]; - memcpy(resource, "fp/", 3); - base16_encode(resource+3, HEX_DIGEST_LEN+1, - bridge->identity, DIGEST_LEN); - memcpy(resource+3+HEX_DIGEST_LEN, ".z", 3); - log_info(LD_DIR, "Fetching bridge info '%s' from bridge authority.", - resource); - directory_get_from_dirserver(DIR_PURPOSE_FETCH_SERVERDESC, - ROUTER_PURPOSE_BRIDGE, resource, 0, DL_WANT_AUTHORITY); - } - } - SMARTLIST_FOREACH_END(bridge); -} - -/** If our <b>bridge</b> is configured to be a different address than - * the bridge gives in <b>node</b>, rewrite the routerinfo - * we received to use the address we meant to use. Now we handle - * multihomed bridges better. - */ -static void -rewrite_node_address_for_bridge(const bridge_info_t *bridge, node_t *node) -{ - /* XXXX move this function. */ - /* XXXX overridden addresses should really live in the node_t, so that the - * routerinfo_t and the microdesc_t can be immutable. But we can only - * do that safely if we know that no function that connects to an OR - * does so through an address from any source other than node_get_addr(). - */ - tor_addr_t addr; - const or_options_t *options = get_options(); - - if (node->ri) { - routerinfo_t *ri = node->ri; - tor_addr_from_ipv4h(&addr, ri->addr); - - if ((!tor_addr_compare(&bridge->addr, &addr, CMP_EXACT) && - bridge->port == ri->or_port) || - (!tor_addr_compare(&bridge->addr, &ri->ipv6_addr, CMP_EXACT) && - bridge->port == ri->ipv6_orport)) { - /* they match, so no need to do anything */ - } else { - if (tor_addr_family(&bridge->addr) == AF_INET) { - ri->addr = tor_addr_to_ipv4h(&bridge->addr); - ri->or_port = bridge->port; - log_info(LD_DIR, - "Adjusted bridge routerinfo for '%s' to match configured " - "address %s:%d.", - ri->nickname, fmt_addr32(ri->addr), ri->or_port); - } else if (tor_addr_family(&bridge->addr) == AF_INET6) { - tor_addr_copy(&ri->ipv6_addr, &bridge->addr); - ri->ipv6_orport = bridge->port; - log_info(LD_DIR, - "Adjusted bridge routerinfo for '%s' to match configured " - "address %s.", - ri->nickname, fmt_addrport(&ri->ipv6_addr, ri->ipv6_orport)); - } else { - log_err(LD_BUG, "Address family not supported: %d.", - tor_addr_family(&bridge->addr)); - return; - } - } - - if (options->ClientPreferIPv6ORPort == -1) { - /* Mark which address to use based on which bridge_t we got. */ - node->ipv6_preferred = (tor_addr_family(&bridge->addr) == AF_INET6 && - !tor_addr_is_null(&node->ri->ipv6_addr)); - } else { - /* Mark which address to use based on user preference */ - node->ipv6_preferred = (fascist_firewall_prefer_ipv6_orport(options) && - !tor_addr_is_null(&node->ri->ipv6_addr)); - } - - /* XXXipv6 we lack support for falling back to another address for - the same relay, warn the user */ - if (!tor_addr_is_null(&ri->ipv6_addr)) { - tor_addr_port_t ap; - node_get_pref_orport(node, &ap); - log_notice(LD_CONFIG, - "Bridge '%s' has both an IPv4 and an IPv6 address. " - "Will prefer using its %s address (%s) based on %s.", - ri->nickname, - node->ipv6_preferred ? "IPv6" : "IPv4", - fmt_addrport(&ap.addr, ap.port), - options->ClientPreferIPv6ORPort == -1 ? - "the configured Bridge address" : - "ClientPreferIPv6ORPort"); - } - } - if (node->rs) { - routerstatus_t *rs = node->rs; - tor_addr_from_ipv4h(&addr, rs->addr); - - if (!tor_addr_compare(&bridge->addr, &addr, CMP_EXACT) && - bridge->port == rs->or_port) { - /* they match, so no need to do anything */ - } else { - rs->addr = tor_addr_to_ipv4h(&bridge->addr); - rs->or_port = bridge->port; - log_info(LD_DIR, - "Adjusted bridge routerstatus for '%s' to match " - "configured address %s.", - rs->nickname, fmt_addrport(&bridge->addr, rs->or_port)); - } - } -} - -/** We just learned a descriptor for a bridge. See if that - * digest is in our entry guard list, and add it if not. */ -void -learned_bridge_descriptor(routerinfo_t *ri, int from_cache) -{ - tor_assert(ri); - tor_assert(ri->purpose == ROUTER_PURPOSE_BRIDGE); - if (get_options()->UseBridges) { - int first = num_bridges_usable() <= 1; - bridge_info_t *bridge = get_configured_bridge_by_routerinfo(ri); - time_t now = time(NULL); - router_set_status(ri->cache_info.identity_digest, 1); - - if (bridge) { /* if we actually want to use this one */ - node_t *node; - /* it's here; schedule its re-fetch for a long time from now. */ - if (!from_cache) - download_status_reset(&bridge->fetch_status); - - node = node_get_mutable_by_id(ri->cache_info.identity_digest); - tor_assert(node); - rewrite_node_address_for_bridge(bridge, node); - if (tor_digest_is_zero(bridge->identity)) { - memcpy(bridge->identity,ri->cache_info.identity_digest, DIGEST_LEN); - log_notice(LD_DIR, "Learned identity %s for bridge at %s:%d", - hex_str(bridge->identity, DIGEST_LEN), - fmt_and_decorate_addr(&bridge->addr), - (int) bridge->port); - } - add_an_entry_guard(get_guard_selection_info(), node, 1, 1, 0, 0); - - log_notice(LD_DIR, "new bridge descriptor '%s' (%s): %s", ri->nickname, - from_cache ? "cached" : "fresh", router_describe(ri)); - /* set entry->made_contact so if it goes down we don't drop it from - * our entry node list */ - entry_guard_register_connect_status(ri->cache_info.identity_digest, - 1, 0, now); - if (first) { - routerlist_retry_directory_downloads(now); - } - } - } -} - -/** Return the number of bridges that have descriptors that - * are marked with purpose 'bridge' and are running. - * - * We use this function to decide if we're ready to start building - * circuits through our bridges, or if we need to wait until the - * directory "server/authority" requests finish. */ -int -any_bridge_descriptors_known(void) -{ - tor_assert(get_options()->UseBridges); - return choose_random_entry(NULL) != NULL; -} - -/** Return the number of bridges that have descriptors that are marked with - * purpose 'bridge' and are running. - */ -static int -num_bridges_usable(void) -{ - int n_options = 0; - tor_assert(get_options()->UseBridges); - (void) choose_random_entry_impl(get_guard_selection_info(), - NULL, 0, 0, &n_options); - return n_options; -} - -/** Return a smartlist containing all bridge identity digests */ -MOCK_IMPL(smartlist_t *, -list_bridge_identities, (void)) -{ - smartlist_t *result = NULL; - char *digest_tmp; - - if (get_options()->UseBridges && bridge_list) { - result = smartlist_new(); - - SMARTLIST_FOREACH_BEGIN(bridge_list, bridge_info_t *, b) { - digest_tmp = tor_malloc(DIGEST_LEN); - memcpy(digest_tmp, b->identity, DIGEST_LEN); - smartlist_add(result, digest_tmp); - } SMARTLIST_FOREACH_END(b); - } - - return result; -} - -/** Get the download status for a bridge descriptor given its identity */ -MOCK_IMPL(download_status_t *, -get_bridge_dl_status_by_id, (const char *digest)) -{ - download_status_t *dl = NULL; - - if (digest && get_options()->UseBridges && bridge_list) { - SMARTLIST_FOREACH_BEGIN(bridge_list, bridge_info_t *, b) { - if (tor_memeq(digest, b->identity, DIGEST_LEN)) { - dl = &(b->fetch_status); - break; - } - } SMARTLIST_FOREACH_END(b); - } - - return dl; -} - /** Return 1 if we have at least one descriptor for an entry guard * (bridge or member of EntryNodes) and all descriptors we know are * down. Else return 0. If <b>act</b> is 1, then mark the down guards @@ -2797,19 +5006,155 @@ entries_retry_all(const or_options_t *options) tor_assert(entry_list_is_constrained(options)); entries_retry_helper(options, 1); } +#endif + +/** Helper: Update the status of all entry guards, in whatever algorithm + * is used. Return true if we should stop using all previously generated + * circuits, by calling circuit_mark_all_unused_circs() and + * circuit_mark_all_dirty_circs_as_unusable(). + */ +int +guards_update_all(void) +{ + int mark_circuits = 0; + if (update_guard_selection_choice(get_options())) + mark_circuits = 1; + + tor_assert(curr_guard_context); + + if (curr_guard_context->type == GS_TYPE_LEGACY) { +#ifdef ENABLE_LEGACY_GUARD_ALGORITHM + entry_guards_compute_status(get_options(), approx_time()); +#else + tor_assert_nonfatal_unreached(); +#endif + } else { + if (entry_guards_update_all(curr_guard_context)) + mark_circuits = 1; + } + + return mark_circuits; +} + +/** Helper: pick a guard for a circuit, with whatever algorithm is + used. */ +const node_t * +guards_choose_guard(cpath_build_state_t *state, + circuit_guard_state_t **guard_state_out) +{ + if (get_options()->UseDeprecatedGuardAlgorithm) { +#ifdef ENABLE_LEGACY_GUARD_ALGORITHM + return choose_random_entry(state); +#else + tor_assert_nonfatal_unreached(); + return NULL; +#endif + } else { + const node_t *r = NULL; + const uint8_t *exit_id = NULL; + entry_guard_restriction_t *rst = NULL; + // XXXX prop271 spec deviation -- use of restriction here. + if (state && (exit_id = build_state_get_exit_rsa_id(state))) { + /* We're building to a targeted exit node, so that node can't be + * chosen as our guard for this circuit. */ + rst = tor_malloc_zero(sizeof(entry_guard_restriction_t)); + memcpy(rst->exclude_id, exit_id, DIGEST_LEN); + } + if (entry_guard_pick_for_circuit(get_guard_selection_info(), + GUARD_USAGE_TRAFFIC, + rst, + &r, + guard_state_out) < 0) { + tor_assert(r == NULL); + } + return r; + } +} + +/** Helper: pick a directory guard, with whatever algorithm is used. */ +const node_t * +guards_choose_dirguard(dirinfo_type_t info, + circuit_guard_state_t **guard_state_out) +{ + if (get_options()->UseDeprecatedGuardAlgorithm) { +#ifdef ENABLE_LEGACY_GUARD_ALGORITHM + return choose_random_dirguard(info); +#else + (void)info; + tor_assert_nonfatal_unreached(); + return NULL; +#endif + } else { + /* XXXX prop271 We don't need to look at the dirinfo_type_t here, + * apparently. If you look at the old implementation, and you follow info + * downwards through choose_random_dirguard(), into + * choose_random_entry_impl(), into populate_live_entry_guards()... you + * find out that it isn't even used, and hasn't been since 0.2.7.1-alpha, + * when we realized that every Tor on the network would support + * microdescriptors. -NM */ + const node_t *r = NULL; + if (entry_guard_pick_for_circuit(get_guard_selection_info(), + GUARD_USAGE_DIRGUARD, + NULL, + &r, + guard_state_out) < 0) { + tor_assert(r == NULL); + } + return r; + } +} + +/** + * If we're running with a constrained guard set, then maybe mark our guards + * usable. Return 1 if we do; 0 if we don't. + */ +int +guards_retry_optimistic(const or_options_t *options) +{ + if (! entry_list_is_constrained(options)) + return 0; + +#ifdef ENABLE_LEGACY_GUARD_ALGORITHM + if (options->UseDeprecatedGuardAlgorithm) { + if (entries_known_but_down(options)) { + entries_retry_all(options); + return 1; + } + } +#endif + + // XXXX prop271 -- is this correct? + mark_primary_guards_maybe_reachable(get_guard_selection_info()); + + return 1; +} /** Free one guard selection context */ -static void +STATIC void guard_selection_free(guard_selection_t *gs) { if (!gs) return; + tor_free(gs->name); + +#ifdef ENABLE_LEGACY_GUARD_ALGORITHM if (gs->chosen_entry_guards) { SMARTLIST_FOREACH(gs->chosen_entry_guards, entry_guard_t *, e, entry_guard_free(e)); smartlist_free(gs->chosen_entry_guards); gs->chosen_entry_guards = NULL; } +#endif + + if (gs->sampled_entry_guards) { + SMARTLIST_FOREACH(gs->sampled_entry_guards, entry_guard_t *, e, + entry_guard_free(e)); + smartlist_free(gs->sampled_entry_guards); + gs->sampled_entry_guards = NULL; + } + + smartlist_free(gs->confirmed_entry_guards); + smartlist_free(gs->primary_entry_guards); tor_free(gs); } @@ -2829,9 +5174,6 @@ entry_guards_free_all(void) smartlist_free(guard_contexts); guard_contexts = NULL; } - clear_bridge_list(); - smartlist_free(bridge_list); - bridge_list = NULL; circuit_build_times_free_timeouts(get_circuit_build_times_mutable()); } diff --git a/src/or/entrynodes.h b/src/or/entrynodes.h index f8aaedf171..5fa0fec37f 100644 --- a/src/or/entrynodes.h +++ b/src/or/entrynodes.h @@ -12,28 +12,32 @@ #ifndef TOR_ENTRYNODES_H #define TOR_ENTRYNODES_H -#if 1 -/* XXXX NM I would prefer that all of this stuff be private to - * entrynodes.c. */ +#include "handles.h" /* Forward declare for guard_selection_t; entrynodes.c has the real struct */ typedef struct guard_selection_s guard_selection_t; -/** An entry_guard_t represents our information about a chosen long-term - * first hop, known as a "helper" node in the literature. We can't just - * use a node_t, since we want to remember these even when we - * don't have any directory info. */ -typedef struct entry_guard_t { - char nickname[MAX_NICKNAME_LEN+1]; - char identity[DIGEST_LEN]; - time_t chosen_on_date; /**< Approximately when was this guard added? - * "0" if we don't know. */ - char *chosen_by_version; /**< What tor version added this guard? NULL - * if we don't know. */ - unsigned int made_contact : 1; /**< 0 if we have never connected to this - * router, 1 if we have. */ - unsigned int can_retry : 1; /**< Should we retry connecting to this entry, - * in spite of having it marked as unreachable?*/ +/* Forward declare for entry_guard_t; the real declaration is private. */ +typedef struct entry_guard_t entry_guard_t; + +/* Forward declaration for circuit_guard_state_t; the real declaration is + private. */ +typedef struct circuit_guard_state_t circuit_guard_state_t; + +/* Forward declaration for entry_guard_restriction_t; the real declaration is + private. */ +typedef struct entry_guard_restriction_t entry_guard_restriction_t; + +/* + XXXX Prop271 undefine this in order to disable all legacy guard functions. +*/ +// #define ENABLE_LEGACY_GUARD_ALGORITHM + +/* Information about a guard's pathbias status. + * These fields are used in circpathbias.c to try to detect entry + * nodes that are failing circuits at a suspicious frequency. + */ +typedef struct guard_pathbias_t { unsigned int path_bias_noticed : 1; /**< Did we alert the user about path * bias for this node already? */ unsigned int path_bias_warned : 1; /**< Did we alert the user about path bias @@ -46,23 +50,6 @@ typedef struct entry_guard_t { * use bias for this node already? */ unsigned int path_bias_use_extreme : 1; /**< Did we alert the user about path * use bias for this node already? */ - unsigned int is_dir_cache : 1; /**< Is this node a directory cache? */ - time_t bad_since; /**< 0 if this guard is currently usable, or the time at - * which it was observed to become (according to the - * directory or the user configuration) unusable. */ - time_t unreachable_since; /**< 0 if we can connect to this guard, or the - * time at which we first noticed we couldn't - * connect to it. */ - time_t last_attempted; /**< 0 if we can connect to this guard, or the time - * at which we last failed to connect to it. */ - - /** - * @name circpathbias fields - * - * These fields are used in circpathbias.c to try to detect entry - * nodes that are failing circuits at a suspicious frequency. - */ - /**@{*/ double circ_attempts; /**< Number of circuits this guard has "attempted" */ double circ_successes; /**< Number of successfully built circuits using @@ -79,26 +66,555 @@ typedef struct entry_guard_t { double use_attempts; /**< Number of circuits we tried to use with streams */ double use_successes; /**< Number of successfully used circuits using * this guard as first hop. */ +} guard_pathbias_t; + +#if defined(ENTRYNODES_PRIVATE) +/** + * @name values for entry_guard_t.is_reachable. + * + * See entry_guard_t.is_reachable for more information. + */ +/**@{*/ +#define GUARD_REACHABLE_NO 0 +#define GUARD_REACHABLE_YES 1 +#define GUARD_REACHABLE_MAYBE 2 +/**@}*/ + +/** An entry_guard_t represents our information about a chosen long-term + * first hop, known as a "helper" node in the literature. We can't just + * use a node_t, since we want to remember these even when we + * don't have any directory info. */ +struct entry_guard_t { + HANDLE_ENTRY(entry_guard, entry_guard_t); + + char nickname[MAX_HEX_NICKNAME_LEN+1]; + char identity[DIGEST_LEN]; + ed25519_public_key_t ed_id; + + /** + * @name new guard selection algorithm fields. + * + * Only the new (prop271) algorithm uses these. For a more full + * description of the algorithm, see the module documentation for + * entrynodes.c + */ + /**@{*/ + + /* == Persistent fields, present for all sampled guards. */ + /** When was this guard added to the sample? */ + time_t sampled_on_date; + /** Since what date has this guard been "unlisted"? A guard counts as + * unlisted if we have a live consensus that does not include it, or + * if we have a live consensus that does not include it as a usable + * guard. This field is zero when the guard is listed. */ + time_t unlisted_since_date; // can be zero + /** What version of Tor added this guard to the sample? */ + char *sampled_by_version; + /** Is this guard listed right now? If this is set, then + * unlisted_since_date should be set too. */ + unsigned currently_listed : 1; + + /* == Persistent fields, for confirmed guards only */ + /** When was this guard confirmed? (That is, when did we first use it + * successfully and decide to keep it?) This field is zero if this is not a + * confirmed guard. */ + time_t confirmed_on_date; /* 0 if not confirmed */ + /** + * In what order was this guard confirmed? Guards with lower indices + * appear earlier on the confirmed list. If the confirmed list is compacted, + * this field corresponds to the index of this guard on the confirmed list. + * + * This field is set to -1 if this guard is not confirmed. + */ + int confirmed_idx; /* -1 if not confirmed; otherwise the order that this + * item should occur in the CONFIRMED_GUARDS ordered + * list */ + + /** + * Which selection does this guard belong to? + */ + char *selection_name; + + /** Bridges only: address of the bridge. */ + tor_addr_port_t *bridge_addr; + + /* ==== Non-persistent fields. */ + /* == These are used by sampled guards */ + /** When did we last decide to try using this guard for a circuit? 0 for + * "not since we started up." */ + time_t last_tried_to_connect; + /** How reachable do we consider this guard to be? One of + * GUARD_REACHABLE_NO, GUARD_REACHABLE_YES, or GUARD_REACHABLE_MAYBE. */ + unsigned is_reachable : 2; + /** Boolean: true iff this guard is pending. A pending guard is one + * that we have an in-progress circuit through, and which we do not plan + * to try again until it either succeeds or fails. Primary guards can + * never be pending. */ + unsigned is_pending : 1; + /** If true, don't write this guard to disk. (Used for bridges with unknown + * identities) */ + unsigned is_persistent : 1; + /** When did we get the earliest connection failure for this guard? + * We clear this field on a successful connect. We do _not_ clear it + * when we mark the guard as "MAYBE" reachable. + */ + time_t failing_since; + + /* == Set inclusion flags. */ + /** If true, this guard is in the filtered set. The filtered set includes + * all sampled guards that our configuration allows us to use. */ + unsigned is_filtered_guard : 1; + /** If true, this guard is in the usable filtered set. The usable filtered + * set includes all filtered guards that are not believed to be + * unreachable. (That is, those for which is_reachable is not + * GUARD_REACHABLE_NO) */ + unsigned is_usable_filtered_guard : 1; + unsigned is_primary:1; + + /** This string holds any fields that we are maintaining because + * we saw them in the state, even if we don't understand them. */ + char *extra_state_fields; + + /** Backpointer to the guard selection that this guard belongs to. */ + guard_selection_t *in_selection; /**@}*/ -} entry_guard_t; +#ifdef ENABLE_LEGACY_GUARD_ALGORITHM + /** + * @name legacy guard selection algorithm fields + * + * These are used and maintained by the legacy (pre-prop271) entry guard + * algorithm. Most of them we will remove as prop271 gets implemented. + * The rest we'll migrate over, if they are 100% semantically identical to + * their prop271 equivalents. XXXXprop271 + */ + /**@{*/ + time_t chosen_on_date; /**< Approximately when was this guard added? + * "0" if we don't know. */ + char *chosen_by_version; /**< What tor version added this guard? NULL + * if we don't know. */ + unsigned int made_contact : 1; /**< 0 if we have never connected to this + * router, 1 if we have. */ + unsigned int can_retry : 1; /**< Should we retry connecting to this entry, + * in spite of having it marked as unreachable?*/ + unsigned int is_dir_cache : 1; /**< Is this node a directory cache? */ + time_t bad_since; /**< 0 if this guard is currently usable, or the time at + * which it was observed to become (according to the + * directory or the user configuration) unusable. */ + time_t unreachable_since; /**< 0 if we can connect to this guard, or the + * time at which we first noticed we couldn't + * connect to it. */ + time_t last_attempted; /**< 0 if we can connect to this guard, or the time + * at which we last failed to connect to it. */ + + /**}@*/ +#endif + + /** Path bias information for this guard. */ + guard_pathbias_t pb; +}; + +/** + * Possible rules for a guard selection to follow + */ +typedef enum guard_selection_type_t { + /** Infer the type of this selection from its name. */ + GS_TYPE_INFER=0, + /** Use the normal guard selection algorithm, taking our sample from the + * complete list of guards in the consensus. */ + GS_TYPE_NORMAL=1, + /** Use the normal guard selection algorithm, taking our sample from the + * configured bridges, and allowing it to grow as large as all the configured + * bridges */ + GS_TYPE_BRIDGE, + /** Use the normal guard selection algorithm, taking our sample from the + * set of filtered nodes. */ + GS_TYPE_RESTRICTED, + /** Use the legacy (pre-prop271) guard selection algorithm and fields */ + GS_TYPE_LEGACY, +} guard_selection_type_t; + +/** + * All of the the context for guard selection on a particular client. + * + * We maintain multiple guard selection contexts for a client, depending + * aspects on its current configuration -- whether an extremely + * restrictive EntryNodes is used, whether UseBridges is enabled, and so + * on.) + * + * See the module documentation for entrynodes.c for more information + * about guard selection algorithms. + */ +struct guard_selection_s { + /** + * The name for this guard-selection object. (Must not contain spaces). + */ + char *name; + + /** + * What rules does this guard-selection object follow? + */ + guard_selection_type_t type; + + /** + * A value of 1 means that primary_entry_guards is up-to-date; 0 + * means we need to recalculate it before using primary_entry_guards + * or the is_primary flag on any guard. + */ + int primary_guards_up_to_date; + + /** + * A list of the sampled entry guards, as entry_guard_t structures. + * Not in any particular order. When we 'sample' a guard, we are + * noting it as a possible guard to pick in the future. The use of + * sampling here prevents us from being forced by an attacker to try + * every guard on the network. This list is persistent. + */ + smartlist_t *sampled_entry_guards; + + /** + * Ordered list (from highest to lowest priority) of guards that we + * have successfully contacted and decided to use. Every member of + * this list is a member of sampled_entry_guards. Every member should + * have confirmed_on_date set, and have confirmed_idx greater than + * any earlier member of the list. + * + * This list is persistent. It is a subset of the elements in + * sampled_entry_guards, and its pointers point to elements of + * sampled_entry_guards. + */ + smartlist_t *confirmed_entry_guards; + + /** + * Ordered list (from highest to lowest priority) of guards that we + * are willing to use the most happily. These guards may or may not + * yet be confirmed yet. If we can use one of these guards, we are + * probably not on a network that is trying to restrict our guard + * choices. + * + * This list is a subset of the elements in + * sampled_entry_guards, and its pointers point to elements of + * sampled_entry_guards. + */ + smartlist_t *primary_entry_guards; + + /** When did we last successfully build a circuit or use a circuit? */ + time_t last_time_on_internet; + + /** What confirmed_idx value should the next-added member of + * confirmed_entry_guards receive? */ + int next_confirmed_idx; + +#ifdef ENABLE_LEGACY_GUARD_ALGORITHM + /** + * A list of our chosen entry guards, as entry_guard_t structures; this + * preserves the pre-Prop271 behavior. + */ + smartlist_t *chosen_entry_guards; + + /** + * When we try to choose an entry guard, should we parse and add + * config's EntryNodes first? This was formerly a global. This + * preserves the pre-Prop271 behavior. + */ + int should_add_entry_nodes; +#endif +}; + +struct entry_guard_handle_t; + +/** + * A restriction to remember which entry guards are off-limits for a given + * circuit. + * + * Right now, we only use restrictions to block a single guard from being + * selected; this mechanism is designed to be more extensible in the future, + * however. + * + * Note: This mechanism is NOT for recording which guards are never to be + * used: only which guards cannot be used on <em>one particular circuit</em>. + */ +struct entry_guard_restriction_t { + /** + * The guard's RSA identity digest must not equal this. + */ + uint8_t exclude_id[DIGEST_LEN]; +}; + +/** + * Per-circuit state to track whether we'll be able to use the circuit. + */ +struct circuit_guard_state_t { + /** Handle to the entry guard object for this circuit. */ + struct entry_guard_handle_t *guard; + /** The time at which <b>state</b> last changed. */ + time_t state_set_at; + /** One of GUARD_CIRC_STATE_* */ + uint8_t state; + + /** + * A set of restrictions that were placed on this guard when we selected it + * for this particular circuit. We need to remember the restrictions here, + * since any guard that breaks these restrictions will not block this + * circuit from becoming COMPLETE. + */ + entry_guard_restriction_t *restrictions; +}; +#endif + +/* Common entry points for old and new guard code */ +int guards_update_all(void); +const node_t *guards_choose_guard(cpath_build_state_t *state, + circuit_guard_state_t **guard_state_out); +const node_t *guards_choose_dirguard(dirinfo_type_t info, + circuit_guard_state_t **guard_state_out); + +#if 1 +/* XXXX NM I would prefer that all of this stuff be private to + * entrynodes.c. */ entry_guard_t *entry_guard_get_by_id_digest_for_guard_selection( guard_selection_t *gs, const char *digest); entry_guard_t *entry_guard_get_by_id_digest(const char *digest); void entry_guards_changed_for_guard_selection(guard_selection_t *gs); void entry_guards_changed(void); guard_selection_t * get_guard_selection_info(void); +#ifdef ENABLE_LEGACY_GUARD_ALGORITHM const smartlist_t *get_entry_guards_for_guard_selection( guard_selection_t *gs); const smartlist_t *get_entry_guards(void); +#endif int num_live_entry_guards_for_guard_selection( guard_selection_t *gs, int for_directory); int num_live_entry_guards(int for_directory); +#endif + +const node_t *entry_guard_find_node(const entry_guard_t *guard); +#ifdef ENABLE_LEGACY_GUARD_ALGORITHM +void entry_guard_mark_bad(entry_guard_t *guard); +#endif +const char *entry_guard_get_rsa_id_digest(const entry_guard_t *guard); +const char *entry_guard_describe(const entry_guard_t *guard); +guard_pathbias_t *entry_guard_get_pathbias_state(entry_guard_t *guard); + +/** Enum to specify how we're going to use a given guard, when we're picking + * one for immediate use. */ +typedef enum { + GUARD_USAGE_TRAFFIC = 0, + GUARD_USAGE_DIRGUARD = 1 +} guard_usage_t; + +void circuit_guard_state_free(circuit_guard_state_t *state); +int entry_guard_pick_for_circuit(guard_selection_t *gs, + guard_usage_t usage, + entry_guard_restriction_t *rst, + const node_t **chosen_node_out, + circuit_guard_state_t **guard_state_out); + +/* We just connected to an entry guard. What should we do with the circuit? */ +typedef enum { + GUARD_USABLE_NEVER = -1, /* Never use the circuit */ + GUARD_MAYBE_USABLE_LATER = 0, /* Keep it. We might use it in the future */ + GUARD_USABLE_NOW = 1, /* Use it right now */ +} guard_usable_t; + +guard_usable_t entry_guard_succeeded(circuit_guard_state_t **guard_state_p); +void entry_guard_failed(circuit_guard_state_t **guard_state_p); +void entry_guard_cancel(circuit_guard_state_t **guard_state_p); +void entry_guard_chan_failed(channel_t *chan); +int entry_guards_update_all(guard_selection_t *gs); +int entry_guards_upgrade_waiting_circuits(guard_selection_t *gs, + const smartlist_t *all_circuits, + smartlist_t *newly_complete_out); +int entry_guard_state_should_expire(circuit_guard_state_t *guard_state); +void entry_guards_note_internet_connectivity(guard_selection_t *gs); +int update_guard_selection_choice(const or_options_t *options); + +/* Used by bridges.c only. */ +#ifdef ENABLE_LEGACY_GUARD_ALGORITHM +void add_bridge_as_entry_guard(guard_selection_t *gs, + const node_t *chosen); #endif +int num_bridges_usable(void); #ifdef ENTRYNODES_PRIVATE +/** + * @name Default values for the parameters for the new (prop271) entry guard + * algorithm. + */ +/**@{*/ +/** + * We never let our sampled guard set grow larger than this percentage + * of the guards on the network. + */ +#define DFLT_MAX_SAMPLE_THRESHOLD_PERCENT 20 +/** + * We never let our sampled guard set grow larger than this number of + * guards. + */ +#define DFLT_MAX_SAMPLE_SIZE 60 +/** + * We always try to make our sample contain at least this many guards. + * + * XXXX prop271 There was a MIN_SAMPLE_THRESHOLD in the proposal, but I + * removed it in favor of MIN_FILTERED_SAMPLE_SIZE. -NM + */ +#define DFLT_MIN_FILTERED_SAMPLE_SIZE 20 +/** + * If a guard is unlisted for this many days in a row, we remove it. + */ +#define DFLT_REMOVE_UNLISTED_GUARDS_AFTER_DAYS 20 +/** + * We remove unconfirmed guards from the sample after this many days, + * regardless of whether they are listed or unlisted. + */ +#define DFLT_GUARD_LIFETIME_DAYS 120 +/** + * We remove confirmed guards from the sample if they were sampled + * GUARD_LIFETIME_DAYS ago and confirmed this many days ago. + */ +#define DFLT_GUARD_CONFIRMED_MIN_LIFETIME_DAYS 60 +/** + * How many guards do we try to keep on our primary guard list? + */ +#define DFLT_N_PRIMARY_GUARDS 3 +/** + * Of the live guards on the primary guard list, how many do we consider when + * choosing a guard to use? + */ +#define DFLT_N_PRIMARY_GUARDS_TO_USE 1 +/** + * As DFLT_N_PRIMARY_GUARDS, but for choosing which directory guard to use. + */ +#define DFLT_N_PRIMARY_DIR_GUARDS_TO_USE 3 +/** + * If we haven't successfully built or used a circuit in this long, then + * consider that the internet is probably down. + */ +#define DFLT_INTERNET_LIKELY_DOWN_INTERVAL (10*60) +/** + * If we're trying to connect to a nonprimary guard for at least this + * many seconds, and we haven't gotten the connection to work, we will treat + * lower-priority guards as usable. + */ +#define DFLT_NONPRIMARY_GUARD_CONNECT_TIMEOUT 15 +/** + * If a circuit has been sitting around in 'waiting for better guard' state + * for at least this long, we'll expire it. + */ +#define DFLT_NONPRIMARY_GUARD_IDLE_TIMEOUT (10*60) +/** + * If our configuration retains fewer than this fraction of guards from the + * torrc, we are in a restricted setting. + */ +#define DFLT_MEANINGFUL_RESTRICTION_PERCENT 20 +/** + * If our configuration retains fewer than this fraction of guards from the + * torrc, we are in an extremely restricted setting, and should warn. + */ +#define DFLT_EXTREME_RESTRICTION_PERCENT 1 +/**@}*/ + +STATIC double get_max_sample_threshold(void); +STATIC int get_max_sample_size_absolute(void); +STATIC int get_min_filtered_sample_size(void); +STATIC int get_remove_unlisted_guards_after_days(void); +STATIC int get_guard_lifetime(void); +STATIC int get_guard_confirmed_min_lifetime(void); +STATIC int get_n_primary_guards(void); +STATIC int get_n_primary_guards_to_use(guard_usage_t usage); +STATIC int get_internet_likely_down_interval(void); +STATIC int get_nonprimary_guard_connect_timeout(void); +STATIC int get_nonprimary_guard_idle_timeout(void); +STATIC double get_meaningful_restriction_threshold(void); +STATIC double get_extreme_restriction_threshold(void); + +// ---------- XXXX these functions and definitions are post-prop271. +HANDLE_DECL(entry_guard, entry_guard_t, STATIC) +STATIC guard_selection_type_t guard_selection_infer_type( + guard_selection_type_t type_in, + const char *name); +STATIC guard_selection_t *guard_selection_new(const char *name, + guard_selection_type_t type); +STATIC guard_selection_t *get_guard_selection_by_name( + const char *name, guard_selection_type_t type, int create_if_absent); +STATIC void guard_selection_free(guard_selection_t *gs); +MOCK_DECL(STATIC int, entry_guard_is_listed, + (guard_selection_t *gs, const entry_guard_t *guard)); +STATIC const char *choose_guard_selection(const or_options_t *options, + const networkstatus_t *ns, + const guard_selection_t *old_selection, + guard_selection_type_t *type_out); +STATIC entry_guard_t *get_sampled_guard_with_id(guard_selection_t *gs, + const uint8_t *rsa_id); + +MOCK_DECL(STATIC time_t, randomize_time, (time_t now, time_t max_backdate)); +STATIC entry_guard_t *entry_guard_add_to_sample(guard_selection_t *gs, + const node_t *node); +STATIC entry_guard_t *entry_guards_expand_sample(guard_selection_t *gs); +STATIC char *entry_guard_encode_for_state(entry_guard_t *guard); +STATIC entry_guard_t *entry_guard_parse_from_state(const char *s); +STATIC void entry_guard_free(entry_guard_t *e); +STATIC void entry_guards_update_filtered_sets(guard_selection_t *gs); +STATIC int entry_guards_all_primary_guards_are_down(guard_selection_t *gs); +/** + * @name Flags for sample_reachable_filtered_entry_guards() + */ +/**@{*/ +#define SAMPLE_EXCLUDE_CONFIRMED (1u<<0) +#define SAMPLE_EXCLUDE_PRIMARY (1u<<1) +#define SAMPLE_EXCLUDE_PENDING (1u<<2) +#define SAMPLE_NO_UPDATE_PRIMARY (1u<<3) +/**@}*/ +STATIC entry_guard_t *sample_reachable_filtered_entry_guards( + guard_selection_t *gs, + const entry_guard_restriction_t *rst, + unsigned flags); +STATIC void entry_guard_consider_retry(entry_guard_t *guard); +STATIC void make_guard_confirmed(guard_selection_t *gs, entry_guard_t *guard); +STATIC void entry_guards_update_confirmed(guard_selection_t *gs); +STATIC void entry_guards_update_primary(guard_selection_t *gs); +STATIC int num_reachable_filtered_guards(guard_selection_t *gs, + const entry_guard_restriction_t *rst); +STATIC void sampled_guards_update_from_consensus(guard_selection_t *gs); +/** + * @name Possible guard-states for a circuit. + */ +/**@{*/ +/** State for a circuit that can (so far as the guard subsystem is + * concerned) be used for actual traffic as soon as it is successfully + * opened. */ +#define GUARD_CIRC_STATE_USABLE_ON_COMPLETION 1 +/** State for an non-open circuit that we shouldn't use for actual + * traffic, when it completes, unless other circuits to preferable + * guards fail. */ +#define GUARD_CIRC_STATE_USABLE_IF_NO_BETTER_GUARD 2 +/** State for an open circuit that we shouldn't use for actual traffic + * unless other circuits to preferable guards fail. */ +#define GUARD_CIRC_STATE_WAITING_FOR_BETTER_GUARD 3 +/** State for a circuit that can (so far as the guard subsystem is + * concerned) be used for actual traffic. */ +#define GUARD_CIRC_STATE_COMPLETE 4 +/** State for a circuit that is unusable, and will not become usable. */ +#define GUARD_CIRC_STATE_DEAD 5 +/**@}*/ +STATIC void entry_guards_note_guard_failure(guard_selection_t *gs, + entry_guard_t *guard); +STATIC entry_guard_t *select_entry_guard_for_circuit(guard_selection_t *gs, + guard_usage_t usage, + const entry_guard_restriction_t *rst, + unsigned *state_out); +STATIC void mark_primary_guards_maybe_reachable(guard_selection_t *gs); +STATIC unsigned entry_guards_note_guard_success(guard_selection_t *gs, + entry_guard_t *guard, + unsigned old_state); +STATIC int entry_guard_has_higher_priority(entry_guard_t *a, entry_guard_t *b); +STATIC char *getinfo_helper_format_single_entry_guard(const entry_guard_t *e, + int is_legacy); +#ifdef ENABLE_LEGACY_GUARD_ALGORITHM +// ---------- XXXX this stuff is pre-prop271. + STATIC const node_t *add_an_entry_guard(guard_selection_t *gs, const node_t *chosen, int reset_status, int prepend, @@ -128,12 +644,20 @@ STATIC const node_t *entry_is_live(const entry_guard_t *e, const char **msg); STATIC int entry_is_time_to_retry(const entry_guard_t *e, time_t now); +#endif #endif +#ifdef ENABLE_LEGACY_GUARD_ALGORITHM void remove_all_entry_guards_for_guard_selection(guard_selection_t *gs); void remove_all_entry_guards(void); +#endif + +struct bridge_info_t; +void entry_guard_learned_bridge_identity(const tor_addr_port_t *addrport, + const uint8_t *rsa_id_digest); +#ifdef ENABLE_LEGACY_GUARD_ALGORITHM void entry_guards_compute_status_for_guard_selection( guard_selection_t *gs, const or_options_t *options, time_t now); void entry_guards_compute_status(const or_options_t *options, time_t now); @@ -144,9 +668,13 @@ int entry_guard_register_connect_status(const char *digest, int succeeded, int mark_relay_status, time_t now); void entry_nodes_should_be_added_for_guard_selection(guard_selection_t *gs); void entry_nodes_should_be_added(void); +#endif int entry_list_is_constrained(const or_options_t *options); +#ifdef ENABLE_LEGACY_GUARD_ALGORITHM const node_t *choose_random_entry(cpath_build_state_t *state); const node_t *choose_random_dirguard(dirinfo_type_t t); +#endif +int guards_retry_optimistic(const or_options_t *options); int entry_guards_parse_state_for_guard_selection( guard_selection_t *gs, or_state_t *state, int set, char **msg); int entry_guards_parse_state(or_state_t *state, int set, char **msg); @@ -154,46 +682,17 @@ void entry_guards_update_state(or_state_t *state); int getinfo_helper_entry_guards(control_connection_t *conn, const char *question, char **answer, const char **errmsg); +#ifdef ENABLE_LEGACY_GUARD_ALGORITHM int is_node_used_as_guard_for_guard_selection(guard_selection_t *gs, const node_t *node); MOCK_DECL(int, is_node_used_as_guard, (const node_t *node)); +#endif -void mark_bridge_list(void); -void sweep_bridge_list(void); - -int addr_is_a_configured_bridge(const tor_addr_t *addr, uint16_t port, - const char *digest); -int extend_info_is_a_configured_bridge(const extend_info_t *ei); -int routerinfo_is_a_configured_bridge(const routerinfo_t *ri); -int node_is_a_configured_bridge(const node_t *node); -void learned_router_identity(const tor_addr_t *addr, uint16_t port, - const char *digest, - const ed25519_public_key_t *ed_id); -struct bridge_line_t; -void bridge_add_from_config(struct bridge_line_t *bridge_line); -void retry_bridge_descriptor_fetch_directly(const char *digest); -void fetch_bridge_descriptors(const or_options_t *options, time_t now); -void learned_bridge_descriptor(routerinfo_t *ri, int from_cache); -int any_bridge_descriptors_known(void); int entries_known_but_down(const or_options_t *options); void entries_retry_all(const or_options_t *options); -const smartlist_t *get_socks_args_by_bridge_addrport(const tor_addr_t *addr, - uint16_t port); - -int any_bridges_dont_support_microdescriptors(void); - void entry_guards_free_all(void); -const char *find_transport_name_by_bridge_addrport(const tor_addr_t *addr, - uint16_t port); -struct transport_t; -int get_transport_by_bridge_addrport(const tor_addr_t *addr, uint16_t port, - const struct transport_t **transport); - -MOCK_DECL(int, transport_is_needed, (const char *transport_name)); -int validate_pluggable_transports_config(void); - double pathbias_get_close_success_count(entry_guard_t *guard); double pathbias_get_use_success_count(entry_guard_t *guard); @@ -213,9 +712,5 @@ guard_get_guardfraction_bandwidth(guardfraction_bandwidth_t *guardfraction_bw, int orig_bandwidth, uint32_t guardfraction_percentage); -MOCK_DECL(smartlist_t *, list_bridge_identities, (void)); -MOCK_DECL(download_status_t *, get_bridge_dl_status_by_id, - (const char *digest)); - #endif diff --git a/src/or/fallback_dirs.inc b/src/or/fallback_dirs.inc index f6289199b7..be94ff50fc 100644 --- a/src/or/fallback_dirs.inc +++ b/src/or/fallback_dirs.inc @@ -1,281 +1,423 @@ +/* Whitelist & blacklist excluded 1177 of 1389 candidates. */ /* To comment-out entries in this file, use C comments, and add * to the start of each line. (stem finds fallback entries using " at the start of a line.) */ -/* Whitelist & blacklist excluded 1273 of 1553 candidates. */ /* Checked IPv4 DirPorts served a consensus within 15.0s. */ /* -Final Count: 100 (Eligible 280, Target 356 (1781 * 0.20), Max 100) -Excluded: 180 (Same Operator 102, Failed/Skipped Download 40, Excess 38) -Bandwidth Range: 6.0 - 67.2 MB/s +Final Count: 177 (Eligible 212, Target 392 (1963 * 0.20), Max 200) +Excluded: 35 (Same Operator 35, Failed/Skipped Download 0, Excess 0) +Bandwidth Range: 1.2 - 107.3 MByte/s */ /* -Onionoo Source: details Date: 2016-04-18 01:00:00 Version: 3.1 -URL: https:onionoo.torproject.orgdetails?fields=fingerprint%2Cnickname%2Ccontact%2Clast_changed_address_or_port%2Cconsensus_weight%2Cadvertised_bandwidth%2Cor_addresses%2Cdir_address%2Crecommended_version%2Cflags%2Ceffective_family&flag=V2Dir&type=relay&last_seen_days=-7&first_seen_days=7- +Onionoo Source: details Date: 2016-12-19 03:00:00 Version: 3.1 +URL: https:onionoo.torproject.orgdetails?fields=fingerprint%2Cnickname%2Ccontact%2Clast_changed_address_or_port%2Cconsensus_weight%2Cadvertised_bandwidth%2Cor_addresses%2Cdir_address%2Crecommended_version%2Cflags%2Ceffective_family%2Cplatform&flag=V2Dir&type=relay&last_seen_days=-0&first_seen_days=7- */ /* -Onionoo Source: uptime Date: 2016-04-18 01:00:00 Version: 3.1 -URL: https:onionoo.torproject.orguptime?first_seen_days=7-&flag=V2Dir&type=relay&last_seen_days=-7 +Onionoo Source: uptime Date: 2016-12-19 03:00:00 Version: 3.1 +URL: https:onionoo.torproject.orguptime?first_seen_days=7-&flag=V2Dir&type=relay&last_seen_days=-0 */ -"193.171.202.146:9030 orport=9001 id=01A9258A46E97FF8B2CAC7910577862C14F2C524" +"185.13.39.197:80 orport=443 id=001524DD403D729F08F7E5D77813EF12756CFA8D" " weight=10", -"5.9.110.236:9030 orport=9001 id=0756B7CD4DFC8182BE23143FAC0642F515182CEB" -" ipv6=[2a01:4f8:162:51e2::2]:9001" +"185.100.85.61:80 orport=443 id=025B66CEBC070FCB0519D206CF0CF4965C20C96E" +" weight=10", +"62.210.92.11:9030 orport=9001 id=0266B0660F3F20A7D1F3D8335931C95EF50F6C6B" +" ipv6=[2001:bc8:338c::1]:9001" +" weight=10", +"185.97.32.18:9030 orport=9001 id=04250C3835019B26AA6764E85D836088BE441088" +" weight=10", +"92.222.20.130:80 orport=443 id=0639612FF149AA19DF3BCEA147E5B8FED6F3C87C" +" weight=10", +"163.172.149.155:80 orport=443 id=0B85617241252517E8ECF2CFC7F4C1A32DCD153F" " weight=10", -/* Fallback was on 0.2.8.2-alpha list, but opted-out before 0.2.8.6 - * "37.187.1.149:9030 orport=9001 id=08DC0F3C6E3D9C527C1FC8745D35DD1B0DE1875D" - * " ipv6=[2001:41d0:a:195::1]:9001" - * " weight=10", - */ "5.39.92.199:80 orport=443 id=0BEA4A88D069753218EAAAD6D22EA87B9A1319D6" +" ipv6=[2001:41d0:8:b1c7::1]:443" " weight=10", -"5.196.88.122:9030 orport=9001 id=0C2C599AFCB26F5CFC2C7592435924C1D63D9484" +"163.172.25.118:80 orport=22 id=0CF8F3E6590F45D50B70F2F7DA6605ECA6CD408F" " weight=10", "178.62.197.82:80 orport=443 id=0D3EBA17E1C78F1E9900BABDB23861D46FCAF163" " weight=10", -/* Fallback was on 0.2.8.6 list, but down for a week before 0.2.9 - * "144.76.14.145:110 orport=143 id=14419131033443AE6E21DA82B0D307F7CAE42BDB" - * " ipv6=[2a01:4f8:190:9490::dead]:443" - * " weight=10", - */ -"178.32.216.146:9030 orport=9001 id=17898F9A2EBC7D69DAF87C00A1BD2FABF3C9E1D2" +"185.100.86.100:80 orport=443 id=0E8C0C8315B66DB5F703804B3889A1DD66C67CE0" +" weight=10", +"5.9.159.14:9030 orport=9001 id=0F100F60C7A63BED90216052324D29B08CFCF797" +" weight=10", +"193.11.114.43:9030 orport=9001 id=12AD30E5D25AA67F519780E2111E611A455FDC89" +" ipv6=[2001:6b0:30:1000::99]:9050" +" weight=10", +"37.157.195.87:8030 orport=443 id=12FD624EE73CEF37137C90D38B2406A66F68FAA2" +" weight=10", +"178.62.60.37:80 orport=443 id=175921396C7C426309AB03775A9930B6F611F794" +" weight=10", +"204.11.50.131:9030 orport=9001 id=185F2A57B0C4620582602761097D17DB81654F70" +" weight=10", +"92.222.4.102:9030 orport=9001 id=1A6B8B8272632D8AD38442027F822A367128405C" +" weight=10", +"5.9.158.75:80 orport=443 id=1AF72E8906E6C49481A791A6F8F84F8DFEBBB2BA" +" ipv6=[2a01:4f8:190:514a::2]:443" " weight=10", "46.101.151.222:80 orport=443 id=1DBAED235E3957DE1ABD25B4206BE71406FB61F8" " weight=10", "91.219.237.229:80 orport=443 id=1ECD73B936CB6E6B3CD647CC204F108D9DF2C9F7" " weight=10", +"5.9.146.203:80 orport=443 id=1F45542A24A61BF9408F1C05E0DCE4E29F2CBA11" +" weight=10", "212.47.229.2:9030 orport=9001 id=20462CBA5DA4C2D963567D17D0B7249718114A68" +" ipv6=[2001:bc8:4400:2100::f03]:9001" +" weight=10", +"91.219.236.222:80 orport=443 id=20704E7DD51501DC303FA51B738D7B7E61397CF6" " weight=10", -"185.61.138.18:8080 orport=4443 id=2541759BEC04D37811C2209A88E863320271EC9C" +"144.76.163.93:9030 orport=9001 id=22F08CF09764C4E8982640D77F71ED72FF26A9AC" +" weight=10", +"163.172.176.167:80 orport=443 id=230A8B2A8BA861210D9B4BA97745AEC217A94207" +" weight=10", +"212.47.240.10:82 orport=443 id=2A4C448784F5A83AFE6C78DA357D5E31F7989DEB" " weight=10", -/* Fallback was on 0.2.8.2-alpha list, but went down before 0.2.8.5 - * "51.254.215.121:80 orport=443 id=262B66AD25C79588AD1FC8ED0E966395B47E5C1D" - * " weight=10", - */ -/* Fallback was on 0.2.8.6 list, but went down before 0.2.9 - * "194.150.168.79:11112 orport=11111 id=29F1020B94BE25E6BE1AD13E93CE19D2131B487C" - * " weight=10", - */ "144.76.26.175:9012 orport=9011 id=2BA2C8E96B2590E1072AECE2BDB5C48921BF8510" " weight=10", +"178.16.208.56:80 orport=443 id=2CDCFED0142B28B002E89D305CBA2E26063FADE2" +" ipv6=[2a00:1c20:4089:1234:cd49:b58a:9ebe:67ec]:443" +" weight=10", "62.210.124.124:9130 orport=9101 id=2EBD117806EE43C3CC885A8F1E4DC60F207E7D3E" " ipv6=[2001:bc8:3f23:100::1]:9101" " weight=10", +"97.74.237.196:9030 orport=9001 id=2F0F32AB1E5B943CA7D062C03F18960C86E70D94" +" weight=10", "213.61.66.118:9031 orport=9001 id=30648BC64CEDB3020F4A405E4AB2A6347FB8FA22" " weight=10", +"107.170.101.39:9030 orport=443 id=30973217E70AF00EBE51797FF6D9AA720A902EAA" +" weight=10", +"64.113.32.29:9030 orport=9001 id=30C19B81981F450C402306E2E7CFB6C3F79CB6B2" +" weight=10", "212.83.154.33:8080 orport=8443 id=322C6E3A973BC10FC36DE3037AD27BC89F14723B" " weight=10", "109.105.109.162:52860 orport=60784 id=32EE911D968BE3E016ECA572BB1ED0A9EE43FC2F" " ipv6=[2001:948:7:2::163]:5001" " weight=10", -"146.0.32.144:9030 orport=9001 id=35E8B344F661F4F2E68B17648F35798B44672D7E" +"185.100.84.212:80 orport=443 id=330CD3DB6AD266DC70CDB512B036957D03D9BC59" +" ipv6=[2a06:1700:0:7::1]:443" +" weight=10", +"163.172.13.165:9030 orport=9001 id=33DA0CAB7C27812EFF2E22C9705630A54D101FEB" +" ipv6=[2001:bc8:38cb:201::8]:9001" +" weight=10", +"45.62.255.25:80 orport=443 id=3473ED788D9E63361D1572B7E82EC54338953D2A" " weight=10", "217.79.190.25:9030 orport=9090 id=361D33C96D0F161275EE67E2C91EE10B276E778B" " weight=10", -"62.210.92.11:9130 orport=9101 id=387B065A38E4DAA16D9D41C2964ECBC4B31D30FF" -" ipv6=[2001:bc8:338c::1]:9101" +"37.187.22.87:9030 orport=9001 id=36B9E7AC1E36B62A9D6F330ABEB6012BA7F0D400" +" ipv6=[2001:41d0:a:1657::1]:9001" +" weight=10", +"176.126.252.12:21 orport=8080 id=379FB450010D17078B3766C2273303C358C3A442" +" ipv6=[2a02:59e0:0:7::12]:81" " weight=10", "198.50.191.95:80 orport=443 id=39F096961ED2576975C866D450373A9913AFDC92" " weight=10", "164.132.77.175:9030 orport=9001 id=3B33F6FCA645AD4E91428A3AF7DC736AD9FB727B" " weight=10", -"176.10.107.180:9030 orport=9001 id=3D7E274A87D9A89AF064C13D1EE4CA1F184F2600" +"212.47.230.49:9030 orport=9001 id=3D6D0771E54056AEFC28BB1DE816951F11826E97" +" weight=10", +"217.79.179.177:9030 orport=9001 id=3E53D3979DB07EFD736661C934A1DED14127B684" +" ipv6=[2001:4ba0:fff9:131:6c4f::90d3]:9001" +" weight=10", +"212.47.237.95:9030 orport=9001 id=3F5D8A879C58961BB45A3D26AC41B543B40236D6" +" weight=10", +"185.100.85.101:9030 orport=9001 id=4061C553CA88021B8302F0814365070AAE617270" +" weight=10", +"178.62.86.96:9030 orport=9001 id=439D0447772CB107B886F7782DBC201FA26B92D1" +" ipv6=[2a03:b0c0:1:d0::3cf:7001]:9050" +" weight=10", +"163.172.157.213:8080 orport=443 id=4623A9EC53BFD83155929E56D6F7B55B5E718C24" +" weight=10", +"31.31.78.49:80 orport=443 id=46791D156C9B6C255C2665D4D8393EC7DBAA7798" +" weight=10", +"69.162.139.9:9030 orport=9001 id=4791FC0692EAB60DF2BCCAFF940B95B74E7654F6" +" ipv6=[2607:f128:40:1212::45a2:8b09]:9001" +" weight=10", +"51.254.246.203:9030 orport=9001 id=47B596B81C9E6277B98623A84B7629798A16E8D5" " weight=10", "37.187.102.186:9030 orport=9001 id=489D94333DF66D57FFE34D9D59CC2D97E2CB0053" " ipv6=[2001:41d0:a:26ba::1]:9001" " weight=10", "188.165.194.195:9030 orport=9001 id=49E7AD01BB96F6FE3AB8C3B15BD2470B150354DF" " weight=10", -"108.53.208.157:80 orport=443 id=4F0DB7E687FC7C0AE55C8F243DA8B0EB27FBF1F2" +"62.102.148.67:80 orport=443 id=4A0C3E177AF684581EF780981AEAF51A98A6B5CF" " weight=10", -"212.51.134.123:9030 orport=9001 id=50586E25BE067FD1F739998550EDDCB1A14CA5B2" -" ipv6=[2a02:168:6e00:0:3a60:77ff:fe9c:8bd1]:9001" +"51.254.101.242:9002 orport=9001 id=4CC9CC9195EC38645B699A33307058624F660CCF" +" weight=10", +"81.7.16.182:80 orport=443 id=51E1CF613FD6F9F11FE24743C91D6F9981807D82" +" ipv6=[2a02:180:1:1::517:10b6]:993" +" weight=10", +"138.201.130.32:9030 orport=9001 id=52AEA31188331F421B2EDB494DB65CD181E5B257" " weight=10", -/* Fallback was on 0.2.8.6 list, but changed IPv4 before 0.2.9 - * "5.175.233.86:80 orport=443 id=5525D0429BFE5DC4F1B0E9DE47A4CFA169661E33" - * " weight=10", - */ "94.23.204.175:9030 orport=9001 id=5665A3904C89E22E971305EE8C1997BCA4123C69" " weight=10", -"109.163.234.9:80 orport=443 id=5714542DCBEE1DD9864824723638FD44B2122CEA" +"95.130.12.119:80 orport=443 id=587E0A9552E4274B251F29B5B2673D38442EE4BF" " weight=10", "185.21.100.50:9030 orport=9001 id=58ED9C9C35E433EE58764D62892B4FFD518A3CD0" " ipv6=[2a00:1158:2:cd00:0:74:6f:72]:443" " weight=10", "78.142.142.246:80 orport=443 id=5A5E03355C1908EBF424CAF1F3ED70782C0D2F74" " weight=10", -/* Fallback was on 0.2.8.2-alpha list, but went down before 0.2.8.5 - * "185.100.85.138:80 orport=46356 id=5C4DF16A0029CC4F67D3E127356E68F219269859" - * " weight=10", - */ -"178.16.208.62:80 orport=443 id=5CF8AFA5E4B0BB88942A44A3F3AAE08C3BDFD60B" -" ipv6=[2a00:1c20:4089:1234:a6a4:2926:d0af:dfee]:443" +"46.28.207.19:80 orport=443 id=5B92FA5C8A49D46D235735504C72DBB3472BA321" +" weight=10", +"120.29.217.46:80 orport=443 id=5E853C94AB1F655E9C908924370A0A6707508C62" " weight=10", "95.128.43.164:80 orport=443 id=616081EC829593AF4232550DE6FFAA1D75B37A90" " ipv6=[2a02:ec0:209:10::4]:443" " weight=10", -"89.187.142.208:80 orport=443 id=64186650FFE4469EBBE52B644AE543864D32F43C" +"195.154.122.54:80 orport=443 id=64E99CB34C595A02A3165484BD1215E7389322C6" +" weight=10", +"163.172.139.104:8080 orport=443 id=68F175CCABE727AA2D2309BCD8789499CEE36ED7" +" weight=10", +"85.214.62.48:80 orport=443 id=6A7551EEE18F78A9813096E82BF84F740D32B911" +" weight=10", +"95.130.11.147:9030 orport=443 id=6B697F3FF04C26123466A5C0E5D1F8D91925967A" +" weight=10", +"91.121.84.137:4951 orport=4051 id=6DE61A6F72C1E5418A66BFED80DFB63E4C77668F" +" ipv6=[2001:41d0:1:8989::1]:4051" +" weight=10", +"213.61.66.117:9032 orport=9002 id=6E44A52E3D1FF7683FE5C399C3FB5E912DE1C6B4" +" weight=10", +"80.127.137.19:80 orport=443 id=6EF897645B79B6CB35E853B32506375014DE3621" +" ipv6=[2001:981:47c1:1::6]:443" " weight=10", -/* Fallback was on 0.2.8.6 list, but operator opted-out before 0.2.9 - * "144.76.73.140:9030 orport=9001 id=6A640018EABF3DA9BAD9321AA37C2C87BBE1F907" - * " weight=10", - */ -/* Fallback was on 0.2.8.6 list, but went down before 0.2.9 - * "94.126.23.174:9030 orport=9001 id=6FC6F08270D565BE89B7C819DD8E2D487397C073" - * " weight=10", - */ -"176.31.191.26:9030 orport=9001 id=7350AB9ED7568F22745198359373C04AC783C37C" +"95.183.48.12:80 orport=443 id=7187CED1A3871F837D0E60AC98F374AC541CB0DA" +" weight=10", +"163.172.35.247:80 orport=443 id=71AB4726D830FAE776D74AEF790CF04D8E0151B4" +" weight=10", +"85.235.250.88:80 orport=443 id=72B2B12A3F60408BDBC98C6DF53988D3A0B3F0EE" " weight=10", "46.101.237.246:9030 orport=9001 id=75F1992FD3F403E9C082A5815EB5D12934CDF46C" " ipv6=[2a03:b0c0:3:d0::208:5001]:9050" " weight=10", +"188.166.133.133:9030 orport=9001 id=774555642FDC1E1D4FDF2E0C31B7CA9501C5C9C7" +" ipv6=[2a03:b0c0:2:d0::5:f001]:9001" +" weight=10", +"81.30.158.213:9030 orport=9001 id=789EA6C9AE9ADDD8760903171CFA9AC5741B0C70" +" ipv6=[2001:4ba0:cafe:e84::1]:9001" +" weight=10", "185.11.180.67:80 orport=9001 id=794D8EA8343A4E820320265D05D4FA83AB6D1778" " weight=10", +"171.25.193.131:80 orport=443 id=79861CF8522FC637EF046F7688F5289E49D94576" +" weight=10", "62.210.129.246:80 orport=443 id=79E169B25E4C7CE99584F6ED06F379478F23E2B8" " weight=10", "82.223.21.74:9030 orport=9001 id=7A32C9519D80CA458FC8B034A28F5F6815649A98" " ipv6=[2001:470:53e0::cafe]:9050" " weight=10", +"51.254.136.195:80 orport=443 id=7BB70F8585DFC27E75D692970C0EEB0F22983A63" +" weight=10", +"193.11.114.45:9031 orport=9002 id=80AAF8D5956A43C197104CEF2550CD42D165C6FB" +" weight=10", "192.160.102.164:80 orport=9001 id=823AA81E277F366505545522CEDC2F529CE4DC3F" +" ipv6=[2605:e200:d00c:c01d::1111]:9002" " weight=10", "192.87.28.82:9030 orport=9001 id=844AE9CAD04325E955E2BE1521563B79FE7094B7" " weight=10", -/* Fallback was on 0.2.8.2-alpha list, but changed address before 0.2.8.5 - * "84.219.173.60:9030 orport=443 id=855BC2DABE24C861CD887DB9B2E950424B49FC34" - * " weight=10", - */ "163.172.138.22:80 orport=443 id=8664DC892540F3C789DB37008236C096C871734D" +" ipv6=[2001:bc8:4400:2100::1:3]:443" +" weight=10", +"188.166.23.127:80 orport=443 id=8672E8A01B4D3FA4C0BBE21C740D4506302EA487" +" ipv6=[2a03:b0c0:2:d0::27b:7001]:9050" " weight=10", -/* Fallback was on 0.2.8.2-alpha list, but downloads were slow before 0.2.8.5 - * "185.96.88.29:80 orport=443 id=86C281AD135058238D7A337D546C902BE8505DDE" - * " weight=10", - */ "93.180.156.84:9030 orport=9001 id=8844D87E9B038BE3270938F05AF797E1D3C74C0F" " weight=10", -/* Fallback was on 0.2.8.2-alpha list, but DirPort changed before 0.2.8.5 - * "178.217.184.32:9030 orport=443 id=8B7F47AE1A5D954A3E58ACDE0865D09DBA5B738D" - * " weight=10", - */ +"212.47.241.21:80 orport=443 id=892F941915F6A0C6E0958E52E0A9685C190CF45C" +" weight=10", +"163.172.194.53:9030 orport=9001 id=8C00FA7369A7A308F6A137600F0FA07990D9D451" +" weight=10", +"178.254.44.135:9030 orport=9001 id=8FA37B93397015B2BC5A525C908485260BE9F422" +" weight=10", "151.80.42.103:9030 orport=9001 id=9007C1D8E4F03D506A4A011B907A9E8D04E3C605" " ipv6=[2001:41d0:e:f67::114]:9001" " weight=10", -"5.79.68.161:81 orport=443 id=9030DCF419F6E2FBF84F63CBACBA0097B06F557E" -" ipv6=[2001:1af8:4700:a012:1::1]:443" +"173.255.245.116:9030 orport=9001 id=91E4015E1F82DAF0121D62267E54A1F661AB6DC7" " weight=10", "51.255.41.65:9030 orport=9001 id=9231DF741915AA1630031A93026D88726877E93A" " weight=10", +"178.16.208.57:80 orport=443 id=92CFD9565B24646CAC2D172D3DB503D69E777B8A" +" ipv6=[2a00:1c20:4089:1234:7825:2c5d:1ecd:c66f]:443" +" weight=10", "91.219.237.244:80 orport=443 id=92ECC9E0E2AF81BB954719B189AC362E254AD4A5" " weight=10", -/* Fallback was on 0.2.8.2-alpha list, but changed fingerprint before 0.2.8.5 - * "46.101.102.71:80 orport=443 id=9504CB22EEB25D344DE63CB7A6F2C46F895C3686" - * " ipv6=[2a03:b0c0:3:d0::2ed:7001]:9050" - * " weight=10", - */ +"204.8.156.142:80 orport=443 id=94C4B7B8C50C86A92B6A20107539EE2678CF9A28" +" weight=10", +"176.10.104.243:8080 orport=8443 id=95DA61AEF23A6C851028C1AA88AD8593F659E60F" +" weight=10", +"85.10.202.87:9030 orport=9001 id=971AFB23C168DCD8EDA17473C1C452B359DE3A5A" +" weight=10", "85.214.206.219:9030 orport=9001 id=98F8D5F359949E41DE8DF3DBB1975A86E96A84A0" " weight=10", +"163.172.223.200:80 orport=443 id=998BF3ED7F70E33D1C307247B9626D9E7573C438" +" weight=10", "81.7.10.93:31336 orport=31337 id=99E246DB480B313A3012BC3363093CC26CD209C7" " weight=10", +"91.229.20.27:9030 orport=9001 id=9A0D54D3A6D2E0767596BF1515E6162A75B3293F" +" weight=10", +"66.111.2.20:9030 orport=9001 id=9A68B85A02318F4E7E87F2828039FBD5D75B0142" +" weight=10", +"5.35.251.247:9030 orport=9001 id=9B1F5187DFBA89DC24B37EA7BF896C12B43A27AE" +" weight=10", +"5.9.151.241:9030 orport=4223 id=9BF04559224F0F1C3C953D641F1744AF0192543A" +" weight=10", +"86.105.212.130:9030 orport=443 id=9C900A7F6F5DD034CFFD192DAEC9CCAA813DB022" +" weight=10", +"146.185.177.103:80 orport=9030 id=9EC5E097663862DF861A18C32B37C5F82284B27D" +" weight=10", +"178.254.20.134:80 orport=443 id=9F5068310818ED7C70B0BC4087AB55CB12CB4377" +" weight=10", "46.28.110.244:80 orport=443 id=9F7D6E6420183C2B76D3CE99624EBC98A21A967E" " weight=10", -"46.165.230.5:80 orport=443 id=A0F06C2FADF88D3A39AA3072B406F09D7095AC9E" +"178.62.22.36:80 orport=443 id=A0766C0D3A667A3232C7D569DE94A28F9922FCB1" +" ipv6=[2a03:b0c0:1:d0::174:1]:9050" " weight=10", "171.25.193.77:80 orport=443 id=A10C4F666D27364036B562823E5830BC448E046A" " ipv6=[2001:67c:289c:3::77]:443" " weight=10", -"176.9.5.116:9030 orport=9001 id=A1EB8D8F1EE28DB98BBB1EAA3B4BEDD303BAB911" +"171.25.193.78:80 orport=443 id=A478E421F83194C114F41E94F95999672AED51FE" +" ipv6=[2001:67c:289c:3::78]:443" +" weight=10", +"178.16.208.58:80 orport=443 id=A4C98CEA3F34E05299417E9F885A642C88EF6029" +" ipv6=[2a00:1c20:4089:1234:cdae:1b3e:cc38:3d45]:443" +" weight=10", +"163.172.149.122:80 orport=443 id=A9406A006D6E7B5DA30F2C6D4E42A338B5E340B2" +" weight=10", +"213.61.66.116:9033 orport=9003 id=A9DEB920B42B4EC1DE6249034039B06D61F38690" " weight=10", "192.34.63.137:9030 orport=443 id=ABCB4965F1FEE193602B50A365425105C889D3F8" " weight=10", -/* Fallback was on 0.2.8.2-alpha list, but had low uptime before 0.2.8.5 - * "195.154.164.243:80 orport=443 id=AC66FFA4AB35A59EBBF5BF4C70008BF24D8A7A5C" - * " ipv6=[2001:bc8:399f:f000::1]:993" - * " weight=10", - */ +"195.154.164.243:80 orport=443 id=AC66FFA4AB35A59EBBF5BF4C70008BF24D8A7A5C" +" weight=10", "86.59.119.88:80 orport=443 id=ACD889D86E02EDDAB1AFD81F598C0936238DC6D0" " weight=10", +"185.129.62.62:9030 orport=9001 id=ACDD9E85A05B127BA010466C13C8C47212E8A38F" +" ipv6=[2a06:d380:0:3700::62]:9001" +" weight=10", +"188.40.128.246:9030 orport=9001 id=AD19490C7DBB26D3A68EFC824F67E69B0A96E601" +" weight=10", "163.172.131.88:80 orport=443 id=AD253B49E303C6AB1E048B014392AC569E8A7DAE" " ipv6=[2001:bc8:4400:2100::2:1009]:443" " weight=10", -"178.254.44.135:80 orport=443 id=AE6A8C18E7499B586CD36246AC4BCAFFBBF93AB2" +"176.10.104.240:8080 orport=8443 id=AD86CD1A49573D52A7B6F4A35750F161AAD89C88" +" weight=10", +"31.185.104.20:80 orport=443 id=ADB2C26629643DBB9F8FE0096E7D16F9414B4F8D" " weight=10", "37.187.7.74:80 orport=443 id=AEA43CB1E47BE5F8051711B2BF01683DB1568E05" " ipv6=[2001:41d0:a:74a::1]:443" " weight=10", +"176.126.252.11:443 orport=9001 id=B0279A521375F3CB2AE210BDBFC645FDD2E1973A" +" ipv6=[2a02:59e0:0:7::11]:9003" +" weight=10", "212.129.62.232:80 orport=443 id=B143D439B72D239A419F8DCE07B8A8EB1B486FA7" " weight=10", "185.66.250.141:9030 orport=9001 id=B1726B94885CE3AC3910CA8B60622B97B98E2529" " weight=10", +"198.199.64.217:80 orport=443 id=B1D81825CFD7209BD1B4520B040EF5653C204A23" +" ipv6=[2604:a880:400:d0::1a9:b001]:9050" +" weight=10", +"136.243.214.137:80 orport=443 id=B291D30517D23299AD7CEE3E60DFE60D0E3A4664" +" weight=10", +"212.47.233.86:9030 orport=9001 id=B4CAFD9CBFB34EC5DAAC146920DC7DFAFE91EA20" +" weight=10", +"93.115.97.242:9030 orport=9001 id=B5212DB685A2A0FCFBAE425738E478D12361710D" +" weight=10", +"81.2.209.10:443 orport=80 id=B6904ADD4C0D10CDA7179E051962350A69A63243" +" ipv6=[2001:15e8:201:1::d10a]:80" +" weight=10", "193.11.114.46:9032 orport=9003 id=B83DC1558F0D34353BB992EF93AFEAFDB226A73E" " weight=10", -/* Fallback was on 0.2.8.2-alpha list, but downloads were slow before 0.2.8.5 - * "178.62.36.64:9030 orport=9001 id=B87C84E38DAECFFFFDE98E5AEE5786AFDC748F2C" - * " weight=10", - */ +"85.248.227.164:444 orport=9002 id=B84F248233FEA90CAD439F292556A3139F6E1B82" +" ipv6=[2a00:1298:8011:212::164]:9004" +" weight=10", "197.231.221.211:9030 orport=9001 id=BC630CBBB518BE7E9F4E09712AB0269E9DC7D626" " weight=10", +"89.163.247.43:9030 orport=9001 id=BC7ACFAC04854C77167C7D66B7E471314ED8C410" +" weight=10", "198.96.155.3:8080 orport=5001 id=BCEDF6C193AA687AE471B8A22EBF6BC57C2D285E" " weight=10", +"128.199.55.207:9030 orport=9001 id=BCEF908195805E03E92CCFE669C48738E556B9C5" +" ipv6=[2a03:b0c0:2:d0::158:3001]:9001" +" weight=10", "148.251.190.229:9030 orport=9010 id=BF0FB582E37F738CD33C3651125F2772705BB8E8" " ipv6=[2a01:4f8:211:c68::2]:9010" " weight=10", -"188.138.112.60:1433 orport=1521 id=C414F28FD2BEC1553024299B31D4E726BEB8E788" +"163.172.35.249:80 orport=443 id=C08DE49658E5B3CFC6F2A952B453C4B608C9A16A" " weight=10", -"195.154.79.128:80 orport=443 id=C697612CA5AED06B8D829FCC6065B9287212CB2F" +"185.35.202.221:9030 orport=9001 id=C13B91384CDD52A871E3ECECE4EF74A7AC7DCB08" +" ipv6=[2a02:ed06::221]:9001" " weight=10", -"37.59.46.159:9030 orport=9001 id=CBD0D1BD110EC52963082D839AC6A89D0AE243E7" +"213.239.217.18:1338 orport=1337 id=C37BC191AC389179674578C3E6944E925FE186C2" +" ipv6=[2a01:4f8:a0:746a:101:1:1:1]:1337" " weight=10", -"91.121.54.8:9030 orport=9001 id=CBEE0F3303C8C50462A12107CA2AE061831931BC" +"188.138.112.60:1433 orport=1521 id=C414F28FD2BEC1553024299B31D4E726BEB8E788" +" weight=10", +"37.59.46.159:9030 orport=9001 id=CBD0D1BD110EC52963082D839AC6A89D0AE243E7" " weight=10", "178.62.199.226:80 orport=443 id=CBEFF7BA4A4062045133C053F2D70524D8BBE5BE" " ipv6=[2a03:b0c0:2:d0::b7:5001]:443" " weight=10", -/* Fallback was on 0.2.8.2-alpha list, but went down before 0.2.8.5 - * "81.7.17.171:80 orport=443 id=CFECDDCA990E3EF7B7EC958B22441386B6B8D820" - * " ipv6=[2a02:180:1:1::517:11ab]:443" - * " weight=10", - */ "134.119.3.164:9030 orport=9001 id=D1B8AAA98C65F3DF7D8BB3AF881CAEB84A33D8EE" " weight=10", "185.13.38.75:9030 orport=9001 id=D2A1703758A0FBBA026988B92C2F88BAB59F9361" " weight=10", -"37.187.115.157:9030 orport=9001 id=D5039E1EBFD96D9A3F9846BF99EC9F75EDDE902A" -" weight=10", -/* Fallback was on 0.2.8.2-alpha list, but lost Guard flag before 0.2.8.5 - * "185.14.185.240:9030 orport=443 id=D62FB817B0288085FAC38A6DC8B36DCD85B70260" - * " weight=10", - */ "37.221.162.226:9030 orport=9001 id=D64366987CB39F61AD21DBCF8142FA0577B92811" " weight=10", +"46.101.169.151:9030 orport=9001 id=D760C5B436E42F93D77EF2D969157EEA14F9B39C" +" ipv6=[2a03:b0c0:3:d0::74f:a001]:9001" +" weight=10", +"46.4.111.124:9030 orport=9001 id=D9065F9E57899B3D272AA212317AF61A9B14D204" +" weight=10", "193.35.52.53:9030 orport=9001 id=DAA39FC00B196B353C2A271459C305C429AF09E4" " weight=10", +"178.33.183.251:80 orport=443 id=DD823AFB415380A802DCAEB9461AE637604107FB" +" ipv6=[2001:41d0:2:a683::251]:443" +" weight=10", "178.62.173.203:9030 orport=9001 id=DD85503F2D1F52EF9EAD621E942298F46CD2FC10" " ipv6=[2a03:b0c0:0:1010::a4:b001]:9001" " weight=10", +"83.212.99.68:80 orport=443 id=DDBB2A38252ADDA53E4492DDF982CA6CC6E10EC0" +" ipv6=[2001:648:2ffc:1225:a800:bff:fe3d:67b5]:443" +" weight=10", "5.34.183.205:80 orport=443 id=DDD7871C1B7FA32CB55061E08869A236E61BDDF8" " weight=10", -/* Fallback was on 0.2.8.6 list, but went down before 0.2.9 - * "195.191.233.221:80 orport=443 id=DE134FC8E5CC4EC8A5DE66934E70AC9D70267197" - * " weight=10", - */ -"46.252.26.2:45212 orport=49991 id=E589316576A399C511A9781A73DA4545640B479D" -" weight=10", -/* Fallback was on 0.2.8.6 list, but went down before 0.2.9 - * "176.31.180.157:143 orport=22 id=E781F4EC69671B3F1864AE2753E0890351506329" - * " ipv6=[2001:41d0:8:eb9d::1]:22" - * " weight=10", - */ +"167.114.66.61:9696 orport=443 id=DE6CD5F09DF26076F26321B0BDFBE78ACD935C65" +" ipv6=[2607:5300:100::78d]:443" +" weight=10", +"78.24.75.53:9030 orport=9001 id=DEB73705B2929AE9BE87091607388939332EF123" +" weight=10", +"92.222.38.67:80 orport=443 id=DED6892FF89DBD737BA689698A171B2392EB3E82" +" weight=10", +"217.12.199.208:80 orport=443 id=DF3AED4322B1824BF5539AE54B2D1B38E080FF05" +" ipv6=[2a02:27a8:0:2::7e]:443" +" weight=10", +"167.114.35.28:9030 orport=9001 id=E65D300F11E1DB12C534B0146BDAB6972F1A8A48" +" weight=10", +"212.47.244.38:8080 orport=443 id=E81EF60A73B3809F8964F73766B01BAA0A171E20" +" weight=10", "131.188.40.188:443 orport=80 id=EBE718E1A49EE229071702964F8DB1F318075FF8" " weight=10", -"91.219.236.222:80 orport=443 id=EC413181CEB1C8EDC17608BBB177CD5FD8535E99" +"89.40.71.149:8081 orport=8080 id=EC639EDAA5121B47DBDF3D6B01A22E48A8CB6CC7" +" weight=10", +"192.87.28.28:9030 orport=9001 id=ED2338CAC2711B3E331392E1ED2831219B794024" +" weight=10", +"212.83.40.238:9030 orport=9001 id=F409FA7902FD89270E8DE0D7977EA23BC38E5887" +" weight=10", +"5.199.142.236:9030 orport=9001 id=F4C0EDAA0BF0F7EC138746F8FEF1CE26C7860265" " weight=10", -"94.242.246.23:443 orport=9001 id=F65E0196C94DFFF48AFBF2F5F9E3E19AAE583FD0" -" ipv6=[2a01:608:ffff:ff07::1:23]:9003" +"46.28.207.141:80 orport=443 id=F69BED36177ED727706512BA6A97755025EEA0FB" " weight=10", -"46.101.143.173:80 orport=443 id=F960DF50F0FD4075AC9B505C1D4FFC8384C490FB" +"78.47.18.110:443 orport=80 id=F8D27B163B9247B232A2EEE68DD8B698695C28DE" +" weight=10", +"178.254.13.126:80 orport=443 id=F9246DEF2B653807236DA134F2AEAB103D58ABFE" +" weight=10", +"185.96.180.29:80 orport=443 id=F93D8F37E35C390BCAD9F9069E13085B745EC216" +" weight=10", +"104.243.35.196:9030 orport=9001 id=FA3415659444AE006E7E9E5375E82F29700CFDFD" +" weight=10", +"86.59.119.83:80 orport=443 id=FC9AC8EA0160D88BCCFDE066940D7DD9FA45495B" " weight=10", -/* Fallback was on 0.2.8.6 list, but changed IPv4 before 0.2.9 - * "195.154.8.111:80 orport=443 id=FCB6695F8F2DC240E974510A4B3A0F2B12AB5B64" - * " weight=10", - */ "192.187.124.98:9030 orport=9001 id=FD1871854BFC06D7B02F10742073069F0528B5CC" " weight=10", +"212.129.38.254:9030 orport=9001 id=FDF845FC159C0020E2BDDA120C30C5C5038F74B4" +" weight=10", +"149.56.45.200:9030 orport=9001 id=FE296180018833AF03A8EACD5894A614623D3F76" +" weight=10", "193.11.164.243:9030 orport=9001 id=FFA72BD683BC2FCF988356E6BEC1E490F313FB07" " ipv6=[2001:6b0:7:125::243]:9001" " weight=10", diff --git a/src/or/hibernate.c b/src/or/hibernate.c index aaf5c4bdcd..c2b3bbb839 100644 --- a/src/or/hibernate.c +++ b/src/or/hibernate.c @@ -424,8 +424,8 @@ configure_accounting(time_t now) if (-0.50 <= delta && delta <= 0.50) { /* The start of the period is now a little later or earlier than we * remembered. That's fine; we might lose some bytes we could otherwise - * have written, but better to err on the side of obeying people's - * accounting settings. */ + * have written, but better to err on the side of obeying accounting + * settings. */ log_info(LD_ACCT, "Accounting interval moved by %.02f%%; " "that's fine.", delta*100); interval_end_time = start_of_accounting_period_after(now); @@ -896,7 +896,7 @@ hibernate_go_dormant(time_t now) log_notice(LD_ACCT,"Going dormant. Blowing away remaining connections."); /* Close all OR/AP/exit conns. Leave dir conns because we still want - * to be able to upload server descriptors so people know we're still + * to be able to upload server descriptors so clients know we're still * running, and download directories so we can detect if we're obsolete. * Leave control conns because we still want to be controllable. */ diff --git a/src/or/hs_cache.c b/src/or/hs_cache.c index b7ff979e5b..43cd8c3258 100644 --- a/src/or/hs_cache.c +++ b/src/or/hs_cache.c @@ -15,6 +15,7 @@ #include "config.h" #include "hs_common.h" #include "hs_descriptor.h" +#include "networkstatus.h" #include "rendcache.h" /* Directory descriptor cache. Map indexed by blinded key. */ @@ -86,7 +87,7 @@ cache_dir_desc_new(const char *desc) } /* The blinded pubkey is the indexed key. */ - dir_desc->key = dir_desc->plaintext_data->blinded_kp.pubkey.pubkey; + dir_desc->key = dir_desc->plaintext_data->blinded_pubkey.pubkey; dir_desc->created_ts = time(NULL); return dir_desc; @@ -366,6 +367,18 @@ hs_cache_handle_oom(time_t now, size_t min_remove_bytes) return bytes_removed; } +/** + * Return the maximum size of an HS descriptor we are willing to accept as an + * HSDir. + */ +unsigned int +hs_cache_get_max_descriptor_size(void) +{ + return (unsigned) networkstatus_get_param(NULL, + "HSV3MaxDescriptorSize", + HS_DESC_MAX_LEN, 1, INT32_MAX); +} + /* Initialize the hidden service cache subsystem. */ void hs_cache_init(void) diff --git a/src/or/hs_cache.h b/src/or/hs_cache.h index 01abb8002f..ba95e73338 100644 --- a/src/or/hs_cache.h +++ b/src/or/hs_cache.h @@ -44,6 +44,8 @@ void hs_cache_free_all(void); void hs_cache_clean_as_dir(time_t now); size_t hs_cache_handle_oom(time_t now, size_t min_remove_bytes); +unsigned int hs_cache_get_max_descriptor_size(void); + /* Store and Lookup function. They are version agnostic that is depending on * the requested version of the descriptor, it will be re-routed to the * right function. */ diff --git a/src/or/hs_circuitmap.c b/src/or/hs_circuitmap.c new file mode 100644 index 0000000000..5003b4b593 --- /dev/null +++ b/src/or/hs_circuitmap.c @@ -0,0 +1,328 @@ +/* Copyright (c) 2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file hs_circuitmap.c + * + * \brief Manage the hidden service circuitmap: A hash table that maps binary + * tokens to introduction and rendezvous circuits. + **/ + +#define HS_CIRCUITMAP_PRIVATE + +#include "or.h" +#include "config.h" +#include "circuitlist.h" +#include "hs_circuitmap.h" + +/************************** HS circuitmap code *******************************/ + +/* This is the hidden service circuitmap. It's a hash table that maps + introduction and rendezvous tokens to specific circuits such that given a + token it's easy to find the corresponding circuit. */ +static struct hs_circuitmap_ht *the_hs_circuitmap = NULL; + +/* This is a helper function used by the hash table code (HT_). It returns 1 if + * two circuits have the same HS token. */ +static int +hs_circuits_have_same_token(const or_circuit_t *first_circuit, + const or_circuit_t *second_circuit) +{ + const hs_token_t *first_token; + const hs_token_t *second_token; + + tor_assert(first_circuit); + tor_assert(second_circuit); + + first_token = first_circuit->hs_token; + second_token = second_circuit->hs_token; + + /* Both circs must have a token */ + if (BUG(!first_token) || BUG(!second_token)) { + return 0; + } + + if (first_token->type != second_token->type) { + return 0; + } + + if (first_token->token_len != second_token->token_len) + return 0; + + return tor_memeq(first_token->token, + second_token->token, + first_token->token_len); +} + +/* This is a helper function for the hash table code (HT_). It hashes a circuit + * HS token into an unsigned int for use as a key by the hash table routines.*/ +static inline unsigned int +hs_circuit_hash_token(const or_circuit_t *circuit) +{ + tor_assert(circuit->hs_token); + + return (unsigned) siphash24g(circuit->hs_token->token, + circuit->hs_token->token_len); +} + +/* Register the circuitmap hash table */ +HT_PROTOTYPE(hs_circuitmap_ht, // The name of the hashtable struct + or_circuit_t, // The name of the element struct, + hs_circuitmap_node, // The name of HT_ENTRY member + hs_circuit_hash_token, hs_circuits_have_same_token) + +HT_GENERATE2(hs_circuitmap_ht, or_circuit_t, hs_circuitmap_node, + hs_circuit_hash_token, hs_circuits_have_same_token, + 0.6, tor_reallocarray, tor_free_) + +#ifdef TOR_UNIT_TESTS + +/* Return the global HS circuitmap. Used by unittests. */ +hs_circuitmap_ht * +get_hs_circuitmap(void) +{ + return the_hs_circuitmap; +} + +#endif + +/****************** HS circuitmap utility functions **************************/ + +/** Return a new HS token of type <b>type</b> containing <b>token</b>. */ +static hs_token_t * +hs_token_new(hs_token_type_t type, size_t token_len, + const uint8_t *token) +{ + tor_assert(token); + + hs_token_t *hs_token = tor_malloc_zero(sizeof(hs_token_t)); + hs_token->type = type; + hs_token->token_len = token_len; + hs_token->token = tor_memdup(token, token_len); + + return hs_token; +} + +/** Free memory allocated by this <b>hs_token</b>. */ +static void +hs_token_free(hs_token_t *hs_token) +{ + if (!hs_token) { + return; + } + + tor_free(hs_token->token); + tor_free(hs_token); +} + +/** Return the circuit from the circuitmap with token <b>search_token</b>. */ +static or_circuit_t * +get_circuit_with_token(hs_token_t *search_token) +{ + tor_assert(the_hs_circuitmap); + + /* We use a dummy circuit object for the hash table search routine. */ + or_circuit_t search_circ; + search_circ.hs_token = search_token; + return HT_FIND(hs_circuitmap_ht, the_hs_circuitmap, &search_circ); +} + +/* Helper function that registers <b>circ</b> with <b>token</b> on the HS + circuitmap. This function steals reference of <b>token</b>. */ +static void +hs_circuitmap_register_impl(or_circuit_t *circ, hs_token_t *token) +{ + tor_assert(circ); + tor_assert(token); + tor_assert(the_hs_circuitmap); + + /* If this circuit already has a token, clear it. */ + if (circ->hs_token) { + hs_circuitmap_remove_circuit(circ); + } + + /* Kill old circuits with the same token. We want new intro/rend circuits to + take precedence over old ones, so that HSes and clients and reestablish + killed circuits without changing the HS token. */ + { + or_circuit_t *found_circ; + found_circ = get_circuit_with_token(token); + if (found_circ) { + hs_circuitmap_remove_circuit(found_circ); + if (!found_circ->base_.marked_for_close) { + circuit_mark_for_close(TO_CIRCUIT(found_circ), + END_CIRC_REASON_FINISHED); + } + } + } + + /* Register circuit and token to circuitmap. */ + circ->hs_token = token; + HT_INSERT(hs_circuitmap_ht, the_hs_circuitmap, circ); +} + +/** Helper function: Register <b>circ</b> of <b>type</b> on the HS + * circuitmap. Use the HS <b>token</b> as the key to the hash table. If + * <b>token</b> is not set, clear the circuit of any HS tokens. */ +static void +hs_circuitmap_register_circuit(or_circuit_t *circ, + hs_token_type_t type, size_t token_len, + const uint8_t *token) +{ + hs_token_t *hs_token = NULL; + + /* Create a new token and register it to the circuitmap */ + tor_assert(token); + hs_token = hs_token_new(type, token_len, token); + tor_assert(hs_token); + hs_circuitmap_register_impl(circ, hs_token); +} + +/* Query circuitmap for circuit with <b>token</b> of size <b>token_len</b>. + * Only returns a circuit with purpose equal to the <b>wanted_circ_purpose</b> + * parameter and if it is NOT marked for close. Return NULL if no such circuit + * is found. */ +static or_circuit_t * +hs_circuitmap_get_circuit(hs_token_type_t type, + size_t token_len, + const uint8_t *token, + uint8_t wanted_circ_purpose) +{ + or_circuit_t *found_circ = NULL; + + tor_assert(the_hs_circuitmap); + + /* Check the circuitmap if we have a circuit with this token */ + { + hs_token_t *search_hs_token = hs_token_new(type, token_len, token); + tor_assert(search_hs_token); + found_circ = get_circuit_with_token(search_hs_token); + hs_token_free(search_hs_token); + } + + /* Check that the circuit is useful to us */ + if (!found_circ || + found_circ->base_.purpose != wanted_circ_purpose || + found_circ->base_.marked_for_close) { + return NULL; + } + + return found_circ; +} + +/************** Public circuitmap API ****************************************/ + +/* Public function: Return v3 introduction circuit with <b>auth_key</b>. Return + * NULL if no such circuit is found in the circuitmap. */ +or_circuit_t * +hs_circuitmap_get_intro_circ_v3(const ed25519_public_key_t *auth_key) +{ + tor_assert(auth_key); + + return hs_circuitmap_get_circuit(HS_TOKEN_INTRO_V3, + ED25519_PUBKEY_LEN, auth_key->pubkey, + CIRCUIT_PURPOSE_INTRO_POINT); +} + +/* Public function: Return v2 introduction circuit with <b>digest</b>. Return + * NULL if no such circuit is found in the circuitmap. */ +or_circuit_t * +hs_circuitmap_get_intro_circ_v2(const uint8_t *digest) +{ + tor_assert(digest); + + return hs_circuitmap_get_circuit(HS_TOKEN_INTRO_V2, + REND_TOKEN_LEN, digest, + CIRCUIT_PURPOSE_INTRO_POINT); +} + +/* Public function: Return rendezvous circuit with rendezvous + * <b>cookie</b>. Return NULL if no such circuit is found in the circuitmap. */ +or_circuit_t * +hs_circuitmap_get_rend_circ(const uint8_t *cookie) +{ + tor_assert(cookie); + + return hs_circuitmap_get_circuit(HS_TOKEN_REND, + REND_TOKEN_LEN, cookie, + CIRCUIT_PURPOSE_REND_POINT_WAITING); +} + +/* Public function: Register rendezvous circuit with key <b>cookie</b> to the + * circuitmap. */ +void +hs_circuitmap_register_rend_circ(or_circuit_t *circ, const uint8_t *cookie) +{ + hs_circuitmap_register_circuit(circ, + HS_TOKEN_REND, + REND_TOKEN_LEN, cookie); +} + +/* Public function: Register v2 intro circuit with key <b>digest</b> to the + * circuitmap. */ +void +hs_circuitmap_register_intro_circ_v2(or_circuit_t *circ, const uint8_t *digest) +{ + hs_circuitmap_register_circuit(circ, + HS_TOKEN_INTRO_V2, + REND_TOKEN_LEN, digest); +} + +/* Public function: Register v3 intro circuit with key <b>auth_key</b> to the + * circuitmap. */ +void +hs_circuitmap_register_intro_circ_v3(or_circuit_t *circ, + const ed25519_public_key_t *auth_key) +{ + hs_circuitmap_register_circuit(circ, + HS_TOKEN_INTRO_V3, + ED25519_PUBKEY_LEN, auth_key->pubkey); +} + +/** Remove this circuit from the HS circuitmap. Clear its HS token, and remove + * it from the hashtable. */ +void +hs_circuitmap_remove_circuit(or_circuit_t *circ) +{ + tor_assert(the_hs_circuitmap); + + if (!circ || !circ->hs_token) { + return; + } + + /* Remove circ from circuitmap */ + or_circuit_t *tmp; + tmp = HT_REMOVE(hs_circuitmap_ht, the_hs_circuitmap, circ); + /* ... and ensure the removal was successful. */ + if (tmp) { + tor_assert(tmp == circ); + } else { + log_warn(LD_BUG, "Could not find circuit (%u) in circuitmap.", + circ->p_circ_id); + } + + /* Clear token from circ */ + hs_token_free(circ->hs_token); + circ->hs_token = NULL; +} + +/* Initialize the global HS circuitmap. */ +void +hs_circuitmap_init(void) +{ + tor_assert(!the_hs_circuitmap); + + the_hs_circuitmap = tor_malloc_zero(sizeof(struct hs_circuitmap_ht)); + HT_INIT(hs_circuitmap_ht, the_hs_circuitmap); +} + +/* Free all memory allocated by the global HS circuitmap. */ +void +hs_circuitmap_free_all(void) +{ + if (the_hs_circuitmap) { + HT_CLEAR(hs_circuitmap_ht, the_hs_circuitmap); + tor_free(the_hs_circuitmap); + } +} + diff --git a/src/or/hs_circuitmap.h b/src/or/hs_circuitmap.h new file mode 100644 index 0000000000..b587039310 --- /dev/null +++ b/src/or/hs_circuitmap.h @@ -0,0 +1,70 @@ +/* Copyright (c) 2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file hs_circuitmap.h + * \brief Header file for hs_circuitmap.c. + **/ + +#ifndef TOR_HS_CIRCUITMAP_H +#define TOR_HS_CIRCUITMAP_H + +typedef HT_HEAD(hs_circuitmap_ht, or_circuit_t) hs_circuitmap_ht; + +typedef struct hs_token_s hs_token_t; +struct or_circuit_t; + +/** Public HS circuitmap API: */ + +struct or_circuit_t *hs_circuitmap_get_rend_circ(const uint8_t *cookie); +struct or_circuit_t *hs_circuitmap_get_intro_circ_v3( + const ed25519_public_key_t *auth_key); +struct or_circuit_t *hs_circuitmap_get_intro_circ_v2(const uint8_t *digest); + +void hs_circuitmap_register_rend_circ(struct or_circuit_t *circ, + const uint8_t *cookie); +void hs_circuitmap_register_intro_circ_v2(struct or_circuit_t *circ, + const uint8_t *digest); +void hs_circuitmap_register_intro_circ_v3(struct or_circuit_t *circ, + const ed25519_public_key_t *auth_key); + +void hs_circuitmap_remove_circuit(struct or_circuit_t *circ); + +void hs_circuitmap_init(void); +void hs_circuitmap_free_all(void); + +#ifdef HS_CIRCUITMAP_PRIVATE + +/** Represents the type of HS token. */ +typedef enum { + /** A rendezvous cookie (128bit)*/ + HS_TOKEN_REND, + /** A v2 introduction point pubkey (160bit) */ + HS_TOKEN_INTRO_V2, + /** A v3 introduction point pubkey (256bit) */ + HS_TOKEN_INTRO_V3, +} hs_token_type_t; + +/** Represents a token used in the HS protocol. Each such token maps to a + * specific introduction or rendezvous circuit. */ +struct hs_token_s { + /* Type of HS token. */ + hs_token_type_t type; + + /* The size of the token (depends on the type). */ + size_t token_len; + + /* The token itself. Memory allocated at runtime. */ + uint8_t *token; +}; + +#endif /* HS_CIRCUITMAP_PRIVATE */ + +#ifdef TOR_UNIT_TESTS + +hs_circuitmap_ht *get_hs_circuitmap(void); + +#endif /* TOR_UNIT_TESTS */ + +#endif /* TOR_HS_CIRCUITMAP_H */ + diff --git a/src/or/hs_common.c b/src/or/hs_common.c index 7dd97e7c7c..de96946ab5 100644 --- a/src/or/hs_common.c +++ b/src/or/hs_common.c @@ -268,17 +268,3 @@ rend_data_get_pk_digest(const rend_data_t *rend_data, size_t *len_out) } } -/* Return true iff the Onion Services protocol version 3 is enabled. This only - * considers the consensus parameter. If the parameter is not found, the - * default is that it's enabled. */ -int -hs_v3_protocol_is_enabled(void) -{ - /* This consensus param controls if the the onion services version 3 is - * enabled or not which is the first version of the next generation - * (proposal 224). If this option is set to 0, the tor daemon won't support - * the protocol as either a relay, directory, service or client. By default, - * it's enabled if the parameter is not found. */ - return networkstatus_get_param(NULL, "EnableOnionServicesV3", 1, 0, 1); -} - diff --git a/src/or/hs_common.h b/src/or/hs_common.h index 2502f35ad4..e0ab510ea4 100644 --- a/src/or/hs_common.h +++ b/src/or/hs_common.h @@ -17,6 +17,12 @@ /* Version 3 of the protocol (prop224). */ #define HS_VERSION_THREE 3 +/* Denotes ed25519 authentication key on ESTABLISH_INTRO cell. */ +#define AUTH_KEY_ED25519 0x02 + +/* String prefix for the signature of ESTABLISH_INTRO */ +#define ESTABLISH_INTRO_SIG_PREFIX "Tor establish-intro cell v1" + void rend_data_free(rend_data_t *data); rend_data_t *rend_data_dup(const rend_data_t *data); rend_data_t *rend_data_client_create(const char *onion_address, @@ -33,7 +39,5 @@ const char *rend_data_get_desc_id(const rend_data_t *rend_data, const uint8_t *rend_data_get_pk_digest(const rend_data_t *rend_data, size_t *len_out); -int hs_v3_protocol_is_enabled(void); - #endif /* TOR_HS_COMMON_H */ diff --git a/src/or/hs_descriptor.c b/src/or/hs_descriptor.c index 37aa1d745e..f16a2fdc14 100644 --- a/src/or/hs_descriptor.c +++ b/src/or/hs_descriptor.c @@ -15,13 +15,14 @@ #include "ed25519_cert.h" /* Trunnel interface. */ #include "parsecommon.h" #include "rendcache.h" +#include "hs_cache.h" #include "torcert.h" /* tor_cert_encode_ed22519() */ /* Constant string value used for the descriptor format. */ #define str_hs_desc "hs-descriptor" #define str_desc_cert "descriptor-signing-key-cert" #define str_rev_counter "revision-counter" -#define str_encrypted "encrypted" +#define str_superencrypted "superencrypted" #define str_signature "signature" #define str_lifetime "descriptor-lifetime" /* Constant string value for the encrypted part of the descriptor. */ @@ -35,7 +36,7 @@ #define str_intro_point_start "\n" str_intro_point " " /* Constant string value for the construction to encrypt the encrypted data * section. */ -#define str_enc_hsdir_data "hsdir-encrypted-data" +#define str_enc_hsdir_data "hsdir-superencrypted-data" /* Prefix required to compute/verify HS desc signatures */ #define str_desc_sig_prefix "Tor onion service descriptor sig v3" @@ -56,7 +57,7 @@ static token_rule_t hs_desc_v3_token_table[] = { T1(str_lifetime, R3_DESC_LIFETIME, EQ(1), NO_OBJ), T1(str_desc_cert, R3_DESC_SIGNING_CERT, NO_ARGS, NEED_OBJ), T1(str_rev_counter, R3_REVISION_COUNTER, EQ(1), NO_OBJ), - T1(str_encrypted, R3_ENCRYPTED, NO_ARGS, NEED_OBJ), + T1(str_superencrypted, R3_SUPERENCRYPTED, NO_ARGS, NEED_OBJ), T1_END(str_signature, R3_SIGNATURE, EQ(1), NO_OBJ), END_OF_TABLE }; @@ -219,7 +220,7 @@ encode_link_specifiers(const smartlist_t *specs) /* Encode an introduction point encryption key and return a newly allocated * string with it. On failure, return NULL. */ static char * -encode_enc_key(const ed25519_keypair_t *sig_key, +encode_enc_key(const ed25519_public_key_t *sig_key, const hs_desc_intro_point_t *ip) { char *encoded = NULL; @@ -237,8 +238,7 @@ encode_enc_key(const ed25519_keypair_t *sig_key, uint8_t *cert_data = NULL; /* Create cross certification cert. */ - cert_len = tor_make_rsa_ed25519_crosscert(&sig_key->pubkey, - ip->enc_key.legacy, + cert_len = tor_make_rsa_ed25519_crosscert(sig_key, ip->enc_key.legacy, now + HS_DESC_CERT_LIFETIME, &cert_data); if (cert_len < 0) { @@ -282,7 +282,7 @@ encode_enc_key(const ed25519_keypair_t *sig_key, } tor_cert_t *cross_cert = tor_cert_create(&curve_kp, CERT_TYPE_CROSS_HS_IP_KEYS, - &sig_key->pubkey, now, + sig_key, now, HS_DESC_CERT_LIFETIME, CERT_FLAG_INCLUDE_SIGNING_KEY); memwipe(&curve_kp, 0, sizeof(curve_kp)); @@ -318,7 +318,7 @@ encode_enc_key(const ed25519_keypair_t *sig_key, /* Encode an introduction point object and return a newly allocated string * with it. On failure, return NULL. */ static char * -encode_intro_point(const ed25519_keypair_t *sig_key, +encode_intro_point(const ed25519_public_key_t *sig_key, const hs_desc_intro_point_t *ip) { char *encoded_ip = NULL; @@ -377,9 +377,9 @@ build_secret_input(const hs_descriptor_t *desc, uint8_t *dst, size_t dstlen) /* XXX use the destination length as the memcpy length */ /* Copy blinded public key. */ - memcpy(dst, desc->plaintext_data.blinded_kp.pubkey.pubkey, - sizeof(desc->plaintext_data.blinded_kp.pubkey.pubkey)); - offset += sizeof(desc->plaintext_data.blinded_kp.pubkey.pubkey); + memcpy(dst, desc->plaintext_data.blinded_pubkey.pubkey, + sizeof(desc->plaintext_data.blinded_pubkey.pubkey)); + offset += sizeof(desc->plaintext_data.blinded_pubkey.pubkey); /* Copy subcredential. */ memcpy(dst + offset, desc->subcredential, sizeof(desc->subcredential)); offset += sizeof(desc->subcredential); @@ -541,8 +541,9 @@ build_encrypted(const uint8_t *key, const uint8_t *iv, const char *plaintext, tor_assert(plaintext); tor_assert(encrypted_out); - /* This creates a cipher for AES128. It can't fail. */ - cipher = crypto_cipher_new_with_iv((const char *) key, (const char *) iv); + /* This creates a cipher for AES. It can't fail. */ + cipher = crypto_cipher_new_with_iv_and_bits(key, iv, + HS_DESC_ENCRYPTED_BIT_SIZE); /* This can't fail. */ encrypted_len = build_plaintext_padding(plaintext, plaintext_len, &padded_plaintext); @@ -573,7 +574,7 @@ encrypt_descriptor_data(const hs_descriptor_t *desc, const char *plaintext, size_t encrypted_len, final_blob_len, offset = 0; uint8_t *encrypted; uint8_t salt[HS_DESC_ENCRYPTED_SALT_LEN]; - uint8_t secret_key[CIPHER_KEY_LEN], secret_iv[CIPHER_IV_LEN]; + uint8_t secret_key[HS_DESC_ENCRYPTED_KEY_LEN], secret_iv[CIPHER_IV_LEN]; uint8_t mac_key[DIGEST256_LEN], mac[DIGEST256_LEN]; tor_assert(desc); @@ -665,7 +666,7 @@ encode_encrypted_data(const hs_descriptor_t *desc, /* Build the introduction point(s) section. */ SMARTLIST_FOREACH_BEGIN(desc->encrypted_data.intro_points, const hs_desc_intro_point_t *, ip) { - char *encoded_ip = encode_intro_point(&desc->plaintext_data.signing_kp, + char *encoded_ip = encode_intro_point(&desc->plaintext_data.signing_pubkey, ip); if (encoded_ip == NULL) { log_err(LD_BUG, "HS desc intro point is malformed."); @@ -710,7 +711,8 @@ encode_encrypted_data(const hs_descriptor_t *desc, * newly allocated string of the encoded descriptor. On error, -1 is returned * and encoded_out is untouched. */ static int -desc_encode_v3(const hs_descriptor_t *desc, char **encoded_out) +desc_encode_v3(const hs_descriptor_t *desc, + const ed25519_keypair_t *signing_kp, char **encoded_out) { int ret = -1; char *encoded_str = NULL; @@ -718,6 +720,7 @@ desc_encode_v3(const hs_descriptor_t *desc, char **encoded_out) smartlist_t *lines = smartlist_new(); tor_assert(desc); + tor_assert(signing_kp); tor_assert(encoded_out); tor_assert(desc->plaintext_data.version == 3); @@ -732,7 +735,7 @@ desc_encode_v3(const hs_descriptor_t *desc, char **encoded_out) goto err; } if (tor_cert_encode_ed22519(desc->plaintext_data.signing_key_cert, - &encoded_cert) < 0) { + &encoded_cert) < 0) { /* The function will print error logs. */ goto err; } @@ -750,7 +753,7 @@ desc_encode_v3(const hs_descriptor_t *desc, char **encoded_out) desc->plaintext_data.revision_counter); } - /* Build the encrypted data section. */ + /* Build the superencrypted data section. */ { char *enc_b64_blob=NULL; if (encode_encrypted_data(desc, &enc_b64_blob) < 0) { @@ -761,7 +764,7 @@ desc_encode_v3(const hs_descriptor_t *desc, char **encoded_out) "-----BEGIN MESSAGE-----\n" "%s" "-----END MESSAGE-----", - str_encrypted, enc_b64_blob); + str_superencrypted, enc_b64_blob); tor_free(enc_b64_blob); } @@ -775,8 +778,7 @@ desc_encode_v3(const hs_descriptor_t *desc, char **encoded_out) char ed_sig_b64[ED25519_SIG_BASE64_LEN + 1]; if (ed25519_sign_prefixed(&sig, (const uint8_t *) encoded_str, encoded_len, - str_desc_sig_prefix, - &desc->plaintext_data.signing_kp) < 0) { + str_desc_sig_prefix, signing_kp) < 0) { log_warn(LD_BUG, "Can't sign encoded HS descriptor!"); tor_free(encoded_str); goto err; @@ -1058,7 +1060,7 @@ static size_t desc_decrypt_data_v3(const hs_descriptor_t *desc, char **decrypted_out) { uint8_t *decrypted = NULL; - uint8_t secret_key[CIPHER_KEY_LEN], secret_iv[CIPHER_IV_LEN]; + uint8_t secret_key[HS_DESC_ENCRYPTED_KEY_LEN], secret_iv[CIPHER_IV_LEN]; uint8_t mac_key[DIGEST256_LEN], our_mac[DIGEST256_LEN]; const uint8_t *salt, *encrypted, *desc_mac; size_t encrypted_len, result_len = 0; @@ -1118,8 +1120,9 @@ desc_decrypt_data_v3(const hs_descriptor_t *desc, char **decrypted_out) /* Decrypt. Here we are assured that the encrypted length is valid for * decryption. */ crypto_cipher_t *cipher; - cipher = crypto_cipher_new_with_iv((const char *) secret_key, - (const char *) secret_iv); + + cipher = crypto_cipher_new_with_iv_and_bits(secret_key, secret_iv, + HS_DESC_ENCRYPTED_BIT_SIZE); /* Extra byte for the NUL terminated byte. */ decrypted = tor_malloc_zero(encrypted_len + 1); crypto_cipher_decrypt(cipher, (char *) decrypted, @@ -1365,7 +1368,8 @@ decode_intro_points(const hs_descriptor_t *desc, /* Return 1 iff the given base64 encoded signature in b64_sig from the encoded * descriptor in encoded_desc validates the descriptor content. */ STATIC int -desc_sig_is_valid(const char *b64_sig, const ed25519_keypair_t *signing_kp, +desc_sig_is_valid(const char *b64_sig, + const ed25519_public_key_t *signing_pubkey, const char *encoded_desc, size_t encoded_len) { int ret = 0; @@ -1373,7 +1377,7 @@ desc_sig_is_valid(const char *b64_sig, const ed25519_keypair_t *signing_kp, const char *sig_start; tor_assert(b64_sig); - tor_assert(signing_kp); + tor_assert(signing_pubkey); tor_assert(encoded_desc); /* Verifying nothing won't end well :). */ tor_assert(encoded_len > 0); @@ -1408,7 +1412,7 @@ desc_sig_is_valid(const char *b64_sig, const ed25519_keypair_t *signing_kp, (const uint8_t *) encoded_desc, sig_start - encoded_desc, str_desc_sig_prefix, - &signing_kp->pubkey) != 0) { + signing_pubkey) != 0) { log_warn(LD_REND, "Invalid signature on service descriptor"); goto err; } @@ -1474,10 +1478,10 @@ desc_decode_plaintext_v3(smartlist_t *tokens, goto err; } - /* Copy the public keys into signing_kp and blinded_kp */ - memcpy(&desc->signing_kp.pubkey, &desc->signing_key_cert->signed_key, + /* Copy the public keys into signing_pubkey and blinded_pubkey */ + memcpy(&desc->signing_pubkey, &desc->signing_key_cert->signed_key, sizeof(ed25519_public_key_t)); - memcpy(&desc->blinded_kp.pubkey, &desc->signing_key_cert->signing_key, + memcpy(&desc->blinded_pubkey, &desc->signing_key_cert->signing_key, sizeof(ed25519_public_key_t)); /* Extract revision counter value. */ @@ -1491,7 +1495,7 @@ desc_decode_plaintext_v3(smartlist_t *tokens, } /* Extract the encrypted data section. */ - tok = find_by_keyword(tokens, R3_ENCRYPTED); + tok = find_by_keyword(tokens, R3_SUPERENCRYPTED); tor_assert(tok->object_body); if (strcmp(tok->object_type, "MESSAGE") != 0) { log_warn(LD_REND, "Service descriptor encrypted data section is invalid"); @@ -1511,7 +1515,7 @@ desc_decode_plaintext_v3(smartlist_t *tokens, tok = find_by_keyword(tokens, R3_SIGNATURE); tor_assert(tok->n_args == 1); /* First arg here is the actual encoded signature. */ - if (!desc_sig_is_valid(tok->args[0], &desc->signing_kp, + if (!desc_sig_is_valid(tok->args[0], &desc->signing_pubkey, encoded_desc, encoded_len)) { goto err; } @@ -1700,8 +1704,9 @@ hs_desc_decode_plaintext(const char *encoded, tor_assert(encoded); tor_assert(plaintext); + /* Check that descriptor is within size limits. */ encoded_len = strlen(encoded); - if (encoded_len >= HS_DESC_MAX_LEN) { + if (encoded_len >= hs_cache_get_max_descriptor_size()) { log_warn(LD_REND, "Service descriptor is too big (%lu bytes)", (unsigned long) encoded_len); goto err; @@ -1806,41 +1811,46 @@ hs_desc_decode_descriptor(const char *encoded, return ret; } -/* Table of encode function version specific. The function are indexed by the +/* Table of encode function version specific. The functions are indexed by the * version number so v3 callback is at index 3 in the array. */ static int (*encode_handlers[])( const hs_descriptor_t *desc, + const ed25519_keypair_t *signing_kp, char **encoded_out) = { /* v0 */ NULL, /* v1 */ NULL, /* v2 */ NULL, desc_encode_v3, }; -/* Encode the given descriptor desc. On success, encoded_out points to a newly - * allocated NUL terminated string that contains the encoded descriptor as a - * string. +/* Encode the given descriptor desc including signing with the given key pair + * signing_kp. On success, encoded_out points to a newly allocated NUL + * terminated string that contains the encoded descriptor as a string. * * Return 0 on success and encoded_out is a valid pointer. On error, -1 is * returned and encoded_out is set to NULL. */ int -hs_desc_encode_descriptor(const hs_descriptor_t *desc, char **encoded_out) +hs_desc_encode_descriptor(const hs_descriptor_t *desc, + const ed25519_keypair_t *signing_kp, + char **encoded_out) { int ret = -1; + uint32_t version; tor_assert(desc); tor_assert(encoded_out); /* Make sure we support the version of the descriptor format. */ - if (!hs_desc_is_supported_version(desc->plaintext_data.version)) { + version = desc->plaintext_data.version; + if (!hs_desc_is_supported_version(version)) { goto err; } /* Extra precaution. Having no handler for the supported version should * never happened else we forgot to add it but we bumped the version. */ - tor_assert(ARRAY_LENGTH(encode_handlers) >= desc->plaintext_data.version); - tor_assert(encode_handlers[desc->plaintext_data.version]); + tor_assert(ARRAY_LENGTH(encode_handlers) >= version); + tor_assert(encode_handlers[version]); - ret = encode_handlers[desc->plaintext_data.version](desc, encoded_out); + ret = encode_handlers[version](desc, signing_kp, encoded_out); if (ret < 0) { goto err; } diff --git a/src/or/hs_descriptor.h b/src/or/hs_descriptor.h index 083d353860..b520d24471 100644 --- a/src/or/hs_descriptor.h +++ b/src/or/hs_descriptor.h @@ -40,7 +40,7 @@ /* Length of the KDF output value which is the length of the secret key, * the secret IV and MAC key length which is the length of H() output. */ #define HS_DESC_ENCRYPTED_KDF_OUTPUT_LEN \ - CIPHER_KEY_LEN + CIPHER_IV_LEN + DIGEST256_LEN + CIPHER256_KEY_LEN + CIPHER_IV_LEN + DIGEST256_LEN /* We need to pad the plaintext version of the encrypted data section before * encryption and it has to be a multiple of this value. */ #define HS_DESC_PLAINTEXT_PADDING_MULTIPLE 128 @@ -54,12 +54,18 @@ HS_DESC_ENCRYPTED_SALT_LEN + \ HS_DESC_PLAINTEXT_PADDING_MULTIPLE + DIGEST256_LEN /* Maximum length in bytes of a full hidden service descriptor. */ -#define HS_DESC_MAX_LEN 32768 // XXX justify +#define HS_DESC_MAX_LEN 50000 /* 50kb max size */ /* The minimum amount of fields a descriptor should contain. The parsing of * the fields are version specific so the only required field, as a generic * view of a descriptor, is 1 that is the version field. */ #define HS_DESC_PLAINTEXT_MIN_FIELDS 1 +/* Key length for the descriptor symmetric encryption. As specified in the + * protocol, we use AES-256 for the encrypted section of the descriptor. The + * following is the length in bytes and the bit size. */ +#define HS_DESC_ENCRYPTED_KEY_LEN CIPHER256_KEY_LEN +#define HS_DESC_ENCRYPTED_BIT_SIZE (HS_DESC_ENCRYPTED_KEY_LEN * 8) + /* Type of authentication in the descriptor. */ typedef enum { HS_DESC_AUTH_PASSWORD = 1, @@ -148,13 +154,13 @@ typedef struct hs_desc_plaintext_data_t { * replica which is signed by the blinded public key for that replica. */ tor_cert_t *signing_key_cert; - /* Signing keypair which is used to sign the descriptor. Same public key + /* Signing public key which is used to sign the descriptor. Same public key * as in the signing key certificate. */ - ed25519_keypair_t signing_kp; + ed25519_public_key_t signing_pubkey; - /* Blinded keypair used for this descriptor derived from the master + /* Blinded public key used for this descriptor derived from the master * identity key and generated for a specific replica number. */ - ed25519_keypair_t blinded_kp; + ed25519_public_key_t blinded_pubkey; /* Revision counter is incremented at each upload, regardless of whether * the descriptor has changed. This avoids leaking whether the descriptor @@ -201,6 +207,7 @@ void hs_desc_plaintext_data_free(hs_desc_plaintext_data_t *desc); void hs_desc_encrypted_data_free(hs_desc_encrypted_data_t *desc); int hs_desc_encode_descriptor(const hs_descriptor_t *desc, + const ed25519_keypair_t *signing_kp, char **encoded_out); int hs_desc_decode_descriptor(const char *encoded, @@ -232,7 +239,7 @@ STATIC int encrypted_data_length_is_valid(size_t len); STATIC int cert_is_valid(tor_cert_t *cert, uint8_t type, const char *log_obj_type); STATIC int desc_sig_is_valid(const char *b64_sig, - const ed25519_keypair_t *signing_kp, + const ed25519_public_key_t *signing_pubkey, const char *encoded_desc, size_t encoded_len); STATIC void desc_intro_point_free(hs_desc_intro_point_t *ip); #endif /* HS_DESCRIPTOR_PRIVATE */ diff --git a/src/or/hs_intropoint.c b/src/or/hs_intropoint.c new file mode 100644 index 0000000000..b37c9a1b9f --- /dev/null +++ b/src/or/hs_intropoint.c @@ -0,0 +1,292 @@ +/* Copyright (c) 2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file hs_intropoint.c + * \brief Implement next generation introductions point functionality + **/ + +#define HS_INTROPOINT_PRIVATE + +#include "or.h" +#include "circuitlist.h" +#include "circuituse.h" +#include "config.h" +#include "relay.h" +#include "rendmid.h" +#include "rephist.h" + +#include "hs/cell_establish_intro.h" +#include "hs/cell_common.h" +#include "hs_circuitmap.h" +#include "hs_intropoint.h" +#include "hs_common.h" + +/** Extract the authentication key from an ESTABLISH_INTRO <b>cell</b> and + * place it in <b>auth_key_out</b>. */ +STATIC void +get_auth_key_from_establish_intro_cell(ed25519_public_key_t *auth_key_out, + const hs_cell_establish_intro_t *cell) +{ + tor_assert(auth_key_out); + + const uint8_t *key_array = + hs_cell_establish_intro_getconstarray_auth_key(cell); + tor_assert(key_array); + tor_assert(hs_cell_establish_intro_getlen_auth_key(cell) == + sizeof(auth_key_out->pubkey)); + + memcpy(auth_key_out->pubkey, key_array, cell->auth_key_len); +} + +/** We received an ESTABLISH_INTRO <b>cell</b>. Verify its signature and MAC, + * given <b>circuit_key_material</b>. Return 0 on success else -1 on error. */ +STATIC int +verify_establish_intro_cell(const hs_cell_establish_intro_t *cell, + const uint8_t *circuit_key_material, + size_t circuit_key_material_len) +{ + /* We only reach this function if the first byte of the cell is 0x02 which + * means that auth_key_type is AUTH_KEY_ED25519, hence this check should + * always pass. See hs_intro_received_establish_intro(). */ + if (BUG(cell->auth_key_type != AUTH_KEY_ED25519)) { + return -1; + } + + /* Make sure the auth key length is of the right size for this type. For + * EXTRA safety, we check both the size of the array and the length which + * must be the same. Safety first!*/ + if (hs_cell_establish_intro_getlen_auth_key(cell) != ED25519_PUBKEY_LEN || + hs_cell_establish_intro_get_auth_key_len(cell) != ED25519_PUBKEY_LEN) { + log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, + "ESTABLISH_INTRO auth key length is invalid"); + return -1; + } + + const uint8_t *msg = cell->start_cell; + + /* Verify the sig */ + { + ed25519_signature_t sig_struct; + const uint8_t *sig_array = hs_cell_establish_intro_getconstarray_sig(cell); + + /* Make sure the signature length is of the right size. For EXTRA safety, + * we check both the size of the array and the length which must be the + * same. Safety first!*/ + if (hs_cell_establish_intro_getlen_sig(cell) != sizeof(sig_struct.sig) || + hs_cell_establish_intro_get_sig_len(cell) != sizeof(sig_struct.sig)) { + log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, + "ESTABLISH_INTRO sig len is invalid"); + return -1; + } + /* We are now sure that sig_len is of the right size. */ + memcpy(sig_struct.sig, sig_array, cell->sig_len); + + ed25519_public_key_t auth_key; + get_auth_key_from_establish_intro_cell(&auth_key, cell); + + const size_t sig_msg_len = cell->end_sig_fields - msg; + int sig_mismatch = ed25519_checksig_prefixed(&sig_struct, + msg, sig_msg_len, + ESTABLISH_INTRO_SIG_PREFIX, + &auth_key); + if (sig_mismatch) { + log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, + "ESTABLISH_INTRO signature not as expected"); + return -1; + } + } + + /* Verify the MAC */ + { + const size_t auth_msg_len = cell->end_mac_fields - msg; + uint8_t mac[DIGEST256_LEN]; + crypto_mac_sha3_256(mac, sizeof(mac), + circuit_key_material, circuit_key_material_len, + msg, auth_msg_len); + if (tor_memneq(mac, cell->handshake_mac, sizeof(mac))) { + log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, + "ESTABLISH_INTRO handshake_auth not as expected"); + return -1; + } + } + + return 0; +} + +/* Send an INTRO_ESTABLISHED cell to <b>circ</b>. */ +MOCK_IMPL(int, +hs_intro_send_intro_established_cell,(or_circuit_t *circ)) +{ + int ret; + uint8_t *encoded_cell = NULL; + ssize_t encoded_len, result_len; + hs_cell_intro_established_t *cell; + cell_extension_t *ext; + + tor_assert(circ); + + /* Build the cell payload. */ + cell = hs_cell_intro_established_new(); + ext = cell_extension_new(); + cell_extension_set_num(ext, 0); + hs_cell_intro_established_set_extensions(cell, ext); + /* Encode the cell to binary format. */ + encoded_len = hs_cell_intro_established_encoded_len(cell); + tor_assert(encoded_len > 0); + encoded_cell = tor_malloc_zero(encoded_len); + result_len = hs_cell_intro_established_encode(encoded_cell, encoded_len, + cell); + tor_assert(encoded_len == result_len); + + ret = relay_send_command_from_edge(0, TO_CIRCUIT(circ), + RELAY_COMMAND_INTRO_ESTABLISHED, + (char *) encoded_cell, encoded_len, + NULL); + /* On failure, the above function will close the circuit. */ + hs_cell_intro_established_free(cell); + tor_free(encoded_cell); + return ret; +} + +/** We received an ESTABLISH_INTRO <b>parsed_cell</b> on <b>circ</b>. It's + * well-formed and passed our verifications. Perform appropriate actions to + * establish an intro point. */ +static int +handle_verified_establish_intro_cell(or_circuit_t *circ, + const hs_cell_establish_intro_t *parsed_cell) +{ + /* Get the auth key of this intro point */ + ed25519_public_key_t auth_key; + get_auth_key_from_establish_intro_cell(&auth_key, parsed_cell); + + /* Then notify the hidden service that the intro point is established by + sending an INTRO_ESTABLISHED cell */ + if (hs_intro_send_intro_established_cell(circ)) { + log_warn(LD_BUG, "Couldn't send INTRO_ESTABLISHED cell."); + return -1; + } + + /* Associate intro point auth key with this circuit. */ + hs_circuitmap_register_intro_circ_v3(circ, &auth_key); + /* Repurpose this circuit into an intro circuit. */ + circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_INTRO_POINT); + + return 0; +} + +/** We just received an ESTABLISH_INTRO cell in <b>circ</b> with payload in + * <b>request</b>. Handle it by making <b>circ</b> an intro circuit. Return 0 + * if everything went well, or -1 if there were errors. */ +static int +handle_establish_intro(or_circuit_t *circ, const uint8_t *request, + size_t request_len) +{ + int cell_ok, retval = -1; + hs_cell_establish_intro_t *parsed_cell = NULL; + + tor_assert(circ); + tor_assert(request); + + log_info(LD_REND, "Received an ESTABLISH_INTRO request on circuit %" PRIu32, + circ->p_circ_id); + + /* Check that the circuit is in shape to become an intro point */ + if (!hs_intro_circuit_is_suitable(circ)) { + goto err; + } + + /* Parse the cell */ + ssize_t parsing_result = hs_cell_establish_intro_parse(&parsed_cell, + request, request_len); + if (parsing_result < 0) { + log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, + "Rejecting %s ESTABLISH_INTRO cell.", + parsing_result == -1 ? "invalid" : "truncated"); + goto err; + } + + cell_ok = verify_establish_intro_cell(parsed_cell, + (uint8_t *) circ->rend_circ_nonce, + sizeof(circ->rend_circ_nonce)); + if (cell_ok < 0) { + log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, + "Failed to verify ESTABLISH_INTRO cell."); + goto err; + } + + /* This cell is legit. Take the appropriate actions. */ + cell_ok = handle_verified_establish_intro_cell(circ, parsed_cell); + if (cell_ok < 0) { + goto err; + } + + log_warn(LD_GENERAL, "Established prop224 intro point on circuit %" PRIu32, + circ->p_circ_id); + + /* We are done! */ + retval = 0; + goto done; + + err: + circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_TORPROTOCOL); + + done: + hs_cell_establish_intro_free(parsed_cell); + return retval; +} + +/* Return True if circuit is suitable for becoming an intro circuit. */ +int +hs_intro_circuit_is_suitable(const or_circuit_t *circ) +{ + /* Basic circuit state sanity checks. */ + if (circ->base_.purpose != CIRCUIT_PURPOSE_OR) { + log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, + "Rejecting ESTABLISH_INTRO on non-OR circuit."); + return 0; + } + + if (circ->base_.n_chan) { + log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, + "Rejecting ESTABLISH_INTRO on non-edge circuit."); + return 0; + } + + return 1; +} + +/* We just received an ESTABLISH_INTRO cell in <b>circ</b>. Figure out of it's + * a legacy or a next gen cell, and pass it to the appropriate handler. */ +int +hs_intro_received_establish_intro(or_circuit_t *circ, const uint8_t *request, + size_t request_len) +{ + tor_assert(circ); + tor_assert(request); + + if (request_len == 0) { + log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Empty ESTABLISH_INTRO cell."); + goto err; + } + + /* Using the first byte of the cell, figure out the version of + * ESTABLISH_INTRO and pass it to the appropriate cell handler */ + const uint8_t first_byte = request[0]; + switch (first_byte) { + case HS_INTRO_AUTH_KEY_TYPE_LEGACY0: + case HS_INTRO_AUTH_KEY_TYPE_LEGACY1: + return rend_mid_establish_intro_legacy(circ, request, request_len); + case HS_INTRO_AUTH_KEY_TYPE_ED25519: + return handle_establish_intro(circ, request, request_len); + default: + log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, + "Unrecognized AUTH_KEY_TYPE %u.", first_byte); + goto err; + } + + err: + circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_TORPROTOCOL); + return -1; +} + diff --git a/src/or/hs_intropoint.h b/src/or/hs_intropoint.h new file mode 100644 index 0000000000..46d8d6f7de --- /dev/null +++ b/src/or/hs_intropoint.h @@ -0,0 +1,42 @@ +/* Copyright (c) 2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file hs_intropoint.h + * \brief Header file for hs_intropoint.c. + **/ + +#ifndef TOR_HS_INTRO_H +#define TOR_HS_INTRO_H + +/* Authentication key type in an ESTABLISH_INTRO cell. */ +enum hs_intro_auth_key_type { + HS_INTRO_AUTH_KEY_TYPE_LEGACY0 = 0x00, + HS_INTRO_AUTH_KEY_TYPE_LEGACY1 = 0x01, + HS_INTRO_AUTH_KEY_TYPE_ED25519 = 0x02, +}; + +int hs_intro_received_establish_intro(or_circuit_t *circ, + const uint8_t *request, + size_t request_len); + +MOCK_DECL(int, hs_intro_send_intro_established_cell,(or_circuit_t *circ)); + +/* also used by rendservice.c */ +int hs_intro_circuit_is_suitable(const or_circuit_t *circ); + +#ifdef HS_INTROPOINT_PRIVATE + +STATIC int +verify_establish_intro_cell(const hs_cell_establish_intro_t *out, + const uint8_t *circuit_key_material, + size_t circuit_key_material_len); + +STATIC void +get_auth_key_from_establish_intro_cell(ed25519_public_key_t *auth_key_out, + const hs_cell_establish_intro_t *cell); + +#endif /* HS_INTROPOINT_PRIVATE */ + +#endif /* TOR_HS_INTRO_H */ + diff --git a/src/or/hs_service.c b/src/or/hs_service.c new file mode 100644 index 0000000000..7b12b63065 --- /dev/null +++ b/src/or/hs_service.c @@ -0,0 +1,178 @@ +/* Copyright (c) 2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file hs_service.c + * \brief Implement next generation hidden service functionality + **/ + +#define HS_SERVICE_PRIVATE + +#include "or.h" +#include "relay.h" +#include "rendservice.h" +#include "circuitlist.h" +#include "circpathbias.h" + +#include "hs_service.h" +#include "hs_common.h" + +#include "hs/cell_establish_intro.h" +#include "hs/cell_common.h" + +/* XXX We don't currently use these functions, apart from generating unittest + data. When we start implementing the service-side support for prop224 we + should revisit these functions and use them. For now we mark them as + unittest-only code: */ +#ifdef TOR_UNIT_TESTS + +/** Given an ESTABLISH_INTRO <b>cell</b>, encode it and place its payload in + * <b>buf_out</b> which has size <b>buf_out_len</b>. Return the number of + * bytes written, or a negative integer if there was an error. */ +STATIC ssize_t +get_establish_intro_payload(uint8_t *buf_out, size_t buf_out_len, + const hs_cell_establish_intro_t *cell) +{ + ssize_t bytes_used = 0; + + if (buf_out_len < RELAY_PAYLOAD_SIZE) { + return -1; + } + + bytes_used = hs_cell_establish_intro_encode(buf_out, buf_out_len, + cell); + return bytes_used; +} + +/* Set the cell extensions of <b>cell</b>. */ +static void +set_cell_extensions(hs_cell_establish_intro_t *cell) +{ + cell_extension_t *cell_extensions = cell_extension_new(); + + /* For now, we don't use extensions at all. */ + cell_extensions->num = 0; /* It's already zeroed, but be explicit. */ + hs_cell_establish_intro_set_extensions(cell, cell_extensions); +} + +/** Given the circuit handshake info in <b>circuit_key_material</b>, create and + * return an ESTABLISH_INTRO cell. Return NULL if something went wrong. The + * returned cell is allocated on the heap and it's the responsibility of the + * caller to free it. */ +STATIC hs_cell_establish_intro_t * +generate_establish_intro_cell(const uint8_t *circuit_key_material, + size_t circuit_key_material_len) +{ + hs_cell_establish_intro_t *cell = NULL; + ssize_t encoded_len; + + log_warn(LD_GENERAL, + "Generating ESTABLISH_INTRO cell (key_material_len: %u)", + (unsigned) circuit_key_material_len); + + /* Generate short-term keypair for use in ESTABLISH_INTRO */ + ed25519_keypair_t key_struct; + if (ed25519_keypair_generate(&key_struct, 0) < 0) { + goto err; + } + + cell = hs_cell_establish_intro_new(); + + /* Set AUTH_KEY_TYPE: 2 means ed25519 */ + hs_cell_establish_intro_set_auth_key_type(cell, AUTH_KEY_ED25519); + + /* Set AUTH_KEY_LEN field */ + /* Must also set byte-length of AUTH_KEY to match */ + int auth_key_len = ED25519_PUBKEY_LEN; + hs_cell_establish_intro_set_auth_key_len(cell, auth_key_len); + hs_cell_establish_intro_setlen_auth_key(cell, auth_key_len); + + /* Set AUTH_KEY field */ + uint8_t *auth_key_ptr = hs_cell_establish_intro_getarray_auth_key(cell); + memcpy(auth_key_ptr, key_struct.pubkey.pubkey, auth_key_len); + + /* No cell extensions needed */ + set_cell_extensions(cell); + + /* Set signature size. + We need to do this up here, because _encode() needs it and we need to call + _encode() to calculate the MAC and signature. + */ + int sig_len = ED25519_SIG_LEN; + hs_cell_establish_intro_set_sig_len(cell, sig_len); + hs_cell_establish_intro_setlen_sig(cell, sig_len); + + /* XXX How to make this process easier and nicer? */ + + /* Calculate the cell MAC (aka HANDSHAKE_AUTH). */ + { + /* To calculate HANDSHAKE_AUTH, we dump the cell in bytes, and then derive + the MAC from it. */ + uint8_t cell_bytes_tmp[RELAY_PAYLOAD_SIZE] = {0}; + uint8_t mac[TRUNNEL_SHA3_256_LEN]; + + encoded_len = hs_cell_establish_intro_encode(cell_bytes_tmp, + sizeof(cell_bytes_tmp), + cell); + if (encoded_len < 0) { + log_warn(LD_OR, "Unable to pre-encode ESTABLISH_INTRO cell."); + goto err; + } + + /* sanity check */ + tor_assert(encoded_len > ED25519_SIG_LEN + 2 + TRUNNEL_SHA3_256_LEN); + + /* Calculate MAC of all fields before HANDSHAKE_AUTH */ + crypto_mac_sha3_256(mac, sizeof(mac), + circuit_key_material, circuit_key_material_len, + cell_bytes_tmp, + encoded_len - + (ED25519_SIG_LEN + 2 + TRUNNEL_SHA3_256_LEN)); + /* Write the MAC to the cell */ + uint8_t *handshake_ptr = + hs_cell_establish_intro_getarray_handshake_mac(cell); + memcpy(handshake_ptr, mac, sizeof(mac)); + } + + /* Calculate the cell signature */ + { + /* To calculate the sig we follow the same procedure as above. We first + dump the cell up to the sig, and then calculate the sig */ + uint8_t cell_bytes_tmp[RELAY_PAYLOAD_SIZE] = {0}; + ed25519_signature_t sig; + + encoded_len = hs_cell_establish_intro_encode(cell_bytes_tmp, + sizeof(cell_bytes_tmp), + cell); + if (encoded_len < 0) { + log_warn(LD_OR, "Unable to pre-encode ESTABLISH_INTRO cell (2)."); + goto err; + } + + tor_assert(encoded_len > ED25519_SIG_LEN); + + if (ed25519_sign_prefixed(&sig, + cell_bytes_tmp, + encoded_len - + (ED25519_SIG_LEN + sizeof(cell->sig_len)), + ESTABLISH_INTRO_SIG_PREFIX, + &key_struct)) { + log_warn(LD_BUG, "Unable to gen signature for ESTABLISH_INTRO cell."); + goto err; + } + + /* And write the signature to the cell */ + uint8_t *sig_ptr = hs_cell_establish_intro_getarray_sig(cell); + memcpy(sig_ptr, sig.sig, sig_len); + } + + /* We are done! Return the cell! */ + return cell; + + err: + hs_cell_establish_intro_free(cell); + return NULL; +} + +#endif /* TOR_UNIT_TESTS */ + diff --git a/src/or/hs_service.h b/src/or/hs_service.h new file mode 100644 index 0000000000..994521fc50 --- /dev/null +++ b/src/or/hs_service.h @@ -0,0 +1,32 @@ +/* Copyright (c) 2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file hs_service.h + * \brief Header file for hs_service.c. + **/ + +#ifndef TOR_HS_SERVICE_H +#define TOR_HS_SERVICE_H + +#include "or.h" +#include "hs/cell_establish_intro.h" + +#ifdef HS_SERVICE_PRIVATE + +#ifdef TOR_UNIT_TESTS + +STATIC hs_cell_establish_intro_t * +generate_establish_intro_cell(const uint8_t *circuit_key_material, + size_t circuit_key_material_len); + +STATIC ssize_t +get_establish_intro_payload(uint8_t *buf, size_t buf_len, + const hs_cell_establish_intro_t *cell); + +#endif /* TOR_UNIT_TESTS */ + +#endif /* HS_SERVICE_PRIVATE */ + +#endif /* TOR_HS_SERVICE_H */ + diff --git a/src/or/include.am b/src/or/include.am index 99912a9947..4e54deca55 100644 --- a/src/or/include.am +++ b/src/or/include.am @@ -19,6 +19,7 @@ EXTRA_DIST+= src/or/ntmain.c src/or/Makefile.nmake LIBTOR_A_SOURCES = \ src/or/addressmap.c \ + src/or/bridges.c \ src/or/buffers.c \ src/or/channel.c \ src/or/channeltls.c \ @@ -45,6 +46,9 @@ LIBTOR_A_SOURCES = \ src/or/dnsserv.c \ src/or/fp_pair.c \ src/or/geoip.c \ + src/or/hs_intropoint.c \ + src/or/hs_circuitmap.c \ + src/or/hs_service.c \ src/or/entrynodes.c \ src/or/ext_orport.c \ src/or/hibernate.c \ @@ -130,6 +134,7 @@ endif ORHEADERS = \ src/or/addressmap.h \ + src/or/bridges.h \ src/or/buffers.h \ src/or/channel.h \ src/or/channeltls.h \ @@ -164,6 +169,9 @@ ORHEADERS = \ src/or/hs_cache.h \ src/or/hs_common.h \ src/or/hs_descriptor.h \ + src/or/hs_intropoint.h \ + src/or/hs_circuitmap.h \ + src/or/hs_service.h \ src/or/keypin.h \ src/or/main.h \ src/or/microdesc.h \ diff --git a/src/or/main.c b/src/or/main.c index 8239606c08..24b209ce8a 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -50,6 +50,7 @@ #include "or.h" #include "addressmap.h" #include "backtrace.h" +#include "bridges.h" #include "buffers.h" #include "channel.h" #include "channeltls.h" @@ -74,6 +75,7 @@ #include "geoip.h" #include "hibernate.h" #include "hs_cache.h" +#include "hs_circuitmap.h" #include "keypin.h" #include "main.h" #include "microdesc.h" @@ -484,7 +486,7 @@ connection_check_event(connection_t *conn, struct event *ev) */ bad = ev != NULL; } else { - /* Everytyhing else should have an underlying socket, or a linked + /* Everything else should have an underlying socket, or a linked * connection (which is also tracked with a read_event/write_event pair). */ bad = ev == NULL; @@ -978,7 +980,13 @@ directory_info_has_arrived(time_t now, int from_cache, int suppress_logs) /* if we have enough dir info, then update our guard status with * whatever we just learned. */ - entry_guards_compute_status(options, now); + int invalidate_circs = guards_update_all(); + + if (invalidate_circs) { + circuit_mark_all_unused_circs(); + circuit_mark_all_dirty_circs_as_unusable(); + } + /* Don't even bother trying to get extrainfo until the rest of our * directory info is up-to-date */ if (options->DownloadExtraInfo) @@ -1375,6 +1383,9 @@ run_scheduled_events(time_t now) /* 0c. If we've deferred log messages for the controller, handle them now */ flush_pending_log_callbacks(); + /* Maybe enough time elapsed for us to reconsider a circuit. */ + circuit_upgrade_circuits_from_guard_wait(); + if (options->UseBridges && !options->DisableNetwork) { fetch_bridge_descriptors(options, now); } @@ -1395,6 +1406,7 @@ run_scheduled_events(time_t now) /* (If our circuit build timeout can ever become lower than a second (which * it can't, currently), we should do this more often.) */ circuit_expire_building(); + circuit_expire_waiting_for_better_guard(); /* 3b. Also look at pending streams and prune the ones that 'began' * a long time ago but haven't gotten a 'connected' yet. @@ -1644,8 +1656,7 @@ static int check_expired_networkstatus_callback(time_t now, const or_options_t *options) { (void)options; - /* 1f. Check whether our networkstatus has expired. - */ + /* Check whether our networkstatus has expired. */ networkstatus_t *ns = networkstatus_get_latest_consensus(); /*XXXX RD: This value needs to be the same as REASONABLY_LIVE_TIME in * networkstatus_get_reasonably_live_consensus(), but that value is way @@ -1757,7 +1768,7 @@ clean_caches_callback(time_t now, const or_options_t *options) /** * Periodic callback: Clean the cache of failed hidden service lookups - * frequently frequently. + * frequently. */ static int rend_cache_failure_clean_callback(time_t now, const or_options_t *options) @@ -2400,6 +2411,9 @@ do_main_loop(void) } } + /* Initialize relay-side HS circuitmap */ + hs_circuitmap_init(); + /* set up once-a-second callback. */ if (! second_timer) { struct timeval one_second; @@ -3108,12 +3122,14 @@ tor_free_all(int postfork) connection_edge_free_all(); scheduler_free_all(); nodelist_free_all(); + hs_circuitmap_free_all(); microdesc_free_all(); routerparse_free_all(); ext_orport_free_all(); control_free_all(); sandbox_free_getaddrinfo_cache(); protover_free_all(); + bridges_free_all(); if (!postfork) { config_free_all(); or_state_free_all(); diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c index bfb36413ce..49ff12bd6c 100644 --- a/src/or/networkstatus.c +++ b/src/or/networkstatus.c @@ -38,6 +38,7 @@ #define NETWORKSTATUS_PRIVATE #include "or.h" +#include "bridges.h" #include "channel.h" #include "circuitmux.h" #include "circuitmux_ewma.h" @@ -1377,6 +1378,24 @@ networkstatus_get_live_consensus,(time_t now)) return NULL; } +/** Determine if <b>consensus</b> is valid or expired recently enough that + * we can still use it. + * + * Return 1 if the consensus is reasonably live, or 0 if it is too old. + */ +int +networkstatus_consensus_reasonably_live(networkstatus_t *consensus, time_t now) +{ +#define REASONABLY_LIVE_TIME (24*60*60) + if (BUG(!consensus)) + return 0; + + if (now <= consensus->valid_until + REASONABLY_LIVE_TIME) + return 1; + + return 0; +} + /* XXXX remove this in favor of get_live_consensus. But actually, * leave something like it for bridge users, who need to not totally * lose if they spend a while fetching a new consensus. */ @@ -1385,12 +1404,11 @@ networkstatus_get_live_consensus,(time_t now)) networkstatus_t * networkstatus_get_reasonably_live_consensus(time_t now, int flavor) { -#define REASONABLY_LIVE_TIME (24*60*60) networkstatus_t *consensus = networkstatus_get_latest_consensus_by_flavor(flavor); if (consensus && consensus->valid_after <= now && - now <= consensus->valid_until+REASONABLY_LIVE_TIME) + networkstatus_consensus_reasonably_live(consensus, now)) return consensus; else return NULL; @@ -2329,6 +2347,25 @@ networkstatus_get_param(const networkstatus_t *ns, const char *param_name, } /** + * As networkstatus_get_param(), but check torrc_value before checking the + * consensus. If torrc_value is in-range, then return it instead of the + * value from the consensus. + */ +int32_t +networkstatus_get_overridable_param(const networkstatus_t *ns, + int32_t torrc_value, + const char *param_name, + int32_t default_val, + int32_t min_val, int32_t max_val) +{ + if (torrc_value >= min_val && torrc_value <= max_val) + return torrc_value; + else + return networkstatus_get_param( + ns, param_name, default_val, min_val, max_val); +} + +/** * Retrieve the consensus parameter that governs the * fixed-point precision of our network balancing 'bandwidth-weights' * (which are themselves integer consensus values). We divide them diff --git a/src/or/networkstatus.h b/src/or/networkstatus.h index 454356e0bb..edf2dc7b7a 100644 --- a/src/or/networkstatus.h +++ b/src/or/networkstatus.h @@ -81,6 +81,8 @@ MOCK_DECL(networkstatus_t *,networkstatus_get_latest_consensus,(void)); MOCK_DECL(networkstatus_t *,networkstatus_get_latest_consensus_by_flavor, (consensus_flavor_t f)); MOCK_DECL(networkstatus_t *, networkstatus_get_live_consensus,(time_t now)); +int networkstatus_consensus_reasonably_live(networkstatus_t *consensus, + time_t now); networkstatus_t *networkstatus_get_reasonably_live_consensus(time_t now, int flavor); MOCK_DECL(int, networkstatus_consensus_is_bootstrapping,(time_t now)); @@ -113,6 +115,11 @@ int32_t networkstatus_get_param(const networkstatus_t *ns, const char *param_name, int32_t default_val, int32_t min_val, int32_t max_val); +int32_t networkstatus_get_overridable_param(const networkstatus_t *ns, + int32_t torrc_value, + const char *param_name, + int32_t default_val, + int32_t min_val, int32_t max_val); int getinfo_helper_networkstatus(control_connection_t *conn, const char *question, char **answer, const char **errmsg); diff --git a/src/or/nodelist.c b/src/or/nodelist.c index b29f217efd..804af297ba 100644 --- a/src/or/nodelist.c +++ b/src/or/nodelist.c @@ -1739,9 +1739,9 @@ count_usable_descriptors(int *num_present, int *num_usable, * If **<b>status_out</b> is present, allocate a new string and print the * available percentages of guard, middle, and exit nodes to it, noting * whether there are exits in the consensus. - * If there are no guards in the consensus, - * we treat the exit fraction as 100%. - */ + * If there are no exits in the consensus, we treat the exit fraction as 100%, + * but set router_have_consensus_path() so that we can only build internal + * paths. */ static double compute_frac_paths_available(const networkstatus_t *consensus, const or_options_t *options, time_t now, diff --git a/src/or/or.h b/src/or/or.h index 7e11bf05aa..600bd9dbe6 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -80,6 +80,7 @@ #include "crypto_ed25519.h" #include "tor_queue.h" #include "util_format.h" +#include "hs_circuitmap.h" /* These signals are defined to help handle_control_signal work. */ @@ -438,8 +439,12 @@ typedef enum { /** Circuit state: I'd like to deliver a create, but my n_chan is still * connecting. */ #define CIRCUIT_STATE_CHAN_WAIT 2 +/** Circuit state: the circuit is open but we don't want to actually use it + * until we find out if a better guard will be available. + */ +#define CIRCUIT_STATE_GUARD_WAIT 3 /** Circuit state: onionskin(s) processed, ready to send/receive cells. */ -#define CIRCUIT_STATE_OPEN 3 +#define CIRCUIT_STATE_OPEN 4 #define CIRCUIT_PURPOSE_MIN_ 1 @@ -1755,7 +1760,7 @@ typedef struct dir_connection_t { /** Which 'resource' did we ask the directory for? This is typically the part * of the URL string that defines, relative to the directory conn purpose, * what thing we want. For example, in router descriptor downloads by - * descriptor digest, it contains "d/", then one ore more +-separated + * descriptor digest, it contains "d/", then one or more +-separated * fingerprints. **/ char *requested_resource; @@ -1782,6 +1787,10 @@ typedef struct dir_connection_t { /** What rendezvous service are we querying for? */ rend_data_t *rend_data; + /** If this is a one-hop connection, tracks the state of the directory guard + * for this connection (if any). */ + struct circuit_guard_state_t *guard_state; + char identity_digest[DIGEST_LEN]; /**< Hash of the public RSA key for * the directory server's signing key. */ @@ -3151,6 +3160,15 @@ typedef struct origin_circuit_t { /** Holds all rendezvous data on either client or service side. */ rend_data_t *rend_data; + /** Holds the data that the entry guard system uses to track the + * status of the guard this circuit is using, and thereby to determine + * whether this circuit can be used. */ + struct circuit_guard_state_t *guard_state; + + /** Index into global_origin_circuit_list for this circuit. -1 if not + * present. */ + int global_origin_circuit_list_idx; + /** How many more relay_early cells can we send on this circuit, according * to the specification? */ unsigned int remaining_relay_early_cells : 4; @@ -3355,7 +3373,12 @@ typedef struct or_circuit_t { * is not marked for close. */ struct or_circuit_t *rend_splice; - struct or_circuit_rendinfo_s *rendinfo; + /** If set, points to an HS token that this circuit might be carrying. + * Used by the HS circuitmap. */ + hs_token_t *hs_token; + /** Hashtable node: used to look up the circuit by its HS token using the HS + circuitmap. */ + HT_ENTRY(or_circuit_t) hs_circuitmap_node; /** Stores KH for the handshake. */ char rend_circ_nonce[DIGEST_LEN];/* KH in tor-spec.txt */ @@ -3390,25 +3413,11 @@ typedef struct or_circuit_t { uint32_t max_middle_cells; } or_circuit_t; -typedef struct or_circuit_rendinfo_s { - #if REND_COOKIE_LEN != DIGEST_LEN #error "The REND_TOKEN_LEN macro assumes REND_COOKIE_LEN == DIGEST_LEN" #endif #define REND_TOKEN_LEN DIGEST_LEN - /** A hash of location-hidden service's PK if purpose is INTRO_POINT, or a - * rendezvous cookie if purpose is REND_POINT_WAITING. Filled with zeroes - * otherwise. - */ - char rend_token[REND_TOKEN_LEN]; - - /** True if this is a rendezvous point circuit; false if this is an - * introduction point. */ - unsigned is_rend_circ; - -} or_circuit_rendinfo_t; - /** Convert a circuit subtype to a circuit_t. */ #define TO_CIRCUIT(x) (&((x)->base_)) @@ -3883,7 +3892,7 @@ typedef struct { uint64_t BandwidthBurst; /**< How much bandwidth, at maximum, are we willing * to use in a second? */ uint64_t MaxAdvertisedBandwidth; /**< How much bandwidth are we willing to - * tell people we have? */ + * tell other nodes we have? */ uint64_t RelayBandwidthRate; /**< How much bandwidth, on average, are we * willing to use for all relayed conns? */ uint64_t RelayBandwidthBurst; /**< How much bandwidth, at maximum, will we @@ -3969,9 +3978,6 @@ typedef struct { * and vote for all other exits as good. */ int AuthDirMaxServersPerAddr; /**< Do not permit more than this * number of servers per IP address. */ - int AuthDirMaxServersPerAuthAddr; /**< Do not permit more than this - * number of servers per IP address shared - * with an authority. */ int AuthDirHasIPv6Connectivity; /**< Boolean: are we on IPv6? */ int AuthDirPinKeys; /**< Boolean: Do we enforce key-pinning? */ @@ -4056,8 +4062,6 @@ typedef struct { int UseEntryGuards; int NumEntryGuards; /**< How many entry guards do we try to establish? */ - int UseEntryGuardsAsDirGuards; /** Boolean: Do we try to get directory info - * from a smallish number of fixed nodes? */ /** If 1, we use any guardfraction information we see in the * consensus. If 0, we don't. If -1, let the consensus parameter @@ -4529,7 +4533,7 @@ typedef struct { * XXXX Eventually, the default will be 0. */ int ExitRelay; - /** For how long (seconds) do we declare our singning keys to be valid? */ + /** For how long (seconds) do we declare our signing keys to be valid? */ int SigningKeyLifetime; /** For how long (seconds) do we declare our link keys to be valid? */ int TestingLinkCertLifetime; @@ -4582,6 +4586,14 @@ typedef struct { * do we enforce Ed25519 identity match? */ /* NOTE: remove this option someday. */ int AuthDirTestEd25519LinkKeys; + + /** If 1, we use the old (pre-prop271) guard selection algorithm. + * + * XXXX prop271 This option is only here as a stopgap while we're + * XXXX tuning and debugging the new (post-prop271) algorithm. Eventually + * we should remove it entirely. + */ + int UseDeprecatedGuardAlgorithm; } or_options_t; /** Persistent state for an onion router, as saved to disk. */ @@ -4605,9 +4617,12 @@ typedef struct { uint64_t AccountingBytesAtSoftLimit; uint64_t AccountingExpectedUsage; - /** A list of Entry Guard-related configuration lines. */ + /** A list of Entry Guard-related configuration lines. (pre-prop271) */ config_line_t *EntryGuards; + /** A list of guard-related configuration lines. (post-prop271) */ + config_line_t *Guard; + config_line_t *TransportProxies; /** These fields hold information on the history of bandwidth usage for diff --git a/src/or/parsecommon.c b/src/or/parsecommon.c index 6622d7d671..ec2cec69f7 100644 --- a/src/or/parsecommon.c +++ b/src/or/parsecommon.c @@ -326,7 +326,7 @@ get_next_token(memarea_t *area, if (tok->tp == ERR_) { /* No keyword matched; call it an "K_opt" or "A_unrecognized" */ - if (**s == '@') + if (*s < eol && **s == '@') tok->tp = A_UNKNOWN_; else tok->tp = K_OPT; diff --git a/src/or/parsecommon.h b/src/or/parsecommon.h index 3019df63eb..15e9f7ae85 100644 --- a/src/or/parsecommon.h +++ b/src/or/parsecommon.h @@ -154,7 +154,7 @@ typedef enum { R3_DESC_LIFETIME, R3_DESC_SIGNING_CERT, R3_REVISION_COUNTER, - R3_ENCRYPTED, + R3_SUPERENCRYPTED, R3_SIGNATURE, R3_CREATE2_FORMATS, R3_AUTHENTICATION_REQUIRED, diff --git a/src/or/rendcache.c b/src/or/rendcache.c index bf43407289..12c23ea87c 100644 --- a/src/or/rendcache.c +++ b/src/or/rendcache.c @@ -908,7 +908,9 @@ rend_cache_store_v2_desc_as_client(const char *desc, if (n_intro_points <= 0) { log_warn(LD_REND, "Failed to parse introduction points. Either the " "service has published a corrupt descriptor or you have " - "provided invalid authorization data."); + "provided invalid authorization data, or (maybe!) the " + "server is deliberately serving broken data in an attempt " + "to crash you with bug 21018."); goto err; } else if (n_intro_points > MAX_INTRO_POINTS) { log_warn(LD_REND, "Found too many introduction points on a hidden " diff --git a/src/or/rendclient.c b/src/or/rendclient.c index b0dcf52507..06744ad795 100644 --- a/src/or/rendclient.c +++ b/src/or/rendclient.c @@ -762,7 +762,7 @@ directory_get_from_hs_dir(const char *desc_id, how_to_fetch, desc_id_base32, NULL, 0, 0, - rend_query); + rend_query, NULL); log_info(LD_REND, "Sending fetch request for v2 descriptor for " "service '%s' with descriptor ID '%s', auth type %d, " "and descriptor cookie '%s' to hidden service " diff --git a/src/or/rendcommon.c b/src/or/rendcommon.c index f2060e528c..def51b7986 100644 --- a/src/or/rendcommon.c +++ b/src/or/rendcommon.c @@ -16,6 +16,7 @@ #include "rendclient.h" #include "rendcommon.h" #include "rendmid.h" +#include "hs_intropoint.h" #include "rendservice.h" #include "rephist.h" #include "router.h" @@ -762,7 +763,7 @@ rend_process_relay_cell(circuit_t *circ, const crypt_path_t *layer_hint, switch (command) { case RELAY_COMMAND_ESTABLISH_INTRO: if (or_circ) - r = rend_mid_establish_intro(or_circ,payload,length); + r = hs_intro_received_establish_intro(or_circ,payload,length); break; case RELAY_COMMAND_ESTABLISH_RENDEZVOUS: if (or_circ) diff --git a/src/or/rendmid.c b/src/or/rendmid.c index f39c92afae..3319a639b9 100644 --- a/src/or/rendmid.c +++ b/src/or/rendmid.c @@ -11,16 +11,19 @@ #include "circuitlist.h" #include "circuituse.h" #include "config.h" +#include "crypto.h" #include "relay.h" #include "rendmid.h" #include "rephist.h" +#include "hs_circuitmap.h" +#include "hs_intropoint.h" /** Respond to an ESTABLISH_INTRO cell by checking the signed data and * setting the circuit's purpose and service pk digest. */ int -rend_mid_establish_intro(or_circuit_t *circ, const uint8_t *request, - size_t request_len) +rend_mid_establish_intro_legacy(or_circuit_t *circ, const uint8_t *request, + size_t request_len) { crypto_pk_t *pk = NULL; char buf[DIGEST_LEN+9]; @@ -32,15 +35,14 @@ rend_mid_establish_intro(or_circuit_t *circ, const uint8_t *request, int reason = END_CIRC_REASON_INTERNAL; log_info(LD_REND, - "Received an ESTABLISH_INTRO request on circuit %u", + "Received a legacy ESTABLISH_INTRO request on circuit %u", (unsigned) circ->p_circ_id); - if (circ->base_.purpose != CIRCUIT_PURPOSE_OR || circ->base_.n_chan) { - log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, - "Rejecting ESTABLISH_INTRO on non-OR or non-edge circuit."); + if (!hs_intro_circuit_is_suitable(circ)) { reason = END_CIRC_REASON_TORPROTOCOL; goto err; } + if (request_len < 2+DIGEST_LEN) goto truncated; /* First 2 bytes: length of asn1-encoded key. */ @@ -94,7 +96,7 @@ rend_mid_establish_intro(or_circuit_t *circ, const uint8_t *request, /* Close any other intro circuits with the same pk. */ c = NULL; - while ((c = circuit_get_intro_point((const uint8_t *)pk_digest))) { + while ((c = hs_circuitmap_get_intro_circ_v2((const uint8_t *)pk_digest))) { log_info(LD_REND, "Replacing old circuit for service %s", safe_str(serviceid)); circuit_mark_for_close(TO_CIRCUIT(c), END_CIRC_REASON_FINISHED); @@ -102,16 +104,14 @@ rend_mid_establish_intro(or_circuit_t *circ, const uint8_t *request, } /* Acknowledge the request. */ - if (relay_send_command_from_edge(0, TO_CIRCUIT(circ), - RELAY_COMMAND_INTRO_ESTABLISHED, - "", 0, NULL)<0) { + if (hs_intro_send_intro_established_cell(circ) < 0) { log_info(LD_GENERAL, "Couldn't send INTRO_ESTABLISHED cell."); goto err_no_close; } /* Now, set up this circuit. */ circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_INTRO_POINT); - circuit_set_intro_point_digest(circ, (uint8_t *)pk_digest); + hs_circuitmap_register_intro_circ_v2(circ, (uint8_t *)pk_digest); log_info(LD_REND, "Established introduction point on circuit %u for service %s", @@ -181,7 +181,7 @@ rend_mid_introduce(or_circuit_t *circ, const uint8_t *request, /* The first 20 bytes are all we look at: they have a hash of the service's * PK. */ - intro_circ = circuit_get_intro_point((const uint8_t*)request); + intro_circ = hs_circuitmap_get_intro_circ_v2((const uint8_t*)request); if (!intro_circ) { log_info(LD_REND, "No intro circ found for INTRODUCE1 cell (%s) from circuit %u; " @@ -258,7 +258,7 @@ rend_mid_establish_rendezvous(or_circuit_t *circ, const uint8_t *request, goto err; } - if (circuit_get_rendezvous(request)) { + if (hs_circuitmap_get_rend_circ(request)) { log_warn(LD_PROTOCOL, "Duplicate rendezvous cookie in ESTABLISH_RENDEZVOUS."); goto err; @@ -274,7 +274,7 @@ rend_mid_establish_rendezvous(or_circuit_t *circ, const uint8_t *request, } circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_REND_POINT_WAITING); - circuit_set_rendezvous_cookie(circ, request); + hs_circuitmap_register_rend_circ(circ, request); base16_encode(hexid,9,(char*)request,4); @@ -323,7 +323,7 @@ rend_mid_rendezvous(or_circuit_t *circ, const uint8_t *request, "Got request for rendezvous from circuit %u to cookie %s.", (unsigned)circ->p_circ_id, hexid); - rend_circ = circuit_get_rendezvous(request); + rend_circ = hs_circuitmap_get_rend_circ(request); if (!rend_circ) { log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Rejecting RENDEZVOUS1 cell with unrecognized rendezvous cookie %s.", @@ -358,7 +358,7 @@ rend_mid_rendezvous(or_circuit_t *circ, const uint8_t *request, circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_REND_ESTABLISHED); circuit_change_purpose(TO_CIRCUIT(rend_circ), CIRCUIT_PURPOSE_REND_ESTABLISHED); - circuit_set_rendezvous_cookie(circ, NULL); + hs_circuitmap_remove_circuit(circ); rend_circ->rend_splice = circ; circ->rend_splice = rend_circ; diff --git a/src/or/rendmid.h b/src/or/rendmid.h index 10d1287085..374f2b7d66 100644 --- a/src/or/rendmid.h +++ b/src/or/rendmid.h @@ -12,8 +12,8 @@ #ifndef TOR_RENDMID_H #define TOR_RENDMID_H -int rend_mid_establish_intro(or_circuit_t *circ, const uint8_t *request, - size_t request_len); +int rend_mid_establish_intro_legacy(or_circuit_t *circ, const uint8_t *request, + size_t request_len); int rend_mid_introduce(or_circuit_t *circ, const uint8_t *request, size_t request_len); int rend_mid_establish_rendezvous(or_circuit_t *circ, const uint8_t *request, diff --git a/src/or/rendservice.c b/src/or/rendservice.c index 545fba1449..bf34aee283 100644 --- a/src/or/rendservice.c +++ b/src/or/rendservice.c @@ -542,6 +542,95 @@ rend_service_check_dir_and_add(smartlist_t *service_list, return rend_add_service(s_list, service); } +/* If this is a reload and there were hidden services configured before, + * keep the introduction points that are still needed and close the + * other ones. */ +STATIC void +prune_services_on_reload(smartlist_t *old_service_list, + smartlist_t *new_service_list) +{ + origin_circuit_t *ocirc = NULL; + smartlist_t *surviving_services = NULL; + + tor_assert(old_service_list); + tor_assert(new_service_list); + + /* This contains all _existing_ services that survives the relaod that is + * that haven't been removed from the configuration. The difference between + * this list and the new service list is that the new list can possibly + * contain newly configured service that have no introduction points opened + * yet nor key material loaded or generated. */ + surviving_services = smartlist_new(); + + /* Preserve the existing ephemeral services. + * + * This is the ephemeral service equivalent of the "Copy introduction + * points to new services" block, except there's no copy required since + * the service structure isn't regenerated. + * + * After this is done, all ephemeral services will be: + * * Removed from old_service_list, so the equivalent non-ephemeral code + * will not attempt to preserve them. + * * Added to the new_service_list (that previously only had the + * services listed in the configuration). + * * Added to surviving_services, which is the list of services that + * will NOT have their intro point closed. + */ + SMARTLIST_FOREACH_BEGIN(old_service_list, rend_service_t *, old) { + if (rend_service_is_ephemeral(old)) { + SMARTLIST_DEL_CURRENT(old_service_list, old); + smartlist_add(surviving_services, old); + smartlist_add(new_service_list, old); + } + } SMARTLIST_FOREACH_END(old); + + /* Copy introduction points to new services. This is O(n^2), but it's only + * called on reconfigure, so it's ok performance wise. */ + SMARTLIST_FOREACH_BEGIN(new_service_list, rend_service_t *, new) { + SMARTLIST_FOREACH_BEGIN(old_service_list, rend_service_t *, old) { + /* Skip ephemeral services as we only want to copy introduction points + * from current services to newly configured one that already exists. + * The same directory means it's the same service. */ + if (rend_service_is_ephemeral(new) || rend_service_is_ephemeral(old) || + strcmp(old->directory, new->directory)) { + continue; + } + smartlist_add_all(new->intro_nodes, old->intro_nodes); + smartlist_clear(old->intro_nodes); + smartlist_add_all(new->expiring_nodes, old->expiring_nodes); + smartlist_clear(old->expiring_nodes); + /* This regular service will survive the closing IPs step after. */ + smartlist_add(surviving_services, old); + break; + } SMARTLIST_FOREACH_END(old); + } SMARTLIST_FOREACH_END(new); + + /* For every service introduction circuit we can find, see if we have a + * matching surviving configured service. If not, close the circuit. */ + while ((ocirc = circuit_get_next_service_intro_circ(ocirc))) { + int keep_it = 0; + tor_assert(ocirc->rend_data); + SMARTLIST_FOREACH_BEGIN(surviving_services, const rend_service_t *, s) { + if (rend_circuit_pk_digest_eq(ocirc, (uint8_t *) s->pk_digest)) { + /* Keep this circuit as we have a matching configured service. */ + keep_it = 1; + break; + } + } SMARTLIST_FOREACH_END(s); + if (keep_it) { + continue; + } + log_info(LD_REND, "Closing intro point %s for service %s.", + safe_str_client(extend_info_describe( + ocirc->build_state->chosen_exit)), + safe_str_client(rend_data_get_address(ocirc->rend_data))); + /* Reason is FINISHED because service has been removed and thus the + * circuit is considered old/uneeded. */ + circuit_mark_for_close(TO_CIRCUIT(ocirc), END_CIRC_REASON_FINISHED); + } + smartlist_free(surviving_services); +} + /** Set up rend_service_list, based on the values of HiddenServiceDir and * HiddenServicePort in <b>options</b>. Return 0 on success and -1 on * failure. (If <b>validate_only</b> is set, parse, warn and return as @@ -556,6 +645,7 @@ rend_config_services(const or_options_t *options, int validate_only) smartlist_t *old_service_list = NULL; smartlist_t *temp_service_list = NULL; int ok = 0; + int rv = -1; /* Use a temporary service list, so that we can check the new services' * consistency with each other */ @@ -568,7 +658,8 @@ rend_config_services(const or_options_t *options, int validate_only) * which is registered below the loop */ if (rend_service_check_dir_and_add(temp_service_list, options, service, validate_only) < 0) { - return -1; + service = NULL; + goto free_and_return; } service = tor_malloc_zero(sizeof(rend_service_t)); service->directory = tor_strdup(line->value); @@ -580,8 +671,7 @@ rend_config_services(const or_options_t *options, int validate_only) if (!service) { log_warn(LD_CONFIG, "%s with no preceding HiddenServiceDir directive", line->key); - rend_service_free(service); - return -1; + goto free_and_return; } if (!strcasecmp(line->key, "HiddenServicePort")) { char *err_msg = NULL; @@ -590,8 +680,7 @@ rend_config_services(const or_options_t *options, int validate_only) if (err_msg) log_warn(LD_CONFIG, "%s", err_msg); tor_free(err_msg); - rend_service_free(service); - return -1; + goto free_and_return; } tor_assert(!err_msg); smartlist_add(service->ports, portcfg); @@ -602,8 +691,8 @@ rend_config_services(const or_options_t *options, int validate_only) log_warn(LD_CONFIG, "HiddenServiceAllowUnknownPorts should be 0 or 1, not %s", line->value); - rend_service_free(service); - return -1; + smartlist_free(temp_service_list); + goto free_and_return; } log_info(LD_CONFIG, "HiddenServiceAllowUnknownPorts=%d for %s", @@ -617,8 +706,7 @@ rend_config_services(const or_options_t *options, int validate_only) log_warn(LD_CONFIG, "HiddenServiceDirGroupReadable should be 0 or 1, not %s", line->value); - rend_service_free(service); - return -1; + goto free_and_return; } log_info(LD_CONFIG, "HiddenServiceDirGroupReadable=%d for %s", @@ -631,8 +719,7 @@ rend_config_services(const or_options_t *options, int validate_only) log_warn(LD_CONFIG, "HiddenServiceMaxStreams should be between 0 and %d, not %s", 65535, line->value); - rend_service_free(service); - return -1; + goto free_and_return; } log_info(LD_CONFIG, "HiddenServiceMaxStreams=%d for %s", @@ -646,8 +733,7 @@ rend_config_services(const or_options_t *options, int validate_only) "HiddenServiceMaxStreamsCloseCircuit should be 0 or 1, " "not %s", line->value); - rend_service_free(service); - return -1; + goto free_and_return; } log_info(LD_CONFIG, "HiddenServiceMaxStreamsCloseCircuit=%d for %s", @@ -664,8 +750,7 @@ rend_config_services(const or_options_t *options, int validate_only) "should be between %d and %d, not %s", NUM_INTRO_POINTS_DEFAULT, NUM_INTRO_POINTS_MAX, line->value); - rend_service_free(service); - return -1; + goto free_and_return; } log_info(LD_CONFIG, "HiddenServiceNumIntroductionPoints=%d for %s", service->n_intro_points_wanted, @@ -680,8 +765,7 @@ rend_config_services(const or_options_t *options, int validate_only) if (service->auth_type != REND_NO_AUTH) { log_warn(LD_CONFIG, "Got multiple HiddenServiceAuthorizeClient " "lines for a single service."); - rend_service_free(service); - return -1; + goto free_and_return; } type_names_split = smartlist_new(); smartlist_split_string(type_names_split, line->value, " ", 0, 2); @@ -689,9 +773,7 @@ rend_config_services(const or_options_t *options, int validate_only) log_warn(LD_BUG, "HiddenServiceAuthorizeClient has no value. This " "should have been prevented when parsing the " "configuration."); - smartlist_free(type_names_split); - rend_service_free(service); - return -1; + goto free_and_return; } authname = smartlist_get(type_names_split, 0); if (!strcasecmp(authname, "basic")) { @@ -705,8 +787,7 @@ rend_config_services(const or_options_t *options, int validate_only) (char *) smartlist_get(type_names_split, 0)); SMARTLIST_FOREACH(type_names_split, char *, cp, tor_free(cp)); smartlist_free(type_names_split); - rend_service_free(service); - return -1; + goto free_and_return; } service->clients = smartlist_new(); if (smartlist_len(type_names_split) < 2) { @@ -743,8 +824,7 @@ rend_config_services(const or_options_t *options, int validate_only) client_name, REND_CLIENTNAME_MAX_LEN); SMARTLIST_FOREACH(clients, char *, cp, tor_free(cp)); smartlist_free(clients); - rend_service_free(service); - return -1; + goto free_and_return; } client = tor_malloc_zero(sizeof(rend_authorized_client_t)); client->client_name = tor_strdup(client_name); @@ -766,16 +846,14 @@ rend_config_services(const or_options_t *options, int validate_only) smartlist_len(service->clients), service->auth_type == REND_BASIC_AUTH ? 512 : 16, service->auth_type == REND_BASIC_AUTH ? "basic" : "stealth"); - rend_service_free(service); - return -1; + goto free_and_return; } } else { tor_assert(!strcasecmp(line->key, "HiddenServiceVersion")); if (strcmp(line->value, "2")) { log_warn(LD_CONFIG, "The only supported HiddenServiceVersion is 2."); - rend_service_free(service); - return -1; + goto free_and_return; } } } @@ -784,16 +862,15 @@ rend_config_services(const or_options_t *options, int validate_only) * within the loop. It is ok for this service to be NULL, it is ignored. */ if (rend_service_check_dir_and_add(temp_service_list, options, service, validate_only) < 0) { - return -1; + service = NULL; + goto free_and_return; } + service = NULL; /* Free the newly added services if validating */ if (validate_only) { - SMARTLIST_FOREACH(temp_service_list, rend_service_t *, ptr, - rend_service_free(ptr)); - smartlist_free(temp_service_list); - temp_service_list = NULL; - return 0; + rv = 0; + goto free_and_return; } /* Otherwise, use the newly added services as the new service list @@ -807,88 +884,21 @@ rend_config_services(const or_options_t *options, int validate_only) * keep the introduction points that are still needed and close the * other ones. */ if (old_service_list && !validate_only) { - smartlist_t *surviving_services = smartlist_new(); - - /* Preserve the existing ephemeral services. - * - * This is the ephemeral service equivalent of the "Copy introduction - * points to new services" block, except there's no copy required since - * the service structure isn't regenerated. - * - * After this is done, all ephemeral services will be: - * * Removed from old_service_list, so the equivalent non-ephemeral code - * will not attempt to preserve them. - * * Added to the new rend_service_list (that previously only had the - * services listed in the configuration). - * * Added to surviving_services, which is the list of services that - * will NOT have their intro point closed. - */ - SMARTLIST_FOREACH(old_service_list, rend_service_t *, old, { - if (rend_service_is_ephemeral(old)) { - SMARTLIST_DEL_CURRENT(old_service_list, old); - smartlist_add(surviving_services, old); - smartlist_add(rend_service_list, old); - } - }); - - /* Copy introduction points to new services. */ - /* XXXX This is O(n^2), but it's only called on reconfigure, so it's - * probably ok? */ - SMARTLIST_FOREACH_BEGIN(rend_service_list, rend_service_t *, new) { - SMARTLIST_FOREACH_BEGIN(old_service_list, rend_service_t *, old) { - if (BUG(rend_service_is_ephemeral(new)) || - BUG(rend_service_is_ephemeral(old))) { - continue; - } - if (BUG(!new->directory) || BUG(!old->directory) || - strcmp(old->directory, new->directory)) { - continue; - } - smartlist_add_all(new->intro_nodes, old->intro_nodes); - smartlist_clear(old->intro_nodes); - smartlist_add_all(new->expiring_nodes, old->expiring_nodes); - smartlist_clear(old->expiring_nodes); - smartlist_add(surviving_services, old); - break; - } SMARTLIST_FOREACH_END(old); - } SMARTLIST_FOREACH_END(new); - - /* Close introduction circuits of services we don't serve anymore. */ - /* XXXX it would be nicer if we had a nicer abstraction to use here, - * so we could just iterate over the list of services to close, but - * once again, this isn't critical-path code. */ - SMARTLIST_FOREACH_BEGIN(circuit_get_global_list(), circuit_t *, circ) { - if (!circ->marked_for_close && - circ->state == CIRCUIT_STATE_OPEN && - (circ->purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO || - circ->purpose == CIRCUIT_PURPOSE_S_INTRO)) { - origin_circuit_t *oc = TO_ORIGIN_CIRCUIT(circ); - int keep_it = 0; - tor_assert(oc->rend_data); - SMARTLIST_FOREACH(surviving_services, rend_service_t *, ptr, { - if (rend_circuit_pk_digest_eq(oc, (uint8_t *) ptr->pk_digest)) { - keep_it = 1; - break; - } - }); - if (keep_it) - continue; - log_info(LD_REND, "Closing intro point %s for service %s.", - safe_str_client(extend_info_describe( - oc->build_state->chosen_exit)), - rend_data_get_address(oc->rend_data)); - circuit_mark_for_close(circ, END_CIRC_REASON_FINISHED); - /* XXXX Is there another reason we should use here? */ - } - } - SMARTLIST_FOREACH_END(circ); - smartlist_free(surviving_services); - SMARTLIST_FOREACH(old_service_list, rend_service_t *, ptr, - rend_service_free(ptr)); + prune_services_on_reload(old_service_list, rend_service_list); + /* Every remaining service in the old list have been removed from the + * configuration so clean them up safely. */ + SMARTLIST_FOREACH(old_service_list, rend_service_t *, s, + rend_service_free(s)); smartlist_free(old_service_list); } return 0; + free_and_return: + rend_service_free(service); + SMARTLIST_FOREACH(temp_service_list, rend_service_t *, ptr, + rend_service_free(ptr)); + smartlist_free(temp_service_list); + return rv; } /** Add the ephemeral service <b>pk</b>/<b>ports</b> if possible, using @@ -3160,6 +3170,57 @@ count_intro_point_circuits(const rend_service_t *service) return num_ipos; } +/* Given a buffer of at least RELAY_PAYLOAD_SIZE bytes in <b>cell_body_out</b>, + write the body of a legacy ESTABLISH_INTRO cell in it. Use <b>intro_key</b> + as the intro point auth key, and <b>rend_circ_nonce</b> as the circuit + crypto material. On success, fill <b>cell_body_out</b> and return the number + of bytes written. On fail, return -1. + */ +STATIC ssize_t +encode_establish_intro_cell_legacy(char *cell_body_out, crypto_pk_t *intro_key, + char *rend_circ_nonce) +{ + int retval = -1; + int r; + int len = 0; + char auth[DIGEST_LEN + 9]; + + tor_assert(intro_key); + tor_assert(rend_circ_nonce); + + /* Build the payload for a RELAY_ESTABLISH_INTRO cell. */ + r = crypto_pk_asn1_encode(intro_key, cell_body_out+2, + RELAY_PAYLOAD_SIZE-2); + if (r < 0) { + log_warn(LD_BUG, "Internal error; failed to establish intro point."); + goto err; + } + len = r; + set_uint16(cell_body_out, htons((uint16_t)len)); + len += 2; + memcpy(auth, rend_circ_nonce, DIGEST_LEN); + memcpy(auth+DIGEST_LEN, "INTRODUCE", 9); + if (crypto_digest(cell_body_out+len, auth, DIGEST_LEN+9)) + goto err; + len += 20; + note_crypto_pk_op(REND_SERVER); + r = crypto_pk_private_sign_digest(intro_key, cell_body_out+len, + sizeof(cell_body_out)-len, + cell_body_out, len); + if (r<0) { + log_warn(LD_BUG, "Internal error: couldn't sign introduction request."); + goto err; + } + len += r; + + retval = len; + + err: + memwipe(auth, 0, sizeof(auth)); + + return retval; +} + /** Called when we're done building a circuit to an introduction point: * sends a RELAY_ESTABLISH_INTRO cell. */ @@ -3167,10 +3228,7 @@ void rend_service_intro_has_opened(origin_circuit_t *circuit) { rend_service_t *service; - size_t len; - int r; char buf[RELAY_PAYLOAD_SIZE]; - char auth[DIGEST_LEN + 9]; char serviceid[REND_SERVICE_ID_LEN_BASE32+1]; int reason = END_CIRC_REASON_TORPROTOCOL; const char *rend_pk_digest; @@ -3245,41 +3303,24 @@ rend_service_intro_has_opened(origin_circuit_t *circuit) (unsigned)circuit->base_.n_circ_id, serviceid); circuit_log_path(LOG_INFO, LD_REND, circuit); - /* Use the intro key instead of the service key in ESTABLISH_INTRO. */ - crypto_pk_t *intro_key = circuit->intro_key; - /* Build the payload for a RELAY_ESTABLISH_INTRO cell. */ - r = crypto_pk_asn1_encode(intro_key, buf+2, - RELAY_PAYLOAD_SIZE-2); - if (r < 0) { - log_warn(LD_BUG, "Internal error; failed to establish intro point."); - reason = END_CIRC_REASON_INTERNAL; - goto err; - } - len = r; - set_uint16(buf, htons((uint16_t)len)); - len += 2; - memcpy(auth, circuit->cpath->prev->rend_circ_nonce, DIGEST_LEN); - memcpy(auth+DIGEST_LEN, "INTRODUCE", 9); - if (crypto_digest(buf+len, auth, DIGEST_LEN+9) < 0) - goto err; - len += 20; - note_crypto_pk_op(REND_SERVER); - r = crypto_pk_private_sign_digest(intro_key, buf+len, sizeof(buf)-len, - buf, len); - if (r<0) { - log_warn(LD_BUG, "Internal error: couldn't sign introduction request."); - reason = END_CIRC_REASON_INTERNAL; - goto err; - } - len += r; + /* Send the ESTABLISH_INTRO cell */ + { + ssize_t len; + len = encode_establish_intro_cell_legacy(buf, circuit->intro_key, + circuit->cpath->prev->rend_circ_nonce); + if (len < 0) { + reason = END_CIRC_REASON_INTERNAL; + goto err; + } - if (relay_send_command_from_edge(0, TO_CIRCUIT(circuit), - RELAY_COMMAND_ESTABLISH_INTRO, - buf, len, circuit->cpath->prev)<0) { - log_info(LD_GENERAL, + if (relay_send_command_from_edge(0, TO_CIRCUIT(circuit), + RELAY_COMMAND_ESTABLISH_INTRO, + buf, len, circuit->cpath->prev)<0) { + log_info(LD_GENERAL, "Couldn't send introduction request for service %s on circuit %u", serviceid, (unsigned)circuit->base_.n_circ_id); - goto done; + goto done; + } } /* We've attempted to use this circuit */ @@ -3291,7 +3332,6 @@ rend_service_intro_has_opened(origin_circuit_t *circuit) circuit_mark_for_close(TO_CIRCUIT(circuit), reason); done: memwipe(buf, 0, sizeof(buf)); - memwipe(auth, 0, sizeof(auth)); memwipe(serviceid, 0, sizeof(serviceid)); return; @@ -3628,7 +3668,7 @@ directory_post_to_hs_dir(rend_service_descriptor_t *renddesc, DIRIND_ANONYMOUS, NULL, desc->desc_str, strlen(desc->desc_str), - 0, rend_data); + 0, rend_data, NULL); rend_data_free(rend_data); base32_encode(desc_id_base32, sizeof(desc_id_base32), desc->desc_id, DIGEST_LEN); diff --git a/src/or/rendservice.h b/src/or/rendservice.h index 3b185672f6..3bfac0bece 100644 --- a/src/or/rendservice.h +++ b/src/or/rendservice.h @@ -129,6 +129,12 @@ STATIC int rend_service_verify_single_onion_poison( STATIC int rend_service_poison_new_single_onion_dir( const rend_service_t *s, const or_options_t* options); +STATIC ssize_t encode_establish_intro_cell_legacy(char *cell_body_out, + crypto_pk_t *intro_key, + char *rend_circ_nonce); +STATIC void prune_services_on_reload(smartlist_t *old_service_list, + smartlist_t *new_service_list); + #endif int num_rend_services(void); diff --git a/src/or/router.c b/src/or/router.c index 917caaa1f5..2d8208aa04 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -1178,9 +1178,9 @@ router_should_be_directory_server(const or_options_t *options, int dir_port) if (accounting_is_enabled(options) && get_options()->AccountingRule != ACCT_IN) { /* Don't spend bytes for directory traffic if we could end up hibernating, - * but allow DirPort otherwise. Some people set AccountingMax because - * they're confused or to get statistics. Directory traffic has a much - * larger effect on output than input so there is no reason to turn it + * but allow DirPort otherwise. Some relay operators set AccountingMax + * because they're confused or to get statistics. Directory traffic has a + * much larger effect on output than input so there is no reason to turn it * off if using AccountingRule in. */ int interval_length = accounting_get_interval_length(); uint32_t effective_bw = get_effective_bwrate(options); diff --git a/src/or/routerlist.c b/src/or/routerlist.c index 043419562c..e6ccdddbef 100644 --- a/src/or/routerlist.c +++ b/src/or/routerlist.c @@ -93,6 +93,7 @@ #define ROUTERLIST_PRIVATE #include "or.h" #include "backtrace.h" +#include "bridges.h" #include "crypto_ed25519.h" #include "circuitstats.h" #include "config.h" @@ -970,7 +971,7 @@ authority_certs_fetch_resource_impl(const char *resource, directory_initiate_command_routerstatus(rs, DIR_PURPOSE_FETCH_CERTIFICATE, 0, indirection, resource, NULL, - 0, 0); + 0, 0, NULL); return; } @@ -2003,6 +2004,10 @@ router_pick_directory_server_impl(dirinfo_type_t type, int flags, int try_excluding = 1, n_excluded = 0, n_busy = 0; int try_ip_pref = 1; +#ifndef ENABLE_LEGACY_GUARD_ALGORITHM + tor_assert_nonfatal(! for_guard); +#endif + if (!consensus) return NULL; @@ -2038,10 +2043,12 @@ router_pick_directory_server_impl(dirinfo_type_t type, int flags, if ((type & EXTRAINFO_DIRINFO) && !router_supports_extrainfo(node->identity, is_trusted_extrainfo)) continue; +#ifdef ENABLE_LEGACY_GUARD_ALGORITHM /* Don't make the same node a guard twice */ if (for_guard && is_node_used_as_guard(node)) { continue; } +#endif /* Ensure that a directory guard is actually a guard node. */ if (for_guard && !node->is_possible_guard) { continue; @@ -2991,20 +2998,6 @@ router_digest_is_trusted_dir_type(const char *digest, dirinfo_type_t type) return 0; } -/** Return true iff <b>addr</b> is the address of one of our trusted - * directory authorities. */ -int -router_addr_is_trusted_dir(uint32_t addr) -{ - if (!trusted_dir_servers) - return 0; - SMARTLIST_FOREACH(trusted_dir_servers, dir_server_t *, ent, - if (ent->addr == addr) - return 1; - ); - return 0; -} - /** If hexdigest is correctly formed, base16_decode it into * digest, which must have DIGEST_LEN space in it. * Return 0 on success, -1 on failure. @@ -4496,7 +4489,7 @@ router_load_extrainfo_from_string(const char *s, const char *eos, ei->cache_info.identity_digest, DIGEST_LEN); smartlist_string_remove(requested_fingerprints, fp); - /* We silently let people stuff us with extrainfos we didn't ask for, + /* We silently let relays stuff us with extrainfos we didn't ask for, * so long as we would have wanted them anyway. Since we always fetch * all the extrainfos we want, and we never actually act on them * inside Tor, this should be harmless. */ @@ -4592,7 +4585,7 @@ router_exit_policy_rejects_all(const routerinfo_t *router) return router->policy_is_reject_star; } -/** Create an directory server at <b>address</b>:<b>port</b>, with OR identity +/** Create a directory server at <b>address</b>:<b>port</b>, with OR identity * key <b>digest</b> which has DIGEST_LEN bytes. If <b>address</b> is NULL, * add ourself. If <b>is_authority</b>, this is a directory authority. Return * the new directory server entry on success or NULL on failure. */ @@ -4960,7 +4953,7 @@ MOCK_IMPL(STATIC void, initiate_descriptor_downloads, directory_initiate_command_routerstatus(source, purpose, ROUTER_PURPOSE_GENERAL, DIRIND_ONEHOP, - resource, NULL, 0, 0); + resource, NULL, 0, 0, NULL); } else { directory_get_from_dirserver(purpose, ROUTER_PURPOSE_GENERAL, resource, pds_flags, DL_WANT_ANY_DIRSERVER); diff --git a/src/or/routerlist.h b/src/or/routerlist.h index 606e9085ce..8b68d69f28 100644 --- a/src/or/routerlist.h +++ b/src/or/routerlist.h @@ -86,7 +86,6 @@ int router_digest_is_trusted_dir_type(const char *digest, #define router_digest_is_trusted_dir(d) \ router_digest_is_trusted_dir_type((d), NO_DIRINFO) -int router_addr_is_trusted_dir(uint32_t addr); int hexdigest_to_digest(const char *hexdigest, char *digest); const routerinfo_t *router_get_by_id_digest(const char *digest); routerinfo_t *router_get_mutable_by_digest(const char *digest); diff --git a/src/or/routerparse.c b/src/or/routerparse.c index 38ceb942a9..d763a63d84 100644 --- a/src/or/routerparse.c +++ b/src/or/routerparse.c @@ -370,8 +370,7 @@ static int router_get_hashes_impl(const char *s, size_t s_len, char end_char); static smartlist_t *find_all_exitpolicy(smartlist_t *s); -#define CST_CHECK_AUTHORITY (1<<0) -#define CST_NO_CHECK_OBJTYPE (1<<1) +#define CST_NO_CHECK_OBJTYPE (1<<0) static int check_signature_token(const char *digest, ssize_t digest_len, directory_token_t *tok, @@ -1173,28 +1172,9 @@ tor_version_is_obsolete(const char *myversion, const char *versionlist) return ret; } -/** Return true iff <b>key</b> is allowed to sign directories. - */ -static int -dir_signing_key_is_trusted(crypto_pk_t *key) -{ - char digest[DIGEST_LEN]; - if (!key) return 0; - if (crypto_pk_get_digest(key, digest) < 0) { - log_warn(LD_DIR, "Error computing dir-signing-key digest"); - return 0; - } - if (!router_digest_is_trusted_dir(digest)) { - log_warn(LD_DIR, "Listed dir-signing-key is not trusted"); - return 0; - } - return 1; -} - /** Check whether the object body of the token in <b>tok</b> has a good - * signature for <b>digest</b> using key <b>pkey</b>. If - * <b>CST_CHECK_AUTHORITY</b> is set, make sure that <b>pkey</b> is the key of - * a directory authority. If <b>CST_NO_CHECK_OBJTYPE</b> is set, do not check + * signature for <b>digest</b> using key <b>pkey</b>. + * If <b>CST_NO_CHECK_OBJTYPE</b> is set, do not check * the object type of the signature object. Use <b>doctype</b> as the type of * the document when generating log messages. Return 0 on success, negative * on failure. @@ -1209,7 +1189,6 @@ check_signature_token(const char *digest, { char *signed_digest; size_t keysize; - const int check_authority = (flags & CST_CHECK_AUTHORITY); const int check_objtype = ! (flags & CST_NO_CHECK_OBJTYPE); tor_assert(pkey); @@ -1217,12 +1196,6 @@ check_signature_token(const char *digest, tor_assert(digest); tor_assert(doctype); - if (check_authority && !dir_signing_key_is_trusted(pkey)) { - log_warn(LD_DIR, "Key on %s did not come from an authority; rejecting", - doctype); - return -1; - } - if (check_objtype) { if (strcmp(tok->object_type, "SIGNATURE")) { log_warn(LD_DIR, "Bad object type on %s signature", doctype); @@ -5150,7 +5123,8 @@ rend_parse_v2_service_descriptor(rend_service_descriptor_t **parsed_out, * descriptor. */ tok = find_by_keyword(tokens, R_PUBLICATION_TIME); tor_assert(tok->n_args == 1); - if (parse_iso_time_(tok->args[0], &result->timestamp, strict_time_fmt) < 0) { + if (parse_iso_time_(tok->args[0], &result->timestamp, + strict_time_fmt, 0) < 0) { log_warn(LD_REND, "Invalid publication time: '%s'", tok->args[0]); goto err; } @@ -5276,7 +5250,7 @@ rend_decrypt_introduction_points(char **ipos_decrypted, crypto_cipher_free(cipher); len = ipos_encrypted_size - 2 - client_entries_len - CIPHER_IV_LEN; - dec = tor_malloc(len); + dec = tor_malloc_zero(len + 1); declen = crypto_cipher_decrypt_with_iv(session_key, dec, len, ipos_encrypted + 2 + client_entries_len, ipos_encrypted_size - 2 - client_entries_len); @@ -5308,7 +5282,7 @@ rend_decrypt_introduction_points(char **ipos_decrypted, "small."); return -1; } - dec = tor_malloc_zero(ipos_encrypted_size - CIPHER_IV_LEN - 1); + dec = tor_malloc_zero(ipos_encrypted_size - CIPHER_IV_LEN - 1 + 1); declen = crypto_cipher_decrypt_with_iv(descriptor_cookie, dec, ipos_encrypted_size - diff --git a/src/or/routerset.c b/src/or/routerset.c index 4182dbc5c4..d0df0a74e6 100644 --- a/src/or/routerset.c +++ b/src/or/routerset.c @@ -28,6 +28,7 @@ #define ROUTERSET_PRIVATE #include "or.h" +#include "bridges.h" #include "geoip.h" #include "nodelist.h" #include "policies.h" @@ -334,6 +335,18 @@ routerset_contains_node(const routerset_t *set, const node_t *node) return 0; } +/** Return true iff <b>routerset</b> contains the bridge <b>bridge</b>. */ +int +routerset_contains_bridge(const routerset_t *set, const bridge_info_t *bridge) +{ + const char *id = (const char*)bridge_get_rsa_id_digest(bridge); + const tor_addr_port_t *addrport = bridge_get_addr_port(bridge); + + tor_assert(addrport); + return routerset_contains(set, &addrport->addr, addrport->port, + NULL, id, -1); +} + /** Add every known node_t that is a member of <b>routerset</b> to * <b>out</b>, but never add any that are part of <b>excludeset</b>. * If <b>running_only</b>, only add the running ones. */ diff --git a/src/or/routerset.h b/src/or/routerset.h index c2f7205c3e..2e3b4b0fe0 100644 --- a/src/or/routerset.h +++ b/src/or/routerset.h @@ -26,8 +26,11 @@ int routerset_contains_routerstatus(const routerset_t *set, country_t country); int routerset_contains_extendinfo(const routerset_t *set, const extend_info_t *ei); - +struct bridge_info_t; +int routerset_contains_bridge(const routerset_t *set, + const struct bridge_info_t *bridge); int routerset_contains_node(const routerset_t *set, const node_t *node); + void routerset_get_all_nodes(smartlist_t *out, const routerset_t *routerset, const routerset_t *excludeset, int running_only); diff --git a/src/or/shared_random.c b/src/or/shared_random.c index 0eb93382ca..f798a51a9f 100644 --- a/src/or/shared_random.c +++ b/src/or/shared_random.c @@ -502,6 +502,20 @@ get_vote_line_from_commit(const sr_commit_t *commit, sr_phase_t phase) return vote_line; } +/* Convert a given srv object to a string for the control port. This doesn't + * fail and the srv object MUST be valid. */ +static char * +srv_to_control_string(const sr_srv_t *srv) +{ + char *srv_str; + char srv_hash_encoded[SR_SRV_VALUE_BASE64_LEN + 1]; + tor_assert(srv); + + sr_srv_encode(srv_hash_encoded, sizeof(srv_hash_encoded), srv); + tor_asprintf(&srv_str, "%s", srv_hash_encoded); + return srv_str; +} + /* Return a heap allocated string that contains the given <b>srv</b> string * representation formatted for a networkstatus document using the * <b>key</b> as the start of the line. This doesn't return NULL. */ @@ -1348,6 +1362,38 @@ sr_save_and_cleanup(void) sr_cleanup(); } +/* Return the current SRV string representation for the control port. Return a + * newly allocated string on success containing the value else "" if not found + * or if we don't have a valid consensus yet. */ +char * +sr_get_current_for_control(void) +{ + char *srv_str; + const networkstatus_t *c = networkstatus_get_latest_consensus(); + if (c && c->sr_info.current_srv) { + srv_str = srv_to_control_string(c->sr_info.current_srv); + } else { + srv_str = tor_strdup(""); + } + return srv_str; +} + +/* Return the previous SRV string representation for the control port. Return + * a newly allocated string on success containing the value else "" if not + * found or if we don't have a valid consensus yet. */ +char * +sr_get_previous_for_control(void) +{ + char *srv_str; + const networkstatus_t *c = networkstatus_get_latest_consensus(); + if (c && c->sr_info.previous_srv) { + srv_str = srv_to_control_string(c->sr_info.previous_srv); + } else { + srv_str = tor_strdup(""); + } + return srv_str; +} + #ifdef TOR_UNIT_TESTS /* Set the global value of number of SRV agreements so the test can play diff --git a/src/or/shared_random.h b/src/or/shared_random.h index 9885934cc7..dbb8effeaa 100644 --- a/src/or/shared_random.h +++ b/src/or/shared_random.h @@ -129,6 +129,10 @@ const char *sr_commit_get_rsa_fpr(const sr_commit_t *commit) void sr_compute_srv(void); sr_commit_t *sr_generate_our_commit(time_t timestamp, const authority_cert_t *my_rsa_cert); + +char *sr_get_current_for_control(void); +char *sr_get_previous_for_control(void); + #ifdef SHARED_RANDOM_PRIVATE /* Encode */ diff --git a/src/or/statefile.c b/src/or/statefile.c index 8fa4324b25..a95ba8533c 100644 --- a/src/or/statefile.c +++ b/src/or/statefile.c @@ -102,6 +102,8 @@ static config_var_t state_vars_[] = { V(BWHistoryDirWriteValues, CSV, ""), V(BWHistoryDirWriteMaxima, CSV, ""), + V(Guard, LINELIST, NULL), + V(TorVersion, STRING, NULL), V(LastRotatedOnionKey, ISOTIME, NULL), diff --git a/src/or/transports.c b/src/or/transports.c index f755882c16..feeff6e66c 100644 --- a/src/or/transports.c +++ b/src/or/transports.c @@ -91,13 +91,13 @@ #define PT_PRIVATE #include "or.h" +#include "bridges.h" #include "config.h" #include "circuitbuild.h" #include "transports.h" #include "util.h" #include "router.h" #include "statefile.h" -#include "entrynodes.h" #include "connection_or.h" #include "ext_orport.h" #include "control.h" |