diff options
Diffstat (limited to 'toxcore')
-rw-r--r-- | toxcore/DHT.c | 81 | ||||
-rw-r--r-- | toxcore/DHT.h | 31 | ||||
-rw-r--r-- | toxcore/Makefile.inc | 2 | ||||
-rw-r--r-- | toxcore/Messenger.c | 9 | ||||
-rw-r--r-- | toxcore/assoc.c | 739 | ||||
-rw-r--r-- | toxcore/assoc.h | 73 | ||||
-rw-r--r-- | toxcore/group_chats.c | 37 | ||||
-rw-r--r-- | toxcore/group_chats.h | 8 | ||||
-rw-r--r-- | toxcore/network.h | 2 | ||||
-rw-r--r-- | toxcore/ping.c | 29 | ||||
-rw-r--r-- | toxcore/ping.h | 4 |
11 files changed, 976 insertions, 39 deletions
diff --git a/toxcore/DHT.c b/toxcore/DHT.c index eb50cc43..9730771b 100644 --- a/toxcore/DHT.c +++ b/toxcore/DHT.c | |||
@@ -28,27 +28,20 @@ | |||
28 | #endif | 28 | #endif |
29 | 29 | ||
30 | #include "DHT.h" | 30 | #include "DHT.h" |
31 | #include "network.h" | 31 | #include "assoc.h" |
32 | #include "ping.h" | 32 | #include "ping.h" |
33 | |||
34 | #include "network.h" | ||
35 | #include "LAN_discovery.h" | ||
33 | #include "misc_tools.h" | 36 | #include "misc_tools.h" |
34 | #include "util.h" | 37 | #include "util.h" |
35 | #include "LAN_discovery.h" | ||
36 | |||
37 | /* The number of seconds for a non responsive node to become bad. */ | ||
38 | #define BAD_NODE_TIMEOUT 70 | ||
39 | 38 | ||
40 | /* The max number of nodes to send with send nodes. */ | 39 | /* The max number of nodes to send with send nodes. */ |
41 | #define MAX_SENT_NODES 8 | 40 | #define MAX_SENT_NODES 8 |
42 | 41 | ||
43 | /* Ping timeout in seconds */ | ||
44 | #define PING_TIMEOUT 5 | ||
45 | |||
46 | /* The timeout after which a node is discarded completely. */ | 42 | /* The timeout after which a node is discarded completely. */ |
47 | #define KILL_NODE_TIMEOUT 300 | 43 | #define KILL_NODE_TIMEOUT 300 |
48 | 44 | ||
49 | /* Ping interval in seconds for each node in our lists. */ | ||
50 | #define PING_INTERVAL 60 | ||
51 | |||
52 | /* Ping interval in seconds for each random sending of a get nodes request. */ | 45 | /* Ping interval in seconds for each random sending of a get nodes request. */ |
53 | #define GET_NODE_INTERVAL 5 | 46 | #define GET_NODE_INTERVAL 5 |
54 | 47 | ||
@@ -850,6 +843,14 @@ static int handle_sendnodes_core(void *object, IP_Port source, uint8_t *packet, | |||
850 | /* store the address the *request* was sent to */ | 843 | /* store the address the *request* was sent to */ |
851 | addto_lists(dht, dht->send_nodes[send_nodes_index - 1].ip_port, packet + 1); | 844 | addto_lists(dht, dht->send_nodes[send_nodes_index - 1].ip_port, packet + 1); |
852 | 845 | ||
846 | if (dht->assoc) { | ||
847 | IPPTs ippts; | ||
848 | |||
849 | ippts.ip_port = dht->send_nodes[send_nodes_index - 1].ip_port; | ||
850 | ippts.timestamp = dht->send_nodes[send_nodes_index - 1].timestamp; | ||
851 | Assoc_add_entry(dht->assoc, packet + 1, &ippts, &source); | ||
852 | } | ||
853 | |||
853 | *num_nodes_out = num_nodes; | 854 | *num_nodes_out = num_nodes; |
854 | 855 | ||
855 | return 0; | 856 | return 0; |
@@ -879,6 +880,13 @@ static int handle_sendnodes(void *object, IP_Port source, uint8_t *packet, uint3 | |||
879 | 880 | ||
880 | send_ping_request(dht->ping, ipp, nodes4_list[i].client_id); | 881 | send_ping_request(dht->ping, ipp, nodes4_list[i].client_id); |
881 | returnedip_ports(dht, ipp, nodes4_list[i].client_id, packet + 1); | 882 | returnedip_ports(dht, ipp, nodes4_list[i].client_id, packet + 1); |
883 | |||
884 | if (dht->assoc) { | ||
885 | IPPTs ippts; | ||
886 | ippts.ip_port = ipp; | ||
887 | ippts.timestamp = 0; | ||
888 | Assoc_add_entry(dht->assoc, nodes4_list[i].client_id, &ippts, NULL); | ||
889 | } | ||
882 | } | 890 | } |
883 | 891 | ||
884 | return 0; | 892 | return 0; |
@@ -902,6 +910,13 @@ static int handle_sendnodes_ipv6(void *object, IP_Port source, uint8_t *packet, | |||
902 | if (ipport_isset(&nodes_list[i].ip_port)) { | 910 | if (ipport_isset(&nodes_list[i].ip_port)) { |
903 | send_ping_request(dht->ping, nodes_list[i].ip_port, nodes_list[i].client_id); | 911 | send_ping_request(dht->ping, nodes_list[i].ip_port, nodes_list[i].client_id); |
904 | returnedip_ports(dht, nodes_list[i].ip_port, nodes_list[i].client_id, packet + 1); | 912 | returnedip_ports(dht, nodes_list[i].ip_port, nodes_list[i].client_id, packet + 1); |
913 | |||
914 | if (dht->assoc) { | ||
915 | IPPTs ippts; | ||
916 | ippts.ip_port = nodes_list[i].ip_port; | ||
917 | ippts.timestamp = 0; | ||
918 | Assoc_add_entry(dht->assoc, nodes_list[i].client_id, &ippts, NULL); | ||
919 | } | ||
905 | } | 920 | } |
906 | 921 | ||
907 | return 0; | 922 | return 0; |
@@ -950,7 +965,38 @@ int DHT_addfriend(DHT *dht, uint8_t *client_id) | |||
950 | 965 | ||
951 | dht->friends_list[dht->num_friends].nat.NATping_id = ((uint64_t)random_int() << 32) + random_int(); | 966 | dht->friends_list[dht->num_friends].nat.NATping_id = ((uint64_t)random_int() << 32) + random_int(); |
952 | ++dht->num_friends; | 967 | ++dht->num_friends; |
953 | get_bunchnodes(dht, dht->close_clientlist, LCLIENT_LIST, MAX_FRIEND_CLIENTS, client_id);/*TODO: make this better?*/ | 968 | |
969 | if (dht->assoc) { | ||
970 | /* get up to MAX_FRIEND_CLIENTS connectable nodes */ | ||
971 | DHT_Friend *friend = &dht->friends_list[dht->num_friends - 1]; | ||
972 | |||
973 | Assoc_close_entries close_entries; | ||
974 | memset(&close_entries, 0, sizeof(close_entries)); | ||
975 | close_entries.wanted_id = client_id; | ||
976 | close_entries.count_good = MAX_FRIEND_CLIENTS / 2; | ||
977 | close_entries.count = MAX_FRIEND_CLIENTS; | ||
978 | close_entries.result = calloc(MAX_FRIEND_CLIENTS, sizeof(*close_entries.result)); | ||
979 | |||
980 | uint8_t i, found = Assoc_get_close_entries(dht->assoc, &close_entries); | ||
981 | |||
982 | for (i = 0; i < found; i++) | ||
983 | memcpy(&friend->client_list[i], close_entries.result[i], sizeof(*close_entries.result[i])); | ||
984 | |||
985 | if (found) { | ||
986 | /* send getnodes to the "best" entry */ | ||
987 | Client_data *client = &friend->client_list[0]; | ||
988 | |||
989 | if (ipport_isset(&client->assoc4.ip_port)) | ||
990 | getnodes(dht, client->assoc4.ip_port, client->client_id, friend->client_id); | ||
991 | |||
992 | if (ipport_isset(&client->assoc6.ip_port)) | ||
993 | getnodes(dht, client->assoc6.ip_port, client->client_id, friend->client_id); | ||
994 | } | ||
995 | } | ||
996 | |||
997 | /*TODO: make this better?*/ | ||
998 | get_bunchnodes(dht, dht->close_clientlist, LCLIENT_LIST, MAX_FRIEND_CLIENTS, client_id); | ||
999 | |||
954 | return 0; | 1000 | return 0; |
955 | } | 1001 | } |
956 | 1002 | ||
@@ -1085,6 +1131,13 @@ static void do_Close(DHT *dht) | |||
1085 | 1131 | ||
1086 | void DHT_bootstrap(DHT *dht, IP_Port ip_port, uint8_t *public_key) | 1132 | void DHT_bootstrap(DHT *dht, IP_Port ip_port, uint8_t *public_key) |
1087 | { | 1133 | { |
1134 | if (dht->assoc) { | ||
1135 | IPPTs ippts; | ||
1136 | ippts.ip_port = ip_port; | ||
1137 | ippts.timestamp = 0; | ||
1138 | Assoc_add_entry(dht->assoc, public_key, &ippts, NULL); | ||
1139 | } | ||
1140 | |||
1088 | getnodes(dht, ip_port, public_key, dht->c->self_public_key); | 1141 | getnodes(dht, ip_port, public_key, dht->c->self_public_key); |
1089 | } | 1142 | } |
1090 | int DHT_bootstrap_from_address(DHT *dht, const char *address, uint8_t ipv6enabled, | 1143 | int DHT_bootstrap_from_address(DHT *dht, const char *address, uint8_t ipv6enabled, |
@@ -1556,6 +1609,9 @@ DHT *new_DHT(Net_Crypto *c) | |||
1556 | init_cryptopackets(dht); | 1609 | init_cryptopackets(dht); |
1557 | cryptopacket_registerhandler(c, CRYPTO_PACKET_NAT_PING, &handle_NATping, dht); | 1610 | cryptopacket_registerhandler(c, CRYPTO_PACKET_NAT_PING, &handle_NATping, dht); |
1558 | 1611 | ||
1612 | /* dhtassoc is not mandatory for now */ | ||
1613 | dht->assoc = new_Assoc(dht); | ||
1614 | |||
1559 | return dht; | 1615 | return dht; |
1560 | } | 1616 | } |
1561 | 1617 | ||
@@ -1570,6 +1626,7 @@ void do_DHT(DHT *dht) | |||
1570 | } | 1626 | } |
1571 | void kill_DHT(DHT *dht) | 1627 | void kill_DHT(DHT *dht) |
1572 | { | 1628 | { |
1629 | kill_Assoc(dht->assoc); | ||
1573 | kill_ping(dht->ping); | 1630 | kill_ping(dht->ping); |
1574 | free(dht->friends_list); | 1631 | free(dht->friends_list); |
1575 | free(dht); | 1632 | free(dht); |
diff --git a/toxcore/DHT.h b/toxcore/DHT.h index 360773ff..dc8b0e38 100644 --- a/toxcore/DHT.h +++ b/toxcore/DHT.h | |||
@@ -43,6 +43,22 @@ | |||
43 | /* Maximum newly announced nodes to ping per TIME_TOPING seconds. */ | 43 | /* Maximum newly announced nodes to ping per TIME_TOPING seconds. */ |
44 | #define MAX_TOPING 16 | 44 | #define MAX_TOPING 16 |
45 | 45 | ||
46 | /* Ping timeout in seconds */ | ||
47 | #define PING_TIMEOUT 5 | ||
48 | |||
49 | /* Ping interval in seconds for each node in our lists. */ | ||
50 | #define PING_INTERVAL 60 | ||
51 | |||
52 | /* The number of seconds for a non responsive node to become bad. */ | ||
53 | #define PINGS_MISSED_NODE_GOES_BAD 3 | ||
54 | #define PING_ROUNDTRIP 2 | ||
55 | #define BAD_NODE_TIMEOUT (PING_INTERVAL + PINGS_MISSED_NODE_GOES_BAD * PING_INTERVAL + PING_ROUNDTRIP) | ||
56 | |||
57 | typedef struct { | ||
58 | IP_Port ip_port; | ||
59 | uint64_t timestamp; | ||
60 | } IPPTs; | ||
61 | |||
46 | typedef struct { | 62 | typedef struct { |
47 | IP_Port ip_port; | 63 | IP_Port ip_port; |
48 | uint64_t timestamp; | 64 | uint64_t timestamp; |
@@ -117,22 +133,25 @@ typedef struct { | |||
117 | uint64_t timestamp; | 133 | uint64_t timestamp; |
118 | } pinged_t; | 134 | } pinged_t; |
119 | 135 | ||
136 | typedef struct PING PING; | ||
137 | typedef struct Assoc Assoc; | ||
138 | |||
120 | typedef struct { | 139 | typedef struct { |
121 | Net_Crypto *c; | 140 | Net_Crypto *c; |
122 | 141 | ||
123 | Client_data close_clientlist[LCLIENT_LIST]; | 142 | Client_data close_clientlist[LCLIENT_LIST]; |
143 | uint64_t close_lastgetnodes; | ||
144 | |||
124 | DHT_Friend *friends_list; | 145 | DHT_Friend *friends_list; |
125 | uint16_t num_friends; | 146 | uint16_t num_friends; |
126 | uint64_t close_lastgetnodes; | ||
127 | 147 | ||
128 | pinged_t send_nodes[LSEND_NODES_ARRAY]; | 148 | pinged_t send_nodes[LSEND_NODES_ARRAY]; |
129 | void *ping; | 149 | PING *ping; |
150 | |||
151 | Assoc *assoc; | ||
130 | } DHT; | 152 | } DHT; |
131 | /*----------------------------------------------------------------------------------*/ | 153 | /*----------------------------------------------------------------------------------*/ |
132 | 154 | ||
133 | |||
134 | Client_data *DHT_get_close_list(DHT *dht); | ||
135 | |||
136 | /* Add a new friend to the friends list. | 155 | /* Add a new friend to the friends list. |
137 | * client_id must be CLIENT_ID_SIZE bytes long. | 156 | * client_id must be CLIENT_ID_SIZE bytes long. |
138 | * | 157 | * |
@@ -256,5 +275,5 @@ int DHT_isconnected(DHT *dht); | |||
256 | 275 | ||
257 | void addto_lists(DHT *dht, IP_Port ip_port, uint8_t *client_id); | 276 | void addto_lists(DHT *dht, IP_Port ip_port, uint8_t *client_id); |
258 | 277 | ||
259 | |||
260 | #endif | 278 | #endif |
279 | |||
diff --git a/toxcore/Makefile.inc b/toxcore/Makefile.inc index 116a3e29..8208c548 100644 --- a/toxcore/Makefile.inc +++ b/toxcore/Makefile.inc | |||
@@ -27,6 +27,8 @@ libtoxcore_la_SOURCES = ../toxcore/DHT.h \ | |||
27 | ../toxcore/util.c \ | 27 | ../toxcore/util.c \ |
28 | ../toxcore/group_chats.h \ | 28 | ../toxcore/group_chats.h \ |
29 | ../toxcore/group_chats.c \ | 29 | ../toxcore/group_chats.c \ |
30 | ../toxcore/assoc.h \ | ||
31 | ../toxcore/assoc.c \ | ||
30 | ../toxcore/misc_tools.h | 32 | ../toxcore/misc_tools.h |
31 | 33 | ||
32 | libtoxcore_la_CFLAGS = -I$(top_srcdir) \ | 34 | libtoxcore_la_CFLAGS = -I$(top_srcdir) \ |
diff --git a/toxcore/Messenger.c b/toxcore/Messenger.c index 57cafc3f..49cffc98 100644 --- a/toxcore/Messenger.c +++ b/toxcore/Messenger.c | |||
@@ -26,9 +26,11 @@ | |||
26 | #endif | 26 | #endif |
27 | 27 | ||
28 | #include "Messenger.h" | 28 | #include "Messenger.h" |
29 | #include "assoc.h" | ||
29 | #include "network.h" | 30 | #include "network.h" |
30 | #include "util.h" | 31 | #include "util.h" |
31 | 32 | ||
33 | |||
32 | #define MIN(a,b) (((a)<(b))?(a):(b)) | 34 | #define MIN(a,b) (((a)<(b))?(a):(b)) |
33 | 35 | ||
34 | 36 | ||
@@ -784,7 +786,7 @@ int add_groupchat(Messenger *m) | |||
784 | 786 | ||
785 | for (i = 0; i < m->numchats; ++i) { | 787 | for (i = 0; i < m->numchats; ++i) { |
786 | if (m->chats[i] == NULL) { | 788 | if (m->chats[i] == NULL) { |
787 | Group_Chat *newchat = new_groupchat(m->net); | 789 | Group_Chat *newchat = new_groupchat(m->net, m->dht->assoc); |
788 | 790 | ||
789 | if (newchat == NULL) | 791 | if (newchat == NULL) |
790 | return -1; | 792 | return -1; |
@@ -803,7 +805,7 @@ int add_groupchat(Messenger *m) | |||
803 | if (temp == NULL) | 805 | if (temp == NULL) |
804 | return -1; | 806 | return -1; |
805 | 807 | ||
806 | temp[m->numchats] = new_groupchat(m->net); | 808 | temp[m->numchats] = new_groupchat(m->net, m->dht->assoc); |
807 | 809 | ||
808 | if (temp[m->numchats] == NULL) | 810 | if (temp[m->numchats] == NULL) |
809 | return -1; | 811 | return -1; |
@@ -2006,6 +2008,9 @@ static int messenger_load_state_callback(void *outer, uint8_t *data, uint32_t le | |||
2006 | if (length == crypto_box_PUBLICKEYBYTES + crypto_box_SECRETKEYBYTES + sizeof(uint32_t)) { | 2008 | if (length == crypto_box_PUBLICKEYBYTES + crypto_box_SECRETKEYBYTES + sizeof(uint32_t)) { |
2007 | set_nospam(&(m->fr), *(uint32_t *)data); | 2009 | set_nospam(&(m->fr), *(uint32_t *)data); |
2008 | load_keys(m->net_crypto, &data[sizeof(uint32_t)]); | 2010 | load_keys(m->net_crypto, &data[sizeof(uint32_t)]); |
2011 | |||
2012 | if (m->dht->assoc) | ||
2013 | Assoc_self_client_id_changed(m->dht->assoc); | ||
2009 | } else | 2014 | } else |
2010 | return -1; /* critical */ | 2015 | return -1; /* critical */ |
2011 | 2016 | ||
diff --git a/toxcore/assoc.c b/toxcore/assoc.c new file mode 100644 index 00000000..7368819d --- /dev/null +++ b/toxcore/assoc.c | |||
@@ -0,0 +1,739 @@ | |||
1 | |||
2 | #include "DHT.h" | ||
3 | #include "assoc.h" | ||
4 | #include "ping.h" | ||
5 | |||
6 | #include "LAN_discovery.h" | ||
7 | |||
8 | #include "util.h" | ||
9 | |||
10 | /* | ||
11 | * BASIC OVERVIEW: | ||
12 | * | ||
13 | * Hash: The client_id is hashed with a local hash function. | ||
14 | * Hashes are used in multiple places for searching. | ||
15 | * Bucket: The first n bits of the client_id are used to | ||
16 | * select a bucket. This speeds up sorting, but the more | ||
17 | * important reason is to enforce a spread in the space of | ||
18 | * client_ids available. | ||
19 | * | ||
20 | * | ||
21 | * Candidates: | ||
22 | * | ||
23 | * Candidates are kept in buckets of hash tables. The hash | ||
24 | * function is calculated from the client_id. Up to | ||
25 | * HASH_COLLIDE_COUNT alternative positions are tried if | ||
26 | * the inital position is already used by a different entry. | ||
27 | * The collision function is multiplicative, not additive. | ||
28 | * | ||
29 | * A new candidate can bump an existing candidate, if it is | ||
30 | * more "desirable": Seen beats Heard. | ||
31 | */ | ||
32 | |||
33 | /* candidates: number of bucket bits/buckets | ||
34 | * if this is raised dramatically, DISTANCE_INDEX_DISTANCE_BITS | ||
35 | * might have to be adjusted */ | ||
36 | #define CANDIDATES_BUCKET_BITS 8 | ||
37 | #define CANDIDATES_BUCKET_COUNT (1 << CANDIDATES_BUCKET_BITS) | ||
38 | |||
39 | /* candidates: number of candidates to keep PER BUCKET (should be a prime | ||
40 | * for hash reasons, other primes e.g. 251, 509, 1021, 2039, 4093, 8191) | ||
41 | * total number of candidates is therefore less than (BUCKET_COUNT * TO_KEEP), | ||
42 | * given that a hash table is usually filling decently to around 50%, the | ||
43 | * total long-term number of entries will be around 0.5 * 256 * 251 ~= 32k | ||
44 | * | ||
45 | * if this is raised dramatically, DISTANCE_INDEX_DISTANCE_BITS | ||
46 | * might have to be adjusted */ | ||
47 | #define CANDIDATES_TO_KEEP 251 | ||
48 | |||
49 | /* candidates: alternative places for the same hash value */ | ||
50 | #define HASH_COLLIDE_COUNT 5 | ||
51 | |||
52 | /* candidates: bump entries: timeout values for seen/heard to be considered of value */ | ||
53 | #define CANDIDATES_SEEN_TIMEOUT 1800 | ||
54 | #define CANDIDATES_HEARD_TIMEOUT 600 | ||
55 | |||
56 | /* distance/index: index size & access mask */ | ||
57 | #define DISTANCE_INDEX_INDEX_BITS (64 - DISTANCE_INDEX_DISTANCE_BITS) | ||
58 | #define DISTANCE_INDEX_INDEX_MASK ((1 << DISTANCE_INDEX_INDEX_BITS) - 1) | ||
59 | |||
60 | /* types to stay consistent */ | ||
61 | #if (CANDIDATES_BUCKET_BITS <= 16) | ||
62 | typedef uint16_t bucket_t; | ||
63 | #else | ||
64 | typedef uint32_t bucket_t; | ||
65 | #endif | ||
66 | typedef uint16_t usecnt_t; | ||
67 | typedef uint32_t hash_t; | ||
68 | |||
69 | /* abbreviations ... */ | ||
70 | typedef Assoc_distance_relative_callback dist_rel_cb; | ||
71 | typedef Assoc_distance_absolute_callback dist_abs_cb; | ||
72 | |||
73 | /* | ||
74 | * Client_data wrapped with additional data | ||
75 | */ | ||
76 | typedef struct Client_entry { | ||
77 | hash_t hash; | ||
78 | |||
79 | /* shortcuts & rumors: timers and data */ | ||
80 | uint64_t seen_at; | ||
81 | uint64_t heard_at; | ||
82 | |||
83 | uint16_t seen_family; | ||
84 | uint16_t heard_family; | ||
85 | |||
86 | IP_Port assoc_heard4; | ||
87 | IP_Port assoc_heard6; | ||
88 | |||
89 | Client_data client; | ||
90 | } Client_entry; | ||
91 | |||
92 | typedef struct candidates_bucket { | ||
93 | Client_entry list[CANDIDATES_TO_KEEP]; /* hashed list (with holes) */ | ||
94 | } candidates_bucket; | ||
95 | |||
96 | typedef struct Assoc { | ||
97 | DHT *dht; /* for ping/getnodes */ | ||
98 | hash_t self_hash; /* hash of self_client_id */ | ||
99 | uint8_t *self_client_id; /* don't store entries for this */ | ||
100 | |||
101 | /* association centralization: clients not in use */ | ||
102 | candidates_bucket candidates[CANDIDATES_BUCKET_COUNT]; | ||
103 | } Assoc; | ||
104 | |||
105 | /*****************************************************************************/ | ||
106 | /* HELPER FUNCTIONS */ | ||
107 | /*****************************************************************************/ | ||
108 | |||
109 | /* the complete distance would be CLIENT_ID_SIZE long... | ||
110 | * returns DISTANCE_INDEX_DISTANCE_BITS valid bits */ | ||
111 | static uint64_t id_distance(Assoc *assoc, void *callback_data, uint8_t *id_ref, uint8_t *id_test) | ||
112 | { | ||
113 | /* with BIG_ENDIAN, this would be a one-liner... */ | ||
114 | uint64_t retval = 0; | ||
115 | |||
116 | uint8_t pos = 0, bits = DISTANCE_INDEX_DISTANCE_BITS; | ||
117 | |||
118 | while (bits > 8) { | ||
119 | uint8_t distance = abs((int8_t)id_ref[pos] ^ (int8_t)id_test[pos]); | ||
120 | retval = (retval << 8) | distance; | ||
121 | bits -= 8; | ||
122 | pos++; | ||
123 | } | ||
124 | |||
125 | return (retval << bits) | ((id_ref[pos] ^ id_test[pos]) >> (8 - bits)); | ||
126 | } | ||
127 | |||
128 | /* qsort() callback for a sorting by id_distance() values */ | ||
129 | static int dist_index_comp(const void *a, const void *b) | ||
130 | { | ||
131 | const uint64_t *_a = a; | ||
132 | const uint64_t *_b = b; | ||
133 | |||
134 | if (*_a < *_b) | ||
135 | return -1; | ||
136 | |||
137 | if (*_a > *_b) | ||
138 | return 1; | ||
139 | |||
140 | return 0; | ||
141 | } | ||
142 | |||
143 | /* get actual entry to a distance_index */ | ||
144 | static Client_entry *dist_index_entry(Assoc *assoc, uint64_t dist_ind) | ||
145 | { | ||
146 | if ((dist_ind & DISTANCE_INDEX_INDEX_MASK) == DISTANCE_INDEX_INDEX_MASK) | ||
147 | return NULL; | ||
148 | |||
149 | size_t offset = CANDIDATES_BUCKET_COUNT * CANDIDATES_TO_KEEP; | ||
150 | uint32_t index = dist_ind & DISTANCE_INDEX_INDEX_MASK; | ||
151 | |||
152 | if (index < offset) { | ||
153 | bucket_t b_id = index / CANDIDATES_TO_KEEP; | ||
154 | candidates_bucket *cnd_bckt = &assoc->candidates[b_id]; | ||
155 | size_t b_ix = index % CANDIDATES_TO_KEEP; | ||
156 | Client_entry *entry = &cnd_bckt->list[b_ix]; | ||
157 | |||
158 | if (entry->hash) | ||
159 | return entry; | ||
160 | } | ||
161 | |||
162 | return NULL; | ||
163 | } | ||
164 | |||
165 | /* get actual entry's client_id to a distance_index */ | ||
166 | static uint8_t *dist_index_id(Assoc *assoc, uint64_t dist_ind) | ||
167 | { | ||
168 | Client_entry *entry = dist_index_entry(assoc, dist_ind); | ||
169 | |||
170 | if (entry) | ||
171 | return entry->client.client_id; | ||
172 | |||
173 | return NULL; | ||
174 | } | ||
175 | |||
176 | /* sorts first .. last, i.e. last is included */ | ||
177 | static void dist_index_bubble(Assoc *assoc, uint64_t *dist_list, size_t first, size_t last, uint8_t *id, | ||
178 | void *custom_data, Assoc_distance_relative_callback dist_rel_func) | ||
179 | { | ||
180 | size_t i, k; | ||
181 | |||
182 | for (i = first; i <= last; i++) { | ||
183 | uint8_t *id1 = dist_index_id(assoc, dist_list[i]); | ||
184 | |||
185 | for (k = i + 1; k <= last; k++) { | ||
186 | uint8_t *id2 = dist_index_id(assoc, dist_list[k]); | ||
187 | |||
188 | if (id1 && id2) | ||
189 | if (dist_rel_func(assoc, custom_data, id, id1, id2) == 2) { | ||
190 | uint64_t swap = dist_list[i]; | ||
191 | dist_list[i] = dist_list[k]; | ||
192 | dist_list[k] = swap; | ||
193 | } | ||
194 | } | ||
195 | } | ||
196 | } | ||
197 | |||
198 | /* TODO: Check that there isn't a function like this elsewhere hidden. | ||
199 | * E.g. the one which creates a handshake_id isn't usable for this, it must | ||
200 | * always map the same ID to the same hash. | ||
201 | * | ||
202 | * Result is NOT MAPPED to CANDIDATES_TO_KEEP range, i.e. map before using | ||
203 | * it for list access. */ | ||
204 | static hash_t id_hash(uint8_t *id) | ||
205 | { | ||
206 | uint32_t i, res = 0x19a64e82; | ||
207 | |||
208 | for (i = 0; i < CLIENT_ID_SIZE; i++) | ||
209 | res = ((res << 1) ^ (res >> 30)) ^ id[i]; | ||
210 | |||
211 | /* can't have zero as hash, a) marks an unused spot, | ||
212 | * and b) slots for collision are multiplied, for | ||
213 | * the latter reason also remap 1 .. 7 */ | ||
214 | if ((res % CANDIDATES_TO_KEEP) < 8) | ||
215 | res = res + (CANDIDATES_TO_KEEP >> 2); | ||
216 | |||
217 | return res; | ||
218 | } | ||
219 | |||
220 | /* up to HASH_COLLIDE_COUNT calls to different spots, | ||
221 | * result IS mapped to CANDIDATES_TO_KEEP range */ | ||
222 | static hash_t hash_collide(hash_t hash) | ||
223 | { | ||
224 | uint64_t hash64 = hash % CANDIDATES_TO_KEEP; | ||
225 | hash64 = (hash64 * 101) % CANDIDATES_TO_KEEP; | ||
226 | |||
227 | hash_t retval = hash64; | ||
228 | |||
229 | /* this should never happen when CANDIDATES_TO_KEEP is prime and hash not a multiple | ||
230 | * (id_hash() checks for a multiple and returns a different hash in that case) | ||
231 | * | ||
232 | * ( 1 .. (prime - 1) is a group over multiplication and every number has its inverse | ||
233 | * in the group, so no multiplication should ever end on zero as long neither | ||
234 | * of the two factors was zero-equivalent ) | ||
235 | * | ||
236 | * BUT: because the usage of the word "never" invokes Murphy's law, catch it */ | ||
237 | if (!retval) | ||
238 | retval = 1; | ||
239 | |||
240 | return retval; | ||
241 | } | ||
242 | |||
243 | /* returns the "seen" assoc related to the ipp */ | ||
244 | static IPPTsPng *entry_assoc(Client_entry *cl_entry, IP_Port *ipp) | ||
245 | { | ||
246 | if (!cl_entry) | ||
247 | return NULL; | ||
248 | |||
249 | if (ipp->ip.family == AF_INET) | ||
250 | return &cl_entry->client.assoc4; | ||
251 | |||
252 | if (ipp->ip.family == AF_INET6) | ||
253 | return &cl_entry->client.assoc6; | ||
254 | |||
255 | return NULL; | ||
256 | } | ||
257 | |||
258 | /* returns the "heard" assoc related to the ipp */ | ||
259 | static IP_Port *entry_heard_get(Client_entry *entry, IP_Port *ipp) | ||
260 | { | ||
261 | if (ipp->ip.family == AF_INET) | ||
262 | return &entry->assoc_heard4; | ||
263 | else if (ipp->ip.family == AF_INET6) | ||
264 | return &entry->assoc_heard6; | ||
265 | else | ||
266 | return NULL; | ||
267 | } | ||
268 | |||
269 | /* store a "heard" entry | ||
270 | * overwrites empty entry, does NOT overwrite non-LAN ip with | ||
271 | * LAN ip | ||
272 | * | ||
273 | * returns 1 if the entry did change */ | ||
274 | static int entry_heard_store(Client_entry *entry, IPPTs *ippts) | ||
275 | { | ||
276 | if (!entry || !ippts) | ||
277 | return 0; | ||
278 | |||
279 | if (!ipport_isset(&ippts->ip_port)) | ||
280 | return 0; | ||
281 | |||
282 | IP_Port *heard, *ipp = &ippts->ip_port; | ||
283 | |||
284 | if (ipp->ip.family == AF_INET) | ||
285 | heard = &entry->assoc_heard4; | ||
286 | else if (ipp->ip.family == AF_INET6) | ||
287 | heard = &entry->assoc_heard6; | ||
288 | else | ||
289 | return 0; | ||
290 | |||
291 | if (ipport_equal(ipp, heard)) | ||
292 | return 0; | ||
293 | |||
294 | if (!ipport_isset(heard)) { | ||
295 | *heard = *ipp; | ||
296 | entry->heard_at = ippts->timestamp; | ||
297 | entry->heard_family = ipp->ip.family; | ||
298 | return 1; | ||
299 | } | ||
300 | |||
301 | /* don't destroy a good address with a crappy one | ||
302 | * (unless we're very timed out) */ | ||
303 | uint8_t LAN_ipp = LAN_ip(ipp->ip) == 0; | ||
304 | uint8_t LAN_entry = LAN_ip(heard->ip) == 0; | ||
305 | |||
306 | if (LAN_ipp && !LAN_entry && !is_timeout(entry->heard_at, CANDIDATES_HEARD_TIMEOUT)) | ||
307 | return 0; | ||
308 | |||
309 | *heard = *ipp; | ||
310 | entry->heard_at = ippts->timestamp; | ||
311 | entry->heard_family = ipp->ip.family; | ||
312 | |||
313 | return 1; | ||
314 | } | ||
315 | |||
316 | /* maps Assoc callback signature to id_closest() */ | ||
317 | static int assoc_id_closest(Assoc *assoc, void *callback_data, uint8_t *client_id, uint8_t *client_id1, | ||
318 | uint8_t *client_id2) | ||
319 | { | ||
320 | return id_closest(client_id, client_id1, client_id2); | ||
321 | } | ||
322 | |||
323 | static bucket_t id_bucket(uint8_t *id, uint8_t bits) | ||
324 | { | ||
325 | /* return the first "bits" bits of id */ | ||
326 | bucket_t retval = 0; | ||
327 | |||
328 | uint8_t pos = 0; | ||
329 | |||
330 | while (bits > 8) { | ||
331 | retval = (retval << 8) | id[pos++]; | ||
332 | bits -= 8; | ||
333 | } | ||
334 | |||
335 | return (retval << bits) | (id[pos] >> (8 - bits)); | ||
336 | } | ||
337 | |||
338 | /*****************************************************************************/ | ||
339 | /* CANDIDATES FUNCTIONS */ | ||
340 | /*****************************************************************************/ | ||
341 | |||
342 | |||
343 | static bucket_t candidates_id_bucket(uint8_t *id) | ||
344 | { | ||
345 | return id_bucket(id, CANDIDATES_BUCKET_BITS); | ||
346 | } | ||
347 | |||
348 | static uint8_t candidates_search(Assoc *assoc, uint8_t *id, hash_t hash, Client_entry **entryptr) | ||
349 | { | ||
350 | bucket_t bucket = candidates_id_bucket(id); | ||
351 | candidates_bucket *cnd_bckt = &assoc->candidates[bucket]; | ||
352 | size_t coll, pos = hash % CANDIDATES_TO_KEEP; | ||
353 | |||
354 | for (coll = 0; coll < HASH_COLLIDE_COUNT; pos = hash_collide(pos) , coll++) { | ||
355 | Client_entry *entry = &cnd_bckt->list[pos]; | ||
356 | |||
357 | if (entry->hash == hash) | ||
358 | if (id_equal(entry->client.client_id, id)) { | ||
359 | *entryptr = entry; | ||
360 | return 1; | ||
361 | } | ||
362 | } | ||
363 | |||
364 | *entryptr = NULL; | ||
365 | return 0; | ||
366 | } | ||
367 | |||
368 | static void candidates_update_assoc(Assoc *assoc, Client_entry *entry, IPPTs *ippts_send, IP_Port *ipp_recv) | ||
369 | { | ||
370 | if (!assoc || !entry || !ippts_send) | ||
371 | return; | ||
372 | |||
373 | IPPTsPng *ipptsp = entry_assoc(entry, &ippts_send->ip_port); | ||
374 | |||
375 | if (!ipptsp) | ||
376 | return; | ||
377 | |||
378 | /* do NOT do anything related to wanted, that's handled outside, | ||
379 | * just update the assoc (in the most sensible way) | ||
380 | */ | ||
381 | if (ipp_recv) { | ||
382 | ipptsp->ip_port = ippts_send->ip_port; | ||
383 | ipptsp->timestamp = ippts_send->timestamp; | ||
384 | ipptsp->ret_ip_port = *ipp_recv; | ||
385 | ipptsp->ret_timestamp = unix_time(); | ||
386 | |||
387 | entry->seen_at = unix_time(); | ||
388 | entry->seen_family = ippts_send->ip_port.ip.family; | ||
389 | |||
390 | return; | ||
391 | } | ||
392 | |||
393 | entry_heard_store(entry, ippts_send); | ||
394 | } | ||
395 | |||
396 | static uint8_t candidates_create_internal(Assoc *assoc, hash_t hash, uint8_t *id, uint8_t seen, | ||
397 | bucket_t *bucketptr, size_t *posptr) | ||
398 | { | ||
399 | if (!assoc || !id || !bucketptr || !posptr) | ||
400 | return 0; | ||
401 | |||
402 | bucket_t bucket = candidates_id_bucket(id); | ||
403 | candidates_bucket *cnd_bckt = &assoc->candidates[bucket]; | ||
404 | |||
405 | size_t coll, pos = hash % CANDIDATES_TO_KEEP, check; | ||
406 | size_t pos_check[6]; | ||
407 | |||
408 | memset(pos_check, 0, sizeof(pos_check)); | ||
409 | |||
410 | for (coll = 0; coll < HASH_COLLIDE_COUNT; pos = hash_collide(pos) , coll++) { | ||
411 | Client_entry *entry = &cnd_bckt->list[pos]; | ||
412 | |||
413 | /* unset */ | ||
414 | if (!entry->hash) { | ||
415 | *bucketptr = bucket; | ||
416 | *posptr = pos; | ||
417 | |||
418 | return 1; | ||
419 | } | ||
420 | |||
421 | /* 0. bad | ||
422 | * 1. seen bad, heard good | ||
423 | * 2. seen good */ | ||
424 | if (!is_timeout(entry->seen_at, CANDIDATES_SEEN_TIMEOUT)) | ||
425 | check = 2; | ||
426 | else if (!is_timeout(entry->heard_at, CANDIDATES_HEARD_TIMEOUT)) | ||
427 | check = 1; | ||
428 | else | ||
429 | check = 0; | ||
430 | |||
431 | if (!pos_check[check]) | ||
432 | pos_check[check] = pos + 1; | ||
433 | } | ||
434 | |||
435 | /* seen can bump heard&bad, heard can bump only bad */ | ||
436 | size_t i, pos_max = seen ? 2 : 1; | ||
437 | |||
438 | for (i = 0; i < pos_max; i++) | ||
439 | if (pos_check[i]) { | ||
440 | *bucketptr = bucket; | ||
441 | *posptr = pos_check[i] - 1; | ||
442 | |||
443 | return 1; | ||
444 | } | ||
445 | |||
446 | return 0; | ||
447 | } | ||
448 | |||
449 | static void candidates_create_new(Assoc *assoc, hash_t hash, uint8_t *id, | ||
450 | IPPTs *ippts_send, IP_Port *ipp_recv) | ||
451 | { | ||
452 | if (!assoc || !id || !ippts_send) | ||
453 | return; | ||
454 | |||
455 | bucket_t bucket; | ||
456 | size_t pos; | ||
457 | |||
458 | if (!candidates_create_internal(assoc, hash, id, ipp_recv != NULL, &bucket, &pos)) | ||
459 | return; | ||
460 | |||
461 | candidates_bucket *cnd_bckt = &assoc->candidates[bucket]; | ||
462 | Client_entry *entry = &cnd_bckt->list[pos]; | ||
463 | memset(entry, 0, sizeof(*entry)); | ||
464 | |||
465 | IPPTsPng *ipptsp = entry_assoc(entry, &ippts_send->ip_port); | ||
466 | |||
467 | if (!ipptsp) | ||
468 | return; | ||
469 | |||
470 | entry->hash = hash; | ||
471 | id_copy(entry->client.client_id, id); | ||
472 | |||
473 | if (ipp_recv && !ipport_isset(ipp_recv)) | ||
474 | ipp_recv = NULL; | ||
475 | |||
476 | if (ipp_recv) { | ||
477 | entry->seen_at = unix_time(); | ||
478 | entry->seen_family = ippts_send->ip_port.ip.family; | ||
479 | |||
480 | ipptsp->ip_port = ippts_send->ip_port; | ||
481 | ipptsp->timestamp = ippts_send->timestamp; | ||
482 | ipptsp->ret_ip_port = *ipp_recv; | ||
483 | ipptsp->ret_timestamp = unix_time(); | ||
484 | } else { | ||
485 | IP_Port *heard = entry_heard_get(entry, &ippts_send->ip_port); | ||
486 | |||
487 | if (heard) { | ||
488 | entry->heard_at = ippts_send->timestamp; | ||
489 | entry->heard_family = ippts_send->ip_port.ip.family; | ||
490 | |||
491 | *heard = ippts_send->ip_port; | ||
492 | } | ||
493 | } | ||
494 | } | ||
495 | |||
496 | /*****************************************************************************/ | ||
497 | |||
498 | static void client_id_self_update(Assoc *assoc) | ||
499 | { | ||
500 | if (assoc->self_hash || !assoc->self_client_id) | ||
501 | return; | ||
502 | |||
503 | if (!assoc->self_hash) { | ||
504 | size_t i, sum = 0; | ||
505 | |||
506 | for (i = 0; i < crypto_box_PUBLICKEYBYTES; i++) | ||
507 | sum |= assoc->self_client_id[i]; | ||
508 | |||
509 | if (!sum) | ||
510 | return; | ||
511 | |||
512 | assoc->self_hash = id_hash(assoc->self_client_id); | ||
513 | } | ||
514 | |||
515 | #ifdef LOGGING | ||
516 | loglog("assoc: id is now set, purging cache of self-references...\n"); | ||
517 | #endif | ||
518 | |||
519 | /* if we already added some (or loaded some) entries, | ||
520 | * look and remove if we find a match | ||
521 | */ | ||
522 | bucket_t b_id = candidates_id_bucket(assoc->self_client_id); | ||
523 | candidates_bucket *cnd_bckt = &assoc->candidates[b_id]; | ||
524 | size_t i, pos = assoc->self_hash % CANDIDATES_TO_KEEP; | ||
525 | |||
526 | for (i = 0; i < HASH_COLLIDE_COUNT; pos = hash_collide(pos), i++) { | ||
527 | Client_entry *entry = &cnd_bckt->list[pos]; | ||
528 | |||
529 | if (entry->hash == assoc->self_hash) | ||
530 | if (id_equal(entry->client.client_id, assoc->self_client_id)) | ||
531 | entry->hash = 0; | ||
532 | } | ||
533 | } | ||
534 | |||
535 | /*****************************************************************************/ | ||
536 | /* TRIGGER FUNCTIONS */ | ||
537 | /*****************************************************************************/ | ||
538 | |||
539 | /* Central entry point for new associations: add a new candidate to the cache | ||
540 | * seen should be 0 (zero), if the candidate was announced by someone else, | ||
541 | * seen should be 1 (one), if there is confirmed connectivity (a definite response) | ||
542 | */ | ||
543 | void Assoc_add_entry(Assoc *assoc, uint8_t *id, IPPTs *ippts_send, IP_Port *ipp_recv) | ||
544 | { | ||
545 | if (!assoc || !id || !ippts_send) | ||
546 | return; | ||
547 | |||
548 | if (!assoc->self_hash) { | ||
549 | client_id_self_update(assoc); | ||
550 | |||
551 | if (!assoc->self_hash) | ||
552 | return; | ||
553 | } | ||
554 | |||
555 | if (!ipport_isset(&ippts_send->ip_port)) | ||
556 | return; | ||
557 | |||
558 | if (ipp_recv && !ipport_isset(ipp_recv)) | ||
559 | ipp_recv = NULL; | ||
560 | |||
561 | hash_t hash = id_hash(id); | ||
562 | |||
563 | if (hash == assoc->self_hash) | ||
564 | if (id_equal(id, assoc->self_client_id)) | ||
565 | return; | ||
566 | |||
567 | /* if it's new: | ||
568 | * callback, if there's desire, add to clients, else to candidates | ||
569 | * | ||
570 | * if it's "old": | ||
571 | * if it's client: refresh | ||
572 | * if it's candidate: | ||
573 | * if !ipp_recv, refresh | ||
574 | * if ipp_recv: callback, if there's desire, move to candidates | ||
575 | */ | ||
576 | Client_entry *cnd_entry; | ||
577 | |||
578 | if (!candidates_search(assoc, id, hash, &cnd_entry)) | ||
579 | candidates_create_new(assoc, hash, id, ippts_send, ipp_recv); | ||
580 | else | ||
581 | candidates_update_assoc(assoc, cnd_entry, ippts_send, ipp_recv); | ||
582 | } | ||
583 | |||
584 | /*****************************************************************************/ | ||
585 | /* MAIN USE */ | ||
586 | /*****************************************************************************/ | ||
587 | |||
588 | uint8_t Assoc_get_close_entries(Assoc *assoc, Assoc_close_entries *state) | ||
589 | { | ||
590 | if (!assoc || !state || !state->wanted_id || !state->result) | ||
591 | return 0; | ||
592 | |||
593 | if (!assoc->self_hash) { | ||
594 | client_id_self_update(assoc); | ||
595 | |||
596 | if (!assoc->self_hash) | ||
597 | return 0; | ||
598 | } | ||
599 | |||
600 | if (!state->distance_relative_func) | ||
601 | state->distance_relative_func = assoc_id_closest; | ||
602 | |||
603 | if (!state->distance_absolute_func) | ||
604 | state->distance_absolute_func = id_distance; | ||
605 | |||
606 | size_t clients_offset = CANDIDATES_BUCKET_COUNT * CANDIDATES_TO_KEEP; | ||
607 | size_t dist_list_len = clients_offset; | ||
608 | uint64_t dist_list[dist_list_len]; | ||
609 | memset(dist_list, ~0, dist_list_len * sizeof(dist_list[0])); | ||
610 | bucket_t b; | ||
611 | size_t i; | ||
612 | |||
613 | for (b = 0; b < CANDIDATES_BUCKET_COUNT; b++) { | ||
614 | candidates_bucket *cnd_bckt = &assoc->candidates[b]; | ||
615 | |||
616 | for (i = 0; i < CANDIDATES_TO_KEEP; i++) { | ||
617 | Client_entry *entry = &cnd_bckt->list[i]; | ||
618 | |||
619 | if (entry->hash) { | ||
620 | uint64_t dist = state->distance_absolute_func(assoc, state->custom_data, state->wanted_id, entry->client.client_id); | ||
621 | uint32_t index = b * CANDIDATES_TO_KEEP + i; | ||
622 | dist_list[index] = (dist << DISTANCE_INDEX_INDEX_BITS) | index; | ||
623 | } | ||
624 | } | ||
625 | } | ||
626 | |||
627 | qsort(dist_list, dist_list_len, sizeof(dist_list[0]), dist_index_comp); | ||
628 | |||
629 | /* ok, ok, it's not *perfectly* sorted, because we used an absolute distance | ||
630 | * go over the result and see if we need to "smoothen things out" | ||
631 | * because those should be only very few and short streaks, the worst regularly | ||
632 | * used sorting function aka bubble sort is used */ | ||
633 | uint64_t dist_prev = ~0; | ||
634 | size_t ind_prev = ~0, ind_curr; | ||
635 | size_t len = 1; | ||
636 | |||
637 | for (ind_curr = 0; ind_curr < dist_list_len; ind_curr++) { | ||
638 | /* sorted increasingly, so an invalid entry marks the end */ | ||
639 | if ((dist_list[ind_curr] & DISTANCE_INDEX_INDEX_MASK) == DISTANCE_INDEX_INDEX_MASK) | ||
640 | break; | ||
641 | |||
642 | uint64_t dist_curr = dist_list[ind_curr] >> DISTANCE_INDEX_INDEX_BITS; | ||
643 | |||
644 | if (dist_prev == dist_curr) | ||
645 | len++; | ||
646 | else { | ||
647 | if (len > 1) | ||
648 | dist_index_bubble(assoc, dist_list, ind_prev, ind_curr - 1, state->wanted_id, state->custom_data, | ||
649 | state->distance_relative_func); | ||
650 | |||
651 | dist_prev = dist_curr; | ||
652 | ind_prev = ind_curr; | ||
653 | len = 1; | ||
654 | } | ||
655 | } | ||
656 | |||
657 | if (len > 1) | ||
658 | dist_index_bubble(assoc, dist_list, ind_prev, ind_curr - 1, state->wanted_id, state->custom_data, | ||
659 | state->distance_relative_func); | ||
660 | |||
661 | /* ok, now dist_list is a strictly ascending sorted list of nodes | ||
662 | * a) extract CLOSE_QUOTA_USED clients, not timed out | ||
663 | * b) extract (1 - QUOTA) (better!) clients & candidates, not timed out | ||
664 | * c) save candidates which would be better, if contact can be established */ | ||
665 | size_t client_quota_good = 0, pos = 0; | ||
666 | size_t client_quota_max = state->count_good; | ||
667 | |||
668 | ssize_t taken_last = - 1; | ||
669 | |||
670 | for (i = 0; (i < dist_list_len) && (pos < state->count); i++) { | ||
671 | Client_entry *entry = dist_index_entry(assoc, dist_list[i]); | ||
672 | |||
673 | if (entry && entry->hash) { | ||
674 | if (client_quota_good >= client_quota_max) { | ||
675 | state->result[pos++] = &entry->client; | ||
676 | taken_last = i; | ||
677 | } else if (!is_timeout(entry->seen_at, BAD_NODE_TIMEOUT)) { | ||
678 | state->result[pos++] = &entry->client; | ||
679 | client_quota_good++; | ||
680 | taken_last = i; | ||
681 | } | ||
682 | } | ||
683 | } | ||
684 | |||
685 | /* if we had not enough valid entries the list might still not be filled. | ||
686 | * | ||
687 | * start again from last taken client, but leave out any requirement | ||
688 | */ | ||
689 | if (pos < state->count) { | ||
690 | for (i = taken_last + 1; (i < dist_list_len) && (pos < state->count); i++) { | ||
691 | Client_entry *entry = dist_index_entry(assoc, dist_list[i]); | ||
692 | |||
693 | if (entry && entry->hash) | ||
694 | state->result[pos++] = &entry->client; | ||
695 | } | ||
696 | } | ||
697 | |||
698 | return pos; | ||
699 | } | ||
700 | |||
701 | /*****************************************************************************/ | ||
702 | /* GLOBAL STRUCTURE FUNCTIONS */ | ||
703 | /*****************************************************************************/ | ||
704 | |||
705 | /* create */ | ||
706 | Assoc *new_Assoc(DHT *dht) | ||
707 | { | ||
708 | Assoc *assoc = calloc(1, sizeof(*assoc)); | ||
709 | |||
710 | if (!assoc) | ||
711 | return NULL; | ||
712 | |||
713 | /* dht MAY be NULL! (e.g. testing) */ | ||
714 | if (dht) { | ||
715 | assoc->dht = dht; | ||
716 | assoc->self_client_id = dht->c->self_public_key; | ||
717 | } else { | ||
718 | assoc->self_client_id = malloc(CLIENT_ID_SIZE); | ||
719 | assoc->self_client_id[0] = 42; | ||
720 | } | ||
721 | |||
722 | return assoc; | ||
723 | } | ||
724 | |||
725 | /* own client_id, assocs for this have to be ignored */ | ||
726 | void Assoc_self_client_id_changed(Assoc *assoc) | ||
727 | { | ||
728 | if (assoc) { | ||
729 | assoc->self_hash = 0; | ||
730 | client_id_self_update(assoc); | ||
731 | } | ||
732 | } | ||
733 | |||
734 | /* destroy */ | ||
735 | void kill_Assoc(Assoc *assoc) | ||
736 | { | ||
737 | /* nothing dynamic left in trim */ | ||
738 | free(assoc); | ||
739 | } | ||
diff --git a/toxcore/assoc.h b/toxcore/assoc.h new file mode 100644 index 00000000..e0a899be --- /dev/null +++ b/toxcore/assoc.h | |||
@@ -0,0 +1,73 @@ | |||
1 | |||
2 | #ifndef __ASSOC_H__ | ||
3 | #define __ASSOC_H__ | ||
4 | |||
5 | /* used by rendezvous */ | ||
6 | #define ASSOC_AVAILABLE | ||
7 | |||
8 | /* For the legalese parts, see tox.h. */ | ||
9 | |||
10 | /* | ||
11 | * Module to store currently unused ID <=> IP associations | ||
12 | * for a potential future use | ||
13 | */ | ||
14 | |||
15 | typedef struct IP_Port IP_Port; | ||
16 | typedef struct Assoc Assoc; | ||
17 | |||
18 | /*****************************************************************************/ | ||
19 | |||
20 | /* custom distance handler, if it's not ID-distance based | ||
21 | * return values exactly like id_closest() */ | ||
22 | typedef int (*Assoc_distance_relative_callback)(Assoc *assoc, void *callback_data, uint8_t *client_id, | ||
23 | uint8_t *client_id1, uint8_t *client_id2); | ||
24 | |||
25 | #define DISTANCE_INDEX_DISTANCE_BITS 44 | ||
26 | |||
27 | /* absolute distance: can be same for different client_id_check values | ||
28 | * return value should have DISTANCE_INDEX_DISTANCE_BITS valid bits */ | ||
29 | typedef uint64_t (*Assoc_distance_absolute_callback)(Assoc *assoc, void *callback_data, | ||
30 | uint8_t *client_id_ref, uint8_t *client_id_check); | ||
31 | |||
32 | /*****************************************************************************/ | ||
33 | |||
34 | /* Central entry point for new associations: add a new candidate to the cache */ | ||
35 | void Assoc_add_entry(Assoc *assoc, uint8_t *id, IPPTs *ippts_send, IP_Port *ipp_recv); | ||
36 | |||
37 | /*****************************************************************************/ | ||
38 | |||
39 | typedef struct Assoc_close_entries { | ||
40 | uint8_t *wanted_id; /* the target client_id */ | ||
41 | void *custom_data; /* given to distance functions */ | ||
42 | |||
43 | Assoc_distance_relative_callback distance_relative_func; | ||
44 | Assoc_distance_absolute_callback distance_absolute_func; | ||
45 | |||
46 | uint8_t count_good; /* that many should be "good" w.r.t. timeout */ | ||
47 | uint8_t count; /* allocated number of close_indices */ | ||
48 | Client_data **result; | ||
49 | } Assoc_close_entries; | ||
50 | |||
51 | /* find up to close_count nodes to put into close_nodes_used of ID_Nodes | ||
52 | * the distance functions can be NULL, then standard distance functions will be used | ||
53 | * the caller is responsible for allocating close_indices of sufficient size | ||
54 | * | ||
55 | * returns 0 on error | ||
56 | * returns the number of found nodes and the list of indices usable by Assoc_client() | ||
57 | * the caller is assumed to be registered from Assoc_register_callback() | ||
58 | * if they aren't, they should copy the Client_data and call Assoc_client_drop() | ||
59 | */ | ||
60 | uint8_t Assoc_get_close_entries(Assoc *assoc, Assoc_close_entries *close_entries); | ||
61 | |||
62 | /*****************************************************************************/ | ||
63 | |||
64 | /* create */ | ||
65 | Assoc *new_Assoc(DHT *dht); | ||
66 | |||
67 | /* avoid storing own ID/assoc */ | ||
68 | void Assoc_self_client_id_changed(Assoc *assoc); | ||
69 | |||
70 | /* destroy */ | ||
71 | void kill_Assoc(Assoc *assoc); | ||
72 | |||
73 | #endif /* !__ASSOC_H__ */ | ||
diff --git a/toxcore/group_chats.c b/toxcore/group_chats.c index 5376713c..ed867365 100644 --- a/toxcore/group_chats.c +++ b/toxcore/group_chats.c | |||
@@ -27,6 +27,8 @@ | |||
27 | #endif | 27 | #endif |
28 | 28 | ||
29 | #include "group_chats.h" | 29 | #include "group_chats.h" |
30 | #include "assoc.h" | ||
31 | #include "LAN_discovery.h" | ||
30 | #include "util.h" | 32 | #include "util.h" |
31 | 33 | ||
32 | #define GROUPCHAT_MAXDATA_LENGTH (MAX_DATA_SIZE - (1 + crypto_box_PUBLICKEYBYTES * 2 + crypto_box_NONCEBYTES)) | 34 | #define GROUPCHAT_MAXDATA_LENGTH (MAX_DATA_SIZE - (1 + crypto_box_PUBLICKEYBYTES * 2 + crypto_box_NONCEBYTES)) |
@@ -306,6 +308,16 @@ static int send_getnodes(Group_Chat *chat, IP_Port ip_port, int peernum) | |||
306 | 308 | ||
307 | chat->group[peernum].last_pinged = unix_time(); | 309 | chat->group[peernum].last_pinged = unix_time(); |
308 | chat->group[peernum].pingid = contents.pingid; | 310 | chat->group[peernum].pingid = contents.pingid; |
311 | chat->group[peernum].ping_via = ip_port; | ||
312 | |||
313 | |||
314 | if (chat->assoc) { | ||
315 | IPPTs ippts; | ||
316 | ippts.timestamp = unix_time(); | ||
317 | ippts.ip_port = ip_port; | ||
318 | |||
319 | Assoc_add_entry(chat->assoc, chat->group[peernum].client_id, &ippts, NULL); | ||
320 | } | ||
309 | 321 | ||
310 | return send_groupchatpacket(chat, ip_port, chat->group[peernum].client_id, (uint8_t *)&contents, sizeof(contents), | 322 | return send_groupchatpacket(chat, ip_port, chat->group[peernum].client_id, (uint8_t *)&contents, sizeof(contents), |
311 | CRYPTO_PACKET_GROUP_CHAT_GET_NODES); | 323 | CRYPTO_PACKET_GROUP_CHAT_GET_NODES); |
@@ -373,6 +385,9 @@ static int handle_sendnodes(Group_Chat *chat, IP_Port source, int peernum, uint8 | |||
373 | uint16_t numnodes = (len - sizeof(contents.pingid)) / sizeof(groupchat_nodes); | 385 | uint16_t numnodes = (len - sizeof(contents.pingid)) / sizeof(groupchat_nodes); |
374 | uint32_t i; | 386 | uint32_t i; |
375 | 387 | ||
388 | IPPTs ippts_send; | ||
389 | ippts_send.timestamp = unix_time(); | ||
390 | |||
376 | for (i = 0; i < numnodes; ++i) { | 391 | for (i = 0; i < numnodes; ++i) { |
377 | if (peer_okping(chat, contents.nodes[i].client_id) > 0) { | 392 | if (peer_okping(chat, contents.nodes[i].client_id) > 0) { |
378 | int peern = peer_in_chat(chat, contents.nodes[i].client_id); | 393 | int peern = peer_in_chat(chat, contents.nodes[i].client_id); |
@@ -385,10 +400,26 @@ static int handle_sendnodes(Group_Chat *chat, IP_Port source, int peernum, uint8 | |||
385 | continue; | 400 | continue; |
386 | 401 | ||
387 | send_getnodes(chat, contents.nodes[i].ip_port, peern); | 402 | send_getnodes(chat, contents.nodes[i].ip_port, peern); |
403 | |||
404 | if (chat->assoc) { | ||
405 | ippts_send.ip_port = contents.nodes[i].ip_port; | ||
406 | Assoc_add_entry(chat->assoc, contents.nodes[i].client_id, &ippts_send, NULL); | ||
407 | } | ||
388 | } | 408 | } |
389 | } | 409 | } |
390 | 410 | ||
391 | add_closepeer(chat, chat->group[peernum].client_id, source); | 411 | add_closepeer(chat, chat->group[peernum].client_id, source); |
412 | |||
413 | if (chat->assoc) { | ||
414 | ippts_send.ip_port = chat->group[peernum].ping_via; | ||
415 | ippts_send.timestamp = chat->group[peernum].last_pinged; | ||
416 | |||
417 | IP_Port ipp_recv; | ||
418 | ipp_recv = source; | ||
419 | |||
420 | Assoc_add_entry(chat->assoc, contents.nodes[i].client_id, &ippts_send, &ipp_recv); | ||
421 | } | ||
422 | |||
392 | return 0; | 423 | return 0; |
393 | } | 424 | } |
394 | 425 | ||
@@ -583,7 +614,8 @@ void callback_groupmessage(Group_Chat *chat, void (*function)(Group_Chat *chat, | |||
583 | chat->group_message_userdata = userdata; | 614 | chat->group_message_userdata = userdata; |
584 | } | 615 | } |
585 | 616 | ||
586 | Group_Chat *new_groupchat(Networking_Core *net) | 617 | |
618 | Group_Chat *new_groupchat(Networking_Core *net, Assoc *assoc) | ||
587 | { | 619 | { |
588 | unix_time_update(); | 620 | unix_time_update(); |
589 | 621 | ||
@@ -593,6 +625,9 @@ Group_Chat *new_groupchat(Networking_Core *net) | |||
593 | Group_Chat *chat = calloc(1, sizeof(Group_Chat)); | 625 | Group_Chat *chat = calloc(1, sizeof(Group_Chat)); |
594 | chat->net = net; | 626 | chat->net = net; |
595 | crypto_box_keypair(chat->self_public_key, chat->self_secret_key); | 627 | crypto_box_keypair(chat->self_public_key, chat->self_secret_key); |
628 | |||
629 | chat->assoc = assoc; | ||
630 | |||
596 | return chat; | 631 | return chat; |
597 | } | 632 | } |
598 | 633 | ||
diff --git a/toxcore/group_chats.h b/toxcore/group_chats.h index 74e2f2d7..90440d5b 100644 --- a/toxcore/group_chats.h +++ b/toxcore/group_chats.h | |||
@@ -27,12 +27,15 @@ | |||
27 | 27 | ||
28 | #include "net_crypto.h" | 28 | #include "net_crypto.h" |
29 | 29 | ||
30 | typedef struct Assoc Assoc; | ||
31 | |||
30 | #define MAX_NICK_BYTES 128 | 32 | #define MAX_NICK_BYTES 128 |
31 | 33 | ||
32 | typedef struct { | 34 | typedef struct { |
33 | uint8_t client_id[crypto_box_PUBLICKEYBYTES]; | 35 | uint8_t client_id[crypto_box_PUBLICKEYBYTES]; |
34 | uint64_t pingid; | 36 | uint64_t pingid; |
35 | uint64_t last_pinged; | 37 | uint64_t last_pinged; |
38 | IP_Port ping_via; | ||
36 | 39 | ||
37 | uint64_t last_recv; | 40 | uint64_t last_recv; |
38 | uint64_t last_recv_msgping; | 41 | uint64_t last_recv_msgping; |
@@ -46,7 +49,6 @@ typedef struct { | |||
46 | uint8_t client_id[crypto_box_PUBLICKEYBYTES]; | 49 | uint8_t client_id[crypto_box_PUBLICKEYBYTES]; |
47 | IP_Port ip_port; | 50 | IP_Port ip_port; |
48 | uint64_t last_recv; | 51 | uint64_t last_recv; |
49 | |||
50 | } Group_Close; | 52 | } Group_Close; |
51 | 53 | ||
52 | #define GROUP_CLOSE_CONNECTIONS 6 | 54 | #define GROUP_CLOSE_CONNECTIONS 6 |
@@ -69,6 +71,8 @@ typedef struct Group_Chat { | |||
69 | uint8_t nick[MAX_NICK_BYTES]; | 71 | uint8_t nick[MAX_NICK_BYTES]; |
70 | uint16_t nick_len; | 72 | uint16_t nick_len; |
71 | uint64_t last_sent_nick; | 73 | uint64_t last_sent_nick; |
74 | |||
75 | Assoc *assoc; | ||
72 | } Group_Chat; | 76 | } Group_Chat; |
73 | 77 | ||
74 | #define GROUP_CHAT_PING 0 | 78 | #define GROUP_CHAT_PING 0 |
@@ -120,7 +124,7 @@ uint32_t group_newpeer(Group_Chat *chat, uint8_t *client_id); | |||
120 | * | 124 | * |
121 | * Returns a NULL pointer if fail. | 125 | * Returns a NULL pointer if fail. |
122 | */ | 126 | */ |
123 | Group_Chat *new_groupchat(Networking_Core *net); | 127 | Group_Chat *new_groupchat(Networking_Core *net, Assoc *assoc); |
124 | 128 | ||
125 | 129 | ||
126 | /* Kill a group chat | 130 | /* Kill a group chat |
diff --git a/toxcore/network.h b/toxcore/network.h index 8b9b8b2f..87cb4794 100644 --- a/toxcore/network.h +++ b/toxcore/network.h | |||
@@ -129,7 +129,7 @@ typedef union { | |||
129 | uint8_t uint8[8]; | 129 | uint8_t uint8[8]; |
130 | } IP4_Port; | 130 | } IP4_Port; |
131 | 131 | ||
132 | typedef struct { | 132 | typedef struct IP_Port { |
133 | IP ip; | 133 | IP ip; |
134 | uint16_t port; | 134 | uint16_t port; |
135 | } IP_Port; | 135 | } IP_Port; |
diff --git a/toxcore/ping.c b/toxcore/ping.c index bd754c53..141a3fb4 100644 --- a/toxcore/ping.c +++ b/toxcore/ping.c | |||
@@ -27,19 +27,24 @@ | |||
27 | #include "config.h" | 27 | #include "config.h" |
28 | #endif | 28 | #endif |
29 | 29 | ||
30 | #include <stdbool.h> | ||
31 | #include <stdint.h> | 30 | #include <stdint.h> |
32 | 31 | ||
33 | #include "net_crypto.h" | ||
34 | #include "DHT.h" | 32 | #include "DHT.h" |
33 | #include "assoc.h" | ||
34 | #include "ping.h" | ||
35 | |||
36 | #include "network.h" | ||
37 | #include "util.h" | ||
35 | 38 | ||
36 | #define PING_NUM_MAX 384 | 39 | #define PING_NUM_MAX 384 |
37 | #define PING_TIMEOUT 5 // 5s | 40 | |
41 | /* 5 seconds */ | ||
42 | #define PING_TIMEOUT 5 | ||
38 | 43 | ||
39 | /* Ping newly announced nodes to ping per TIME_TOPING seconds*/ | 44 | /* Ping newly announced nodes to ping per TIME_TOPING seconds*/ |
40 | #define TIME_TOPING 5 | 45 | #define TIME_TOPING 5 |
41 | 46 | ||
42 | typedef struct { | 47 | typedef struct PING { |
43 | Net_Crypto *c; | 48 | Net_Crypto *c; |
44 | 49 | ||
45 | pinged_t pings[PING_NUM_MAX]; | 50 | pinged_t pings[PING_NUM_MAX]; |
@@ -50,13 +55,7 @@ typedef struct { | |||
50 | uint64_t last_toping; | 55 | uint64_t last_toping; |
51 | } PING; | 56 | } PING; |
52 | 57 | ||
53 | #define __PING_C__ | 58 | static int is_ping_timeout(uint64_t time) |
54 | |||
55 | #include "network.h" | ||
56 | #include "util.h" | ||
57 | #include "ping.h" | ||
58 | |||
59 | static bool is_ping_timeout(uint64_t time) | ||
60 | { | 59 | { |
61 | return is_timeout(time, PING_TIMEOUT); | 60 | return is_timeout(time, PING_TIMEOUT); |
62 | } | 61 | } |
@@ -260,6 +259,14 @@ static int handle_ping_response(void *_dht, IP_Port source, uint8_t *packet, uin | |||
260 | 259 | ||
261 | /* Associate client_id with the ip the request was sent to */ | 260 | /* Associate client_id with the ip the request was sent to */ |
262 | addto_lists(dht, ping->pings[ping_index - 1].ip_port, packet + 1); | 261 | addto_lists(dht, ping->pings[ping_index - 1].ip_port, packet + 1); |
262 | |||
263 | if (dht->assoc) { | ||
264 | IPPTs ippts; | ||
265 | ippts.ip_port = ping->pings[ping_index - 1].ip_port; | ||
266 | ippts.timestamp = ping->pings[ping_index - 1].timestamp; | ||
267 | Assoc_add_entry(dht->assoc, packet + 1, &ippts, &source); | ||
268 | } | ||
269 | |||
263 | return 0; | 270 | return 0; |
264 | } | 271 | } |
265 | 272 | ||
diff --git a/toxcore/ping.h b/toxcore/ping.h index 32742401..361fd3ef 100644 --- a/toxcore/ping.h +++ b/toxcore/ping.h | |||
@@ -24,11 +24,7 @@ | |||
24 | #ifndef __PING_H__ | 24 | #ifndef __PING_H__ |
25 | #define __PING_H__ | 25 | #define __PING_H__ |
26 | 26 | ||
27 | #include <stdbool.h> | ||
28 | |||
29 | #ifndef __PING_C__ | ||
30 | typedef struct PING PING; | 27 | typedef struct PING PING; |
31 | #endif | ||
32 | 28 | ||
33 | /* Add nodes to the toping list. | 29 | /* Add nodes to the toping list. |
34 | * All nodes in this list are pinged every TIME_TOPING seconds | 30 | * All nodes in this list are pinged every TIME_TOPING seconds |