LCOV - code coverage report
Current view: top level - feature/nodelist - microdesc.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 353 483 73.1 %
Date: 2021-11-24 03:28:48 Functions: 35 39 89.7 %

          Line data    Source code
       1             : /* Copyright (c) 2009-2021, The Tor Project, Inc. */
       2             : /* See LICENSE for licensing information */
       3             : 
       4             : /**
       5             :  * \file microdesc.c
       6             :  *
       7             :  * \brief Implements microdescriptors -- an abbreviated description of
       8             :  *  less-frequently-changing router information.
       9             :  */
      10             : 
      11             : #include "core/or/or.h"
      12             : 
      13             : #include "lib/fdio/fdio.h"
      14             : 
      15             : #include "app/config/config.h"
      16             : #include "core/or/circuitbuild.h"
      17             : #include "core/or/policies.h"
      18             : #include "feature/client/entrynodes.h"
      19             : #include "feature/dircache/dirserv.h"
      20             : #include "feature/dirclient/dlstatus.h"
      21             : #include "feature/dirclient/dirclient_modes.h"
      22             : #include "feature/dircommon/directory.h"
      23             : #include "feature/dirparse/microdesc_parse.h"
      24             : #include "feature/nodelist/dirlist.h"
      25             : #include "feature/nodelist/microdesc.h"
      26             : #include "feature/nodelist/networkstatus.h"
      27             : #include "feature/nodelist/nodefamily.h"
      28             : #include "feature/nodelist/nodelist.h"
      29             : #include "feature/nodelist/routerlist.h"
      30             : #include "feature/relay/router.h"
      31             : 
      32             : #include "feature/nodelist/microdesc_st.h"
      33             : #include "feature/nodelist/networkstatus_st.h"
      34             : #include "feature/nodelist/node_st.h"
      35             : #include "feature/nodelist/routerstatus_st.h"
      36             : 
      37             : #ifdef HAVE_FCNTL_H
      38             : #include <fcntl.h>
      39             : #endif
      40             : #ifdef HAVE_SYS_STAT_H
      41             : #include <sys/stat.h>
      42             : #endif
      43             : 
      44             : /** A data structure to hold a bunch of cached microdescriptors.  There are
      45             :  * two active files in the cache: a "cache file" that we mmap, and a "journal
      46             :  * file" that we append to.  Periodically, we rebuild the cache file to hold
      47             :  * only the microdescriptors that we want to keep */
      48             : struct microdesc_cache_t {
      49             :   /** Map from sha256-digest to microdesc_t for every microdesc_t in the
      50             :    * cache. */
      51             :   HT_HEAD(microdesc_map, microdesc_t) map;
      52             : 
      53             :   /** Name of the cache file. */
      54             :   char *cache_fname;
      55             :   /** Name of the journal file. */
      56             :   char *journal_fname;
      57             :   /** Mmap'd contents of the cache file, or NULL if there is none. */
      58             :   tor_mmap_t *cache_content;
      59             :   /** Number of bytes used in the journal file. */
      60             :   size_t journal_len;
      61             :   /** Number of bytes in descriptors removed as too old. */
      62             :   size_t bytes_dropped;
      63             : 
      64             :   /** Total bytes of microdescriptor bodies we have added to this cache */
      65             :   uint64_t total_len_seen;
      66             :   /** Total number of microdescriptors we have added to this cache */
      67             :   unsigned n_seen;
      68             : 
      69             :   /** True iff we have loaded this cache from disk ever. */
      70             :   int is_loaded;
      71             : };
      72             : 
      73             : static microdesc_cache_t *get_microdesc_cache_noload(void);
      74             : static void warn_if_nul_found(const char *inp, size_t len, int64_t offset,
      75             :                               const char *activity);
      76             : 
      77             : /** Helper: computes a hash of <b>md</b> to place it in a hash table. */
      78             : static inline unsigned int
      79          98 : microdesc_hash_(microdesc_t *md)
      80             : {
      81          98 :   return (unsigned) siphash24g(md->digest, sizeof(md->digest));
      82             : }
      83             : 
      84             : /** Helper: compares <b>a</b> and <b>b</b> for equality for hash-table
      85             :  * purposes. */
      86             : static inline int
      87          13 : microdesc_eq_(microdesc_t *a, microdesc_t *b)
      88             : {
      89          13 :   return tor_memeq(a->digest, b->digest, DIGEST256_LEN);
      90             : }
      91             : 
      92         739 : HT_PROTOTYPE(microdesc_map, microdesc_t, node,
      93             :              microdesc_hash_, microdesc_eq_);
      94          29 : HT_GENERATE2(microdesc_map, microdesc_t, node,
      95             :              microdesc_hash_, microdesc_eq_, 0.6,
      96             :              tor_reallocarray_, tor_free_);
      97             : 
      98             : /************************* md fetch fail cache *****************************/
      99             : 
     100             : /* If we end up with too many outdated dirservers, something probably went
     101             :  * wrong so clean up the list. */
     102             : #define TOO_MANY_OUTDATED_DIRSERVERS 30
     103             : 
     104             : /** List of dirservers with outdated microdesc information. The smartlist is
     105             :  *  filled with the hex digests of outdated dirservers. */
     106             : static smartlist_t *outdated_dirserver_list = NULL;
     107             : 
     108             : /** Note that we failed to fetch a microdescriptor from the relay with
     109             :  *  <b>relay_digest</b> (of size DIGEST_LEN). */
     110             : void
     111           9 : microdesc_note_outdated_dirserver(const char *relay_digest)
     112             : {
     113           9 :   char relay_hexdigest[HEX_DIGEST_LEN+1];
     114             : 
     115             :   /* If we have a reasonably live consensus, then most of our dirservers should
     116             :    * still be caching all the microdescriptors in it. Reasonably live
     117             :    * consensuses are up to a day old (or a day in the future). But
     118             :    * microdescriptors expire 7 days after the last consensus that referenced
     119             :    * them. */
     120           9 :   if (!networkstatus_get_reasonably_live_consensus(approx_time(),
     121             :                                                    FLAV_MICRODESC)) {
     122           0 :     return;
     123             :   }
     124             : 
     125           9 :   if (!outdated_dirserver_list) {
     126           3 :     outdated_dirserver_list = smartlist_new();
     127             :   }
     128             : 
     129           9 :   tor_assert(outdated_dirserver_list);
     130             : 
     131             :   /* If the list grows too big, clean it up */
     132           9 :   if (smartlist_len(outdated_dirserver_list) > TOO_MANY_OUTDATED_DIRSERVERS) {
     133           0 :     log_info(LD_GENERAL,"Too many outdated directory servers (%d). Resetting.",
     134             :              smartlist_len(outdated_dirserver_list));
     135           0 :     microdesc_reset_outdated_dirservers_list();
     136             :   }
     137             : 
     138             :   /* Turn the binary relay digest to a hex since smartlists have better support
     139             :    * for strings than digests. */
     140           9 :   base16_encode(relay_hexdigest,sizeof(relay_hexdigest),
     141             :                 relay_digest, DIGEST_LEN);
     142             : 
     143             :   /* Make sure we don't add a dirauth as an outdated dirserver */
     144           9 :   if (router_get_trusteddirserver_by_digest(relay_digest)) {
     145           0 :     log_info(LD_GENERAL, "Auth %s gave us outdated dirinfo.", relay_hexdigest);
     146           0 :     return;
     147             :   }
     148             : 
     149             :   /* Don't double-add outdated dirservers */
     150           9 :   if (smartlist_contains_string(outdated_dirserver_list, relay_hexdigest)) {
     151             :     return;
     152             :   }
     153             : 
     154             :   /* Add it to the list of outdated dirservers */
     155           9 :   smartlist_add_strdup(outdated_dirserver_list, relay_hexdigest);
     156             : 
     157           9 :   log_info(LD_GENERAL, "Noted %s as outdated md dirserver", relay_hexdigest);
     158             : }
     159             : 
     160             : /** Return True if the relay with <b>relay_digest</b> (size DIGEST_LEN) is an
     161             :  *  outdated dirserver */
     162             : int
     163         129 : microdesc_relay_is_outdated_dirserver(const char *relay_digest)
     164             : {
     165         129 :   char relay_hexdigest[HEX_DIGEST_LEN+1];
     166             : 
     167         129 :   if (!outdated_dirserver_list) {
     168             :     return 0;
     169             :   }
     170             : 
     171             :   /* Convert identity digest to hex digest */
     172         129 :   base16_encode(relay_hexdigest, sizeof(relay_hexdigest),
     173             :                 relay_digest, DIGEST_LEN);
     174             : 
     175             :   /* Last time we tried to fetch microdescs, was this directory mirror missing
     176             :    * any mds we asked for? */
     177         129 :   if (smartlist_contains_string(outdated_dirserver_list, relay_hexdigest)) {
     178          27 :     return 1;
     179             :   }
     180             : 
     181             :   return 0;
     182             : }
     183             : 
     184             : /** Reset the list of outdated dirservers. */
     185             : void
     186           5 : microdesc_reset_outdated_dirservers_list(void)
     187             : {
     188           5 :   if (!outdated_dirserver_list) {
     189             :     return;
     190             :   }
     191             : 
     192           0 :   SMARTLIST_FOREACH(outdated_dirserver_list, char *, cp, tor_free(cp));
     193           0 :   smartlist_clear(outdated_dirserver_list);
     194             : }
     195             : 
     196             : /****************************************************************************/
     197             : 
     198             : /** Write the body of <b>md</b> into <b>f</b>, with appropriate annotations.
     199             :  * On success, return the total number of bytes written, and set
     200             :  * *<b>annotation_len_out</b> to the number of bytes written as
     201             :  * annotations. */
     202             : static ssize_t
     203          16 : dump_microdescriptor(int fd, microdesc_t *md, size_t *annotation_len_out)
     204             : {
     205          16 :   ssize_t r = 0;
     206          16 :   ssize_t written;
     207          16 :   if (md->body == NULL) {
     208           0 :     *annotation_len_out = 0;
     209           0 :     return 0;
     210             :   }
     211             :   /* XXXX drops unknown annotations. */
     212          16 :   if (md->last_listed) {
     213          16 :     char buf[ISO_TIME_LEN+1];
     214          16 :     char annotation[ISO_TIME_LEN+32];
     215          16 :     format_iso_time(buf, md->last_listed);
     216          16 :     tor_snprintf(annotation, sizeof(annotation), "@last-listed %s\n", buf);
     217          16 :     if (write_all_to_fd(fd, annotation, strlen(annotation)) < 0) {
     218           0 :       log_warn(LD_DIR,
     219             :                "Couldn't write microdescriptor annotation: %s",
     220             :                strerror(errno));
     221           0 :       return -1;
     222             :     }
     223          16 :     r += strlen(annotation);
     224          16 :     *annotation_len_out = r;
     225             :   } else {
     226           0 :     *annotation_len_out = 0;
     227             :   }
     228             : 
     229          16 :   md->off = tor_fd_getpos(fd);
     230          16 :   warn_if_nul_found(md->body, md->bodylen, (int64_t) md->off,
     231             :                     "dumping a microdescriptor");
     232          16 :   written = write_all_to_fd(fd, md->body, md->bodylen);
     233          16 :   if (written != (ssize_t)md->bodylen) {
     234           0 :     written = written < 0 ? 0 : written;
     235           0 :     log_warn(LD_DIR,
     236             :              "Couldn't dump microdescriptor (wrote %ld out of %lu): %s",
     237             :              (long)written, (unsigned long)md->bodylen,
     238             :              strerror(errno));
     239           0 :     return -1;
     240             :   }
     241          16 :   r += md->bodylen;
     242          16 :   return r;
     243             : }
     244             : 
     245             : /** Holds a pointer to the current microdesc_cache_t object, or NULL if no
     246             :  * such object has been allocated. */
     247             : static microdesc_cache_t *the_microdesc_cache = NULL;
     248             : 
     249             : /** Return a pointer to the microdescriptor cache, loading it if necessary. */
     250             : microdesc_cache_t *
     251          82 : get_microdesc_cache(void)
     252             : {
     253          82 :   microdesc_cache_t *cache = get_microdesc_cache_noload();
     254          82 :   if (PREDICT_UNLIKELY(cache->is_loaded == 0)) {
     255          18 :     microdesc_cache_reload(cache);
     256             :   }
     257          82 :   return cache;
     258             : }
     259             : 
     260             : /** Return a pointer to the microdescriptor cache, creating (but not loading)
     261             :  * it if necessary. */
     262             : static microdesc_cache_t *
     263          82 : get_microdesc_cache_noload(void)
     264             : {
     265          82 :   if (PREDICT_UNLIKELY(the_microdesc_cache==NULL)) {
     266          18 :     microdesc_cache_t *cache = tor_malloc_zero(sizeof(*cache));
     267          18 :     HT_INIT(microdesc_map, &cache->map);
     268          18 :     cache->cache_fname = get_cachedir_fname("cached-microdescs");
     269          18 :     cache->journal_fname = get_cachedir_fname("cached-microdescs.new");
     270          18 :     the_microdesc_cache = cache;
     271             :   }
     272          82 :   return the_microdesc_cache;
     273             : }
     274             : 
     275             : /* There are three sources of microdescriptors:
     276             :    1) Generated by us while acting as a directory authority.
     277             :    2) Loaded from the cache on disk.
     278             :    3) Downloaded.
     279             : */
     280             : 
     281             : /** Decode the microdescriptors from the string starting at <b>s</b> and
     282             :  * ending at <b>eos</b>, and store them in <b>cache</b>.  If <b>no_save</b>,
     283             :  * mark them as non-writable to disk.  If <b>where</b> is SAVED_IN_CACHE,
     284             :  * leave their bodies as pointers to the mmap'd cache.  If where is
     285             :  * <b>SAVED_NOWHERE</b>, do not allow annotations.  If listed_at is not -1,
     286             :  * set the last_listed field of every microdesc to listed_at.  If
     287             :  * requested_digests is non-null, then it contains a list of digests we mean
     288             :  * to allow, so we should reject any non-requested microdesc with a different
     289             :  * digest, and alter the list to contain only the digests of those microdescs
     290             :  * we didn't find.
     291             :  * Return a newly allocated list of the added microdescriptors, or NULL  */
     292             : smartlist_t *
     293          15 : microdescs_add_to_cache(microdesc_cache_t *cache,
     294             :                         const char *s, const char *eos, saved_location_t where,
     295             :                         int no_save, time_t listed_at,
     296             :                         smartlist_t *requested_digests256)
     297             : {
     298          15 :   void * const DIGEST_REQUESTED = (void*)1;
     299          15 :   void * const DIGEST_RECEIVED = (void*)2;
     300          15 :   void * const DIGEST_INVALID = (void*)3;
     301             : 
     302          15 :   smartlist_t *descriptors, *added;
     303          15 :   const int allow_annotations = (where != SAVED_NOWHERE);
     304          15 :   smartlist_t *invalid_digests = smartlist_new();
     305             : 
     306          15 :   descriptors = microdescs_parse_from_string(s, eos,
     307             :                                              allow_annotations,
     308             :                                              where, invalid_digests);
     309          15 :   if (listed_at != (time_t)-1) {
     310          32 :     SMARTLIST_FOREACH(descriptors, microdesc_t *, md,
     311             :                       md->last_listed = listed_at);
     312             :   }
     313          15 :   if (requested_digests256) {
     314           3 :     digest256map_t *requested;
     315           3 :     requested = digest256map_new();
     316             :     /* Set requested[d] to DIGEST_REQUESTED for every md we requested. */
     317           9 :     SMARTLIST_FOREACH(requested_digests256, const uint8_t *, cp,
     318             :       digest256map_set(requested, cp, DIGEST_REQUESTED));
     319             :     /* Set requested[d] to DIGEST_INVALID for every md we requested which we
     320             :      * will never be able to parse.  Remove the ones we didn't request from
     321             :      * invalid_digests.
     322             :      */
     323           7 :     SMARTLIST_FOREACH_BEGIN(invalid_digests, uint8_t *, cp) {
     324           4 :       if (digest256map_get(requested, cp)) {
     325           2 :         digest256map_set(requested, cp, DIGEST_INVALID);
     326             :       } else {
     327           2 :         tor_free(cp);
     328           2 :         SMARTLIST_DEL_CURRENT(invalid_digests, cp);
     329             :       }
     330           4 :     } SMARTLIST_FOREACH_END(cp);
     331             :     /* Update requested[d] to 2 for the mds we asked for and got. Delete the
     332             :      * ones we never requested from the 'descriptors' smartlist.
     333             :      */
     334          19 :     SMARTLIST_FOREACH_BEGIN(descriptors, microdesc_t *, md) {
     335          16 :       if (digest256map_get(requested, (const uint8_t*)md->digest)) {
     336           3 :         digest256map_set(requested, (const uint8_t*)md->digest,
     337             :                          DIGEST_RECEIVED);
     338             :       } else {
     339          13 :         log_fn(LOG_PROTOCOL_WARN, LD_DIR, "Received non-requested microdesc");
     340          13 :         microdesc_free(md);
     341          13 :         SMARTLIST_DEL_CURRENT(descriptors, md);
     342             :       }
     343          16 :     } SMARTLIST_FOREACH_END(md);
     344             :     /* Remove the ones we got or the invalid ones from requested_digests256.
     345             :      */
     346           9 :     SMARTLIST_FOREACH_BEGIN(requested_digests256, uint8_t *, cp) {
     347           6 :       void *status = digest256map_get(requested, cp);
     348           6 :       if (status == DIGEST_RECEIVED || status == DIGEST_INVALID) {
     349           5 :         tor_free(cp);
     350           5 :         SMARTLIST_DEL_CURRENT(requested_digests256, cp);
     351             :       }
     352           6 :     } SMARTLIST_FOREACH_END(cp);
     353           3 :     digest256map_free(requested, NULL);
     354             :   }
     355             : 
     356             :   /* For every requested microdescriptor that was unparseable, mark it
     357             :    * as not to be retried. */
     358          15 :   if (smartlist_len(invalid_digests)) {
     359           6 :     networkstatus_t *ns =
     360           6 :       networkstatus_get_latest_consensus_by_flavor(FLAV_MICRODESC);
     361           6 :     if (ns) {
     362           3 :       SMARTLIST_FOREACH_BEGIN(invalid_digests, char *, d) {
     363           2 :         routerstatus_t *rs =
     364           2 :           router_get_mutable_consensus_status_by_descriptor_digest(ns, d);
     365           2 :         if (rs && tor_memeq(d, rs->descriptor_digest, DIGEST256_LEN)) {
     366           2 :           download_status_mark_impossible(&rs->dl_status);
     367             :         }
     368           2 :       } SMARTLIST_FOREACH_END(d);
     369             :     }
     370             :   }
     371          22 :   SMARTLIST_FOREACH(invalid_digests, uint8_t *, d, tor_free(d));
     372          15 :   smartlist_free(invalid_digests);
     373             : 
     374          15 :   added = microdescs_add_list_to_cache(cache, descriptors, where, no_save);
     375          15 :   smartlist_free(descriptors);
     376          15 :   return added;
     377             : }
     378             : 
     379             : /** As microdescs_add_to_cache, but takes a list of microdescriptors instead of
     380             :  * a string to decode.  Frees any members of <b>descriptors</b> that it does
     381             :  * not add. */
     382             : smartlist_t *
     383          15 : microdescs_add_list_to_cache(microdesc_cache_t *cache,
     384             :                              smartlist_t *descriptors, saved_location_t where,
     385             :                              int no_save)
     386             : {
     387          15 :   smartlist_t *added;
     388          15 :   open_file_t *open_file = NULL;
     389          15 :   int fd = -1;
     390             :   //  int n_added = 0;
     391          15 :   ssize_t size = 0;
     392             : 
     393          15 :   if (where == SAVED_NOWHERE && !no_save) {
     394           9 :     fd = start_writing_to_file(cache->journal_fname,
     395             :                                OPEN_FLAGS_APPEND|O_BINARY,
     396             :                                0600, &open_file);
     397           9 :     if (fd < 0) {
     398           0 :       log_warn(LD_DIR, "Couldn't append to journal in %s: %s",
     399             :                cache->journal_fname, strerror(errno));
     400             :     }
     401             :   }
     402             : 
     403          15 :   added = smartlist_new();
     404          26 :   SMARTLIST_FOREACH_BEGIN(descriptors, microdesc_t *, md) {
     405          11 :     microdesc_t *md2;
     406          11 :     md2 = HT_FIND(microdesc_map, &cache->map, md);
     407          11 :     if (md2) {
     408             :       /* We already had this one. */
     409           0 :       if (md2->last_listed < md->last_listed)
     410           0 :         md2->last_listed = md->last_listed;
     411           0 :       microdesc_free(md);
     412           0 :       if (where != SAVED_NOWHERE)
     413           0 :         cache->bytes_dropped += size;
     414           0 :       continue;
     415             :     }
     416             : 
     417             :     /* Okay, it's a new one. */
     418          11 :     if (fd >= 0) {
     419           8 :       size_t annotation_len;
     420           8 :       size = dump_microdescriptor(fd, md, &annotation_len);
     421           8 :       if (size < 0) {
     422             :         /* we already warned in dump_microdescriptor */
     423           0 :         abort_writing_to_file(open_file);
     424           0 :         fd = -1;
     425             :       } else {
     426           8 :         md->saved_location = SAVED_IN_JOURNAL;
     427           8 :         cache->journal_len += size;
     428             :       }
     429             :     } else {
     430           3 :       md->saved_location = where;
     431             :     }
     432             : 
     433          11 :     md->no_save = no_save;
     434             : 
     435          11 :     HT_INSERT(microdesc_map, &cache->map, md);
     436          11 :     md->held_in_map = 1;
     437          11 :     smartlist_add(added, md);
     438          11 :     ++cache->n_seen;
     439          11 :     cache->total_len_seen += md->bodylen;
     440          11 :   } SMARTLIST_FOREACH_END(md);
     441             : 
     442          15 :   if (fd >= 0) {
     443           9 :     if (finish_writing_to_file(open_file) < 0) {
     444           0 :       log_warn(LD_DIR, "Error appending to microdescriptor file: %s",
     445             :                strerror(errno));
     446           0 :       smartlist_clear(added);
     447           0 :       return added;
     448             :     }
     449             :   }
     450             : 
     451             :   {
     452          15 :     networkstatus_t *ns = networkstatus_get_latest_consensus();
     453          15 :     if (ns && ns->flavor == FLAV_MICRODESC)
     454           0 :       SMARTLIST_FOREACH(added, microdesc_t *, md, nodelist_add_microdesc(md));
     455             :   }
     456             : 
     457          15 :   if (smartlist_len(added))
     458           8 :     router_dir_info_changed();
     459             : 
     460             :   return added;
     461             : }
     462             : 
     463             : /** Remove every microdescriptor in <b>cache</b>. */
     464             : void
     465          24 : microdesc_cache_clear(microdesc_cache_t *cache)
     466             : {
     467          24 :   microdesc_t **entry, **next;
     468             : 
     469          34 :   for (entry = HT_START(microdesc_map, &cache->map); entry; entry = next) {
     470          10 :     microdesc_t *md = *entry;
     471          10 :     next = HT_NEXT_RMV(microdesc_map, &cache->map, entry);
     472          10 :     md->held_in_map = 0;
     473          10 :     microdesc_free(md);
     474             :   }
     475          24 :   HT_CLEAR(microdesc_map, &cache->map);
     476          24 :   if (cache->cache_content) {
     477           3 :     int res = tor_munmap_file(cache->cache_content);
     478           3 :     if (res != 0) {
     479           0 :       log_warn(LD_FS,
     480             :                "tor_munmap_file() failed clearing microdesc cache; "
     481             :                "we are probably about to leak memory.");
     482             :       /* TODO something smarter? */
     483             :     }
     484           3 :     cache->cache_content = NULL;
     485             :   }
     486          24 :   cache->total_len_seen = 0;
     487          24 :   cache->n_seen = 0;
     488          24 :   cache->bytes_dropped = 0;
     489          24 : }
     490             : 
     491             : static void
     492          19 : warn_if_nul_found(const char *inp, size_t len, int64_t offset,
     493             :                   const char *activity)
     494             : {
     495          19 :   const char *nul_found = memchr(inp, 0, len);
     496          19 :   if (BUG(nul_found)) {
     497           0 :     log_warn(LD_BUG, "Found unexpected NUL while %s, offset %"PRId64
     498             :              "at position %"TOR_PRIuSZ"/%"TOR_PRIuSZ".",
     499             :              activity, offset, (nul_found - inp), len);
     500           0 :     const char *start_excerpt_at, *eos = inp + len;
     501           0 :     if ((nul_found - inp) >= 16)
     502           0 :       start_excerpt_at = nul_found - 16;
     503             :     else
     504             :       start_excerpt_at = inp;
     505           0 :     size_t excerpt_len = MIN(32, eos - start_excerpt_at);
     506           0 :     char tmp[65];
     507           0 :     base16_encode(tmp, sizeof(tmp), start_excerpt_at, excerpt_len);
     508           0 :     log_warn(LD_BUG, "      surrounding string: %s", tmp);
     509             :   }
     510          19 : }
     511             : 
     512             : /** Reload the contents of <b>cache</b> from disk.  If it is empty, load it
     513             :  * for the first time.  Return 0 on success, -1 on failure. */
     514             : int
     515          18 : microdesc_cache_reload(microdesc_cache_t *cache)
     516             : {
     517          18 :   struct stat st;
     518          18 :   char *journal_content;
     519          18 :   smartlist_t *added;
     520          18 :   tor_mmap_t *mm;
     521          18 :   int total = 0;
     522             : 
     523          18 :   microdesc_cache_clear(cache);
     524             : 
     525          18 :   cache->is_loaded = 1;
     526             : 
     527          18 :   mm = cache->cache_content = tor_mmap_file(cache->cache_fname);
     528          18 :   if (mm) {
     529           2 :     warn_if_nul_found(mm->data, mm->size, 0, "scanning microdesc cache");
     530           2 :     added = microdescs_add_to_cache(cache, mm->data, mm->data+mm->size,
     531             :                                     SAVED_IN_CACHE, 0, -1, NULL);
     532           2 :     if (added) {
     533           2 :       total += smartlist_len(added);
     534           2 :       smartlist_free(added);
     535             :     }
     536             :   }
     537             : 
     538          18 :   journal_content = read_file_to_str(cache->journal_fname,
     539             :                                      RFTS_IGNORE_MISSING, &st);
     540          18 :   if (journal_content) {
     541           1 :     cache->journal_len = strlen(journal_content);
     542           1 :     warn_if_nul_found(journal_content, (size_t)st.st_size, 0,
     543             :                       "reading microdesc journal");
     544           2 :     added = microdescs_add_to_cache(cache, journal_content,
     545           1 :                                     journal_content+st.st_size,
     546             :                                     SAVED_IN_JOURNAL, 0, -1, NULL);
     547           1 :     if (added) {
     548           1 :       total += smartlist_len(added);
     549           1 :       smartlist_free(added);
     550             :     }
     551           1 :     tor_free(journal_content);
     552             :   }
     553          18 :   log_info(LD_DIR, "Reloaded microdescriptor cache. Found %d descriptors.",
     554             :            total);
     555             : 
     556          18 :   microdesc_cache_rebuild(cache, 0 /* don't force */);
     557             : 
     558          18 :   return 0;
     559             : }
     560             : 
     561             : /** By default, we remove any microdescriptors that have gone at least this
     562             :  * long without appearing in a current consensus. */
     563             : #define TOLERATE_MICRODESC_AGE (7*24*60*60)
     564             : 
     565             : /** Remove all microdescriptors from <b>cache</b> that haven't been listed for
     566             :  * a long time.  Does not rebuild the cache on disk.  If <b>cutoff</b> is
     567             :  * positive, specifically remove microdescriptors that have been unlisted
     568             :  * since <b>cutoff</b>.  If <b>force</b> is true, remove microdescriptors even
     569             :  * if we have no current live microdescriptor consensus.
     570             :  */
     571             : void
     572          22 : microdesc_cache_clean(microdesc_cache_t *cache, time_t cutoff, int force)
     573             : {
     574          22 :   microdesc_t **mdp, *victim;
     575          22 :   int dropped=0, kept=0;
     576          22 :   size_t bytes_dropped = 0;
     577          22 :   time_t now = time(NULL);
     578             : 
     579             :   /* If we don't know a reasonably live consensus, don't believe last_listed
     580             :    * values: we might be starting up after being down for a while. */
     581          43 :   if (! force &&
     582          21 :       ! networkstatus_get_reasonably_live_consensus(now, FLAV_MICRODESC))
     583             :       return;
     584             : 
     585           8 :   if (cutoff <= 0)
     586           7 :     cutoff = now - TOLERATE_MICRODESC_AGE;
     587             : 
     588          11 :   for (mdp = HT_START(microdesc_map, &cache->map); mdp != NULL; ) {
     589           3 :     const int is_old = (*mdp)->last_listed < cutoff;
     590           3 :     const unsigned held_by_nodes = (*mdp)->held_by_nodes;
     591           3 :     if (is_old && !held_by_nodes) {
     592           1 :       ++dropped;
     593           1 :       victim = *mdp;
     594           1 :       mdp = HT_NEXT_RMV(microdesc_map, &cache->map, mdp);
     595           1 :       victim->held_in_map = 0;
     596           1 :       bytes_dropped += victim->bodylen;
     597           1 :       microdesc_free(victim);
     598             :     } else {
     599           2 :       if (is_old) {
     600             :         /* It's old, but it has held_by_nodes set.  That's not okay. */
     601             :         /* Let's try to diagnose and fix #7164 . */
     602           0 :         smartlist_t *nodes = nodelist_find_nodes_with_microdesc(*mdp);
     603           0 :         const networkstatus_t *ns = networkstatus_get_latest_consensus();
     604           0 :         long networkstatus_age = -1;
     605           0 :         const int ht_badness = HT_REP_IS_BAD_(microdesc_map, &cache->map);
     606           0 :         if (ns) {
     607           0 :           networkstatus_age = now - ns->valid_after;
     608             :         }
     609           0 :         log_warn(LD_BUG, "Microdescriptor seemed very old "
     610             :                  "(last listed %d hours ago vs %d hour cutoff), but is still "
     611             :                  "marked as being held by %d node(s). I found %d node(s) "
     612             :                  "holding it. Current networkstatus is %ld hours old. "
     613             :                  "Hashtable badness is %d.",
     614             :                  (int)((now - (*mdp)->last_listed) / 3600),
     615             :                  (int)((now - cutoff) / 3600),
     616             :                  held_by_nodes,
     617             :                  smartlist_len(nodes),
     618             :                  networkstatus_age / 3600,
     619             :                  ht_badness);
     620             : 
     621           0 :         SMARTLIST_FOREACH_BEGIN(nodes, const node_t *, node) {
     622           0 :           const char *rs_match = "No RS";
     623           0 :           const char *rs_present = "";
     624           0 :           if (node->rs) {
     625           0 :             if (tor_memeq(node->rs->descriptor_digest,
     626           0 :                           (*mdp)->digest, DIGEST256_LEN)) {
     627             :               rs_match = "Microdesc digest in RS matches";
     628             :             } else {
     629           0 :               rs_match = "Microdesc digest in RS does match";
     630             :             }
     631           0 :             if (ns) {
     632             :               /* This should be impossible, but let's see! */
     633           0 :               rs_present = " RS not present in networkstatus.";
     634           0 :               SMARTLIST_FOREACH(ns->routerstatus_list, routerstatus_t *,rs, {
     635             :                 if (rs == node->rs) {
     636             :                   rs_present = " RS okay in networkstatus.";
     637             :                 }
     638             :               });
     639             :             }
     640             :           }
     641           0 :           log_warn(LD_BUG, "  [%d]: ID=%s. md=%p, rs=%p, ri=%p. %s.%s",
     642             :                    node_sl_idx,
     643             :                    hex_str(node->identity, DIGEST_LEN),
     644             :                    node->md, node->rs, node->ri, rs_match, rs_present);
     645           0 :         } SMARTLIST_FOREACH_END(node);
     646           0 :         smartlist_free(nodes);
     647           0 :         (*mdp)->last_listed = now;
     648             :       }
     649             : 
     650           2 :       ++kept;
     651           2 :       mdp = HT_NEXT(microdesc_map, &cache->map, mdp);
     652             :     }
     653             :   }
     654             : 
     655           8 :   if (dropped) {
     656           1 :     log_info(LD_DIR, "Removed %d/%d microdescriptors as old.",
     657             :              dropped,dropped+kept);
     658           1 :     cache->bytes_dropped += bytes_dropped;
     659             :   }
     660             : }
     661             : 
     662             : static int
     663          18 : should_rebuild_md_cache(microdesc_cache_t *cache)
     664             : {
     665          36 :     const size_t old_len =
     666          18 :       cache->cache_content ? cache->cache_content->size : 0;
     667          18 :     const size_t journal_len = cache->journal_len;
     668          18 :     const size_t dropped = cache->bytes_dropped;
     669             : 
     670          18 :     if (journal_len < 16384)
     671             :       return 0; /* Don't bother, not enough has happened yet. */
     672           0 :     if (dropped > (journal_len + old_len) / 3)
     673             :       return 1; /* We could save 1/3 or more of the currently used space. */
     674           0 :     if (journal_len > old_len / 2)
     675           0 :       return 1; /* We should append to the regular file */
     676             : 
     677             :     return 0;
     678             : }
     679             : 
     680             : /**
     681             :  * Mark <b>md</b> as having no body, and release any storage previously held
     682             :  * by its body.
     683             :  */
     684             : static void
     685           0 : microdesc_wipe_body(microdesc_t *md)
     686             : {
     687           0 :   if (!md)
     688             :     return;
     689             : 
     690           0 :   if (md->saved_location != SAVED_IN_CACHE)
     691           0 :     tor_free(md->body);
     692             : 
     693           0 :   md->off = 0;
     694           0 :   md->saved_location = SAVED_NOWHERE;
     695           0 :   md->body = NULL;
     696           0 :   md->bodylen = 0;
     697           0 :   md->no_save = 1;
     698             : }
     699             : 
     700             : /** Regenerate the main cache file for <b>cache</b>, clear the journal file,
     701             :  * and update every microdesc_t in the cache with pointers to its new
     702             :  * location.  If <b>force</b> is true, do this unconditionally.  If
     703             :  * <b>force</b> is false, do it only if we expect to save space on disk. */
     704             : int
     705          21 : microdesc_cache_rebuild(microdesc_cache_t *cache, int force)
     706             : {
     707          21 :   open_file_t *open_file;
     708          21 :   int fd = -1, res;
     709          21 :   microdesc_t **mdp;
     710          21 :   smartlist_t *wrote;
     711          21 :   ssize_t size;
     712          21 :   off_t off = 0, off_real;
     713          21 :   int orig_size, new_size;
     714             : 
     715          21 :   if (cache == NULL) {
     716           0 :     cache = the_microdesc_cache;
     717           0 :     if (cache == NULL)
     718             :       return 0;
     719             :   }
     720             : 
     721             :   /* Remove dead descriptors */
     722          21 :   microdesc_cache_clean(cache, 0/*cutoff*/, 0/*force*/);
     723             : 
     724          21 :   if (!force && !should_rebuild_md_cache(cache))
     725             :     return 0;
     726             : 
     727           3 :   log_info(LD_DIR, "Rebuilding the microdescriptor cache...");
     728             : 
     729           3 :   orig_size = (int)(cache->cache_content ? cache->cache_content->size : 0);
     730           3 :   orig_size += (int)cache->journal_len;
     731             : 
     732           3 :   fd = start_writing_to_file(cache->cache_fname,
     733             :                              OPEN_FLAGS_REPLACE|O_BINARY,
     734             :                              0600, &open_file);
     735           3 :   if (fd < 0)
     736             :     return -1;
     737             : 
     738           3 :   wrote = smartlist_new();
     739             : 
     740          11 :   HT_FOREACH(mdp, microdesc_map, &cache->map) {
     741           8 :     microdesc_t *md = *mdp;
     742           8 :     size_t annotation_len;
     743           8 :     if (md->no_save || !md->body)
     744           0 :       continue;
     745             : 
     746           8 :     size = dump_microdescriptor(fd, md, &annotation_len);
     747           8 :     if (size < 0) {
     748           0 :       microdesc_wipe_body(md);
     749             : 
     750             :       /* rewind, in case it was a partial write. */
     751           0 :       tor_fd_setpos(fd, off);
     752           0 :       continue;
     753             :     }
     754           8 :     tor_assert(((size_t)size) == annotation_len + md->bodylen);
     755           8 :     md->off = off + annotation_len;
     756           8 :     off += size;
     757           8 :     off_real = tor_fd_getpos(fd);
     758           8 :     if (off_real != off) {
     759           0 :       log_warn(LD_BUG, "Discontinuity in position in microdescriptor cache."
     760             :                "By my count, I'm at %"PRId64
     761             :                ", but I should be at %"PRId64,
     762             :                (int64_t)(off), (int64_t)(off_real));
     763           0 :       if (off_real >= 0)
     764           0 :         off = off_real;
     765             :     }
     766           8 :     if (md->saved_location != SAVED_IN_CACHE) {
     767           4 :       tor_free(md->body);
     768           4 :       md->saved_location = SAVED_IN_CACHE;
     769             :     }
     770           8 :     smartlist_add(wrote, md);
     771             :   }
     772             : 
     773             :   /* We must do this unmap _before_ we call finish_writing_to_file(), or
     774             :    * windows will not actually replace the file. */
     775           3 :   if (cache->cache_content) {
     776           2 :     res = tor_munmap_file(cache->cache_content);
     777           2 :     if (res != 0) {
     778           0 :       log_warn(LD_FS,
     779             :                "Failed to unmap old microdescriptor cache while rebuilding");
     780             :     }
     781           2 :     cache->cache_content = NULL;
     782             :   }
     783             : 
     784           3 :   if (finish_writing_to_file(open_file) < 0) {
     785           0 :     log_warn(LD_DIR, "Error rebuilding microdescriptor cache: %s",
     786             :              strerror(errno));
     787             :     /* Okay. Let's prevent from making things worse elsewhere. */
     788           0 :     cache->cache_content = NULL;
     789           0 :     HT_FOREACH(mdp, microdesc_map, &cache->map) {
     790           0 :       microdesc_t *md = *mdp;
     791           0 :       if (md->saved_location == SAVED_IN_CACHE) {
     792           0 :         microdesc_wipe_body(md);
     793             :       }
     794             :     }
     795           0 :     smartlist_free(wrote);
     796           0 :     return -1;
     797             :   }
     798             : 
     799           3 :   cache->cache_content = tor_mmap_file(cache->cache_fname);
     800             : 
     801           3 :   if (!cache->cache_content && smartlist_len(wrote)) {
     802           0 :     log_err(LD_DIR, "Couldn't map file that we just wrote to %s!",
     803             :             cache->cache_fname);
     804           0 :     smartlist_free(wrote);
     805           0 :     return -1;
     806             :   }
     807          11 :   SMARTLIST_FOREACH_BEGIN(wrote, microdesc_t *, md) {
     808           8 :     tor_assert(md->saved_location == SAVED_IN_CACHE);
     809           8 :     md->body = (char*)cache->cache_content->data + md->off;
     810           8 :     if (PREDICT_UNLIKELY(
     811             :              md->bodylen < 9 || fast_memneq(md->body, "onion-key", 9) != 0)) {
     812             :       /* XXXX once bug 2022 is solved, we can kill this block and turn it
     813             :        * into just the tor_assert(fast_memeq) */
     814           0 :       off_t avail = cache->cache_content->size - md->off;
     815           0 :       char *bad_str;
     816           0 :       tor_assert(avail >= 0);
     817           0 :       bad_str = tor_strndup(md->body, MIN(128, (size_t)avail));
     818           0 :       log_err(LD_BUG, "After rebuilding microdesc cache, offsets seem wrong. "
     819             :               " At offset %d, I expected to find a microdescriptor starting "
     820             :               " with \"onion-key\".  Instead I got %s.",
     821             :               (int)md->off, escaped(bad_str));
     822           0 :       tor_free(bad_str);
     823           0 :       tor_assert(fast_memeq(md->body, "onion-key", 9));
     824             :     }
     825           8 :   } SMARTLIST_FOREACH_END(md);
     826             : 
     827           3 :   smartlist_free(wrote);
     828             : 
     829           3 :   write_str_to_file(cache->journal_fname, "", 1);
     830           3 :   cache->journal_len = 0;
     831           3 :   cache->bytes_dropped = 0;
     832             : 
     833           3 :   new_size = cache->cache_content ? (int)cache->cache_content->size : 0;
     834           3 :   log_info(LD_DIR, "Done rebuilding microdesc cache. "
     835             :            "Saved %d bytes; %d still used.",
     836             :            orig_size-new_size, new_size);
     837             : 
     838           3 :   return 0;
     839             : }
     840             : 
     841             : /** Make sure that the reference count of every microdescriptor in cache is
     842             :  * accurate. */
     843             : void
     844           0 : microdesc_check_counts(void)
     845             : {
     846           0 :   microdesc_t **mdp;
     847           0 :   if (!the_microdesc_cache)
     848             :     return;
     849             : 
     850           0 :   HT_FOREACH(mdp, microdesc_map, &the_microdesc_cache->map) {
     851           0 :     microdesc_t *md = *mdp;
     852           0 :     unsigned int found=0;
     853           0 :     const smartlist_t *nodes = nodelist_get_list();
     854           0 :     SMARTLIST_FOREACH(nodes, node_t *, node, {
     855             :         if (node->md == md) {
     856             :           ++found;
     857             :         }
     858             :       });
     859           0 :     tor_assert(found == md->held_by_nodes);
     860             :   }
     861             : }
     862             : 
     863             : /** Deallocate a single microdescriptor.  Note: the microdescriptor MUST have
     864             :  * previously been removed from the cache if it had ever been inserted. */
     865             : void
     866         103 : microdesc_free_(microdesc_t *md, const char *fname, int lineno)
     867             : {
     868         103 :   if (!md)
     869             :     return;
     870             : 
     871             :   /* Make sure that the microdesc was really removed from the appropriate data
     872             :      structures. */
     873          60 :   if (md->held_in_map) {
     874           0 :     microdesc_cache_t *cache = get_microdesc_cache_noload();
     875           0 :     microdesc_t *md2 = HT_FIND(microdesc_map, &cache->map, md);
     876           0 :     if (md2 == md) {
     877           0 :       log_warn(LD_BUG, "microdesc_free() called from %s:%d, but md was still "
     878             :                "in microdesc_map", fname, lineno);
     879           0 :       HT_REMOVE(microdesc_map, &cache->map, md);
     880             :     } else {
     881           0 :       log_warn(LD_BUG, "microdesc_free() called from %s:%d with held_in_map "
     882             :                "set, but microdesc was not in the map.", fname, lineno);
     883             :     }
     884           0 :     tor_fragile_assert();
     885             :   }
     886          60 :   if (md->held_by_nodes) {
     887           0 :     microdesc_cache_t *cache = get_microdesc_cache_noload();
     888           0 :     int found=0;
     889           0 :     const smartlist_t *nodes = nodelist_get_list();
     890           0 :     const int ht_badness = HT_REP_IS_BAD_(microdesc_map, &cache->map);
     891           0 :     SMARTLIST_FOREACH(nodes, node_t *, node, {
     892             :         if (node->md == md) {
     893             :           ++found;
     894             :           node->md = NULL;
     895             :         }
     896             :       });
     897           0 :     if (found) {
     898           0 :       log_warn(LD_BUG, "microdesc_free() called from %s:%d, but md was still "
     899             :                "referenced %d node(s); held_by_nodes == %u, ht_badness == %d",
     900             :                fname, lineno, found, md->held_by_nodes, ht_badness);
     901             :     } else {
     902           0 :       log_warn(LD_BUG, "microdesc_free() called from %s:%d with held_by_nodes "
     903             :                "set to %u, but md was not referenced by any nodes. "
     904             :                "ht_badness == %d",
     905             :                fname, lineno, md->held_by_nodes, ht_badness);
     906             :     }
     907           0 :     tor_fragile_assert();
     908             :   }
     909             :   //tor_assert(md->held_in_map == 0);
     910             :   //tor_assert(md->held_by_nodes == 0);
     911             : 
     912          60 :   if (md->onion_pkey)
     913          46 :     tor_free(md->onion_pkey);
     914          60 :   tor_free(md->onion_curve25519_pkey);
     915          60 :   tor_free(md->ed25519_identity_pkey);
     916          60 :   if (md->body && md->saved_location != SAVED_IN_CACHE)
     917          49 :     tor_free(md->body);
     918             : 
     919          60 :   nodefamily_free(md->family);
     920          60 :   short_policy_free(md->exit_policy);
     921          60 :   short_policy_free(md->ipv6_exit_policy);
     922             : 
     923          60 :   tor_free(md);
     924             : }
     925             : 
     926             : /** Free all storage held in the microdesc.c module. */
     927             : void
     928         241 : microdesc_free_all(void)
     929             : {
     930         241 :   if (the_microdesc_cache) {
     931           6 :     microdesc_cache_clear(the_microdesc_cache);
     932           6 :     tor_free(the_microdesc_cache->cache_fname);
     933           6 :     tor_free(the_microdesc_cache->journal_fname);
     934           6 :     tor_free(the_microdesc_cache);
     935             :   }
     936             : 
     937         241 :   if (outdated_dirserver_list) {
     938           0 :     SMARTLIST_FOREACH(outdated_dirserver_list, char *, cp, tor_free(cp));
     939           0 :     smartlist_free(outdated_dirserver_list);
     940             :   }
     941         241 : }
     942             : 
     943             : /** If there is a microdescriptor in <b>cache</b> whose sha256 digest is
     944             :  * <b>d</b>, return it.  Otherwise return NULL. */
     945             : microdesc_t *
     946          76 : microdesc_cache_lookup_by_digest256(microdesc_cache_t *cache, const char *d)
     947             : {
     948          76 :   microdesc_t *md, search;
     949          76 :   if (!cache)
     950          53 :     cache = get_microdesc_cache();
     951          76 :   memcpy(search.digest, d, DIGEST256_LEN);
     952          76 :   md = HT_FIND(microdesc_map, &cache->map, &search);
     953          76 :   return md;
     954             : }
     955             : 
     956             : /** Return a smartlist of all the sha256 digest of the microdescriptors that
     957             :  * are listed in <b>ns</b> but not present in <b>cache</b>. Returns pointers
     958             :  * to internals of <b>ns</b>; you should not free the members of the resulting
     959             :  * smartlist.  Omit all microdescriptors whose digest appear in <b>skip</b>. */
     960             : smartlist_t *
     961           1 : microdesc_list_missing_digest256(networkstatus_t *ns, microdesc_cache_t *cache,
     962             :                                  int downloadable_only, digest256map_t *skip)
     963             : {
     964           1 :   smartlist_t *result = smartlist_new();
     965           1 :   time_t now = time(NULL);
     966           1 :   tor_assert(ns->flavor == FLAV_MICRODESC);
     967           4 :   SMARTLIST_FOREACH_BEGIN(ns->routerstatus_list, routerstatus_t *, rs) {
     968           3 :     if (microdesc_cache_lookup_by_digest256(cache, rs->descriptor_digest))
     969           0 :       continue;
     970           6 :     if (downloadable_only &&
     971           3 :         !download_status_is_ready(&rs->dl_status, now))
     972           0 :       continue;
     973           3 :     if (skip && digest256map_get(skip, (const uint8_t*)rs->descriptor_digest))
     974           0 :       continue;
     975           3 :     if (fast_mem_is_zero(rs->descriptor_digest, DIGEST256_LEN))
     976           0 :       continue;
     977             :     /* XXXX Also skip if we're a noncache and wouldn't use this router.
     978             :      * XXXX NM Microdesc
     979             :      */
     980           3 :     smartlist_add(result, rs->descriptor_digest);
     981           3 :   } SMARTLIST_FOREACH_END(rs);
     982           1 :   return result;
     983             : }
     984             : 
     985             : /** Launch download requests for microdescriptors as appropriate.
     986             :  *
     987             :  * Specifically, we should launch download requests if we are configured to
     988             :  * download mirodescriptors, and there are some microdescriptors listed in the
     989             :  * current microdesc consensus that we don't have, and either we never asked
     990             :  * for them, or we failed to download them but we're willing to retry.
     991             :  */
     992             : void
     993           1 : update_microdesc_downloads(time_t now)
     994             : {
     995           1 :   const or_options_t *options = get_options();
     996           1 :   networkstatus_t *consensus;
     997           1 :   smartlist_t *missing;
     998           1 :   digest256map_t *pending;
     999             : 
    1000           1 :   if (should_delay_dir_fetches(options, NULL))
    1001           1 :     return;
    1002           1 :   if (dirclient_too_idle_to_fetch_descriptors(options, now))
    1003             :     return;
    1004             : 
    1005             :   /* Give up if we don't have a reasonably live consensus. */
    1006           1 :   consensus = networkstatus_get_reasonably_live_consensus(now, FLAV_MICRODESC);
    1007           1 :   if (!consensus)
    1008             :     return;
    1009             : 
    1010           1 :   if (!we_fetch_microdescriptors(options))
    1011             :     return;
    1012             : 
    1013           1 :   pending = digest256map_new();
    1014           1 :   list_pending_microdesc_downloads(pending);
    1015             : 
    1016           1 :   missing = microdesc_list_missing_digest256(consensus,
    1017             :                                              get_microdesc_cache(),
    1018             :                                              1,
    1019             :                                              pending);
    1020           1 :   digest256map_free(pending, NULL);
    1021             : 
    1022           1 :   launch_descriptor_downloads(DIR_PURPOSE_FETCH_MICRODESC,
    1023             :                               missing, NULL, now);
    1024             : 
    1025           1 :   smartlist_free(missing);
    1026             : }
    1027             : 
    1028             : /** For every microdescriptor listed in the current microdescriptor consensus,
    1029             :  * update its last_listed field to be at least as recent as the publication
    1030             :  * time of the current microdescriptor consensus.
    1031             :  */
    1032             : void
    1033           1 : update_microdescs_from_networkstatus(time_t now)
    1034             : {
    1035           1 :   microdesc_cache_t *cache = get_microdesc_cache();
    1036           1 :   microdesc_t *md;
    1037           1 :   networkstatus_t *ns =
    1038           1 :     networkstatus_get_reasonably_live_consensus(now, FLAV_MICRODESC);
    1039             : 
    1040           1 :   if (! ns)
    1041             :     return;
    1042             : 
    1043           1 :   tor_assert(ns->flavor == FLAV_MICRODESC);
    1044             : 
    1045           4 :   SMARTLIST_FOREACH_BEGIN(ns->routerstatus_list, routerstatus_t *, rs) {
    1046           3 :     md = microdesc_cache_lookup_by_digest256(cache, rs->descriptor_digest);
    1047           3 :     if (md && ns->valid_after > md->last_listed)
    1048           0 :       md->last_listed = ns->valid_after;
    1049           3 :   } SMARTLIST_FOREACH_END(rs);
    1050             : }
    1051             : 
    1052             : /** Return true iff we should prefer to use microdescriptors rather than
    1053             :  * routerdescs for building circuits. */
    1054             : int
    1055      154063 : we_use_microdescriptors_for_circuits(const or_options_t *options)
    1056             : {
    1057      154063 :   if (options->UseMicrodescriptors == 0)
    1058         128 :     return 0; /* the user explicitly picked no */
    1059             :   return 1; /* yes and auto both mean yes */
    1060             : }
    1061             : 
    1062             : /** Return true iff we should try to download microdescriptors at all. */
    1063             : int
    1064           1 : we_fetch_microdescriptors(const or_options_t *options)
    1065             : {
    1066           1 :   if (directory_caches_dir_info(options))
    1067             :     return 1;
    1068           1 :   if (options->FetchUselessDescriptors)
    1069             :     return 1;
    1070           1 :   return we_use_microdescriptors_for_circuits(options);
    1071             : }
    1072             : 
    1073             : /** Return true iff we should try to download router descriptors at all. */
    1074             : int
    1075           1 : we_fetch_router_descriptors(const or_options_t *options)
    1076             : {
    1077           1 :   if (directory_caches_dir_info(options))
    1078             :     return 1;
    1079           1 :   if (options->FetchUselessDescriptors)
    1080             :     return 1;
    1081           1 :   return ! we_use_microdescriptors_for_circuits(options);
    1082             : }
    1083             : 
    1084             : /** Return the consensus flavor we actually want to use to build circuits. */
    1085      103543 : MOCK_IMPL(int,
    1086             : usable_consensus_flavor,(void))
    1087             : {
    1088      103543 :   if (we_use_microdescriptors_for_circuits(get_options())) {
    1089             :     return FLAV_MICRODESC;
    1090             :   } else {
    1091          17 :     return FLAV_NS;
    1092             :   }
    1093             : }

Generated by: LCOV version 1.14