minor updates on upcoming changelog
[tor.git] / src / or / hs_cache.c
blob3ebe13fb4dbc83fa96b00319dc8aef0832a6d9d0
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 /* Free a directory descriptor object. */
56 static void
57 cache_dir_desc_free(hs_cache_dir_descriptor_t *desc)
59 if (desc == NULL) {
60 return;
62 hs_desc_plaintext_data_free(desc->plaintext_data);
63 tor_free(desc->encoded_desc);
64 tor_free(desc);
67 /* Helper function: Use by the free all function using the digest256map
68 * interface to cache entries. */
69 static void
70 cache_dir_desc_free_(void *ptr)
72 hs_cache_dir_descriptor_t *desc = ptr;
73 cache_dir_desc_free(desc);
76 /* Create a new directory cache descriptor object from a encoded descriptor.
77 * On success, return the heap-allocated cache object, otherwise return NULL if
78 * we can't decode the descriptor. */
79 static hs_cache_dir_descriptor_t *
80 cache_dir_desc_new(const char *desc)
82 hs_cache_dir_descriptor_t *dir_desc;
84 tor_assert(desc);
86 dir_desc = tor_malloc_zero(sizeof(hs_cache_dir_descriptor_t));
87 dir_desc->plaintext_data =
88 tor_malloc_zero(sizeof(hs_desc_plaintext_data_t));
89 dir_desc->encoded_desc = tor_strdup(desc);
91 if (hs_desc_decode_plaintext(desc, dir_desc->plaintext_data) < 0) {
92 log_debug(LD_DIR, "Unable to decode descriptor. Rejecting.");
93 goto err;
96 /* The blinded pubkey is the indexed key. */
97 dir_desc->key = dir_desc->plaintext_data->blinded_pubkey.pubkey;
98 dir_desc->created_ts = time(NULL);
99 return dir_desc;
101 err:
102 cache_dir_desc_free(dir_desc);
103 return NULL;
106 /* Return the size of a cache entry in bytes. */
107 static size_t
108 cache_get_dir_entry_size(const hs_cache_dir_descriptor_t *entry)
110 return (sizeof(*entry) + hs_desc_plaintext_obj_size(entry->plaintext_data)
111 + strlen(entry->encoded_desc));
114 /* Try to store a valid version 3 descriptor in the directory cache. Return 0
115 * on success else a negative value is returned indicating that we have a
116 * newer version in our cache. On error, caller is responsible to free the
117 * given descriptor desc. */
118 static int
119 cache_store_v3_as_dir(hs_cache_dir_descriptor_t *desc)
121 hs_cache_dir_descriptor_t *cache_entry;
123 tor_assert(desc);
125 /* Verify if we have an entry in the cache for that key and if yes, check
126 * if we should replace it? */
127 cache_entry = lookup_v3_desc_as_dir(desc->key);
128 if (cache_entry != NULL) {
129 /* Only replace descriptor if revision-counter is greater than the one
130 * in our cache */
131 if (cache_entry->plaintext_data->revision_counter >=
132 desc->plaintext_data->revision_counter) {
133 log_info(LD_REND, "Descriptor revision counter in our cache is "
134 "greater or equal than the one we received (%d/%d). "
135 "Rejecting!",
136 (int)cache_entry->plaintext_data->revision_counter,
137 (int)desc->plaintext_data->revision_counter);
138 goto err;
140 /* We now know that the descriptor we just received is a new one so
141 * remove the entry we currently have from our cache so we can then
142 * store the new one. */
143 remove_v3_desc_as_dir(cache_entry);
144 rend_cache_decrement_allocation(cache_get_dir_entry_size(cache_entry));
145 cache_dir_desc_free(cache_entry);
147 /* Store the descriptor we just got. We are sure here that either we
148 * don't have the entry or we have a newer descriptor and the old one
149 * has been removed from the cache. */
150 store_v3_desc_as_dir(desc);
152 /* Update our total cache size with this entry for the OOM. This uses the
153 * old HS protocol cache subsystem for which we are tied with. */
154 rend_cache_increment_allocation(cache_get_dir_entry_size(desc));
156 /* XXX: Update HS statistics. We should have specific stats for v3. */
158 return 0;
160 err:
161 return -1;
164 /* Using the query which is the base64 encoded blinded key of a version 3
165 * descriptor, lookup in our directory cache the entry. If found, 1 is
166 * returned and desc_out is populated with a newly allocated string being the
167 * encoded descriptor. If not found, 0 is returned and desc_out is untouched.
168 * On error, a negative value is returned and desc_out is untouched. */
169 static int
170 cache_lookup_v3_as_dir(const char *query, const char **desc_out)
172 int found = 0;
173 ed25519_public_key_t blinded_key;
174 const hs_cache_dir_descriptor_t *entry;
176 tor_assert(query);
178 /* Decode blinded key using the given query value. */
179 if (ed25519_public_from_base64(&blinded_key, query) < 0) {
180 log_info(LD_REND, "Unable to decode the v3 HSDir query %s.",
181 safe_str_client(query));
182 goto err;
185 entry = lookup_v3_desc_as_dir(blinded_key.pubkey);
186 if (entry != NULL) {
187 found = 1;
188 if (desc_out) {
189 *desc_out = entry->encoded_desc;
193 return found;
195 err:
196 return -1;
199 /* Clean the v3 cache by removing any entry that has expired using the
200 * <b>global_cutoff</b> value. If <b>global_cutoff</b> is 0, the cleaning
201 * process will use the lifetime found in the plaintext data section. Return
202 * the number of bytes cleaned. */
203 STATIC size_t
204 cache_clean_v3_as_dir(time_t now, time_t global_cutoff)
206 size_t bytes_removed = 0;
208 /* Code flow error if this ever happens. */
209 tor_assert(global_cutoff >= 0);
211 if (!hs_cache_v3_dir) { /* No cache to clean. Just return. */
212 return 0;
215 DIGEST256MAP_FOREACH_MODIFY(hs_cache_v3_dir, key,
216 hs_cache_dir_descriptor_t *, entry) {
217 size_t entry_size;
218 time_t cutoff = global_cutoff;
219 if (!cutoff) {
220 /* Cutoff is the lifetime of the entry found in the descriptor. */
221 cutoff = now - entry->plaintext_data->lifetime_sec;
224 /* If the entry has been created _after_ the cutoff, not expired so
225 * continue to the next entry in our v3 cache. */
226 if (entry->created_ts > cutoff) {
227 continue;
229 /* Here, our entry has expired, remove and free. */
230 MAP_DEL_CURRENT(key);
231 entry_size = cache_get_dir_entry_size(entry);
232 bytes_removed += entry_size;
233 /* Entry is not in the cache anymore, destroy it. */
234 cache_dir_desc_free(entry);
235 /* Update our cache entry allocation size for the OOM. */
236 rend_cache_decrement_allocation(entry_size);
237 /* Logging. */
239 char key_b64[BASE64_DIGEST256_LEN + 1];
240 digest256_to_base64(key_b64, (const char *) key);
241 log_info(LD_REND, "Removing v3 descriptor '%s' from HSDir cache",
242 safe_str_client(key_b64));
244 } DIGEST256MAP_FOREACH_END;
246 return bytes_removed;
249 /* Given an encoded descriptor, store it in the directory cache depending on
250 * which version it is. Return a negative value on error. On success, 0 is
251 * returned. */
253 hs_cache_store_as_dir(const char *desc)
255 hs_cache_dir_descriptor_t *dir_desc = NULL;
257 tor_assert(desc);
259 /* Create a new cache object. This can fail if the descriptor plaintext data
260 * is unparseable which in this case a log message will be triggered. */
261 dir_desc = cache_dir_desc_new(desc);
262 if (dir_desc == NULL) {
263 goto err;
266 /* Call the right function against the descriptor version. At this point,
267 * we are sure that the descriptor's version is supported else the
268 * decoding would have failed. */
269 switch (dir_desc->plaintext_data->version) {
270 case HS_VERSION_THREE:
271 default:
272 if (cache_store_v3_as_dir(dir_desc) < 0) {
273 goto err;
275 break;
277 return 0;
279 err:
280 cache_dir_desc_free(dir_desc);
281 return -1;
284 /* Using the query, lookup in our directory cache the entry. If found, 1 is
285 * returned and desc_out is populated with a newly allocated string being
286 * the encoded descriptor. If not found, 0 is returned and desc_out is
287 * untouched. On error, a negative value is returned and desc_out is
288 * untouched. */
290 hs_cache_lookup_as_dir(uint32_t version, const char *query,
291 const char **desc_out)
293 int found;
295 tor_assert(query);
296 /* This should never be called with an unsupported version. */
297 tor_assert(hs_desc_is_supported_version(version));
299 switch (version) {
300 case HS_VERSION_THREE:
301 default:
302 found = cache_lookup_v3_as_dir(query, desc_out);
303 break;
306 return found;
309 /* Clean all directory caches using the current time now. */
310 void
311 hs_cache_clean_as_dir(time_t now)
313 time_t cutoff;
315 /* Start with v2 cache cleaning. */
316 cutoff = now - rend_cache_max_entry_lifetime();
317 rend_cache_clean_v2_descs_as_dir(cutoff);
319 /* Now, clean the v3 cache. Set the cutoff to 0 telling the cleanup function
320 * to compute the cutoff by itself using the lifetime value. */
321 cache_clean_v3_as_dir(now, 0);
324 /********************** Client-side HS cache ******************/
326 /* Client-side HS descriptor cache. Map indexed by service identity key. */
327 static digest256map_t *hs_cache_v3_client;
329 /* Client-side introduction point state cache. Map indexed by service public
330 * identity key (onion address). It contains hs_cache_client_intro_state_t
331 * objects all related to a specific service. */
332 static digest256map_t *hs_cache_client_intro_state;
334 /* Return the size of a client cache entry in bytes. */
335 static size_t
336 cache_get_client_entry_size(const hs_cache_client_descriptor_t *entry)
338 return sizeof(*entry) +
339 strlen(entry->encoded_desc) + hs_desc_obj_size(entry->desc);
342 /* Remove a given descriptor from our cache. */
343 static void
344 remove_v3_desc_as_client(const hs_cache_client_descriptor_t *desc)
346 tor_assert(desc);
347 digest256map_remove(hs_cache_v3_client, desc->key.pubkey);
348 /* Update cache size with this entry for the OOM handler. */
349 rend_cache_decrement_allocation(cache_get_client_entry_size(desc));
352 /* Store a given descriptor in our cache. */
353 static void
354 store_v3_desc_as_client(hs_cache_client_descriptor_t *desc)
356 tor_assert(desc);
357 digest256map_set(hs_cache_v3_client, desc->key.pubkey, desc);
358 /* Update cache size with this entry for the OOM handler. */
359 rend_cache_increment_allocation(cache_get_client_entry_size(desc));
362 /* Query our cache and return the entry or NULL if not found or if expired. */
363 STATIC hs_cache_client_descriptor_t *
364 lookup_v3_desc_as_client(const uint8_t *key)
366 time_t now = approx_time();
367 hs_cache_client_descriptor_t *cached_desc;
369 tor_assert(key);
371 /* Do the lookup */
372 cached_desc = digest256map_get(hs_cache_v3_client, key);
373 if (!cached_desc) {
374 return NULL;
377 /* Don't return expired entries */
378 if (cached_client_descriptor_has_expired(now, cached_desc)) {
379 return NULL;
382 return cached_desc;
385 /* Parse the encoded descriptor in <b>desc_str</b> using
386 * <b>service_identity_pk<b> to decrypt it first.
388 * If everything goes well, allocate and return a new
389 * hs_cache_client_descriptor_t object. In case of error, return NULL. */
390 static hs_cache_client_descriptor_t *
391 cache_client_desc_new(const char *desc_str,
392 const ed25519_public_key_t *service_identity_pk)
394 hs_descriptor_t *desc = NULL;
395 hs_cache_client_descriptor_t *client_desc = NULL;
397 tor_assert(desc_str);
398 tor_assert(service_identity_pk);
400 /* Decode the descriptor we just fetched. */
401 if (hs_client_decode_descriptor(desc_str, service_identity_pk, &desc) < 0) {
402 goto end;
404 tor_assert(desc);
406 /* All is good: make a cache object for this descriptor */
407 client_desc = tor_malloc_zero(sizeof(hs_cache_client_descriptor_t));
408 ed25519_pubkey_copy(&client_desc->key, service_identity_pk);
409 /* Set expiration time for this cached descriptor to be the start of the next
410 * time period since that's when clients need to start using the next blinded
411 * pk of the service (and hence will need its next descriptor). */
412 client_desc->expiration_ts = hs_get_start_time_of_next_time_period(0);
413 client_desc->desc = desc;
414 client_desc->encoded_desc = tor_strdup(desc_str);
416 end:
417 return client_desc;
420 /** Free memory allocated by <b>desc</b>. */
421 static void
422 cache_client_desc_free(hs_cache_client_descriptor_t *desc)
424 if (desc == NULL) {
425 return;
427 hs_descriptor_free(desc->desc);
428 memwipe(&desc->key, 0, sizeof(desc->key));
429 memwipe(desc->encoded_desc, 0, strlen(desc->encoded_desc));
430 tor_free(desc->encoded_desc);
431 tor_free(desc);
434 /** Helper function: Use by the free all function to clear the client cache */
435 static void
436 cache_client_desc_free_(void *ptr)
438 hs_cache_client_descriptor_t *desc = ptr;
439 cache_client_desc_free(desc);
442 /* Return a newly allocated and initialized hs_cache_intro_state_t object. */
443 static hs_cache_intro_state_t *
444 cache_intro_state_new(void)
446 hs_cache_intro_state_t *state = tor_malloc_zero(sizeof(*state));
447 state->created_ts = approx_time();
448 return state;
451 /* Free an hs_cache_intro_state_t object. */
452 static void
453 cache_intro_state_free(hs_cache_intro_state_t *state)
455 tor_free(state);
458 /* Helper function: use by the free all function. */
459 static void
460 cache_intro_state_free_(void *state)
462 cache_intro_state_free(state);
465 /* Return a newly allocated and initialized hs_cache_client_intro_state_t
466 * object. */
467 static hs_cache_client_intro_state_t *
468 cache_client_intro_state_new(void)
470 hs_cache_client_intro_state_t *cache = tor_malloc_zero(sizeof(*cache));
471 cache->intro_points = digest256map_new();
472 return cache;
475 /* Free a cache client intro state object. */
476 static void
477 cache_client_intro_state_free(hs_cache_client_intro_state_t *cache)
479 if (cache == NULL) {
480 return;
482 digest256map_free(cache->intro_points, cache_intro_state_free_);
483 tor_free(cache);
486 /* Helper function: use by the free all function. */
487 static void
488 cache_client_intro_state_free_(void *entry)
490 cache_client_intro_state_free(entry);
493 /* For the given service identity key service_pk and an introduction
494 * authentication key auth_key, lookup the intro state object. Return 1 if
495 * found and put it in entry if not NULL. Return 0 if not found and entry is
496 * untouched. */
497 static int
498 cache_client_intro_state_lookup(const ed25519_public_key_t *service_pk,
499 const ed25519_public_key_t *auth_key,
500 hs_cache_intro_state_t **entry)
502 hs_cache_intro_state_t *state;
503 hs_cache_client_intro_state_t *cache;
505 tor_assert(service_pk);
506 tor_assert(auth_key);
508 /* Lookup the intro state cache for this service key. */
509 cache = digest256map_get(hs_cache_client_intro_state, service_pk->pubkey);
510 if (cache == NULL) {
511 goto not_found;
514 /* From the cache we just found for the service, lookup in the introduction
515 * points map for the given authentication key. */
516 state = digest256map_get(cache->intro_points, auth_key->pubkey);
517 if (state == NULL) {
518 goto not_found;
520 if (entry) {
521 *entry = state;
523 return 1;
524 not_found:
525 return 0;
528 /* Note the given failure in state. */
529 static void
530 cache_client_intro_state_note(hs_cache_intro_state_t *state,
531 rend_intro_point_failure_t failure)
533 tor_assert(state);
534 switch (failure) {
535 case INTRO_POINT_FAILURE_GENERIC:
536 state->error = 1;
537 break;
538 case INTRO_POINT_FAILURE_TIMEOUT:
539 state->timed_out = 1;
540 break;
541 case INTRO_POINT_FAILURE_UNREACHABLE:
542 state->unreachable_count++;
543 break;
544 default:
545 tor_assert_nonfatal_unreached();
546 return;
550 /* For the given service identity key service_pk and an introduction
551 * authentication key auth_key, add an entry in the client intro state cache
552 * If no entry exists for the service, it will create one. If state is non
553 * NULL, it will point to the new intro state entry. */
554 static void
555 cache_client_intro_state_add(const ed25519_public_key_t *service_pk,
556 const ed25519_public_key_t *auth_key,
557 hs_cache_intro_state_t **state)
559 hs_cache_intro_state_t *entry, *old_entry;
560 hs_cache_client_intro_state_t *cache;
562 tor_assert(service_pk);
563 tor_assert(auth_key);
565 /* Lookup the state cache for this service key. */
566 cache = digest256map_get(hs_cache_client_intro_state, service_pk->pubkey);
567 if (cache == NULL) {
568 cache = cache_client_intro_state_new();
569 digest256map_set(hs_cache_client_intro_state, service_pk->pubkey, cache);
572 entry = cache_intro_state_new();
573 old_entry = digest256map_set(cache->intro_points, auth_key->pubkey, entry);
574 /* This should never happened because the code flow is to lookup the entry
575 * before adding it. But, just in case, non fatal assert and free it. */
576 tor_assert_nonfatal(old_entry == NULL);
577 tor_free(old_entry);
579 if (state) {
580 *state = entry;
584 /* Remove every intro point state entry from cache that has been created
585 * before or at the cutoff. */
586 static void
587 cache_client_intro_state_clean(time_t cutoff,
588 hs_cache_client_intro_state_t *cache)
590 tor_assert(cache);
592 DIGEST256MAP_FOREACH_MODIFY(cache->intro_points, key,
593 hs_cache_intro_state_t *, entry) {
594 if (entry->created_ts <= cutoff) {
595 cache_intro_state_free(entry);
596 MAP_DEL_CURRENT(key);
598 } DIGEST256MAP_FOREACH_END;
601 /* Return true iff no intro points are in this cache. */
602 static int
603 cache_client_intro_state_is_empty(const hs_cache_client_intro_state_t *cache)
605 return digest256map_isempty(cache->intro_points);
608 /** Check whether <b>client_desc</b> is useful for us, and store it in the
609 * client-side HS cache if so. The client_desc is freed if we already have a
610 * fresher (higher revision counter count) in the cache. */
611 static int
612 cache_store_as_client(hs_cache_client_descriptor_t *client_desc)
614 hs_cache_client_descriptor_t *cache_entry;
616 /* TODO: Heavy code duplication with cache_store_as_dir(). Consider
617 * refactoring and uniting! */
619 tor_assert(client_desc);
621 /* Check if we already have a descriptor from this HS in cache. If we do,
622 * check if this descriptor is newer than the cached one */
623 cache_entry = lookup_v3_desc_as_client(client_desc->key.pubkey);
624 if (cache_entry != NULL) {
625 /* If we have an entry in our cache that has a revision counter greater
626 * than the one we just fetched, discard the one we fetched. */
627 if (BUG(cache_entry->desc->plaintext_data.revision_counter >
628 client_desc->desc->plaintext_data.revision_counter)) {
629 cache_client_desc_free(client_desc);
630 goto done;
632 /* Remove old entry. Make space for the new one! */
633 remove_v3_desc_as_client(cache_entry);
634 cache_client_desc_free(cache_entry);
637 /* Store descriptor in cache */
638 store_v3_desc_as_client(client_desc);
640 done:
641 return 0;
644 /* Return true iff the cached client descriptor at <b>cached_desc</b has
645 * expired. */
646 static int
647 cached_client_descriptor_has_expired(time_t now,
648 const hs_cache_client_descriptor_t *cached_desc)
650 /* We use the current consensus time to see if we should expire this
651 * descriptor since we use consensus time for all other parts of the protocol
652 * as well (e.g. to build the blinded key and compute time periods). */
653 const networkstatus_t *ns = networkstatus_get_live_consensus(now);
654 /* If we don't have a recent consensus, consider this entry expired since we
655 * will want to fetch a new HS desc when we get a live consensus. */
656 if (!ns) {
657 return 1;
660 if (cached_desc->expiration_ts <= ns->valid_after) {
661 return 1;
664 return 0;
667 /* clean the client cache using now as the current time. Return the total size
668 * of removed bytes from the cache. */
669 static size_t
670 cache_clean_v3_as_client(time_t now)
672 size_t bytes_removed = 0;
674 if (!hs_cache_v3_client) { /* No cache to clean. Just return. */
675 return 0;
678 DIGEST256MAP_FOREACH_MODIFY(hs_cache_v3_client, key,
679 hs_cache_client_descriptor_t *, entry) {
680 size_t entry_size;
682 /* If the entry has not expired, continue to the next cached entry */
683 if (!cached_client_descriptor_has_expired(now, entry)) {
684 continue;
686 /* Here, our entry has expired, remove and free. */
687 MAP_DEL_CURRENT(key);
688 entry_size = cache_get_client_entry_size(entry);
689 bytes_removed += entry_size;
690 /* Entry is not in the cache anymore, destroy it. */
691 cache_client_desc_free(entry);
692 /* Update our OOM. We didn't use the remove() function because we are in
693 * a loop so we have to explicitely decrement. */
694 rend_cache_decrement_allocation(entry_size);
695 /* Logging. */
697 char key_b64[BASE64_DIGEST256_LEN + 1];
698 digest256_to_base64(key_b64, (const char *) key);
699 log_info(LD_REND, "Removing hidden service v3 descriptor '%s' "
700 "from client cache",
701 safe_str_client(key_b64));
703 } DIGEST256MAP_FOREACH_END;
705 return bytes_removed;
708 /** Public API: Given the HS ed25519 identity public key in <b>key</b>, return
709 * its HS descriptor if it's stored in our cache, or NULL if not. */
710 const hs_descriptor_t *
711 hs_cache_lookup_as_client(const ed25519_public_key_t *key)
713 hs_cache_client_descriptor_t *cached_desc = NULL;
715 tor_assert(key);
717 cached_desc = lookup_v3_desc_as_client(key->pubkey);
718 if (cached_desc) {
719 tor_assert(cached_desc->desc);
720 return cached_desc->desc;
723 return NULL;
726 /** Public API: Given an encoded descriptor, store it in the client HS
727 * cache. Return -1 on error, 0 on success .*/
729 hs_cache_store_as_client(const char *desc_str,
730 const ed25519_public_key_t *identity_pk)
732 hs_cache_client_descriptor_t *client_desc = NULL;
734 tor_assert(desc_str);
735 tor_assert(identity_pk);
737 /* Create client cache descriptor object */
738 client_desc = cache_client_desc_new(desc_str, identity_pk);
739 if (!client_desc) {
740 log_warn(LD_GENERAL, "Failed to parse received descriptor %s.",
741 escaped(desc_str));
742 goto err;
745 /* Push it to the cache */
746 if (cache_store_as_client(client_desc) < 0) {
747 goto err;
750 return 0;
752 err:
753 cache_client_desc_free(client_desc);
754 return -1;
757 /* Clean all client caches using the current time now. */
758 void
759 hs_cache_clean_as_client(time_t now)
761 /* Start with v2 cache cleaning. */
762 rend_cache_clean(now, REND_CACHE_TYPE_CLIENT);
763 /* Now, clean the v3 cache. Set the cutoff to 0 telling the cleanup function
764 * to compute the cutoff by itself using the lifetime value. */
765 cache_clean_v3_as_client(now);
768 /* Purge the client descriptor cache. */
769 void
770 hs_cache_purge_as_client(void)
772 DIGEST256MAP_FOREACH_MODIFY(hs_cache_v3_client, key,
773 hs_cache_client_descriptor_t *, entry) {
774 size_t entry_size = cache_get_client_entry_size(entry);
775 MAP_DEL_CURRENT(key);
776 cache_client_desc_free(entry);
777 /* Update our OOM. We didn't use the remove() function because we are in
778 * a loop so we have to explicitely decrement. */
779 rend_cache_decrement_allocation(entry_size);
780 } DIGEST256MAP_FOREACH_END;
782 log_info(LD_REND, "Hidden service client descriptor cache purged.");
785 /* For a given service identity public key and an introduction authentication
786 * key, note the given failure in the client intro state cache. */
787 void
788 hs_cache_client_intro_state_note(const ed25519_public_key_t *service_pk,
789 const ed25519_public_key_t *auth_key,
790 rend_intro_point_failure_t failure)
792 int found;
793 hs_cache_intro_state_t *entry;
795 tor_assert(service_pk);
796 tor_assert(auth_key);
798 found = cache_client_intro_state_lookup(service_pk, auth_key, &entry);
799 if (!found) {
800 /* Create a new entry and add it to the cache. */
801 cache_client_intro_state_add(service_pk, auth_key, &entry);
803 /* Note down the entry. */
804 cache_client_intro_state_note(entry, failure);
807 /* For a given service identity public key and an introduction authentication
808 * key, return true iff it is present in the failure cache. */
809 const hs_cache_intro_state_t *
810 hs_cache_client_intro_state_find(const ed25519_public_key_t *service_pk,
811 const ed25519_public_key_t *auth_key)
813 hs_cache_intro_state_t *state = NULL;
814 cache_client_intro_state_lookup(service_pk, auth_key, &state);
815 return state;
818 /* Cleanup the client introduction state cache. */
819 void
820 hs_cache_client_intro_state_clean(time_t now)
822 time_t cutoff = now - HS_CACHE_CLIENT_INTRO_STATE_MAX_AGE;
824 DIGEST256MAP_FOREACH_MODIFY(hs_cache_client_intro_state, key,
825 hs_cache_client_intro_state_t *, cache) {
826 /* Cleanup intro points failure. */
827 cache_client_intro_state_clean(cutoff, cache);
829 /* Is this cache empty for this service key? If yes, remove it from the
830 * cache. Else keep it. */
831 if (cache_client_intro_state_is_empty(cache)) {
832 cache_client_intro_state_free(cache);
833 MAP_DEL_CURRENT(key);
835 } DIGEST256MAP_FOREACH_END;
838 /* Purge the client introduction state cache. */
839 void
840 hs_cache_client_intro_state_purge(void)
842 DIGEST256MAP_FOREACH_MODIFY(hs_cache_client_intro_state, key,
843 hs_cache_client_intro_state_t *, cache) {
844 MAP_DEL_CURRENT(key);
845 cache_client_intro_state_free(cache);
846 } DIGEST256MAP_FOREACH_END;
848 log_info(LD_REND, "Hidden service client introduction point state "
849 "cache purged.");
852 /**************** Generics *********************************/
854 /* Do a round of OOM cleanup on all directory caches. Return the amount of
855 * removed bytes. It is possible that the returned value is lower than
856 * min_remove_bytes if the caches get emptied out so the caller should be
857 * aware of this. */
858 size_t
859 hs_cache_handle_oom(time_t now, size_t min_remove_bytes)
861 time_t k;
862 size_t bytes_removed = 0;
864 /* Our OOM handler called with 0 bytes to remove is a code flow error. */
865 tor_assert(min_remove_bytes != 0);
867 /* The algorithm is as follow. K is the oldest expected descriptor age.
869 * 1) Deallocate all entries from v2 cache that are older than K hours.
870 * 1.1) If the amount of remove bytes has been reached, stop.
871 * 2) Deallocate all entries from v3 cache that are older than K hours
872 * 2.1) If the amount of remove bytes has been reached, stop.
873 * 3) Set K = K - RendPostPeriod and repeat process until K is < 0.
875 * This ends up being O(Kn).
878 /* Set K to the oldest expected age in seconds which is the maximum
879 * lifetime of a cache entry. We'll use the v2 lifetime because it's much
880 * bigger than the v3 thus leading to cleaning older descriptors. */
881 k = rend_cache_max_entry_lifetime();
883 do {
884 time_t cutoff;
886 /* If K becomes negative, it means we've empty the caches so stop and
887 * return what we were able to cleanup. */
888 if (k < 0) {
889 break;
891 /* Compute a cutoff value with K and the current time. */
892 cutoff = now - k;
894 /* Start by cleaning the v2 cache with that cutoff. */
895 bytes_removed += rend_cache_clean_v2_descs_as_dir(cutoff);
897 if (bytes_removed < min_remove_bytes) {
898 /* We haven't remove enough bytes so clean v3 cache. */
899 bytes_removed += cache_clean_v3_as_dir(now, cutoff);
900 /* Decrement K by a post period to shorten the cutoff. */
901 k -= get_options()->RendPostPeriod;
903 } while (bytes_removed < min_remove_bytes);
905 return bytes_removed;
908 /* Return the maximum size of a v3 HS descriptor. */
909 unsigned int
910 hs_cache_get_max_descriptor_size(void)
912 return (unsigned) networkstatus_get_param(NULL,
913 "HSV3MaxDescriptorSize",
914 HS_DESC_MAX_LEN, 1, INT32_MAX);
917 /* Initialize the hidden service cache subsystem. */
918 void
919 hs_cache_init(void)
921 /* Calling this twice is very wrong code flow. */
922 tor_assert(!hs_cache_v3_dir);
923 hs_cache_v3_dir = digest256map_new();
925 tor_assert(!hs_cache_v3_client);
926 hs_cache_v3_client = digest256map_new();
928 tor_assert(!hs_cache_client_intro_state);
929 hs_cache_client_intro_state = digest256map_new();
932 /* Cleanup the hidden service cache subsystem. */
933 void
934 hs_cache_free_all(void)
936 digest256map_free(hs_cache_v3_dir, cache_dir_desc_free_);
937 hs_cache_v3_dir = NULL;
939 digest256map_free(hs_cache_v3_client, cache_client_desc_free_);
940 hs_cache_v3_client = NULL;
942 digest256map_free(hs_cache_client_intro_state,
943 cache_client_intro_state_free_);
944 hs_cache_client_intro_state = NULL;