dirvote: Handling adding vote and signature if module is disabled
[tor.git] / src / or / hs_cache.c
blobdf53efd32d031eeb8c3b9c48a0e47b2537fb8f55
1 /* Copyright (c) 2016-2017, The Tor Project, Inc. */
2 /* See LICENSE for licensing information */
4 /**
5 * \file hs_cache.c
6 * \brief Handle hidden service descriptor caches.
7 **/
9 /* For unit tests.*/
10 #define HS_CACHE_PRIVATE
12 #include "or.h"
13 #include "config.h"
14 #include "hs_ident.h"
15 #include "hs_common.h"
16 #include "hs_client.h"
17 #include "hs_descriptor.h"
18 #include "networkstatus.h"
19 #include "rendcache.h"
21 #include "hs_cache.h"
23 static int cached_client_descriptor_has_expired(time_t now,
24 const hs_cache_client_descriptor_t *cached_desc);
26 /********************** Directory HS cache ******************/
28 /* Directory descriptor cache. Map indexed by blinded key. */
29 static digest256map_t *hs_cache_v3_dir;
31 /* Remove a given descriptor from our cache. */
32 static void
33 remove_v3_desc_as_dir(const hs_cache_dir_descriptor_t *desc)
35 tor_assert(desc);
36 digest256map_remove(hs_cache_v3_dir, desc->key);
39 /* Store a given descriptor in our cache. */
40 static void
41 store_v3_desc_as_dir(hs_cache_dir_descriptor_t *desc)
43 tor_assert(desc);
44 digest256map_set(hs_cache_v3_dir, desc->key, desc);
47 /* Query our cache and return the entry or NULL if not found. */
48 static hs_cache_dir_descriptor_t *
49 lookup_v3_desc_as_dir(const uint8_t *key)
51 tor_assert(key);
52 return digest256map_get(hs_cache_v3_dir, key);
55 #define cache_dir_desc_free(val) \
56 FREE_AND_NULL(hs_cache_dir_descriptor_t, cache_dir_desc_free_, (val))
58 /* Free a directory descriptor object. */
59 static void
60 cache_dir_desc_free_(hs_cache_dir_descriptor_t *desc)
62 if (desc == NULL) {
63 return;
65 hs_desc_plaintext_data_free(desc->plaintext_data);
66 tor_free(desc->encoded_desc);
67 tor_free(desc);
70 /* Helper function: Use by the free all function using the digest256map
71 * interface to cache entries. */
72 static void
73 cache_dir_desc_free_void(void *ptr)
75 cache_dir_desc_free_(ptr);
78 /* Create a new directory cache descriptor object from a encoded descriptor.
79 * On success, return the heap-allocated cache object, otherwise return NULL if
80 * we can't decode the descriptor. */
81 static hs_cache_dir_descriptor_t *
82 cache_dir_desc_new(const char *desc)
84 hs_cache_dir_descriptor_t *dir_desc;
86 tor_assert(desc);
88 dir_desc = tor_malloc_zero(sizeof(hs_cache_dir_descriptor_t));
89 dir_desc->plaintext_data =
90 tor_malloc_zero(sizeof(hs_desc_plaintext_data_t));
91 dir_desc->encoded_desc = tor_strdup(desc);
93 if (hs_desc_decode_plaintext(desc, dir_desc->plaintext_data) < 0) {
94 log_debug(LD_DIR, "Unable to decode descriptor. Rejecting.");
95 goto err;
98 /* The blinded pubkey is the indexed key. */
99 dir_desc->key = dir_desc->plaintext_data->blinded_pubkey.pubkey;
100 dir_desc->created_ts = time(NULL);
101 return dir_desc;
103 err:
104 cache_dir_desc_free(dir_desc);
105 return NULL;
108 /* Return the size of a cache entry in bytes. */
109 static size_t
110 cache_get_dir_entry_size(const hs_cache_dir_descriptor_t *entry)
112 return (sizeof(*entry) + hs_desc_plaintext_obj_size(entry->plaintext_data)
113 + strlen(entry->encoded_desc));
116 /* Try to store a valid version 3 descriptor in the directory cache. Return 0
117 * on success else a negative value is returned indicating that we have a
118 * newer version in our cache. On error, caller is responsible to free the
119 * given descriptor desc. */
120 static int
121 cache_store_v3_as_dir(hs_cache_dir_descriptor_t *desc)
123 hs_cache_dir_descriptor_t *cache_entry;
125 tor_assert(desc);
127 /* Verify if we have an entry in the cache for that key and if yes, check
128 * if we should replace it? */
129 cache_entry = lookup_v3_desc_as_dir(desc->key);
130 if (cache_entry != NULL) {
131 /* Only replace descriptor if revision-counter is greater than the one
132 * in our cache */
133 if (cache_entry->plaintext_data->revision_counter >=
134 desc->plaintext_data->revision_counter) {
135 log_info(LD_REND, "Descriptor revision counter in our cache is "
136 "greater or equal than the one we received (%d/%d). "
137 "Rejecting!",
138 (int)cache_entry->plaintext_data->revision_counter,
139 (int)desc->plaintext_data->revision_counter);
140 goto err;
142 /* We now know that the descriptor we just received is a new one so
143 * remove the entry we currently have from our cache so we can then
144 * store the new one. */
145 remove_v3_desc_as_dir(cache_entry);
146 rend_cache_decrement_allocation(cache_get_dir_entry_size(cache_entry));
147 cache_dir_desc_free(cache_entry);
149 /* Store the descriptor we just got. We are sure here that either we
150 * don't have the entry or we have a newer descriptor and the old one
151 * has been removed from the cache. */
152 store_v3_desc_as_dir(desc);
154 /* Update our total cache size with this entry for the OOM. This uses the
155 * old HS protocol cache subsystem for which we are tied with. */
156 rend_cache_increment_allocation(cache_get_dir_entry_size(desc));
158 /* XXX: Update HS statistics. We should have specific stats for v3. */
160 return 0;
162 err:
163 return -1;
166 /* Using the query which is the base64 encoded blinded key of a version 3
167 * descriptor, lookup in our directory cache the entry. If found, 1 is
168 * returned and desc_out is populated with a newly allocated string being the
169 * encoded descriptor. If not found, 0 is returned and desc_out is untouched.
170 * On error, a negative value is returned and desc_out is untouched. */
171 static int
172 cache_lookup_v3_as_dir(const char *query, const char **desc_out)
174 int found = 0;
175 ed25519_public_key_t blinded_key;
176 const hs_cache_dir_descriptor_t *entry;
178 tor_assert(query);
180 /* Decode blinded key using the given query value. */
181 if (ed25519_public_from_base64(&blinded_key, query) < 0) {
182 log_info(LD_REND, "Unable to decode the v3 HSDir query %s.",
183 safe_str_client(query));
184 goto err;
187 entry = lookup_v3_desc_as_dir(blinded_key.pubkey);
188 if (entry != NULL) {
189 found = 1;
190 if (desc_out) {
191 *desc_out = entry->encoded_desc;
195 return found;
197 err:
198 return -1;
201 /* Clean the v3 cache by removing any entry that has expired using the
202 * <b>global_cutoff</b> value. If <b>global_cutoff</b> is 0, the cleaning
203 * process will use the lifetime found in the plaintext data section. Return
204 * the number of bytes cleaned. */
205 STATIC size_t
206 cache_clean_v3_as_dir(time_t now, time_t global_cutoff)
208 size_t bytes_removed = 0;
210 /* Code flow error if this ever happens. */
211 tor_assert(global_cutoff >= 0);
213 if (!hs_cache_v3_dir) { /* No cache to clean. Just return. */
214 return 0;
217 DIGEST256MAP_FOREACH_MODIFY(hs_cache_v3_dir, key,
218 hs_cache_dir_descriptor_t *, entry) {
219 size_t entry_size;
220 time_t cutoff = global_cutoff;
221 if (!cutoff) {
222 /* Cutoff is the lifetime of the entry found in the descriptor. */
223 cutoff = now - entry->plaintext_data->lifetime_sec;
226 /* If the entry has been created _after_ the cutoff, not expired so
227 * continue to the next entry in our v3 cache. */
228 if (entry->created_ts > cutoff) {
229 continue;
231 /* Here, our entry has expired, remove and free. */
232 MAP_DEL_CURRENT(key);
233 entry_size = cache_get_dir_entry_size(entry);
234 bytes_removed += entry_size;
235 /* Entry is not in the cache anymore, destroy it. */
236 cache_dir_desc_free(entry);
237 /* Update our cache entry allocation size for the OOM. */
238 rend_cache_decrement_allocation(entry_size);
239 /* Logging. */
241 char key_b64[BASE64_DIGEST256_LEN + 1];
242 digest256_to_base64(key_b64, (const char *) key);
243 log_info(LD_REND, "Removing v3 descriptor '%s' from HSDir cache",
244 safe_str_client(key_b64));
246 } DIGEST256MAP_FOREACH_END;
248 return bytes_removed;
251 /* Given an encoded descriptor, store it in the directory cache depending on
252 * which version it is. Return a negative value on error. On success, 0 is
253 * returned. */
255 hs_cache_store_as_dir(const char *desc)
257 hs_cache_dir_descriptor_t *dir_desc = NULL;
259 tor_assert(desc);
261 /* Create a new cache object. This can fail if the descriptor plaintext data
262 * is unparseable which in this case a log message will be triggered. */
263 dir_desc = cache_dir_desc_new(desc);
264 if (dir_desc == NULL) {
265 goto err;
268 /* Call the right function against the descriptor version. At this point,
269 * we are sure that the descriptor's version is supported else the
270 * decoding would have failed. */
271 switch (dir_desc->plaintext_data->version) {
272 case HS_VERSION_THREE:
273 default:
274 if (cache_store_v3_as_dir(dir_desc) < 0) {
275 goto err;
277 break;
279 return 0;
281 err:
282 cache_dir_desc_free(dir_desc);
283 return -1;
286 /* Using the query, lookup in our directory cache the entry. If found, 1 is
287 * returned and desc_out is populated with a newly allocated string being
288 * the encoded descriptor. If not found, 0 is returned and desc_out is
289 * untouched. On error, a negative value is returned and desc_out is
290 * untouched. */
292 hs_cache_lookup_as_dir(uint32_t version, const char *query,
293 const char **desc_out)
295 int found;
297 tor_assert(query);
298 /* This should never be called with an unsupported version. */
299 tor_assert(hs_desc_is_supported_version(version));
301 switch (version) {
302 case HS_VERSION_THREE:
303 default:
304 found = cache_lookup_v3_as_dir(query, desc_out);
305 break;
308 return found;
311 /* Clean all directory caches using the current time now. */
312 void
313 hs_cache_clean_as_dir(time_t now)
315 time_t cutoff;
317 /* Start with v2 cache cleaning. */
318 cutoff = now - rend_cache_max_entry_lifetime();
319 rend_cache_clean_v2_descs_as_dir(cutoff);
321 /* Now, clean the v3 cache. Set the cutoff to 0 telling the cleanup function
322 * to compute the cutoff by itself using the lifetime value. */
323 cache_clean_v3_as_dir(now, 0);
326 /********************** Client-side HS cache ******************/
328 /* Client-side HS descriptor cache. Map indexed by service identity key. */
329 static digest256map_t *hs_cache_v3_client;
331 /* Client-side introduction point state cache. Map indexed by service public
332 * identity key (onion address). It contains hs_cache_client_intro_state_t
333 * objects all related to a specific service. */
334 static digest256map_t *hs_cache_client_intro_state;
336 /* Return the size of a client cache entry in bytes. */
337 static size_t
338 cache_get_client_entry_size(const hs_cache_client_descriptor_t *entry)
340 return sizeof(*entry) +
341 strlen(entry->encoded_desc) + hs_desc_obj_size(entry->desc);
344 /* Remove a given descriptor from our cache. */
345 static void
346 remove_v3_desc_as_client(const hs_cache_client_descriptor_t *desc)
348 tor_assert(desc);
349 digest256map_remove(hs_cache_v3_client, desc->key.pubkey);
350 /* Update cache size with this entry for the OOM handler. */
351 rend_cache_decrement_allocation(cache_get_client_entry_size(desc));
354 /* Store a given descriptor in our cache. */
355 static void
356 store_v3_desc_as_client(hs_cache_client_descriptor_t *desc)
358 tor_assert(desc);
359 digest256map_set(hs_cache_v3_client, desc->key.pubkey, desc);
360 /* Update cache size with this entry for the OOM handler. */
361 rend_cache_increment_allocation(cache_get_client_entry_size(desc));
364 /* Query our cache and return the entry or NULL if not found or if expired. */
365 STATIC hs_cache_client_descriptor_t *
366 lookup_v3_desc_as_client(const uint8_t *key)
368 time_t now = approx_time();
369 hs_cache_client_descriptor_t *cached_desc;
371 tor_assert(key);
373 /* Do the lookup */
374 cached_desc = digest256map_get(hs_cache_v3_client, key);
375 if (!cached_desc) {
376 return NULL;
379 /* Don't return expired entries */
380 if (cached_client_descriptor_has_expired(now, cached_desc)) {
381 return NULL;
384 return cached_desc;
387 /* Parse the encoded descriptor in <b>desc_str</b> using
388 * <b>service_identity_pk<b> to decrypt it first.
390 * If everything goes well, allocate and return a new
391 * hs_cache_client_descriptor_t object. In case of error, return NULL. */
392 static hs_cache_client_descriptor_t *
393 cache_client_desc_new(const char *desc_str,
394 const ed25519_public_key_t *service_identity_pk)
396 hs_descriptor_t *desc = NULL;
397 hs_cache_client_descriptor_t *client_desc = NULL;
399 tor_assert(desc_str);
400 tor_assert(service_identity_pk);
402 /* Decode the descriptor we just fetched. */
403 if (hs_client_decode_descriptor(desc_str, service_identity_pk, &desc) < 0) {
404 goto end;
406 tor_assert(desc);
408 /* All is good: make a cache object for this descriptor */
409 client_desc = tor_malloc_zero(sizeof(hs_cache_client_descriptor_t));
410 ed25519_pubkey_copy(&client_desc->key, service_identity_pk);
411 /* Set expiration time for this cached descriptor to be the start of the next
412 * time period since that's when clients need to start using the next blinded
413 * pk of the service (and hence will need its next descriptor). */
414 client_desc->expiration_ts = hs_get_start_time_of_next_time_period(0);
415 client_desc->desc = desc;
416 client_desc->encoded_desc = tor_strdup(desc_str);
418 end:
419 return client_desc;
422 #define cache_client_desc_free(val) \
423 FREE_AND_NULL(hs_cache_client_descriptor_t, cache_client_desc_free_, (val))
425 /** Free memory allocated by <b>desc</b>. */
426 static void
427 cache_client_desc_free_(hs_cache_client_descriptor_t *desc)
429 if (desc == NULL) {
430 return;
432 hs_descriptor_free(desc->desc);
433 memwipe(&desc->key, 0, sizeof(desc->key));
434 memwipe(desc->encoded_desc, 0, strlen(desc->encoded_desc));
435 tor_free(desc->encoded_desc);
436 tor_free(desc);
439 /** Helper function: Use by the free all function to clear the client cache */
440 static void
441 cache_client_desc_free_void(void *ptr)
443 hs_cache_client_descriptor_t *desc = ptr;
444 cache_client_desc_free(desc);
447 /* Return a newly allocated and initialized hs_cache_intro_state_t object. */
448 static hs_cache_intro_state_t *
449 cache_intro_state_new(void)
451 hs_cache_intro_state_t *state = tor_malloc_zero(sizeof(*state));
452 state->created_ts = approx_time();
453 return state;
456 #define cache_intro_state_free(val) \
457 FREE_AND_NULL(hs_cache_intro_state_t, cache_intro_state_free_, (val))
459 /* Free an hs_cache_intro_state_t object. */
460 static void
461 cache_intro_state_free_(hs_cache_intro_state_t *state)
463 tor_free(state);
466 /* Helper function: use by the free all function. */
467 static void
468 cache_intro_state_free_void(void *state)
470 cache_intro_state_free_(state);
473 /* Return a newly allocated and initialized hs_cache_client_intro_state_t
474 * object. */
475 static hs_cache_client_intro_state_t *
476 cache_client_intro_state_new(void)
478 hs_cache_client_intro_state_t *cache = tor_malloc_zero(sizeof(*cache));
479 cache->intro_points = digest256map_new();
480 return cache;
483 #define cache_client_intro_state_free(val) \
484 FREE_AND_NULL(hs_cache_client_intro_state_t, \
485 cache_client_intro_state_free_, (val))
487 /* Free a cache client intro state object. */
488 static void
489 cache_client_intro_state_free_(hs_cache_client_intro_state_t *cache)
491 if (cache == NULL) {
492 return;
494 digest256map_free(cache->intro_points, cache_intro_state_free_void);
495 tor_free(cache);
498 /* Helper function: use by the free all function. */
499 static void
500 cache_client_intro_state_free_void(void *entry)
502 cache_client_intro_state_free_(entry);
505 /* For the given service identity key service_pk and an introduction
506 * authentication key auth_key, lookup the intro state object. Return 1 if
507 * found and put it in entry if not NULL. Return 0 if not found and entry is
508 * untouched. */
509 static int
510 cache_client_intro_state_lookup(const ed25519_public_key_t *service_pk,
511 const ed25519_public_key_t *auth_key,
512 hs_cache_intro_state_t **entry)
514 hs_cache_intro_state_t *state;
515 hs_cache_client_intro_state_t *cache;
517 tor_assert(service_pk);
518 tor_assert(auth_key);
520 /* Lookup the intro state cache for this service key. */
521 cache = digest256map_get(hs_cache_client_intro_state, service_pk->pubkey);
522 if (cache == NULL) {
523 goto not_found;
526 /* From the cache we just found for the service, lookup in the introduction
527 * points map for the given authentication key. */
528 state = digest256map_get(cache->intro_points, auth_key->pubkey);
529 if (state == NULL) {
530 goto not_found;
532 if (entry) {
533 *entry = state;
535 return 1;
536 not_found:
537 return 0;
540 /* Note the given failure in state. */
541 static void
542 cache_client_intro_state_note(hs_cache_intro_state_t *state,
543 rend_intro_point_failure_t failure)
545 tor_assert(state);
546 switch (failure) {
547 case INTRO_POINT_FAILURE_GENERIC:
548 state->error = 1;
549 break;
550 case INTRO_POINT_FAILURE_TIMEOUT:
551 state->timed_out = 1;
552 break;
553 case INTRO_POINT_FAILURE_UNREACHABLE:
554 state->unreachable_count++;
555 break;
556 default:
557 tor_assert_nonfatal_unreached();
558 return;
562 /* For the given service identity key service_pk and an introduction
563 * authentication key auth_key, add an entry in the client intro state cache
564 * If no entry exists for the service, it will create one. If state is non
565 * NULL, it will point to the new intro state entry. */
566 static void
567 cache_client_intro_state_add(const ed25519_public_key_t *service_pk,
568 const ed25519_public_key_t *auth_key,
569 hs_cache_intro_state_t **state)
571 hs_cache_intro_state_t *entry, *old_entry;
572 hs_cache_client_intro_state_t *cache;
574 tor_assert(service_pk);
575 tor_assert(auth_key);
577 /* Lookup the state cache for this service key. */
578 cache = digest256map_get(hs_cache_client_intro_state, service_pk->pubkey);
579 if (cache == NULL) {
580 cache = cache_client_intro_state_new();
581 digest256map_set(hs_cache_client_intro_state, service_pk->pubkey, cache);
584 entry = cache_intro_state_new();
585 old_entry = digest256map_set(cache->intro_points, auth_key->pubkey, entry);
586 /* This should never happened because the code flow is to lookup the entry
587 * before adding it. But, just in case, non fatal assert and free it. */
588 tor_assert_nonfatal(old_entry == NULL);
589 tor_free(old_entry);
591 if (state) {
592 *state = entry;
596 /* Remove every intro point state entry from cache that has been created
597 * before or at the cutoff. */
598 static void
599 cache_client_intro_state_clean(time_t cutoff,
600 hs_cache_client_intro_state_t *cache)
602 tor_assert(cache);
604 DIGEST256MAP_FOREACH_MODIFY(cache->intro_points, key,
605 hs_cache_intro_state_t *, entry) {
606 if (entry->created_ts <= cutoff) {
607 cache_intro_state_free(entry);
608 MAP_DEL_CURRENT(key);
610 } DIGEST256MAP_FOREACH_END;
613 /* Return true iff no intro points are in this cache. */
614 static int
615 cache_client_intro_state_is_empty(const hs_cache_client_intro_state_t *cache)
617 return digest256map_isempty(cache->intro_points);
620 /** Check whether <b>client_desc</b> is useful for us, and store it in the
621 * client-side HS cache if so. The client_desc is freed if we already have a
622 * fresher (higher revision counter count) in the cache. */
623 static int
624 cache_store_as_client(hs_cache_client_descriptor_t *client_desc)
626 hs_cache_client_descriptor_t *cache_entry;
628 /* TODO: Heavy code duplication with cache_store_as_dir(). Consider
629 * refactoring and uniting! */
631 tor_assert(client_desc);
633 /* Check if we already have a descriptor from this HS in cache. If we do,
634 * check if this descriptor is newer than the cached one */
635 cache_entry = lookup_v3_desc_as_client(client_desc->key.pubkey);
636 if (cache_entry != NULL) {
637 /* If we have an entry in our cache that has a revision counter greater
638 * than the one we just fetched, discard the one we fetched. */
639 if (cache_entry->desc->plaintext_data.revision_counter >
640 client_desc->desc->plaintext_data.revision_counter) {
641 cache_client_desc_free(client_desc);
642 goto done;
644 /* Remove old entry. Make space for the new one! */
645 remove_v3_desc_as_client(cache_entry);
646 cache_client_desc_free(cache_entry);
649 /* Store descriptor in cache */
650 store_v3_desc_as_client(client_desc);
652 done:
653 return 0;
656 /* Return true iff the cached client descriptor at <b>cached_desc</b has
657 * expired. */
658 static int
659 cached_client_descriptor_has_expired(time_t now,
660 const hs_cache_client_descriptor_t *cached_desc)
662 /* We use the current consensus time to see if we should expire this
663 * descriptor since we use consensus time for all other parts of the protocol
664 * as well (e.g. to build the blinded key and compute time periods). */
665 const networkstatus_t *ns = networkstatus_get_live_consensus(now);
666 /* If we don't have a recent consensus, consider this entry expired since we
667 * will want to fetch a new HS desc when we get a live consensus. */
668 if (!ns) {
669 return 1;
672 if (cached_desc->expiration_ts <= ns->valid_after) {
673 return 1;
676 return 0;
679 /* clean the client cache using now as the current time. Return the total size
680 * of removed bytes from the cache. */
681 static size_t
682 cache_clean_v3_as_client(time_t now)
684 size_t bytes_removed = 0;
686 if (!hs_cache_v3_client) { /* No cache to clean. Just return. */
687 return 0;
690 DIGEST256MAP_FOREACH_MODIFY(hs_cache_v3_client, key,
691 hs_cache_client_descriptor_t *, entry) {
692 size_t entry_size;
694 /* If the entry has not expired, continue to the next cached entry */
695 if (!cached_client_descriptor_has_expired(now, entry)) {
696 continue;
698 /* Here, our entry has expired, remove and free. */
699 MAP_DEL_CURRENT(key);
700 entry_size = cache_get_client_entry_size(entry);
701 bytes_removed += entry_size;
702 /* Entry is not in the cache anymore, destroy it. */
703 cache_client_desc_free(entry);
704 /* Update our OOM. We didn't use the remove() function because we are in
705 * a loop so we have to explicitly decrement. */
706 rend_cache_decrement_allocation(entry_size);
707 /* Logging. */
709 char key_b64[BASE64_DIGEST256_LEN + 1];
710 digest256_to_base64(key_b64, (const char *) key);
711 log_info(LD_REND, "Removing hidden service v3 descriptor '%s' "
712 "from client cache",
713 safe_str_client(key_b64));
715 } DIGEST256MAP_FOREACH_END;
717 return bytes_removed;
720 /** Public API: Given the HS ed25519 identity public key in <b>key</b>, return
721 * its HS encoded descriptor if it's stored in our cache, or NULL if not. */
722 const char *
723 hs_cache_lookup_encoded_as_client(const ed25519_public_key_t *key)
725 hs_cache_client_descriptor_t *cached_desc = NULL;
727 tor_assert(key);
729 cached_desc = lookup_v3_desc_as_client(key->pubkey);
730 if (cached_desc) {
731 tor_assert(cached_desc->encoded_desc);
732 return cached_desc->encoded_desc;
735 return NULL;
738 /** Public API: Given the HS ed25519 identity public key in <b>key</b>, return
739 * its HS descriptor if it's stored in our cache, or NULL if not. */
740 const hs_descriptor_t *
741 hs_cache_lookup_as_client(const ed25519_public_key_t *key)
743 hs_cache_client_descriptor_t *cached_desc = NULL;
745 tor_assert(key);
747 cached_desc = lookup_v3_desc_as_client(key->pubkey);
748 if (cached_desc) {
749 tor_assert(cached_desc->desc);
750 return cached_desc->desc;
753 return NULL;
756 /** Public API: Given an encoded descriptor, store it in the client HS
757 * cache. Return -1 on error, 0 on success .*/
759 hs_cache_store_as_client(const char *desc_str,
760 const ed25519_public_key_t *identity_pk)
762 hs_cache_client_descriptor_t *client_desc = NULL;
764 tor_assert(desc_str);
765 tor_assert(identity_pk);
767 /* Create client cache descriptor object */
768 client_desc = cache_client_desc_new(desc_str, identity_pk);
769 if (!client_desc) {
770 log_warn(LD_GENERAL, "Failed to parse received descriptor %s.",
771 escaped(desc_str));
772 goto err;
775 /* Push it to the cache */
776 if (cache_store_as_client(client_desc) < 0) {
777 goto err;
780 return 0;
782 err:
783 cache_client_desc_free(client_desc);
784 return -1;
787 /* Clean all client caches using the current time now. */
788 void
789 hs_cache_clean_as_client(time_t now)
791 /* Start with v2 cache cleaning. */
792 rend_cache_clean(now, REND_CACHE_TYPE_CLIENT);
793 /* Now, clean the v3 cache. Set the cutoff to 0 telling the cleanup function
794 * to compute the cutoff by itself using the lifetime value. */
795 cache_clean_v3_as_client(now);
798 /* Purge the client descriptor cache. */
799 void
800 hs_cache_purge_as_client(void)
802 DIGEST256MAP_FOREACH_MODIFY(hs_cache_v3_client, key,
803 hs_cache_client_descriptor_t *, entry) {
804 size_t entry_size = cache_get_client_entry_size(entry);
805 MAP_DEL_CURRENT(key);
806 cache_client_desc_free(entry);
807 /* Update our OOM. We didn't use the remove() function because we are in
808 * a loop so we have to explicitly decrement. */
809 rend_cache_decrement_allocation(entry_size);
810 } DIGEST256MAP_FOREACH_END;
812 log_info(LD_REND, "Hidden service client descriptor cache purged.");
815 /* For a given service identity public key and an introduction authentication
816 * key, note the given failure in the client intro state cache. */
817 void
818 hs_cache_client_intro_state_note(const ed25519_public_key_t *service_pk,
819 const ed25519_public_key_t *auth_key,
820 rend_intro_point_failure_t failure)
822 int found;
823 hs_cache_intro_state_t *entry;
825 tor_assert(service_pk);
826 tor_assert(auth_key);
828 found = cache_client_intro_state_lookup(service_pk, auth_key, &entry);
829 if (!found) {
830 /* Create a new entry and add it to the cache. */
831 cache_client_intro_state_add(service_pk, auth_key, &entry);
833 /* Note down the entry. */
834 cache_client_intro_state_note(entry, failure);
837 /* For a given service identity public key and an introduction authentication
838 * key, return true iff it is present in the failure cache. */
839 const hs_cache_intro_state_t *
840 hs_cache_client_intro_state_find(const ed25519_public_key_t *service_pk,
841 const ed25519_public_key_t *auth_key)
843 hs_cache_intro_state_t *state = NULL;
844 cache_client_intro_state_lookup(service_pk, auth_key, &state);
845 return state;
848 /* Cleanup the client introduction state cache. */
849 void
850 hs_cache_client_intro_state_clean(time_t now)
852 time_t cutoff = now - HS_CACHE_CLIENT_INTRO_STATE_MAX_AGE;
854 DIGEST256MAP_FOREACH_MODIFY(hs_cache_client_intro_state, key,
855 hs_cache_client_intro_state_t *, cache) {
856 /* Cleanup intro points failure. */
857 cache_client_intro_state_clean(cutoff, cache);
859 /* Is this cache empty for this service key? If yes, remove it from the
860 * cache. Else keep it. */
861 if (cache_client_intro_state_is_empty(cache)) {
862 cache_client_intro_state_free(cache);
863 MAP_DEL_CURRENT(key);
865 } DIGEST256MAP_FOREACH_END;
868 /* Purge the client introduction state cache. */
869 void
870 hs_cache_client_intro_state_purge(void)
872 DIGEST256MAP_FOREACH_MODIFY(hs_cache_client_intro_state, key,
873 hs_cache_client_intro_state_t *, cache) {
874 MAP_DEL_CURRENT(key);
875 cache_client_intro_state_free(cache);
876 } DIGEST256MAP_FOREACH_END;
878 log_info(LD_REND, "Hidden service client introduction point state "
879 "cache purged.");
882 /**************** Generics *********************************/
884 /* Do a round of OOM cleanup on all directory caches. Return the amount of
885 * removed bytes. It is possible that the returned value is lower than
886 * min_remove_bytes if the caches get emptied out so the caller should be
887 * aware of this. */
888 size_t
889 hs_cache_handle_oom(time_t now, size_t min_remove_bytes)
891 time_t k;
892 size_t bytes_removed = 0;
894 /* Our OOM handler called with 0 bytes to remove is a code flow error. */
895 tor_assert(min_remove_bytes != 0);
897 /* The algorithm is as follow. K is the oldest expected descriptor age.
899 * 1) Deallocate all entries from v2 cache that are older than K hours.
900 * 1.1) If the amount of remove bytes has been reached, stop.
901 * 2) Deallocate all entries from v3 cache that are older than K hours
902 * 2.1) If the amount of remove bytes has been reached, stop.
903 * 3) Set K = K - RendPostPeriod and repeat process until K is < 0.
905 * This ends up being O(Kn).
908 /* Set K to the oldest expected age in seconds which is the maximum
909 * lifetime of a cache entry. We'll use the v2 lifetime because it's much
910 * bigger than the v3 thus leading to cleaning older descriptors. */
911 k = rend_cache_max_entry_lifetime();
913 do {
914 time_t cutoff;
916 /* If K becomes negative, it means we've empty the caches so stop and
917 * return what we were able to cleanup. */
918 if (k < 0) {
919 break;
921 /* Compute a cutoff value with K and the current time. */
922 cutoff = now - k;
924 /* Start by cleaning the v2 cache with that cutoff. */
925 bytes_removed += rend_cache_clean_v2_descs_as_dir(cutoff);
927 if (bytes_removed < min_remove_bytes) {
928 /* We haven't remove enough bytes so clean v3 cache. */
929 bytes_removed += cache_clean_v3_as_dir(now, cutoff);
930 /* Decrement K by a post period to shorten the cutoff. */
931 k -= get_options()->RendPostPeriod;
933 } while (bytes_removed < min_remove_bytes);
935 return bytes_removed;
938 /* Return the maximum size of a v3 HS descriptor. */
939 unsigned int
940 hs_cache_get_max_descriptor_size(void)
942 return (unsigned) networkstatus_get_param(NULL,
943 "HSV3MaxDescriptorSize",
944 HS_DESC_MAX_LEN, 1, INT32_MAX);
947 /* Initialize the hidden service cache subsystem. */
948 void
949 hs_cache_init(void)
951 /* Calling this twice is very wrong code flow. */
952 tor_assert(!hs_cache_v3_dir);
953 hs_cache_v3_dir = digest256map_new();
955 tor_assert(!hs_cache_v3_client);
956 hs_cache_v3_client = digest256map_new();
958 tor_assert(!hs_cache_client_intro_state);
959 hs_cache_client_intro_state = digest256map_new();
962 /* Cleanup the hidden service cache subsystem. */
963 void
964 hs_cache_free_all(void)
966 digest256map_free(hs_cache_v3_dir, cache_dir_desc_free_void);
967 hs_cache_v3_dir = NULL;
969 digest256map_free(hs_cache_v3_client, cache_client_desc_free_void);
970 hs_cache_v3_client = NULL;
972 digest256map_free(hs_cache_client_intro_state,
973 cache_client_intro_state_free_void);
974 hs_cache_client_intro_state = NULL;