📄 directory.c
字号:
log_debug(LD_DIR, "anonymized %d, use_begindir %d.",
anonymized_connection, use_begindir);
log_debug(LD_DIR, "Initiating %s", dir_conn_purpose_to_string(dir_purpose));
conn = TO_DIR_CONN(connection_new(CONN_TYPE_DIR, AF_INET));
/* set up conn so it's got all the data we need to remember */
conn->_base.addr = addr;
conn->_base.port = use_begindir ? or_port : dir_port;
conn->_base.address = tor_strdup(address);
memcpy(conn->identity_digest, digest, DIGEST_LEN);
conn->_base.purpose = dir_purpose;
conn->router_purpose = router_purpose;
/* give it an initial state */
conn->_base.state = DIR_CONN_STATE_CONNECTING;
/* decide whether we can learn our IP address from this conn */
conn->dirconn_direct = !anonymized_connection;
if (!anonymized_connection && !use_begindir) {
/* then we want to connect to dirport directly */
if (options->HttpProxy) {
addr = options->HttpProxyAddr;
dir_port = options->HttpProxyPort;
}
switch (connection_connect(TO_CONN(conn), conn->_base.address, addr,
dir_port)) {
case -1:
connection_dir_request_failed(conn); /* retry if we want */
/* XXX we only pass 'conn' above, not 'resource', 'payload',
* etc. So in many situations it can't retry! -RD */
connection_free(TO_CONN(conn));
return;
case 1:
/* start flushing conn */
conn->_base.state = DIR_CONN_STATE_CLIENT_SENDING;
/* fall through */
case 0:
/* queue the command on the outbuf */
directory_send_command(conn, dir_purpose, 1, resource,
payload, payload_len,
supports_conditional_consensus,
if_modified_since);
connection_watch_events(TO_CONN(conn), EV_READ | EV_WRITE);
/* writable indicates finish, readable indicates broken link,
error indicates broken link in windowsland. */
}
} else { /* we want to connect via a tor connection */
edge_connection_t *linked_conn;
/* If it's an anonymized connection, remember the fact that we
* wanted it for later: maybe we'll want it again soon. */
if (anonymized_connection && use_begindir)
rep_hist_note_used_internal(time(NULL), 0, 1);
else if (anonymized_connection && !use_begindir)
rep_hist_note_used_port(time(NULL), conn->_base.port);
/* make an AP connection
* populate it and add it at the right state
* hook up both sides
*/
linked_conn =
connection_ap_make_link(conn->_base.address, conn->_base.port,
digest, use_begindir, conn->dirconn_direct);
if (!linked_conn) {
log_warn(LD_NET,"Making tunnel to dirserver failed.");
connection_mark_for_close(TO_CONN(conn));
return;
}
connection_link_connections(TO_CONN(conn), TO_CONN(linked_conn));
if (connection_add(TO_CONN(conn)) < 0) {
log_warn(LD_NET,"Unable to add connection for link to dirserver.");
connection_mark_for_close(TO_CONN(conn));
return;
}
conn->_base.state = DIR_CONN_STATE_CLIENT_SENDING;
/* queue the command on the outbuf */
directory_send_command(conn, dir_purpose, 0, resource,
payload, payload_len,
supports_conditional_consensus,
if_modified_since);
connection_watch_events(TO_CONN(conn), EV_READ | EV_WRITE);
connection_start_reading(TO_CONN(linked_conn));
}
}
/** Return true iff anything we say on <b>conn</b> is being encrypted before
* we send it to the client/server. */
int
connection_dir_is_encrypted(dir_connection_t *conn)
{
/* Right now it's sufficient to see if conn is or has been linked, since
* the only thing it could be linked to is an edge connection on a
* circuit, and the only way it could have been unlinked is at the edge
* connection getting closed.
*/
return TO_CONN(conn)->linked;
}
/** Helper for sorting
*
* sort strings alphabetically
*/
static int
_compare_strs(const void **a, const void **b)
{
const char *s1 = *a, *s2 = *b;
return strcmp(s1, s2);
}
/** Return the URL we should use for a consensus download.
*
* This url depends on whether or not the server we go to
* is sufficiently new to support conditional consensus downloading,
* i.e. GET .../consensus/<b>fpr</b>+<b>fpr</b>+<b>fpr</b>
*/
#define CONDITIONAL_CONSENSUS_FPR_LEN 3
#if (CONDITIONAL_CONSENSUS_FPR_LEN > DIGEST_LEN)
#error "conditional consensus fingerprint length is larger than digest length"
#endif
static char *
directory_get_consensus_url(int supports_conditional_consensus)
{
char *url;
int len;
if (supports_conditional_consensus) {
char *authority_id_list;
smartlist_t *authority_digets = smartlist_create();
SMARTLIST_FOREACH(router_get_trusted_dir_servers(),
trusted_dir_server_t *, ds,
{
char *hex;
if (!(ds->type & V3_AUTHORITY))
continue;
hex = tor_malloc(2*CONDITIONAL_CONSENSUS_FPR_LEN+1);
base16_encode(hex, 2*CONDITIONAL_CONSENSUS_FPR_LEN+1,
ds->v3_identity_digest, CONDITIONAL_CONSENSUS_FPR_LEN);
smartlist_add(authority_digets, hex);
});
smartlist_sort(authority_digets, _compare_strs);
authority_id_list = smartlist_join_strings(authority_digets,
"+", 0, NULL);
len = strlen(authority_id_list)+64;
url = tor_malloc(len);
tor_snprintf(url, len, "/tor/status-vote/current/consensus/%s.z",
authority_id_list);
SMARTLIST_FOREACH(authority_digets, char *, cp, tor_free(cp));
smartlist_free(authority_digets);
tor_free(authority_id_list);
} else {
url = tor_strdup("/tor/status-vote/current/consensus.z");
}
return url;
}
/** Queue an appropriate HTTP command on conn-\>outbuf. The other args
* are as in directory_initiate_command.
*/
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)
{
char proxystring[256];
char proxyauthstring[256];
char hoststring[128];
char imsstring[RFC1123_TIME_LEN+32];
char *url;
char request[8192];
const char *httpcommand = NULL;
size_t len;
tor_assert(conn);
tor_assert(conn->_base.type == CONN_TYPE_DIR);
tor_free(conn->requested_resource);
if (resource)
conn->requested_resource = tor_strdup(resource);
/* come up with a string for which Host: we want */
if (conn->_base.port == 80) {
strlcpy(hoststring, conn->_base.address, sizeof(hoststring));
} else {
tor_snprintf(hoststring, sizeof(hoststring),"%s:%d",
conn->_base.address, conn->_base.port);
}
/* Format if-modified-since */
if (!if_modified_since) {
imsstring[0] = '\0';
} else {
char b[RFC1123_TIME_LEN+1];
format_rfc1123_time(b, if_modified_since);
tor_snprintf(imsstring, sizeof(imsstring), "\r\nIf-Modified-Since: %s", b);
}
/* come up with some proxy lines, if we're using one. */
if (direct && get_options()->HttpProxy) {
char *base64_authenticator=NULL;
const char *authenticator = get_options()->HttpProxyAuthenticator;
tor_snprintf(proxystring, sizeof(proxystring),"http://%s", hoststring);
if (authenticator) {
base64_authenticator = alloc_http_authenticator(authenticator);
if (!base64_authenticator)
log_warn(LD_BUG, "Encoding http authenticator failed");
}
if (base64_authenticator) {
tor_snprintf(proxyauthstring, sizeof(proxyauthstring),
"\r\nProxy-Authorization: Basic %s",
base64_authenticator);
tor_free(base64_authenticator);
} else {
proxyauthstring[0] = 0;
}
} else {
proxystring[0] = 0;
proxyauthstring[0] = 0;
}
switch (purpose) {
case DIR_PURPOSE_FETCH_DIR:
tor_assert(!resource);
tor_assert(!payload);
httpcommand = "GET";
url = tor_strdup("/tor/dir.z");
break;
case DIR_PURPOSE_FETCH_RUNNING_LIST:
tor_assert(!resource);
tor_assert(!payload);
httpcommand = "GET";
url = tor_strdup("/tor/running-routers");
break;
case DIR_PURPOSE_FETCH_NETWORKSTATUS:
httpcommand = "GET";
len = strlen(resource)+32;
url = tor_malloc(len);
tor_snprintf(url, len, "/tor/status/%s", resource);
break;
case DIR_PURPOSE_FETCH_CONSENSUS:
tor_assert(!resource);
tor_assert(!payload);
httpcommand = "GET";
url = directory_get_consensus_url(supports_conditional_consensus);
/* XXX021: downgrade/remove once done with conditional consensus fu */
log_notice(LD_DIR, "Downloading consensus from %s using %s",
hoststring, url);
break;
case DIR_PURPOSE_FETCH_CERTIFICATE:
tor_assert(resource);
tor_assert(!payload);
httpcommand = "GET";
len = strlen(resource)+32;
url = tor_malloc(len);
tor_snprintf(url, len, "/tor/keys/%s", resource);
break;
case DIR_PURPOSE_FETCH_STATUS_VOTE:
tor_assert(resource);
tor_assert(!payload);
httpcommand = "GET";
len = strlen(resource)+32;
url = tor_malloc(len);
tor_snprintf(url, len, "/tor/status-vote/next/%s.z", resource);
break;
case DIR_PURPOSE_FETCH_DETACHED_SIGNATURES:
tor_assert(!resource);
tor_assert(!payload);
httpcommand = "GET";
url = tor_strdup("/tor/status-vote/next/consensus-signatures.z");
break;
case DIR_PURPOSE_FETCH_SERVERDESC:
httpcommand = "GET";
len = strlen(resource)+32;
url = tor_malloc(len);
tor_snprintf(url, len, "/tor/server/%s", resource);
break;
case DIR_PURPOSE_FETCH_EXTRAINFO:
httpcommand = "GET";
len = strlen(resource)+32;
url = tor_malloc(len);
tor_snprintf(url, len, "/tor/extra/%s", resource);
break;
case DIR_PURPOSE_UPLOAD_DIR:
tor_assert(!resource);
tor_assert(payload);
httpcommand = "POST";
url = tor_strdup("/tor/");
break;
case DIR_PURPOSE_UPLOAD_VOTE:
tor_assert(!resource);
tor_assert(payload);
httpcommand = "POST";
url = tor_strdup("/tor/post/vote");
break;
case DIR_PURPOSE_UPLOAD_SIGNATURES:
tor_assert(!resource);
tor_assert(payload);
httpcommand = "POST";
url = tor_strdup("/tor/post/consensus-signature");
break;
case DIR_PURPOSE_FETCH_RENDDESC:
tor_assert(resource);
tor_assert(!payload);
/* this must be true or we wouldn't be doing the lookup */
tor_assert(strlen(resource) <= REND_SERVICE_ID_LEN_BASE32);
/* This breaks the function abstraction. */
strlcpy(conn->rend_query, resource, sizeof(conn->rend_query));
conn->rend_version = 0;
httpcommand = "GET";
/* Request the most recent versioned descriptor. */
// (XXXX We were going to switch this to fetch rendezvous1 descriptors,
// but that never got testing, and it wasn't a good design.)
len = strlen(resource)+32;
url = tor_malloc(len);
tor_snprintf(url, len, "/tor/rendezvous/%s", resource);
break;
case DIR_PURPOSE_FETCH_RENDDESC_V2:
tor_assert(resource);
tor_assert(strlen(resource) <= REND_DESC_ID_V2_LEN_BASE32);
/* Remember the query to refer to it when a response arrives. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -