aboutsummaryrefslogtreecommitdiff
path: root/src/or
diff options
context:
space:
mode:
Diffstat (limited to 'src/or')
-rw-r--r--src/or/control.c210
-rw-r--r--src/or/control.h24
-rw-r--r--src/or/directory.c38
-rw-r--r--src/or/or.h4
-rw-r--r--src/or/rendclient.c296
-rw-r--r--src/or/rendclient.h2
-rw-r--r--src/or/rendcommon.c38
-rw-r--r--src/or/rendcommon.h4
-rw-r--r--src/or/routerparse.c3
9 files changed, 489 insertions, 130 deletions
diff --git a/src/or/control.c b/src/or/control.c
index b2706bc18a..8c95b056b2 100644
--- a/src/or/control.c
+++ b/src/or/control.c
@@ -37,6 +37,8 @@
#include "nodelist.h"
#include "policies.h"
#include "reasons.h"
+#include "rendclient.h"
+#include "rendcommon.h"
#include "rephist.h"
#include "router.h"
#include "routerlist.h"
@@ -159,6 +161,8 @@ static int handle_control_resolve(control_connection_t *conn, uint32_t len,
static int handle_control_usefeature(control_connection_t *conn,
uint32_t len,
const char *body);
+static int handle_control_hsfetch(control_connection_t *conn, uint32_t len,
+ const char *body);
static int write_stream_target_to_buf(entry_connection_t *conn, char *buf,
size_t len);
static void orconn_target_get_name(char *buf, size_t len,
@@ -943,6 +947,7 @@ static const struct control_event_t control_event_table[] = {
{ EVENT_CIRC_BANDWIDTH_USED, "CIRC_BW" },
{ EVENT_TRANSPORT_LAUNCHED, "TRANSPORT_LAUNCHED" },
{ EVENT_HS_DESC, "HS_DESC" },
+ { EVENT_HS_DESC_CONTENT, "HS_DESC_CONTENT" },
{ 0, NULL },
};
@@ -3276,6 +3281,113 @@ handle_control_dropguards(control_connection_t *conn,
return 0;
}
+/** Implementation for the HSFETCH command. */
+static int
+handle_control_hsfetch(control_connection_t *conn, uint32_t len,
+ const char *body)
+{
+ int i;
+ char digest[DIGEST_LEN], *hsaddress = NULL, *arg1 = NULL, *desc_id = NULL;
+ smartlist_t *args = NULL, *hsdirs = NULL;
+ (void) len; /* body is nul-terminated; it's safe to ignore the length */
+ static const char *hsfetch_command = "HSFETCH";
+ static const char *v2_str = "v2-";
+ const size_t v2_str_len = strlen(v2_str);
+ rend_data_t *rend_query = NULL;
+
+ /* Make sure we have at least one argument, the HSAddress. */
+ args = getargs_helper(hsfetch_command, conn, body, 1, -1);
+ if (!args) {
+ goto exit;
+ }
+
+ /* Extract the first argument (either HSAddress or DescID). */
+ arg1 = smartlist_get(args, 0);
+ /* Test if it's an HS address without the .onion part. */
+ if (rend_valid_service_id(arg1)) {
+ hsaddress = arg1;
+ } else if (strcmpstart(arg1, v2_str) == 0 &&
+ rend_valid_descriptor_id(arg1 + v2_str_len) &&
+ base32_decode(digest, sizeof(digest), arg1 + v2_str_len,
+ REND_DESC_ID_V2_LEN_BASE32) == 0) {
+ /* We have a well formed version 2 descriptor ID. Keep the decoded value
+ * of the id. */
+ desc_id = digest;
+ } else {
+ connection_printf_to_buf(conn, "513 Unrecognized \"%s\"\r\n",
+ arg1);
+ goto done;
+ }
+
+ static const char *opt_server = "SERVER=";
+
+ /* Skip first argument because it's the HSAddress or DescID. */
+ for (i = 1; i < smartlist_len(args); ++i) {
+ const char *arg = smartlist_get(args, i);
+ const node_t *node;
+
+ if (!strcasecmpstart(arg, opt_server)) {
+ const char *server;
+
+ server = arg + strlen(opt_server);
+ node = node_get_by_hex_id(server);
+ if (!node) {
+ connection_printf_to_buf(conn, "552 Server \"%s\" not found\r\n",
+ server);
+ goto done;
+ }
+ if (!hsdirs) {
+ /* Stores routerstatus_t object for each specified server. */
+ hsdirs = smartlist_new();
+ }
+ /* Valid server, add it to our local list. */
+ smartlist_add(hsdirs, node->rs);
+ } else {
+ connection_printf_to_buf(conn, "513 Unexpected argument \"%s\"\r\n",
+ arg);
+ goto done;
+ }
+ }
+
+ rend_query = tor_malloc_zero(sizeof(*rend_query));
+
+ if (hsaddress) {
+ strncpy(rend_query->onion_address, hsaddress,
+ sizeof(rend_query->onion_address));
+ } else if (desc_id) {
+ /* Using a descriptor ID, we force the user to provide at least one
+ * hsdir server using the SERVER= option. */
+ if (!hsdirs || !smartlist_len(hsdirs)) {
+ connection_printf_to_buf(conn, "512 %s option is required\r\n",
+ opt_server);
+ goto done;
+ }
+ memcpy(rend_query->descriptor_id, desc_id,
+ sizeof(rend_query->descriptor_id));
+ } else {
+ /* We can't get in here because of the first argument check. */
+ tor_assert(0);
+ }
+ /* We are about to trigger HSDir fetch so send the OK now because after
+ * that 650 event(s) are possible so better to have the 250 OK before them
+ * to avoid out of order replies. */
+ send_control_done(conn);
+
+ /* Trigger the fetch using the built rend query and possibly a list of HS
+ * directory to use. This function ignores the client cache thus this will
+ * always send a fetch command. */
+ rend_client_fetch_v2_desc(rend_query, hsdirs);
+
+done:
+ SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
+ smartlist_free(args);
+ /* Contains data pointer that we don't own thus no cleanup. */
+ smartlist_free(hsdirs);
+ tor_free(rend_query);
+exit:
+ return 0;
+}
+
/** Called when <b>conn</b> has no more bytes left on its outbuf. */
int
connection_control_finished_flushing(control_connection_t *conn)
@@ -3573,6 +3685,9 @@ connection_control_process_inbuf(control_connection_t *conn)
} else if (!strcasecmp(conn->incoming_cmd, "DROPGUARDS")) {
if (handle_control_dropguards(conn, cmd_data_len, args))
return -1;
+ } else if (!strcasecmp(conn->incoming_cmd, "HSFETCH")) {
+ if (handle_control_hsfetch(conn, cmd_data_len, args))
+ return -1;
} else {
connection_printf_to_buf(conn, "510 Unrecognized command \"%s\"\r\n",
conn->incoming_cmd);
@@ -5241,6 +5356,31 @@ node_describe_longname_by_id,(const char *id_digest))
return longname;
}
+
+/** Return either the onion address if the given pointer is a non empty
+ * string else the unknown string. */
+static const char *
+rend_hsaddress_str_or_unknown(const char *onion_address)
+{
+ static const char *str_unknown = "UNKNOWN";
+ const char *str_ret = str_unknown;
+
+ /* No valid pointer, unknown it is. */
+ if (!onion_address) {
+ goto end;
+ }
+ /* Empty onion address thus we don't know, unknown it is. */
+ if (onion_address[0] == '\0') {
+ goto end;
+ }
+ /* All checks are good so return the given onion address. */
+ str_ret = onion_address;
+
+end:
+ return str_ret;
+}
+
+
/** send HS_DESC requested event.
*
* <b>rend_query</b> is used to fetch requested onion address and auth type.
@@ -5261,7 +5401,7 @@ control_event_hs_descriptor_requested(const rend_data_t *rend_query,
send_control_event(EVENT_HS_DESC, ALL_FORMATS,
"650 HS_DESC REQUESTED %s %s %s %s\r\n",
- rend_query->onion_address,
+ rend_hsaddress_str_or_unknown(rend_query->onion_address),
rend_auth_type_to_string(rend_query->auth_type),
node_describe_longname_by_id(id_digest),
desc_id_base32);
@@ -5277,15 +5417,16 @@ control_event_hs_descriptor_requested(const rend_data_t *rend_query,
*/
void
control_event_hs_descriptor_receive_end(const char *action,
- const rend_data_t *rend_query,
+ const char *onion_address,
+ rend_auth_type_t auth_type,
const char *id_digest,
const char *reason)
{
char *reason_field = NULL;
- if (!action || !rend_query || !id_digest) {
- log_warn(LD_BUG, "Called with action==%p, rend_query==%p, "
- "id_digest==%p", action, rend_query, id_digest);
+ if (!action || !id_digest || !onion_address) {
+ log_warn(LD_BUG, "Called with action==%p, id_digest==%p "
+ "onion_address==%p", action, id_digest, onion_address);
return;
}
@@ -5296,8 +5437,8 @@ control_event_hs_descriptor_receive_end(const char *action,
send_control_event(EVENT_HS_DESC, ALL_FORMATS,
"650 HS_DESC %s %s %s %s%s\r\n",
action,
- rend_query->onion_address,
- rend_auth_type_to_string(rend_query->auth_type),
+ rend_hsaddress_str_or_unknown(onion_address),
+ rend_auth_type_to_string(auth_type),
node_describe_longname_by_id(id_digest),
reason_field ? reason_field : "");
@@ -5309,16 +5450,16 @@ control_event_hs_descriptor_receive_end(const char *action,
* called when a we successfully received a hidden service descriptor.
*/
void
-control_event_hs_descriptor_received(const rend_data_t *rend_query,
+control_event_hs_descriptor_received(const char *onion_address,
+ rend_auth_type_t auth_type,
const char *id_digest)
{
- if (!rend_query || !id_digest) {
- log_warn(LD_BUG, "Called with rend_query==%p, id_digest==%p",
- rend_query, id_digest);
+ if (!id_digest) {
+ log_warn(LD_BUG, "Called with id_digest==%p", id_digest);
return;
}
- control_event_hs_descriptor_receive_end("RECEIVED", rend_query,
- id_digest, NULL);
+ control_event_hs_descriptor_receive_end("RECEIVED", onion_address,
+ auth_type, id_digest, NULL);
}
/** Send HS_DESC event to inform controller that query <b>rend_query</b>
@@ -5327,19 +5468,52 @@ control_event_hs_descriptor_received(const rend_data_t *rend_query,
* field.
*/
void
-control_event_hs_descriptor_failed(const rend_data_t *rend_query,
+control_event_hs_descriptor_failed(const char *onion_address,
+ rend_auth_type_t auth_type,
const char *id_digest,
const char *reason)
{
- if (!rend_query || !id_digest) {
- log_warn(LD_BUG, "Called with rend_query==%p, id_digest==%p",
- rend_query, id_digest);
+ if (!id_digest) {
+ log_warn(LD_BUG, "Called with id_digest==%p", id_digest);
return;
}
- control_event_hs_descriptor_receive_end("FAILED", rend_query,
+ control_event_hs_descriptor_receive_end("FAILED", onion_address, auth_type,
id_digest, reason);
}
+/** send HS_DESC_CONTENT event after completion of a successful fetch from
+ * hs directory. */
+void
+control_event_hs_descriptor_content(const char *onion_address,
+ const char *desc_id,
+ const char *hsdir_id_digest,
+ const char *content)
+{
+ static const char *event_name = "HS_DESC_CONTENT";
+ char *esc_content = NULL;
+
+ if (!onion_address || !desc_id || !hsdir_id_digest) {
+ log_warn(LD_BUG, "Called with onion_address==%p, desc_id==%p, "
+ "hsdir_id_digest==%p", onion_address, desc_id, hsdir_id_digest);
+ return;
+ }
+
+ if (content == NULL) {
+ /* Point it to empty content so it can still be escaped. */
+ content = "";
+ }
+ write_escaped_data(content, strlen(content), &esc_content);
+
+ send_control_event(EVENT_HS_DESC_CONTENT, ALL_FORMATS,
+ "650+%s %s %s %s\r\n%s650 OK\r\n",
+ event_name,
+ rend_hsaddress_str_or_unknown(onion_address),
+ desc_id,
+ node_describe_longname_by_id(hsdir_id_digest),
+ esc_content);
+ tor_free(esc_content);
+}
+
/** Free any leftover allocated memory of the control.c subsystem. */
void
control_free_all(void)
diff --git a/src/or/control.h b/src/or/control.h
index dbb80b1f20..93bcb8ef3d 100644
--- a/src/or/control.h
+++ b/src/or/control.h
@@ -107,14 +107,21 @@ void control_event_hs_descriptor_requested(const rend_data_t *rend_query,
const char *desc_id_base32,
const char *hs_dir);
void control_event_hs_descriptor_receive_end(const char *action,
- const rend_data_t *rend_query,
- const char *hs_dir,
- const char *reason);
-void control_event_hs_descriptor_received(const rend_data_t *rend_query,
- const char *hs_dir);
-void control_event_hs_descriptor_failed(const rend_data_t *rend_query,
- const char *hs_dir,
+ const char *onion_address,
+ rend_auth_type_t auth_type,
+ const char *id_digest,
+ const char *reason);
+void control_event_hs_descriptor_received(const char *onion_address,
+ rend_auth_type_t auth_type,
+ const char *id_digest);
+void control_event_hs_descriptor_failed(const char *onion_address,
+ rend_auth_type_t auth_type,
+ const char *id_digest,
const char *reason);
+void control_event_hs_descriptor_content(const char *onion_address,
+ const char *desc_id,
+ const char *hsdir_fp,
+ const char *content);
void control_free_all(void);
@@ -158,7 +165,8 @@ void control_free_all(void);
#define EVENT_CIRC_BANDWIDTH_USED 0x001D
#define EVENT_TRANSPORT_LAUNCHED 0x0020
#define EVENT_HS_DESC 0x0021
-#define EVENT_MAX_ 0x0021
+#define EVENT_HS_DESC_CONTENT 0x0022
+#define EVENT_MAX_ 0x0022
/* sizeof(control_connection_t.event_mask) in bits, currently a uint64_t */
#define EVENT_CAPACITY_ 0x0040
diff --git a/src/or/directory.c b/src/or/directory.c
index d2b6b86f6d..bad1f62153 100644
--- a/src/or/directory.c
+++ b/src/or/directory.c
@@ -2099,17 +2099,27 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
if (conn->base_.purpose == DIR_PURPOSE_FETCH_RENDDESC_V2) {
#define SEND_HS_DESC_FAILED_EVENT(reason) ( \
- control_event_hs_descriptor_failed(conn->rend_data, \
+ control_event_hs_descriptor_failed(conn->rend_data->onion_address, \
+ conn->rend_data->auth_type, \
conn->identity_digest, \
reason) )
+ #define SEND_HS_DESC_FAILED_CONTENT() ( \
+ control_event_hs_descriptor_content(conn->rend_data->onion_address, \
+ conn->requested_resource, \
+ conn->identity_digest, \
+ NULL) )
tor_assert(conn->rend_data);
log_info(LD_REND,"Received rendezvous descriptor (size %d, status %d "
"(%s))",
(int)body_len, status_code, escaped(reason));
switch (status_code) {
case 200:
+ {
+ rend_cache_entry_t *entry = NULL;
+
switch (rend_cache_store_v2_desc_as_client(body,
- conn->requested_resource, conn->rend_data)) {
+ conn->requested_resource, conn->rend_data,
+ &entry)) {
case RCS_BADDESC:
case RCS_NOTDIR: /* Impossible */
log_warn(LD_REND,"Fetching v2 rendezvous descriptor failed. "
@@ -2117,25 +2127,41 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
/* We'll retry when connection_about_to_close_connection()
* cleans this dir conn up. */
SEND_HS_DESC_FAILED_EVENT("BAD_DESC");
+ SEND_HS_DESC_FAILED_CONTENT();
break;
case RCS_OKAY:
default:
+ {
+ char service_id[REND_SERVICE_ID_LEN_BASE32 + 1];
+ /* Should never be NULL here for an OKAY returned code. */
+ tor_assert(entry);
+ rend_get_service_id(entry->parsed->pk, service_id);
+
/* success. notify pending connections about this. */
log_info(LD_REND, "Successfully fetched v2 rendezvous "
"descriptor.");
- control_event_hs_descriptor_received(conn->rend_data,
+ control_event_hs_descriptor_received(service_id,
+ conn->rend_data->auth_type,
conn->identity_digest);
+ control_event_hs_descriptor_content(service_id,
+ conn->requested_resource,
+ conn->identity_digest,
+ body);
conn->base_.purpose = DIR_PURPOSE_HAS_FETCHED_RENDDESC_V2;
- rend_client_desc_trynow(conn->rend_data->onion_address);
+ rend_client_desc_trynow(service_id);
+ memwipe(service_id, 0, sizeof(service_id));
break;
+ }
}
break;
+ }
case 404:
/* Not there. We'll retry when
* connection_about_to_close_connection() cleans this conn up. */
log_info(LD_REND,"Fetching v2 rendezvous descriptor failed: "
"Retrying at another directory.");
SEND_HS_DESC_FAILED_EVENT("NOT_FOUND");
+ SEND_HS_DESC_FAILED_CONTENT();
break;
case 400:
log_warn(LD_REND, "Fetching v2 rendezvous descriptor failed: "
@@ -2143,6 +2169,7 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
"v2 rendezvous query? Retrying at another directory.",
escaped(reason));
SEND_HS_DESC_FAILED_EVENT("QUERY_REJECTED");
+ SEND_HS_DESC_FAILED_CONTENT();
break;
default:
log_warn(LD_REND, "Fetching v2 rendezvous descriptor failed: "
@@ -2152,6 +2179,7 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
status_code, escaped(reason), conn->base_.address,
conn->base_.port);
SEND_HS_DESC_FAILED_EVENT("UNEXPECTED");
+ SEND_HS_DESC_FAILED_CONTENT();
break;
}
}
@@ -3066,7 +3094,7 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers,
/* Handle v2 rendezvous descriptor fetch request. */
const char *descp;
const char *query = url + strlen("/tor/rendezvous2/");
- if (strlen(query) == REND_DESC_ID_V2_LEN_BASE32) {
+ if (rend_valid_descriptor_id(query)) {
log_info(LD_REND, "Got a v2 rendezvous descriptor request for ID '%s'",
safe_str(escaped(query)));
switch (rend_cache_lookup_v2_desc_as_dir(query, &descp)) {
diff --git a/src/or/or.h b/src/or/or.h
index 2f6890a9ef..1c00423166 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -799,6 +799,10 @@ typedef struct rend_data_t {
/** Authorization type for accessing a service used by a client. */
rend_auth_type_t auth_type;
+ /** Descriptor ID for a client request. The control port command HSFETCH
+ * can use this. */
+ char descriptor_id[DIGEST_LEN];
+
/** Hash of the hidden service's PK used by a service. */
char rend_pk_digest[DIGEST_LEN];
diff --git a/src/or/rendclient.c b/src/or/rendclient.c
index 84d0ccef55..0854d9371e 100644
--- a/src/or/rendclient.c
+++ b/src/or/rendclient.c
@@ -496,15 +496,13 @@ get_last_hid_serv_requests(void)
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> 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. */
+ * for descriptor ID <b>desc_id_base32</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,
- const rend_data_t *rend_query,
time_t now, int set)
{
char hsdir_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
@@ -513,10 +511,9 @@ lookup_last_hid_serv_request(routerstatus_t *hs_dir,
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%s",
+ tor_snprintf(hsdir_desc_comb_id, sizeof(hsdir_desc_comb_id), "%s%s",
hsdir_id_base32,
- desc_id_base32,
- rend_query->onion_address);
+ desc_id_base32);
/* XXX023 tor_assert(strlen(hsdir_desc_comb_id) ==
LAST_HID_SERV_REQUEST_KEY_LEN); */
if (set) {
@@ -609,64 +606,53 @@ rend_client_purge_last_hid_serv_requests(void)
}
}
-/** Determine the responsible hidden service directories for <b>desc_id</b>
- * 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. */
-static int
-directory_get_from_hs_dir(const char *desc_id, const rend_data_t *rend_query)
+/** This returns a good valid hs dir that should be used for the given
+ * descriptor id.
+ *
+ * Return NULL on error else the hsdir node pointer. */
+static routerstatus_t *
+pick_hsdir(const char *desc_id, const char *desc_id_base32)
{
smartlist_t *responsible_dirs = smartlist_new();
smartlist_t *usable_responsible_dirs = smartlist_new();
const or_options_t *options = get_options();
routerstatus_t *hs_dir;
- char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
time_t now = time(NULL);
- char descriptor_cookie_base64[3*REND_DESC_COOKIE_LEN_BASE64];
-#ifdef ENABLE_TOR2WEB_MODE
- const int tor2web_mode = options->Tor2webMode;
- const int how_to_fetch = tor2web_mode ? DIRIND_ONEHOP : DIRIND_ANONYMOUS;
-#else
- const int how_to_fetch = DIRIND_ANONYMOUS;
-#endif
int excluded_some;
- tor_assert(desc_id);
- tor_assert(rend_query);
- /* Determine responsible dirs. Even if we can't get all we want,
- * work with the ones we have. If it's empty, we'll notice below. */
- hid_serv_get_responsible_directories(responsible_dirs, desc_id);
- base32_encode(desc_id_base32, sizeof(desc_id_base32),
- desc_id, DIGEST_LEN);
+ tor_assert(desc_id);
+ tor_assert(desc_id_base32);
- /* Only select those hidden service directories to which we did not send
- * a request recently and for which we have a router descriptor here. */
+ /* Determine responsible dirs. Even if we can't get all we want, work with
+ * the ones we have. If it's empty, we'll notice below. */
+ hid_serv_get_responsible_directories(responsible_dirs, desc_id);
/* Clean request history first. */
directory_clean_last_hid_serv_requests(now);
- SMARTLIST_FOREACH(responsible_dirs, routerstatus_t *, dir, {
- 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);
- continue;
- }
- if (! routerset_contains_node(options->ExcludeNodes, node)) {
- smartlist_add(usable_responsible_dirs, dir);
- }
- });
+ /* Only select those hidden service directories to which we did not send a
+ * request recently and for which we have a router descriptor here. */
+ SMARTLIST_FOREACH_BEGIN(responsible_dirs, routerstatus_t *, dir) {
+ time_t last = lookup_last_hid_serv_request(dir, desc_id_base32,
+ 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);
+ continue;
+ }
+ if (!routerset_contains_node(options->ExcludeNodes, node)) {
+ smartlist_add(usable_responsible_dirs, dir);
+ }
+ } SMARTLIST_FOREACH_END(dir);
excluded_some =
smartlist_len(usable_responsible_dirs) < smartlist_len(responsible_dirs);
hs_dir = smartlist_choose(usable_responsible_dirs);
- if (! hs_dir && ! options->StrictNodes)
+ if (!hs_dir && !options->StrictNodes) {
hs_dir = smartlist_choose(responsible_dirs);
+ }
smartlist_free(responsible_dirs);
smartlist_free(usable_responsible_dirs);
@@ -679,14 +665,52 @@ directory_get_from_hs_dir(const char *desc_id, const rend_data_t *rend_query)
"requested hidden service: they are all either down or "
"excluded, and StrictNodes is set.");
}
- return 0;
+ } else {
+ /* 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);
}
- /* Remember that we are requesting a descriptor from this hidden service
- * directory now. */
- lookup_last_hid_serv_request(hs_dir, desc_id_base32, rend_query, now, 1);
+ return hs_dir;
+}
- /* Encode descriptor cookie for logging purposes. */
+/** Determine the responsible hidden service directories for <b>desc_id</b>
+ * 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. */
+static int
+directory_get_from_hs_dir(const char *desc_id, const rend_data_t *rend_query,
+ routerstatus_t *rs_hsdir)
+{
+ routerstatus_t *hs_dir = rs_hsdir;
+ char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
+ char descriptor_cookie_base64[3*REND_DESC_COOKIE_LEN_BASE64];
+#ifdef ENABLE_TOR2WEB_MODE
+ const int tor2web_mode = get_options()->Tor2webMode;
+ const int how_to_fetch = tor2web_mode ? DIRIND_ONEHOP : DIRIND_ANONYMOUS;
+#else
+ const int how_to_fetch = DIRIND_ANONYMOUS;
+#endif
+
+ tor_assert(desc_id);
+
+ base32_encode(desc_id_base32, sizeof(desc_id_base32),
+ desc_id, DIGEST_LEN);
+
+ /* Automatically pick an hs dir if none given. */
+ if (!rs_hsdir) {
+ hs_dir = pick_hsdir(desc_id, desc_id_base32);
+ if (!hs_dir) {
+ /* No suitable hs dir can be found, stop right now. */
+ return 0;
+ }
+ }
+
+ /* Encode descriptor cookie for logging purposes. Also, if the cookie is
+ * malformed, no fetch is triggered thus this needs to be done before the
+ * fetch request. */
if (rend_query->auth_type != REND_NO_AUTH) {
if (base64_encode(descriptor_cookie_base64,
sizeof(descriptor_cookie_base64),
@@ -727,16 +751,136 @@ directory_get_from_hs_dir(const char *desc_id, const rend_data_t *rend_query)
return 1;
}
+/** Fetch a v2 descriptor using the given descriptor id. If any hsdir(s) are
+ * given, they will be used instead.
+ *
+ * On success, 1 is returned. If no hidden service is left to ask, return 0.
+ * On error, -1 is returned. */
+static int
+fetch_v2_desc_by_descid(const char *desc_id, const rend_data_t *rend_query,
+ smartlist_t *hsdirs)
+{
+ int ret;
+
+ tor_assert(rend_query);
+
+ if (!hsdirs) {
+ ret = directory_get_from_hs_dir(desc_id, rend_query, NULL);
+ goto end; /* either success or failure, but we're done */
+ }
+
+ /* Using the given hsdir list, trigger a fetch on each of them. */
+ SMARTLIST_FOREACH_BEGIN(hsdirs, routerstatus_t *, hs_dir) {
+ /* This should always be a success. */
+ ret = directory_get_from_hs_dir(desc_id, rend_query, hs_dir);
+ tor_assert(ret);
+ } SMARTLIST_FOREACH_END(hs_dir);
+
+ /* Everything went well. */
+ ret = 0;
+
+end:
+ return ret;
+}
+
+/** Fetch a v2 descriptor using the onion address in the given query object.
+ * This will compute the descriptor id for each replicas and fetch it on the
+ * given hsdir(s) if any or the responsible ones that are choosen
+ * automatically.
+ *
+ * On success, 1 is returned. If no hidden service is left to ask, return 0.
+ * On error, -1 is returned. */
+static int
+fetch_v2_desc_by_addr(const rend_data_t *query,
+ smartlist_t *hsdirs)
+{
+ char descriptor_id[DIGEST_LEN];
+ int replicas_left_to_try[REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS];
+ int i, tries_left, ret;
+
+ tor_assert(query);
+
+ /* Randomly iterate over the replicas until a descriptor can be fetched
+ * from one of the consecutive nodes, or no options are left. */
+ for (i = 0; i < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; i++) {
+ replicas_left_to_try[i] = i;
+ }
+
+ tries_left = REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS;
+ while (tries_left > 0) {
+ int rand = crypto_rand_int(tries_left);
+ int chosen_replica = replicas_left_to_try[rand];
+ replicas_left_to_try[rand] = replicas_left_to_try[--tries_left];
+
+ ret = rend_compute_v2_desc_id(descriptor_id, query->onion_address,
+ query->auth_type == REND_STEALTH_AUTH ?
+ query->descriptor_cookie : NULL,
+ time(NULL), chosen_replica);
+ if (ret < 0) {
+ /* Normally, on failure the descriptor_id is untouched but let's be
+ * safe in general in case the function changes at some point. */
+ goto end;
+ }
+
+ /* Trigger the fetch with the computed descriptor ID. */
+ ret = fetch_v2_desc_by_descid(descriptor_id, query, hsdirs);
+ if (ret != 0) {
+ /* Either on success or failure, as long as we tried a fetch we are
+ * done here. */
+ goto end;
+ }
+ }
+
+ /* If we come here, there are no hidden service directories left. */
+ log_info(LD_REND, "Could not pick one of the responsible hidden "
+ "service directories to fetch descriptors, because "
+ "we already tried them all unsuccessfully.");
+ ret = 0;
+
+end:
+ memwipe(descriptor_id, 0, sizeof(descriptor_id));
+ return ret;
+}
+
+/** Fetch a v2 descriptor using the given query. If any hsdir are specified,
+ * use them for the fetch.
+ *
+ * On success, 1 is returned. If no hidden service is left to ask, return 0.
+ * On error, -1 is returned. */
+int
+rend_client_fetch_v2_desc(const rend_data_t *query,
+ smartlist_t *hsdirs)
+{
+ int ret;
+
+ tor_assert(query);
+
+ /* Depending on what's available in the rend data query object, we will
+ * trigger a fetch by HS address or using a descriptor ID. */
+
+ if (query->onion_address[0] != '\0') {
+ ret = fetch_v2_desc_by_addr(query, hsdirs);
+ } else if (query->descriptor_id[0] != '\0') {
+ ret = fetch_v2_desc_by_descid(query->descriptor_id, query, hsdirs);
+ } else {
+ /* Query data is invalid. */
+ ret = -1;
+ goto error;
+ }
+
+error:
+ return ret;
+}
+
/** Unless we already have a descriptor for <b>rend_query</b> with at least
* one (possibly) working introduction point in it, start a connection to a
* hidden service directory to fetch a v2 rendezvous service descriptor. */
void
rend_client_refetch_v2_renddesc(const rend_data_t *rend_query)
{
- char descriptor_id[DIGEST_LEN];
- int replicas_left_to_try[REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS];
- int i, tries_left;
+ int ret;
rend_cache_entry_t *e = NULL;
+
tor_assert(rend_query);
/* Are we configured to fetch descriptors? */
if (!get_options()->FetchHidServDescriptors) {
@@ -753,44 +897,12 @@ rend_client_refetch_v2_renddesc(const rend_data_t *rend_query)
}
log_debug(LD_REND, "Fetching v2 rendezvous descriptor for service %s",
safe_str_client(rend_query->onion_address));
- /* Randomly iterate over the replicas until a descriptor can be fetched
- * from one of the consecutive nodes, or no options are left. */
- tries_left = REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS;
- for (i = 0; i < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; i++)
- replicas_left_to_try[i] = i;
- while (tries_left > 0) {
- int rand = crypto_rand_int(tries_left);
- int chosen_replica = replicas_left_to_try[rand];
- replicas_left_to_try[rand] = replicas_left_to_try[--tries_left];
- if (rend_compute_v2_desc_id(descriptor_id, rend_query->onion_address,
- rend_query->auth_type == REND_STEALTH_AUTH ?
- rend_query->descriptor_cookie : NULL,
- time(NULL), chosen_replica) < 0) {
- log_warn(LD_REND, "Internal error: Computing v2 rendezvous "
- "descriptor ID did not succeed.");
- /*
- * Hmm, can this write anything to descriptor_id and still fail?
- * Let's clear it just to be safe.
- *
- * From here on, any returns should goto done which clears
- * descriptor_id so we don't leave key-derived material on the stack.
- */
- goto done;
- }
- if (directory_get_from_hs_dir(descriptor_id, rend_query) != 0)
- goto done; /* either success or failure, but we're done */
+ ret = rend_client_fetch_v2_desc(rend_query, NULL);
+ if (ret <= 0) {
+ /* Close pending connections on error or if no hsdir can be found. */
+ rend_client_desc_trynow(rend_query->onion_address);
}
- /* If we come here, there are no hidden service directories left. */
- log_info(LD_REND, "Could not pick one of the responsible hidden "
- "service directories to fetch descriptors, because "
- "we already tried them all unsuccessfully.");
- /* Close pending connections. */
- rend_client_desc_trynow(rend_query->onion_address);
-
- done:
- memwipe(descriptor_id, 0, sizeof(descriptor_id));
-
return;
}
diff --git a/src/or/rendclient.h b/src/or/rendclient.h
index 098c61d0a1..d6fea678d0 100644
--- a/src/or/rendclient.h
+++ b/src/or/rendclient.h
@@ -20,6 +20,8 @@ int rend_client_introduction_acked(origin_circuit_t *circ,
const uint8_t *request,
size_t request_len);
void rend_client_refetch_v2_renddesc(const rend_data_t *rend_query);
+int rend_client_fetch_v2_desc(const rend_data_t *query,
+ smartlist_t *hsdirs);
void rend_client_cancel_descriptor_fetches(void);
void rend_client_purge_last_hid_serv_requests(void);
diff --git a/src/or/rendcommon.c b/src/or/rendcommon.c
index 371c6bf9b8..03dd757921 100644
--- a/src/or/rendcommon.c
+++ b/src/or/rendcommon.c
@@ -920,6 +920,24 @@ rend_valid_service_id(const char *query)
return 1;
}
+/** Return true iff <b>query</b> is a syntactically valid descriptor ID.
+ * (as generated by rend_get_descriptor_id_bytes). */
+int
+rend_valid_descriptor_id(const char *query)
+{
+ if (strlen(query) != REND_DESC_ID_V2_LEN_BASE32) {
+ goto invalid;
+ }
+ if (strspn(query, BASE32_CHARS) != REND_DESC_ID_V2_LEN_BASE32) {
+ goto invalid;
+ }
+
+ return 1;
+
+invalid:
+ return 0;
+}
+
/** Lookup in the client cache the given service ID <b>query</b> for
* <b>version</b>.
*
@@ -1137,12 +1155,14 @@ rend_cache_store_v2_desc_as_dir(const char *desc)
* If the descriptor's descriptor ID doesn't match <b>desc_id_base32</b>,
* reject it.
*
- * Return an appropriate rend_cache_store_status_t.
+ * Return an appropriate rend_cache_store_status_t. If entry is not NULL,
+ * set it with the cache entry pointer of the descriptor.
*/
rend_cache_store_status_t
rend_cache_store_v2_desc_as_client(const char *desc,
const char *desc_id_base32,
- const rend_data_t *rend_query)
+ const rend_data_t *rend_query,
+ rend_cache_entry_t **entry)
{
/*XXXX this seems to have a bit of duplicate code with
* rend_cache_store_v2_desc_as_dir(). Fix that. */
@@ -1175,6 +1195,9 @@ rend_cache_store_v2_desc_as_client(const char *desc,
tor_assert(desc);
tor_assert(desc_id_base32);
memset(want_desc_id, 0, sizeof(want_desc_id));
+ if (entry) {
+ *entry = NULL;
+ }
if (base32_decode(want_desc_id, sizeof(want_desc_id),
desc_id_base32, strlen(desc_id_base32)) != 0) {
log_warn(LD_BUG, "Couldn't decode base32 %s for descriptor id.",
@@ -1193,7 +1216,8 @@ rend_cache_store_v2_desc_as_client(const char *desc,
log_warn(LD_REND, "Couldn't compute service ID.");
goto err;
}
- if (strcmp(rend_query->onion_address, service_id)) {
+ if (rend_query->onion_address[0] != '\0' &&
+ strcmp(rend_query->onion_address, service_id)) {
log_warn(LD_REND, "Received service descriptor for service ID %s; "
"expected descriptor for service ID %s.",
service_id, safe_str(rend_query->onion_address));
@@ -1240,7 +1264,7 @@ rend_cache_store_v2_desc_as_client(const char *desc,
"service descriptor for %s. This is probably a (misguided) "
"attempt to improve reliability, but it could also be an "
"attempt to do a guard enumeration attack. Rejecting.",
- safe_str_client(rend_query->onion_address));
+ safe_str_client(service_id));
goto err;
}
@@ -1286,9 +1310,15 @@ rend_cache_store_v2_desc_as_client(const char *desc,
rend_cache_increment_allocation(rend_cache_entry_allocation(e));
log_debug(LD_REND,"Successfully stored rend desc '%s', len %d.",
safe_str_client(service_id), (int)encoded_size);
+ if (entry) {
+ *entry = e;
+ }
return RCS_OKAY;
okay:
+ if (entry) {
+ *entry = e;
+ }
retval = RCS_OKAY;
err:
diff --git a/src/or/rendcommon.h b/src/or/rendcommon.h
index 8396cc3551..b86cdb7fa6 100644
--- a/src/or/rendcommon.h
+++ b/src/or/rendcommon.h
@@ -37,6 +37,7 @@ void rend_cache_clean_v2_descs_as_dir(time_t now, size_t min_to_remove);
void rend_cache_purge(void);
void rend_cache_free_all(void);
int rend_valid_service_id(const char *query);
+int rend_valid_descriptor_id(const char *query);
int rend_cache_lookup_entry(const char *query, int version,
rend_cache_entry_t **entry_out);
int rend_cache_lookup_v2_desc_as_dir(const char *query, const char **desc);
@@ -50,7 +51,8 @@ typedef enum {
rend_cache_store_status_t rend_cache_store_v2_desc_as_dir(const char *desc);
rend_cache_store_status_t rend_cache_store_v2_desc_as_client(const char *desc,
const char *desc_id_base32,
- const rend_data_t *rend_query);
+ const rend_data_t *rend_query,
+ rend_cache_entry_t **entry);
int rend_encode_v2_descriptors(smartlist_t *descs_out,
rend_service_descriptor_t *desc, time_t now,
uint8_t period, rend_auth_type_t auth_type,
diff --git a/src/or/routerparse.c b/src/or/routerparse.c
index d2eca62491..f15aeeb0cf 100644
--- a/src/or/routerparse.c
+++ b/src/or/routerparse.c
@@ -4575,8 +4575,7 @@ rend_parse_v2_service_descriptor(rend_service_descriptor_t **parsed_out,
tok = find_by_keyword(tokens, R_RENDEZVOUS_SERVICE_DESCRIPTOR);
tor_assert(tok == smartlist_get(tokens, 0));
tor_assert(tok->n_args == 1);
- if (strlen(tok->args[0]) != REND_DESC_ID_V2_LEN_BASE32 ||
- strspn(tok->args[0], BASE32_CHARS) != REND_DESC_ID_V2_LEN_BASE32) {
+ if (!rend_valid_descriptor_id(tok->args[0])) {
log_warn(LD_REND, "Invalid descriptor ID: '%s'", tok->args[0]);
goto err;
}