|  | @@ -718,9 +718,14 @@ authority_cert_dl_looks_uncertain(const char *id_digest)
 | 
	
		
			
				|  |  |   * <b>status</b>.  Additionally, try to have a non-expired certificate for
 | 
	
		
			
				|  |  |   * every V3 authority in trusted_dir_servers.  Don't fetch certificates we
 | 
	
		
			
				|  |  |   * already have.
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * If dir_hint is non-NULL, it's the identity digest for a directory that
 | 
	
		
			
				|  |  | + * we've just successfully retrieved a consensus from, so try it first to
 | 
	
		
			
				|  |  | + * fetch any missing certificates.
 | 
	
		
			
				|  |  |   **/
 | 
	
		
			
				|  |  |  void
 | 
	
		
			
				|  |  | -authority_certs_fetch_missing(networkstatus_t *status, time_t now)
 | 
	
		
			
				|  |  | +authority_certs_fetch_missing(networkstatus_t *status, time_t now,
 | 
	
		
			
				|  |  | +                              const char *dir_hint)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  |    /*
 | 
	
		
			
				|  |  |     * The pending_id digestmap tracks pending certificate downloads by
 | 
	
	
		
			
				|  | @@ -884,6 +889,37 @@ authority_certs_fetch_missing(networkstatus_t *status, time_t now)
 | 
	
		
			
				|  |  |      } SMARTLIST_FOREACH_END(voter);
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +  /* Look up the routerstatus for the dir_hint  */
 | 
	
		
			
				|  |  | +  const routerstatus_t *rs = NULL;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /* If we still need certificates, try the directory that just successfully
 | 
	
		
			
				|  |  | +   * served us a consensus or certificates.
 | 
	
		
			
				|  |  | +   * As soon as the directory fails to provide additional certificates, we try
 | 
	
		
			
				|  |  | +   * another, randomly selected directory. This avoids continual retries.
 | 
	
		
			
				|  |  | +   * (We only ever have one outstanding request per certificate.)
 | 
	
		
			
				|  |  | +   *
 | 
	
		
			
				|  |  | +   * Bridge clients won't find their bridges using this hint, so they will
 | 
	
		
			
				|  |  | +   * fall back to using directory_get_from_dirserver, which selects a bridge.
 | 
	
		
			
				|  |  | +   */
 | 
	
		
			
				|  |  | +  if (dir_hint) {
 | 
	
		
			
				|  |  | +    /* First try the consensus routerstatus, then the fallback
 | 
	
		
			
				|  |  | +     * routerstatus */
 | 
	
		
			
				|  |  | +    rs = router_get_consensus_status_by_id(dir_hint);
 | 
	
		
			
				|  |  | +    if (!rs) {
 | 
	
		
			
				|  |  | +      /* This will also find authorities */
 | 
	
		
			
				|  |  | +      const dir_server_t *ds = router_get_fallback_dirserver_by_digest(
 | 
	
		
			
				|  |  | +                                                                    dir_hint);
 | 
	
		
			
				|  |  | +      if (ds) {
 | 
	
		
			
				|  |  | +        rs = &ds->fake_status;
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if (!rs) {
 | 
	
		
			
				|  |  | +      log_warn(LD_BUG, "Directory %s delivered a consensus, but a "
 | 
	
		
			
				|  |  | +               "routerstatus could not be found for it.", dir_hint);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |    /* Do downloads by identity digest */
 | 
	
		
			
				|  |  |    if (smartlist_len(missing_id_digests) > 0) {
 | 
	
		
			
				|  |  |      int need_plus = 0;
 | 
	
	
		
			
				|  | @@ -913,11 +949,25 @@ authority_certs_fetch_missing(networkstatus_t *status, time_t now)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      if (smartlist_len(fps) > 1) {
 | 
	
		
			
				|  |  |        resource = smartlist_join_strings(fps, "", 0, NULL);
 | 
	
		
			
				|  |  | -      /* We want certs from mirrors, because they will almost always succeed.
 | 
	
		
			
				|  |  | -       */
 | 
	
		
			
				|  |  | -      directory_get_from_dirserver(DIR_PURPOSE_FETCH_CERTIFICATE, 0,
 | 
	
		
			
				|  |  | -                                   resource, PDS_RETRY_IF_NO_SERVERS,
 | 
	
		
			
				|  |  | -                                   DL_WANT_ANY_DIRSERVER);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +      /* If we've just downloaded a consensus from a directory, re-use that
 | 
	
		
			
				|  |  | +       * directory */
 | 
	
		
			
				|  |  | +      if (rs) {
 | 
	
		
			
				|  |  | +        /* Certificate fetches are one-hop, unless AllDirActionsPrivate is 1 */
 | 
	
		
			
				|  |  | +        int get_via_tor = get_options()->AllDirActionsPrivate;
 | 
	
		
			
				|  |  | +        const dir_indirection_t indirection = get_via_tor ? DIRIND_ANONYMOUS
 | 
	
		
			
				|  |  | +                                                          : DIRIND_ONEHOP;
 | 
	
		
			
				|  |  | +        directory_initiate_command_routerstatus(rs,
 | 
	
		
			
				|  |  | +                                                DIR_PURPOSE_FETCH_CERTIFICATE,
 | 
	
		
			
				|  |  | +                                                0, indirection, resource, NULL,
 | 
	
		
			
				|  |  | +                                                0, 0);
 | 
	
		
			
				|  |  | +      } else {
 | 
	
		
			
				|  |  | +        /* Otherwise, we want certs from a random fallback or directory
 | 
	
		
			
				|  |  | +         * mirror, because they will almost always succeed. */
 | 
	
		
			
				|  |  | +        directory_get_from_dirserver(DIR_PURPOSE_FETCH_CERTIFICATE, 0,
 | 
	
		
			
				|  |  | +                                     resource, PDS_RETRY_IF_NO_SERVERS,
 | 
	
		
			
				|  |  | +                                     DL_WANT_ANY_DIRSERVER);
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  |        tor_free(resource);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |      /* else we didn't add any: they were all pending */
 | 
	
	
		
			
				|  | @@ -960,11 +1010,25 @@ authority_certs_fetch_missing(networkstatus_t *status, time_t now)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      if (smartlist_len(fp_pairs) > 1) {
 | 
	
		
			
				|  |  |        resource = smartlist_join_strings(fp_pairs, "", 0, NULL);
 | 
	
		
			
				|  |  | -      /* We want certs from mirrors, because they will almost always succeed.
 | 
	
		
			
				|  |  | -       */
 | 
	
		
			
				|  |  | -      directory_get_from_dirserver(DIR_PURPOSE_FETCH_CERTIFICATE, 0,
 | 
	
		
			
				|  |  | -                                   resource, PDS_RETRY_IF_NO_SERVERS,
 | 
	
		
			
				|  |  | -                                   DL_WANT_ANY_DIRSERVER);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +      /* If we've just downloaded a consensus from a directory, re-use that
 | 
	
		
			
				|  |  | +       * directory */
 | 
	
		
			
				|  |  | +      if (rs) {
 | 
	
		
			
				|  |  | +        /* Certificate fetches are one-hop, unless AllDirActionsPrivate is 1 */
 | 
	
		
			
				|  |  | +        int get_via_tor = get_options()->AllDirActionsPrivate;
 | 
	
		
			
				|  |  | +        const dir_indirection_t indirection = get_via_tor ? DIRIND_ANONYMOUS
 | 
	
		
			
				|  |  | +                                                          : DIRIND_ONEHOP;
 | 
	
		
			
				|  |  | +        directory_initiate_command_routerstatus(rs,
 | 
	
		
			
				|  |  | +                                                DIR_PURPOSE_FETCH_CERTIFICATE,
 | 
	
		
			
				|  |  | +                                                0, indirection, resource, NULL,
 | 
	
		
			
				|  |  | +                                                0, 0);
 | 
	
		
			
				|  |  | +      } else {
 | 
	
		
			
				|  |  | +        /* Otherwise, we want certs from a random fallback or directory
 | 
	
		
			
				|  |  | +         * mirror, because they will almost always succeed. */
 | 
	
		
			
				|  |  | +        directory_get_from_dirserver(DIR_PURPOSE_FETCH_CERTIFICATE, 0,
 | 
	
		
			
				|  |  | +                                     resource, PDS_RETRY_IF_NO_SERVERS,
 | 
	
		
			
				|  |  | +                                     DL_WANT_ANY_DIRSERVER);
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  |        tor_free(resource);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |      /* else they were all pending */
 |