diff options
Diffstat (limited to 'libs/libtox/src/toxcore/DHT.h')
-rw-r--r-- | libs/libtox/src/toxcore/DHT.h | 448 |
1 files changed, 448 insertions, 0 deletions
diff --git a/libs/libtox/src/toxcore/DHT.h b/libs/libtox/src/toxcore/DHT.h new file mode 100644 index 0000000000..510b3c5c92 --- /dev/null +++ b/libs/libtox/src/toxcore/DHT.h @@ -0,0 +1,448 @@ +/* + * An implementation of the DHT as seen in docs/updates/DHT.md + */ + +/* + * Copyright © 2016-2017 The TokTok team. + * Copyright © 2013 Tox project. + * + * This file is part of Tox, the free peer to peer instant messenger. + * + * Tox is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tox is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tox. If not, see <http://www.gnu.org/licenses/>. + */ +#ifndef DHT_H +#define DHT_H + +#include "crypto_core.h" +#include "logger.h" +#include "network.h" +#include "ping_array.h" + +#include <stdbool.h> + +/* Maximum number of clients stored per friend. */ +#define MAX_FRIEND_CLIENTS 8 + +#define LCLIENT_NODES (MAX_FRIEND_CLIENTS) +#define LCLIENT_LENGTH 128 + +/* A list of the clients mathematically closest to ours. */ +#define LCLIENT_LIST (LCLIENT_LENGTH * LCLIENT_NODES) + +#define MAX_CLOSE_TO_BOOTSTRAP_NODES 8 + +/* The max number of nodes to send with send nodes. */ +#define MAX_SENT_NODES 4 + +/* Ping timeout in seconds */ +#define PING_TIMEOUT 5 + +/* size of DHT ping arrays. */ +#define DHT_PING_ARRAY_SIZE 512 + +/* Ping interval in seconds for each node in our lists. */ +#define PING_INTERVAL 60 + +/* The number of seconds for a non responsive node to become bad. */ +#define PINGS_MISSED_NODE_GOES_BAD 1 +#define PING_ROUNDTRIP 2 +#define BAD_NODE_TIMEOUT (PING_INTERVAL + PINGS_MISSED_NODE_GOES_BAD * (PING_INTERVAL + PING_ROUNDTRIP)) + +/* The number of "fake" friends to add (for optimization purposes and so our paths for the onion part are more random) */ +#define DHT_FAKE_FRIEND_NUMBER 2 + +#define MAX_CRYPTO_REQUEST_SIZE 1024 + +#define CRYPTO_PACKET_FRIEND_REQ 32 /* Friend request crypto packet ID. */ +#define CRYPTO_PACKET_HARDENING 48 /* Hardening crypto packet ID. */ +#define CRYPTO_PACKET_DHTPK 156 +#define CRYPTO_PACKET_NAT_PING 254 /* NAT ping crypto packet ID. */ + +/* Create a request to peer. + * send_public_key and send_secret_key are the pub/secret keys of the sender. + * recv_public_key is public key of receiver. + * packet must be an array of MAX_CRYPTO_REQUEST_SIZE big. + * Data represents the data we send with the request with length being the length of the data. + * request_id is the id of the request (32 = friend request, 254 = ping request). + * + * return -1 on failure. + * return the length of the created packet on success. + */ +int create_request(const uint8_t *send_public_key, const uint8_t *send_secret_key, uint8_t *packet, + const uint8_t *recv_public_key, const uint8_t *data, uint32_t length, uint8_t request_id); + +/* puts the senders public key in the request in public_key, the data from the request + in data if a friend or ping request was sent to us and returns the length of the data. + packet is the request packet and length is its length + return -1 if not valid request. */ +int handle_request(const uint8_t *self_public_key, const uint8_t *self_secret_key, uint8_t *public_key, uint8_t *data, + uint8_t *request_id, const uint8_t *packet, uint16_t length); + +typedef struct { + IP_Port ip_port; + uint64_t timestamp; +} IPPTs; + +typedef struct { + /* Node routes request correctly (true (1) or false/didn't check (0)) */ + uint8_t routes_requests_ok; + /* Time which we last checked this.*/ + uint64_t routes_requests_timestamp; + uint8_t routes_requests_pingedid[CRYPTO_PUBLIC_KEY_SIZE]; + /* Node sends correct send_node (true (1) or false/didn't check (0)) */ + uint8_t send_nodes_ok; + /* Time which we last checked this.*/ + uint64_t send_nodes_timestamp; + uint8_t send_nodes_pingedid[CRYPTO_PUBLIC_KEY_SIZE]; + /* Node can be used to test other nodes (true (1) or false/didn't check (0)) */ + uint8_t testing_requests; + /* Time which we last checked this.*/ + uint64_t testing_timestamp; + uint8_t testing_pingedid[CRYPTO_PUBLIC_KEY_SIZE]; +} Hardening; + +typedef struct { + IP_Port ip_port; + uint64_t timestamp; + uint64_t last_pinged; + + Hardening hardening; + /* Returned by this node. Either our friend or us. */ + IP_Port ret_ip_port; + uint64_t ret_timestamp; +} IPPTsPng; + +typedef struct { + uint8_t public_key[CRYPTO_PUBLIC_KEY_SIZE]; + IPPTsPng assoc4; + IPPTsPng assoc6; +} Client_data; + +/*----------------------------------------------------------------------------------*/ + +typedef struct { + /* 1 if currently hole punching, otherwise 0 */ + uint8_t hole_punching; + uint32_t punching_index; + uint32_t tries; + uint32_t punching_index2; + + uint64_t punching_timestamp; + uint64_t recvNATping_timestamp; + uint64_t NATping_id; + uint64_t NATping_timestamp; +} NAT; + +#define DHT_FRIEND_MAX_LOCKS 32 + +typedef struct { + uint8_t public_key[CRYPTO_PUBLIC_KEY_SIZE]; + IP_Port ip_port; +} +Node_format; + +typedef struct { + uint8_t public_key[CRYPTO_PUBLIC_KEY_SIZE]; + Client_data client_list[MAX_FRIEND_CLIENTS]; + + /* Time at which the last get_nodes request was sent. */ + uint64_t lastgetnode; + /* number of times get_node packets were sent. */ + uint32_t bootstrap_times; + + /* Symetric NAT hole punching stuff. */ + NAT nat; + + uint16_t lock_count; + struct { + void (*ip_callback)(void *, int32_t, IP_Port); + void *data; + int32_t number; + } callbacks[DHT_FRIEND_MAX_LOCKS]; + + Node_format to_bootstrap[MAX_SENT_NODES]; + unsigned int num_to_bootstrap; +} DHT_Friend; + +/* Return packet size of packed node with ip_family on success. + * Return -1 on failure. + */ +int packed_node_size(uint8_t ip_family); + +/* Pack number of nodes into data of maxlength length. + * + * return length of packed nodes on success. + * return -1 on failure. + */ +int pack_nodes(uint8_t *data, uint16_t length, const Node_format *nodes, uint16_t number); + +/* Unpack data of length into nodes of size max_num_nodes. + * Put the length of the data processed in processed_data_len. + * tcp_enabled sets if TCP nodes are expected (true) or not (false). + * + * return number of unpacked nodes on success. + * return -1 on failure. + */ +int unpack_nodes(Node_format *nodes, uint16_t max_num_nodes, uint16_t *processed_data_len, const uint8_t *data, + uint16_t length, uint8_t tcp_enabled); + + +/*----------------------------------------------------------------------------------*/ +/* struct to store some shared keys so we don't have to regenerate them for each request. */ +#define MAX_KEYS_PER_SLOT 4 +#define KEYS_TIMEOUT 600 + +typedef struct { + uint8_t public_key[CRYPTO_PUBLIC_KEY_SIZE]; + uint8_t shared_key[CRYPTO_SHARED_KEY_SIZE]; + uint32_t times_requested; + uint8_t stored; /* 0 if not, 1 if is */ + uint64_t time_last_requested; +} Shared_Key; + +typedef struct { + Shared_Key keys[256 * MAX_KEYS_PER_SLOT]; +} Shared_Keys; + +/*----------------------------------------------------------------------------------*/ + +typedef int (*cryptopacket_handler_callback)(void *object, IP_Port ip_port, const uint8_t *source_pubkey, + const uint8_t *data, uint16_t len, void *userdata); + +typedef struct { + cryptopacket_handler_callback function; + void *object; +} Cryptopacket_Handles; + +typedef struct { + Logger *log; + Networking_Core *net; + + bool hole_punching_enabled; + + Client_data close_clientlist[LCLIENT_LIST]; + uint64_t close_lastgetnodes; + uint32_t close_bootstrap_times; + + /* Note: this key should not be/is not used to transmit any sensitive materials */ + uint8_t secret_symmetric_key[CRYPTO_SYMMETRIC_KEY_SIZE]; + /* DHT keypair */ + uint8_t self_public_key[CRYPTO_PUBLIC_KEY_SIZE]; + uint8_t self_secret_key[CRYPTO_SECRET_KEY_SIZE]; + + DHT_Friend *friends_list; + uint16_t num_friends; + + Node_format *loaded_nodes_list; + uint32_t loaded_num_nodes; + unsigned int loaded_nodes_index; + + Shared_Keys shared_keys_recv; + Shared_Keys shared_keys_sent; + + struct PING *ping; + Ping_Array dht_ping_array; + Ping_Array dht_harden_ping_array; + uint64_t last_run; + + Cryptopacket_Handles cryptopackethandlers[256]; + + Node_format to_bootstrap[MAX_CLOSE_TO_BOOTSTRAP_NODES]; + unsigned int num_to_bootstrap; +} DHT; +/*----------------------------------------------------------------------------------*/ + +/* Shared key generations are costly, it is therefor smart to store commonly used + * ones so that they can re used later without being computed again. + * + * If shared key is already in shared_keys, copy it to shared_key. + * else generate it into shared_key and copy it to shared_keys + */ +void get_shared_key(Shared_Keys *shared_keys, uint8_t *shared_key, const uint8_t *secret_key, + const uint8_t *public_key); + +/* Copy shared_key to encrypt/decrypt DHT packet from public_key into shared_key + * for packets that we receive. + */ +void DHT_get_shared_key_recv(DHT *dht, uint8_t *shared_key, const uint8_t *public_key); + +/* Copy shared_key to encrypt/decrypt DHT packet from public_key into shared_key + * for packets that we send. + */ +void DHT_get_shared_key_sent(DHT *dht, uint8_t *shared_key, const uint8_t *public_key); + +void DHT_getnodes(DHT *dht, const IP_Port *from_ipp, const uint8_t *from_id, const uint8_t *which_id); + +/* Add a new friend to the friends list. + * public_key must be CRYPTO_PUBLIC_KEY_SIZE bytes long. + * + * ip_callback is the callback of a function that will be called when the ip address + * is found along with arguments data and number. + * + * lock_count will be set to a non zero number that must be passed to DHT_delfriend() + * to properly remove the callback. + * + * return 0 if success. + * return -1 if failure (friends list is full). + */ +int DHT_addfriend(DHT *dht, const uint8_t *public_key, void (*ip_callback)(void *data, int32_t number, IP_Port), + void *data, int32_t number, uint16_t *lock_count); + +/* Delete a friend from the friends list. + * public_key must be CRYPTO_PUBLIC_KEY_SIZE bytes long. + * + * return 0 if success. + * return -1 if failure (public_key not in friends list). + */ +int DHT_delfriend(DHT *dht, const uint8_t *public_key, uint16_t lock_count); + +/* Get ip of friend. + * public_key must be CRYPTO_PUBLIC_KEY_SIZE bytes long. + * ip must be 4 bytes long. + * port must be 2 bytes long. + * + * int DHT_getfriendip(DHT *dht, uint8_t *public_key, IP_Port *ip_port); + * + * return -1, -- if public_key does NOT refer to a friend + * return 0, -- if public_key refers to a friend and we failed to find the friend (yet) + * return 1, ip if public_key refers to a friend and we found him + */ +int DHT_getfriendip(const DHT *dht, const uint8_t *public_key, IP_Port *ip_port); + +/* Compares pk1 and pk2 with pk. + * + * return 0 if both are same distance. + * return 1 if pk1 is closer. + * return 2 if pk2 is closer. + */ +int id_closest(const uint8_t *pk, const uint8_t *pk1, const uint8_t *pk2); + +/* Add node to the node list making sure only the nodes closest to cmp_pk are in the list. + */ +bool add_to_list(Node_format *nodes_list, unsigned int length, const uint8_t *pk, IP_Port ip_port, + const uint8_t *cmp_pk); + +/* Return 1 if node can be added to close list, 0 if it can't. + */ +bool node_addable_to_close_list(DHT *dht, const uint8_t *public_key, IP_Port ip_port); + +/* Get the (maximum MAX_SENT_NODES) closest nodes to public_key we know + * and put them in nodes_list (must be MAX_SENT_NODES big). + * + * sa_family = family (IPv4 or IPv6) (0 if we don't care)? + * is_LAN = return some LAN ips (true or false) + * want_good = do we want tested nodes or not? (TODO(irungentoo)) + * + * return the number of nodes returned. + * + */ +int get_close_nodes(const DHT *dht, const uint8_t *public_key, Node_format *nodes_list, Family sa_family, + uint8_t is_LAN, uint8_t want_good); + + +/* Put up to max_num nodes in nodes from the random friends. + * + * return the number of nodes. + */ +uint16_t randfriends_nodes(DHT *dht, Node_format *nodes, uint16_t max_num); + +/* Put up to max_num nodes in nodes from the closelist. + * + * return the number of nodes. + */ +uint16_t closelist_nodes(DHT *dht, Node_format *nodes, uint16_t max_num); + +/* Run this function at least a couple times per second (It's the main loop). */ +void do_DHT(DHT *dht); + +/* + * Use these two functions to bootstrap the client. + */ +/* Sends a "get nodes" request to the given node with ip, port and public_key + * to setup connections + */ +void DHT_bootstrap(DHT *dht, IP_Port ip_port, const uint8_t *public_key); +/* Resolves address into an IP address. If successful, sends a "get nodes" + * request to the given node with ip, port and public_key to setup connections + * + * address can be a hostname or an IP address (IPv4 or IPv6). + * if ipv6enabled is 0 (zero), the resolving sticks STRICTLY to IPv4 addresses + * if ipv6enabled is not 0 (zero), the resolving looks for IPv6 addresses first, + * then IPv4 addresses. + * + * returns 1 if the address could be converted into an IP address + * returns 0 otherwise + */ +int DHT_bootstrap_from_address(DHT *dht, const char *address, uint8_t ipv6enabled, + uint16_t port, const uint8_t *public_key); + +/* Start sending packets after DHT loaded_friends_list and loaded_clients_list are set. + * + * returns 0 if successful + * returns -1 otherwise + */ +int DHT_connect_after_load(DHT *dht); + +/* ROUTING FUNCTIONS */ + +/* Send the given packet to node with public_key. + * + * return -1 if failure. + */ +int route_packet(const DHT *dht, const uint8_t *public_key, const uint8_t *packet, uint16_t length); + +/* Send the following packet to everyone who tells us they are connected to friend_id. + * + * return number of nodes it sent the packet to. + */ +int route_tofriend(const DHT *dht, const uint8_t *friend_id, const uint8_t *packet, uint16_t length); + +/* Function to handle crypto packets. + */ +void cryptopacket_registerhandler(DHT *dht, uint8_t byte, cryptopacket_handler_callback cb, void *object); + +/* SAVE/LOAD functions */ + +/* Get the size of the DHT (for saving). */ +uint32_t DHT_size(const DHT *dht); + +/* Save the DHT in data where data is an array of size DHT_size(). */ +void DHT_save(DHT *dht, uint8_t *data); + +/* Load the DHT from data of size size. + * + * return -1 if failure. + * return 0 if success. + */ +int DHT_load(DHT *dht, const uint8_t *data, uint32_t length); + +/* Initialize DHT. */ +DHT *new_DHT(Logger *log, Networking_Core *net, bool holepunching_enabled); + +void kill_DHT(DHT *dht); + +/* return 0 if we are not connected to the DHT. + * return 1 if we are. + */ +int DHT_isconnected(const DHT *dht); + +/* return 0 if we are not connected or only connected to lan peers with the DHT. + * return 1 if we are. + */ +int DHT_non_lan_connected(const DHT *dht); + + +uint32_t addto_lists(DHT *dht, IP_Port ip_port, const uint8_t *public_key); + +#endif |