📄 directory.c
字号:
return;
if (!get_via_tor) {
if (options->UseBridges && type != BRIDGE_AUTHORITY) {
/* want to ask a running bridge for which we have a descriptor. */
/* XXX021 we assume that all of our bridges can answer any
* possible directory question. This won't be true forever. -RD */
/* It certainly is not true with conditional consensus downloading,
* so, for now, never assume the server supports that. */
routerinfo_t *ri = choose_random_entry(NULL);
if (ri) {
directory_initiate_command(ri->address, ri->addr,
ri->or_port, 0,
0, /* don't use conditional consensus url */
1, ri->cache_info.identity_digest,
dir_purpose,
router_purpose,
0, resource, NULL, 0, if_modified_since);
} else
log_notice(LD_DIR, "Ignoring directory request, since no bridge "
"nodes are available yet.");
return;
} else {
if (prefer_authority || type == BRIDGE_AUTHORITY) {
/* only ask authdirservers, and don't ask myself */
rs = router_pick_trusteddirserver(type, flags);
}
if (!rs && type != BRIDGE_AUTHORITY) {
/* anybody with a non-zero dirport will do */
rs = router_pick_directory_server(type, flags);
if (!rs) {
log_info(LD_DIR, "No router found for %s; falling back to "
"dirserver list.", dir_conn_purpose_to_string(dir_purpose));
rs = router_pick_trusteddirserver(type, flags);
if (!rs)
get_via_tor = 1; /* last resort: try routing it via Tor */
}
}
}
} else { /* get_via_tor */
/* Never use fascistfirewall; we're going via Tor. */
if (dir_purpose == DIR_PURPOSE_FETCH_RENDDESC) {
/* only ask hidserv authorities, any of them will do */
flags |= PDS_IGNORE_FASCISTFIREWALL|PDS_ALLOW_SELF;
rs = router_pick_trusteddirserver(HIDSERV_AUTHORITY, flags);
} else {
/* anybody with a non-zero dirport will do. Disregard firewalls. */
flags |= PDS_IGNORE_FASCISTFIREWALL;
rs = router_pick_directory_server(type, flags);
/* If we have any hope of building an indirect conn, we know some router
* descriptors. If (rs==NULL), we can't build circuits anyway, so
* there's no point in falling back to the authorities in this case. */
}
}
if (rs)
directory_initiate_command_routerstatus(rs, dir_purpose,
router_purpose,
get_via_tor,
resource, NULL, 0,
if_modified_since);
else {
log_notice(LD_DIR,
"While fetching directory info, "
"no running dirservers known. Will try again later. "
"(purpose %d)", dir_purpose);
if (!purpose_needs_anonymity(dir_purpose, router_purpose)) {
/* remember we tried them all and failed. */
directory_all_unreachable(time(NULL));
}
}
}
/** As directory_get_from_dirserver, but initiates a request to <i>every</i>
* directory authority other than ourself. Only for use by authorities when
* searching for missing information while voting. */
void
directory_get_from_all_authorities(uint8_t dir_purpose,
uint8_t router_purpose,
const char *resource)
{
tor_assert(dir_purpose == DIR_PURPOSE_FETCH_STATUS_VOTE ||
dir_purpose == DIR_PURPOSE_FETCH_DETACHED_SIGNATURES);
SMARTLIST_FOREACH(router_get_trusted_dir_servers(),
trusted_dir_server_t *, ds,
{
routerstatus_t *rs;
if (router_digest_is_me(ds->digest))
continue;
if (!(ds->type & V3_AUTHORITY))
continue;
rs = &ds->fake_status;
directory_initiate_command_routerstatus(rs, dir_purpose, router_purpose,
0, resource, NULL, 0, 0);
});
}
/** Launch a new connection to the directory server <b>status</b> to
* upload or download a server or rendezvous
* descriptor. <b>dir_purpose</b> determines what
* kind of directory connection we're launching, and must be one of
* DIR_PURPOSE_{FETCH|UPLOAD}_{DIR|RENDDESC|RENDDESC_V2}. <b>router_purpose</b>
* specifies the descriptor purposes we have in mind (currently only
* used for FETCH_DIR).
*
* When uploading, <b>payload</b> and <b>payload_len</b> determine the content
* of the HTTP post. Otherwise, <b>payload</b> should be NULL.
*
* When fetching a rendezvous descriptor, <b>resource</b> is the service ID we
* want to fetch.
*/
void
directory_initiate_command_routerstatus(routerstatus_t *status,
uint8_t dir_purpose,
uint8_t router_purpose,
int anonymized_connection,
const char *resource,
const char *payload,
size_t payload_len,
time_t if_modified_since)
{
routerinfo_t *router;
char address_buf[INET_NTOA_BUF_LEN+1];
struct in_addr in;
const char *address;
if ((router = router_get_by_digest(status->identity_digest))) {
address = router->address;
} else {
in.s_addr = htonl(status->addr);
tor_inet_ntoa(&in, address_buf, sizeof(address_buf));
address = address_buf;
}
directory_initiate_command(address, status->addr,
status->or_port, status->dir_port,
status->version_supports_conditional_consensus,
status->version_supports_begindir,
status->identity_digest,
dir_purpose, router_purpose,
anonymized_connection, resource,
payload, payload_len, if_modified_since);
}
/** Return true iff <b>conn</b> is the client side of a directory connection
* we launched to ourself in order to determine the reachability of our
* dir_port. */
static int
directory_conn_is_self_reachability_test(dir_connection_t *conn)
{
if (conn->requested_resource &&
!strcmpstart(conn->requested_resource,"authority")) {
routerinfo_t *me = router_get_my_routerinfo();
if (me &&
router_digest_is_me(conn->identity_digest) &&
me->addr == conn->_base.addr &&
me->dir_port == conn->_base.port)
return 1;
}
return 0;
}
/** Called when we are unable to complete the client's request to a directory
* server due to a network error: Mark the router as down and try again if
* possible.
*/
void
connection_dir_request_failed(dir_connection_t *conn)
{
if (directory_conn_is_self_reachability_test(conn)) {
routerinfo_t *me = router_get_my_routerinfo();
if (me)
control_event_server_status(LOG_WARN,
"REACHABILITY_FAILED DIRADDRESS=%s:%d",
me->address, me->dir_port);
return; /* this was a test fetch. don't retry. */
}
if (entry_list_can_grow(get_options()))
router_set_status(conn->identity_digest, 0); /* don't try him again */
if (conn->_base.purpose == DIR_PURPOSE_FETCH_DIR ||
conn->_base.purpose == DIR_PURPOSE_FETCH_RUNNING_LIST) {
log_info(LD_DIR, "Giving up on directory server at '%s:%d'; retrying",
conn->_base.address, conn->_base.port);
directory_get_from_dirserver(conn->_base.purpose, conn->router_purpose,
NULL, 0 /* don't retry_if_no_servers */);
} else if (conn->_base.purpose == DIR_PURPOSE_FETCH_NETWORKSTATUS) {
log_info(LD_DIR, "Giving up on directory server at '%s'; retrying",
conn->_base.address);
connection_dir_download_networkstatus_failed(conn, -1);
} else if (conn->_base.purpose == DIR_PURPOSE_FETCH_SERVERDESC ||
conn->_base.purpose == DIR_PURPOSE_FETCH_EXTRAINFO) {
log_info(LD_DIR, "Giving up on directory server at '%s'; retrying",
conn->_base.address);
connection_dir_download_routerdesc_failed(conn);
} else if (conn->_base.purpose == DIR_PURPOSE_FETCH_CONSENSUS) {
networkstatus_consensus_download_failed(0);
} else if (conn->_base.purpose == DIR_PURPOSE_FETCH_CERTIFICATE) {
log_info(LD_DIR, "Giving up on directory server at '%s'; retrying",
conn->_base.address);
connection_dir_download_cert_failed(conn, 0);
} else if (conn->_base.purpose == DIR_PURPOSE_FETCH_DETACHED_SIGNATURES) {
log_info(LD_DIR, "Giving up downloading detached signatures from '%s'",
conn->_base.address);
} else if (conn->_base.purpose == DIR_PURPOSE_FETCH_STATUS_VOTE) {
log_info(LD_DIR, "Giving up downloading votes from '%s'",
conn->_base.address);
}
}
/** Called when an attempt to download one or more network status
* documents on connection <b>conn</b> failed. Decide whether to
* retry the fetch now, later, or never.
*/
static void
connection_dir_download_networkstatus_failed(dir_connection_t *conn,
int status_code)
{
if (!conn->requested_resource) {
/* We never reached directory_send_command, which means that we never
* opened a network connection. Either we're out of sockets, or the
* network is down. Either way, retrying would be pointless. */
return;
}
if (!strcmpstart(conn->requested_resource, "all")) {
/* We're a non-authoritative directory cache; try again. Ignore status
* code, since we don't want to keep trying forever in a tight loop
* if all the authorities are shutting us out. */
smartlist_t *trusted_dirs = router_get_trusted_dir_servers();
SMARTLIST_FOREACH(trusted_dirs, trusted_dir_server_t *, ds,
download_status_failed(&ds->v2_ns_dl_status, 0));
directory_get_from_dirserver(conn->_base.purpose, conn->router_purpose,
"all.z", 0 /* don't retry_if_no_servers */);
} else if (!strcmpstart(conn->requested_resource, "fp/")) {
/* We were trying to download by fingerprint; mark them all as having
* failed, and possibly retry them later.*/
smartlist_t *failed = smartlist_create();
dir_split_resource_into_fingerprints(conn->requested_resource+3,
failed, NULL, 0, 0);
if (smartlist_len(failed)) {
dir_networkstatus_download_failed(failed, status_code);
SMARTLIST_FOREACH(failed, char *, cp, tor_free(cp));
}
smartlist_free(failed);
}
}
/** Called when an attempt to download one or more router descriptors
* or extra-info documents on connection <b>conn</b> failed.
*/
static void
connection_dir_download_routerdesc_failed(dir_connection_t *conn)
{
/* No need to increment the failure count for routerdescs, since
* it's not their fault. */
/* No need to relaunch descriptor downloads here: we already do it
* every 10 seconds (DESCRIPTOR_RETRY_INTERVAL) in main.c. */
tor_assert(conn->_base.purpose == DIR_PURPOSE_FETCH_SERVERDESC ||
conn->_base.purpose == DIR_PURPOSE_FETCH_EXTRAINFO);
(void) conn;
}
/** Called when an attempt to fetch a certificate fails. */
static void
connection_dir_download_cert_failed(dir_connection_t *conn, int status)
{
smartlist_t *failed;
tor_assert(conn->_base.purpose == DIR_PURPOSE_FETCH_CERTIFICATE);
if (!conn->requested_resource)
return;
failed = smartlist_create();
dir_split_resource_into_fingerprints(conn->requested_resource+3,
failed, NULL, 1, 0);
SMARTLIST_FOREACH(failed, char *, cp,
{
authority_cert_dl_failed(cp, status);
tor_free(cp);
});
smartlist_free(failed);
update_certificate_downloads(time(NULL));
}
/** Evaluate the situation and decide if we should use an encrypted
* "begindir-style" connection for this directory request.
* 1) If or_port is 0, or it's a direct conn and or_port is firewalled
* or we're a dir mirror, no.
* 2) If we prefer to avoid begindir conns, and we're not fetching or
* publishing a bridge relay descriptor, no.
* 3) Else yes.
*/
static int
directory_command_should_use_begindir(or_options_t *options, uint32_t addr,
int or_port, uint8_t router_purpose,
int anonymized_connection)
{
if (!or_port)
return 0; /* We don't know an ORPort -- no chance. */
if (!anonymized_connection)
if (!fascist_firewall_allows_address_or(addr, or_port) ||
directory_fetches_from_authorities(options))
return 0; /* We're firewalled or are acting like a relay -- also no. */
if (!options->TunnelDirConns &&
router_purpose != ROUTER_PURPOSE_BRIDGE)
return 0; /* We prefer to avoid using begindir conns. Fine. */
return 1;
}
/** Helper for directory_initiate_command_routerstatus: send the
* command to a server whose address is <b>address</b>, whose IP is
* <b>addr</b>, whose directory port is <b>dir_port</b>, whose tor version
* <b>supports_begindir</b>, and whose identity key digest is
* <b>digest</b>. */
void
directory_initiate_command(const char *address, uint32_t addr,
uint16_t or_port, uint16_t dir_port,
int supports_conditional_consensus,
int supports_begindir, const char *digest,
uint8_t dir_purpose, uint8_t router_purpose,
int anonymized_connection, const char *resource,
const char *payload, size_t payload_len,
time_t if_modified_since)
{
dir_connection_t *conn;
or_options_t *options = get_options();
int use_begindir = supports_begindir &&
directory_command_should_use_begindir(options, addr,
or_port, router_purpose, anonymized_connection);
tor_assert(address);
tor_assert(addr);
tor_assert(or_port || dir_port);
tor_assert(digest);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -