Back to index

tor  0.2.3.18-rc
microdesc.c
Go to the documentation of this file.
00001 /* Copyright (c) 2009-2012, The Tor Project, Inc. */
00002 /* See LICENSE for licensing information */
00003 
00004 #include "or.h"
00005 #include "circuitbuild.h"
00006 #include "config.h"
00007 #include "directory.h"
00008 #include "dirserv.h"
00009 #include "microdesc.h"
00010 #include "networkstatus.h"
00011 #include "nodelist.h"
00012 #include "policies.h"
00013 #include "router.h"
00014 #include "routerlist.h"
00015 #include "routerparse.h"
00016 
00021 struct microdesc_cache_t {
00024   HT_HEAD(microdesc_map, microdesc_t) map;
00025 
00027   char *cache_fname;
00029   char *journal_fname;
00031   tor_mmap_t *cache_content;
00033   size_t journal_len;
00035   size_t bytes_dropped;
00036 
00038   uint64_t total_len_seen;
00040   unsigned n_seen;
00041 };
00042 
00044 static INLINE unsigned int
00045 _microdesc_hash(microdesc_t *md)
00046 {
00047   unsigned *d = (unsigned*)md->digest;
00048 #if SIZEOF_INT == 4
00049   return d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[4] ^ d[5] ^ d[6] ^ d[7];
00050 #else
00051   return d[0] ^ d[1] ^ d[2] ^ d[3];
00052 #endif
00053 }
00054 
00056 static INLINE int
00057 _microdesc_eq(microdesc_t *a, microdesc_t *b)
00058 {
00059   return tor_memeq(a->digest, b->digest, DIGEST256_LEN);
00060 }
00061 
00062 HT_PROTOTYPE(microdesc_map, microdesc_t, node,
00063              _microdesc_hash, _microdesc_eq);
00064 HT_GENERATE(microdesc_map, microdesc_t, node,
00065              _microdesc_hash, _microdesc_eq, 0.6,
00066              malloc, realloc, free);
00067 
00072 static ssize_t
00073 dump_microdescriptor(FILE *f, microdesc_t *md, size_t *annotation_len_out)
00074 {
00075   ssize_t r = 0;
00076   size_t written;
00077   /* XXXX drops unkown annotations. */
00078   if (md->last_listed) {
00079     char buf[ISO_TIME_LEN+1];
00080     char annotation[ISO_TIME_LEN+32];
00081     format_iso_time(buf, md->last_listed);
00082     tor_snprintf(annotation, sizeof(annotation), "@last-listed %s\n", buf);
00083     if (fputs(annotation, f) < 0) {
00084       log_warn(LD_DIR,
00085                "Couldn't write microdescriptor annotation: %s",
00086                strerror(ferror(f)));
00087       return -1;
00088     }
00089     r += strlen(annotation);
00090     *annotation_len_out = r;
00091   } else {
00092     *annotation_len_out = 0;
00093   }
00094 
00095   md->off = (off_t) ftell(f);
00096   written = fwrite(md->body, 1, md->bodylen, f);
00097   if (written != md->bodylen) {
00098     log_warn(LD_DIR,
00099              "Couldn't dump microdescriptor (wrote %lu out of %lu): %s",
00100              (unsigned long)written, (unsigned long)md->bodylen,
00101              strerror(ferror(f)));
00102     return -1;
00103   }
00104   r += md->bodylen;
00105   return r;
00106 }
00107 
00110 static microdesc_cache_t *the_microdesc_cache = NULL;
00111 
00113 microdesc_cache_t *
00114 get_microdesc_cache(void)
00115 {
00116   if (PREDICT_UNLIKELY(the_microdesc_cache==NULL)) {
00117     microdesc_cache_t *cache = tor_malloc_zero(sizeof(microdesc_cache_t));
00118     HT_INIT(microdesc_map, &cache->map);
00119     cache->cache_fname = get_datadir_fname("cached-microdescs");
00120     cache->journal_fname = get_datadir_fname("cached-microdescs.new");
00121     microdesc_cache_reload(cache);
00122     the_microdesc_cache = cache;
00123   }
00124   return the_microdesc_cache;
00125 }
00126 
00127 /* There are three sources of microdescriptors:
00128    1) Generated by us while acting as a directory authority.
00129    2) Loaded from the cache on disk.
00130    3) Downloaded.
00131 */
00132 
00144 smartlist_t *
00145 microdescs_add_to_cache(microdesc_cache_t *cache,
00146                         const char *s, const char *eos, saved_location_t where,
00147                         int no_save, time_t listed_at,
00148                         smartlist_t *requested_digests256)
00149 {
00150   smartlist_t *descriptors, *added;
00151   const int allow_annotations = (where != SAVED_NOWHERE);
00152   const int copy_body = (where != SAVED_IN_CACHE);
00153 
00154   descriptors = microdescs_parse_from_string(s, eos,
00155                                              allow_annotations,
00156                                              copy_body);
00157   if (listed_at > 0) {
00158     SMARTLIST_FOREACH(descriptors, microdesc_t *, md,
00159                       md->last_listed = listed_at);
00160   }
00161   if (requested_digests256) {
00162     digestmap_t *requested; /* XXXX actuqlly we should just use a
00163                                digest256map */
00164     requested = digestmap_new();
00165     SMARTLIST_FOREACH(requested_digests256, const char *, cp,
00166       digestmap_set(requested, cp, (void*)1));
00167     SMARTLIST_FOREACH_BEGIN(descriptors, microdesc_t *, md) {
00168       if (digestmap_get(requested, md->digest)) {
00169         digestmap_set(requested, md->digest, (void*)2);
00170       } else {
00171         log_fn(LOG_PROTOCOL_WARN, LD_DIR, "Received non-requested microcdesc");
00172         microdesc_free(md);
00173         SMARTLIST_DEL_CURRENT(descriptors, md);
00174       }
00175     } SMARTLIST_FOREACH_END(md);
00176     SMARTLIST_FOREACH_BEGIN(requested_digests256, char *, cp) {
00177       if (digestmap_get(requested, cp) == (void*)2) {
00178         tor_free(cp);
00179         SMARTLIST_DEL_CURRENT(requested_digests256, cp);
00180       }
00181     } SMARTLIST_FOREACH_END(cp);
00182     digestmap_free(requested, NULL);
00183   }
00184 
00185   added = microdescs_add_list_to_cache(cache, descriptors, where, no_save);
00186   smartlist_free(descriptors);
00187   return added;
00188 }
00189 
00193 smartlist_t *
00194 microdescs_add_list_to_cache(microdesc_cache_t *cache,
00195                              smartlist_t *descriptors, saved_location_t where,
00196                              int no_save)
00197 {
00198   smartlist_t *added;
00199   open_file_t *open_file = NULL;
00200   FILE *f = NULL;
00201   //  int n_added = 0;
00202   ssize_t size = 0;
00203 
00204   if (where == SAVED_NOWHERE && !no_save) {
00205     f = start_writing_to_stdio_file(cache->journal_fname,
00206                                     OPEN_FLAGS_APPEND|O_BINARY,
00207                                     0600, &open_file);
00208     if (!f) {
00209       log_warn(LD_DIR, "Couldn't append to journal in %s: %s",
00210                cache->journal_fname, strerror(errno));
00211       return NULL;
00212     }
00213   }
00214 
00215   added = smartlist_new();
00216   SMARTLIST_FOREACH_BEGIN(descriptors, microdesc_t *, md) {
00217     microdesc_t *md2;
00218     md2 = HT_FIND(microdesc_map, &cache->map, md);
00219     if (md2) {
00220       /* We already had this one. */
00221       if (md2->last_listed < md->last_listed)
00222         md2->last_listed = md->last_listed;
00223       microdesc_free(md);
00224       if (where != SAVED_NOWHERE)
00225         cache->bytes_dropped += size;
00226       continue;
00227     }
00228 
00229     /* Okay, it's a new one. */
00230     if (f) {
00231       size_t annotation_len;
00232       size = dump_microdescriptor(f, md, &annotation_len);
00233       if (size < 0) {
00234         /* we already warned in dump_microdescriptor; */
00235         abort_writing_to_file(open_file);
00236         smartlist_clear(added);
00237         return added;
00238       }
00239       md->saved_location = SAVED_IN_JOURNAL;
00240       cache->journal_len += size;
00241     } else {
00242       md->saved_location = where;
00243     }
00244 
00245     md->no_save = no_save;
00246 
00247     HT_INSERT(microdesc_map, &cache->map, md);
00248     md->held_in_map = 1;
00249     smartlist_add(added, md);
00250     ++cache->n_seen;
00251     cache->total_len_seen += md->bodylen;
00252   } SMARTLIST_FOREACH_END(md);
00253 
00254   if (f)
00255     finish_writing_to_file(open_file); /*XXX Check me.*/
00256 
00257   {
00258     networkstatus_t *ns = networkstatus_get_latest_consensus();
00259     if (ns && ns->flavor == FLAV_MICRODESC)
00260       SMARTLIST_FOREACH(added, microdesc_t *, md, nodelist_add_microdesc(md));
00261   }
00262 
00263   if (smartlist_len(added))
00264     router_dir_info_changed();
00265 
00266   return added;
00267 }
00268 
00270 void
00271 microdesc_cache_clear(microdesc_cache_t *cache)
00272 {
00273   microdesc_t **entry, **next;
00274   for (entry = HT_START(microdesc_map, &cache->map); entry; entry = next) {
00275     microdesc_t *md = *entry;
00276     next = HT_NEXT_RMV(microdesc_map, &cache->map, entry);
00277     md->held_in_map = 0;
00278     microdesc_free(md);
00279   }
00280   HT_CLEAR(microdesc_map, &cache->map);
00281   if (cache->cache_content) {
00282     tor_munmap_file(cache->cache_content);
00283     cache->cache_content = NULL;
00284   }
00285   cache->total_len_seen = 0;
00286   cache->n_seen = 0;
00287   cache->bytes_dropped = 0;
00288 }
00289 
00292 int
00293 microdesc_cache_reload(microdesc_cache_t *cache)
00294 {
00295   struct stat st;
00296   char *journal_content;
00297   smartlist_t *added;
00298   tor_mmap_t *mm;
00299   int total = 0;
00300 
00301   microdesc_cache_clear(cache);
00302 
00303   mm = cache->cache_content = tor_mmap_file(cache->cache_fname);
00304   if (mm) {
00305     added = microdescs_add_to_cache(cache, mm->data, mm->data+mm->size,
00306                                     SAVED_IN_CACHE, 0, -1, NULL);
00307     if (added) {
00308       total += smartlist_len(added);
00309       smartlist_free(added);
00310     }
00311   }
00312 
00313   journal_content = read_file_to_str(cache->journal_fname,
00314                                      RFTS_IGNORE_MISSING, &st);
00315   if (journal_content) {
00316     cache->journal_len = (size_t) st.st_size;
00317     added = microdescs_add_to_cache(cache, journal_content,
00318                                     journal_content+st.st_size,
00319                                     SAVED_IN_JOURNAL, 0, -1, NULL);
00320     if (added) {
00321       total += smartlist_len(added);
00322       smartlist_free(added);
00323     }
00324     tor_free(journal_content);
00325   }
00326   log_notice(LD_DIR, "Reloaded microdescriptor cache.  Found %d descriptors.",
00327              total);
00328 
00329   microdesc_cache_rebuild(cache, 0 /* don't force */);
00330 
00331   return 0;
00332 }
00333 
00336 #define TOLERATE_MICRODESC_AGE (7*24*60*60)
00337 
00344 void
00345 microdesc_cache_clean(microdesc_cache_t *cache, time_t cutoff, int force)
00346 {
00347   microdesc_t **mdp, *victim;
00348   int dropped=0, kept=0;
00349   size_t bytes_dropped = 0;
00350   time_t now = time(NULL);
00351 
00352   /* If we don't know a live consensus, don't believe last_listed values: we
00353    * might be starting up after being down for a while. */
00354   if (! force &&
00355       ! networkstatus_get_reasonably_live_consensus(now, FLAV_MICRODESC))
00356       return;
00357 
00358   if (cutoff <= 0)
00359     cutoff = now - TOLERATE_MICRODESC_AGE;
00360 
00361   for (mdp = HT_START(microdesc_map, &cache->map); mdp != NULL; ) {
00362     if ((*mdp)->last_listed < cutoff) {
00363       ++dropped;
00364       victim = *mdp;
00365       mdp = HT_NEXT_RMV(microdesc_map, &cache->map, mdp);
00366       victim->held_in_map = 0;
00367       bytes_dropped += victim->bodylen;
00368       microdesc_free(victim);
00369     } else {
00370       ++kept;
00371       mdp = HT_NEXT(microdesc_map, &cache->map, mdp);
00372     }
00373   }
00374 
00375   if (dropped) {
00376     log_info(LD_DIR, "Removed %d/%d microdescriptors as old.",
00377              dropped,dropped+kept);
00378     cache->bytes_dropped += bytes_dropped;
00379   }
00380 }
00381 
00382 static int
00383 should_rebuild_md_cache(microdesc_cache_t *cache)
00384 {
00385     const size_t old_len =
00386       cache->cache_content ? cache->cache_content->size : 0;
00387     const size_t journal_len = cache->journal_len;
00388     const size_t dropped = cache->bytes_dropped;
00389 
00390     if (journal_len < 16384)
00391       return 0; /* Don't bother, not enough has happened yet. */
00392     if (dropped > (journal_len + old_len) / 3)
00393       return 1; /* We could save 1/3 or more of the currently used space. */
00394     if (journal_len > old_len / 2)
00395       return 1; /* We should append to the regular file */
00396 
00397     return 0;
00398 }
00399 
00404 int
00405 microdesc_cache_rebuild(microdesc_cache_t *cache, int force)
00406 {
00407   open_file_t *open_file;
00408   FILE *f;
00409   microdesc_t **mdp;
00410   smartlist_t *wrote;
00411   ssize_t size;
00412   off_t off = 0;
00413   int orig_size, new_size;
00414 
00415   if (cache == NULL) {
00416     cache = the_microdesc_cache;
00417     if (cache == NULL)
00418       return 0;
00419   }
00420 
00421   /* Remove dead descriptors */
00422   microdesc_cache_clean(cache, 0/*cutoff*/, 0/*force*/);
00423 
00424   if (!force && !should_rebuild_md_cache(cache))
00425     return 0;
00426 
00427   log_info(LD_DIR, "Rebuilding the microdescriptor cache...");
00428 
00429   orig_size = (int)(cache->cache_content ? cache->cache_content->size : 0);
00430   orig_size += (int)cache->journal_len;
00431 
00432   f = start_writing_to_stdio_file(cache->cache_fname,
00433                                   OPEN_FLAGS_REPLACE|O_BINARY,
00434                                   0600, &open_file);
00435   if (!f)
00436     return -1;
00437 
00438   wrote = smartlist_new();
00439 
00440   HT_FOREACH(mdp, microdesc_map, &cache->map) {
00441     microdesc_t *md = *mdp;
00442     size_t annotation_len;
00443     if (md->no_save)
00444       continue;
00445 
00446     size = dump_microdescriptor(f, md, &annotation_len);
00447     if (size < 0) {
00448       /* XXX handle errors from dump_microdescriptor() */
00449       /* log?  return -1?  die?  coredump the universe? */
00450       continue;
00451     }
00452     tor_assert(((size_t)size) == annotation_len + md->bodylen);
00453     md->off = off + annotation_len;
00454     off += size;
00455     if (md->saved_location != SAVED_IN_CACHE) {
00456       tor_free(md->body);
00457       md->saved_location = SAVED_IN_CACHE;
00458     }
00459     smartlist_add(wrote, md);
00460   }
00461 
00462   if (cache->cache_content)
00463     tor_munmap_file(cache->cache_content);
00464 
00465   finish_writing_to_file(open_file); /*XXX Check me.*/
00466 
00467   cache->cache_content = tor_mmap_file(cache->cache_fname);
00468 
00469   if (!cache->cache_content && smartlist_len(wrote)) {
00470     log_err(LD_DIR, "Couldn't map file that we just wrote to %s!",
00471             cache->cache_fname);
00472     smartlist_free(wrote);
00473     return -1;
00474   }
00475   SMARTLIST_FOREACH_BEGIN(wrote, microdesc_t *, md) {
00476     tor_assert(md->saved_location == SAVED_IN_CACHE);
00477     md->body = (char*)cache->cache_content->data + md->off;
00478     if (PREDICT_UNLIKELY(
00479              md->bodylen < 9 || fast_memneq(md->body, "onion-key", 9) != 0)) {
00480       /* XXXX once bug 2022 is solved, we can kill this block and turn it
00481        * into just the tor_assert(!memcmp) */
00482       off_t avail = cache->cache_content->size - md->off;
00483       char *bad_str;
00484       tor_assert(avail >= 0);
00485       bad_str = tor_strndup(md->body, MIN(128, (size_t)avail));
00486       log_err(LD_BUG, "After rebuilding microdesc cache, offsets seem wrong. "
00487               " At offset %d, I expected to find a microdescriptor starting "
00488               " with \"onion-key\".  Instead I got %s.",
00489               (int)md->off, escaped(bad_str));
00490       tor_free(bad_str);
00491       tor_assert(fast_memeq(md->body, "onion-key", 9));
00492     }
00493   } SMARTLIST_FOREACH_END(md);
00494 
00495   smartlist_free(wrote);
00496 
00497   write_str_to_file(cache->journal_fname, "", 1);
00498   cache->journal_len = 0;
00499   cache->bytes_dropped = 0;
00500 
00501   new_size = cache->cache_content ? (int)cache->cache_content->size : 0;
00502   log_info(LD_DIR, "Done rebuilding microdesc cache. "
00503            "Saved %d bytes; %d still used.",
00504            orig_size-new_size, new_size);
00505 
00506   return 0;
00507 }
00508 
00511 void
00512 microdesc_check_counts(void)
00513 {
00514   microdesc_t **mdp;
00515   if (!the_microdesc_cache)
00516     return;
00517 
00518   HT_FOREACH(mdp, microdesc_map, &the_microdesc_cache->map) {
00519     microdesc_t *md = *mdp;
00520     unsigned int found=0;
00521     const smartlist_t *nodes = nodelist_get_list();
00522     SMARTLIST_FOREACH(nodes, node_t *, node, {
00523         if (node->md == md) {
00524           ++found;
00525         }
00526       });
00527     tor_assert(found == md->held_by_nodes);
00528   }
00529 }
00530 
00533 void
00534 microdesc_free(microdesc_t *md)
00535 {
00536   if (!md)
00537     return;
00538 
00539   /* Make sure that the microdesc was really removed from the appropriate data
00540      structures. */
00541   if (md->held_in_map) {
00542     microdesc_cache_t *cache = get_microdesc_cache();
00543     microdesc_t *md2 = HT_FIND(microdesc_map, &cache->map, md);
00544     if (md2 == md) {
00545       log_warn(LD_BUG, "microdesc_free() called, but md was still in "
00546                "microdesc_map");
00547       HT_REMOVE(microdesc_map, &cache->map, md);
00548     } else {
00549       log_warn(LD_BUG, "microdesc_free() called with held_in_map set, but "
00550                "microdesc was not in the map.");
00551     }
00552     tor_fragile_assert();
00553   }
00554   if (md->held_by_nodes) {
00555     int found=0;
00556     const smartlist_t *nodes = nodelist_get_list();
00557     SMARTLIST_FOREACH(nodes, node_t *, node, {
00558         if (node->md == md) {
00559           ++found;
00560           node->md = NULL;
00561         }
00562       });
00563     if (found) {
00564       log_warn(LD_BUG, "microdesc_free() called, but md was still referenced "
00565                "%d node(s); held_by_nodes == %u", found, md->held_by_nodes);
00566     } else {
00567       log_warn(LD_BUG, "microdesc_free() called with held_by_nodes set to %u, "
00568                "but md was not referenced by any nodes", md->held_by_nodes);
00569     }
00570     tor_fragile_assert();
00571   }
00572   //tor_assert(md->held_in_map == 0);
00573   //tor_assert(md->held_by_nodes == 0);
00574 
00575   if (md->onion_pkey)
00576     crypto_pk_free(md->onion_pkey);
00577   if (md->body && md->saved_location != SAVED_IN_CACHE)
00578     tor_free(md->body);
00579 
00580   if (md->family) {
00581     SMARTLIST_FOREACH(md->family, char *, cp, tor_free(cp));
00582     smartlist_free(md->family);
00583   }
00584   short_policy_free(md->exit_policy);
00585 
00586   tor_free(md);
00587 }
00588 
00590 void
00591 microdesc_free_all(void)
00592 {
00593   if (the_microdesc_cache) {
00594     microdesc_cache_clear(the_microdesc_cache);
00595     tor_free(the_microdesc_cache->cache_fname);
00596     tor_free(the_microdesc_cache->journal_fname);
00597     tor_free(the_microdesc_cache);
00598   }
00599 }
00600 
00603 microdesc_t *
00604 microdesc_cache_lookup_by_digest256(microdesc_cache_t *cache, const char *d)
00605 {
00606   microdesc_t *md, search;
00607   if (!cache)
00608     cache = get_microdesc_cache();
00609   memcpy(search.digest, d, DIGEST256_LEN);
00610   md = HT_FIND(microdesc_map, &cache->map, &search);
00611   return md;
00612 }
00613 
00616 size_t
00617 microdesc_average_size(microdesc_cache_t *cache)
00618 {
00619   if (!cache)
00620     cache = get_microdesc_cache();
00621   if (!cache->n_seen)
00622     return 512;
00623   return (size_t)(cache->total_len_seen / cache->n_seen);
00624 }
00625 
00630 smartlist_t *
00631 microdesc_list_missing_digest256(networkstatus_t *ns, microdesc_cache_t *cache,
00632                                  int downloadable_only, digestmap_t *skip)
00633 {
00634   smartlist_t *result = smartlist_new();
00635   time_t now = time(NULL);
00636   tor_assert(ns->flavor == FLAV_MICRODESC);
00637   SMARTLIST_FOREACH_BEGIN(ns->routerstatus_list, routerstatus_t *, rs) {
00638     if (microdesc_cache_lookup_by_digest256(cache, rs->descriptor_digest))
00639       continue;
00640     if (downloadable_only &&
00641         !download_status_is_ready(&rs->dl_status, now,
00642                                   MAX_MICRODESC_DOWNLOAD_FAILURES))
00643       continue;
00644     if (skip && digestmap_get(skip, rs->descriptor_digest))
00645       continue;
00646     if (tor_mem_is_zero(rs->descriptor_digest, DIGEST256_LEN)) {
00647       log_info(LD_BUG, "Found an entry in networkstatus with no "
00648                "microdescriptor digest. (Router %s=%s at %s:%d.)",
00649                rs->nickname, hex_str(rs->identity_digest, DIGEST_LEN),
00650                fmt_addr32(rs->addr), rs->or_port);
00651       continue;
00652     }
00653     /* XXXX Also skip if we're a noncache and wouldn't use this router.
00654      * XXXX NM Microdesc
00655      */
00656     smartlist_add(result, rs->descriptor_digest);
00657   } SMARTLIST_FOREACH_END(rs);
00658   return result;
00659 }
00660 
00668 void
00669 update_microdesc_downloads(time_t now)
00670 {
00671   const or_options_t *options = get_options();
00672   networkstatus_t *consensus;
00673   smartlist_t *missing;
00674   digestmap_t *pending;
00675 
00676   if (should_delay_dir_fetches(options))
00677     return;
00678   if (directory_too_idle_to_fetch_descriptors(options, now))
00679     return;
00680 
00681   consensus = networkstatus_get_reasonably_live_consensus(now, FLAV_MICRODESC);
00682   if (!consensus)
00683     return;
00684 
00685   if (!we_fetch_microdescriptors(options))
00686     return;
00687 
00688   pending = digestmap_new();
00689   list_pending_microdesc_downloads(pending);
00690 
00691   missing = microdesc_list_missing_digest256(consensus,
00692                                              get_microdesc_cache(),
00693                                              1,
00694                                              pending);
00695   digestmap_free(pending, NULL);
00696 
00697   launch_descriptor_downloads(DIR_PURPOSE_FETCH_MICRODESC,
00698                               missing, NULL, now);
00699 
00700   smartlist_free(missing);
00701 }
00702 
00707 void
00708 update_microdescs_from_networkstatus(time_t now)
00709 {
00710   microdesc_cache_t *cache = get_microdesc_cache();
00711   microdesc_t *md;
00712   networkstatus_t *ns =
00713     networkstatus_get_reasonably_live_consensus(now, FLAV_MICRODESC);
00714 
00715   if (! ns)
00716     return;
00717 
00718   tor_assert(ns->flavor == FLAV_MICRODESC);
00719 
00720   SMARTLIST_FOREACH_BEGIN(ns->routerstatus_list, routerstatus_t *, rs) {
00721     md = microdesc_cache_lookup_by_digest256(cache, rs->descriptor_digest);
00722     if (md && ns->valid_after > md->last_listed)
00723       md->last_listed = ns->valid_after;
00724   } SMARTLIST_FOREACH_END(rs);
00725 }
00726 
00729 int
00730 we_use_microdescriptors_for_circuits(const or_options_t *options)
00731 {
00732   int ret = options->UseMicrodescriptors;
00733   if (ret == -1) {
00734     /* UseMicrodescriptors is "auto"; we need to decide: */
00735     /* If we are configured to use bridges and one of our bridges doesn't
00736      * know what a microdescriptor is, the answer is no. */
00737     if (options->UseBridges && any_bridges_dont_support_microdescriptors())
00738       return 0;
00739     /* Otherwise, we decide that we'll use microdescriptors iff we are
00740      * not a server, and we're not autofetching everything. */
00741     /* XXX023 what does not being a server have to do with it? also there's
00742      * a partitioning issue here where bridges differ from clients. */
00743     ret = !server_mode(options) && !options->FetchUselessDescriptors;
00744   }
00745   return ret;
00746 }
00747 
00749 int
00750 we_fetch_microdescriptors(const or_options_t *options)
00751 {
00752   if (directory_caches_dir_info(options))
00753     return 1;
00754   if (options->FetchUselessDescriptors)
00755     return 1;
00756   return we_use_microdescriptors_for_circuits(options);
00757 }
00758 
00760 int
00761 we_fetch_router_descriptors(const or_options_t *options)
00762 {
00763   if (directory_caches_dir_info(options))
00764     return 1;
00765   if (options->FetchUselessDescriptors)
00766     return 1;
00767   return ! we_use_microdescriptors_for_circuits(options);
00768 }
00769 
00771 int
00772 usable_consensus_flavor(void)
00773 {
00774   if (we_use_microdescriptors_for_circuits(get_options())) {
00775     return FLAV_MICRODESC;
00776   } else {
00777     return FLAV_NS;
00778   }
00779 }
00780