|
@@ -82,18 +82,18 @@ static void dir_microdesc_download_failed(smartlist_t *failed,
|
|
|
static void note_client_request(int purpose, int compressed, size_t bytes);
|
|
|
static int client_likes_consensus(networkstatus_t *v, const char *want_url);
|
|
|
|
|
|
-static void directory_initiate_command_rend(const tor_addr_t *addr,
|
|
|
- uint16_t or_port,
|
|
|
- uint16_t dir_port,
|
|
|
- const char *digest,
|
|
|
- uint8_t dir_purpose,
|
|
|
- uint8_t router_purpose,
|
|
|
- dir_indirection_t indirection,
|
|
|
- const char *resource,
|
|
|
- const char *payload,
|
|
|
- size_t payload_len,
|
|
|
- time_t if_modified_since,
|
|
|
- const rend_data_t *rend_query);
|
|
|
+static void directory_initiate_command_rend(
|
|
|
+ const tor_addr_port_t *or_addr_port,
|
|
|
+ const tor_addr_port_t *dir_addr_port,
|
|
|
+ const char *digest,
|
|
|
+ uint8_t dir_purpose,
|
|
|
+ uint8_t router_purpose,
|
|
|
+ dir_indirection_t indirection,
|
|
|
+ const char *resource,
|
|
|
+ const char *payload,
|
|
|
+ size_t payload_len,
|
|
|
+ time_t if_modified_since,
|
|
|
+ const rend_data_t *rend_query);
|
|
|
|
|
|
/********* START VARIABLES **********/
|
|
|
|
|
@@ -624,8 +624,10 @@ directory_initiate_command_routerstatus_rend(const routerstatus_t *status,
|
|
|
{
|
|
|
const or_options_t *options = get_options();
|
|
|
const node_t *node;
|
|
|
- tor_addr_t addr;
|
|
|
+ tor_addr_port_t use_or_ap, use_dir_ap;
|
|
|
const int anonymized_connection = dirind_is_anon(indirection);
|
|
|
+ int have_or = 0, have_dir = 0;
|
|
|
+
|
|
|
node = node_get_by_id(status->identity_digest);
|
|
|
|
|
|
if (!node && anonymized_connection) {
|
|
@@ -634,7 +636,6 @@ directory_initiate_command_routerstatus_rend(const routerstatus_t *status,
|
|
|
routerstatus_describe(status));
|
|
|
return;
|
|
|
}
|
|
|
- tor_addr_from_ipv4h(&addr, status->addr);
|
|
|
|
|
|
if (options->ExcludeNodes && options->StrictNodes &&
|
|
|
routerset_contains_routerstatus(options->ExcludeNodes, status, -1)) {
|
|
@@ -646,13 +647,68 @@ directory_initiate_command_routerstatus_rend(const routerstatus_t *status,
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- directory_initiate_command_rend(&addr,
|
|
|
- status->or_port, status->dir_port,
|
|
|
- status->identity_digest,
|
|
|
- dir_purpose, router_purpose,
|
|
|
- indirection, resource,
|
|
|
- payload, payload_len, if_modified_since,
|
|
|
- rend_query);
|
|
|
+ /* At this point, if we are a clients making a direct connection to a
|
|
|
+ * directory server, we have selected a server that has at least one address
|
|
|
+ * allowed by ClientUseIPv4/6 and Reachable{"",OR,Dir}Addresses. This
|
|
|
+ * selection uses the preference in ClientPreferIPv6{OR,Dir}Port, if
|
|
|
+ * possible. (If UseBridges is set, clients ignore all these settings.)
|
|
|
+ *
|
|
|
+ * Now we use a similar process to select an address for the relay,
|
|
|
+ * but simply use the other address if the one we want isn't allowed by
|
|
|
+ * the firewall.
|
|
|
+ *
|
|
|
+ * (When Tor uploads and downloads a hidden service descriptor, it uses
|
|
|
+ * DIRIND_ANONYMOUS, except for Tor2Web, which uses DIRIND_ONEHOP.
|
|
|
+ * So this code will only modify the address for Tor2Web's HS descriptor
|
|
|
+ * fetches. Even Single Onion Servers (NYI) use DIRIND_ANONYMOUS, to avoid
|
|
|
+ * HSDirs denying service by rejecting descriptors.)
|
|
|
+ */
|
|
|
+
|
|
|
+ /* Initialise the OR / Dir addresses */
|
|
|
+ tor_addr_make_null(&use_or_ap.addr, AF_UNSPEC);
|
|
|
+ use_or_ap.port = 0;
|
|
|
+ tor_addr_make_null(&use_dir_ap.addr, AF_UNSPEC);
|
|
|
+ use_dir_ap.port = 0;
|
|
|
+
|
|
|
+ if (anonymized_connection) {
|
|
|
+ /* Use the primary (IPv4) OR address if we're making an indirect
|
|
|
+ * connection. */
|
|
|
+ tor_addr_from_ipv4h(&use_or_ap.addr, status->addr);
|
|
|
+ use_or_ap.port = status->or_port;
|
|
|
+ have_or = 1;
|
|
|
+ } else {
|
|
|
+ /* We use an IPv6 address if we have one and we prefer it.
|
|
|
+ * Use the preferred address and port if they are reachable, otherwise,
|
|
|
+ * use the alternate address and port (if any).
|
|
|
+ */
|
|
|
+ have_or = fascist_firewall_choose_address_rs(status,
|
|
|
+ FIREWALL_OR_CONNECTION, 0,
|
|
|
+ &use_or_ap);
|
|
|
+ }
|
|
|
+
|
|
|
+ have_dir = fascist_firewall_choose_address_rs(status,
|
|
|
+ FIREWALL_DIR_CONNECTION, 0,
|
|
|
+ &use_dir_ap);
|
|
|
+
|
|
|
+ /* We rejected both addresses. This isn't great. */
|
|
|
+ if (!have_or && !have_dir) {
|
|
|
+ log_info(LD_DIR, "Rejected both the OR and Dir address when launching a "
|
|
|
+ "directory connection to: IPv4 %s OR %d Dir %d IPv6 %s OR %d "
|
|
|
+ "Dir %d", fmt_addr32(status->addr), status->or_port,
|
|
|
+ status->dir_port, fmt_addr(&status->ipv6_addr),
|
|
|
+ status->ipv6_orport, status->dir_port);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* XX/teor - we don't retry the alternate OR/Dir address if this one fails.
|
|
|
+ * (See #6772.) Instead, we'll retry another directory on failure. */
|
|
|
+
|
|
|
+ directory_initiate_command_rend(&use_or_ap, &use_dir_ap,
|
|
|
+ status->identity_digest,
|
|
|
+ dir_purpose, router_purpose,
|
|
|
+ indirection, resource,
|
|
|
+ payload, payload_len, if_modified_since,
|
|
|
+ rend_query);
|
|
|
}
|
|
|
|
|
|
/** Launch a new connection to the directory server <b>status</b> to
|
|
@@ -874,17 +930,21 @@ directory_command_should_use_begindir(const or_options_t *options,
|
|
|
if (indirection == DIRIND_DIRECT_CONN || indirection == DIRIND_ANON_DIRPORT)
|
|
|
return 0;
|
|
|
if (indirection == DIRIND_ONEHOP)
|
|
|
- if (!fascist_firewall_allows_address_or(addr, or_port) ||
|
|
|
+ if (!fascist_firewall_allows_address_addr(addr, or_port,
|
|
|
+ FIREWALL_OR_CONNECTION, 0) ||
|
|
|
directory_fetches_from_authorities(options))
|
|
|
return 0; /* We're firewalled or are acting like a relay -- also no. */
|
|
|
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>. */
|
|
|
+/** Helper for directory_initiate_command_rend: send the
|
|
|
+ * command to a server whose address is <b>_addr</b>, whose OR port is
|
|
|
+ * <b>or_port</b>, whose directory port is <b>dir_port</b>, whose identity key
|
|
|
+ * digest is <b>digest</b>, with purposes <b>dir_purpose</b> and
|
|
|
+ * <b>router_purpose</b>, making an (in)direct connection as specified in
|
|
|
+ * <b>indirection</b>, with command <b>resource</b>, <b>payload</b> of
|
|
|
+ * <b>payload_len</b>, and asking for a result only <b>if_modified_since</b>.
|
|
|
+ */
|
|
|
void
|
|
|
directory_initiate_command(const tor_addr_t *_addr,
|
|
|
uint16_t or_port, uint16_t dir_port,
|
|
@@ -894,7 +954,27 @@ directory_initiate_command(const tor_addr_t *_addr,
|
|
|
const char *payload, size_t payload_len,
|
|
|
time_t if_modified_since)
|
|
|
{
|
|
|
- directory_initiate_command_rend(_addr, or_port, dir_port,
|
|
|
+ /* Assume _addr applies to both the ORPort and the DirPort, but use the
|
|
|
+ * null tor_addr if ORPort or DirPort are 0. */
|
|
|
+ tor_addr_port_t or_ap, dir_ap;
|
|
|
+
|
|
|
+ if (or_port) {
|
|
|
+ tor_addr_copy(&or_ap.addr, _addr);
|
|
|
+ } else {
|
|
|
+ /* the family doesn't matter here, so make it the same as _addr */
|
|
|
+ tor_addr_make_null(&or_ap.addr, tor_addr_family(_addr));
|
|
|
+ }
|
|
|
+ or_ap.port = or_port;
|
|
|
+
|
|
|
+ if (dir_port) {
|
|
|
+ tor_addr_copy(&dir_ap.addr, _addr);
|
|
|
+ } else {
|
|
|
+ /* the family doesn't matter here, so make it the same as _addr */
|
|
|
+ tor_addr_make_null(&dir_ap.addr, tor_addr_family(_addr));
|
|
|
+ }
|
|
|
+ dir_ap.port = dir_port;
|
|
|
+
|
|
|
+ directory_initiate_command_rend(&or_ap, &dir_ap,
|
|
|
digest, dir_purpose,
|
|
|
router_purpose, indirection,
|
|
|
resource, payload, payload_len,
|
|
@@ -914,10 +994,11 @@ is_sensitive_dir_purpose(uint8_t dir_purpose)
|
|
|
}
|
|
|
|
|
|
/** Same as directory_initiate_command(), but accepts rendezvous data to
|
|
|
- * fetch a hidden service descriptor. */
|
|
|
+ * fetch a hidden service descriptor, and takes its address & port arguments
|
|
|
+ * as tor_addr_port_t. */
|
|
|
static void
|
|
|
-directory_initiate_command_rend(const tor_addr_t *_addr,
|
|
|
- uint16_t or_port, uint16_t dir_port,
|
|
|
+directory_initiate_command_rend(const tor_addr_port_t *or_addr_port,
|
|
|
+ const tor_addr_port_t *dir_addr_port,
|
|
|
const char *digest,
|
|
|
uint8_t dir_purpose, uint8_t router_purpose,
|
|
|
dir_indirection_t indirection,
|
|
@@ -926,19 +1007,24 @@ directory_initiate_command_rend(const tor_addr_t *_addr,
|
|
|
time_t if_modified_since,
|
|
|
const rend_data_t *rend_query)
|
|
|
{
|
|
|
+ tor_assert(or_addr_port);
|
|
|
+ tor_assert(dir_addr_port);
|
|
|
+ tor_assert(or_addr_port->port || dir_addr_port->port);
|
|
|
+ tor_assert(digest);
|
|
|
+
|
|
|
dir_connection_t *conn;
|
|
|
const or_options_t *options = get_options();
|
|
|
int socket_error = 0;
|
|
|
- int use_begindir = directory_command_should_use_begindir(options, _addr,
|
|
|
- or_port, router_purpose, indirection);
|
|
|
+ const int use_begindir = directory_command_should_use_begindir(options,
|
|
|
+ &or_addr_port->addr, or_addr_port->port,
|
|
|
+ router_purpose, indirection);
|
|
|
const int anonymized_connection = dirind_is_anon(indirection);
|
|
|
- tor_addr_t addr;
|
|
|
-
|
|
|
- tor_assert(_addr);
|
|
|
- tor_assert(or_port || dir_port);
|
|
|
- tor_assert(digest);
|
|
|
+ const int or_connection = use_begindir || anonymized_connection;
|
|
|
|
|
|
- tor_addr_copy(&addr, _addr);
|
|
|
+ tor_addr_t addr;
|
|
|
+ tor_addr_copy(&addr, &(or_connection ? or_addr_port : dir_addr_port)->addr);
|
|
|
+ uint16_t port = (or_connection ? or_addr_port : dir_addr_port)->port;
|
|
|
+ uint16_t dir_port = dir_addr_port->port;
|
|
|
|
|
|
log_debug(LD_DIR, "anonymized %d, use_begindir %d.",
|
|
|
anonymized_connection, use_begindir);
|
|
@@ -954,13 +1040,23 @@ directory_initiate_command_rend(const tor_addr_t *_addr,
|
|
|
|
|
|
/* ensure that we don't make direct connections when a SOCKS server is
|
|
|
* configured. */
|
|
|
- if (!anonymized_connection && !use_begindir && !options->HTTPProxy &&
|
|
|
+ if (!or_connection && !options->HTTPProxy &&
|
|
|
(options->Socks4Proxy || options->Socks5Proxy)) {
|
|
|
log_warn(LD_DIR, "Cannot connect to a directory server through a "
|
|
|
"SOCKS proxy!");
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
+ if (or_connection && (!or_addr_port->port
|
|
|
+ || tor_addr_is_null(&or_addr_port->addr))) {
|
|
|
+ log_warn(LD_DIR, "Cannot make an OR connection without an OR port.");
|
|
|
+ return;
|
|
|
+ } else if (!or_connection && (!dir_addr_port->port
|
|
|
+ || tor_addr_is_null(&dir_addr_port->addr))) {
|
|
|
+ log_warn(LD_DIR, "Cannot make a Dir connection without a Dir port.");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
/* ensure we don't make excess connections when we're already downloading
|
|
|
* a consensus during bootstrap */
|
|
|
if (connection_dir_avoid_extra_connection_for_purpose(dir_purpose)) {
|
|
@@ -971,7 +1067,7 @@ directory_initiate_command_rend(const tor_addr_t *_addr,
|
|
|
|
|
|
/* set up conn so it's got all the data we need to remember */
|
|
|
tor_addr_copy(&conn->base_.addr, &addr);
|
|
|
- conn->base_.port = use_begindir ? or_port : dir_port;
|
|
|
+ conn->base_.port = port;
|
|
|
conn->base_.address = tor_dup_addr(&addr);
|
|
|
memcpy(conn->identity_digest, digest, DIGEST_LEN);
|
|
|
|
|
@@ -989,7 +1085,7 @@ directory_initiate_command_rend(const tor_addr_t *_addr,
|
|
|
if (rend_query)
|
|
|
conn->rend_data = rend_data_dup(rend_query);
|
|
|
|
|
|
- if (!anonymized_connection && !use_begindir) {
|
|
|
+ if (!or_connection) {
|
|
|
/* then we want to connect to dirport directly */
|
|
|
|
|
|
if (options->HTTPProxy) {
|