More work towards making bridge users able to connect via bridges:

- demand options->Bridges and options->TunnelDirConns if 
    options->UseBridges is set.
  - after directory fetches, accept descriptors that aren't referenced by
    our networkstatuses, *if* they're for a configured bridge.
  - delay directory fetching until we have at least one bridge descriptor.
  - learn how to build a one-hop circuit when we have neither routerinfo
    nor routerstatus for our destination.
  - teach directory connections how to pick a bridge as the destination
    directory when doing non-anonymous fetches.
  - tolerate directory commands for which the dir_port is 0.
  - remember descriptors when the requested_resource was "authority", 
    rather than just ignoring them.
  - put bridges on our entry_guards list once we have a descriptor for them.
    When UseBridges is set, only pick entry guards that are bridges. Else
    vice versa.


svn:r10571
This commit is contained in:
Roger Dingledine 2007-06-12 09:17:23 +00:00
parent 622dd4927e
commit af658b7828
9 changed files with 235 additions and 69 deletions

View File

@ -219,10 +219,19 @@ Things we'd like to do in 0.2.0.x:
D Do we want to maintain our own set of entryguards that we use
after the bridge? Open research question; let's say no for 0.2.0
unless we learn otherwise.
- Ask all directory questions to bridge via BEGIN_DIR.
o if you don't have any routerinfos for your bridges, or you don't
like the ones you have, ask a new bridge for its server/authority.
. Ask all directory questions to bridge via BEGIN_DIR.
- use the bridges for dir fetches even when our dirport is open.
N - Design/implement the "local-status" or something like it, from the
"Descriptor purposes: how to tell them apart" section of
http://archives.seul.org/or/dev/May-2007/msg00008.html
- timeout and retry schedules for fetching bridge descriptors
- give extend_info_t a router_purpose again
- react faster to download networkstatuses after the first bridge
descriptor arrives
- be more robust to bridges being marked as down and leaving us
stranded without any known "running" bridges.
- Bridges operators (rudimentary version)
- Ability to act as dir cache without a dir port.
o Bridges publish to bridge authorities

View File

@ -192,14 +192,13 @@ $Id$
corresponding roughly to "status votes" above. They would compute the
result of the vote on the client side.
Authorities used sign documents using the same private keys they used
Authorities used to sign documents using the same private keys they used
for their roles as routers. This forced them to keep these extremely
sensitive keys in memory unencrypted.
All of the information in extra-info documents used to be kept in the
main descriptors.
1.2. Document meta-format
Router descriptors, directories, and running-routers documents all obey the

View File

@ -55,7 +55,6 @@ static int onion_extend_cpath(origin_circuit_t *circ);
static int count_acceptable_routers(smartlist_t *routers);
static int onion_append_hop(crypt_path_t **head_ptr, extend_info_t *choice);
static routerinfo_t *choose_random_entry(cpath_build_state_t *state);
static void entry_guards_changed(void);
/** Iterate over values of circ_id, starting from conn-\>next_circ_id,
@ -1581,10 +1580,10 @@ choose_good_middle_server(uint8_t purpose,
/** Pick a good entry server for the circuit to be built according to
* <b>state</b>. Don't reuse a chosen exit (if any), don't use this
* router (if we're an OR), and respect firewall settings; if we're
* using entry_guards, return one.
* configured to use entry guards, return one.
*
* If <b>state</b> is NULL, we're choosing routers to serve as entry
* nodes, not for any particular circuit.
* If <b>state</b> is NULL, we're choosing a router to serve as an entry
* guard, not for any particular circuit.
*/
static routerinfo_t *
choose_good_entry_server(uint8_t purpose, cpath_build_state_t *state)
@ -1721,49 +1720,62 @@ onion_append_hop(crypt_path_t **head_ptr, extend_info_t *choice)
return 0;
}
/** Allocate a new extend_info object based on the various arguments. */
extend_info_t *
extend_info_alloc(const char *nickname, const char *digest,
crypto_pk_env_t *onion_key,
uint32_t addr, uint16_t port)
{
extend_info_t *info = tor_malloc_zero(sizeof(extend_info_t));
memcpy(info->identity_digest, digest, DIGEST_LEN);
if (nickname)
strlcpy(info->nickname, nickname, sizeof(info->nickname));
else {
/* make one up */
}
if (onion_key)
info->onion_key = crypto_pk_dup_key(onion_key);
info->addr = addr;
info->port = port;
return info;
}
/** Allocate and return a new extend_info_t that can be used to build a
* circuit to or through the router <b>r</b>. */
extend_info_t *
extend_info_from_router(routerinfo_t *r)
{
extend_info_t *info;
tor_assert(r);
info = tor_malloc_zero(sizeof(extend_info_t));
strlcpy(info->nickname, r->nickname, sizeof(info->nickname));
memcpy(info->identity_digest, r->cache_info.identity_digest, DIGEST_LEN);
info->onion_key = crypto_pk_dup_key(r->onion_pkey);
info->addr = r->addr;
info->port = r->or_port;
info->router_purpose = r->purpose;
return info;
return extend_info_alloc(r->nickname, r->cache_info.identity_digest,
r->onion_pkey, r->addr, r->or_port);
}
#if 0
/** What router purpose is <b>digest</b>?
* It's a general purpose router unless it's on our bridges list.
*/
static uint8_t
get_router_purpose_from_digest(char *digest)
{
(void)digest;
return ROUTER_PURPOSE_GENERAL; /* XXX020 */
if (digest_is_a_bridge(digest))
return ROUTER_PURPOSE_BRIDGE;
return ROUTER_PURPOSE_GENERAL;
}
#endif
#if 0
/** Allocate and return a new extend_info_t that can be used to build a
* circuit to or through the router <b>r</b>. */
extend_info_t *
extend_info_from_routerstatus(routerstatus_t *s)
{
extend_info_t *info;
tor_assert(s);
info = tor_malloc_zero(sizeof(extend_info_t));
strlcpy(info->nickname, s->nickname, sizeof(info->nickname));
memcpy(info->identity_digest, s->identity_digest, DIGEST_LEN);
info->onion_key = NULL; /* routerstatus doesn't know this */
info->addr = s->addr;
info->port = s->or_port;
info->router_purpose = get_router_purpose_from_digest(info->identity_digest);
return info;
/* routerstatus doesn't know onion_key; leave it NULL */
return extend_info_alloc(s->nickname, s->identity_digest,
NULL, s->addr, s->or_port);
// get_router_purpose_from_digest(s->identity_digest));
}
#endif
/** Release storage held by an extend_info_t struct. */
void
@ -1834,7 +1846,9 @@ entry_guard_set_status(entry_guard_t *e, routerinfo_t *ri,
reason = "unlisted";
else if (!ri->is_running)
reason = "down";
else if (!ri->is_possible_guard &&
else if (options->UseBridges && ri->purpose != ROUTER_PURPOSE_BRIDGE)
reason = "not a bridge";
else if (!options->UseBridges && !ri->is_possible_guard &&
!router_nickname_is_in_list(ri, options->EntryNodes))
reason = "not recommended as a guard";
else if (router_nickname_is_in_list(ri, options->ExcludeNodes))
@ -1909,6 +1923,10 @@ entry_is_live(entry_guard_t *e, int need_uptime, int need_capacity,
r = router_get_by_digest(e->identity);
if (!r)
return NULL;
if (get_options()->UseBridges && r->purpose != ROUTER_PURPOSE_BRIDGE)
return NULL;
if (!get_options()->UseBridges && r->purpose != ROUTER_PURPOSE_GENERAL)
return NULL;
if (router_is_unreliable(r, need_uptime, need_capacity, 0))
return NULL;
if (firewall_is_fascist_or() &&
@ -2337,18 +2355,33 @@ entry_guards_prepend_from_config(void)
entry_guards_changed();
}
/** Pick a live (up and listed) entry guard from entry_guards, and
* make sure not to pick this circuit's exit. */
static routerinfo_t *
/** Return 1 if we're fine adding arbitrary routers out of the
* directory to our entry guard list. Else return 0. */
static int
can_grow_entry_list(or_options_t *options)
{
if (options->StrictEntryNodes)
return 0;
if (options->UseBridges)
return 0;
return 1;
}
/** Pick a live (up and listed) entry guard from entry_guards. If
* <b>state</b> is non-NULL, this is for a specific circuit --
* make sure not to pick this circuit's exit or any node in the
* exit's family. If <b>state</b> is NULL, we're looking for a random
* guard (likely a bridge). */
routerinfo_t *
choose_random_entry(cpath_build_state_t *state)
{
or_options_t *options = get_options();
smartlist_t *live_entry_guards = smartlist_create();
smartlist_t *exit_family = smartlist_create();
routerinfo_t *chosen_exit = build_state_get_exit_router(state);
routerinfo_t *chosen_exit = state?build_state_get_exit_router(state) : NULL;
routerinfo_t *r = NULL;
int need_uptime = state->need_uptime;
int need_capacity = state->need_capacity;
int need_uptime = state ? state->need_uptime : 0;
int need_capacity = state ? state->need_capacity : 0;
if (chosen_exit) {
smartlist_add(exit_family, chosen_exit);
@ -2361,7 +2394,7 @@ choose_random_entry(cpath_build_state_t *state)
if (should_add_entry_nodes)
entry_guards_prepend_from_config();
if (!options->StrictEntryNodes &&
if (can_grow_entry_list(options) &&
(! entry_guards ||
smartlist_len(entry_guards) < options->NumEntryGuards))
pick_entry_guards();
@ -2383,7 +2416,7 @@ choose_random_entry(cpath_build_state_t *state)
* using him.
* (We might get 2 live-but-crummy entry guards, but so be it.) */
if (smartlist_len(live_entry_guards) < 2) {
if (!options->StrictEntryNodes) {
if (can_grow_entry_list(options)) {
/* still no? try adding a new entry then */
/* XXX if guard doesn't imply fast and stable, then we need
* to tell add_an_entry_guard below what we want, or it might
@ -2403,7 +2436,7 @@ choose_random_entry(cpath_build_state_t *state)
need_capacity = 0;
goto retry;
}
/* live_entry_guards will be empty below. Oh well, we tried. */
/* live_entry_guards may be empty below. Oh well, we tried. */
}
r = smartlist_choose(live_entry_guards);
@ -2641,6 +2674,18 @@ clear_bridge_list(void)
smartlist_clear(bridge_list);
}
/** Return 1 if <b>digest</b> is one of our known bridges. */
int
identity_digest_is_a_bridge(const char *digest)
{
SMARTLIST_FOREACH(bridge_list, bridge_info_t *, bridge,
{
if (!memcmp(bridge->identity, digest, DIGEST_LEN))
return 1;
});
return 0;
}
/** Remember a new bridge at <b>addr</b>:<b>port</b>. If <b>digest</b>
* is set, it tells us the identity key too. */
void
@ -2660,7 +2705,7 @@ bridge_add_from_config(uint32_t addr, uint16_t port, char *digest)
* descriptor, fetch a new copy of its descriptor -- either directly
* from the bridge or via a bridge authority. */
void
learn_bridge_descriptors(void)
fetch_bridge_descriptors(void)
{
char address_buf[INET_NTOA_BUF_LEN+1];
struct in_addr in;
@ -2698,3 +2743,41 @@ learn_bridge_descriptors(void)
});
}
/** We just learned a descriptor for a bridge. See if that
* digest is in our entry guard list, and add it if not. */
void
learned_bridge_descriptor(routerinfo_t *ri)
{
tor_assert(ri);
tor_assert(ri->purpose == ROUTER_PURPOSE_BRIDGE);
if (get_options()->UseBridges) {
ri->is_running = 1;
add_an_entry_guard(ri);
log_notice(LD_DIR, "new bridge descriptor '%s'", ri->nickname);
}
}
/** Return 1 if any of our entry guards have descriptors that
* are marked with purpose 'bridge'. Else return 0.
*
* We use this function to decide if we're ready to start building
* circuits through our bridges, or if we need to wait until the
* directory "server/authority" requests finish. */
int
any_bridge_descriptors_known(void)
{
return choose_random_entry(NULL)!=NULL ? 1 : 0;
#if 0
routerinfo_t *ri;
if (!entry_guards)
entry_guards = smartlist_create();
SMARTLIST_FOREACH(entry_guards, entry_guard_t *, e,
{
ri = router_get_by_digest(e->identity);
if (ri && ri->purpose == ROUTER_PURPOSE_BRIDGE)
return 1;
});
return 0;
#endif
}

View File

@ -1034,18 +1034,22 @@ circuit_get_open_circ_or_launch(edge_connection_t *conn,
want_onehop, conn->chosen_exit_name);
if (want_onehop && conn->chosen_exit_name[0] == '$') {
/* We're asking for a one-hop circuit to a router that
* we don't have a routerinfo about. Hope we have a
* routerstatus or equivalent. */
routerstatus_t *s =
routerstatus_get_by_hexdigest(conn->chosen_exit_name+1);
if (s) {
extend_info = extend_info_from_routerstatus(s);
} else {
log_warn(LD_APP,
"Requested router '%s' is not known. Closing.",
conn->chosen_exit_name);
* we don't have a routerinfo about. Make up an extend_info. */
char digest[DIGEST_LEN];
char *hexdigest = conn->chosen_exit_name+1;
struct in_addr in;
if (strlen(hexdigest) < HEX_DIGEST_LEN ||
base16_decode(digest,DIGEST_LEN,hexdigest,HEX_DIGEST_LEN)<0) {
log_info(LD_DIR, "Broken exit digest on tunnel conn. Closing.");
return -1;
}
if (!tor_inet_aton(conn->socks_request->address, &in)) {
log_info(LD_DIR, "Broken address on tunnel conn. Closing.");
return -1;
}
extend_info = extend_info_alloc(conn->chosen_exit_name+1,
digest, NULL, ntohl(in.s_addr),
conn->socks_request->port);
} else {
/* We will need an onion key for the router, and we
* don't have one. Refuse or relax requirements. */

View File

@ -2914,6 +2914,10 @@ options_validate(or_options_t *old_options, or_options_t *options,
}
}
if (options->UseBridges && !options->Bridges)
REJECT("If you set UseBridges, you must specify at least one bridge.");
if (options->UseBridges && !options->TunnelDirConns)
REJECT("If you set UseBridges, you must set TunnelDirConns.");
if (options->Bridges) {
for (cl = options->Bridges; cl; cl = cl->next) {
if (parse_bridge_line(cl->value, 1)<0)

View File

@ -230,7 +230,21 @@ directory_get_from_dirserver(uint8_t dir_purpose, const char *resource,
if (!options->FetchServerDescriptors && type != HIDSERV_AUTHORITY)
return;
if (directconn) {
if (directconn && options->UseBridges) {
/* want to pick a bridge for which we have a descriptor. */
routerinfo_t *ri = choose_random_entry(NULL);
if (ri) {
directory_initiate_command(ri->address, ri->addr,
ri->or_port, 0,
1, ri->cache_info.identity_digest,
dir_purpose,
ROUTER_PURPOSE_GENERAL,
0, resource, NULL, 0);
} else
log_notice(LD_DIR, "Ignoring directory request, since no bridge "
"nodes are available yet.");
return;
} else if (directconn) {
if (prefer_authority) {
/* only ask authdirservers, and don't ask myself */
rs = router_pick_trusteddirserver(type, 1, 1,
@ -259,8 +273,7 @@ directory_get_from_dirserver(uint8_t dir_purpose, const char *resource,
directconn = 0; /* last resort: try routing it via Tor */
}
}
}
if (!directconn) {
} else { /* !directconn */
/* Never use fascistfirewall; we're going via Tor. */
if (dir_purpose == DIR_PURPOSE_FETCH_RENDDESC) {
/* only ask hidserv authorities, any of them will do */
@ -463,7 +476,7 @@ directory_initiate_command(const char *address, uint32_t addr,
tor_assert(address);
tor_assert(addr);
tor_assert(dir_port);
tor_assert(or_port || dir_port);
tor_assert(digest);
log_debug(LD_DIR, "anonymized %d, want_to_tunnel %d.",
@ -1221,7 +1234,7 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
smartlist_t *which = NULL;
int n_asked_for = 0;
log_info(LD_DIR,"Received %s (size %d) from server '%s:%d'",
was_ei ? "server info" : "extra server info",
was_ei ? "extra server info" : "server info",
(int)body_len, conn->_base.address, conn->_base.port);
if (was_ei)
note_request(was_compressed?"dl/extra.z":"dl/extra", orig_len);
@ -1255,13 +1268,15 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
tor_free(body); tor_free(headers); tor_free(reason);
return dir_okay ? 0 : -1;
}
/* Learn the routers, assuming we requested by fingerprint or "all".
* Right now, we only use "authority" to fetch ourself, so we don't want
* to risk replacing ourself with a router running at the addr:port we
* think we have.
/* Learn the routers, assuming we requested by fingerprint or "all"
* or "authority". (We use "authority" to fetch our own descriptor for
* testing, and to fetch bridge descriptors for bootstrapping.)
*/
/* XXX020 We now risk replacing ourself with a router running at
* the addr:port we think we have. Might want to check more carefully. */
if (which || (conn->requested_resource &&
!strcmpstart(conn->requested_resource, "all"))) {
(!strcmpstart(conn->requested_resource, "all") ||
!strcmpstart(conn->requested_resource, "authority")))) {
/* as we learn from them, we remove them from 'which' */
if (was_ei) {
router_load_extrainfo_from_string(body, NULL, SAVED_NOWHERE, which);

View File

@ -884,6 +884,8 @@ run_scheduled_events(time_t now)
if (time_to_reset_descriptor_failures < now) {
router_reset_descriptor_download_failures();
if (options->UseBridges)
fetch_bridge_descriptors(); /* XXX get this its own retry schedule -RD */
time_to_reset_descriptor_failures =
now + DESCRIPTOR_FAILURE_RESET_INTERVAL;
}
@ -951,6 +953,7 @@ run_scheduled_events(time_t now)
* and the rend cache. */
rep_history_clean(now - options->RephistTrackTime);
rend_cache_clean();
/* XXX020 we only clean this stuff if DirPort is set?! -RD */
}
/* 2b. Once per minute, regenerate and upload the descriptor if the old

View File

@ -1384,7 +1384,7 @@ typedef struct extend_info_t {
* display. */
char identity_digest[DIGEST_LEN]; /**< Hash of this router's identity key. */
uint16_t port; /**< OR port. */
uint8_t router_purpose; /**< General, controller, or bridge. */
// uint8_t router_purpose; /**< General, controller, or bridge. */
uint32_t addr; /**< IP address in host order. */
crypto_pk_env_t *onion_key; /**< Current onionskin key. */
} extend_info_t;
@ -2200,8 +2200,11 @@ int circuit_all_predicted_ports_handled(time_t now, int *need_uptime,
int circuit_append_new_exit(origin_circuit_t *circ, extend_info_t *info);
int circuit_extend_to_new_exit(origin_circuit_t *circ, extend_info_t *info);
void onion_append_to_cpath(crypt_path_t **head_ptr, crypt_path_t *new_hop);
extend_info_t *extend_info_alloc(const char *nickname, const char *digest,
crypto_pk_env_t *onion_key,
uint32_t addr, uint16_t port);
extend_info_t *extend_info_from_router(routerinfo_t *r);
extend_info_t *extend_info_from_routerstatus(routerstatus_t *s);
//extend_info_t *extend_info_from_routerstatus(routerstatus_t *s);
extend_info_t *extend_info_dup(extend_info_t *info);
void extend_info_free(extend_info_t *info);
routerinfo_t *build_state_get_exit_router(cpath_build_state_t *state);
@ -2211,15 +2214,19 @@ void entry_guards_compute_status(void);
int entry_guard_register_connect_status(const char *digest, int succeeded,
time_t now);
void entry_nodes_should_be_added(void);
void entry_guards_update_state(or_state_t *state);
routerinfo_t *choose_random_entry(cpath_build_state_t *state);
int entry_guards_parse_state(or_state_t *state, int set, char **msg);
void entry_guards_update_state(or_state_t *state);
int getinfo_helper_entry_guards(control_connection_t *conn,
const char *question, char **answer);
void entry_guards_free_all(void);
void clear_bridge_list(void);
int identity_digest_is_a_bridge(const char *digest);
void bridge_add_from_config(uint32_t addr, uint16_t port, char *digest);
void learn_bridge_descriptors(void);
void fetch_bridge_descriptors(void);
void learned_bridge_descriptor(routerinfo_t *ri);
int any_bridge_descriptors_known(void);
/********************************* circuitlist.c ***********************/
@ -3315,7 +3322,8 @@ local_routerstatus_t *router_get_combined_status_by_digest(const char *digest);
local_routerstatus_t *router_get_combined_status_by_descriptor_digest(
const char *digest);
routerstatus_t *routerstatus_get_by_hexdigest(const char *hexdigest);
//routerstatus_t *routerstatus_get_by_hexdigest(const char *hexdigest);
int should_delay_dir_fetches(or_options_t *options);
void update_networkstatus_downloads(time_t now);
void update_router_descriptor_downloads(time_t now);
void update_extrainfo_downloads(time_t now);

View File

@ -2340,7 +2340,7 @@ router_set_status(const char *digest, int up)
*
* This function should be called *after*
* routers_update_status_from_networkstatus; subsequently, you should call
* router_rebuild_store and control_event_descriptors_changed.
* router_rebuild_store and routerlist_descriptors_added.
*/
int
router_add_to_routerlist(routerinfo_t *router, const char **msg,
@ -2387,7 +2387,8 @@ router_add_to_routerlist(routerinfo_t *router, const char **msg,
/* Only check the descriptor digest against the network statuses when
* we are receiving in response to a fetch. */
if (!signed_desc_digest_is_recognized(&router->cache_info)) {
if (!signed_desc_digest_is_recognized(&router->cache_info) &&
!identity_digest_is_a_bridge(router->cache_info.identity_digest)) {
/* We asked for it, so some networkstatus must have listed it when we
* did. Save it if we're a cache in case somebody else asks for it. */
log_info(LD_DIR,
@ -2725,6 +2726,19 @@ routerlist_remove_old_routers(void)
digestmap_free(retain, NULL);
}
/** We just added a new descriptor that isn't of purpose
* ROUTER_PURPOSE_GENERAL. Take whatever extra steps we need. */
static void
routerlist_descriptors_added(smartlist_t *sl)
{
tor_assert(sl);
control_event_descriptors_changed(sl);
SMARTLIST_FOREACH(sl, routerinfo_t *, ri,
if (ri->purpose == ROUTER_PURPOSE_BRIDGE)
learned_bridge_descriptor(ri);
);
}
/**
* Code to parse a single router descriptor and insert it into the
* routerlist. Return -1 if the descriptor was ill-formed; 0 if the
@ -2753,7 +2767,7 @@ router_load_single_router(const char *s, uint8_t purpose, const char **msg)
return -1;
}
ri->purpose = purpose;
if (purpose != ROUTER_PURPOSE_GENERAL)
if (ri->purpose != ROUTER_PURPOSE_GENERAL)
ri->cache_info.do_not_cache = 1;
if (router_is_me(ri)) {
log_warn(LD_DIR, "Router's identity key matches mine; dropping.");
@ -2774,7 +2788,7 @@ router_load_single_router(const char *s, uint8_t purpose, const char **msg)
smartlist_free(lst);
return 0;
} else {
control_event_descriptors_changed(lst);
routerlist_descriptors_added(lst);
smartlist_free(lst);
log_debug(LD_DIR, "Added router to list");
return 1;
@ -2837,7 +2851,7 @@ router_load_routers_from_string(const char *s, const char *eos,
});
if (smartlist_len(changed))
control_event_descriptors_changed(changed);
routerlist_descriptors_added(changed);
routerlist_assert_ok(routerlist);
router_rebuild_store(0, 0);
@ -3272,6 +3286,7 @@ router_get_combined_status_by_nickname(const char *nickname,
return best;
}
#if 0
/** Find a routerstatus_t that corresponds to <b>hexdigest</b>, if
* any. Prefer ones that belong to authorities. */
routerstatus_t *
@ -3290,6 +3305,7 @@ routerstatus_get_by_hexdigest(const char *hexdigest)
return &(rs->status);
return NULL;
}
#endif
/** Return true iff any networkstatus includes a descriptor whose digest
* is that of <b>desc</b>. */
@ -3503,11 +3519,26 @@ update_networkstatus_client_downloads(time_t now)
smartlist_free(missing);
}
/** Return 1 if there's a reason we shouldn't try any directory
* fetches yet (e.g. we demand bridges and none are yet known).
* Else return 0. */
int
should_delay_dir_fetches(or_options_t *options)
{
if (options->UseBridges && !any_bridge_descriptors_known()) {
log_notice(LD_DIR, "delaying dir fetches");
return 1;
}
return 0;
}
/** Launch requests for networkstatus documents as appropriate. */
void
update_networkstatus_downloads(time_t now)
{
or_options_t *options = get_options();
if (should_delay_dir_fetches(options))
return;
if (options->DirPort)
update_networkstatus_cache_downloads(now);
else
@ -4729,6 +4760,8 @@ void
update_router_descriptor_downloads(time_t now)
{
or_options_t *options = get_options();
if (should_delay_dir_fetches(options))
return;
if (options->DirPort) {
update_router_descriptor_cache_downloads(now);
} else {
@ -4753,7 +4786,7 @@ should_download_extrainfo(signed_descriptor_t *sd,
!digestmap_get(pending, d));
}
/** Laucnch extrainfo downloads as needed. */
/** Launch extrainfo downloads as needed. */
void
update_extrainfo_downloads(time_t now)
{
@ -4764,6 +4797,8 @@ update_extrainfo_downloads(time_t now)
int i;
if (! options->DownloadExtraInfo)
return;
if (should_delay_dir_fetches(options))
return;
pending = digestmap_new();
list_pending_descriptor_downloads(pending, 1);
@ -4853,6 +4888,12 @@ update_router_have_minimum_dir_info(void)
routerlist_remove_old_routers();
networkstatus_list_clean(now);
if (should_delay_dir_fetches(get_options())) {
log_notice(LD_DIR, "no bridge descs known yet");
res = 0;
goto done;
}
n_authorities = get_n_v2_authorities();
n_ns = smartlist_len(networkstatus_list);
if (n_ns<=n_authorities/2) {