Add DHT queries to private API

This commit adds functionality for clients to interact with
the DHT, sending getnodes requests to their peers and receiving
nodes in getnodes responses.
This commit is contained in:
jfreegman 2022-01-31 12:03:25 -05:00
parent e58ee06492
commit 652ae27eeb
No known key found for this signature in database
GPG Key ID: 3627F3144076AE63
6 changed files with 203 additions and 2 deletions

View File

@ -1 +1 @@
502cc22df74fa369b2c09d117176705a1e801726db6f8360c688aee90973fa22 /usr/local/bin/tox-bootstrapd
dd7740d26e60b20495e1c9145ee820d32cbb4996f0a6104f2e3e4705b16244f7 /usr/local/bin/tox-bootstrapd

View File

@ -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)
@ -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);
}
}
}
@ -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)

View File

@ -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`.
*
@ -254,6 +253,11 @@ 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. */
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.
*

View File

@ -21,6 +21,7 @@
#include "group.h"
#include "logger.h"
#include "mono_time.h"
#include "network.h"
#include "../toxencryptsave/defines.h"
@ -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,
@ -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];
@ -314,6 +320,21 @@ static void tox_conference_peer_list_changed_handler(Messenger *m, uint32_t conf
}
}
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)
@ -616,6 +637,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);
@ -2553,3 +2575,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 = 0;
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;
}

View File

@ -2,6 +2,7 @@
* Copyright © 2016-2021 The TokTok team.
*/
#include "tox.h"
#include "tox_private.h"
#include <stdlib.h>
#include <string.h>
@ -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) \

View File

@ -10,6 +10,8 @@
#include <stddef.h>
#include <stdint.h>
#include "DHT.h"
#ifdef __cplusplus
extern "C" {
#endif
@ -37,6 +39,99 @@ void tox_callback_friend_lossless_packet_per_pktid(Tox *tox, tox_friend_lossless
void tox_set_av_object(Tox *tox, void *object);
void *tox_get_av_object(const Tox *tox);
/*******************************************************************************
*
* :: DHT network queries.
*
******************************************************************************/
/**
* The minimum size of an IP string buffer in bytes.
*/
#define TOX_DHT_NODE_IP_STRING_SIZE 96
//!TOKSTYLE-
uint32_t tox_dht_node_ip_string_size(void);
//!TOKSTYLE+
/**
* The size of a DHT node public key in bytes.
*/
#define TOX_DHT_NODE_PUBLIC_KEY_SIZE 32
//!TOKSTYLE-
uint32_t tox_dht_node_public_key_size(void);
//!TOKSTYLE+
/**
* @param public_key The node's public key.
* @param ip The node's IP address, represented as a null terminated string.
* @param port The node's port.
*/
typedef void tox_dht_get_nodes_response_cb(Tox *tox, const uint8_t *public_key, const char *ip, uint16_t port,
void *user_data);
/**
* Set the callback for the `dht_get_nodes_response` event. Pass NULL to unset.
*
* This event is triggered when a getnodes response is received from a DHT peer.
*/
void tox_callback_dht_get_nodes_response(Tox *tox, tox_dht_get_nodes_response_cb *callback);
typedef enum Tox_Err_Dht_Get_Nodes {
/**
* The function returned successfully.
*/
TOX_ERR_DHT_GET_NODES_OK,
/**
* UDP is disabled in tox options; the DHT can only be queried when UDP is enabled.
*/
TOX_ERR_DHT_GET_NODES_UDP_DISABLED,
/**
* One of the arguments to the function was NULL when it was not expected.
*/
TOX_ERR_DHT_GET_NODES_NULL,
/**
* The supplied port is invalid.
*/
TOX_ERR_DHT_GET_NODES_BAD_PORT,
/**
* The supplied IP address is invalid.
*/
TOX_ERR_DHT_GET_NODES_BAD_IP,
/**
* The getnodes request failed. This usually means the packet failed to send.
*/
TOX_ERR_DHT_GET_NODES_FAIL,
} Tox_Err_Dht_Get_Nodes;
/**
* This function sends a getnodes request to a DHT node for its peers that
* are "close" to the passed target public key according to the distance metric used
* by the DHT implementation.
*
* @param public_key The public key of the node that we wish to query. This key must be
* at least `TOX_DHT_NODE_PUBLIC_KEY_SIZE` bytes in length.
* @param ip A NULL terminated string representing the IP address of the node we wish to query.
* @param port The port of the node we wish to query.
* @param target_public_key The public key for which we want to find close nodes.
*
* @return true on success.
*/
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);
#ifdef __cplusplus
}
#endif