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