mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-28 06:13:31 +01:00
Merge remote-tracking branch 'dgoulet/bug14847_027_06'
This commit is contained in:
commit
54000d7ad9
210
src/or/control.c
210
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)
|
||||
|
@ -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 *onion_address,
|
||||
rend_auth_type_t auth_type,
|
||||
const char *id_digest,
|
||||
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,
|
||||
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
|
||||
|
@ -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)) {
|
||||
|
@ -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];
|
||||
|
||||
|
@ -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. */
|
||||
tor_assert(desc_id_base32);
|
||||
|
||||
/* 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);
|
||||
|
||||
/* Only select those hidden service directories to which we did not send
|
||||
* a request recently and for which we have a router descriptor here. */
|
||||
|
||||
/* 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);
|
||||
/* 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)) {
|
||||
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, rend_query, now, 1);
|
||||
lookup_last_hid_serv_request(hs_dir, desc_id_base32, now, 1);
|
||||
}
|
||||
|
||||
/* Encode descriptor cookie for logging purposes. */
|
||||
return hs_dir;
|
||||
}
|
||||
|
||||
/** 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 */
|
||||
}
|
||||
/* 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. */
|
||||
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);
|
||||
|
||||
done:
|
||||
memwipe(descriptor_id, 0, sizeof(descriptor_id));
|
||||
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -28,6 +28,68 @@
|
||||
#define STR_HSDIR_NONE_EXIST_LONGNAME \
|
||||
"$BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
|
||||
|
||||
/* DuckDuckGo descriptor as an example. */
|
||||
static const char *hs_desc_content = "\
|
||||
rendezvous-service-descriptor g5ojobzupf275beh5ra72uyhb3dkpxwg\r\n\
|
||||
version 2\r\n\
|
||||
permanent-key\r\n\
|
||||
-----BEGIN RSA PUBLIC KEY-----\r\n\
|
||||
MIGJAoGBAJ/SzzgrXPxTlFrKVhXh3buCWv2QfcNgncUpDpKouLn3AtPH5Ocys0jE\r\n\
|
||||
aZSKdvaiQ62md2gOwj4x61cFNdi05tdQjS+2thHKEm/KsB9BGLSLBNJYY356bupg\r\n\
|
||||
I5gQozM65ENelfxYlysBjJ52xSDBd8C4f/p9umdzaaaCmzXG/nhzAgMBAAE=\r\n\
|
||||
-----END RSA PUBLIC KEY-----\r\n\
|
||||
secret-id-part anmjoxxwiupreyajjt5yasimfmwcnxlf\r\n\
|
||||
publication-time 2015-03-11 19:00:00\r\n\
|
||||
protocol-versions 2,3\r\n\
|
||||
introduction-points\r\n\
|
||||
-----BEGIN MESSAGE-----\r\n\
|
||||
aW50cm9kdWN0aW9uLXBvaW50IDd1bnd4cmg2dG5kNGh6eWt1Z3EzaGZzdHduc2ll\r\n\
|
||||
cmhyCmlwLWFkZHJlc3MgMTg4LjEzOC4xMjEuMTE4Cm9uaW9uLXBvcnQgOTAwMQpv\r\n\
|
||||
bmlvbi1rZXkKLS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tCk1JR0pBb0dC\r\n\
|
||||
QUxGRVVyeVpDbk9ROEhURmV5cDVjMTRObWVqL1BhekFLTTBxRENTNElKUWh0Y3g1\r\n\
|
||||
NXpRSFdOVWIKQ2hHZ0JqR1RjV3ZGRnA0N3FkdGF6WUZhVXE2c0lQKzVqeWZ5b0Q4\r\n\
|
||||
UmJ1bzBwQmFWclJjMmNhYUptWWM0RDh6Vgpuby9sZnhzOVVaQnZ1cWY4eHIrMDB2\r\n\
|
||||
S0JJNmFSMlA2OE1WeDhrMExqcUpUU2RKOE9idm9yQWdNQkFBRT0KLS0tLS1FTkQg\r\n\
|
||||
UlNBIFBVQkxJQyBLRVktLS0tLQpzZXJ2aWNlLWtleQotLS0tLUJFR0lOIFJTQSBQ\r\n\
|
||||
VUJMSUMgS0VZLS0tLS0KTUlHSkFvR0JBTnJHb0ozeTlHNXQzN2F2ekI1cTlwN1hG\r\n\
|
||||
VUplRUVYMUNOaExnWmJXWGJhVk5OcXpoZFhyL0xTUQppM1Z6dW5OaUs3cndUVnE2\r\n\
|
||||
K2QyZ1lRckhMMmIvMXBBY3ZKWjJiNSs0bTRRc0NibFpjRENXTktRbHJnRWN5WXRJ\r\n\
|
||||
CkdscXJTbFFEaXA0ZnNrUFMvNDVkWTI0QmJsQ3NGU1k3RzVLVkxJck4zZFpGbmJr\r\n\
|
||||
NEZIS1hBZ01CQUFFPQotLS0tLUVORCBSU0EgUFVCTElDIEtFWS0tLS0tCmludHJv\r\n\
|
||||
ZHVjdGlvbi1wb2ludCBiNGM3enlxNXNheGZzN2prNXFibG1wN3I1b3pwdHRvagpp\r\n\
|
||||
cC1hZGRyZXNzIDEwOS4xNjkuNDUuMjI2Cm9uaW9uLXBvcnQgOTAwMQpvbmlvbi1r\r\n\
|
||||
ZXkKLS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tCk1JR0pBb0dCQU8xSXpw\r\n\
|
||||
WFFUTUY3RXZUb1NEUXpzVnZiRVFRQUQrcGZ6NzczMVRXZzVaUEJZY1EyUkRaeVp4\r\n\
|
||||
OEQKNUVQSU1FeUE1RE83cGd0ak5LaXJvYXJGMC8yempjMkRXTUlSaXZyU29YUWVZ\r\n\
|
||||
ZXlMM1pzKzFIajJhMDlCdkYxZAp6MEswblRFdVhoNVR5V3lyMHdsbGI1SFBnTlI0\r\n\
|
||||
MS9oYkprZzkwZitPVCtIeGhKL1duUml2QWdNQkFBRT0KLS0tLS1FTkQgUlNBIFBV\r\n\
|
||||
QkxJQyBLRVktLS0tLQpzZXJ2aWNlLWtleQotLS0tLUJFR0lOIFJTQSBQVUJMSUMg\r\n\
|
||||
S0VZLS0tLS0KTUlHSkFvR0JBSzNWZEJ2ajFtQllLL3JrcHNwcm9Ub0llNUtHVmth\r\n\
|
||||
QkxvMW1tK1I2YUVJek1VZFE1SjkwNGtyRwpCd3k5NC8rV0lGNFpGYXh5Z2phejl1\r\n\
|
||||
N2pKY1k3ZGJhd1pFeG1hYXFCRlRwL2h2ZG9rcHQ4a1ByRVk4OTJPRHJ1CmJORUox\r\n\
|
||||
N1FPSmVMTVZZZk5Kcjl4TWZCQ3JQai8zOGh2RUdrbWVRNmRVWElvbVFNaUJGOVRB\r\n\
|
||||
Z01CQUFFPQotLS0tLUVORCBSU0EgUFVCTElDIEtFWS0tLS0tCmludHJvZHVjdGlv\r\n\
|
||||
bi1wb2ludCBhdjVtcWl0Y2Q3cjJkandsYmN0c2Jlc2R3eGt0ZWtvegppcC1hZGRy\r\n\
|
||||
ZXNzIDE0NC43Ni44LjczCm9uaW9uLXBvcnQgNDQzCm9uaW9uLWtleQotLS0tLUJF\r\n\
|
||||
R0lOIFJTQSBQVUJMSUMgS0VZLS0tLS0KTUlHSkFvR0JBTzVweVZzQmpZQmNmMXBE\r\n\
|
||||
dklHUlpmWXUzQ05nNldka0ZLMGlvdTBXTGZtejZRVDN0NWhzd3cyVwpjejlHMXhx\r\n\
|
||||
MmN0Nkd6VWkrNnVkTDlITTRVOUdHTi9BbW8wRG9GV1hKWHpBQkFXd2YyMVdsd1lW\r\n\
|
||||
eFJQMHRydi9WCkN6UDkzcHc5OG5vSmdGUGRUZ05iMjdKYmVUZENLVFBrTEtscXFt\r\n\
|
||||
b3NveUN2RitRa25vUS9BZ01CQUFFPQotLS0tLUVORCBSU0EgUFVCTElDIEtFWS0t\r\n\
|
||||
LS0tCnNlcnZpY2Uta2V5Ci0tLS0tQkVHSU4gUlNBIFBVQkxJQyBLRVktLS0tLQpN\r\n\
|
||||
SUdKQW9HQkFMVjNKSmtWN3lTNU9jc1lHMHNFYzFQOTVRclFRR3ZzbGJ6Wi9zRGxl\r\n\
|
||||
RlpKYXFSOUYvYjRUVERNClNGcFMxcU1GbldkZDgxVmRGMEdYRmN2WVpLamRJdHU2\r\n\
|
||||
SndBaTRJeEhxeXZtdTRKdUxrcXNaTEFLaXRLVkx4eGsKeERlMjlDNzRWMmJrOTRJ\r\n\
|
||||
MEgybTNKS2tzTHVwc3VxWWRVUmhOVXN0SElKZmgyZmNIalF0bEFnTUJBQUU9Ci0t\r\n\
|
||||
LS0tRU5EIFJTQSBQVUJMSUMgS0VZLS0tLS0KCg==\r\n\
|
||||
-----END MESSAGE-----\r\n\
|
||||
signature\r\n\
|
||||
-----BEGIN SIGNATURE-----\r\n\
|
||||
d4OuCE5OLAOnRB6cQN6WyMEmg/BHem144Vec+eYgeWoKwx3MxXFplUjFxgnMlmwN\r\n\
|
||||
PcftsZf2ztN0sbNCtPgDL3d0PqvxY3iHTQAI8EbaGq/IAJUZ8U4y963dD5+Bn6JQ\r\n\
|
||||
myE3ctmh0vy5+QxSiRjmQBkuEpCyks7LvWvHYrhnmcg=\r\n\
|
||||
-----END SIGNATURE-----";
|
||||
|
||||
/* Helper global variable for hidden service descriptor event test.
|
||||
* It's used as a pointer to dynamically created message buffer in
|
||||
* send_control_event_string_replacement function, which mocks
|
||||
@ -70,6 +132,7 @@ test_hs_desc_event(void *arg)
|
||||
{
|
||||
#define STR_HS_ADDR "ajhb7kljbiru65qo"
|
||||
#define STR_HS_ID "b3oeducbhjmbqmgw2i3jtz4fekkrinwj"
|
||||
#define STR_DESC_ID "g5ojobzupf275beh5ra72uyhb3dkpxwg"
|
||||
|
||||
rend_data_t rend_query;
|
||||
const char *expected_msg;
|
||||
@ -96,7 +159,8 @@ test_hs_desc_event(void *arg)
|
||||
|
||||
/* test received event */
|
||||
rend_query.auth_type = 1;
|
||||
control_event_hs_descriptor_received(&rend_query, HSDIR_EXIST_ID);
|
||||
control_event_hs_descriptor_received(rend_query.onion_address,
|
||||
rend_query.auth_type, HSDIR_EXIST_ID);
|
||||
expected_msg = "650 HS_DESC RECEIVED "STR_HS_ADDR" BASIC_AUTH "\
|
||||
STR_HSDIR_EXIST_LONGNAME"\r\n";
|
||||
tt_assert(received_msg);
|
||||
@ -105,7 +169,8 @@ test_hs_desc_event(void *arg)
|
||||
|
||||
/* test failed event */
|
||||
rend_query.auth_type = 2;
|
||||
control_event_hs_descriptor_failed(&rend_query, HSDIR_NONE_EXIST_ID,
|
||||
control_event_hs_descriptor_failed(rend_query.onion_address,
|
||||
rend_query.auth_type, HSDIR_NONE_EXIST_ID,
|
||||
"QUERY_REJECTED");
|
||||
expected_msg = "650 HS_DESC FAILED "STR_HS_ADDR" STEALTH_AUTH "\
|
||||
STR_HSDIR_NONE_EXIST_LONGNAME" REASON=QUERY_REJECTED\r\n";
|
||||
@ -115,7 +180,8 @@ test_hs_desc_event(void *arg)
|
||||
|
||||
/* test invalid auth type */
|
||||
rend_query.auth_type = 999;
|
||||
control_event_hs_descriptor_failed(&rend_query, HSDIR_EXIST_ID,
|
||||
control_event_hs_descriptor_failed(rend_query.onion_address,
|
||||
rend_query.auth_type, HSDIR_EXIST_ID,
|
||||
"QUERY_REJECTED");
|
||||
expected_msg = "650 HS_DESC FAILED "STR_HS_ADDR" UNKNOWN "\
|
||||
STR_HSDIR_EXIST_LONGNAME" REASON=QUERY_REJECTED\r\n";
|
||||
@ -123,6 +189,18 @@ test_hs_desc_event(void *arg)
|
||||
tt_str_op(received_msg,OP_EQ, expected_msg);
|
||||
tor_free(received_msg);
|
||||
|
||||
/* test valid content. */
|
||||
char *exp_msg;
|
||||
control_event_hs_descriptor_content(rend_query.onion_address, STR_DESC_ID,
|
||||
HSDIR_EXIST_ID, hs_desc_content);
|
||||
tor_asprintf(&exp_msg, "650+HS_DESC_CONTENT " STR_HS_ADDR " " STR_DESC_ID \
|
||||
" " STR_HSDIR_EXIST_LONGNAME "\r\n%s\r\n.\r\n650 OK\r\n",
|
||||
hs_desc_content);
|
||||
tt_assert(received_msg);
|
||||
tt_str_op(received_msg, OP_EQ, exp_msg);
|
||||
tor_free(received_msg);
|
||||
tor_free(exp_msg);
|
||||
|
||||
done:
|
||||
UNMOCK(send_control_event_string);
|
||||
UNMOCK(node_describe_longname_by_id);
|
||||
|
Loading…
Reference in New Issue
Block a user