diff --git a/src/or/circuit.c b/src/or/circuit.c index 16fc8bb6e4..eafead450d 100644 --- a/src/or/circuit.c +++ b/src/or/circuit.c @@ -997,6 +997,13 @@ int _circuit_mark_for_close(circuit_t *circ) { circuit_build_failed(circ); /* take actions if necessary */ circuit_rep_hist_note_result(circ); } + if (circ->purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) { + assert(circ->state == CIRCUIT_STATE_OPEN); + /* treat this like getting a nack from it */ + log_fn(LOG_INFO,"Failed intro circ %s to %s (awaiting ack). Removing from descriptor.", + circ->rend_query, circ->build_state->chosen_exit); + rend_client_remove_intro_point(circ->build_state->chosen_exit, circ->rend_query); + } if(circ->n_conn) connection_send_destroy(circ->n_circ_id, circ->n_conn); diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c index fea18fc8f3..46577ae32e 100644 --- a/src/or/connection_edge.c +++ b/src/or/connection_edge.c @@ -757,14 +757,7 @@ static int connection_ap_handshake_process_socks(connection_t *conn) { return connection_ap_handshake_attach_circuit(conn); } else { conn->state = AP_CONN_STATE_RENDDESC_WAIT; - if(connection_get_by_type_rendquery(CONN_TYPE_DIR, conn->rend_query)) { - log_fn(LOG_INFO,"Would fetch a new renddesc here (for %s), but one is already in progress.", conn->rend_query); - } else { - /* not one already; initiate a dir rend desc lookup */ - directory_initiate_command(router_pick_directory_server(), - DIR_PURPOSE_FETCH_RENDDESC, - conn->rend_query, strlen(conn->rend_query)); - } + rend_client_refetch_renddesc(conn->rend_query); return 0; } } diff --git a/src/or/or.h b/src/or/or.h index f68cd16c9f..cf8238a612 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -1043,6 +1043,8 @@ void rep_hist_dump_stats(time_t now, int severity); void rend_client_introcirc_is_open(circuit_t *circ); void rend_client_rendcirc_is_open(circuit_t *circ); int rend_client_introduction_acked(circuit_t *circ, const char *request, int request_len); +void rend_client_refetch_renddesc(char *query); +int rend_client_remove_intro_point(char *failed_intro, char *query); int rend_client_rendezvous_acked(circuit_t *circ, const char *request, int request_len); int rend_client_receive_rendezvous(circuit_t *circ, const char *request, int request_len); void rend_client_desc_fetched(char *query, int success); diff --git a/src/or/rendclient.c b/src/or/rendclient.c index 7af0a03a73..1f086acbd2 100644 --- a/src/or/rendclient.c +++ b/src/or/rendclient.c @@ -147,8 +147,6 @@ int rend_client_introduction_acked(circuit_t *circ, const char *request, int request_len) { - int i, r; - rend_cache_entry_t *ent; char *nickname; circuit_t *rendcirc; @@ -177,71 +175,93 @@ rend_client_introduction_acked(circuit_t *circ, } else { /* It's a NAK; the introduction point didn't relay our request. */ circ->purpose = CIRCUIT_PURPOSE_C_INTRODUCING; - /* XXXX - * Now become non-open, extend to another one of Bob's - * introduction points, and try again. Maybe mark the service as - * non-functional at the first intro point somehow? - * - * Or re-fetch the service descriptor? Hm.... + /* Remove this intro point from the set of viable introduction + * points. If any remain, extend to a new one and try again. + * If none remain, refetch the service descriptor. */ - r = rend_cache_lookup_entry(circ->rend_query, &ent); - if (r<0) { - log_fn(LOG_WARN, "Malformed service ID '%s'", circ->rend_query); - return -1; - } - if (r>0) { - /* Okay, we found the right service desc. First, remove this intro point - * from the parsed descriptor (if it's still there!) - */ - for (i=0; i < ent->parsed->n_intro_points; ++i) { - if (!strcasecmp(ent->parsed->intro_points[i], - circ->build_state->chosen_exit)) { - tor_free(ent->parsed->intro_points[i]); - ent->parsed->intro_points[i] = - ent->parsed->intro_points[--ent->parsed->n_intro_points]; - break; - } - } - /* If there are any introduction points left, re-extend the circuit to + if(rend_client_remove_intro_point(circ->build_state->chosen_exit, + circ->rend_query) > 0) { + /* There are introduction points left. re-extend the circuit to * another intro point and try again. */ - if (ent->parsed->n_intro_points) { - nickname = rend_client_get_random_intro(circ->rend_query); - assert(nickname); - if (!router_get_by_nickname(nickname)) { - log_fn(LOG_WARN, "Advertised intro point '%s' for %s is not known. Closing.", - nickname, circ->rend_query); - circuit_mark_for_close(circ); - return -1; - } - log_fn(LOG_INFO, "Chose new intro point %s for %s (circ %d, %d choices left)", - nickname, circ->rend_query, circ->n_circ_id, ent->parsed->n_intro_points); - circ->state = CIRCUIT_STATE_BUILDING; - tor_free(circ->build_state->chosen_exit); - circ->build_state->chosen_exit = tor_strdup(nickname); - ++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 0; + nickname = rend_client_get_random_intro(circ->rend_query); + assert(nickname); + log_fn(LOG_INFO,"Got nack for %s from %s, extending to %s.", circ->rend_query, circ->build_state->chosen_exit, nickname); + if (!router_get_by_nickname(nickname)) { + log_fn(LOG_WARN, "Advertised intro point '%s' for %s is not known. Closing.", + nickname, circ->rend_query); + circuit_mark_for_close(circ); + return -1; + } + log_fn(LOG_INFO, "Chose new intro point %s for %s (circ %d)", + nickname, circ->rend_query, circ->n_circ_id); + circ->state = CIRCUIT_STATE_BUILDING; + tor_free(circ->build_state->chosen_exit); + circ->build_state->chosen_exit = tor_strdup(nickname); + ++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; } - } - /* Either we have no service desc, or all the intro points in that - * descriptor failed. So re-fetch the descriptor and try again. */ - circ->purpose = CIRCUIT_PURPOSE_C_INTRODUCING; - /* XXX anything else we need to do to circ? */ - /* Refetch descriptor */ - if(!connection_get_by_type_rendquery(CONN_TYPE_DIR, circ->rend_query)) { - /* not one already; initiate a dir rend desc lookup */ - directory_initiate_command(router_pick_directory_server(), - DIR_PURPOSE_FETCH_RENDDESC, - circ->rend_query, strlen(circ->rend_query)); } } return 0; } +void +rend_client_refetch_renddesc(char *query) +{ + if(connection_get_by_type_rendquery(CONN_TYPE_DIR, query)) { + log_fn(LOG_INFO,"Would fetch a new renddesc here (for %s), but one is already in progress.", query); + } else { + /* not one already; initiate a dir rend desc lookup */ + directory_initiate_command(router_pick_directory_server(), + DIR_PURPOSE_FETCH_RENDDESC, + query, strlen(query)); + } +} + +/* remove failed_intro from ent. if ent now has no intro points, or + * service is unrecognized, then launch a new renddesc fetch. + * + * Return -1 if error, 0 if no intro points remain or service + * unrecognized, 1 if recognized and some intro points remain. + */ +int +rend_client_remove_intro_point(char *failed_intro, char *query) +{ + int i, r; + rend_cache_entry_t *ent; + + r = rend_cache_lookup_entry(query, &ent); + if (r<0) { + log_fn(LOG_WARN, "Malformed service ID '%s'", query); + return -1; + } + if (r==0) { + log_fn(LOG_INFO, "Unknown service %s. Re-fetching descriptor.", query); + rend_client_refetch_renddesc(query); + return 0; + } + + for (i=0; i < ent->parsed->n_intro_points; ++i) { + if (!strcasecmp(ent->parsed->intro_points[i], failed_intro)) { + tor_free(ent->parsed->intro_points[i]); + ent->parsed->intro_points[i] = + ent->parsed->intro_points[--ent->parsed->n_intro_points]; + break; + } + } + + if(!ent->parsed->n_intro_points) { + log_fn(LOG_INFO,"No more intro points remain for %s. Re-fetching descriptor.", query); + rend_client_refetch_renddesc(query); + return 0; + } + log_fn(LOG_INFO,"%d options left for %s.", ent->parsed->n_intro_points, query); + return 1; +} + /* Called when we receive a RENDEZVOUS_ESTABLISHED cell; changes the state of * the circuit to C_REND_READY. */