📄 directory.c
字号:
/* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
* Copyright (c) 2007-2008, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/* $Id$ */
const char directory_c_id[] =
"$Id$";
#include "or.h"
#if defined(EXPORTMALLINFO) && defined(HAVE_MALLOC_H) && defined(HAVE_MALLINFO)
#include <malloc.h>
#endif
/**
* \file directory.c
* \brief Code to send and fetch directories and router
* descriptors via HTTP. Directories use dirserv.c to generate the
* results; clients use routers.c to parse them.
**/
/* In-points to directory.c:
*
* - directory_post_to_dirservers(), called from
* router_upload_dir_desc_to_dirservers() in router.c
* upload_service_descriptor() in rendservice.c
* - directory_get_from_dirserver(), called from
* rend_client_refetch_renddesc() in rendclient.c
* run_scheduled_events() in main.c
* do_hup() in main.c
* - connection_dir_process_inbuf(), called from
* connection_process_inbuf() in connection.c
* - connection_dir_finished_flushing(), called from
* connection_finished_flushing() in connection.c
* - connection_dir_finished_connecting(), called from
* connection_finished_connecting() in connection.c
*/
static void directory_send_command(dir_connection_t *conn,
int purpose, int direct, const char *resource,
const char *payload, size_t payload_len,
int supports_conditional_consensus,
time_t if_modified_since);
static int directory_handle_command(dir_connection_t *conn);
static int body_is_plausible(const char *body, size_t body_len, int purpose);
static int purpose_needs_anonymity(uint8_t dir_purpose,
uint8_t router_purpose);
static char *http_get_header(const char *headers, const char *which);
static void http_set_address_origin(const char *headers, connection_t *conn);
static void connection_dir_download_networkstatus_failed(
dir_connection_t *conn, int status_code);
static void connection_dir_download_routerdesc_failed(dir_connection_t *conn);
static void connection_dir_download_cert_failed(
dir_connection_t *conn, int status_code);
static void dir_networkstatus_download_failed(smartlist_t *failed,
int status_code);
static void dir_routerdesc_download_failed(smartlist_t *failed,
int status_code,
int router_purpose,
int was_extrainfo,
int was_descriptor_digests);
static void note_request(const char *key, size_t bytes);
static int client_likes_consensus(networkstatus_t *v, const char *want_url);
/********* START VARIABLES **********/
/** How far in the future do we allow a directory server to tell us it is
* before deciding that one of us has the wrong time? */
#define ALLOW_DIRECTORY_TIME_SKEW (30*60)
#define X_ADDRESS_HEADER "X-Your-Address-Is: "
/** HTTP cache control: how long do we tell proxies they can cache each
* kind of document we serve? */
#define FULL_DIR_CACHE_LIFETIME (60*60)
#define RUNNINGROUTERS_CACHE_LIFETIME (20*60)
#define NETWORKSTATUS_CACHE_LIFETIME (5*60)
#define ROUTERDESC_CACHE_LIFETIME (30*60)
#define ROUTERDESC_BY_DIGEST_CACHE_LIFETIME (48*60*60)
#define ROBOTS_CACHE_LIFETIME (24*60*60)
/********* END VARIABLES ************/
/** Return true iff the directory purpose 'purpose' must use an
* anonymous connection to a directory. */
static int
purpose_needs_anonymity(uint8_t dir_purpose, uint8_t router_purpose)
{
if (get_options()->AllDirActionsPrivate)
return 1;
if (router_purpose == ROUTER_PURPOSE_BRIDGE && has_completed_circuit)
return 1; /* if no circuits yet, we may need this info to bootstrap. */
if (dir_purpose == DIR_PURPOSE_FETCH_DIR ||
dir_purpose == DIR_PURPOSE_UPLOAD_DIR ||
dir_purpose == DIR_PURPOSE_UPLOAD_VOTE ||
dir_purpose == DIR_PURPOSE_UPLOAD_SIGNATURES ||
dir_purpose == DIR_PURPOSE_FETCH_RUNNING_LIST ||
dir_purpose == DIR_PURPOSE_FETCH_NETWORKSTATUS ||
dir_purpose == DIR_PURPOSE_FETCH_STATUS_VOTE ||
dir_purpose == DIR_PURPOSE_FETCH_DETACHED_SIGNATURES ||
dir_purpose == DIR_PURPOSE_FETCH_CONSENSUS ||
dir_purpose == DIR_PURPOSE_FETCH_CERTIFICATE ||
dir_purpose == DIR_PURPOSE_FETCH_SERVERDESC ||
dir_purpose == DIR_PURPOSE_FETCH_EXTRAINFO)
return 0;
return 1;
}
/** Return a newly allocated string describing <b>auth</b>. */
char *
authority_type_to_string(authority_type_t auth)
{
char *result;
smartlist_t *lst = smartlist_create();
if (auth & V1_AUTHORITY)
smartlist_add(lst, (void*)"V1");
if (auth & V2_AUTHORITY)
smartlist_add(lst, (void*)"V2");
if (auth & BRIDGE_AUTHORITY)
smartlist_add(lst, (void*)"Bridge");
if (auth & HIDSERV_AUTHORITY)
smartlist_add(lst, (void*)"Hidden service");
if (smartlist_len(lst)) {
result = smartlist_join_strings(lst, ", ", 0, NULL);
} else {
result = tor_strdup("[Not an authority]");
}
smartlist_free(lst);
return result;
}
/** Return a string describing a given directory connection purpose. */
static const char *
dir_conn_purpose_to_string(int purpose)
{
switch (purpose)
{
case DIR_PURPOSE_FETCH_DIR:
return "v1 directory fetch";
case DIR_PURPOSE_FETCH_RENDDESC:
return "hidden-service descriptor fetch";
case DIR_PURPOSE_UPLOAD_DIR:
return "server descriptor upload";
case DIR_PURPOSE_UPLOAD_RENDDESC:
return "hidden-service descriptor upload";
case DIR_PURPOSE_UPLOAD_VOTE:
return "server vote upload";
case DIR_PURPOSE_UPLOAD_SIGNATURES:
return "consensus signature upload";
case DIR_PURPOSE_FETCH_RUNNING_LIST:
return "running-routers fetch";
case DIR_PURPOSE_FETCH_NETWORKSTATUS:
return "network-status fetch";
case DIR_PURPOSE_FETCH_SERVERDESC:
return "server descriptor fetch";
case DIR_PURPOSE_FETCH_EXTRAINFO:
return "extra-info fetch";
case DIR_PURPOSE_FETCH_CONSENSUS:
return "consensus network-status fetch";
case DIR_PURPOSE_FETCH_CERTIFICATE:
return "authority cert fetch";
case DIR_PURPOSE_FETCH_STATUS_VOTE:
return "status vote fetch";
case DIR_PURPOSE_FETCH_DETACHED_SIGNATURES:
return "consensus signature fetch";
case DIR_PURPOSE_FETCH_RENDDESC_V2:
return "hidden-service v2 descriptor fetch";
case DIR_PURPOSE_UPLOAD_RENDDESC_V2:
return "hidden-service v2 descriptor upload";
}
log_warn(LD_BUG, "Called with unknown purpose %d", purpose);
return "(unknown)";
}
/** Return true iff <b>identity_digest</b> is the digest of a router we
* believe to support extrainfo downloads. (If <b>is_authority</b> we do
* additional checking that's only valid for authorities.) */
int
router_supports_extrainfo(const char *identity_digest, int is_authority)
{
routerinfo_t *ri = router_get_by_digest(identity_digest);
if (ri) {
if (ri->caches_extra_info)
return 1;
if (is_authority && ri->platform &&
tor_version_as_new_as(ri->platform, "Tor 0.2.0.0-alpha-dev (r10070)"))
return 1;
}
if (is_authority) {
routerstatus_t *rs = router_get_consensus_status_by_id(identity_digest);
if (rs && rs->version_supports_extrainfo_upload)
return 1;
}
return 0;
}
/** Return true iff any trusted directory authority has accepted our
* server descriptor.
*
* We consider any authority sufficient because waiting for all of
* them means it never happens while any authority is down; we don't
* go for something more complex in the middle (like \>1/3 or \>1/2 or
* \>=1/2) because that doesn't seem necessary yet.
*/
int
directories_have_accepted_server_descriptor(void)
{
smartlist_t *servers = router_get_trusted_dir_servers();
or_options_t *options = get_options();
SMARTLIST_FOREACH(servers, trusted_dir_server_t *, d, {
if ((d->type & options->_PublishServerDescriptor) &&
d->has_accepted_serverdesc) {
return 1;
}
});
return 0;
}
/** Start a connection to every suitable directory authority, using
* connection purpose 'purpose' and uploading the payload 'payload'
* (length 'payload_len'). The purpose should be one of
* 'DIR_PURPOSE_UPLOAD_DIR' or 'DIR_PURPOSE_UPLOAD_RENDDESC'.
*
* <b>type</b> specifies what sort of dir authorities (V1, V2,
* HIDSERV, BRIDGE) we should upload to.
*
* If <b>extrainfo_len</b> is nonzero, the first <b>payload_len</b> bytes of
* <b>payload</b> hold a router descriptor, and the next <b>extrainfo_len</b>
* bytes of <b>payload</b> hold an extra-info document. Upload the descriptor
* to all authorities, and the extra-info document to all authorities that
* support it.
*/
void
directory_post_to_dirservers(uint8_t dir_purpose, uint8_t router_purpose,
authority_type_t type,
const char *payload,
size_t payload_len, size_t extrainfo_len)
{
int post_via_tor;
smartlist_t *dirservers = router_get_trusted_dir_servers();
int found = 0;
tor_assert(dirservers);
/* This tries dirservers which we believe to be down, but ultimately, that's
* harmless, and we may as well err on the side of getting things uploaded.
*/
SMARTLIST_FOREACH(dirservers, trusted_dir_server_t *, ds,
{
routerstatus_t *rs = &(ds->fake_status);
size_t upload_len = payload_len;
if ((type & ds->type) == 0)
continue;
found = 1; /* at least one authority of this type was listed */
if (dir_purpose == DIR_PURPOSE_UPLOAD_DIR)
ds->has_accepted_serverdesc = 0;
if (extrainfo_len && router_supports_extrainfo(ds->digest, 1)) {
upload_len += extrainfo_len;
log_info(LD_DIR, "Uploading an extrainfo (length %d)",
(int) extrainfo_len);
}
post_via_tor = purpose_needs_anonymity(dir_purpose, router_purpose) ||
!fascist_firewall_allows_address_dir(ds->addr, ds->dir_port);
directory_initiate_command_routerstatus(rs, dir_purpose,
router_purpose,
post_via_tor,
NULL, payload, upload_len, 0);
});
if (!found) {
char *s = authority_type_to_string(type);
log_warn(LD_DIR, "Publishing server descriptor to directory authorities "
"of type '%s', but no authorities of that type listed!", s);
tor_free(s);
}
}
/** Start a connection to a random running directory server, using
* connection purpose <b>dir_purpose</b>, intending to fetch descriptors
* of purpose <b>router_purpose</b>, and requesting <b>resource</b>.
* If <b>retry_if_no_servers</b>, then if all the possible servers seem
* down, mark them up and try again.
*/
void
directory_get_from_dirserver(uint8_t dir_purpose, uint8_t router_purpose,
const char *resource, int retry_if_no_servers)
{
routerstatus_t *rs = NULL;
or_options_t *options = get_options();
int prefer_authority = directory_fetches_from_authorities(options);
int get_via_tor = purpose_needs_anonymity(dir_purpose, router_purpose);
authority_type_t type;
int flags = retry_if_no_servers ? PDS_RETRY_IF_NO_SERVERS : 0;
time_t if_modified_since = 0;
/* FFFF we could break this switch into its own function, and call
* it elsewhere in directory.c. -RD */
switch (dir_purpose) {
case DIR_PURPOSE_FETCH_EXTRAINFO:
type = EXTRAINFO_CACHE |
(router_purpose == ROUTER_PURPOSE_BRIDGE ? BRIDGE_AUTHORITY :
V2_AUTHORITY);
break;
case DIR_PURPOSE_FETCH_NETWORKSTATUS:
case DIR_PURPOSE_FETCH_SERVERDESC:
type = (router_purpose == ROUTER_PURPOSE_BRIDGE ? BRIDGE_AUTHORITY :
V2_AUTHORITY);
break;
case DIR_PURPOSE_FETCH_DIR:
case DIR_PURPOSE_FETCH_RUNNING_LIST:
type = V1_AUTHORITY;
break;
case DIR_PURPOSE_FETCH_RENDDESC:
type = HIDSERV_AUTHORITY;
break;
case DIR_PURPOSE_FETCH_STATUS_VOTE:
case DIR_PURPOSE_FETCH_DETACHED_SIGNATURES:
type = V3_AUTHORITY;
break;
case DIR_PURPOSE_FETCH_CONSENSUS:
case DIR_PURPOSE_FETCH_CERTIFICATE:
type = V3_AUTHORITY;
break;
default:
log_warn(LD_BUG, "Unexpected purpose %d", (int)dir_purpose);
return;
}
if (DIR_PURPOSE_FETCH_CONSENSUS) {
networkstatus_t *v = networkstatus_get_latest_consensus();
if (v)
if_modified_since = v->valid_after + 180;
}
if (!options->FetchServerDescriptors && type != HIDSERV_AUTHORITY)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -