Merge maint-0.2.2 for the bug1090-part1-squashed branch

Resolved conflicts in:
	doc/tor.1.txt
	src/or/circuitbuild.c
	src/or/circuituse.c
	src/or/connection_edge.c
	src/or/connection_edge.h
	src/or/directory.c
	src/or/rendclient.c
	src/or/routerlist.c
	src/or/routerlist.h

These were mostly releated to the routerinfo_t->node_t conversion.
This commit is contained in:
Nick Mathewson 2011-04-27 14:36:30 -04:00
commit 8b686d98c4
21 changed files with 715 additions and 203 deletions

73
changes/bug1090-general Normal file
View File

@ -0,0 +1,73 @@
o Major features and bugfixes (node selection)
- Revise and unify the meaning of the ExitNodes, EntryNodes,
ExcludeEntryNodes, ExcludeExitNodes, ExcludeNodes, and
StrictNodes options. Previously, we had been ambiguous in
describing what counted as an "exit" node, and what operations
exactly "StrictNodes 0" would permit. This created confusion
when people saw nodes built through unexpected circuits, and
made it hard to tell real bugs from surprises. We now stipulate
that the intended behavior is:
. "Exit", in the context of ExitNodes and ExcludeExitNodes,
means a node that delivers user traffic outside the Tor
network.
. "Entry", in the context of EntryNodes and ExcludeEntryNodes,
means a node used as the first hop of a multihop circuit:
it doesn't include direct connections to directory servers.
. "ExcludeNodes" applies to all nodes.
. "StrictNodes" changes the behavior of ExcludeNodes only.
When StrictNodes is set, Tor should avoid all nodes listed
in ExcludeNodes, even when it will make user requests
fail. When StrictNodes is *not* set, then Tor should
follow ExcludeNodes whenever it can, except when it must
use an excluded node to perform self-tests, connect to a
hidden service, provide a hidden service, fulfill a .exit
request, upload directory information, or fetch directory
information.
Collectively, the changes to implement the behavior are a fix for
bug 1090.
- ExcludeNodes now takes precedence over EntryNodes and ExitNodes:
if a node is listed in both, it's treated as excluded.
- ExcludeNodes now applies to directory nodes: as a preference if
StrictNodes is 0, or an absolute requirement if StrictNodes is 1.
(Don't exclude all the directory authorities and set StrictNodes
to 1 unless you really want your Tor to break.)
- ExcludeNodes and ExcludeExitNodes now override exit enclaving.
- ExcludeExitNodes now overrides .exit requests.
- We don't use bridges from ExcludeNodes.
- When StrictNodes is 1:
. We now apply ExcludeNodes to hidden service introduction points
and to rendezvous points selected by hidden service users.
This can make your hidden service less reliable: use it with
caution!
. If we have used ExcludeNodes on ourself, do not try self-tests.
. If we have excluded all the directory authorities, we will
not even try to upload our descriptor if we're a server.
. Do not honor .exit requests to an excluded node.
- Remove a misfeature that caused us to ignore the Fast/Stable flags
if ExitNodes was set. Bugfix on 0.2.2.7-alpha.
- When the set of permitted nodes changes, we now remove any
mappings introduced via TrackExitHosts to now-excluded nodes.
Bugfix on 0.1.0.1-rc.
- We never cannibalize a circuit that had excluded nodes on it,
even if StrictNodes is 0. Bugfix on 0.1.0.1-rc.
- Improve log messages related to excluded nodes.
- Revert a change where we would be laxer about attaching streams to
circuits than when building the circuits. This was meant to
prevent a set of bugs where streams were never attachable, but our
improved code here should make this unnecessary. Bugfix on
0.2.2.7-alpha.

View File

@ -0,0 +1,5 @@
o Minor features:
- Keep track of how many times we launch a new circuit to handle
a given stream. Too many launches could indicate an inconsistency
between our "launch a circuit to handle this stream" logic and our
"attach our stream to one of the available circuits" logic.

View File

@ -0,0 +1,7 @@
o Minor features:
- If ExitNodes is set, still pay attention to the Fast/Stable
status of exits when picking exit nodes. (We used to ignore
these flags when ExitNodes was set, on the grounds that people
who set exitnodes wanted all of those nodes to get used, but
with the ability to pick exits by country and IP range, this
doesn't necessarily make sense any more.)

View File

@ -507,32 +507,73 @@ The following options are useful only for clients (that is, if
**ExcludeNodes** __node__,__node__,__...__::
A list of identity fingerprints, nicknames, country codes and address
patterns of nodes to never use when building a circuit. (Example:
ExcludeNodes SlowServer, $ EFFFFFFFFFFFFFFF, \{cc}, 255.254.0.0/8)
patterns of nodes to avoid when building a circuit.
(Example:
ExcludeNodes SlowServer, $ EFFFFFFFFFFFFFFF, \{cc}, 255.254.0.0/8) +
+
By default, this option is treated as a preference that Tor is allowed
to override in order to keep working.
For example, if you try to connect to a hidden service,
but you have excluded all of the hidden service's introduction points,
Tor will connect to one of them anyway. If you do not want this
behavior, set the StrictNodes option (documented below). +
+
Note also that if you are a relay, this (and the other node selection
options below) only affects your own circuits that Tor builds for you.
Clients can still build circuits through you to any node. Controllers
can tell Tor to build circuits through any node.
**ExcludeExitNodes** __node__,__node__,__...__::
A list of identity fingerprints, nicknames, country codes and address
patterns of nodes to never use when picking an exit node. Note that any
patterns of nodes to never use when picking an exit node---that is, a
node that delivers traffic for you outside the Tor network. Note that any
node listed in ExcludeNodes is automatically considered to be part of this
list.
**EntryNodes** __node__,__node__,__...__::
A list of identity fingerprints, nicknames, country codes and address
patterns of nodes to use for the first hop in normal circuits. These are
treated only as preferences unless StrictNodes (see below) is also set.
list too. See also the caveats on the "ExitNodes" option below
**ExitNodes** __node__,__node__,__...__::
A list of identity fingerprints, nicknames, country codes and address
patterns of nodes to use for the last hop in normal exit circuits. These
are treated only as preferences unless StrictNodes (see below) is also set.
patterns of nodes to use as exit node---that is, a
node that delivers traffic for you outside the Tor network. +
+
Note that if you list too few nodes here, or if you exclude too many exit
nodes with ExcludeExitNodes, you can degrade functionality. For example,
if none of the exits you list allows traffic on port 80 or 443, you won't
be able to browse the web. +
+
Note also that not every circuit is used to deliver traffic outside of
the Tor network. It is normal to see non-exit circuits (such as those
used to connect to hidden services, those that do directory fetches,
those used for self-tests, and so on) that end at a non-exit node. To
keep a node from being used entirely, see ExcludeNodes and StrictNodes. +
+
The ExcludeNodes option overrides this option: any node listed in both
ExitNodes and ExcludeNodes is treated as excluded. +
+
The .exit address notation, if enabled, overrides this option.
**EntryNodes** __node__,__node__,__...__::
A list of identity fingerprints, nicknames, and country codes of nodes
to use for the first hop in your normal circuits.
This includes all
circuits except for direct connections to directory servers. The Bridge
option overrides this option; if you have configured bridges and
UseBridges is 1, the Bridges are used as your entry nodes. +
+
The ExcludeNodes option overrides this option: any node listed in both
EntryNodes and ExcludeNodes is treated as excluded.
**StrictNodes** **0**|**1**::
If 1 and EntryNodes config option is set, Tor will never use any nodes
besides those listed in EntryNodes for the first hop of a normal circuit.
If 1 and ExitNodes config option is set, Tor will never use any nodes
besides those listed in ExitNodes for the last hop of a normal exit
circuit. Note that Tor might still use these nodes for non-exit circuits
such as one-hop directory fetches or hidden service support circuits.
If StrictNodes is set to 1, Tor will treat the ExcludeNodes option as a
requirement to follow for all the circuits you generate, even if doing so
will break functionality for you. If StrictNodes is set to 0, Tor will
still try to avoid nodes in the ExcludeNodes list, but it will err on the
side of avoiding unexpected errors. Specifically, StrictNodes 0 tells
Tor that it is okay to use an excluded node when it is *necessary* to
perform self-tests, connect to
a hidden service, provide a hidden service to a client, fulfill a .exit
request, upload directory information, or download directory information.
(Default: 0)
**FascistFirewall** **0**|**1**::
If 1, Tor will only create outgoing connections to ORs running on ports

View File

@ -2046,8 +2046,9 @@ circuit_send_next_onion_skin(origin_circuit_t *circ)
*/
if (timediff < 0 || timediff > 2*circ_times.close_ms+1000) {
log_notice(LD_CIRC, "Strange value for circuit build time: %ldmsec. "
"Assuming clock jump. Purpose %d", timediff,
circ->_base.purpose);
"Assuming clock jump. Purpose %d (%s)", timediff,
circ->_base.purpose,
circuit_purpose_to_string(circ->_base.purpose));
} else if (!circuit_build_times_disabled()) {
/* Only count circuit times if the network is live */
if (circuit_build_times_network_check_live(&circ_times)) {
@ -2673,16 +2674,23 @@ choose_good_exit_server_general(int need_uptime, int need_capacity)
n_supported[i] = -1;
continue; /* skip routers that are known to be down or bad exits */
}
if (node_is_unreliable(node, need_uptime, need_capacity, 0) &&
(!options->ExitNodes ||
!routerset_contains_node(options->ExitNodes, node))) {
/* FFFF Someday, differentiate between a routerset that names
* routers, and a routerset that names countries, and only do this
* check if they've asked for specific exit relays. Or if the country
* they ask for is rare. Or something. */
if (options->_ExcludeExitNodesUnion &&
routerset_contains_node(options->_ExcludeExitNodesUnion, node)) {
n_supported[i] = -1;
continue; /* skip routers that are not suitable, unless we have
* ExitNodes set, in which case we asked for it */
continue; /* user asked us not to use it, no matter what */
}
if (options->ExitNodes &&
!routerset_contains_node(options->ExitNodes, node)) {
n_supported[i] = -1;
continue; /* not one of our chosen exit nodes */
}
if (node_is_unreliable(node, need_uptime, need_capacity, 0)) {
n_supported[i] = -1;
continue; /* skip routers that are not suitable. Don't worry if
* this makes us reject all the possible routers: if so,
* we'll retry later in this function with need_update and
* need_capacity set to 0. */
}
if (!(node->is_valid || options->_AllowInvalid & ALLOW_INVALID_EXIT)) {
/* if it's invalid and we don't want it */
@ -2707,7 +2715,7 @@ choose_good_exit_server_general(int need_uptime, int need_capacity)
SMARTLIST_FOREACH_BEGIN(connections, connection_t *, conn) {
if (!ap_stream_wants_exit_attention(conn))
continue; /* Skip everything but APs in CIRCUIT_WAIT */
if (connection_ap_can_use_exit(TO_EDGE_CONN(conn), node, 1)) {
if (connection_ap_can_use_exit(TO_EDGE_CONN(conn), node)) {
++n_supported[i];
// log_fn(LOG_DEBUG,"%s is supported. n_supported[%d] now %d.",
// router->nickname, i, n_supported[i]);
@ -2741,22 +2749,14 @@ choose_good_exit_server_general(int need_uptime, int need_capacity)
/* If any routers definitely support any pending connections, choose one
* at random. */
if (best_support > 0) {
smartlist_t *supporting = smartlist_create(), *use = smartlist_create();
smartlist_t *supporting = smartlist_create();
SMARTLIST_FOREACH(the_nodes, const node_t *, node, {
if (n_supported[node_sl_idx] == best_support)
smartlist_add(supporting, (void*)node);
});
routersets_get_node_disjunction(use, supporting, options->ExitNodes,
options->_ExcludeExitNodesUnion, 1);
if (smartlist_len(use) == 0 && options->ExitNodes &&
!options->StrictNodes) { /* give up on exitnodes and try again */
routersets_get_node_disjunction(use, supporting, NULL,
options->_ExcludeExitNodesUnion, 1);
}
node = node_sl_choose_by_bandwidth(use, WEIGHT_FOR_EXIT);
smartlist_free(use);
node = node_sl_choose_by_bandwidth(supporting, WEIGHT_FOR_EXIT);
smartlist_free(supporting);
} else {
/* Either there are no pending connections, or no routers even seem to
@ -2764,7 +2764,7 @@ choose_good_exit_server_general(int need_uptime, int need_capacity)
* at least one predicted exit port. */
int attempt;
smartlist_t *needed_ports, *supporting, *use;
smartlist_t *needed_ports, *supporting;
if (best_support == -1) {
if (need_uptime || need_capacity) {
@ -2781,7 +2781,6 @@ choose_good_exit_server_general(int need_uptime, int need_capacity)
options->_ExcludeExitNodesUnion ? " or are Excluded" : "");
}
supporting = smartlist_create();
use = smartlist_create();
needed_ports = circuit_get_unhandled_ports(time(NULL));
for (attempt = 0; attempt < 2; attempt++) {
/* try once to pick only from routers that satisfy a needed port,
@ -2797,25 +2796,13 @@ choose_good_exit_server_general(int need_uptime, int need_capacity)
}
} SMARTLIST_FOREACH_END(node);
routersets_get_node_disjunction(use, supporting, options->ExitNodes,
options->_ExcludeExitNodesUnion, 1);
if (smartlist_len(use) == 0 && options->ExitNodes &&
!options->StrictNodes) { /* give up on exitnodes and try again */
routersets_get_node_disjunction(use, supporting, NULL,
options->_ExcludeExitNodesUnion, 1);
}
/* FFF sometimes the above results in null, when the requested
* exit node is considered down by the consensus. we should pick
* it anyway, since the user asked for it. */
node = node_sl_choose_by_bandwidth(use, WEIGHT_FOR_EXIT);
node = node_sl_choose_by_bandwidth(supporting, WEIGHT_FOR_EXIT);
if (node)
break;
smartlist_clear(supporting);
smartlist_clear(use);
}
SMARTLIST_FOREACH(needed_ports, uint16_t *, cp, tor_free(cp));
smartlist_free(needed_ports);
smartlist_free(use);
smartlist_free(supporting);
}
@ -2824,10 +2811,11 @@ choose_good_exit_server_general(int need_uptime, int need_capacity)
log_info(LD_CIRC, "Chose exit server '%s'", node_get_nickname(node));
return node;
}
if (options->ExitNodes && options->StrictNodes) {
if (options->ExitNodes) {
log_warn(LD_CIRC,
"No specified exit routers seem to be running, and "
"StrictNodes is set: can't choose an exit.");
"No specified %sexit routers seem to be running: "
"can't choose an exit.",
options->_ExcludeExitNodesUnion ? "non-excluded " : "");
}
return NULL;
}
@ -2879,7 +2867,6 @@ warn_if_last_router_excluded(origin_circuit_t *circ, const extend_info_t *exit)
or_options_t *options = get_options();
routerset_t *rs = options->ExcludeNodes;
const char *description;
int domain = LD_CIRC;
uint8_t purpose = circ->_base.purpose;
if (circ->build_state->onehop_tunnel)
@ -2892,13 +2879,14 @@ warn_if_last_router_excluded(origin_circuit_t *circ, const extend_info_t *exit)
case CIRCUIT_PURPOSE_INTRO_POINT:
case CIRCUIT_PURPOSE_REND_POINT_WAITING:
case CIRCUIT_PURPOSE_REND_ESTABLISHED:
log_warn(LD_BUG, "Called on non-origin circuit (purpose %d)",
(int)purpose);
log_warn(LD_BUG, "Called on non-origin circuit (purpose %d, %s)",
(int)purpose,
circuit_purpose_to_string(purpose));
return;
case CIRCUIT_PURPOSE_C_GENERAL:
if (circ->build_state->is_internal)
return;
description = "Requested exit node";
description = "requested exit node";
rs = options->_ExcludeExitNodesUnion;
break;
case CIRCUIT_PURPOSE_C_INTRODUCING:
@ -2913,22 +2901,34 @@ warn_if_last_router_excluded(origin_circuit_t *circ, const extend_info_t *exit)
case CIRCUIT_PURPOSE_C_REND_READY:
case CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED:
case CIRCUIT_PURPOSE_C_REND_JOINED:
description = "Chosen rendezvous point";
domain = LD_BUG;
description = "chosen rendezvous point";
break;
case CIRCUIT_PURPOSE_CONTROLLER:
rs = options->_ExcludeExitNodesUnion;
description = "Controller-selected circuit target";
description = "controller-selected circuit target";
break;
}
if (routerset_contains_extendinfo(rs, exit)) {
log_fn(LOG_WARN, domain, "%s '%s' is in ExcludeNodes%s. Using anyway "
"(circuit purpose %d).",
description,exit->nickname,
rs==options->ExcludeNodes?"":" or ExcludeExitNodes",
(int)purpose);
circuit_log_path(LOG_WARN, domain, circ);
/* We should never get here if StrictNodes is set to 1. */
if (options->StrictNodes) {
log_warn(LD_BUG, "Using %s '%s' which is listed in ExcludeNodes%s, "
"even though StrictNodes is set. Please report. "
"(Circuit purpose: %s)",
description, exit->nickname,
rs==options->ExcludeNodes?"":" or ExcludeExitNodes",
circuit_purpose_to_string(purpose));
} else {
log_warn(LD_CIRC, "Using %s '%s' which is listed in "
"ExcludeNodes%s, because no better options were available. To "
"prevent this (and possibly break your Tor functionality), "
"set the StrictNodes configuration option. "
"(Circuit purpose: %s)",
description, exit->nickname,
rs==options->ExcludeNodes?"":" or ExcludeExitNodes",
circuit_purpose_to_string(purpose));
}
circuit_log_path(LOG_WARN, LD_CIRC, circ);
}
return;
@ -3990,12 +3990,14 @@ entry_guards_prepend_from_config(or_options_t *options)
/* Split entry guards into those on the list and those not. */
/* Now that we allow countries and IP ranges in EntryNodes, this is
* potentially an enormous list. It's not so bad though because we
* only call this function when a) we're making a new circuit, and b)
* we've called directory_info_has_arrived() or changed our EntryNodes
* since the last time we made a circuit. */
routerset_get_all_nodes(entry_nodes, options->EntryNodes, 0);
/* XXXX023 Now that we allow countries and IP ranges in EntryNodes, this is
* potentially an enormous list. For now, we disable such values for
* EntryNodes in options_validate(); really, this wants a better solution.
* Perhaps we should do this calculation once whenever the list of routers
* changes or the entrynodes setting changes.
*/
routerset_get_all_nodes(entry_nodes, options->EntryNodes,
options->ExcludeNodes, 0);
SMARTLIST_FOREACH(entry_nodes, const node_t *,node,
smartlist_add(entry_fps, (void*)node->identity));
@ -4021,14 +4023,10 @@ entry_guards_prepend_from_config(or_options_t *options)
SMARTLIST_FOREACH(entry_nodes, const node_t *, node, {
add_an_entry_guard(node, 0);
});
/* Finally, the remaining previously configured guards that are not in
* EntryNodes, unless we're strict in which case we drop them */
if (options->StrictNodes) {
SMARTLIST_FOREACH(old_entry_guards_not_on_list, entry_guard_t *, e,
entry_guard_free(e));
} else {
smartlist_add_all(entry_guards, old_entry_guards_not_on_list);
}
/* Finally, free the remaining previously configured guards that are not in
* EntryNodes. */
SMARTLIST_FOREACH(old_entry_guards_not_on_list, entry_guard_t *, e,
entry_guard_free(e));
smartlist_free(entry_nodes);
smartlist_free(entry_fps);
@ -4039,7 +4037,7 @@ entry_guards_prepend_from_config(or_options_t *options)
/** Return 0 if we're fine adding arbitrary routers out of the
* directory to our entry guard list, or return 1 if we have a
* list already and we'd prefer to stick to it.
* list already and we must stick to it.
*/
int
entry_list_is_constrained(or_options_t *options)
@ -4051,18 +4049,6 @@ entry_list_is_constrained(or_options_t *options)
return 0;
}
/* Are we dead set against changing our entry guard list, or would we
* change it if it means keeping Tor usable? */
static int
entry_list_is_totally_static(or_options_t *options)
{
if (options->EntryNodes && options->StrictNodes)
return 1;
if (options->UseBridges)
return 1;
return 0;
}
/** Pick a live (up and listed) entry guard from entry_guards. If
* <b>state</b> is non-NULL, this is for a specific circuit --
* make sure not to pick this circuit's exit or any node in the
@ -4107,6 +4093,7 @@ choose_random_entry(cpath_build_state_t *state)
continue; /* don't pick the same node for entry and exit */
if (consider_exit_family && smartlist_isin(exit_family, node))
continue; /* avoid relays that are family members of our exit */
#if 0 /* since EntryNodes is always strict now, this clause is moot */
if (options->EntryNodes &&
!routerset_contains_node(options->EntryNodes, node)) {
/* We've come to the end of our preferred entry nodes. */
@ -4121,6 +4108,7 @@ choose_random_entry(cpath_build_state_t *state)
"No relays from EntryNodes available. Using others.");
}
}
#endif
smartlist_add(live_entry_guards, (void*)node);
if (!entry->made_contact) {
/* Always start with the first not-yet-contacted entry
@ -4146,7 +4134,7 @@ choose_random_entry(cpath_build_state_t *state)
}
if (smartlist_len(live_entry_guards) < preferred_min) {
if (!entry_list_is_totally_static(options)) {
if (!entry_list_is_constrained(options)) {
/* still no? try adding a new entry then */
/* XXX if guard doesn't imply fast and stable, then we need
* to tell add_an_entry_guard below what we want, or it might
@ -4171,6 +4159,10 @@ choose_random_entry(cpath_build_state_t *state)
need_capacity = 0;
goto retry;
}
#if 0
/* Removing this retry logic: if we only allow one exit, and it is in the
same family as all our entries, then we are just plain not going to win
here. */
if (!node && entry_list_is_constrained(options) && consider_exit_family) {
/* still no? if we're using bridges or have strictentrynodes
* set, and our chosen exit is in the same family as all our
@ -4178,6 +4170,7 @@ choose_random_entry(cpath_build_state_t *state)
consider_exit_family = 0;
goto retry;
}
#endif
/* live_entry_guards may be empty below. Oh well, we tried. */
}
@ -4578,6 +4571,24 @@ bridge_add_from_config(const tor_addr_t *addr, uint16_t port, char *digest)
smartlist_add(bridge_list, b);
}
/** Return true iff <b>routerset</b> contains the bridge <b>bridge</b>. */
static int
routerset_contains_bridge(const routerset_t *routerset,
const bridge_info_t *bridge)
{
int result;
extend_info_t *extinfo;
tor_assert(bridge);
if (!routerset)
return 0;
extinfo = extend_info_alloc(
NULL, bridge->identity, NULL, &bridge->addr, bridge->port);
result = routerset_contains_extendinfo(routerset, extinfo);
extend_info_free(extinfo);
return result;
}
/** If <b>digest</b> is one of our known bridges, return it. */
static bridge_info_t *
find_bridge_by_digest(const char *digest)
@ -4596,6 +4607,7 @@ static void
launch_direct_bridge_descriptor_fetch(bridge_info_t *bridge)
{
char *address;
or_options_t *options = get_options();
if (connection_get_by_type_addr_port_purpose(
CONN_TYPE_DIR, &bridge->addr, bridge->port,
@ -4603,6 +4615,13 @@ launch_direct_bridge_descriptor_fetch(bridge_info_t *bridge)
return; /* it's already on the way */
address = tor_dup_addr(&bridge->addr);
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_addr(&bridge->addr)));
return;
}
directory_initiate_command(address, &bridge->addr,
bridge->port, 0,
0, /* does not matter */
@ -4643,6 +4662,12 @@ fetch_bridge_descriptors(or_options_t *options, time_t now)
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_addr(&bridge->addr)));
continue;
}
/* schedule another fetch as if this one will fail, in case it does */
download_status_failed(&bridge->fetch_status, 0);

View File

@ -376,6 +376,62 @@ circuit_purpose_to_controller_string(uint8_t purpose)
}
}
/** Return a human-readable string for the circuit purpose <b>purpose</b>. */
const char *
circuit_purpose_to_string(uint8_t purpose)
{
static char buf[32];
switch (purpose)
{
case CIRCUIT_PURPOSE_OR:
return "Circuit at relay";
case CIRCUIT_PURPOSE_INTRO_POINT:
return "Acting as intro point";
case CIRCUIT_PURPOSE_REND_POINT_WAITING:
return "Acting as rendevous (pending)";
case CIRCUIT_PURPOSE_REND_ESTABLISHED:
return "Acting as rendevous (established)";
case CIRCUIT_PURPOSE_C_GENERAL:
return "General-purpose client";
case CIRCUIT_PURPOSE_C_INTRODUCING:
return "Hidden service client: Connecting to intro point";
case CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT:
return "Hidden service client: Waiting for ack from intro point";
case CIRCUIT_PURPOSE_C_INTRODUCE_ACKED:
return "Hidden service client: Received ack from intro point";
case CIRCUIT_PURPOSE_C_ESTABLISH_REND:
return "Hidden service client: Establishing rendezvous point";
case CIRCUIT_PURPOSE_C_REND_READY:
return "Hidden service client: Pending rendezvous point";
case CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED:
return "Hidden service client: Pending rendezvous point (ack received)";
case CIRCUIT_PURPOSE_C_REND_JOINED:
return "Hidden service client: Active rendezvous point";
case CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT:
return "Measuring circuit timeout";
case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO:
return "Hidden service: Establishing introduction point";
case CIRCUIT_PURPOSE_S_INTRO:
return "Hidden service: Introduction point";
case CIRCUIT_PURPOSE_S_CONNECT_REND:
return "Hidden service: Connecting to rendezvous point";
case CIRCUIT_PURPOSE_S_REND_JOINED:
return "Hidden service: Active rendezvous point";
case CIRCUIT_PURPOSE_TESTING:
return "Testing circuit";
case CIRCUIT_PURPOSE_CONTROLLER:
return "Circuit made by controller";
default:
tor_snprintf(buf, sizeof(buf), "UNKNOWN_%d", (int)purpose);
return buf;
}
}
/** Pick a reasonable package_window to start out for our circuits.
* Originally this was hard-coded at 1000, but now the consensus votes
* on the answer. See proposal 168. */
@ -921,6 +977,7 @@ circuit_find_to_cannibalize(uint8_t purpose, extend_info_t *info,
int need_uptime = (flags & CIRCLAUNCH_NEED_UPTIME) != 0;
int need_capacity = (flags & CIRCLAUNCH_NEED_CAPACITY) != 0;
int internal = (flags & CIRCLAUNCH_IS_INTERNAL) != 0;
or_options_t *options = get_options();
/* Make sure we're not trying to create a onehop circ by
* cannibalization. */
@ -959,6 +1016,19 @@ circuit_find_to_cannibalize(uint8_t purpose, extend_info_t *info,
hop=hop->next;
} while (hop!=circ->cpath);
}
if (options->ExcludeNodes) {
/* Make sure no existing nodes in the circuit are excluded for
* general use. (This may be possible if StrictNodes is 0, and we
* thought we needed to use an otherwise excluded node for, say, a
* directory operation.) */
crypt_path_t *hop = circ->cpath;
do {
if (routerset_contains_extendinfo(options->ExcludeNodes,
hop->extend_info))
goto next;
hop = hop->next;
} while (hop != circ->cpath);
}
if (!best || (best->build_state->need_uptime && !need_uptime))
best = circ;
next: ;

View File

@ -15,6 +15,7 @@
circuit_t * _circuit_get_global_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_string(uint8_t purpose);
void circuit_dump_by_conn(connection_t *conn, int severity);
void circuit_set_p_circid_orconn(or_circuit_t *circ, circid_t id,
or_connection_t *conn);

View File

@ -128,7 +128,7 @@ circuit_is_acceptable(circuit_t *circ, edge_connection_t *conn,
return 0;
}
}
if (exitnode && !connection_ap_can_use_exit(conn, exitnode, 0)) {
if (exitnode && !connection_ap_can_use_exit(conn, exitnode)) {
/* can't exit from this router */
return 0;
}
@ -167,6 +167,10 @@ circuit_is_better(circuit_t *a, circuit_t *b, uint8_t purpose)
return 1;
if (CIRCUIT_IS_ORIGIN(b) &&
TO_ORIGIN_CIRCUIT(b)->build_state->is_internal)
/* XXX023 what the heck is this internal thing doing here. I
* think we can get rid of it. circuit_is_acceptable() already
* makes sure that is_internal is exactly what we need it to
* be. -RD */
return 1;
}
break;
@ -243,33 +247,34 @@ circuit_get_best(edge_connection_t *conn, int must_be_open, uint8_t purpose,
return best ? TO_ORIGIN_CIRCUIT(best) : NULL;
}
#if 0
/** Check whether, according to the policies in <b>options</b>, the
* circuit <b>circ</b> makes sense. */
/* XXXX currently only checks Exclude{Exit}Nodes. It should check more. */
/* XXXX currently only checks Exclude{Exit}Nodes; it should check more.
* Also, it doesn't have the right definition of an exit circuit. Also,
* it's never called. */
int
circuit_conforms_to_options(const origin_circuit_t *circ,
const or_options_t *options)
{
const crypt_path_t *cpath, *cpath_next = NULL;
for (cpath = circ->cpath; cpath && cpath_next != circ->cpath;
cpath = cpath_next) {
/* first check if it includes any excluded nodes */
for (cpath = circ->cpath; cpath_next != circ->cpath; cpath = cpath_next) {
cpath_next = cpath->next;
if (routerset_contains_extendinfo(options->ExcludeNodes,
cpath->extend_info))
return 0;
if (cpath->next == circ->cpath) {
/* This is apparently the exit node. */
if (routerset_contains_extendinfo(options->ExcludeExitNodes,
cpath->extend_info))
return 0;
}
}
/* then consider the final hop */
if (routerset_contains_extendinfo(options->ExcludeExitNodes,
circ->cpath->prev->extend_info))
return 0;
return 1;
}
#endif
/** Close all circuits that start at us, aren't open, and were born
* at least CircuitBuildTimeout seconds ago.
@ -392,10 +397,11 @@ circuit_expire_building(void)
TO_ORIGIN_CIRCUIT(victim)->cpath->state == CPATH_STATE_OPEN;
if (TO_ORIGIN_CIRCUIT(victim)->p_streams != NULL) {
log_warn(LD_BUG, "Circuit %d (purpose %d) has timed out, "
log_warn(LD_BUG, "Circuit %d (purpose %d, %s) has timed out, "
"yet has attached streams!",
TO_ORIGIN_CIRCUIT(victim)->global_identifier,
victim->purpose);
victim->purpose,
circuit_purpose_to_string(victim->purpose));
tor_fragile_assert();
continue;
}
@ -426,9 +432,10 @@ circuit_expire_building(void)
if (timercmp(&victim->timestamp_created, &extremely_old_cutoff, <)) {
log_notice(LD_CIRC,
"Extremely large value for circuit build timeout: %lds. "
"Assuming clock jump. Purpose %d",
"Assuming clock jump. Purpose %d (%s)",
(long)(now.tv_sec - victim->timestamp_created.tv_sec),
victim->purpose);
victim->purpose,
circuit_purpose_to_string(victim->purpose));
} else if (circuit_build_times_count_close(&circ_times,
first_hop_succeeded,
victim->timestamp_created.tv_sec)) {
@ -509,7 +516,7 @@ circuit_stream_is_being_handled(edge_connection_t *conn,
if (exitnode && (!need_uptime || build_state->need_uptime)) {
int ok;
if (conn) {
ok = connection_ap_can_use_exit(conn, exitnode, 0);
ok = connection_ap_can_use_exit(conn, exitnode);
} else {
addr_policy_result_t r;
r = compare_addr_to_node_policy(0, port, exitnode);
@ -794,12 +801,11 @@ circuit_expire_old_circuits_clientside(void)
circ->purpose != CIRCUIT_PURPOSE_S_INTRO) {
log_notice(LD_CIRC,
"Ancient non-dirty circuit %d is still around after "
"%ld milliseconds. Purpose: %d",
"%ld milliseconds. Purpose: %d (%s)",
TO_ORIGIN_CIRCUIT(circ)->global_identifier,
tv_mdiff(&circ->timestamp_created, &now),
circ->purpose);
/* FFFF implement a new circuit_purpose_to_string() so we don't
* just print out a number for circ->purpose */
circ->purpose,
circuit_purpose_to_string(circ->purpose));
TO_ORIGIN_CIRCUIT(circ)->is_ancient = 1;
}
}
@ -1128,8 +1134,9 @@ circuit_launch_by_extend_info(uint8_t purpose,
* internal circs rather than exit circs? -RD */
circ = circuit_find_to_cannibalize(purpose, extend_info, flags);
if (circ) {
log_info(LD_CIRC,"Cannibalizing circ '%s' for purpose %d",
build_state_get_exit_nickname(circ->build_state), purpose);
log_info(LD_CIRC,"Cannibalizing circ '%s' for purpose %d (%s)",
build_state_get_exit_nickname(circ->build_state), purpose,
circuit_purpose_to_string(purpose));
circ->_base.purpose = purpose;
/* reset the birth date of this circ, else expire_building
* will see it and think it's been trying to build since it
@ -1281,9 +1288,10 @@ circuit_get_open_circ_or_launch(edge_connection_t *conn,
* refactor into a single function? */
const node_t *node = node_get_by_nickname(conn->chosen_exit_name, 1);
int opt = conn->chosen_exit_optional;
if (node && !connection_ap_can_use_exit(conn, node, 0)) {
if (node && !connection_ap_can_use_exit(conn, node)) {
log_fn(opt ? LOG_INFO : LOG_WARN, LD_APP,
"Requested exit point '%s' would refuse request. %s.",
"Requested exit point '%s' is excluded or "
"would refuse request. %s.",
conn->chosen_exit_name, opt ? "Trying others" : "Closing");
if (opt) {
conn->chosen_exit_optional = 0;
@ -1394,7 +1402,18 @@ circuit_get_open_circ_or_launch(edge_connection_t *conn,
extend_info_free(extend_info);
if (desired_circuit_purpose != CIRCUIT_PURPOSE_C_GENERAL) {
if (desired_circuit_purpose == CIRCUIT_PURPOSE_C_GENERAL) {
/* We just caused a circuit to get built because of this stream.
* If this stream has caused a _lot_ of circuits to be built, that's
* a bad sign: we should tell the user. */
if (conn->num_circuits_launched < NUM_CIRCUITS_LAUNCHED_THRESHOLD &&
++conn->num_circuits_launched == NUM_CIRCUITS_LAUNCHED_THRESHOLD)
log_warn(LD_BUG, "The application request to %s:%d has launched "
"%d circuits without finding one it likes.",
escaped_safe_str_client(conn->socks_request->address),
conn->socks_request->port,
conn->num_circuits_launched);
} else {
/* help predict this next time */
rep_hist_note_used_internal(time(NULL), need_uptime, 1);
if (circ) {
@ -1601,9 +1620,10 @@ connection_ap_handshake_attach_circuit(edge_connection_t *conn)
}
return -1;
}
if (node && !connection_ap_can_use_exit(conn, node, 0)) {
if (node && !connection_ap_can_use_exit(conn, node)) {
log_fn(opt ? LOG_INFO : LOG_WARN, LD_APP,
"Requested exit point '%s' would refuse request. %s.",
"Requested exit point '%s' is excluded or "
"would refuse request. %s.",
conn->chosen_exit_name, opt ? "Trying others" : "Closing");
if (opt) {
conn->chosen_exit_optional = 0;

View File

@ -16,8 +16,10 @@ void circuit_expire_building(void);
void circuit_remove_handled_ports(smartlist_t *needed_ports);
int circuit_stream_is_being_handled(edge_connection_t *conn, uint16_t port,
int min);
#if 0
int circuit_conforms_to_options(const origin_circuit_t *circ,
const or_options_t *options);
#endif
void circuit_build_needed_circs(time_t now);
void circuit_detach_stream(circuit_t *circ, edge_connection_t *conn);

View File

@ -1289,21 +1289,18 @@ options_act(or_options_t *old_options)
/* Check for transitions that need action. */
if (old_options) {
if ((options->UseEntryGuards && !old_options->UseEntryGuards) ||
(options->ExcludeNodes &&
!routerset_equal(old_options->ExcludeNodes,options->ExcludeNodes)) ||
(options->ExcludeExitNodes &&
!routerset_equal(old_options->ExcludeExitNodes,
options->ExcludeExitNodes)) ||
(options->EntryNodes &&
!routerset_equal(old_options->EntryNodes, options->EntryNodes)) ||
(options->ExitNodes &&
!routerset_equal(old_options->ExitNodes, options->ExitNodes)) ||
!routerset_equal(old_options->ExcludeNodes,options->ExcludeNodes) ||
!routerset_equal(old_options->ExcludeExitNodes,
options->ExcludeExitNodes) ||
!routerset_equal(old_options->EntryNodes, options->EntryNodes) ||
!routerset_equal(old_options->ExitNodes, options->ExitNodes) ||
options->StrictNodes != old_options->StrictNodes) {
log_info(LD_CIRC,
"Changed to using entry guards, or changed preferred or "
"excluded node lists. Abandoning previous circuits.");
circuit_mark_all_unused_circs();
circuit_expire_all_dirty_circs();
addressmap_clear_excluded_trackexithosts(options);
}
/* How long should we delay counting bridge stats after becoming a bridge?
@ -1454,7 +1451,8 @@ options_act(or_options_t *old_options)
/* Check if we need to parse and add the EntryNodes config option. */
if (options->EntryNodes &&
(!old_options ||
(!routerset_equal(old_options->EntryNodes,options->EntryNodes))))
!routerset_equal(old_options->EntryNodes,options->EntryNodes) ||
!routerset_equal(old_options->ExcludeNodes,options->ExcludeNodes)))
entry_nodes_should_be_added();
/* Since our options changed, we might need to regenerate and upload our
@ -3253,6 +3251,12 @@ options_validate(or_options_t *old_options, or_options_t *options,
REJECT("Servers must be able to freely connect to the rest "
"of the Internet, so they must not set UseBridges.");
/* If both of these are set, we'll end up with funny behavior where we
* demand enough entrynodes be up and running else we won't build
* circuits, yet we never actually use them. */
if (options->UseBridges && options->EntryNodes)
REJECT("You cannot set both UseBridges and EntryNodes.");
options->_AllowInvalid = 0;
if (options->AllowInvalidNodes) {
SMARTLIST_FOREACH(options->AllowInvalidNodes, const char *, cp, {

View File

@ -799,6 +799,7 @@ clear_trackexithost_mappings(const char *exitname)
tor_strlower(suffix);
STRMAP_FOREACH_MODIFY(addressmap, address, addressmap_entry_t *, ent) {
/* XXXX022 HEY! Shouldn't this look at ent->new_address? */
if (ent->source == ADDRMAPSRC_TRACKEXIT && !strcmpend(address, suffix)) {
addressmap_ent_remove(address, ent);
MAP_DEL_CURRENT(address);
@ -808,6 +809,56 @@ clear_trackexithost_mappings(const char *exitname)
tor_free(suffix);
}
/** Remove all TRACKEXIT mappings from the addressmap for which the target
* host is unknown or no longer allowed. */
void
addressmap_clear_excluded_trackexithosts(or_options_t *options)
{
const routerset_t *allow_nodes = options->ExitNodes;
const routerset_t *exclude_nodes = options->_ExcludeExitNodesUnion;
if (!addressmap)
return;
if (routerset_is_empty(allow_nodes))
allow_nodes = NULL;
if (allow_nodes == NULL && routerset_is_empty(exclude_nodes))
return;
STRMAP_FOREACH_MODIFY(addressmap, address, addressmap_entry_t *, ent) {
size_t len;
const char *target = ent->new_address, *dot;
char *nodename;
const node_t *node;
if (strcmpend(target, ".exit")) {
/* Not a .exit mapping */
continue;
} else if (ent->source != ADDRMAPSRC_TRACKEXIT) {
/* Not a trackexit mapping. */
continue;
}
len = strlen(target);
if (len < 6)
continue; /* malformed. */
dot = target + len - 6; /* dot now points to just before .exit */
dot = strrchr(dot, '.'); /* dot now points to the . before .exit or NULL */
if (!dot) {
nodename = tor_strndup(target, len-5);
} else {
nodename = tor_strndup(dot+1, strlen(dot+1)-5);
}
node = node_get_by_nickname(nodename, 0);
tor_free(nodename);
if (!node ||
(allow_nodes && !routerset_contains_node(allow_nodes, node)) ||
routerset_contains_node(exclude_nodes, node)) {
/* We don't know this one, or we want to be rid of it. */
addressmap_ent_remove(address, ent);
MAP_DEL_CURRENT(address);
}
} STRMAP_FOREACH_END;
}
/** Remove all entries from the addressmap that were set via the
* configuration file or the command line. */
void
@ -1494,9 +1545,13 @@ connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn,
hostname_type_t addresstype;
or_options_t *options = get_options();
struct in_addr addr_tmp;
/* We set this to true if this is an address we should automatically
* remap to a local address in VirtualAddrNetwork */
int automap = 0;
char orig_address[MAX_SOCKS_ADDR_LEN];
time_t map_expires = TIME_MAX;
/* This will be set to true iff the address starts out as a non-.exit
address, and we remap it to one because of an entry in the addressmap. */
int remapped_to_exit = 0;
time_t now = time(NULL);
@ -1607,14 +1662,23 @@ connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn,
/* foo.exit -- modify conn->chosen_exit_node to specify the exit
* node, and conn->address to hold only the address portion. */
char *s = strrchr(socks->address,'.');
/* If StrictNodes is not set, then .exit overrides ExcludeNodes. */
routerset_t *excludeset = options->StrictNodes ?
options->_ExcludeExitNodesUnion : options->ExcludeExitNodes;
const node_t *node;
tor_assert(!automap);
if (s) {
/* The address was of the form "(stuff).(name).exit */
if (s[1] != '\0') {
conn->chosen_exit_name = tor_strdup(s+1);
node = node_get_by_nickname(conn->chosen_exit_name, 1);
if (remapped_to_exit) /* 5 tries before it expires the addressmap */
conn->chosen_exit_retries = TRACKHOSTEXITS_RETRIES;
*s = 0;
} else {
/* Oops, the address was (stuff)..exit. That's not okay. */
log_warn(LD_APP,"Malformed exit address '%s.exit'. Refusing.",
safe_str_client(socks->address));
control_event_client_status(LOG_WARN, "SOCKS_BAD_HOSTNAME HOSTNAME=%s",
@ -1623,20 +1687,34 @@ connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn,
return -1;
}
} else {
const node_t *r;
/* It looks like they just asked for "foo.exit". */
conn->chosen_exit_name = tor_strdup(socks->address);
r = node_get_by_nickname(conn->chosen_exit_name, 1);
*socks->address = 0;
if (r) {
node_get_address_string(r, socks->address, sizeof(socks->address));
} else {
log_warn(LD_APP,
"Unrecognized server in exit address '%s.exit'. Refusing.",
safe_str_client(socks->address));
connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL);
return -1;
node = node_get_by_nickname(conn->chosen_exit_name, 1);
if (node) {
*socks->address = 0;
node_get_address_string(node, socks->address, sizeof(socks->address));
}
}
/* Now make sure that the chosen exit exists... */
if (!node) {
log_warn(LD_APP,
"Unrecognized relay in exit address '%s.exit'. Refusing.",
safe_str_client(socks->address));
connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL);
return -1;
}
/* ...and make sure that it isn't excluded. */
if (routerset_contains_node(excludeset, node)) {
log_warn(LD_APP,
"Excluded relay in exit address '%s.exit'. Refusing.",
safe_str_client(socks->address));
connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL);
return -1;
}
/* XXXX022-1090 Should we also allow foo.bar.exit if ExitNodes is set and
Bar is not listed in it? I say yes, but our revised manpage branch
implies no. */
}
if (addresstype != ONION_HOSTNAME) {
@ -2977,13 +3055,9 @@ connection_edge_is_rendezvous_stream(edge_connection_t *conn)
* to exit from it, or 0 if it probably will not allow it.
* (We might be uncertain if conn's destination address has not yet been
* resolved.)
*
* If <b>excluded_means_no</b> is 1 and Exclude*Nodes is set and excludes
* this relay, return 0.
*/
int
connection_ap_can_use_exit(edge_connection_t *conn, const node_t *exit,
int excluded_means_no)
connection_ap_can_use_exit(edge_connection_t *conn, const node_t *exit)
{
or_options_t *options = get_options();
@ -3027,17 +3101,8 @@ connection_ap_can_use_exit(edge_connection_t *conn, const node_t *exit,
return 0;
}
if (options->_ExcludeExitNodesUnion &&
(options->StrictNodes || excluded_means_no) &&
routerset_contains_node(options->_ExcludeExitNodesUnion, exit)) {
/* If we are trying to avoid this node as exit, and we have StrictNodes
* set, then this is not a suitable exit. Refuse it.
*
* If we don't have StrictNodes set, then this function gets called in
* two contexts. First, we've got a circuit open and we want to know
* whether we can use it. In that case, we somehow built this circuit
* despite having the last hop in ExcludeExitNodes, so we should be
* willing to use it. Second, we are evaluating whether this is an
* acceptable exit for a new circuit. In that case, skip it. */
/* Not a suitable exit. Refuse it. */
return 0;
}

View File

@ -49,8 +49,7 @@ int connection_exit_begin_resolve(cell_t *cell, or_circuit_t *circ);
void connection_exit_connect(edge_connection_t *conn);
int connection_edge_is_rendezvous_stream(edge_connection_t *conn);
int connection_ap_can_use_exit(edge_connection_t *conn,
const node_t *exit,
int excluded_means_no);
const node_t *exit);
void connection_ap_expire_beginning(void);
void connection_ap_attach_pending(void);
void connection_ap_fail_onehop(const char *failed_digest,
@ -64,6 +63,7 @@ int connection_ap_process_transparent(edge_connection_t *conn);
int address_is_invalid_destination(const char *address, int client);
void addressmap_init(void);
void addressmap_clear_excluded_trackexithosts(or_options_t *options);
void addressmap_clean(time_t now);
void addressmap_clear_configured(void);
void addressmap_clear_transient(void);

View File

@ -262,10 +262,13 @@ directories_have_accepted_server_descriptor(void)
}
/** Start a connection to every suitable directory authority, using
* connection purpose 'purpose' and uploading the payload 'payload'
* (length 'payload_len'). dir_purpose should be one of
* connection purpose <b>dir_purpose</b> and uploading <b>payload</b>
* (of length <b>payload_len</b>). The dir_purpose should be one of
* 'DIR_PURPOSE_UPLOAD_DIR' or 'DIR_PURPOSE_UPLOAD_RENDDESC'.
*
* <b>router_purpose</b> describes the type of descriptor we're
* publishing, if we're publishing a descriptor -- e.g. general or bridge.
*
* <b>type</b> specifies what sort of dir authorities (V1, V2,
* HIDSERV, BRIDGE) we should upload to.
*
@ -281,6 +284,7 @@ directory_post_to_dirservers(uint8_t dir_purpose, uint8_t router_purpose,
const char *payload,
size_t payload_len, size_t extrainfo_len)
{
or_options_t *options = get_options();
int post_via_tor;
smartlist_t *dirservers = router_get_trusted_dir_servers();
int found = 0;
@ -296,6 +300,16 @@ directory_post_to_dirservers(uint8_t dir_purpose, uint8_t router_purpose,
if ((type & ds->type) == 0)
continue;
if (options->ExcludeNodes && options->StrictNodes &&
routerset_contains_routerstatus(options->ExcludeNodes, rs, -1)) {
log_warn(LD_DIR, "Wanted to contact authority '%s' for %s, but "
"it's in our ExcludedNodes list and StrictNodes is set. "
"Skipping.",
ds->nickname,
dir_conn_purpose_to_string(dir_purpose));
continue;
}
found = 1; /* at least one authority of this type was listed */
if (dir_purpose == DIR_PURPOSE_UPLOAD_DIR)
ds->has_accepted_serverdesc = 0;
@ -527,12 +541,14 @@ directory_initiate_command_routerstatus_rend(const routerstatus_t *status,
time_t if_modified_since,
const rend_data_t *rend_query)
{
or_options_t *options = get_options();
const node_t *node;
char address_buf[INET_NTOA_BUF_LEN+1];
struct in_addr in;
const char *address;
tor_addr_t addr;
node = node_get_by_id(status->identity_digest);
if (!node && anonymized_connection) {
log_info(LD_DIR, "Not sending anonymized request to directory '%s'; we "
"don't have its router descriptor.", status->nickname);
@ -546,6 +562,17 @@ directory_initiate_command_routerstatus_rend(const routerstatus_t *status,
address = address_buf;
}
tor_addr_from_ipv4h(&addr, status->addr);
if (options->ExcludeNodes && options->StrictNodes &&
routerset_contains_routerstatus(options->ExcludeNodes, status, -1)) {
log_warn(LD_DIR, "Wanted to contact directory mirror '%s' for %s, but "
"it's in our ExcludedNodes list and StrictNodes is set. "
"Skipping. This choice might make your Tor not work.",
status->nickname,
dir_conn_purpose_to_string(dir_purpose));
return;
}
directory_initiate_command_rend(address, &addr,
status->or_port, status->dir_port,
status->version_supports_conditional_consensus,

View File

@ -1175,6 +1175,13 @@ typedef struct edge_connection_t {
* already retried several times. */
uint8_t num_socks_retries;
#define NUM_CIRCUITS_LAUNCHED_THRESHOLD 10
/** Number of times we've launched a circuit to handle this stream. If
* it gets too high, that could indicate an inconsistency between our
* "launch a circuit to handle this stream" logic and our "attach our
* stream to one of the available circuits" logic. */
unsigned int num_circuits_launched:4;
/** True iff this connection is for a DNS request only. */
unsigned int is_dns_request:1;
@ -2544,7 +2551,7 @@ typedef struct {
* ORs not to consider as exits. */
/** Union of ExcludeNodes and ExcludeExitNodes */
struct routerset_t *_ExcludeExitNodesUnion;
routerset_t *_ExcludeExitNodesUnion;
int DisableAllSwap; /**< Boolean: Attempt to call mlockall() on our
* process for all current and future memory. */
@ -3661,7 +3668,7 @@ typedef struct trusted_dir_server_t {
#define ROUTER_MAX_DECLARED_BANDWIDTH INT32_MAX
/* Flags for pick_directory_server and pick_trusteddirserver. */
/* Flags for pick_directory_server() and pick_trusteddirserver(). */
/** Flag to indicate that we should not automatically be willing to use
* ourself to answer a directory request.
* Passed to router_pick_directory_server (et al).*/

View File

@ -23,6 +23,10 @@
#include "rephist.h"
#include "routerlist.h"
static extend_info_t *rend_client_get_random_intro_impl(
const rend_cache_entry_t *rend_query,
const int strict, const int warnings);
/** Called when we've established a circuit to an introduction point:
* send the introduction request. */
void
@ -562,7 +566,7 @@ rend_client_remove_intro_point(extend_info_t *failed_intro,
}
}
if (smartlist_len(ent->parsed->intro_nodes) == 0) {
if (! rend_client_any_intro_points_usable(ent)) {
log_info(LD_REND,
"No more intro points remain for %s. Re-fetching descriptor.",
escaped_safe_str_client(rend_query->onion_address));
@ -708,7 +712,7 @@ rend_client_desc_trynow(const char *query)
assert_connection_ok(TO_CONN(conn), now);
if (rend_cache_lookup_entry(conn->rend_data->onion_address, -1,
&entry) == 1 &&
smartlist_len(entry->parsed->intro_nodes) > 0) {
rend_client_any_intro_points_usable(entry)) {
/* either this fetch worked, or it failed but there was a
* valid entry from before which we should reuse */
log_info(LD_REND,"Rend desc is usable. Launching circuits.");
@ -742,23 +746,62 @@ rend_client_desc_trynow(const char *query)
extend_info_t *
rend_client_get_random_intro(const rend_data_t *rend_query)
{
int i;
extend_info_t *result;
rend_cache_entry_t *entry;
rend_intro_point_t *intro;
if (rend_cache_lookup_entry(rend_query->onion_address, -1, &entry) < 1) {
log_warn(LD_REND,
"Query '%s' didn't have valid rend desc in cache. Failing.",
safe_str_client(rend_query->onion_address));
log_warn(LD_REND,
"Query '%s' didn't have valid rend desc in cache. Failing.",
safe_str_client(rend_query->onion_address));
return NULL;
}
again:
if (smartlist_len(entry->parsed->intro_nodes) == 0)
return NULL;
/* See if we can get a node that complies with ExcludeNodes */
if ((result = rend_client_get_random_intro_impl(entry, 1, 1)))
return result;
/* If not, and StrictNodes is not set, see if we can return any old node
*/
if (!get_options()->StrictNodes)
return rend_client_get_random_intro_impl(entry, 0, 1);
return NULL;
}
i = crypto_rand_int(smartlist_len(entry->parsed->intro_nodes));
intro = smartlist_get(entry->parsed->intro_nodes, i);
/** As rend_client_get_random_intro, except assume that StrictNodes is set
* iff <b>strict</b> is true. If <b>warnings</b> is false, don't complain
* to the user when we're out of nodes, even if StrictNodes is true.
*/
static extend_info_t *
rend_client_get_random_intro_impl(const rend_cache_entry_t *entry,
const int strict,
const int warnings)
{
int i;
rend_intro_point_t *intro;
or_options_t *options = get_options();
smartlist_t *usable_nodes;
int n_excluded = 0;
/* We'll keep a separate list of the usable nodes. If this becomes empty,
* no nodes are usable. */
usable_nodes = smartlist_create();
smartlist_add_all(usable_nodes, entry->parsed->intro_nodes);
again:
if (smartlist_len(usable_nodes) == 0) {
if (n_excluded && get_options()->StrictNodes && warnings) {
/* We only want to warn if StrictNodes is really set. Otherwise
* we're just about to retry anyways.
*/
log_warn(LD_REND, "All introduction points for hidden service are "
"at excluded relays, and StrictNodes is set. Skipping.");
}
smartlist_free(usable_nodes);
return NULL;
}
i = crypto_rand_int(smartlist_len(usable_nodes));
intro = smartlist_get(usable_nodes, i);
/* Do we need to look up the router or is the extend info complete? */
if (!intro->extend_info->onion_key) {
const node_t *node;
@ -769,16 +812,34 @@ rend_client_get_random_intro(const rend_data_t *rend_query)
if (!node) {
log_info(LD_REND, "Unknown router with nickname '%s'; trying another.",
intro->extend_info->nickname);
rend_intro_point_free(intro);
smartlist_del(entry->parsed->intro_nodes, i);
smartlist_del(usable_nodes, i);
goto again;
}
extend_info_free(intro->extend_info);
intro->extend_info = extend_info_from_node(node);
}
/* Check if we should refuse to talk to this router. */
if (options->ExcludeNodes && strict &&
routerset_contains_extendinfo(options->ExcludeNodes,
intro->extend_info)) {
n_excluded++;
smartlist_del(usable_nodes, i);
goto again;
}
smartlist_free(usable_nodes);
return extend_info_dup(intro->extend_info);
}
/** Return true iff any introduction points still listed in <b>entry</b> are
* usable. */
int
rend_client_any_intro_points_usable(const rend_cache_entry_t *entry)
{
return rend_client_get_random_intro_impl(
entry, get_options()->StrictNodes, 0) != NULL;
}
/** Client-side authorizations for hidden services; map of onion address to
* rend_service_authorization_t*. */
static strmap_t *auth_hid_servs = NULL;

View File

@ -29,6 +29,7 @@ int rend_client_receive_rendezvous(origin_circuit_t *circ,
void rend_client_desc_trynow(const char *query);
extend_info_t *rend_client_get_random_intro(const rend_data_t *rend_query);
int rend_client_any_intro_points_usable(const rend_cache_entry_t *entry);
int rend_client_send_introduction(origin_circuit_t *introcirc,
origin_circuit_t *rendcirc);

View File

@ -933,7 +933,7 @@ rend_cache_lookup_entry(const char *query, int version, rend_cache_entry_t **e)
tor_assert((*e)->parsed && (*e)->parsed->intro_nodes);
/* XXX023 hack for now, to return "not found" if there are no intro
* points remaining. See bug 997. */
if (smartlist_len((*e)->parsed->intro_nodes) == 0)
if (! rend_client_any_intro_points_usable(*e))
return 0;
return 1;
}

View File

@ -849,6 +849,7 @@ clean_accepted_intros(rend_service_t *service, time_t now)
/** Respond to an INTRODUCE2 cell by launching a circuit to the chosen
* rendezvous point.
*/
/* XXX022 this function sure could use some organizing. -RD */
int
rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request,
size_t request_len)
@ -876,6 +877,8 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request,
time_t now = time(NULL);
char diffie_hellman_hash[DIGEST_LEN];
time_t *access_time;
or_options_t *options = get_options();
tor_assert(circuit->rend_data);
base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32+1,
@ -1048,6 +1051,15 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request,
goto err;
}
/* Check if we'd refuse to talk to this router */
if (options->ExcludeNodes && options->StrictNodes &&
routerset_contains_extendinfo(options->ExcludeNodes, extend_info)) {
log_warn(LD_REND, "Client asked to rendezvous at a relay that we "
"exclude, and StrictNodes is set. Refusing service.");
reason = END_CIRC_REASON_INTERNAL; /* XXX might leak why we refused */
goto err;
}
r_cookie = ptr;
base16_encode(hexcookie,9,r_cookie,4);
@ -1336,14 +1348,26 @@ rend_service_intro_has_opened(origin_circuit_t *circuit)
}
/* If we already have enough introduction circuits for this service,
* redefine this one as a general circuit. */
* redefine this one as a general circuit or close it, depending. */
if (count_established_intro_points(serviceid) > NUM_INTRO_POINTS) {
log_info(LD_CIRC|LD_REND, "We have just finished an introduction "
"circuit, but we already have enough. Redefining purpose to "
"general.");
TO_CIRCUIT(circuit)->purpose = CIRCUIT_PURPOSE_C_GENERAL;
circuit_has_opened(circuit);
return;
or_options_t *options = get_options();
if (options->ExcludeNodes) {
/* XXXX in some future version, we can test whether the transition is
allowed or not given the actual nodes in the circuit. But for now,
this case, we might as well close the thing. */
log_info(LD_CIRC|LD_REND, "We have just finished an introduction "
"circuit, but we already have enough. Closing it.");
circuit_mark_for_close(TO_CIRCUIT(circuit), END_CIRC_REASON_NONE);
return;
} else {
tor_assert(circuit->build_state->is_internal);
log_info(LD_CIRC|LD_REND, "We have just finished an introduction "
"circuit, but we already have enough. Redefining purpose to "
"general; leaving as internal.");
TO_CIRCUIT(circuit)->purpose = CIRCUIT_PURPOSE_C_GENERAL;
circuit_has_opened(circuit);
return;
}
}
log_info(LD_REND,
@ -1395,7 +1419,7 @@ rend_service_intro_has_opened(origin_circuit_t *circuit)
/** Called when we get an INTRO_ESTABLISHED cell; mark the circuit as a
* live introduction point, and note that the service descriptor is
* now out-of-date.*/
* now out-of-date. */
int
rend_service_intro_established(origin_circuit_t *circuit,
const uint8_t *request,

View File

@ -852,9 +852,29 @@ consider_testing_reachability(int test_or, int test_dir)
const routerinfo_t *me = router_get_my_routerinfo();
int orport_reachable = check_whether_orport_reachable();
tor_addr_t addr;
or_options_t *options = get_options();
if (!me)
return;
if (routerset_contains_router(options->ExcludeNodes, me, -1) &&
options->StrictNodes) {
/* If we've excluded ourself, and StrictNodes is set, we can't test
* ourself. */
if (test_or || test_dir) {
#define SELF_EXCLUDED_WARN_INTERVAL 3600
static ratelim_t warning_limit=RATELIM_INIT(SELF_EXCLUDED_WARN_INTERVAL);
char *msg;
if ((msg = rate_limit_log(&warning_limit, approx_time()))) {
log_warn(LD_CIRC, "Can't peform self-tests for this relay: we have "
"listed ourself in ExcludeNodes, and StrictNodes is set. "
"We cannot learn whether we are usable, and will not "
"be able to advertise ourself.%s", msg);
tor_free(msg);
}
}
return;
}
if (test_or && (!orport_reachable || !circuit_enough_testing_circs())) {
extend_info_t *ei;
log_info(LD_CIRC, "Testing %s of my ORPort: %s:%d.",

View File

@ -1075,6 +1075,7 @@ router_pick_trusteddirserver(authority_type_t type, int flags)
static const routerstatus_t *
router_pick_directory_server_impl(authority_type_t type, int flags)
{
or_options_t *options = get_options();
const node_t *result;
smartlist_t *direct, *tunnel;
smartlist_t *trusted_direct, *trusted_tunnel;
@ -1084,10 +1085,13 @@ router_pick_directory_server_impl(authority_type_t type, int flags)
int requireother = ! (flags & PDS_ALLOW_SELF);
int fascistfirewall = ! (flags & PDS_IGNORE_FASCISTFIREWALL);
int prefer_tunnel = (flags & _PDS_PREFER_TUNNELED_DIR_CONNS);
int try_excluding = 1, n_excluded = 0;
if (!consensus)
return NULL;
retry_without_exclude:
direct = smartlist_create();
tunnel = smartlist_create();
trusted_direct = smartlist_create();
@ -1101,6 +1105,7 @@ router_pick_directory_server_impl(authority_type_t type, int flags)
int is_overloaded;
tor_addr_t addr;
const routerstatus_t *status = node->rs;
const country_t country = node->country;
if (!status)
continue;
@ -1122,6 +1127,12 @@ router_pick_directory_server_impl(authority_type_t type, int flags)
if ((type & EXTRAINFO_CACHE) &&
!router_supports_extrainfo(node->identity, 0))
continue;
if (try_excluding && options->ExcludeNodes &&
routerset_contains_routerstatus(options->ExcludeNodes, status,
country)) {
++n_excluded;
continue;
}
/* XXXX IP6 proposal 118 */
tor_addr_from_ipv4h(&addr, node->rs->addr);
@ -1165,6 +1176,15 @@ router_pick_directory_server_impl(authority_type_t type, int flags)
smartlist_free(trusted_tunnel);
smartlist_free(overloaded_direct);
smartlist_free(overloaded_tunnel);
if (result == NULL && try_excluding && !options->StrictNodes && n_excluded) {
/* If we got no result, and we are excluding nodes, and StrictNodes is
* not set, try again without excluding nodes. */
try_excluding = 0;
n_excluded = 0;
goto retry_without_exclude;
}
return result ? result->rs : NULL;
}
@ -1175,6 +1195,7 @@ static const routerstatus_t *
router_pick_trusteddirserver_impl(authority_type_t type, int flags,
int *n_busy_out)
{
or_options_t *options = get_options();
smartlist_t *direct, *tunnel;
smartlist_t *overloaded_direct, *overloaded_tunnel;
const routerinfo_t *me = router_get_my_routerinfo();
@ -1186,10 +1207,13 @@ router_pick_trusteddirserver_impl(authority_type_t type, int flags,
const int no_serverdesc_fetching =(flags & PDS_NO_EXISTING_SERVERDESC_FETCH);
const int no_microdesc_fetching =(flags & PDS_NO_EXISTING_MICRODESC_FETCH);
int n_busy = 0;
int try_excluding = 1, n_excluded = 0;
if (!trusted_dir_servers)
return NULL;
retry_without_exclude:
direct = smartlist_create();
tunnel = smartlist_create();
overloaded_direct = smartlist_create();
@ -1208,6 +1232,12 @@ router_pick_trusteddirserver_impl(authority_type_t type, int flags,
continue;
if (requireother && me && router_digest_is_me(d->digest))
continue;
if (try_excluding && options->ExcludeNodes &&
routerset_contains_routerstatus(options->ExcludeNodes,
&d->fake_status, -1)) {
++n_excluded;
continue;
}
/* XXXX IP6 proposal 118 */
tor_addr_from_ipv4h(&addr, d->addr);
@ -1261,6 +1291,15 @@ router_pick_trusteddirserver_impl(authority_type_t type, int flags,
smartlist_free(tunnel);
smartlist_free(overloaded_direct);
smartlist_free(overloaded_tunnel);
if (result == NULL && try_excluding && !options->StrictNodes && n_excluded) {
/* If we got no result, and we are excluding nodes, and StrictNodes is
* not set, try again without excluding nodes. */
try_excluding = 0;
n_excluded = 0;
goto retry_without_exclude;
}
return result;
}
@ -1367,7 +1406,7 @@ nodelist_add_node_family(smartlist_t *sl, const node_t *node)
if (options->NodeFamilySets) {
SMARTLIST_FOREACH(options->NodeFamilySets, const routerset_t *, rs, {
if (routerset_contains_node(rs, node)) {
routerset_get_all_nodes(sl, rs, 0);
routerset_get_all_nodes(sl, rs, NULL, 0);
}
});
}
@ -1512,6 +1551,8 @@ routerlist_find_my_routerinfo(void)
/** Find a router that's up, that has this IP address, and
* that allows exit to this address:port, or return NULL if there
* isn't a good one.
* Don't exit enclave to excluded relays -- it wouldn't actually
* hurt anything, but this way there are fewer confused users.
*/
const node_t *
router_find_exact_exit_enclave(const char *address, uint16_t port)
@ -1519,6 +1560,7 @@ router_find_exact_exit_enclave(const char *address, uint16_t port)
uint32_t addr;
struct in_addr in;
tor_addr_t a;
or_options_t *options = get_options();
if (!tor_inet_aton(address, &in))
return NULL; /* it's not an IP already */
@ -1530,7 +1572,8 @@ router_find_exact_exit_enclave(const char *address, uint16_t port)
if (node_get_addr_ipv4h(node) == addr &&
node->is_running &&
compare_tor_addr_to_node_policy(&a, port, node) ==
ADDR_POLICY_ACCEPTED)
ADDR_POLICY_ACCEPTED &&
!routerset_contains_node(options->_ExcludeExitNodesUnion, node))
return node;
});
return NULL;
@ -5484,7 +5527,7 @@ routerset_needs_geoip(const routerset_t *set)
}
/** Return true iff there are no entries in <b>set</b>. */
static int
int
routerset_is_empty(const routerset_t *set)
{
return !set || smartlist_len(set->list) == 0;
@ -5580,10 +5623,11 @@ routerset_contains_node(const routerset_t *set, const node_t *node)
}
/** Add every known node_t that is a member of <b>routerset</b> to
* <b>out</b>. If <b>running_only</b>, only add the running ones. */
* <b>out</b>, but never add any that are part of <b>excludeset</b>.
* If <b>running_only</b>, only add the running ones. */
void
routerset_get_all_nodes(smartlist_t *out, const routerset_t *routerset,
int running_only)
const routerset_t *excludeset, int running_only)
{ /* XXXX MOVE */
tor_assert(out);
if (!routerset || !routerset->list)
@ -5591,12 +5635,13 @@ routerset_get_all_nodes(smartlist_t *out, const routerset_t *routerset,
if (routerset_is_list(routerset)) {
/* No routers are specified by type; all are given by name or digest.
* we can do a lookup in O(len(list)). */
* we can do a lookup in O(len(routerset)). */
SMARTLIST_FOREACH(routerset->list, const char *, name, {
const node_t *node = node_get_by_nickname(name, 1);
if (node) {
if (!running_only || node->is_running)
smartlist_add(out, (void*)node);
if (!routerset_contains_node(excludeset, node))
smartlist_add(out, (void*)node);
}
});
} else {
@ -5606,12 +5651,14 @@ routerset_get_all_nodes(smartlist_t *out, const routerset_t *routerset,
SMARTLIST_FOREACH(nodes, const node_t *, node, {
if (running_only && !node->is_running)
continue;
if (routerset_contains_node(routerset, node))
if (routerset_contains_node(routerset, node) &&
!routerset_contains_node(excludeset, node))
smartlist_add(out, (void*)node);
});
}
}
#if 0
/** Add to <b>target</b> every node_t from <b>source</b> except:
*
* 1) Don't add it if <b>include</b> is non-empty and the relay isn't in
@ -5642,6 +5689,7 @@ routersets_get_node_disjunction(smartlist_t *target,
}
});
}
#endif
/** Remove every node_t from <b>lst</b> that is in <b>routerset</b>. */
void
@ -5673,10 +5721,15 @@ routerset_to_string(const routerset_t *set)
int
routerset_equal(const routerset_t *old, const routerset_t *new)
{
if (old == NULL && new == NULL)
if (routerset_is_empty(old) && routerset_is_empty(new)) {
/* Two empty sets are equal */
return 1;
else if (old == NULL || new == NULL)
} else if (routerset_is_empty(old) || routerset_is_empty(new)) {
/* An empty set is equal to nothing else. */
return 0;
}
tor_assert(old != NULL);
tor_assert(new != NULL);
if (smartlist_len(old->list) != smartlist_len(new->list))
return 0;

View File

@ -169,6 +169,7 @@ int routerset_parse(routerset_t *target, const char *s,
void routerset_union(routerset_t *target, const routerset_t *source);
int routerset_is_list(const routerset_t *set);
int routerset_needs_geoip(const routerset_t *set);
int routerset_is_empty(const routerset_t *set);
int routerset_contains_router(const routerset_t *set, const routerinfo_t *ri,
country_t country);
int routerset_contains_routerstatus(const routerset_t *set,
@ -176,15 +177,20 @@ int routerset_contains_routerstatus(const routerset_t *set,
country_t country);
int routerset_contains_extendinfo(const routerset_t *set,
const extend_info_t *ei);
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);
#if 0
void routersets_get_node_disjunction(smartlist_t *target,
const smartlist_t *source,
const routerset_t *include,
const routerset_t *exclude, int running_only);
#endif
void routerset_subtract_nodes(smartlist_t *out,
const routerset_t *routerset);
const routerset_t *routerset);
char *routerset_to_string(const routerset_t *routerset);
int routerset_equal(const routerset_t *old, const routerset_t *new);
void routerset_free(routerset_t *routerset);