Introduce a notion of 'internal' circs, which are chosen without regard

to the exit policy of the last hop. Intro and rendezvous circs must
be internal circs, to avoid leaking information. Resolve and connect
streams can use internal circs if they want.

New circuit pooling algorithm: make sure to have enough circs around
to satisfy any predicted ports, and also make sure to have 2 internal
circs around if we've required internal circs lately (with high uptime
if we've seen that lately).

Split NewCircuitPeriod config option into NewCircuitPeriod (30 secs),
which describes how often we retry making new circuits if current ones
are dirty, and MaxCircuitDirtiness (10 mins), which describes how long
we're willing to make use of an already-dirty circuit.

Once rendezvous circuits are established, keep using the same circuit as
long as you attach a new stream to it at least every 10 minutes. (So web
browsing doesn't require you to build new rend circs every 30 seconds.)

Cannibalize GENERAL circs to be C_REND, C_INTRO, S_INTRO, and S_REND
circ as necessary, if there are any completed ones lying around when
we try to launch one.

Re-instate the ifdef's to use version-0 style introduce cells, since
there was yet another bug in handling version-1 style. We'll try switching
over again after 0.0.9 is obsolete.

Bugfix: when choosing an exit node for a new non-internal circ, don't take
into account whether it'll be useful for any pending x.onion addresses --
it won't.

Bugfix: we weren't actually publishing the hidden service descriptor when
it became dirty. So we only published it every 20 minutes or so, which
means when you first start your Tor, the hidden service will seem broken.


svn:r3360
This commit is contained in:
Roger Dingledine 2005-01-17 18:13:09 +00:00
parent 9c8c90ec2f
commit d2400a5afd
11 changed files with 306 additions and 163 deletions

View File

@ -23,7 +23,7 @@ static int
circuit_deliver_create_cell(circuit_t *circ, char *payload); circuit_deliver_create_cell(circuit_t *circ, char *payload);
static cpath_build_state_t * static cpath_build_state_t *
onion_new_cpath_build_state(uint8_t purpose, const char *exit_digest, onion_new_cpath_build_state(uint8_t purpose, const char *exit_digest,
int need_uptime, int need_capacity); int need_uptime, int need_capacity, int internal);
static int onion_extend_cpath(crypt_path_t **head_ptr, static int onion_extend_cpath(crypt_path_t **head_ptr,
cpath_build_state_t *state, routerinfo_t **router_out); cpath_build_state_t *state, routerinfo_t **router_out);
static int count_acceptable_routers(smartlist_t *routers); static int count_acceptable_routers(smartlist_t *routers);
@ -82,7 +82,9 @@ circuit_list_path(circuit_t *circ, int verbose)
elements = smartlist_create(); elements = smartlist_create();
if (verbose) { if (verbose) {
tor_snprintf(buf, sizeof(buf)-1, "circ (length %d, exit %s):", tor_snprintf(buf, sizeof(buf)-1, "%s%s circ (length %d, exit %s):",
circ->build_state->is_internal ? "internal" : "exit",
circ->build_state->need_uptime ? " (high-uptime)" : "",
circ->build_state->desired_path_len, circ->build_state->desired_path_len,
circ->build_state->chosen_exit_name); circ->build_state->chosen_exit_name);
smartlist_add(elements, tor_strdup(buf)); smartlist_add(elements, tor_strdup(buf));
@ -236,7 +238,7 @@ void circuit_dump_by_conn(connection_t *conn, int severity) {
*/ */
circuit_t * circuit_t *
circuit_establish_circuit(uint8_t purpose, const char *exit_digest, circuit_establish_circuit(uint8_t purpose, const char *exit_digest,
int need_uptime, int need_capacity) { int need_uptime, int need_capacity, int internal) {
routerinfo_t *firsthop; routerinfo_t *firsthop;
connection_t *n_conn; connection_t *n_conn;
circuit_t *circ; circuit_t *circ;
@ -244,7 +246,7 @@ circuit_establish_circuit(uint8_t purpose, const char *exit_digest,
circ = circuit_new(0, NULL); /* sets circ->p_circ_id and circ->p_conn */ circ = circuit_new(0, NULL); /* sets circ->p_circ_id and circ->p_conn */
circ->state = CIRCUIT_STATE_OR_WAIT; circ->state = CIRCUIT_STATE_OR_WAIT;
circ->build_state = onion_new_cpath_build_state(purpose, exit_digest, circ->build_state = onion_new_cpath_build_state(purpose, exit_digest,
need_uptime, need_capacity); need_uptime, need_capacity, internal);
circ->purpose = purpose; circ->purpose = purpose;
if (! circ->build_state) { if (! circ->build_state) {
@ -951,6 +953,7 @@ choose_good_exit_server_general(routerlist_t *dir, int need_uptime,
if (carray[j]->type != CONN_TYPE_AP || if (carray[j]->type != CONN_TYPE_AP ||
carray[j]->state != AP_CONN_STATE_CIRCUIT_WAIT || carray[j]->state != AP_CONN_STATE_CIRCUIT_WAIT ||
carray[j]->marked_for_close || carray[j]->marked_for_close ||
connection_edge_is_rendezvous_stream(carray[j]) ||
circuit_stream_is_being_handled(carray[j], 0, MIN_CIRCUITS_HANDLING_STREAM)) circuit_stream_is_being_handled(carray[j], 0, MIN_CIRCUITS_HANDLING_STREAM))
continue; /* Skip everything but APs in CIRCUIT_WAIT */ continue; /* Skip everything but APs in CIRCUIT_WAIT */
if (connection_ap_can_use_exit(carray[j], router)) { if (connection_ap_can_use_exit(carray[j], router)) {
@ -1080,7 +1083,7 @@ choose_good_exit_server(uint8_t purpose, routerlist_t *dir,
*/ */
static cpath_build_state_t * static cpath_build_state_t *
onion_new_cpath_build_state(uint8_t purpose, const char *exit_digest, onion_new_cpath_build_state(uint8_t purpose, const char *exit_digest,
int need_uptime, int need_capacity) int need_uptime, int need_capacity, int internal)
{ {
routerlist_t *rl; routerlist_t *rl;
int r; int r;
@ -1097,6 +1100,7 @@ onion_new_cpath_build_state(uint8_t purpose, const char *exit_digest,
info->desired_path_len = r; info->desired_path_len = r;
info->need_uptime = need_uptime; info->need_uptime = need_uptime;
info->need_capacity = need_capacity; info->need_capacity = need_capacity;
info->is_internal = internal;
if (exit_digest) { /* the circuit-builder pre-requested one */ if (exit_digest) { /* the circuit-builder pre-requested one */
memcpy(info->chosen_exit_digest, exit_digest, DIGEST_LEN); memcpy(info->chosen_exit_digest, exit_digest, DIGEST_LEN);
exit = router_get_by_digest(exit_digest); exit = router_get_by_digest(exit_digest);
@ -1121,6 +1125,35 @@ onion_new_cpath_build_state(uint8_t purpose, const char *exit_digest,
return info; return info;
} }
/** Take the open circ originating here, give it a new exit destination
* to exit_digest (use nickname directly if it's provided, else strdup
* out of router->nickname), and get it to send the next extend cell.
*/
int
circuit_append_new_hop(circuit_t *circ, char *nickname, const char *exit_digest) {
routerinfo_t *exit = router_get_by_digest(exit_digest);
tor_assert(CIRCUIT_IS_ORIGIN(circ));
circ->state = CIRCUIT_STATE_BUILDING;
tor_free(circ->build_state->chosen_exit_name);
if (nickname) {
circ->build_state->chosen_exit_name = nickname;
} else if (exit) {
circ->build_state->chosen_exit_name = tor_strdup(exit->nickname);
} else {
circ->build_state->chosen_exit_name = tor_malloc(HEX_DIGEST_LEN+1);
base16_encode(circ->build_state->chosen_exit_name, HEX_DIGEST_LEN+1,
exit_digest, DIGEST_LEN);
}
memcpy(circ->build_state->chosen_exit_digest, exit_digest, DIGEST_LEN);
++circ->build_state->desired_path_len;
if (circuit_send_next_onion_skin(circ)<0) {
log_fn(LOG_WARN, "Couldn't extend circuit to new point '%s'.", circ->build_state->chosen_exit_name);
circuit_mark_for_close(circ);
return -1;
}
return 0;
}
/** Return the number of routers in <b>routers</b> that are currently up /** Return the number of routers in <b>routers</b> that are currently up
* and available for building circuits through. * and available for building circuits through.
*/ */

View File

@ -292,41 +292,29 @@ circuit_t *circuit_get_rendezvous(const char *cookie)
return NULL; return NULL;
} }
/** Count the number of circs originating here that aren't open, and /** Return a circuit that is open, has specified <b>purpose</b>,
* that have the specified <b>purpose</b>. */ * has a timestamp_dirty value of 0, and is uptime/capacity/internal
int circuit_count_building(uint8_t purpose) { * if required; or NULL if no circuit fits this description.
circuit_t *circ;
int num=0;
for (circ=global_circuitlist;circ;circ = circ->next) {
if (CIRCUIT_IS_ORIGIN(circ) &&
circ->state != CIRCUIT_STATE_OPEN &&
circ->purpose == purpose &&
!circ->marked_for_close)
num++;
}
return num;
}
/** Return the circuit that is open, has specified <b>purpose</b>,
* has a timestamp_dirty value of 0, and was created most recently,
* or NULL if no circuit fits this description.
*/ */
circuit_t * circuit_t *
circuit_get_youngest_clean_open(uint8_t purpose) { circuit_get_clean_open(uint8_t purpose, int need_uptime,
int need_capacity, int internal) {
circuit_t *circ; circuit_t *circ;
circuit_t *youngest=NULL;
for (circ=global_circuitlist;circ;circ = circ->next) { log_fn(LOG_DEBUG,"Hunting for a circ to cannibalize: purpose %d, uptime %d, capacity %d, internal %d", purpose, need_uptime, need_capacity, internal);
for (circ=global_circuitlist; circ; circ = circ->next) {
if (CIRCUIT_IS_ORIGIN(circ) && if (CIRCUIT_IS_ORIGIN(circ) &&
circ->state == CIRCUIT_STATE_OPEN && circ->state == CIRCUIT_STATE_OPEN &&
!circ->marked_for_close && !circ->marked_for_close &&
circ->purpose == purpose && circ->purpose == purpose &&
!circ->timestamp_dirty && !circ->timestamp_dirty &&
(!youngest || youngest->timestamp_created < circ->timestamp_created)) (!need_uptime || circ->build_state->need_uptime) &&
youngest = circ; (!need_capacity || circ->build_state->need_capacity) &&
(!internal || circ->build_state->is_internal))
return circ;
} }
return youngest; return NULL;
} }
/** Mark <b>circ</b> to be closed next time we call /** Mark <b>circ</b> to be closed next time we call

View File

@ -35,6 +35,9 @@ static int circuit_is_acceptable(circuit_t *circ,
time_t now) time_t now)
{ {
routerinfo_t *exitrouter; routerinfo_t *exitrouter;
tor_assert(circ);
tor_assert(conn);
tor_assert(conn->socks_request);
if (!CIRCUIT_IS_ORIGIN(circ)) if (!CIRCUIT_IS_ORIGIN(circ))
return 0; /* this circ doesn't start at us */ return 0; /* this circ doesn't start at us */
@ -61,40 +64,36 @@ static int circuit_is_acceptable(circuit_t *circ,
if (purpose == CIRCUIT_PURPOSE_C_GENERAL) if (purpose == CIRCUIT_PURPOSE_C_GENERAL)
if (circ->timestamp_dirty && if (circ->timestamp_dirty &&
circ->timestamp_dirty+get_options()->NewCircuitPeriod <= now) circ->timestamp_dirty+get_options()->MaxCircuitDirtiness <= now)
return 0; return 0;
if (conn) { /* decide if this circ is suitable for this conn */
/* decide if this circ is suitable for this conn */
/* for rend circs, circ->cpath->prev is not the last router in the /* 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 * circuit, it's the magical extra bob hop. so just check the nickname
* of the one we meant to finish at. * of the one we meant to finish at.
*/ */
exitrouter = router_get_by_digest(circ->build_state->chosen_exit_digest); exitrouter = router_get_by_digest(circ->build_state->chosen_exit_digest);
if (!exitrouter) { if (!exitrouter) {
log_fn(LOG_INFO,"Skipping broken circ (exit router vanished)"); log_fn(LOG_INFO,"Skipping broken circ (exit router vanished)");
return 0; /* this circuit is screwed and doesn't know it yet */ return 0; /* this circuit is screwed and doesn't know it yet */
} }
if (!circ->build_state->need_uptime && if (!circ->build_state->need_uptime &&
smartlist_string_num_isin(get_options()->LongLivedPorts, smartlist_string_num_isin(get_options()->LongLivedPorts,
conn->socks_request->port)) conn->socks_request->port))
return 0; return 0;
if (conn->socks_request && if (conn->socks_request->command == SOCKS_COMMAND_CONNECT) {
conn->socks_request->command == SOCKS_COMMAND_RESOLVE) { if (purpose == CIRCUIT_PURPOSE_C_GENERAL) {
} else if (purpose == CIRCUIT_PURPOSE_C_GENERAL) {
if (!connection_ap_can_use_exit(conn, exitrouter)) { if (!connection_ap_can_use_exit(conn, exitrouter)) {
/* can't exit from this router */ /* can't exit from this router */
return 0; return 0;
} }
} else { /* not general */ } else { /* not general */
if (rend_cmp_service_ids(conn->rend_query, circ->rend_query) && if (rend_cmp_service_ids(conn->rend_query, circ->rend_query)) {
(circ->rend_query[0] || purpose != CIRCUIT_PURPOSE_C_REND_JOINED)) { /* this circ is not for this conn */
/* this circ is not for this conn, and it's not suitable
* for cannibalizing either */
return 0; return 0;
} }
} }
@ -178,9 +177,8 @@ circuit_get_best(connection_t *conn, int must_be_open, uint8_t purpose)
return best; return best;
} }
/** Circuits that were born at the end of their second might be expired /** If we find a circuit that isn't open yet and was born this many
* after 30.1 seconds; circuits born at the beginning might be expired * seconds ago, then assume something went wrong, and cull it.
* after closer to 31 seconds.
*/ */
#define MIN_SECONDS_BEFORE_EXPIRING_CIRC 30 #define MIN_SECONDS_BEFORE_EXPIRING_CIRC 30
@ -289,7 +287,7 @@ int circuit_stream_is_being_handled(connection_t *conn, uint16_t port, int min)
!circ->marked_for_close && !circ->marked_for_close &&
circ->purpose == CIRCUIT_PURPOSE_C_GENERAL && circ->purpose == CIRCUIT_PURPOSE_C_GENERAL &&
(!circ->timestamp_dirty || (!circ->timestamp_dirty ||
circ->timestamp_dirty + get_options()->NewCircuitPeriod < now)) { circ->timestamp_dirty + get_options()->MaxCircuitDirtiness < now)) {
exitrouter = router_get_by_digest(circ->build_state->chosen_exit_digest); exitrouter = router_get_by_digest(circ->build_state->chosen_exit_digest);
if (exitrouter && if (exitrouter &&
(!need_uptime || circ->build_state->need_uptime) && (!need_uptime || circ->build_state->need_uptime) &&
@ -305,6 +303,65 @@ int circuit_stream_is_being_handled(connection_t *conn, uint16_t port, int min)
return 0; return 0;
} }
/** Don't keep more than 10 unused open circuits around. */
#define MAX_UNUSED_OPEN_CIRCUITS 10
/** Figure out how many circuits we have open that are clean. Make
* sure it's enough for all the upcoming behaviors we predict we'll have.
* But if we have too many, close the not-so-useful ones.
*/
static void
circuit_predict_and_launch_new(void)
{
circuit_t *circ;
int num=0, num_internal=0, num_uptime_internal=0;
int hidserv_needs_uptime=0, hidserv_needs_capacity=1;
int port_needs_uptime=0, port_needs_capacity=1;
int need_ports, need_hidserv;
time_t now = time(NULL);
/* check if we know of a port that's been requested recently
* and no circuit is currently available that can handle it. */
need_ports = !circuit_all_predicted_ports_handled(now, &port_needs_uptime,
&port_needs_capacity);
need_hidserv = rep_hist_get_predicted_hidserv(now, &hidserv_needs_uptime,
&hidserv_needs_capacity);
for (circ=global_circuitlist;circ;circ = circ->next) {
if (!CIRCUIT_IS_ORIGIN(circ))
continue;
if (circ->marked_for_close)
continue; /* don't mess with marked circs */
if (circ->timestamp_dirty)
continue; /* only count clean circs */
if (circ->purpose != CIRCUIT_PURPOSE_C_GENERAL)
continue; /* only pay attention to general-purpose circs */
num++;
if (circ->build_state->is_internal)
num_internal++;
if (circ->build_state->need_uptime && circ->build_state->is_internal)
num_uptime_internal++;
}
if (num < MAX_UNUSED_OPEN_CIRCUITS) {
/* perhaps we want another */
if (need_ports) {
log_fn(LOG_INFO,"Have %d clean circs (%d internal), need another exit circ.",
num, num_internal);
circuit_launch_by_identity(CIRCUIT_PURPOSE_C_GENERAL, NULL,
port_needs_uptime, port_needs_capacity, 0);
} else if (need_hidserv &&
((num_uptime_internal<2 && hidserv_needs_uptime) ||
num_internal<2)) {
log_fn(LOG_INFO,"Have %d clean circs (%d uptime-internal, %d internal),"
" need another hidserv circ.", num, num_uptime_internal, num_internal);
circuit_launch_by_identity(CIRCUIT_PURPOSE_C_GENERAL, NULL,
hidserv_needs_uptime, hidserv_needs_capacity, 1);
}
}
}
/** Build a new test circuit every 5 minutes */ /** Build a new test circuit every 5 minutes */
#define TESTING_CIRCUIT_INTERVAL 300 #define TESTING_CIRCUIT_INTERVAL 300
@ -315,8 +372,6 @@ int circuit_stream_is_being_handled(connection_t *conn, uint16_t port, int min)
*/ */
void circuit_build_needed_circs(time_t now) { void circuit_build_needed_circs(time_t now) {
static long time_to_new_circuit = 0; static long time_to_new_circuit = 0;
circuit_t *circ;
int need_uptime=0, need_capacity=1;
/* launch a new circ for any pending streams that need one */ /* launch a new circ for any pending streams that need one */
connection_ap_attach_pending(); connection_ap_attach_pending();
@ -325,8 +380,6 @@ void circuit_build_needed_circs(time_t now) {
if (has_fetched_directory) if (has_fetched_directory)
rend_services_introduce(); rend_services_introduce();
circ = circuit_get_youngest_clean_open(CIRCUIT_PURPOSE_C_GENERAL);
if (time_to_new_circuit < now) { if (time_to_new_circuit < now) {
circuit_reset_failure_count(1); circuit_reset_failure_count(1);
time_to_new_circuit = now + get_options()->NewCircuitPeriod; time_to_new_circuit = now + get_options()->NewCircuitPeriod;
@ -334,37 +387,17 @@ void circuit_build_needed_circs(time_t now) {
client_dns_clean(); client_dns_clean();
circuit_expire_old_circuits(); circuit_expire_old_circuits();
#if 0 /* disable for now, until predict-and-launch-new can cull leftovers */
circ = circuit_get_youngest_clean_open(CIRCUIT_PURPOSE_C_GENERAL);
if (get_options()->RunTesting && if (get_options()->RunTesting &&
circ && circ &&
circ->timestamp_created + TESTING_CIRCUIT_INTERVAL < now) { circ->timestamp_created + TESTING_CIRCUIT_INTERVAL < now) {
log_fn(LOG_INFO,"Creating a new testing circuit."); log_fn(LOG_INFO,"Creating a new testing circuit.");
circuit_launch_by_identity(CIRCUIT_PURPOSE_C_GENERAL, NULL, 0, 0); circuit_launch_by_identity(CIRCUIT_PURPOSE_C_GENERAL, NULL, 0, 0, 0);
} }
}
#if 0
/** How many simultaneous in-progress general-purpose circuits do we
* want to be building at once, if there are no open general-purpose
* circuits?
*/
#define CIRCUIT_MIN_BUILDING_GENERAL 5
/* if there's no open circ, and less than 5 are on the way,
* go ahead and try another. */
if (!circ && circuit_count_building(CIRCUIT_PURPOSE_C_GENERAL)
< CIRCUIT_MIN_BUILDING_GENERAL) {
circuit_launch_by_identity(CIRCUIT_PURPOSE_C_GENERAL, NULL);
}
#endif #endif
/* if we know of a port that's been requested recently and no
* circuit is currently available that can handle it, start one
* for that too. */
if (!circuit_all_predicted_ports_handled(now, &need_uptime, &need_capacity)) {
circuit_launch_by_identity(CIRCUIT_PURPOSE_C_GENERAL, NULL,
need_uptime, need_capacity);
} }
circuit_predict_and_launch_new();
/* XXX count idle rendezvous circs and build more */
} }
/** If the stream <b>conn</b> is a member of any of the linked /** If the stream <b>conn</b> is a member of any of the linked
@ -469,24 +502,14 @@ void circuit_about_to_close_connection(connection_t *conn) {
} /* end switch */ } /* end switch */
} }
/** Don't keep more than 10 unused open circuits around. */
#define MAX_UNUSED_OPEN_CIRCUITS 10
/** Find each circuit that has been dirty for too long, and has /** Find each circuit that has been dirty for too long, and has
* no streams on it: mark it for close. * no streams on it: mark it for close.
*
* Also, if there are more than MAX_UNUSED_OPEN_CIRCUITS open and
* unused circuits, then mark the excess circs for close.
*/ */
static void static void
circuit_expire_old_circuits(void) circuit_expire_old_circuits(void)
{ {
circuit_t *circ; circuit_t *circ;
time_t now = time(NULL); time_t now = time(NULL);
smartlist_t *unused_open_circs;
int i;
unused_open_circs = smartlist_create();
for (circ = global_circuitlist; circ; circ = circ->next) { for (circ = global_circuitlist; circ; circ = circ->next) {
if (circ->marked_for_close) if (circ->marked_for_close)
@ -495,8 +518,8 @@ circuit_expire_old_circuits(void)
* on it, mark it for close. * on it, mark it for close.
*/ */
if (circ->timestamp_dirty && if (circ->timestamp_dirty &&
circ->timestamp_dirty + get_options()->NewCircuitPeriod < now && circ->timestamp_dirty + get_options()->MaxCircuitDirtiness < now &&
!circ->p_conn && /* we're the origin */ CIRCUIT_IS_ORIGIN(circ) &&
!circ->p_streams /* nothing attached */ ) { !circ->p_streams /* nothing attached */ ) {
log_fn(LOG_DEBUG,"Closing n_circ_id %d (dirty %d secs ago, purp %d)",circ->n_circ_id, log_fn(LOG_DEBUG,"Closing n_circ_id %d (dirty %d secs ago, purp %d)",circ->n_circ_id,
(int)(now - circ->timestamp_dirty), circ->purpose); (int)(now - circ->timestamp_dirty), circ->purpose);
@ -512,23 +535,9 @@ circuit_expire_old_circuits(void)
log_fn(LOG_DEBUG,"Closing circuit that has been unused for %d seconds.", log_fn(LOG_DEBUG,"Closing circuit that has been unused for %d seconds.",
(int)(now - circ->timestamp_created)); (int)(now - circ->timestamp_created));
circuit_mark_for_close(circ); circuit_mark_for_close(circ);
} else {
/* Also, gather a list of open unused general circuits that we created.
* Because we add elements to the front of global_circuitlist,
* the last elements of unused_open_circs will be the oldest
* ones.
*/
smartlist_add(unused_open_circs, circ);
} }
} }
} }
for (i = MAX_UNUSED_OPEN_CIRCUITS; i < smartlist_len(unused_open_circs); ++i) {
circuit_t *circ = smartlist_get(unused_open_circs, i);
log_fn(LOG_DEBUG,"Expiring excess clean circ (n_circ_id %d, purp %d)",
circ->n_circ_id, circ->purpose);
circuit_mark_for_close(circ);
}
smartlist_free(unused_open_circs);
} }
/** The circuit <b>circ</b> has just become open. Take the next /** The circuit <b>circ</b> has just become open. Take the next
@ -646,13 +655,52 @@ static int did_circs_fail_last_period = 0;
circuit_t * circuit_t *
circuit_launch_by_identity(uint8_t purpose, const char *exit_digest, circuit_launch_by_identity(uint8_t purpose, const char *exit_digest,
int need_uptime, int need_capacity) int need_uptime, int need_capacity, int internal)
{ {
circuit_t *circ;
if (!has_fetched_directory) { if (!has_fetched_directory) {
log_fn(LOG_DEBUG,"Haven't fetched directory yet; canceling circuit launch."); log_fn(LOG_DEBUG,"Haven't fetched directory yet; canceling circuit launch.");
return NULL; return NULL;
} }
if (purpose != CIRCUIT_PURPOSE_C_GENERAL) {
/* see if there are appropriate circs available to cannibalize. */
if ((circ = circuit_get_clean_open(CIRCUIT_PURPOSE_C_GENERAL, need_uptime,
need_capacity, internal))) {
log_fn(LOG_INFO,"Cannibalizing circ '%s' for purpose %d",
circ->build_state->chosen_exit_name, purpose);
circ->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->timestamp_created = time(NULL);
switch (purpose) {
case CIRCUIT_PURPOSE_C_ESTABLISH_REND:
/* it's ready right now */
/* XXX should we call control_event_circuit_status() here? */
rend_client_rendcirc_has_opened(circ);
break;
case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO:
/* it's ready right now */
rend_service_intro_has_opened(circ);
break;
case CIRCUIT_PURPOSE_C_INTRODUCING:
case CIRCUIT_PURPOSE_S_CONNECT_REND:
/* need to add a new hop */
tor_assert(exit_digest);
if (circuit_append_new_hop(circ, NULL, exit_digest) < 0)
return NULL;
break;
default:
log_fn(LOG_WARN,"Bug: unexpected purpose %d when cannibalizing a general circ.",
purpose);
return NULL;
}
return circ;
}
}
if (did_circs_fail_last_period && if (did_circs_fail_last_period &&
n_circuit_failures > MAX_CIRCUIT_FAILURES) { n_circuit_failures > MAX_CIRCUIT_FAILURES) {
/* too many failed circs in a row. don't try. */ /* too many failed circs in a row. don't try. */
@ -662,13 +710,13 @@ circuit_launch_by_identity(uint8_t purpose, const char *exit_digest,
/* try a circ. if it fails, circuit_mark_for_close will increment n_circuit_failures */ /* try a circ. if it fails, circuit_mark_for_close will increment n_circuit_failures */
return circuit_establish_circuit(purpose, exit_digest, return circuit_establish_circuit(purpose, exit_digest,
need_uptime, need_capacity); need_uptime, need_capacity, internal);
} }
/** Launch a new circuit and return a pointer to it. Return NULL if you failed. */ /** Launch a new circuit and return a pointer to it. Return NULL if you failed. */
circuit_t * circuit_t *
circuit_launch_by_nickname(uint8_t purpose, const char *exit_nickname, circuit_launch_by_nickname(uint8_t purpose, const char *exit_nickname,
int need_uptime, int need_capacity) int need_uptime, int need_capacity, int internal)
{ {
const char *digest = NULL; const char *digest = NULL;
@ -681,7 +729,7 @@ circuit_launch_by_nickname(uint8_t purpose, const char *exit_nickname,
digest = r->identity_digest; digest = r->identity_digest;
} }
return circuit_launch_by_identity(purpose, digest, return circuit_launch_by_identity(purpose, digest,
need_uptime, need_capacity); need_uptime, need_capacity, internal);
} }
/** Record another failure at opening a general circuit. When we have /** Record another failure at opening a general circuit. When we have
@ -762,6 +810,7 @@ circuit_get_open_circ_or_launch(connection_t *conn,
if (!circ) { if (!circ) {
char *exitname=NULL; char *exitname=NULL;
uint8_t new_circ_purpose; uint8_t new_circ_purpose;
int is_internal;
if (desired_circuit_purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) { if (desired_circuit_purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) {
/* need to pick an intro point */ /* need to pick an intro point */
@ -801,13 +850,18 @@ circuit_get_open_circ_or_launch(connection_t *conn,
else else
new_circ_purpose = desired_circuit_purpose; new_circ_purpose = desired_circuit_purpose;
circ = circuit_launch_by_nickname(new_circ_purpose, exitname, need_uptime, 1); is_internal = (new_circ_purpose != CIRCUIT_PURPOSE_C_GENERAL || is_resolve);
circ = circuit_launch_by_nickname(new_circ_purpose, exitname, need_uptime,
1, is_internal);
tor_free(exitname); tor_free(exitname);
if (circ && if (desired_circuit_purpose != CIRCUIT_PURPOSE_C_GENERAL) {
desired_circuit_purpose != CIRCUIT_PURPOSE_C_GENERAL) { /* help predict this next time */
/* then write the service_id into circ */ rep_hist_note_used_hidserv(time(NULL), need_uptime, 1);
strlcpy(circ->rend_query, conn->rend_query, sizeof(circ->rend_query)); if (circ) {
/* write the service_id into circ */
strlcpy(circ->rend_query, conn->rend_query, sizeof(circ->rend_query));
}
} }
} }
if (!circ) if (!circ)
@ -914,6 +968,12 @@ int connection_ap_handshake_attach_circuit(connection_t *conn) {
/* one is already established, attach */ /* one is already established, attach */
log_fn(LOG_INFO,"rend joined circ %d already here. attaching. (stream %d sec old)", log_fn(LOG_INFO,"rend joined circ %d already here. attaching. (stream %d sec old)",
rendcirc->n_circ_id, conn_age); rendcirc->n_circ_id, conn_age);
/* Mark rendezvous circuits as 'newly dirty' every time you use
* them, since the process of rebuilding a rendezvous circ is so
* expensive. There is a tradeoffs between linkability and
* feasibility, at this point.
*/
rendcirc->timestamp_dirty = time(NULL);
link_apconn_to_circ(conn, rendcirc); link_apconn_to_circ(conn, rendcirc);
if (connection_ap_handshake_send_begin(conn, rendcirc) < 0) if (connection_ap_handshake_send_begin(conn, rendcirc) < 0)
return 0; /* already marked, let them fade away */ return 0; /* already marked, let them fade away */
@ -947,7 +1007,6 @@ int connection_ap_handshake_attach_circuit(connection_t *conn) {
if (introcirc->state == CIRCUIT_STATE_OPEN) { if (introcirc->state == CIRCUIT_STATE_OPEN) {
log_fn(LOG_INFO,"found open intro circ %d (rend %d); sending introduction. (stream %d sec old)", log_fn(LOG_INFO,"found open intro circ %d (rend %d); sending introduction. (stream %d sec old)",
introcirc->n_circ_id, rendcirc->n_circ_id, conn_age); introcirc->n_circ_id, rendcirc->n_circ_id, conn_age);
/* XXX here we should cannibalize the rend circ if it's a zero service id */
if (rend_client_send_introduction(introcirc, rendcirc) < 0) { if (rend_client_send_introduction(introcirc, rendcirc) < 0) {
return -1; return -1;
} }

View File

@ -145,6 +145,7 @@ static config_var_t config_vars[] = {
VAR("AccountingMax", MEMUNIT, AccountingMax, "0 bytes"), VAR("AccountingMax", MEMUNIT, AccountingMax, "0 bytes"),
VAR("Nickname", STRING, Nickname, NULL), VAR("Nickname", STRING, Nickname, NULL),
VAR("NewCircuitPeriod", INTERVAL, NewCircuitPeriod, "30 seconds"), VAR("NewCircuitPeriod", INTERVAL, NewCircuitPeriod, "30 seconds"),
VAR("MaxCircuitDirtiness", INTERVAL, MaxCircuitDirtiness, "10 minutes"),
VAR("NumCpus", UINT, NumCpus, "1"), VAR("NumCpus", UINT, NumCpus, "1"),
VAR("ORPort", UINT, ORPort, "0"), VAR("ORPort", UINT, ORPort, "0"),
VAR("ORBindAddress", LINELIST, ORBindAddress, NULL), VAR("ORBindAddress", LINELIST, ORBindAddress, NULL),

View File

@ -281,7 +281,7 @@ void connection_ap_expire_beginning(void) {
* current streams on it to survive if they can: make it * current streams on it to survive if they can: make it
* unattractive to use for new streams */ * unattractive to use for new streams */
tor_assert(circ->timestamp_dirty); tor_assert(circ->timestamp_dirty);
circ->timestamp_dirty -= options->NewCircuitPeriod; circ->timestamp_dirty -= options->MaxCircuitDirtiness;
/* give our stream another 15 seconds to try */ /* give our stream another 15 seconds to try */
conn->timestamp_lastread += 15; conn->timestamp_lastread += 15;
/* attaching to a dirty circuit is fine */ /* attaching to a dirty circuit is fine */
@ -403,14 +403,15 @@ static int connection_ap_handshake_process_socks(connection_t *conn) {
conn->hold_open_until_flushed = 1; conn->hold_open_until_flushed = 1;
return 0; return 0;
} }
} rep_hist_note_used_resolve(time(NULL)); /* help predict this next time */
} else { /* socks->command == SOCKS_COMMAND_CONNECT */
if (socks->command == SOCKS_COMMAND_CONNECT && socks->port == 0) { if (socks->port == 0) {
log_fn(LOG_NOTICE,"Application asked to connect to port 0. Refusing."); log_fn(LOG_NOTICE,"Application asked to connect to port 0. Refusing.");
return -1; return -1;
}
rep_hist_note_used_port(socks->port, time(NULL)); /* help predict this next time */
} }
conn->state = AP_CONN_STATE_CIRCUIT_WAIT; conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
rep_hist_note_used_port(socks->port, time(NULL)); /* help predict this next time */
return connection_ap_handshake_attach_circuit(conn); return connection_ap_handshake_attach_circuit(conn);
} else { } else {
/* it's a hidden-service request */ /* it's a hidden-service request */

View File

@ -528,8 +528,8 @@ void directory_all_unreachable(time_t now) {
while ((conn = connection_get_by_type_state(CONN_TYPE_AP, while ((conn = connection_get_by_type_state(CONN_TYPE_AP,
AP_CONN_STATE_CIRCUIT_WAIT))) { AP_CONN_STATE_CIRCUIT_WAIT))) {
conn->has_sent_end = 1; /* it's not connected anywhere, so no need to end */ conn->has_sent_end = 1; /* it's not connected anywhere, so no need to end */
log_fn(LOG_NOTICE,"Network down? Failing connection to '%s'.", log_fn(LOG_NOTICE,"Network down? Failing connection to '%s:%d'.",
conn->socks_request->address); conn->socks_request->address, conn->socks_request->port);
connection_mark_for_close(conn); connection_mark_for_close(conn);
} }
} }
@ -818,7 +818,7 @@ static void run_scheduled_events(time_t now) {
/** 4. Every second, we try a new circuit if there are no valid /** 4. Every second, we try a new circuit if there are no valid
* circuits. Every NewCircuitPeriod seconds, we expire circuits * circuits. Every NewCircuitPeriod seconds, we expire circuits
* that became dirty more than NewCircuitPeriod seconds ago, * that became dirty more than MaxCircuitDirtiness seconds ago,
* and we make a new circ if there are no clean circuits. * and we make a new circ if there are no clean circuits.
*/ */
if (has_fetched_directory && !we_are_hibernating()) if (has_fetched_directory && !we_are_hibernating())
@ -833,7 +833,7 @@ static void run_scheduled_events(time_t now) {
circuit_close_all_marked(); circuit_close_all_marked();
/** 7. And upload service descriptors if necessary. */ /** 7. And upload service descriptors if necessary. */
if (!we_are_hibernating()) if (has_fetched_directory && !we_are_hibernating())
rend_consider_services_upload(now); rend_consider_services_upload(now);
/** 8. and blow away any connections that need to die. have to do this now, /** 8. and blow away any connections that need to die. have to do this now,

View File

@ -731,6 +731,8 @@ typedef struct {
int need_uptime; int need_uptime;
/** Whether every node in the circ must have adequate capacity. */ /** Whether every node in the circ must have adequate capacity. */
int need_capacity; int need_capacity;
/** Whether the last hop was picked with exiting in mind. */
int is_internal;
/** The crypt_path_t to append after rendezvous: used for rendezvous. */ /** The crypt_path_t to append after rendezvous: used for rendezvous. */
struct crypt_path_t *pending_final_cpath; struct crypt_path_t *pending_final_cpath;
/** How many times has building a circuit for this task failed? */ /** How many times has building a circuit for this task failed? */
@ -955,6 +957,8 @@ typedef struct {
* them? */ * them? */
int NewCircuitPeriod; /**< How long do we use a circuit before building int NewCircuitPeriod; /**< How long do we use a circuit before building
* a new one? */ * a new one? */
int MaxCircuitDirtiness; /**< Never use circs that were first used more than
this interval ago. */
uint64_t BandwidthRate; /**< How much bandwidth, on average, are we willing to uint64_t BandwidthRate; /**< How much bandwidth, on average, are we willing to
* use in a second? */ * use in a second? */
uint64_t BandwidthBurst; /**< How much bandwidth, at maximum, are we willing to uint64_t BandwidthBurst; /**< How much bandwidth, at maximum, are we willing to
@ -1052,7 +1056,7 @@ void circuit_log_path(int severity, circuit_t *circ);
void circuit_rep_hist_note_result(circuit_t *circ); void circuit_rep_hist_note_result(circuit_t *circ);
void circuit_dump_by_conn(connection_t *conn, int severity); void circuit_dump_by_conn(connection_t *conn, int severity);
circuit_t *circuit_establish_circuit(uint8_t purpose, const char *exit_digest, circuit_t *circuit_establish_circuit(uint8_t purpose, const char *exit_digest,
int need_uptime, int need_capacity); int need_uptime, int need_capacity, int internal);
void circuit_n_conn_done(connection_t *or_conn, int status); void circuit_n_conn_done(connection_t *or_conn, int status);
int circuit_send_next_onion_skin(circuit_t *circ); int circuit_send_next_onion_skin(circuit_t *circ);
int circuit_extend(cell_t *cell, circuit_t *circ); int circuit_extend(cell_t *cell, circuit_t *circ);
@ -1063,6 +1067,7 @@ int onionskin_answer(circuit_t *circ, unsigned char *payload, unsigned char *key
int circuit_all_predicted_ports_handled(time_t now, int *need_uptime, int circuit_all_predicted_ports_handled(time_t now, int *need_uptime,
int *need_capacity); int *need_capacity);
int circuit_append_new_hop(circuit_t *circ, char *nickname, const char *exit_digest);
void onion_append_to_cpath(crypt_path_t **head_ptr, crypt_path_t *new_hop); void onion_append_to_cpath(crypt_path_t **head_ptr, crypt_path_t *new_hop);
/********************************* circuitlist.c ***********************/ /********************************* circuitlist.c ***********************/
@ -1076,8 +1081,8 @@ circuit_t *circuit_get_by_rend_query_and_purpose(const char *rend_query, uint8_t
circuit_t *circuit_get_next_by_pk_and_purpose(circuit_t *start, circuit_t *circuit_get_next_by_pk_and_purpose(circuit_t *start,
const char *digest, uint8_t purpose); const char *digest, uint8_t purpose);
circuit_t *circuit_get_rendezvous(const char *cookie); circuit_t *circuit_get_rendezvous(const char *cookie);
int circuit_count_building(uint8_t purpose); circuit_t *circuit_get_clean_open(uint8_t purpose, int need_uptime,
circuit_t *circuit_get_youngest_clean_open(uint8_t purpose); int need_capacity, int internal);
int _circuit_mark_for_close(circuit_t *circ); int _circuit_mark_for_close(circuit_t *circ);
#define circuit_mark_for_close(c) \ #define circuit_mark_for_close(c) \
@ -1106,9 +1111,9 @@ void circuit_about_to_close_connection(connection_t *conn);
void circuit_has_opened(circuit_t *circ); void circuit_has_opened(circuit_t *circ);
void circuit_build_failed(circuit_t *circ); void circuit_build_failed(circuit_t *circ);
circuit_t *circuit_launch_by_nickname(uint8_t purpose, const char *exit_nickname, circuit_t *circuit_launch_by_nickname(uint8_t purpose, const char *exit_nickname,
int need_uptime, int need_capacity); int need_uptime, int need_capacity, int is_internal);
circuit_t *circuit_launch_by_identity(uint8_t purpose, const char *exit_digest, circuit_t *circuit_launch_by_identity(uint8_t purpose, const char *exit_digest,
int need_uptime, int need_capacity); int need_uptime, int need_capacity, int is_internal);
void circuit_reset_failure_count(int timeout); void circuit_reset_failure_count(int timeout);
int connection_ap_handshake_attach_circuit(connection_t *conn); int connection_ap_handshake_attach_circuit(connection_t *conn);
@ -1472,8 +1477,13 @@ void rep_hist_note_bytes_written(int num_bytes, time_t when);
int rep_hist_bandwidth_assess(void); int rep_hist_bandwidth_assess(void);
char *rep_hist_get_bandwidth_lines(void); char *rep_hist_get_bandwidth_lines(void);
void rep_history_clean(time_t before); void rep_history_clean(time_t before);
void rep_hist_note_used_port(uint16_t port, time_t now); void rep_hist_note_used_port(uint16_t port, time_t now);
smartlist_t *rep_hist_get_predicted_ports(time_t now); smartlist_t *rep_hist_get_predicted_ports(time_t now);
void rep_hist_note_used_hidserv(time_t now, int need_uptime, int need_capacity);
int rep_hist_get_predicted_hidserv(time_t now, int *need_uptime, int *need_capacity);
void rep_hist_note_used_resolve(time_t now);
int rep_hist_get_predicted_resolve(time_t now);
/********************************* rendclient.c ***************************/ /********************************* rendclient.c ***************************/

View File

@ -547,7 +547,7 @@ connection_edge_process_relay_cell_not_open(
conn->state = AP_CONN_STATE_CIRCUIT_WAIT; conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
circuit_detach_stream(circ,conn); circuit_detach_stream(circ,conn);
tor_assert(circ->timestamp_dirty); tor_assert(circ->timestamp_dirty);
circ->timestamp_dirty -= get_options()->NewCircuitPeriod; circ->timestamp_dirty -= get_options()->MaxCircuitDirtiness;
/* make sure not to expire/retry the stream quite yet */ /* make sure not to expire/retry the stream quite yet */
conn->timestamp_lastread = time(NULL); conn->timestamp_lastread = time(NULL);
if (connection_ap_handshake_attach_circuit(conn) >= 0) if (connection_ap_handshake_attach_circuit(conn) >= 0)

View File

@ -93,11 +93,22 @@ rend_client_send_introduction(circuit_t *introcirc, circuit_t *rendcirc) {
} }
/* write the remaining items into tmp */ /* write the remaining items into tmp */
#if 0
tmp[0] = 1; /* version 1 of the cell format */ tmp[0] = 1; /* version 1 of the cell format */
strncpy(tmp+1, rendcirc->build_state->chosen_exit_name, (MAX_HEX_NICKNAME_LEN+1)); /* nul pads */ /* nul pads */
strncpy(tmp+1, rendcirc->build_state->chosen_exit_name, (MAX_HEX_NICKNAME_LEN+1));
memcpy(tmp+1+MAX_HEX_NICKNAME_LEN+1, rendcirc->rend_cookie, REND_COOKIE_LEN); memcpy(tmp+1+MAX_HEX_NICKNAME_LEN+1, rendcirc->rend_cookie, REND_COOKIE_LEN);
#else
strncpy(tmp, rendcirc->build_state->chosen_exit_name, (MAX_NICKNAME_LEN+1)); /* nul pads */
memcpy(tmp+MAX_NICKNAME_LEN+1, rendcirc->rend_cookie, REND_COOKIE_LEN);
#endif
if (crypto_dh_get_public(cpath->handshake_state, if (crypto_dh_get_public(cpath->handshake_state,
#if 0
tmp+1+MAX_HEX_NICKNAME_LEN+1+REND_COOKIE_LEN, tmp+1+MAX_HEX_NICKNAME_LEN+1+REND_COOKIE_LEN,
#else
tmp+MAX_NICKNAME_LEN+1+REND_COOKIE_LEN,
#endif
DH_KEY_LEN)<0) { DH_KEY_LEN)<0) {
log_fn(LOG_WARN, "Couldn't extract g^x"); log_fn(LOG_WARN, "Couldn't extract g^x");
goto err; goto err;
@ -106,7 +117,11 @@ rend_client_send_introduction(circuit_t *introcirc, circuit_t *rendcirc) {
/*XXX maybe give crypto_pk_public_hybrid_encrypt a max_len arg, /*XXX maybe give crypto_pk_public_hybrid_encrypt a max_len arg,
* to avoid buffer overflows? */ * to avoid buffer overflows? */
r = crypto_pk_public_hybrid_encrypt(entry->parsed->pk, payload+DIGEST_LEN, tmp, r = crypto_pk_public_hybrid_encrypt(entry->parsed->pk, payload+DIGEST_LEN, tmp,
#if 0
1+MAX_HEX_NICKNAME_LEN+1+REND_COOKIE_LEN+DH_KEY_LEN, 1+MAX_HEX_NICKNAME_LEN+1+REND_COOKIE_LEN+DH_KEY_LEN,
#else
MAX_NICKNAME_LEN+1+REND_COOKIE_LEN+DH_KEY_LEN,
#endif
PK_PKCS1_OAEP_PADDING, 0); PK_PKCS1_OAEP_PADDING, 0);
if (r<0) { if (r<0) {
log_fn(LOG_WARN,"hybrid pk encrypt failed."); log_fn(LOG_WARN,"hybrid pk encrypt failed.");
@ -176,11 +191,13 @@ rend_client_introduction_acked(circuit_t *circ,
/* Locate the rend circ which is waiting to hear about this ack, /* Locate the rend circ which is waiting to hear about this ack,
* and tell it. * and tell it.
*/ */
log_fn(LOG_INFO,"Received ack. Telling rend circ."); log_fn(LOG_INFO,"Received ack. Telling rend circ...");
rendcirc = circuit_get_by_rend_query_and_purpose( rendcirc = circuit_get_by_rend_query_and_purpose(
circ->rend_query, CIRCUIT_PURPOSE_C_REND_READY); circ->rend_query, CIRCUIT_PURPOSE_C_REND_READY);
if (rendcirc) { /* remember the ack */ if (rendcirc) { /* remember the ack */
rendcirc->purpose = CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED; rendcirc->purpose = CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED;
} else {
log_fn(LOG_INFO,"...Found no rend circ. Dropping on the floor.");
} }
/* close the circuit: we won't need it anymore. */ /* close the circuit: we won't need it anymore. */
circ->purpose = CIRCUIT_PURPOSE_C_INTRODUCE_ACKED; circ->purpose = CIRCUIT_PURPOSE_C_INTRODUCE_ACKED;
@ -199,7 +216,8 @@ rend_client_introduction_acked(circuit_t *circ,
routerinfo_t *r; routerinfo_t *r;
nickname = rend_client_get_random_intro(circ->rend_query); nickname = rend_client_get_random_intro(circ->rend_query);
tor_assert(nickname); tor_assert(nickname);
log_fn(LOG_INFO,"Got nack for %s from %s, extending to %s.", circ->rend_query, circ->build_state->chosen_exit_name, nickname); log_fn(LOG_INFO,"Got nack for %s from %s, extending to %s.",
circ->rend_query, circ->build_state->chosen_exit_name, nickname);
if (!(r = router_get_by_nickname(nickname))) { if (!(r = router_get_by_nickname(nickname))) {
log_fn(LOG_WARN, "Advertised intro point '%s' for %s is not known. Closing.", log_fn(LOG_WARN, "Advertised intro point '%s' for %s is not known. Closing.",
nickname, circ->rend_query); nickname, circ->rend_query);
@ -209,18 +227,8 @@ rend_client_introduction_acked(circuit_t *circ,
} }
log_fn(LOG_INFO, "Chose new intro point %s for %s (circ %d)", log_fn(LOG_INFO, "Chose new intro point %s for %s (circ %d)",
nickname, circ->rend_query, circ->n_circ_id); nickname, circ->rend_query, circ->n_circ_id);
circ->state = CIRCUIT_STATE_BUILDING; if (circuit_append_new_hop(circ, nickname, r->identity_digest) < 0)
tor_free(circ->build_state->chosen_exit_name);
/* no need to strdup, since rend_client_get_random_intro() made
* it just for us: */
circ->build_state->chosen_exit_name = nickname;
memcpy(circ->build_state->chosen_exit_digest, r->identity_digest, DIGEST_LEN);
++circ->build_state->desired_path_len;
if (circuit_send_next_onion_skin(circ)<0) {
log_fn(LOG_WARN, "Couldn't extend circuit to new intro point.");
circuit_mark_for_close(circ);
return -1; return -1;
}
} }
} }
return 0; return 0;

View File

@ -50,7 +50,7 @@ typedef struct rend_service_t {
int n_intro_circuits_launched; /**< count of intro circuits we have int n_intro_circuits_launched; /**< count of intro circuits we have
* established in this period. */ * established in this period. */
rend_service_descriptor_t *desc; rend_service_descriptor_t *desc;
int desc_is_dirty; time_t desc_is_dirty;
time_t next_upload_time; time_t next_upload_time;
} rend_service_t; } rend_service_t;
@ -363,7 +363,7 @@ rend_service_requires_uptime(rend_service_t *service) {
******/ ******/
/** Respond to an INTRODUCE2 cell by launching a circuit to the chosen /** Respond to an INTRODUCE2 cell by launching a circuit to the chosen
* rendezvous points. * rendezvous point.
*/ */
int int
rend_service_introduce(circuit_t *circuit, const char *request, size_t request_len) rend_service_introduce(circuit_t *circuit, const char *request, size_t request_len)
@ -381,6 +381,7 @@ rend_service_introduce(circuit_t *circuit, const char *request, size_t request_l
char hexcookie[9]; char hexcookie[9];
int version; int version;
size_t nickname_field_len; size_t nickname_field_len;
int circ_needs_uptime;
base32_encode(serviceid, REND_SERVICE_ID_LEN+1, base32_encode(serviceid, REND_SERVICE_ID_LEN+1,
circuit->rend_pk_digest,10); circuit->rend_pk_digest,10);
@ -452,6 +453,7 @@ rend_service_introduce(circuit_t *circuit, const char *request, size_t request_l
/* Okay, now we know that a nickname is at the start of the buffer. */ /* Okay, now we know that a nickname is at the start of the buffer. */
ptr = rp_nickname+nickname_field_len; ptr = rp_nickname+nickname_field_len;
len -= nickname_field_len; len -= nickname_field_len;
len -= rp_nickname - buf; /* also remove header space used by version, if any */
if (len != REND_COOKIE_LEN+DH_KEY_LEN) { if (len != REND_COOKIE_LEN+DH_KEY_LEN) {
log_fn(LOG_WARN, "Bad length %u for INTRODUCE2 cell.", (int)len); log_fn(LOG_WARN, "Bad length %u for INTRODUCE2 cell.", (int)len);
return -1; return -1;
@ -471,11 +473,16 @@ rend_service_introduce(circuit_t *circuit, const char *request, size_t request_l
goto err; goto err;
} }
circ_needs_uptime = rend_service_requires_uptime(service);
/* help predict this next time */
rep_hist_note_used_hidserv(time(NULL), circ_needs_uptime, 1);
/* Launch a circuit to alice's chosen rendezvous point. /* Launch a circuit to alice's chosen rendezvous point.
*/ */
for (i=0;i<MAX_REND_FAILURES;i++) { for (i=0;i<MAX_REND_FAILURES;i++) {
launched = circuit_launch_by_nickname(CIRCUIT_PURPOSE_S_CONNECT_REND, rp_nickname, launched = circuit_launch_by_nickname(CIRCUIT_PURPOSE_S_CONNECT_REND, rp_nickname,
rend_service_requires_uptime(service), 1); circ_needs_uptime, 1, 1);
if (launched) if (launched)
break; break;
} }
@ -540,7 +547,7 @@ rend_service_relaunch_rendezvous(circuit_t *oldcirc)
oldstate->chosen_exit_name); oldstate->chosen_exit_name);
newcirc = circuit_launch_by_nickname(CIRCUIT_PURPOSE_S_CONNECT_REND, newcirc = circuit_launch_by_nickname(CIRCUIT_PURPOSE_S_CONNECT_REND,
oldstate->chosen_exit_name, 0, 1); oldstate->chosen_exit_name, 0, 1, 1);
if (!newcirc) { if (!newcirc) {
log_fn(LOG_WARN,"Couldn't relaunch rendezvous circuit to %s", log_fn(LOG_WARN,"Couldn't relaunch rendezvous circuit to %s",
oldstate->chosen_exit_name); oldstate->chosen_exit_name);
@ -568,8 +575,10 @@ rend_service_launch_establish_intro(rend_service_t *service, const char *nicknam
log_fn(LOG_INFO, "Launching circuit to introduction point %s for service %s", log_fn(LOG_INFO, "Launching circuit to introduction point %s for service %s",
nickname, service->service_id); nickname, service->service_id);
rep_hist_note_used_hidserv(time(NULL), 1, 0);
++service->n_intro_circuits_launched; ++service->n_intro_circuits_launched;
launched = circuit_launch_by_nickname(CIRCUIT_PURPOSE_S_ESTABLISH_INTRO, nickname, 1, 0); launched = circuit_launch_by_nickname(CIRCUIT_PURPOSE_S_ESTABLISH_INTRO, nickname, 1, 0, 1);
if (!launched) { if (!launched) {
log_fn(LOG_WARN, "Can't launch circuit to establish introduction at '%s'", log_fn(LOG_WARN, "Can't launch circuit to establish introduction at '%s'",
nickname); nickname);
@ -661,7 +670,7 @@ rend_service_intro_established(circuit_t *circuit, const char *request, size_t r
circuit->n_circ_id); circuit->n_circ_id);
goto err; goto err;
} }
service->desc_is_dirty = 1; service->desc_is_dirty = time(NULL);
circuit->purpose = CIRCUIT_PURPOSE_S_INTRO; circuit->purpose = CIRCUIT_PURPOSE_S_INTRO;
return 0; return 0;
@ -849,7 +858,8 @@ void rend_services_introduce(void) {
intro, service->service_id); intro, service->service_id);
tor_free(intro); tor_free(intro);
smartlist_del(service->intro_nodes,j--); smartlist_del(service->intro_nodes,j--);
changed = service->desc_is_dirty = 1; changed = 1;
service->desc_is_dirty = now;
} }
smartlist_add(intro_routers, router); smartlist_add(intro_routers, router);
} }
@ -931,9 +941,9 @@ rend_consider_services_upload(time_t now) {
} }
if (service->next_upload_time < now || if (service->next_upload_time < now ||
(service->desc_is_dirty && (service->desc_is_dirty &&
service->next_upload_time < now-5)) { service->desc_is_dirty < now-5)) {
/* if it's time, or if the directory servers have a wrong service /* if it's time, or if the directory servers have a wrong service
* descriptor and this has been the case for 5 seconds, upload a * descriptor and ours has been stable for 5 seconds, upload a
* new one. */ * new one. */
upload_service_descriptor(service); upload_service_descriptor(service);
service->next_upload_time = now + rendpostperiod; service->next_upload_time = now + rendpostperiod;

View File

@ -666,7 +666,7 @@ void rep_hist_note_used_port(uint16_t port, time_t now) {
add_predicted_port(port, now); add_predicted_port(port, now);
} }
#define PREDICTED_PORTS_RELEVANCE_TIME (6*3600) /* 6 hours */ #define PREDICTED_CIRCS_RELEVANCE_TIME (3600) /* 1 hour */
/** Return a pointer to the list of port numbers that /** Return a pointer to the list of port numbers that
* are likely to be asked for in the near future. * are likely to be asked for in the near future.
@ -684,7 +684,7 @@ smartlist_t *rep_hist_get_predicted_ports(time_t now) {
/* clean out obsolete entries */ /* clean out obsolete entries */
for (i = 0; i < smartlist_len(predicted_ports_list); ++i) { for (i = 0; i < smartlist_len(predicted_ports_list); ++i) {
tmp_time = smartlist_get(predicted_ports_times, i); tmp_time = smartlist_get(predicted_ports_times, i);
if (*tmp_time + PREDICTED_PORTS_RELEVANCE_TIME < now) { if (*tmp_time + PREDICTED_CIRCS_RELEVANCE_TIME < now) {
tmp_port = smartlist_get(predicted_ports_list, i); tmp_port = smartlist_get(predicted_ports_list, i);
log_fn(LOG_DEBUG, "Expiring predicted port %d", *tmp_port); log_fn(LOG_DEBUG, "Expiring predicted port %d", *tmp_port);
smartlist_del(predicted_ports_list, i); smartlist_del(predicted_ports_list, i);
@ -697,3 +697,36 @@ smartlist_t *rep_hist_get_predicted_ports(time_t now) {
return predicted_ports_list; return predicted_ports_list;
} }
/** The last time at which we needed an internal circ. */
static time_t predicted_hidserv_time = 0;
/** The last time we needed an internal circ with good uptime. */
static time_t predicted_hidserv_uptime_time = 0;
/** The last time we needed an internal circ with good capacity. */
static time_t predicted_hidserv_capacity_time = 0;
/** Remember that we used an internal circ at time <b>now</b>. */
void rep_hist_note_used_hidserv(time_t now, int need_uptime, int need_capacity) {
predicted_hidserv_time = now;
if (need_uptime)
predicted_hidserv_uptime_time = now;
if (need_capacity)
predicted_hidserv_capacity_time = now;
}
/** Return 1 if we've used an internal circ recently; else return 0. */
int rep_hist_get_predicted_hidserv(time_t now, int *need_uptime, int *need_capacity) {
if (!predicted_hidserv_time) /* initialize it */
predicted_hidserv_time = now;
if (predicted_hidserv_time + PREDICTED_CIRCS_RELEVANCE_TIME < now)
return 0; /* too long ago */
if (predicted_hidserv_uptime_time + PREDICTED_CIRCS_RELEVANCE_TIME < now)
*need_uptime = 1;
if (predicted_hidserv_capacity_time + PREDICTED_CIRCS_RELEVANCE_TIME < now)
*need_capacity = 1;
return 1;
}
/* not used yet */
void rep_hist_note_used_resolve(time_t now) { }
int rep_hist_get_predicted_resolve(time_t now) { return 0; }