commit 4b13c33c0c18c66cc39caba9b70005bbe43c6613
Author: Robert Ransom <rransom.8774(a)gmail.com>
Date: Fri Dec 23 05:25:17 2011 -0800
Don't close HS client circs which are 'almost connected' on timeout
---
changes/bug1297b | 12 +++++++++++
src/or/circuituse.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++-
src/or/or.h | 7 ++++++
src/or/rendclient.c | 5 ++++
4 files changed, 76 insertions(+), 1 deletions(-)
diff --git a/changes/bug1297b b/changes/bug1297b
new file mode 100644
index 0000000..2b2754a
--- /dev/null
+++ b/changes/bug1297b
@@ -0,0 +1,12 @@
+ o Minor bugfixes:
+
+ - Don't close hidden service client circuits which have almost
+ finished connecting to their destination when they reach the
+ normal circuit-build timeout. Previously, we would close
+ introduction circuits which are waiting for an acknowledgement
+ from the introduction-point relay and rendezvous circuits which
+ have been specified in an INTRODUCE1 cell sent to a hidden
+ service after the normal CBT; now, we mark them as 'timed out',
+ and launch another rendezvous attempt in parallel. Fixes part
+ of bug 1297.
+
diff --git a/src/or/circuituse.c b/src/or/circuituse.c
index ef4ac6f..58d8aa6 100644
--- a/src/or/circuituse.c
+++ b/src/or/circuituse.c
@@ -75,6 +75,11 @@ circuit_is_acceptable(const origin_circuit_t *origin_circ,
return 0;
}
+ /* If this is a timed-out hidden service circuit, skip it. */
+ if (origin_circ->hs_circ_has_timed_out) {
+ return 0;
+ }
+
if (purpose == CIRCUIT_PURPOSE_C_GENERAL ||
purpose == CIRCUIT_PURPOSE_C_REND_JOINED)
if (circ->timestamp_dirty &&
@@ -351,7 +356,9 @@ circuit_expire_building(void)
* circuit_build_times_get_initial_timeout() if we haven't computed
* custom timeouts yet */
struct timeval general_cutoff, begindir_cutoff, fourhop_cutoff,
- cannibalize_cutoff, close_cutoff, extremely_old_cutoff;
+ cannibalize_cutoff, close_cutoff, extremely_old_cutoff,
+ hs_extremely_old_cutoff;
+ const or_options_t *options = get_options();
struct timeval now;
cpath_build_state_t *build_state;
@@ -371,6 +378,10 @@ circuit_expire_building(void)
SET_CUTOFF(close_cutoff, circ_times.close_ms);
SET_CUTOFF(extremely_old_cutoff, circ_times.close_ms*2 + 1000);
+ SET_CUTOFF(hs_extremely_old_cutoff,
+ MAX(circ_times.close_ms*2 + 1000,
+ options->SocksTimeout * 1000));
+
while (next_circ) {
struct timeval cutoff;
victim = next_circ;
@@ -392,6 +403,9 @@ circuit_expire_building(void)
else
cutoff = general_cutoff;
+ if (TO_ORIGIN_CIRCUIT(victim)->hs_circ_has_timed_out)
+ cutoff = hs_extremely_old_cutoff;
+
if (timercmp(&victim->timestamp_created, &cutoff, >))
continue; /* it's still young, leave it alone */
@@ -497,6 +511,43 @@ circuit_expire_building(void)
}
}
+ /* If this is a hidden-service circuit which is far enough along
+ * in connecting to its destination, and we haven't already
+ * flagged it as 'timed out', flag it as 'timed out' so we'll
+ * launch another intro or rend circ, but don't mark it for close
+ * yet.
+ *
+ * (Circs flagged as 'timed out' are given a much longer timeout
+ * period above, so we won't close them in the next call to
+ * circuit_expire_building.) */
+ if (!(TO_ORIGIN_CIRCUIT(victim)->hs_circ_has_timed_out)) {
+ switch (victim->purpose) {
+ case CIRCUIT_PURPOSE_C_REND_READY:
+ /* We only want to spare a rend circ if it has been specified in
+ * an INTRODUCE1 cell sent to a hidden service. A circ's
+ * pending_final_cpath field is non-NULL iff it is a rend circ
+ * and we have tried to send an INTRODUCE1 cell specifying it.
+ * Thus, if the pending_final_cpath field *is* NULL, then we
+ * want to not spare it. */
+ if (TO_ORIGIN_CIRCUIT(victim)->build_state->pending_final_cpath ==
+ NULL)
+ break;
+ case CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT:
+ case CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED:
+ /* If we have reached this line, we want to spare the circ for now. */
+ log_info(LD_CIRC,"Marking circ %s:%d:%d (state %d:%s, purpose %d) "
+ "as timed-out HS circ",
+ victim->n_conn->_base.address, victim->n_conn->_base.port,
+ victim->n_circ_id,
+ victim->state, circuit_state_to_string(victim->state),
+ victim->purpose);
+ TO_ORIGIN_CIRCUIT(victim)->hs_circ_has_timed_out = 1;
+ continue;
+ default:
+ 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_conn->_base.port,
diff --git a/src/or/or.h b/src/or/or.h
index 63ff5c4..63eb64c 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -2607,6 +2607,13 @@ typedef struct origin_circuit_t {
* cannibalized circuits. */
unsigned int has_opened : 1;
+ /** Set iff this is a hidden-service circuit which has timed out
+ * according to our current circuit-build timeout, but which has
+ * been kept around because it might still succeed in connecting to
+ * its destination, and which is not a fully-connected rendezvous
+ * circuit. */
+ unsigned int hs_circ_has_timed_out : 1;
+
/** What commands were sent over this circuit that decremented the
* RELAY_EARLY counter? This is for debugging task 878. */
uint8_t relay_early_commands[MAX_RELAY_EARLY_CELLS_PER_CIRCUIT];
diff --git a/src/or/rendclient.c b/src/or/rendclient.c
index 84a9d49..17c92ab 100644
--- a/src/or/rendclient.c
+++ b/src/or/rendclient.c
@@ -895,6 +895,11 @@ rend_client_receive_rendezvous(origin_circuit_t *circ, const uint8_t *request,
hop->package_window = circuit_initial_package_window();
hop->deliver_window = CIRCWINDOW_START;
+ /* Now that this circuit has finished connecting to its destination,
+ * make sure circuit_get_open_circ_or_launch is willing to return it
+ * so we can actually use it. */
+ circ->hs_circ_has_timed_out = 0;
+
onion_append_to_cpath(&circ->cpath, hop);
circ->build_state->pending_final_cpath = NULL; /* prevent double-free */