diff options
Diffstat (limited to 'protocols/Tox/libtox/src/toxcore/DHT.c')
-rw-r--r-- | protocols/Tox/libtox/src/toxcore/DHT.c | 308 |
1 files changed, 97 insertions, 211 deletions
diff --git a/protocols/Tox/libtox/src/toxcore/DHT.c b/protocols/Tox/libtox/src/toxcore/DHT.c index 8aa161102b..91f0e0ae47 100644 --- a/protocols/Tox/libtox/src/toxcore/DHT.c +++ b/protocols/Tox/libtox/src/toxcore/DHT.c @@ -18,6 +18,7 @@ #include "mono_time.h" #include "network.h" #include "ping.h" +#include "shared_key_cache.h" #include "state.h" #include "util.h" @@ -43,6 +44,13 @@ /** Number of get node requests to send to quickly find close nodes. */ #define MAX_BOOTSTRAP_TIMES 5 +// TODO(sudden6): find out why we need multiple callbacks and if we really need 32 +#define DHT_FRIEND_MAX_LOCKS 32 + +/* Settings for the shared key cache */ +#define MAX_KEYS_PER_SLOT 4 +#define KEYS_TIMEOUT 600 + typedef struct DHT_Friend_Callback { dht_ip_cb *ip_callback; void *data; @@ -61,7 +69,8 @@ struct DHT_Friend { /* Symmetric NAT hole punching stuff. */ NAT nat; - uint16_t lock_count; + /* Each set bit represents one installed callback */ + uint32_t lock_flags; DHT_Friend_Callback callbacks[DHT_FRIEND_MAX_LOCKS]; Node_format to_bootstrap[MAX_SENT_NODES]; @@ -71,6 +80,8 @@ struct DHT_Friend { static const DHT_Friend empty_dht_friend = {{0}}; const Node_format empty_node_format = {{0}}; +static_assert(sizeof (empty_dht_friend.lock_flags) * 8 == DHT_FRIEND_MAX_LOCKS, "Bitfield size and number of locks don't match"); + typedef struct Cryptopacket_Handler { cryptopacket_handler_cb *function; void *object; @@ -101,8 +112,8 @@ struct DHT { uint32_t loaded_num_nodes; unsigned int loaded_nodes_index; - Shared_Keys shared_keys_recv; - Shared_Keys shared_keys_sent; + Shared_Key_Cache *shared_keys_recv; + Shared_Key_Cache *shared_keys_sent; struct Ping *ping; Ping_Array *dht_ping_array; @@ -200,12 +211,6 @@ static IP_Port ip_port_normalize(const IP_Port *ip_port) return res; } -/** @brief Compares pk1 and pk2 with pk. - * - * @retval 0 if both are same distance. - * @retval 1 if pk1 is closer. - * @retval 2 if pk2 is closer. - */ int id_closest(const uint8_t *pk, const uint8_t *pk1, const uint8_t *pk2) { for (size_t i = 0; i < CRYPTO_PUBLIC_KEY_SIZE; ++i) { @@ -250,98 +255,25 @@ unsigned int bit_by_bit_cmp(const uint8_t *pk1, const uint8_t *pk2) } /** - * Shared key generations are costly, it is therefore smart to store commonly used - * ones so that they can be re-used later without being computed again. - * - * If a shared key is already in shared_keys, copy it to shared_key. - * Otherwise generate it into shared_key and copy it to shared_keys - */ -void get_shared_key(const Mono_Time *mono_time, Shared_Keys *shared_keys, uint8_t *shared_key, - const uint8_t *secret_key, const uint8_t *public_key) -{ - uint32_t num = -1; - uint32_t curr = 0; - - for (uint32_t i = 0; i < MAX_KEYS_PER_SLOT; ++i) { - const int index = public_key[30] * MAX_KEYS_PER_SLOT + i; - Shared_Key *const key = &shared_keys->keys[index]; - - if (key->stored) { - if (pk_equal(public_key, key->public_key)) { - memcpy(shared_key, key->shared_key, CRYPTO_SHARED_KEY_SIZE); - ++key->times_requested; - key->time_last_requested = mono_time_get(mono_time); - return; - } - - if (num != 0) { - if (mono_time_is_timeout(mono_time, key->time_last_requested, KEYS_TIMEOUT)) { - num = 0; - curr = index; - } else if (num > key->times_requested) { - num = key->times_requested; - curr = index; - } - } - } else if (num != 0) { - num = 0; - curr = index; - } - } - - encrypt_precompute(public_key, secret_key, shared_key); - - if (num != UINT32_MAX) { - Shared_Key *const key = &shared_keys->keys[curr]; - key->stored = true; - key->times_requested = 1; - memcpy(key->public_key, public_key, CRYPTO_PUBLIC_KEY_SIZE); - memcpy(key->shared_key, shared_key, CRYPTO_SHARED_KEY_SIZE); - key->time_last_requested = mono_time_get(mono_time); - } -} - -/** * 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) +const uint8_t *dht_get_shared_key_recv(DHT *dht, const uint8_t *public_key) { - get_shared_key(dht->mono_time, &dht->shared_keys_recv, shared_key, dht->self_secret_key, public_key); + return shared_key_cache_lookup(dht->shared_keys_recv, 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) +const uint8_t *dht_get_shared_key_sent(DHT *dht, const uint8_t *public_key) { - get_shared_key(dht->mono_time, &dht->shared_keys_sent, shared_key, dht->self_secret_key, public_key); + return shared_key_cache_lookup(dht->shared_keys_sent, public_key); } #define CRYPTO_SIZE (1 + CRYPTO_PUBLIC_KEY_SIZE * 2 + CRYPTO_NONCE_SIZE) -/** - * @brief Create a request to peer. - * - * Packs the data and sender public key and encrypts the packet. - * - * @param[in] send_public_key public key of the sender. - * @param[in] send_secret_key secret key of the sender. - * @param[out] packet an array of @ref MAX_CRYPTO_REQUEST_SIZE big. - * @param[in] recv_public_key public key of the receiver. - * @param[in] data represents the data we send with the request. - * @param[in] data_length the length of the data. - * @param[in] request_id the id of the request (32 = friend request, 254 = ping request). - * - * @attention Constraints: - * @code - * sizeof(packet) >= MAX_CRYPTO_REQUEST_SIZE - * @endcode - * - * @retval -1 on failure. - * @return the length of the created packet on success. - */ int create_request(const Random *rng, 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 data_length, uint8_t request_id) @@ -375,28 +307,6 @@ int create_request(const Random *rng, const uint8_t *send_public_key, const uint return len + CRYPTO_SIZE; } -/** - * @brief Decrypts and unpacks a DHT request packet. - * - * Puts the senders public key in the request in @p public_key, the data from - * the request in @p data. - * - * @param[in] self_public_key public key of the receiver (us). - * @param[in] self_secret_key secret key of the receiver (us). - * @param[out] public_key public key of the sender, copied from the input packet. - * @param[out] data decrypted request data, copied from the input packet, must - * have room for @ref MAX_CRYPTO_REQUEST_SIZE bytes. - * @param[in] packet is the request packet. - * @param[in] packet_length length of the packet. - * - * @attention Constraints: - * @code - * sizeof(data) >= MAX_CRYPTO_REQUEST_SIZE - * @endcode - * - * @retval -1 if not valid request. - * @return the length of the unpacked data. - */ 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 packet_length) { @@ -435,9 +345,6 @@ int handle_request(const uint8_t *self_public_key, const uint8_t *self_secret_ke return len1; } -/** @return packet size of packed node with ip_family on success. - * @retval -1 on failure. - */ int packed_node_size(Family ip_family) { if (net_family_is_ipv4(ip_family) || net_family_is_tcp_ipv4(ip_family)) { @@ -452,13 +359,6 @@ int packed_node_size(Family ip_family) } -/** @brief Pack an IP_Port structure into data of max size length. - * - * Packed_length is the offset of data currently packed. - * - * @return size of packed IP_Port data on success. - * @retval -1 on failure. - */ int pack_ip_port(const Logger *logger, uint8_t *data, uint16_t length, const IP_Port *ip_port) { if (data == nullptr) { @@ -514,11 +414,6 @@ int pack_ip_port(const Logger *logger, uint8_t *data, uint16_t length, const IP_ } } -/** @brief Encrypt plain and write resulting DHT packet into packet with max size length. - * - * @return size of packet on success. - * @retval -1 on failure. - */ int dht_create_packet(const Random *rng, const uint8_t public_key[CRYPTO_PUBLIC_KEY_SIZE], const uint8_t *shared_key, const uint8_t type, const uint8_t *plain, size_t plain_length, @@ -554,13 +449,6 @@ int dht_create_packet(const Random *rng, const uint8_t public_key[CRYPTO_PUBLIC_ return 1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE + encrypted_length; } -/** @brief Unpack IP_Port structure from data of max size length into ip_port. - * - * len_processed is the offset of data currently unpacked. - * - * @return size of unpacked ip_port on success. - * @retval -1 on failure. - */ int unpack_ip_port(IP_Port *ip_port, const uint8_t *data, uint16_t length, bool tcp_enabled) { if (data == nullptr) { @@ -621,11 +509,6 @@ int unpack_ip_port(IP_Port *ip_port, const uint8_t *data, uint16_t length, bool } } -/** @brief Pack number of nodes into data of maxlength length. - * - * @return length of packed nodes on success. - * @retval -1 on failure. - */ int pack_nodes(const Logger *logger, uint8_t *data, uint16_t length, const Node_format *nodes, uint16_t number) { uint32_t packed_length = 0; @@ -655,13 +538,6 @@ int pack_nodes(const Logger *logger, uint8_t *data, uint16_t length, const Node_ return packed_length; } -/** @brief 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. - * @retval -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, bool tcp_enabled) { @@ -1057,8 +933,7 @@ static bool send_announce_ping(DHT *dht, const uint8_t *public_key, const IP_Por public_key, CRYPTO_PUBLIC_KEY_SIZE); memcpy(plain + CRYPTO_PUBLIC_KEY_SIZE, &ping_id, sizeof(ping_id)); - uint8_t shared_key[CRYPTO_SHARED_KEY_SIZE]; - dht_get_shared_key_sent(dht, shared_key, public_key); + const uint8_t *shared_key = dht_get_shared_key_sent(dht, public_key); uint8_t request[1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE + sizeof(plain) + CRYPTO_MAC_SIZE]; @@ -1086,8 +961,7 @@ static int handle_data_search_response(void *object, const IP_Port *source, VLA(uint8_t, plain, plain_len); const uint8_t *public_key = packet + 1; - uint8_t shared_key[CRYPTO_SHARED_KEY_SIZE]; - dht_get_shared_key_recv(dht, shared_key, public_key); + const uint8_t *shared_key = dht_get_shared_key_recv(dht, public_key); if (decrypt_data_symmetric(shared_key, packet + 1 + CRYPTO_PUBLIC_KEY_SIZE, @@ -1419,8 +1293,9 @@ uint32_t addto_lists(DHT *dht, const IP_Port *ip_port, const uint8_t *public_key return used; } - for (uint32_t i = 0; i < friend_foundip->lock_count; ++i) { - if (friend_foundip->callbacks[i].ip_callback != nullptr) { + for (uint32_t i = 0; i < DHT_FRIEND_MAX_LOCKS; ++i) { + const bool has_lock = (friend_foundip->lock_flags & (UINT32_C(1) << i)) > 0; + if (has_lock && friend_foundip->callbacks[i].ip_callback != nullptr) { friend_foundip->callbacks[i].ip_callback(friend_foundip->callbacks[i].data, friend_foundip->callbacks[i].number, &ipp_copy); } @@ -1515,15 +1390,12 @@ bool dht_getnodes(DHT *dht, const IP_Port *ip_port, const uint8_t *public_key, c memcpy(plain, client_id, CRYPTO_PUBLIC_KEY_SIZE); memcpy(plain + CRYPTO_PUBLIC_KEY_SIZE, &ping_id, sizeof(ping_id)); - uint8_t shared_key[CRYPTO_SHARED_KEY_SIZE]; - dht_get_shared_key_sent(dht, shared_key, public_key); + const uint8_t *shared_key = dht_get_shared_key_sent(dht, public_key); const int len = dht_create_packet(dht->rng, dht->self_public_key, shared_key, NET_PACKET_GET_NODES, plain, sizeof(plain), data, sizeof(data)); - crypto_memzero(shared_key, sizeof(shared_key)); - if (len != sizeof(data)) { LOGGER_ERROR(dht->log, "getnodes packet encryption failed"); return false; @@ -1598,9 +1470,7 @@ static int handle_getnodes(void *object, const IP_Port *source, const uint8_t *p } uint8_t plain[CRYPTO_NODE_SIZE]; - uint8_t shared_key[CRYPTO_SHARED_KEY_SIZE]; - - dht_get_shared_key_recv(dht, shared_key, packet + 1); + const uint8_t *shared_key = dht_get_shared_key_recv(dht, packet + 1); const int len = decrypt_data_symmetric( shared_key, packet + 1 + CRYPTO_PUBLIC_KEY_SIZE, @@ -1609,7 +1479,6 @@ static int handle_getnodes(void *object, const IP_Port *source, const uint8_t *p plain); if (len != CRYPTO_NODE_SIZE) { - crypto_memzero(shared_key, sizeof(shared_key)); return 1; } @@ -1617,8 +1486,6 @@ static int handle_getnodes(void *object, const IP_Port *source, const uint8_t *p ping_add(dht->ping, packet + 1, source); - crypto_memzero(shared_key, sizeof(shared_key)); - return 0; } @@ -1663,8 +1530,7 @@ static bool handle_sendnodes_core(void *object, const IP_Port *source, const uin } VLA(uint8_t, plain, 1 + data_size + sizeof(uint64_t)); - uint8_t shared_key[CRYPTO_SHARED_KEY_SIZE]; - dht_get_shared_key_sent(dht, shared_key, packet + 1); + const uint8_t *shared_key = dht_get_shared_key_sent(dht, packet + 1); const int len = decrypt_data_symmetric( shared_key, packet + 1 + CRYPTO_PUBLIC_KEY_SIZE, @@ -1672,8 +1538,6 @@ static bool handle_sendnodes_core(void *object, const IP_Port *source, const uin 1 + data_size + sizeof(uint64_t) + CRYPTO_MAC_SIZE, plain); - crypto_memzero(shared_key, sizeof(shared_key)); - if ((unsigned int)len != SIZEOF_VLA(plain)) { return false; } @@ -1745,35 +1609,75 @@ static int handle_sendnodes_ipv6(void *object, const IP_Port *source, const uint /*----------------------------------------------------------------------------------*/ /*------------------------END of packet handling functions--------------------------*/ -non_null(1) nullable(2, 3, 5) -static void dht_friend_lock(DHT_Friend *const dht_friend, dht_ip_cb *ip_callback, - void *data, int32_t number, uint16_t *lock_count) +non_null(1) nullable(2, 3) +static uint32_t dht_friend_lock(DHT_Friend *const dht_friend, dht_ip_cb *ip_callback, + void *data, int32_t number) { - const uint16_t lock_num = dht_friend->lock_count; - ++dht_friend->lock_count; + // find first free slot + uint8_t lock_num; + uint32_t lock_token = 0; + for (lock_num = 0; lock_num < DHT_FRIEND_MAX_LOCKS; ++lock_num) { + lock_token = UINT32_C(1) << lock_num; + if ((dht_friend->lock_flags & lock_token) == 0) { + break; + } + } + + // One of the conditions would be enough, but static analyzers don't get that + if (lock_token == 0 || lock_num == DHT_FRIEND_MAX_LOCKS) { + return 0; + } + + // Claim that slot + dht_friend->lock_flags |= lock_token; + dht_friend->callbacks[lock_num].ip_callback = ip_callback; dht_friend->callbacks[lock_num].data = data; dht_friend->callbacks[lock_num].number = number; - if (lock_count != nullptr) { - *lock_count = lock_num + 1; + return lock_token; +} + +non_null() +static void dht_friend_unlock(DHT_Friend *const dht_friend, uint32_t lock_token) +{ + // If this triggers, there was a double free + assert((lock_token & dht_friend->lock_flags) > 0); + + // find used slot + uint8_t lock_num; + for (lock_num = 0; lock_num < DHT_FRIEND_MAX_LOCKS; ++lock_num) { + if (((UINT32_C(1) << lock_num) & lock_token) > 0) { + break; + } } + + if (lock_num == DHT_FRIEND_MAX_LOCKS) { + // Gracefully handle double unlock + return; + } + + // Clear the slot + dht_friend->lock_flags &= ~lock_token; + + dht_friend->callbacks[lock_num].ip_callback = nullptr; + dht_friend->callbacks[lock_num].data = nullptr; + dht_friend->callbacks[lock_num].number = 0; } int dht_addfriend(DHT *dht, const uint8_t *public_key, dht_ip_cb *ip_callback, - void *data, int32_t number, uint16_t *lock_count) + void *data, int32_t number, uint32_t *lock_token) { const uint32_t friend_num = index_of_friend_pk(dht->friends_list, dht->num_friends, public_key); if (friend_num != UINT32_MAX) { /* Is friend already in DHT? */ DHT_Friend *const dht_friend = &dht->friends_list[friend_num]; + const uint32_t tmp_lock_token = dht_friend_lock(dht_friend, ip_callback, data, number); - if (dht_friend->lock_count == DHT_FRIEND_MAX_LOCKS) { + if (tmp_lock_token == 0) { return -1; } - dht_friend_lock(dht_friend, ip_callback, data, number, lock_count); - return 0; } @@ -1791,7 +1695,8 @@ int dht_addfriend(DHT *dht, const uint8_t *public_key, dht_ip_cb *ip_callback, dht_friend->nat.nat_ping_id = random_u64(dht->rng); ++dht->num_friends; - dht_friend_lock(dht_friend, ip_callback, data, number, lock_count); + *lock_token = dht_friend_lock(dht_friend, ip_callback, data, number); + assert(*lock_token != 0); // Friend was newly allocated dht_friend->num_to_bootstrap = get_close_nodes(dht, dht_friend->public_key, dht_friend->to_bootstrap, net_family_unspec(), true, false); @@ -1799,7 +1704,7 @@ int dht_addfriend(DHT *dht, const uint8_t *public_key, dht_ip_cb *ip_callback, return 0; } -int dht_delfriend(DHT *dht, const uint8_t *public_key, uint16_t lock_count) +int dht_delfriend(DHT *dht, const uint8_t *public_key, uint32_t lock_token) { const uint32_t friend_num = index_of_friend_pk(dht->friends_list, dht->num_friends, public_key); @@ -1808,13 +1713,9 @@ int dht_delfriend(DHT *dht, const uint8_t *public_key, uint16_t lock_count) } DHT_Friend *const dht_friend = &dht->friends_list[friend_num]; - --dht_friend->lock_count; - - if (dht_friend->lock_count > 0 && lock_count > 0) { /* DHT friend is still in use.*/ - --lock_count; - dht_friend->callbacks[lock_count].ip_callback = nullptr; - dht_friend->callbacks[lock_count].data = nullptr; - dht_friend->callbacks[lock_count].number = 0; + dht_friend_unlock(dht_friend, lock_token); + if (dht_friend->lock_flags > 0) { + /* DHT friend is still in use.*/ return 0; } @@ -1855,7 +1756,7 @@ int dht_getfriendip(const DHT *dht, const uint8_t *public_key, IP_Port *ip_port) const DHT_Friend *const frnd = &dht->friends_list[friend_index]; const uint32_t client_index = index_of_client_pk(frnd->client_list, MAX_FRIEND_CLIENTS, public_key); - if (client_index == -1) { + if (client_index == UINT32_MAX) { return 0; } @@ -2059,11 +1960,6 @@ int dht_bootstrap_from_address(DHT *dht, const char *address, bool ipv6enabled, return 0; } -/** @brief Send the given packet to node with public_key. - * - * @return number of bytes sent. - * @retval -1 if failure. - */ int route_packet(const DHT *dht, const uint8_t *public_key, const uint8_t *packet, uint16_t length) { for (uint32_t i = 0; i < LCLIENT_LIST; ++i) { @@ -2752,6 +2648,15 @@ DHT *new_dht(const Logger *log, const Random *rng, const Network *ns, Mono_Time crypto_new_keypair(rng, dht->self_public_key, dht->self_secret_key); + dht->shared_keys_recv = shared_key_cache_new(mono_time, dht->self_secret_key, KEYS_TIMEOUT, MAX_KEYS_PER_SLOT); + dht->shared_keys_sent = shared_key_cache_new(mono_time, dht->self_secret_key, KEYS_TIMEOUT, MAX_KEYS_PER_SLOT); + + if (dht->shared_keys_recv == nullptr || dht->shared_keys_sent == nullptr) { + kill_dht(dht); + return nullptr; + } + + dht->dht_ping_array = ping_array_new(DHT_PING_ARRAY_SIZE, PING_TIMEOUT); if (dht->dht_ping_array == nullptr) { @@ -2765,7 +2670,8 @@ DHT *new_dht(const Logger *log, const Random *rng, const Network *ns, Mono_Time crypto_new_keypair(rng, random_public_key_bytes, random_secret_key_bytes); - if (dht_addfriend(dht, random_public_key_bytes, nullptr, nullptr, 0, nullptr) != 0) { + uint32_t token; // We don't intend to delete these ever, but need to pass the token + if (dht_addfriend(dht, random_public_key_bytes, nullptr, nullptr, 0, &token) != 0) { kill_dht(dht); return nullptr; } @@ -2813,12 +2719,12 @@ void kill_dht(DHT *dht) networking_registerhandler(dht->net, NET_PACKET_LAN_DISCOVERY, nullptr, nullptr); cryptopacket_registerhandler(dht, CRYPTO_PACKET_NAT_PING, nullptr, nullptr); + shared_key_cache_free(dht->shared_keys_recv); + shared_key_cache_free(dht->shared_keys_sent); ping_array_kill(dht->dht_ping_array); ping_kill(dht->ping); free(dht->friends_list); free(dht->loaded_nodes_list); - crypto_memzero(&dht->shared_keys_recv, sizeof(dht->shared_keys_recv)); - crypto_memzero(&dht->shared_keys_sent, sizeof(dht->shared_keys_sent)); crypto_memzero(dht->self_secret_key, sizeof(dht->self_secret_key)); free(dht); } @@ -2929,11 +2835,6 @@ void dht_save(const DHT *dht, uint8_t *data) /** Bootstrap from this number of nodes every time `dht_connect_after_load()` is called */ #define SAVE_BOOTSTAP_FREQUENCY 8 -/** @brief Start sending packets after DHT loaded_friends_list and loaded_clients_list are set. - * - * @retval 0 if successful - * @retval -1 otherwise - */ int dht_connect_after_load(DHT *dht) { if (dht == nullptr) { @@ -3003,11 +2904,6 @@ static State_Load_Status dht_load_state_callback(void *outer, const uint8_t *dat return STATE_LOAD_STATUS_CONTINUE; } -/** @brief Load the DHT from data of size size. - * - * @retval -1 if failure. - * @retval 0 if success. - */ int dht_load(DHT *dht, const uint8_t *data, uint32_t length) { const uint32_t cookie_len = sizeof(uint32_t); @@ -3066,16 +2962,6 @@ bool dht_non_lan_connected(const DHT *dht) return false; } -/** @brief Copies our own ip_port structure to `dest`. - * - * WAN addresses take priority over LAN addresses. - * - * This function will zero the `dest` buffer before use. - * - * @retval 0 if our ip port can't be found (this usually means we're not connected to the DHT). - * @retval 1 if IP is a WAN address. - * @retval 2 if IP is a LAN address. - */ unsigned int ipport_self_copy(const DHT *dht, IP_Port *dest) { ipport_reset(dest); |