From ab9a0e340728abd96128da726f67b4ccca10ba52 Mon Sep 17 00:00:00 2001 From: David Goulet Date: Thu, 18 Jun 2015 16:09:18 -0400 Subject: [PATCH 1/6] Add rend failure cache When fetching a descriptor, we know test every introduction points in it against our rend failure cache to know if we keep it or not. For this to work, now everytime an introduction points is discareded (ex: receiving a NACK), we note it down in our introduction cache. See rendcache.c for a detailed explanation of the cache's behavior. Fixes #16389 Signed-off-by: David Goulet --- src/or/rendcache.c | 259 +++++++++++++++++++++++++++++++++++++++++++- src/or/rendcache.h | 16 +++ src/or/rendclient.c | 6 + 3 files changed, 278 insertions(+), 3 deletions(-) diff --git a/src/or/rendcache.c b/src/or/rendcache.c index be94ef4445..7a86546b7a 100644 --- a/src/or/rendcache.c +++ b/src/or/rendcache.c @@ -9,7 +9,6 @@ #include "rendcache.h" #include "config.h" -#include "rendcommon.h" #include "rephist.h" #include "routerlist.h" #include "routerparse.h" @@ -22,6 +21,33 @@ static strmap_t *rend_cache = NULL; * directories. */ static digestmap_t *rend_cache_v2_dir = NULL; +/** (Client side only) Map from service id to rend_cache_failure_t. This + * cache is used to track intro point(IP) failures so we know when to keep + * or discard a new descriptor we just fetched. Here is a description of the + * cache behavior. + * + * Everytime tor discards an IP (ex: receives a NACK), we add an entry to + * this cache noting the identity digest of the IP and it's failure type for + * the service ID. The reason we indexed this cache by service ID is to + * differentiate errors that can occur only for a specific service like a + * NACK for instance. It applies for one but maybe not for the others. + * + * Once a service descriptor is fetched and considered valid, each IP is + * looked up in this cache and if present, it is discarded from the fetched + * descriptor. At the end, all IP(s) in the cache, for a specific service + * ID, that were NOT present in the descriptor are removed from this cache. + * Which means that if at least one IP was not in this cache, thus usuable, + * it's considered a new descriptor so we keep it. Else, if all IPs were in + * this cache, we discard the descriptor as it's considered unsuable. + * + * Once a descriptor is removed from the rend cache or expires, the entry + * in this cache is also removed for the service ID. + * + * This scheme allows us to not realy on the descriptor's timestamp (which + * is rounded down to the hour) to know if we have a newer descriptor. We + * only rely on the usability of intro points from an internal state. */ +static strmap_t *rend_cache_failure = NULL; + /** DOCDOC */ static size_t rend_cache_total_allocation = 0; @@ -32,6 +58,7 @@ rend_cache_init(void) { rend_cache = strmap_new(); rend_cache_v2_dir = digestmap_new(); + rend_cache_failure = strmap_new(); } /** Return the approximate number of bytes needed to hold e. */ @@ -85,6 +112,82 @@ rend_cache_increment_allocation(size_t n) } } +/** Helper: free a rend cache failure intro object. */ +static void +rend_cache_failure_intro_entry_free(rend_cache_failure_intro_t *entry) +{ + if (entry == NULL) { + return; + } + tor_free(entry); +} + +/** Allocate a rend cache failure intro object and return it. failure + * is set into the object. This function can not fail. */ +static rend_cache_failure_intro_t * +rend_cache_failure_intro_entry_new(unsigned int failure) +{ + rend_cache_failure_intro_t *entry = tor_malloc(sizeof(*entry)); + entry->failure_type = failure; + return entry; +} + +/** Helper: free a rend cache failure object. */ +static void +rend_cache_failure_entry_free(rend_cache_failure_t *entry) +{ + if (entry == NULL) { + return; + } + + /* Free and remove every intro failure object. */ + DIGESTMAP_FOREACH_MODIFY(entry->intro_failures, key, + rend_cache_failure_intro_t *, e) { + rend_cache_failure_intro_entry_free(e); + MAP_DEL_CURRENT(key); + } DIGESTMAP_FOREACH_END; + tor_free(entry); +} + +/** Helper: deallocate a rend_cache_failure_t. (Used with strmap_free(), + * which requires a function pointer whose argument is void*). */ +static void +rend_cache_failure_entry_free_(void *entry) +{ + rend_cache_failure_entry_free(entry); +} + +/** Allocate a rend cache failure object and return it. This function can + * not fail. */ +static rend_cache_failure_t * +rend_cache_failure_entry_new(void) +{ + rend_cache_failure_t *entry = tor_malloc(sizeof(*entry)); + entry->intro_failures = digestmap_new(); + return entry; +} + +/** Remove failure cache entry for the service ID in the given descriptor + * desc. */ +static void +rend_cache_failure_remove(rend_service_descriptor_t *desc) +{ + char service_id[REND_SERVICE_ID_LEN_BASE32 + 1]; + rend_cache_failure_t *entry; + + if (desc == NULL) { + return; + } + if (rend_get_service_id(desc->pk, service_id) < 0) { + return; + } + entry = strmap_get_lc(rend_cache_failure, service_id); + if (entry != NULL) { + strmap_remove_lc(rend_cache_failure, service_id); + rend_cache_failure_entry_free(entry); + } +} + /** Helper: free storage held by a single service descriptor cache entry. */ static void rend_cache_entry_free(rend_cache_entry_t *e) @@ -92,6 +195,9 @@ rend_cache_entry_free(rend_cache_entry_t *e) if (!e) return; rend_cache_decrement_allocation(rend_cache_entry_allocation(e)); + /* We are about to remove a descriptor from the cache so remove the entry + * in the failure cache. */ + rend_cache_failure_remove(e->parsed); rend_service_descriptor_free(e->parsed); tor_free(e->desc); tor_free(e); @@ -111,8 +217,10 @@ rend_cache_free_all(void) { strmap_free(rend_cache, rend_cache_entry_free_); digestmap_free(rend_cache_v2_dir, rend_cache_entry_free_); + strmap_free(rend_cache_failure, rend_cache_failure_entry_free_); rend_cache = NULL; rend_cache_v2_dir = NULL; + rend_cache_failure = NULL; rend_cache_total_allocation = 0; } @@ -150,6 +258,127 @@ rend_cache_purge(void) rend_cache = strmap_new(); } +/** Lookup the rend failure cache using a relay identity digest in + * identity and service ID service_id. If found, the intro + * failure is set in intro_entry else it stays untouched. Return 1 + * iff found else 0. */ +static int +cache_failure_intro_lookup(const uint8_t *identity, const char *service_id, + rend_cache_failure_intro_t **intro_entry) +{ + rend_cache_failure_t *elem; + rend_cache_failure_intro_t *intro_elem; + + tor_assert(rend_cache_failure); + + if (intro_entry) { + *intro_entry = NULL; + } + + /* Lookup descriptor and return it. */ + elem = strmap_get_lc(rend_cache_failure, service_id); + if (elem == NULL) { + goto not_found; + } + intro_elem = digestmap_get(elem->intro_failures, (char *) identity); + if (intro_elem == NULL) { + goto not_found; + } + if (intro_entry) { + *intro_entry = intro_elem; + } + return 1; +not_found: + return 0; +} + +/** Add an intro point failure to the failure cache using the relay + * identity and service ID service_id. Record the + * failure in that object. */ +static void +cache_failure_intro_add(const uint8_t *identity, const char *service_id, + unsigned int failure) +{ + rend_cache_failure_t *fail_entry; + rend_cache_failure_intro_t *entry; + + /* Make sure we have a failure object for this service ID and if not, + * create it with this new intro failure entry. */ + fail_entry = strmap_get_lc(rend_cache_failure, service_id); + if (fail_entry == NULL) { + fail_entry = rend_cache_failure_entry_new(); + /* Add failure entry to global rend failure cache. */ + strmap_set_lc(rend_cache_failure, service_id, fail_entry); + } + entry = rend_cache_failure_intro_entry_new(failure); + digestmap_set(fail_entry->intro_failures, (char *) identity, entry); +} + +/** Using a parsed descriptor desc, check if the introduction points + * are present in the failure cache and if so they are removed from the + * descriptor and kept into the failure cache. Then, each intro points that + * are NOT in the descriptor but in the failure cache for the given + * service_id are removed from the failure cache. */ +static void +validate_intro_point_failure(const rend_service_descriptor_t *desc, + const char *service_id) +{ + rend_cache_failure_t *new_entry, *cur_entry; + /* New entry for the service ID that will be replacing the one in the + * failure cache since we have a new descriptor. In the case where all + * intro points are removed, we are assured that the new entry is the same + * as the current one. */ + new_entry = tor_malloc(sizeof(*new_entry)); + new_entry->intro_failures = digestmap_new(); + + tor_assert(desc); + + SMARTLIST_FOREACH_BEGIN(desc->intro_nodes, rend_intro_point_t *, intro) { + int found; + rend_cache_failure_intro_t *entry; + const uint8_t *identity = + (uint8_t *) intro->extend_info->identity_digest; + + found = cache_failure_intro_lookup(identity, service_id, &entry); + if (found) { + /* This intro point is in our cache, discard it from the descriptor + * because chances are that it's unusable. */ + SMARTLIST_DEL_CURRENT(desc->intro_nodes, intro); + rend_intro_point_free(intro); + /* Keep it for our new entry. */ + digestmap_set(new_entry->intro_failures, (char *) identity, entry); + continue; + } + } SMARTLIST_FOREACH_END(intro); + + /* Swap the failure entry in the cache and free the current one. */ + cur_entry = strmap_get_lc(rend_cache_failure, service_id); + if (cur_entry != NULL) { + rend_cache_failure_entry_free(cur_entry); + } + strmap_set_lc(rend_cache_failure, service_id, new_entry); +} + +/** Note down an intro failure in the rend failure cache using the type of + * failure in failure for the relay identity digest in + * identity and service ID service_id. If an entry already + * exists in the cache, the failure type is changed with failure. */ +void +rend_cache_intro_failure_note(unsigned int failure, const uint8_t *identity, + const char *service_id) +{ + int found; + rend_cache_failure_intro_t *entry; + + found = cache_failure_intro_lookup(identity, service_id, &entry); + if (!found) { + cache_failure_intro_add(identity, service_id, failure); + } else { + /* Replace introduction point failure with this one. */ + entry->failure_type = failure; + } +} + /** Remove all old v2 descriptors and those for which this hidden service * directory is not responsible for any more. * @@ -537,20 +766,44 @@ rend_cache_store_v2_desc_as_client(const char *desc, "the future.", safe_str_client(service_id)); goto err; } - /* Do we already have a newer descriptor? */ + /* Do we have the same exact copy already in our cache? */ tor_snprintf(key, sizeof(key), "2%s", service_id); e = (rend_cache_entry_t*) strmap_get_lc(rend_cache, key); - if (e && e->parsed->timestamp >= parsed->timestamp) { + if (e && !strcmp(desc, e->desc)) { + log_info(LD_REND,"We already have this service descriptor %s.", + safe_str_client(service_id)); + goto okay; + } + /* Verify that we are not replacing an older descriptor. It's important to + * avoid an evil HSDir serving old descriptor. We validate if the + * timestamp is greater than and not equal because it's a rounded down + * timestamp to the hour so if the descriptor changed in the same hour, + * the rend cache failure will tells us if we have a new descriptor. */ + if (e && e->parsed->timestamp > parsed->timestamp) { log_info(LD_REND, "We already have a new enough service descriptor for " "service ID %s with the same desc ID and version.", safe_str_client(service_id)); goto okay; } + /* Lookup our failure cache for intro point that might be unsuable. */ + validate_intro_point_failure(parsed, service_id); + /* It's now possible that our intro point list is empty, this means that + * this descriptor is useless to us because intro points have all failed + * somehow before. Discard the descriptor. */ + if (smartlist_len(parsed->intro_nodes) == 0) { + log_info(LD_REND, "Service descriptor with service ID %s, every " + "intro points are unusable. Discarding it.", + safe_str_client(service_id)); + goto err; + } + /* Now either purge the current one and replace it's content or create a + * new one and add it to the rend cache. */ if (!e) { e = tor_malloc_zero(sizeof(rend_cache_entry_t)); strmap_set_lc(rend_cache, key, e); } else { rend_cache_decrement_allocation(rend_cache_entry_allocation(e)); + rend_cache_failure_remove(e->parsed); rend_service_descriptor_free(e->parsed); tor_free(e->desc); } diff --git a/src/or/rendcache.h b/src/or/rendcache.h index f61f02a8e6..21be9f908b 100644 --- a/src/or/rendcache.h +++ b/src/or/rendcache.h @@ -10,6 +10,7 @@ #define TOR_RENDCACHE_H #include "or.h" +#include "rendcommon.h" /** How old do we let hidden service descriptors get before discarding * them as too old? */ @@ -31,6 +32,17 @@ typedef struct rend_cache_entry_t { rend_service_descriptor_t *parsed; /**< Parsed value of 'desc' */ } rend_cache_entry_t; +/* Introduction point failure type. */ +typedef struct rend_cache_failure_intro_t { + unsigned int failure_type; +} rend_cache_failure_intro_t; + +/** Cache failure object indexed by service ID. */ +typedef struct rend_cache_failure_t { + /* Contains rend_cache_failure_intro_t indexed by identity digest. */ + digestmap_t *intro_failures; +} rend_cache_failure_t; + void rend_cache_init(void); void rend_cache_clean(time_t now); void rend_cache_clean_v2_descs_as_dir(time_t now, size_t min_to_remove); @@ -53,5 +65,9 @@ rend_cache_store_status_t rend_cache_store_v2_desc_as_client(const char *desc, rend_cache_entry_t **entry); size_t rend_cache_get_total_allocation(void); +void rend_cache_intro_failure_note(unsigned int failure, + const uint8_t *identity, + const char *service_id); + #endif /* TOR_RENDCACHE_H */ diff --git a/src/or/rendclient.c b/src/or/rendclient.c index 59e938e89c..0b22f46ac0 100644 --- a/src/or/rendclient.c +++ b/src/or/rendclient.c @@ -1019,6 +1019,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; @@ -1034,6 +1037,9 @@ 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); } From d06af95ba3fdcb0c9820dcf34146ba68458d5e46 Mon Sep 17 00:00:00 2001 From: David Goulet Date: Thu, 25 Jun 2015 15:11:40 -0400 Subject: [PATCH 2/6] Purge client HS failure cache on NEWNYM Signed-off-by: David Goulet --- src/or/rendcache.c | 12 ++++++++++++ src/or/rendcache.h | 1 + src/or/rendclient.c | 1 + 3 files changed, 14 insertions(+) diff --git a/src/or/rendcache.c b/src/or/rendcache.c index 7a86546b7a..acb9447de8 100644 --- a/src/or/rendcache.c +++ b/src/or/rendcache.c @@ -258,6 +258,18 @@ rend_cache_purge(void) rend_cache = strmap_new(); } +/** Remove ALL entries from the failure cache. This is also called when a + * NEWNYM signal is received. */ +void +rend_cache_failure_purge(void) +{ + if (rend_cache_failure) { + log_info(LD_REND, "Purging HS failure cache"); + strmap_free(rend_cache_failure, rend_cache_failure_entry_free_); + } + rend_cache_failure = strmap_new(); +} + /** Lookup the rend failure cache using a relay identity digest in * identity and service ID service_id. If found, the intro * failure is set in intro_entry else it stays untouched. Return 1 diff --git a/src/or/rendcache.h b/src/or/rendcache.h index 21be9f908b..9a18810821 100644 --- a/src/or/rendcache.h +++ b/src/or/rendcache.h @@ -68,6 +68,7 @@ size_t rend_cache_get_total_allocation(void); void rend_cache_intro_failure_note(unsigned int failure, const uint8_t *identity, const char *service_id); +void rend_cache_failure_purge(void); #endif /* TOR_RENDCACHE_H */ diff --git a/src/or/rendclient.c b/src/or/rendclient.c index 0b22f46ac0..c6f29a7707 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(); } From 1070be8217c26c8917323ead944124e2cd14f9f2 Mon Sep 17 00:00:00 2001 From: David Goulet Date: Thu, 25 Jun 2015 16:13:15 -0400 Subject: [PATCH 3/6] Use an enum for INTRO_POINT_FAILURE_* Safer, wiser, stronger! Signed-off-by: David Goulet --- src/or/rendcache.c | 7 ++++--- src/or/rendcache.h | 4 ++-- src/or/rendclient.h | 4 ---- src/or/rendcommon.h | 6 ++++++ 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/or/rendcache.c b/src/or/rendcache.c index acb9447de8..8f3ddf5585 100644 --- a/src/or/rendcache.c +++ b/src/or/rendcache.c @@ -125,7 +125,7 @@ rend_cache_failure_intro_entry_free(rend_cache_failure_intro_t *entry) /** Allocate a rend cache failure intro object and return it. failure * is set into the object. This function can not fail. */ static rend_cache_failure_intro_t * -rend_cache_failure_intro_entry_new(unsigned int failure) +rend_cache_failure_intro_entry_new(rend_intro_point_failure_t failure) { rend_cache_failure_intro_t *entry = tor_malloc(sizeof(*entry)); entry->failure_type = failure; @@ -309,7 +309,7 @@ not_found: * failure in that object. */ static void cache_failure_intro_add(const uint8_t *identity, const char *service_id, - unsigned int failure) + rend_intro_point_failure_t failure) { rend_cache_failure_t *fail_entry; rend_cache_failure_intro_t *entry; @@ -376,7 +376,8 @@ validate_intro_point_failure(const rend_service_descriptor_t *desc, * identity and service ID service_id. If an entry already * exists in the cache, the failure type is changed with failure. */ void -rend_cache_intro_failure_note(unsigned int failure, const uint8_t *identity, +rend_cache_intro_failure_note(rend_intro_point_failure_t failure, + const uint8_t *identity, const char *service_id) { int found; diff --git a/src/or/rendcache.h b/src/or/rendcache.h index 9a18810821..b214a48ccc 100644 --- a/src/or/rendcache.h +++ b/src/or/rendcache.h @@ -34,7 +34,7 @@ typedef struct rend_cache_entry_t { /* Introduction point failure type. */ typedef struct rend_cache_failure_intro_t { - unsigned int failure_type; + rend_intro_point_failure_t failure_type; } rend_cache_failure_intro_t; /** Cache failure object indexed by service ID. */ @@ -65,7 +65,7 @@ rend_cache_store_status_t rend_cache_store_v2_desc_as_client(const char *desc, rend_cache_entry_t **entry); size_t rend_cache_get_total_allocation(void); -void rend_cache_intro_failure_note(unsigned int failure, +void rend_cache_intro_failure_note(rend_intro_point_failure_t failure, const uint8_t *identity, const char *service_id); void rend_cache_failure_purge(void); diff --git a/src/or/rendclient.h b/src/or/rendclient.h index 439f42875b..124433ef31 100644 --- a/src/or/rendclient.h +++ b/src/or/rendclient.h @@ -26,10 +26,6 @@ int rend_client_fetch_v2_desc(rend_data_t *query, smartlist_t *hsdirs); void rend_client_cancel_descriptor_fetches(void); void rend_client_purge_last_hid_serv_requests(void); -#define INTRO_POINT_FAILURE_GENERIC 0 -#define INTRO_POINT_FAILURE_TIMEOUT 1 -#define INTRO_POINT_FAILURE_UNREACHABLE 2 - int rend_client_report_intro_point_failure(extend_info_t *failed_intro, rend_data_t *rend_query, unsigned int failure_type); diff --git a/src/or/rendcommon.h b/src/or/rendcommon.h index ba5e077642..3b2f86d614 100644 --- a/src/or/rendcommon.h +++ b/src/or/rendcommon.h @@ -12,6 +12,12 @@ #ifndef TOR_RENDCOMMON_H #define TOR_RENDCOMMON_H +typedef enum rend_intro_point_failure_t { + INTRO_POINT_FAILURE_GENERIC = 0, + INTRO_POINT_FAILURE_TIMEOUT = 1, + INTRO_POINT_FAILURE_UNREACHABLE = 2, +} rend_intro_point_failure_t; + /** Free all storage associated with data */ static INLINE void rend_data_free(rend_data_t *data) From c265621a525f23f5e208f5ca0c7211bd6c7483ce Mon Sep 17 00:00:00 2001 From: David Goulet Date: Thu, 25 Jun 2015 16:19:25 -0400 Subject: [PATCH 4/6] Fix typo in comment about digest256map_t Signed-off-by: David Goulet --- src/common/container.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/container.h b/src/common/container.h index 125900c8ca..2a6ba01e62 100644 --- a/src/common/container.h +++ b/src/common/container.h @@ -361,7 +361,7 @@ char *smartlist_join_strings2(smartlist_t *sl, const char *join, DECLARE_MAP_FNS(strmap_t, const char *, strmap_); /* Map from const char[DIGEST_LEN] to void *. Implemented with a hash table. */ DECLARE_MAP_FNS(digestmap_t, const char *, digestmap_); -/* Map from const uint8_t[DIGEST_LEN] to void *. Implemented with a hash +/* Map from const uint8_t[DIGEST256_LEN] to void *. Implemented with a hash * table. */ DECLARE_MAP_FNS(digest256map_t, const uint8_t *, digest256map_); From 6e967235240dede017bbf0a696de62443fd2a7c3 Mon Sep 17 00:00:00 2001 From: David Goulet Date: Wed, 5 Aug 2015 13:48:12 -0400 Subject: [PATCH 5/6] Add created timestamp to a rend cache failure entry Signed-off-by: David Goulet --- src/or/rendcache.c | 1 + src/or/rendcache.h | 3 +++ 2 files changed, 4 insertions(+) diff --git a/src/or/rendcache.c b/src/or/rendcache.c index 8f3ddf5585..9be9e24941 100644 --- a/src/or/rendcache.c +++ b/src/or/rendcache.c @@ -129,6 +129,7 @@ rend_cache_failure_intro_entry_new(rend_intro_point_failure_t failure) { rend_cache_failure_intro_t *entry = tor_malloc(sizeof(*entry)); entry->failure_type = failure; + entry->created_ts = time(NULL); return entry; } diff --git a/src/or/rendcache.h b/src/or/rendcache.h index b214a48ccc..98c0f95264 100644 --- a/src/or/rendcache.h +++ b/src/or/rendcache.h @@ -34,6 +34,9 @@ typedef struct rend_cache_entry_t { /* Introduction point failure type. */ typedef struct rend_cache_failure_intro_t { + /* When this intro point failure occured thus we allocated this object and + * cache it. */ + time_t created_ts; rend_intro_point_failure_t failure_type; } rend_cache_failure_intro_t; From 7dce409802193eed9f8378e11b1c38eeb1127929 Mon Sep 17 00:00:00 2001 From: David Goulet Date: Wed, 5 Aug 2015 14:06:09 -0400 Subject: [PATCH 6/6] Expire after 5 minutes rend cache failure entries Signed-off-by: David Goulet --- src/or/main.c | 4 ++++ src/or/rendcache.c | 29 +++++++++++++++++++++++++++++ src/or/rendcache.h | 3 +++ 3 files changed, 36 insertions(+) diff --git a/src/or/main.c b/src/or/main.c index 5bff82b3cf..e564e6c132 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -1488,6 +1488,10 @@ run_scheduled_events(time_t now) #define CLEAN_CACHES_INTERVAL (30*60) time_to.clean_caches = now + CLEAN_CACHES_INTERVAL; } + /* We don't keep entries that are more than five minutes old so we try to + * clean it as soon as we can since we want to make sure the client waits + * as little as possible for reachability reasons. */ + rend_cache_failure_clean(now); #define RETRY_DNS_INTERVAL (10*60) /* If we're a server and initializing dns failed, retry periodically. */ diff --git a/src/or/rendcache.c b/src/or/rendcache.c index 9be9e24941..9a33046fb6 100644 --- a/src/or/rendcache.c +++ b/src/or/rendcache.c @@ -225,6 +225,35 @@ rend_cache_free_all(void) rend_cache_total_allocation = 0; } +/** Remove all entries that re REND_CACHE_FAILURE_MAX_AGE old. This is + * called every second. + * + * We have to clean these regurlarly else if for whatever reasons an hidden + * service goes offline and a client tries to connect to it during that + * time, a failure entry is created and the client will be unable to connect + * for a while even though the service has return online. */ +void +rend_cache_failure_clean(time_t now) +{ + time_t cutoff = now - REND_CACHE_FAILURE_MAX_AGE; + STRMAP_FOREACH_MODIFY(rend_cache_failure, key, + rend_cache_failure_t *, ent) { + /* Free and remove every intro failure object that match the cutoff. */ + DIGESTMAP_FOREACH_MODIFY(ent->intro_failures, ip_key, + rend_cache_failure_intro_t *, ip_ent) { + if (ip_ent->created_ts < cutoff) { + rend_cache_failure_intro_entry_free(ip_ent); + MAP_DEL_CURRENT(ip_key); + } + } DIGESTMAP_FOREACH_END; + /* If the entry is now empty of intro point failures, remove it. */ + if (digestmap_isempty(ent->intro_failures)) { + rend_cache_failure_entry_free(ent); + MAP_DEL_CURRENT(key); + } + } STRMAP_FOREACH_END; +} + /** Removes all old entries from the service descriptor cache. */ void diff --git a/src/or/rendcache.h b/src/or/rendcache.h index 98c0f95264..0512058054 100644 --- a/src/or/rendcache.h +++ b/src/or/rendcache.h @@ -18,6 +18,8 @@ /** How wrong do we assume our clock may be when checking whether hidden * services are too old or too new? */ #define REND_CACHE_MAX_SKEW (24*60*60) +/** How old do we keep an intro point failure entry in the failure cache? */ +#define REND_CACHE_FAILURE_MAX_AGE (5*60) /* Do not allow more than this many introduction points in a hidden service * descriptor */ @@ -48,6 +50,7 @@ typedef struct rend_cache_failure_t { void rend_cache_init(void); void rend_cache_clean(time_t now); +void rend_cache_failure_clean(time_t now); void rend_cache_clean_v2_descs_as_dir(time_t now, size_t min_to_remove); void rend_cache_purge(void); void rend_cache_free_all(void);