summaryrefslogtreecommitdiff
path: root/toxcore
diff options
context:
space:
mode:
Diffstat (limited to 'toxcore')
-rw-r--r--toxcore/DHT.c81
-rw-r--r--toxcore/DHT.h31
-rw-r--r--toxcore/Makefile.inc2
-rw-r--r--toxcore/Messenger.c9
-rw-r--r--toxcore/assoc.c739
-rw-r--r--toxcore/assoc.h73
-rw-r--r--toxcore/group_chats.c37
-rw-r--r--toxcore/group_chats.h8
-rw-r--r--toxcore/network.h2
-rw-r--r--toxcore/ping.c29
-rw-r--r--toxcore/ping.h4
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
1086void DHT_bootstrap(DHT *dht, IP_Port ip_port, uint8_t *public_key) 1132void 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}
1090int DHT_bootstrap_from_address(DHT *dht, const char *address, uint8_t ipv6enabled, 1143int 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}
1571void kill_DHT(DHT *dht) 1627void 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
57typedef struct {
58 IP_Port ip_port;
59 uint64_t timestamp;
60} IPPTs;
61
46typedef struct { 62typedef 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
136typedef struct PING PING;
137typedef struct Assoc Assoc;
138
120typedef struct { 139typedef 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
134Client_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
257void addto_lists(DHT *dht, IP_Port ip_port, uint8_t *client_id); 276void 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
32libtoxcore_la_CFLAGS = -I$(top_srcdir) \ 34libtoxcore_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)
62typedef uint16_t bucket_t;
63#else
64typedef uint32_t bucket_t;
65#endif
66typedef uint16_t usecnt_t;
67typedef uint32_t hash_t;
68
69/* abbreviations ... */
70typedef Assoc_distance_relative_callback dist_rel_cb;
71typedef Assoc_distance_absolute_callback dist_abs_cb;
72
73/*
74 * Client_data wrapped with additional data
75 */
76typedef 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
92typedef struct candidates_bucket {
93 Client_entry list[CANDIDATES_TO_KEEP]; /* hashed list (with holes) */
94} candidates_bucket;
95
96typedef 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 */
111static 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 */
129static 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 */
144static 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 */
166static 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 */
177static 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. */
204static 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 */
222static 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 */
244static 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 */
259static 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 */
274static 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() */
317static 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
323static 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
343static bucket_t candidates_id_bucket(uint8_t *id)
344{
345 return id_bucket(id, CANDIDATES_BUCKET_BITS);
346}
347
348static 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
368static 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
396static 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
449static 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
498static 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 */
543void 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
588uint8_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 */
706Assoc *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 */
726void 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 */
735void 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
15typedef struct IP_Port IP_Port;
16typedef struct Assoc Assoc;
17
18/*****************************************************************************/
19
20/* custom distance handler, if it's not ID-distance based
21 * return values exactly like id_closest() */
22typedef 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 */
29typedef 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 */
35void Assoc_add_entry(Assoc *assoc, uint8_t *id, IPPTs *ippts_send, IP_Port *ipp_recv);
36
37/*****************************************************************************/
38
39typedef 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 */
60uint8_t Assoc_get_close_entries(Assoc *assoc, Assoc_close_entries *close_entries);
61
62/*****************************************************************************/
63
64/* create */
65Assoc *new_Assoc(DHT *dht);
66
67/* avoid storing own ID/assoc */
68void Assoc_self_client_id_changed(Assoc *assoc);
69
70/* destroy */
71void 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
586Group_Chat *new_groupchat(Networking_Core *net) 617
618Group_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
30typedef struct Assoc Assoc;
31
30#define MAX_NICK_BYTES 128 32#define MAX_NICK_BYTES 128
31 33
32typedef struct { 34typedef 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 */
123Group_Chat *new_groupchat(Networking_Core *net); 127Group_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
132typedef struct { 132typedef 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
42typedef struct { 47typedef 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__ 58static int is_ping_timeout(uint64_t time)
54
55#include "network.h"
56#include "util.h"
57#include "ping.h"
58
59static 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__
30typedef struct PING PING; 27typedef 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