diff options
author | Roger Dingledine <arma@torproject.org> | 2008-01-24 03:28:50 +0000 |
---|---|---|
committer | Roger Dingledine <arma@torproject.org> | 2008-01-24 03:28:50 +0000 |
commit | 6b1374556e877478f8bf0d394b379a8e29513967 (patch) | |
tree | 6c00c7cb13df04d6c7393651504f336eebcb4f61 /src | |
parent | 980fcb1ca79fe812bf6033c35424fc1c112389d5 (diff) | |
download | tor-6b1374556e877478f8bf0d394b379a8e29513967.tar.gz tor-6b1374556e877478f8bf0d394b379a8e29513967.zip |
put in karsten's "patch 14". needs a lot of cleanup and a changelog.
svn:r13250
Diffstat (limited to 'src')
-rw-r--r-- | src/or/directory.c | 147 | ||||
-rw-r--r-- | src/or/or.h | 2 | ||||
-rw-r--r-- | src/or/rendclient.c | 47 |
3 files changed, 172 insertions, 24 deletions
diff --git a/src/or/directory.c b/src/or/directory.c index 0819c99fe3..b1f7b759e6 100644 --- a/src/or/directory.c +++ b/src/or/directory.c @@ -893,8 +893,10 @@ directory_send_command(dir_connection_t *conn, break; case DIR_PURPOSE_FETCH_RENDDESC_V2: tor_assert(resource); - tor_assert(!payload); tor_assert(strlen(resource) <= REND_DESC_ID_V2_LEN_BASE32); + /* Remember the query to refer to it when a response arrives. */ + strlcpy(conn->rend_query, payload, sizeof(conn->rend_query)); + payload = NULL; httpcommand = "GET"; len = strlen(resource) + 32; url = tor_malloc(len); @@ -1804,9 +1806,11 @@ connection_dir_client_reached_eof(dir_connection_t *conn) switch (status_code) { case 200: if (rend_cache_store_v2_desc_as_client(body, NULL) < 0) { - log_warn(LD_REND,"Fetching v2 rendezvous descriptor failed."); + log_warn(LD_REND,"Fetching v2 rendezvous descriptor failed. " + "Retrying at another directory."); /* alice's ap_stream will notice when connection_mark_for_close * cleans it up */ + rend_client_refetch_v2_renddesc(conn->rend_query); } else { /* success. notify pending connections about this. */ log_info(LD_REND, "Successfully fetched rendezvous descriptor."); @@ -1817,18 +1821,25 @@ connection_dir_client_reached_eof(dir_connection_t *conn) case 404: /* not there. pending connections will be notified when * connection_mark_for_close cleans it up. */ + log_info(LD_REND,"Fetching v2 rendezvous descriptor failed: " + "Retrying at another directory."); + rend_client_refetch_v2_renddesc(conn->rend_query); break; case 400: log_warn(LD_REND, "Fetching v2 rendezvous descriptor failed: " "http status 400 (%s). Dirserver didn't like our " - "v2 rendezvous query?", escaped(reason)); + "v2 rendezvous query? Retrying at another directory.", + escaped(reason)); + rend_client_refetch_v2_renddesc(conn->rend_query); break; default: log_warn(LD_REND, "Fetching v2 rendezvous descriptor failed: " "http status %d (%s) response unexpected while " - "fetching v2 hidden service descriptor (server '%s:%d').", + "fetching v2 hidden service descriptor (server '%s:%d'). " + "Retrying at another directory.", status_code, escaped(reason), conn->_base.address, conn->_base.port); + rend_client_refetch_v2_renddesc(conn->rend_query); break; } } @@ -3237,15 +3248,90 @@ directory_post_to_hs_dir(smartlist_t *descs, const char *service_id, smartlist_free(responsible_dirs); } +/** The period for which a hidden service directory cannot be queried for + * the same descriptor ID again. */ +#define REND_HID_SERV_DIR_REQUERY_PERIOD (15 * 60) + +/** Contains the last request times to hidden service directories for + * certain queries; keys are strings consisting of base32-encoded + * hidden service directory identities and base32-encoded descriptor IDs; + * values are pointers to timestamps of the last requests. */ +static strmap_t *last_hid_serv_requests = NULL; + +/** Return the last request time to hidden service directory <b>hs_dir</b> + * for descriptor ID <b>desc_id_base32</b> or 0 if no such request has been + * sent before. */ +static time_t +get_last_hid_serv_request(routerstatus_t *hs_dir, const char *desc_id_base32) +{ + char hsdir_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1]; + char hsdir_desc_comb_id[2 * REND_DESC_ID_V2_LEN_BASE32 + 1]; + time_t *last_request_ptr; + 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", + hsdir_id_base32, desc_id_base32); + last_request_ptr = strmap_get_lc(last_hid_serv_requests, hsdir_desc_comb_id); + return (last_request_ptr) ? *last_request_ptr : 0; +} + +/** Store the time in <b>now</b> as the last request time to hidden service + * directory <b>hs_dir</b> for descriptor ID <b>desc_id_base32</b>. */ +static void +set_last_hid_serv_request(routerstatus_t *hs_dir, const char *desc_id_base32, + time_t now) +{ + char hsdir_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1]; + char hsdir_desc_comb_id[2 * REND_DESC_ID_V2_LEN_BASE32 + 1]; + time_t *last_request_ptr = tor_malloc_zero(sizeof(time_t *)); + if (!last_hid_serv_requests) last_hid_serv_requests = strmap_new(); + *last_request_ptr = now; + 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", + hsdir_id_base32, desc_id_base32); + strmap_set(last_hid_serv_requests, hsdir_desc_comb_id, last_request_ptr); +} + +/** Clean the history of request times to hidden service directories, so that + * it does not contain requests older than REND_HID_SERV_DIR_REQUERY_PERIOD + * seconds any more. */ +static void +directory_clean_last_hid_serv_requests(void) +{ + strmap_iter_t *iter; + time_t cutoff = time(NULL) - REND_HID_SERV_DIR_REQUERY_PERIOD; + for (iter = strmap_iter_init(last_hid_serv_requests); + !strmap_iter_done(iter); ) { + const char *key; + void *val; + time_t *ent; + strmap_iter_get(iter, &key, &val); + ent = (time_t *) val; + if (*ent < cutoff) { + iter = strmap_iter_next_rmv(last_hid_serv_requests, iter); + tor_free(ent); + } else { + iter = strmap_iter_next(last_hid_serv_requests, iter); + } + } +} + /** Determine the responsible hidden service directories for <b>desc_id</b> - * and fetch the descriptor belonging to this ID from one of them; - * <b>query</b> is only passed for pretty log statements. */ -void + * and fetch the descriptor belonging to that ID from one of them. Only + * send a request to hidden service directories that we did not try within + * the last REND_HID_SERV_DIR_REQUERY_PERIOD seconds; 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. <b>query</b> is only + * passed for pretty log statements. */ +int directory_get_from_hs_dir(const char *desc_id, const char *query) { smartlist_t *responsible_dirs = smartlist_create(); + smartlist_t *selectible_dirs = smartlist_create(); routerstatus_t *hs_dir; char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1]; + time_t now = time(NULL); tor_assert(desc_id); tor_assert(query); tor_assert(strlen(query) == REND_SERVICE_ID_LEN_BASE32); @@ -3255,26 +3341,57 @@ directory_get_from_hs_dir(const char *desc_id, const char *query) log_info(LD_REND, "Could not determine the responsible hidden service " "directories to fetch descriptors."); smartlist_free(responsible_dirs); - return; + return -1; + } + + base32_encode(desc_id_base32, sizeof(desc_id_base32), + desc_id, DIGEST_LEN); + + /* Only select those hidden service directories to which we did not send + * a request earlier. */ + if (last_hid_serv_requests) { + /* Clean up request history first. */ + directory_clean_last_hid_serv_requests(); + + SMARTLIST_FOREACH(responsible_dirs, routerstatus_t *, dir, { + time_t last_request = get_last_hid_serv_request(dir, desc_id_base32); + if (!last_request || + last_request + REND_HID_SERV_DIR_REQUERY_PERIOD < now) + smartlist_add(selectible_dirs, dir); + }); + } else { + smartlist_add_all(selectible_dirs, responsible_dirs); } - hs_dir = smartlist_choose(responsible_dirs); smartlist_free(responsible_dirs); + + if (smartlist_len(selectible_dirs) == 0) { + log_info(LD_REND, "Could not pick one of the responsible hidden " + "service directories, because we requested them all " + "recently without success."); + return 0; + } + hs_dir = smartlist_choose(selectible_dirs); + smartlist_free(selectible_dirs); if (!hs_dir) { log_warn(LD_BUG, "Could not pick one of the responsible hidden service " "directories to fetch descriptors."); - return; + return -1; } - /* XXXX020 if hsdir fails, use another one... */ - base32_encode(desc_id_base32, sizeof(desc_id_base32), - desc_id, DIGEST_LEN); - /* Send fetch request. */ + + /* Remember, that we are requesting a descriptor from this hidden service + * directory now. */ + set_last_hid_serv_request(hs_dir, desc_id_base32, now); + + /* Send fetch request. (Pass query as payload to write it to the directory + * connection so that it can be referred to when the response arrives.) */ directory_initiate_command_routerstatus(hs_dir, DIR_PURPOSE_FETCH_RENDDESC_V2, ROUTER_PURPOSE_GENERAL, - 1, desc_id_base32, NULL, 0, 0); + 1, desc_id_base32, query, 0, 0); log_info(LD_REND, "Sending fetch request for v2 descriptor for " "service '%s' with descriptor ID '%s' to hidden " "service directory '%s' on port %d.", query, desc_id_base32, hs_dir->nickname, hs_dir->dir_port); + return 1; } diff --git a/src/or/or.h b/src/or/or.h index 9d019dc557..edc786b101 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -3035,7 +3035,7 @@ int router_supports_extrainfo(const char *identity_digest, int is_authority); void directory_post_to_hs_dir(smartlist_t *descs, const char *service_id, int seconds_valid); -void directory_get_from_hs_dir(const char *desc_id, const char *query); +int directory_get_from_hs_dir(const char *desc_id, const char *query); time_t download_status_increment_failure(download_status_t *dls, int status_code, const char *item, diff --git a/src/or/rendclient.c b/src/or/rendclient.c index 3c5f75552c..2dfc50580f 100644 --- a/src/or/rendclient.c +++ b/src/or/rendclient.c @@ -295,7 +295,8 @@ void rend_client_refetch_v2_renddesc(const char *query) { char descriptor_id[DIGEST_LEN]; - int replica; + int replicas_left_to_try[REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS]; + int i, tries_left; tor_assert(query); tor_assert(strlen(query) == REND_SERVICE_ID_LEN_BASE32); /* Are we configured to fetch descriptors? */ @@ -306,14 +307,44 @@ rend_client_refetch_v2_renddesc(const char *query) } log_debug(LD_REND, "Fetching v2 rendezvous descriptor for service %s", query); - replica = crypto_rand_int(REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS); - if (rend_compute_v2_desc_id(descriptor_id, query, NULL, time(NULL), - replica) < 0) { - log_warn(LD_REND, "Internal error: Computing v2 rendezvous " - "descriptor ID did not succeed."); - return; + /* 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, query, NULL, time(NULL), + chosen_replica) < 0) { + log_warn(LD_REND, "Internal error: Computing v2 rendezvous " + "descriptor ID did not succeed."); + return; + } + switch (directory_get_from_hs_dir(descriptor_id, query)) { + case -1: + /* Whatever error this was, it was already logged. */ + log_info(LD_REND, "Error while trying to fetch descriptor from " + "hidden service directory!"); + return; + case 0: + /* Try the next replica, if available. */ + log_info(LD_REND, "No hidden service directory left for this replica; " + "trying another."); + continue; + case 1: + /* Request was sent, we are done here. */ + log_info(LD_REND, "Request to fetch descriptor from hidden service " + "directory sent; waiting for response."); + return; + } } - directory_get_from_hs_dir(descriptor_id, query); + /* 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."); return; } |