diff --git a/CMakeLists.txt b/CMakeLists.txt index 024c5723..7937fdfb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -470,6 +470,7 @@ auto_test(conference_simple) auto_test(conference_two) auto_test(crypto) #auto_test(dht) # Doesn't work with UNITY_BUILD. +auto_test(dht_getnodes_api) auto_test(encryptsave) auto_test(file_transfer) auto_test(file_saving) diff --git a/auto_tests/auto_test_support.h b/auto_tests/auto_test_support.h index b4f3a208..16faa40f 100644 --- a/auto_tests/auto_test_support.h +++ b/auto_tests/auto_test_support.h @@ -35,7 +35,7 @@ void set_mono_time_callback(AutoTox *tox); typedef enum Graph_Type { GRAPH_COMPLETE = 0, - GRAPH_LINEAR + GRAPH_LINEAR, } Graph_Type; typedef struct Run_Auto_Options { diff --git a/auto_tests/dht_getnodes_api_test.c b/auto_tests/dht_getnodes_api_test.c new file mode 100644 index 00000000..ae519cf9 --- /dev/null +++ b/auto_tests/dht_getnodes_api_test.c @@ -0,0 +1,152 @@ +/** + * This autotest creates a small local DHT and makes sure that each peer can crawl + * the entire DHT using the DHT getnodes api functions. + */ + +#include +#include +#include +#include + +#include "../toxcore/tox.h" +#include "../toxcore/tox_private.h" +#include "auto_test_support.h" +#include "check_compat.h" + +#define NUM_TOXES 30 + +typedef struct Dht_Node { + uint8_t public_key[TOX_DHT_NODE_PUBLIC_KEY_SIZE]; + char ip[TOX_DHT_NODE_IP_STRING_SIZE]; + uint16_t port; +} Dht_Node; + +typedef struct State { + Dht_Node **nodes; + size_t num_nodes; + uint8_t **public_key_list; +} State; + +static void free_nodes(Dht_Node **nodes, size_t num_nodes) +{ + for (size_t i = 0; i < num_nodes; ++i) { + free(nodes[i]); + } + + free(nodes); +} + +static bool node_crawled(Dht_Node **nodes, size_t num_nodes, const uint8_t *public_key) +{ + for (size_t i = 0; i < num_nodes; ++i) { + if (memcmp(nodes[i]->public_key, public_key, TOX_DHT_NODE_PUBLIC_KEY_SIZE) == 0) { + return true; + } + } + + return false; +} + +static bool all_nodes_crawled(AutoTox *autotoxes, uint32_t num_toxes, uint8_t **public_key_list) +{ + for (uint32_t i = 0; i < num_toxes; ++i) { + const State *state = (const State *)autotoxes[i].state; + + // make sure each peer has crawled the correct number of nodes + if (state->num_nodes < num_toxes) { + return false; + } + } + + for (uint32_t i = 0; i < num_toxes; ++i) { + const State *state = (const State *)autotoxes[i].state; + + // make sure each peer has the full list of public keys + for (uint32_t j = 0; j < num_toxes; ++j) { + if (!node_crawled(state->nodes, state->num_nodes, public_key_list[j])) { + return false; + } + } + } + + return true; +} + +static void getnodes_response_cb(Tox *tox, const uint8_t *public_key, const char *ip, uint16_t port, void *user_data) +{ + ck_assert(user_data != nullptr); + + AutoTox *autotoxes = (AutoTox *)user_data; + State *state = (State *)autotoxes->state; + + if (node_crawled(state->nodes, state->num_nodes, public_key)) { + return; + } + + ck_assert(state->num_nodes < NUM_TOXES); + + Dht_Node *node = (Dht_Node *)calloc(1, sizeof(Dht_Node)); + ck_assert(node != nullptr); + + memcpy(node->public_key, public_key, TOX_DHT_NODE_PUBLIC_KEY_SIZE); + snprintf(node->ip, sizeof(node->ip), "%s", ip); + node->port = port; + + state->nodes[state->num_nodes] = node; + ++state->num_nodes; + + // ask new node to give us their close nodes to every public key + for (size_t i = 0; i < NUM_TOXES; ++i) { + tox_dht_get_nodes(tox, public_key, ip, port, state->public_key_list[i], nullptr); + } +} + +static void test_dht_getnodes(AutoTox *autotoxes) +{ + ck_assert(NUM_TOXES >= 2); + + uint8_t **public_key_list = (uint8_t **)calloc(NUM_TOXES, sizeof(uint8_t *)); + ck_assert(public_key_list != nullptr); + + for (size_t i = 0; i < NUM_TOXES; ++i) { + State *state = (State *)autotoxes[i].state; + + state->nodes = (Dht_Node **)calloc(NUM_TOXES, sizeof(Dht_Node *)); + ck_assert(state->nodes != nullptr); + + state->num_nodes = 0; + state->public_key_list = public_key_list; + + public_key_list[i] = (uint8_t *)malloc(sizeof(uint8_t) * TOX_PUBLIC_KEY_SIZE); + ck_assert(public_key_list[i] != nullptr); + + tox_self_get_dht_id(autotoxes[i].tox, public_key_list[i]); + tox_callback_dht_get_nodes_response(autotoxes[i].tox, getnodes_response_cb); + } + + while (!all_nodes_crawled(autotoxes, NUM_TOXES, public_key_list)) { + iterate_all_wait(autotoxes, NUM_TOXES, ITERATION_INTERVAL); + } + + for (size_t i = 0; i < NUM_TOXES; ++i) { + State *state = (State *)autotoxes[i].state; + free_nodes(state->nodes, state->num_nodes); + free(public_key_list[i]); + } + + free(public_key_list); +} + +int main(void) +{ + setvbuf(stdout, nullptr, _IONBF, 0); + + Run_Auto_Options options = default_run_auto_options; + options.graph = GRAPH_LINEAR; + + run_auto_test(nullptr, NUM_TOXES, test_dht_getnodes, sizeof(State), &options); + + return 0; +} + +#undef NUM_TOXES diff --git a/other/bootstrap_daemon/docker/tox-bootstrapd.sha256 b/other/bootstrap_daemon/docker/tox-bootstrapd.sha256 index c852264b..a3a61101 100644 --- a/other/bootstrap_daemon/docker/tox-bootstrapd.sha256 +++ b/other/bootstrap_daemon/docker/tox-bootstrapd.sha256 @@ -1 +1 @@ -dd7740d26e60b20495e1c9145ee820d32cbb4996f0a6104f2e3e4705b16244f7 /usr/local/bin/tox-bootstrapd +7d31dd00dd4d8fefd21d375f2c9b69499f356867b364a386c562c15d6ac58d93 /usr/local/bin/tox-bootstrapd diff --git a/toxcore/BUILD.bazel b/toxcore/BUILD.bazel index 34e016ad..ecef1c65 100644 --- a/toxcore/BUILD.bazel +++ b/toxcore/BUILD.bazel @@ -452,9 +452,11 @@ cc_library( srcs = [ "tox.c", "tox_api.c", + ], + hdrs = [ + "tox.h", "tox_private.h", ], - hdrs = ["tox.h"], visibility = ["//c-toxcore:__subpackages__"], deps = [ ":Messenger", diff --git a/toxcore/DHT.h b/toxcore/DHT.h index 3bcea771..102441dc 100644 --- a/toxcore/DHT.h +++ b/toxcore/DHT.h @@ -256,6 +256,7 @@ typedef void dht_ip_cb(void *object, int32_t number, const IP_Port *ip_port); typedef void dht_get_nodes_response_cb(const DHT *dht, const Node_format *node, void *userdata); /** Sets the callback to be triggered on a getnodes response. */ +non_null(1) nullable(2) void dht_callback_get_nodes_response(DHT *dht, dht_get_nodes_response_cb *function); /** Add a new friend to the friends list. diff --git a/toxcore/tox.c b/toxcore/tox.c index db3b9fd9..961fe57b 100644 --- a/toxcore/tox.c +++ b/toxcore/tox.c @@ -320,6 +320,7 @@ static void tox_conference_peer_list_changed_handler(Messenger *m, uint32_t conf } } +non_null(1, 2) nullable(3) static void tox_dht_get_nodes_response_handler(const DHT *dht, const Node_format *node, void *user_data) { struct Tox_Userdata *tox_data = (struct Tox_Userdata *)user_data; @@ -2618,7 +2619,7 @@ bool tox_dht_get_nodes(const Tox *tox, const uint8_t *public_key, const char *ip return false; } - bool success = 0; + bool success = false; for (int32_t i = 0; i < count; ++i) { root[i].port = net_htons(port);