Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add DHT queries to private API #1935

Merged
merged 2 commits into from
Feb 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion auto_tests/auto_test_support.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
152 changes: 152 additions & 0 deletions auto_tests/dht_getnodes_api_test.c
Original file line number Diff line number Diff line change
@@ -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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#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
2 changes: 1 addition & 1 deletion other/bootstrap_daemon/docker/tox-bootstrapd.sha256
Original file line number Diff line number Diff line change
@@ -1 +1 @@
502cc22df74fa369b2c09d117176705a1e801726db6f8360c688aee90973fa22 /usr/local/bin/tox-bootstrapd
7d31dd00dd4d8fefd21d375f2c9b69499f356867b364a386c562c15d6ac58d93 /usr/local/bin/tox-bootstrapd
4 changes: 3 additions & 1 deletion toxcore/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
11 changes: 11 additions & 0 deletions toxcore/DHT.c
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ struct DHT {

Node_format to_bootstrap[MAX_CLOSE_TO_BOOTSTRAP_NODES];
unsigned int num_to_bootstrap;

dht_get_nodes_response_cb *get_nodes_response;
};

const uint8_t *dht_friend_public_key(const DHT_Friend *dht_friend)
Expand Down Expand Up @@ -1552,6 +1554,10 @@ static int handle_sendnodes_ipv6(void *object, const IP_Port *source, const uint
if (ipport_isset(&plain_nodes[i].ip_port)) {
ping_node_from_getnodes_ok(dht, plain_nodes[i].public_key, &plain_nodes[i].ip_port);
returnedip_ports(dht, &plain_nodes[i].ip_port, plain_nodes[i].public_key, packet + 1);

if (dht->get_nodes_response) {
dht->get_nodes_response(dht, &plain_nodes[i], userdata);
}
}
}

Expand Down Expand Up @@ -2470,6 +2476,11 @@ static int cryptopacket_handle(void *object, const IP_Port *source, const uint8_
return 1;
}

void dht_callback_get_nodes_response(DHT *dht, dht_get_nodes_response_cb *function)
{
dht->get_nodes_response = function;
}

/*----------------------------------------------------------------------------------*/

DHT *new_dht(const Logger *log, Mono_Time *mono_time, Networking_Core *net, bool holepunching_enabled)
Expand Down
7 changes: 6 additions & 1 deletion toxcore/DHT.h
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,6 @@ void dht_get_shared_key_recv(DHT *dht, uint8_t *shared_key, const uint8_t *publi
non_null()
void dht_get_shared_key_sent(DHT *dht, uint8_t *shared_key, const uint8_t *public_key);


/** Sends a getnodes request to `ip_port` with the public key `public_key` for nodes
* that are close to `client_id`.
*
Expand All @@ -254,6 +253,12 @@ bool dht_getnodes(DHT *dht, const IP_Port *ip_port, const uint8_t *public_key, c

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.
* public_key must be CRYPTO_PUBLIC_KEY_SIZE bytes long.
*
Expand Down
89 changes: 89 additions & 0 deletions toxcore/tox.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "group.h"
#include "logger.h"
#include "mono_time.h"
#include "network.h"

#include "../toxencryptsave/defines.h"

Expand All @@ -35,6 +36,10 @@ static_assert(TOX_HASH_LENGTH == CRYPTO_SHA256_SIZE,
"TOX_HASH_LENGTH is assumed to be equal to CRYPTO_SHA256_SIZE");
static_assert(FILE_ID_LENGTH == CRYPTO_SYMMETRIC_KEY_SIZE,
"FILE_ID_LENGTH is assumed to be equal to CRYPTO_SYMMETRIC_KEY_SIZE");
static_assert(TOX_DHT_NODE_IP_STRING_SIZE == IP_NTOA_LEN,
"TOX_DHT_NODE_IP_STRING_SIZE is assumed to be equal to IP_NTOA_LEN");
static_assert(TOX_DHT_NODE_PUBLIC_KEY_SIZE == CRYPTO_PUBLIC_KEY_SIZE,
"TOX_DHT_NODE_PUBLIC_KEY_SIZE is assumed to be equal to CRYPTO_PUBLIC_KEY_SIZE");
static_assert(TOX_FILE_ID_LENGTH == CRYPTO_SYMMETRIC_KEY_SIZE,
"TOX_FILE_ID_LENGTH is assumed to be equal to CRYPTO_SYMMETRIC_KEY_SIZE");
static_assert(TOX_FILE_ID_LENGTH == TOX_HASH_LENGTH,
Expand Down Expand Up @@ -74,6 +79,7 @@ struct Tox {
tox_conference_title_cb *conference_title_callback;
tox_conference_peer_name_cb *conference_peer_name_callback;
tox_conference_peer_list_changed_cb *conference_peer_list_changed_callback;
tox_dht_get_nodes_response_cb *dht_get_nodes_response_callback;
tox_friend_lossy_packet_cb *friend_lossy_packet_callback_per_pktid[UINT8_MAX + 1];
tox_friend_lossless_packet_cb *friend_lossless_packet_callback_per_pktid[UINT8_MAX + 1];

Expand Down Expand Up @@ -314,6 +320,22 @@ 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;

if (tox_data->tox->dht_get_nodes_response_callback == nullptr) {
return;
}

char ip[IP_NTOA_LEN];
ip_ntoa(&node->ip_port.ip, ip, sizeof(ip));

tox_data->tox->dht_get_nodes_response_callback(tox_data->tox, node->public_key, ip, net_ntohs(node->ip_port.port),
tox_data->user_data);
}

non_null(1, 4) nullable(6)
static void tox_friend_lossy_packet_handler(Messenger *m, uint32_t friend_number, uint8_t packet_id,
const uint8_t *data, size_t length, void *user_data)
Expand Down Expand Up @@ -616,6 +638,7 @@ Tox *tox_new(const struct Tox_Options *options, Tox_Err_New *error)
callback_file_reqchunk(tox->m, tox_file_chunk_request_handler);
callback_file_sendrequest(tox->m, tox_file_recv_handler);
callback_file_data(tox->m, tox_file_recv_chunk_handler);
dht_callback_get_nodes_response(tox->m->dht, tox_dht_get_nodes_response_handler);
g_callback_group_invite(tox->m->conferences_object, tox_conference_invite_handler);
g_callback_group_connected(tox->m->conferences_object, tox_conference_connected_handler);
g_callback_group_message(tox->m->conferences_object, tox_conference_message_handler);
Expand Down Expand Up @@ -2553,3 +2576,69 @@ uint16_t tox_self_get_tcp_port(const Tox *tox, Tox_Err_Get_Port *error)
unlock(tox);
return 0;
}

void tox_callback_dht_get_nodes_response(Tox *tox, tox_dht_get_nodes_response_cb *callback)
{
assert(tox != nullptr);
tox->dht_get_nodes_response_callback = callback;
}

bool tox_dht_get_nodes(const Tox *tox, const uint8_t *public_key, const char *ip, uint16_t port,
const uint8_t *target_public_key, Tox_Err_Dht_Get_Nodes *error)
{
assert(tox != nullptr);

lock(tox);

if (tox->m->options.udp_disabled) {
SET_ERROR_PARAMETER(error, TOX_ERR_DHT_GET_NODES_UDP_DISABLED);
unlock(tox);
return false;
}

if (public_key == nullptr || ip == nullptr || target_public_key == nullptr) {
SET_ERROR_PARAMETER(error, TOX_ERR_DHT_GET_NODES_NULL);
unlock(tox);
return false;
}

if (port == 0) {
SET_ERROR_PARAMETER(error, TOX_ERR_DHT_GET_NODES_BAD_PORT);
unlock(tox);
return false;
}

IP_Port *root;

const int32_t count = net_getipport(ip, &root, TOX_SOCK_DGRAM);

if (count < 1) {
SET_ERROR_PARAMETER(error, TOX_ERR_DHT_GET_NODES_BAD_IP);
net_freeipport(root);
unlock(tox);
return false;
}

bool success = false;

for (int32_t i = 0; i < count; ++i) {
root[i].port = net_htons(port);

if (dht_getnodes(tox->m->dht, &root[i], public_key, target_public_key)) {
success = true;
}
}

unlock(tox);

net_freeipport(root);

if (!success) {
SET_ERROR_PARAMETER(error, TOX_ERR_DHT_GET_NODES_FAIL);
return false;
}

SET_ERROR_PARAMETER(error, TOX_ERR_DHT_GET_NODES_OK);

return true;
}
3 changes: 3 additions & 0 deletions toxcore/tox_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* Copyright © 2016-2021 The TokTok team.
*/
#include "tox.h"
#include "tox_private.h"

#include <stdlib.h>
#include <string.h>
Expand Down Expand Up @@ -41,6 +42,8 @@ CONST_FUNCTION(hash_length, HASH_LENGTH)
CONST_FUNCTION(file_id_length, FILE_ID_LENGTH)
CONST_FUNCTION(max_filename_length, MAX_FILENAME_LENGTH)
CONST_FUNCTION(max_hostname_length, MAX_HOSTNAME_LENGTH)
CONST_FUNCTION(dht_node_ip_string_size, DHT_NODE_IP_STRING_SIZE)
CONST_FUNCTION(dht_node_public_key_size, DHT_NODE_PUBLIC_KEY_SIZE)


#define ACCESSORS(type, ns, name) \
Expand Down
Loading