📄 circuituse.c
字号:
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
* Copyright (c) 2007-2008, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/* $Id$ */
const char circuituse_c_id[] =
"$Id$";
/**
* \file circuituse.c
* \brief Launch the right sort of circuits and attach streams to them.
**/
#include "or.h"
/********* START VARIABLES **********/
extern circuit_t *global_circuitlist; /* from circuitlist.c */
/********* END VARIABLES ************/
static void circuit_expire_old_circuits(time_t now);
static void circuit_increment_failure_count(void);
/** Return 1 if <b>circ</b> could be returned by circuit_get_best().
* Else return 0.
*/
static int
circuit_is_acceptable(circuit_t *circ, edge_connection_t *conn,
int must_be_open, uint8_t purpose,
int need_uptime, int need_internal,
time_t now)
{
routerinfo_t *exitrouter;
cpath_build_state_t *build_state;
tor_assert(circ);
tor_assert(conn);
tor_assert(conn->socks_request);
if (!CIRCUIT_IS_ORIGIN(circ))
return 0; /* this circ doesn't start at us */
if (must_be_open && (circ->state != CIRCUIT_STATE_OPEN || !circ->n_conn))
return 0; /* ignore non-open circs */
if (circ->marked_for_close)
return 0;
/* if this circ isn't our purpose, skip. */
if (purpose == CIRCUIT_PURPOSE_C_REND_JOINED && !must_be_open) {
if (circ->purpose != CIRCUIT_PURPOSE_C_ESTABLISH_REND &&
circ->purpose != CIRCUIT_PURPOSE_C_REND_READY &&
circ->purpose != CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED &&
circ->purpose != CIRCUIT_PURPOSE_C_REND_JOINED)
return 0;
} else if (purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT &&
!must_be_open) {
if (circ->purpose != CIRCUIT_PURPOSE_C_INTRODUCING &&
circ->purpose != CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT)
return 0;
} else {
if (purpose != circ->purpose)
return 0;
}
if (purpose == CIRCUIT_PURPOSE_C_GENERAL)
if (circ->timestamp_dirty &&
circ->timestamp_dirty+get_options()->MaxCircuitDirtiness <= now)
return 0;
/* decide if this circ is suitable for this conn */
/* for rend circs, circ->cpath->prev is not the last router in the
* circuit, it's the magical extra bob hop. so just check the nickname
* of the one we meant to finish at.
*/
build_state = TO_ORIGIN_CIRCUIT(circ)->build_state;
exitrouter = build_state_get_exit_router(build_state);
if (need_uptime && !build_state->need_uptime)
return 0;
if (need_internal != build_state->is_internal)
return 0;
if (purpose == CIRCUIT_PURPOSE_C_GENERAL) {
if (!exitrouter && !build_state->onehop_tunnel) {
log_debug(LD_CIRC,"Not considering circuit with unknown router.");
return 0; /* this circuit is screwed and doesn't know it yet,
* or is a rendezvous circuit. */
}
if (build_state->onehop_tunnel) {
if (!conn->want_onehop) {
log_debug(LD_CIRC,"Skipping one-hop circuit.");
return 0;
}
tor_assert(conn->chosen_exit_name);
if (build_state->chosen_exit) {
char digest[DIGEST_LEN];
if (hexdigest_to_digest(conn->chosen_exit_name, digest) < 0 ||
memcmp(digest, build_state->chosen_exit->identity_digest,
DIGEST_LEN))
return 0; /* this is a circuit to somewhere else */
}
} else {
if (conn->want_onehop) {
/* don't use three-hop circuits -- that could hurt our anonymity. */
return 0;
}
}
if (exitrouter && !connection_ap_can_use_exit(conn, exitrouter)) {
/* can't exit from this router */
return 0;
}
} else { /* not general */
if (rend_cmp_service_ids(conn->rend_query,
TO_ORIGIN_CIRCUIT(circ)->rend_query)) {
/* this circ is not for this conn */
return 0;
}
}
return 1;
}
/** Return 1 if circuit <b>a</b> is better than circuit <b>b</b> for
* <b>purpose</b>, and return 0 otherwise. Used by circuit_get_best.
*/
static int
circuit_is_better(circuit_t *a, circuit_t *b, uint8_t purpose)
{
switch (purpose) {
case CIRCUIT_PURPOSE_C_GENERAL:
/* if it's used but less dirty it's best;
* else if it's more recently created it's best
*/
if (b->timestamp_dirty) {
if (a->timestamp_dirty &&
a->timestamp_dirty > b->timestamp_dirty)
return 1;
} else {
if (a->timestamp_dirty ||
a->timestamp_created > b->timestamp_created)
return 1;
if (CIRCUIT_IS_ORIGIN(b) &&
TO_ORIGIN_CIRCUIT(b)->build_state->is_internal)
return 1;
}
break;
case CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT:
/* the closer it is to ack_wait the better it is */
if (a->purpose > b->purpose)
return 1;
break;
case CIRCUIT_PURPOSE_C_REND_JOINED:
/* the closer it is to rend_joined the better it is */
if (a->purpose > b->purpose)
return 1;
break;
}
return 0;
}
/** Find the best circ that conn can use, preferably one which is
* dirty. Circ must not be too old.
*
* Conn must be defined.
*
* If must_be_open, ignore circs not in CIRCUIT_STATE_OPEN.
*
* circ_purpose specifies what sort of circuit we must have.
* It can be C_GENERAL, C_INTRODUCE_ACK_WAIT, or C_REND_JOINED.
*
* If it's REND_JOINED and must_be_open==0, then return the closest
* rendezvous-purposed circuit that you can find.
*
* If it's INTRODUCE_ACK_WAIT and must_be_open==0, then return the
* closest introduce-purposed circuit that you can find.
*/
static origin_circuit_t *
circuit_get_best(edge_connection_t *conn, int must_be_open, uint8_t purpose,
int need_uptime, int need_internal)
{
circuit_t *circ, *best=NULL;
time_t now = time(NULL);
tor_assert(conn);
tor_assert(purpose == CIRCUIT_PURPOSE_C_GENERAL ||
purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT ||
purpose == CIRCUIT_PURPOSE_C_REND_JOINED);
for (circ=global_circuitlist;circ;circ = circ->next) {
if (!circuit_is_acceptable(circ,conn,must_be_open,purpose,
need_uptime,need_internal,now))
continue;
/* now this is an acceptable circ to hand back. but that doesn't
* mean it's the *best* circ to hand back. try to decide.
*/
if (!best || circuit_is_better(circ,best,purpose))
best = circ;
}
return best ? TO_ORIGIN_CIRCUIT(best) : NULL;
}
/** Close all circuits that start at us, aren't open, and were born
* at least CircuitBuildTimeout seconds ago.
*/
void
circuit_expire_building(time_t now)
{
circuit_t *victim, *circ = global_circuitlist;
time_t cutoff = now - get_options()->CircuitBuildTimeout;
time_t begindir_cutoff = now - get_options()->CircuitBuildTimeout/2;
cpath_build_state_t *build_state;
while (circ) {
victim = circ;
circ = circ->next;
if (!CIRCUIT_IS_ORIGIN(victim) || /* didn't originate here */
victim->marked_for_close) /* don't mess with marked circs */
continue;
build_state = TO_ORIGIN_CIRCUIT(victim)->build_state;
if (victim->timestamp_created >
((build_state && build_state->onehop_tunnel) ?
begindir_cutoff : cutoff))
continue; /* it's still young, leave it alone */
#if 0
/* some debug logs, to help track bugs */
if (victim->purpose >= CIRCUIT_PURPOSE_C_INTRODUCING &&
victim->purpose <= CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED) {
if (!victim->timestamp_dirty)
log_fn(LOG_DEBUG,"Considering %sopen purp %d to %s (circid %d)."
"(clean).",
victim->state == CIRCUIT_STATE_OPEN ? "" : "non",
victim->purpose, victim->build_state->chosen_exit_name,
victim->n_circ_id);
else
log_fn(LOG_DEBUG,"Considering %sopen purp %d to %s (circid %d). "
"%d secs since dirty.",
victim->state == CIRCUIT_STATE_OPEN ? "" : "non",
victim->purpose, victim->build_state->chosen_exit_name,
victim->n_circ_id,
(int)(now - victim->timestamp_dirty));
}
#endif
/* if circ is !open, or if it's open but purpose is a non-finished
* intro or rend, then mark it for close */
if (victim->state == CIRCUIT_STATE_OPEN) {
switch (victim->purpose) {
default: /* most open circuits can be left alone. */
continue; /* yes, continue inside a switch refers to the nearest
* enclosing loop. C is smart. */
case CIRCUIT_PURPOSE_C_ESTABLISH_REND:
case CIRCUIT_PURPOSE_C_INTRODUCING:
case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO:
break; /* too old, need to die */
case CIRCUIT_PURPOSE_C_REND_READY:
/* it's a rend_ready circ -- has it already picked a query? */
/* c_rend_ready circs measure age since timestamp_dirty,
* because that's set when they switch purposes
*/
if (TO_ORIGIN_CIRCUIT(victim)->rend_query[0] ||
victim->timestamp_dirty > cutoff)
continue;
break;
case CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED:
case CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT:
/* rend and intro circs become dirty each time they
* make an introduction attempt. so timestamp_dirty
* will reflect the time since the last attempt.
*/
if (victim->timestamp_dirty > cutoff)
continue;
break;
}
}
if (victim->n_conn)
log_info(LD_CIRC,"Abandoning circ %s:%d:%d (state %d:%s, purpose %d)",
victim->n_conn->_base.address, victim->n_port,
victim->n_circ_id,
victim->state, circuit_state_to_string(victim->state),
victim->purpose);
else
log_info(LD_CIRC,"Abandoning circ %d (state %d:%s, purpose %d)",
victim->n_circ_id, victim->state,
circuit_state_to_string(victim->state), victim->purpose);
circuit_log_path(LOG_INFO,LD_CIRC,TO_ORIGIN_CIRCUIT(victim));
circuit_mark_for_close(victim, END_CIRC_REASON_TIMEOUT);
}
}
/** Remove any elements in <b>needed_ports</b> that are handled by an
* open or in-progress circuit.
*/
void
circuit_remove_handled_ports(smartlist_t *needed_ports)
{
int i;
uint16_t *port;
for (i = 0; i < smartlist_len(needed_ports); ++i) {
port = smartlist_get(needed_ports, i);
tor_assert(*port);
if (circuit_stream_is_being_handled(NULL, *port,
MIN_CIRCUITS_HANDLING_STREAM)) {
// log_debug(LD_CIRC,"Port %d is already being handled; removing.", port);
smartlist_del(needed_ports, i--);
tor_free(port);
} else {
log_debug(LD_CIRC,"Port %d is not handled.", *port);
}
}
}
/** Return 1 if at least <b>min</b> general-purpose non-internal circuits
* will have an acceptable exit node for exit stream <b>conn</b> if it
* is defined, else for "*:port".
* Else return 0.
*/
int
circuit_stream_is_being_handled(edge_connection_t *conn,
uint16_t port, int min)
{
circuit_t *circ;
routerinfo_t *exitrouter;
int num=0;
time_t now = time(NULL);
int need_uptime = smartlist_string_num_isin(get_options()->LongLivedPorts,
conn ? conn->socks_request->port : port);
for (circ=global_circuitlist;circ;circ = circ->next) {
if (CIRCUIT_IS_ORIGIN(circ) &&
!circ->marked_for_close &&
circ->purpose == CIRCUIT_PURPOSE_C_GENERAL &&
(!circ->timestamp_dirty ||
circ->timestamp_dirty + get_options()->MaxCircuitDirtiness > now)) {
cpath_build_state_t *build_state = TO_ORIGIN_CIRCUIT(circ)->build_state;
if (build_state->is_internal || build_state->onehop_tunnel)
continue;
exitrouter = build_state_get_exit_router(build_state);
if (exitrouter && (!need_uptime || build_state->need_uptime)) {
int ok;
if (conn) {
ok = connection_ap_can_use_exit(conn, exitrouter);
} else {
addr_policy_result_t r = compare_addr_to_addr_policy(
0, port, exitrouter->exit_policy);
ok = r != ADDR_POLICY_REJECTED && r != ADDR_POLICY_PROBABLY_REJECTED;
}
if (ok) {
if (++num >= min)
return 1;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -