diff --git a/changes/bug4655 b/changes/bug4655 new file mode 100644 index 0000000000..b91d87123c --- /dev/null +++ b/changes/bug4655 @@ -0,0 +1,10 @@ + o Minor bugfixes: + + - If we can't attach streams to a rendezvous circuit when we + finish connecting to a hidden service, clear the rendezvous + circuit's stream-isolation state and try to attach streams + again. Previously, we cleared rendezvous circuits' isolation + state either too early (if they were freshly built) or not at + all (if they had been built earlier and were cannibalized). + Bugfix on 0.2.3.3-alpha; fixes bug 4655. + diff --git a/src/or/circuituse.c b/src/or/circuituse.c index bdff75cd5b..ef4ac6faa3 100644 --- a/src/or/circuituse.c +++ b/src/or/circuituse.c @@ -1004,7 +1004,6 @@ circuit_testing_failed(origin_circuit_t *circ, int at_last_hop) void circuit_has_opened(origin_circuit_t *circ) { - int can_try_clearing_isolation = 0, tried_clearing_isolation = 0; control_event_circuit_status(circ, CIRC_EVENT_BUILT, 0); /* Remember that this circuit has finished building. Now if we start @@ -1012,20 +1011,18 @@ circuit_has_opened(origin_circuit_t *circ) * to consider its build time. */ circ->has_opened = 1; - again: - switch (TO_CIRCUIT(circ)->purpose) { case CIRCUIT_PURPOSE_C_ESTABLISH_REND: rend_client_rendcirc_has_opened(circ); - /* XXXX We'd like to set something like "can_try_clearing_isolation" - * here, so that we can change the isolation of this circuit (and maybe - * its purpose too) if it turns out that we no longer have any streams - * that want to use it. But connection_ap_attach_pending() doesn't - * actually attach streams to a C_ESTABLISH_REND circuit-- streams - * don't get attached until the circuit is in C_REND_JOINED... so - * we can't clear isolation now. - */ + /* Start building an intro circ if we don't have one yet. */ connection_ap_attach_pending(); + /* This isn't a call to circuit_try_attaching_streams because a + * circuit in _C_ESTABLISH_REND state isn't connected to its + * hidden service yet, thus we can't attach streams to it yet, + * thus circuit_try_attaching_streams would always clear the + * circuit's isolation state. circuit_try_attaching_streams is + * called later, when the rend circ enters _C_REND_JOINED + * state. */ break; case CIRCUIT_PURPOSE_C_INTRODUCING: rend_client_introcirc_has_opened(circ); @@ -1033,8 +1030,7 @@ circuit_has_opened(origin_circuit_t *circ) case CIRCUIT_PURPOSE_C_GENERAL: /* Tell any AP connections that have been waiting for a new * circuit that one is ready. */ - can_try_clearing_isolation = 1; - connection_ap_attach_pending(); + circuit_try_attaching_streams(circ); break; case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO: /* at Bob, waiting for introductions */ @@ -1051,12 +1047,15 @@ circuit_has_opened(origin_circuit_t *circ) * This won't happen in normal operation, but might happen if the * controller did it. Just let it slide. */ } +} +/** If the stream-isolation state of circ can be cleared, clear + * it. Return non-zero iff circ's isolation state was cleared. */ +static int +circuit_try_clearing_isolation_state(origin_circuit_t *circ) +{ if (/* The circuit may have become non-open if it was cannibalized.*/ circ->_base.state == CIRCUIT_STATE_OPEN && - /* Only if the purpose is clearable, and only if we haven't tried - * to clear isolation yet, do we try. */ - can_try_clearing_isolation && !tried_clearing_isolation && /* If !isolation_values_set, there is nothing to clear. */ circ->isolation_values_set && /* It's not legal to clear a circuit's isolation info if it's ever had @@ -1066,8 +1065,26 @@ circuit_has_opened(origin_circuit_t *circ) * we didn't manage to attach any streams to it, then we can * and should clear it and try again. */ circuit_clear_isolation(circ); - tried_clearing_isolation = 1; - goto again; + return 1; + } else { + return 0; + } +} + +/** Called when a circuit becomes ready for streams to be attached to + * it. */ +void +circuit_try_attaching_streams(origin_circuit_t *circ) +{ + /* Attach streams to this circuit if we can. */ + connection_ap_attach_pending(); + + /* The call to circuit_try_clearing_isolation_state here will do + * nothing and return 0 if we didn't attach any streams to circ + * above. */ + if (circuit_try_clearing_isolation_state(circ)) { + /* Maybe *now* we can attach some streams to this circuit. */ + connection_ap_attach_pending(); } } diff --git a/src/or/circuituse.h b/src/or/circuituse.h index 9867fd8205..bc11fe5d91 100644 --- a/src/or/circuituse.h +++ b/src/or/circuituse.h @@ -29,6 +29,7 @@ void reset_bandwidth_test(void); int circuit_enough_testing_circs(void); void circuit_has_opened(origin_circuit_t *circ); +void circuit_try_attaching_streams(origin_circuit_t *circ); void circuit_build_failed(origin_circuit_t *circ); /** Flag to set when a circuit should have only a single hop. */ diff --git a/src/or/rendclient.c b/src/or/rendclient.c index 5429b6c7e3..8aae8c5cb5 100644 --- a/src/or/rendclient.c +++ b/src/or/rendclient.c @@ -892,10 +892,9 @@ rend_client_receive_rendezvous(origin_circuit_t *circ, const uint8_t *request, onion_append_to_cpath(&circ->cpath, hop); circ->build_state->pending_final_cpath = NULL; /* prevent double-free */ - /* XXXX023 This is a pretty brute-force approach. It'd be better to - * attach only the connections that are waiting on this circuit, rather - * than trying to attach them all. See comments bug 743. */ - connection_ap_attach_pending(); + + circuit_try_attaching_streams(circ); + memset(keys, 0, sizeof(keys)); return 0; err: