Skip to content
Permalink
Browse files
Basic tests for the handoff code: let's make sure we can encode them
Decoding tests will follow later
  • Loading branch information
TvdW committed Dec 5, 2015
1 parent 495d388 commit 3a8c941752ff56712633b771831aae50675a9f42
Showing with 174 additions and 74 deletions.
  1. +3 −3 src/or/control.c
  2. +2 −1 src/or/control.h
  3. +2 −69 src/or/rendservice.c
  4. +66 −0 src/or/rendservice.h
  5. +101 −1 src/test/test_introduce.c
@@ -4652,9 +4652,9 @@ write_stream_target_to_buf(entry_connection_t *conn, char *buf, size_t len)
return 0;
}

/** TODO: docs */
int
control_event_rend_handoff(const char *tag, const char *rendezvousdata)
/** We got an INTRODUCE2 cell and decided to hand it off to a controller. */
MOCK_IMPL(int, control_event_rend_handoff, (const char *tag,
const char *rendezvousdata))
{
send_control_event(EVENT_REND_HANDOFF,
"650 INTRODUCE_HANDOFF %s %s\r\n",
@@ -72,7 +72,8 @@ int control_event_my_descriptor_changed(void);
int control_event_network_liveness_update(int liveness);
int control_event_networkstatus_changed(smartlist_t *statuses);

int control_event_rend_handoff(const char *tag, const char *rendezvousdata);
MOCK_DECL(int, control_event_rend_handoff, (const char *tag,
const char *rendezvousdata));

int control_event_newconsensus(const networkstatus_t *consensus);
int control_event_networkstatus_changed_single(const routerstatus_t *rs);
@@ -32,7 +32,6 @@
#include "routerparse.h"
#include "routerset.h"

struct rend_service_t;
static origin_circuit_t *find_intro_circuit(rend_intro_point_t *intro,
const char *pk_digest);
static rend_intro_point_t *find_intro_point(origin_circuit_t *circ);
@@ -58,11 +57,6 @@ static struct rend_service_t *rend_service_get_by_service_id(const char *id);
static const char *rend_service_escaped_dir(
const struct rend_service_t *s);

static int rend_service_perform_rendezvous(rend_intro_cell_t *parsed_req,
struct rend_service_t *service);
static int rend_service_handoff_introduce(rend_intro_cell_t *parsed_req,
struct rend_service_t *service);

static ssize_t rend_service_parse_intro_for_v0_or_v1(
rend_intro_cell_t *intro,
const uint8_t *buf,
@@ -115,66 +109,6 @@ struct rend_service_port_config_s {
* rendezvous point before giving up? */
#define MAX_REND_TIMEOUT 30

/** Represents a single hidden service running at this OP. */
typedef struct rend_service_t {
/* Fields specified in config file */
char *directory; /**< where in the filesystem it stores it. Will be NULL if
* this service is ephemeral. */
char *tag; /** Name tag given to the HS by the user; used to identify
* a hidden service across nodes (when fingerprints
* may differ) */
int dir_group_readable; /**< if 1, allow group read
permissions on directory */
smartlist_t *ports; /**< List of rend_service_port_config_t */
rend_auth_type_t auth_type; /**< Client authorization type or 0 if no client
* authorization is performed. */
smartlist_t *clients; /**< List of rend_authorized_client_t's of
* clients that may access our service. Can be NULL
* if no client authorization is performed. */
/* Other fields */
crypto_pk_t *private_key; /**< Permanent hidden-service key. */
char service_id[REND_SERVICE_ID_LEN_BASE32+1]; /**< Onion address without
* '.onion' */
char pk_digest[DIGEST_LEN]; /**< Hash of permanent hidden-service key. */
smartlist_t *intro_nodes; /**< List of rend_intro_point_t's we have,
* or are trying to establish. */
/** List of rend_intro_point_t that are expiring. They are removed once
* the new descriptor is successfully uploaded. A node in this list CAN
* NOT appear in the intro_nodes list. */
smartlist_t *expiring_nodes;
time_t intro_period_started; /**< Start of the current period to build
* introduction points. */
int n_intro_circuits_launched; /**< Count of intro circuits we have
* established in this period. */
unsigned int n_intro_points_wanted; /**< Number of intro points this
* service wants to have open. */
rend_service_descriptor_t *desc; /**< Current hidden service descriptor. */
time_t desc_is_dirty; /**< Time at which changes to the hidden service
* descriptor content occurred, or 0 if it's
* up-to-date. */
time_t next_upload_time; /**< Scheduled next hidden service descriptor
* upload time. */
/** Replay cache for Diffie-Hellman values of INTRODUCE2 cells, to
* detect repeats. Clients may send INTRODUCE1 cells for the same
* rendezvous point through two or more different introduction points;
* when they do, this keeps us from launching multiple simultaneous attempts
* to connect to the same rend point. */
replaycache_t *accepted_intro_dh_parts;
/** If true, we don't close circuits for making requests to unsupported
* ports. */
int allow_unknown_ports;
/** If false, we send introduce data to the controller instead of
* automatically performing the rendezvous */
int automatic_rendezvous;
/** The maximum number of simultanious streams-per-circuit that are allowed
* to be established, or 0 if no limit is set.
*/
int max_streams_per_circuit;
/** If true, we close circuits that exceed the max_streams_per_circuit
* limit. */
int max_streams_close_circuit;
} rend_service_t;

/** Returns a escaped string representation of the service, <b>s</b>.
*/
static const char *
@@ -1704,9 +1638,8 @@ rend_service_receive_introduction(origin_circuit_t *circuit,
}

/** Perform the rendezvous by launching a circuit to the rendezvous point */
int
rend_service_perform_rendezvous(rend_intro_cell_t *parsed_req,
rend_service_t *service)
MOCK_IMPL(int, rend_service_perform_rendezvous,
(rend_intro_cell_t *parsed_req, rend_service_t *service))
{
/* Global status stuff */
int status = 0;
@@ -63,6 +63,72 @@ struct rend_intro_cell_s {
uint8_t dh[DH_KEY_LEN];
};

/** Represents a single hidden service running at this OP. */
typedef struct rend_service_t {
/* Fields specified in config file */
char *directory; /**< where in the filesystem it stores it. Will be NULL if
* this service is ephemeral. */
char *tag; /** Name tag given to the HS by the user; used to identify
* a hidden service across nodes (when fingerprints
* may differ) */
int dir_group_readable; /**< if 1, allow group read
permissions on directory */
smartlist_t *ports; /**< List of rend_service_port_config_t */
rend_auth_type_t auth_type; /**< Client authorization type or 0 if no client
* authorization is performed. */
smartlist_t *clients; /**< List of rend_authorized_client_t's of
* clients that may access our service. Can be NULL
* if no client authorization is performed. */
/* Other fields */
crypto_pk_t *private_key; /**< Permanent hidden-service key. */
char service_id[REND_SERVICE_ID_LEN_BASE32+1]; /**< Onion address without
* '.onion' */
char pk_digest[DIGEST_LEN]; /**< Hash of permanent hidden-service key. */
smartlist_t *intro_nodes; /**< List of rend_intro_point_t's we have,
* or are trying to establish. */
/** List of rend_intro_point_t that are expiring. They are removed once
* the new descriptor is successfully uploaded. A node in this list CAN
* NOT appear in the intro_nodes list. */
smartlist_t *expiring_nodes;
time_t intro_period_started; /**< Start of the current period to build
* introduction points. */
int n_intro_circuits_launched; /**< Count of intro circuits we have
* established in this period. */
unsigned int n_intro_points_wanted; /**< Number of intro points this
* service wants to have open. */
rend_service_descriptor_t *desc; /**< Current hidden service descriptor. */
time_t desc_is_dirty; /**< Time at which changes to the hidden service
* descriptor content occurred, or 0 if it's
* up-to-date. */
time_t next_upload_time; /**< Scheduled next hidden service descriptor
* upload time. */
/** Replay cache for Diffie-Hellman values of INTRODUCE2 cells, to
* detect repeats. Clients may send INTRODUCE1 cells for the same
* rendezvous point through two or more different introduction points;
* when they do, this keeps us from launching multiple simultaneous attempts
* to connect to the same rend point. */
replaycache_t *accepted_intro_dh_parts;
/** If true, we don't close circuits for making requests to unsupported
* ports. */
int allow_unknown_ports;
/** If false, we send introduce data to the controller instead of
* automatically performing the rendezvous */
int automatic_rendezvous;
/** The maximum number of simultanious streams-per-circuit that are allowed
* to be established, or 0 if no limit is set.
*/
int max_streams_per_circuit;
/** If true, we close circuits that exceed the max_streams_per_circuit
* limit. */
int max_streams_close_circuit;
} rend_service_t;

MOCK_DECL(int, rend_service_perform_rendezvous,
(rend_intro_cell_t *parsed_req, struct rend_service_t *service));

int rend_service_handoff_introduce(rend_intro_cell_t *parsed_req,
struct rend_service_t *service);

#endif

int num_rend_services(void);
@@ -5,6 +5,7 @@
#include "crypto.h"
#include "or.h"
#include "test.h"
#include "control.h"

#define RENDSERVICE_PRIVATE
#include "rendservice.h"
@@ -248,13 +249,18 @@ static uint8_t v3_basic_auth_test_plaintext[] =
static void do_decrypt_test(uint8_t *plaintext, size_t plaintext_len);
static void do_early_parse_test(uint8_t *plaintext, size_t plaintext_len);
static void do_late_parse_test(uint8_t *plaintext, size_t plaintext_len);
static void do_handoff_test(uint8_t *plaintext, size_t plaintext_len);
static void do_parse_test(uint8_t *plaintext, size_t plaintext_len, int phase);
static ssize_t make_intro_from_plaintext(
void *buf, size_t len, crypto_pk_t *key, void **cell_out);

static int control_event_rend_handoff_mocked(const char *tag,
const char *data);

#define EARLY_PARSE_ONLY 1
#define DECRYPT_ONLY 2
#define ALL_PARSING 3
#define LATE_PARSE_ONLY 3
#define ALL_PARSING 4

static void
do_early_parse_test(uint8_t *plaintext, size_t plaintext_len)
@@ -270,10 +276,35 @@ do_decrypt_test(uint8_t *plaintext, size_t plaintext_len)

static void
do_late_parse_test(uint8_t *plaintext, size_t plaintext_len)
{
do_parse_test(plaintext, plaintext_len, LATE_PARSE_ONLY);
}

static void
do_handoff_test(uint8_t *plaintext, size_t plaintext_len)
{
do_parse_test(plaintext, plaintext_len, ALL_PARSING);
}

/** Test utility function: stores (copies of) the data we wanted to handoff */
char *from_mocked_handoff_tag;
char *from_mocked_handoff_data;

static int
control_event_rend_handoff_mocked(const char *tag,
const char *data)
{
if (from_mocked_handoff_tag != NULL)
tor_free(from_mocked_handoff_tag);
if (from_mocked_handoff_data != NULL)
tor_free(from_mocked_handoff_data);

from_mocked_handoff_tag = tor_strdup(tag);
from_mocked_handoff_data = tor_strdup(data);

return 0;
}

/** Test utility function: checks that the <b>plaintext_len</b>-byte string at
* <b>plaintext</b> is at least superficially parseable.
*/
@@ -285,6 +316,7 @@ do_parse_test(uint8_t *plaintext, size_t plaintext_len, int phase)
uint8_t *cell = NULL;
size_t cell_len;
rend_intro_cell_t *parsed_req = NULL;
rend_service_t service;
char *err_msg = NULL;
char digest[DIGEST_LEN];

@@ -333,11 +365,32 @@ do_parse_test(uint8_t *plaintext, size_t plaintext_len, int phase)
tt_assert(!err_msg);
tt_assert(parsed_req->parsed);

if (phase == LATE_PARSE_ONLY)
goto done;

/* Do handoff */
MOCK(control_event_rend_handoff, control_event_rend_handoff_mocked);

memset(&service, 0, sizeof(service));
service.tag = "test";
from_mocked_handoff_tag = NULL;
from_mocked_handoff_data = NULL;
r = rend_service_handoff_introduce(parsed_req, &service);
tt_assert(!r);
tt_assert(from_mocked_handoff_tag);
tt_assert(from_mocked_handoff_data);

done:
UNMOCK(control_event_rend_handoff);

tor_free(cell);
crypto_pk_free(k);
rend_service_free_intro(parsed_req);
tor_free(err_msg);
tor_free(from_mocked_handoff_tag);
tor_free(from_mocked_handoff_data);
from_mocked_handoff_tag = NULL;
from_mocked_handoff_data = NULL;
}

/** Given the plaintext of the encrypted part of an INTRODUCE1/2 and a key,
@@ -519,6 +572,49 @@ test_introduce_late_parse_v3(void *arg)
v3_basic_auth_test_plaintext, sizeof(v3_basic_auth_test_plaintext));
}

/** Test v0 INTRODUCE2 parsing with handoff
*/

static void
test_introduce_handoff_v0(void *arg)
{
(void)arg;
do_handoff_test(v0_test_plaintext, sizeof(v0_test_plaintext));
}

/** Test v1 INTRODUCE2 parsing with handoff
*/

static void
test_introduce_handoff_v1(void *arg)
{
(void)arg;
do_handoff_test(v1_test_plaintext, sizeof(v1_test_plaintext));
}

/** Test v2 INTRODUCE2 parsing with handoff
*/

static void
test_introduce_handoff_v2(void *arg)
{
(void)arg;
do_handoff_test(v2_test_plaintext, sizeof(v2_test_plaintext));
}

/** Test v3 INTRODUCE2 parsing with handoff
*/

static void
test_introduce_handoff_v3(void *arg)
{
(void)arg;
do_handoff_test(
v3_no_auth_test_plaintext, sizeof(v3_no_auth_test_plaintext));
do_handoff_test(
v3_basic_auth_test_plaintext, sizeof(v3_basic_auth_test_plaintext));
}

#define INTRODUCE_LEGACY(name) \
{ #name, test_introduce_ ## name , 0, NULL, NULL }

@@ -535,6 +631,10 @@ struct testcase_t introduce_tests[] = {
INTRODUCE_LEGACY(late_parse_v1),
INTRODUCE_LEGACY(late_parse_v2),
INTRODUCE_LEGACY(late_parse_v3),
INTRODUCE_LEGACY(handoff_v0),
INTRODUCE_LEGACY(handoff_v1),
INTRODUCE_LEGACY(handoff_v2),
INTRODUCE_LEGACY(handoff_v3),
END_OF_TESTCASES
};

0 comments on commit 3a8c941

Please sign in to comment.