ffi-tox 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. data/ProjectTox-Core/CMakeLists.txt +28 -0
  2. data/ProjectTox-Core/COPYING +674 -0
  3. data/ProjectTox-Core/INSTALL.md +91 -0
  4. data/ProjectTox-Core/README.md +54 -0
  5. data/ProjectTox-Core/core/CMakeLists.txt +17 -0
  6. data/ProjectTox-Core/core/DHT.c +1104 -0
  7. data/ProjectTox-Core/core/DHT.h +111 -0
  8. data/ProjectTox-Core/core/LAN_discovery.c +79 -0
  9. data/ProjectTox-Core/core/LAN_discovery.h +50 -0
  10. data/ProjectTox-Core/core/Lossless_UDP.c +749 -0
  11. data/ProjectTox-Core/core/Lossless_UDP.h +106 -0
  12. data/ProjectTox-Core/core/Messenger.c +581 -0
  13. data/ProjectTox-Core/core/Messenger.h +157 -0
  14. data/ProjectTox-Core/core/friend_requests.c +131 -0
  15. data/ProjectTox-Core/core/friend_requests.h +51 -0
  16. data/ProjectTox-Core/core/net_crypto.c +564 -0
  17. data/ProjectTox-Core/core/net_crypto.h +134 -0
  18. data/ProjectTox-Core/core/network.c +188 -0
  19. data/ProjectTox-Core/core/network.h +134 -0
  20. data/ProjectTox-Core/other/CMakeLists.txt +9 -0
  21. data/ProjectTox-Core/other/DHT_bootstrap.c +139 -0
  22. data/ProjectTox-Core/testing/CMakeLists.txt +18 -0
  23. data/ProjectTox-Core/testing/DHT_cryptosendfiletest.c +228 -0
  24. data/ProjectTox-Core/testing/DHT_sendfiletest.c +176 -0
  25. data/ProjectTox-Core/testing/DHT_test.c +182 -0
  26. data/ProjectTox-Core/testing/Lossless_UDP_testclient.c +214 -0
  27. data/ProjectTox-Core/testing/Lossless_UDP_testserver.c +201 -0
  28. data/ProjectTox-Core/testing/Messenger_test.c +145 -0
  29. data/ProjectTox-Core/testing/misc_tools.c +40 -0
  30. data/ProjectTox-Core/testing/misc_tools.h +29 -0
  31. data/ProjectTox-Core/testing/nTox.c +381 -0
  32. data/ProjectTox-Core/testing/nTox.h +50 -0
  33. data/ProjectTox-Core/testing/nTox_win32.c +323 -0
  34. data/ProjectTox-Core/testing/nTox_win32.h +31 -0
  35. data/ProjectTox-Core/testing/rect.py +45 -0
  36. data/ext/ffi-tox/extconf.rb +16 -0
  37. data/interfaces/libtox.i +18 -0
  38. data/lib/ffi-tox/libtox.rb +37 -0
  39. data/lib/ffi-tox.rb +1 -0
  40. metadata +116 -0
@@ -0,0 +1,1104 @@
1
+ /* DHT.c
2
+ *
3
+ * An implementation of the DHT as seen in docs/DHT.txt
4
+ *
5
+ * Copyright (C) 2013 Tox project All Rights Reserved.
6
+ *
7
+ * This file is part of Tox.
8
+ *
9
+ * Tox is free software: you can redistribute it and/or modify
10
+ * it under the terms of the GNU General Public License as published by
11
+ * the Free Software Foundation, either version 3 of the License, or
12
+ * (at your option) any later version.
13
+ *
14
+ * Tox is distributed in the hope that it will be useful,
15
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
+ * GNU General Public License for more details.
18
+ *
19
+ * You should have received a copy of the GNU General Public License
20
+ * along with Tox. If not, see <http://www.gnu.org/licenses/>.
21
+ *
22
+ */
23
+
24
+ #include "DHT.h"
25
+
26
+ typedef struct {
27
+ uint8_t client_id[CLIENT_ID_SIZE];
28
+ IP_Port ip_port;
29
+ uint32_t timestamp;
30
+ uint32_t last_pinged;
31
+ IP_Port ret_ip_port;/* The ip_port returned by this node for the friend
32
+ (for nodes in friends_list) or us (for nodes in close_clientlist) */
33
+ uint32_t ret_timestamp;
34
+ } Client_data;
35
+
36
+ /* maximum number of clients stored per friend. */
37
+ #define MAX_FRIEND_CLIENTS 8
38
+
39
+ typedef struct {
40
+ uint8_t client_id[CLIENT_ID_SIZE];
41
+ Client_data client_list[MAX_FRIEND_CLIENTS];
42
+ uint32_t lastgetnode; /* time at which the last get_nodes request was sent. */
43
+
44
+ /*Symetric NAT hole punching stuff*/
45
+ uint8_t hole_punching; /*0 if not hole punching, 1 if currently hole punching */
46
+ uint32_t punching_index;
47
+ uint32_t punching_timestamp;
48
+ uint32_t recvNATping_timestamp;
49
+ uint64_t NATping_id;
50
+ uint32_t NATping_timestamp;
51
+ } Friend;
52
+
53
+ typedef struct {
54
+ uint8_t client_id[CLIENT_ID_SIZE];
55
+ IP_Port ip_port;
56
+ } Node_format;
57
+
58
+ typedef struct {
59
+ IP_Port ip_port;
60
+ uint64_t ping_id;
61
+ uint32_t timestamp;
62
+
63
+ } Pinged;
64
+
65
+ /* Our client id/public key */
66
+ uint8_t self_public_key[CLIENT_ID_SIZE];
67
+ uint8_t self_secret_key[crypto_box_SECRETKEYBYTES];
68
+
69
+ /* TODO: Move these out of here and put them into the .c file.
70
+ A list of the clients mathematically closest to ours. */
71
+ #define LCLIENT_LIST 32
72
+ static Client_data close_clientlist[LCLIENT_LIST];
73
+
74
+ static Friend * friends_list;
75
+ static uint16_t num_friends;
76
+
77
+ /* The list of ip ports along with the ping_id of what we sent them and a timestamp */
78
+ #define LPING_ARRAY 256
79
+
80
+ static Pinged pings[LPING_ARRAY];
81
+
82
+ #define LSEND_NODES_ARRAY LPING_ARRAY/2
83
+
84
+ static Pinged send_nodes[LSEND_NODES_ARRAY];
85
+
86
+ /* Compares client_id1 and client_id2 with client_id
87
+ return 0 if both are same distance
88
+ return 1 if client_id1 is closer
89
+ return 2 if client_id2 is closer */
90
+ int id_closest(uint8_t * client_id, uint8_t * client_id1, uint8_t * client_id2) /* tested */
91
+ {
92
+ uint32_t i;
93
+ for(i = 0; i < CLIENT_ID_SIZE; ++i) {
94
+ if(abs(client_id[i] ^ client_id1[i]) < abs(client_id[i] ^ client_id2[i]))
95
+ return 1;
96
+ else if(abs(client_id[i] ^ client_id1[i]) > abs(client_id[i] ^ client_id2[i]))
97
+ return 2;
98
+ }
99
+ return 0;
100
+ }
101
+
102
+ /* check if client with client_id is already in list of length length.
103
+ if it is set it's corresponding timestamp to current time.
104
+ if the id is already in the list with a different ip_port, update it.
105
+ return True(1) or False(0)
106
+ TODO: maybe optimize this. */
107
+ int client_in_list(Client_data * list, uint32_t length, uint8_t * client_id, IP_Port ip_port)
108
+ {
109
+ uint32_t i;
110
+ uint32_t temp_time = unix_time();
111
+
112
+ for(i = 0; i < length; ++i) {
113
+ /*If ip_port is assigned to a different client_id replace it*/
114
+ if(list[i].ip_port.ip.i == ip_port.ip.i &&
115
+ list[i].ip_port.port == ip_port.port) {
116
+ memcpy(list[i].client_id, client_id, CLIENT_ID_SIZE);
117
+ }
118
+
119
+ if(memcmp(list[i].client_id, client_id, CLIENT_ID_SIZE) == 0) {
120
+ /* Refresh the client timestamp. */
121
+ list[i].timestamp = temp_time;
122
+ list[i].ip_port.ip.i = ip_port.ip.i;
123
+ list[i].ip_port.port = ip_port.port;
124
+ return 1;
125
+ }
126
+ }
127
+ return 0;
128
+
129
+ }
130
+
131
+ /* check if client with client_id is already in node format list of length length.
132
+ return True(1) or False(0) */
133
+ int client_in_nodelist(Node_format * list, uint32_t length, uint8_t * client_id)
134
+ {
135
+ uint32_t i;
136
+ for(i = 0; i < length; ++i)
137
+ if(memcmp(list[i].client_id, client_id, CLIENT_ID_SIZE) == 0)
138
+ return 1;
139
+ return 0;
140
+ }
141
+
142
+ /*Return the friend number from the client_id
143
+ Return -1 if failure, number of friend if success*/
144
+ static int friend_number(uint8_t * client_id)
145
+ {
146
+ uint32_t i;
147
+ for(i = 0; i < num_friends; ++i)
148
+ if(memcmp(friends_list[i].client_id, client_id, CLIENT_ID_SIZE) == 0) /* Equal */
149
+ return i;
150
+ return -1;
151
+ }
152
+
153
+ /* the number of seconds for a non responsive node to become bad. */
154
+ #define BAD_NODE_TIMEOUT 70
155
+ /* the max number of nodes to send with send nodes. */
156
+ #define MAX_SENT_NODES 8
157
+
158
+ /* Find MAX_SENT_NODES nodes closest to the client_id for the send nodes request:
159
+ put them in the nodes_list and return how many were found.
160
+ TODO: Make this function much more efficient. */
161
+ int get_close_nodes(uint8_t * client_id, Node_format * nodes_list)
162
+ {
163
+ uint32_t i, j, k;
164
+ int num_nodes=0;
165
+ uint32_t temp_time = unix_time();
166
+ for(i = 0; i < LCLIENT_LIST; ++i)
167
+ if(close_clientlist[i].timestamp + BAD_NODE_TIMEOUT > temp_time &&
168
+ !client_in_nodelist(nodes_list, MAX_SENT_NODES,close_clientlist[i].client_id)) {
169
+ /* if node is good and not already in list. */
170
+ if(num_nodes < MAX_SENT_NODES) {
171
+ memcpy(nodes_list[num_nodes].client_id, close_clientlist[i].client_id, CLIENT_ID_SIZE);
172
+ nodes_list[num_nodes].ip_port = close_clientlist[i].ip_port;
173
+ num_nodes++;
174
+ } else for(j = 0; j < MAX_SENT_NODES; ++j)
175
+ if(id_closest(client_id, nodes_list[j].client_id, close_clientlist[i].client_id) == 2) {
176
+ memcpy(nodes_list[j].client_id, close_clientlist[i].client_id, CLIENT_ID_SIZE);
177
+ nodes_list[j].ip_port = close_clientlist[i].ip_port;
178
+ break;
179
+ }
180
+ }
181
+
182
+ for(i = 0; i < num_friends; ++i)
183
+ for(j = 0; j < MAX_FRIEND_CLIENTS; ++j)
184
+ if(friends_list[i].client_list[j].timestamp + BAD_NODE_TIMEOUT > temp_time &&
185
+ !client_in_nodelist(nodes_list, MAX_SENT_NODES,friends_list[i].client_list[j].client_id)) {
186
+ /* if node is good and not already in list. */
187
+ if(num_nodes < MAX_SENT_NODES) {
188
+ memcpy(nodes_list[num_nodes].client_id, friends_list[i].client_list[j].client_id, CLIENT_ID_SIZE);
189
+ nodes_list[num_nodes].ip_port = friends_list[i].client_list[j].ip_port;
190
+ num_nodes++;
191
+ } else for(k = 0; k < MAX_SENT_NODES; ++k)
192
+ if(id_closest(client_id, nodes_list[k].client_id, friends_list[i].client_list[j].client_id) == 2) {
193
+ memcpy(nodes_list[k].client_id, friends_list[i].client_list[j].client_id, CLIENT_ID_SIZE);
194
+ nodes_list[k].ip_port = friends_list[i].client_list[j].ip_port;
195
+ break;
196
+ }
197
+ }
198
+ return num_nodes;
199
+ }
200
+
201
+ /* replace first bad (or empty) node with this one
202
+ return 0 if successful
203
+ return 1 if not (list contains no bad nodes) */
204
+ int replace_bad(Client_data * list, uint32_t length, uint8_t * client_id, IP_Port ip_port) /* tested */
205
+ {
206
+ uint32_t i;
207
+ uint32_t temp_time = unix_time();
208
+ for(i = 0; i < length; ++i)
209
+ if(list[i].timestamp + BAD_NODE_TIMEOUT < temp_time) { /* if node is bad. */
210
+ memcpy(list[i].client_id, client_id, CLIENT_ID_SIZE);
211
+ list[i].ip_port = ip_port;
212
+ list[i].timestamp = temp_time;
213
+ list[i].ret_ip_port.ip.i = 0;
214
+ list[i].ret_ip_port.port = 0;
215
+ list[i].ret_timestamp = 0;
216
+ return 0;
217
+ }
218
+
219
+ return 1;
220
+ }
221
+
222
+ /* replace the first good node that is further to the comp_client_id than that of the client_id in the list */
223
+ int replace_good(Client_data * list, uint32_t length, uint8_t * client_id, IP_Port ip_port, uint8_t * comp_client_id)
224
+ {
225
+ uint32_t i;
226
+ uint32_t temp_time = unix_time();
227
+
228
+ for(i = 0; i < length; ++i)
229
+ if(id_closest(comp_client_id, list[i].client_id, client_id) == 2) {
230
+ memcpy(list[i].client_id, client_id, CLIENT_ID_SIZE);
231
+ list[i].ip_port = ip_port;
232
+ list[i].timestamp = temp_time;
233
+ list[i].ret_ip_port.ip.i = 0;
234
+ list[i].ret_ip_port.port = 0;
235
+ list[i].ret_timestamp = 0;
236
+ return 0;
237
+ }
238
+
239
+ return 1;
240
+ }
241
+
242
+ /* Attempt to add client with ip_port and client_id to the friends client list and close_clientlist */
243
+ void addto_lists(IP_Port ip_port, uint8_t * client_id)
244
+ {
245
+ uint32_t i;
246
+
247
+ /* NOTE: current behavior if there are two clients with the same id is to replace the first ip by the second. */
248
+ if(!client_in_list(close_clientlist, LCLIENT_LIST, client_id, ip_port))
249
+ if(replace_bad(close_clientlist, LCLIENT_LIST, client_id, ip_port))
250
+ /* if we can't replace bad nodes we try replacing good ones */
251
+ replace_good(close_clientlist, LCLIENT_LIST, client_id, ip_port, self_public_key);
252
+
253
+ for(i = 0; i < num_friends; ++i)
254
+ if(!client_in_list(friends_list[i].client_list, MAX_FRIEND_CLIENTS, client_id, ip_port))
255
+ if(replace_bad(friends_list[i].client_list, MAX_FRIEND_CLIENTS, client_id, ip_port))
256
+ /* if we can't replace bad nodes we try replacing good ones. */
257
+ replace_good(friends_list[i].client_list, MAX_FRIEND_CLIENTS, client_id, ip_port, friends_list[i].client_id);
258
+ }
259
+
260
+ /* If client_id is a friend or us, update ret_ip_port
261
+ nodeclient_id is the id of the node that sent us this info */
262
+ void returnedip_ports(IP_Port ip_port, uint8_t * client_id, uint8_t * nodeclient_id)
263
+ {
264
+ uint32_t i, j;
265
+ uint32_t temp_time = unix_time();
266
+ if(memcmp(client_id, self_public_key, CLIENT_ID_SIZE) == 0) {
267
+ for(i = 0; i < LCLIENT_LIST; ++i)
268
+ if(memcmp(nodeclient_id, close_clientlist[i].client_id, CLIENT_ID_SIZE) == 0) {
269
+ close_clientlist[i].ret_ip_port = ip_port;
270
+ close_clientlist[i].ret_timestamp = temp_time;
271
+ return;
272
+ }
273
+ } else
274
+ for(i = 0; i < num_friends; ++i)
275
+ if(memcmp(client_id, friends_list[i].client_id, CLIENT_ID_SIZE) == 0)
276
+ for(j = 0; j < MAX_FRIEND_CLIENTS; ++j)
277
+ if(memcmp(nodeclient_id, friends_list[i].client_list[j].client_id, CLIENT_ID_SIZE) == 0) {
278
+ friends_list[i].client_list[j].ret_ip_port = ip_port;
279
+ friends_list[i].client_list[j].ret_timestamp = temp_time;
280
+ return;
281
+ }
282
+ }
283
+
284
+ /* ping timeout in seconds */
285
+ #define PING_TIMEOUT 5
286
+
287
+ /* check if we are currently pinging an ip_port and/or a ping_id
288
+ variables with values of zero will not be checked.
289
+ if we are already, return 1
290
+ else return 0
291
+ TODO: optimize this */
292
+ int is_pinging(IP_Port ip_port, uint64_t ping_id)
293
+ {
294
+ uint32_t i;
295
+ uint8_t pinging;
296
+ uint32_t temp_time = unix_time();
297
+
298
+ for(i = 0; i < LPING_ARRAY; ++i )
299
+ if((pings[i].timestamp + PING_TIMEOUT) > temp_time) {
300
+ pinging = 0;
301
+ if(ip_port.ip.i != 0)
302
+ if(pings[i].ip_port.ip.i == ip_port.ip.i &&
303
+ pings[i].ip_port.port == ip_port.port)
304
+ ++pinging;
305
+ if(ping_id != 0)
306
+ if(pings[i].ping_id == ping_id)
307
+ ++pinging;
308
+ if(pinging == (ping_id != 0) + (ip_port.ip.i != 0))
309
+ return 1;
310
+ }
311
+
312
+ return 0;
313
+ }
314
+
315
+ /* Same as last function but for get_node requests. */
316
+ int is_gettingnodes(IP_Port ip_port, uint64_t ping_id)
317
+ {
318
+ uint32_t i;
319
+ uint8_t pinging;
320
+ uint32_t temp_time = unix_time();
321
+
322
+ for(i = 0; i < LSEND_NODES_ARRAY; ++i )
323
+ if((send_nodes[i].timestamp + PING_TIMEOUT) > temp_time) {
324
+ pinging = 0;
325
+ if(ip_port.ip.i != 0)
326
+ if(send_nodes[i].ip_port.ip.i == ip_port.ip.i &&
327
+ send_nodes[i].ip_port.port == ip_port.port)
328
+ ++pinging;
329
+ if(ping_id != 0)
330
+ if(send_nodes[i].ping_id == ping_id)
331
+ ++pinging;
332
+ if(pinging == (ping_id != 0) + (ip_port.ip.i != 0))
333
+ return 1;
334
+
335
+ }
336
+
337
+ return 0;
338
+ }
339
+
340
+ /* Add a new ping request to the list of ping requests
341
+ returns the ping_id to put in the ping request
342
+ returns 0 if problem.
343
+ TODO: optimize this */
344
+ uint64_t add_pinging(IP_Port ip_port)
345
+ {
346
+ uint32_t i, j;
347
+ uint64_t ping_id = ((uint64_t)random_int() << 32) + random_int();
348
+ uint32_t temp_time = unix_time();
349
+
350
+ for(i = 0; i < PING_TIMEOUT; ++i )
351
+ for(j = 0; j < LPING_ARRAY; ++j )
352
+ if((pings[j].timestamp + PING_TIMEOUT - i) < temp_time) {
353
+ pings[j].timestamp = temp_time;
354
+ pings[j].ip_port = ip_port;
355
+ pings[j].ping_id = ping_id;
356
+ return ping_id;
357
+ }
358
+
359
+ return 0;
360
+ }
361
+
362
+ /* Same but for get node requests */
363
+ uint64_t add_gettingnodes(IP_Port ip_port)
364
+ {
365
+ uint32_t i, j;
366
+ uint64_t ping_id = ((uint64_t)random_int() << 32) + random_int();
367
+ uint32_t temp_time = unix_time();
368
+
369
+ for(i = 0; i < PING_TIMEOUT; ++i )
370
+ for(j = 0; j < LSEND_NODES_ARRAY; ++j )
371
+ if((send_nodes[j].timestamp + PING_TIMEOUT - i) < temp_time) {
372
+ send_nodes[j].timestamp = temp_time;
373
+ send_nodes[j].ip_port = ip_port;
374
+ send_nodes[j].ping_id = ping_id;
375
+ return ping_id;
376
+ }
377
+
378
+ return 0;
379
+ }
380
+
381
+ /* send a ping request
382
+ Ping request only works if none has been sent to that ip/port in the last 5 seconds. */
383
+ static int pingreq(IP_Port ip_port, uint8_t * public_key)
384
+ {
385
+ if(memcmp(public_key, self_public_key, CLIENT_ID_SIZE) == 0) /* check if packet is gonna be sent to ourself */
386
+ return 1;
387
+
388
+ if(is_pinging(ip_port, 0))
389
+ return 1;
390
+
391
+ uint64_t ping_id = add_pinging(ip_port);
392
+ if(ping_id == 0)
393
+ return 1;
394
+
395
+ uint8_t data[1 + CLIENT_ID_SIZE + crypto_box_NONCEBYTES + sizeof(ping_id) + ENCRYPTION_PADDING];
396
+ uint8_t encrypt[sizeof(ping_id) + ENCRYPTION_PADDING];
397
+ uint8_t nonce[crypto_box_NONCEBYTES];
398
+ random_nonce(nonce);
399
+
400
+ int len = encrypt_data(public_key, self_secret_key, nonce, (uint8_t *)&ping_id, sizeof(ping_id), encrypt);
401
+ if(len != sizeof(ping_id) + ENCRYPTION_PADDING)
402
+ return -1;
403
+ data[0] = 0;
404
+ memcpy(data + 1, self_public_key, CLIENT_ID_SIZE);
405
+ memcpy(data + 1 + CLIENT_ID_SIZE, nonce, crypto_box_NONCEBYTES);
406
+ memcpy(data + 1 + CLIENT_ID_SIZE + crypto_box_NONCEBYTES, encrypt, len);
407
+
408
+ return sendpacket(ip_port, data, sizeof(data));
409
+ }
410
+
411
+ /* send a ping response */
412
+ static int pingres(IP_Port ip_port, uint8_t * public_key, uint64_t ping_id)
413
+ {
414
+ /* check if packet is gonna be sent to ourself */
415
+ if(memcmp(public_key, self_public_key, CLIENT_ID_SIZE) == 0)
416
+ return 1;
417
+
418
+ uint8_t data[1 + CLIENT_ID_SIZE + crypto_box_NONCEBYTES + sizeof(ping_id) + ENCRYPTION_PADDING];
419
+ uint8_t encrypt[sizeof(ping_id) + ENCRYPTION_PADDING];
420
+ uint8_t nonce[crypto_box_NONCEBYTES];
421
+ random_nonce(nonce);
422
+
423
+ int len = encrypt_data(public_key, self_secret_key, nonce, (uint8_t *)&ping_id, sizeof(ping_id), encrypt);
424
+ if(len != sizeof(ping_id) + ENCRYPTION_PADDING)
425
+ return -1;
426
+ data[0] = 1;
427
+ memcpy(data + 1, self_public_key, CLIENT_ID_SIZE);
428
+ memcpy(data + 1 + CLIENT_ID_SIZE, nonce, crypto_box_NONCEBYTES);
429
+ memcpy(data + 1 + CLIENT_ID_SIZE + crypto_box_NONCEBYTES, encrypt, len);
430
+
431
+ return sendpacket(ip_port, data, sizeof(data));
432
+ }
433
+
434
+ /* send a getnodes request */
435
+ static int getnodes(IP_Port ip_port, uint8_t * public_key, uint8_t * client_id)
436
+ {
437
+ /* check if packet is gonna be sent to ourself */
438
+ if(memcmp(public_key, self_public_key, CLIENT_ID_SIZE) == 0)
439
+ return 1;
440
+
441
+ if(is_gettingnodes(ip_port, 0))
442
+ return 1;
443
+
444
+ uint64_t ping_id = add_gettingnodes(ip_port);
445
+
446
+ if(ping_id == 0)
447
+ return 1;
448
+
449
+ uint8_t data[1 + CLIENT_ID_SIZE + crypto_box_NONCEBYTES + sizeof(ping_id) + CLIENT_ID_SIZE + ENCRYPTION_PADDING];
450
+ uint8_t plain[sizeof(ping_id) + CLIENT_ID_SIZE];
451
+ uint8_t encrypt[sizeof(ping_id) + CLIENT_ID_SIZE + ENCRYPTION_PADDING];
452
+ uint8_t nonce[crypto_box_NONCEBYTES];
453
+ random_nonce(nonce);
454
+
455
+ memcpy(plain, &ping_id, sizeof(ping_id));
456
+ memcpy(plain + sizeof(ping_id), client_id, CLIENT_ID_SIZE);
457
+
458
+ int len = encrypt_data(public_key, self_secret_key, nonce, plain, sizeof(ping_id) + CLIENT_ID_SIZE, encrypt);
459
+
460
+ if(len != sizeof(ping_id) + CLIENT_ID_SIZE + ENCRYPTION_PADDING)
461
+ return -1;
462
+ data[0] = 2;
463
+ memcpy(data + 1, self_public_key, CLIENT_ID_SIZE);
464
+ memcpy(data + 1 + CLIENT_ID_SIZE, nonce, crypto_box_NONCEBYTES);
465
+ memcpy(data + 1 + CLIENT_ID_SIZE + crypto_box_NONCEBYTES, encrypt, len);
466
+ return sendpacket(ip_port, data, sizeof(data));
467
+ }
468
+
469
+ /* send a send nodes response */
470
+ static int sendnodes(IP_Port ip_port, uint8_t * public_key, uint8_t * client_id, uint64_t ping_id)
471
+ {
472
+ if(memcmp(public_key, self_public_key, CLIENT_ID_SIZE) == 0) /* check if packet is gonna be sent to ourself */
473
+ return 1;
474
+
475
+ uint8_t data[1 + CLIENT_ID_SIZE + crypto_box_NONCEBYTES + sizeof(ping_id)
476
+ + sizeof(Node_format) * MAX_SENT_NODES + ENCRYPTION_PADDING];
477
+
478
+ Node_format nodes_list[MAX_SENT_NODES];
479
+ int num_nodes = get_close_nodes(client_id, nodes_list);
480
+
481
+ if(num_nodes == 0)
482
+ return 0;
483
+
484
+ uint8_t plain[sizeof(ping_id) + sizeof(Node_format) * MAX_SENT_NODES];
485
+ uint8_t encrypt[sizeof(ping_id) + sizeof(Node_format) * MAX_SENT_NODES + ENCRYPTION_PADDING];
486
+ uint8_t nonce[crypto_box_NONCEBYTES];
487
+ random_nonce(nonce);
488
+
489
+ memcpy(plain, &ping_id, sizeof(ping_id));
490
+ memcpy(plain + sizeof(ping_id), nodes_list, num_nodes * sizeof(Node_format));
491
+
492
+ int len = encrypt_data(public_key, self_secret_key, nonce, plain,
493
+ sizeof(ping_id) + num_nodes * sizeof(Node_format), encrypt);
494
+
495
+ if(len != sizeof(ping_id) + num_nodes * sizeof(Node_format) + ENCRYPTION_PADDING)
496
+ return -1;
497
+
498
+ data[0] = 3;
499
+ memcpy(data + 1, self_public_key, CLIENT_ID_SIZE);
500
+ memcpy(data + 1 + CLIENT_ID_SIZE, nonce, crypto_box_NONCEBYTES);
501
+ memcpy(data + 1 + CLIENT_ID_SIZE + crypto_box_NONCEBYTES, encrypt, len);
502
+
503
+ return sendpacket(ip_port, data, 1 + CLIENT_ID_SIZE + crypto_box_NONCEBYTES + len);
504
+ }
505
+
506
+ /* Packet handling functions
507
+ One to handle each types of packets we receive
508
+ return 0 if handled correctly, 1 if packet is bad. */
509
+ int handle_pingreq(uint8_t * packet, uint32_t length, IP_Port source)
510
+ {
511
+ uint64_t ping_id;
512
+ if(length != 1 + CLIENT_ID_SIZE + crypto_box_NONCEBYTES + sizeof(ping_id) + ENCRYPTION_PADDING)
513
+ return 1;
514
+ /* check if packet is from ourself. */
515
+ if(memcmp(packet + 1, self_public_key, CLIENT_ID_SIZE) == 0)
516
+ return 1;
517
+
518
+ int len = decrypt_data(packet + 1, self_secret_key, packet + 1 + CLIENT_ID_SIZE,
519
+ packet + 1 + CLIENT_ID_SIZE + crypto_box_NONCEBYTES,
520
+ sizeof(ping_id) + ENCRYPTION_PADDING, (uint8_t *)&ping_id);
521
+ if(len != sizeof(ping_id))
522
+ return 1;
523
+
524
+ pingres(source, packet + 1, ping_id);
525
+
526
+ pingreq(source, packet + 1); /* TODO: make this smarter? */
527
+
528
+ return 0;
529
+ }
530
+
531
+ int handle_pingres(uint8_t * packet, uint32_t length, IP_Port source)
532
+ {
533
+ uint64_t ping_id;
534
+ if(length != 1 + CLIENT_ID_SIZE + crypto_box_NONCEBYTES + sizeof(ping_id) + ENCRYPTION_PADDING)
535
+ return 1;
536
+ if(memcmp(packet + 1, self_public_key, CLIENT_ID_SIZE) == 0) /* check if packet is from ourself. */
537
+ return 1;
538
+
539
+ int len = decrypt_data(packet + 1, self_secret_key, packet + 1 + CLIENT_ID_SIZE,
540
+ packet + 1 + CLIENT_ID_SIZE + crypto_box_NONCEBYTES,
541
+ sizeof(ping_id) + ENCRYPTION_PADDING, (uint8_t *)&ping_id);
542
+ if(len != sizeof(ping_id))
543
+ return 1;
544
+
545
+ if(is_pinging(source, ping_id)) {
546
+ addto_lists(source, packet + 1);
547
+ return 0;
548
+ }
549
+ return 1;
550
+
551
+ }
552
+
553
+ int handle_getnodes(uint8_t * packet, uint32_t length, IP_Port source)
554
+ {
555
+ uint64_t ping_id;
556
+ if(length != 1 + CLIENT_ID_SIZE + crypto_box_NONCEBYTES + sizeof(ping_id) + CLIENT_ID_SIZE + ENCRYPTION_PADDING)
557
+ return 1;
558
+ /* check if packet is from ourself. */
559
+ if(memcmp(packet + 1, self_public_key, CLIENT_ID_SIZE) == 0)
560
+ return 1;
561
+
562
+ uint8_t plain[sizeof(ping_id) + CLIENT_ID_SIZE];
563
+
564
+ int len = decrypt_data(packet + 1, self_secret_key, packet + 1 + CLIENT_ID_SIZE,
565
+ packet + 1 + CLIENT_ID_SIZE + crypto_box_NONCEBYTES,
566
+ sizeof(ping_id) + CLIENT_ID_SIZE + ENCRYPTION_PADDING, plain);
567
+
568
+ if(len != sizeof(ping_id) + CLIENT_ID_SIZE)
569
+ return 1;
570
+
571
+ memcpy(&ping_id, plain, sizeof(ping_id));
572
+ sendnodes(source, packet + 1, plain + sizeof(ping_id), ping_id);
573
+
574
+ pingreq(source, packet + 1); /* TODO: make this smarter? */
575
+
576
+ return 0;
577
+
578
+ }
579
+
580
+ int handle_sendnodes(uint8_t * packet, uint32_t length, IP_Port source)
581
+ {
582
+ uint64_t ping_id;
583
+ /* TODO: make this more readable */
584
+ if(length > (1 + CLIENT_ID_SIZE + crypto_box_NONCEBYTES + sizeof(ping_id)
585
+ + sizeof(Node_format) * MAX_SENT_NODES + ENCRYPTION_PADDING) ||
586
+ (length - (1 + CLIENT_ID_SIZE + crypto_box_NONCEBYTES + sizeof(ping_id)
587
+ + ENCRYPTION_PADDING)) % (sizeof(Node_format)) != 0 ||
588
+ length < 1 + CLIENT_ID_SIZE + crypto_box_NONCEBYTES + sizeof(ping_id)
589
+ + sizeof(Node_format) + ENCRYPTION_PADDING) {
590
+ return 1;
591
+ }
592
+ uint32_t num_nodes = (length - (1 + CLIENT_ID_SIZE + crypto_box_NONCEBYTES
593
+ + sizeof(ping_id) + ENCRYPTION_PADDING)) / sizeof(Node_format);
594
+
595
+ uint8_t plain[sizeof(ping_id) + sizeof(Node_format) * MAX_SENT_NODES];
596
+
597
+ int len = decrypt_data(packet + 1, self_secret_key, packet + 1 + CLIENT_ID_SIZE,
598
+ packet + 1 + CLIENT_ID_SIZE + crypto_box_NONCEBYTES,
599
+ sizeof(ping_id) + num_nodes * sizeof(Node_format) + ENCRYPTION_PADDING, plain);
600
+
601
+ if(len != sizeof(ping_id) + num_nodes * sizeof(Node_format))
602
+ return 1;
603
+
604
+ memcpy(&ping_id, plain, sizeof(ping_id));
605
+ if(!is_gettingnodes(source, ping_id))
606
+ return 1;
607
+
608
+ Node_format nodes_list[MAX_SENT_NODES];
609
+ memcpy(nodes_list, plain + sizeof(ping_id), num_nodes * sizeof(Node_format));
610
+
611
+ addto_lists(source, packet + 1);
612
+
613
+ uint32_t i;
614
+ for(i = 0; i < num_nodes; ++i) {
615
+ pingreq(nodes_list[i].ip_port, nodes_list[i].client_id);
616
+ returnedip_ports(nodes_list[i].ip_port, nodes_list[i].client_id, packet + 1);
617
+ }
618
+
619
+ return 0;
620
+ }
621
+
622
+ /* END of packet handling functions */
623
+
624
+ int DHT_addfriend(uint8_t * client_id)
625
+ {
626
+ Friend * temp;
627
+ temp = realloc(friends_list, sizeof(Friend) * (num_friends + 1));
628
+ if(temp == NULL)
629
+ return 1;
630
+
631
+ friends_list = temp;
632
+ memset(&friends_list[num_friends], 0, sizeof(Friend));
633
+ memcpy(friends_list[num_friends].client_id, client_id, CLIENT_ID_SIZE);
634
+ friends_list[num_friends].NATping_id = ((uint64_t)random_int() << 32) + random_int();
635
+ ++num_friends;
636
+ return 0;
637
+ }
638
+
639
+ int DHT_delfriend(uint8_t * client_id)
640
+ {
641
+ uint32_t i;
642
+ Friend * temp;
643
+ for(i = 0; i < num_friends; ++i)
644
+ /* Equal */
645
+ if(memcmp(friends_list[i].client_id, client_id, CLIENT_ID_SIZE) == 0) {
646
+ --num_friends;
647
+ if(num_friends != i)
648
+ memcpy(friends_list[i].client_id, friends_list[num_friends].client_id, CLIENT_ID_SIZE);
649
+ temp = realloc(friends_list, sizeof(Friend) * (num_friends));
650
+ if(temp != NULL)
651
+ friends_list = temp;
652
+ return 0;
653
+ }
654
+
655
+ return 1;
656
+ }
657
+
658
+ /* TODO: Optimize this. */
659
+ IP_Port DHT_getfriendip(uint8_t * client_id)
660
+ {
661
+ uint32_t i, j;
662
+ IP_Port empty = {{{0}}, 0};
663
+ uint32_t temp_time = unix_time();
664
+ for(i = 0; i < num_friends; ++i)
665
+ /* Equal */
666
+ if(memcmp(friends_list[i].client_id, client_id, CLIENT_ID_SIZE) == 0) {
667
+ for(j = 0; j < MAX_FRIEND_CLIENTS; ++j)
668
+ if(memcmp(friends_list[i].client_list[j].client_id, client_id, CLIENT_ID_SIZE) == 0 &&
669
+ friends_list[i].client_list[j].timestamp + BAD_NODE_TIMEOUT > temp_time)
670
+ return friends_list[i].client_list[j].ip_port;
671
+
672
+ return empty;
673
+ }
674
+ empty.ip.i = 1;
675
+ return empty;
676
+
677
+ }
678
+
679
+ /* The timeout after which a node is discarded completely. */
680
+ #define Kill_NODE_TIMEOUT 300
681
+
682
+ /* ping interval in seconds for each node in our lists. */
683
+ #define PING_INTERVAL 60
684
+
685
+ /* ping interval in seconds for each random sending of a get nodes request. */
686
+ #define GET_NODE_INTERVAL 10
687
+
688
+ /* Ping each client in the "friends" list every 60 seconds.
689
+ Send a get nodes request every 20 seconds to a random good node for each "friend" in our "friends" list. */
690
+ void doDHTFriends()
691
+ {
692
+ uint32_t i, j;
693
+ uint32_t temp_time = unix_time();
694
+ uint32_t rand_node;
695
+ uint32_t index[MAX_FRIEND_CLIENTS];
696
+
697
+ for(i = 0; i < num_friends; ++i) {
698
+ uint32_t num_nodes = 0;
699
+ for(j = 0; j < MAX_FRIEND_CLIENTS; ++j)
700
+ if(friends_list[i].client_list[j].timestamp + Kill_NODE_TIMEOUT > temp_time) { /* if node is not dead. */
701
+ if((friends_list[i].client_list[j].last_pinged + PING_INTERVAL) <= temp_time) {
702
+ pingreq(friends_list[i].client_list[j].ip_port, friends_list[i].client_list[j].client_id);
703
+ friends_list[i].client_list[j].last_pinged = temp_time;
704
+ }
705
+ if(friends_list[i].client_list[j].timestamp + BAD_NODE_TIMEOUT > temp_time) { /* if node is good. */
706
+ index[num_nodes] = j;
707
+ ++num_nodes;
708
+ }
709
+ }
710
+ if(friends_list[i].lastgetnode + GET_NODE_INTERVAL <= temp_time && num_nodes != 0) {
711
+ rand_node = rand() % num_nodes;
712
+ getnodes(friends_list[i].client_list[index[rand_node]].ip_port,
713
+ friends_list[i].client_list[index[rand_node]].client_id,
714
+ friends_list[i].client_id);
715
+ friends_list[i].lastgetnode = temp_time;
716
+ }
717
+ }
718
+ }
719
+
720
+ static uint32_t close_lastgetnodes;
721
+
722
+ /* Ping each client in the close nodes list every 60 seconds.
723
+ Send a get nodes request every 20 seconds to a random good node in the list. */
724
+ void doClose() /* tested */
725
+ {
726
+ uint32_t i;
727
+ uint32_t temp_time = unix_time();
728
+ uint32_t num_nodes = 0;
729
+ uint32_t rand_node;
730
+ uint32_t index[LCLIENT_LIST];
731
+
732
+ for(i = 0; i < LCLIENT_LIST; ++i)
733
+ /* if node is not dead. */
734
+ if(close_clientlist[i].timestamp + Kill_NODE_TIMEOUT > temp_time) {
735
+ if((close_clientlist[i].last_pinged + PING_INTERVAL) <= temp_time) {
736
+ pingreq(close_clientlist[i].ip_port, close_clientlist[i].client_id);
737
+ close_clientlist[i].last_pinged = temp_time;
738
+ }
739
+ /* if node is good. */
740
+ if(close_clientlist[i].timestamp + BAD_NODE_TIMEOUT > temp_time) {
741
+ index[num_nodes] = i;
742
+ ++num_nodes;
743
+ }
744
+ }
745
+
746
+ if(close_lastgetnodes + GET_NODE_INTERVAL <= temp_time && num_nodes != 0) {
747
+ rand_node = rand() % num_nodes;
748
+ getnodes(close_clientlist[index[rand_node]].ip_port,
749
+ close_clientlist[index[rand_node]].client_id,
750
+ self_public_key);
751
+ close_lastgetnodes = temp_time;
752
+ }
753
+ }
754
+
755
+ void DHT_bootstrap(IP_Port ip_port, uint8_t * public_key)
756
+ {
757
+ getnodes(ip_port, public_key, self_public_key);
758
+ }
759
+
760
+ /* send the given packet to node with client_id
761
+ returns -1 if failure */
762
+ int route_packet(uint8_t * client_id, uint8_t * packet, uint32_t length)
763
+ {
764
+ uint32_t i;
765
+ for(i = 0; i < LCLIENT_LIST; ++i)
766
+ if(memcmp(client_id, close_clientlist[i].client_id, CLIENT_ID_SIZE) == 0)
767
+ return sendpacket(close_clientlist[i].ip_port, packet, length);
768
+ return -1;
769
+ }
770
+
771
+ /* Puts all the different ips returned by the nodes for a friend_num into array ip_portlist
772
+ ip_portlist must be at least MAX_FRIEND_CLIENTS big
773
+ returns the number of ips returned
774
+ return 0 if we are connected to friend or if no ips were found.
775
+ returns -1 if no such friend*/
776
+ static int friend_iplist(IP_Port * ip_portlist, uint16_t friend_num)
777
+ {
778
+ int num_ips = 0;
779
+ uint32_t i;
780
+ uint32_t temp_time = unix_time();
781
+ if(friend_num >= num_friends)
782
+ return -1;
783
+ for(i = 0; i < MAX_FRIEND_CLIENTS; ++i)
784
+ /*If ip is not zero and node is good */
785
+ if(friends_list[friend_num].client_list[i].ret_ip_port.ip.i != 0 &&
786
+ friends_list[friend_num].client_list[i].ret_timestamp + BAD_NODE_TIMEOUT > temp_time) {
787
+ if(memcmp(friends_list[friend_num].client_list[i].client_id, friends_list[friend_num].client_id, CLIENT_ID_SIZE) == 0 )
788
+ return 0;
789
+ ip_portlist[num_ips] = friends_list[friend_num].client_list[i].ret_ip_port;
790
+ ++num_ips;
791
+ }
792
+ return num_ips;
793
+ }
794
+
795
+ /* Send the following packet to everyone who tells us they are connected to friend_id
796
+ returns the number of nodes it sent the packet to */
797
+ int route_tofriend(uint8_t * friend_id, uint8_t * packet, uint32_t length)
798
+ {
799
+ uint32_t i, j;
800
+ uint32_t sent = 0;
801
+ uint32_t temp_time = unix_time();
802
+ for(i = 0; i < num_friends; ++i)
803
+ /* Equal */
804
+ if(memcmp(friends_list[i].client_id, friend_id, CLIENT_ID_SIZE) == 0) {
805
+ for(j = 0; j < MAX_FRIEND_CLIENTS; ++j)
806
+ /*If ip is not zero and node is good */
807
+ if(friends_list[i].client_list[j].ret_ip_port.ip.i != 0 &&
808
+ friends_list[i].client_list[j].ret_timestamp + BAD_NODE_TIMEOUT > temp_time)
809
+ if(sendpacket(friends_list[i].client_list[j].ip_port, packet, length) == length)
810
+ ++sent;
811
+ return sent;
812
+ }
813
+ return 0;
814
+ }
815
+
816
+ /* Send the following packet to one random person who tells us they are connected to friend_id
817
+ returns the number of nodes it sent the packet to */
818
+ int routeone_tofriend(uint8_t * friend_id, uint8_t * packet, uint32_t length)
819
+ {
820
+ int num = friend_number(friend_id);
821
+ if(num == -1)
822
+ return 0;
823
+
824
+ IP_Port ip_list[MAX_FRIEND_CLIENTS];
825
+ int n = 0;
826
+ uint32_t i;
827
+ uint32_t temp_time = unix_time();
828
+ for(i = 0; i < MAX_FRIEND_CLIENTS; ++i)
829
+ /*If ip is not zero and node is good */
830
+ if(friends_list[num].client_list[i].ret_ip_port.ip.i != 0 &&
831
+ friends_list[num].client_list[i].ret_timestamp + BAD_NODE_TIMEOUT > temp_time) {
832
+ ip_list[n] = friends_list[num].client_list[i].ip_port;
833
+ ++n;
834
+ }
835
+ if(n < 1)
836
+ return 0;
837
+ if(sendpacket(ip_list[rand() % n], packet, length) == length)
838
+ return 1;
839
+ return 0;
840
+ }
841
+
842
+ /* Puts all the different ips returned by the nodes for a friend_id into array ip_portlist
843
+ ip_portlist must be at least MAX_FRIEND_CLIENTS big
844
+ returns the number of ips returned
845
+ return 0 if we are connected to friend or if no ips were found.
846
+ returns -1 if no such friend*/
847
+ int friend_ips(IP_Port * ip_portlist, uint8_t * friend_id)
848
+ {
849
+
850
+ uint32_t i;
851
+ for(i = 0; i < num_friends; ++i)
852
+ /* Equal */
853
+ if(memcmp(friends_list[i].client_id, friend_id, CLIENT_ID_SIZE) == 0)
854
+ return friend_iplist(ip_portlist, i);
855
+ return -1;
856
+ }
857
+
858
+ /*BEGINNING OF NAT PUNCHING FUNCTIONS*/
859
+
860
+ int send_NATping(uint8_t * public_key, uint64_t ping_id, uint8_t type)
861
+ {
862
+ uint8_t data[sizeof(uint64_t) + 1];
863
+ data[0] = type;
864
+ memcpy(data + 1, &ping_id, sizeof(uint64_t));
865
+
866
+ uint8_t packet[MAX_DATA_SIZE];
867
+ int len = create_request(packet, public_key, data, sizeof(uint64_t) + 1, 254); /* 254 is NAT ping request packet id */
868
+ if(len == -1)
869
+ return -1;
870
+
871
+ int num = 0;
872
+
873
+ if(type == 0)
874
+ num = route_tofriend(public_key, packet, len);/*If packet is request use many people to route it*/
875
+ else if(type == 1)
876
+ num = routeone_tofriend(public_key, packet, len);/*If packet is response use only one person to route it*/
877
+ if(num == 0)
878
+ return -1;
879
+ return num;
880
+ }
881
+
882
+ /* Handle a recieved ping request for */
883
+ int handle_NATping(uint8_t * packet, uint32_t length, IP_Port source)
884
+ {
885
+ if(length <= crypto_box_PUBLICKEYBYTES * 2 + crypto_box_NONCEBYTES + 1 + ENCRYPTION_PADDING &&
886
+ length > MAX_DATA_SIZE + ENCRYPTION_PADDING)
887
+ return 1;
888
+ /* check if request is for us. */
889
+ if(memcmp(packet + 1, self_public_key, crypto_box_PUBLICKEYBYTES) == 0) {
890
+ uint8_t public_key[crypto_box_PUBLICKEYBYTES];
891
+ uint8_t data[MAX_DATA_SIZE];
892
+ int len = handle_request(public_key, data, packet, length);
893
+ if(len != sizeof(uint64_t) + 1)
894
+ return 1;
895
+ uint64_t ping_id;
896
+ memcpy(&ping_id, data + 1, sizeof(uint64_t));
897
+
898
+ int friendnumber = friend_number(public_key);
899
+ if(friendnumber == -1)
900
+ return 1;
901
+
902
+ if(data[0] == 0) {
903
+ send_NATping(public_key, ping_id, 1); /*1 is reply*/
904
+ friends_list[friendnumber].recvNATping_timestamp = unix_time();
905
+ return 0;
906
+ } else if (data[0] == 1)
907
+ if(friends_list[friendnumber].NATping_id == ping_id) {
908
+ friends_list[friendnumber].NATping_id = ((uint64_t)random_int() << 32) + random_int();
909
+ friends_list[friendnumber].hole_punching = 1;
910
+ return 0;
911
+ }
912
+ return 1;
913
+ }
914
+ /* if request is not for us, try routing it. */
915
+ else if(route_packet(packet + 1, packet, length) == length)
916
+ return 0;
917
+ return 0;
918
+ }
919
+
920
+ /*Get the most common ip in the ip_portlist
921
+ Only return ip if it appears in list min_num or more
922
+ len must not be bigger than MAX_FRIEND_CLIENTS
923
+ return ip of 0 if failure */
924
+ static IP NAT_commonip(IP_Port * ip_portlist, uint16_t len, uint16_t min_num)
925
+ {
926
+ IP zero = {{0}};
927
+ if(len > MAX_FRIEND_CLIENTS)
928
+ return zero;
929
+
930
+ uint32_t i, j;
931
+ uint16_t numbers[MAX_FRIEND_CLIENTS] = {0};
932
+ for(i = 0; i < len; ++i) {
933
+ for(j = 0; j < len; ++j)
934
+ if(ip_portlist[i].ip.i == ip_portlist[j].ip.i)
935
+ ++numbers[i];
936
+ if(numbers[i] >= min_num)
937
+ return ip_portlist[i].ip;
938
+ }
939
+ return zero;
940
+ }
941
+
942
+ /*Return all the ports for one ip in a list
943
+ portlist must be at least len long
944
+ where len is the length of ip_portlist
945
+ returns the number of ports and puts the list of ports in portlist*/
946
+ static uint16_t NAT_getports(uint16_t * portlist, IP_Port * ip_portlist, uint16_t len, IP ip)
947
+ {
948
+ uint32_t i;
949
+ uint16_t num = 0;
950
+ for(i = 0; i < len; ++i)
951
+ if(ip_portlist[i].ip.i == ip.i) {
952
+ portlist[num] = ntohs(ip_portlist[i].port);
953
+ ++num;
954
+ }
955
+ return num;
956
+ }
957
+
958
+ #define MAX_PUNCHING_PORTS 32
959
+
960
+ static void punch_holes(IP ip, uint16_t * port_list, uint16_t numports, uint16_t friend_num)
961
+ {
962
+ if(numports > MAX_FRIEND_CLIENTS || numports == 0)
963
+ return;
964
+ uint32_t i;
965
+ uint32_t top = friends_list[friend_num].punching_index + MAX_PUNCHING_PORTS;
966
+ for(i = friends_list[friend_num].punching_index; i != top; i++) {
967
+ /*TODO: improve port guessing algorithm*/
968
+ uint16_t port = port_list[(i/2) % numports] + (i/(2*numports))*((i % 2) ? -1 : 1);
969
+ IP_Port pinging = {ip, htons(port)};
970
+ pingreq(pinging, friends_list[friend_num].client_id);
971
+ }
972
+ friends_list[friend_num].punching_index = i;
973
+ }
974
+
975
+ /*Interval in seconds between punching attempts*/
976
+ #define PUNCH_INTERVAL 10
977
+
978
+ static void doNAT()
979
+ {
980
+ uint32_t i;
981
+ uint32_t temp_time = unix_time();
982
+ for(i = 0; i < num_friends; ++i) {
983
+ IP_Port ip_list[MAX_FRIEND_CLIENTS];
984
+ int num = friend_iplist(ip_list, i);
985
+ /*If we are connected to friend or if friend is not online don't try to hole punch with him*/
986
+ if(num < MAX_FRIEND_CLIENTS/2)
987
+ continue;
988
+
989
+
990
+ if(friends_list[i].NATping_timestamp + PUNCH_INTERVAL < temp_time) {
991
+ send_NATping(friends_list[i].client_id, friends_list[i].NATping_id, 0); /*0 is request*/
992
+ friends_list[i].NATping_timestamp = temp_time;
993
+ }
994
+ if(friends_list[i].hole_punching == 1 &&
995
+ friends_list[i].punching_timestamp + PUNCH_INTERVAL < temp_time &&
996
+ friends_list[i].recvNATping_timestamp + PUNCH_INTERVAL*2 >= temp_time) {
997
+ IP ip = NAT_commonip(ip_list, num, MAX_FRIEND_CLIENTS/2);
998
+ if(ip.i == 0)
999
+ continue;
1000
+
1001
+ uint16_t port_list[MAX_FRIEND_CLIENTS];
1002
+ uint16_t numports = NAT_getports(port_list, ip_list, num, ip);
1003
+ punch_holes(ip, port_list, numports, i);
1004
+
1005
+ friends_list[i].punching_timestamp = temp_time;
1006
+ friends_list[i].hole_punching = 0;
1007
+ }
1008
+ }
1009
+ }
1010
+
1011
+ /*END OF NAT PUNCHING FUNCTIONS*/
1012
+
1013
+ int DHT_handlepacket(uint8_t * packet, uint32_t length, IP_Port source)
1014
+ {
1015
+ switch (packet[0]) {
1016
+ case 0:
1017
+ return handle_pingreq(packet, length, source);
1018
+
1019
+ case 1:
1020
+ return handle_pingres(packet, length, source);
1021
+
1022
+ case 2:
1023
+ return handle_getnodes(packet, length, source);
1024
+
1025
+ case 3:
1026
+ return handle_sendnodes(packet, length, source);
1027
+
1028
+ case 254:
1029
+ return handle_NATping(packet, length, source);
1030
+
1031
+ default:
1032
+ return 1;
1033
+
1034
+ }
1035
+
1036
+ return 0;
1037
+ }
1038
+
1039
+ void doDHT()
1040
+ {
1041
+ doClose();
1042
+ doDHTFriends();
1043
+ doNAT();
1044
+ }
1045
+
1046
+ /* get the size of the DHT (for saving) */
1047
+ uint32_t DHT_size()
1048
+ {
1049
+ return sizeof(close_clientlist) + sizeof(Friend) * num_friends;
1050
+ }
1051
+
1052
+ /* save the DHT in data where data is an array of size DHT_size() */
1053
+ void DHT_save(uint8_t * data)
1054
+ {
1055
+ memcpy(data, close_clientlist, sizeof(close_clientlist));
1056
+ memcpy(data + sizeof(close_clientlist), friends_list, sizeof(Friend) * num_friends);
1057
+ }
1058
+
1059
+ /* load the DHT from data of size size;
1060
+ return -1 if failure
1061
+ return 0 if success */
1062
+ int DHT_load(uint8_t * data, uint32_t size)
1063
+ {
1064
+ if(size < sizeof(close_clientlist))
1065
+ return -1;
1066
+ if((size - sizeof(close_clientlist)) % sizeof(Friend) != 0)
1067
+ return -1;
1068
+ uint32_t i, j;
1069
+ /* uint32_t temp_time = unix_time(); */
1070
+ uint16_t temp;
1071
+
1072
+ temp = (size - sizeof(close_clientlist))/sizeof(Friend);
1073
+
1074
+ if(temp != 0) {
1075
+ Friend * tempfriends_list = (Friend *)(data + sizeof(close_clientlist));
1076
+
1077
+ for(i = 0; i < temp; ++i) {
1078
+ DHT_addfriend(tempfriends_list[i].client_id);
1079
+ for(j = 0; j < MAX_FRIEND_CLIENTS; ++j)
1080
+ if(tempfriends_list[i].client_list[j].timestamp != 0) {
1081
+ getnodes(tempfriends_list[i].client_list[j].ip_port,
1082
+ tempfriends_list[i].client_list[j].client_id, tempfriends_list[i].client_id);
1083
+ }
1084
+ }
1085
+ }
1086
+ Client_data * tempclose_clientlist = (Client_data *)data;
1087
+
1088
+ for(i = 0; i < LCLIENT_LIST; ++i)
1089
+ if(tempclose_clientlist[i].timestamp != 0)
1090
+ DHT_bootstrap(tempclose_clientlist[i].ip_port, tempclose_clientlist[i].client_id);
1091
+ return 0;
1092
+ }
1093
+
1094
+ /* returns 0 if we are not connected to the DHT
1095
+ returns 1 if we are */
1096
+ int DHT_isconnected()
1097
+ {
1098
+ uint32_t i;
1099
+ uint32_t temp_time = unix_time();
1100
+ for(i = 0; i < LCLIENT_LIST; ++i)
1101
+ if(close_clientlist[i].timestamp + BAD_NODE_TIMEOUT > temp_time)
1102
+ return 1;
1103
+ return 0;
1104
+ }