LCOV - code coverage report
Current view: top level - feature/hs - hs_cache.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 422 450 93.8 %
Date: 2021-11-24 03:28:48 Functions: 52 53 98.1 %

          Line data    Source code
       1             : /* Copyright (c) 2016-2021, The Tor Project, Inc. */
       2             : /* See LICENSE for licensing information */
       3             : 
       4             : /**
       5             :  * \file hs_cache.c
       6             :  * \brief Handle hidden service descriptor caches.
       7             :  **/
       8             : 
       9             : /* For unit tests.*/
      10             : #define HS_CACHE_PRIVATE
      11             : 
      12             : #include "core/or/or.h"
      13             : #include "app/config/config.h"
      14             : #include "lib/crypt_ops/crypto_format.h"
      15             : #include "lib/crypt_ops/crypto_util.h"
      16             : #include "feature/hs/hs_ident.h"
      17             : #include "feature/hs/hs_common.h"
      18             : #include "feature/hs/hs_client.h"
      19             : #include "feature/hs/hs_descriptor.h"
      20             : #include "feature/nodelist/microdesc.h"
      21             : #include "feature/nodelist/networkstatus.h"
      22             : #include "feature/stats/rephist.h"
      23             : 
      24             : #include "feature/hs/hs_cache.h"
      25             : 
      26             : #include "feature/nodelist/networkstatus_st.h"
      27             : 
      28             : /* Total counter of the cache size. */
      29             : static size_t hs_cache_total_allocation = 0;
      30             : 
      31             : static int cached_client_descriptor_has_expired(time_t now,
      32             :            const hs_cache_client_descriptor_t *cached_desc);
      33             : 
      34             : /** Helper function: Return true iff the cache entry has a decrypted
      35             :  * descriptor.
      36             :  *
      37             :  * A NULL desc object in the entry means that we were not able to decrypt the
      38             :  * descriptor because we are likely lacking client authorization.  It is still
      39             :  * a valid entry but some operations can't be done without the decrypted
      40             :  * descriptor thus this function MUST be used to safe guard access to the
      41             :  * decrypted desc object. */
      42             : static inline bool
      43         116 : entry_has_decrypted_descriptor(const hs_cache_client_descriptor_t *entry)
      44             : {
      45         116 :   tor_assert(entry);
      46         116 :   return (entry->desc != NULL);
      47             : }
      48             : 
      49             : /********************** Directory HS cache ******************/
      50             : 
      51             : /** Directory descriptor cache. Map indexed by blinded key. */
      52             : static digest256map_t *hs_cache_v3_dir;
      53             : 
      54             : /** Remove a given descriptor from our cache. */
      55             : static void
      56           3 : remove_v3_desc_as_dir(const hs_cache_dir_descriptor_t *desc)
      57             : {
      58           3 :   tor_assert(desc);
      59           3 :   digest256map_remove(hs_cache_v3_dir, desc->key);
      60           3 : }
      61             : 
      62             : /** Store a given descriptor in our cache. */
      63             : static void
      64          13 : store_v3_desc_as_dir(hs_cache_dir_descriptor_t *desc)
      65             : {
      66          13 :   tor_assert(desc);
      67          13 :   digest256map_set(hs_cache_v3_dir, desc->key, desc);
      68          13 : }
      69             : 
      70             : /** Query our cache and return the entry or NULL if not found. */
      71             : static hs_cache_dir_descriptor_t *
      72          30 : lookup_v3_desc_as_dir(const uint8_t *key)
      73             : {
      74          30 :   tor_assert(key);
      75          30 :   return digest256map_get(hs_cache_v3_dir, key);
      76             : }
      77             : 
      78             : #define cache_dir_desc_free(val) \
      79             :   FREE_AND_NULL(hs_cache_dir_descriptor_t, cache_dir_desc_free_, (val))
      80             : 
      81             : /** Free a directory descriptor object. */
      82             : static void
      83          14 : cache_dir_desc_free_(hs_cache_dir_descriptor_t *desc)
      84             : {
      85          14 :   if (desc == NULL) {
      86             :     return;
      87             :   }
      88          12 :   hs_desc_plaintext_data_free(desc->plaintext_data);
      89          12 :   tor_free(desc->encoded_desc);
      90          12 :   tor_free(desc);
      91             : }
      92             : 
      93             : /** Helper function: Use by the free all function using the digest256map
      94             :  * interface to cache entries. */
      95             : static void
      96           0 : cache_dir_desc_free_void(void *ptr)
      97             : {
      98           0 :   cache_dir_desc_free_(ptr);
      99           0 : }
     100             : 
     101             : /** Create a new directory cache descriptor object from a encoded descriptor.
     102             :  * On success, return the heap-allocated cache object, otherwise return NULL if
     103             :  * we can't decode the descriptor. */
     104             : static hs_cache_dir_descriptor_t *
     105          18 : cache_dir_desc_new(const char *desc)
     106             : {
     107          18 :   hs_cache_dir_descriptor_t *dir_desc;
     108             : 
     109          18 :   tor_assert(desc);
     110             : 
     111          18 :   dir_desc = tor_malloc_zero(sizeof(hs_cache_dir_descriptor_t));
     112          36 :   dir_desc->plaintext_data =
     113          18 :     tor_malloc_zero(sizeof(hs_desc_plaintext_data_t));
     114          18 :   dir_desc->encoded_desc = tor_strdup(desc);
     115             : 
     116          18 :   if (hs_desc_decode_plaintext(desc, dir_desc->plaintext_data) < 0) {
     117           2 :     log_debug(LD_DIR, "Unable to decode descriptor. Rejecting.");
     118           2 :     goto err;
     119             :   }
     120             : 
     121             :   /* The blinded pubkey is the indexed key. */
     122          16 :   dir_desc->key = dir_desc->plaintext_data->blinded_pubkey.pubkey;
     123          16 :   dir_desc->created_ts = time(NULL);
     124          16 :   return dir_desc;
     125             : 
     126           2 :  err:
     127           2 :   cache_dir_desc_free(dir_desc);
     128           2 :   return NULL;
     129             : }
     130             : 
     131             : /** Return the size of a cache entry in bytes. */
     132             : static size_t
     133          20 : cache_get_dir_entry_size(const hs_cache_dir_descriptor_t *entry)
     134             : {
     135          20 :   return (sizeof(*entry) + hs_desc_plaintext_obj_size(entry->plaintext_data)
     136          20 :           + strlen(entry->encoded_desc));
     137             : }
     138             : 
     139             : /** Try to store a valid version 3 descriptor in the directory cache. Return 0
     140             :  * on success else a negative value is returned indicating that we have a
     141             :  * newer version in our cache. On error, caller is responsible to free the
     142             :  * given descriptor desc. */
     143             : static int
     144          16 : cache_store_v3_as_dir(hs_cache_dir_descriptor_t *desc)
     145             : {
     146          16 :   hs_cache_dir_descriptor_t *cache_entry;
     147             : 
     148          16 :   tor_assert(desc);
     149             : 
     150             :   /* Verify if we have an entry in the cache for that key and if yes, check
     151             :    * if we should replace it? */
     152          16 :   cache_entry = lookup_v3_desc_as_dir(desc->key);
     153          16 :   if (cache_entry != NULL) {
     154             :     /* Only replace descriptor if revision-counter is greater than the one
     155             :      * in our cache */
     156           6 :     if (cache_entry->plaintext_data->revision_counter >=
     157           6 :         desc->plaintext_data->revision_counter) {
     158           3 :       log_info(LD_REND, "Descriptor revision counter in our cache is "
     159             :                "greater or equal than the one we received (%d/%d). "
     160             :                "Rejecting!",
     161             :                (int)cache_entry->plaintext_data->revision_counter,
     162             :                (int)desc->plaintext_data->revision_counter);
     163           3 :       goto err;
     164             :     }
     165             :     /* We now know that the descriptor we just received is a new one so
     166             :      * remove the entry we currently have from our cache so we can then
     167             :      * store the new one. */
     168           3 :     remove_v3_desc_as_dir(cache_entry);
     169           3 :     hs_cache_decrement_allocation(cache_get_dir_entry_size(cache_entry));
     170           3 :     cache_dir_desc_free(cache_entry);
     171             :   }
     172             :   /* Store the descriptor we just got. We are sure here that either we
     173             :    * don't have the entry or we have a newer descriptor and the old one
     174             :    * has been removed from the cache. */
     175          13 :   store_v3_desc_as_dir(desc);
     176             : 
     177             :   /* Update our total cache size with this entry for the OOM. This uses the
     178             :    * old HS protocol cache subsystem for which we are tied with. */
     179          13 :   hs_cache_increment_allocation(cache_get_dir_entry_size(desc));
     180             : 
     181             :   /* Update HSv3 statistics */
     182          13 :   if (get_options()->HiddenServiceStatistics) {
     183           4 :     rep_hist_hsdir_stored_maybe_new_v3_onion(desc->key);
     184             :   }
     185             : 
     186             :   return 0;
     187             : 
     188           3 :  err:
     189           3 :   return -1;
     190             : }
     191             : 
     192             : /** Using the query which is the base64 encoded blinded key of a version 3
     193             :  * descriptor, lookup in our directory cache the entry. If found, 1 is
     194             :  * returned and desc_out is populated with a newly allocated string being the
     195             :  * encoded descriptor. If not found, 0 is returned and desc_out is untouched.
     196             :  * On error, a negative value is returned and desc_out is untouched. */
     197             : static int
     198          16 : cache_lookup_v3_as_dir(const char *query, const char **desc_out)
     199             : {
     200          16 :   int found = 0;
     201          16 :   ed25519_public_key_t blinded_key;
     202          16 :   const hs_cache_dir_descriptor_t *entry;
     203             : 
     204          16 :   tor_assert(query);
     205             : 
     206             :   /* Decode blinded key using the given query value. */
     207          16 :   if (ed25519_public_from_base64(&blinded_key, query) < 0) {
     208           2 :     log_info(LD_REND, "Unable to decode the v3 HSDir query %s.",
     209             :              safe_str_client(query));
     210           2 :     goto err;
     211             :   }
     212             : 
     213          14 :   entry = lookup_v3_desc_as_dir(blinded_key.pubkey);
     214          14 :   if (entry != NULL) {
     215           9 :     found = 1;
     216           9 :     if (desc_out) {
     217           7 :       *desc_out = entry->encoded_desc;
     218             :     }
     219             :   }
     220             : 
     221             :   return found;
     222             : 
     223           2 :  err:
     224           2 :   return -1;
     225             : }
     226             : 
     227             : /** Clean the v3 cache by removing any entry that has expired using the
     228             :  * <b>global_cutoff</b> value. If <b>global_cutoff</b> is 0, the cleaning
     229             :  * process will use the lifetime found in the plaintext data section. Return
     230             :  * the number of bytes cleaned. */
     231             : STATIC size_t
     232         151 : cache_clean_v3_as_dir(time_t now, time_t global_cutoff)
     233             : {
     234         151 :   size_t bytes_removed = 0;
     235             : 
     236             :   /* Code flow error if this ever happens. */
     237         151 :   tor_assert(global_cutoff >= 0);
     238             : 
     239         151 :   if (!hs_cache_v3_dir) { /* No cache to clean. Just return. */
     240             :     return 0;
     241             :   }
     242             : 
     243         307 :   DIGEST256MAP_FOREACH_MODIFY(hs_cache_v3_dir, key,
     244             :                               hs_cache_dir_descriptor_t *, entry) {
     245         152 :     size_t entry_size;
     246         152 :     time_t cutoff = global_cutoff;
     247         152 :     if (!cutoff) {
     248             :       /* Cutoff is the lifetime of the entry found in the descriptor. */
     249           4 :       cutoff = now - entry->plaintext_data->lifetime_sec;
     250             :     }
     251             : 
     252             :     /* If the entry has been created _after_ the cutoff, not expired so
     253             :      * continue to the next entry in our v3 cache. */
     254         152 :     if (entry->created_ts > cutoff) {
     255         148 :       continue;
     256             :     }
     257             :     /* Here, our entry has expired, remove and free. */
     258           4 :     MAP_DEL_CURRENT(key);
     259           4 :     entry_size = cache_get_dir_entry_size(entry);
     260           4 :     bytes_removed += entry_size;
     261             :     /* Entry is not in the cache anymore, destroy it. */
     262           4 :     cache_dir_desc_free(entry);
     263             :     /* Update our cache entry allocation size for the OOM. */
     264           4 :     hs_cache_decrement_allocation(entry_size);
     265             :     /* Logging. */
     266             :     {
     267           4 :       char key_b64[BASE64_DIGEST256_LEN + 1];
     268           4 :       digest256_to_base64(key_b64, (const char *) key);
     269           4 :       log_info(LD_REND, "Removing v3 descriptor '%s' from HSDir cache",
     270             :                safe_str_client(key_b64));
     271             :     }
     272             :   } DIGEST256MAP_FOREACH_END;
     273             : 
     274             :   return bytes_removed;
     275             : }
     276             : 
     277             : /** Given an encoded descriptor, store it in the directory cache depending on
     278             :  * which version it is. Return a negative value on error. On success, 0 is
     279             :  * returned. */
     280             : int
     281          18 : hs_cache_store_as_dir(const char *desc)
     282             : {
     283          18 :   hs_cache_dir_descriptor_t *dir_desc = NULL;
     284             : 
     285          18 :   tor_assert(desc);
     286             : 
     287             :   /* Create a new cache object. This can fail if the descriptor plaintext data
     288             :    * is unparseable which in this case a log message will be triggered. */
     289          18 :   dir_desc = cache_dir_desc_new(desc);
     290          18 :   if (dir_desc == NULL) {
     291           2 :     goto err;
     292             :   }
     293             : 
     294             :   /* Call the right function against the descriptor version. At this point,
     295             :    * we are sure that the descriptor's version is supported else the
     296             :    * decoding would have failed. */
     297          16 :   switch (dir_desc->plaintext_data->version) {
     298             :   case HS_VERSION_THREE:
     299             :   default:
     300          16 :     if (cache_store_v3_as_dir(dir_desc) < 0) {
     301           3 :       goto err;
     302             :     }
     303             :     break;
     304             :   }
     305             :   return 0;
     306             : 
     307           5 :  err:
     308           5 :   cache_dir_desc_free(dir_desc);
     309           5 :   return -1;
     310             : }
     311             : 
     312             : /** Using the query, lookup in our directory cache the entry. If found, 1 is
     313             :  * returned and desc_out is populated with a newly allocated string being
     314             :  * the encoded descriptor. If not found, 0 is returned and desc_out is
     315             :  * untouched. On error, a negative value is returned and desc_out is
     316             :  * untouched. */
     317             : int
     318          16 : hs_cache_lookup_as_dir(uint32_t version, const char *query,
     319             :                        const char **desc_out)
     320             : {
     321          16 :   int found;
     322             : 
     323          16 :   tor_assert(query);
     324             :   /* This should never be called with an unsupported version. */
     325          16 :   tor_assert(hs_desc_is_supported_version(version));
     326             : 
     327          16 :   switch (version) {
     328             :   case HS_VERSION_THREE:
     329             :   default:
     330          16 :     found = cache_lookup_v3_as_dir(query, desc_out);
     331          16 :     break;
     332             :   }
     333             : 
     334          16 :   return found;
     335             : }
     336             : 
     337             : /** Clean all directory caches using the current time now. */
     338             : void
     339           2 : hs_cache_clean_as_dir(time_t now)
     340             : {
     341             :   /* Now, clean the v3 cache. Set the cutoff to 0 telling the cleanup function
     342             :    * to compute the cutoff by itself using the lifetime value. */
     343           2 :   cache_clean_v3_as_dir(now, 0);
     344           2 : }
     345             : 
     346             : /********************** Client-side HS cache ******************/
     347             : 
     348             : /** Client-side HS descriptor cache. Map indexed by service identity key. */
     349             : static digest256map_t *hs_cache_v3_client;
     350             : 
     351             : /** Client-side introduction point state cache. Map indexed by service public
     352             :  * identity key (onion address). It contains hs_cache_client_intro_state_t
     353             :  * objects all related to a specific service. */
     354             : static digest256map_t *hs_cache_client_intro_state;
     355             : 
     356             : #define cache_client_desc_free(val) \
     357             :   FREE_AND_NULL(hs_cache_client_descriptor_t, cache_client_desc_free_, (val))
     358             : 
     359             : /** Free memory allocated by <b>desc</b>. */
     360             : static void
     361          12 : cache_client_desc_free_(hs_cache_client_descriptor_t *desc)
     362             : {
     363          12 :   if (desc == NULL) {
     364             :     return;
     365             :   }
     366          12 :   hs_descriptor_free(desc->desc);
     367          12 :   memwipe(&desc->key, 0, sizeof(desc->key));
     368          12 :   memwipe(desc->encoded_desc, 0, strlen(desc->encoded_desc));
     369          12 :   tor_free(desc->encoded_desc);
     370          12 :   tor_free(desc);
     371             : }
     372             : 
     373             : /** Helper function: Use by the free all function to clear the client cache */
     374             : static void
     375           4 : cache_client_desc_free_void(void *ptr)
     376             : {
     377           4 :   hs_cache_client_descriptor_t *desc = ptr;
     378           4 :   cache_client_desc_free(desc);
     379           4 : }
     380             : 
     381             : /** Return the size of a client cache entry in bytes. */
     382             : static size_t
     383          22 : cache_get_client_entry_size(const hs_cache_client_descriptor_t *entry)
     384             : {
     385          22 :   size_t size = 0;
     386             : 
     387          22 :   if (entry == NULL) {
     388           0 :     goto end;
     389             :   }
     390          22 :   size += sizeof(*entry);
     391             : 
     392          22 :   if (entry->encoded_desc) {
     393          22 :     size += strlen(entry->encoded_desc);
     394             :   }
     395             : 
     396          22 :   if (entry_has_decrypted_descriptor(entry)) {
     397          16 :     size += hs_desc_obj_size(entry->desc);
     398             :   }
     399             : 
     400           6 :  end:
     401          22 :   return size;
     402             : }
     403             : 
     404             : /** Remove a given descriptor from our cache. */
     405             : static void
     406           5 : remove_v3_desc_as_client(const hs_cache_client_descriptor_t *desc)
     407             : {
     408           5 :   tor_assert(desc);
     409           5 :   digest256map_remove(hs_cache_v3_client, desc->key.pubkey);
     410             :   /* Update cache size with this entry for the OOM handler. */
     411           5 :   hs_cache_decrement_allocation(cache_get_client_entry_size(desc));
     412           5 : }
     413             : 
     414             : /** Store a given descriptor in our cache. */
     415             : static void
     416          14 : store_v3_desc_as_client(hs_cache_client_descriptor_t *desc)
     417             : {
     418          14 :   hs_cache_client_descriptor_t *cached_desc;
     419             : 
     420          14 :   tor_assert(desc);
     421             : 
     422             :   /* Because the lookup function doesn't return an expired entry, it can linger
     423             :    * in the cache until we clean it up or a new descriptor is stored. So,
     424             :    * before adding, we'll make sure we are not overwriting an old descriptor
     425             :    * (which is OK in terms of semantic) but leads to memory leak. */
     426          14 :   cached_desc = digest256map_get(hs_cache_v3_client, desc->key.pubkey);
     427          14 :   if (cached_desc) {
     428           0 :     cache_client_desc_free(cached_desc);
     429             :   }
     430          14 :   digest256map_set(hs_cache_v3_client, desc->key.pubkey, desc);
     431             :   /* Update cache size with this entry for the OOM handler. */
     432          14 :   hs_cache_increment_allocation(cache_get_client_entry_size(desc));
     433          14 : }
     434             : 
     435             : /** Query our cache and return the entry or NULL if not found or if expired. */
     436             : STATIC hs_cache_client_descriptor_t *
     437         119 : lookup_v3_desc_as_client(const uint8_t *key)
     438             : {
     439         119 :   time_t now = approx_time();
     440         119 :   hs_cache_client_descriptor_t *cached_desc;
     441             : 
     442         119 :   tor_assert(key);
     443             : 
     444             :   /* Do the lookup */
     445         119 :   cached_desc = digest256map_get(hs_cache_v3_client, key);
     446         119 :   if (!cached_desc) {
     447             :     return NULL;
     448             :   }
     449             : 
     450             :   /* Don't return expired entries */
     451          93 :   if (cached_client_descriptor_has_expired(now, cached_desc)) {
     452           1 :     return NULL;
     453             :   }
     454             : 
     455             :   return cached_desc;
     456             : }
     457             : 
     458             : /** Parse the encoded descriptor in <b>desc_str</b> using
     459             :  * <b>service_identity_pk</b> to decrypt it first.
     460             :  *
     461             :  * If everything goes well, allocate and return a new
     462             :  * hs_cache_client_descriptor_t object. In case of error, return NULL. */
     463             : static hs_cache_client_descriptor_t *
     464          14 : cache_client_desc_new(const char *desc_str,
     465             :                       const ed25519_public_key_t *service_identity_pk,
     466             :                       hs_desc_decode_status_t *decode_status_out)
     467             : {
     468          14 :   hs_desc_decode_status_t ret;
     469          14 :   hs_descriptor_t *desc = NULL;
     470          14 :   hs_cache_client_descriptor_t *client_desc = NULL;
     471             : 
     472          14 :   tor_assert(desc_str);
     473          14 :   tor_assert(service_identity_pk);
     474             : 
     475             :   /* Decode the descriptor we just fetched. */
     476          14 :   ret = hs_client_decode_descriptor(desc_str, service_identity_pk, &desc);
     477          14 :   if (ret != HS_DESC_DECODE_OK &&
     478          14 :       ret != HS_DESC_DECODE_NEED_CLIENT_AUTH &&
     479             :       ret != HS_DESC_DECODE_BAD_CLIENT_AUTH) {
     480             :     /* In the case of a missing or bad client authorization, we'll keep the
     481             :      * descriptor in the cache because those credentials can arrive later. */
     482           0 :     goto end;
     483             :   }
     484             :   /* Make sure we do have a descriptor if decoding was successful. */
     485          14 :   if (ret == HS_DESC_DECODE_OK) {
     486          10 :     tor_assert(desc);
     487             :   } else {
     488           4 :     if (BUG(desc != NULL)) {
     489             :       /* We are not suppose to have a descriptor if the decoding code is not
     490             :        * indicating success. Just in case, bail early to recover. */
     491           0 :       goto end;
     492             :     }
     493             :   }
     494             : 
     495             :   /* All is good: make a cache object for this descriptor */
     496          14 :   client_desc = tor_malloc_zero(sizeof(hs_cache_client_descriptor_t));
     497          14 :   ed25519_pubkey_copy(&client_desc->key, service_identity_pk);
     498             :   /* Set expiration time for this cached descriptor to be the start of the next
     499             :    * time period since that's when clients need to start using the next blinded
     500             :    * pk of the service (and hence will need its next descriptor). */
     501          14 :   client_desc->expiration_ts = hs_get_start_time_of_next_time_period(0);
     502          14 :   client_desc->desc = desc;
     503          14 :   client_desc->encoded_desc = tor_strdup(desc_str);
     504             : 
     505          14 :  end:
     506          14 :   if (decode_status_out) {
     507          14 :     *decode_status_out = ret;
     508             :   }
     509          14 :   return client_desc;
     510             : }
     511             : 
     512             : /** Return a newly allocated and initialized hs_cache_intro_state_t object. */
     513             : static hs_cache_intro_state_t *
     514          14 : cache_intro_state_new(void)
     515             : {
     516          14 :   hs_cache_intro_state_t *state = tor_malloc_zero(sizeof(*state));
     517          14 :   state->created_ts = approx_time();
     518          14 :   return state;
     519             : }
     520             : 
     521             : #define cache_intro_state_free(val) \
     522             :   FREE_AND_NULL(hs_cache_intro_state_t, cache_intro_state_free_, (val))
     523             : 
     524             : /** Free an hs_cache_intro_state_t object. */
     525             : static void
     526          14 : cache_intro_state_free_(hs_cache_intro_state_t *state)
     527             : {
     528          14 :   tor_free(state);
     529          14 : }
     530             : 
     531             : /** Helper function: used by the free all function. */
     532             : static void
     533          10 : cache_intro_state_free_void(void *state)
     534             : {
     535          10 :   cache_intro_state_free_(state);
     536          10 : }
     537             : 
     538             : /** Return a newly allocated and initialized hs_cache_client_intro_state_t
     539             :  * object. */
     540             : static hs_cache_client_intro_state_t *
     541           5 : cache_client_intro_state_new(void)
     542             : {
     543           5 :   hs_cache_client_intro_state_t *cache = tor_malloc_zero(sizeof(*cache));
     544           5 :   cache->intro_points = digest256map_new();
     545           5 :   return cache;
     546             : }
     547             : 
     548             : #define cache_client_intro_state_free(val)              \
     549             :   FREE_AND_NULL(hs_cache_client_intro_state_t,          \
     550             :                 cache_client_intro_state_free_, (val))
     551             : 
     552             : /** Free a cache_client_intro_state object. */
     553             : static void
     554           5 : cache_client_intro_state_free_(hs_cache_client_intro_state_t *cache)
     555             : {
     556           5 :   if (cache == NULL) {
     557             :     return;
     558             :   }
     559           5 :   digest256map_free(cache->intro_points, cache_intro_state_free_void);
     560           5 :   tor_free(cache);
     561             : }
     562             : 
     563             : /** Helper function: used by the free all function. */
     564             : static void
     565           1 : cache_client_intro_state_free_void(void *entry)
     566             : {
     567           1 :   cache_client_intro_state_free_(entry);
     568           1 : }
     569             : 
     570             : /** For the given service identity key service_pk and an introduction
     571             :  * authentication key auth_key, lookup the intro state object. Return 1 if
     572             :  * found and put it in entry if not NULL. Return 0 if not found and entry is
     573             :  * untouched. */
     574             : static int
     575         283 : cache_client_intro_state_lookup(const ed25519_public_key_t *service_pk,
     576             :                                 const ed25519_public_key_t *auth_key,
     577             :                                 hs_cache_intro_state_t **entry)
     578             : {
     579         283 :   hs_cache_intro_state_t *state;
     580         283 :   hs_cache_client_intro_state_t *cache;
     581             : 
     582         283 :   tor_assert(service_pk);
     583         283 :   tor_assert(auth_key);
     584             : 
     585             :   /* Lookup the intro state cache for this service key. */
     586         283 :   cache = digest256map_get(hs_cache_client_intro_state, service_pk->pubkey);
     587         283 :   if (cache == NULL) {
     588          16 :     goto not_found;
     589             :   }
     590             : 
     591             :   /* From the cache we just found for the service, lookup in the introduction
     592             :    * points map for the given authentication key. */
     593         267 :   state = digest256map_get(cache->intro_points, auth_key->pubkey);
     594         267 :   if (state == NULL) {
     595         137 :     goto not_found;
     596             :   }
     597         130 :   if (entry) {
     598         130 :     *entry = state;
     599             :   }
     600             :   return 1;
     601             :  not_found:
     602             :   return 0;
     603             : }
     604             : 
     605             : /** Note the given failure in state. */
     606             : static void
     607          18 : cache_client_intro_state_note(hs_cache_intro_state_t *state,
     608             :                               rend_intro_point_failure_t failure)
     609             : {
     610          18 :   tor_assert(state);
     611          18 :   switch (failure) {
     612          11 :   case INTRO_POINT_FAILURE_GENERIC:
     613          11 :     state->error = 1;
     614          11 :     break;
     615           6 :   case INTRO_POINT_FAILURE_TIMEOUT:
     616           6 :     state->timed_out = 1;
     617           6 :     break;
     618           1 :   case INTRO_POINT_FAILURE_UNREACHABLE:
     619           1 :     state->unreachable_count++;
     620           1 :     break;
     621           0 :   default:
     622           0 :     tor_assert_nonfatal_unreached();
     623           0 :     return;
     624             :   }
     625             : }
     626             : 
     627             : /** For the given service identity key service_pk and an introduction
     628             :  * authentication key auth_key, add an entry in the client intro state cache
     629             :  * If no entry exists for the service, it will create one. If state is non
     630             :  * NULL, it will point to the new intro state entry. */
     631             : static void
     632          14 : cache_client_intro_state_add(const ed25519_public_key_t *service_pk,
     633             :                              const ed25519_public_key_t *auth_key,
     634             :                              hs_cache_intro_state_t **state)
     635             : {
     636          14 :   hs_cache_intro_state_t *entry, *old_entry;
     637          14 :   hs_cache_client_intro_state_t *cache;
     638             : 
     639          14 :   tor_assert(service_pk);
     640          14 :   tor_assert(auth_key);
     641             : 
     642             :   /* Lookup the state cache for this service key. */
     643          14 :   cache = digest256map_get(hs_cache_client_intro_state, service_pk->pubkey);
     644          14 :   if (cache == NULL) {
     645           5 :     cache = cache_client_intro_state_new();
     646           5 :     digest256map_set(hs_cache_client_intro_state, service_pk->pubkey, cache);
     647             :   }
     648             : 
     649          14 :   entry = cache_intro_state_new();
     650          14 :   old_entry = digest256map_set(cache->intro_points, auth_key->pubkey, entry);
     651             :   /* This should never happened because the code flow is to lookup the entry
     652             :    * before adding it. But, just in case, non fatal assert and free it. */
     653          14 :   tor_assert_nonfatal(old_entry == NULL);
     654          14 :   tor_free(old_entry);
     655             : 
     656          14 :   if (state) {
     657          14 :     *state = entry;
     658             :   }
     659          14 : }
     660             : 
     661             : /** Remove every intro point state entry from cache that has been created
     662             :  * before or at the cutoff. */
     663             : static void
     664           1 : cache_client_intro_state_clean(time_t cutoff,
     665             :                                hs_cache_client_intro_state_t *cache)
     666             : {
     667           1 :   tor_assert(cache);
     668             : 
     669           5 :   DIGEST256MAP_FOREACH_MODIFY(cache->intro_points, key,
     670             :                               hs_cache_intro_state_t *, entry) {
     671           4 :     if (entry->created_ts <= cutoff) {
     672           4 :       cache_intro_state_free(entry);
     673           4 :       MAP_DEL_CURRENT(key);
     674             :     }
     675           1 :   } DIGEST256MAP_FOREACH_END;
     676           1 : }
     677             : 
     678             : /** Return true iff no intro points are in this cache. */
     679             : static int
     680           1 : cache_client_intro_state_is_empty(const hs_cache_client_intro_state_t *cache)
     681             : {
     682           1 :   return digest256map_isempty(cache->intro_points);
     683             : }
     684             : 
     685             : /** Check whether <b>client_desc</b> is useful for us, and store it in the
     686             :  *  client-side HS cache if so. The client_desc is freed if we already have a
     687             :  *  fresher (higher revision counter count) in the cache. */
     688             : static int
     689          14 : cache_store_as_client(hs_cache_client_descriptor_t *client_desc)
     690             : {
     691          14 :   hs_cache_client_descriptor_t *cache_entry;
     692             : 
     693             :   /* TODO: Heavy code duplication with cache_store_as_dir(). Consider
     694             :    * refactoring and uniting! */
     695             : 
     696          14 :   tor_assert(client_desc);
     697             : 
     698             :   /* Check if we already have a descriptor from this HS in cache. If we do,
     699             :    * check if this descriptor is newer than the cached one only if we have a
     700             :    * decoded descriptor. We do keep non-decoded descriptor that requires
     701             :    * client authorization. */
     702          14 :   cache_entry = lookup_v3_desc_as_client(client_desc->key.pubkey);
     703          14 :   if (cache_entry != NULL) {
     704             :     /* If the current or the new cache entry don't have a decrypted descriptor
     705             :      * (missing client authorization), we always replace the current one with
     706             :      * the new one. Reason is that we can't inspect the revision counter
     707             :      * within the plaintext data so we blindly replace. */
     708           6 :     if (!entry_has_decrypted_descriptor(cache_entry) ||
     709           2 :         !entry_has_decrypted_descriptor(client_desc)) {
     710           2 :       remove_v3_desc_as_client(cache_entry);
     711           2 :       cache_client_desc_free(cache_entry);
     712           2 :       goto store;
     713             :     }
     714             : 
     715             :     /* From this point on, we know that the decrypted descriptor is in the
     716             :      * current entry and new object thus safe to access. */
     717             : 
     718             :     /* If we have an entry in our cache that has a revision counter greater
     719             :      * than the one we just fetched, discard the one we fetched. */
     720           2 :     if (cache_entry->desc->plaintext_data.revision_counter >
     721           2 :         client_desc->desc->plaintext_data.revision_counter) {
     722           0 :       cache_client_desc_free(client_desc);
     723           0 :       goto done;
     724             :     }
     725             :     /* Remove old entry. Make space for the new one! */
     726           2 :     remove_v3_desc_as_client(cache_entry);
     727             : 
     728             :     /* We just removed an old descriptor and will replace it. We'll close all
     729             :      * intro circuits related to this old one so we don't have leftovers. We
     730             :      * leave the rendezvous circuits opened because they could be in use. */
     731           2 :     hs_client_close_intro_circuits_from_desc(cache_entry->desc);
     732             : 
     733             :     /* Free it. */
     734           2 :     cache_client_desc_free(cache_entry);
     735             :   }
     736             : 
     737          10 :  store:
     738             :   /* Store descriptor in cache */
     739          14 :   store_v3_desc_as_client(client_desc);
     740             : 
     741          14 :  done:
     742          14 :   return 0;
     743             : }
     744             : 
     745             : /** Return true iff the cached client descriptor at <b>cached_desc</b> has
     746             :  * expired. */
     747             : static int
     748          95 : cached_client_descriptor_has_expired(time_t now,
     749             :                                const hs_cache_client_descriptor_t *cached_desc)
     750             : {
     751             :   /* We use the current consensus time to see if we should expire this
     752             :    * descriptor since we use consensus time for all other parts of the protocol
     753             :    * as well (e.g. to build the blinded key and compute time periods). */
     754          95 :   const networkstatus_t *ns =
     755          95 :     networkstatus_get_reasonably_live_consensus(now,
     756             :       usable_consensus_flavor());
     757             :   /* If we don't have a recent consensus, consider this entry expired since we
     758             :    * will want to fetch a new HS desc when we get a live consensus. */
     759          95 :   if (!ns) {
     760             :     return 1;
     761             :   }
     762             : 
     763          95 :   if (cached_desc->expiration_ts <= ns->valid_after) {
     764           2 :     return 1;
     765             :   }
     766             : 
     767             :   return 0;
     768             : }
     769             : 
     770             : /** clean the client cache using now as the current time. Return the total size
     771             :  * of removed bytes from the cache. */
     772             : static size_t
     773           2 : cache_clean_v3_as_client(time_t now)
     774             : {
     775           2 :   size_t bytes_removed = 0;
     776             : 
     777           2 :   if (!hs_cache_v3_client) { /* No cache to clean. Just return. */
     778             :     return 0;
     779             :   }
     780             : 
     781           5 :   DIGEST256MAP_FOREACH_MODIFY(hs_cache_v3_client, key,
     782             :                               hs_cache_client_descriptor_t *, entry) {
     783           2 :     size_t entry_size;
     784             : 
     785             :     /* If the entry has not expired, continue to the next cached entry */
     786           2 :     if (!cached_client_descriptor_has_expired(now, entry)) {
     787           1 :       continue;
     788             :     }
     789             :     /* Here, our entry has expired, remove and free. */
     790           1 :     MAP_DEL_CURRENT(key);
     791           1 :     entry_size = cache_get_client_entry_size(entry);
     792           1 :     bytes_removed += entry_size;
     793             : 
     794             :     /* We just removed an old descriptor. We need to close all intro circuits
     795             :      * if the descriptor is decrypted so we don't have leftovers that can be
     796             :      * selected while lacking a descriptor. Circuits are selected by intro
     797             :      * authentication key thus we need the descriptor. We leave the rendezvous
     798             :      * circuits opened because they could be in use. */
     799           1 :     if (entry_has_decrypted_descriptor(entry)) {
     800           1 :       hs_client_close_intro_circuits_from_desc(entry->desc);
     801             :     }
     802             :     /* Entry is not in the cache anymore, destroy it. */
     803           1 :     cache_client_desc_free(entry);
     804             :     /* Update our OOM. We didn't use the remove() function because we are in
     805             :      * a loop so we have to explicitly decrement. */
     806           1 :     hs_cache_decrement_allocation(entry_size);
     807             :     /* Logging. */
     808             :     {
     809           1 :       char key_b64[BASE64_DIGEST256_LEN + 1];
     810           1 :       digest256_to_base64(key_b64, (const char *) key);
     811           1 :       log_info(LD_REND, "Removing hidden service v3 descriptor '%s' "
     812             :                         "from client cache",
     813             :                safe_str_client(key_b64));
     814             :     }
     815             :   } DIGEST256MAP_FOREACH_END;
     816             : 
     817             :   return bytes_removed;
     818             : }
     819             : 
     820             : /** Public API: Given the HS ed25519 identity public key in <b>key</b>, return
     821             :  *  its HS encoded descriptor if it's stored in our cache, or NULL if not. */
     822             : const char *
     823           1 : hs_cache_lookup_encoded_as_client(const ed25519_public_key_t *key)
     824             : {
     825           1 :   hs_cache_client_descriptor_t *cached_desc = NULL;
     826             : 
     827           1 :   tor_assert(key);
     828             : 
     829           1 :   cached_desc = lookup_v3_desc_as_client(key->pubkey);
     830           1 :   if (cached_desc) {
     831           1 :     tor_assert(cached_desc->encoded_desc);
     832             :     return cached_desc->encoded_desc;
     833             :   }
     834             : 
     835             :   return NULL;
     836             : }
     837             : 
     838             : /** Public API: Given the HS ed25519 identity public key in <b>key</b>, return
     839             :  *  its HS descriptor if it's stored in our cache, or NULL if not or if the
     840             :  *  descriptor was never decrypted. The later can happen if we are waiting for
     841             :  *  client authorization to be added. */
     842             : const hs_descriptor_t *
     843          90 : hs_cache_lookup_as_client(const ed25519_public_key_t *key)
     844             : {
     845          90 :   hs_cache_client_descriptor_t *cached_desc = NULL;
     846             : 
     847          90 :   tor_assert(key);
     848             : 
     849          90 :   cached_desc = lookup_v3_desc_as_client(key->pubkey);
     850          90 :   if (cached_desc && entry_has_decrypted_descriptor(cached_desc)) {
     851          83 :     return cached_desc->desc;
     852             :   }
     853             : 
     854             :   return NULL;
     855             : }
     856             : 
     857             : /** Public API: Given an encoded descriptor, store it in the client HS cache.
     858             :  *  Return a decode status which changes how we handle the SOCKS connection
     859             :  *  depending on its value:
     860             :  *
     861             :  *  HS_DESC_DECODE_OK: Returned on success. Descriptor was properly decoded
     862             :  *                     and is now stored.
     863             :  *
     864             :  *  HS_DESC_DECODE_NEED_CLIENT_AUTH: Client authorization is needed but the
     865             :  *                                   descriptor was still stored.
     866             :  *
     867             :  *  HS_DESC_DECODE_BAD_CLIENT_AUTH: Client authorization for this descriptor
     868             :  *                                  was not usable but the descriptor was
     869             :  *                                  still stored.
     870             :  *
     871             :  *  Any other codes means indicate where the error occurred and the descriptor
     872             :  *  was not stored. */
     873             : hs_desc_decode_status_t
     874          14 : hs_cache_store_as_client(const char *desc_str,
     875             :                          const ed25519_public_key_t *identity_pk)
     876             : {
     877          14 :   hs_desc_decode_status_t ret;
     878          14 :   hs_cache_client_descriptor_t *client_desc = NULL;
     879             : 
     880          14 :   tor_assert(desc_str);
     881          14 :   tor_assert(identity_pk);
     882             : 
     883             :   /* Create client cache descriptor object */
     884          14 :   client_desc = cache_client_desc_new(desc_str, identity_pk, &ret);
     885          14 :   if (!client_desc) {
     886           0 :     log_warn(LD_GENERAL, "HSDesc parsing failed!");
     887           0 :     log_debug(LD_GENERAL, "Failed to parse HSDesc: %s.", escaped(desc_str));
     888           0 :     goto err;
     889             :   }
     890             : 
     891             :   /* Push it to the cache */
     892          14 :   if (cache_store_as_client(client_desc) < 0) {
     893           0 :     ret = HS_DESC_DECODE_GENERIC_ERROR;
     894           0 :     goto err;
     895             :   }
     896             : 
     897          14 :   return ret;
     898             : 
     899           0 :  err:
     900           0 :   cache_client_desc_free(client_desc);
     901           0 :   return ret;
     902             : }
     903             : 
     904             : /** Remove and free a client cache descriptor entry for the given onion
     905             :  * service ed25519 public key. If the descriptor is decoded, the intro
     906             :  * circuits are closed if any.
     907             :  *
     908             :  * This does nothing if no descriptor exists for the given key. */
     909             : void
     910           5 : hs_cache_remove_as_client(const ed25519_public_key_t *key)
     911             : {
     912           5 :   hs_cache_client_descriptor_t *cached_desc = NULL;
     913             : 
     914           5 :   tor_assert(key);
     915             : 
     916           5 :   cached_desc = lookup_v3_desc_as_client(key->pubkey);
     917           5 :   if (!cached_desc) {
     918           5 :     return;
     919             :   }
     920             :   /* If we have a decrypted/decoded descriptor, attempt to close its
     921             :    * introduction circuit(s). We shouldn't have circuit(s) without a
     922             :    * descriptor else it will lead to a failure. */
     923           1 :   if (entry_has_decrypted_descriptor(cached_desc)) {
     924           1 :     hs_client_close_intro_circuits_from_desc(cached_desc->desc);
     925             :   }
     926             :   /* Remove and free. */
     927           1 :   remove_v3_desc_as_client(cached_desc);
     928           1 :   cache_client_desc_free(cached_desc);
     929             : 
     930             :   /* Logging. */
     931             :   {
     932           1 :     char key_b64[BASE64_DIGEST256_LEN + 1];
     933           1 :     digest256_to_base64(key_b64, (const char *) key);
     934           1 :     log_info(LD_REND, "Onion service v3 descriptor '%s' removed "
     935             :                       "from client cache",
     936             :              safe_str_client(key_b64));
     937             :   }
     938             : }
     939             : 
     940             : /** Clean all client caches using the current time now. */
     941             : void
     942           2 : hs_cache_clean_as_client(time_t now)
     943             : {
     944             :   /* Now, clean the v3 cache. Set the cutoff to 0 telling the cleanup function
     945             :    * to compute the cutoff by itself using the lifetime value. */
     946           2 :   cache_clean_v3_as_client(now);
     947           2 : }
     948             : 
     949             : /** Purge the client descriptor cache. */
     950             : void
     951           2 : hs_cache_purge_as_client(void)
     952             : {
     953           4 :   DIGEST256MAP_FOREACH_MODIFY(hs_cache_v3_client, key,
     954             :                               hs_cache_client_descriptor_t *, entry) {
     955           2 :     size_t entry_size = cache_get_client_entry_size(entry);
     956           2 :     MAP_DEL_CURRENT(key);
     957           2 :     cache_client_desc_free(entry);
     958             :     /* Update our OOM. We didn't use the remove() function because we are in
     959             :      * a loop so we have to explicitly decrement. */
     960           2 :     hs_cache_decrement_allocation(entry_size);
     961           2 :   } DIGEST256MAP_FOREACH_END;
     962             : 
     963           2 :   log_info(LD_REND, "Hidden service client descriptor cache purged.");
     964           2 : }
     965             : 
     966             : /** For a given service identity public key and an introduction authentication
     967             :  * key, note the given failure in the client intro state cache. */
     968             : void
     969          18 : hs_cache_client_intro_state_note(const ed25519_public_key_t *service_pk,
     970             :                                  const ed25519_public_key_t *auth_key,
     971             :                                  rend_intro_point_failure_t failure)
     972             : {
     973          18 :   int found;
     974          18 :   hs_cache_intro_state_t *entry;
     975             : 
     976          18 :   tor_assert(service_pk);
     977          18 :   tor_assert(auth_key);
     978             : 
     979          18 :   found = cache_client_intro_state_lookup(service_pk, auth_key, &entry);
     980          18 :   if (!found) {
     981             :     /* Create a new entry and add it to the cache. */
     982          14 :     cache_client_intro_state_add(service_pk, auth_key, &entry);
     983             :   }
     984             :   /* Note down the entry. */
     985          18 :   cache_client_intro_state_note(entry, failure);
     986          18 : }
     987             : 
     988             : /** For a given service identity public key and an introduction authentication
     989             :  * key, return true iff it is present in the failure cache. */
     990             : const hs_cache_intro_state_t *
     991         265 : hs_cache_client_intro_state_find(const ed25519_public_key_t *service_pk,
     992             :                                  const ed25519_public_key_t *auth_key)
     993             : {
     994         265 :   hs_cache_intro_state_t *state = NULL;
     995         265 :   cache_client_intro_state_lookup(service_pk, auth_key, &state);
     996         265 :   return state;
     997             : }
     998             : 
     999             : /** Cleanup the client introduction state cache. */
    1000             : void
    1001           1 : hs_cache_client_intro_state_clean(time_t now)
    1002             : {
    1003           1 :   time_t cutoff = now - HS_CACHE_CLIENT_INTRO_STATE_MAX_AGE;
    1004             : 
    1005           2 :   DIGEST256MAP_FOREACH_MODIFY(hs_cache_client_intro_state, key,
    1006             :                               hs_cache_client_intro_state_t *, cache) {
    1007             :     /* Cleanup intro points failure. */
    1008           1 :     cache_client_intro_state_clean(cutoff, cache);
    1009             : 
    1010             :     /* Is this cache empty for this service key? If yes, remove it from the
    1011             :      * cache. Else keep it. */
    1012           1 :     if (cache_client_intro_state_is_empty(cache)) {
    1013           1 :       cache_client_intro_state_free(cache);
    1014           1 :       MAP_DEL_CURRENT(key);
    1015             :     }
    1016           1 :   } DIGEST256MAP_FOREACH_END;
    1017           1 : }
    1018             : 
    1019             : /** Purge the client introduction state cache. */
    1020             : void
    1021           3 : hs_cache_client_intro_state_purge(void)
    1022             : {
    1023           6 :   DIGEST256MAP_FOREACH_MODIFY(hs_cache_client_intro_state, key,
    1024             :                               hs_cache_client_intro_state_t *, cache) {
    1025           3 :     MAP_DEL_CURRENT(key);
    1026           3 :     cache_client_intro_state_free(cache);
    1027           3 :   } DIGEST256MAP_FOREACH_END;
    1028             : 
    1029           3 :   log_info(LD_REND, "Hidden service client introduction point state "
    1030             :                     "cache purged.");
    1031           3 : }
    1032             : 
    1033             : /* This is called when new client authorization was added to the global state.
    1034             :  * It attempts to decode the descriptor of the given service identity key.
    1035             :  *
    1036             :  * Return true if decoding was successful else false. */
    1037             : bool
    1038           9 : hs_cache_client_new_auth_parse(const ed25519_public_key_t *service_pk)
    1039             : {
    1040           9 :   bool ret = false;
    1041           9 :   hs_cache_client_descriptor_t *cached_desc = NULL;
    1042             : 
    1043           9 :   tor_assert(service_pk);
    1044             : 
    1045           9 :   if (!hs_cache_v3_client) {
    1046             :     return false;
    1047             :   }
    1048             : 
    1049           9 :   cached_desc = lookup_v3_desc_as_client(service_pk->pubkey);
    1050           9 :   if (cached_desc == NULL || entry_has_decrypted_descriptor(cached_desc)) {
    1051             :     /* No entry for that service or the descriptor is already decoded. */
    1052           7 :     goto end;
    1053             :   }
    1054             : 
    1055             :   /* Attempt a decode. If we are successful, inform the caller. */
    1056           2 :   if (hs_client_decode_descriptor(cached_desc->encoded_desc, service_pk,
    1057             :                                   &cached_desc->desc) == HS_DESC_DECODE_OK) {
    1058           1 :     ret = true;
    1059             :   }
    1060             : 
    1061           1 :  end:
    1062             :   return ret;
    1063             : }
    1064             : 
    1065             : /**************** Generics *********************************/
    1066             : 
    1067             : /** Do a round of OOM cleanup on all directory caches. Return the amount of
    1068             :  * removed bytes. It is possible that the returned value is lower than
    1069             :  * min_remove_bytes if the caches get emptied out so the caller should be
    1070             :  * aware of this. */
    1071             : size_t
    1072           2 : hs_cache_handle_oom(time_t now, size_t min_remove_bytes)
    1073             : {
    1074           2 :   time_t k;
    1075           2 :   size_t bytes_removed = 0;
    1076             : 
    1077             :   /* Our OOM handler called with 0 bytes to remove is a code flow error. */
    1078           2 :   tor_assert(min_remove_bytes != 0);
    1079             : 
    1080             :   /* The algorithm is as follow. K is the oldest expected descriptor age.
    1081             :    *
    1082             :    *   1) Deallocate all entries from v3 cache that are older than K hours
    1083             :    *      2.1) If the amount of remove bytes has been reached, stop.
    1084             :    *   2) Set K = K - RendPostPeriod and repeat process until K is < 0.
    1085             :    *
    1086             :    * This ends up being O(Kn).
    1087             :    */
    1088             : 
    1089             :   /* Set K to the oldest expected age in seconds which is the maximum
    1090             :    * lifetime of a cache entry. */
    1091           2 :   k = hs_cache_max_entry_lifetime();
    1092             : 
    1093         146 :   do {
    1094         146 :     time_t cutoff;
    1095             : 
    1096             :     /* If K becomes negative, it means we've empty the caches so stop and
    1097             :      * return what we were able to cleanup. */
    1098         146 :     if (k < 0) {
    1099             :       break;
    1100             :     }
    1101             :     /* Compute a cutoff value with K and the current time. */
    1102         146 :     cutoff = now - k;
    1103             : 
    1104         146 :     if (bytes_removed < min_remove_bytes) {
    1105             :       /* We haven't remove enough bytes so clean v3 cache. */
    1106         146 :       bytes_removed += cache_clean_v3_as_dir(now, cutoff);
    1107             :       /* Decrement K by a post period to shorten the cutoff. */
    1108         146 :       k -= get_options()->RendPostPeriod;
    1109             :     }
    1110         146 :   } while (bytes_removed < min_remove_bytes);
    1111             : 
    1112           2 :   return bytes_removed;
    1113             : }
    1114             : 
    1115             : /** Return the maximum size of a v3 HS descriptor. */
    1116             : unsigned int
    1117         161 : hs_cache_get_max_descriptor_size(void)
    1118             : {
    1119         161 :   return (unsigned) networkstatus_get_param(NULL,
    1120             :                                             "HSV3MaxDescriptorSize",
    1121             :                                             HS_DESC_MAX_LEN, 1, INT32_MAX);
    1122             : }
    1123             : 
    1124             : /** Initialize the hidden service cache subsystem. */
    1125             : void
    1126         290 : hs_cache_init(void)
    1127             : {
    1128             :   /* Calling this twice is very wrong code flow. */
    1129         290 :   tor_assert(!hs_cache_v3_dir);
    1130         290 :   hs_cache_v3_dir = digest256map_new();
    1131             : 
    1132         290 :   tor_assert(!hs_cache_v3_client);
    1133         290 :   hs_cache_v3_client = digest256map_new();
    1134             : 
    1135         290 :   tor_assert(!hs_cache_client_intro_state);
    1136         290 :   hs_cache_client_intro_state = digest256map_new();
    1137         290 : }
    1138             : 
    1139             : /** Cleanup the hidden service cache subsystem. */
    1140             : void
    1141         278 : hs_cache_free_all(void)
    1142             : {
    1143         278 :   digest256map_free(hs_cache_v3_dir, cache_dir_desc_free_void);
    1144         278 :   hs_cache_v3_dir = NULL;
    1145             : 
    1146         278 :   digest256map_free(hs_cache_v3_client, cache_client_desc_free_void);
    1147         278 :   hs_cache_v3_client = NULL;
    1148             : 
    1149         278 :   digest256map_free(hs_cache_client_intro_state,
    1150             :                     cache_client_intro_state_free_void);
    1151         278 :   hs_cache_client_intro_state = NULL;
    1152         278 :   hs_cache_total_allocation = 0;
    1153         278 : }
    1154             : 
    1155             : /* Return total size of the cache. */
    1156             : size_t
    1157          19 : hs_cache_get_total_allocation(void)
    1158             : {
    1159          19 :   return hs_cache_total_allocation;
    1160             : }
    1161             : 
    1162             : /** Decrement the total bytes attributed to the rendezvous cache by n. */
    1163             : void
    1164          15 : hs_cache_decrement_allocation(size_t n)
    1165             : {
    1166          15 :   static int have_underflowed = 0;
    1167             : 
    1168          15 :   if (hs_cache_total_allocation >= n) {
    1169          15 :     hs_cache_total_allocation -= n;
    1170             :   } else {
    1171           0 :     hs_cache_total_allocation = 0;
    1172           0 :     if (! have_underflowed) {
    1173           0 :       have_underflowed = 1;
    1174           0 :       log_warn(LD_BUG, "Underflow in hs_cache_decrement_allocation");
    1175             :     }
    1176             :   }
    1177          15 : }
    1178             : 
    1179             : /** Increase the total bytes attributed to the rendezvous cache by n. */
    1180             : void
    1181          27 : hs_cache_increment_allocation(size_t n)
    1182             : {
    1183          27 :   static int have_overflowed = 0;
    1184          27 :   if (hs_cache_total_allocation <= SIZE_MAX - n) {
    1185          27 :     hs_cache_total_allocation += n;
    1186             :   } else {
    1187           0 :     hs_cache_total_allocation = SIZE_MAX;
    1188           0 :     if (! have_overflowed) {
    1189           0 :       have_overflowed = 1;
    1190           0 :       log_warn(LD_BUG, "Overflow in hs_cache_increment_allocation");
    1191             :     }
    1192             :   }
    1193          27 : }

Generated by: LCOV version 1.14