diff options
Diffstat (limited to 'src/or/rendclient.c')
-rw-r--r-- | src/or/rendclient.c | 461 |
1 files changed, 315 insertions, 146 deletions
diff --git a/src/or/rendclient.c b/src/or/rendclient.c index 162e0ac53e..a39e518e99 100644 --- a/src/or/rendclient.c +++ b/src/or/rendclient.c @@ -38,6 +38,7 @@ void rend_client_purge_state(void) { rend_cache_purge(); + rend_cache_failure_purge(); rend_client_cancel_descriptor_fetches(); rend_client_purge_last_hid_serv_requests(); } @@ -141,7 +142,7 @@ rend_client_send_introduction(origin_circuit_t *introcirc, int r, v3_shift = 0; char payload[RELAY_PAYLOAD_SIZE]; char tmp[RELAY_PAYLOAD_SIZE]; - rend_cache_entry_t *entry; + rend_cache_entry_t *entry = NULL; crypt_path_t *cpath; off_t dh_offset; crypto_pk_t *intro_key = NULL; @@ -158,8 +159,13 @@ rend_client_send_introduction(origin_circuit_t *introcirc, tor_assert(!(rendcirc->build_state->onehop_tunnel)); #endif - if (rend_cache_lookup_entry(introcirc->rend_data->onion_address, -1, - &entry) < 1) { + r = rend_cache_lookup_entry(introcirc->rend_data->onion_address, -1, + &entry); + /* An invalid onion address is not possible else we have a big issue. */ + tor_assert(r != -EINVAL); + if (r < 0 || !rend_client_any_intro_points_usable(entry)) { + /* If the descriptor is not found or the intro points are not usable + * anymore, trigger a fetch. */ log_info(LD_REND, "query %s didn't have valid rend desc in cache. " "Refetching descriptor.", @@ -469,9 +475,8 @@ rend_client_introduction_acked(origin_circuit_t *circ, /** Contains the last request times to hidden service directories for * certain queries; each key is a string consisting of the - * concatenation of a base32-encoded HS directory identity digest, a - * base32-encoded HS descriptor ID, and a hidden service address - * (without the ".onion" part); each value is a pointer to a time_t + * concatenation of a base32-encoded HS directory identity digest and + * base32-encoded HS descriptor ID; each value is a pointer to a time_t * holding the time of the last request for that descriptor ID to that * HS directory. */ static strmap_t *last_hid_serv_requests_ = NULL; @@ -487,19 +492,16 @@ get_last_hid_serv_requests(void) } #define LAST_HID_SERV_REQUEST_KEY_LEN (REND_DESC_ID_V2_LEN_BASE32 + \ - REND_DESC_ID_V2_LEN_BASE32 + \ - REND_SERVICE_ID_LEN_BASE32) + REND_DESC_ID_V2_LEN_BASE32) /** Look up the last request time to hidden service directory <b>hs_dir</b> - * for descriptor ID <b>desc_id_base32</b> for the service specified in - * <b>rend_query</b>. If <b>set</b> is non-zero, - * assign the current time <b>now</b> and return that. Otherwise, return - * the most recent request time, or 0 if no such request has been sent - * before. */ + * for descriptor ID <b>desc_id_base32</b>. If <b>set</b> is non-zero, + * assign the current time <b>now</b> and return that. Otherwise, return the + * most recent request time, or 0 if no such request has been sent before. + */ static time_t lookup_last_hid_serv_request(routerstatus_t *hs_dir, const char *desc_id_base32, - const rend_data_t *rend_query, time_t now, int set) { char hsdir_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1]; @@ -508,10 +510,9 @@ lookup_last_hid_serv_request(routerstatus_t *hs_dir, strmap_t *last_hid_serv_requests = get_last_hid_serv_requests(); base32_encode(hsdir_id_base32, sizeof(hsdir_id_base32), hs_dir->identity_digest, DIGEST_LEN); - tor_snprintf(hsdir_desc_comb_id, sizeof(hsdir_desc_comb_id), "%s%s%s", + tor_snprintf(hsdir_desc_comb_id, sizeof(hsdir_desc_comb_id), "%s%s", hsdir_id_base32, - desc_id_base32, - rend_query->onion_address); + desc_id_base32); /* XXX023 tor_assert(strlen(hsdir_desc_comb_id) == LAST_HID_SERV_REQUEST_KEY_LEN); */ if (set) { @@ -552,20 +553,23 @@ directory_clean_last_hid_serv_requests(time_t now) } } -/** Remove all requests related to the hidden service named - * <b>onion_address</b> from the history of times of requests to - * hidden service directories. +/** Remove all requests related to the descriptor ID <b>desc_id</b> from the + * history of times of requests to hidden service directories. + * <b>desc_id</b> is an unencoded descriptor ID of size DIGEST_LEN. * * This is called from rend_client_note_connection_attempt_ended(), which - * must be idempotent, so any future changes to this function must leave - * it idempotent too. - */ + * must be idempotent, so any future changes to this function must leave it + * idempotent too. */ static void -purge_hid_serv_from_last_hid_serv_requests(const char *onion_address) +purge_hid_serv_from_last_hid_serv_requests(const char *desc_id) { strmap_iter_t *iter; strmap_t *last_hid_serv_requests = get_last_hid_serv_requests(); - /* XXX023 tor_assert(strlen(onion_address) == REND_SERVICE_ID_LEN_BASE32); */ + char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1]; + + /* Key is stored with the base32 encoded desc_id. */ + base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_id, + DIGEST_LEN); for (iter = strmap_iter_init(last_hid_serv_requests); !strmap_iter_done(iter); ) { const char *key; @@ -573,9 +577,9 @@ purge_hid_serv_from_last_hid_serv_requests(const char *onion_address) strmap_iter_get(iter, &key, &val); /* XXX023 tor_assert(strlen(key) == LAST_HID_SERV_REQUEST_KEY_LEN); */ if (tor_memeq(key + LAST_HID_SERV_REQUEST_KEY_LEN - - REND_SERVICE_ID_LEN_BASE32, - onion_address, - REND_SERVICE_ID_LEN_BASE32)) { + REND_DESC_ID_V2_LEN_BASE32, + desc_id_base32, + REND_DESC_ID_V2_LEN_BASE32)) { iter = strmap_iter_next_rmv(last_hid_serv_requests, iter); tor_free(val); } else { @@ -604,64 +608,53 @@ rend_client_purge_last_hid_serv_requests(void) } } -/** Determine the responsible hidden service directories for <b>desc_id</b> - * and fetch the descriptor with that ID from one of them. Only - * send a request to a hidden service directory that we have not yet tried - * during this attempt to connect to this hidden service; on success, return 1, - * in the case that no hidden service directory is left to ask for the - * descriptor, return 0, and in case of a failure -1. */ -static int -directory_get_from_hs_dir(const char *desc_id, const rend_data_t *rend_query) +/** This returns a good valid hs dir that should be used for the given + * descriptor id. + * + * Return NULL on error else the hsdir node pointer. */ +static routerstatus_t * +pick_hsdir(const char *desc_id, const char *desc_id_base32) { smartlist_t *responsible_dirs = smartlist_new(); smartlist_t *usable_responsible_dirs = smartlist_new(); const or_options_t *options = get_options(); routerstatus_t *hs_dir; - char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1]; time_t now = time(NULL); - char descriptor_cookie_base64[3*REND_DESC_COOKIE_LEN_BASE64]; -#ifdef ENABLE_TOR2WEB_MODE - const int tor2web_mode = options->Tor2webMode; - const int how_to_fetch = tor2web_mode ? DIRIND_ONEHOP : DIRIND_ANONYMOUS; -#else - const int how_to_fetch = DIRIND_ANONYMOUS; -#endif int excluded_some; - tor_assert(desc_id); - tor_assert(rend_query); - /* Determine responsible dirs. Even if we can't get all we want, - * work with the ones we have. If it's empty, we'll notice below. */ - hid_serv_get_responsible_directories(responsible_dirs, desc_id); - base32_encode(desc_id_base32, sizeof(desc_id_base32), - desc_id, DIGEST_LEN); + tor_assert(desc_id); + tor_assert(desc_id_base32); - /* Only select those hidden service directories to which we did not send - * a request recently and for which we have a router descriptor here. */ + /* Determine responsible dirs. Even if we can't get all we want, work with + * the ones we have. If it's empty, we'll notice below. */ + hid_serv_get_responsible_directories(responsible_dirs, desc_id); /* Clean request history first. */ directory_clean_last_hid_serv_requests(now); - SMARTLIST_FOREACH(responsible_dirs, routerstatus_t *, dir, { - time_t last = lookup_last_hid_serv_request( - dir, desc_id_base32, rend_query, 0, 0); - const node_t *node = node_get_by_id(dir->identity_digest); - if (last + REND_HID_SERV_DIR_REQUERY_PERIOD >= now || - !node || !node_has_descriptor(node)) { - SMARTLIST_DEL_CURRENT(responsible_dirs, dir); - continue; - } - if (! routerset_contains_node(options->ExcludeNodes, node)) { - smartlist_add(usable_responsible_dirs, dir); - } - }); + /* Only select those hidden service directories to which we did not send a + * request recently and for which we have a router descriptor here. */ + SMARTLIST_FOREACH_BEGIN(responsible_dirs, routerstatus_t *, dir) { + time_t last = lookup_last_hid_serv_request(dir, desc_id_base32, + 0, 0); + const node_t *node = node_get_by_id(dir->identity_digest); + if (last + REND_HID_SERV_DIR_REQUERY_PERIOD >= now || + !node || !node_has_descriptor(node)) { + SMARTLIST_DEL_CURRENT(responsible_dirs, dir); + continue; + } + if (!routerset_contains_node(options->ExcludeNodes, node)) { + smartlist_add(usable_responsible_dirs, dir); + } + } SMARTLIST_FOREACH_END(dir); excluded_some = smartlist_len(usable_responsible_dirs) < smartlist_len(responsible_dirs); hs_dir = smartlist_choose(usable_responsible_dirs); - if (! hs_dir && ! options->StrictNodes) + if (!hs_dir && !options->StrictNodes) { hs_dir = smartlist_choose(responsible_dirs); + } smartlist_free(responsible_dirs); smartlist_free(usable_responsible_dirs); @@ -674,23 +667,69 @@ directory_get_from_hs_dir(const char *desc_id, const rend_data_t *rend_query) "requested hidden service: they are all either down or " "excluded, and StrictNodes is set."); } - return 0; + } else { + /* Remember that we are requesting a descriptor from this hidden service + * directory now. */ + lookup_last_hid_serv_request(hs_dir, desc_id_base32, now, 1); + } + + return hs_dir; +} + +/** Determine the responsible hidden service directories for <b>desc_id</b> + * and fetch the descriptor with that ID from one of them. Only + * send a request to a hidden service directory that we have not yet tried + * during this attempt to connect to this hidden service; on success, return 1, + * in the case that no hidden service directory is left to ask for the + * descriptor, return 0, and in case of a failure -1. */ +static int +directory_get_from_hs_dir(const char *desc_id, const rend_data_t *rend_query, + routerstatus_t *rs_hsdir) +{ + routerstatus_t *hs_dir = rs_hsdir; + char *hsdir_fp; + char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1]; + char descriptor_cookie_base64[3*REND_DESC_COOKIE_LEN_BASE64]; +#ifdef ENABLE_TOR2WEB_MODE + const int tor2web_mode = get_options()->Tor2webMode; + const int how_to_fetch = tor2web_mode ? DIRIND_ONEHOP : DIRIND_ANONYMOUS; +#else + const int how_to_fetch = DIRIND_ANONYMOUS; +#endif + + tor_assert(desc_id); + + base32_encode(desc_id_base32, sizeof(desc_id_base32), + desc_id, DIGEST_LEN); + + /* Automatically pick an hs dir if none given. */ + if (!rs_hsdir) { + hs_dir = pick_hsdir(desc_id, desc_id_base32); + if (!hs_dir) { + /* No suitable hs dir can be found, stop right now. */ + return 0; + } } - /* Remember that we are requesting a descriptor from this hidden service - * directory now. */ - lookup_last_hid_serv_request(hs_dir, desc_id_base32, rend_query, now, 1); + /* Add a copy of the HSDir identity digest to the query so we can track it + * on the control port. */ + hsdir_fp = tor_memdup(hs_dir->identity_digest, + sizeof(hs_dir->identity_digest)); + smartlist_add(rend_query->hsdirs_fp, hsdir_fp); - /* Encode descriptor cookie for logging purposes. */ + /* Encode descriptor cookie for logging purposes. Also, if the cookie is + * malformed, no fetch is triggered thus this needs to be done before the + * fetch request. */ if (rend_query->auth_type != REND_NO_AUTH) { if (base64_encode(descriptor_cookie_base64, sizeof(descriptor_cookie_base64), - rend_query->descriptor_cookie, REND_DESC_COOKIE_LEN)<0) { + rend_query->descriptor_cookie, REND_DESC_COOKIE_LEN, + 0)<0) { log_warn(LD_BUG, "Could not base64-encode descriptor cookie."); return 0; } - /* Remove == signs and newline. */ - descriptor_cookie_base64[strlen(descriptor_cookie_base64)-3] = '\0'; + /* Remove == signs. */ + descriptor_cookie_base64[strlen(descriptor_cookie_base64)-2] = '\0'; } else { strlcpy(descriptor_cookie_base64, "(none)", sizeof(descriptor_cookie_base64)); @@ -721,16 +760,144 @@ directory_get_from_hs_dir(const char *desc_id, const rend_data_t *rend_query) return 1; } +/** Fetch a v2 descriptor using the given descriptor id. If any hsdir(s) are + * given, they will be used instead. + * + * On success, 1 is returned. If no hidden service is left to ask, return 0. + * On error, -1 is returned. */ +static int +fetch_v2_desc_by_descid(const char *desc_id, const rend_data_t *rend_query, + smartlist_t *hsdirs) +{ + int ret; + + tor_assert(rend_query); + + if (!hsdirs) { + ret = directory_get_from_hs_dir(desc_id, rend_query, NULL); + goto end; /* either success or failure, but we're done */ + } + + /* Using the given hsdir list, trigger a fetch on each of them. */ + SMARTLIST_FOREACH_BEGIN(hsdirs, routerstatus_t *, hs_dir) { + /* This should always be a success. */ + ret = directory_get_from_hs_dir(desc_id, rend_query, hs_dir); + tor_assert(ret); + } SMARTLIST_FOREACH_END(hs_dir); + + /* Everything went well. */ + ret = 0; + + end: + return ret; +} + +/** Fetch a v2 descriptor using the onion address in the given query object. + * This will compute the descriptor id for each replicas and fetch it on the + * given hsdir(s) if any or the responsible ones that are choosen + * automatically. + * + * On success, 1 is returned. If no hidden service is left to ask, return 0. + * On error, -1 is returned. */ +static int +fetch_v2_desc_by_addr(rend_data_t *query, smartlist_t *hsdirs) +{ + char descriptor_id[DIGEST_LEN]; + int replicas_left_to_try[REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS]; + int i, tries_left, ret; + + tor_assert(query); + + /* Randomly iterate over the replicas until a descriptor can be fetched + * from one of the consecutive nodes, or no options are left. */ + for (i = 0; i < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; i++) { + replicas_left_to_try[i] = i; + } + + tries_left = REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; + while (tries_left > 0) { + int rand = crypto_rand_int(tries_left); + int chosen_replica = replicas_left_to_try[rand]; + replicas_left_to_try[rand] = replicas_left_to_try[--tries_left]; + + ret = rend_compute_v2_desc_id(descriptor_id, query->onion_address, + query->auth_type == REND_STEALTH_AUTH ? + query->descriptor_cookie : NULL, + time(NULL), chosen_replica); + if (ret < 0) { + /* Normally, on failure the descriptor_id is untouched but let's be + * safe in general in case the function changes at some point. */ + goto end; + } + + if (tor_memcmp(descriptor_id, query->descriptor_id[chosen_replica], + sizeof(descriptor_id)) != 0) { + /* Not equal from what we currently have so purge the last hid serv + * request cache and update the descriptor ID with the new value. */ + purge_hid_serv_from_last_hid_serv_requests( + query->descriptor_id[chosen_replica]); + memcpy(query->descriptor_id[chosen_replica], descriptor_id, + sizeof(query->descriptor_id[chosen_replica])); + } + + /* Trigger the fetch with the computed descriptor ID. */ + ret = fetch_v2_desc_by_descid(descriptor_id, query, hsdirs); + if (ret != 0) { + /* Either on success or failure, as long as we tried a fetch we are + * done here. */ + goto end; + } + } + + /* If we come here, there are no hidden service directories left. */ + log_info(LD_REND, "Could not pick one of the responsible hidden " + "service directories to fetch descriptors, because " + "we already tried them all unsuccessfully."); + ret = 0; + + end: + memwipe(descriptor_id, 0, sizeof(descriptor_id)); + return ret; +} + +/** Fetch a v2 descriptor using the given query. If any hsdir are specified, + * use them for the fetch. + * + * On success, 1 is returned. If no hidden service is left to ask, return 0. + * On error, -1 is returned. */ +int +rend_client_fetch_v2_desc(rend_data_t *query, smartlist_t *hsdirs) +{ + int ret; + + tor_assert(query); + + /* Depending on what's available in the rend data query object, we will + * trigger a fetch by HS address or using a descriptor ID. */ + + if (query->onion_address[0] != '\0') { + ret = fetch_v2_desc_by_addr(query, hsdirs); + } else if (!tor_digest_is_zero(query->desc_id_fetch)) { + ret = fetch_v2_desc_by_descid(query->desc_id_fetch, query, hsdirs); + } else { + /* Query data is invalid. */ + ret = -1; + goto error; + } + + error: + return ret; +} + /** Unless we already have a descriptor for <b>rend_query</b> with at least * one (possibly) working introduction point in it, start a connection to a * hidden service directory to fetch a v2 rendezvous service descriptor. */ void -rend_client_refetch_v2_renddesc(const rend_data_t *rend_query) +rend_client_refetch_v2_renddesc(rend_data_t *rend_query) { - char descriptor_id[DIGEST_LEN]; - int replicas_left_to_try[REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS]; - int i, tries_left; + int ret; rend_cache_entry_t *e = NULL; + tor_assert(rend_query); /* Are we configured to fetch descriptors? */ if (!get_options()->FetchHidServDescriptors) { @@ -739,7 +906,7 @@ rend_client_refetch_v2_renddesc(const rend_data_t *rend_query) return; } /* Before fetching, check if we already have a usable descriptor here. */ - if (rend_cache_lookup_entry(rend_query->onion_address, -1, &e) > 0 && + if (rend_cache_lookup_entry(rend_query->onion_address, -1, &e) == 0 && rend_client_any_intro_points_usable(e)) { log_info(LD_REND, "We would fetch a v2 rendezvous descriptor, but we " "already have a usable descriptor here. Not fetching."); @@ -747,44 +914,12 @@ rend_client_refetch_v2_renddesc(const rend_data_t *rend_query) } log_debug(LD_REND, "Fetching v2 rendezvous descriptor for service %s", safe_str_client(rend_query->onion_address)); - /* Randomly iterate over the replicas until a descriptor can be fetched - * from one of the consecutive nodes, or no options are left. */ - tries_left = REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; - for (i = 0; i < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; i++) - replicas_left_to_try[i] = i; - while (tries_left > 0) { - int rand = crypto_rand_int(tries_left); - int chosen_replica = replicas_left_to_try[rand]; - replicas_left_to_try[rand] = replicas_left_to_try[--tries_left]; - if (rend_compute_v2_desc_id(descriptor_id, rend_query->onion_address, - rend_query->auth_type == REND_STEALTH_AUTH ? - rend_query->descriptor_cookie : NULL, - time(NULL), chosen_replica) < 0) { - log_warn(LD_REND, "Internal error: Computing v2 rendezvous " - "descriptor ID did not succeed."); - /* - * Hmm, can this write anything to descriptor_id and still fail? - * Let's clear it just to be safe. - * - * From here on, any returns should goto done which clears - * descriptor_id so we don't leave key-derived material on the stack. - */ - goto done; - } - if (directory_get_from_hs_dir(descriptor_id, rend_query) != 0) - goto done; /* either success or failure, but we're done */ + ret = rend_client_fetch_v2_desc(rend_query, NULL); + if (ret <= 0) { + /* Close pending connections on error or if no hsdir can be found. */ + rend_client_desc_trynow(rend_query->onion_address); } - /* If we come here, there are no hidden service directories left. */ - log_info(LD_REND, "Could not pick one of the responsible hidden " - "service directories to fetch descriptors, because " - "we already tried them all unsuccessfully."); - /* Close pending connections. */ - rend_client_desc_trynow(rend_query->onion_address); - - done: - memwipe(descriptor_id, 0, sizeof(descriptor_id)); - return; } @@ -845,7 +980,7 @@ rend_client_cancel_descriptor_fetches(void) */ int rend_client_report_intro_point_failure(extend_info_t *failed_intro, - const rend_data_t *rend_query, + rend_data_t *rend_query, unsigned int failure_type) { int i, r; @@ -853,17 +988,26 @@ rend_client_report_intro_point_failure(extend_info_t *failed_intro, connection_t *conn; r = rend_cache_lookup_entry(rend_query->onion_address, -1, &ent); - if (r<0) { - log_warn(LD_BUG, "Malformed service ID %s.", - escaped_safe_str_client(rend_query->onion_address)); - return -1; - } - if (r==0) { - log_info(LD_REND, "Unknown service %s. Re-fetching descriptor.", - escaped_safe_str_client(rend_query->onion_address)); - rend_client_refetch_v2_renddesc(rend_query); - return 0; + if (r < 0) { + /* Either invalid onion address or cache entry not found. */ + switch (-r) { + case EINVAL: + log_warn(LD_BUG, "Malformed service ID %s.", + escaped_safe_str_client(rend_query->onion_address)); + return -1; + case ENOENT: + log_info(LD_REND, "Unknown service %s. Re-fetching descriptor.", + escaped_safe_str_client(rend_query->onion_address)); + rend_client_refetch_v2_renddesc(rend_query); + return 0; + default: + log_warn(LD_BUG, "Unknown cache lookup returned code: %d", r); + return -1; + } } + /* The intro points are not checked here if they are usable or not because + * this is called when an intro point circuit is closed thus there must be + * at least one intro point that is usable and is about to be flagged. */ for (i = 0; i < smartlist_len(ent->parsed->intro_nodes); i++) { rend_intro_point_t *intro = smartlist_get(ent->parsed->intro_nodes, i); @@ -876,6 +1020,9 @@ rend_client_report_intro_point_failure(extend_info_t *failed_intro, tor_fragile_assert(); /* fall through */ case INTRO_POINT_FAILURE_GENERIC: + rend_cache_intro_failure_note(failure_type, + (uint8_t *)failed_intro->identity_digest, + rend_query->onion_address); rend_intro_point_free(intro); smartlist_del(ent->parsed->intro_nodes, i); break; @@ -891,6 +1038,10 @@ rend_client_report_intro_point_failure(extend_info_t *failed_intro, intro->unreachable_count, zap_intro_point ? " Removing from descriptor.": ""); if (zap_intro_point) { + rend_cache_intro_failure_note( + failure_type, + (uint8_t *) failed_intro->identity_digest, + rend_query->onion_address); rend_intro_point_free(intro); smartlist_del(ent->parsed->intro_nodes, i); } @@ -1062,7 +1213,7 @@ rend_client_desc_trynow(const char *query) continue; assert_connection_ok(base_conn, now); if (rend_cache_lookup_entry(rend_data->onion_address, -1, - &entry) == 1 && + &entry) == 0 && rend_client_any_intro_points_usable(entry)) { /* either this fetch worked, or it failed but there was a * valid entry from before which we should reuse */ @@ -1086,27 +1237,28 @@ rend_client_desc_trynow(const char *query) "unavailable (try again later).", safe_str_client(query)); connection_mark_unattached_ap(conn, END_STREAM_REASON_RESOLVEFAILED); - rend_client_note_connection_attempt_ended(query); + rend_client_note_connection_attempt_ended(rend_data); } } SMARTLIST_FOREACH_END(base_conn); } -/** Clear temporary state used only during an attempt to connect to - * the hidden service named <b>onion_address</b>. Called when a - * connection attempt has ended; it is possible for this to be called - * multiple times while handling an ended connection attempt, and - * any future changes to this function must ensure it remains - * idempotent. - */ +/** Clear temporary state used only during an attempt to connect to the + * hidden service with <b>rend_data</b>. Called when a connection attempt + * has ended; it is possible for this to be called multiple times while + * handling an ended connection attempt, and any future changes to this + * function must ensure it remains idempotent. */ void -rend_client_note_connection_attempt_ended(const char *onion_address) +rend_client_note_connection_attempt_ended(const rend_data_t *rend_data) { + unsigned int have_onion = 0; rend_cache_entry_t *cache_entry = NULL; - rend_cache_lookup_entry(onion_address, -1, &cache_entry); - log_info(LD_REND, "Connection attempt for %s has ended; " - "cleaning up temporary state.", - safe_str_client(onion_address)); + if (*rend_data->onion_address != '\0') { + /* Ignore return value; we find an entry, or we don't. */ + (void) rend_cache_lookup_entry(rend_data->onion_address, -1, + &cache_entry); + have_onion = 1; + } /* Clear the timed_out flag on all remaining intro points for this HS. */ if (cache_entry != NULL) { @@ -1116,7 +1268,20 @@ rend_client_note_connection_attempt_ended(const char *onion_address) } /* Remove the HS's entries in last_hid_serv_requests. */ - purge_hid_serv_from_last_hid_serv_requests(onion_address); + if (have_onion) { + unsigned int replica; + for (replica = 0; replica < ARRAY_LENGTH(rend_data->descriptor_id); + replica++) { + const char *desc_id = rend_data->descriptor_id[replica]; + purge_hid_serv_from_last_hid_serv_requests(desc_id); + } + log_info(LD_REND, "Connection attempt for %s has ended; " + "cleaning up temporary state.", + safe_str_client(rend_data->onion_address)); + } else { + /* We only have an ID for a fetch. Probably used by HSFETCH. */ + purge_hid_serv_from_last_hid_serv_requests(rend_data->desc_id_fetch); + } } /** Return a newly allocated extend_info_t* for a randomly chosen introduction @@ -1126,13 +1291,17 @@ rend_client_note_connection_attempt_ended(const char *onion_address) extend_info_t * rend_client_get_random_intro(const rend_data_t *rend_query) { + int ret; extend_info_t *result; rend_cache_entry_t *entry; - if (rend_cache_lookup_entry(rend_query->onion_address, -1, &entry) < 1) { - log_warn(LD_REND, - "Query '%s' didn't have valid rend desc in cache. Failing.", - safe_str_client(rend_query->onion_address)); + ret = rend_cache_lookup_entry(rend_query->onion_address, -1, &entry); + if (ret < 0 || !rend_client_any_intro_points_usable(entry)) { + log_warn(LD_REND, + "Query '%s' didn't have valid rend desc in cache. Failing.", + safe_str_client(rend_query->onion_address)); + /* XXX: Should we refetch the descriptor here if the IPs are not usable + * anymore ?. */ return NULL; } |