1 /* Copyright (c) 2016-2017, The Tor Project, Inc. */
2 /* See LICENSE for licensing information */
6 * \brief Handle hidden service descriptor caches.
10 #define HS_CACHE_PRIVATE
15 #include "hs_common.h"
16 #include "hs_client.h"
17 #include "hs_descriptor.h"
18 #include "networkstatus.h"
19 #include "rendcache.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. */
33 remove_v3_desc_as_dir(const hs_cache_dir_descriptor_t
*desc
)
36 digest256map_remove(hs_cache_v3_dir
, desc
->key
);
39 /* Store a given descriptor in our cache. */
41 store_v3_desc_as_dir(hs_cache_dir_descriptor_t
*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
)
52 return digest256map_get(hs_cache_v3_dir
, key
);
55 /* Free a directory descriptor object. */
57 cache_dir_desc_free(hs_cache_dir_descriptor_t
*desc
)
62 hs_desc_plaintext_data_free(desc
->plaintext_data
);
63 tor_free(desc
->encoded_desc
);
67 /* Helper function: Use by the free all function using the digest256map
68 * interface to cache entries. */
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
;
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.");
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
);
102 cache_dir_desc_free(dir_desc
);
106 /* Return the size of a cache entry in bytes. */
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. */
119 cache_store_v3_as_dir(hs_cache_dir_descriptor_t
*desc
)
121 hs_cache_dir_descriptor_t
*cache_entry
;
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
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). "
136 (int)cache_entry
->plaintext_data
->revision_counter
,
137 (int)desc
->plaintext_data
->revision_counter
);
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. */
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. */
170 cache_lookup_v3_as_dir(const char *query
, const char **desc_out
)
173 ed25519_public_key_t blinded_key
;
174 const hs_cache_dir_descriptor_t
*entry
;
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
));
185 entry
= lookup_v3_desc_as_dir(blinded_key
.pubkey
);
189 *desc_out
= entry
->encoded_desc
;
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. */
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. */
215 DIGEST256MAP_FOREACH_MODIFY(hs_cache_v3_dir
, key
,
216 hs_cache_dir_descriptor_t
*, entry
) {
218 time_t cutoff
= global_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
) {
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
);
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
253 hs_cache_store_as_dir(const char *desc
)
255 hs_cache_dir_descriptor_t
*dir_desc
= NULL
;
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
) {
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
:
272 if (cache_store_v3_as_dir(dir_desc
) < 0) {
280 cache_dir_desc_free(dir_desc
);
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
290 hs_cache_lookup_as_dir(uint32_t version
, const char *query
,
291 const char **desc_out
)
296 /* This should never be called with an unsupported version. */
297 tor_assert(hs_desc_is_supported_version(version
));
300 case HS_VERSION_THREE
:
302 found
= cache_lookup_v3_as_dir(query
, desc_out
);
309 /* Clean all directory caches using the current time now. */
311 hs_cache_clean_as_dir(time_t now
)
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. */
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. */
344 remove_v3_desc_as_client(const hs_cache_client_descriptor_t
*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. */
354 store_v3_desc_as_client(hs_cache_client_descriptor_t
*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
;
372 cached_desc
= digest256map_get(hs_cache_v3_client
, key
);
377 /* Don't return expired entries */
378 if (cached_client_descriptor_has_expired(now
, 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) {
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
);
420 /** Free memory allocated by <b>desc</b>. */
422 cache_client_desc_free(hs_cache_client_descriptor_t
*desc
)
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
);
434 /** Helper function: Use by the free all function to clear the client cache */
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();
451 /* Free an hs_cache_intro_state_t object. */
453 cache_intro_state_free(hs_cache_intro_state_t
*state
)
458 /* Helper function: use by the free all function. */
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
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();
475 /* Free a cache client intro state object. */
477 cache_client_intro_state_free(hs_cache_client_intro_state_t
*cache
)
482 digest256map_free(cache
->intro_points
, cache_intro_state_free_
);
486 /* Helper function: use by the free all function. */
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
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
);
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
);
528 /* Note the given failure in state. */
530 cache_client_intro_state_note(hs_cache_intro_state_t
*state
,
531 rend_intro_point_failure_t failure
)
535 case INTRO_POINT_FAILURE_GENERIC
:
538 case INTRO_POINT_FAILURE_TIMEOUT
:
539 state
->timed_out
= 1;
541 case INTRO_POINT_FAILURE_UNREACHABLE
:
542 state
->unreachable_count
++;
545 tor_assert_nonfatal_unreached();
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. */
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
);
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
);
584 /* Remove every intro point state entry from cache that has been created
585 * before or at the cutoff. */
587 cache_client_intro_state_clean(time_t cutoff
,
588 hs_cache_client_intro_state_t
*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. */
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. */
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
);
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
);
644 /* Return true iff the cached client descriptor at <b>cached_desc</b has
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. */
660 if (cached_desc
->expiration_ts
<= ns
->valid_after
) {
667 /* clean the client cache using now as the current time. Return the total size
668 * of removed bytes from the cache. */
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. */
678 DIGEST256MAP_FOREACH_MODIFY(hs_cache_v3_client
, key
,
679 hs_cache_client_descriptor_t
*, entry
) {
682 /* If the entry has not expired, continue to the next cached entry */
683 if (!cached_client_descriptor_has_expired(now
, entry
)) {
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
);
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' "
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
;
717 cached_desc
= lookup_v3_desc_as_client(key
->pubkey
);
719 tor_assert(cached_desc
->desc
);
720 return cached_desc
->desc
;
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
);
740 log_warn(LD_GENERAL
, "Failed to parse received descriptor %s.",
745 /* Push it to the cache */
746 if (cache_store_as_client(client_desc
) < 0) {
753 cache_client_desc_free(client_desc
);
757 /* Clean all client caches using the current time now. */
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. */
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. */
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
)
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
);
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
);
818 /* Cleanup the client introduction state cache. */
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. */
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 "
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
859 hs_cache_handle_oom(time_t now
, size_t min_remove_bytes
)
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();
886 /* If K becomes negative, it means we've empty the caches so stop and
887 * return what we were able to cleanup. */
891 /* Compute a cutoff value with K and the current time. */
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. */
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. */
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. */
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
;