diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/core/or/sendme.c | 12 | ||||
-rw-r--r-- | src/core/or/sendme.h | 23 | ||||
-rw-r--r-- | src/test/include.am | 1 | ||||
-rw-r--r-- | src/test/test.c | 1 | ||||
-rw-r--r-- | src/test/test.h | 1 | ||||
-rw-r--r-- | src/test/test_relaycell.c | 1 | ||||
-rw-r--r-- | src/test/test_sendme.c | 223 |
7 files changed, 257 insertions, 5 deletions
diff --git a/src/core/or/sendme.c b/src/core/or/sendme.c index 76f551a929..980684c827 100644 --- a/src/core/or/sendme.c +++ b/src/core/or/sendme.c @@ -7,6 +7,8 @@ * creating/parsing cells and handling the content. */ +#define SENDME_PRIVATE + #include "core/or/or.h" #include "app/config/config.h" @@ -37,7 +39,7 @@ /* Return the minimum version given by the consensus (if any) that should be * used when emitting a SENDME cell. */ -static int +STATIC int get_emit_min_version(void) { return networkstatus_get_param(NULL, "sendme_emit_min_version", @@ -48,7 +50,7 @@ get_emit_min_version(void) /* Return the minimum version given by the consensus (if any) that should be * accepted when receiving a SENDME cell. */ -static int +STATIC int get_accept_min_version(void) { return networkstatus_get_param(NULL, "sendme_accept_min_version", @@ -112,7 +114,7 @@ cell_v1_is_valid(const sendme_cell_t *cell, const circuit_t *circ) /* Return true iff the given cell version can be handled or if the minimum * accepted version from the consensus is known to us. */ -static bool +STATIC bool cell_version_is_valid(uint8_t cell_version) { int accept_version = get_accept_min_version(); @@ -149,7 +151,7 @@ cell_version_is_valid(uint8_t cell_version) * This is the main critical function to make sure we can continue to * send/recv cells on a circuit. If the SENDME is invalid, the circuit should * be mark for close. */ -static bool +STATIC bool sendme_is_valid(const circuit_t *circ, const uint8_t *cell_payload, size_t cell_payload_len) { @@ -206,7 +208,7 @@ sendme_is_valid(const circuit_t *circ, const uint8_t *cell_payload, * * Return the size in bytes of the encoded cell in payload. A negative value * is returned on encoding failure. */ -static ssize_t +STATIC ssize_t build_cell_payload_v1(crypto_digest_t *cell_digest, uint8_t *payload) { ssize_t len = -1; diff --git a/src/core/or/sendme.h b/src/core/or/sendme.h index e7cf718bb1..c2e2518da8 100644 --- a/src/core/or/sendme.h +++ b/src/core/or/sendme.h @@ -36,4 +36,27 @@ int sendme_stream_data_packaged(edge_connection_t *conn); /* Track cell digest. */ void sendme_note_cell_digest(circuit_t *circ); +/* Private section starts. */ +#ifdef SENDME_PRIVATE + +/* + * Unit tests declaractions. + */ +#ifdef TOR_UNIT_TESTS + +STATIC int get_emit_min_version(void); +STATIC int get_accept_min_version(void); + +STATIC bool cell_version_is_valid(uint8_t cell_version); + +STATIC ssize_t build_cell_payload_v1(crypto_digest_t *cell_digest, + uint8_t *payload); +STATIC bool sendme_is_valid(const circuit_t *circ, + const uint8_t *cell_payload, + size_t cell_payload_len); + +#endif /* TOR_UNIT_TESTS */ + +#endif /* SENDME_PRIVATE */ + #endif /* !defined(TOR_SENDME_H) */ diff --git a/src/test/include.am b/src/test/include.am index 497aa320a4..5f6b05fa95 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -179,6 +179,7 @@ src_test_test_SOURCES += \ src/test/test_routerlist.c \ src/test/test_routerset.c \ src/test/test_scheduler.c \ + src/test/test_sendme.c \ src/test/test_shared_random.c \ src/test/test_socks.c \ src/test/test_status.c \ diff --git a/src/test/test.c b/src/test/test.c index fbc30fb64e..17159b71c5 100644 --- a/src/test/test.c +++ b/src/test/test.c @@ -923,6 +923,7 @@ struct testgroup_t testgroups[] = { { "routerlist/", routerlist_tests }, { "routerset/" , routerset_tests }, { "scheduler/", scheduler_tests }, + { "sendme/", sendme_tests }, { "shared-random/", sr_tests }, { "socks/", socks_tests }, { "status/" , status_tests }, diff --git a/src/test/test.h b/src/test/test.h index 7d19af9b20..167fd090ac 100644 --- a/src/test/test.h +++ b/src/test/test.h @@ -266,6 +266,7 @@ extern struct testcase_t routerkeys_tests[]; extern struct testcase_t routerlist_tests[]; extern struct testcase_t routerset_tests[]; extern struct testcase_t scheduler_tests[]; +extern struct testcase_t sendme_tests[]; extern struct testcase_t socks_tests[]; extern struct testcase_t sr_tests[]; extern struct testcase_t status_tests[]; diff --git a/src/test/test_relaycell.c b/src/test/test_relaycell.c index 0623583511..c4ed215c7c 100644 --- a/src/test/test_relaycell.c +++ b/src/test/test_relaycell.c @@ -705,6 +705,7 @@ test_circbw_relay(void *arg) circ = helper_create_origin_circuit(CIRCUIT_PURPOSE_C_GENERAL, 0); circ->cpath->state = CPATH_STATE_AWAITING_KEYS; circ->cpath->deliver_window = CIRCWINDOW_START; + circ->cpath->crypto.sendme_digest = crypto_digest_new(); entryconn1 = fake_entry_conn(circ, 1); edgeconn = ENTRY_TO_EDGE_CONN(entryconn1); diff --git a/src/test/test_sendme.c b/src/test/test_sendme.c new file mode 100644 index 0000000000..ad6aac6c0c --- /dev/null +++ b/src/test/test_sendme.c @@ -0,0 +1,223 @@ +/* Copyright (c) 2014-2019, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/* Unit tests for handling different kinds of relay cell */ + +#define CIRCUITLIST_PRIVATE +#define NETWORKSTATUS_PRIVATE +#define SENDME_PRIVATE + +#include "core/or/circuit_st.h" +#include "core/or/or_circuit_st.h" +#include "core/or/origin_circuit_st.h" +#include "core/or/circuitlist.h" +#include "core/or/sendme.h" + +#include "feature/nodelist/networkstatus.h" +#include "feature/nodelist/networkstatus_st.h" + +#include "test/test.h" +#include "test/log_test_helpers.h" + +static void +setup_mock_consensus(void) +{ + current_md_consensus = current_ns_consensus = + tor_malloc_zero(sizeof(networkstatus_t)); + current_md_consensus->net_params = smartlist_new(); + current_md_consensus->routerstatus_list = smartlist_new(); +} + +static void +free_mock_consensus(void) +{ + SMARTLIST_FOREACH(current_md_consensus->routerstatus_list, void *, r, + tor_free(r)); + smartlist_free(current_md_consensus->routerstatus_list); + smartlist_free(current_ns_consensus->net_params); + tor_free(current_ns_consensus); +} + +static void +test_v1_note_digest(void *arg) +{ + or_circuit_t *or_circ = NULL; + origin_circuit_t *orig_circ = NULL; + circuit_t *circ = NULL; + + (void) arg; + + /* Create our dummy circuits. */ + orig_circ = origin_circuit_new(); + tt_assert(orig_circ); + or_circ = or_circuit_new(1, NULL); + + /* Start by pointing to the origin circuit. */ + circ = TO_CIRCUIT(orig_circ); + circ->purpose = CIRCUIT_PURPOSE_S_REND_JOINED; + + /* We should never note SENDME digest on origin circuit. */ + sendme_note_cell_digest(circ); + tt_assert(!circ->sendme_last_digests); + /* We do not need the origin circuit for now. */ + orig_circ = NULL; + circuit_free_(circ); + /* Points it to the OR circuit now. */ + circ = TO_CIRCUIT(or_circ); + or_circ->crypto.sendme_digest = crypto_digest_new(); + + /* The package window has to be a multiple of CIRCWINDOW_INCREMENT minus 1 + * in order to catched the CIRCWINDOW_INCREMENT-nth cell. Try something that + * shouldn't be noted. */ + circ->package_window = CIRCWINDOW_INCREMENT; + sendme_note_cell_digest(circ); + tt_assert(!circ->sendme_last_digests); + + /* This should work now. Package window at CIRCWINDOW_INCREMENT + 1. */ + circ->package_window++; + sendme_note_cell_digest(circ); + tt_assert(circ->sendme_last_digests); + tt_int_op(smartlist_len(circ->sendme_last_digests), OP_EQ, 1); + + /* Next cell in the package window shouldn't do anything. */ + circ->package_window++; + sendme_note_cell_digest(circ); + tt_int_op(smartlist_len(circ->sendme_last_digests), OP_EQ, 1); + + /* The next CIRCWINDOW_INCREMENT should add one more digest. */ + circ->package_window = (CIRCWINDOW_INCREMENT * 2) + 1; + sendme_note_cell_digest(circ); + tt_int_op(smartlist_len(circ->sendme_last_digests), OP_EQ, 2); + + done: + circuit_free_(circ); +} + +static void +test_v1_consensus_params(void *arg) +{ + (void) arg; + + setup_mock_consensus(); + tt_assert(current_md_consensus); + + /* Both zeroes. */ + smartlist_add(current_md_consensus->net_params, + (void *) "sendme_emit_min_version=0"); + smartlist_add(current_md_consensus->net_params, + (void *) "sendme_accept_min_version=0"); + tt_int_op(get_emit_min_version(), OP_EQ, 0); + tt_int_op(get_accept_min_version(), OP_EQ, 0); + smartlist_clear(current_md_consensus->net_params); + + /* Both ones. */ + smartlist_add(current_md_consensus->net_params, + (void *) "sendme_emit_min_version=1"); + smartlist_add(current_md_consensus->net_params, + (void *) "sendme_accept_min_version=1"); + tt_int_op(get_emit_min_version(), OP_EQ, 1); + tt_int_op(get_accept_min_version(), OP_EQ, 1); + smartlist_clear(current_md_consensus->net_params); + + /* Different values from each other. */ + smartlist_add(current_md_consensus->net_params, + (void *) "sendme_emit_min_version=1"); + smartlist_add(current_md_consensus->net_params, + (void *) "sendme_accept_min_version=0"); + tt_int_op(get_emit_min_version(), OP_EQ, 1); + tt_int_op(get_accept_min_version(), OP_EQ, 0); + smartlist_clear(current_md_consensus->net_params); + + /* Validate is the cell version is coherent with our internal default value + * and the one in the consensus. */ + smartlist_add(current_md_consensus->net_params, + (void *) "sendme_accept_min_version=1"); + /* Minimum acceptable value is 1. */ + tt_int_op(cell_version_is_valid(1), OP_EQ, true); + /* Minimum acceptable value is 1 so a cell version of 0 is refused. */ + tt_int_op(cell_version_is_valid(0), OP_EQ, false); + + done: + free_mock_consensus(); +} + +static void +test_v1_build_cell(void *arg) +{ + uint8_t payload[RELAY_PAYLOAD_SIZE]; + ssize_t ret; + crypto_digest_t *cell_digest = NULL; + or_circuit_t *or_circ = NULL; + circuit_t *circ = NULL; + + (void) arg; + + or_circ = or_circuit_new(1, NULL); + circ = TO_CIRCUIT(or_circ); + + cell_digest = crypto_digest_new(); + crypto_digest_add_bytes(cell_digest, "AAAA", 4); + tt_assert(cell_digest); + + /* SENDME v1 payload is 7 bytes. See spec. */ + ret = build_cell_payload_v1(cell_digest, payload); + tt_int_op(ret, OP_EQ, 7); + + /* Validation. */ + + /* An empty payload means SENDME version 0 thus valid. */ + tt_int_op(sendme_is_valid(circ, payload, 0), OP_EQ, true); + + /* An unparseable cell means invalid. */ + setup_full_capture_of_logs(LOG_INFO); + tt_int_op(sendme_is_valid(circ, (const uint8_t *) "A", 1), OP_EQ, false); + expect_log_msg_containing("Unparseable SENDME cell received. " + "Closing circuit."); + teardown_capture_of_logs(); + + /* No cell digest recorded for this. */ + setup_full_capture_of_logs(LOG_INFO); + tt_int_op(sendme_is_valid(circ, payload, sizeof(payload)), OP_EQ, false); + expect_log_msg_containing("We received a SENDME but we have no cell digests " + "to match. Closing circuit."); + teardown_capture_of_logs(); + + /* Note the wrong digest in the circuit, cell should fail validation. */ + or_circ->crypto.sendme_digest = crypto_digest_new(); + circ->package_window = CIRCWINDOW_INCREMENT + 1; + sendme_note_cell_digest(circ); + tt_int_op(smartlist_len(circ->sendme_last_digests), OP_EQ, 1); + setup_full_capture_of_logs(LOG_INFO); + tt_int_op(sendme_is_valid(circ, payload, sizeof(payload)), OP_EQ, false); + /* After a validation, the last digests is always popped out. */ + tt_int_op(smartlist_len(circ->sendme_last_digests), OP_EQ, 0); + expect_log_msg_containing("SENDME v1 cell digest do not match."); + teardown_capture_of_logs(); + + /* Cleanup */ + crypto_digest_free(or_circ->crypto.sendme_digest); + + /* Record the cell digest into the circuit, cell should validate. */ + or_circ->crypto.sendme_digest = crypto_digest_dup(cell_digest); + circ->package_window = CIRCWINDOW_INCREMENT + 1; + sendme_note_cell_digest(circ); + tt_int_op(smartlist_len(circ->sendme_last_digests), OP_EQ, 1); + tt_int_op(sendme_is_valid(circ, payload, sizeof(payload)), OP_EQ, true); + /* After a validation, the last digests is always popped out. */ + tt_int_op(smartlist_len(circ->sendme_last_digests), OP_EQ, 0); + + done: + crypto_digest_free(cell_digest); + circuit_free_(circ); +} + +struct testcase_t sendme_tests[] = { + { "v1_note_digest", test_v1_note_digest, TT_FORK, + NULL, NULL }, + { "v1_consensus_params", test_v1_consensus_params, TT_FORK, + NULL, NULL }, + { "v1_build_cell", test_v1_build_cell, TT_FORK, + NULL, NULL }, + + END_OF_TESTCASES +}; |