aboutsummaryrefslogtreecommitdiff
path: root/src/or/rendclient.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/or/rendclient.c')
-rw-r--r--src/or/rendclient.c244
1 files changed, 186 insertions, 58 deletions
diff --git a/src/or/rendclient.c b/src/or/rendclient.c
index f84475acbc..1038378de1 100644
--- a/src/or/rendclient.c
+++ b/src/or/rendclient.c
@@ -16,6 +16,7 @@
#include "connection_edge.h"
#include "directory.h"
#include "main.h"
+#include "nodelist.h"
#include "relay.h"
#include "rendclient.h"
#include "rendcommon.h"
@@ -138,6 +139,8 @@ rend_client_send_introduction(origin_circuit_t *introcirc,
tor_assert(rendcirc->rend_data);
tor_assert(!rend_cmp_service_ids(introcirc->rend_data->onion_address,
rendcirc->rend_data->onion_address));
+ tor_assert(!(introcirc->build_state->onehop_tunnel));
+ tor_assert(!(rendcirc->build_state->onehop_tunnel));
if (rend_cache_lookup_entry(introcirc->rend_data->onion_address, -1,
&entry) < 1) {
@@ -328,6 +331,7 @@ rend_client_introduction_acked(origin_circuit_t *circ,
}
tor_assert(circ->build_state->chosen_exit);
+ tor_assert(!(circ->build_state->onehop_tunnel));
tor_assert(circ->rend_data);
if (request_len == 0) {
@@ -339,6 +343,7 @@ rend_client_introduction_acked(origin_circuit_t *circ,
rendcirc = circuit_get_by_rend_query_and_purpose(
circ->rend_data->onion_address, CIRCUIT_PURPOSE_C_REND_READY);
if (rendcirc) { /* remember the ack */
+ tor_assert(!(rendcirc->build_state->onehop_tunnel));
rendcirc->_base.purpose = CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED;
/* Set timestamp_dirty, because circuit_expire_building expects
* it to specify when a circuit entered the
@@ -360,8 +365,9 @@ rend_client_introduction_acked(origin_circuit_t *circ,
log_info(LD_REND, "Got nack for %s from %s...",
safe_str_client(circ->rend_data->onion_address),
safe_str_client(extend_info_describe(circ->build_state->chosen_exit)));
- if (rend_client_remove_intro_point(circ->build_state->chosen_exit,
- circ->rend_data) > 0) {
+ if (rend_client_report_intro_point_failure(circ->build_state->chosen_exit,
+ circ->rend_data,
+ INTRO_POINT_FAILURE_GENERIC)>0) {
/* There are introduction points left. Re-extend the circuit to
* another intro point and try again. */
int result = rend_client_reextend_intro_circuit(circ);
@@ -378,9 +384,12 @@ rend_client_introduction_acked(origin_circuit_t *circ,
#define REND_HID_SERV_DIR_REQUERY_PERIOD (15 * 60)
/** Contains the last request times to hidden service directories for
- * certain queries; keys are strings consisting of base32-encoded
- * hidden service directory identities and base32-encoded descriptor IDs;
- * values are pointers to timestamps of the last requests. */
+ * certain queries; each key is a string consisting of the
+ * concatenation of a base32-encoded HS directory identity digest, a
+ * base32-encoded HS descriptor ID, and a hidden service address
+ * (without the ".onion" part); each value is a pointer to a time_t
+ * holding the time of the last request for that descriptor ID to that
+ * HS directory. */
static strmap_t *last_hid_serv_requests_ = NULL;
/** Returns last_hid_serv_requests_, initializing it to a new strmap if
@@ -393,23 +402,34 @@ get_last_hid_serv_requests(void)
return last_hid_serv_requests_;
}
+#define LAST_HID_SERV_REQUEST_KEY_LEN (REND_DESC_ID_V2_LEN_BASE32 + \
+ REND_DESC_ID_V2_LEN_BASE32 + \
+ REND_SERVICE_ID_LEN_BASE32)
+
/** Look up the last request time to hidden service directory <b>hs_dir</b>
- * for descriptor ID <b>desc_id_base32</b>. If <b>set</b> is non-zero,
+ * for descriptor ID <b>desc_id_base32</b> for the service specified in
+ * <b>rend_query</b>. If <b>set</b> is non-zero,
* assign the current time <b>now</b> and return that. Otherwise, return
* the most recent request time, or 0 if no such request has been sent
* before. */
static time_t
lookup_last_hid_serv_request(routerstatus_t *hs_dir,
- const char *desc_id_base32, time_t now, int set)
+ const char *desc_id_base32,
+ const rend_data_t *rend_query,
+ time_t now, int set)
{
char hsdir_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
- char hsdir_desc_comb_id[2 * REND_DESC_ID_V2_LEN_BASE32 + 1];
+ char hsdir_desc_comb_id[LAST_HID_SERV_REQUEST_KEY_LEN + 1];
time_t *last_request_ptr;
strmap_t *last_hid_serv_requests = get_last_hid_serv_requests();
base32_encode(hsdir_id_base32, sizeof(hsdir_id_base32),
hs_dir->identity_digest, DIGEST_LEN);
- tor_snprintf(hsdir_desc_comb_id, sizeof(hsdir_desc_comb_id), "%s%s",
- hsdir_id_base32, desc_id_base32);
+ tor_snprintf(hsdir_desc_comb_id, sizeof(hsdir_desc_comb_id), "%s%s%s",
+ hsdir_id_base32,
+ desc_id_base32,
+ rend_query->onion_address);
+ /* XXX023 tor_assert(strlen(hsdir_desc_comb_id) ==
+ LAST_HID_SERV_REQUEST_KEY_LEN); */
if (set) {
time_t *oldptr;
last_request_ptr = tor_malloc_zero(sizeof(time_t));
@@ -427,10 +447,10 @@ lookup_last_hid_serv_request(routerstatus_t *hs_dir,
* it does not contain requests older than REND_HID_SERV_DIR_REQUERY_PERIOD
* seconds any more. */
static void
-directory_clean_last_hid_serv_requests(void)
+directory_clean_last_hid_serv_requests(time_t now)
{
strmap_iter_t *iter;
- time_t cutoff = time(NULL) - REND_HID_SERV_DIR_REQUERY_PERIOD;
+ time_t cutoff = now - REND_HID_SERV_DIR_REQUERY_PERIOD;
strmap_t *last_hid_serv_requests = get_last_hid_serv_requests();
for (iter = strmap_iter_init(last_hid_serv_requests);
!strmap_iter_done(iter); ) {
@@ -448,6 +468,33 @@ directory_clean_last_hid_serv_requests(void)
}
}
+/** Remove all requests related to the hidden service named
+ * <b>onion_address</b> from the history of times of requests to
+ * hidden service directories. */
+static void
+purge_hid_serv_from_last_hid_serv_requests(const char *onion_address)
+{
+ strmap_iter_t *iter;
+ strmap_t *last_hid_serv_requests = get_last_hid_serv_requests();
+ /* XXX023 tor_assert(strlen(onion_address) == REND_SERVICE_ID_LEN_BASE32); */
+ for (iter = strmap_iter_init(last_hid_serv_requests);
+ !strmap_iter_done(iter); ) {
+ const char *key;
+ void *val;
+ strmap_iter_get(iter, &key, &val);
+ /* XXX023 tor_assert(strlen(key) == LAST_HID_SERV_REQUEST_KEY_LEN); */
+ if (tor_memeq(key + LAST_HID_SERV_REQUEST_KEY_LEN -
+ REND_SERVICE_ID_LEN_BASE32,
+ onion_address,
+ REND_SERVICE_ID_LEN_BASE32)) {
+ iter = strmap_iter_next_rmv(last_hid_serv_requests, iter);
+ tor_free(val);
+ } else {
+ iter = strmap_iter_next(last_hid_serv_requests, iter);
+ }
+ }
+}
+
/** Purge the history of request times to hidden service directories,
* so that future lookups of an HS descriptor will not fail because we
* accessed all of the HSDir relays responsible for the descriptor
@@ -469,12 +516,11 @@ rend_client_purge_last_hid_serv_requests(void)
}
/** Determine the responsible hidden service directories for <b>desc_id</b>
- * and fetch the descriptor belonging to that ID from one of them. Only
- * send a request to hidden service directories that we did not try within
- * the last REND_HID_SERV_DIR_REQUERY_PERIOD seconds; on success, return 1,
+ * and fetch the descriptor with that ID from one of them. Only
+ * send a request to a hidden service directory that we have not yet tried
+ * during this attempt to connect to this hidden service; on success, return 1,
* in the case that no hidden service directory is left to ask for the
- * descriptor, return 0, and in case of a failure -1. <b>query</b> is only
- * passed for pretty log statements. */
+ * descriptor, return 0, and in case of a failure -1. */
static int
directory_get_from_hs_dir(const char *desc_id, const rend_data_t *rend_query)
{
@@ -494,12 +540,16 @@ directory_get_from_hs_dir(const char *desc_id, const rend_data_t *rend_query)
/* Only select those hidden service directories to which we did not send
* a request recently and for which we have a router descriptor here. */
- directory_clean_last_hid_serv_requests(); /* Clean request history first. */
+
+ /* Clean request history first. */
+ directory_clean_last_hid_serv_requests(now);
SMARTLIST_FOREACH(responsible_dirs, routerstatus_t *, dir, {
- if (lookup_last_hid_serv_request(dir, desc_id_base32, 0, 0) +
- REND_HID_SERV_DIR_REQUERY_PERIOD >= now ||
- !router_get_by_digest(dir->identity_digest))
+ time_t last = lookup_last_hid_serv_request(
+ dir, desc_id_base32, rend_query, 0, 0);
+ const node_t *node = node_get_by_id(dir->identity_digest);
+ if (last + REND_HID_SERV_DIR_REQUERY_PERIOD >= now ||
+ !node || !node_has_descriptor(node))
SMARTLIST_DEL_CURRENT(responsible_dirs, dir);
});
@@ -512,9 +562,9 @@ directory_get_from_hs_dir(const char *desc_id, const rend_data_t *rend_query)
return 0;
}
- /* Remember, that we are requesting a descriptor from this hidden service
+ /* Remember that we are requesting a descriptor from this hidden service
* directory now. */
- lookup_last_hid_serv_request(hs_dir, desc_id_base32, now, 1);
+ lookup_last_hid_serv_request(hs_dir, desc_id_base32, rend_query, now, 1);
/* Encode descriptor cookie for logging purposes. */
if (rend_query->auth_type != REND_NO_AUTH) {
@@ -568,10 +618,11 @@ rend_client_refetch_v2_renddesc(const rend_data_t *rend_query)
"service descriptor, but are not fetching service descriptors.");
return;
}
- /* Before fetching, check if we already have the descriptor here. */
- if (rend_cache_lookup_entry(rend_query->onion_address, -1, &e) > 0) {
+ /* Before fetching, check if we already have a usable descriptor here. */
+ if (rend_cache_lookup_entry(rend_query->onion_address, -1, &e) > 0 &&
+ rend_client_any_intro_points_usable(e)) {
log_info(LD_REND, "We would fetch a v2 rendezvous descriptor, but we "
- "already have that descriptor here. Not fetching.");
+ "already have a usable descriptor here. Not fetching.");
return;
}
log_debug(LD_REND, "Fetching v2 rendezvous descriptor for service %s",
@@ -641,16 +692,31 @@ rend_client_cancel_descriptor_fetches(void)
} SMARTLIST_FOREACH_END(conn);
}
-/** Remove failed_intro from ent. If ent now has no intro points, or
- * service is unrecognized, then launch a new renddesc fetch.
-
+/** Mark <b>failed_intro</b> as a failed introduction point for the
+ * hidden service specified by <b>rend_query</b>. If the HS now has no
+ * usable intro points, or we do not have an HS descriptor for it,
+ * then launch a new renddesc fetch.
*
- * Return -1 if error, 0 if no intro points remain or service
+ * If <b>failure_type</b> is INTRO_POINT_FAILURE_GENERIC, remove the
+ * intro point from (our parsed copy of) the HS descriptor.
+ *
+ * If <b>failure_type</b> is INTRO_POINT_FAILURE_TIMEOUT, mark the
+ * intro point as 'timed out'; it will not be retried until the
+ * current hidden service connection attempt has ended or it has
+ * appeared in a newly fetched rendezvous descriptor.
+ *
+ * If <b>failure_type</b> is INTRO_POINT_FAILURE_UNREACHABLE,
+ * increment the intro point's reachability-failure count; if it has
+ * now failed MAX_INTRO_POINT_REACHABILITY_FAILURES or more times,
+ * remove the intro point from (our parsed copy of) the HS descriptor.
+ *
+ * Return -1 if error, 0 if no usable intro points remain or service
* unrecognized, 1 if recognized and some intro points remain.
*/
int
-rend_client_remove_intro_point(extend_info_t *failed_intro,
- const rend_data_t *rend_query)
+rend_client_report_intro_point_failure(extend_info_t *failed_intro,
+ const rend_data_t *rend_query,
+ unsigned int failure_type)
{
int i, r;
rend_cache_entry_t *ent;
@@ -673,8 +739,34 @@ rend_client_remove_intro_point(extend_info_t *failed_intro,
rend_intro_point_t *intro = smartlist_get(ent->parsed->intro_nodes, i);
if (tor_memeq(failed_intro->identity_digest,
intro->extend_info->identity_digest, DIGEST_LEN)) {
- rend_intro_point_free(intro);
- smartlist_del(ent->parsed->intro_nodes, i);
+ switch (failure_type) {
+ default:
+ log_warn(LD_BUG, "Unknown failure type %u. Removing intro point.",
+ failure_type);
+ tor_fragile_assert();
+ /* fall through */
+ case INTRO_POINT_FAILURE_GENERIC:
+ rend_intro_point_free(intro);
+ smartlist_del(ent->parsed->intro_nodes, i);
+ break;
+ case INTRO_POINT_FAILURE_TIMEOUT:
+ intro->timed_out = 1;
+ break;
+ case INTRO_POINT_FAILURE_UNREACHABLE:
+ ++(intro->unreachable_count);
+ {
+ int zap_intro_point =
+ intro->unreachable_count >= MAX_INTRO_POINT_REACHABILITY_FAILURES;
+ log_info(LD_REND, "Failed to reach this intro point %u times.%s",
+ intro->unreachable_count,
+ zap_intro_point ? " Removing from descriptor.": "");
+ if (zap_intro_point) {
+ rend_intro_point_free(intro);
+ smartlist_del(ent->parsed->intro_nodes, i);
+ }
+ }
+ break;
+ }
break;
}
}
@@ -810,40 +902,42 @@ rend_client_receive_rendezvous(origin_circuit_t *circ, const uint8_t *request,
void
rend_client_desc_trynow(const char *query)
{
- edge_connection_t *conn;
+ entry_connection_t *conn;
rend_cache_entry_t *entry;
+ const rend_data_t *rend_data;
time_t now = time(NULL);
smartlist_t *conns = get_connection_array();
- SMARTLIST_FOREACH_BEGIN(conns, connection_t *, _conn) {
- if (_conn->type != CONN_TYPE_AP ||
- _conn->state != AP_CONN_STATE_RENDDESC_WAIT ||
- _conn->marked_for_close)
+ SMARTLIST_FOREACH_BEGIN(conns, connection_t *, base_conn) {
+ if (base_conn->type != CONN_TYPE_AP ||
+ base_conn->state != AP_CONN_STATE_RENDDESC_WAIT ||
+ base_conn->marked_for_close)
continue;
- conn = TO_EDGE_CONN(_conn);
- if (!conn->rend_data)
+ conn = TO_ENTRY_CONN(base_conn);
+ rend_data = ENTRY_TO_EDGE_CONN(conn)->rend_data;
+ if (!rend_data)
continue;
- if (rend_cmp_service_ids(query, conn->rend_data->onion_address))
+ if (rend_cmp_service_ids(query, rend_data->onion_address))
continue;
- assert_connection_ok(TO_CONN(conn), now);
- if (rend_cache_lookup_entry(conn->rend_data->onion_address, -1,
+ assert_connection_ok(base_conn, now);
+ if (rend_cache_lookup_entry(rend_data->onion_address, -1,
&entry) == 1 &&
rend_client_any_intro_points_usable(entry)) {
/* either this fetch worked, or it failed but there was a
* valid entry from before which we should reuse */
log_info(LD_REND,"Rend desc is usable. Launching circuits.");
- conn->_base.state = AP_CONN_STATE_CIRCUIT_WAIT;
+ base_conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
/* restart their timeout values, so they get a fair shake at
* connecting to the hidden service. */
- conn->_base.timestamp_created = now;
- conn->_base.timestamp_lastread = now;
- conn->_base.timestamp_lastwritten = now;
+ base_conn->timestamp_created = now;
+ base_conn->timestamp_lastread = now;
+ base_conn->timestamp_lastwritten = now;
if (connection_ap_handshake_attach_circuit(conn) < 0) {
/* it will never work */
log_warn(LD_REND,"Rendezvous attempt failed. Closing.");
- if (!conn->_base.marked_for_close)
+ if (!base_conn->marked_for_close)
connection_mark_unattached_ap(conn, END_STREAM_REASON_CANT_ATTACH);
}
} else { /* 404, or fetch didn't get that far */
@@ -851,8 +945,34 @@ rend_client_desc_trynow(const char *query)
"unavailable (try again later).",
safe_str_client(query));
connection_mark_unattached_ap(conn, END_STREAM_REASON_RESOLVEFAILED);
+ rend_client_note_connection_attempt_ended(query);
}
- } SMARTLIST_FOREACH_END(_conn);
+ } SMARTLIST_FOREACH_END(base_conn);
+}
+
+/** Clear temporary state used only during an attempt to connect to
+ * the hidden service named <b>onion_address</b>. Called when a
+ * connection attempt has ended; may be called occasionally at other
+ * times, and should be reasonably harmless. */
+void
+rend_client_note_connection_attempt_ended(const char *onion_address)
+{
+ rend_cache_entry_t *cache_entry = NULL;
+ rend_cache_lookup_entry(onion_address, -1, &cache_entry);
+
+ log_info(LD_REND, "Connection attempt for %s has ended; "
+ "cleaning up temporary state.",
+ safe_str_client(onion_address));
+
+ /* Clear the timed_out flag on all remaining intro points for this HS. */
+ if (cache_entry != NULL) {
+ SMARTLIST_FOREACH(cache_entry->parsed->intro_nodes,
+ rend_intro_point_t *, ip,
+ ip->timed_out = 0; );
+ }
+
+ /* Remove the HS's entries in last_hid_serv_requests. */
+ purge_hid_serv_from_last_hid_serv_requests(onion_address);
}
/** Return a newly allocated extend_info_t* for a randomly chosen introduction
@@ -894,8 +1014,7 @@ rend_client_get_random_intro_impl(const rend_cache_entry_t *entry,
int i;
rend_intro_point_t *intro;
- routerinfo_t *router;
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
smartlist_t *usable_nodes;
int n_excluded = 0;
@@ -904,6 +1023,13 @@ rend_client_get_random_intro_impl(const rend_cache_entry_t *entry,
usable_nodes = smartlist_create();
smartlist_add_all(usable_nodes, entry->parsed->intro_nodes);
+ /* Remove the intro points that have timed out during this HS
+ * connection attempt from our list of usable nodes. */
+ SMARTLIST_FOREACH(usable_nodes, rend_intro_point_t *, ip,
+ if (ip->timed_out) {
+ SMARTLIST_DEL_CURRENT(usable_nodes, ip);
+ });
+
again:
if (smartlist_len(usable_nodes) == 0) {
if (n_excluded && get_options()->StrictNodes && warnings) {
@@ -921,21 +1047,22 @@ rend_client_get_random_intro_impl(const rend_cache_entry_t *entry,
intro = smartlist_get(usable_nodes, i);
/* Do we need to look up the router or is the extend info complete? */
if (!intro->extend_info->onion_key) {
+ const node_t *node;
if (tor_digest_is_zero(intro->extend_info->identity_digest))
- router = router_get_by_hexdigest(intro->extend_info->nickname);
+ node = node_get_by_hex_id(intro->extend_info->nickname);
else
- router = router_get_by_digest(intro->extend_info->identity_digest);
- if (!router) {
+ node = node_get_by_id(intro->extend_info->identity_digest);
+ if (!node) {
log_info(LD_REND, "Unknown router with nickname '%s'; trying another.",
intro->extend_info->nickname);
smartlist_del(usable_nodes, i);
goto again;
}
extend_info_free(intro->extend_info);
- intro->extend_info = extend_info_from_router(router);
+ intro->extend_info = extend_info_from_node(node);
}
/* Check if we should refuse to talk to this router. */
- if (options->ExcludeNodes && strict &&
+ if (strict &&
routerset_contains_extendinfo(options->ExcludeNodes,
intro->extend_info)) {
n_excluded++;
@@ -1001,7 +1128,8 @@ rend_service_authorization_free_all(void)
* service and add it to the local map of hidden service authorizations.
* Return 0 for success and -1 for failure. */
int
-rend_parse_service_authorization(or_options_t *options, int validate_only)
+rend_parse_service_authorization(const or_options_t *options,
+ int validate_only)
{
config_line_t *line;
int res = -1;