diff options
Diffstat (limited to 'src/feature/client')
-rw-r--r-- | src/feature/client/.may_include | 1 | ||||
-rw-r--r-- | src/feature/client/addressmap.c | 13 | ||||
-rw-r--r-- | src/feature/client/addressmap.h | 8 | ||||
-rw-r--r-- | src/feature/client/bridges.c | 164 | ||||
-rw-r--r-- | src/feature/client/bridges.h | 4 | ||||
-rw-r--r-- | src/feature/client/circpathbias.c | 46 | ||||
-rw-r--r-- | src/feature/client/circpathbias.h | 2 | ||||
-rw-r--r-- | src/feature/client/dnsserv.c | 10 | ||||
-rw-r--r-- | src/feature/client/dnsserv.h | 2 | ||||
-rw-r--r-- | src/feature/client/entrynodes.c | 312 | ||||
-rw-r--r-- | src/feature/client/entrynodes.h | 33 | ||||
-rw-r--r-- | src/feature/client/feature_client.md | 5 | ||||
-rw-r--r-- | src/feature/client/include.am | 20 | ||||
-rw-r--r-- | src/feature/client/proxymode.c | 32 | ||||
-rw-r--r-- | src/feature/client/proxymode.h | 17 | ||||
-rw-r--r-- | src/feature/client/transports.c | 507 | ||||
-rw-r--r-- | src/feature/client/transports.h | 23 |
17 files changed, 866 insertions, 333 deletions
diff --git a/src/feature/client/.may_include b/src/feature/client/.may_include new file mode 100644 index 0000000000..424c745c12 --- /dev/null +++ b/src/feature/client/.may_include @@ -0,0 +1 @@ +*.h diff --git a/src/feature/client/addressmap.c b/src/feature/client/addressmap.c index bbe786a6a2..e5bf2cc49c 100644 --- a/src/feature/client/addressmap.c +++ b/src/feature/client/addressmap.c @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -22,8 +22,7 @@ #include "core/or/circuituse.h" #include "app/config/config.h" #include "core/or/connection_edge.h" -#include "feature/control/control.h" -#include "feature/relay/dns.h" +#include "feature/control/control_events.h" #include "feature/nodelist/nodelist.h" #include "feature/nodelist/routerset.h" @@ -423,7 +422,7 @@ addressmap_rewrite(char *address, size_t maxlen, goto done; } - /* Check wither the flags we were passed tell us not to use this + /* Check whether the flags we were passed tell us not to use this * mapping. */ switch (ent->source) { case ADDRMAPSRC_DNS: @@ -516,7 +515,7 @@ addressmap_rewrite_reverse(char *address, size_t maxlen, unsigned flags, else if (f == AF_INET6 && !(flags & AMR_FLAG_USE_IPV6_DNS)) return 0; /* FFFF we should reverse-map virtual addresses even if we haven't - * enabled DNS cacheing. */ + * enabled DNS caching. */ } tor_asprintf(&s, "REVERSE[%s]", address); @@ -689,7 +688,7 @@ client_dns_set_addressmap_impl(entry_connection_t *for_conn, if (ttl<0) ttl = DEFAULT_DNS_TTL; else - ttl = dns_clip_ttl(ttl); + ttl = clip_dns_ttl(ttl); if (exitname) { /* XXXX fails to ever get attempts to get an exit address of @@ -903,7 +902,7 @@ get_random_virtual_addr(const virtual_addr_conf_t *conf, tor_addr_t *addr_out) } if (ipv6) - tor_addr_from_ipv6_bytes(addr_out, (char*) bytes); + tor_addr_from_ipv6_bytes(addr_out, bytes); else tor_addr_from_ipv4n(addr_out, get_uint32(bytes)); diff --git a/src/feature/client/addressmap.h b/src/feature/client/addressmap.h index 9179aef1d0..7f1024e09a 100644 --- a/src/feature/client/addressmap.h +++ b/src/feature/client/addressmap.h @@ -1,9 +1,14 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file addressmap.h + * @brief Header for addressmap.c + **/ + #ifndef TOR_ADDRESSMAP_H #define TOR_ADDRESSMAP_H @@ -62,4 +67,3 @@ STATIC void get_random_virtual_addr(const virtual_addr_conf_t *conf, #endif /* defined(ADDRESSMAP_PRIVATE) */ #endif /* !defined(TOR_ADDRESSMAP_H) */ - diff --git a/src/feature/client/bridges.c b/src/feature/client/bridges.c index 626c5efcae..96c3497c6f 100644 --- a/src/feature/client/bridges.c +++ b/src/feature/client/bridges.c @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -164,6 +164,28 @@ bridge_get_addr_port(const bridge_info_t *bridge) return &bridge->addrport_configured; } +/** + * Given a <b>bridge</b>, return the transport name. If none were configured, + * NULL is returned. + */ +const char * +bridget_get_transport_name(const bridge_info_t *bridge) +{ + tor_assert(bridge); + return bridge->transport_name; +} + +/** + * Return true if @a bridge has a transport name for which we don't actually + * know a transport. + */ +bool +bridge_has_invalid_transport(const bridge_info_t *bridge) +{ + const char *tname = bridget_get_transport_name(bridge); + return tname && transport_get_by_name(tname) == NULL; +} + /** 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 @@ -249,8 +271,8 @@ get_configured_bridge_by_exact_addr_port_digest(const tor_addr_t *addr, * address/port matches only. */ int addr_is_a_configured_bridge(const tor_addr_t *addr, - uint16_t port, - const char *digest) + uint16_t port, + const char *digest) { tor_assert(addr); return get_configured_bridge_by_addr_port_digest(addr, port, digest) ? 1 : 0; @@ -259,12 +281,26 @@ addr_is_a_configured_bridge(const tor_addr_t *addr, /** 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. */ + * If <b>ei->onion_key</b> is NULL, check for address/port matches only. + * + * Note that if the extend_info_t contains multiple addresses, we return true + * only if _every_ address is a bridge. + */ 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); + const tor_addr_port_t *ap1 = NULL, *ap2 = NULL; + if (! tor_addr_is_null(&ei->orports[0].addr)) + ap1 = &ei->orports[0]; + if (! tor_addr_is_null(&ei->orports[1].addr)) + ap2 = &ei->orports[1]; + IF_BUG_ONCE(ap1 == NULL) { + return 0; + } + return addr_is_a_configured_bridge(&ap1->addr, ap1->port, digest) && + (ap2 == NULL || + addr_is_a_configured_bridge(&ap2->addr, ap2->port, digest)); } /** Wrapper around get_configured_bridge_by_addr_port_digest() to look @@ -289,51 +325,21 @@ routerinfo_is_a_configured_bridge(const routerinfo_t *ri) } /** - * Return 1 iff <b>bridge_list</b> contains entry matching - * given; IPv4 address in host byte order (<b>ipv4_addr</b> - * and <b>port</b> (and no identity digest) OR it contains an - * entry whose identity matches <b>digest</b>. Otherwise, - * return 0. - */ -static int -bridge_exists_with_ipv4h_addr_and_port(const uint32_t ipv4_addr, - const uint16_t port, - const char *digest) -{ - tor_addr_t node_ipv4; - - if (tor_addr_port_is_valid_ipv4h(ipv4_addr, port, 0)) { - tor_addr_from_ipv4h(&node_ipv4, ipv4_addr); - - bridge_info_t *bridge = - get_configured_bridge_by_addr_port_digest(&node_ipv4, - port, - digest); - - return (bridge != NULL); - } - - return 0; -} - -/** * Return 1 iff <b>bridge_list</b> contains entry matching given - * <b>ipv6_addr</b> and <b>port</b> (and no identity digest) OR + * <b>addr</b> and <b>port</b> (and no identity digest) OR * it contains an entry whose identity matches <b>digest</b>. * Otherwise, return 0. */ static int -bridge_exists_with_ipv6_addr_and_port(const tor_addr_t *ipv6_addr, - const uint16_t port, - const char *digest) +bridge_exists_with_addr_and_port(const tor_addr_t *addr, + const uint16_t port, + const char *digest) { - if (!tor_addr_port_is_valid(ipv6_addr, port, 0)) + if (!tor_addr_port_is_valid(addr, port, 0)) return 0; bridge_info_t *bridge = - get_configured_bridge_by_addr_port_digest(ipv6_addr, - port, - digest); + get_configured_bridge_by_addr_port_digest(addr, port, digest); return (bridge != NULL); } @@ -348,7 +354,7 @@ int node_is_a_configured_bridge(const node_t *node) { /* First, let's try searching for a bridge with matching identity. */ - if (BUG(tor_digest_is_zero(node->identity))) + if (BUG(fast_mem_is_zero(node->identity, DIGEST_LEN))) return 0; if (find_bridge_by_digest(node->identity) != NULL) @@ -360,29 +366,29 @@ node_is_a_configured_bridge(const node_t *node) * check for absence of identity digest in a bridge. */ if (node->ri) { - if (bridge_exists_with_ipv4h_addr_and_port(node->ri->addr, - node->ri->or_port, - node->identity)) + if (bridge_exists_with_addr_and_port(&node->ri->ipv4_addr, + node->ri->ipv4_orport, + node->identity)) return 1; - if (bridge_exists_with_ipv6_addr_and_port(&node->ri->ipv6_addr, - node->ri->ipv6_orport, - node->identity)) + if (bridge_exists_with_addr_and_port(&node->ri->ipv6_addr, + node->ri->ipv6_orport, + node->identity)) return 1; } else if (node->rs) { - if (bridge_exists_with_ipv4h_addr_and_port(node->rs->addr, - node->rs->or_port, - node->identity)) + if (bridge_exists_with_addr_and_port(&node->rs->ipv4_addr, + node->rs->ipv4_orport, + node->identity)) return 1; - if (bridge_exists_with_ipv6_addr_and_port(&node->rs->ipv6_addr, - node->rs->ipv6_orport, - node->identity)) + if (bridge_exists_with_addr_and_port(&node->rs->ipv6_addr, + node->rs->ipv6_orport, + node->identity)) return 1; } else if (node->md) { - if (bridge_exists_with_ipv6_addr_and_port(&node->md->ipv6_addr, - node->md->ipv6_orport, - node->identity)) + if (bridge_exists_with_addr_and_port(&node->md->ipv6_addr, + node->md->ipv6_orport, + node->identity)) return 1; } @@ -612,7 +618,7 @@ find_transport_name_by_bridge_addrport(const tor_addr_t *addr, uint16_t port) */ int get_transport_by_bridge_addrport(const tor_addr_t *addr, uint16_t port, - const transport_t **transport) + const transport_t **transport) { *transport = NULL; if (!bridge_list) @@ -661,6 +667,15 @@ launch_direct_bridge_descriptor_fetch(bridge_info_t *bridge) DIR_PURPOSE_FETCH_SERVERDESC)) return; /* it's already on the way */ + if (bridge_has_invalid_transport(bridge)) { + download_status_mark_impossible(&bridge->fetch_status); + log_warn(LD_CONFIG, "Can't use bridge at %s: there is no configured " + "transport called \"%s\".", + safe_str_client(fmt_and_decorate_addr(&bridge->addr)), + bridget_get_transport_name(bridge)); + return; /* Can't use this bridge; it has not */ + } + 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.", @@ -670,7 +685,7 @@ launch_direct_bridge_descriptor_fetch(bridge_info_t *bridge) /* 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, + if (!reachable_addr_allows_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 " @@ -762,7 +777,7 @@ fetch_bridge_descriptors(const or_options_t *options, time_t now) !options->UpdateBridgesFromAuthority, !num_bridge_auths); if (ask_bridge_directly && - !fascist_firewall_allows_address_addr(&bridge->addr, bridge->port, + !reachable_addr_allows_addr(&bridge->addr, bridge->port, FIREWALL_OR_CONNECTION, 0, 0)) { log_notice(LD_DIR, "Bridge at '%s' isn't reachable by our " @@ -811,25 +826,23 @@ rewrite_node_address_for_bridge(const bridge_info_t *bridge, node_t *node) * 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) || + if ((!tor_addr_compare(&bridge->addr, &ri->ipv4_addr, CMP_EXACT) && + bridge->port == ri->ipv4_orport) || (!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; + tor_addr_copy(&ri->ipv4_addr, &bridge->addr); + ri->ipv4_orport = 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); + ri->nickname, fmt_addr(&ri->ipv4_addr), ri->ipv4_orport); } else if (tor_addr_family(&bridge->addr) == AF_INET6) { tor_addr_copy(&ri->ipv6_addr, &bridge->addr); ri->ipv6_orport = bridge->port; @@ -850,7 +863,7 @@ rewrite_node_address_for_bridge(const bridge_info_t *bridge, node_t *node) !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) && + node->ipv6_preferred = (reachable_addr_prefer_ipv6_orport(options) && !tor_addr_is_null(&node->ri->ipv6_addr)); } @@ -872,21 +885,20 @@ rewrite_node_address_for_bridge(const bridge_info_t *bridge, node_t *node) } 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) || + if ((!tor_addr_compare(&bridge->addr, &rs->ipv4_addr, CMP_EXACT) && + bridge->port == rs->ipv4_orport) || (!tor_addr_compare(&bridge->addr, &rs->ipv6_addr, CMP_EXACT) && bridge->port == rs->ipv6_orport)) { /* they match, so no need to do anything */ } else { if (tor_addr_family(&bridge->addr) == AF_INET) { - rs->addr = tor_addr_to_ipv4h(&bridge->addr); - rs->or_port = bridge->port; + tor_addr_copy(&rs->ipv4_addr, &bridge->addr); + rs->ipv4_orport = 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)); + rs->nickname, fmt_addrport(&bridge->addr, rs->ipv4_orport)); /* set IPv6 preferences even if there is no ri */ } else if (tor_addr_family(&bridge->addr) == AF_INET6) { tor_addr_copy(&rs->ipv6_addr, &bridge->addr); @@ -908,7 +920,7 @@ rewrite_node_address_for_bridge(const bridge_info_t *bridge, node_t *node) !tor_addr_is_null(&node->rs->ipv6_addr)); } else { /* Mark which address to use based on user preference */ - node->ipv6_preferred = (fascist_firewall_prefer_ipv6_orport(options) && + node->ipv6_preferred = (reachable_addr_prefer_ipv6_orport(options) && !tor_addr_is_null(&node->rs->ipv6_addr)); } @@ -953,7 +965,7 @@ learned_bridge_descriptor(routerinfo_t *ri, int from_cache) if (!from_cache) { /* This schedules the re-fetch at a constant interval, which produces * a pattern of bridge traffic. But it's better than trying all - * configured briges several times in the first few minutes. */ + * configured bridges several times in the first few minutes. */ download_status_reset(&bridge->fetch_status); } diff --git a/src/feature/client/bridges.h b/src/feature/client/bridges.h index 27b2750a45..f5ecc1b76d 100644 --- a/src/feature/client/bridges.h +++ b/src/feature/client/bridges.h @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -23,6 +23,8 @@ void sweep_bridge_list(void); const smartlist_t *bridge_list_get(void); 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); +const char *bridget_get_transport_name(const bridge_info_t *bridge); +bool bridge_has_invalid_transport(const bridge_info_t *bridge); bridge_info_t *get_configured_bridge_by_addr_port_digest( const tor_addr_t *addr, uint16_t port, diff --git a/src/feature/client/circpathbias.c b/src/feature/client/circpathbias.c index 1743ab5a81..4d27553926 100644 --- a/src/feature/client/circpathbias.c +++ b/src/feature/client/circpathbias.c @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -176,6 +176,7 @@ pathbias_get_scale_threshold(const or_options_t *options) static double pathbias_get_scale_ratio(const or_options_t *options) { + (void) options; /* * The scale factor is the denominator for our scaling * of circuit counts for our path bias window. @@ -185,7 +186,8 @@ pathbias_get_scale_ratio(const or_options_t *options) */ int denominator = networkstatus_get_param(NULL, "pb_scalefactor", 2, 2, INT32_MAX); - (void) options; + tor_assert(denominator > 0); + /** * The mult factor is the numerator for our scaling * of circuit counts for our path bias window. It @@ -301,7 +303,7 @@ pathbias_is_new_circ_attempt(origin_circuit_t *circ) return circ->cpath && circ->cpath->next != circ->cpath && circ->cpath->next->state == CPATH_STATE_AWAITING_KEYS; -#else /* !(defined(N2N_TAGGING_IS_POSSIBLE)) */ +#else /* !defined(N2N_TAGGING_IS_POSSIBLE) */ /* If tagging attacks are no longer possible, we probably want to * count bias from the first hop. However, one could argue that * timing-based tagging is still more useful than per-hop failure. @@ -369,8 +371,9 @@ pathbias_should_count(origin_circuit_t *circ) !circ->build_state->onehop_tunnel) { if ((rate_msg = rate_limit_log(&count_limit, approx_time()))) { log_info(LD_BUG, - "One-hop circuit has length %d. Path state is %s. " + "One-hop circuit %d has length %d. Path state is %s. " "Circuit is a %s currently %s.%s", + circ->global_identifier, circ->build_state->desired_path_len, pathbias_state_to_string(circ->path_state), circuit_purpose_to_string(circ->base_.purpose), @@ -398,12 +401,13 @@ pathbias_should_count(origin_circuit_t *circ) /* Check to see if the shouldcount result has changed due to a * unexpected purpose change that would affect our results */ if (circ->pathbias_shouldcount == PATHBIAS_SHOULDCOUNT_IGNORED) { - log_info(LD_BUG, - "Circuit %d is now being counted despite being ignored " - "in the past. Purpose is %s, path state is %s", - circ->global_identifier, - circuit_purpose_to_string(circ->base_.purpose), - pathbias_state_to_string(circ->path_state)); + log_info(LD_CIRC, + "Circuit %d is not being counted by pathbias because it was " + "ignored in the past. Purpose is %s, path state is %s", + circ->global_identifier, + circuit_purpose_to_string(circ->base_.purpose), + pathbias_state_to_string(circ->path_state)); + return 0; } circ->pathbias_shouldcount = PATHBIAS_SHOULDCOUNT_COUNTED; @@ -434,8 +438,9 @@ pathbias_count_build_attempt(origin_circuit_t *circ) if ((rate_msg = rate_limit_log(&circ_attempt_notice_limit, approx_time()))) { log_info(LD_BUG, - "Opened circuit is in strange path state %s. " + "Opened circuit %d is in strange path state %s. " "Circuit is a %s currently %s.%s", + circ->global_identifier, pathbias_state_to_string(circ->path_state), circuit_purpose_to_string(circ->base_.purpose), circuit_state_to_string(circ->base_.state), @@ -468,8 +473,9 @@ pathbias_count_build_attempt(origin_circuit_t *circ) if ((rate_msg = rate_limit_log(&circ_attempt_notice_limit, approx_time()))) { log_info(LD_BUG, - "Unopened circuit has strange path state %s. " + "Unopened circuit %d has strange path state %s. " "Circuit is a %s currently %s.%s", + circ->global_identifier, pathbias_state_to_string(circ->path_state), circuit_purpose_to_string(circ->base_.purpose), circuit_state_to_string(circ->base_.state), @@ -538,8 +544,9 @@ pathbias_count_build_success(origin_circuit_t *circ) if ((rate_msg = rate_limit_log(&success_notice_limit, approx_time()))) { log_info(LD_BUG, - "Succeeded circuit is in strange path state %s. " + "Succeeded circuit %d is in strange path state %s. " "Circuit is a %s currently %s.%s", + circ->global_identifier, pathbias_state_to_string(circ->path_state), circuit_purpose_to_string(circ->base_.purpose), circuit_state_to_string(circ->base_.state), @@ -574,8 +581,9 @@ pathbias_count_build_success(origin_circuit_t *circ) if ((rate_msg = rate_limit_log(&success_notice_limit, approx_time()))) { log_info(LD_BUG, - "Opened circuit is in strange path state %s. " + "Opened circuit %d is in strange path state %s. " "Circuit is a %s currently %s.%s", + circ->global_identifier, pathbias_state_to_string(circ->path_state), circuit_purpose_to_string(circ->base_.purpose), circuit_state_to_string(circ->base_.state), @@ -601,8 +609,9 @@ pathbias_count_use_attempt(origin_circuit_t *circ) if (circ->path_state < PATH_STATE_BUILD_SUCCEEDED) { log_notice(LD_BUG, - "Used circuit is in strange path state %s. " + "Used circuit %d is in strange path state %s. " "Circuit is a %s currently %s.", + circ->global_identifier, pathbias_state_to_string(circ->path_state), circuit_purpose_to_string(circ->base_.purpose), circuit_state_to_string(circ->base_.state)); @@ -674,7 +683,7 @@ pathbias_mark_use_success(origin_circuit_t *circ) } /** - * If a stream ever detatches from a circuit in a retriable way, + * If a stream ever detaches from a circuit in a retriable way, * we need to mark this circuit as still needing either another * successful stream, or in need of a probe. * @@ -817,6 +826,11 @@ pathbias_send_usable_probe(circuit_t *circ) ocirc->pathbias_probe_nonce &= 0x00ffffff; probe_nonce = tor_dup_ip(ocirc->pathbias_probe_nonce); + if (!probe_nonce) { + log_err(LD_BUG, "Failed to generate nonce"); + return -1; + } + tor_snprintf(payload,RELAY_PAYLOAD_SIZE, "%s:25", probe_nonce); payload_len = (int)strlen(payload)+1; diff --git a/src/feature/client/circpathbias.h b/src/feature/client/circpathbias.h index a9a8d18df2..88cc982dd4 100644 --- a/src/feature/client/circpathbias.h +++ b/src/feature/client/circpathbias.h @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** diff --git a/src/feature/client/dnsserv.c b/src/feature/client/dnsserv.c index cf593cfb68..c1981ecde0 100644 --- a/src/feature/client/dnsserv.c +++ b/src/feature/client/dnsserv.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2007-2019, The Tor Project, Inc. */ +/* Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -26,8 +26,9 @@ #include "app/config/config.h" #include "core/mainloop/connection.h" #include "core/or/connection_edge.h" -#include "feature/control/control.h" +#include "feature/control/control_events.h" #include "core/mainloop/mainloop.h" +#include "core/mainloop/netstatus.h" #include "core/or/policies.h" #include "feature/control/control_connection_st.h" @@ -213,6 +214,9 @@ dnsserv_launch_request(const char *name, int reverse, edge_connection_t *conn; char *q_name; + /* Launching a request for a user counts as user activity. */ + note_user_activity(approx_time()); + /* Make a new dummy AP connection, and attach the request to it. */ entry_conn = entry_connection_new(CONN_TYPE_AP, AF_INET); entry_conn->entry_cfg.dns_request = 1; @@ -234,7 +238,7 @@ dnsserv_launch_request(const char *name, int reverse, TO_CONN(conn)->port = control_conn->base_.port; TO_CONN(conn)->address = tor_addr_to_str_dup(&control_conn->base_.addr); } -#else /* !(defined(AF_UNIX)) */ +#else /* !defined(AF_UNIX) */ TO_CONN(conn)->port = control_conn->base_.port; TO_CONN(conn)->address = tor_addr_to_str_dup(&control_conn->base_.addr); #endif /* defined(AF_UNIX) */ diff --git a/src/feature/client/dnsserv.h b/src/feature/client/dnsserv.h index fff1ed2adb..4011cb4e02 100644 --- a/src/feature/client/dnsserv.h +++ b/src/feature/client/dnsserv.h @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** diff --git a/src/feature/client/entrynodes.c b/src/feature/client/entrynodes.c index 6c7f8057bd..82866ea668 100644 --- a/src/feature/client/entrynodes.c +++ b/src/feature/client/entrynodes.c @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -47,8 +47,7 @@ * 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. + * to use that circuit. * * And as a final group, we have an ordered list of PRIMARY GUARDS, * whose elements are taken from the filtered set. We prefer @@ -59,7 +58,7 @@ * * 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 + * or the first (by sampled order) 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 @@ -92,14 +91,18 @@ * [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 confirmed, update the primary list, and sort them + * by sampled order. * * [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 + * [x] When we update the confirmed list, or when we re-build the primary list + * and detect a change, we sort those lists by sampled_idx + * + * [x] Before calling first_reachable_filtered_entry_guard(), 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 @@ -114,7 +117,7 @@ #include "core/or/or.h" #include "app/config/config.h" -#include "app/config/confparse.h" +#include "lib/confmgt/confmgt.h" #include "app/config/statefile.h" #include "core/mainloop/connection.h" #include "core/mainloop/mainloop.h" @@ -128,7 +131,7 @@ #include "feature/client/circpathbias.h" #include "feature/client/entrynodes.h" #include "feature/client/transports.h" -#include "feature/control/control.h" +#include "feature/control/control_events.h" #include "feature/dircommon/directory.h" #include "feature/nodelist/describe.h" #include "feature/nodelist/microdesc.h" @@ -172,6 +175,7 @@ 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); +static int compare_guards_by_sampled_idx(const void **a_, const void **b_); /** Return 0 if we should apply guardfraction information found in the * consensus. A specific consensus can be specified with the @@ -338,7 +342,7 @@ entry_guard_get_pathbias_state(entry_guard_t *guard) HANDLE_IMPL(entry_guard, entry_guard_t, ATTR_UNUSED STATIC) -/** Return an interval betweeen 'now' and 'max_backdate' seconds in the past, +/** Return an interval between '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. */ @@ -890,6 +894,7 @@ entry_guard_add_to_sample_impl(guard_selection_t *gs, tor_free(guard->sampled_by_version); guard->sampled_by_version = tor_strdup(VERSION); guard->currently_listed = 1; + guard->sampled_idx = gs->next_sampled_idx++; guard->confirmed_idx = -1; /* non-persistent fields */ @@ -901,6 +906,11 @@ entry_guard_add_to_sample_impl(guard_selection_t *gs, guard->in_selection = gs; entry_guard_set_filtered_flags(get_options(), gs, guard); entry_guards_changed_for_guard_selection(gs); + + /* Just added this guard to the sampled set and hence it might be used as a + * guard in the future: send GUARD NEW control event. */ + control_event_guard(guard->nickname, guard->identity, "NEW"); + return guard; } @@ -1038,7 +1048,7 @@ get_max_sample_size(guard_selection_t *gs, * 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> + * 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. */ @@ -1383,7 +1393,7 @@ sampled_guards_prune_obsolete_entries(guard_selection_t *gs, if (rmv) { ++n_changes; - SMARTLIST_DEL_CURRENT(gs->sampled_entry_guards, guard); + SMARTLIST_DEL_CURRENT_KEEPORDER(gs->sampled_entry_guards, guard); remove_guard_from_confirmed_and_primary_lists(gs, guard); entry_guard_free(guard); } @@ -1456,7 +1466,7 @@ node_passes_guard_filter(const or_options_t *options, !routerset_contains_node(options->EntryNodes, node)) return 0; - if (!fascist_firewall_allows_node(node, FIREWALL_OR_CONNECTION, 0)) + if (!reachable_addr_allows_node(node, FIREWALL_OR_CONNECTION, 0)) return 0; if (node_is_a_configured_bridge(node)) @@ -1482,7 +1492,7 @@ bridge_passes_guard_filter(const or_options_t *options, /* Ignore entrynodes */ const tor_addr_port_t *addrport = bridge_get_addr_port(bridge); - if (!fascist_firewall_allows_address_addr(&addrport->addr, + if (!reachable_addr_allows_addr(&addrport->addr, addrport->port, FIREWALL_OR_CONNECTION, 0, 0)) @@ -1544,7 +1554,7 @@ guard_in_node_family(const entry_guard_t *guard, const node_t *node) if (get_options()->EnforceDistinctSubnets && guard->bridge_addr) { tor_addr_t node_addr; node_get_addr(node, &node_addr); - if (addrs_in_same_network_family(&node_addr, + if (router_addrs_in_same_network(&node_addr, &guard->bridge_addr->addr)) { return 1; } @@ -1566,12 +1576,12 @@ guard_create_exit_restriction(const uint8_t *exit_id) } /** If we have fewer than this many possible usable guards, don't set - * MD-availability-based restrictions: we might blacklist all of them. */ + * MD-availability-based restrictions: we might denylist all of them. */ #define MIN_GUARDS_FOR_MD_RESTRICTION 10 /** Return true if we should set md dirserver restrictions. We might not want * to set those if our guard options are too restricted, since we don't want - * to blacklist all of them. */ + * to denylist all of them. */ static int should_set_md_dirserver_restriction(void) { @@ -1707,7 +1717,7 @@ entry_guards_update_filtered_sets(guard_selection_t *gs) } /** - * Return a random guard from the reachable filtered sample guards + * Return the first sampled 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. * @@ -1718,7 +1728,7 @@ entry_guards_update_filtered_sets(guard_selection_t *gs) * violate it. **/ STATIC entry_guard_t * -sample_reachable_filtered_entry_guards(guard_selection_t *gs, +first_reachable_filtered_entry_guard(guard_selection_t *gs, const entry_guard_restriction_t *rst, unsigned flags) { @@ -1771,7 +1781,17 @@ sample_reachable_filtered_entry_guards(guard_selection_t *gs, flags, smartlist_len(reachable_filtered_sample)); if (smartlist_len(reachable_filtered_sample)) { - result = smartlist_choose(reachable_filtered_sample); + /** + * Get the first guard of the filtered set builds from + * sampled_entry_guards. Proposal 310 suggests this design to overcome + * performance and security issues linked to the previous selection + * method. The guard selected here should be filtered out if this function + * is called again in the same context. I.e., if we filter guards to add + * them into some list X, then the guards from list X will be filtered out + * when this function is called again. Hence it requires setting exclude + * flags in a appropriate way (depending of the context of the caller). + */ + result = smartlist_get(reachable_filtered_sample, 0); log_info(LD_GUARD, " (Selected %s.)", result ? entry_guard_describe(result) : "<null>"); } @@ -1780,10 +1800,6 @@ sample_reachable_filtered_entry_guards(guard_selection_t *gs, 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_) { @@ -1795,6 +1811,21 @@ compare_guards_by_confirmed_idx(const void **a_, const void **b_) else return 0; } +/** + * Helper: compare two entry_guard_t by their sampled_idx values. + * Used to sort the sampled list + */ +static int +compare_guards_by_sampled_idx(const void **a_, const void **b_) +{ + const entry_guard_t *a = *a_, *b = *b_; + if (a->sampled_idx < b->sampled_idx) + return -1; + else if (a->sampled_idx > b->sampled_idx) + return 1; + else + return 0; +} /** * Find the confirmed guards from among the sampled guards in <b>gs</b>, @@ -1811,7 +1842,7 @@ entry_guards_update_confirmed(guard_selection_t *gs) } SMARTLIST_FOREACH_END(guard); smartlist_sort(gs->confirmed_entry_guards, compare_guards_by_confirmed_idx); - + /** Needed to keep a dense array of confirmed_idx */ int any_changed = 0; SMARTLIST_FOREACH_BEGIN(gs->confirmed_entry_guards, entry_guard_t *, guard) { if (guard->confirmed_idx != guard_sl_idx) { @@ -1821,6 +1852,8 @@ entry_guards_update_confirmed(guard_selection_t *gs) } SMARTLIST_FOREACH_END(guard); gs->next_confirmed_idx = smartlist_len(gs->confirmed_entry_guards); + // We need the confirmed list to always be give guards in sampled order + smartlist_sort(gs->confirmed_entry_guards, compare_guards_by_sampled_idx); if (any_changed) { entry_guards_changed_for_guard_selection(gs); @@ -1849,6 +1882,9 @@ make_guard_confirmed(guard_selection_t *gs, entry_guard_t *guard) guard->confirmed_idx = gs->next_confirmed_idx++; smartlist_add(gs->confirmed_entry_guards, guard); + /** The confirmation ordering might not be the sample ording. We need to + * reorder */ + smartlist_sort(gs->confirmed_entry_guards, compare_guards_by_sampled_idx); // This confirmed guard might kick something else out of the primary // guards. @@ -1912,7 +1948,7 @@ entry_guards_update_primary(guard_selection_t *gs) /* 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, + entry_guard_t *guard = first_reachable_filtered_entry_guard(gs, NULL, SAMPLE_EXCLUDE_CONFIRMED| SAMPLE_EXCLUDE_PRIMARY| SAMPLE_NO_UPDATE_PRIMARY); @@ -1943,6 +1979,7 @@ entry_guards_update_primary(guard_selection_t *gs) g->confirmed_idx >= 0 ? " (confirmed)" : "", g->is_filtered_guard ? "" : " (excluded by filter)"); } SMARTLIST_FOREACH_END(g); + smartlist_sort(new_primary_guards, compare_guards_by_sampled_idx); } smartlist_free(old_primary_guards); @@ -1974,10 +2011,12 @@ get_retry_schedule(time_t failing_since, time_t now, const struct { time_t maximum; int primary_delay; int nonprimary_delay; } delays[] = { + // clang-format off { 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 } + // clang-format on }; unsigned i; @@ -2053,10 +2092,15 @@ select_primary_guard_for_circuit(guard_selection_t *gs, SMARTLIST_FOREACH_BEGIN(gs->primary_entry_guards, entry_guard_t *, guard) { entry_guard_consider_retry(guard); - if (! entry_guard_obeys_restriction(guard, rst)) + if (!entry_guard_obeys_restriction(guard, rst)) { + log_info(LD_GUARD, "Entry guard %s doesn't obey restriction, we test the" + " next one", entry_guard_describe(guard)); continue; + } if (guard->is_reachable != GUARD_REACHABLE_NO) { if (need_descriptor && !guard_has_descriptor(guard)) { + log_info(LD_GUARD, "Guard %s does not have a descriptor", + entry_guard_describe(guard)); continue; } *state_out = GUARD_CIRC_STATE_USABLE_ON_COMPLETION; @@ -2069,9 +2113,11 @@ select_primary_guard_for_circuit(guard_selection_t *gs, if (smartlist_len(usable_primary_guards)) { chosen_guard = smartlist_choose(usable_primary_guards); + log_info(LD_GUARD, + "Selected primary guard %s for circuit from a list size of %d.", + entry_guard_describe(chosen_guard), + smartlist_len(usable_primary_guards)); smartlist_free(usable_primary_guards); - log_info(LD_GUARD, "Selected primary guard %s for circuit.", - entry_guard_describe(chosen_guard)); } smartlist_free(usable_primary_guards); @@ -2116,10 +2162,10 @@ select_confirmed_guard_for_circuit(guard_selection_t *gs, } /** - * For use with a circuit, pick a confirmed usable filtered guard - * at random. 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. + * For use with a circuit, pick a usable 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_filtered_guard_for_circuit(guard_selection_t *gs, @@ -2132,7 +2178,7 @@ select_filtered_guard_for_circuit(guard_selection_t *gs, unsigned flags = 0; if (need_descriptor) flags |= SAMPLE_EXCLUDE_NO_DESCRIPTOR; - chosen_guard = sample_reachable_filtered_entry_guards(gs, + chosen_guard = first_reachable_filtered_entry_guard(gs, rst, SAMPLE_EXCLUDE_CONFIRMED | SAMPLE_EXCLUDE_PRIMARY | @@ -2146,7 +2192,7 @@ select_filtered_guard_for_circuit(guard_selection_t *gs, chosen_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 " + "guard %s for circuit. Will try other guards before " "using this circuit.", entry_guard_describe(chosen_guard)); return chosen_guard; @@ -2187,8 +2233,8 @@ select_entry_guard_for_circuit(guard_selection_t *gs, if (chosen_guard) return chosen_guard; - /* "Otherwise, if there is no such entry, select a member at - random from {USABLE_FILTERED_GUARDS}." */ + /* "Otherwise, if there is no such entry, select a member + * {USABLE_FILTERED_GUARDS} following the sample ordering" */ chosen_guard = select_filtered_guard_for_circuit(gs, usage, rst, state_out); if (chosen_guard == NULL) { @@ -2218,6 +2264,9 @@ entry_guards_note_guard_failure(guard_selection_t *gs, if (guard->failing_since == 0) guard->failing_since = approx_time(); + /* This guard not reachable: send GUARD DOWN event */ + control_event_guard(guard->nickname, guard->identity, "DOWN"); + log_info(LD_GUARD, "Recorded failure for %s%sguard %s", guard->is_primary?"primary ":"", guard->confirmed_idx>=0?"confirmed ":"", @@ -2243,6 +2292,11 @@ entry_guards_note_guard_success(guard_selection_t *gs, const time_t last_time_on_internet = gs->last_time_on_internet; gs->last_time_on_internet = approx_time(); + /* If guard was not already marked as reachable, send a GUARD UP signal */ + if (guard->is_reachable != GUARD_REACHABLE_YES) { + control_event_guard(guard->nickname, guard->identity, "UP"); + } + guard->is_reachable = GUARD_REACHABLE_YES; guard->failing_since = 0; guard->is_pending = 0; @@ -2771,10 +2825,12 @@ entry_guards_update_all(guard_selection_t *gs) /** * Return a newly allocated string for encoding the persistent parts of - * <b>guard</b> to the state file. + * <b>guard</b> to the state file. <b>dense_sampled_idx</b> refers to the + * sampled_idx made dense for this <b>guard</b>. Encoding all guards should + * lead to a dense array of sampled_idx in the state file. */ STATIC char * -entry_guard_encode_for_state(entry_guard_t *guard) +entry_guard_encode_for_state(entry_guard_t *guard, int dense_sampled_idx) { /* * The meta-format we use is K=V K=V K=V... where K can be any @@ -2803,7 +2859,8 @@ entry_guard_encode_for_state(entry_guard_t *guard) format_iso_time_nospace(tbuf, guard->sampled_on_date); smartlist_add_asprintf(result, "sampled_on=%s", tbuf); - + // Replacing the sampled_idx by dense array + smartlist_add_asprintf(result, "sampled_idx=%d", dense_sampled_idx); if (guard->sampled_by_version) { smartlist_add_asprintf(result, "sampled_by=%s", guard->sampled_by_version); @@ -2859,6 +2916,78 @@ entry_guard_encode_for_state(entry_guard_t *guard) } /** + * Extract key=val from the state string <b>s</b> and duplicate the value to + * some string target declared in entry_guard_parse_from_state + */ +static void +parse_from_state_set_vals(const char *s, smartlist_t *entries, smartlist_t + *extra, strmap_t *vals) +{ + 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); +} + +/** + * Handle part of the parsing state file logic, focused on time related things + */ +static void +parse_from_state_handle_time(entry_guard_t *guard, char *sampled_on, char + *unlisted_since, char *confirmed_on) +{ +#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; +} + +/** * 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. @@ -2874,6 +3003,7 @@ entry_guard_parse_from_state(const char *s) char *rsa_id = NULL; char *nickname = NULL; char *sampled_on = NULL; + char *sampled_idx = NULL; char *sampled_by = NULL; char *unlisted_since = NULL; char *listed = NULL; @@ -2890,6 +3020,7 @@ entry_guard_parse_from_state(const char *s) char *pb_collapsed_circuits = NULL; char *pb_unusable_circuits = NULL; char *pb_timeouts = NULL; + int invalid_sampled_idx = get_max_sample_size_absolute(); /* Split up the entries. Put the ones we know about in strings and the * rest in "extra". */ @@ -2903,6 +3034,7 @@ entry_guard_parse_from_state(const char *s) FIELD(rsa_id); FIELD(nickname); FIELD(sampled_on); + FIELD(sampled_idx); FIELD(sampled_by); FIELD(unlisted_since); FIELD(listed); @@ -2918,29 +3050,8 @@ entry_guard_parse_from_state(const char *s) 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); + /* Extract from s the key=val that we recognize, put the others in extra*/ + parse_from_state_set_vals(s, entries, extra, vals); smartlist_free(entries); strmap_free(vals, NULL); @@ -2988,43 +3099,12 @@ entry_guard_parse_from_state(const char *s) } /* 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; + parse_from_state_handle_time(guard, sampled_on, unlisted_since, + confirmed_on); /* Take sampled_by_version verbatim. */ guard->sampled_by_version = sampled_by; sampled_by = NULL; /* prevent free */ - /* Listed is a boolean */ if (listed && strcmp(listed, "0")) guard->currently_listed = 1; @@ -3042,6 +3122,29 @@ entry_guard_parse_from_state(const char *s) } } + if (sampled_idx) { + int ok = 1; + long idx = tor_parse_long(sampled_idx, 10, 0, INT_MAX, &ok, NULL); + if (!ok) { + log_warn(LD_GUARD, "Guard has invalid sampled_idx %s", + escaped(sampled_idx)); + /* set it to a idx higher than the max sample size */ + guard->sampled_idx = invalid_sampled_idx++; + } else { + guard->sampled_idx = (int)idx; + } + } else if (confirmed_idx) { + /* This state has been written by an older Tor version which did not have + * sample ordering */ + + guard->sampled_idx = guard->confirmed_idx; + } else { + log_info(LD_GUARD, "The state file seems to be into a status that could" + " yield to weird entry node selection: we're missing both a" + " sampled_idx and a confirmed_idx."); + guard->sampled_idx = invalid_sampled_idx++; + } + /* Anything we didn't recognize gets crammed together */ if (smartlist_len(extra) > 0) { guard->extra_state_fields = smartlist_join_strings(extra, " ", 0, NULL); @@ -3096,6 +3199,7 @@ entry_guard_parse_from_state(const char *s) tor_free(listed); tor_free(confirmed_on); tor_free(confirmed_idx); + tor_free(sampled_idx); tor_free(bridge_addr); tor_free(pb_use_attempts); tor_free(pb_use_successes); @@ -3125,13 +3229,15 @@ entry_guards_update_guards_in_state(or_state_t *state) config_line_t **nextline = &lines; SMARTLIST_FOREACH_BEGIN(guard_contexts, guard_selection_t *, gs) { + int i = 0; 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)->value = entry_guard_encode_for_state(guard, i); nextline = &(*nextline)->next; + i++; } SMARTLIST_FOREACH_END(guard); } SMARTLIST_FOREACH_END(gs); @@ -3184,6 +3290,14 @@ entry_guards_load_guards_from_state(or_state_t *state, int set) tor_assert(gs); smartlist_add(gs->sampled_entry_guards, guard); guard->in_selection = gs; + /* Recompute the next_sampled_id from the state. We do not assume that + * sampled guards appear in the correct order within the file, and we + * need to know what would be the next sampled idx to give to any + * new sampled guard (i.e., max of guard->sampled_idx + 1)*/ + if (gs->next_sampled_idx <= guard->sampled_idx) { + gs->next_sampled_idx = guard->sampled_idx + 1; + } + } else { entry_guard_free(guard); } @@ -3191,6 +3305,10 @@ entry_guards_load_guards_from_state(or_state_t *state, int set) if (set) { SMARTLIST_FOREACH_BEGIN(guard_contexts, guard_selection_t *, gs) { + /** Guards should be in sample order within the file, but it is maybe + * better NOT to assume that. Let's order them before updating lists + */ + smartlist_sort(gs->sampled_entry_guards, compare_guards_by_sampled_idx); entry_guards_update_all(gs); } SMARTLIST_FOREACH_END(gs); } @@ -3241,7 +3359,7 @@ get_guard_state_for_bridge_desc_fetch(const char *digest) } /* Update the guard last_tried_to_connect time since it's checked by the - * guard susbsystem. */ + * guard subsystem. */ guard->last_tried_to_connect = approx_time(); /* Create the guard state */ diff --git a/src/feature/client/entrynodes.h b/src/feature/client/entrynodes.h index 4e5eb4e960..4b236dc80c 100644 --- a/src/feature/client/entrynodes.h +++ b/src/feature/client/entrynodes.h @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -15,7 +15,7 @@ #include "lib/container/handles.h" /* Forward declare for guard_selection_t; entrynodes.c has the real struct */ -typedef struct guard_selection_s guard_selection_t; +typedef struct guard_selection_t guard_selection_t; /* Forward declare for entry_guard_t; the real declaration is private. */ typedef struct entry_guard_t entry_guard_t; @@ -28,7 +28,7 @@ typedef struct circuit_guard_state_t circuit_guard_state_t; private. */ typedef struct entry_guard_restriction_t entry_guard_restriction_t; -/* Information about a guard's pathbias status. +/** 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. */ @@ -117,6 +117,13 @@ struct entry_guard_t { * confirmed guard. */ time_t confirmed_on_date; /* 0 if not confirmed */ /** + * In what order was this guard sampled? Guards with + * lower indices appear earlier on the sampled list, the confirmed list and + * the primary list as a result of Prop 310 + */ + int sampled_idx; + + /** * 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. @@ -210,7 +217,7 @@ typedef enum guard_selection_type_t { * See the module documentation for entrynodes.c for more information * about guard selection algorithms. */ -struct guard_selection_s { +struct guard_selection_t { /** * The name for this guard-selection object. (Must not contain spaces). */ @@ -242,8 +249,9 @@ struct guard_selection_s { * 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. + * have confirmed_on_date set. + * The ordering of the list should be by sampled idx. The reasoning behind + * it is linked to Proposal 310. * * This list is persistent. It is a subset of the elements in * sampled_entry_guards, and its pointers point to elements of @@ -271,6 +279,12 @@ struct guard_selection_s { * confirmed_entry_guards receive? */ int next_confirmed_idx; + /** What sampled_idx value should the next-added member of + * sampled_entry_guards receive? This should follow the size of the sampled + * list until sampled relays get pruned for some reason + */ + int next_sampled_idx; + }; struct entry_guard_handle_t; @@ -515,7 +529,8 @@ MOCK_DECL(STATIC circuit_guard_state_t *, 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 char *entry_guard_encode_for_state(entry_guard_t *guard, int + dense_sampled_index); STATIC entry_guard_t *entry_guard_parse_from_state(const char *s); #define entry_guard_free(e) \ FREE_AND_NULL(entry_guard_t, entry_guard_free_, (e)) @@ -523,7 +538,7 @@ 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() + * @name Flags for first_reachable_filtered_entry_guard() */ /**@{*/ #define SAMPLE_EXCLUDE_CONFIRMED (1u<<0) @@ -532,7 +547,7 @@ STATIC int entry_guards_all_primary_guards_are_down(guard_selection_t *gs); #define SAMPLE_NO_UPDATE_PRIMARY (1u<<3) #define SAMPLE_EXCLUDE_NO_DESCRIPTOR (1u<<4) /**@}*/ -STATIC entry_guard_t *sample_reachable_filtered_entry_guards( +STATIC entry_guard_t *first_reachable_filtered_entry_guard( guard_selection_t *gs, const entry_guard_restriction_t *rst, unsigned flags); diff --git a/src/feature/client/feature_client.md b/src/feature/client/feature_client.md new file mode 100644 index 0000000000..dd4bf78ec8 --- /dev/null +++ b/src/feature/client/feature_client.md @@ -0,0 +1,5 @@ +@dir /feature/client +@brief feature/client: Client-specific code + +(There is also a bunch of client-specific code in other modules.) + diff --git a/src/feature/client/include.am b/src/feature/client/include.am new file mode 100644 index 0000000000..53c9f047d4 --- /dev/null +++ b/src/feature/client/include.am @@ -0,0 +1,20 @@ + +# ADD_C_FILE: INSERT SOURCES HERE. +LIBTOR_APP_A_SOURCES += \ + src/feature/client/addressmap.c \ + src/feature/client/bridges.c \ + src/feature/client/circpathbias.c \ + src/feature/client/dnsserv.c \ + src/feature/client/entrynodes.c \ + src/feature/client/proxymode.c \ + src/feature/client/transports.c + +# ADD_C_FILE: INSERT HEADERS HERE. +noinst_HEADERS += \ + src/feature/client/addressmap.h \ + src/feature/client/bridges.h \ + src/feature/client/circpathbias.h \ + src/feature/client/dnsserv.h \ + src/feature/client/entrynodes.h \ + src/feature/client/proxymode.h \ + src/feature/client/transports.h diff --git a/src/feature/client/proxymode.c b/src/feature/client/proxymode.c new file mode 100644 index 0000000000..aa269ec7fb --- /dev/null +++ b/src/feature/client/proxymode.c @@ -0,0 +1,32 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2020, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file proxymode.c + * @brief Determine whether we are trying to be a proxy. + **/ + +#include "core/or/or.h" + +#include "app/config/config.h" +#include "core/mainloop/connection.h" +#include "core/or/port_cfg_st.h" +#include "feature/client/proxymode.h" + +/** Return true iff we are trying to proxy client connections. */ +int +proxy_mode(const or_options_t *options) +{ + (void)options; + SMARTLIST_FOREACH_BEGIN(get_configured_ports(), const port_cfg_t *, p) { + if (p->type == CONN_TYPE_AP_LISTENER || + p->type == CONN_TYPE_AP_TRANS_LISTENER || + p->type == CONN_TYPE_AP_DNS_LISTENER || + p->type == CONN_TYPE_AP_NATD_LISTENER) + return 1; + } SMARTLIST_FOREACH_END(p); + return 0; +} diff --git a/src/feature/client/proxymode.h b/src/feature/client/proxymode.h new file mode 100644 index 0000000000..30be08ff78 --- /dev/null +++ b/src/feature/client/proxymode.h @@ -0,0 +1,17 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2020, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file proxymode.h + * \brief Header file for proxymode.c. + **/ + +#ifndef TOR_PROXYMODE_H +#define TOR_PROXYMODE_H + +int proxy_mode(const or_options_t *options); + +#endif /* !defined(TOR_PROXYMODE_H) */ diff --git a/src/feature/client/transports.c b/src/feature/client/transports.c index 0d1342c87b..4b05d55494 100644 --- a/src/feature/client/transports.c +++ b/src/feature/client/transports.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2019, The Tor Project, Inc. */ +/* Copyright (c) 2011-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -16,7 +16,7 @@ * managed proxies that are still unconfigured. * * In every run_scheduled_event() tick, we attempt to launch and then - * configure the unconfiged managed proxies, using the configuration + * configure the unconfigured managed proxies, using the configuration * protocol defined in the 180_pluggable_transport.txt proposal. A * managed proxy might need several ticks to get fully configured. * @@ -71,7 +71,7 @@ * * We then start parsing torrc again. * - * Everytime we encounter a transport line using a managed proxy that + * Every time we encounter a transport line using a managed proxy that * was around before the config read, we cleanse that proxy from the * removal mark. We also toggle the <b>check_if_restarts_needed</b> * flag, so that on the next <b>pt_configure_remaining_proxies</b> @@ -97,15 +97,20 @@ #include "core/or/circuitbuild.h" #include "feature/client/transports.h" #include "feature/relay/router.h" +#include "feature/relay/relay_find_addr.h" +/* 31851: split the server transport code out of the client module */ +#include "feature/relay/transport_config.h" #include "app/config/statefile.h" #include "core/or/connection_or.h" #include "feature/relay/ext_orport.h" -#include "feature/control/control.h" +#include "feature/control/control_events.h" +#include "lib/encoding/confline.h" +#include "lib/encoding/kvline.h" +#include "lib/process/process.h" #include "lib/process/env.h" -#include "lib/process/subprocess.h" -static process_environment_t * +static smartlist_t * create_managed_proxy_environment(const managed_proxy_t *mp); static inline int proxy_configuration_finished(const managed_proxy_t *mp); @@ -127,6 +132,8 @@ static void parse_method_error(const char *line, int is_server_method); #define PROTO_SMETHODS_DONE "SMETHODS DONE" #define PROTO_PROXY_DONE "PROXY DONE" #define PROTO_PROXY_ERROR "PROXY-ERROR" +#define PROTO_LOG "LOG" +#define PROTO_STATUS "STATUS" /** The first and only supported - at the moment - configuration protocol version. */ @@ -361,6 +368,28 @@ static int unconfigured_proxies_n = 0; /** Boolean: True iff we might need to restart some proxies. */ static int check_if_restarts_needed = 0; +/** Return true iff we have a managed_proxy_t in the global list is for the + * given transport name. */ +bool +managed_proxy_has_transport(const char *transport_name) +{ + tor_assert(transport_name); + + if (!managed_proxy_list) { + return false; + } + + SMARTLIST_FOREACH_BEGIN(managed_proxy_list, const managed_proxy_t *, mp) { + SMARTLIST_FOREACH_BEGIN(mp->transports_to_launch, const char *, name) { + if (!strcasecmp(name, transport_name)) { + return true; + } + } SMARTLIST_FOREACH_END(name); + } SMARTLIST_FOREACH_END(mp); + + return false; +} + /** Return true if there are still unconfigured managed proxies, or proxies * that need restarting. */ int @@ -490,8 +519,8 @@ proxy_prepare_for_restart(managed_proxy_t *mp) tor_assert(mp->conf_state == PT_PROTO_COMPLETED); /* destroy the process handle and terminate the process. */ - tor_process_handle_destroy(mp->process_handle, 1); - mp->process_handle = NULL; + process_set_data(mp->process, NULL); + process_terminate(mp->process); /* destroy all its registered transports, since we will no longer use them. */ @@ -520,34 +549,35 @@ proxy_prepare_for_restart(managed_proxy_t *mp) static int launch_managed_proxy(managed_proxy_t *mp) { - int retval; - - process_environment_t *env = create_managed_proxy_environment(mp); - -#ifdef _WIN32 - /* Passing NULL as lpApplicationName makes Windows search for the .exe */ - retval = tor_spawn_background(NULL, - (const char **)mp->argv, - env, - &mp->process_handle); -#else /* !(defined(_WIN32)) */ - retval = tor_spawn_background(mp->argv[0], - (const char **)mp->argv, - env, - &mp->process_handle); -#endif /* defined(_WIN32) */ - - process_environment_free(env); - - if (retval == PROCESS_STATUS_ERROR) { - log_warn(LD_GENERAL, "Managed proxy at '%s' failed at launch.", + tor_assert(mp); + + smartlist_t *env = create_managed_proxy_environment(mp); + + /* Configure our process. */ + process_set_data(mp->process, mp); + process_set_stdout_read_callback(mp->process, managed_proxy_stdout_callback); + process_set_stderr_read_callback(mp->process, managed_proxy_stderr_callback); + process_set_exit_callback(mp->process, managed_proxy_exit_callback); + process_set_protocol(mp->process, PROCESS_PROTOCOL_LINE); + process_reset_environment(mp->process, env); + + /* Cleanup our env. */ + SMARTLIST_FOREACH(env, char *, x, tor_free(x)); + smartlist_free(env); + + /* Skip the argv[0] as we get that from process_new(argv[0]). */ + for (int i = 1; mp->argv[i] != NULL; ++i) + process_append_argument(mp->process, mp->argv[i]); + + if (process_exec(mp->process) != PROCESS_STATUS_RUNNING) { + log_warn(LD_CONFIG, "Managed proxy at '%s' failed at launch.", mp->argv[0]); return -1; } - log_info(LD_CONFIG, "Managed proxy at '%s' has spawned with PID '%d'.", - mp->argv[0], tor_process_get_pid(mp->process_handle)); - + log_info(LD_CONFIG, + "Managed proxy at '%s' has spawned with PID '%" PRIu64 "'.", + mp->argv[0], process_get_pid(mp->process)); mp->conf_state = PT_PROTO_LAUNCHED; return 0; @@ -615,10 +645,6 @@ pt_configure_remaining_proxies(void) STATIC int configure_proxy(managed_proxy_t *mp) { - int configuration_finished = 0; - smartlist_t *proxy_output = NULL; - enum stream_status stream_status = 0; - /* if we haven't launched the proxy yet, do it now */ if (mp->conf_state == PT_PROTO_INFANT) { if (launch_managed_proxy(mp) < 0) { /* launch fail */ @@ -629,45 +655,8 @@ configure_proxy(managed_proxy_t *mp) } tor_assert(mp->conf_state != PT_PROTO_INFANT); - tor_assert(mp->process_handle); - - proxy_output = - tor_get_lines_from_handle(tor_process_get_stdout_pipe(mp->process_handle), - &stream_status); - if (!proxy_output) { /* failed to get input from proxy */ - if (stream_status != IO_STREAM_EAGAIN) { /* bad stream status! */ - mp->conf_state = PT_PROTO_BROKEN; - log_warn(LD_GENERAL, "The communication stream of managed proxy '%s' " - "is '%s'. Most probably the managed proxy stopped running. " - "This might be a bug of the managed proxy, a bug of Tor, or " - "a misconfiguration. Please enable logging on your managed " - "proxy and check the logs for errors.", - mp->argv[0], stream_status_to_string(stream_status)); - } - - goto done; - } - - /* Handle lines. */ - SMARTLIST_FOREACH_BEGIN(proxy_output, const char *, line) { - handle_proxy_line(line, mp); - if (proxy_configuration_finished(mp)) - goto done; - } SMARTLIST_FOREACH_END(line); - - done: - /* if the proxy finished configuring, exit the loop. */ - if (proxy_configuration_finished(mp)) { - handle_finished_proxy(mp); - configuration_finished = 1; - } - - if (proxy_output) { - SMARTLIST_FOREACH(proxy_output, char *, cp, tor_free(cp)); - smartlist_free(proxy_output); - } - - return configuration_finished; + tor_assert(mp->process); + return mp->conf_state == PT_PROTO_COMPLETED; } /** Register server managed proxy <b>mp</b> transports to state */ @@ -748,8 +737,14 @@ managed_proxy_destroy(managed_proxy_t *mp, /* free the outgoing proxy URI */ tor_free(mp->proxy_uri); - tor_process_handle_destroy(mp->process_handle, also_terminate_process); - mp->process_handle = NULL; + /* do we want to terminate our process if it's still running? */ + if (also_terminate_process && mp->process) { + /* Note that we do not call process_free(mp->process) here because we let + * the exit handler in managed_proxy_exit_callback() return `true` which + * makes the process subsystem deallocate the process_t. */ + process_set_data(mp->process, NULL); + process_terminate(mp->process); + } tor_free(mp); } @@ -763,6 +758,9 @@ get_pt_proxy_uri(void) const or_options_t *options = get_options(); char *uri = NULL; + /* XXX: Currently TCPProxy is not supported in TOR_PT_PROXY because + * there isn't a standard URI scheme for some proxy protocols, such as + * haproxy. */ if (options->Socks4Proxy || options->Socks5Proxy || options->HTTPSProxy) { char addr[TOR_ADDR_BUF_LEN+1]; @@ -945,21 +943,16 @@ handle_proxy_line(const char *line, managed_proxy_t *mp) parse_proxy_error(line); goto err; - } else if (!strcmpstart(line, SPAWN_ERROR_MESSAGE)) { - /* managed proxy launch failed: parse error message to learn why. */ - int retval, child_state, saved_errno; - retval = tor_sscanf(line, SPAWN_ERROR_MESSAGE "%x/%x", - &child_state, &saved_errno); - if (retval == 2) { - log_warn(LD_GENERAL, - "Could not launch managed proxy executable at '%s' ('%s').", - mp->argv[0], strerror(saved_errno)); - } else { /* failed to parse error message */ - log_warn(LD_GENERAL,"Could not launch managed proxy executable at '%s'.", - mp->argv[0]); - } - mp->conf_state = PT_PROTO_FAILED_LAUNCH; + /* We check for the additional " " after the PROTO_LOG * PROTO_STATUS + * string to make sure we can later extend this big if/else-if table with + * something that begins with "LOG" without having to get the order right. + * */ + } else if (!strcmpstart(line, PROTO_LOG " ")) { + parse_log_line(line, mp); + return; + } else if (!strcmpstart(line, PROTO_STATUS " ")) { + parse_status_line(line, mp); return; } @@ -1182,6 +1175,121 @@ parse_proxy_error(const char *line) line+strlen(PROTO_PROXY_ERROR)+1); } +/** Parses a LOG <b>line</b> and emit log events accordingly. */ +STATIC void +parse_log_line(const char *line, managed_proxy_t *mp) +{ + tor_assert(line); + tor_assert(mp); + + config_line_t *values = NULL; + char *log_message = NULL; + + if (strlen(line) < (strlen(PROTO_LOG) + 1)) { + log_warn(LD_PT, "Managed proxy sent us a %s line " + "with missing argument.", PROTO_LOG); + goto done; + } + + const char *data = line + strlen(PROTO_LOG) + 1; + values = kvline_parse(data, KV_QUOTED); + + if (! values) { + log_warn(LD_PT, "Managed proxy \"%s\" wrote an invalid LOG message: %s", + mp->argv[0], data); + goto done; + } + + const config_line_t *severity = config_line_find(values, "SEVERITY"); + const config_line_t *message = config_line_find(values, "MESSAGE"); + + /* Check if we got a message. */ + if (! message) { + log_warn(LD_PT, "Managed proxy \"%s\" wrote a LOG line without " + "MESSAGE: %s", mp->argv[0], escaped(data)); + goto done; + } + + /* Check if severity is there and whether it's valid. */ + if (! severity) { + log_warn(LD_PT, "Managed proxy \"%s\" wrote a LOG line without " + "SEVERITY: %s", mp->argv[0], escaped(data)); + goto done; + } + + int log_severity = managed_proxy_severity_parse(severity->value); + + if (log_severity == -1) { + log_warn(LD_PT, "Managed proxy \"%s\" wrote a LOG line with an " + "invalid severity level: %s", + mp->argv[0], severity->value); + goto done; + } + + tor_log(log_severity, LD_PT, "Managed proxy \"%s\": %s", + mp->argv[0], message->value); + + /* Prepend the PT name. */ + config_line_prepend(&values, "PT", mp->argv[0]); + log_message = kvline_encode(values, KV_QUOTED); + + /* Emit control port event. */ + control_event_pt_log(log_message); + + done: + config_free_lines(values); + tor_free(log_message); +} + +/** Parses a STATUS <b>line</b> and emit control events accordingly. */ +STATIC void +parse_status_line(const char *line, managed_proxy_t *mp) +{ + tor_assert(line); + tor_assert(mp); + + config_line_t *values = NULL; + char *status_message = NULL; + + if (strlen(line) < (strlen(PROTO_STATUS) + 1)) { + log_warn(LD_PT, "Managed proxy sent us a %s line " + "with missing argument.", PROTO_STATUS); + goto done; + } + + const char *data = line + strlen(PROTO_STATUS) + 1; + + values = kvline_parse(data, KV_QUOTED); + + if (! values) { + log_warn(LD_PT, "Managed proxy \"%s\" wrote an invalid " + "STATUS message: %s", mp->argv[0], escaped(data)); + goto done; + } + + /* We check if we received the TRANSPORT parameter, which is the only + * *required* value. */ + const config_line_t *type = config_line_find(values, "TRANSPORT"); + + if (! type) { + log_warn(LD_PT, "Managed proxy \"%s\" wrote a STATUS line without " + "TRANSPORT: %s", mp->argv[0], escaped(data)); + goto done; + } + + /* Prepend the PT name. */ + config_line_prepend(&values, "PT", mp->argv[0]); + status_message = kvline_encode(values, KV_QUOTED); + + /* We have checked that TRANSPORT is there, we can now emit the STATUS event + * via the control port. */ + control_event_pt_status(status_message); + + done: + config_free_lines(values); + tor_free(status_message); +} + /** Return a newly allocated string that tor should place in * TOR_PT_SERVER_TRANSPORT_OPTIONS while configuring the server * manged proxy in <b>mp</b>. Return NULL if no such options are found. */ @@ -1199,7 +1307,7 @@ get_transport_options_for_server_proxy(const managed_proxy_t *mp) string. */ SMARTLIST_FOREACH_BEGIN(mp->transports_to_launch, const char *, transport) { smartlist_t *options_tmp_sl = NULL; - options_tmp_sl = get_options_for_server_transport(transport); + options_tmp_sl = pt_get_options_for_server_transport(transport); if (!options_tmp_sl) continue; @@ -1257,7 +1365,7 @@ get_bindaddr_for_server_proxy(const managed_proxy_t *mp) /** Return a newly allocated process_environment_t * for <b>mp</b>'s * process. */ -static process_environment_t * +static smartlist_t * create_managed_proxy_environment(const managed_proxy_t *mp) { const or_options_t *options = get_options(); @@ -1272,8 +1380,6 @@ create_managed_proxy_environment(const managed_proxy_t *mp) /* The final environment to be passed to mp. */ smartlist_t *merged_env_vars = get_current_process_environment_variables(); - process_environment_t *env; - { char *state_tmp = get_datadir_fname("pt_state/"); /* XXX temp */ smartlist_add_asprintf(envs, "TOR_PT_STATE_LOCATION=%s", state_tmp); @@ -1337,8 +1443,10 @@ create_managed_proxy_environment(const managed_proxy_t *mp) smartlist_add_asprintf(envs, "TOR_PT_EXTENDED_SERVER_PORT=%s", ext_or_addrport_tmp); } - smartlist_add_asprintf(envs, "TOR_PT_AUTH_COOKIE_FILE=%s", - cookie_file_loc); + if (cookie_file_loc) { + smartlist_add_asprintf(envs, "TOR_PT_AUTH_COOKIE_FILE=%s", + cookie_file_loc); + } tor_free(ext_or_addrport_tmp); tor_free(cookie_file_loc); @@ -1346,11 +1454,6 @@ create_managed_proxy_environment(const managed_proxy_t *mp) } else { smartlist_add_asprintf(envs, "TOR_PT_EXTENDED_SERVER_PORT="); } - - /* All new versions of tor will keep stdin open, so PTs can use it - * as a reliable termination detection mechanism. - */ - smartlist_add_asprintf(envs, "TOR_PT_EXIT_ON_STDIN_CLOSE=1"); } else { /* If ClientTransportPlugin has a HTTPS/SOCKS proxy configured, set the * TOR_PT_PROXY line. @@ -1361,19 +1464,50 @@ create_managed_proxy_environment(const managed_proxy_t *mp) } } + /* All new versions of tor will keep stdin open, so PTs can use it + * as a reliable termination detection mechanism. + */ + smartlist_add_asprintf(envs, "TOR_PT_EXIT_ON_STDIN_CLOSE=1"); + + /* Specify which IPv4 and IPv6 addresses the PT should make its outgoing + * connections from. See: https://bugs.torproject.org/5304 for more + * information about this. */ + { + /* Set TOR_PT_OUTBOUND_BIND_ADDRESS_V4. */ + const tor_addr_t *ipv4_addr = managed_proxy_outbound_address(options, + AF_INET); + + /* managed_proxy_outbound_address() only returns a non-NULL value if + * tor_addr_is_null() was false, which means we don't have to check that + * here. */ + if (ipv4_addr) { + char *ipv4_addr_str = tor_addr_to_str_dup(ipv4_addr); + smartlist_add_asprintf(envs, + "TOR_PT_OUTBOUND_BIND_ADDRESS_V4=%s", + ipv4_addr_str); + tor_free(ipv4_addr_str); + } + + /* Set TOR_PT_OUTBOUND_BIND_ADDRESS_V6. */ + const tor_addr_t *ipv6_addr = managed_proxy_outbound_address(options, + AF_INET6); + if (ipv6_addr) { + char *ipv6_addr_str = tor_addr_to_str_dup(ipv6_addr); + smartlist_add_asprintf(envs, + "TOR_PT_OUTBOUND_BIND_ADDRESS_V6=[%s]", + ipv6_addr_str); + tor_free(ipv6_addr_str); + } + } + SMARTLIST_FOREACH_BEGIN(envs, const char *, env_var) { set_environment_variable_in_smartlist(merged_env_vars, env_var, tor_free_, 1); } SMARTLIST_FOREACH_END(env_var); - env = process_environment_make(merged_env_vars); - smartlist_free(envs); - SMARTLIST_FOREACH(merged_env_vars, void *, x, tor_free(x)); - smartlist_free(merged_env_vars); - - return env; + return merged_env_vars; } /** Create and return a new managed proxy for <b>transport</b> using @@ -1392,6 +1526,7 @@ managed_proxy_create(const smartlist_t *with_transport_list, mp->argv = proxy_argv; mp->transports = smartlist_new(); mp->proxy_uri = get_pt_proxy_uri(); + mp->process = process_new(proxy_argv[0]); mp->transports_to_launch = smartlist_new(); SMARTLIST_FOREACH(with_transport_list, const char *, transport, @@ -1561,17 +1696,26 @@ pt_get_extra_info_descriptor_string(void) SMARTLIST_FOREACH_BEGIN(mp->transports, const transport_t *, t) { char *transport_args = NULL; + const char *addrport = NULL; /* If the transport proxy returned "0.0.0.0" as its address, and * we know our external IP address, use it. Otherwise, use the * returned address. */ - const char *addrport = NULL; - uint32_t external_ip_address = 0; - if (tor_addr_is_null(&t->addr) && - router_pick_published_address(get_options(), - &external_ip_address, 0) >= 0) { + if (tor_addr_is_null(&t->addr)) { tor_addr_t addr; - tor_addr_from_ipv4h(&addr, external_ip_address); + /* Attempt to find the IPv4 and then attempt to find the IPv6 if we + * can't find it. */ + bool found = relay_find_addr_to_publish(get_options(), AF_INET, + RELAY_FIND_ADDR_NO_FLAG, + &addr); + if (!found) { + found = relay_find_addr_to_publish(get_options(), AF_INET6, + RELAY_FIND_ADDR_NO_FLAG, &addr); + } + if (!found) { + log_err(LD_PT, "Unable to find address for transport %s", t->name); + continue; + } addrport = fmt_addrport(&addr, t->port); } else { addrport = fmt_addrport(&t->addr, t->port); @@ -1736,3 +1880,138 @@ tor_escape_str_for_pt_args(const char *string, const char *chars_to_escape) return new_string; } + +/** Callback function that is called when our PT process have data on its + * stdout. Our process can be found in <b>process</b>, the data can be found in + * <b>line</b> and the length of our line is given in <b>size</b>. */ +STATIC void +managed_proxy_stdout_callback(process_t *process, + const char *line, + size_t size) +{ + tor_assert(process); + tor_assert(line); + + (void)size; + + managed_proxy_t *mp = process_get_data(process); + + if (mp == NULL) + return; + + handle_proxy_line(line, mp); + + if (proxy_configuration_finished(mp)) + handle_finished_proxy(mp); +} + +/** Callback function that is called when our PT process have data on its + * stderr. Our process can be found in <b>process</b>, the data can be found in + * <b>line</b> and the length of our line is given in <b>size</b>. */ +STATIC void +managed_proxy_stderr_callback(process_t *process, + const char *line, + size_t size) +{ + tor_assert(process); + tor_assert(line); + + (void)size; + + managed_proxy_t *mp = process_get_data(process); + + if (BUG(mp == NULL)) + return; + + log_info(LD_PT, + "Managed proxy at '%s' reported via standard error: %s", + mp->argv[0], line); +} + +/** Callback function that is called when our PT process terminates. The + * process exit code can be found in <b>exit_code</b> and our process can be + * found in <b>process</b>. Returns true iff we want the process subsystem to + * free our process_t handle for us. */ +STATIC bool +managed_proxy_exit_callback(process_t *process, process_exit_code_t exit_code) +{ + tor_assert(process); + + log_warn(LD_PT, + "Pluggable Transport process terminated with status code %" PRIu64, + exit_code); + + /* Returning true here means that the process subsystem will take care of + * calling process_free() on our process_t. */ + return true; +} + +/** Returns a valid integer log severity level from <b>severity</b> that + * is compatible with Tor's logging functions. Returns <b>-1</b> on + * error. */ +STATIC int +managed_proxy_severity_parse(const char *severity) +{ + tor_assert(severity); + + /* Slightly different than log.c's parse_log_level :-( */ + if (! strcmp(severity, "debug")) + return LOG_DEBUG; + + if (! strcmp(severity, "info")) + return LOG_INFO; + + if (! strcmp(severity, "notice")) + return LOG_NOTICE; + + if (! strcmp(severity, "warning")) + return LOG_WARN; + + if (! strcmp(severity, "error")) + return LOG_ERR; + + return -1; +} + +/** Return the outbound address from the given <b>family</b>. Returns NULL if + * the user haven't specified a specific outbound address in either + * OutboundBindAddress or OutboundBindAddressPT. */ +STATIC const tor_addr_t * +managed_proxy_outbound_address(const or_options_t *options, sa_family_t family) +{ + tor_assert(options); + + const tor_addr_t *address = NULL; + int family_index; + + switch (family) { + case AF_INET: + family_index = 0; + break; + case AF_INET6: + family_index = 1; + break; + default: + /* LCOV_EXCL_START */ + tor_assert_unreached(); + return NULL; + /* LCOV_EXCL_STOP */ + } + + /* We start by checking if the user specified an address in + * OutboundBindAddressPT. */ + address = &options->OutboundBindAddresses[OUTBOUND_ADDR_PT][family_index]; + + if (! tor_addr_is_null(address)) + return address; + + /* We fallback to check if the user specified an address in + * OutboundBindAddress. */ + address = &options->OutboundBindAddresses[OUTBOUND_ADDR_ANY][family_index]; + + if (! tor_addr_is_null(address)) + return address; + + /* The user have not specified a preference for outgoing connections. */ + return NULL; +} diff --git a/src/feature/client/transports.h b/src/feature/client/transports.h index e2fa45828f..47b118e77b 100644 --- a/src/feature/client/transports.h +++ b/src/feature/client/transports.h @@ -1,6 +1,6 @@ /* Copyright (c) 2003-2004, Roger Dingledine * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -11,6 +11,8 @@ #ifndef TOR_TRANSPORTS_H #define TOR_TRANSPORTS_H +#include "lib/process/process.h" + /** Represents a pluggable transport used by a bridge. */ typedef struct transport_t { /** SOCKS version: One of PROXY_SOCKS4, PROXY_SOCKS5. */ @@ -39,6 +41,7 @@ void transport_free_(transport_t *transport); #define transport_free(tr) FREE_AND_NULL(transport_t, transport_free_, (tr)) MOCK_DECL(transport_t*, transport_get_by_name, (const char *name)); +bool managed_proxy_has_transport(const char *transport_name); MOCK_DECL(void, pt_kickstart_proxy, (const smartlist_t *transport_list, char **proxy_argv, @@ -81,7 +84,7 @@ enum pt_proto_state { PT_PROTO_FAILED_LAUNCH /* failed while launching */ }; -struct process_handle_t; +struct process_t; /** Structure containing information of a managed proxy. */ typedef struct { @@ -94,10 +97,8 @@ typedef struct { int is_server; /* is it a server proxy? */ - /* A pointer to the process handle of this managed proxy. */ - struct process_handle_t *process_handle; - - int pid; /* The Process ID this managed proxy is using. */ + /* A pointer to the process of this managed proxy. */ + struct process_t *process; /** Boolean: We are re-parsing our config, and we are going to * remove this managed proxy if we don't find it any transport @@ -128,6 +129,8 @@ STATIC int parse_version(const char *line, managed_proxy_t *mp); STATIC void parse_env_error(const char *line); STATIC void parse_proxy_error(const char *line); STATIC void handle_proxy_line(const char *line, managed_proxy_t *mp); +STATIC void parse_log_line(const char *line, managed_proxy_t *mp); +STATIC void parse_status_line(const char *line, managed_proxy_t *mp); STATIC char *get_transport_options_for_server_proxy(const managed_proxy_t *mp); STATIC void managed_proxy_destroy(managed_proxy_t *mp, @@ -142,6 +145,14 @@ STATIC char* get_pt_proxy_uri(void); STATIC void free_execve_args(char **arg); +STATIC void managed_proxy_stdout_callback(process_t *, const char *, size_t); +STATIC void managed_proxy_stderr_callback(process_t *, const char *, size_t); +STATIC bool managed_proxy_exit_callback(process_t *, process_exit_code_t); + +STATIC int managed_proxy_severity_parse(const char *); +STATIC const tor_addr_t *managed_proxy_outbound_address(const or_options_t *, + sa_family_t); + #endif /* defined(PT_PRIVATE) */ #endif /* !defined(TOR_TRANSPORTS_H) */ |