|  | @@ -834,6 +834,68 @@ authority_cert_dl_looks_uncertain(const char *id_digest)
 | 
	
		
			
				|  |  |    return n_failures >= N_AUTH_CERT_DL_FAILURES_TO_BUG_USER;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +/* Fetch the authority certificates specified in resource.
 | 
	
		
			
				|  |  | + * If we are a bridge client, and node is a configured bridge, fetch from node
 | 
	
		
			
				|  |  | + * using dir_hint as the fingerprint. Otherwise, if rs is not NULL, fetch from
 | 
	
		
			
				|  |  | + * rs. Otherwise, fetch from a random directory mirror. */
 | 
	
		
			
				|  |  | +static void
 | 
	
		
			
				|  |  | +authority_certs_fetch_resource_impl(const char *resource,
 | 
	
		
			
				|  |  | +                                    const char *dir_hint,
 | 
	
		
			
				|  |  | +                                    const node_t *node,
 | 
	
		
			
				|  |  | +                                    const routerstatus_t *rs)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +  const or_options_t *options = get_options();
 | 
	
		
			
				|  |  | +  int get_via_tor = purpose_needs_anonymity(DIR_PURPOSE_FETCH_CERTIFICATE, 0);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /* Make sure bridge clients never connect to anything but a bridge */
 | 
	
		
			
				|  |  | +  if (options->UseBridges) {
 | 
	
		
			
				|  |  | +    if (node && !node_is_a_configured_bridge(node)) {
 | 
	
		
			
				|  |  | +      /* If we're using bridges, and node is not a bridge, use a 3-hop path. */
 | 
	
		
			
				|  |  | +      get_via_tor = 1;
 | 
	
		
			
				|  |  | +    } else if (!node) {
 | 
	
		
			
				|  |  | +      /* If we're using bridges, and there's no node, use a 3-hop path. */
 | 
	
		
			
				|  |  | +      get_via_tor = 1;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  const dir_indirection_t indirection = get_via_tor ? DIRIND_ANONYMOUS
 | 
	
		
			
				|  |  | +                                                    : DIRIND_ONEHOP;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /* If we've just downloaded a consensus from a bridge, re-use that
 | 
	
		
			
				|  |  | +   * bridge */
 | 
	
		
			
				|  |  | +  if (options->UseBridges && node && !get_via_tor) {
 | 
	
		
			
				|  |  | +    /* clients always make OR connections to bridges */
 | 
	
		
			
				|  |  | +    tor_addr_port_t or_ap;
 | 
	
		
			
				|  |  | +    /* we are willing to use a non-preferred address if we need to */
 | 
	
		
			
				|  |  | +    fascist_firewall_choose_address_node(node, FIREWALL_OR_CONNECTION, 0,
 | 
	
		
			
				|  |  | +                                         &or_ap);
 | 
	
		
			
				|  |  | +    directory_initiate_command(&or_ap.addr, or_ap.port,
 | 
	
		
			
				|  |  | +                               NULL, 0, /*no dirport*/
 | 
	
		
			
				|  |  | +                               dir_hint,
 | 
	
		
			
				|  |  | +                               DIR_PURPOSE_FETCH_CERTIFICATE,
 | 
	
		
			
				|  |  | +                               0,
 | 
	
		
			
				|  |  | +                               indirection,
 | 
	
		
			
				|  |  | +                               resource, NULL, 0, 0);
 | 
	
		
			
				|  |  | +    return;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  if (rs) {
 | 
	
		
			
				|  |  | +    /* If we've just downloaded a consensus from a directory, re-use that
 | 
	
		
			
				|  |  | +     * directory */
 | 
	
		
			
				|  |  | +    directory_initiate_command_routerstatus(rs,
 | 
	
		
			
				|  |  | +                                            DIR_PURPOSE_FETCH_CERTIFICATE,
 | 
	
		
			
				|  |  | +                                            0, indirection, resource, NULL,
 | 
	
		
			
				|  |  | +                                            0, 0);
 | 
	
		
			
				|  |  | +    return;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /* 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);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  /** Try to download any v3 authority certificates that we may be missing.  If
 | 
	
		
			
				|  |  |   * <b>status</b> is provided, try to get all the ones that were used to sign
 | 
	
		
			
				|  |  |   * <b>status</b>.  Additionally, try to have a non-expired certificate for
 | 
	
	
		
			
				|  | @@ -865,12 +927,13 @@ authority_certs_fetch_missing(networkstatus_t *status, time_t now,
 | 
	
		
			
				|  |  |    smartlist_t *missing_cert_digests, *missing_id_digests;
 | 
	
		
			
				|  |  |    char *resource = NULL;
 | 
	
		
			
				|  |  |    cert_list_t *cl;
 | 
	
		
			
				|  |  | -  const int cache = directory_caches_unknown_auth_certs(get_options());
 | 
	
		
			
				|  |  | +  const or_options_t *options = get_options();
 | 
	
		
			
				|  |  | +  const int cache = directory_caches_unknown_auth_certs(options);
 | 
	
		
			
				|  |  |    fp_pair_t *fp_tmp = NULL;
 | 
	
		
			
				|  |  |    char id_digest_str[2*DIGEST_LEN+1];
 | 
	
		
			
				|  |  |    char sk_digest_str[2*DIGEST_LEN+1];
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  if (should_delay_dir_fetches(get_options(), NULL))
 | 
	
		
			
				|  |  | +  if (should_delay_dir_fetches(options, NULL))
 | 
	
		
			
				|  |  |      return;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    pending_cert = fp_pair_map_new();
 | 
	
	
		
			
				|  | @@ -911,7 +974,7 @@ authority_certs_fetch_missing(networkstatus_t *status, time_t now,
 | 
	
		
			
				|  |  |      } SMARTLIST_FOREACH_END(cert);
 | 
	
		
			
				|  |  |      if (!found &&
 | 
	
		
			
				|  |  |          download_status_is_ready(&(cl->dl_status_by_id), now,
 | 
	
		
			
				|  |  | -                                 get_options()->TestingCertMaxDownloadTries) &&
 | 
	
		
			
				|  |  | +                                 options->TestingCertMaxDownloadTries) &&
 | 
	
		
			
				|  |  |          !digestmap_get(pending_id, ds->v3_identity_digest)) {
 | 
	
		
			
				|  |  |        log_info(LD_DIR,
 | 
	
		
			
				|  |  |                 "No current certificate known for authority %s "
 | 
	
	
		
			
				|  | @@ -973,7 +1036,7 @@ authority_certs_fetch_missing(networkstatus_t *status, time_t now,
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |          if (download_status_is_ready_by_sk_in_cl(
 | 
	
		
			
				|  |  |                cl, sig->signing_key_digest,
 | 
	
		
			
				|  |  | -              now, get_options()->TestingCertMaxDownloadTries) &&
 | 
	
		
			
				|  |  | +              now, options->TestingCertMaxDownloadTries) &&
 | 
	
		
			
				|  |  |              !fp_pair_map_get_by_digests(pending_cert,
 | 
	
		
			
				|  |  |                                          voter->identity_digest,
 | 
	
		
			
				|  |  |                                          sig->signing_key_digest)) {
 | 
	
	
		
			
				|  | @@ -1010,7 +1073,10 @@ authority_certs_fetch_missing(networkstatus_t *status, time_t now,
 | 
	
		
			
				|  |  |      } SMARTLIST_FOREACH_END(voter);
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  /* Look up the routerstatus for the dir_hint  */
 | 
	
		
			
				|  |  | +  /* Bridge clients look up the node for the dir_hint  */
 | 
	
		
			
				|  |  | +  const node_t *node = NULL;
 | 
	
		
			
				|  |  | +  /* All clients, including bridge clients, look up the routerstatus for the
 | 
	
		
			
				|  |  | +   * dir_hint */
 | 
	
		
			
				|  |  |    const routerstatus_t *rs = NULL;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    /* If we still need certificates, try the directory that just successfully
 | 
	
	
		
			
				|  | @@ -1018,12 +1084,16 @@ authority_certs_fetch_missing(networkstatus_t *status, time_t now,
 | 
	
		
			
				|  |  |     * 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
 | 
	
		
			
				|  |  | +    if (options->UseBridges) {
 | 
	
		
			
				|  |  | +      /* Bridge clients try the nodelist. If the dir_hint is from an authority,
 | 
	
		
			
				|  |  | +       * or something else fetched over tor, we won't find the node here, but
 | 
	
		
			
				|  |  | +       * we will find the rs. */
 | 
	
		
			
				|  |  | +      node = node_get_by_id(dir_hint);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /* All clients try the consensus routerstatus, then the fallback
 | 
	
		
			
				|  |  |       * routerstatus */
 | 
	
		
			
				|  |  |      rs = router_get_consensus_status_by_id(dir_hint);
 | 
	
		
			
				|  |  |      if (!rs) {
 | 
	
	
		
			
				|  | @@ -1035,9 +1105,11 @@ authority_certs_fetch_missing(networkstatus_t *status, time_t now,
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if (!rs) {
 | 
	
		
			
				|  |  | -      log_warn(LD_BUG, "Directory %s delivered a consensus, but a "
 | 
	
		
			
				|  |  | -               "routerstatus could not be found for it.", dir_hint);
 | 
	
		
			
				|  |  | +    if (!node && !rs) {
 | 
	
		
			
				|  |  | +      log_warn(LD_BUG, "Directory %s delivered a consensus, but %s"
 | 
	
		
			
				|  |  | +               "no routerstatus could be found for it.",
 | 
	
		
			
				|  |  | +               options->UseBridges ? "no node and " : "",
 | 
	
		
			
				|  |  | +               hex_str(dir_hint, DIGEST_LEN));
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -1070,25 +1142,9 @@ authority_certs_fetch_missing(networkstatus_t *status, time_t now,
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      if (smartlist_len(fps) > 1) {
 | 
	
		
			
				|  |  |        resource = smartlist_join_strings(fps, "", 0, NULL);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -      /* 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);
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | +      /* node and rs are directories that just gave us a consensus or
 | 
	
		
			
				|  |  | +       * certificates */
 | 
	
		
			
				|  |  | +      authority_certs_fetch_resource_impl(resource, dir_hint, node, rs);
 | 
	
		
			
				|  |  |        tor_free(resource);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |      /* else we didn't add any: they were all pending */
 | 
	
	
		
			
				|  | @@ -1131,25 +1187,9 @@ authority_certs_fetch_missing(networkstatus_t *status, time_t now,
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      if (smartlist_len(fp_pairs) > 1) {
 | 
	
		
			
				|  |  |        resource = smartlist_join_strings(fp_pairs, "", 0, NULL);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -      /* 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);
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | +      /* node and rs are directories that just gave us a consensus or
 | 
	
		
			
				|  |  | +       * certificates */
 | 
	
		
			
				|  |  | +      authority_certs_fetch_resource_impl(resource, dir_hint, node, rs);
 | 
	
		
			
				|  |  |        tor_free(resource);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |      /* else they were all pending */
 |