void
update_extrainfo_downloads(time_t now)
{
  const or_options_t *options = get_options();
  routerlist_t *rl;
  smartlist_t *wanted;
  digestmap_t *pending;
  int old_routers, i, max_dl_per_req;
  int n_no_ei = 0, n_pending = 0, n_have = 0, n_delay = 0, n_bogus[2] = {0,0};
  if (! options->DownloadExtraInfo)
    return;
  if (should_delay_dir_fetches(options, NULL))
    return;
  if (!router_have_minimum_dir_info())
    return;

  pending = digestmap_new();
  list_pending_descriptor_downloads(pending, 1);
  rl = router_get_routerlist();
  wanted = smartlist_new();
  for (old_routers = 0; old_routers < 2; ++old_routers) {
    smartlist_t *lst = old_routers ? rl->old_routers : rl->routers;
    for (i = 0; i < smartlist_len(lst); ++i) {
      signed_descriptor_t *sd;
      char *d;
      if (old_routers)
        sd = smartlist_get(lst, i);
      else
        sd = &((routerinfo_t*)smartlist_get(lst, i))->cache_info;
      if (sd->is_extrainfo)
        continue; /* This should never happen. */
      if (old_routers && !router_get_by_id_digest(sd->identity_digest))
        continue; /* Couldn't check the signature if we got it. */
      if (sd->extrainfo_is_bogus)
        continue;
      d = sd->extra_info_digest;
      if (tor_digest_is_zero(d)) {
        ++n_no_ei;
        continue;
      }
      if (eimap_get(rl->extra_info_map, d)) {
        ++n_have;
        continue;
      }
      if (!download_status_is_ready(&sd->ei_dl_status, now,
                          options->TestingDescriptorMaxDownloadTries)) {
        ++n_delay;
        continue;
      }
      if (digestmap_get(pending, d)) {
        ++n_pending;
        continue;
      }

      const signed_descriptor_t *sd2 = router_get_by_extrainfo_digest(d);
      if (sd2 != sd) {
        if (sd2 != NULL) {
          char d1[HEX_DIGEST_LEN+1], d2[HEX_DIGEST_LEN+1];
          char d3[HEX_DIGEST_LEN+1], d4[HEX_DIGEST_LEN+1];
          base16_encode(d1, sizeof(d1), sd->identity_digest, DIGEST_LEN);
          base16_encode(d2, sizeof(d2), sd2->identity_digest, DIGEST_LEN);
          base16_encode(d3, sizeof(d3), d, DIGEST_LEN);
          base16_encode(d4, sizeof(d3), sd2->extra_info_digest, DIGEST_LEN);

          log_info(LD_DIR, "Found an entry in %s with mismatched "
                   "router_get_by_extrainfo_digest() value. This has ID %s "
                   "but the entry in the map has ID %s. This has EI digest "
                   "%s and the entry in the map has EI digest %s.",
                   old_routers?"old_routers":"routers",
                   d1, d2, d3, d4);
        } else {
          char d1[HEX_DIGEST_LEN+1], d2[HEX_DIGEST_LEN+1];
          base16_encode(d1, sizeof(d1), sd->identity_digest, DIGEST_LEN);
          base16_encode(d2, sizeof(d2), d, DIGEST_LEN);

          log_info(LD_DIR, "Found an entry in %s with NULL "
                   "router_get_by_extrainfo_digest() value. This has ID %s "
                   "and EI digest %s.",
                   old_routers?"old_routers":"routers",
                   d1, d2);
        }
        ++n_bogus[old_routers];
        continue;
      }
      smartlist_add(wanted, d);
    }
  }
  digestmap_free(pending, NULL);

  log_info(LD_DIR, "Extrainfo download status: %d router with no ei, %d "
           "with present ei, %d delaying, %d pending, %d downloadable, %d "
           "bogus in routers, %d bogus in old_routers",
           n_no_ei, n_have, n_delay, n_pending, smartlist_len(wanted),
           n_bogus[0], n_bogus[1]);

  smartlist_shuffle(wanted);

  max_dl_per_req = max_dl_per_request(options, DIR_PURPOSE_FETCH_EXTRAINFO);
  for (i = 0; i < smartlist_len(wanted); i += max_dl_per_req) {
    initiate_descriptor_downloads(NULL, DIR_PURPOSE_FETCH_EXTRAINFO,
                                  wanted, i, i+max_dl_per_req,
                PDS_RETRY_IF_NO_SERVERS|PDS_NO_EXISTING_SERVERDESC_FETCH);
  }

  smartlist_free(wanted);
void
update_extrainfo_downloads(time_t now)
{
  const or_options_t *options = get_options();
  routerlist_t *rl;
  smartlist_t *wanted;
  digestmap_t *pending;
  int old_routers, i, max_dl_per_req;
  int n_no_ei = 0, n_pending = 0, n_have = 0, n_delay = 0, n_bogus[2] = {0,0};
  if (! options->DownloadExtraInfo)
    return;
  if (should_delay_dir_fetches(options, NULL))
    return;
  if (!router_have_minimum_dir_info())
    return;

  pending = digestmap_new();
  list_pending_descriptor_downloads(pending, 1);
  rl = router_get_routerlist();
  wanted = smartlist_new();
  for (old_routers = 0; old_routers < 2; ++old_routers) {
    smartlist_t *lst = old_routers ? rl->old_routers : rl->routers;
    for (i = 0; i < smartlist_len(lst); ++i) {
      signed_descriptor_t *sd;
      char *d;
      if (old_routers)
        sd = smartlist_get(lst, i);
      else
        sd = &((routerinfo_t*)smartlist_get(lst, i))->cache_info;
      if (sd->is_extrainfo)
        continue;  
      if (old_routers && !router_get_by_id_digest(sd->identity_digest))
        continue;  
      if (sd->extrainfo_is_bogus)
        continue;
      d = sd->extra_info_digest;
      if (tor_digest_is_zero(d)) {
        ++n_no_ei;
        continue;
      }
      if (eimap_get(rl->extra_info_map, d)) {
        ++n_have;
        continue;
      }
      if (!download_status_is_ready(&sd->ei_dl_status, now,
                          options->TestingDescriptorMaxDownloadTries)) {
        ++n_delay;
        continue;
      }
      if (digestmap_get(pending, d)) {
        ++n_pending;
        continue;
      }

      const signed_descriptor_t *sd2 = router_get_by_extrainfo_digest(d);
      if (sd2 != sd) {
        if (sd2 != NULL) {
          char d1[HEX_DIGEST_LEN+1], d2[HEX_DIGEST_LEN+1];
          char d3[HEX_DIGEST_LEN+1], d4[HEX_DIGEST_LEN+1];
          base16_encode(d1, sizeof(d1), sd->identity_digest, DIGEST_LEN);
          base16_encode(d2, sizeof(d2), sd2->identity_digest, DIGEST_LEN);
          base16_encode(d3, sizeof(d3), d, DIGEST_LEN);
          base16_encode(d4, sizeof(d3), sd2->extra_info_digest, DIGEST_LEN);

          log_info(LD_DIR, "Found an entry in %s with mismatched "
                   "router_get_by_extrainfo_digest() value. This has ID %s "
                   "but the entry in the map has ID %s. This has EI digest "
                   "%s and the entry in the map has EI digest %s.",
                   old_routers?"old_routers":"routers",
                   d1, d2, d3, d4);
        } else {
          char d1[HEX_DIGEST_LEN+1], d2[HEX_DIGEST_LEN+1];
          base16_encode(d1, sizeof(d1), sd->identity_digest, DIGEST_LEN);
          base16_encode(d2, sizeof(d2), d, DIGEST_LEN);

          log_info(LD_DIR, "Found an entry in %s with NULL "
                   "router_get_by_extrainfo_digest() value. This has ID %s "
                   "and EI digest %s.",
                   old_routers?"old_routers":"routers",
                   d1, d2);
        }
        ++n_bogus[old_routers];
        continue;
      }
      smartlist_add(wanted, d);
    }
  }
  digestmap_free(pending, NULL);

  log_info(LD_DIR, "Extrainfo download status: %d router with no ei, %d "
           "with present ei, %d delaying, %d pending, %d downloadable, %d "
           "bogus in routers, %d bogus in old_routers",
           n_no_ei, n_have, n_delay, n_pending, smartlist_len(wanted),
           n_bogus[0], n_bogus[1]);

  smartlist_shuffle(wanted);

  max_dl_per_req = max_dl_per_request(options, DIR_PURPOSE_FETCH_EXTRAINFO);
  for (i = 0; i < smartlist_len(wanted); i += max_dl_per_req) {
    initiate_descriptor_downloads(NULL, DIR_PURPOSE_FETCH_EXTRAINFO,
                                  wanted, i, i+max_dl_per_req,
                PDS_RETRY_IF_NO_SERVERS|PDS_NO_EXISTING_SERVERDESC_FETCH);
  }

  smartlist_free(wanted);