|
@@ -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,62 @@ circuit_expire_building(void)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /* If this is a hidden service client circuit which is far enough
|
|
|
+ * along in connecting to its destination, and we haven't already
|
|
|
+ * flagged it as 'timed out', and the user has not told us to
|
|
|
+ * close such circs immediately on timeout, 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 (!(options->CloseHSClientCircuitsImmediatelyOnTimeout) &&
|
|
|
+ !(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 this is a service-side rendezvous circuit which is far
|
|
|
+ * enough along in connecting to its destination, consider sparing
|
|
|
+ * it. */
|
|
|
+ if (!(options->CloseHSServiceRendCircuitsImmediatelyOnTimeout) &&
|
|
|
+ !(TO_ORIGIN_CIRCUIT(victim)->hs_circ_has_timed_out) &&
|
|
|
+ victim->purpose == CIRCUIT_PURPOSE_S_CONNECT_REND) {
|
|
|
+ log_info(LD_CIRC,"Marking circ %s:%d:%d (state %d:%s, purpose %d) "
|
|
|
+ "as timed-out HS circ; relaunching rendezvous attempt.",
|
|
|
+ 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;
|
|
|
+ rend_service_relaunch_rendezvous(TO_ORIGIN_CIRCUIT(victim));
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
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,
|