toxcore/auto_tests/onion_test.c
Gregory Mullen (grayhatter) aad1e0ad3f
Make friend requests stateless
Messenger is slightly twisty when it comes to sending connection status
callbacks It will very likely need at the very least a partial refactor to
clean it up a bit. Toxcore shouldn't need void *userdata as deep as is
currently does.

(amend 1) Because of the nature of toxcore connection callbacks, I decided to
change this commit from statelessness for connections changes to statelessness
for friend requests. It's simpler this was and doesn't include doing anything
foolish in the time between commits.

group fixup because grayhatter doesn't want to do it

"arguably correct" is not how you write security sensitive code

Clear a compiler warning about types within a function.
2016-09-06 02:22:04 -07:00

487 lines
14 KiB
C

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <check.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <time.h>
#include "../toxcore/onion.h"
#include "../toxcore/onion_announce.h"
#include "../toxcore/onion_client.h"
#include "../toxcore/util.h"
#include "helpers.h"
#if defined(_WIN32) || defined(__WIN32__) || defined (WIN32)
#define c_sleep(x) Sleep(1*x)
#else
#include <unistd.h>
#define c_sleep(x) usleep(1000*x)
#endif
void do_onion(Onion *onion)
{
networking_poll(onion->net, NULL);
do_DHT(onion->dht);
}
static int handled_test_1;
static int handle_test_1(void *object, IP_Port source, const uint8_t *packet, uint16_t length, void *userdata)
{
Onion *onion = object;
if (memcmp(packet, "Install Gentoo", sizeof("Install Gentoo")) != 0) {
return 1;
}
if (send_onion_response(onion->net, source, (uint8_t *)"install gentoo", sizeof("install gentoo"),
packet + sizeof("Install Gentoo")) == -1) {
return 1;
}
handled_test_1 = 1;
return 0;
}
static int handled_test_2;
static int handle_test_2(void *object, IP_Port source, const uint8_t *packet, uint16_t length, void *userdata)
{
if (length != sizeof("install Gentoo")) {
return 1;
}
if (memcmp(packet, (uint8_t *)"install gentoo", sizeof("install gentoo")) != 0) {
return 1;
}
handled_test_2 = 1;
return 0;
}
/*
void print_client_id(uint8_t *client_id, uint32_t length)
{
uint32_t j;
for (j = 0; j < length; j++) {
printf("%02hhX", client_id[j]);
}
printf("\n");
}
*/
uint8_t sb_data[ONION_ANNOUNCE_SENDBACK_DATA_LENGTH];
static int handled_test_3;
uint8_t test_3_pub_key[crypto_box_PUBLICKEYBYTES];
uint8_t test_3_ping_id[crypto_hash_sha256_BYTES];
static int handle_test_3(void *object, IP_Port source, const uint8_t *packet, uint16_t length, void *userdata)
{
Onion *onion = object;
if (length != (1 + crypto_box_NONCEBYTES + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH + 1 + crypto_hash_sha256_BYTES +
crypto_box_MACBYTES)) {
return 1;
}
uint8_t plain[1 + crypto_hash_sha256_BYTES];
//print_client_id(packet, length);
int len = decrypt_data(test_3_pub_key, onion->dht->self_secret_key, packet + 1 + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH,
packet + 1 + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH + crypto_box_NONCEBYTES,
1 + crypto_hash_sha256_BYTES + crypto_box_MACBYTES, plain);
if (len == -1) {
return 1;
}
if (memcmp(packet + 1, sb_data, ONION_ANNOUNCE_SENDBACK_DATA_LENGTH) != 0) {
return 1;
}
memcpy(test_3_ping_id, plain + 1, crypto_hash_sha256_BYTES);
//print_client_id(test_3_ping_id, sizeof(test_3_ping_id));
handled_test_3 = 1;
return 0;
}
uint8_t nonce[crypto_box_NONCEBYTES];
static int handled_test_4;
static int handle_test_4(void *object, IP_Port source, const uint8_t *packet, uint16_t length, void *userdata)
{
Onion *onion = object;
if (length != (1 + crypto_box_NONCEBYTES + crypto_box_PUBLICKEYBYTES + sizeof("Install gentoo") +
crypto_box_MACBYTES)) {
return 1;
}
uint8_t plain[sizeof("Install gentoo")] = {0};
if (memcmp(nonce, packet + 1, crypto_box_NONCEBYTES) != 0) {
return 1;
}
int len = decrypt_data(packet + 1 + crypto_box_NONCEBYTES, onion->dht->self_secret_key, packet + 1,
packet + 1 + crypto_box_NONCEBYTES + crypto_box_PUBLICKEYBYTES, sizeof("Install gentoo") + crypto_box_MACBYTES, plain);
if (len == -1) {
return 1;
}
if (memcmp(plain, "Install gentoo", sizeof("Install gentoo")) != 0) {
return 1;
}
handled_test_4 = 1;
return 0;
}
START_TEST(test_basic)
{
IP ip;
ip_init(&ip, 1);
ip.ip6.uint8[15] = 1;
Onion *onion1 = new_onion(new_DHT(NULL, new_networking(NULL, ip, 34567)));
Onion *onion2 = new_onion(new_DHT(NULL, new_networking(NULL, ip, 34568)));
ck_assert_msg((onion1 != NULL) && (onion2 != NULL), "Onion failed initializing.");
networking_registerhandler(onion2->net, 'I', &handle_test_1, onion2);
IP_Port on1 = {ip, onion1->net->port};
Node_format n1;
memcpy(n1.public_key, onion1->dht->self_public_key, crypto_box_PUBLICKEYBYTES);
n1.ip_port = on1;
IP_Port on2 = {ip, onion2->net->port};
Node_format n2;
memcpy(n2.public_key, onion2->dht->self_public_key, crypto_box_PUBLICKEYBYTES);
n2.ip_port = on2;
Node_format nodes[4];
nodes[0] = n1;
nodes[1] = n2;
nodes[2] = n1;
nodes[3] = n2;
Onion_Path path;
create_onion_path(onion1->dht, &path, nodes);
int ret = send_onion_packet(onion1->net, &path, nodes[3].ip_port, (uint8_t *)"Install Gentoo",
sizeof("Install Gentoo"));
ck_assert_msg(ret == 0, "Failed to create/send onion packet.");
handled_test_1 = 0;
while (handled_test_1 == 0) {
do_onion(onion1);
do_onion(onion2);
}
networking_registerhandler(onion1->net, 'i', &handle_test_2, onion1);
handled_test_2 = 0;
while (handled_test_2 == 0) {
do_onion(onion1);
do_onion(onion2);
}
Onion_Announce *onion1_a = new_onion_announce(onion1->dht);
Onion_Announce *onion2_a = new_onion_announce(onion2->dht);
networking_registerhandler(onion1->net, NET_PACKET_ANNOUNCE_RESPONSE, &handle_test_3, onion1);
ck_assert_msg((onion1_a != NULL) && (onion2_a != NULL), "Onion_Announce failed initializing.");
uint8_t zeroes[64] = {0};
randombytes(sb_data, sizeof(sb_data));
uint64_t s;
memcpy(&s, sb_data, sizeof(uint64_t));
memcpy(test_3_pub_key, nodes[3].public_key, crypto_box_PUBLICKEYBYTES);
ret = send_announce_request(onion1->net, &path, nodes[3], onion1->dht->self_public_key,
onion1->dht->self_secret_key,
zeroes, onion1->dht->self_public_key, onion1->dht->self_public_key, s);
ck_assert_msg(ret == 0, "Failed to create/send onion announce_request packet.");
handled_test_3 = 0;
while (handled_test_3 == 0) {
do_onion(onion1);
do_onion(onion2);
c_sleep(50);
}
randombytes(sb_data, sizeof(sb_data));
memcpy(&s, sb_data, sizeof(uint64_t));
memcpy(onion2_a->entries[1].public_key, onion2->dht->self_public_key, crypto_box_PUBLICKEYBYTES);
onion2_a->entries[1].time = unix_time();
networking_registerhandler(onion1->net, NET_PACKET_ONION_DATA_RESPONSE, &handle_test_4, onion1);
send_announce_request(onion1->net, &path, nodes[3], onion1->dht->self_public_key, onion1->dht->self_secret_key,
test_3_ping_id, onion1->dht->self_public_key, onion1->dht->self_public_key, s);
while (memcmp(onion2_a->entries[ONION_ANNOUNCE_MAX_ENTRIES - 2].public_key, onion1->dht->self_public_key,
crypto_box_PUBLICKEYBYTES) != 0) {
do_onion(onion1);
do_onion(onion2);
c_sleep(50);
}
c_sleep(1000);
Onion *onion3 = new_onion(new_DHT(NULL, new_networking(NULL, ip, 34569)));
ck_assert_msg((onion3 != NULL), "Onion failed initializing.");
new_nonce(nonce);
ret = send_data_request(onion3->net, &path, nodes[3].ip_port, onion1->dht->self_public_key,
onion1->dht->self_public_key,
nonce, (uint8_t *)"Install gentoo", sizeof("Install gentoo"));
ck_assert_msg(ret == 0, "Failed to create/send onion data_request packet.");
handled_test_4 = 0;
while (handled_test_4 == 0) {
do_onion(onion1);
do_onion(onion2);
c_sleep(50);
}
kill_onion_announce(onion1_a);
kill_onion_announce(onion2_a);
{
Onion *onion = onion1;
Networking_Core *net = onion->dht->net;
DHT *dht = onion->dht;
kill_onion(onion);
kill_DHT(dht);
kill_networking(net);
}
{
Onion *onion = onion2;
Networking_Core *net = onion->dht->net;
DHT *dht = onion->dht;
kill_onion(onion);
kill_DHT(dht);
kill_networking(net);
}
{
Onion *onion = onion3;
Networking_Core *net = onion->dht->net;
DHT *dht = onion->dht;
kill_onion(onion);
kill_DHT(dht);
kill_networking(net);
}
}
END_TEST
typedef struct {
Onion *onion;
Onion_Announce *onion_a;
Onion_Client *onion_c;
} Onions;
Onions *new_onions(uint16_t port)
{
IP ip;
ip_init(&ip, 1);
ip.ip6.uint8[15] = 1;
Onions *on = malloc(sizeof(Onions));
DHT *dht = new_DHT(NULL, new_networking(NULL, ip, port));
on->onion = new_onion(dht);
on->onion_a = new_onion_announce(dht);
TCP_Proxy_Info inf = {{{0}}};
on->onion_c = new_onion_client(new_net_crypto(NULL, dht, &inf));
if (on->onion && on->onion_a && on->onion_c) {
return on;
}
return NULL;
}
void do_onions(Onions *on)
{
networking_poll(on->onion->net, NULL);
do_DHT(on->onion->dht);
do_onion_client(on->onion_c);
}
void kill_onions(Onions *on)
{
Networking_Core *net = on->onion->dht->net;
DHT *dht = on->onion->dht;
Net_Crypto *c = on->onion_c->c;
kill_onion_client(on->onion_c);
kill_onion_announce(on->onion_a);
kill_onion(on->onion);
kill_net_crypto(c);
kill_DHT(dht);
kill_networking(net);
free(on);
}
#define NUM_ONIONS 50
#define NUM_FIRST 7
#define NUM_LAST 37
_Bool first_ip, last_ip;
void dht_ip_callback(void *object, int32_t number, IP_Port ip_port)
{
if (NUM_FIRST == number) {
first_ip = 1;
return;
}
if (NUM_LAST == number) {
last_ip = 1;
return;
}
ck_abort_msg("Error.");
}
_Bool first, last;
uint8_t first_dht_pk[crypto_box_PUBLICKEYBYTES];
uint8_t last_dht_pk[crypto_box_PUBLICKEYBYTES];
static void dht_pk_callback(void *object, int32_t number, const uint8_t *dht_public_key, void *userdata)
{
if ((NUM_FIRST == number && !first) || (NUM_LAST == number && !last)) {
Onions *on = object;
uint16_t count = 0;
int ret = DHT_addfriend(on->onion->dht, dht_public_key, &dht_ip_callback, object, number, &count);
ck_assert_msg(ret == 0, "DHT_addfriend() did not return 0");
ck_assert_msg(count == 1, "Count not 1, count is %u", count);
if (NUM_FIRST == number && !first) {
first = 1;
if (memcmp(dht_public_key, last_dht_pk, crypto_box_PUBLICKEYBYTES) != 0) {
ck_abort_msg("Error wrong dht key.");
}
return;
}
if (NUM_LAST == number && !last) {
last = 1;
if (memcmp(dht_public_key, first_dht_pk, crypto_box_PUBLICKEYBYTES) != 0) {
ck_abort_msg("Error wrong dht key.");
}
return;
}
ck_abort_msg("Error.");
}
}
START_TEST(test_announce)
{
uint32_t i, j;
Onions *onions[NUM_ONIONS];
for (i = 0; i < NUM_ONIONS; ++i) {
onions[i] = new_onions(i + 34655);
ck_assert_msg(onions[i] != 0, "Failed to create onions. %u");
}
IP ip;
ip_init(&ip, 1);
ip.ip6.uint8[15] = 1;
for (i = 3; i < NUM_ONIONS; ++i) {
IP_Port ip_port = {ip, onions[i - 1]->onion->net->port};
DHT_bootstrap(onions[i]->onion->dht, ip_port, onions[i - 1]->onion->dht->self_public_key);
IP_Port ip_port1 = {ip, onions[i - 2]->onion->net->port};
DHT_bootstrap(onions[i]->onion->dht, ip_port1, onions[i - 2]->onion->dht->self_public_key);
IP_Port ip_port2 = {ip, onions[i - 3]->onion->net->port};
DHT_bootstrap(onions[i]->onion->dht, ip_port2, onions[i - 3]->onion->dht->self_public_key);
}
uint32_t connected = 0;
while (connected != NUM_ONIONS) {
connected = 0;
for (i = 0; i < NUM_ONIONS; ++i) {
do_onions(onions[i]);
connected += DHT_isconnected(onions[i]->onion->dht);
}
c_sleep(50);
}
printf("connected\n");
for (i = 0; i < 25 * 2; ++i) {
for (j = 0; j < NUM_ONIONS; ++j) {
do_onions(onions[j]);
}
c_sleep(50);
}
memcpy(first_dht_pk, onions[NUM_FIRST]->onion->dht->self_public_key, crypto_box_PUBLICKEYBYTES);
memcpy(last_dht_pk, onions[NUM_LAST]->onion->dht->self_public_key, crypto_box_PUBLICKEYBYTES);
printf("adding friend\n");
int frnum_f = onion_addfriend(onions[NUM_FIRST]->onion_c, onions[NUM_LAST]->onion_c->c->self_public_key);
int frnum = onion_addfriend(onions[NUM_LAST]->onion_c, onions[NUM_FIRST]->onion_c->c->self_public_key);
onion_dht_pk_callback(onions[NUM_FIRST]->onion_c, frnum_f, &dht_pk_callback, onions[NUM_FIRST], NUM_FIRST);
onion_dht_pk_callback(onions[NUM_LAST]->onion_c, frnum, &dht_pk_callback, onions[NUM_LAST], NUM_LAST);
IP_Port ip_port;
while (!first || !last) {
for (i = 0; i < NUM_ONIONS; ++i) {
do_onions(onions[i]);
}
c_sleep(50);
}
printf("Waiting for ips\n");
while (!first_ip || !last_ip) {
for (i = 0; i < NUM_ONIONS; ++i) {
do_onions(onions[i]);
}
c_sleep(50);
}
onion_getfriendip(onions[NUM_LAST]->onion_c, frnum, &ip_port);
ck_assert_msg(ip_port.port == onions[NUM_FIRST]->onion->net->port, "Port in returned ip not correct.");
for (i = 0; i < NUM_ONIONS; ++i) {
kill_onions(onions[i]);
}
}
END_TEST
Suite *onion_suite(void)
{
Suite *s = suite_create("Onion");
DEFTESTCASE_SLOW(basic, 5);
DEFTESTCASE_SLOW(announce, 70);
return s;
}
int main(int argc, char *argv[])
{
srand((unsigned int) time(NULL));
Suite *onion = onion_suite();
SRunner *test_runner = srunner_create(onion);
int number_failed = 0;
srunner_run_all(test_runner, CK_NORMAL);
number_failed = srunner_ntests_failed(test_runner);
srunner_free(test_runner);
return number_failed;
}