diff --git a/auto_tests/Makefile.inc b/auto_tests/Makefile.inc index d78a6a5a..58cb0249 100644 --- a/auto_tests/Makefile.inc +++ b/auto_tests/Makefile.inc @@ -1,7 +1,7 @@ if BUILD_TESTS -TESTS = encryptsave_test messenger_autotest crypto_test network_test assoc_test onion_test TCP_test tox_test dht_autotest -check_PROGRAMS = encryptsave_test messenger_autotest crypto_test network_test assoc_test onion_test TCP_test tox_test dht_autotest +TESTS = encryptsave_test messenger_autotest crypto_test network_test onion_test TCP_test tox_test dht_autotest +check_PROGRAMS = encryptsave_test messenger_autotest crypto_test network_test onion_test TCP_test tox_test dht_autotest AUTOTEST_CFLAGS = \ $(LIBSODIUM_CFLAGS) \ @@ -47,12 +47,6 @@ network_test_CFLAGS = $(AUTOTEST_CFLAGS) network_test_LDADD = $(AUTOTEST_LDADD) -assoc_test_SOURCES = ../auto_tests/assoc_test.c - -assoc_test_CFLAGS = $(AUTOTEST_CFLAGS) - -assoc_test_LDADD = $(AUTOTEST_LDADD) - onion_test_SOURCES = ../auto_tests/onion_test.c diff --git a/auto_tests/assoc_test.c b/auto_tests/assoc_test.c deleted file mode 100644 index 165724e9..00000000 --- a/auto_tests/assoc_test.c +++ /dev/null @@ -1,162 +0,0 @@ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#define AUTO_TEST -#include "../toxcore/DHT.h" -#include "../toxcore/assoc.h" -#include "../toxcore/util.h" - -#include -#include -#include - -#include - -#include "helpers.h" - -START_TEST(test_basics) -{ - /* TODO: real test */ - uint8_t id[crypto_box_PUBLICKEYBYTES]; - Assoc *assoc = new_Assoc_default(id); - ck_assert_msg(assoc != NULL, "failed to create default assoc"); - - kill_Assoc(assoc); - assoc = new_Assoc(17, 4, id); /* results in an assoc of 16/3 */ - ck_assert_msg(assoc != NULL, "failed to create customized assoc"); - - IP_Port ipp; - ipp.ip.family = AF_INET; - ipp.ip.ip4.uint8[0] = 1; - ipp.port = htons(12345); - - IPPTs ippts_send; - ippts_send.ip_port = ipp; - ippts_send.timestamp = unix_time(); - IP_Port ipp_recv = ipp; - - uint8_t res = Assoc_add_entry(assoc, id, &ippts_send, &ipp_recv, 0); - ck_assert_msg(res == 0, "stored self as entry: expected %u, got %u", 0, res); - - id[0]++; - - res = Assoc_add_entry(assoc, id, &ippts_send, &ipp_recv, 0); - ck_assert_msg(res == 1, "failed to store entry: expected %u, got %u", 1, res); - - Assoc_close_entries close_entries; - memset(&close_entries, 0, sizeof(close_entries)); - close_entries.count = 4; - close_entries.count_good = 2; - close_entries.wanted_id = id; - - Client_data *entries[close_entries.count]; - close_entries.result = entries; - - uint8_t found = Assoc_get_close_entries(assoc, &close_entries); - ck_assert_msg(found == 1, "get_close_entries(): expected %u, got %u", 1, found); - kill_Assoc(assoc); -} -END_TEST - -START_TEST(test_fillup) -{ - /* TODO: real test */ - int i, j; - uint8_t id[crypto_box_PUBLICKEYBYTES]; - //uint32_t a = current_time(); - uint32_t a = 2710106197; - srand(a); - - for (i = 0; i < crypto_box_PUBLICKEYBYTES; ++i) { - id[i] = rand(); - } - - Assoc *assoc = new_Assoc(6, 15, id); - ck_assert_msg(assoc != NULL, "failed to create default assoc"); - struct entry { - uint8_t id[crypto_box_PUBLICKEYBYTES]; - IPPTs ippts_send; - IP_Port ipp_recv; - }; - unsigned int fail = 0; - struct entry entries[128]; - struct entry closest[8]; - - for (j = 0; j < 128; ++j) { - - for (i = 0; i < crypto_box_PUBLICKEYBYTES; ++i) { - entries[j].id[i] = rand(); - } - - IP_Port ipp; - ipp.ip.family = AF_INET; - ipp.ip.ip4.uint32 = rand(); - ipp.port = rand(); - entries[j].ippts_send.ip_port = ipp; - entries[j].ippts_send.timestamp = unix_time(); - ipp.ip.ip4.uint32 = rand(); - ipp.port = rand(); - entries[j].ipp_recv = ipp; - - if (j % 16 == 0) { - memcpy(entries[j].id, id, crypto_box_PUBLICKEYBYTES - 30); - memcpy(&closest[j / 16], &entries[j], sizeof(struct entry)); - - } - - uint8_t res = Assoc_add_entry(assoc, entries[j].id, &entries[j].ippts_send, &entries[j].ipp_recv, 1); - ck_assert_msg(res == 1, "failed to store entry: expected %u, got %u, j = %u", 1, res, j); - } - - int good = 0; - Assoc_close_entries close_entries; - memset(&close_entries, 0, sizeof(close_entries)); - close_entries.count = 8; - close_entries.count_good = 8; - close_entries.wanted_id = id; - - Client_data *entri[close_entries.count]; - close_entries.result = entri; - - uint8_t found = Assoc_get_close_entries(assoc, &close_entries); - ck_assert_msg(found == 8, "get_close_entries(): expected %u, got %u", 1, found); - - for (i = 0; i < 8; ++i) { - for (j = 0; j < 8; ++j) { - if (id_equal(entri[j]->public_key, closest[i].id)) - ++good; - } - } - - ck_assert_msg(good == 8, "Entries found were not the closest ones. Only %u/8 were.", good); - //printf("good: %u %u %u\n", good, a, ((uint32_t)current_time() - a)); - kill_Assoc(assoc); -} -END_TEST - -Suite *Assoc_suite(void) -{ - Suite *s = suite_create("Assoc"); - - DEFTESTCASE(basics); - DEFTESTCASE(fillup); - return s; -} - -int main(int argc, char *argv[]) -{ - unix_time_update(); - Suite *Assoc = Assoc_suite(); - SRunner *test_runner = srunner_create(Assoc); - - srunner_set_fork_status(test_runner, CK_NOFORK); - - srunner_run_all(test_runner, CK_NORMAL); - - int number_failed = srunner_ntests_failed(test_runner); - - srunner_free(test_runner); - - return number_failed; -} diff --git a/auto_tests/dht_test.c b/auto_tests/dht_test.c index 2fe139be..b424b79b 100644 --- a/auto_tests/dht_test.c +++ b/auto_tests/dht_test.c @@ -18,41 +18,6 @@ } while(0) -void mark_bad(IPPTsPng *ipptp) -{ - ipptp->timestamp = unix_time() - 2 * BAD_NODE_TIMEOUT; - ipptp->hardening.routes_requests_ok = 0; - ipptp->hardening.send_nodes_ok = 0; - ipptp->hardening.testing_requests = 0; -} - -void mark_possible_bad(IPPTsPng *ipptp) -{ - ipptp->timestamp = unix_time(); - ipptp->hardening.routes_requests_ok = 0; - ipptp->hardening.send_nodes_ok = 0; - ipptp->hardening.testing_requests = 0; -} - -void mark_good(IPPTsPng *ipptp) -{ - ipptp->timestamp = unix_time(); - ipptp->hardening.routes_requests_ok = (HARDENING_ALL_OK >> 0) & 1; - ipptp->hardening.send_nodes_ok = (HARDENING_ALL_OK >> 1) & 1; - ipptp->hardening.testing_requests = (HARDENING_ALL_OK >> 2) & 1; -} - -void mark_all_good(Client_data *list, uint32_t length, uint8_t ipv6) -{ - uint32_t i; - - for (i = 0; i < length; ++i) { - if (ipv6) - mark_good(&list[i].assoc6); - else - mark_good(&list[i].assoc4); - } -} /* Returns 1 if public_key has a furthest distance to comp_client_id than all public_key's in the list */ @@ -67,304 +32,7 @@ uint8_t is_furthest(const uint8_t *comp_client_id, Client_data *list, uint32_t l return 1; } -int client_in_list(Client_data *list, uint32_t length, const uint8_t *public_key) -{ - int i; - for (i = 0; i < (int)length; ++i) - if (id_equal(public_key, list[i].public_key)) - return i; - - return -1; -} - -void test_addto_lists_update(DHT *dht, - Client_data *list, - uint32_t length, - IP_Port *ip_port) -{ - int used, test, test1, test2, found; - IP_Port test_ipp; - uint8_t test_id[crypto_box_PUBLICKEYBYTES]; - uint8_t ipv6 = ip_port->ip.family == AF_INET6 ? 1 : 0; - - // check id update for existing ip_port - test = rand() % length; - ipport_copy(&test_ipp, ipv6 ? &list[test].assoc6.ip_port : &list[test].assoc4.ip_port); - - randombytes(test_id, sizeof(test_id)); - used = addto_lists(dht, test_ipp, test_id); - ck_assert_msg(used >= 1, "Wrong number of added clients"); - // it is possible to have ip_port duplicates in the list, so ip_port @ found not always equal to ip_port @ test - found = client_in_list(list, length, test_id); - ck_assert_msg(found >= 0, "Client id is not in the list"); - ck_assert_msg(ipport_equal(&test_ipp, ipv6 ? &list[found].assoc6.ip_port : &list[found].assoc4.ip_port), - "Client IP_Port is incorrect"); - - // check ip_port update for existing id - test = rand() % length; - test_ipp.port = rand() % TOX_PORT_DEFAULT; - id_copy(test_id, list[test].public_key); - - used = addto_lists(dht, test_ipp, test_id); - ck_assert_msg(used >= 1, "Wrong number of added clients"); - // it is not possible to have id duplicates in the list, so id @ found must be equal id @ test - ck_assert_msg(client_in_list(list, length, test_id) == test, "Client id is not in the list"); - ck_assert_msg(ipport_equal(&test_ipp, ipv6 ? &list[test].assoc6.ip_port : &list[test].assoc4.ip_port), - "Client IP_Port is incorrect"); - - // check ip_port update for existing id and ip_port (... port ... id ...) - test1 = rand() % (length / 2); - test2 = rand() % (length / 2) + length / 2; - - ipport_copy(&test_ipp, ipv6 ? &list[test1].assoc6.ip_port : &list[test1].assoc4.ip_port); - id_copy(test_id, list[test2].public_key); - - if (ipv6) list[test2].assoc6.ip_port.port = -1; - else list[test2].assoc4.ip_port.port = -1; - - used = addto_lists(dht, test_ipp, test_id); - ck_assert_msg(used >= 1, "Wrong number of added clients"); - ck_assert_msg(client_in_list(list, length, test_id) == test2, "Client id is not in the list"); - ck_assert_msg(ipport_equal(&test_ipp, ipv6 ? &list[test2].assoc6.ip_port : &list[test2].assoc4.ip_port), - "Client IP_Port is incorrect"); - - // check ip_port update for existing id and ip_port (... id ... port ...) - test1 = rand() % (length / 2); - test2 = rand() % (length / 2) + length / 2; - - ipport_copy(&test_ipp, ipv6 ? &list[test2].assoc6.ip_port : &list[test2].assoc4.ip_port); - id_copy(test_id, list[test1].public_key); - - if (ipv6) list[test1].assoc6.ip_port.port = -1; - else list[test1].assoc4.ip_port.port = -1; - - used = addto_lists(dht, test_ipp, test_id); - ck_assert_msg(used >= 1, "Wrong number of added clients"); - ck_assert_msg(client_in_list(list, length, test_id) == test1, "Client id is not in the list"); - ck_assert_msg(ipport_equal(&test_ipp, ipv6 ? &list[test1].assoc6.ip_port : &list[test1].assoc4.ip_port), - "Client IP_Port is incorrect"); -} - -void test_addto_lists_bad(DHT *dht, - Client_data *list, - uint32_t length, - IP_Port *ip_port) -{ - // check "bad" clients replacement - int used, test1, test2, test3; - uint8_t public_key[crypto_box_PUBLICKEYBYTES], test_id1[crypto_box_PUBLICKEYBYTES], test_id2[crypto_box_PUBLICKEYBYTES], - test_id3[crypto_box_PUBLICKEYBYTES]; - uint8_t ipv6 = ip_port->ip.family == AF_INET6 ? 1 : 0; - - randombytes(public_key, sizeof(public_key)); - mark_all_good(list, length, ipv6); - - test1 = rand() % (length / 3); - test2 = rand() % (length / 3) + length / 3; - test3 = rand() % (length / 3) + 2 * length / 3; - ck_assert_msg(!(test1 == test2 || test1 == test3 || test2 == test3), "Wrong test indices are chosen"); - - id_copy((uint8_t *)&test_id1, list[test1].public_key); - id_copy((uint8_t *)&test_id2, list[test2].public_key); - id_copy((uint8_t *)&test_id3, list[test3].public_key); - - // mark nodes as "bad" - if (ipv6) { - mark_bad(&list[test1].assoc6); - mark_bad(&list[test2].assoc6); - mark_bad(&list[test3].assoc6); - } else { - mark_bad(&list[test1].assoc4); - mark_bad(&list[test2].assoc4); - mark_bad(&list[test3].assoc4); - } - - ip_port->port += 1; - used = addto_lists(dht, *ip_port, public_key); - ck_assert_msg(used >= 1, "Wrong number of added clients"); - - ck_assert_msg(client_in_list(list, length, public_key) >= 0, "Client id is not in the list"); - ck_assert_msg(client_in_list(list, length, test_id2) >= 0, "Wrong bad client removed"); - ck_assert_msg(client_in_list(list, length, test_id3) >= 0, "Wrong bad client removed"); -} - -void test_addto_lists_possible_bad(DHT *dht, - Client_data *list, - uint32_t length, - IP_Port *ip_port, - const uint8_t *comp_client_id) -{ - // check "possibly bad" clients replacement - int used, test1, test2, test3; - uint8_t public_key[crypto_box_PUBLICKEYBYTES], test_id1[crypto_box_PUBLICKEYBYTES], test_id2[crypto_box_PUBLICKEYBYTES], - test_id3[crypto_box_PUBLICKEYBYTES]; - uint8_t ipv6 = ip_port->ip.family == AF_INET6 ? 1 : 0; - - randombytes(public_key, sizeof(public_key)); - mark_all_good(list, length, ipv6); - - test1 = rand() % (length / 3); - test2 = rand() % (length / 3) + length / 3; - test3 = rand() % (length / 3) + 2 * length / 3; - ck_assert_msg(!(test1 == test2 || test1 == test3 || test2 == test3), "Wrong test indices are chosen"); - - id_copy((uint8_t *)&test_id1, list[test1].public_key); - id_copy((uint8_t *)&test_id2, list[test2].public_key); - id_copy((uint8_t *)&test_id3, list[test3].public_key); - - // mark nodes as "possibly bad" - if (ipv6) { - mark_possible_bad(&list[test1].assoc6); - mark_possible_bad(&list[test2].assoc6); - mark_possible_bad(&list[test3].assoc6); - } else { - mark_possible_bad(&list[test1].assoc4); - mark_possible_bad(&list[test2].assoc4); - mark_possible_bad(&list[test3].assoc4); - } - - ip_port->port += 1; - used = addto_lists(dht, *ip_port, public_key); - ck_assert_msg(used >= 1, "Wrong number of added clients"); - - ck_assert_msg(client_in_list(list, length, public_key) >= 0, "Client id is not in the list"); - - int inlist_id1 = client_in_list(list, length, test_id1) >= 0; - int inlist_id2 = client_in_list(list, length, test_id2) >= 0; - int inlist_id3 = client_in_list(list, length, test_id3) >= 0; - - ck_assert_msg(inlist_id1 + inlist_id2 + inlist_id3 == 2, "Wrong client removed"); - - if (!inlist_id1) { - ck_assert_msg(id_closest(comp_client_id, test_id2, test_id1) == 1, - "Id has been removed but is closer to than another one"); - ck_assert_msg(id_closest(comp_client_id, test_id3, test_id1) == 1, - "Id has been removed but is closer to than another one"); - } else if (!inlist_id2) { - ck_assert_msg(id_closest(comp_client_id, test_id1, test_id2) == 1, - "Id has been removed but is closer to than another one"); - ck_assert_msg(id_closest(comp_client_id, test_id3, test_id2) == 1, - "Id has been removed but is closer to than another one"); - } else if (!inlist_id3) { - ck_assert_msg(id_closest(comp_client_id, test_id1, test_id3) == 1, - "Id has been removed but is closer to than another one"); - ck_assert_msg(id_closest(comp_client_id, test_id2, test_id3) == 1, - "Id has been removed but is closer to than another one"); - } -} - -void test_addto_lists_good(DHT *dht, - Client_data *list, - uint32_t length, - IP_Port *ip_port, - const uint8_t *comp_client_id) -{ - uint8_t public_key[crypto_box_PUBLICKEYBYTES]; - uint8_t ipv6 = ip_port->ip.family == AF_INET6 ? 1 : 0; - - mark_all_good(list, length, ipv6); - - // check "good" client id replacement - do { - randombytes(public_key, sizeof(public_key)); - } while (is_furthest(comp_client_id, list, length, public_key)); - - ip_port->port += 1; - addto_lists(dht, *ip_port, public_key); - ck_assert_msg(client_in_list(list, length, public_key) >= 0, "Good client id is not in the list"); - - // check "good" client id skip - do { - randombytes(public_key, sizeof(public_key)); - } while (!is_furthest(comp_client_id, list, length, public_key)); - - ip_port->port += 1; - addto_lists(dht, *ip_port, public_key); - ck_assert_msg(client_in_list(list, length, public_key) == -1, "Good client id is in the list"); -} - -void test_addto_lists(IP ip) -{ - Networking_Core *net = new_networking(ip, TOX_PORT_DEFAULT); - ck_assert_msg(net != 0, "Failed to create Networking_Core"); - - DHT *dht = new_DHT(net); - ck_assert_msg(dht != 0, "Failed to create DHT"); - - IP_Port ip_port = { .ip = ip, .port = TOX_PORT_DEFAULT }; - uint8_t public_key[crypto_box_PUBLICKEYBYTES]; - int i, used; - - // check lists filling - for (i = 0; i < MAX(LCLIENT_LIST, MAX_FRIEND_CLIENTS); ++i) { - randombytes(public_key, sizeof(public_key)); - used = addto_lists(dht, ip_port, public_key); - ck_assert_msg(used == dht->num_friends + 1, "Wrong number of added clients with existing ip_port"); - } - - for (i = 0; i < MAX(LCLIENT_LIST, MAX_FRIEND_CLIENTS); ++i) { - ip_port.port += 1; - used = addto_lists(dht, ip_port, public_key); - ck_assert_msg(used == dht->num_friends + 1, "Wrong number of added clients with existing public_key"); - } - - for (i = 0; i < MAX(LCLIENT_LIST, MAX_FRIEND_CLIENTS); ++i) { - ip_port.port += 1; - randombytes(public_key, sizeof(public_key)); - used = addto_lists(dht, ip_port, public_key); - ck_assert_msg(used >= 1, "Wrong number of added clients"); - } - - /*check: Current behavior if there are two clients with the same id is - * to replace the first ip by the second. */ - test_addto_lists_update(dht, dht->close_clientlist, LCLIENT_LIST, &ip_port); - - for (i = 0; i < dht->num_friends; ++i) - test_addto_lists_update(dht, dht->friends_list[i].client_list, MAX_FRIEND_CLIENTS, &ip_port); - - // check "bad" entries - test_addto_lists_bad(dht, dht->close_clientlist, LCLIENT_LIST, &ip_port); - - for (i = 0; i < dht->num_friends; ++i) - test_addto_lists_bad(dht, dht->friends_list[i].client_list, MAX_FRIEND_CLIENTS, &ip_port); - - // check "possibly bad" entries - /* - test_addto_lists_possible_bad(dht, dht->close_clientlist, LCLIENT_LIST, &ip_port, dht->self_public_key); - - for (i = 0; i < dht->num_friends; ++i) - test_addto_lists_possible_bad(dht, dht->friends_list[i].client_list, MAX_FRIEND_CLIENTS, &ip_port, - dht->friends_list[i].public_key); - */ - // check "good" entries - test_addto_lists_good(dht, dht->close_clientlist, LCLIENT_LIST, &ip_port, dht->self_public_key); - - for (i = 0; i < dht->num_friends; ++i) - test_addto_lists_good(dht, dht->friends_list[i].client_list, MAX_FRIEND_CLIENTS, &ip_port, - dht->friends_list[i].public_key); - - kill_DHT(dht); - kill_networking(net); -} - -START_TEST(test_addto_lists_ipv4) -{ - IP ip; - ip_init(&ip, 0); - test_addto_lists(ip); - -} -END_TEST - -START_TEST(test_addto_lists_ipv6) -{ - IP ip; - ip_init(&ip, 1); - test_addto_lists(ip); - -} -END_TEST #define DHT_DEFAULT_PORT (TOX_PORT_DEFAULT + 20) @@ -413,45 +81,46 @@ void test_add_to_list(uint8_t cmp_list[][crypto_box_PUBLICKEYBYTES + 1], unsigne void test_list_main() { - DHT *dhts[NUM_DHT]; + /* + DHT *dhts[NUM_DHT]; - uint8_t cmp_list1[NUM_DHT][MAX_FRIEND_CLIENTS][crypto_box_PUBLICKEYBYTES + 1]; - memset(cmp_list1, 0, sizeof(cmp_list1)); + uint8_t cmp_list1[NUM_DHT][MAX_FRIEND_CLIENTS][crypto_box_PUBLICKEYBYTES + 1]; + memset(cmp_list1, 0, sizeof(cmp_list1)); - IP ip; - ip_init(&ip, 1); + IP ip; + ip_init(&ip, 1); - unsigned int i, j, k, l; + unsigned int i, j, k, l; - for (i = 0; i < NUM_DHT; ++i) { - IP ip; - ip_init(&ip, 1); + for (i = 0; i < NUM_DHT; ++i) { + IP ip; + ip_init(&ip, 1); - dhts[i] = new_DHT(new_networking(ip, DHT_DEFAULT_PORT + i)); - ck_assert_msg(dhts[i] != 0, "Failed to create dht instances %u", i); - ck_assert_msg(dhts[i]->net->port != DHT_DEFAULT_PORT + i, "Bound to wrong port"); - } + dhts[i] = new_DHT(new_networking(ip, DHT_DEFAULT_PORT + i)); + ck_assert_msg(dhts[i] != 0, "Failed to create dht instances %u", i); + ck_assert_msg(dhts[i]->net->port != DHT_DEFAULT_PORT + i, "Bound to wrong port"); + } - for (j = 0; j < NUM_DHT; ++j) { - for (i = 1; i < NUM_DHT; ++i) { - test_add_to_list(cmp_list1[j], MAX_FRIEND_CLIENTS, dhts[(i + j) % NUM_DHT]->self_public_key, dhts[j]->self_public_key); - } - } + for (j = 0; j < NUM_DHT; ++j) { + for (i = 1; i < NUM_DHT; ++i) { + test_add_to_list(cmp_list1[j], MAX_FRIEND_CLIENTS, dhts[(i + j) % NUM_DHT]->self_public_key, dhts[j]->self_public_key); + } + } - for (j = 0; j < NUM_DHT; ++j) { - for (i = 0; i < NUM_DHT; ++i) { - if (i == j) - continue; - - IP_Port ip_port; - ip_init(&ip_port.ip, 0); - ip_port.ip.ip4.uint32 = rand(); - ip_port.port = rand() % (UINT16_MAX - 1); - ++ip_port.port; - addto_lists(dhts[j], ip_port, dhts[i]->self_public_key); - } - } + for (j = 0; j < NUM_DHT; ++j) { + for (i = 0; i < NUM_DHT; ++i) { + if (i == j) + continue; + IP_Port ip_port; + ip_init(&ip_port.ip, 0); + ip_port.ip.ip4.uint32 = rand(); + ip_port.port = rand() % (UINT16_MAX - 1); + ++ip_port.port; + addto_lists(dhts[j], ip_port, dhts[i]->self_public_key); + } + } + */ /* print_pk(dhts[0]->self_public_key); @@ -460,72 +129,73 @@ void test_list_main() print_pk(cmp_list1[i]); } - */ - unsigned int m_count = 0; + *//* +unsigned int m_count = 0; - for (l = 0; l < NUM_DHT; ++l) { - for (i = 0; i < MAX_FRIEND_CLIENTS; ++i) { - for (j = 1; j < NUM_DHT; ++j) { - if (memcmp(cmp_list1[l][i], dhts[(l + j) % NUM_DHT]->self_public_key, crypto_box_PUBLICKEYBYTES) != 0) - continue; +for (l = 0; l < NUM_DHT; ++l) { +for (i = 0; i < MAX_FRIEND_CLIENTS; ++i) { + for (j = 1; j < NUM_DHT; ++j) { + if (memcmp(cmp_list1[l][i], dhts[(l + j) % NUM_DHT]->self_public_key, crypto_box_PUBLICKEYBYTES) != 0) + continue; - unsigned int count = 0; + unsigned int count = 0; - for (k = 0; k < LCLIENT_LIST; ++k) { - if (memcmp(dhts[l]->self_public_key, dhts[(l + j) % NUM_DHT]->close_clientlist[k].public_key, - crypto_box_PUBLICKEYBYTES) == 0) - ++count; - } - - if (count != 1) { - print_pk(dhts[l]->self_public_key); - - for (k = 0; k < MAX_FRIEND_CLIENTS; ++k) { - printf("----Entry %u----\n", k); - - print_pk(cmp_list1[l][k]); - } - - for (k = 0; k < LCLIENT_LIST; ++k) { - printf("----Closel %u----\n", k); - print_pk(dhts[(l + j) % NUM_DHT]->close_clientlist[k].public_key); - } - - print_pk(dhts[(l + j) % NUM_DHT]->self_public_key); - } - - ck_assert_msg(count == 1, "Nodes in search don't know ip of friend. %u %u %u", i, j, count); - - Node_format ln[MAX_SENT_NODES]; - int n = get_close_nodes(dhts[(l + j) % NUM_DHT], dhts[l]->self_public_key, ln, 0, 1, 0); - ck_assert_msg(n == MAX_SENT_NODES, "bad num close %u | %u %u", n, i, j); - - count = 0; - - for (k = 0; k < MAX_SENT_NODES; ++k) { - if (memcmp(dhts[l]->self_public_key, ln[k].public_key, crypto_box_PUBLICKEYBYTES) == 0) - ++count; - } - - ck_assert_msg(count == 1, "Nodes in search don't know ip of friend. %u %u %u", i, j, count); - /* - for (k = 0; k < MAX_SENT_NODES; ++k) { - printf("----gn %u----\n", k); - print_pk(ln[k].public_key); - }*/ - ++m_count; - } + for (k = 0; k < LCLIENT_LIST; ++k) { + if (memcmp(dhts[l]->self_public_key, dhts[(l + j) % NUM_DHT]->close_clientlist[k].public_key, + crypto_box_PUBLICKEYBYTES) == 0) + ++count; } - } - ck_assert_msg(m_count == (NUM_DHT) * (MAX_FRIEND_CLIENTS), "Bad count. %u != %u", m_count, - (NUM_DHT) * (MAX_FRIEND_CLIENTS)); + if (count != 1) { + print_pk(dhts[l]->self_public_key); - for (i = 0; i < NUM_DHT; ++i) { - void *n = dhts[i]->net; - kill_DHT(dhts[i]); - kill_networking(n); - } + for (k = 0; k < MAX_FRIEND_CLIENTS; ++k) { + printf("----Entry %u----\n", k); + + print_pk(cmp_list1[l][k]); + } + + for (k = 0; k < LCLIENT_LIST; ++k) { + printf("----Closel %u----\n", k); + print_pk(dhts[(l + j) % NUM_DHT]->close_clientlist[k].public_key); + } + + print_pk(dhts[(l + j) % NUM_DHT]->self_public_key); + } + + ck_assert_msg(count == 1, "Nodes in search don't know ip of friend. %u %u %u", i, j, count); + + Node_format ln[MAX_SENT_NODES]; + int n = get_close_nodes(dhts[(l + j) % NUM_DHT], dhts[l]->self_public_key, ln, 0, 1, 0); + ck_assert_msg(n == MAX_SENT_NODES, "bad num close %u | %u %u", n, i, j); + + count = 0; + + for (k = 0; k < MAX_SENT_NODES; ++k) { + if (memcmp(dhts[l]->self_public_key, ln[k].public_key, crypto_box_PUBLICKEYBYTES) == 0) + ++count; + } + + ck_assert_msg(count == 1, "Nodes in search don't know ip of friend. %u %u %u", i, j, count);*/ + /* + for (k = 0; k < MAX_SENT_NODES; ++k) { + printf("----gn %u----\n", k); + print_pk(ln[k].public_key); + }*//* +++m_count; +} +} +} + +ck_assert_msg(m_count == (NUM_DHT) * (MAX_FRIEND_CLIENTS), "Bad count. %u != %u", m_count, +(NUM_DHT) * (MAX_FRIEND_CLIENTS)); + +for (i = 0; i < NUM_DHT; ++i) { +void *n = dhts[i]->net; +kill_DHT(dhts[i]); +kill_networking(n); +} +*/ } @@ -652,7 +322,7 @@ int main(int argc, char *argv[]) SRunner *test_runner = srunner_create(dht); int number_failed = 0; - //srunner_set_fork_status(test_runner, CK_NOFORK); + srunner_set_fork_status(test_runner, CK_NOFORK); srunner_run_all(test_runner, CK_NORMAL); number_failed = srunner_ntests_failed(test_runner); diff --git a/testing/DHT_test.c b/testing/DHT_test.c index 1078ce6f..445c49fa 100644 --- a/testing/DHT_test.c +++ b/testing/DHT_test.c @@ -62,7 +62,7 @@ void print_client_id(uint8_t *public_key) printf("%02hhX", public_key[j]); } } - +/* void print_hardening(Hardening *h) { printf("Hardening:\n"); @@ -165,7 +165,7 @@ void printpacket(uint8_t *data, uint32_t length, IP_Port ip_port) } printf("\n--------------------END-----------------------------\n\n\n"); -} +}*/ int main(int argc, char *argv[]) { @@ -245,8 +245,8 @@ int main(int argc, char *argv[]) */ networking_poll(dht->net); - print_clientlist(dht); - print_friendlist(dht); + //print_clientlist(dht); + //print_friendlist(dht); c_sleep(300); } diff --git a/toxcore/DHT.c b/toxcore/DHT.c index 6d9479a6..0fe19179 100644 --- a/toxcore/DHT.c +++ b/toxcore/DHT.c @@ -34,11 +34,6 @@ #include "logger.h" #include "DHT.h" - -#ifdef ENABLE_ASSOC_DHT -#include "assoc.h" -#endif - #include "ping.h" #include "network.h" @@ -50,7 +45,7 @@ #define KILL_NODE_TIMEOUT (BAD_NODE_TIMEOUT + PING_INTERVAL) /* Ping interval in seconds for each random sending of a get nodes request. */ -#define GET_NODE_INTERVAL 20 +#define GET_NODE_INTERVAL 5 #define MAX_PUNCHING_PORTS 48 @@ -93,7 +88,7 @@ int id_closest(const uint8_t *pk, const uint8_t *pk1, const uint8_t *pk2) /* Return index of first unequal bit number. */ -static unsigned int bit_by_bit_cmp(const uint8_t *pk1, const uint8_t *pk2) +int bit_by_bit_cmp(const uint8_t *pk1, const uint8_t *pk2) { unsigned int i, j = 0; @@ -353,82 +348,227 @@ int unpack_nodes(Node_format *nodes, uint16_t max_num_nodes, uint16_t *processed return num; } - - -/* Check if client with public_key is already in list of length length. - * If it is then set its corresponding timestamp to current time. - * If the id is already in the list with a different ip_port, update it. - * TODO: Maybe optimize this. - * - * return True(1) or False(0) - */ -static int client_or_ip_port_in_list(Client_data *list, uint16_t length, const uint8_t *public_key, IP_Port ip_port) +static int get_bit_at(const uint8_t *pk, unsigned int index) { - uint32_t i; - uint64_t temp_time = unix_time(); + if (index >= crypto_box_PUBLICKEYBYTES * 8) + return -1; - /* if public_key is in list, find it and maybe overwrite ip_port */ - for (i = 0; i < length; ++i) - if (id_equal(list[i].public_key, public_key)) { - /* Refresh the client timestamp. */ - if (ip_port.ip.family == AF_INET) { + return !!(pk[index / 8] & (1 << (7 - (index % 8)))); +} - LOGGER_SCOPE( if (!ipport_equal(&list[i].assoc4.ip_port, &ip_port)) { - LOGGER_TRACE("coipil[%u]: switching ipv4 from %s:%u to %s:%u", i, - ip_ntoa(&list[i].assoc4.ip_port.ip), ntohs(list[i].assoc4.ip_port.port), - ip_ntoa(&ip_port.ip), ntohs(ip_port.port)); +static int set_bit_at(uint8_t *pk, unsigned int index) +{ + if (index >= crypto_box_PUBLICKEYBYTES * 8) + return -1; + + pk[index / 8] |= (1 << (7 - (index % 8))); + return 0; +} + +static int unset_bit_at(uint8_t *pk, unsigned int index) +{ + if (index >= crypto_box_PUBLICKEYBYTES * 8) + return -1; + + pk[index / 8] &= ~(1 << (7 - (index % 8))); + return 0; +} + +static int alloc_buckets(DHT_Bucket *bucket) +{ + DHT_Bucket *b0 = calloc(1, sizeof(DHT_Bucket)); + DHT_Bucket *b1 = calloc(1, sizeof(DHT_Bucket)); + + if (b0 && b1) { + bucket->buckets[0] = b0; + bucket->buckets[1] = b1; + + b0->deepness = bucket->deepness + 1; + b1->deepness = bucket->deepness + 1; + + unsigned int i, b0_ind = 0, b1_ind = 0; + + for (i = 0; i < DHT_BUCKET_NODES; ++i) { + if (!is_timeout(bucket->client_list[i].timestamp, BAD_NODE_TIMEOUT)) { + int bit = get_bit_at(bucket->client_list[i].public_key, bucket->deepness); + + if (bit == 0) { + memcpy(&b0->client_list[b0_ind], &bucket->client_list[i], sizeof(Client_data)); + ++b0_ind; + } else if (bit == 1) { + memcpy(&b1->client_list[b1_ind], &bucket->client_list[i], sizeof(Client_data)); + ++b1_ind; } - ); - - if (LAN_ip(list[i].assoc4.ip_port.ip) != 0 && LAN_ip(ip_port.ip) == 0) - return 1; - - list[i].assoc4.ip_port = ip_port; - list[i].assoc4.timestamp = temp_time; - } else if (ip_port.ip.family == AF_INET6) { - - LOGGER_SCOPE( if (!ipport_equal(&list[i].assoc4.ip_port, &ip_port)) { - LOGGER_TRACE("coipil[%u]: switching ipv6 from %s:%u to %s:%u", i, - ip_ntoa(&list[i].assoc6.ip_port.ip), ntohs(list[i].assoc6.ip_port.port), - ip_ntoa(&ip_port.ip), ntohs(ip_port.port)); - } - ); - - if (LAN_ip(list[i].assoc6.ip_port.ip) != 0 && LAN_ip(ip_port.ip) == 0) - return 1; - - list[i].assoc6.ip_port = ip_port; - list[i].assoc6.timestamp = temp_time; } - - return 1; } - /* public_key not in list yet: see if we can find an identical ip_port, in - * that case we kill the old public_key by overwriting it with the new one - * TODO: maybe we SHOULDN'T do that if that public_key is in a friend_list - * and the one who is the actual friend's public_key/address set? */ + if (bucket->public_key) { + int bit = get_bit_at(bucket->searched_public_key, bucket->deepness); + + if (bit == 0) { + memcpy(b0->searched_public_key, bucket->searched_public_key, crypto_box_PUBLICKEYBYTES); + b0->public_key = 1; + } else if (bit == 1) { + memcpy(b1->searched_public_key, bucket->searched_public_key, crypto_box_PUBLICKEYBYTES); + b1->public_key = 1; + } + } + + bucket->empty = 1; + bucket->public_key = 0; + + memset(bucket->client_list, 0, sizeof(bucket->client_list)); + + return 0; + } else { + free(b0); + free(b1); + return -1; + } +} + +static void recursive_free_buckets(DHT_Bucket *bucket) +{ + if (bucket) { + recursive_free_buckets(bucket->buckets[0]); + recursive_free_buckets(bucket->buckets[1]); + + free(bucket->buckets[0]); + free(bucket->buckets[1]); + + bucket->buckets[0] = 0; + bucket->buckets[1] = 0; + } +} + +void free_buckets(DHT_Bucket *bucket) +{ + recursive_free_buckets(bucket); +} + + +static int recursive_DHT_bucket_add_key(DHT_Bucket *bucket, const uint8_t *public_key) +{ + int bit = get_bit_at(public_key, bucket->deepness); + + if (bit == -1) + return -1; + + if (bucket->empty) { + return recursive_DHT_bucket_add_key(bucket->buckets[bit], public_key); + } + + if (bucket->public_key) { + if (id_equal(bucket->searched_public_key, public_key)) + return -1; + + if (alloc_buckets(bucket) == -1) + return -1; + + return recursive_DHT_bucket_add_key(bucket->buckets[bit], public_key); + } else { + memcpy(bucket->searched_public_key, public_key, crypto_box_PUBLICKEYBYTES); + bucket->public_key = 1; + return 0; + } +} + + +int DHT_bucket_add_key(DHT_Bucket *bucket, const uint8_t *public_key) +{ + return recursive_DHT_bucket_add_key(bucket, public_key); +} + +static int recursive_DHT_bucket_add_node(DHT_Bucket *bucket, const uint8_t *public_key, IP_Port ip_port, _Bool pretend) +{ + int bit = get_bit_at(public_key, bucket->deepness); + + if (bit == -1) + return -1; + + if (bucket->empty) { + return recursive_DHT_bucket_add_node(bucket->buckets[bit], public_key, ip_port, pretend); + } else { + unsigned int store_index = DHT_BUCKET_NODES; + + unsigned int i; + + for (i = 0; i < DHT_BUCKET_NODES; ++i) { + Client_data *client = &bucket->client_list[i]; + + if (is_timeout(client->timestamp, BAD_NODE_TIMEOUT)) { + store_index = i; + } else { + if (id_equal(client->public_key, public_key)) { + if (pretend) { + return -1; + } + + client->ip_port = ip_port; + client->timestamp = unix_time(); + return 0; + } + } + } + + if (store_index < DHT_BUCKET_NODES) { + if (pretend) { + return 0; + } + + Client_data *client = &bucket->client_list[store_index]; + memset(client, 0, sizeof(Client_data)); + id_copy(client->public_key, public_key); + client->ip_port = ip_port; + client->last_pinged = 0; + client->timestamp = unix_time(); + return 0; + } + + if (bucket->public_key) { + if (pretend) { + return 0; + } + + /* Bucket Full */ + if (alloc_buckets(bucket) == -1) + return -1; + + return recursive_DHT_bucket_add_node(bucket->buckets[bit], public_key, ip_port, pretend); + } else { + return -1; + } + } +} + +int DHT_bucket_add_node(DHT_Bucket *bucket, const uint8_t *public_key, IP_Port ip_port, _Bool pretend) +{ + return recursive_DHT_bucket_add_node(bucket, public_key, ip_port, pretend); +} + +/* Add node to the node list making sure only the nodes closest to cmp_pk are in the list. + */ +static _Bool add_to_ret_ip_list(Client_data *client, const uint8_t *node_public_key, const uint8_t *public_key, + IP_Port ret_ip_port, uint64_t timestamp) +{ + uint8_t pk_bak[crypto_box_PUBLICKEYBYTES]; + IP_Port ip_port_bak; + uint64_t timestamp_bak; + + unsigned int i, length = DHT_BUCKET_NODES; + for (i = 0; i < length; ++i) { - /* MAYBE: check the other address, if valid, don't nuke? */ - if ((ip_port.ip.family == AF_INET) && ipport_equal(&list[i].assoc4.ip_port, &ip_port)) { - /* Initialize client timestamp. */ - list[i].assoc4.timestamp = temp_time; - memcpy(list[i].public_key, public_key, crypto_box_PUBLICKEYBYTES); + if (id_closest(node_public_key, client->ret[i].pk, public_key) == 2) { + id_copy(pk_bak, client->ret[i].pk); + ip_port_bak = client->ret[i].ip_port; + timestamp_bak = client->ret[i].timestamp; + id_copy(client->ret[i].pk, public_key); + client->ret[i].ip_port = ret_ip_port; + client->ret[i].timestamp = timestamp; - LOGGER_DEBUG("coipil[%u]: switching public_key (ipv4)", i); + if (i != (length - 1)) + add_to_ret_ip_list(client, node_public_key, pk_bak, ip_port_bak, timestamp_bak); - /* kill the other address, if it was set */ - memset(&list[i].assoc6, 0, sizeof(list[i].assoc6)); - return 1; - } else if ((ip_port.ip.family == AF_INET6) && ipport_equal(&list[i].assoc6.ip_port, &ip_port)) { - /* Initialize client timestamp. */ - list[i].assoc6.timestamp = temp_time; - memcpy(list[i].public_key, public_key, crypto_box_PUBLICKEYBYTES); - - LOGGER_DEBUG("coipil[%u]: switching public_key (ipv6)", i); - - /* kill the other address, if it was set */ - memset(&list[i].assoc4, 0, sizeof(list[i].assoc4)); return 1; } } @@ -436,6 +576,225 @@ static int client_or_ip_port_in_list(Client_data *list, uint16_t length, const u return 0; } +static int recursive_DHT_bucket_set_node_ret_ip_port(DHT_Bucket *bucket, const uint8_t *node_public_key, + const uint8_t *public_key, IP_Port ret_ip_port) +{ + int bit = get_bit_at(node_public_key, bucket->deepness); + + if (bit == -1) + return -1; + + if (bucket->empty) { + return recursive_DHT_bucket_set_node_ret_ip_port(bucket->buckets[bit], node_public_key, public_key, ret_ip_port); + } else { + unsigned int i, j, counter = 0; + + for (i = 0; (i < DHT_BUCKET_NODES); ++i) { + Client_data *client = &bucket->client_list[i]; + + if (!is_timeout(client->timestamp, BAD_NODE_TIMEOUT)) { + if (id_equal(client->public_key, node_public_key)) { + uint64_t smallest_timestamp = ~0; + unsigned int index_dht = DHT_BUCKET_NODES; + + for (j = 0; j < DHT_BUCKET_NODES; ++j) { + if (id_equal(public_key, client->ret[j].pk)) { + client->ret[j].ip_port = ret_ip_port; + client->ret[j].timestamp = unix_time(); + return 0; + } + + if (smallest_timestamp > client->ret[j].timestamp) { + index_dht = j; + smallest_timestamp = client->ret[j].timestamp; + } + } + + if (index_dht < DHT_BUCKET_NODES && is_timeout(smallest_timestamp, BAD_NODE_TIMEOUT * 2)) { + id_copy(client->ret[index_dht].pk, public_key); + client->ret[index_dht].ip_port = ret_ip_port; + client->ret[index_dht].timestamp = unix_time(); + } else { + if (add_to_ret_ip_list(client, node_public_key, public_key, ret_ip_port, unix_time())) + return 0; + + return -1; + } + + return 0; + } + } + } + } + + return -1; +} + +static int DHT_bucket_set_node_ret_ip_port(DHT_Bucket *bucket, const uint8_t *node_public_key, + const uint8_t *public_key, IP_Port ret_ip_port) +{ + return recursive_DHT_bucket_set_node_ret_ip_port(bucket, node_public_key, public_key, ret_ip_port); +} + +static int recursive_DHT_bucket_get_nodes(const DHT_Bucket *bucket, Client_data *nodes, unsigned int number, + const uint8_t *public_key) +{ + int bit = get_bit_at(public_key, bucket->deepness); + + if (bit == -1) + return -1; + + if (bucket->empty) { + int ret = recursive_DHT_bucket_get_nodes(bucket->buckets[bit], nodes, number, public_key); + + if (ret < 0) + return -1; + + if (ret < number) { + number -= ret; + int ret1 = recursive_DHT_bucket_get_nodes(bucket->buckets[!bit], nodes, number, public_key); + + if (ret < 0) + return -1; + + return ret1 + ret; + } else { + return ret; + } + } else { + unsigned int i, counter = 0; + + for (i = 0; (i < DHT_BUCKET_NODES) && (counter < number); ++i) { + if (!is_timeout(bucket->client_list[i].timestamp, BAD_NODE_TIMEOUT)) { + memcpy(&nodes[number - (counter + 1)], &bucket->client_list[i], sizeof(Client_data)); + ++counter; + } + } + + return counter; + } +} + + +int DHT_bucket_get_nodes(const DHT_Bucket *bucket, Client_data *nodes, unsigned int number, const uint8_t *public_key) +{ + return recursive_DHT_bucket_get_nodes(bucket, nodes, number, public_key); +} + +static int dealloc_buckets(DHT_Bucket *bucket) +{ + if (!bucket->empty) + return -1; + + if (bucket->buckets[0]->public_key || bucket->buckets[1]->public_key) + return -1; + + if (bucket->buckets[0]->empty || bucket->buckets[1]->empty) + return -1; + + /* pk doesn't matter, want any nodes from both lower buckets. */ + uint8_t pk[crypto_box_PUBLICKEYBYTES] = {0}; + int ret = recursive_DHT_bucket_get_nodes(bucket, bucket->client_list, DHT_BUCKET_NODES, pk); + + recursive_free_buckets(bucket); + bucket->empty = 0; + + if (ret >= 0) { + return 0; + } else { + return -1; + } +} + + +static int recursive_DHT_bucket_rm_key(DHT_Bucket *bucket, const uint8_t *public_key) +{ + int bit = get_bit_at(public_key, bucket->deepness); + + if (bit == -1) + return -1; + + if (bucket->empty) { + int ret = recursive_DHT_bucket_rm_key(bucket->buckets[bit], public_key); + + if (ret == 0) { + if (dealloc_buckets(bucket) == -1) { + return -1; + } + + return 0; + } + } + + if (bucket->public_key) { + if (!id_equal(bucket->searched_public_key, public_key)) + return -1; + + bucket->public_key = 0; + return 0; + } else { + return -1; + } +} + + +int DHT_bucket_rm_key(DHT_Bucket *bucket, const uint8_t *public_key) +{ + return recursive_DHT_bucket_rm_key(bucket, public_key); +} + +static int getnodes(DHT *dht, IP_Port ip_port, const uint8_t *public_key, const uint8_t *client_id, + const Node_format *sendback_node); + +static int recursive_do_ping_nodes(DHT *dht, DHT_Bucket *bucket, uint8_t *key) +{ + if (bucket->empty) { + if (recursive_do_ping_nodes(dht, bucket->buckets[0], key) == -1) + return -1; + + set_bit_at(key, bucket->deepness); + + if (recursive_do_ping_nodes(dht, bucket->buckets[1], key) == -1) + return -1; + + unset_bit_at(key, bucket->deepness); + return 0; + } else { + uint8_t *search_key = key; + + if (bucket->public_key) { + search_key = bucket->searched_public_key; + } + + unsigned int i; + + for (i = 0; i < DHT_BUCKET_NODES; ++i) { + Client_data *client = &bucket->client_list[i]; + + if (!is_timeout(client->timestamp, BAD_NODE_TIMEOUT)) { + if (is_timeout(client->last_pinged, PING_INTERVAL)) { + getnodes(dht, client->ip_port, client->public_key, search_key, NULL); + client->last_pinged = unix_time(); + } + } + } + + return 0; + } +} + +static int do_ping_nodes(DHT *dht, DHT_Bucket *bucket) +{ + uint8_t key[crypto_box_PUBLICKEYBYTES]; + memset(key, 0, sizeof(key)); + + return recursive_do_ping_nodes(dht, bucket, key); +} + +/* TODO: count ips */ + + + /* Check if client with public_key is already in node format list of length length. * * return 1 if true. @@ -495,281 +854,59 @@ _Bool add_to_list(Node_format *nodes_list, unsigned int length, const uint8_t *p return 0; } -/*TODO: change this to 7 when done*/ -#define HARDENING_ALL_OK 2 -/* return 0 if not. - * return 1 if route request are ok - * return 2 if it responds to send node packets correctly - * return 4 if it can test other nodes correctly - * return HARDENING_ALL_OK if all ok. - */ -static uint8_t hardening_correct(const Hardening *h) -{ - return h->routes_requests_ok + (h->send_nodes_ok << 1) + (h->testing_requests << 2); -} -/* - * helper for get_close_nodes(). argument list is a monster :D - */ -static void get_close_nodes_inner(const uint8_t *public_key, Node_format *nodes_list, - sa_family_t sa_family, const Client_data *client_list, uint32_t client_list_length, - uint32_t *num_nodes_ptr, uint8_t is_LAN, uint8_t want_good) -{ - if ((sa_family != AF_INET) && (sa_family != AF_INET6) && (sa_family != 0)) - return; - - uint32_t num_nodes = *num_nodes_ptr; - uint32_t i; - - for (i = 0; i < client_list_length; i++) { - const Client_data *client = &client_list[i]; - - /* node already in list? */ - if (client_in_nodelist(nodes_list, MAX_SENT_NODES, client->public_key)) - continue; - - const IPPTsPng *ipptp = NULL; - - if (sa_family == AF_INET) { - ipptp = &client->assoc4; - } else if (sa_family == AF_INET6) { - ipptp = &client->assoc6; - } else { - if (client->assoc4.timestamp >= client->assoc6.timestamp) { - ipptp = &client->assoc4; - } else { - ipptp = &client->assoc6; - } - } - - /* node not in a good condition? */ - if (is_timeout(ipptp->timestamp, BAD_NODE_TIMEOUT)) - continue; - - /* don't send LAN ips to non LAN peers */ - if (LAN_ip(ipptp->ip_port.ip) == 0 && !is_LAN) - continue; - - if (LAN_ip(ipptp->ip_port.ip) != 0 && want_good && hardening_correct(&ipptp->hardening) != HARDENING_ALL_OK - && !id_equal(public_key, client->public_key)) - continue; - - if (num_nodes < MAX_SENT_NODES) { - memcpy(nodes_list[num_nodes].public_key, - client->public_key, - crypto_box_PUBLICKEYBYTES ); - - nodes_list[num_nodes].ip_port = ipptp->ip_port; - num_nodes++; - } else { - add_to_list(nodes_list, MAX_SENT_NODES, client->public_key, ipptp->ip_port, public_key); - } - } - - *num_nodes_ptr = num_nodes; -} - -/* Find MAX_SENT_NODES nodes closest to the public_key for the send nodes request: - * put them in the nodes_list and return how many were found. - * - * TODO: For the love of based make - * this function cleaner and much more efficient. - * - * want_good : do we want only good nodes as checked with the hardening returned or not? - */ -static int get_somewhat_close_nodes(const DHT *dht, const uint8_t *public_key, Node_format *nodes_list, - sa_family_t sa_family, uint8_t is_LAN, uint8_t want_good) -{ - uint32_t num_nodes = 0, i; - get_close_nodes_inner(public_key, nodes_list, sa_family, - dht->close_clientlist, LCLIENT_LIST, &num_nodes, is_LAN, 0); - - /*TODO uncomment this when hardening is added to close friend clients - for (i = 0; i < dht->num_friends; ++i) - get_close_nodes_inner(dht, public_key, nodes_list, sa_family, - dht->friends_list[i].client_list, MAX_FRIEND_CLIENTS, - &num_nodes, is_LAN, want_good); - */ - for (i = 0; i < dht->num_friends; ++i) - get_close_nodes_inner(public_key, nodes_list, sa_family, - dht->friends_list[i].client_list, MAX_FRIEND_CLIENTS, - &num_nodes, is_LAN, 0); - - return num_nodes; -} int get_close_nodes(const DHT *dht, const uint8_t *public_key, Node_format *nodes_list, sa_family_t sa_family, uint8_t is_LAN, uint8_t want_good) { memset(nodes_list, 0, MAX_SENT_NODES * sizeof(Node_format)); -#ifdef ENABLE_ASSOC_DHT + Client_data client_data[DHT_BUCKET_NODES * 3] = {0}; - if (!dht->assoc) -#endif - return get_somewhat_close_nodes(dht, public_key, nodes_list, sa_family, is_LAN, want_good); - -#ifdef ENABLE_ASSOC_DHT - //TODO: assoc, sa_family 0 (don't care if ipv4 or ipv6) support. - Client_data *result[MAX_SENT_NODES]; - - Assoc_close_entries request; - memset(&request, 0, sizeof(request)); - request.count = MAX_SENT_NODES; - request.count_good = MAX_SENT_NODES - 2; /* allow 2 'indirect' nodes */ - request.result = result; - request.wanted_id = public_key; - request.flags = (is_LAN ? LANOk : 0) + (sa_family == AF_INET ? ProtoIPv4 : ProtoIPv6); - - uint8_t num_found = Assoc_get_close_entries(dht->assoc, &request); - - if (!num_found) { - LOGGER_DEBUG("get_close_nodes(): Assoc_get_close_entries() returned zero nodes"); - return get_somewhat_close_nodes(dht, public_key, nodes_list, sa_family, is_LAN, want_good); - } - - LOGGER_DEBUG("get_close_nodes(): Assoc_get_close_entries() returned %i 'direct' and %i 'indirect' nodes", - request.count_good, num_found - request.count_good); - - uint8_t i, num_returned = 0; - - for (i = 0; i < num_found; i++) { - Client_data *client = result[i]; - - if (client) { - id_copy(nodes_list[num_returned].public_key, client->public_key); - - if (sa_family == AF_INET) - if (ipport_isset(&client->assoc4.ip_port)) { - nodes_list[num_returned].ip_port = client->assoc4.ip_port; - num_returned++; - continue; - } - - if (sa_family == AF_INET6) - if (ipport_isset(&client->assoc6.ip_port)) { - nodes_list[num_returned].ip_port = client->assoc6.ip_port; - num_returned++; - continue; - } - } - } - - return num_returned; -#endif -} - -static uint8_t cmp_public_key[crypto_box_PUBLICKEYBYTES]; -static int cmp_dht_entry(const void *a, const void *b) -{ - Client_data entry1, entry2; - memcpy(&entry1, a, sizeof(Client_data)); - memcpy(&entry2, b, sizeof(Client_data)); - int t1 = is_timeout(entry1.assoc4.timestamp, BAD_NODE_TIMEOUT) && is_timeout(entry1.assoc6.timestamp, BAD_NODE_TIMEOUT); - int t2 = is_timeout(entry2.assoc4.timestamp, BAD_NODE_TIMEOUT) && is_timeout(entry2.assoc6.timestamp, BAD_NODE_TIMEOUT); - - if (t1 && t2) - return 0; - - if (t1) - return -1; - - if (t2) - return 1; - - t1 = hardening_correct(&entry1.assoc4.hardening) != HARDENING_ALL_OK - && hardening_correct(&entry1.assoc6.hardening) != HARDENING_ALL_OK; - t2 = hardening_correct(&entry2.assoc4.hardening) != HARDENING_ALL_OK - && hardening_correct(&entry2.assoc6.hardening) != HARDENING_ALL_OK; - - if (t1 != t2) { - if (t1) - return -1; - - if (t2) - return 1; - } - - int close = id_closest(cmp_public_key, entry1.public_key, entry2.public_key); - - if (close == 1) - return 1; - - if (close == 2) - return -1; - - return 0; -} - -/* Is it ok to store node with public_key in client. - * - * return 0 if node can't be stored. - * return 1 if it can. - */ -static unsigned int store_node_ok(const Client_data *client, const uint8_t *public_key, const uint8_t *comp_public_key) -{ - if ((is_timeout(client->assoc4.timestamp, BAD_NODE_TIMEOUT) && is_timeout(client->assoc6.timestamp, BAD_NODE_TIMEOUT)) - || (id_closest(comp_public_key, client->public_key, public_key) == 2)) { - return 1; + if (sa_family == AF_INET) { + DHT_bucket_get_nodes(&dht->bucket_v4, client_data, DHT_BUCKET_NODES, public_key); + } else if (sa_family == AF_INET6) { + DHT_bucket_get_nodes(&dht->bucket_v6, client_data + DHT_BUCKET_NODES, DHT_BUCKET_NODES, public_key); } else { - return 0; - } -} - -static void sort_client_list(Client_data *list, unsigned int length, const uint8_t *comp_public_key) -{ - memcpy(cmp_public_key, comp_public_key, crypto_box_PUBLICKEYBYTES); - qsort(list, length, sizeof(Client_data), cmp_dht_entry); -} - -/* Replace a first bad (or empty) node with this one - * or replace a possibly bad node (tests failed or not done yet) - * that is further than any other in the list - * from the comp_public_key - * or replace a good node that is further - * than any other in the list from the comp_public_key - * and further than public_key. - * - * Do not replace any node if the list has no bad or possibly bad nodes - * and all nodes in the list are closer to comp_public_key - * than public_key. - * - * returns True(1) when the item was stored, False(0) otherwise */ -static int replace_all( Client_data *list, - uint16_t length, - const uint8_t *public_key, - IP_Port ip_port, - const uint8_t *comp_public_key ) -{ - if ((ip_port.ip.family != AF_INET) && (ip_port.ip.family != AF_INET6)) - return 0; - - if (store_node_ok(&list[1], public_key, comp_public_key) || store_node_ok(&list[0], public_key, comp_public_key)) { - sort_client_list(list, length, comp_public_key); - - IPPTsPng *ipptp_write = NULL; - IPPTsPng *ipptp_clear = NULL; - - Client_data *client = &list[0]; - - if (ip_port.ip.family == AF_INET) { - ipptp_write = &client->assoc4; - ipptp_clear = &client->assoc6; + if (rand() % 2) { + DHT_bucket_get_nodes(&dht->bucket_v4, client_data, DHT_BUCKET_NODES, public_key); + DHT_bucket_get_nodes(&dht->bucket_v6, client_data + DHT_BUCKET_NODES, DHT_BUCKET_NODES, public_key); } else { - ipptp_write = &client->assoc6; - ipptp_clear = &client->assoc4; + DHT_bucket_get_nodes(&dht->bucket_v6, client_data, DHT_BUCKET_NODES, public_key); + DHT_bucket_get_nodes(&dht->bucket_v4, client_data + DHT_BUCKET_NODES, DHT_BUCKET_NODES, public_key); } + } - id_copy(client->public_key, public_key); - ipptp_write->ip_port = ip_port; - ipptp_write->timestamp = unix_time(); + if (is_LAN) { + DHT_bucket_get_nodes(&dht->bucket_lan, client_data + (DHT_BUCKET_NODES * 2), DHT_BUCKET_NODES, public_key); + } - ip_reset(&ipptp_write->ret_ip_port.ip); - ipptp_write->ret_ip_port.port = 0; - ipptp_write->ret_timestamp = 0; + unsigned int i, num_nodes = 0; - /* zero out other address */ - memset(ipptp_clear, 0, sizeof(*ipptp_clear)); + for (i = DHT_BUCKET_NODES * 3; i != 0; --i) { + unsigned int index = i - 1; - return 1; + if (client_data[index].timestamp == 0 || client_in_nodelist(nodes_list, MAX_SENT_NODES, client_data[index].public_key)) + continue; + + if (num_nodes < MAX_SENT_NODES) { + id_copy(nodes_list[num_nodes].public_key, client_data[index].public_key); + nodes_list[num_nodes].ip_port = client_data[index].ip_port; + num_nodes++; + } else { + add_to_list(nodes_list, MAX_SENT_NODES, client_data[index].public_key, client_data[index].ip_port, public_key); + } + } + + return num_nodes; +} + +static DHT_Bucket *ip_bucket(DHT *dht, IP_Port ip_port) +{ + if (LAN_ip(ip_port.ip) == 0) { + return &dht->bucket_lan; + } else if (ip_port.ip.family == AF_INET) { + return &dht->bucket_v4; + } else if (ip_port.ip.family == AF_INET6) { + return &dht->bucket_v6; } return 0; @@ -779,79 +916,24 @@ static int replace_all( Client_data *list, * * simulate is set to 1 if we want to check if a node can be added to the list without adding it. * - * return -1 on failure. - * return 0 on success. + * return 0 on failure. + * return 1 on success. */ -static int add_to_close(DHT *dht, const uint8_t *public_key, IP_Port ip_port, _Bool simulate) +static _Bool add_to_close(DHT *dht, const uint8_t *public_key, IP_Port ip_port, _Bool simulate) { - unsigned int i; + DHT_Bucket *bucket = ip_bucket(dht, ip_port); - unsigned int index = bit_by_bit_cmp(public_key, dht->self_public_key); + if (bucket) + return (DHT_bucket_add_node(bucket, public_key, ip_port, simulate) == 0); - if (index > LCLIENT_LENGTH) - index = LCLIENT_LENGTH - 1; - - for (i = 0; i < LCLIENT_NODES; ++i) { - Client_data *client = &dht->close_clientlist[(index * LCLIENT_NODES) + i]; - - if (is_timeout(client->assoc4.timestamp, BAD_NODE_TIMEOUT) && is_timeout(client->assoc6.timestamp, BAD_NODE_TIMEOUT)) { - if (!simulate) { - IPPTsPng *ipptp_write = NULL; - IPPTsPng *ipptp_clear = NULL; - - if (ip_port.ip.family == AF_INET) { - ipptp_write = &client->assoc4; - ipptp_clear = &client->assoc6; - } else { - ipptp_write = &client->assoc6; - ipptp_clear = &client->assoc4; - } - - id_copy(client->public_key, public_key); - ipptp_write->ip_port = ip_port; - ipptp_write->timestamp = unix_time(); - - ip_reset(&ipptp_write->ret_ip_port.ip); - ipptp_write->ret_ip_port.port = 0; - ipptp_write->ret_timestamp = 0; - - /* zero out other address */ - memset(ipptp_clear, 0, sizeof(*ipptp_clear)); - } - - return 0; - } - } - - return -1; + return 0; } /* 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) { - if (add_to_close(dht, public_key, ip_port, 1) == 0) { - return 1; - } - - return 0; -} - -static _Bool is_pk_in_client_list(Client_data *list, unsigned int client_list_length, const uint8_t *public_key, - IP_Port ip_port) -{ - unsigned int i; - - for (i = 0; i < client_list_length; ++i) { - if ((ip_port.ip.family == AF_INET && !is_timeout(list[i].assoc4.timestamp, BAD_NODE_TIMEOUT)) - || (ip_port.ip.family == AF_INET6 && !is_timeout(list[i].assoc6.timestamp, BAD_NODE_TIMEOUT))) { - if (public_key_cmp(list[i].public_key, public_key) == 0) { - return 1; - } - } - } - - return 0; + return add_to_close(dht, public_key, ip_port, 1); } /* Check if the node obtained with a get_nodes with public_key should be pinged. @@ -864,10 +946,11 @@ static unsigned int ping_node_from_getnodes_ok(DHT *dht, const uint8_t *public_k { _Bool ret = 0; - if (add_to_close(dht, public_key, ip_port, 1) == 0) { + if (add_to_close(dht, public_key, ip_port, 1)) { ret = 1; } + //TODO: make less wasteful. if (ret && !client_in_nodelist(dht->to_bootstrap, dht->num_to_bootstrap, public_key)) { if (dht->num_to_bootstrap < MAX_CLOSE_TO_BOOTSTRAP_NODES) { memcpy(dht->to_bootstrap[dht->num_to_bootstrap].public_key, public_key, crypto_box_PUBLICKEYBYTES); @@ -882,20 +965,9 @@ static unsigned int ping_node_from_getnodes_ok(DHT *dht, const uint8_t *public_k unsigned int i; for (i = 0; i < dht->num_friends; ++i) { - _Bool store_ok = 0; - DHT_Friend *friend = &dht->friends_list[i]; - if (store_node_ok(&friend->client_list[1], public_key, friend->public_key)) { - store_ok = 1; - } - - if (store_node_ok(&friend->client_list[0], public_key, friend->public_key)) { - store_ok = 1; - } - - if (store_ok && !client_in_nodelist(friend->to_bootstrap, friend->num_to_bootstrap, public_key) - && !is_pk_in_client_list(friend->client_list, MAX_FRIEND_CLIENTS, public_key, ip_port)) { + if (!client_in_nodelist(friend->to_bootstrap, friend->num_to_bootstrap, public_key)) { if (friend->num_to_bootstrap < MAX_SENT_NODES) { memcpy(friend->to_bootstrap[friend->num_to_bootstrap].public_key, public_key, crypto_box_PUBLICKEYBYTES); friend->to_bootstrap[friend->num_to_bootstrap].ip_port = ip_port; @@ -918,7 +990,7 @@ static unsigned int ping_node_from_getnodes_ok(DHT *dht, const uint8_t *public_k */ int addto_lists(DHT *dht, IP_Port ip_port, const uint8_t *public_key) { - uint32_t i, used = 0; + unsigned int used = 0; /* convert IPv4-in-IPv6 to IPv4 */ if ((ip_port.ip.family == AF_INET6) && IPV6_IPV4_IN_V6(ip_port.ip.ip6)) { @@ -929,41 +1001,19 @@ int addto_lists(DHT *dht, IP_Port ip_port, const uint8_t *public_key) /* NOTE: Current behavior if there are two clients with the same id is * to replace the first ip by the second. */ - if (!client_or_ip_port_in_list(dht->close_clientlist, LCLIENT_LIST, public_key, ip_port)) { - if (add_to_close(dht, public_key, ip_port, 0)) - used++; - } else + + if (add_to_close(dht, public_key, ip_port, 0)) used++; DHT_Friend *friend_foundip = 0; - for (i = 0; i < dht->num_friends; ++i) { - if (!client_or_ip_port_in_list(dht->friends_list[i].client_list, - MAX_FRIEND_CLIENTS, public_key, ip_port)) { - if (replace_all(dht->friends_list[i].client_list, MAX_FRIEND_CLIENTS, - public_key, ip_port, dht->friends_list[i].public_key)) { + int friend_num = friend_number(dht, public_key); - DHT_Friend *friend = &dht->friends_list[i]; - - if (public_key_cmp(public_key, friend->public_key) == 0) { - friend_foundip = friend; - } - - used++; - } - } else { - DHT_Friend *friend = &dht->friends_list[i]; - - if (public_key_cmp(public_key, friend->public_key) == 0) { - friend_foundip = friend; - } - - used++; - } - } + if (friend_num != -1) + friend_foundip = &dht->friends_list[friend_num]; if (friend_foundip) { - uint32_t j; + unsigned int j; for (j = 0; j < friend_foundip->lock_count; ++j) { if (friend_foundip->callbacks[j].ip_callback) @@ -972,29 +1022,14 @@ int addto_lists(DHT *dht, IP_Port ip_port, const uint8_t *public_key) } } -#ifdef ENABLE_ASSOC_DHT - - if (dht->assoc) { - IPPTs ippts; - - ippts.ip_port = ip_port; - ippts.timestamp = unix_time(); - - Assoc_add_entry(dht->assoc, public_key, &ippts, NULL, used ? 1 : 0); - } - -#endif return used; } /* If public_key is a friend or us, update ret_ip_port * nodepublic_key is the id of the node that sent us this info. */ -static int returnedip_ports(DHT *dht, IP_Port ip_port, const uint8_t *public_key, const uint8_t *nodepublic_key) +static int returnedip_ports(DHT *dht, IP_Port ip_port, const uint8_t *public_key, const uint8_t *node_public_key) { - uint32_t i, j; - uint64_t temp_time = unix_time(); - uint32_t used = 0; /* convert IPv4-in-IPv6 to IPv4 */ @@ -1003,56 +1038,15 @@ static int returnedip_ports(DHT *dht, IP_Port ip_port, const uint8_t *public_key ip_port.ip.ip4.uint32 = ip_port.ip.ip6.uint32[3]; } - if (id_equal(public_key, dht->self_public_key)) { - for (i = 0; i < LCLIENT_LIST; ++i) { - if (id_equal(nodepublic_key, dht->close_clientlist[i].public_key)) { - if (ip_port.ip.family == AF_INET) { - dht->close_clientlist[i].assoc4.ret_ip_port = ip_port; - dht->close_clientlist[i].assoc4.ret_timestamp = temp_time; - } else if (ip_port.ip.family == AF_INET6) { - dht->close_clientlist[i].assoc6.ret_ip_port = ip_port; - dht->close_clientlist[i].assoc6.ret_timestamp = temp_time; - } + /* Only set ret ips for known nodes. */ + if (id_equal(public_key, dht->self_public_key) || friend_number(dht, public_key) != -1) { + DHT_Bucket *bucket = ip_bucket(dht, ip_port); - ++used; - break; - } - } - } else { - for (i = 0; i < dht->num_friends; ++i) { - if (id_equal(public_key, dht->friends_list[i].public_key)) { - for (j = 0; j < MAX_FRIEND_CLIENTS; ++j) { - if (id_equal(nodepublic_key, dht->friends_list[i].client_list[j].public_key)) { - if (ip_port.ip.family == AF_INET) { - dht->friends_list[i].client_list[j].assoc4.ret_ip_port = ip_port; - dht->friends_list[i].client_list[j].assoc4.ret_timestamp = temp_time; - } else if (ip_port.ip.family == AF_INET6) { - dht->friends_list[i].client_list[j].assoc6.ret_ip_port = ip_port; - dht->friends_list[i].client_list[j].assoc6.ret_timestamp = temp_time; - } - - ++used; - goto end; - } - } - } - } + if (bucket) + return DHT_bucket_set_node_ret_ip_port(bucket, node_public_key, public_key, ip_port); } -end: -#ifdef ENABLE_ASSOC_DHT - - if (dht->assoc) { - IPPTs ippts; - ippts.ip_port = ip_port; - ippts.timestamp = temp_time; - /* this is only a hear-say entry, so ret-ipp is NULL, but used is required - * to decide how valuable it is ("used" may throw an "unused" entry out) */ - Assoc_add_entry(dht->assoc, public_key, &ippts, NULL, used ? 1 : 0); - } - -#endif - return 0; + return -1; } /* Send a getnodes request. @@ -1219,10 +1213,6 @@ static uint8_t sent_getnode_to_node(DHT *dht, const uint8_t *public_key, IP_Port return 1; } -/* Function is needed in following functions. */ -static int send_hardening_getnode_res(const DHT *dht, const Node_format *sendto, const uint8_t *queried_client_id, - const uint8_t *nodes_data, uint16_t nodes_data_length); - static int handle_sendnodes_core(void *object, IP_Port source, const uint8_t *packet, uint16_t length, Node_format *plain_nodes, uint16_t size_plain_nodes, uint32_t *num_nodes_out) { @@ -1281,7 +1271,6 @@ static int handle_sendnodes_core(void *object, IP_Port source, const uint8_t *pa *num_nodes_out = num_nodes; - send_hardening_getnode_res(dht, &sendback_node, packet + 1, plain + 1, data_size); return 0; } @@ -1313,6 +1302,34 @@ static int handle_sendnodes_ipv6(void *object, IP_Port source, const uint8_t *pa /*----------------------------------------------------------------------------------*/ /*------------------------END of packet handling functions--------------------------*/ +static int DHT_add_key_all_buckets(DHT *dht, const uint8_t *public_key) +{ + if (DHT_bucket_add_key(&dht->bucket_lan, public_key) == -1) + return -1; + + if (DHT_bucket_add_key(&dht->bucket_v4, public_key) == -1) + return -1; + + if (DHT_bucket_add_key(&dht->bucket_v6, public_key) == -1) + return -1; + + return 0; +} + +static int DHT_rm_key_all_buckets(DHT *dht, const uint8_t *public_key) +{ + if (DHT_bucket_rm_key(&dht->bucket_lan, public_key) == -1) + return -1; + + if (DHT_bucket_rm_key(&dht->bucket_v4, public_key) == -1) + return -1; + + if (DHT_bucket_rm_key(&dht->bucket_v6, public_key) == -1) + return -1; + + return 0; +} + 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) { @@ -1349,7 +1366,11 @@ int DHT_addfriend(DHT *dht, const uint8_t *public_key, void (*ip_callback)(void memset(friend, 0, sizeof(DHT_Friend)); memcpy(friend->public_key, public_key, crypto_box_PUBLICKEYBYTES); - friend->nat.NATping_id = random_64b(); + friend->NATping_id = random_64b(); + + if (DHT_add_key_all_buckets(dht, public_key) == -1) + return -1; + ++dht->num_friends; lock_num = friend->lock_count; @@ -1387,6 +1408,7 @@ int DHT_delfriend(DHT *dht, const uint8_t *public_key, uint16_t lock_count) DHT_Friend *temp; + DHT_rm_key_all_buckets(dht, friend->public_key); --dht->num_friends; if (dht->num_friends != friend_num) { @@ -1410,161 +1432,35 @@ int DHT_delfriend(DHT *dht, const uint8_t *public_key, uint16_t lock_count) return 0; } -/* TODO: Optimize this. */ int DHT_getfriendip(const DHT *dht, const uint8_t *public_key, IP_Port *ip_port) { - uint32_t i, j; + Client_data client_data[DHT_BUCKET_NODES * 3] = {0}; - ip_reset(&ip_port->ip); - ip_port->port = 0; + if (friend_number(dht, public_key) == -1) + return -1; - for (i = 0; i < dht->num_friends; ++i) { - /* Equal */ - if (id_equal(dht->friends_list[i].public_key, public_key)) { - for (j = 0; j < MAX_FRIEND_CLIENTS; ++j) { - Client_data *client = &dht->friends_list[i].client_list[j]; - - if (id_equal(client->public_key, public_key)) { - IPPTsPng *assoc = NULL; - uint32_t a; - - for (a = 0, assoc = &client->assoc6; a < 2; a++, assoc = &client->assoc4) - if (!is_timeout(assoc->timestamp, BAD_NODE_TIMEOUT)) { - *ip_port = assoc->ip_port; - return 1; - } - } - } - - return 0; - } + if (rand() % 2) { + DHT_bucket_get_nodes(&dht->bucket_v4, client_data, DHT_BUCKET_NODES, public_key); + DHT_bucket_get_nodes(&dht->bucket_v6, client_data + DHT_BUCKET_NODES, DHT_BUCKET_NODES, public_key); + } else { + DHT_bucket_get_nodes(&dht->bucket_v6, client_data, DHT_BUCKET_NODES, public_key); + DHT_bucket_get_nodes(&dht->bucket_v4, client_data + DHT_BUCKET_NODES, DHT_BUCKET_NODES, public_key); } - return -1; -} + DHT_bucket_get_nodes(&dht->bucket_lan, client_data + (DHT_BUCKET_NODES * 2), DHT_BUCKET_NODES, public_key); -/* returns number of nodes not in kill-timeout */ -static uint8_t do_ping_and_sendnode_requests(DHT *dht, uint64_t *lastgetnode, const uint8_t *public_key, - Client_data *list, uint32_t list_count, uint32_t *bootstrap_times, _Bool sortable) -{ - uint32_t i; - uint8_t not_kill = 0; - uint64_t temp_time = unix_time(); - - uint32_t num_nodes = 0; - Client_data *client_list[list_count * 2]; - IPPTsPng *assoc_list[list_count * 2]; - unsigned int sort = 0; - _Bool sort_ok = 0; - - for (i = 0; i < list_count; i++) { - /* If node is not dead. */ - Client_data *client = &list[i]; - IPPTsPng *assoc; - uint32_t a; - - for (a = 0, assoc = &client->assoc6; a < 2; a++, assoc = &client->assoc4) - if (!is_timeout(assoc->timestamp, KILL_NODE_TIMEOUT)) { - sort = 0; - not_kill++; - - if (is_timeout(assoc->last_pinged, PING_INTERVAL)) { - getnodes(dht, assoc->ip_port, client->public_key, public_key, NULL); - assoc->last_pinged = temp_time; - } - - /* If node is good. */ - if (!is_timeout(assoc->timestamp, BAD_NODE_TIMEOUT)) { - client_list[num_nodes] = client; - assoc_list[num_nodes] = assoc; - ++num_nodes; - } - } else { - ++sort; - - /* Timed out should be at beginning, if they are not, sort the list. */ - if (sort > 1 && sort < (((i + 1) * 2) - 1)) { - sort_ok = 1; - } - } - } - - if (sortable && sort_ok) { - sort_client_list(list, list_count, public_key); - } - - if ((num_nodes != 0) && (is_timeout(*lastgetnode, GET_NODE_INTERVAL) || *bootstrap_times < MAX_BOOTSTRAP_TIMES)) { - uint32_t rand_node = rand() % (num_nodes); - - if ((num_nodes - 1) != rand_node) { - rand_node += rand() % (num_nodes - (rand_node + 1)); - } - - getnodes(dht, assoc_list[rand_node]->ip_port, client_list[rand_node]->public_key, public_key, NULL); - - *lastgetnode = temp_time; - ++*bootstrap_times; - } - - return not_kill; -} - -/* Ping each client in the "friends" list every PING_INTERVAL seconds. Send a get nodes request - * every GET_NODE_INTERVAL seconds to a random good node for each "friend" in our "friends" list. - */ -static void do_DHT_friends(DHT *dht) -{ - unsigned int i, j; - - for (i = 0; i < dht->num_friends; ++i) { - DHT_Friend *friend = &dht->friends_list[i]; - - for (j = 0; j < friend->num_to_bootstrap; ++j) { - getnodes(dht, friend->to_bootstrap[j].ip_port, friend->to_bootstrap[j].public_key, friend->public_key, NULL); - } - - friend->num_to_bootstrap = 0; - - do_ping_and_sendnode_requests(dht, &friend->lastgetnode, friend->public_key, friend->client_list, MAX_FRIEND_CLIENTS, - &friend->bootstrap_times, 1); - } -} - -/* Ping each client in the close nodes list every PING_INTERVAL seconds. - * Send a get nodes request every GET_NODE_INTERVAL seconds to a random good node in the list. - */ -static void do_Close(DHT *dht) -{ unsigned int i; - for (i = 0; i < dht->num_to_bootstrap; ++i) { - getnodes(dht, dht->to_bootstrap[i].ip_port, dht->to_bootstrap[i].public_key, dht->self_public_key, NULL); - } + for (i = DHT_BUCKET_NODES * 3; i != 0; --i) { + unsigned int index = i - 1; - dht->num_to_bootstrap = 0; - - uint8_t not_killed = do_ping_and_sendnode_requests(dht, &dht->close_lastgetnodes, dht->self_public_key, - dht->close_clientlist, LCLIENT_LIST, &dht->close_bootstrap_times, 0); - - if (!not_killed) { - /* all existing nodes are at least KILL_NODE_TIMEOUT, - * which means we are mute, as we only send packets to - * nodes NOT in KILL_NODE_TIMEOUT - * - * so: reset all nodes to be BAD_NODE_TIMEOUT, but not - * KILL_NODE_TIMEOUT, so we at least keep trying pings */ - uint64_t badonly = unix_time() - BAD_NODE_TIMEOUT; - size_t i, a; - - for (i = 0; i < LCLIENT_LIST; i++) { - Client_data *client = &dht->close_clientlist[i]; - IPPTsPng *assoc; - - for (a = 0, assoc = &client->assoc4; a < 2; a++, assoc = &client->assoc6) - if (assoc->timestamp) - assoc->timestamp = badonly; + if (client_data[index].timestamp != 0 && id_equal(client_data[index].public_key, public_key)) { + *ip_port = client_data[index].ip_port; + return 1; } } + + return 0; } void DHT_getnodes(DHT *dht, const IP_Port *from_ipp, const uint8_t *from_id, const uint8_t *which_id) @@ -1574,18 +1470,9 @@ void DHT_getnodes(DHT *dht, const IP_Port *from_ipp, const uint8_t *from_id, con void DHT_bootstrap(DHT *dht, IP_Port ip_port, const uint8_t *public_key) { - /*#ifdef ENABLE_ASSOC_DHT - if (dht->assoc) { - IPPTs ippts; - ippts.ip_port = ip_port; - ippts.timestamp = 0; - - Assoc_add_entry(dht->assoc, public_key, &ippts, NULL, 0); - } - #endif*/ - getnodes(dht, ip_port, public_key, dht->self_public_key, NULL); } + int DHT_bootstrap_from_address(DHT *dht, const char *address, uint8_t ipv6enabled, uint16_t port, const uint8_t *public_key) { @@ -1621,198 +1508,138 @@ int DHT_bootstrap_from_address(DHT *dht, const char *address, uint8_t ipv6enable */ int route_packet(const DHT *dht, const uint8_t *public_key, const uint8_t *packet, uint16_t length) { - uint32_t i; + Client_data client_data[DHT_BUCKET_NODES * 3] = {0}; - for (i = 0; i < LCLIENT_LIST; ++i) { - if (id_equal(public_key, dht->close_clientlist[i].public_key)) { - const Client_data *client = &dht->close_clientlist[i]; + if (friend_number(dht, public_key) == -1) + return -1; - if (ip_isset(&client->assoc6.ip_port.ip)) - return sendpacket(dht->net, client->assoc6.ip_port, packet, length); - else if (ip_isset(&client->assoc4.ip_port.ip)) - return sendpacket(dht->net, client->assoc4.ip_port, packet, length); - else - break; + DHT_bucket_get_nodes(&dht->bucket_v4, client_data, DHT_BUCKET_NODES, public_key); + DHT_bucket_get_nodes(&dht->bucket_v6, client_data + DHT_BUCKET_NODES, DHT_BUCKET_NODES, public_key); + DHT_bucket_get_nodes(&dht->bucket_lan, client_data + (DHT_BUCKET_NODES * 2), DHT_BUCKET_NODES, public_key); + + unsigned int i; + + for (i = DHT_BUCKET_NODES * 3; i != 0; --i) { + unsigned int index = i - 1; + + if (client_data[index].timestamp != 0 && id_equal(client_data[index].public_key, public_key)) { + return sendpacket(dht->net, client_data[index].ip_port, packet, length); } } return -1; } +static _Bool get_ret_ip_port(Client_data *client, const uint8_t *public_key, IP_Port *ip_port) +{ + unsigned int i; + + for (i = 0; i < DHT_BUCKET_NODES; ++i) { + if (client->ret[i].timestamp != 0 && !is_timeout(client->ret[i].timestamp, BAD_NODE_TIMEOUT * 2) + && id_equal(client->ret[i].pk, public_key)) { + *ip_port = client->ret[i].ip_port; + return 1; + } + } + + return 0; +} + /* Puts all the different ips returned by the nodes for a friend_num into array ip_portlist. - * ip_portlist must be at least MAX_FRIEND_CLIENTS big. + * ip_portlist must be at least (DHT_BUCKET_NODES * 2) big. * * return the number of ips returned. * return 0 if we are connected to friend or if no ips were found. * return -1 if no such friend. */ -static int friend_iplist(const DHT *dht, IP_Port *ip_portlist, uint16_t friend_num) +static int friend_iplist(const DHT *dht, IP_Port *ip_portlist, uint16_t friend_num, _Bool v6) { if (friend_num >= dht->num_friends) return -1; DHT_Friend *friend = &dht->friends_list[friend_num]; - Client_data *client; - IP_Port ipv4s[MAX_FRIEND_CLIENTS]; - int num_ipv4s = 0; - IP_Port ipv6s[MAX_FRIEND_CLIENTS]; - int num_ipv6s = 0; - int i; - for (i = 0; i < MAX_FRIEND_CLIENTS; ++i) { - client = &(friend->client_list[i]); + Client_data client_data[(DHT_BUCKET_NODES * 2)] = {0}; - /* If ip is not zero and node is good. */ - if (ip_isset(&client->assoc4.ret_ip_port.ip) && !is_timeout(client->assoc4.ret_timestamp, BAD_NODE_TIMEOUT)) { - ipv4s[num_ipv4s] = client->assoc4.ret_ip_port; - ++num_ipv4s; - } + const DHT_Bucket *bucket; - if (ip_isset(&client->assoc6.ret_ip_port.ip) && !is_timeout(client->assoc6.ret_timestamp, BAD_NODE_TIMEOUT)) { - ipv6s[num_ipv6s] = client->assoc6.ret_ip_port; - ++num_ipv6s; - } - - if (id_equal(client->public_key, friend->public_key)) - if (!is_timeout(client->assoc6.timestamp, BAD_NODE_TIMEOUT) || !is_timeout(client->assoc4.timestamp, BAD_NODE_TIMEOUT)) - return 0; /* direct connectivity */ + if (v6) { + bucket = &dht->bucket_v6; + } else { + bucket = &dht->bucket_v4; } -#ifdef FRIEND_IPLIST_PAD - memcpy(ip_portlist, ipv6s, num_ipv6s * sizeof(IP_Port)); + DHT_bucket_get_nodes(bucket, client_data, (DHT_BUCKET_NODES * 2), friend->public_key); - if (num_ipv6s == MAX_FRIEND_CLIENTS) - return MAX_FRIEND_CLIENTS; + unsigned int i, j, count = 0; + IP_Port ip_ports[(DHT_BUCKET_NODES * 2)]; - int num_ipv4s_used = MAX_FRIEND_CLIENTS - num_ipv6s; + for (i = (DHT_BUCKET_NODES * 2); i != 0; --i) { + unsigned int index = i - 1; - if (num_ipv4s_used > num_ipv4s) - num_ipv4s_used = num_ipv4s; + if (client_data[index].timestamp != 0) { + if (id_equal(client_data[index].public_key, friend->public_key)) + return 0; - memcpy(&ip_portlist[num_ipv6s], ipv4s, num_ipv4s_used * sizeof(IP_Port)); - return num_ipv6s + num_ipv4s_used; - -#else /* !FRIEND_IPLIST_PAD */ - - /* there must be some secret reason why we can't pad the longer list - * with the shorter one... - */ - if (num_ipv6s >= num_ipv4s) { - memcpy(ip_portlist, ipv6s, num_ipv6s * sizeof(IP_Port)); - return num_ipv6s; + if (get_ret_ip_port(&client_data[index], friend->public_key, &ip_ports[count])) + ++count; + } } - memcpy(ip_portlist, ipv4s, num_ipv4s * sizeof(IP_Port)); - return num_ipv4s; - -#endif /* !FRIEND_IPLIST_PAD */ + memcpy(ip_portlist, ip_ports, sizeof(IP_Port) * count); + return count; } -/* Send the following packet to everyone who tells us they are connected to friend_id. - * - * return ip for friend. - * return number of nodes the packet was sent to. (Only works if more than (MAX_FRIEND_CLIENTS / 4). - */ -int route_tofriend(const DHT *dht, const uint8_t *friend_id, const uint8_t *packet, uint16_t length) -{ - int num = friend_number(dht, friend_id); - - if (num == -1) - return 0; - - uint32_t i, sent = 0; - uint8_t friend_sent[MAX_FRIEND_CLIENTS] = {0}; - - IP_Port ip_list[MAX_FRIEND_CLIENTS]; - int ip_num = friend_iplist(dht, ip_list, num); - - if (ip_num < (MAX_FRIEND_CLIENTS / 4)) - return 0; /* Reason for that? */ - - DHT_Friend *friend = &dht->friends_list[num]; - Client_data *client; - - /* extra legwork, because having the outside allocating the space for us - * is *usually* good(tm) (bites us in the behind in this case though) */ - uint32_t a; - - for (a = 0; a < 2; a++) - for (i = 0; i < MAX_FRIEND_CLIENTS; ++i) { - if (friend_sent[i])/* Send one packet per client.*/ - continue; - - client = &friend->client_list[i]; - IPPTsPng *assoc = NULL; - - if (!a) - assoc = &client->assoc4; - else - assoc = &client->assoc6; - - /* If ip is not zero and node is good. */ - if (ip_isset(&assoc->ret_ip_port.ip) && - !is_timeout(assoc->ret_timestamp, BAD_NODE_TIMEOUT)) { - int retval = sendpacket(dht->net, assoc->ip_port, packet, length); - - if ((unsigned int)retval == length) { - ++sent; - friend_sent[i] = 1; - } - } - } - - return sent; -} - -/* Send the following packet to one random person who tells us they are connected to friend_id. +/* Send the following packet to X nodes who tells us they are connected to friend_pk. * * return number of nodes the packet was sent to. */ -static int routeone_tofriend(DHT *dht, const uint8_t *friend_id, const uint8_t *packet, uint16_t length) +int route_tofriend(const DHT *dht, const uint8_t *friend_pk, const uint8_t *packet, uint16_t length, + unsigned int num_to_send) { - int num = friend_number(dht, friend_id); + Client_data client_data[(DHT_BUCKET_NODES * 2) * 3] = {0}; - if (num == -1) + if (num_to_send >= (DHT_BUCKET_NODES * 2)) return 0; - DHT_Friend *friend = &dht->friends_list[num]; - Client_data *client; + if (friend_number(dht, friend_pk) == -1) + return 0; - IP_Port ip_list[MAX_FRIEND_CLIENTS * 2]; - int n = 0; - uint32_t i; + DHT_bucket_get_nodes(&dht->bucket_v4, client_data, (DHT_BUCKET_NODES * 2), friend_pk); + DHT_bucket_get_nodes(&dht->bucket_v6, client_data + (DHT_BUCKET_NODES * 2), (DHT_BUCKET_NODES * 2), friend_pk); + DHT_bucket_get_nodes(&dht->bucket_lan, client_data + ((DHT_BUCKET_NODES * 2) * 2), (DHT_BUCKET_NODES * 2), friend_pk); - /* extra legwork, because having the outside allocating the space for us - * is *usually* good(tm) (bites us in the behind in this case though) */ - uint32_t a; + unsigned int i, j, count = 0, r = rand(); - for (a = 0; a < 2; a++) - for (i = 0; i < MAX_FRIEND_CLIENTS; ++i) { - client = &friend->client_list[i]; - IPPTsPng *assoc = NULL; + IP_Port ip_ports[num_to_send]; - if (!a) - assoc = &client->assoc4; - else - assoc = &client->assoc6; + for (i = (DHT_BUCKET_NODES * 2) * 3; i != 0; --i) { + unsigned int index = (i + r) % ((DHT_BUCKET_NODES * 2) * 3); - /* If ip is not zero and node is good. */ - if (ip_isset(&assoc->ret_ip_port.ip) && !is_timeout(assoc->ret_timestamp, BAD_NODE_TIMEOUT)) { - ip_list[n] = assoc->ip_port; - ++n; - } + if (count == num_to_send) + break; + + if (client_data[index].timestamp != 0) { + if (get_ret_ip_port(&client_data[index], friend_pk, &ip_ports[count])) + ++count; } + } - if (n < 1) + if (count != num_to_send) return 0; - int retval = sendpacket(dht->net, ip_list[rand() % n], packet, length); + unsigned int sent = 0; - if ((unsigned int)retval == length) - return 1; + for (i = 0; i < count; ++i) { + int retval = sendpacket(dht->net, ip_ports[i], packet, length); - return 0; + if ((unsigned int)retval == length) { + ++sent; + } + } + + return sent; } /*----------------------------------------------------------------------------------*/ @@ -1834,10 +1661,10 @@ static int send_NATping(DHT *dht, const uint8_t *public_key, uint64_t ping_id, u if (len == -1) return -1; - if (type == 0) /* If packet is request use many people to route it. */ - num = route_tofriend(dht, public_key, packet, len); - else if (type == 1) /* If packet is response use only one person to route it */ - num = routeone_tofriend(dht, public_key, packet, len); + if (type == NAT_PING_REQUEST) /* If packet is request use many people to route it. */ + num = route_tofriend(dht, public_key, packet, len, DHT_BUCKET_NODES / 2); + else if (type == NAT_PING_RESPONSE) /* If packet is response use only one person to route it */ + num = route_tofriend(dht, public_key, packet, len, 1); if (num == 0) return -1; @@ -1866,12 +1693,13 @@ static int handle_NATping(void *object, IP_Port source, const uint8_t *source_pu if (packet[0] == NAT_PING_REQUEST) { /* 1 is reply */ send_NATping(dht, source_pubkey, ping_id, NAT_PING_RESPONSE); - friend->nat.recvNATping_timestamp = unix_time(); + friend->recvNATping_timestamp = unix_time(); return 0; } else if (packet[0] == NAT_PING_RESPONSE) { - if (friend->nat.NATping_id == ping_id) { - friend->nat.NATping_id = random_64b(); - friend->nat.hole_punching = 1; + if (friend->NATping_id == ping_id) { + friend->NATping_id = random_64b(); + friend->nat_v4.hole_punching = 1; + friend->nat_v6.hole_punching = 1; return 0; } } @@ -1930,13 +1758,23 @@ static uint16_t NAT_getports(uint16_t *portlist, IP_Port *ip_portlist, uint16_t return num; } -static void punch_holes(DHT *dht, IP ip, uint16_t *port_list, uint16_t numports, uint16_t friend_num) +static void punch_holes(DHT *dht, IP ip, uint16_t *port_list, uint16_t numports, uint16_t friendnumber, _Bool v6) { if (numports > MAX_FRIEND_CLIENTS || numports == 0) return; - uint32_t i; - uint32_t top = dht->friends_list[friend_num].nat.punching_index + MAX_PUNCHING_PORTS; + DHT_Friend *friend = &dht->friends_list[friendnumber]; + + NAT *nat; + + if (v6) { + nat = &friend->nat_v6; + } else { + nat = &friend->nat_v4; + } + + unsigned int i; + unsigned int top = nat->punching_index + MAX_PUNCHING_PORTS; uint16_t firstport = port_list[0]; for (i = 0; i < numports; ++i) { @@ -1948,58 +1786,69 @@ static void punch_holes(DHT *dht, IP ip, uint16_t *port_list, uint16_t numports, IP_Port pinging; ip_copy(&pinging.ip, &ip); pinging.port = htons(firstport); - send_ping_request(dht->ping, pinging, dht->friends_list[friend_num].public_key); + send_ping_request(dht->ping, pinging, friend->public_key); } else { - for (i = dht->friends_list[friend_num].nat.punching_index; i != top; ++i) { + for (i = nat->punching_index; i != top; ++i) { /* TODO: Improve port guessing algorithm. */ uint16_t port = port_list[(i / 2) % numports] + (i / (2 * numports)) * ((i % 2) ? -1 : 1); IP_Port pinging; ip_copy(&pinging.ip, &ip); pinging.port = htons(port); - send_ping_request(dht->ping, pinging, dht->friends_list[friend_num].public_key); + send_ping_request(dht->ping, pinging, friend->public_key); } - dht->friends_list[friend_num].nat.punching_index = i; + nat->punching_index = i; } - if (dht->friends_list[friend_num].nat.tries > MAX_NORMAL_PUNCHING_TRIES) { - top = dht->friends_list[friend_num].nat.punching_index2 + MAX_PUNCHING_PORTS; + if (nat->tries > MAX_NORMAL_PUNCHING_TRIES) { + top = nat->punching_index2 + MAX_PUNCHING_PORTS; uint16_t port = 1024; IP_Port pinging; ip_copy(&pinging.ip, &ip); - for (i = dht->friends_list[friend_num].nat.punching_index2; i != top; ++i) { + for (i = nat->punching_index2; i != top; ++i) { pinging.port = htons(port + i); - send_ping_request(dht->ping, pinging, dht->friends_list[friend_num].public_key); + send_ping_request(dht->ping, pinging, friend->public_key); } - dht->friends_list[friend_num].nat.punching_index2 = i - (MAX_PUNCHING_PORTS / 2); + nat->punching_index2 = i - (MAX_PUNCHING_PORTS / 2); } - ++dht->friends_list[friend_num].nat.tries; + ++nat->tries; } -static void do_NAT(DHT *dht) +static void do_NAT(DHT *dht, _Bool v6) { + uint32_t i; uint64_t temp_time = unix_time(); for (i = 0; i < dht->num_friends; ++i) { IP_Port ip_list[MAX_FRIEND_CLIENTS]; - int num = friend_iplist(dht, ip_list, i); + int num = friend_iplist(dht, ip_list, i, v6); /* If already connected or friend is not online don't try to hole punch. */ if (num < MAX_FRIEND_CLIENTS / 2) continue; - if (dht->friends_list[i].nat.NATping_timestamp + PUNCH_INTERVAL < temp_time) { - send_NATping(dht, dht->friends_list[i].public_key, dht->friends_list[i].nat.NATping_id, NAT_PING_REQUEST); - dht->friends_list[i].nat.NATping_timestamp = temp_time; + DHT_Friend *friend = &dht->friends_list[i]; + + NAT *nat; + + if (v6) { + nat = &friend->nat_v6; + } else { + nat = &friend->nat_v4; } - if (dht->friends_list[i].nat.hole_punching == 1 && - dht->friends_list[i].nat.punching_timestamp + PUNCH_INTERVAL < temp_time && - dht->friends_list[i].nat.recvNATping_timestamp + PUNCH_INTERVAL * 2 >= temp_time) { + if (friend->NATping_timestamp + PUNCH_INTERVAL < temp_time) { + send_NATping(dht, friend->public_key, friend->NATping_id, NAT_PING_REQUEST); + friend->NATping_timestamp = temp_time; + } + + if (nat->hole_punching == 1 && + nat->punching_timestamp + PUNCH_INTERVAL < temp_time && + friend->recvNATping_timestamp + PUNCH_INTERVAL * 2 >= temp_time) { IP ip = NAT_commonip(ip_list, num, MAX_FRIEND_CLIENTS / 2); @@ -2008,10 +1857,10 @@ static void do_NAT(DHT *dht) uint16_t port_list[MAX_FRIEND_CLIENTS]; uint16_t numports = NAT_getports(port_list, ip_list, num, ip); - punch_holes(dht, ip, port_list, numports, i); + punch_holes(dht, ip, port_list, numports, i, v6); - dht->friends_list[i].nat.punching_timestamp = temp_time; - dht->friends_list[i].nat.hole_punching = 0; + nat->punching_timestamp = temp_time; + nat->hole_punching = 0; } } } @@ -2019,177 +1868,6 @@ static void do_NAT(DHT *dht) /*----------------------------------------------------------------------------------*/ /*-----------------------END OF NAT PUNCHING FUNCTIONS------------------------------*/ -#define HARDREQ_DATA_SIZE 384 /* Attempt to prevent amplification/other attacks*/ - -#define CHECK_TYPE_ROUTE_REQ 0 -#define CHECK_TYPE_ROUTE_RES 1 -#define CHECK_TYPE_GETNODE_REQ 2 -#define CHECK_TYPE_GETNODE_RES 3 -#define CHECK_TYPE_TEST_REQ 4 -#define CHECK_TYPE_TEST_RES 5 - -static int send_hardening_req(DHT *dht, Node_format *sendto, uint8_t type, uint8_t *contents, uint16_t length) -{ - if (length > HARDREQ_DATA_SIZE - 1) - return -1; - - uint8_t packet[MAX_CRYPTO_REQUEST_SIZE]; - uint8_t data[HARDREQ_DATA_SIZE] = {0}; - data[0] = type; - memcpy(data + 1, contents, length); - int len = create_request(dht->self_public_key, dht->self_secret_key, packet, sendto->public_key, data, - sizeof(data), CRYPTO_PACKET_HARDENING); - - if (len == -1) - return -1; - - return sendpacket(dht->net, sendto->ip_port, packet, len); -} - -/* Send a get node hardening request */ -static int send_hardening_getnode_req(DHT *dht, Node_format *dest, Node_format *node_totest, uint8_t *search_id) -{ - uint8_t data[sizeof(Node_format) + crypto_box_PUBLICKEYBYTES]; - memcpy(data, node_totest, sizeof(Node_format)); - memcpy(data + sizeof(Node_format), search_id, crypto_box_PUBLICKEYBYTES); - return send_hardening_req(dht, dest, CHECK_TYPE_GETNODE_REQ, data, sizeof(Node_format) + crypto_box_PUBLICKEYBYTES); -} - -/* Send a get node hardening response */ -static int send_hardening_getnode_res(const DHT *dht, const Node_format *sendto, const uint8_t *queried_client_id, - const uint8_t *nodes_data, uint16_t nodes_data_length) -{ - if (!ip_isset(&sendto->ip_port.ip)) - return -1; - - uint8_t packet[MAX_CRYPTO_REQUEST_SIZE]; - uint8_t data[1 + crypto_box_PUBLICKEYBYTES + nodes_data_length]; - data[0] = CHECK_TYPE_GETNODE_RES; - memcpy(data + 1, queried_client_id, crypto_box_PUBLICKEYBYTES); - memcpy(data + 1 + crypto_box_PUBLICKEYBYTES, nodes_data, nodes_data_length); - int len = create_request(dht->self_public_key, dht->self_secret_key, packet, sendto->public_key, data, - sizeof(data), CRYPTO_PACKET_HARDENING); - - if (len == -1) - return -1; - - return sendpacket(dht->net, sendto->ip_port, packet, len); -} - -/* TODO: improve */ -static IPPTsPng *get_closelist_IPPTsPng(DHT *dht, const uint8_t *public_key, sa_family_t sa_family) -{ - uint32_t i; - - for (i = 0; i < LCLIENT_LIST; ++i) { - if (public_key_cmp(dht->close_clientlist[i].public_key, public_key) != 0) - continue; - - if (sa_family == AF_INET) - return &dht->close_clientlist[i].assoc4; - else if (sa_family == AF_INET6) - return &dht->close_clientlist[i].assoc6; - } - - return NULL; -} - -/* - * check how many nodes in nodes are also present in the closelist. - * TODO: make this function better. - */ -static uint32_t have_nodes_closelist(DHT *dht, Node_format *nodes, uint16_t num) -{ - uint32_t counter = 0; - uint32_t i; - - for (i = 0; i < num; ++i) { - if (id_equal(nodes[i].public_key, dht->self_public_key)) { - ++counter; - continue; - } - - IPPTsPng *temp = get_closelist_IPPTsPng(dht, nodes[i].public_key, nodes[i].ip_port.ip.family); - - if (temp) { - if (!is_timeout(temp->timestamp, BAD_NODE_TIMEOUT)) { - ++counter; - } - } - } - - return counter; -} - -/* Interval in seconds between hardening checks */ -#define HARDENING_INTERVAL 120 -#define HARDEN_TIMEOUT 1200 - -/* Handle a received hardening packet */ -static int handle_hardening(void *object, IP_Port source, const uint8_t *source_pubkey, const uint8_t *packet, - uint16_t length) -{ - DHT *dht = object; - - if (length < 2) { - return 1; - } - - switch (packet[0]) { - case CHECK_TYPE_GETNODE_REQ: { - if (length != HARDREQ_DATA_SIZE) - return 1; - - Node_format node, tocheck_node; - node.ip_port = source; - memcpy(node.public_key, source_pubkey, crypto_box_PUBLICKEYBYTES); - memcpy(&tocheck_node, packet + 1, sizeof(Node_format)); - - if (getnodes(dht, tocheck_node.ip_port, tocheck_node.public_key, packet + 1 + sizeof(Node_format), &node) == -1) - return 1; - - return 0; - } - - case CHECK_TYPE_GETNODE_RES: { - if (length <= crypto_box_PUBLICKEYBYTES + 1) - return 1; - - if (length > 1 + crypto_box_PUBLICKEYBYTES + sizeof(Node_format) * MAX_SENT_NODES) - return 1; - - uint16_t length_nodes = length - 1 - crypto_box_PUBLICKEYBYTES; - Node_format nodes[MAX_SENT_NODES]; - int num_nodes = unpack_nodes(nodes, MAX_SENT_NODES, 0, packet + 1 + crypto_box_PUBLICKEYBYTES, length_nodes, 0); - - /* TODO: MAX_SENT_NODES nodes should be returned at all times - (right now we have a small network size so it could cause problems for testing and etc..) */ - if (num_nodes <= 0) - return 1; - - /* NOTE: This should work for now but should be changed to something better. */ - if (have_nodes_closelist(dht, nodes, num_nodes) < (uint32_t)((num_nodes + 2) / 2)) - return 1; - - IPPTsPng *temp = get_closelist_IPPTsPng(dht, packet + 1, nodes[0].ip_port.ip.family); - - if (temp == NULL) - return 1; - - if (is_timeout(temp->hardening.send_nodes_timestamp, HARDENING_INTERVAL)) - return 1; - - if (public_key_cmp(temp->hardening.send_nodes_pingedid, source_pubkey) != 0) - return 1; - - /* If Nodes look good and the request checks out */ - temp->hardening.send_nodes_ok = 1; - return 0;/* success*/ - } - } - - return 1; -} /* Return a random node from all the nodes we are connected to. * TODO: improve this function. @@ -2218,46 +1896,41 @@ Node_format random_node(DHT *dht, sa_family_t sa_family) * * return the number of nodes. */ -uint16_t list_nodes(Client_data *list, unsigned int length, Node_format *nodes, uint16_t max_num) +static uint16_t list_nodes(const DHT *dht, uint8_t *public_key, Node_format *nodes, uint16_t max_num) { if (max_num == 0) return 0; - uint16_t count = 0; + Client_data client_data[DHT_BUCKET_NODES * 2] = {0}; - unsigned int i; + if (rand() % 2) { + DHT_bucket_get_nodes(&dht->bucket_v4, client_data, DHT_BUCKET_NODES, public_key); + DHT_bucket_get_nodes(&dht->bucket_v6, client_data + DHT_BUCKET_NODES, DHT_BUCKET_NODES, public_key); + } else { + DHT_bucket_get_nodes(&dht->bucket_v6, client_data, DHT_BUCKET_NODES, public_key); + DHT_bucket_get_nodes(&dht->bucket_v4, client_data + DHT_BUCKET_NODES, DHT_BUCKET_NODES, public_key); + } - for (i = length; i != 0; --i) { - IPPTsPng *assoc = NULL; + unsigned int i, num_nodes = 0, r = rand(); - if (!is_timeout(list[i - 1].assoc4.timestamp, BAD_NODE_TIMEOUT)) - assoc = &list[i - 1].assoc4; + for (i = DHT_BUCKET_NODES * 2; i != 0 && (num_nodes < max_num); --i) { + unsigned int index = (i + rand()) % (DHT_BUCKET_NODES * 2); - if (!is_timeout(list[i - 1].assoc6.timestamp, BAD_NODE_TIMEOUT)) { - if (assoc == NULL) - assoc = &list[i - 1].assoc6; - else if (rand() % 2) - assoc = &list[i - 1].assoc6; - } - - if (assoc != NULL) { - memcpy(nodes[count].public_key, list[i - 1].public_key, crypto_box_PUBLICKEYBYTES); - nodes[count].ip_port = assoc->ip_port; - ++count; - - if (count >= max_num) - return count; + if (client_data[index].timestamp != 0) { + id_copy(nodes[num_nodes].public_key, client_data[index].public_key); + nodes[num_nodes].ip_port = client_data[index].ip_port; + ++num_nodes; } } - return count; + return num_nodes; } /* 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) +uint16_t randfriends_nodes(const DHT *dht, Node_format *nodes, uint16_t max_num) { if (max_num == 0) return 0; @@ -2266,7 +1939,7 @@ uint16_t randfriends_nodes(DHT *dht, Node_format *nodes, uint16_t max_num) unsigned int i, r = rand(); for (i = 0; i < DHT_FAKE_FRIEND_NUMBER; ++i) { - count += list_nodes(dht->friends_list[(i + r) % DHT_FAKE_FRIEND_NUMBER].client_list, MAX_FRIEND_CLIENTS, nodes + count, + count += list_nodes(dht, dht->friends_list[(i + r) % DHT_FAKE_FRIEND_NUMBER].public_key, nodes + count, max_num - count); if (count >= max_num) @@ -2282,57 +1955,7 @@ uint16_t randfriends_nodes(DHT *dht, Node_format *nodes, uint16_t max_num) */ uint16_t closelist_nodes(DHT *dht, Node_format *nodes, uint16_t max_num) { - return list_nodes(dht->close_clientlist, LCLIENT_LIST, nodes, max_num); -} - -void do_hardening(DHT *dht) -{ - uint32_t i; - - for (i = 0; i < LCLIENT_LIST * 2; ++i) { - IPPTsPng *cur_iptspng; - sa_family_t sa_family; - uint8_t *public_key = dht->close_clientlist[i / 2].public_key; - - if (i % 2 == 0) { - cur_iptspng = &dht->close_clientlist[i / 2].assoc4; - sa_family = AF_INET; - } else { - cur_iptspng = &dht->close_clientlist[i / 2].assoc6; - sa_family = AF_INET6; - } - - if (is_timeout(cur_iptspng->timestamp, BAD_NODE_TIMEOUT)) - continue; - - if (cur_iptspng->hardening.send_nodes_ok == 0) { - if (is_timeout(cur_iptspng->hardening.send_nodes_timestamp, HARDENING_INTERVAL)) { - Node_format rand_node = random_node(dht, sa_family); - - if (!ipport_isset(&rand_node.ip_port)) - continue; - - if (id_equal(public_key, rand_node.public_key)) - continue; - - Node_format to_test; - to_test.ip_port = cur_iptspng->ip_port; - memcpy(to_test.public_key, public_key, crypto_box_PUBLICKEYBYTES); - - //TODO: The search id should maybe not be ours? - if (send_hardening_getnode_req(dht, &rand_node, &to_test, dht->self_public_key) > 0) { - memcpy(cur_iptspng->hardening.send_nodes_pingedid, rand_node.public_key, crypto_box_PUBLICKEYBYTES); - cur_iptspng->hardening.send_nodes_timestamp = unix_time(); - } - } - } else { - if (is_timeout(cur_iptspng->hardening.send_nodes_timestamp, HARDEN_TIMEOUT)) { - cur_iptspng->hardening.send_nodes_ok = 0; - } - } - - //TODO: add the 2 other testers. - } + return list_nodes(dht, dht->self_public_key, nodes, max_num); } /*----------------------------------------------------------------------------------*/ @@ -2377,6 +2000,55 @@ static int cryptopacket_handle(void *object, IP_Port source, const uint8_t *pack return 1; } +static void do_DHT_pings(DHT *dht) +{ + do_ping_nodes(dht, &dht->bucket_lan); + do_ping_nodes(dht, &dht->bucket_v4); + do_ping_nodes(dht, &dht->bucket_v6); +} + + +/* Ping each client in the "friends" list every PING_INTERVAL seconds. Send a get nodes request + * every GET_NODE_INTERVAL seconds to a random good node for each "friend" in our "friends" list. + */ +static void do_DHT_friends(DHT *dht) +{ + unsigned int i, j; + + for (i = 0; i < dht->num_friends; ++i) { + DHT_Friend *friend = &dht->friends_list[i]; + + for (j = 0; j < friend->num_to_bootstrap; ++j) { + getnodes(dht, friend->to_bootstrap[j].ip_port, friend->to_bootstrap[j].public_key, friend->public_key, NULL); + } + + friend->num_to_bootstrap = 0; + } +} + +/* Ping each client in the close nodes list every PING_INTERVAL seconds. + * Send a get nodes request every GET_NODE_INTERVAL seconds to a random good node in the list. + */ +static void do_Close(DHT *dht) +{ + unsigned int i; + + for (i = 0; i < dht->num_to_bootstrap; ++i) { + getnodes(dht, dht->to_bootstrap[i].ip_port, dht->to_bootstrap[i].public_key, dht->self_public_key, NULL); + } + + dht->num_to_bootstrap = 0; + + Node_format node; + + if (is_timeout(dht->close_lastgetnodes, GET_NODE_INTERVAL)) { + if (closelist_nodes(dht, &node, 1) == 1) { + getnodes(dht, node.ip_port, node.public_key, dht->self_public_key, NULL); + dht->close_lastgetnodes = unix_time(); + } + } +} + /*----------------------------------------------------------------------------------*/ DHT *new_DHT(Networking_Core *net) @@ -2404,17 +2076,19 @@ DHT *new_DHT(Networking_Core *net) networking_registerhandler(dht->net, NET_PACKET_SEND_NODES_IPV6, &handle_sendnodes_ipv6, dht); networking_registerhandler(dht->net, NET_PACKET_CRYPTO, &cryptopacket_handle, dht); cryptopacket_registerhandler(dht, CRYPTO_PACKET_NAT_PING, &handle_NATping, dht); - cryptopacket_registerhandler(dht, CRYPTO_PACKET_HARDENING, &handle_hardening, dht); new_symmetric_key(dht->secret_symmetric_key); crypto_box_keypair(dht->self_public_key, dht->self_secret_key); ping_array_init(&dht->dht_ping_array, DHT_PING_ARRAY_SIZE, PING_TIMEOUT); ping_array_init(&dht->dht_harden_ping_array, DHT_PING_ARRAY_SIZE, PING_TIMEOUT); -#ifdef ENABLE_ASSOC_DHT - dht->assoc = new_Assoc_default(dht->self_public_key); -#endif - uint32_t i; + + if (DHT_add_key_all_buckets(dht, dht->self_public_key) == -1) { + kill_DHT(dht); + return NULL; + } + + unsigned int i; for (i = 0; i < DHT_FAKE_FRIEND_NUMBER; ++i) { uint8_t random_key_bytes[crypto_box_PUBLICKEYBYTES]; @@ -2442,28 +2116,23 @@ void do_DHT(DHT *dht) DHT_connect_after_load(dht); } + do_DHT_pings(dht); do_Close(dht); do_DHT_friends(dht); - do_NAT(dht); + do_NAT(dht, 0); + do_NAT(dht, 1); do_to_ping(dht->ping); - //do_hardening(dht); -#ifdef ENABLE_ASSOC_DHT - - if (dht->assoc) - do_Assoc(dht->assoc, dht); - -#endif dht->last_run = unix_time(); } + void kill_DHT(DHT *dht) { -#ifdef ENABLE_ASSOC_DHT - kill_Assoc(dht->assoc); -#endif + free_buckets(&dht->bucket_v4); + free_buckets(&dht->bucket_v6); + free_buckets(&dht->bucket_lan); networking_registerhandler(dht->net, NET_PACKET_GET_NODES, NULL, NULL); networking_registerhandler(dht->net, NET_PACKET_SEND_NODES_IPV6, NULL, NULL); cryptopacket_registerhandler(dht, CRYPTO_PACKET_NAT_PING, NULL, NULL); - cryptopacket_registerhandler(dht, CRYPTO_PACKET_HARDENING, NULL, NULL); ping_array_free_all(&dht->dht_ping_array); ping_array_free_all(&dht->dht_harden_ping_array); kill_ping(dht->ping); @@ -2479,30 +2148,21 @@ void kill_DHT(DHT *dht) #define DHT_STATE_COOKIE_TYPE 0x11ce #define DHT_STATE_TYPE_NODES 4 -#define MAX_SAVED_DHT_NODES (((DHT_FAKE_FRIEND_NUMBER * MAX_FRIEND_CLIENTS) + LCLIENT_LIST) * 2) +#define MAX_SAVED_V4_DHT_NODES (DHT_BUCKET_NODES * DHT_FAKE_FRIEND_NUMBER) +#define MAX_SAVED_V6_DHT_NODES (DHT_BUCKET_NODES * DHT_FAKE_FRIEND_NUMBER) + /* Get the size of the DHT (for saving). */ uint32_t DHT_size(const DHT *dht) { - uint32_t numv4 = 0, numv6 = 0, i, j; - - for (i = 0; i < LCLIENT_LIST; ++i) { - numv4 += (dht->close_clientlist[i].assoc4.timestamp != 0); - numv6 += (dht->close_clientlist[i].assoc6.timestamp != 0); - } - - for (i = 0; i < DHT_FAKE_FRIEND_NUMBER && i < dht->num_friends; ++i) { - DHT_Friend *fr = &dht->friends_list[i]; - - for (j = 0; j < MAX_FRIEND_CLIENTS; ++j) { - numv4 += (fr->client_list[j].assoc4.timestamp != 0); - numv6 += (fr->client_list[j].assoc6.timestamp != 0); - } - } + uint8_t data[(packed_node_size(AF_INET) * MAX_SAVED_V4_DHT_NODES) + (packed_node_size( + AF_INET6) * MAX_SAVED_V6_DHT_NODES)]; uint32_t size32 = sizeof(uint32_t), sizesubhead = size32 * 2; + Node_format clients[MAX_SAVED_V4_DHT_NODES + MAX_SAVED_V6_DHT_NODES]; + unsigned int num = randfriends_nodes(dht, clients, MAX_SAVED_V4_DHT_NODES + MAX_SAVED_V6_DHT_NODES); - return size32 + sizesubhead + (packed_node_size(AF_INET) * numv4) + (packed_node_size(AF_INET6) * numv6); + return size32 + sizesubhead + pack_nodes(data, sizeof(Node_format) * num, clients, num); } static uint8_t *z_state_save_subheader(uint8_t *data, uint32_t len, uint16_t type) @@ -2521,47 +2181,16 @@ void DHT_save(DHT *dht, uint8_t *data) host_to_lendian32(data, DHT_STATE_COOKIE_GLOBAL); data += sizeof(uint32_t); - uint32_t num, i, j; + unsigned int num = 0, i, j; uint8_t *old_data = data; /* get right offset. we write the actual header later. */ data = z_state_save_subheader(data, 0, 0); - Node_format clients[MAX_SAVED_DHT_NODES]; - - for (num = 0, i = 0; i < LCLIENT_LIST; ++i) { - if (dht->close_clientlist[i].assoc4.timestamp != 0) { - memcpy(clients[num].public_key, dht->close_clientlist[i].public_key, crypto_box_PUBLICKEYBYTES); - clients[num].ip_port = dht->close_clientlist[i].assoc4.ip_port; - ++num; - } - - if (dht->close_clientlist[i].assoc6.timestamp != 0) { - memcpy(clients[num].public_key, dht->close_clientlist[i].public_key, crypto_box_PUBLICKEYBYTES); - clients[num].ip_port = dht->close_clientlist[i].assoc6.ip_port; - ++num; - } - } - - for (i = 0; i < DHT_FAKE_FRIEND_NUMBER && i < dht->num_friends; ++i) { - DHT_Friend *fr = &dht->friends_list[i]; - - for (j = 0; j < MAX_FRIEND_CLIENTS; ++j) { - if (fr->client_list[j].assoc4.timestamp != 0) { - memcpy(clients[num].public_key, fr->client_list[j].public_key, crypto_box_PUBLICKEYBYTES); - clients[num].ip_port = fr->client_list[j].assoc4.ip_port; - ++num; - } - - if (fr->client_list[j].assoc6.timestamp != 0) { - memcpy(clients[num].public_key, fr->client_list[j].public_key, crypto_box_PUBLICKEYBYTES); - clients[num].ip_port = fr->client_list[j].assoc6.ip_port; - ++num; - } - } - } + Node_format clients[MAX_SAVED_V4_DHT_NODES + MAX_SAVED_V6_DHT_NODES]; + num = randfriends_nodes(dht, clients, MAX_SAVED_V4_DHT_NODES + MAX_SAVED_V6_DHT_NODES); z_state_save_subheader(old_data, pack_nodes(data, sizeof(Node_format) * num, clients, num), DHT_STATE_TYPE_NODES); } @@ -2608,9 +2237,9 @@ static int dht_load_state_callback(void *outer, const uint8_t *data, uint32_t le { free(dht->loaded_nodes_list); // Copy to loaded_clients_list - dht->loaded_nodes_list = calloc(MAX_SAVED_DHT_NODES, sizeof(Node_format)); + dht->loaded_nodes_list = calloc(MAX_SAVED_V4_DHT_NODES + MAX_SAVED_V6_DHT_NODES, sizeof(Node_format)); - int num = unpack_nodes(dht->loaded_nodes_list, MAX_SAVED_DHT_NODES, NULL, data, length, 0); + int num = unpack_nodes(dht->loaded_nodes_list, MAX_SAVED_V4_DHT_NODES + MAX_SAVED_V6_DHT_NODES, NULL, data, length, 0); if (num > 0) { dht->loaded_num_nodes = num; @@ -2655,43 +2284,37 @@ int DHT_load(DHT *dht, const uint8_t *data, uint32_t length) return -1; } -/* return 0 if we are not connected to the DHT. - * return 1 if we are. - */ -int DHT_isconnected(const DHT *dht) -{ - uint32_t i; - unix_time_update(); - - for (i = 0; i < LCLIENT_LIST; ++i) { - const Client_data *client = &dht->close_clientlist[i]; - - if (!is_timeout(client->assoc4.timestamp, BAD_NODE_TIMEOUT) || - !is_timeout(client->assoc6.timestamp, BAD_NODE_TIMEOUT)) - return 1; - } - - return 0; -} - /* 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 i; - unix_time_update(); + Client_data cd; - for (i = 0; i < LCLIENT_LIST; ++i) { - const Client_data *client = &dht->close_clientlist[i]; + if (DHT_bucket_get_nodes(&dht->bucket_v4, &cd, 1, dht->self_public_key) == 1) + return 1; - if (!is_timeout(client->assoc4.timestamp, BAD_NODE_TIMEOUT) && LAN_ip(client->assoc4.ip_port.ip) == -1) - return 1; - - if (!is_timeout(client->assoc6.timestamp, BAD_NODE_TIMEOUT) && LAN_ip(client->assoc6.ip_port.ip) == -1) - return 1; - - } + if (DHT_bucket_get_nodes(&dht->bucket_v6, &cd, 1, dht->self_public_key) == 1) + return 1; return 0; } + +/* return 0 if we are not connected to the DHT. + * return 1 if we are. + */ +int DHT_isconnected(const DHT *dht) +{ + unix_time_update(); + + if (DHT_non_lan_connected(dht)) + return 1; + + Client_data cd; + + if (DHT_bucket_get_nodes(&dht->bucket_lan, &cd, 1, dht->self_public_key) == 1) + return 1; + + return 0; +} + diff --git a/toxcore/DHT.h b/toxcore/DHT.h index aea3d73b..d54278a9 100644 --- a/toxcore/DHT.h +++ b/toxcore/DHT.h @@ -31,11 +31,7 @@ /* 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 DHT_BUCKET_NODES (MAX_FRIEND_CLIENTS) #define MAX_CLOSE_TO_BOOTSTRAP_NODES 8 @@ -72,58 +68,31 @@ void to_net_family(IP *ip); int to_host_family(IP *ip); typedef struct { - IP_Port ip_port; - uint64_t timestamp; -} IPPTs; + uint8_t public_key[crypto_box_PUBLICKEYBYTES]; -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_box_PUBLICKEYBYTES]; - /* 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_box_PUBLICKEYBYTES]; - /* 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_box_PUBLICKEYBYTES]; -} 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; + struct { + uint8_t pk[crypto_box_PUBLICKEYBYTES]; + IP_Port ip_port; + uint64_t timestamp; + } ret[DHT_BUCKET_NODES]; -typedef struct { - uint8_t public_key[crypto_box_PUBLICKEYBYTES]; - IPPTsPng assoc4; - IPPTsPng assoc6; } Client_data; /*----------------------------------------------------------------------------------*/ typedef struct { /* 1 if currently hole punching, otherwise 0 */ - uint8_t hole_punching; + _Bool 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 @@ -134,9 +103,19 @@ typedef struct { } Node_format; +typedef struct DHT_Bucket { + unsigned int deepness; + _Bool empty; + + _Bool public_key; + uint8_t searched_public_key[crypto_box_PUBLICKEYBYTES]; + + Client_data client_list[DHT_BUCKET_NODES]; + struct DHT_Bucket *buckets[2]; +} DHT_Bucket; + typedef struct { uint8_t public_key[crypto_box_PUBLICKEYBYTES]; - Client_data client_list[MAX_FRIEND_CLIENTS]; /* Time at which the last get_nodes request was sent. */ uint64_t lastgetnode; @@ -144,7 +123,12 @@ typedef struct { uint32_t bootstrap_times; /* Symetric NAT hole punching stuff. */ - NAT nat; + NAT nat_v4; + NAT nat_v6; + + uint64_t recvNATping_timestamp; + uint64_t NATping_id; + uint64_t NATping_timestamp; uint16_t lock_count; struct { @@ -206,8 +190,10 @@ typedef struct { typedef struct { Networking_Core *net; + DHT_Bucket bucket_v4; + DHT_Bucket bucket_v6; + DHT_Bucket bucket_lan; - Client_data close_clientlist[LCLIENT_LIST]; uint64_t close_lastgetnodes; uint32_t close_bootstrap_times; @@ -307,6 +293,10 @@ int DHT_getfriendip(const DHT *dht, const uint8_t *public_key, IP_Port *ip_port) */ int id_closest(const uint8_t *pk, const uint8_t *pk1, const uint8_t *pk2); +/* Return index of first unequal bit number. + */ +int bit_by_bit_cmp(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, @@ -334,7 +324,7 @@ int get_close_nodes(const DHT *dht, const uint8_t *public_key, Node_format *node * * return the number of nodes. */ -uint16_t randfriends_nodes(DHT *dht, Node_format *nodes, uint16_t max_num); +uint16_t randfriends_nodes(const DHT *dht, Node_format *nodes, uint16_t max_num); /* Put up to max_num nodes in nodes from the closelist. * @@ -381,11 +371,12 @@ int DHT_connect_after_load(DHT *dht); */ 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. +/* Send the following packet to X nodes who tells us they are connected to friend_pk. * - * return number of nodes it sent the packet to. + * return number of nodes the packet was sent to. */ -int route_tofriend(const DHT *dht, const uint8_t *friend_id, const uint8_t *packet, uint16_t length); +int route_tofriend(const DHT *dht, const uint8_t *friend_pk, const uint8_t *packet, uint16_t length, + unsigned int num_to_send); /* Function to handle crypto packets. */ diff --git a/toxcore/Makefile.inc b/toxcore/Makefile.inc index d6b67eb2..2424281a 100644 --- a/toxcore/Makefile.inc +++ b/toxcore/Makefile.inc @@ -32,8 +32,6 @@ libtoxcore_la_SOURCES = ../toxcore/DHT.h \ ../toxcore/util.c \ ../toxcore/group.h \ ../toxcore/group.c \ - ../toxcore/assoc.h \ - ../toxcore/assoc.c \ ../toxcore/onion.h \ ../toxcore/onion.c \ ../toxcore/logger.h \ diff --git a/toxcore/Messenger.c b/toxcore/Messenger.c index 34296c24..673984e8 100644 --- a/toxcore/Messenger.c +++ b/toxcore/Messenger.c @@ -31,7 +31,6 @@ #include "logger.h" #include "Messenger.h" -#include "assoc.h" #include "network.h" #include "util.h" @@ -2325,10 +2324,6 @@ void do_messenger(Messenger *m) if (unix_time() > lastdump + DUMPING_CLIENTS_FRIENDS_EVERY_N_SECONDS) { -#ifdef ENABLE_ASSOC_DHT - Assoc_status(m->dht->assoc); -#endif - lastdump = unix_time(); uint32_t client, last_pinged; diff --git a/toxcore/assoc.c b/toxcore/assoc.c deleted file mode 100644 index 932adc76..00000000 --- a/toxcore/assoc.c +++ /dev/null @@ -1,1031 +0,0 @@ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "logger.h" -#include "DHT.h" -#include "assoc.h" -#include "ping.h" - -#include "LAN_discovery.h" - -#include -#include "util.h" - -/* - * BASIC OVERVIEW: - * - * Hash: The client_id is hashed with a local hash function. - * Hashes are used in multiple places for searching. - * Bucket: The first n bits of the client_id are used to - * select a bucket. This speeds up sorting, but the more - * important reason is to enforce a spread in the space of - * client_ids available. - * - * - * Candidates: - * - * Candidates are kept in buckets of hash tables. The hash - * function is calculated from the client_id. Up to - * HASH_COLLIDE_COUNT alternative positions are tried if - * the initial position is already used by a different entry. - * The collision function is multiplicative, not additive. - * - * A new candidate can bump an existing candidate, if it is - * more "desirable": Seen beats Heard. - */ - -/* candidates: alternative places for the same hash value */ -#define HASH_COLLIDE_COUNT 5 - -/* bucket size shall be co-prime to this */ -#define HASH_COLLIDE_PRIME 101 - -/* candidates: bump entries: timeout values for seen/heard to be considered of value */ -#define CANDIDATES_SEEN_TIMEOUT 1800 -#define CANDIDATES_HEARD_TIMEOUT 600 - -/* distance/index: index size & access mask */ -#define DISTANCE_INDEX_INDEX_BITS (64 - DISTANCE_INDEX_DISTANCE_BITS) -#define DISTANCE_INDEX_INDEX_MASK ((1 << DISTANCE_INDEX_INDEX_BITS) - 1) - -/* types to stay consistent */ -typedef uint16_t bucket_t; -typedef uint32_t hash_t; -typedef uint16_t usecnt_t; - -/* abbreviations ... */ -typedef Assoc_distance_relative_callback dist_rel_cb; -typedef Assoc_distance_absolute_callback dist_abs_cb; - -/* - * Client_data wrapped with additional data - */ -typedef struct Client_entry { - hash_t hash; - - /* shortcuts & rumors: timers and data */ - uint64_t getnodes; - uint64_t used_at; - - uint64_t seen_at; - uint64_t heard_at; - - uint16_t seen_family; - uint16_t heard_family; - - IP_Port assoc_heard4; - IP_Port assoc_heard6; - - Client_data client; -} Client_entry; - -typedef struct candidates_bucket { - Client_entry *list; /* hashed list */ -} candidates_bucket; - -struct Assoc { - hash_t self_hash; /* hash of self_client_id */ - uint8_t self_client_id[crypto_box_PUBLICKEYBYTES]; /* don't store entries for this */ - - /* association centralization: clients not in use */ - size_t candidates_bucket_bits; - size_t candidates_bucket_count; - size_t candidates_bucket_size; - candidates_bucket *candidates; - uint64_t getnodes; -}; - -/*****************************************************************************/ -/* HELPER FUNCTIONS */ -/*****************************************************************************/ - -/* the complete distance would be crypto_box_PUBLICKEYBYTES long... - * returns DISTANCE_INDEX_DISTANCE_BITS valid bits */ -static uint64_t id_distance(const Assoc *assoc, void *callback_data, const uint8_t *id_ref, const uint8_t *id_test) -{ - /* with BIG_ENDIAN, this would be a one-liner... */ - uint64_t retval = 0; - - uint8_t pos = 0, bits = DISTANCE_INDEX_DISTANCE_BITS; - - while (bits > 8) { - uint8_t distance = abs((int8_t)id_ref[pos] ^ (int8_t)id_test[pos]); - retval = (retval << 8) | distance; - bits -= 8; - pos++; - } - - return (retval << bits) | ((id_ref[pos] ^ id_test[pos]) >> (8 - bits)); -} - -/* qsort() callback for a sorting by id_distance() values */ -static int dist_index_comp(const void *a, const void *b) -{ - const uint64_t *_a = a; - const uint64_t *_b = b; - - if (*_a < *_b) - return -1; - - if (*_a > *_b) - return 1; - - return 0; -} - -/* get actual entry to a distance_index */ -static Client_entry *dist_index_entry(Assoc *assoc, uint64_t dist_ind) -{ - if ((dist_ind & DISTANCE_INDEX_INDEX_MASK) == DISTANCE_INDEX_INDEX_MASK) - return NULL; - - size_t total = assoc->candidates_bucket_count * assoc->candidates_bucket_size; - uint32_t index = dist_ind & DISTANCE_INDEX_INDEX_MASK; - - if (index < total) { - bucket_t b_id = index / assoc->candidates_bucket_size; - candidates_bucket *cnd_bckt = &assoc->candidates[b_id]; - size_t b_ix = index % assoc->candidates_bucket_size; - Client_entry *entry = &cnd_bckt->list[b_ix]; - - if (entry->hash) - return entry; - } - - return NULL; -} - -/* get actual entry's public_key to a distance_index */ -static uint8_t *dist_index_id(Assoc *assoc, uint64_t dist_ind) -{ - Client_entry *entry = dist_index_entry(assoc, dist_ind); - - if (entry) - return entry->client.public_key; - - return NULL; -} - -/* sorts first .. last, i.e. last is included */ -static void dist_index_bubble(Assoc *assoc, uint64_t *dist_list, size_t first, size_t last, uint8_t *id, - void *custom_data, Assoc_distance_relative_callback dist_rel_func) -{ - size_t i, k; - - for (i = first; i <= last; i++) { - uint8_t *id1 = dist_index_id(assoc, dist_list[i]); - - for (k = i + 1; k <= last; k++) { - uint8_t *id2 = dist_index_id(assoc, dist_list[k]); - - if (id1 && id2) - if (dist_rel_func(assoc, custom_data, id, id1, id2) == 2) { - uint64_t swap = dist_list[i]; - dist_list[i] = dist_list[k]; - dist_list[k] = swap; - } - } - } -} - -/* TODO: Check that there isn't a function like this elsewhere hidden. - * E.g. the one which creates a handshake_id isn't usable for this, it must - * always map the same ID to the same hash. - * - * Result is NOT MAPPED to CANDIDATES_TO_KEEP range, i.e. map before using - * it for list access. */ -static hash_t id_hash(const Assoc *assoc, const uint8_t *id) -{ - uint32_t i, res = 0x19a64e82; - - for (i = 0; i < crypto_box_PUBLICKEYBYTES; i++) - res = ((res << 1) ^ id[i]) + (res >> 31); - - /* can't have zero as hash, a) marks an unused spot, - * b) collision function is multiplicative */ - if (!(res % assoc->candidates_bucket_size)) - res++; - - return res; -} - -/* up to HASH_COLLIDE_COUNT calls to different spots, - * result IS mapped to CANDIDATES_TO_KEEP range */ -static hash_t hash_collide(const Assoc *assoc, hash_t hash) -{ - uint64_t hash64 = hash % assoc->candidates_bucket_size; - hash64 = (hash64 * HASH_COLLIDE_PRIME) % assoc->candidates_bucket_size; - - hash_t retval = hash64; - - /* this should never happen when CANDIDATES_TO_KEEP is prime and hash not a multiple - * (id_hash() checks for a multiple and returns a different hash in that case) - * - * ( 1 .. (prime - 1) is a group over multiplication and every number has its inverse - * in the group, so no multiplication should ever end on zero as long neither - * of the two factors was zero-equivalent ) - * - * BUT: because the usage of the word "never" invokes Murphy's law, catch it */ - if (!retval) { -#ifdef DEBUG - fprintf(stderr, "assoc::hash_collide: hash %u, bucket size %u => %u!", hash, (uint)assoc->candidates_bucket_size, - retval); - assert(retval != 0); -#endif - retval = 1; - } - - return retval; -} - -/* returns the "seen" assoc related to the ipp */ -static IPPTsPng *entry_assoc(Client_entry *cl_entry, const IP_Port *ipp) -{ - if (!cl_entry) - return NULL; - - if (ipp->ip.family == AF_INET) - return &cl_entry->client.assoc4; - - if (ipp->ip.family == AF_INET6) - return &cl_entry->client.assoc6; - - return NULL; -} - -/* returns the "heard" assoc related to the ipp */ -static IP_Port *entry_heard_get(Client_entry *entry, const IP_Port *ipp) -{ - if (ipp->ip.family == AF_INET) - return &entry->assoc_heard4; - else if (ipp->ip.family == AF_INET6) - return &entry->assoc_heard6; - else - return NULL; -} - -/* store a "heard" entry - * overwrites empty entry, does NOT overwrite non-LAN ip with - * LAN ip - * - * returns 1 if the entry did change */ -static int entry_heard_store(Client_entry *entry, const IPPTs *ippts) -{ - if (!entry || !ippts) - return 0; - - if (!ipport_isset(&ippts->ip_port)) - return 0; - - IP_Port *heard; - const IP_Port *ipp = &ippts->ip_port; - - if (ipp->ip.family == AF_INET) - heard = &entry->assoc_heard4; - else if (ipp->ip.family == AF_INET6) - heard = &entry->assoc_heard6; - else - return 0; - - if (ipport_equal(ipp, heard)) - return 0; - - if (!ipport_isset(heard)) { - *heard = *ipp; - entry->heard_at = ippts->timestamp; - entry->heard_family = ipp->ip.family; - return 1; - } - - /* don't destroy a good address with a crappy one - * (unless we're very timed out) */ - uint8_t LAN_ipp = LAN_ip(ipp->ip) == 0; - uint8_t LAN_entry = LAN_ip(heard->ip) == 0; - - if (LAN_ipp && !LAN_entry && !is_timeout(entry->heard_at, CANDIDATES_HEARD_TIMEOUT)) - return 0; - - *heard = *ipp; - entry->heard_at = ippts->timestamp; - entry->heard_family = ipp->ip.family; - - return 1; -} - -/* maps Assoc callback signature to id_closest() */ -static int assoc_id_closest(const Assoc *assoc, void *callback_data, const uint8_t *client_id, - const uint8_t *client_id1, const uint8_t *client_id2) -{ - return id_closest(client_id, client_id1, client_id2); -} - -static bucket_t id_bucket(const uint8_t *id, uint8_t bits) -{ - /* return the first "bits" bits of id */ - bucket_t retval = 0; - - uint8_t pos = 0; - - while (bits > 8) { - retval = (retval << 8) | id[pos++]; - bits -= 8; - } - - return (retval << bits) | (id[pos] >> (8 - bits)); -} - -/*****************************************************************************/ -/* CANDIDATES FUNCTIONS */ -/*****************************************************************************/ - - -static bucket_t candidates_id_bucket(const Assoc *assoc, const uint8_t *id) -{ - return id_bucket(id, assoc->candidates_bucket_bits); -} - -static uint8_t candidates_search(const Assoc *assoc, const uint8_t *id, hash_t hash, Client_entry **entryptr) -{ - bucket_t bucket = candidates_id_bucket(assoc, id); - candidates_bucket *cnd_bckt = &assoc->candidates[bucket]; - size_t coll, pos = hash % assoc->candidates_bucket_size; - - for (coll = 0; coll < HASH_COLLIDE_COUNT; pos = hash_collide(assoc, pos) , coll++) { - Client_entry *entry = &cnd_bckt->list[pos]; - - if (entry->hash == hash) - if (id_equal(entry->client.public_key, id)) { - *entryptr = entry; - return 1; - } - } - - *entryptr = NULL; - return 0; -} - -static void candidates_update_assoc(const Assoc *assoc, Client_entry *entry, uint8_t used, const IPPTs *ippts_send, - const IP_Port *ipp_recv) -{ - if (!assoc || !entry || !ippts_send) - return; - - IPPTsPng *ipptsp = entry_assoc(entry, &ippts_send->ip_port); - - if (!ipptsp) - return; - - if (used) - entry->used_at = unix_time(); - - /* do NOT do anything related to wanted, that's handled outside, - * just update the assoc (in the most sensible way) - */ - if (ipp_recv) { - ipptsp->ip_port = ippts_send->ip_port; - ipptsp->timestamp = ippts_send->timestamp; - ipptsp->ret_ip_port = *ipp_recv; - ipptsp->ret_timestamp = unix_time(); - - entry->seen_at = unix_time(); - entry->seen_family = ippts_send->ip_port.ip.family; - - return; - } - - entry_heard_store(entry, ippts_send); -} - -static uint8_t candidates_create_internal(const Assoc *assoc, hash_t const hash, const uint8_t *id, uint8_t seen, - uint8_t used, bucket_t *bucketptr, size_t *posptr) -{ - if (!assoc || !id || !bucketptr || !posptr) - return 0; - - bucket_t bucket = candidates_id_bucket(assoc, id); - candidates_bucket *cnd_bckt = &assoc->candidates[bucket]; - - size_t coll, pos = hash % assoc->candidates_bucket_size, check; - size_t pos_check[6]; - - memset(pos_check, 0, sizeof(pos_check)); - - for (coll = 0; coll < HASH_COLLIDE_COUNT; pos = hash_collide(assoc, pos) , coll++) { - Client_entry *entry = &cnd_bckt->list[pos]; - - /* unset */ - if (!entry->hash) { - *bucketptr = bucket; - *posptr = pos; - - return 1; - } - - /* 0. bad - * 1. seen bad, heard good - * 2. seen good - * 3. used */ - // enumerated lists are superior to magic numbers - if (!is_timeout(entry->used_at, BAD_NODE_TIMEOUT)) - check = USED; - else if (!is_timeout(entry->seen_at, CANDIDATES_SEEN_TIMEOUT)) - check = SEENG; - else if (!is_timeout(entry->heard_at, CANDIDATES_HEARD_TIMEOUT)) - check = SEENB_HEARDG; - else - check = BAD; - - if (!pos_check[check]) - pos_check[check] = pos + 1; - } - - /* used > seen > heard > bad */ - size_t i, pos_max = used ? USED : (seen ? SEENG : SEENB_HEARDG); - - for (i = 0; i < pos_max; i++) - if (pos_check[i]) { - *bucketptr = bucket; - *posptr = pos_check[i] - 1; - - return 1; - } - - return 0; -} - -static uint8_t candidates_create_new(const Assoc *assoc, hash_t hash, const uint8_t *id, uint8_t used, - const IPPTs *ippts_send, const IP_Port *ipp_recv) -{ - if (!assoc || !id || !ippts_send) - return 0; - - bucket_t bucket; - size_t pos; - - if (!candidates_create_internal(assoc, hash, id, ipp_recv != NULL, used, &bucket, &pos)) - return 0; - - candidates_bucket *cnd_bckt = &assoc->candidates[bucket]; - Client_entry *entry = &cnd_bckt->list[pos]; - memset(entry, 0, sizeof(*entry)); - IPPTsPng *ipptsp = entry_assoc(entry, &ippts_send->ip_port); - - if (!ipptsp) - return 0; - - entry->hash = hash; - id_copy(entry->client.public_key, id); - - if (used) - entry->used_at = unix_time(); - - if (ipp_recv && !ipport_isset(ipp_recv)) - ipp_recv = NULL; - - if (ipp_recv) { - entry->seen_at = ippts_send->timestamp; - entry->seen_family = ippts_send->ip_port.ip.family; - - ipptsp->ip_port = ippts_send->ip_port; - ipptsp->timestamp = ippts_send->timestamp; - ipptsp->ret_ip_port = *ipp_recv; - ipptsp->ret_timestamp = unix_time(); - } else { - IP_Port *heard = entry_heard_get(entry, &ippts_send->ip_port); - - if (heard) { - entry->heard_at = ippts_send->timestamp; - entry->heard_family = ippts_send->ip_port.ip.family; - - *heard = ippts_send->ip_port; - } - } - - return 1; -} - -/*****************************************************************************/ - -static void client_id_self_update(Assoc *assoc) -{ - if (assoc->self_hash) - return; - - size_t i, sum = 0; - - for (i = 0; i < crypto_box_PUBLICKEYBYTES; i++) - sum |= assoc->self_client_id[i]; - - if (!sum) - return; - - assoc->self_hash = id_hash(assoc, assoc->self_client_id); - - LOGGER_DEBUG("id is now set, purging cache of self-references"); - - /* if we already added some (or loaded some) entries, - * look and remove if we find a match - */ - bucket_t b_id = candidates_id_bucket(assoc, assoc->self_client_id); - candidates_bucket *cnd_bckt = &assoc->candidates[b_id]; - size_t pos = assoc->self_hash % assoc->candidates_bucket_size; - - for (i = 0; i < HASH_COLLIDE_COUNT; pos = hash_collide(assoc, pos), i++) { - Client_entry *entry = &cnd_bckt->list[pos]; - - if (entry->hash == assoc->self_hash) - if (id_equal(entry->client.public_key, assoc->self_client_id)) - entry->hash = 0; - } -} - -/*****************************************************************************/ -/* TRIGGER FUNCTIONS */ -/*****************************************************************************/ - -/* Central entry point for new associations: add a new candidate to the cache - * seen should be 0 (zero), if the candidate was announced by someone else, - * seen should be 1 (one), if there is confirmed connectivity (a definite response) - */ -uint8_t Assoc_add_entry(Assoc *assoc, const uint8_t *id, const IPPTs *ippts_send, const IP_Port *ipp_recv, uint8_t used) -{ - if (!assoc || !id || !ippts_send) - return 0; - - if (!assoc->self_hash) { - client_id_self_update(assoc); - - if (!assoc->self_hash) - return 0; - } - - if (!ipport_isset(&ippts_send->ip_port)) - return 0; - - if (ipp_recv && !ipport_isset(ipp_recv)) - ipp_recv = NULL; - - hash_t hash = id_hash(assoc, id); - - if (hash == assoc->self_hash) - if (id_equal(id, assoc->self_client_id)) - return 0; - - /* if it's new: - * callback, if there's desire, add to clients, else to candidates - * - * if it's "old": - * if it's client: refresh - * if it's candidate: - * if !ipp_recv, refresh - * if ipp_recv: callback, if there's desire, move to candidates - */ - Client_entry *cnd_entry; - - if (!candidates_search(assoc, id, hash, &cnd_entry)) { - if (candidates_create_new(assoc, hash, id, used, ippts_send, ipp_recv)) - return 1; - else - return 0; - } else { - candidates_update_assoc(assoc, cnd_entry, used, ippts_send, ipp_recv); - return 2; - } -} - -/*****************************************************************************/ -/* MAIN USE */ -/*****************************************************************************/ - -uint8_t Assoc_get_close_entries(Assoc *assoc, Assoc_close_entries *state) -{ - if (!assoc || !state || !state->wanted_id || !state->result) - return 0; - - if (!assoc->self_hash) { - client_id_self_update(assoc); - - if (!assoc->self_hash) - return 0; - } - - if (!state->distance_relative_func) - state->distance_relative_func = assoc_id_closest; - - if (!state->distance_absolute_func) - state->distance_absolute_func = id_distance; - - size_t dist_list_len = assoc->candidates_bucket_count * assoc->candidates_bucket_size; - uint64_t dist_list[dist_list_len]; - memset(dist_list, ~0, dist_list_len * sizeof(dist_list[0])); - bucket_t b; - size_t i; - - for (b = 0; b < assoc->candidates_bucket_count; b++) { - candidates_bucket *cnd_bckt = &assoc->candidates[b]; - - for (i = 0; i < assoc->candidates_bucket_size; i++) { - Client_entry *entry = &cnd_bckt->list[i]; - - if (entry->hash) { - if (state->flags & ProtoIPv4) { - if (!ipport_isset(&entry->client.assoc4.ip_port)) - continue; - - if (!(state->flags & LANOk)) - if (!LAN_ip(entry->client.assoc4.ip_port.ip)) - continue; - } - - if (state->flags & ProtoIPv6) { - if (!ipport_isset(&entry->client.assoc6.ip_port)) - continue; - - if (!(state->flags & LANOk)) - if (!LAN_ip(entry->client.assoc6.ip_port.ip)) - continue; - } - - uint64_t dist = state->distance_absolute_func(assoc, state->custom_data, state->wanted_id, entry->client.public_key); - uint32_t index = b * assoc->candidates_bucket_size + i; - dist_list[index] = (dist << DISTANCE_INDEX_INDEX_BITS) | index; - } - } - } - - qsort(dist_list, dist_list_len, sizeof(dist_list[0]), dist_index_comp); - - /* ok, ok, it's not *perfectly* sorted, because we used an absolute distance - * go over the result and see if we need to "smoothen things out" - * because those should be only very few and short streaks, the worst regularly - * used sorting function aka bubble sort is used */ - uint64_t dist_prev = ~0; - size_t ind_prev = ~0, ind_curr; - size_t len = 1; - - for (ind_curr = 0; ind_curr < dist_list_len; ind_curr++) { - /* sorted increasingly, so an invalid entry marks the end */ - if ((dist_list[ind_curr] & DISTANCE_INDEX_INDEX_MASK) == DISTANCE_INDEX_INDEX_MASK) - break; - - uint64_t dist_curr = dist_list[ind_curr] >> DISTANCE_INDEX_INDEX_BITS; - - if (dist_prev == dist_curr) - len++; - else { - if (len > 1) - dist_index_bubble(assoc, dist_list, ind_prev, ind_curr - 1, state->wanted_id, state->custom_data, - state->distance_relative_func); - - dist_prev = dist_curr; - ind_prev = ind_curr; - len = 1; - } - } - - if (len > 1) - dist_index_bubble(assoc, dist_list, ind_prev, ind_curr - 1, state->wanted_id, state->custom_data, - state->distance_relative_func); - - /* ok, now dist_list is a strictly ascending sorted list of nodes - * a) extract CLOSE_QUOTA_USED clients, not timed out - * b) extract (1 - QUOTA) (better!) clients & candidates, not timed out - * c) save candidates which would be better, if contact can be established */ - size_t client_quota_good = 0, pos = 0; - size_t client_quota_max = state->count_good; - - ssize_t taken_last = - 1; - - for (i = 0; (i < dist_list_len) && (pos < state->count); i++) { - /* sorted increasingly, so an invalid entry marks the end */ - if ((dist_list[i] & DISTANCE_INDEX_INDEX_MASK) == DISTANCE_INDEX_INDEX_MASK) - break; - - Client_entry *entry = dist_index_entry(assoc, dist_list[i]); - - if (entry && entry->hash) { - if (client_quota_good >= client_quota_max) { - state->result[pos++] = &entry->client; - taken_last = i; - } else { - if (state->flags & (ProtoIPv4 | ProtoIPv6)) { - if ((state->flags & ProtoIPv4) && is_timeout(entry->client.assoc4.timestamp, BAD_NODE_TIMEOUT)) - continue; - - if ((state->flags & ProtoIPv6) && is_timeout(entry->client.assoc6.timestamp, BAD_NODE_TIMEOUT)) - continue; - } else if (is_timeout(entry->seen_at, BAD_NODE_TIMEOUT)) - continue; - - state->result[pos++] = &entry->client; - client_quota_good++; - taken_last = i; - } - } - } - - /* if we had not enough valid entries the list might still not be filled. - * - * start again from last taken client, but leave out any requirement - */ - if (pos < state->count) { - for (i = taken_last + 1; (i < dist_list_len) && (pos < state->count); i++) { - /* sorted increasingly, so an invalid entry marks the end */ - if ((dist_list[i] & DISTANCE_INDEX_INDEX_MASK) == DISTANCE_INDEX_INDEX_MASK) - break; - - Client_entry *entry = dist_index_entry(assoc, dist_list[i]); - - if (entry && entry->hash) - state->result[pos++] = &entry->client; - } - } - - return pos; -} - -/*****************************************************************************/ -/* GLOBAL STRUCTURE FUNCTIONS */ -/*****************************************************************************/ - -static uint8_t odd_min9_is_prime(size_t value) -{ - size_t i = 3; - - while (i * i <= value) { - if (!(value % i)) - return 0; - - i += 2; - } - - return 1; -} - -static size_t prime_upto_min9(size_t limit) -{ - /* even => odd */ - limit = limit - (1 - (limit % 2)); - - while (!odd_min9_is_prime(limit)) - limit -= 2; - - return limit; -} - -/* create */ -Assoc *new_Assoc(size_t bits, size_t entries, const uint8_t *public_id) -{ - if (!public_id) - return NULL; - - Assoc *assoc = calloc(1, sizeof(*assoc)); - - if (!assoc) - return NULL; - - /* - * bits must be in [ 2 .. 15 ] - * entries must be a prime - */ - if (bits < 2) - bits = 2; - else if (bits > 15) - bits = 15; - - assoc->candidates_bucket_bits = bits; - assoc->candidates_bucket_count = 1U << bits; - - if (entries < 25) { - if (entries <= 6) - entries = 5; - else { - entries = entries - (1 - (entries % 2)); /* even => odd */ - - /* 7..23: all odds but 9&15 are prime */ - if (!(entries % 3)) /* 9, 15 */ - entries -= 2; /* 7, 13 */ - } - } else if (entries > ((1 << 17) - 1)) /* 130k+ */ - entries = (1 << 17) - 1; - else { - /* 9+: test and find a prime less or equal */ - size_t entries_test = prime_upto_min9(entries); - - if (entries_test == HASH_COLLIDE_PRIME) /* disallowed */ - entries_test = prime_upto_min9(entries_test - 1); - - if (entries_test != entries) { - - LOGGER_DEBUG("trimmed %i to %i.\n", (int)entries, (int)entries_test); - entries = (size_t)entries_test; - } - } - - assoc->candidates_bucket_size = entries; - - /* allocation: preferably few blobs */ - size_t bckt, cix; - Client_entry *clients = malloc(sizeof(*clients) * assoc->candidates_bucket_count * assoc->candidates_bucket_size); - - if (!clients) { - free(assoc); - return NULL; - } - - candidates_bucket *lists = malloc(sizeof(*lists) * assoc->candidates_bucket_count); - - if (!lists) { - free(assoc); - free(clients); - return NULL; - } - - for (bckt = 0; bckt < assoc->candidates_bucket_count; bckt++) { - candidates_bucket *list = &lists[bckt]; - - list->list = &clients[bckt * assoc->candidates_bucket_size]; - - for (cix = 0; cix < assoc->candidates_bucket_size; cix++) - list->list[cix].hash = 0; - } - - assoc->candidates = lists; - assoc->getnodes = unix_time(); - - id_copy(assoc->self_client_id, public_id); - client_id_self_update(assoc); - - return assoc; -} - -Assoc *new_Assoc_default(const uint8_t *public_id) -{ - /* original 8, 251 averages to ~32k entries... probably the whole DHT :D - * 320 entries is fine, hopefully */ - return new_Assoc(6, 15, public_id); -} - -/* own client_id, assocs for this have to be ignored */ -void Assoc_self_client_id_changed(Assoc *assoc, const uint8_t *id) -{ - if (assoc && id) { - assoc->self_hash = 0; - id_copy(assoc->self_client_id, id); - client_id_self_update(assoc); - } -} - -#ifdef TOX_LOGGER -static char *idpart2str(uint8_t *id, size_t len); -#endif /* TOX_LOGGER */ - -/* refresh buckets */ -void do_Assoc(Assoc *assoc, DHT *dht) -{ - if (is_timeout(assoc->getnodes, ASSOC_BUCKET_REFRESH)) { - assoc->getnodes = unix_time(); - - size_t candidate = (rand() % assoc->candidates_bucket_count) + assoc->candidates_bucket_count; - - /* in that bucket or the buckets closest to it: - * find the best heard candidate - * find the best seen candidate - * send getnode() requests to both */ - uint8_t *target_id = NULL; - Client_entry *heard = NULL, *seen = NULL; - size_t i, k, m; - - for (i = 1; i < assoc->candidates_bucket_count; i++) { - if (i % 2) - k = - (i >> 1); - else - k = i >> 1; - - size_t bckt = (candidate + k) % assoc->candidates_bucket_count; - - for (m = 0; m < assoc->candidates_bucket_size; m++) - if (assoc->candidates[bckt].list[m].hash) { - Client_entry *entry = &assoc->candidates[bckt].list[m]; - - if (!is_timeout(entry->getnodes, CANDIDATES_SEEN_TIMEOUT)) - continue; - - if (!target_id) - target_id = entry->client.public_key; - - if (entry->seen_at) { - if (!seen) - if (!is_timeout(entry->seen_at, CANDIDATES_SEEN_TIMEOUT)) - seen = entry; - } - - if (entry->heard_at) { - if (!heard) - if (!is_timeout(entry->heard_at, CANDIDATES_HEARD_TIMEOUT)) - heard = entry; - } - - if (seen && heard) - break; - } - - if (seen && heard) - break; - } - - if (seen) { - IPPTsPng *ippts = seen->seen_family == AF_INET ? &seen->client.assoc4 : &seen->client.assoc6; - - LOGGER_DEBUG("[%u] => S[%s...] %s:%u", (uint32_t)(candidate % assoc->candidates_bucket_count), - idpart2str(seen->client.public_key, 8), ip_ntoa(&ippts->ip_port.ip), htons(ippts->ip_port.port)); - - DHT_getnodes(dht, &ippts->ip_port, seen->client.public_key, target_id); - seen->getnodes = unix_time(); - } - - if (heard && (heard != seen)) { - IP_Port *ipp = heard->heard_family == AF_INET ? &heard->assoc_heard4 : &heard->assoc_heard6; - - LOGGER_DEBUG("[%u] => H[%s...] %s:%u", (uint32_t)(candidate % assoc->candidates_bucket_count), - idpart2str(heard->client.public_key, 8), ip_ntoa(&ipp->ip), htons(ipp->port)); - - DHT_getnodes(dht, ipp, heard->client.public_key, target_id); - heard->getnodes = unix_time(); - } - - LOGGER_SCOPE ( - - if ( !heard && !seen ) - LOGGER_DEBUG("[%u] => no nodes to talk to??", (uint32_t)(candidate % assoc->candidates_bucket_count)); - ); - } -} - -/* destroy */ -void kill_Assoc(Assoc *assoc) -{ - if (assoc) { - free(assoc->candidates->list); - free(assoc->candidates); - free(assoc); - } -} - -#ifdef TOX_LOGGER - -static char buffer[crypto_box_PUBLICKEYBYTES * 2 + 1]; -static char *idpart2str(uint8_t *id, size_t len) -{ - if (len > crypto_box_PUBLICKEYBYTES) - len = crypto_box_PUBLICKEYBYTES; - - size_t i; - - for (i = 0; i < len; i++) - sprintf(buffer + i * 2, "%02hhx", id[i]); - - buffer[len * 2] = 0; - return buffer; -} - -void Assoc_status(const Assoc *assoc) -{ - if (!assoc) { - LOGGER_TRACE("Assoc status: no assoc"); - return; - } - - LOGGER_TRACE("[b:p] hash => [id...] used, seen, heard"); - - size_t bid, cid, total = 0; - - for (bid = 0; bid < assoc->candidates_bucket_count; bid++) { - candidates_bucket *bucket = &assoc->candidates[bid]; - - for (cid = 0; cid < assoc->candidates_bucket_size; cid++) { - Client_entry *entry = &bucket->list[cid]; - - if (entry->hash) { - total++; - - LOGGER_TRACE("[%3i:%3i] %08x => [%s...] %i, %i(%c), %i(%c)\n", - (int)bid, (int)cid, entry->hash, idpart2str(entry->client.public_key, 8), - entry->used_at ? (int)(unix_time() - entry->used_at) : 0, - entry->seen_at ? (int)(unix_time() - entry->seen_at) : 0, - entry->seen_at ? (entry->seen_family == AF_INET ? '4' : (entry->seen_family == AF_INET6 ? '6' : '?')) : '?', - entry->heard_at ? (int)(unix_time() - entry->heard_at) : 0, - entry->heard_at ? (entry->heard_family == AF_INET ? '4' : (entry->heard_family == AF_INET6 ? '6' : '?')) : '?'); - } - } - } - - if (total) { - LOGGER_TRACE("Total: %i entries, table usage %i%%.\n", (int)total, - (int)(total * 100 / (assoc->candidates_bucket_count * assoc->candidates_bucket_size))); - } -} - -#endif /* TOX_LOGGER */ diff --git a/toxcore/assoc.h b/toxcore/assoc.h deleted file mode 100644 index 65a2745d..00000000 --- a/toxcore/assoc.h +++ /dev/null @@ -1,104 +0,0 @@ - -#ifndef __ASSOC_H__ -#define __ASSOC_H__ - -/* used by rendezvous */ -#define ASSOC_AVAILABLE - -/* For the legalese parts, see tox.h. */ - -/* enumerated lists are superior to magic numbers */ -enum NODE_STATUS { BAD, SEENB_HEARDG, SEENG, USED }; - -/* - * Module to store currently unused ID <=> IP associations - * for a potential future use - */ - -typedef struct Assoc Assoc; - -/*****************************************************************************/ - -/* custom distance handler, if it's not ID-distance based - * return values exactly like id_closest() */ -typedef int (*Assoc_distance_relative_callback)(const Assoc *assoc, void *callback_data, const uint8_t *client_id, - const uint8_t *client_id1, const uint8_t *client_id2); - -#define DISTANCE_INDEX_DISTANCE_BITS 44 - -/* absolute distance: can be same for different client_id_check values - * return value should have DISTANCE_INDEX_DISTANCE_BITS valid bits */ -typedef uint64_t (*Assoc_distance_absolute_callback)(const Assoc *assoc, void *callback_data, - const uint8_t *client_id_ref, const uint8_t *client_id_check); - -/*****************************************************************************/ - -/* Central entry point for new associations: add a new candidate to the cache - * returns 1 if entry is stored, 2 if existing entry was updated, 0 else */ -uint8_t Assoc_add_entry(Assoc *assoc, const uint8_t *id, const IPPTs *ippts_send, const IP_Port *ipp_recv, - uint8_t used); - -/*****************************************************************************/ - -typedef enum AssocCloseEntriesFlags { - ProtoIPv4 = 1, - ProtoIPv6 = 2, - LANOk = 4, -} AssocCloseEntriesFlags; - -typedef struct Assoc_close_entries { - void *custom_data; /* given to distance functions */ - uint8_t *wanted_id; /* the target client_id */ - uint8_t flags; /* additional flags */ - - Assoc_distance_relative_callback distance_relative_func; - Assoc_distance_absolute_callback distance_absolute_func; - - uint8_t count_good; /* that many should be "good" w.r.t. timeout */ - uint8_t count; /* allocated number of close_indices */ - Client_data **result; -} Assoc_close_entries; - -/* find up to close_count nodes to put into close_nodes_used of ID_Nodes - * the distance functions can be NULL, then standard distance functions will be used - * the caller is responsible for allocating close_indices of sufficient size - * - * returns 0 on error - * returns the number of found nodes and the list of indices usable by Assoc_client() - * the caller is assumed to be registered from Assoc_register_callback() - * if they aren't, they should copy the Client_data and call Assoc_client_drop() - */ -uint8_t Assoc_get_close_entries(Assoc *assoc, Assoc_close_entries *close_entries); - -/*****************************************************************************/ - -/* create: default sizes (6, 5 => 320 entries) */ -Assoc *new_Assoc_default(const uint8_t *public_id); - -/* create: customized sizes - * total is (2^bits) * entries - * bits should be between 2 and 15 (else it's trimmed) - * entries will be reduced to the closest prime smaller or equal - * - * preferably bits should be large and entries small to ensure spread - * in the search space (e. g. 5, 5 is preferable to 2, 41) */ -Assoc *new_Assoc(size_t bits, size_t entries, const uint8_t *public_id); - -/* public_id changed (loaded), update which entry isn't stored */ -void Assoc_self_client_id_changed(Assoc *assoc, const uint8_t *public_id); - -/* every 45s send out a getnodes() for a "random" bucket */ -#define ASSOC_BUCKET_REFRESH 45 - -/* refresh bucket's data from time to time - * this must be called only from DHT */ -void do_Assoc(Assoc *assoc, DHT *dht); - -/* destroy */ -void kill_Assoc(Assoc *assoc); - -#ifdef TOX_LOGGER -void Assoc_status(const Assoc *assoc); -#endif /* TOX_LOGGER */ - -#endif /* !__ASSOC_H__ */ diff --git a/toxcore/crypto_core.h b/toxcore/crypto_core.h index ab509f09..2279fb24 100644 --- a/toxcore/crypto_core.h +++ b/toxcore/crypto_core.h @@ -125,7 +125,6 @@ void new_nonce(uint8_t *nonce); #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. */ diff --git a/toxcore/onion_client.c b/toxcore/onion_client.c index b879f393..a75425ad 100644 --- a/toxcore/onion_client.c +++ b/toxcore/onion_client.c @@ -891,7 +891,8 @@ static int send_dht_dhtpk(const Onion_Client *onion_c, int friend_num, const uin if (len == -1) return -1; - return route_tofriend(onion_c->dht, onion_c->friends_list[friend_num].dht_public_key, packet, len); + return route_tofriend(onion_c->dht, onion_c->friends_list[friend_num].dht_public_key, packet, len, + DHT_BUCKET_NODES / 2); } static int handle_dht_dhtpk(void *object, IP_Port source, const uint8_t *source_pubkey, const uint8_t *packet, diff --git a/toxcore/ping.c b/toxcore/ping.c index f81766cb..29c91017 100644 --- a/toxcore/ping.c +++ b/toxcore/ping.c @@ -220,33 +220,6 @@ static int handle_ping_response(void *_dht, IP_Port source, const uint8_t *packe return 0; } -/* Check if public_key with ip_port is in the list. - * - * return 1 if it is. - * return 0 if it isn't. - */ -static int in_list(const Client_data *list, uint16_t length, const uint8_t *public_key, IP_Port ip_port) -{ - unsigned int i; - - for (i = 0; i < length; ++i) { - if (id_equal(list[i].public_key, public_key)) { - const IPPTsPng *ipptp; - - if (ip_port.ip.family == AF_INET) { - ipptp = &list[i].assoc4; - } else { - ipptp = &list[i].assoc6; - } - - if (!is_timeout(ipptp->timestamp, BAD_NODE_TIMEOUT) && ipport_equal(&ipptp->ip_port, &ip_port)) - return 1; - } - } - - return 0; -} - /* Add nodes to the to_ping list. * All nodes in this list are pinged every TIME_TO_PING seconds * and are then removed from the list. @@ -265,9 +238,6 @@ int add_to_ping(PING *ping, const uint8_t *public_key, IP_Port ip_port) if (!node_addable_to_close_list(ping->dht, public_key, ip_port)) return -1; - if (in_list(ping->dht->close_clientlist, LCLIENT_LIST, public_key, ip_port)) - return -1; - IP_Port temp; if (DHT_getfriendip(ping->dht, public_key, &temp) == 0) {