diff options
Diffstat (limited to 'src/or/rendclient.c')
-rw-r--r-- | src/or/rendclient.c | 244 |
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; |