diff --git a/other/bootstrap_daemon/docker/tox-bootstrapd.sha256 b/other/bootstrap_daemon/docker/tox-bootstrapd.sha256 index 00089e35..c852264b 100644 --- a/other/bootstrap_daemon/docker/tox-bootstrapd.sha256 +++ b/other/bootstrap_daemon/docker/tox-bootstrapd.sha256 @@ -1 +1 @@ -502cc22df74fa369b2c09d117176705a1e801726db6f8360c688aee90973fa22 /usr/local/bin/tox-bootstrapd +dd7740d26e60b20495e1c9145ee820d32cbb4996f0a6104f2e3e4705b16244f7 /usr/local/bin/tox-bootstrapd diff --git a/toxcore/DHT.c b/toxcore/DHT.c index 1f4dba52..1d6a492d 100644 --- a/toxcore/DHT.c +++ b/toxcore/DHT.c @@ -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) diff --git a/toxcore/DHT.h b/toxcore/DHT.h index 09a6f4e4..3bcea771 100644 --- a/toxcore/DHT.h +++ b/toxcore/DHT.h @@ -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. * diff --git a/toxcore/tox.c b/toxcore/tox.c index e0157b8b..db3b9fd9 100644 --- a/toxcore/tox.c +++ b/toxcore/tox.c @@ -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; +} diff --git a/toxcore/tox_api.c b/toxcore/tox_api.c index 89524ae4..149bb59c 100644 --- a/toxcore/tox_api.c +++ b/toxcore/tox_api.c @@ -2,6 +2,7 @@ * Copyright © 2016-2021 The TokTok team. */ #include "tox.h" +#include "tox_private.h" #include #include @@ -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) \ diff --git a/toxcore/tox_private.h b/toxcore/tox_private.h index 593282f7..5e16d1a9 100644 --- a/toxcore/tox_private.h +++ b/toxcore/tox_private.h @@ -10,6 +10,8 @@ #include #include +#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