diff options
Diffstat (limited to 'src/or/rendcache.c')
-rw-r--r-- | src/or/rendcache.c | 225 |
1 files changed, 169 insertions, 56 deletions
diff --git a/src/or/rendcache.c b/src/or/rendcache.c index d4bdd68698..f8206cd53b 100644 --- a/src/or/rendcache.c +++ b/src/or/rendcache.c @@ -1,25 +1,30 @@ -/* Copyright (c) 2015, The Tor Project, Inc. */ +/* Copyright (c) 2015-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** * \file rendcache.c - * \brief Hidden service desriptor cache. + * \brief Hidden service descriptor cache. **/ +#define RENDCACHE_PRIVATE #include "rendcache.h" #include "config.h" #include "rephist.h" #include "routerlist.h" #include "routerparse.h" +#include "rendcommon.h" /** Map from service id (as generated by rend_get_service_id) to * rend_cache_entry_t. */ -static strmap_t *rend_cache = NULL; +STATIC strmap_t *rend_cache = NULL; + +/** Map from service id to rend_cache_entry_t; only for hidden services. */ +static strmap_t *rend_cache_local_service = NULL; /** Map from descriptor id to rend_cache_entry_t; only for hidden service * directories. */ -static digestmap_t *rend_cache_v2_dir = NULL; +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 @@ -46,10 +51,10 @@ static digestmap_t *rend_cache_v2_dir = NULL; * 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; +STATIC strmap_t *rend_cache_failure = NULL; -/** DOCDOC */ -static size_t rend_cache_total_allocation = 0; +/* DOCDOC */ +STATIC size_t rend_cache_total_allocation = 0; /** Initializes the service descriptor cache. */ @@ -58,11 +63,12 @@ rend_cache_init(void) { rend_cache = strmap_new(); rend_cache_v2_dir = digestmap_new(); + rend_cache_local_service = strmap_new(); rend_cache_failure = strmap_new(); } /** Return the approximate number of bytes needed to hold <b>e</b>. */ -static size_t +STATIC size_t rend_cache_entry_allocation(const rend_cache_entry_t *e) { if (!e) @@ -72,7 +78,7 @@ rend_cache_entry_allocation(const rend_cache_entry_t *e) return sizeof(*e) + e->len + sizeof(*e->parsed); } -/** DOCDOC */ +/* DOCDOC */ size_t rend_cache_get_total_allocation(void) { @@ -80,7 +86,7 @@ rend_cache_get_total_allocation(void) } /** Decrement the total bytes attributed to the rendezvous cache by n. */ -static void +STATIC void rend_cache_decrement_allocation(size_t n) { static int have_underflowed = 0; @@ -97,7 +103,7 @@ rend_cache_decrement_allocation(size_t n) } /** Increase the total bytes attributed to the rendezvous cache by n. */ -static void +STATIC void rend_cache_increment_allocation(size_t n) { static int have_overflowed = 0; @@ -113,7 +119,7 @@ rend_cache_increment_allocation(size_t n) } /** Helper: free a rend cache failure intro object. */ -static void +STATIC void rend_cache_failure_intro_entry_free(rend_cache_failure_intro_t *entry) { if (entry == NULL) { @@ -130,7 +136,7 @@ rend_cache_failure_intro_entry_free_(void *entry) /** Allocate a rend cache failure intro object and return it. <b>failure</b> * is set into the object. This function can not fail. */ -static rend_cache_failure_intro_t * +STATIC rend_cache_failure_intro_t * rend_cache_failure_intro_entry_new(rend_intro_point_failure_t failure) { rend_cache_failure_intro_t *entry = tor_malloc(sizeof(*entry)); @@ -140,7 +146,7 @@ rend_cache_failure_intro_entry_new(rend_intro_point_failure_t failure) } /** Helper: free a rend cache failure object. */ -static void +STATIC void rend_cache_failure_entry_free(rend_cache_failure_t *entry) { if (entry == NULL) { @@ -156,7 +162,7 @@ rend_cache_failure_entry_free(rend_cache_failure_t *entry) /** Helper: deallocate a rend_cache_failure_t. (Used with strmap_free(), * which requires a function pointer whose argument is void*). */ -static void +STATIC void rend_cache_failure_entry_free_(void *entry) { rend_cache_failure_entry_free(entry); @@ -164,7 +170,7 @@ rend_cache_failure_entry_free_(void *entry) /** Allocate a rend cache failure object and return it. This function can * not fail. */ -static rend_cache_failure_t * +STATIC rend_cache_failure_t * rend_cache_failure_entry_new(void) { rend_cache_failure_t *entry = tor_malloc(sizeof(*entry)); @@ -174,7 +180,7 @@ rend_cache_failure_entry_new(void) /** Remove failure cache entry for the service ID in the given descriptor * <b>desc</b>. */ -static void +STATIC void rend_cache_failure_remove(rend_service_descriptor_t *desc) { char service_id[REND_SERVICE_ID_LEN_BASE32 + 1]; @@ -194,7 +200,7 @@ rend_cache_failure_remove(rend_service_descriptor_t *desc) } /** Helper: free storage held by a single service descriptor cache entry. */ -static void +STATIC void rend_cache_entry_free(rend_cache_entry_t *e) { if (!e) @@ -222,9 +228,11 @@ 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_local_service, rend_cache_entry_free_); strmap_free(rend_cache_failure, rend_cache_failure_entry_free_); rend_cache = NULL; rend_cache_v2_dir = NULL; + rend_cache_local_service = NULL; rend_cache_failure = NULL; rend_cache_total_allocation = 0; } @@ -258,24 +266,33 @@ rend_cache_failure_clean(time_t now) } STRMAP_FOREACH_END; } -/** Removes all old entries from the service descriptor cache. +/** Removes all old entries from the client or service descriptor cache. */ void -rend_cache_clean(time_t now) +rend_cache_clean(time_t now, rend_cache_type_t cache_type) { strmap_iter_t *iter; const char *key; void *val; rend_cache_entry_t *ent; time_t cutoff = now - REND_CACHE_MAX_AGE - REND_CACHE_MAX_SKEW; - for (iter = strmap_iter_init(rend_cache); !strmap_iter_done(iter); ) { + strmap_t *cache = NULL; + + if (cache_type == REND_CACHE_TYPE_CLIENT) { + cache = rend_cache; + } else if (cache_type == REND_CACHE_TYPE_SERVICE) { + cache = rend_cache_local_service; + } + tor_assert(cache); + + for (iter = strmap_iter_init(cache); !strmap_iter_done(iter); ) { strmap_iter_get(iter, &key, &val); ent = (rend_cache_entry_t*)val; if (ent->parsed->timestamp < cutoff) { - iter = strmap_iter_next_rmv(rend_cache, iter); + iter = strmap_iter_next_rmv(cache, iter); rend_cache_entry_free(ent); } else { - iter = strmap_iter_next(rend_cache, iter); + iter = strmap_iter_next(cache, iter); } } } @@ -305,10 +322,10 @@ rend_cache_failure_purge(void) } /** Lookup the rend failure cache using a relay identity digest in - * <b>identity</b> and service ID <b>service_id</b>. If found, the intro - * failure is set in <b>intro_entry</b> else it stays untouched. Return 1 - * iff found else 0. */ -static int + * <b>identity</b> which has DIGEST_LEN bytes and service ID <b>service_id</b> + * which is a null-terminated string. If found, the intro failure is set in + * <b>intro_entry</b> 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) { @@ -352,7 +369,7 @@ cache_failure_intro_dup(const rend_cache_failure_intro_t *entry) /** Add an intro point failure to the failure cache using the relay * <b>identity</b> and service ID <b>service_id</b>. Record the * <b>failure</b> in that object. */ -static void +STATIC void cache_failure_intro_add(const uint8_t *identity, const char *service_id, rend_intro_point_failure_t failure) { @@ -379,7 +396,7 @@ cache_failure_intro_add(const uint8_t *identity, const char *service_id, * 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 * <b>service_id</b> are removed from the failure cache. */ -static void +STATIC void validate_intro_point_failure(const rend_service_descriptor_t *desc, const char *service_id) { @@ -466,8 +483,7 @@ rend_cache_clean_v2_descs_as_dir(time_t now, size_t force_remove) digestmap_iter_get(iter, &key, &val); ent = val; if (ent->parsed->timestamp < cutoff || - ent->last_served < last_served_cutoff || - !hid_serv_responsible_for_desc_id(key)) { + ent->last_served < last_served_cutoff) { char key_base32[REND_DESC_ID_V2_LEN_BASE32 + 1]; base32_encode(key_base32, sizeof(key_base32), key, DIGEST_LEN); log_info(LD_REND, "Removing descriptor with ID '%s' from cache", @@ -535,6 +551,42 @@ rend_cache_lookup_entry(const char *query, int version, rend_cache_entry_t **e) return ret; } +/* + * Lookup the v2 service descriptor with the service ID <b>query</b> in the + * local service descriptor cache. Return 0 if found and if <b>e</b> is + * non NULL, set it with the entry found. Else, a negative value is returned + * and <b>e</b> is untouched. + * -EINVAL means that <b>query</b> is not a valid service id. + * -ENOENT means that no entry in the cache was found. */ +int +rend_cache_lookup_v2_desc_as_service(const char *query, rend_cache_entry_t **e) +{ + int ret = 0; + rend_cache_entry_t *entry = NULL; + + tor_assert(rend_cache_local_service); + tor_assert(query); + + if (!rend_valid_service_id(query)) { + ret = -EINVAL; + goto end; + } + + /* Lookup descriptor and return. */ + entry = strmap_get_lc(rend_cache_local_service, query); + if (!entry) { + ret = -ENOENT; + goto end; + } + + if (e) { + *e = entry; + } + + end: + return ret; +} + /** Lookup the v2 service descriptor with base32-encoded <b>desc_id</b> and * copy the pointer to it to *<b>desc</b>. Return 1 on success, 0 on * well-formed-but-not-found, and -1 on failure. @@ -570,9 +622,11 @@ rend_cache_lookup_v2_desc_as_dir(const char *desc_id, const char **desc) * If we have a newer descriptor with the same ID, ignore this one. * If we have an older descriptor with the same ID, replace it. * - * Return an appropriate rend_cache_store_status_t. + * Return 0 on success, or -1 if we couldn't parse any of them. + * + * We should only call this function for public (e.g. non bridge) relays. */ -rend_cache_store_status_t +int rend_cache_store_v2_desc_as_dir(const char *desc) { const or_options_t *options = get_options(); @@ -589,12 +643,6 @@ rend_cache_store_v2_desc_as_dir(const char *desc) time_t now = time(NULL); tor_assert(rend_cache_v2_dir); tor_assert(desc); - if (!hid_serv_acting_as_directory()) { - /* Cannot store descs, because we are (currently) not acting as - * hidden service directory. */ - log_info(LD_REND, "Cannot store descs: Not acting as hs dir"); - return RCS_NOTDIR; - } while (rend_parse_v2_service_descriptor(&parsed, desc_id, &intro_content, &intro_size, &encoded_size, &next_desc, current_desc, 1) >= 0) { @@ -604,14 +652,6 @@ rend_cache_store_v2_desc_as_dir(const char *desc) /* For pretty log statements. */ base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_id, DIGEST_LEN); - /* Is desc ID in the range that we are (directly or indirectly) responsible - * for? */ - if (!hid_serv_responsible_for_desc_id(desc_id)) { - log_info(LD_REND, "Service descriptor with desc ID %s is not in " - "interval that we are responsible for.", - safe_str_client(desc_id_base32)); - goto skip; - } /* Is descriptor too old? */ if (parsed->timestamp < now - REND_CACHE_MAX_AGE-REND_CACHE_MAX_SKEW) { log_info(LD_REND, "Service descriptor with desc ID %s is too old.", @@ -660,7 +700,6 @@ rend_cache_store_v2_desc_as_dir(const char *desc) log_info(LD_REND, "Successfully stored service descriptor with desc ID " "'%s' and len %d.", safe_str(desc_id_base32), (int)encoded_size); - /* Statistics: Note down this potentially new HS. */ if (options->HiddenServiceStatistics) { rep_hist_stored_maybe_new_hs(e->parsed->pk); @@ -680,11 +719,85 @@ rend_cache_store_v2_desc_as_dir(const char *desc) } if (!number_parsed) { log_info(LD_REND, "Could not parse any descriptor."); - return RCS_BADDESC; + return -1; } log_info(LD_REND, "Parsed %d and added %d descriptor%s.", number_parsed, number_stored, number_stored != 1 ? "s" : ""); - return RCS_OKAY; + return 0; +} + +/** Parse the v2 service descriptor in <b>desc</b> and store it to the +* local service rend cache. Don't attempt to decrypt the included list of +* introduction points. +* +* If we have a newer descriptor with the same ID, ignore this one. +* If we have an older descriptor with the same ID, replace it. +* +* Return 0 on success, or -1 if we couldn't understand the descriptor. +*/ +int +rend_cache_store_v2_desc_as_service(const char *desc) +{ + rend_service_descriptor_t *parsed = NULL; + char desc_id[DIGEST_LEN]; + char *intro_content = NULL; + size_t intro_size; + size_t encoded_size; + const char *next_desc; + char service_id[REND_SERVICE_ID_LEN_BASE32+1]; + rend_cache_entry_t *e; + int retval = -1; + tor_assert(rend_cache_local_service); + tor_assert(desc); + + /* Parse the descriptor. */ + if (rend_parse_v2_service_descriptor(&parsed, desc_id, &intro_content, + &intro_size, &encoded_size, + &next_desc, desc, 0) < 0) { + log_warn(LD_REND, "Could not parse descriptor."); + goto err; + } + /* Compute service ID from public key. */ + if (rend_get_service_id(parsed->pk, service_id)<0) { + log_warn(LD_REND, "Couldn't compute service ID."); + goto err; + } + + /* Do we already have a newer descriptor? Allow new descriptors with a + rounded timestamp equal to or newer than the current descriptor */ + e = (rend_cache_entry_t*) strmap_get_lc(rend_cache_local_service, + service_id); + if (e && e->parsed->timestamp > parsed->timestamp) { + log_info(LD_REND, "We already have a newer service descriptor for " + "service ID %s.", safe_str_client(service_id)); + goto okay; + } + /* We don't care about the introduction points. */ + tor_free(intro_content); + if (!e) { + e = tor_malloc_zero(sizeof(rend_cache_entry_t)); + strmap_set_lc(rend_cache_local_service, service_id, e); + } else { + rend_cache_decrement_allocation(rend_cache_entry_allocation(e)); + rend_service_descriptor_free(e->parsed); + tor_free(e->desc); + } + e->parsed = parsed; + e->desc = tor_malloc_zero(encoded_size + 1); + strlcpy(e->desc, desc, encoded_size + 1); + e->len = encoded_size; + rend_cache_increment_allocation(rend_cache_entry_allocation(e)); + log_debug(LD_REND,"Successfully stored rend desc '%s', len %d.", + safe_str_client(service_id), (int)encoded_size); + return 0; + + okay: + retval = 0; + + err: + rend_service_descriptor_free(parsed); + tor_free(intro_content); + return retval; } /** Parse the v2 service descriptor in <b>desc</b>, decrypt the included list @@ -700,10 +813,10 @@ rend_cache_store_v2_desc_as_dir(const char *desc) * If the descriptor's descriptor ID doesn't match <b>desc_id_base32</b>, * reject it. * - * Return an appropriate rend_cache_store_status_t. If entry is not NULL, - * set it with the cache entry pointer of the descriptor. + * Return 0 on success, or -1 if we rejected the descriptor. + * If entry is not NULL, set it with the cache entry pointer of the descriptor. */ -rend_cache_store_status_t +int rend_cache_store_v2_desc_as_client(const char *desc, const char *desc_id_base32, const rend_data_t *rend_query, @@ -735,7 +848,7 @@ rend_cache_store_v2_desc_as_client(const char *desc, char service_id[REND_SERVICE_ID_LEN_BASE32+1]; char want_desc_id[DIGEST_LEN]; rend_cache_entry_t *e; - rend_cache_store_status_t retval = RCS_BADDESC; + int retval = -1; tor_assert(rend_cache); tor_assert(desc); tor_assert(desc_id_base32); @@ -882,13 +995,13 @@ rend_cache_store_v2_desc_as_client(const char *desc, if (entry) { *entry = e; } - return RCS_OKAY; + return 0; okay: if (entry) { *entry = e; } - retval = RCS_OKAY; + retval = 0; err: rend_service_descriptor_free(parsed); |