tor/src/test/test_channeltls.c
2019-01-16 12:32:32 -05:00

340 lines
9.6 KiB
C

/* Copyright (c) 2014-2019, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#include "orconfig.h"
#include <math.h>
#define TOR_CHANNEL_INTERNAL_
#include "core/or/or.h"
#include "lib/net/address.h"
#include "lib/container/buffers.h"
#include "core/or/channel.h"
#include "core/or/channeltls.h"
#include "core/mainloop/connection.h"
#include "core/or/connection_or.h"
#include "app/config/config.h"
/* For init/free stuff */
#include "core/or/scheduler.h"
#include "lib/tls/tortls.h"
#include "core/or/or_connection_st.h"
/* Test suite stuff */
#include "test/test.h"
#include "test/fakechans.h"
/* The channeltls unit tests */
static void test_channeltls_create(void *arg);
static void test_channeltls_num_bytes_queued(void *arg);
static void test_channeltls_overhead_estimate(void *arg);
/* Mocks used by channeltls unit tests */
static size_t tlschan_buf_datalen_mock(const buf_t *buf);
static or_connection_t * tlschan_connection_or_connect_mock(
const tor_addr_t *addr,
uint16_t port,
const char *digest,
const ed25519_public_key_t *ed_id,
channel_tls_t *tlschan);
static int tlschan_is_local_addr_mock(const tor_addr_t *addr);
/* Fake close method */
static void tlschan_fake_close_method(channel_t *chan);
/* Flags controlling behavior of channeltls unit test mocks */
static int tlschan_local = 0;
static const buf_t * tlschan_buf_datalen_mock_target = NULL;
static size_t tlschan_buf_datalen_mock_size = 0;
/* Thing to cast to fake tor_tls_t * to appease assert_connection_ok() */
static int fake_tortls = 0; /* Bleh... */
static void
test_channeltls_create(void *arg)
{
tor_addr_t test_addr;
channel_t *ch = NULL;
const char test_digest[DIGEST_LEN] = {
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14 };
(void)arg;
/* Set up a fake address to fake-connect to */
test_addr.family = AF_INET;
test_addr.addr.in_addr.s_addr = htonl(0x01020304);
/* For this test we always want the address to be treated as non-local */
tlschan_local = 0;
/* Install is_local_addr() mock */
MOCK(is_local_addr, tlschan_is_local_addr_mock);
/* Install mock for connection_or_connect() */
MOCK(connection_or_connect, tlschan_connection_or_connect_mock);
/* Try connecting */
ch = channel_tls_connect(&test_addr, 567, test_digest, NULL);
tt_ptr_op(ch, OP_NE, NULL);
done:
if (ch) {
MOCK(scheduler_release_channel, scheduler_release_channel_mock);
/*
* Use fake close method that doesn't try to do too much to fake
* orconn
*/
ch->close = tlschan_fake_close_method;
channel_mark_for_close(ch);
free_fake_channel(ch);
UNMOCK(scheduler_release_channel);
}
UNMOCK(connection_or_connect);
UNMOCK(is_local_addr);
return;
}
static void
test_channeltls_num_bytes_queued(void *arg)
{
tor_addr_t test_addr;
channel_t *ch = NULL;
const char test_digest[DIGEST_LEN] = {
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14 };
channel_tls_t *tlschan = NULL;
size_t len;
int fake_outbuf = 0, n;
(void)arg;
/* Set up a fake address to fake-connect to */
test_addr.family = AF_INET;
test_addr.addr.in_addr.s_addr = htonl(0x01020304);
/* For this test we always want the address to be treated as non-local */
tlschan_local = 0;
/* Install is_local_addr() mock */
MOCK(is_local_addr, tlschan_is_local_addr_mock);
/* Install mock for connection_or_connect() */
MOCK(connection_or_connect, tlschan_connection_or_connect_mock);
/* Try connecting */
ch = channel_tls_connect(&test_addr, 567, test_digest, NULL);
tt_ptr_op(ch, OP_NE, NULL);
/*
* Next, we have to test ch->num_bytes_queued, which is
* channel_tls_num_bytes_queued_method. We can't mock
* connection_get_outbuf_len() directly because it's static inline
* in connection.h, but we can mock buf_datalen().
*/
tt_assert(ch->num_bytes_queued != NULL);
tlschan = BASE_CHAN_TO_TLS(ch);
tt_ptr_op(tlschan, OP_NE, NULL);
if (TO_CONN(tlschan->conn)->outbuf == NULL) {
/* We need an outbuf to make sure buf_datalen() gets called */
fake_outbuf = 1;
TO_CONN(tlschan->conn)->outbuf = buf_new();
}
tlschan_buf_datalen_mock_target = TO_CONN(tlschan->conn)->outbuf;
tlschan_buf_datalen_mock_size = 1024;
MOCK(buf_datalen, tlschan_buf_datalen_mock);
len = ch->num_bytes_queued(ch);
tt_int_op(len, OP_EQ, tlschan_buf_datalen_mock_size);
/*
* We also cover num_cells_writeable here; since wide_circ_ids = 0 on
* the fake tlschans, cell_network_size returns 512, and so with
* tlschan_buf_datalen_mock_size == 1024, we should be able to write
* ceil((OR_CONN_HIGHWATER - 1024) / 512) = ceil(OR_CONN_HIGHWATER / 512)
* - 2 cells.
*/
n = ch->num_cells_writeable(ch);
tt_int_op(n, OP_EQ, CEIL_DIV(OR_CONN_HIGHWATER, 512) - 2);
UNMOCK(buf_datalen);
tlschan_buf_datalen_mock_target = NULL;
tlschan_buf_datalen_mock_size = 0;
if (fake_outbuf) {
buf_free(TO_CONN(tlschan->conn)->outbuf);
TO_CONN(tlschan->conn)->outbuf = NULL;
}
done:
if (ch) {
MOCK(scheduler_release_channel, scheduler_release_channel_mock);
/*
* Use fake close method that doesn't try to do too much to fake
* orconn
*/
ch->close = tlschan_fake_close_method;
channel_mark_for_close(ch);
free_fake_channel(ch);
UNMOCK(scheduler_release_channel);
}
UNMOCK(connection_or_connect);
UNMOCK(is_local_addr);
return;
}
static void
test_channeltls_overhead_estimate(void *arg)
{
tor_addr_t test_addr;
channel_t *ch = NULL;
const char test_digest[DIGEST_LEN] = {
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14 };
double r;
channel_tls_t *tlschan = NULL;
(void)arg;
/* Set up a fake address to fake-connect to */
test_addr.family = AF_INET;
test_addr.addr.in_addr.s_addr = htonl(0x01020304);
/* For this test we always want the address to be treated as non-local */
tlschan_local = 0;
/* Install is_local_addr() mock */
MOCK(is_local_addr, tlschan_is_local_addr_mock);
/* Install mock for connection_or_connect() */
MOCK(connection_or_connect, tlschan_connection_or_connect_mock);
/* Try connecting */
ch = channel_tls_connect(&test_addr, 567, test_digest, NULL);
tt_ptr_op(ch, OP_NE, NULL);
/* First case: silly low ratios should get clamped to 1.0 */
tlschan = BASE_CHAN_TO_TLS(ch);
tt_ptr_op(tlschan, OP_NE, NULL);
tlschan->conn->bytes_xmitted = 128;
tlschan->conn->bytes_xmitted_by_tls = 64;
r = ch->get_overhead_estimate(ch);
tt_assert(fabs(r - 1.0) < 1E-12);
tlschan->conn->bytes_xmitted_by_tls = 127;
r = ch->get_overhead_estimate(ch);
tt_assert(fabs(r - 1.0) < 1E-12);
/* Now middle of the range */
tlschan->conn->bytes_xmitted_by_tls = 192;
r = ch->get_overhead_estimate(ch);
tt_assert(fabs(r - 1.5) < 1E-12);
/* Now above the 2.0 clamp */
tlschan->conn->bytes_xmitted_by_tls = 257;
r = ch->get_overhead_estimate(ch);
tt_assert(fabs(r - 2.0) < 1E-12);
tlschan->conn->bytes_xmitted_by_tls = 512;
r = ch->get_overhead_estimate(ch);
tt_assert(fabs(r - 2.0) < 1E-12);
done:
if (ch) {
MOCK(scheduler_release_channel, scheduler_release_channel_mock);
/*
* Use fake close method that doesn't try to do too much to fake
* orconn
*/
ch->close = tlschan_fake_close_method;
channel_mark_for_close(ch);
free_fake_channel(ch);
UNMOCK(scheduler_release_channel);
}
UNMOCK(connection_or_connect);
UNMOCK(is_local_addr);
return;
}
static size_t
tlschan_buf_datalen_mock(const buf_t *buf)
{
if (buf != NULL && buf == tlschan_buf_datalen_mock_target) {
return tlschan_buf_datalen_mock_size;
} else {
return buf_datalen__real(buf);
}
}
static or_connection_t *
tlschan_connection_or_connect_mock(const tor_addr_t *addr,
uint16_t port,
const char *digest,
const ed25519_public_key_t *ed_id,
channel_tls_t *tlschan)
{
or_connection_t *result = NULL;
(void) ed_id; // XXXX Not yet used.
tt_ptr_op(addr, OP_NE, NULL);
tt_uint_op(port, OP_NE, 0);
tt_ptr_op(digest, OP_NE, NULL);
tt_ptr_op(tlschan, OP_NE, NULL);
/* Make a fake orconn */
result = tor_malloc_zero(sizeof(*result));
result->base_.magic = OR_CONNECTION_MAGIC;
result->base_.state = OR_CONN_STATE_OPEN;
result->base_.type = CONN_TYPE_OR;
result->base_.socket_family = addr->family;
result->base_.address = tor_strdup("<fake>");
memcpy(&(result->base_.addr), addr, sizeof(tor_addr_t));
result->base_.port = port;
memcpy(result->identity_digest, digest, DIGEST_LEN);
result->chan = tlschan;
memcpy(&(result->real_addr), addr, sizeof(tor_addr_t));
result->tls = (tor_tls_t *)((void *)(&fake_tortls));
done:
return result;
}
static void
tlschan_fake_close_method(channel_t *chan)
{
channel_tls_t *tlschan = NULL;
tt_ptr_op(chan, OP_NE, NULL);
tt_int_op(chan->magic, OP_EQ, TLS_CHAN_MAGIC);
tlschan = BASE_CHAN_TO_TLS(chan);
tt_ptr_op(tlschan, OP_NE, NULL);
/* Just free the fake orconn */
tor_free(tlschan->conn->base_.address);
tor_free(tlschan->conn);
channel_closed(chan);
done:
return;
}
static int
tlschan_is_local_addr_mock(const tor_addr_t *addr)
{
tt_ptr_op(addr, OP_NE, NULL);
done:
return tlschan_local;
}
struct testcase_t channeltls_tests[] = {
{ "create", test_channeltls_create, TT_FORK, NULL, NULL },
{ "num_bytes_queued", test_channeltls_num_bytes_queued,
TT_FORK, NULL, NULL },
{ "overhead_estimate", test_channeltls_overhead_estimate,
TT_FORK, NULL, NULL },
END_OF_TESTCASES
};