Merge branch 'prop271_030_v1_squashed'

This commit is contained in:
Nick Mathewson 2016-12-16 11:20:59 -05:00
commit 2cee38f76a
47 changed files with 8132 additions and 1298 deletions

View File

@ -2122,3 +2122,11 @@ tor_addr_port_new(const tor_addr_t *addr, uint16_t port)
return ap;
}
/** Return true iff <a>a</b> and <b>b</b> are the same address and port */
int
tor_addr_port_eq(const tor_addr_port_t *a,
const tor_addr_port_t *b)
{
return tor_addr_eq(&a->addr, &b->addr) && a->port == b->port;
}

View File

@ -342,6 +342,8 @@ get_interface_address_list(int severity, int include_internal)
}
tor_addr_port_t *tor_addr_port_new(const tor_addr_t *addr, uint16_t port);
int tor_addr_port_eq(const tor_addr_port_t *a,
const tor_addr_port_t *b);
#ifdef ADDRESS_PRIVATE
MOCK_DECL(smartlist_t *,get_interface_addresses_raw,(int severity,

View File

@ -132,6 +132,24 @@ smartlist_remove(smartlist_t *sl, const void *element)
}
}
/** As <b>smartlist_remove</b>, but do not change the order of
* any elements not removed */
void
smartlist_remove_keeporder(smartlist_t *sl, const void *element)
{
int i, j, num_used_orig = sl->num_used;
if (element == NULL)
return;
for (i=j=0; j < num_used_orig; ++j) {
if (sl->list[j] == element) {
--sl->num_used;
} else {
sl->list[i++] = sl->list[j];
}
}
}
/** If <b>sl</b> is nonempty, remove and return the final element. Otherwise,
* return NULL. */
void *

View File

@ -33,6 +33,7 @@ void smartlist_clear(smartlist_t *sl);
void smartlist_add(smartlist_t *sl, void *element);
void smartlist_add_all(smartlist_t *sl, const smartlist_t *s2);
void smartlist_remove(smartlist_t *sl, const void *element);
void smartlist_remove_keeporder(smartlist_t *sl, const void *element);
void *smartlist_pop_last(smartlist_t *sl);
void smartlist_reverse(smartlist_t *sl);
void smartlist_string_remove(smartlist_t *sl, const char *element);

View File

@ -1177,7 +1177,7 @@ static const char *domain_list[] = {
"GENERAL", "CRYPTO", "NET", "CONFIG", "FS", "PROTOCOL", "MM",
"HTTP", "APP", "CONTROL", "CIRC", "REND", "BUG", "DIR", "DIRSERV",
"OR", "EDGE", "ACCT", "HIST", "HANDSHAKE", "HEARTBEAT", "CHANNEL",
"SCHED", NULL
"SCHED", "GUARD", NULL
};
/** Return a bitmask for the log domain for which <b>domain</b> is the name,

View File

@ -99,8 +99,10 @@
#define LD_CHANNEL (1u<<21)
/** Scheduler */
#define LD_SCHED (1u<<22)
/** Guard nodes */
#define LD_GUARD (1u<<23)
/** Number of logging domains in the code. */
#define N_LOGGING_DOMAINS 23
#define N_LOGGING_DOMAINS 24
/** This log message is not safe to send to a callback-based logger
* immediately. Used as a flag, not a log domain. */

View File

@ -1803,17 +1803,26 @@ format_iso_time_nospace_usec(char *buf, const struct timeval *tv)
/** Given an ISO-formatted UTC time value (after the epoch) in <b>cp</b>,
* parse it and store its value in *<b>t</b>. Return 0 on success, -1 on
* failure. Ignore extraneous stuff in <b>cp</b> after the end of the time
* string, unless <b>strict</b> is set. */
* string, unless <b>strict</b> is set. If <b>nospace</b> is set,
* expect the YYYY-MM-DDTHH:MM:SS format. */
int
parse_iso_time_(const char *cp, time_t *t, int strict)
parse_iso_time_(const char *cp, time_t *t, int strict, int nospace)
{
struct tm st_tm;
unsigned int year=0, month=0, day=0, hour=0, minute=0, second=0;
int n_fields;
char extra_char;
n_fields = tor_sscanf(cp, "%u-%2u-%2u %2u:%2u:%2u%c", &year, &month,
&day, &hour, &minute, &second, &extra_char);
if (strict ? (n_fields != 6) : (n_fields < 6)) {
char extra_char, separator_char;
n_fields = tor_sscanf(cp, "%u-%2u-%2u%c%2u:%2u:%2u%c",
&year, &month, &day,
&separator_char,
&hour, &minute, &second, &extra_char);
if (strict ? (n_fields != 7) : (n_fields < 7)) {
char *esc = esc_for_log(cp);
log_warn(LD_GENERAL, "ISO time %s was unparseable", esc);
tor_free(esc);
return -1;
}
if (separator_char != (nospace ? 'T' : ' ')) {
char *esc = esc_for_log(cp);
log_warn(LD_GENERAL, "ISO time %s was unparseable", esc);
tor_free(esc);
@ -1855,7 +1864,16 @@ parse_iso_time_(const char *cp, time_t *t, int strict)
int
parse_iso_time(const char *cp, time_t *t)
{
return parse_iso_time_(cp, t, 1);
return parse_iso_time_(cp, t, 1, 0);
}
/**
* As parse_iso_time, but parses a time encoded by format_iso_time_nospace().
*/
int
parse_iso_time_nospace(const char *cp, time_t *t)
{
return parse_iso_time_(cp, t, 1, 1);
}
/** Given a <b>date</b> in one of the three formats allowed by HTTP (ugh),

View File

@ -255,8 +255,9 @@ void format_local_iso_time(char *buf, time_t t);
void format_iso_time(char *buf, time_t t);
void format_iso_time_nospace(char *buf, time_t t);
void format_iso_time_nospace_usec(char *buf, const struct timeval *tv);
int parse_iso_time_(const char *cp, time_t *t, int strict);
int parse_iso_time_(const char *cp, time_t *t, int strict, int nospace);
int parse_iso_time(const char *buf, time_t *t);
int parse_iso_time_nospace(const char *cp, time_t *t);
int parse_http_time(const char *buf, struct tm *tm);
int format_time_interval(char *out, size_t out_len, long interval);

866
src/or/bridges.c Normal file
View File

@ -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;
}

66
src/or/bridges.h Normal file
View File

@ -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

View File

@ -2578,6 +2578,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 +2590,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 */

View File

@ -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) {

View File

@ -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();

View File

@ -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,38 @@ circuit_send_next_onion_skin(origin_circuit_t *circ)
memset(&ec, 0, sizeof(ec));
if (!hop) {
/* done building the circuit. whew. */
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) {
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,6 +1039,7 @@ circuit_send_next_onion_skin(origin_circuit_t *circ)
pathbias_count_build_success(circ);
circuit_rep_hist_note_result(circ);
if (is_usable_for_streams)
circuit_has_opened(circ); /* do other actions as necessary */
if (!have_completed_a_circuit() && !circ->build_state->onehop_tunnel) {
@ -2227,9 +2267,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 +2295,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 +2306,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 +2320,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 +2381,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 +2565,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 +2577,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 +2680,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);
}

View File

@ -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);

View File

@ -63,8 +63,9 @@
#include "connection_edge.h"
#include "connection_or.h"
#include "control.h"
#include "hs_circuitmap.h"
#include "entrynodes.h"
#include "main.h"
#include "hs_circuitmap.h"
#include "hs_common.h"
#include "networkstatus.h"
#include "nodelist.h"
@ -85,9 +86,17 @@
/** 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;
@ -426,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);
@ -436,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;
}
@ -514,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);
@ -521,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))
{
@ -530,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)
@ -539,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);
@ -769,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;
@ -826,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);
@ -833,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(&ocirc->guard_state);
}
circuit_guard_state_free(ocirc->guard_state);
circuit_clear_cpath(ocirc);
crypto_pk_free(ocirc->intro_key);
@ -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);
@ -1501,6 +1573,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
@ -1695,7 +1798,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 */
@ -1708,7 +1812,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);
}
@ -2230,7 +2336,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);

View File

@ -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);
@ -73,6 +74,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);

View File

@ -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. "

View File

@ -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);

View File

@ -60,6 +60,7 @@
#define CONFIG_PRIVATE
#include "or.h"
#include "bridges.h"
#include "compat.h"
#include "addressmap.h"
#include "channel.h"
@ -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"),
@ -1557,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.
@ -1576,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 */
{
@ -1852,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 &&
@ -1868,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;
@ -2050,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.
@ -2959,6 +3009,13 @@ options_validate(or_options_t *old_options, or_options_t *options,
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") ||

View File

@ -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)) {

View File

@ -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))

View File

@ -36,6 +36,7 @@
#include "or.h"
#include "addressmap.h"
#include "bridges.h"
#include "buffers.h"
#include "channel.h"
#include "channeltls.h"
@ -2595,6 +2596,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
@ -3378,7 +3381,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 +4041,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);

View File

@ -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);
@ -457,7 +460,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 +470,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 +551,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 +560,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*/
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);
} else
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 +619,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 +644,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 +679,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 +790,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 +846,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 +872,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 +908,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 +1160,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 +1175,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 +1271,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 +1319,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
@ -2539,6 +2579,21 @@ connection_dir_process_inbuf(dir_connection_t *conn)
tor_assert(conn);
tor_assert(conn->base_.type == CONN_TYPE_DIR);
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;
}
/* Directory clients write, then read data until they receive EOF;
* directory servers read data until they get an HTTP command, then
* write their response (when it's finished flushing, they mark for

View File

@ -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);

File diff suppressed because it is too large Load Diff

View File

@ -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,535 @@ 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. */
/**@}*/
} entry_guard_t;
} 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;
/**@}*/
#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);
void circuit_guard_state_free(circuit_guard_state_t *state);
int entry_guard_pick_for_circuit(guard_selection_t *gs,
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
/**
* 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_days(void);
STATIC int get_guard_confirmed_min_lifetime_days(void);
STATIC int get_n_primary_guards(void);
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,
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);
#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 +624,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 +648,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 +662,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 +692,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

View File

@ -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 \
@ -133,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 \

View File

@ -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"
@ -979,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)
@ -1376,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);
}
@ -1396,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.
@ -3119,6 +3130,7 @@ tor_free_all(int postfork)
control_free_all();
sandbox_free_getaddrinfo_cache();
protover_free_all();
bridges_free_all();
if (!postfork) {
config_free_all();
or_state_free_all();

View File

@ -38,6 +38,7 @@
#define NETWORKSTATUS_PRIVATE
#include "or.h"
#include "bridges.h"
#include "channel.h"
#include "circuitmux.h"
#include "circuitmux_ewma.h"
@ -2345,6 +2346,25 @@ networkstatus_get_param(const networkstatus_t *ns, const char *param_name,
default_val, min_val, max_val);
}
/**
* 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'

View File

@ -115,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);

View File

@ -439,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
@ -1783,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. */
@ -3152,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;
@ -4571,6 +4588,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. */
@ -4594,9 +4619,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

View File

@ -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 "

View File

@ -3658,7 +3658,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);

View File

@ -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;
@ -4946,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);

View File

@ -5123,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;
}

View File

@ -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. */

View File

@ -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);

View File

@ -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),

View File

@ -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"

View File

@ -11,6 +11,7 @@
#include "or.h"
#include "address.h"
#include "addressmap.h"
#include "bridges.h"
#include "circuitmux_ewma.h"
#include "circuitbuild.h"
#include "config.h"

View File

@ -882,6 +882,46 @@ test_container_strmap(void *arg)
tor_free(v105);
}
static void
test_container_smartlist_remove(void *arg)
{
(void) arg;
int array[5];
smartlist_t *sl = smartlist_new();
int i,j;
for (j=0; j < 2; ++j)
for (i=0; i < 5; ++i)
smartlist_add(sl, &array[i]);
smartlist_remove(sl, &array[0]);
smartlist_remove(sl, &array[3]);
smartlist_remove(sl, &array[4]);
tt_assert(! smartlist_contains(sl, &array[0]));
tt_assert(smartlist_contains(sl, &array[1]));
tt_assert(smartlist_contains(sl, &array[2]));
tt_assert(! smartlist_contains(sl, &array[3]));
tt_assert(! smartlist_contains(sl, &array[4]));
tt_int_op(smartlist_len(sl), OP_EQ, 4);
smartlist_clear(sl);
for (j=0; j < 2; ++j)
for (i=0; i < 5; ++i)
smartlist_add(sl, &array[i]);
smartlist_remove_keeporder(sl, &array[0]);
smartlist_remove_keeporder(sl, &array[3]);
smartlist_remove_keeporder(sl, &array[4]);
tt_int_op(smartlist_len(sl), OP_EQ, 4);
tt_ptr_op(smartlist_get(sl, 0), OP_EQ, &array[1]);
tt_ptr_op(smartlist_get(sl, 1), OP_EQ, &array[2]);
tt_ptr_op(smartlist_get(sl, 2), OP_EQ, &array[1]);
tt_ptr_op(smartlist_get(sl, 3), OP_EQ, &array[2]);
done:
smartlist_free(sl);
}
/** Run unit tests for getting the median of a list. */
static void
test_container_order_functions(void *arg)
@ -1239,6 +1279,7 @@ struct testcase_t container_tests[] = {
CONTAINER_LEGACY(smartlist_digests),
CONTAINER_LEGACY(smartlist_join),
CONTAINER_LEGACY(smartlist_pos),
CONTAINER(smartlist_remove, 0),
CONTAINER(smartlist_ints_eq, 0),
CONTAINER_LEGACY(bitarray),
CONTAINER_LEGACY(digestset),

View File

@ -3,6 +3,7 @@
#define CONTROL_PRIVATE
#include "or.h"
#include "bridges.h"
#include "control.h"
#include "entrynodes.h"
#include "networkstatus.h"

View File

@ -23,6 +23,7 @@
#include "directory.h"
#include "dirserv.h"
#include "dirvote.h"
#include "entrynodes.h"
#include "hibernate.h"
#include "memarea.h"
#include "networkstatus.h"
@ -1493,6 +1494,15 @@ test_dir_param_voting(void *arg)
tt_int_op(-8,OP_EQ, networkstatus_get_param(&vote4, "ab", -12, -100, -8));
tt_int_op(0,OP_EQ, networkstatus_get_param(&vote4, "foobar", 0, -100, 8));
tt_int_op(100,OP_EQ, networkstatus_get_overridable_param(
&vote4, -1, "x-yz", 50, 0, 300));
tt_int_op(30,OP_EQ, networkstatus_get_overridable_param(
&vote4, 30, "x-yz", 50, 0, 300));
tt_int_op(0,OP_EQ, networkstatus_get_overridable_param(
&vote4, -101, "foobar", 0, -100, 8));
tt_int_op(-99,OP_EQ, networkstatus_get_overridable_param(
&vote4, -99, "foobar", 0, -100, 8));
smartlist_add(votes, &vote1);
/* Do the first tests without adding all the other votes, for
@ -4402,7 +4412,8 @@ directory_initiate_command_routerstatus, (const routerstatus_t *status,
const char *resource,
const char *payload,
size_t payload_len,
time_t if_modified_since));
time_t if_modified_since,
circuit_guard_state_t *guardstate));
static void
test_dir_should_not_init_request_to_ourselves(void *data)
@ -4509,7 +4520,8 @@ NS(directory_initiate_command_routerstatus)(const routerstatus_t *status,
const char *resource,
const char *payload,
size_t payload_len,
time_t if_modified_since)
time_t if_modified_since,
circuit_guard_state_t *guardstate)
{
(void)status;
(void)dir_purpose;
@ -4519,6 +4531,7 @@ NS(directory_initiate_command_routerstatus)(const routerstatus_t *status,
(void)payload;
(void)payload_len;
(void)if_modified_since;
(void)guardstate;
CALLED(directory_initiate_command_routerstatus)++;
}

File diff suppressed because it is too large Load Diff

View File

@ -204,6 +204,7 @@ mock_usable_consensus_flavor(void)
return mock_usable_consensus_flavor_value;
}
#ifdef ENABLE_LEGACY_GUARD_ALGORITHM
static smartlist_t *mock_is_guard_list = NULL;
static int
@ -250,6 +251,7 @@ clear_mock_guard_list(void)
mock_is_guard_list = NULL;
}
}
#endif
static void
test_router_pick_directory_server_impl(void *arg)
@ -271,7 +273,9 @@ test_router_pick_directory_server_impl(void *arg)
(void)arg;
MOCK(usable_consensus_flavor, mock_usable_consensus_flavor);
#ifdef ENABLE_LEGACY_GUARD_ALGORITHM
MOCK(is_node_used_as_guard, mock_is_node_used_as_guard);
#endif
/* With no consensus, we must be bootstrapping, regardless of time or flavor
*/
@ -384,6 +388,7 @@ test_router_pick_directory_server_impl(void *arg)
node_router1->is_valid = 1;
node_router3->is_valid = 1;
#ifdef ENABLE_LEGACY_GUARD_ALGORITHM
flags |= PDS_FOR_GUARD;
mark_node_used_as_guard(node_router1);
mark_node_used_as_guard(node_router2);
@ -397,8 +402,10 @@ test_router_pick_directory_server_impl(void *arg)
rs = NULL;
mark_node_unused_as_guard(node_router2);
mark_node_unused_as_guard(node_router3);
#endif
/* One not valid, one guard. This should leave one remaining */
#ifdef ENABLE_LEGACY_GUARD_ALGORITHM
node_router1->is_valid = 0;
mark_node_used_as_guard(node_router2);
rs = router_pick_directory_server_impl(V3_DIRINFO, flags, NULL);
@ -407,6 +414,7 @@ test_router_pick_directory_server_impl(void *arg)
rs = NULL;
node_router1->is_valid = 1;
mark_node_unused_as_guard(node_router2);
#endif
/* Manipulate overloaded */
@ -469,8 +477,10 @@ test_router_pick_directory_server_impl(void *arg)
done:
UNMOCK(usable_consensus_flavor);
#ifdef ENABLE_LEGACY_GUARD_ALGORITHM
UNMOCK(is_node_used_as_guard);
clear_mock_guard_list();
#endif
if (router1_id)
tor_free(router1_id);

View File

@ -1059,6 +1059,23 @@ test_util_time(void *arg)
tt_int_op(-1,OP_EQ, parse_iso_time("2004-08-04 00:48:22.100", &t_res));
tt_int_op(-1,OP_EQ, parse_iso_time("2004-08-04 00:48:22XYZ", &t_res));
/* but... that _is_ acceptable if we aren't being strict. */
t_res = 0;
i = parse_iso_time_("2004-08-04 00:48:22XYZ", &t_res, 0, 0);
tt_int_op(0,OP_EQ, i);
tt_int_op(t_res,OP_EQ, (time_t)1091580502UL);
/* try nospace variant. */
t_res = 0;
i = parse_iso_time_nospace("2004-08-04T00:48:22", &t_res);
tt_int_op(0,OP_EQ, i);
tt_int_op(t_res,OP_EQ, (time_t)1091580502UL);
tt_int_op(-1,OP_EQ, parse_iso_time("2004-08-04T00:48:22", &t_res));
tt_int_op(-1,OP_EQ, parse_iso_time_nospace("2004-08-04 00:48:22", &t_res));
tt_int_op(-1,OP_EQ, parse_iso_time("2004-08-04x00:48:22", &t_res));
tt_int_op(-1,OP_EQ, parse_iso_time_nospace("2004-08-04x00:48:22", &t_res));
/* Test tor_gettimeofday */
end.tv_sec = 4;