📄 circuituse.c
字号:
*/
int failed_at_last_hop = 0;
/* If the last hop isn't open, and the second-to-last is, we failed
* at the last hop. */
if (circ->cpath &&
circ->cpath->prev->state != CPATH_STATE_OPEN &&
circ->cpath->prev->prev->state == CPATH_STATE_OPEN) {
failed_at_last_hop = 1;
}
if (circ->cpath &&
circ->cpath->state != CPATH_STATE_OPEN) {
/* We failed at the first hop. If there's an OR connection
to blame, blame it. */
or_connection_t *n_conn = NULL;
if (circ->_base.n_conn) {
n_conn = circ->_base.n_conn;
} else if (circ->_base.state == CIRCUIT_STATE_OR_WAIT) {
/* we have to hunt for it */
n_conn = connection_or_get_by_identity_digest(
circ->_base.n_conn_id_digest);
}
if (n_conn) {
log_info(LD_OR,
"Our circuit failed to get a response from the first hop "
"(%s:%d). I'm going to try to rotate to a better connection.",
n_conn->_base.address, n_conn->_base.port);
n_conn->_base.or_is_obsolete = 1;
entry_guard_register_connect_status(n_conn->identity_digest, 0,
time(NULL));
}
/* if there are any one-hop streams waiting on this circuit, fail
* them now so they can retry elsewhere. */
connection_ap_fail_onehop(circ->_base.n_conn_id_digest);
}
switch (circ->_base.purpose) {
case CIRCUIT_PURPOSE_C_GENERAL:
/* If we never built the circuit, note it as a failure. */
circuit_increment_failure_count();
if (failed_at_last_hop) {
/* Make sure any streams that demand our last hop as their exit
* know that it's unlikely to happen. */
circuit_discard_optional_exit_enclaves(circ->cpath->prev->extend_info);
}
break;
case CIRCUIT_PURPOSE_TESTING:
circuit_testing_failed(circ, failed_at_last_hop);
break;
case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO:
/* at Bob, waiting for introductions */
if (circ->_base.state != CIRCUIT_STATE_OPEN) {
circuit_increment_failure_count();
}
/* no need to care here, because bob will rebuild intro
* points periodically. */
break;
case CIRCUIT_PURPOSE_C_INTRODUCING:
/* at Alice, connecting to intro point */
/* Don't increment failure count, since Bob may have picked
* the introduction point maliciously */
/* Alice will pick a new intro point when this one dies, if
* the stream in question still cares. No need to act here. */
break;
case CIRCUIT_PURPOSE_C_ESTABLISH_REND:
/* at Alice, waiting for Bob */
circuit_increment_failure_count();
/* Alice will pick a new rend point when this one dies, if
* the stream in question still cares. No need to act here. */
break;
case CIRCUIT_PURPOSE_S_CONNECT_REND:
/* at Bob, connecting to rend point */
/* Don't increment failure count, since Alice may have picked
* the rendezvous point maliciously */
log_info(LD_REND,
"Couldn't connect to Alice's chosen rend point %s "
"(%s hop failed).",
escaped(build_state_get_exit_nickname(circ->build_state)),
failed_at_last_hop?"last":"non-last");
rend_service_relaunch_rendezvous(circ);
break;
/* default:
* This won't happen in normal operation, but might happen if the
* controller did it. Just let it slide. */
}
}
/** Number of consecutive failures so far; should only be touched by
* circuit_launch_new and circuit_*_failure_count.
*/
static int n_circuit_failures = 0;
/** Before the last time we called circuit_reset_failure_count(), were
* there a lot of failures? */
static int did_circs_fail_last_period = 0;
/** Don't retry launching a new circuit if we try this many times with no
* success. */
#define MAX_CIRCUIT_FAILURES 5
/** Launch a new circuit; see circuit_launch_by_extend_info() for
* details on arguments. */
origin_circuit_t *
circuit_launch_by_router(uint8_t purpose,
routerinfo_t *exit, int flags)
{
origin_circuit_t *circ;
extend_info_t *info = NULL;
if (exit)
info = extend_info_from_router(exit);
circ = circuit_launch_by_extend_info(purpose, info, flags);
if (info)
extend_info_free(info);
return circ;
}
/** Launch a new circuit with purpose <b>purpose</b> and exit node
* <b>extend_info</b> (or NULL to select a random exit node). If flags
* contains CIRCLAUNCH_NEED_UPTIME, choose among routers with high uptime. If
* CIRCLAUNCH_NEED_CAPACITY is set, choose among routers with high bandwidth.
* If CIRCLAUNCH_IS_INTERNAL is true, the last hop need not be an exit node.
* If CIRCLAUNCH_ONEHOP_TUNNEL is set, the circuit will have only one hop.
* Return the newly allocated circuit on success, or NULL on failure. */
origin_circuit_t *
circuit_launch_by_extend_info(uint8_t purpose,
extend_info_t *extend_info,
int flags)
{
origin_circuit_t *circ;
int onehop_tunnel = flags & CIRCLAUNCH_ONEHOP_TUNNEL;
if (!onehop_tunnel && !router_have_minimum_dir_info()) {
log_debug(LD_CIRC,"Haven't fetched enough directory info yet; canceling "
"circuit launch.");
return NULL;
}
if ((extend_info || purpose != CIRCUIT_PURPOSE_C_GENERAL) &&
purpose != CIRCUIT_PURPOSE_TESTING && !onehop_tunnel) {
/* see if there are appropriate circs available to cannibalize. */
/* XXX020 if we're planning to add a hop, perhaps we want to look for
* 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);
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
* began. */
circ->_base.timestamp_created = time(NULL);
switch (purpose) {
case CIRCUIT_PURPOSE_C_ESTABLISH_REND:
case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO:
/* it's ready right now */
break;
case CIRCUIT_PURPOSE_C_INTRODUCING:
case CIRCUIT_PURPOSE_S_CONNECT_REND:
case CIRCUIT_PURPOSE_C_GENERAL:
/* need to add a new hop */
tor_assert(extend_info);
if (circuit_extend_to_new_exit(circ, extend_info) < 0)
return NULL;
break;
default:
log_warn(LD_BUG,
"unexpected purpose %d when cannibalizing a circ.",
purpose);
tor_fragile_assert();
return NULL;
}
return circ;
}
}
if (did_circs_fail_last_period &&
n_circuit_failures > MAX_CIRCUIT_FAILURES) {
/* too many failed circs in a row. don't try. */
// log_fn(LOG_INFO,"%d failures so far, not trying.",n_circuit_failures);
return NULL;
}
/* try a circ. if it fails, circuit_mark_for_close will increment
* n_circuit_failures */
return circuit_establish_circuit(purpose, extend_info, flags);
}
/** Launch a new circuit; see circuit_launch_by_extend_info() for
* details on arguments. */
origin_circuit_t *
circuit_launch_by_nickname(uint8_t purpose,
const char *exit_nickname, int flags)
{
routerinfo_t *router = NULL;
if (exit_nickname) {
router = router_get_by_nickname(exit_nickname, 1);
if (!router) {
log_warn(LD_GENERAL, "Trying to launch circ by nickname, but "
"no such OR as '%s'", exit_nickname);
return NULL;
}
}
return circuit_launch_by_router(purpose, router, flags);
}
/** Record another failure at opening a general circuit. When we have
* too many, we'll stop trying for the remainder of this minute.
*/
static void
circuit_increment_failure_count(void)
{
++n_circuit_failures;
log_debug(LD_CIRC,"n_circuit_failures now %d.",n_circuit_failures);
}
/** Reset the failure count for opening general circuits. This means
* we will try MAX_CIRCUIT_FAILURES times more (if necessary) before
* stopping again.
*/
void
circuit_reset_failure_count(int timeout)
{
if (timeout && n_circuit_failures > MAX_CIRCUIT_FAILURES)
did_circs_fail_last_period = 1;
else
did_circs_fail_last_period = 0;
n_circuit_failures = 0;
}
/** Find an open circ that we're happy to use for <b>conn</b> and return 1. If
* there isn't one, and there isn't one on the way, launch one and return
* 0. If it will never work, return -1.
*
* Write the found or in-progress or launched circ into *circp.
*/
static int
circuit_get_open_circ_or_launch(edge_connection_t *conn,
uint8_t desired_circuit_purpose,
origin_circuit_t **circp)
{
origin_circuit_t *circ;
int check_exit_policy;
int need_uptime, need_internal;
int want_onehop;
or_options_t *options = get_options();
tor_assert(conn);
tor_assert(circp);
tor_assert(conn->_base.state == AP_CONN_STATE_CIRCUIT_WAIT);
check_exit_policy =
conn->socks_request->command == SOCKS_COMMAND_CONNECT &&
!conn->use_begindir &&
!connection_edge_is_rendezvous_stream(conn);
want_onehop = conn->want_onehop;
need_uptime = !conn->want_onehop && !conn->use_begindir &&
smartlist_string_num_isin(options->LongLivedPorts,
conn->socks_request->port);
need_internal = desired_circuit_purpose != CIRCUIT_PURPOSE_C_GENERAL;
circ = circuit_get_best(conn, 1, desired_circuit_purpose,
need_uptime, need_internal);
if (circ) {
*circp = circ;
return 1; /* we're happy */
}
if (!want_onehop && !router_have_minimum_dir_info()) {
if (!connection_get_by_type(CONN_TYPE_DIR)) {
if (options->UseBridges && bridges_known_but_down()) {
log_notice(LD_APP|LD_DIR,
"Application request when we're believed to be "
"offline. Optimistically trying known bridges again.");
bridges_retry_all();
} else if (!options->UseBridges || any_bridge_descriptors_known()) {
log_notice(LD_APP|LD_DIR,
"Application request when we're believed to be "
"offline. Optimistically trying directory fetches again.");
routerlist_retry_directory_downloads(time(NULL));
}
}
/* the stream will be dealt with when router_have_minimum_dir_info becomes
* 1, or when all directory attempts fail and directory_all_unreachable()
* kills it.
*/
return 0;
}
/* Do we need to check exit policy? */
if (check_exit_policy) {
struct in_addr in;
uint32_t addr = 0;
if (tor_inet_aton(conn->socks_request->address, &in))
addr = ntohl(in.s_addr);
if (router_exit_policy_all_routers_reject(addr, conn->socks_request->port,
need_uptime)) {
log_notice(LD_APP,
"No Tor server exists that allows exit to %s:%d. Rejecting.",
safe_str(conn->socks_request->address),
conn->socks_request->port);
return -1;
}
}
/* is one already on the way? */
circ = circuit_get_best(conn, 0, desired_circuit_purpose,
need_uptime, need_internal);
if (circ)
log_debug(LD_CIRC, "one on the way!");
if (!circ) {
extend_info_t *extend_info=NULL;
uint8_t new_circ_purpose;
if (desired_circuit_purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) {
/* need to pick an intro point */
extend_info = rend_client_get_random_intro(conn->rend_query);
if (!extend_info) {
log_info(LD_REND,
"No intro points for '%s': refetching service descriptor.",
safe_str(conn->rend_query));
rend_client_refetch_renddesc(conn->rend_query);
rend_client_refetch_v2_renddesc(conn->rend_query);
conn->_base.state = AP_CONN_STATE_RENDDESC_WAIT;
return 0;
}
log_info(LD_REND,"Chose '%s' as intro point for '%s'.",
extend_info->nickname, safe_str(conn->rend_query));
}
/* If we have specified a particular exit node for our
* connection, then be sure to open a circuit to that exit node.
*/
if (desired_circuit_purpose == CIRCUIT_PURPOSE_C_GENERAL) {
if (conn->chosen_exit_name) {
routerinfo_t *r;
int opt = conn->_base.chosen_exit_optional;
r = router_get_by_nickname(conn->chosen_exit_name, 1);
if (r) {
extend_info = extend_info_from_router(r);
} else {
log_debug(LD_DIR, "considering %d, %s",
want_onehop, conn->chosen_exit_name);
if (want_onehop && conn->chosen_exit_name[0] == '$') {
/* We're asking for a one-hop circuit to a router that
* we don't have a routerinfo about. Make up an extend_info. */
char digest[DIGEST_LEN];
char *hexdigest = conn->chosen_exit_name+1;
struct in_addr in;
if (strlen(hexdigest) < HEX_DIGEST_LEN ||
base16_decode(digest,DIGEST_LEN,hexdigest,HEX_DIGEST_LEN)<0) {
log_info(LD_DIR, "Broken exit digest on tunnel conn. Closing.");
return -1;
}
if (!tor_inet_aton(conn->socks_request->address, &in)) {
log_info(LD_DIR, "Broken address on tunnel conn. Closing.");
return -1;
}
extend_info = extend_info_alloc(conn->chosen_exit_name+1,
digest, NULL, ntohl(in.s_addr),
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -