summaryrefslogtreecommitdiff
path: root/toxcore
diff options
context:
space:
mode:
authorjin-eld <jin at mediatomb dot cc>2013-08-04 15:10:37 +0300
committerjin-eld <jin at mediatomb dot cc>2013-08-24 03:25:07 +0300
commite658892793c42b2d058eed0937025ef2ddaaa372 (patch)
tree2a022cab057f2c16ca95860ed980092880052f6e /toxcore
parente2aa8161adc85795fe4d63d4642f47e90937ddc2 (diff)
Rename core directory because of autoconf name clash
While doing the checks configure might generate "core" files and will then try to remove them. Having a "core" directory generates an error while runing the configure script. There's no workaround but to rename the core directory.
Diffstat (limited to 'toxcore')
-rw-r--r--toxcore/CMakeLists.txt45
-rw-r--r--toxcore/DHT.c1264
-rw-r--r--toxcore/DHT.h193
-rw-r--r--toxcore/LAN_discovery.c152
-rw-r--r--toxcore/LAN_discovery.h55
-rw-r--r--toxcore/Lossless_UDP.c842
-rw-r--r--toxcore/Lossless_UDP.h222
-rw-r--r--toxcore/Messenger.c1020
-rw-r--r--toxcore/Messenger.h351
-rw-r--r--toxcore/friend_requests.c141
-rw-r--r--toxcore/friend_requests.h70
-rw-r--r--toxcore/net_crypto.c771
-rw-r--r--toxcore/net_crypto.h200
-rw-r--r--toxcore/network.c218
-rw-r--r--toxcore/network.h159
-rw-r--r--toxcore/packets.h31
-rw-r--r--toxcore/ping.c229
-rw-r--r--toxcore/ping.h17
-rw-r--r--toxcore/tox.c374
-rw-r--r--toxcore/tox.h289
-rw-r--r--toxcore/util.c45
-rw-r--r--toxcore/util.h12
22 files changed, 6700 insertions, 0 deletions
diff --git a/toxcore/CMakeLists.txt b/toxcore/CMakeLists.txt
new file mode 100644
index 00000000..ecbb65c2
--- /dev/null
+++ b/toxcore/CMakeLists.txt
@@ -0,0 +1,45 @@
1cmake_minimum_required(VERSION 2.6.0)
2project(toxcore C)
3
4set(core_sources
5 DHT.c
6 network.c
7 Lossless_UDP.c
8 net_crypto.c
9 friend_requests.c
10 LAN_discovery.c
11 Messenger.c
12 util.c
13 ping.c
14 tox.c)
15
16set(core_headers
17 DHT.h
18 network.h
19 Lossless_UDP.h
20 net_crypto.h
21 friend_requests.h
22 LAN_discovery.h
23 Messenger.h
24 util.h
25 ping.h)
26
27add_library(toxcore SHARED ${core_sources})
28add_library(toxcore_static ${core_sources})
29set_target_properties(toxcore_static PROPERTIES OUTPUT_NAME toxcore)
30
31target_link_libraries(toxcore ${LINK_CRYPTO_LIBRARY})
32
33install(TARGETS toxcore toxcore_static DESTINATION lib)
34install(FILES ${core_headers} DESTINATION include)
35
36if(WIN32)
37 target_link_libraries(toxcore ws2_32)
38endif()
39
40execute_process(COMMAND git rev-list HEAD --count OUTPUT_VARIABLE COMMIT)
41
42# Write pkgconfig-file:
43include(InstallPkgConfigFile)
44install_pkg_config_file(toxcore CFLAGS LIBS -ltoxcore REQUIRES VERSION 0.1.1_r${COMMIT})
45
diff --git a/toxcore/DHT.c b/toxcore/DHT.c
new file mode 100644
index 00000000..533425c4
--- /dev/null
+++ b/toxcore/DHT.c
@@ -0,0 +1,1264 @@
1/* DHT.c
2 *
3 * An implementation of the DHT as seen in http://wiki.tox.im/index.php/DHT
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/*----------------------------------------------------------------------------------*/
25
26#include "DHT.h"
27#include "packets.h"
28#include "ping.h"
29
30/* the number of seconds for a non responsive node to become bad. */
31#define BAD_NODE_TIMEOUT 70
32
33/* the max number of nodes to send with send nodes. */
34#define MAX_SENT_NODES 8
35
36/* ping timeout in seconds */
37#define PING_TIMEOUT 5
38
39/* The timeout after which a node is discarded completely. */
40#define Kill_NODE_TIMEOUT 300
41
42/* ping interval in seconds for each node in our lists. */
43#define PING_INTERVAL 60
44
45/* ping interval in seconds for each random sending of a get nodes request. */
46#define GET_NODE_INTERVAL 10
47
48#define MAX_PUNCHING_PORTS 32
49
50/*Interval in seconds between punching attempts*/
51#define PUNCH_INTERVAL 10
52
53/*Ping newly announced nodes to ping per TIME_TOPING seconds*/
54#define TIME_TOPING 5
55
56#define NAT_PING_REQUEST 0
57#define NAT_PING_RESPONSE 1
58
59
60Client_data *DHT_get_close_list(DHT *dht)
61{
62 return dht->close_clientlist;
63}
64
65/* Compares client_id1 and client_id2 with client_id
66 * return 0 if both are same distance
67 * return 1 if client_id1 is closer
68 * return 2 if client_id2 is closer
69 */
70static int id_closest(uint8_t *id, uint8_t *id1, uint8_t *id2)
71{
72 size_t i;
73 uint8_t distance1, distance2;
74
75 for (i = 0; i < CLIENT_ID_SIZE; ++i) {
76
77 distance1 = abs(id[i] ^ id1[i]);
78 distance2 = abs(id[i] ^ id2[i]);
79
80 if (distance1 < distance2)
81 return 1;
82
83 if (distance1 > distance2)
84 return 2;
85 }
86
87 return 0;
88}
89
90static int ipport_equal(IP_Port a, IP_Port b)
91{
92 return (a.ip.i == b.ip.i) && (a.port == b.port);
93}
94
95static int id_equal(uint8_t *a, uint8_t *b)
96{
97 return memcmp(a, b, CLIENT_ID_SIZE) == 0;
98}
99
100static int is_timeout(uint64_t time_now, uint64_t timestamp, uint64_t timeout)
101{
102 return timestamp + timeout <= time_now;
103}
104
105/* check if client with client_id is already in list of length length.
106 * if it is then set its corresponding timestamp to current time.
107 * if the id is already in the list with a different ip_port, update it.
108 * return True(1) or False(0)
109 *
110 * TODO: maybe optimize this.
111 */
112static int client_in_list(Client_data *list, uint32_t length, uint8_t *client_id, IP_Port ip_port)
113{
114 uint32_t i;
115 uint64_t temp_time = unix_time();
116
117 for (i = 0; i < length; ++i) {
118 /*If ip_port is assigned to a different client_id replace it*/
119 if (ipport_equal(list[i].ip_port, ip_port)) {
120 memcpy(list[i].client_id, client_id, CLIENT_ID_SIZE);
121 }
122
123 if (id_equal(list[i].client_id, client_id)) {
124 /* Refresh the client timestamp. */
125 list[i].timestamp = temp_time;
126 list[i].ip_port.ip.i = ip_port.ip.i;
127 list[i].ip_port.port = ip_port.port;
128 return 1;
129 }
130 }
131
132 return 0;
133}
134
135/* check if client with client_id is already in node format list of length length.
136 * return True(1) or False(0)
137 */
138static int client_in_nodelist(Node_format *list, uint32_t length, uint8_t *client_id)
139{
140 uint32_t i;
141
142 for (i = 0; i < length; ++i) {
143 if (id_equal(list[i].client_id, client_id))
144 return 1;
145 }
146
147 return 0;
148}
149
150/* Returns the friend number from the client_id, or -1 if a failure occurs
151 */
152static int friend_number(DHT *dht, uint8_t *client_id)
153{
154 uint32_t i;
155
156 for (i = 0; i < dht->num_friends; ++i) {
157 if (id_equal(dht->friends_list[i].client_id, client_id))
158 return i;
159 }
160
161 return -1;
162}
163
164/* Find MAX_SENT_NODES nodes closest to the client_id for the send nodes request:
165 * put them in the nodes_list and return how many were found.
166 *
167 * TODO: For the love of based Allah make this function cleaner and much more efficient.
168 */
169static int get_close_nodes(DHT *dht, uint8_t *client_id, Node_format *nodes_list)
170{
171 uint32_t i, j, k;
172 uint64_t temp_time = unix_time();
173 int num_nodes = 0, closest, tout, inlist;
174
175 for (i = 0; i < LCLIENT_LIST; ++i) {
176 tout = is_timeout(temp_time, dht->close_clientlist[i].timestamp, BAD_NODE_TIMEOUT);
177 inlist = client_in_nodelist(nodes_list, MAX_SENT_NODES, dht->close_clientlist[i].client_id);
178
179 /* if node isn't good or is already in list. */
180 if (tout || inlist)
181 continue;
182
183 if (num_nodes < MAX_SENT_NODES) {
184
185 memcpy( nodes_list[num_nodes].client_id,
186 dht->close_clientlist[i].client_id,
187 CLIENT_ID_SIZE );
188
189 nodes_list[num_nodes].ip_port = dht->close_clientlist[i].ip_port;
190 num_nodes++;
191
192 } else {
193
194 for (j = 0; j < MAX_SENT_NODES; ++j) {
195 closest = id_closest( client_id,
196 nodes_list[j].client_id,
197 dht->close_clientlist[i].client_id );
198
199 if (closest == 2) {
200 memcpy( nodes_list[j].client_id,
201 dht->close_clientlist[i].client_id,
202 CLIENT_ID_SIZE);
203
204 nodes_list[j].ip_port = dht->close_clientlist[i].ip_port;
205 break;
206 }
207 }
208 }
209 }
210
211 for (i = 0; i < dht->num_friends; ++i) {
212 for (j = 0; j < MAX_FRIEND_CLIENTS; ++j) {
213
214 tout = is_timeout(temp_time, dht->friends_list[i].client_list[j].timestamp, BAD_NODE_TIMEOUT);
215 inlist = client_in_nodelist( nodes_list,
216 MAX_SENT_NODES,
217 dht->friends_list[i].client_list[j].client_id);
218
219 /* if node isn't good or is already in list. */
220 if (tout || inlist)
221 continue;
222
223 if (num_nodes < MAX_SENT_NODES) {
224
225 memcpy( nodes_list[num_nodes].client_id,
226 dht->friends_list[i].client_list[j].client_id,
227 CLIENT_ID_SIZE);
228
229 nodes_list[num_nodes].ip_port = dht->friends_list[i].client_list[j].ip_port;
230 num_nodes++;
231 } else {
232 for (k = 0; k < MAX_SENT_NODES; ++k) {
233
234 closest = id_closest( client_id,
235 nodes_list[k].client_id,
236 dht->friends_list[i].client_list[j].client_id );
237
238 if (closest == 2) {
239 memcpy( nodes_list[k].client_id,
240 dht->friends_list[i].client_list[j].client_id,
241 CLIENT_ID_SIZE );
242
243 nodes_list[k].ip_port = dht->friends_list[i].client_list[j].ip_port;
244 break;
245 }
246 }
247 }
248 }
249 }
250
251 return num_nodes;
252}
253
254/* replace first bad (or empty) node with this one
255 * return 0 if successful
256 * return 1 if not (list contains no bad nodes)
257 */
258static int replace_bad( Client_data *list,
259 uint32_t length,
260 uint8_t *client_id,
261 IP_Port ip_port )
262{
263 uint32_t i;
264 uint64_t temp_time = unix_time();
265
266 for (i = 0; i < length; ++i) {
267 /* if node is bad */
268 if (is_timeout(temp_time, list[i].timestamp, BAD_NODE_TIMEOUT)) {
269 memcpy(list[i].client_id, client_id, CLIENT_ID_SIZE);
270 list[i].ip_port = ip_port;
271 list[i].timestamp = temp_time;
272 list[i].ret_ip_port.ip.i = 0;
273 list[i].ret_ip_port.port = 0;
274 list[i].ret_timestamp = 0;
275 return 0;
276 }
277 }
278
279 return 1;
280}
281/*Sort the list. It will be sorted from furthest to closest.
282 TODO: this is innefficient and needs to be optimized.*/
283static void sort_list(Client_data *list, uint32_t length, uint8_t *comp_client_id)
284{
285 if (length == 0)
286 return;
287
288 uint32_t i, count;
289
290 while (1) {
291 count = 0;
292
293 for (i = 0; i < (length - 1); ++i) {
294 if (id_closest(comp_client_id, list[i].client_id, list[i + 1].client_id) == 1) {
295 Client_data temp = list[i + 1];
296 list[i + 1] = list[i];
297 list[i] = temp;
298 ++count;
299 }
300 }
301
302 if (count == 0)
303 return;
304 }
305}
306
307
308/* replace the first good node that is further to the comp_client_id than that of the client_id in the list */
309static int replace_good( Client_data *list,
310 uint32_t length,
311 uint8_t *client_id,
312 IP_Port ip_port,
313 uint8_t *comp_client_id )
314{
315 uint32_t i;
316 uint64_t temp_time = unix_time();
317 sort_list(list, length, comp_client_id);
318
319 for (i = 0; i < length; ++i)
320 if (id_closest(comp_client_id, list[i].client_id, client_id) == 2) {
321 memcpy(list[i].client_id, client_id, CLIENT_ID_SIZE);
322 list[i].ip_port = ip_port;
323 list[i].timestamp = temp_time;
324 list[i].ret_ip_port.ip.i = 0;
325 list[i].ret_ip_port.port = 0;
326 list[i].ret_timestamp = 0;
327 return 0;
328 }
329
330 return 1;
331}
332
333/* Attempt to add client with ip_port and client_id to the friends client list
334 * and close_clientlist
335 */
336void addto_lists(DHT *dht, IP_Port ip_port, uint8_t *client_id)
337{
338 uint32_t i;
339
340 /* NOTE: current behavior if there are two clients with the same id is
341 * to replace the first ip by the second.
342 */
343 if (!client_in_list(dht->close_clientlist, LCLIENT_LIST, client_id, ip_port)) {
344 if (replace_bad(dht->close_clientlist, LCLIENT_LIST, client_id, ip_port)) {
345 /* if we can't replace bad nodes we try replacing good ones */
346 replace_good( dht->close_clientlist,
347 LCLIENT_LIST,
348 client_id,
349 ip_port,
350 dht->c->self_public_key );
351 }
352 }
353
354 for (i = 0; i < dht->num_friends; ++i) {
355 if (!client_in_list( dht->friends_list[i].client_list,
356 MAX_FRIEND_CLIENTS,
357 client_id,
358 ip_port )) {
359
360 if (replace_bad( dht->friends_list[i].client_list,
361 MAX_FRIEND_CLIENTS,
362 client_id,
363 ip_port )) {
364 /* if we can't replace bad nodes we try replacing good ones. */
365 replace_good( dht->friends_list[i].client_list,
366 MAX_FRIEND_CLIENTS,
367 client_id,
368 ip_port,
369 dht->friends_list[i].client_id );
370 }
371 }
372 }
373}
374
375/* If client_id is a friend or us, update ret_ip_port
376 * nodeclient_id is the id of the node that sent us this info
377 */
378static void returnedip_ports(DHT *dht, IP_Port ip_port, uint8_t *client_id, uint8_t *nodeclient_id)
379{
380 uint32_t i, j;
381 uint64_t temp_time = unix_time();
382
383 if (id_equal(client_id, dht->c->self_public_key)) {
384
385 for (i = 0; i < LCLIENT_LIST; ++i) {
386 if (id_equal(nodeclient_id, dht->close_clientlist[i].client_id)) {
387 dht->close_clientlist[i].ret_ip_port = ip_port;
388 dht->close_clientlist[i].ret_timestamp = temp_time;
389 return;
390 }
391 }
392
393 } else {
394
395 for (i = 0; i < dht->num_friends; ++i) {
396 if (id_equal(client_id, dht->friends_list[i].client_id)) {
397
398 for (j = 0; j < MAX_FRIEND_CLIENTS; ++j) {
399 if (id_equal(nodeclient_id, dht->friends_list[i].client_list[j].client_id)) {
400 dht->friends_list[i].client_list[j].ret_ip_port = ip_port;
401 dht->friends_list[i].client_list[j].ret_timestamp = temp_time;
402 return;
403 }
404 }
405 }
406 }
407
408 }
409}
410
411/* Same as last function but for get_node requests. */
412static int is_gettingnodes(DHT *dht, IP_Port ip_port, uint64_t ping_id)
413{
414 uint32_t i;
415 uint8_t pinging;
416 uint64_t temp_time = unix_time();
417
418 for (i = 0; i < LSEND_NODES_ARRAY; ++i ) {
419 if (!is_timeout(temp_time, dht->send_nodes[i].timestamp, PING_TIMEOUT)) {
420 pinging = 0;
421
422 if (ip_port.ip.i != 0 && ipport_equal(dht->send_nodes[i].ip_port, ip_port))
423 ++pinging;
424
425 if (ping_id != 0 && dht->send_nodes[i].ping_id == ping_id)
426 ++pinging;
427
428 if (pinging == (ping_id != 0) + (ip_port.ip.i != 0))
429 return 1;
430 }
431 }
432
433 return 0;
434}
435
436/* Same but for get node requests */
437static uint64_t add_gettingnodes(DHT *dht, IP_Port ip_port)
438{
439 uint32_t i, j;
440 uint64_t ping_id = ((uint64_t)random_int() << 32) + random_int();
441 uint64_t temp_time = unix_time();
442
443 for (i = 0; i < PING_TIMEOUT; ++i ) {
444 for (j = 0; j < LSEND_NODES_ARRAY; ++j ) {
445 if (is_timeout(temp_time, dht->send_nodes[j].timestamp, PING_TIMEOUT - i)) {
446 dht->send_nodes[j].timestamp = temp_time;
447 dht->send_nodes[j].ip_port = ip_port;
448 dht->send_nodes[j].ping_id = ping_id;
449 return ping_id;
450 }
451 }
452 }
453
454 return 0;
455}
456
457/* send a getnodes request */
458static int getnodes(DHT *dht, IP_Port ip_port, uint8_t *public_key, uint8_t *client_id)
459{
460 /* check if packet is gonna be sent to ourself */
461 if (id_equal(public_key, dht->c->self_public_key) || is_gettingnodes(dht, ip_port, 0))
462 return 1;
463
464 uint64_t ping_id = add_gettingnodes(dht, ip_port);
465
466 if (ping_id == 0)
467 return 1;
468
469 uint8_t data[1 + CLIENT_ID_SIZE + crypto_box_NONCEBYTES + sizeof(ping_id) + CLIENT_ID_SIZE + ENCRYPTION_PADDING];
470 uint8_t plain[sizeof(ping_id) + CLIENT_ID_SIZE];
471 uint8_t encrypt[sizeof(ping_id) + CLIENT_ID_SIZE + ENCRYPTION_PADDING];
472 uint8_t nonce[crypto_box_NONCEBYTES];
473 random_nonce(nonce);
474
475 memcpy(plain, &ping_id, sizeof(ping_id));
476 memcpy(plain + sizeof(ping_id), client_id, CLIENT_ID_SIZE);
477
478 int len = encrypt_data( public_key,
479 dht->c->self_secret_key,
480 nonce,
481 plain,
482 sizeof(ping_id) + CLIENT_ID_SIZE,
483 encrypt );
484
485 if (len != sizeof(ping_id) + CLIENT_ID_SIZE + ENCRYPTION_PADDING)
486 return -1;
487
488 data[0] = NET_PACKET_GET_NODES;
489 memcpy(data + 1, dht->c->self_public_key, CLIENT_ID_SIZE);
490 memcpy(data + 1 + CLIENT_ID_SIZE, nonce, crypto_box_NONCEBYTES);
491 memcpy(data + 1 + CLIENT_ID_SIZE + crypto_box_NONCEBYTES, encrypt, len);
492
493 return sendpacket(dht->c->lossless_udp->net->sock, ip_port, data, sizeof(data));
494}
495
496/* send a send nodes response */
497static int sendnodes(DHT *dht, IP_Port ip_port, uint8_t *public_key, uint8_t *client_id, uint64_t ping_id)
498{
499 /* check if packet is gonna be sent to ourself */
500 if (id_equal(public_key, dht->c->self_public_key))
501 return 1;
502
503 uint8_t data[1 + CLIENT_ID_SIZE + crypto_box_NONCEBYTES + sizeof(ping_id)
504 + sizeof(Node_format) * MAX_SENT_NODES + ENCRYPTION_PADDING];
505
506 Node_format nodes_list[MAX_SENT_NODES];
507 int num_nodes = get_close_nodes(dht, client_id, nodes_list);
508
509 if (num_nodes == 0)
510 return 0;
511
512 uint8_t plain[sizeof(ping_id) + sizeof(Node_format) * MAX_SENT_NODES];
513 uint8_t encrypt[sizeof(ping_id) + sizeof(Node_format) * MAX_SENT_NODES + ENCRYPTION_PADDING];
514 uint8_t nonce[crypto_box_NONCEBYTES];
515 random_nonce(nonce);
516
517 memcpy(plain, &ping_id, sizeof(ping_id));
518 memcpy(plain + sizeof(ping_id), nodes_list, num_nodes * sizeof(Node_format));
519
520 int len = encrypt_data( public_key,
521 dht->c->self_secret_key,
522 nonce,
523 plain,
524 sizeof(ping_id) + num_nodes * sizeof(Node_format),
525 encrypt );
526
527 if (len != sizeof(ping_id) + num_nodes * sizeof(Node_format) + ENCRYPTION_PADDING)
528 return -1;
529
530 data[0] = NET_PACKET_SEND_NODES;
531 memcpy(data + 1, dht->c->self_public_key, CLIENT_ID_SIZE);
532 memcpy(data + 1 + CLIENT_ID_SIZE, nonce, crypto_box_NONCEBYTES);
533 memcpy(data + 1 + CLIENT_ID_SIZE + crypto_box_NONCEBYTES, encrypt, len);
534
535 return sendpacket(dht->c->lossless_udp->net->sock, ip_port, data, 1 + CLIENT_ID_SIZE + crypto_box_NONCEBYTES + len);
536}
537
538static int handle_getnodes(void *object, IP_Port source, uint8_t *packet, uint32_t length)
539{
540 DHT *dht = object;
541 uint64_t ping_id;
542
543 if (length != ( 1 + CLIENT_ID_SIZE + crypto_box_NONCEBYTES
544 + sizeof(ping_id) + CLIENT_ID_SIZE + ENCRYPTION_PADDING ))
545 return 1;
546
547 /* check if packet is from ourself. */
548 if (id_equal(packet + 1, dht->c->self_public_key))
549 return 1;
550
551 uint8_t plain[sizeof(ping_id) + CLIENT_ID_SIZE];
552
553 int len = decrypt_data( packet + 1,
554 dht->c->self_secret_key,
555 packet + 1 + CLIENT_ID_SIZE,
556 packet + 1 + CLIENT_ID_SIZE + crypto_box_NONCEBYTES,
557 sizeof(ping_id) + CLIENT_ID_SIZE + ENCRYPTION_PADDING,
558 plain );
559
560 if (len != sizeof(ping_id) + CLIENT_ID_SIZE)
561 return 1;
562
563 memcpy(&ping_id, plain, sizeof(ping_id));
564 sendnodes(dht, source, packet + 1, plain + sizeof(ping_id), ping_id);
565
566 //send_ping_request(dht, source, (clientid_t*) (packet + 1)); /* TODO: make this smarter? */
567
568 return 0;
569}
570
571static int handle_sendnodes(void *object, IP_Port source, uint8_t *packet, uint32_t length)
572{
573 DHT *dht = object;
574 uint64_t ping_id;
575 uint32_t cid_size = 1 + CLIENT_ID_SIZE;
576 cid_size += crypto_box_NONCEBYTES + sizeof(ping_id) + ENCRYPTION_PADDING;
577
578 if (length > (cid_size + sizeof(Node_format) * MAX_SENT_NODES) ||
579 ((length - cid_size) % sizeof(Node_format)) != 0 ||
580 (length < cid_size + sizeof(Node_format)))
581 return 1;
582
583 uint32_t num_nodes = (length - cid_size) / sizeof(Node_format);
584 uint8_t plain[sizeof(ping_id) + sizeof(Node_format) * MAX_SENT_NODES];
585
586 int len = decrypt_data(
587 packet + 1,
588 dht->c->self_secret_key,
589 packet + 1 + CLIENT_ID_SIZE,
590 packet + 1 + CLIENT_ID_SIZE + crypto_box_NONCEBYTES,
591 sizeof(ping_id) + num_nodes * sizeof(Node_format) + ENCRYPTION_PADDING, plain );
592
593 if (len != sizeof(ping_id) + num_nodes * sizeof(Node_format))
594 return 1;
595
596 memcpy(&ping_id, plain, sizeof(ping_id));
597
598 if (!is_gettingnodes(dht, source, ping_id))
599 return 1;
600
601 Node_format nodes_list[MAX_SENT_NODES];
602 memcpy(nodes_list, plain + sizeof(ping_id), num_nodes * sizeof(Node_format));
603
604 addto_lists(dht, source, packet + 1);
605
606 uint32_t i;
607
608 for (i = 0; i < num_nodes; ++i) {
609 send_ping_request(dht->ping, dht->c, nodes_list[i].ip_port, (clientid_t *) &nodes_list[i].client_id);
610 returnedip_ports(dht, nodes_list[i].ip_port, nodes_list[i].client_id, packet + 1);
611 }
612
613 return 0;
614}
615
616/*----------------------------------------------------------------------------------*/
617/*------------------------END of packet handling functions--------------------------*/
618
619int DHT_addfriend(DHT *dht, uint8_t *client_id)
620{
621 if (friend_number(dht, client_id) != -1) /*Is friend already in DHT?*/
622 return 1;
623
624 DHT_Friend *temp;
625 temp = realloc(dht->friends_list, sizeof(DHT_Friend) * (dht->num_friends + 1));
626
627 if (temp == NULL)
628 return 1;
629
630 dht->friends_list = temp;
631 memset(&dht->friends_list[dht->num_friends], 0, sizeof(DHT_Friend));
632 memcpy(dht->friends_list[dht->num_friends].client_id, client_id, CLIENT_ID_SIZE);
633
634 dht->friends_list[dht->num_friends].NATping_id = ((uint64_t)random_int() << 32) + random_int();
635 ++dht->num_friends;
636 return 0;
637}
638
639int DHT_delfriend(DHT *dht, uint8_t *client_id)
640{
641 uint32_t i;
642 DHT_Friend *temp;
643
644 for (i = 0; i < dht->num_friends; ++i) {
645 /* Equal */
646 if (id_equal(dht->friends_list[i].client_id, client_id)) {
647 --dht->num_friends;
648
649 if (dht->num_friends != i) {
650 memcpy( dht->friends_list[i].client_id,
651 dht->friends_list[dht->num_friends].client_id,
652 CLIENT_ID_SIZE );
653 }
654
655 if (dht->num_friends == 0) {
656 free(dht->friends_list);
657 dht->friends_list = NULL;
658 return 0;
659 }
660
661 temp = realloc(dht->friends_list, sizeof(DHT_Friend) * (dht->num_friends));
662
663 if (temp == NULL)
664 return 1;
665
666 dht->friends_list = temp;
667 return 0;
668 }
669 }
670
671 return 1;
672}
673
674/* TODO: Optimize this. */
675IP_Port DHT_getfriendip(DHT *dht, uint8_t *client_id)
676{
677 uint32_t i, j;
678 uint64_t temp_time = unix_time();
679 IP_Port empty = {{{0}}, 0};
680
681 for (i = 0; i < dht->num_friends; ++i) {
682 /* Equal */
683 if (id_equal(dht->friends_list[i].client_id, client_id)) {
684 for (j = 0; j < MAX_FRIEND_CLIENTS; ++j) {
685 if (id_equal(dht->friends_list[i].client_list[j].client_id, client_id)
686 && !is_timeout(temp_time, dht->friends_list[i].client_list[j].timestamp, BAD_NODE_TIMEOUT))
687 return dht->friends_list[i].client_list[j].ip_port;
688 }
689
690 return empty;
691 }
692 }
693
694 empty.ip.i = 1;
695 return empty;
696}
697
698/* Ping each client in the "friends" list every PING_INTERVAL seconds. Send a get nodes request
699 * every GET_NODE_INTERVAL seconds to a random good node for each "friend" in our "friends" list.
700 */
701static void do_DHT_friends(DHT *dht)
702{
703 uint32_t i, j;
704 uint64_t temp_time = unix_time();
705 uint32_t rand_node;
706 uint32_t index[MAX_FRIEND_CLIENTS];
707
708 for (i = 0; i < dht->num_friends; ++i) {
709 uint32_t num_nodes = 0;
710
711 for (j = 0; j < MAX_FRIEND_CLIENTS; ++j) {
712 /* if node is not dead. */
713 if (!is_timeout(temp_time, dht->friends_list[i].client_list[j].timestamp, Kill_NODE_TIMEOUT)) {
714 if ((dht->friends_list[i].client_list[j].last_pinged + PING_INTERVAL) <= temp_time) {
715 send_ping_request(dht->ping, dht->c, dht->friends_list[i].client_list[j].ip_port,
716 (clientid_t *) &dht->friends_list[i].client_list[j].client_id );
717 dht->friends_list[i].client_list[j].last_pinged = temp_time;
718 }
719
720 /* if node is good. */
721 if (!is_timeout(temp_time, dht->friends_list[i].client_list[j].timestamp, BAD_NODE_TIMEOUT)) {
722 index[num_nodes] = j;
723 ++num_nodes;
724 }
725 }
726 }
727
728 if (dht->friends_list[i].lastgetnode + GET_NODE_INTERVAL <= temp_time && num_nodes != 0) {
729 rand_node = rand() % num_nodes;
730 getnodes(dht, dht->friends_list[i].client_list[index[rand_node]].ip_port,
731 dht->friends_list[i].client_list[index[rand_node]].client_id,
732 dht->friends_list[i].client_id );
733 dht->friends_list[i].lastgetnode = temp_time;
734 }
735 }
736}
737
738/* Ping each client in the close nodes list every PING_INTERVAL seconds.
739 * Send a get nodes request every GET_NODE_INTERVAL seconds to a random good node in the list.
740 */
741static void do_Close(DHT *dht)
742{
743 uint32_t i;
744 uint64_t temp_time = unix_time();
745 uint32_t num_nodes = 0;
746 uint32_t rand_node;
747 uint32_t index[LCLIENT_LIST];
748
749 for (i = 0; i < LCLIENT_LIST; ++i) {
750 /* if node is not dead. */
751 if (!is_timeout(temp_time, dht->close_clientlist[i].timestamp, Kill_NODE_TIMEOUT)) {
752 if ((dht->close_clientlist[i].last_pinged + PING_INTERVAL) <= temp_time) {
753 send_ping_request(dht->ping, dht->c, dht->close_clientlist[i].ip_port,
754 (clientid_t *) &dht->close_clientlist[i].client_id );
755 dht->close_clientlist[i].last_pinged = temp_time;
756 }
757
758 /* if node is good. */
759 if (!is_timeout(temp_time, dht->close_clientlist[i].timestamp, BAD_NODE_TIMEOUT)) {
760 index[num_nodes] = i;
761 ++num_nodes;
762 }
763 }
764 }
765
766 if (dht->close_lastgetnodes + GET_NODE_INTERVAL <= temp_time && num_nodes != 0) {
767 rand_node = rand() % num_nodes;
768 getnodes(dht, dht->close_clientlist[index[rand_node]].ip_port,
769 dht->close_clientlist[index[rand_node]].client_id,
770 dht->c->self_public_key );
771 dht->close_lastgetnodes = temp_time;
772 }
773}
774
775void DHT_bootstrap(DHT *dht, IP_Port ip_port, uint8_t *public_key)
776{
777 getnodes(dht, ip_port, public_key, dht->c->self_public_key);
778 send_ping_request(dht->ping, dht->c, ip_port, (clientid_t *) public_key);
779}
780
781/* send the given packet to node with client_id
782 * returns -1 if failure
783 */
784int route_packet(DHT *dht, uint8_t *client_id, uint8_t *packet, uint32_t length)
785{
786 uint32_t i;
787
788 for (i = 0; i < LCLIENT_LIST; ++i) {
789 if (id_equal(client_id, dht->close_clientlist[i].client_id))
790 return sendpacket(dht->c->lossless_udp->net->sock, dht->close_clientlist[i].ip_port, packet, length);
791 }
792
793 return -1;
794}
795
796/* Puts all the different ips returned by the nodes for a friend_num into array ip_portlist
797 * ip_portlist must be at least MAX_FRIEND_CLIENTS big
798 * returns the number of ips returned
799 * return 0 if we are connected to friend or if no ips were found.
800 * returns -1 if no such friend
801 */
802static int friend_iplist(DHT *dht, IP_Port *ip_portlist, uint16_t friend_num)
803{
804 int num_ips = 0;
805 uint32_t i;
806 uint64_t temp_time = unix_time();
807
808 if (friend_num >= dht->num_friends)
809 return -1;
810
811 DHT_Friend *friend = &dht->friends_list[friend_num];
812 Client_data *client;
813
814 for (i = 0; i < MAX_FRIEND_CLIENTS; ++i) {
815 client = &friend->client_list[i];
816
817 /*If ip is not zero and node is good */
818 if (client->ret_ip_port.ip.i != 0 && !is_timeout(temp_time, client->ret_timestamp, BAD_NODE_TIMEOUT)) {
819
820 if (id_equal(client->client_id, friend->client_id))
821 return 0;
822
823 ip_portlist[num_ips] = client->ret_ip_port;
824 ++num_ips;
825 }
826 }
827
828 return num_ips;
829}
830
831
832/* Send the following packet to everyone who tells us they are connected to friend_id
833 * returns the number of nodes it sent the packet to
834 *
835 * Only works if more than (MAX_FRIEND_CLIENTS / 2) return an ip for friend.
836 */
837int route_tofriend(DHT *dht, uint8_t *friend_id, uint8_t *packet, uint32_t length)
838{
839 int num = friend_number(dht, friend_id);
840
841 if (num == -1)
842 return 0;
843
844 uint32_t i, sent = 0;
845
846 IP_Port ip_list[MAX_FRIEND_CLIENTS];
847 int ip_num = friend_iplist(dht, ip_list, num);
848
849 if (ip_num < (MAX_FRIEND_CLIENTS / 2))
850 return 0;
851
852 uint64_t temp_time = unix_time();
853 DHT_Friend *friend = &dht->friends_list[num];
854 Client_data *client;
855
856 for (i = 0; i < MAX_FRIEND_CLIENTS; ++i) {
857 client = &friend->client_list[i];
858
859 /*If ip is not zero and node is good */
860 if (client->ret_ip_port.ip.i != 0 && !is_timeout(temp_time, client->ret_timestamp, BAD_NODE_TIMEOUT)) {
861 if (sendpacket(dht->c->lossless_udp->net->sock, client->ip_port, packet, length) == length)
862 ++sent;
863 }
864 }
865
866 return sent;
867}
868
869/* Send the following packet to one random person who tells us they are connected to friend_id
870* returns the number of nodes it sent the packet to
871*/
872static int routeone_tofriend(DHT *dht, uint8_t *friend_id, uint8_t *packet, uint32_t length)
873{
874 int num = friend_number(dht, friend_id);
875
876 if (num == -1)
877 return 0;
878
879 DHT_Friend *friend = &dht->friends_list[num];
880 Client_data *client;
881
882 IP_Port ip_list[MAX_FRIEND_CLIENTS];
883 int n = 0;
884 uint32_t i;
885 uint64_t temp_time = unix_time();
886
887 for (i = 0; i < MAX_FRIEND_CLIENTS; ++i) {
888 client = &friend->client_list[i];
889
890 /*If ip is not zero and node is good */
891 if (client->ret_ip_port.ip.i != 0 && !is_timeout(temp_time, client->ret_timestamp, BAD_NODE_TIMEOUT)) {
892 ip_list[n] = client->ip_port;
893 ++n;
894 }
895 }
896
897 if (n < 1)
898 return 0;
899
900 if (sendpacket(dht->c->lossless_udp->net->sock, ip_list[rand() % n], packet, length) == length)
901 return 1;
902
903 return 0;
904}
905
906/* Puts all the different ips returned by the nodes for a friend_id into array ip_portlist
907 * ip_portlist must be at least MAX_FRIEND_CLIENTS big
908 * returns the number of ips returned
909 * return 0 if we are connected to friend or if no ips were found.
910 * returns -1 if no such friend
911 */
912int friend_ips(DHT *dht, IP_Port *ip_portlist, uint8_t *friend_id)
913{
914 uint32_t i;
915
916 for (i = 0; i < dht->num_friends; ++i) {
917 /* Equal */
918 if (id_equal(dht->friends_list[i].client_id, friend_id))
919 return friend_iplist(dht, ip_portlist, i);
920 }
921
922 return -1;
923}
924
925/*----------------------------------------------------------------------------------*/
926/*---------------------BEGINNING OF NAT PUNCHING FUNCTIONS--------------------------*/
927
928static int send_NATping(DHT *dht, uint8_t *public_key, uint64_t ping_id, uint8_t type)
929{
930 uint8_t data[sizeof(uint64_t) + 1];
931 uint8_t packet[MAX_DATA_SIZE];
932
933 int num = 0;
934
935 data[0] = type;
936 memcpy(data + 1, &ping_id, sizeof(uint64_t));
937 /* 254 is NAT ping request packet id */
938 int len = create_request(dht->c->self_public_key, dht->c->self_secret_key, packet, public_key, data,
939 sizeof(uint64_t) + 1, CRYPTO_PACKET_NAT_PING);
940
941 if (len == -1)
942 return -1;
943
944 if (type == 0) /*If packet is request use many people to route it*/
945 num = route_tofriend(dht, public_key, packet, len);
946 else if (type == 1) /*If packet is response use only one person to route it*/
947 num = routeone_tofriend(dht, public_key, packet, len);
948
949 if (num == 0)
950 return -1;
951
952 return num;
953}
954
955/* Handle a received ping request for */
956static int handle_NATping(void *object, IP_Port source, uint8_t *source_pubkey, uint8_t *packet, uint32_t length)
957{
958 DHT *dht = object;
959 uint64_t ping_id;
960 memcpy(&ping_id, packet + 1, sizeof(uint64_t));
961
962 int friendnumber = friend_number(dht, source_pubkey);
963
964 if (friendnumber == -1)
965 return 1;
966
967 DHT_Friend *friend = &dht->friends_list[friendnumber];
968
969 if (packet[0] == NAT_PING_REQUEST) {
970 /* 1 is reply */
971 send_NATping(dht, source_pubkey, ping_id, NAT_PING_RESPONSE);
972 friend->recvNATping_timestamp = unix_time();
973 return 0;
974 } else if (packet[0] == NAT_PING_RESPONSE) {
975 if (friend->NATping_id == ping_id) {
976 friend->NATping_id = ((uint64_t)random_int() << 32) + random_int();
977 friend->hole_punching = 1;
978 return 0;
979 }
980 }
981
982 return 1;
983}
984
985/* Get the most common ip in the ip_portlist
986 * Only return ip if it appears in list min_num or more
987 * len must not be bigger than MAX_FRIEND_CLIENTS
988 * return ip of 0 if failure
989 */
990static IP NAT_commonip(IP_Port *ip_portlist, uint16_t len, uint16_t min_num)
991{
992 IP zero = {{0}};
993
994 if (len > MAX_FRIEND_CLIENTS)
995 return zero;
996
997 uint32_t i, j;
998 uint16_t numbers[MAX_FRIEND_CLIENTS] = {0};
999
1000 for (i = 0; i < len; ++i) {
1001 for (j = 0; j < len; ++j) {
1002 if (ip_portlist[i].ip.i == ip_portlist[j].ip.i)
1003 ++numbers[i];
1004 }
1005
1006 if (numbers[i] >= min_num)
1007 return ip_portlist[i].ip;
1008 }
1009
1010 return zero;
1011}
1012
1013/* Return all the ports for one ip in a list
1014 * portlist must be at least len long
1015 * where len is the length of ip_portlist
1016 * returns the number of ports and puts the list of ports in portlist
1017 */
1018static uint16_t NAT_getports(uint16_t *portlist, IP_Port *ip_portlist, uint16_t len, IP ip)
1019{
1020 uint32_t i;
1021 uint16_t num = 0;
1022
1023 for (i = 0; i < len; ++i) {
1024 if (ip_portlist[i].ip.i == ip.i) {
1025 portlist[num] = ntohs(ip_portlist[i].port);
1026 ++num;
1027 }
1028 }
1029
1030 return num;
1031}
1032
1033static void punch_holes(DHT *dht, IP ip, uint16_t *port_list, uint16_t numports, uint16_t friend_num)
1034{
1035 if (numports > MAX_FRIEND_CLIENTS || numports == 0)
1036 return;
1037
1038 uint32_t i;
1039 uint32_t top = dht->friends_list[friend_num].punching_index + MAX_PUNCHING_PORTS;
1040
1041 for (i = dht->friends_list[friend_num].punching_index; i != top; i++) {
1042 /*TODO: improve port guessing algorithm*/
1043 uint16_t port = port_list[(i / 2) % numports] + (i / (2 * numports)) * ((i % 2) ? -1 : 1);
1044 IP_Port pinging = {ip, htons(port)};
1045 send_ping_request(dht->ping, dht->c, pinging, (clientid_t *) &dht->friends_list[friend_num].client_id);
1046 }
1047
1048 dht->friends_list[friend_num].punching_index = i;
1049}
1050
1051static void do_NAT(DHT *dht)
1052{
1053 uint32_t i;
1054 uint64_t temp_time = unix_time();
1055
1056 for (i = 0; i < dht->num_friends; ++i) {
1057 IP_Port ip_list[MAX_FRIEND_CLIENTS];
1058 int num = friend_iplist(dht, ip_list, i);
1059
1060 /*If already connected or friend is not online don't try to hole punch*/
1061 if (num < MAX_FRIEND_CLIENTS / 2)
1062 continue;
1063
1064 if (dht->friends_list[i].NATping_timestamp + PUNCH_INTERVAL < temp_time) {
1065 send_NATping(dht, dht->friends_list[i].client_id, dht->friends_list[i].NATping_id, NAT_PING_REQUEST);
1066 dht->friends_list[i].NATping_timestamp = temp_time;
1067 }
1068
1069 if (dht->friends_list[i].hole_punching == 1 &&
1070 dht->friends_list[i].punching_timestamp + PUNCH_INTERVAL < temp_time &&
1071 dht->friends_list[i].recvNATping_timestamp + PUNCH_INTERVAL * 2 >= temp_time) {
1072
1073 IP ip = NAT_commonip(ip_list, num, MAX_FRIEND_CLIENTS / 2);
1074
1075 if (ip.i == 0)
1076 continue;
1077
1078 uint16_t port_list[MAX_FRIEND_CLIENTS];
1079 uint16_t numports = NAT_getports(port_list, ip_list, num, ip);
1080 punch_holes(dht, ip, port_list, numports, i);
1081
1082 dht->friends_list[i].punching_timestamp = temp_time;
1083 dht->friends_list[i].hole_punching = 0;
1084 }
1085 }
1086}
1087
1088/*----------------------------------------------------------------------------------*/
1089/*-----------------------END OF NAT PUNCHING FUNCTIONS------------------------------*/
1090
1091
1092/* Add nodes to the toping list
1093 all nodes in this list are pinged every TIME_TOPING seconds
1094 and are then removed from the list.
1095 if the list is full the nodes farthest from our client_id are replaced
1096 the purpose of this list is to enable quick integration of new nodes into the
1097 network while preventing amplification attacks.
1098 return 0 if node was added
1099 return -1 if node was not added */
1100int add_toping(DHT *dht, uint8_t *client_id, IP_Port ip_port)
1101{
1102 if (ip_port.ip.i == 0)
1103 return -1;
1104
1105 uint32_t i;
1106
1107 for (i = 0; i < MAX_TOPING; ++i) {
1108 if (dht->toping[i].ip_port.ip.i == 0) {
1109 memcpy(dht->toping[i].client_id, client_id, CLIENT_ID_SIZE);
1110 dht->toping[i].ip_port.ip.i = ip_port.ip.i;
1111 dht->toping[i].ip_port.port = ip_port.port;
1112 return 0;
1113 }
1114 }
1115
1116 for (i = 0; i < MAX_TOPING; ++i) {
1117 if (id_closest(dht->c->self_public_key, dht->toping[i].client_id, client_id) == 2) {
1118 memcpy(dht->toping[i].client_id, client_id, CLIENT_ID_SIZE);
1119 dht->toping[i].ip_port.ip.i = ip_port.ip.i;
1120 dht->toping[i].ip_port.port = ip_port.port;
1121 return 0;
1122 }
1123 }
1124
1125 return -1;
1126}
1127
1128/*Ping all the valid nodes in the toping list every TIME_TOPING seconds
1129 this function must be run at least once every TIME_TOPING seconds*/
1130static void do_toping(DHT *dht)
1131{
1132 uint64_t temp_time = unix_time();
1133
1134 if (!is_timeout(temp_time, dht->last_toping, TIME_TOPING))
1135 return;
1136
1137 dht->last_toping = temp_time;
1138 uint32_t i;
1139
1140 for (i = 0; i < MAX_TOPING; ++i) {
1141 if (dht->toping[i].ip_port.ip.i == 0)
1142 return;
1143
1144 send_ping_request(dht->ping, dht->c, dht->toping[i].ip_port, (clientid_t *) dht->toping[i].client_id);
1145 dht->toping[i].ip_port.ip.i = 0;
1146 }
1147}
1148
1149
1150DHT *new_DHT(Net_Crypto *c)
1151{
1152 if (c == NULL)
1153 return NULL;
1154
1155 DHT *temp = calloc(1, sizeof(DHT));
1156
1157 if (temp == NULL)
1158 return NULL;
1159
1160 temp->ping = new_ping();
1161
1162 if (temp->ping == NULL) {
1163 kill_DHT(temp);
1164 return NULL;
1165 }
1166
1167 temp->c = c;
1168 networking_registerhandler(c->lossless_udp->net, NET_PACKET_PING_REQUEST, &handle_ping_request, temp);
1169 networking_registerhandler(c->lossless_udp->net, NET_PACKET_PING_RESPONSE, &handle_ping_response, temp);
1170 networking_registerhandler(c->lossless_udp->net, NET_PACKET_GET_NODES, &handle_getnodes, temp);
1171 networking_registerhandler(c->lossless_udp->net, NET_PACKET_SEND_NODES, &handle_sendnodes, temp);
1172 init_cryptopackets(temp);
1173 cryptopacket_registerhandler(c, CRYPTO_PACKET_NAT_PING, &handle_NATping, temp);
1174 return temp;
1175}
1176
1177void do_DHT(DHT *dht)
1178{
1179 do_Close(dht);
1180 do_DHT_friends(dht);
1181 do_NAT(dht);
1182 do_toping(dht);
1183}
1184void kill_DHT(DHT *dht)
1185{
1186 kill_ping(dht->ping);
1187 free(dht->friends_list);
1188 free(dht);
1189}
1190
1191/* get the size of the DHT (for saving) */
1192uint32_t DHT_size(DHT *dht)
1193{
1194 return sizeof(dht->close_clientlist) + sizeof(DHT_Friend) * dht->num_friends;
1195}
1196
1197/* save the DHT in data where data is an array of size DHT_size() */
1198void DHT_save(DHT *dht, uint8_t *data)
1199{
1200 memcpy(data, dht->close_clientlist, sizeof(dht->close_clientlist));
1201 memcpy(data + sizeof(dht->close_clientlist), dht->friends_list, sizeof(DHT_Friend) * dht->num_friends);
1202}
1203
1204/* load the DHT from data of size size;
1205 * return -1 if failure
1206 * return 0 if success
1207 */
1208int DHT_load(DHT *dht, uint8_t *data, uint32_t size)
1209{
1210 if (size < sizeof(dht->close_clientlist))
1211 return -1;
1212
1213 if ((size - sizeof(dht->close_clientlist)) % sizeof(DHT_Friend) != 0)
1214 return -1;
1215
1216 uint32_t i, j;
1217 uint16_t temp;
1218 /* uint64_t temp_time = unix_time(); */
1219
1220 Client_data *client;
1221
1222 temp = (size - sizeof(dht->close_clientlist)) / sizeof(DHT_Friend);
1223
1224 if (temp != 0) {
1225 DHT_Friend *tempfriends_list = (DHT_Friend *)(data + sizeof(dht->close_clientlist));
1226
1227 for (i = 0; i < temp; ++i) {
1228 DHT_addfriend(dht, tempfriends_list[i].client_id);
1229
1230 for (j = 0; j < MAX_FRIEND_CLIENTS; ++j) {
1231 client = &tempfriends_list[i].client_list[j];
1232
1233 if (client->timestamp != 0)
1234 getnodes(dht, client->ip_port, client->client_id, tempfriends_list[i].client_id);
1235 }
1236 }
1237 }
1238
1239 Client_data *tempclose_clientlist = (Client_data *)data;
1240
1241 for (i = 0; i < LCLIENT_LIST; ++i) {
1242 if (tempclose_clientlist[i].timestamp != 0)
1243 DHT_bootstrap(dht, tempclose_clientlist[i].ip_port,
1244 tempclose_clientlist[i].client_id );
1245 }
1246
1247 return 0;
1248}
1249
1250/* returns 0 if we are not connected to the DHT
1251 * returns 1 if we are
1252 */
1253int DHT_isconnected(DHT *dht)
1254{
1255 uint32_t i;
1256 uint64_t temp_time = unix_time();
1257
1258 for (i = 0; i < LCLIENT_LIST; ++i) {
1259 if (!is_timeout(temp_time, dht->close_clientlist[i].timestamp, BAD_NODE_TIMEOUT))
1260 return 1;
1261 }
1262
1263 return 0;
1264}
diff --git a/toxcore/DHT.h b/toxcore/DHT.h
new file mode 100644
index 00000000..6295581b
--- /dev/null
+++ b/toxcore/DHT.h
@@ -0,0 +1,193 @@
1/* DHT.h
2 *
3 * An implementation of the DHT as seen in http://wiki.tox.im/index.php/DHT
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#ifndef DHT_H
25#define DHT_H
26
27#include "net_crypto.h"
28
29#ifdef __cplusplus
30extern "C" {
31#endif
32
33
34/* size of the client_id in bytes */
35#define CLIENT_ID_SIZE crypto_box_PUBLICKEYBYTES
36
37/* maximum number of clients stored per friend. */
38#define MAX_FRIEND_CLIENTS 8
39
40/* A list of the clients mathematically closest to ours. */
41#define LCLIENT_LIST 32
42
43/* The list of ip ports along with the ping_id of what we sent them and a timestamp */
44#define LPING_ARRAY 256 //NOTE Deprecated (doesn't do anything)
45
46#define LSEND_NODES_ARRAY LPING_ARRAY/2
47
48/*Maximum newly announced nodes to ping per TIME_TOPING seconds*/
49#define MAX_TOPING 16
50
51typedef struct {
52 uint8_t client_id[CLIENT_ID_SIZE];
53 IP_Port ip_port;
54 uint64_t timestamp;
55 uint64_t last_pinged;
56
57 /* Returned by this node. Either our friend or us */
58 IP_Port ret_ip_port;
59 uint64_t ret_timestamp;
60} Client_data;
61
62/*----------------------------------------------------------------------------------*/
63
64typedef struct {
65 uint8_t client_id[CLIENT_ID_SIZE];
66 Client_data client_list[MAX_FRIEND_CLIENTS];
67
68 /* time at which the last get_nodes request was sent. */
69 uint64_t lastgetnode;
70
71 /* Symetric NAT hole punching stuff */
72
73 /* 1 if currently hole punching, otherwise 0 */
74 uint8_t hole_punching;
75 uint32_t punching_index;
76 uint64_t punching_timestamp;
77 uint64_t recvNATping_timestamp;
78 uint64_t NATping_id;
79 uint64_t NATping_timestamp;
80} DHT_Friend;
81
82typedef struct {
83 uint8_t client_id[CLIENT_ID_SIZE];
84 IP_Port ip_port;
85} Node_format;
86
87typedef struct {
88 IP_Port ip_port;
89 uint64_t ping_id;
90 uint64_t timestamp;
91} Pinged;
92
93/*----------------------------------------------------------------------------------*/
94typedef struct {
95 Net_Crypto *c;
96 Client_data close_clientlist[LCLIENT_LIST];
97 DHT_Friend *friends_list;
98 uint16_t num_friends;
99 Pinged send_nodes[LSEND_NODES_ARRAY];
100 Node_format toping[MAX_TOPING];
101 uint64_t last_toping;
102 uint64_t close_lastgetnodes;
103 void *ping;
104} DHT;
105/*----------------------------------------------------------------------------------*/
106
107Client_data *DHT_get_close_list(DHT *dht);
108
109/* Add a new friend to the friends list
110 client_id must be CLIENT_ID_SIZE bytes long.
111 returns 0 if success
112 returns 1 if failure (friends list is full) */
113int DHT_addfriend(DHT *dht, uint8_t *client_id);
114
115/* Delete a friend from the friends list
116 client_id must be CLIENT_ID_SIZE bytes long.
117 returns 0 if success
118 returns 1 if failure (client_id not in friends list) */
119int DHT_delfriend(DHT *dht, uint8_t *client_id);
120
121/* Get ip of friend
122 client_id must be CLIENT_ID_SIZE bytes long.
123 ip must be 4 bytes long.
124 port must be 2 bytes long.
125 returns ip if success
126 returns ip of 0 if failure (This means the friend is either offline or we have not found him yet.)
127 returns ip of 1 if friend is not in list. */
128IP_Port DHT_getfriendip(DHT *dht, uint8_t *client_id);
129
130/* Run this function at least a couple times per second (It's the main loop) */
131void do_DHT(DHT *dht);
132
133/* Use this function to bootstrap the client
134 Sends a get nodes request to the given node with ip port and public_key */
135void DHT_bootstrap(DHT *dht, IP_Port ip_port, uint8_t *public_key);
136
137/* Add nodes to the toping list
138 all nodes in this list are pinged every TIME_TOPING seconds
139 and are then removed from the list.
140 if the list is full the nodes farthest from our client_id are replaced
141 the purpose of this list is to enable quick integration of new nodes into the
142 network while preventing amplification attacks.
143 return 0 if node was added
144 return -1 if node was not added */
145int add_toping(DHT *dht, uint8_t *client_id, IP_Port ip_port);
146
147/* ROUTING FUNCTIONS */
148
149/* send the given packet to node with client_id
150 returns -1 if failure */
151int route_packet(DHT *dht, uint8_t *client_id, uint8_t *packet, uint32_t length);
152
153/* Send the following packet to everyone who tells us they are connected to friend_id
154 returns the number of nodes it sent the packet to */
155int route_tofriend(DHT *dht, uint8_t *friend_id, uint8_t *packet, uint32_t length);
156
157/* NAT PUNCHING FUNCTIONS */
158
159/* Puts all the different ips returned by the nodes for a friend_id into array ip_portlist
160 ip_portlist must be at least MAX_FRIEND_CLIENTS big
161 returns the number of ips returned
162 returns -1 if no such friend*/
163int friend_ips(DHT *dht, IP_Port *ip_portlist, uint8_t *friend_id);
164
165/* SAVE/LOAD functions */
166
167/* get the size of the DHT (for saving) */
168uint32_t DHT_size(DHT *dht);
169
170/* save the DHT in data where data is an array of size DHT_size() */
171void DHT_save(DHT *dht, uint8_t *data);
172
173/* init DHT */
174DHT *new_DHT(Net_Crypto *c);
175
176void kill_DHT(DHT *dht);
177
178/* load the DHT from data of size size;
179 return -1 if failure
180 return 0 if success */
181int DHT_load(DHT *dht, uint8_t *data, uint32_t size);
182
183/* returns 0 if we are not connected to the DHT
184 returns 1 if we are */
185int DHT_isconnected(DHT *dht);
186
187void addto_lists(DHT *dht, IP_Port ip_port, uint8_t *client_id);
188
189#ifdef __cplusplus
190}
191#endif
192
193#endif
diff --git a/toxcore/LAN_discovery.c b/toxcore/LAN_discovery.c
new file mode 100644
index 00000000..49f52ce7
--- /dev/null
+++ b/toxcore/LAN_discovery.c
@@ -0,0 +1,152 @@
1/* LAN_discovery.c
2 *
3 * LAN discovery implementation.
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 "LAN_discovery.h"
25
26#define MAX_INTERFACES 16
27
28#ifdef __linux
29/* get the first working broadcast address that's not from "lo"
30 * returns higher than 0 on success
31 * returns 0 on error */
32static uint32_t get_broadcast(void)
33{
34 /* not sure how many platforms this will
35 * run on, so it's wrapped in __linux for now */
36 struct sockaddr_in *sock_holder = NULL;
37 struct ifreq i_faces[MAX_INTERFACES];
38 struct ifconf ifconf;
39 int count = 0;
40 int sock = 0;
41 int i = 0;
42
43 /* configure ifconf for the ioctl call */
44 if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
45 perror("[!] get_broadcast: socket() error");
46 return 0;
47 }
48
49 memset(i_faces, 0, sizeof(struct ifreq) * MAX_INTERFACES);
50
51 ifconf.ifc_buf = (char *)i_faces;
52 ifconf.ifc_len = sizeof(i_faces);
53 count = ifconf.ifc_len / sizeof(struct ifreq);
54
55 if (ioctl(sock, SIOCGIFCONF, &ifconf) < 0) {
56 perror("get_broadcast: ioctl() error");
57 return 0;
58 }
59
60 for (i = 0; i < count; i++) {
61 /* skip the loopback interface, as it's useless */
62 if (strcmp(i_faces[i].ifr_name, "lo") != 0) {
63 if (ioctl(sock, SIOCGIFBRDADDR, &i_faces[i]) < 0) {
64 perror("[!] get_broadcast: ioctl error");
65 return 0;
66 }
67
68 /* just to clarify where we're getting the values from */
69 sock_holder = (struct sockaddr_in *)&i_faces[i].ifr_broadaddr;
70 break;
71 }
72 }
73
74 close(sock);
75
76 if (sock_holder == NULL) {
77 perror("[!] no broadcast device found");
78 return 0;
79 }
80
81 return sock_holder->sin_addr.s_addr;
82}
83#endif
84
85/* Return the broadcast ip */
86static IP broadcast_ip(void)
87{
88 IP ip;
89#ifdef __linux
90 ip.i = get_broadcast();
91
92 if (ip.i == 0)
93 /* error errored, but try anyway? */
94 ip.i = ~0;
95
96#else
97 ip.i = ~0;
98#endif
99 return ip;
100}
101
102/*return 0 if ip is a LAN ip
103 return -1 if it is not */
104static int LAN_ip(IP ip)
105{
106 if (ip.c[0] == 127)/* Loopback */
107 return 0;
108
109 if (ip.c[0] == 10)/* 10.0.0.0 to 10.255.255.255 range */
110 return 0;
111
112 if (ip.c[0] == 172 && ip.c[1] >= 16 && ip.c[1] <= 31)/* 172.16.0.0 to 172.31.255.255 range */
113 return 0;
114
115 if (ip.c[0] == 192 && ip.c[1] == 168) /* 192.168.0.0 to 192.168.255.255 range */
116 return 0;
117
118 if (ip.c[0] == 169 && ip.c[1] == 254 && ip.c[2] != 0 && ip.c[2] != 255)/* 169.254.1.0 to 169.254.254.255 range */
119 return 0;
120
121 return -1;
122}
123
124static int handle_LANdiscovery(void *object, IP_Port source, uint8_t *packet, uint32_t length)
125{
126 DHT *dht = object;
127
128 if (LAN_ip(source.ip) == -1)
129 return 1;
130
131 if (length != crypto_box_PUBLICKEYBYTES + 1)
132 return 1;
133
134 DHT_bootstrap(dht, source, packet + 1);
135 return 0;
136}
137
138
139int send_LANdiscovery(uint16_t port, Net_Crypto *c)
140{
141 uint8_t data[crypto_box_PUBLICKEYBYTES + 1];
142 data[0] = NET_PACKET_LAN_DISCOVERY;
143 memcpy(data + 1, c->self_public_key, crypto_box_PUBLICKEYBYTES);
144 IP_Port ip_port = {broadcast_ip(), port};
145 return sendpacket(c->lossless_udp->net->sock, ip_port, data, 1 + crypto_box_PUBLICKEYBYTES);
146}
147
148
149void LANdiscovery_init(DHT *dht)
150{
151 networking_registerhandler(dht->c->lossless_udp->net, NET_PACKET_LAN_DISCOVERY, &handle_LANdiscovery, dht);
152}
diff --git a/toxcore/LAN_discovery.h b/toxcore/LAN_discovery.h
new file mode 100644
index 00000000..5a790331
--- /dev/null
+++ b/toxcore/LAN_discovery.h
@@ -0,0 +1,55 @@
1/* LAN_discovery.h
2 *
3 * LAN discovery implementation.
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
25#ifndef LAN_DISCOVERY_H
26#define LAN_DISCOVERY_H
27
28
29#include "DHT.h"
30
31/* used for get_broadcast() */
32#ifdef __linux
33#include <sys/ioctl.h>
34#include <arpa/inet.h>
35#include <linux/netdevice.h>
36#endif
37
38#ifdef __cplusplus
39extern "C" {
40#endif
41
42/*Send a LAN discovery pcaket to the broadcast address with port port*/
43int send_LANdiscovery(uint16_t port, Net_Crypto *c);
44
45
46/* sets up packet handlers */
47void LANdiscovery_init(DHT *dht);
48
49
50
51#ifdef __cplusplus
52}
53#endif
54
55#endif
diff --git a/toxcore/Lossless_UDP.c b/toxcore/Lossless_UDP.c
new file mode 100644
index 00000000..c30eb903
--- /dev/null
+++ b/toxcore/Lossless_UDP.c
@@ -0,0 +1,842 @@
1/* Lossless_UDP.c
2 *
3 * An implementation of the Lossless_UDP protocol as seen in http://wiki.tox.im/index.php/Lossless_UDP
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/*
25 * TODO: clean this file a bit.
26 * There are a couple of useless variables to get rid of.
27 */
28
29#include "Lossless_UDP.h"
30
31
32/* Functions */
33
34/*
35 * Get connection id from IP_Port
36 * Return -1 if there are no connections like we are looking for
37 * Return id if it found it
38 */
39int getconnection_id(Lossless_UDP *ludp, IP_Port ip_port)
40{
41 uint32_t i;
42
43 for (i = 0; i < ludp->connections_length; ++i) {
44 if (ludp->connections[i].ip_port.ip.i == ip_port.ip.i &&
45 ludp->connections[i].ip_port.port == ip_port.port &&
46 ludp->connections[i].status > 0)
47 return i;
48 }
49
50 return -1;
51}
52
53
54/*
55 * Generate a handshake_id which depends on the ip_port.
56 * This function will always give one unique handshake_id per ip_port.
57 *
58 * TODO: make this better
59 */
60static uint32_t handshake_id(Lossless_UDP *ludp, IP_Port source)
61{
62 uint32_t id = 0, i;
63
64 for (i = 0; i < 6; ++i) {
65 if (ludp->randtable[i][((uint8_t *)&source)[i]] == 0)
66 ludp->randtable[i][((uint8_t *)&source)[i]] = random_int();
67
68 id ^= ludp->randtable[i][((uint8_t *)&source)[i]];
69 }
70
71 if (id == 0) /* id can't be zero */
72 id = 1;
73
74 return id;
75}
76
77/*
78 * Change the hanshake id associated with that ip_port
79 *
80 * TODO: make this better
81 */
82static void change_handshake(Lossless_UDP *ludp, IP_Port source)
83{
84 uint8_t rand = random_int() % 4;
85 ludp->randtable[rand][((uint8_t *)&source)[rand]] = random_int();
86}
87
88/*
89 * Initialize a new connection to ip_port
90 * Returns an integer corresponding to the connection idt
91 * Return -1 if it could not initialize the connectiont
92 * If there already was an existing connection to that ip_port return its number.
93 */
94int new_connection(Lossless_UDP *ludp, IP_Port ip_port)
95{
96 int connect = getconnection_id(ludp, ip_port);
97
98 if (connect != -1)
99 return connect;
100
101 if (ludp->connections_number == ludp->connections_length) {
102 Connection *temp;
103 temp = realloc(ludp->connections, sizeof(Connection) * (ludp->connections_length + 1));
104
105 if (temp == NULL)
106 return -1;
107
108 memset(&temp[ludp->connections_length], 0, sizeof(Connection));
109 ++ludp->connections_length;
110 ludp->connections = temp;
111 }
112
113 uint32_t i;
114
115 for (i = 0; i < ludp->connections_length; ++i) {
116 if (ludp->connections[i].status == 0) {
117 memset(&ludp->connections[i], 0, sizeof(Connection));
118 uint32_t handshake_id1 = handshake_id(ludp, ip_port);
119
120 ludp->connections[i] = (Connection) {
121 .ip_port = ip_port,
122 .status = 1,
123 .inbound = 0,
124 .handshake_id1 = handshake_id1,
125 .sent_packetnum = handshake_id1,
126 .sendbuff_packetnum = handshake_id1,
127 .successful_sent = handshake_id1,
128 .SYNC_rate = SYNC_RATE,
129 .data_rate = DATA_SYNC_RATE,
130 .last_recvSYNC = current_time(),
131 .last_sent = current_time(),
132 .killat = ~0,
133 .send_counter = 0,
134 /* add randomness to timeout to prevent connections getting stuck in a loop. */
135 .timeout = CONNEXION_TIMEOUT + rand() % CONNEXION_TIMEOUT
136 };
137 ++ludp->connections_number;
138
139 return i;
140 }
141 }
142
143 return -1;
144}
145
146/*
147 * Initialize a new inbound connection from ip_port
148 * Returns an integer corresponding to the connection id.
149 * Return -1 if it could not initialize the connection.
150 */
151static int new_inconnection(Lossless_UDP *ludp, IP_Port ip_port)
152{
153 if (getconnection_id(ludp, ip_port) != -1)
154 return -1;
155
156 if (ludp->connections_number == ludp->connections_length) {
157 Connection *temp;
158 temp = realloc(ludp->connections, sizeof(Connection) * (ludp->connections_length + 1));
159
160 if (temp == NULL)
161 return -1;
162
163 memset(&temp[ludp->connections_length], 0, sizeof(Connection));
164 ++ludp->connections_length;
165 ludp->connections = temp;
166 }
167
168 uint32_t i;
169
170 for (i = 0; i < ludp->connections_length; ++i) {
171 if (ludp->connections[i].status == 0) {
172 memset(&ludp->connections[i], 0, sizeof(Connection));
173 uint64_t timeout = CONNEXION_TIMEOUT + rand() % CONNEXION_TIMEOUT;
174
175 ludp->connections[i] = (Connection) {
176 .ip_port = ip_port,
177 .status = 2,
178 .inbound = 2,
179 .SYNC_rate = SYNC_RATE,
180 .data_rate = DATA_SYNC_RATE,
181 .last_recvSYNC = current_time(),
182 .last_sent = current_time(),
183 .send_counter = 127,
184
185 /* add randomness to timeout to prevent connections getting stuck in a loop. */
186 .timeout = timeout,
187
188 /* if this connection isn't handled within the timeout kill it. */
189 .killat = current_time() + 1000000UL * timeout
190 };
191 ++ludp->connections_number;
192 return i;
193 }
194 }
195
196 return -1;
197}
198
199/*
200 * Returns an integer corresponding to the next connection in our incoming connection list.
201 * Return -1 if there are no new incoming connections in the list.
202 */
203int incoming_connection(Lossless_UDP *ludp)
204{
205 uint32_t i;
206
207 for (i = 0; i < ludp->connections_length; ++i) {
208 if (ludp->connections[i].inbound == 2) {
209 ludp->connections[i].inbound = 1;
210 return i;
211 }
212 }
213
214 return -1;
215}
216
217/* Try to free some memory from the connections array. */
218static void free_connections(Lossless_UDP *ludp)
219{
220 uint32_t i;
221
222 for (i = ludp->connections_length; i != 0; --i)
223 if (ludp->connections[i - 1].status != 0)
224 break;
225
226 if (ludp->connections_length == i)
227 return;
228
229 if (i == 0) {
230 free(ludp->connections);
231 ludp->connections = NULL;
232 ludp->connections_length = i;
233 return;
234 }
235
236 Connection *temp;
237 temp = realloc(ludp->connections, sizeof(Connection) * i);
238
239 if (temp == NULL && i != 0)
240 return;
241
242 ludp->connections = temp;
243 ludp->connections_length = i;
244}
245
246/*
247 * Return -1 if it could not kill the connection.
248 * Return 0 if killed successfully
249 */
250int kill_connection(Lossless_UDP *ludp, int connection_id)
251{
252 if (connection_id >= 0 && connection_id < ludp->connections_length) {
253 if (ludp->connections[connection_id].status > 0) {
254 ludp->connections[connection_id].status = 0;
255 change_handshake(ludp, ludp->connections[connection_id].ip_port);
256 --ludp->connections_number;
257 free_connections(ludp);
258 return 0;
259 }
260 }
261
262 return -1;
263}
264
265/*
266 * Kill connection in seconds.
267 * Return -1 if it can not kill the connection.
268 * Return 0 if it will kill it.
269 */
270int kill_connection_in(Lossless_UDP *ludp, int connection_id, uint32_t seconds)
271{
272 if (connection_id >= 0 && connection_id < ludp->connections_length) {
273 if (ludp->connections[connection_id].status > 0) {
274 ludp->connections[connection_id].killat = current_time() + 1000000UL * seconds;
275 return 0;
276 }
277 }
278
279 return -1;
280}
281
282/*
283 * Check if connection is connected:
284 * Return 0 no.
285 * Return 1 if attempting handshake.
286 * Return 2 if handshake is done.
287 * Return 3 if fully connected.
288 * Return 4 if timed out and waiting to be killed.
289 */
290int is_connected(Lossless_UDP *ludp, int connection_id)
291{
292 if (connection_id >= 0 && connection_id < ludp->connections_length)
293 return ludp->connections[connection_id].status;
294
295 return 0;
296}
297
298/* returns the ip_port of the corresponding connection. */
299IP_Port connection_ip(Lossless_UDP *ludp, int connection_id)
300{
301 if (connection_id >= 0 && connection_id < ludp->connections_length)
302 return ludp->connections[connection_id].ip_port;
303
304 IP_Port zero = {{{0}}, 0};
305 return zero;
306}
307
308/* returns the number of packets in the queue waiting to be successfully sent. */
309uint32_t sendqueue(Lossless_UDP *ludp, int connection_id)
310{
311 if (connection_id < 0 || connection_id >= ludp->connections_length)
312 return 0;
313
314 return ludp->connections[connection_id].sendbuff_packetnum - ludp->connections[connection_id].successful_sent;
315}
316
317/* returns the number of packets in the queue waiting to be successfully read with read_packet(...) */
318uint32_t recvqueue(Lossless_UDP *ludp, int connection_id)
319{
320 if (connection_id < 0 || connection_id >= ludp->connections_length)
321 return 0;
322
323 return ludp->connections[connection_id].recv_packetnum - ludp->connections[connection_id].successful_read;
324}
325
326/* returns the id of the next packet in the queue
327 return -1 if no packet in queue */
328char id_packet(Lossless_UDP *ludp, int connection_id)
329{
330 if (connection_id < 0 || connection_id >= ludp->connections_length)
331 return -1;
332
333 if (recvqueue(ludp, connection_id) != 0 && ludp->connections[connection_id].status != 0)
334 return ludp->connections[connection_id].recvbuffer[ludp->connections[connection_id].successful_read %
335 MAX_QUEUE_NUM].data[0];
336
337 return -1;
338}
339
340/* return 0 if there is no received data in the buffer.
341 return length of received packet if successful */
342int read_packet(Lossless_UDP *ludp, int connection_id, uint8_t *data)
343{
344 if (recvqueue(ludp, connection_id) != 0) {
345 uint16_t index = ludp->connections[connection_id].successful_read % MAX_QUEUE_NUM;
346 uint16_t size = ludp->connections[connection_id].recvbuffer[index].size;
347 memcpy(data, ludp->connections[connection_id].recvbuffer[index].data, size);
348 ++ludp->connections[connection_id].successful_read;
349 ludp->connections[connection_id].recvbuffer[index].size = 0;
350 return size;
351 }
352
353 return 0;
354}
355
356/*
357 * Return 0 if data could not be put in packet queue
358 * Return 1 if data was put into the queue
359 */
360int write_packet(Lossless_UDP *ludp, int connection_id, uint8_t *data, uint32_t length)
361{
362 if (length > MAX_DATA_SIZE || length == 0)
363 return 0;
364
365 if (sendqueue(ludp, connection_id) < BUFFER_PACKET_NUM) {
366 uint32_t index = ludp->connections[connection_id].sendbuff_packetnum % MAX_QUEUE_NUM;
367 memcpy(ludp->connections[connection_id].sendbuffer[index].data, data, length);
368 ludp->connections[connection_id].sendbuffer[index].size = length;
369 ludp->connections[connection_id].sendbuff_packetnum++;
370 return 1;
371 }
372
373 return 0;
374}
375
376/* put the packet numbers the we are missing in requested and return the number */
377uint32_t missing_packets(Lossless_UDP *ludp, int connection_id, uint32_t *requested)
378{
379 uint32_t number = 0;
380 uint32_t i;
381 uint32_t temp;
382
383 /* don't request packets if the buffer is full. */
384 if (recvqueue(ludp, connection_id) >= (BUFFER_PACKET_NUM - 1))
385 return 0;
386
387 for (i = ludp->connections[connection_id].recv_packetnum; i != ludp->connections[connection_id].osent_packetnum; i++) {
388 if (ludp->connections[connection_id].recvbuffer[i % MAX_QUEUE_NUM].size == 0) {
389 temp = htonl(i);
390 memcpy(requested + number, &temp, 4);
391 ++number;
392 }
393 }
394
395 if (number == 0)
396 ludp->connections[connection_id].recv_packetnum = ludp->connections[connection_id].osent_packetnum;
397
398 return number;
399}
400
401/*
402 * BEGIN Packet sending functions
403 * One per packet type.
404 * see http://wiki.tox.im/index.php/Lossless_UDP for more information.
405 */
406
407static int send_handshake(Lossless_UDP *ludp, IP_Port ip_port, uint32_t handshake_id1, uint32_t handshake_id2)
408{
409 uint8_t packet[1 + 4 + 4];
410 uint32_t temp;
411
412 packet[0] = NET_PACKET_HANDSHAKE;
413 temp = htonl(handshake_id1);
414 memcpy(packet + 1, &temp, 4);
415 temp = htonl(handshake_id2);
416 memcpy(packet + 5, &temp, 4);
417
418 return sendpacket(ludp->net->sock, ip_port, packet, sizeof(packet));
419}
420
421static int send_SYNC(Lossless_UDP *ludp, uint32_t connection_id)
422{
423 uint8_t packet[(BUFFER_PACKET_NUM * 4 + 4 + 4 + 2)];
424 uint16_t index = 0;
425
426 IP_Port ip_port = ludp->connections[connection_id].ip_port;
427 uint8_t counter = ludp->connections[connection_id].send_counter;
428 uint32_t recv_packetnum = htonl(ludp->connections[connection_id].recv_packetnum);
429 uint32_t sent_packetnum = htonl(ludp->connections[connection_id].sent_packetnum);
430
431 uint32_t requested[BUFFER_PACKET_NUM];
432 uint32_t number = missing_packets(ludp, connection_id, requested);
433
434 packet[0] = NET_PACKET_SYNC;
435 index += 1;
436 memcpy(packet + index, &counter, 1);
437 index += 1;
438 memcpy(packet + index, &recv_packetnum, 4);
439 index += 4;
440 memcpy(packet + index, &sent_packetnum, 4);
441 index += 4;
442 memcpy(packet + index, requested, 4 * number);
443
444 return sendpacket(ludp->net->sock, ip_port, packet, (number * 4 + 4 + 4 + 2));
445
446}
447
448static int send_data_packet(Lossless_UDP *ludp, uint32_t connection_id, uint32_t packet_num)
449{
450 uint32_t index = packet_num % MAX_QUEUE_NUM;
451 uint32_t temp;
452 uint8_t packet[1 + 4 + MAX_DATA_SIZE];
453 packet[0] = NET_PACKET_DATA;
454 temp = htonl(packet_num);
455 memcpy(packet + 1, &temp, 4);
456 memcpy(packet + 5, ludp->connections[connection_id].sendbuffer[index].data,
457 ludp->connections[connection_id].sendbuffer[index].size);
458 return sendpacket(ludp->net->sock, ludp->connections[connection_id].ip_port, packet,
459 1 + 4 + ludp->connections[connection_id].sendbuffer[index].size);
460}
461
462/* sends 1 data packet */
463static int send_DATA(Lossless_UDP *ludp, uint32_t connection_id)
464{
465 int ret;
466 uint32_t buffer[BUFFER_PACKET_NUM];
467
468 if (ludp->connections[connection_id].num_req_paquets > 0) {
469 ret = send_data_packet(ludp, connection_id, ludp->connections[connection_id].req_packets[0]);
470 ludp->connections[connection_id].num_req_paquets--;
471 memcpy(buffer, ludp->connections[connection_id].req_packets + 1, ludp->connections[connection_id].num_req_paquets * 4);
472 memcpy(ludp->connections[connection_id].req_packets, buffer, ludp->connections[connection_id].num_req_paquets * 4);
473 return ret;
474 }
475
476 if (ludp->connections[connection_id].sendbuff_packetnum != ludp->connections[connection_id].sent_packetnum) {
477 ret = send_data_packet(ludp, connection_id, ludp->connections[connection_id].sent_packetnum);
478 ludp->connections[connection_id].sent_packetnum++;
479 return ret;
480 }
481
482 return 0;
483}
484
485/*
486 * END of packet sending functions
487 *
488 *
489 * BEGIN Packet handling functions
490 * One to handle each type of packets we receive
491 */
492
493
494/* Return 0 if handled correctly, 1 if packet is bad. */
495static int handle_handshake(void *object, IP_Port source, uint8_t *packet, uint32_t length)
496{
497 Lossless_UDP *ludp = object;
498
499 if (length != (1 + 4 + 4))
500 return 1;
501
502 uint32_t temp;
503 uint32_t handshake_id1, handshake_id2;
504
505 int connection = getconnection_id(ludp, source);
506 memcpy(&temp, packet + 1, 4);
507 handshake_id1 = ntohl(temp);
508 memcpy(&temp, packet + 5, 4);
509 handshake_id2 = ntohl(temp);
510
511 if (handshake_id2 == 0 && is_connected(ludp, connection) < 3) {
512 send_handshake(ludp, source, handshake_id(ludp, source), handshake_id1);
513 return 0;
514 }
515
516 if (is_connected(ludp, connection) != 1)
517 return 1;
518
519 /* if handshake_id2 is what we sent previously as handshake_id1 */
520 if (handshake_id2 == ludp->connections[connection].handshake_id1) {
521 ludp->connections[connection].status = 2;
522 /* NOTE: is this necessary?
523 ludp->connections[connection].handshake_id2 = handshake_id1; */
524 ludp->connections[connection].orecv_packetnum = handshake_id2;
525 ludp->connections[connection].osent_packetnum = handshake_id1;
526 ludp->connections[connection].recv_packetnum = handshake_id1;
527 ludp->connections[connection].successful_read = handshake_id1;
528 }
529
530 return 0;
531}
532
533/* returns 1 if sync packet is valid 0 if not. */
534static int SYNC_valid(uint32_t length)
535{
536 if (length < 4 + 4 + 2)
537 return 0;
538
539 if (length > (BUFFER_PACKET_NUM * 4 + 4 + 4 + 2) ||
540 ((length - 4 - 4 - 2) % 4) != 0)
541 return 0;
542
543 return 1;
544}
545
546/* case 1 in handle_SYNC: */
547static int handle_SYNC1(Lossless_UDP *ludp, IP_Port source, uint32_t recv_packetnum, uint32_t sent_packetnum)
548{
549 if (handshake_id(ludp, source) == recv_packetnum) {
550 int x = new_inconnection(ludp, source);
551
552 if (x != -1) {
553 ludp->connections[x].orecv_packetnum = recv_packetnum;
554 ludp->connections[x].sent_packetnum = recv_packetnum;
555 ludp->connections[x].sendbuff_packetnum = recv_packetnum;
556 ludp->connections[x].successful_sent = recv_packetnum;
557 ludp->connections[x].osent_packetnum = sent_packetnum;
558 ludp->connections[x].recv_packetnum = sent_packetnum;
559 ludp->connections[x].successful_read = sent_packetnum;
560
561 return x;
562 }
563 }
564
565 return -1;
566}
567
568/* case 2 in handle_SYNC: */
569static int handle_SYNC2(Lossless_UDP *ludp, int connection_id, uint8_t counter, uint32_t recv_packetnum,
570 uint32_t sent_packetnum)
571{
572 if (recv_packetnum == ludp->connections[connection_id].orecv_packetnum) {
573 /* && sent_packetnum == ludp->connections[connection_id].osent_packetnum) */
574 ludp->connections[connection_id].status = 3;
575 ludp->connections[connection_id].recv_counter = counter;
576 ++ludp->connections[connection_id].send_counter;
577 send_SYNC(ludp, connection_id);
578 return 0;
579 }
580
581 return 1;
582}
583/* case 3 in handle_SYNC: */
584static int handle_SYNC3(Lossless_UDP *ludp, int connection_id, uint8_t counter, uint32_t recv_packetnum,
585 uint32_t sent_packetnum,
586 uint32_t *req_packets,
587 uint16_t number)
588{
589 uint8_t comp_counter = (counter - ludp->connections[connection_id].recv_counter );
590 uint32_t i, temp;
591 /* uint32_t comp_1 = (recv_packetnum - ludp->connections[connection_id].successful_sent);
592 uint32_t comp_2 = (sent_packetnum - ludp->connections[connection_id].successful_read); */
593 uint32_t comp_1 = (recv_packetnum - ludp->connections[connection_id].orecv_packetnum);
594 uint32_t comp_2 = (sent_packetnum - ludp->connections[connection_id].osent_packetnum);
595
596 /* packet valid */
597 if (comp_1 <= BUFFER_PACKET_NUM &&
598 comp_2 <= BUFFER_PACKET_NUM &&
599 comp_counter < 10 && comp_counter != 0) {
600
601 ludp->connections[connection_id].orecv_packetnum = recv_packetnum;
602 ludp->connections[connection_id].osent_packetnum = sent_packetnum;
603 ludp->connections[connection_id].successful_sent = recv_packetnum;
604 ludp->connections[connection_id].last_recvSYNC = current_time();
605 ludp->connections[connection_id].recv_counter = counter;
606
607 ++ludp->connections[connection_id].send_counter;
608
609 for (i = 0; i < number; ++i) {
610 temp = ntohl(req_packets[i]);
611 memcpy(ludp->connections[connection_id].req_packets + i, &temp, 4 * number);
612 }
613
614 ludp->connections[connection_id].num_req_paquets = number;
615 return 0;
616 }
617
618 return 1;
619}
620
621static int handle_SYNC(void *object, IP_Port source, uint8_t *packet, uint32_t length)
622{
623 Lossless_UDP *ludp = object;
624
625 if (!SYNC_valid(length))
626 return 1;
627
628 int connection = getconnection_id(ludp, source);
629 uint8_t counter;
630 uint32_t temp;
631 uint32_t recv_packetnum, sent_packetnum;
632 uint32_t req_packets[BUFFER_PACKET_NUM];
633 uint16_t number = (length - 4 - 4 - 2) / 4;
634
635 memcpy(&counter, packet + 1, 1);
636 memcpy(&temp, packet + 2, 4);
637 recv_packetnum = ntohl(temp);
638 memcpy(&temp, packet + 6, 4);
639 sent_packetnum = ntohl(temp);
640
641 if (number != 0)
642 memcpy(req_packets, packet + 10, 4 * number);
643
644 if (connection == -1)
645 return handle_SYNC1(ludp, source, recv_packetnum, sent_packetnum);
646
647 if (ludp->connections[connection].status == 2)
648 return handle_SYNC2(ludp, connection, counter,
649 recv_packetnum, sent_packetnum);
650
651 if (ludp->connections[connection].status == 3)
652 return handle_SYNC3(ludp, connection, counter, recv_packetnum,
653 sent_packetnum, req_packets, number);
654
655 return 0;
656}
657
658/*
659 * Add a packet to the received buffer and set the recv_packetnum of the
660 * connection to its proper value. Return 1 if data was too big, 0 if not.
661 */
662static int add_recv(Lossless_UDP *ludp, int connection_id, uint32_t data_num, uint8_t *data, uint16_t size)
663{
664 if (size > MAX_DATA_SIZE)
665 return 1;
666
667 uint32_t i;
668 uint32_t maxnum = ludp->connections[connection_id].successful_read + BUFFER_PACKET_NUM;
669 uint32_t sent_packet = data_num - ludp->connections[connection_id].osent_packetnum;
670
671 for (i = ludp->connections[connection_id].recv_packetnum; i != maxnum; ++i) {
672 if (i == data_num) {
673 memcpy(ludp->connections[connection_id].recvbuffer[i % MAX_QUEUE_NUM].data, data, size);
674
675 ludp->connections[connection_id].recvbuffer[i % MAX_QUEUE_NUM].size = size;
676 ludp->connections[connection_id].last_recvdata = current_time();
677
678 if (sent_packet < BUFFER_PACKET_NUM) {
679 ludp->connections[connection_id].osent_packetnum = data_num;
680 }
681
682 break;
683 }
684 }
685
686 for (i = ludp->connections[connection_id].recv_packetnum; i != maxnum; ++i) {
687 if (ludp->connections[connection_id].recvbuffer[i % MAX_QUEUE_NUM].size != 0)
688 ludp->connections[connection_id].recv_packetnum = i;
689 else
690 break;
691 }
692
693 return 0;
694}
695
696static int handle_data(void *object, IP_Port source, uint8_t *packet, uint32_t length)
697{
698 Lossless_UDP *ludp = object;
699 int connection = getconnection_id(ludp, source);
700
701 if (connection == -1)
702 return 1;
703
704 /* Drop the data packet if connection is not connected. */
705 if (ludp->connections[connection].status != 3)
706 return 1;
707
708 if (length > 1 + 4 + MAX_DATA_SIZE || length < 1 + 4 + 1)
709 return 1;
710
711 uint32_t temp;
712 uint32_t number;
713 uint16_t size = length - 1 - 4;
714
715 memcpy(&temp, packet + 1, 4);
716 number = ntohl(temp);
717
718 return add_recv(ludp, connection, number, packet + 5, size);
719}
720
721/*
722 * END of packet handling functions
723 */
724
725Lossless_UDP *new_lossless_udp(Networking_Core *net)
726{
727 if (net == NULL)
728 return NULL;
729
730 Lossless_UDP *temp = calloc(1, sizeof(Lossless_UDP));
731
732 if (temp == NULL)
733 return NULL;
734
735 temp->net = net;
736 networking_registerhandler(net, NET_PACKET_HANDSHAKE, &handle_handshake, temp);
737 networking_registerhandler(net, NET_PACKET_SYNC, &handle_SYNC, temp);
738 networking_registerhandler(net, NET_PACKET_DATA, &handle_data, temp);
739 return temp;
740}
741
742/*
743 * Send handshake requests
744 * handshake packets are sent at the same rate as SYNC packets
745 */
746static void do_new(Lossless_UDP *ludp)
747{
748 uint32_t i;
749 uint64_t temp_time = current_time();
750
751 for (i = 0; i < ludp->connections_length; ++i) {
752 if (ludp->connections[i].status == 1)
753 if ((ludp->connections[i].last_sent + (1000000UL / ludp->connections[i].SYNC_rate)) <= temp_time) {
754 send_handshake(ludp, ludp->connections[i].ip_port, ludp->connections[i].handshake_id1, 0);
755 ludp->connections[i].last_sent = temp_time;
756 }
757
758 /* kill all timed out connections */
759 if (ludp->connections[i].status > 0 &&
760 (ludp->connections[i].last_recvSYNC + ludp->connections[i].timeout * 1000000UL) < temp_time &&
761 ludp->connections[i].status != 4) {
762 ludp->connections[i].status = 4;
763 /* kill_connection(i); */
764 }
765
766 if (ludp->connections[i].status > 0 && ludp->connections[i].killat < temp_time)
767 kill_connection(ludp, i);
768 }
769}
770
771static void do_SYNC(Lossless_UDP *ludp)
772{
773 uint32_t i;
774 uint64_t temp_time = current_time();
775
776 for (i = 0; i < ludp->connections_length; ++i) {
777 if (ludp->connections[i].status == 2 || ludp->connections[i].status == 3)
778 if ((ludp->connections[i].last_SYNC + (1000000UL / ludp->connections[i].SYNC_rate)) <= temp_time) {
779 send_SYNC(ludp, i);
780 ludp->connections[i].last_SYNC = temp_time;
781 }
782 }
783}
784
785static void do_data(Lossless_UDP *ludp)
786{
787 uint32_t i;
788 uint64_t j;
789 uint64_t temp_time = current_time();
790
791 for (i = 0; i < ludp->connections_length; ++i)
792 if (ludp->connections[i].status == 3 && sendqueue(ludp, i) != 0)
793 if ((ludp->connections[i].last_sent + (1000000UL / ludp->connections[i].data_rate)) <= temp_time) {
794 for (j = ludp->connections[i].last_sent; j < temp_time; j += (1000000UL / ludp->connections[i].data_rate))
795 send_DATA(ludp, i);
796
797 ludp->connections[i].last_sent = temp_time;
798 }
799}
800
801#define MAX_SYNC_RATE 10
802
803/*
804 * Automatically adjusts send rates of packets for optimal transmission.
805 *
806 * TODO: flow control.
807 */
808static void adjust_rates(Lossless_UDP *ludp)
809{
810 uint32_t i;
811 uint64_t temp_time = current_time();
812
813 for (i = 0; i < ludp->connections_length; ++i) {
814 if (ludp->connections[i].status == 1 || ludp->connections[i].status == 2)
815 ludp->connections[i].SYNC_rate = MAX_SYNC_RATE;
816
817 if (ludp->connections[i].status == 3) {
818 if (sendqueue(ludp, i) != 0) {
819 ludp->connections[i].data_rate = (BUFFER_PACKET_NUM - ludp->connections[i].num_req_paquets) * MAX_SYNC_RATE;
820 ludp->connections[i].SYNC_rate = MAX_SYNC_RATE;
821 } else if (ludp->connections[i].last_recvdata + 1000000UL > temp_time)
822 ludp->connections[i].SYNC_rate = MAX_SYNC_RATE;
823 else
824 ludp->connections[i].SYNC_rate = SYNC_RATE;
825 }
826 }
827}
828
829/* Call this function a couple times per second It's the main loop. */
830void do_lossless_udp(Lossless_UDP *ludp)
831{
832 do_new(ludp);
833 do_SYNC(ludp);
834 do_data(ludp);
835 adjust_rates(ludp);
836}
837
838void kill_lossless_udp(Lossless_UDP *ludp)
839{
840 free(ludp->connections);
841 free(ludp);
842}
diff --git a/toxcore/Lossless_UDP.h b/toxcore/Lossless_UDP.h
new file mode 100644
index 00000000..176e86ce
--- /dev/null
+++ b/toxcore/Lossless_UDP.h
@@ -0,0 +1,222 @@
1/* Lossless_UDP.h
2 *
3 * An implementation of the Lossless_UDP protocol as seen in http://wiki.tox.im/index.php/Lossless_UDP
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#ifndef LOSSLESS_UDP_H
25#define LOSSLESS_UDP_H
26
27#include "network.h"
28
29#ifdef __cplusplus
30extern "C" {
31#endif
32
33/* maximum length of the data in the data packets */
34#define MAX_DATA_SIZE 1024
35
36/* maximum data packets in sent and receive queues. */
37#define MAX_QUEUE_NUM 16
38
39/* maximum number of data packets in the buffer */
40#define BUFFER_PACKET_NUM (16-1)
41
42/* timeout per connection is randomly set between CONNEXION_TIMEOUT and 2*CONNEXION_TIMEOUT */
43#define CONNEXION_TIMEOUT 5
44
45/* initial amount of sync/hanshake packets to send per second. */
46#define SYNC_RATE 2
47
48/* initial send rate of data. */
49#define DATA_SYNC_RATE 30
50
51typedef struct {
52 uint8_t data[MAX_DATA_SIZE];
53 uint16_t size;
54} Data;
55
56typedef struct {
57 IP_Port ip_port;
58
59 /*
60 * 0 if connection is dead, 1 if attempting handshake,
61 * 2 if handshake is done (we start sending SYNC packets)
62 * 3 if we are sending SYNC packets and can send data
63 * 4 if the connection has timed out.
64 */
65 uint8_t status;
66
67 /*
68 * 1 or 2 if connection was initiated by someone else, 0 if not.
69 * 2 if incoming_connection() has not returned it yet, 1 if it has.
70 */
71 uint8_t inbound;
72
73 uint16_t SYNC_rate; /* current SYNC packet send rate packets per second. */
74 uint16_t data_rate; /* current data packet send rate packets per second. */
75
76 uint64_t last_SYNC; /* time our last SYNC packet was sent. */
77 uint64_t last_sent; /* time our last data or handshake packet was sent. */
78 uint64_t last_recvSYNC; /* time we last received a SYNC packet from the other */
79 uint64_t last_recvdata; /* time we last received a DATA packet from the other */
80 uint64_t killat; /* time to kill the connection */
81
82 Data sendbuffer[MAX_QUEUE_NUM]; /* packet send buffer. */
83 Data recvbuffer[MAX_QUEUE_NUM]; /* packet receive buffer. */
84
85 uint32_t handshake_id1;
86 uint32_t handshake_id2;
87
88 /* number of data packets received (also used as handshake_id1) */
89 uint32_t recv_packetnum;
90
91 /* number of packets received by the other peer */
92 uint32_t orecv_packetnum;
93
94 /* number of data packets sent */
95 uint32_t sent_packetnum;
96
97 /* number of packets sent by the other peer. */
98 uint32_t osent_packetnum;
99
100 /* number of latest packet written onto the sendbuffer */
101 uint32_t sendbuff_packetnum;
102
103 /* we know all packets before that number were successfully sent */
104 uint32_t successful_sent;
105
106 /* packet number of last packet read with the read_packet function */
107 uint32_t successful_read;
108
109 /* list of currently requested packet numbers(by the other person) */
110 uint32_t req_packets[BUFFER_PACKET_NUM];
111
112 /* total number of currently requested packets(by the other person) */
113 uint16_t num_req_paquets;
114
115 uint8_t recv_counter;
116 uint8_t send_counter;
117 uint8_t timeout; /* connection timeout in seconds. */
118} Connection;
119
120typedef struct {
121 Networking_Core *net;
122 Connection *connections;
123
124 uint32_t connections_length; /* Length of connections array */
125 uint32_t connections_number; /* Number of connections in connections array */
126
127 /* table of random numbers used in handshake_id. */
128 uint32_t randtable[6][256];
129
130} Lossless_UDP;
131
132/*
133 * Initialize a new connection to ip_port
134 * Returns an integer corresponding to the connection id.
135 * Return -1 if it could not initialize the connection.
136 * Return number if there already was an existing connection to that ip_port.
137 */
138int new_connection(Lossless_UDP *ludp, IP_Port ip_port);
139
140/*
141 * Get connection id from IP_Port.
142 * Return -1 if there are no connections like we are looking for.
143 * Return id if it found it .
144 */
145int getconnection_id(Lossless_UDP *ludp, IP_Port ip_port);
146
147/*
148 * Returns an int corresponding to the next connection in our imcoming connection list
149 * Return -1 if there are no new incoming connections in the list.
150 */
151int incoming_connection(Lossless_UDP *ludp);
152
153/*
154 * Return -1 if it could not kill the connection.
155 * Return 0 if killed successfully
156 */
157int kill_connection(Lossless_UDP *ludp, int connection_id);
158
159/*
160 * Kill connection in seconds seconds.
161 * Return -1 if it can not kill the connection.
162 * Return 0 if it will kill it
163 */
164int kill_connection_in(Lossless_UDP *ludp, int connection_id, uint32_t seconds);
165
166/*
167 * Returns the ip_port of the corresponding connection.
168 * Return 0 if there is no such connection.
169 */
170IP_Port connection_ip(Lossless_UDP *ludp, int connection_id);
171
172/*
173 * Returns the id of the next packet in the queue
174 * Return -1 if no packet in queue
175 */
176char id_packet(Lossless_UDP *ludp, int connection_id);
177
178/*
179 * Return 0 if there is no received data in the buffer.
180 * Return length of received packet if successful
181 */
182int read_packet(Lossless_UDP *ludp, int connection_id, uint8_t *data);
183
184/*
185 * Return 0 if data could not be put in packet queue
186 * Return 1 if data was put into the queue
187 */
188int write_packet(Lossless_UDP *ludp, int connection_id, uint8_t *data, uint32_t length);
189
190/* Returns the number of packets in the queue waiting to be successfully sent. */
191uint32_t sendqueue(Lossless_UDP *ludp, int connection_id);
192
193/*
194 * returns the number of packets in the queue waiting to be successfully
195 * read with read_packet(...)
196 */
197uint32_t recvqueue(Lossless_UDP *ludp, int connection_id);
198
199/* Check if connection is connected:
200 * Return 0 no.
201 * Return 1 if attempting handshake.
202 * Return 2 if handshake is done.
203 * Return 3 if fully connected.
204 * Return 4 if timed out and wating to be killed.
205 */
206int is_connected(Lossless_UDP *ludp, int connection_id);
207
208/* Call this function a couple times per second It's the main loop. */
209void do_lossless_udp(Lossless_UDP *ludp);
210
211/*
212 * This function sets up LosslessUDP packet handling.
213 */
214Lossless_UDP *new_lossless_udp(Networking_Core *net);
215
216void kill_lossless_udp(Lossless_UDP *ludp);
217
218#ifdef __cplusplus
219}
220#endif
221
222#endif
diff --git a/toxcore/Messenger.c b/toxcore/Messenger.c
new file mode 100644
index 00000000..c5dd8a40
--- /dev/null
+++ b/toxcore/Messenger.c
@@ -0,0 +1,1020 @@
1/* Messenger.c
2 *
3 * An implementation of a simple text chat only messenger on the tox network core.
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 "Messenger.h"
25
26#define MIN(a,b) (((a)<(b))?(a):(b))
27
28static void set_friend_status(Messenger *m, int friendnumber, uint8_t status);
29static int write_cryptpacket_id(Messenger *m, int friendnumber, uint8_t packet_id, uint8_t *data, uint32_t length);
30
31/* 1 if we are online
32 0 if we are offline
33 static uint8_t online; */
34
35/* set the size of the friend list to numfriends
36 return -1 if realloc fails */
37int realloc_friendlist(Messenger *m, uint32_t num)
38{
39 if (num == 0) {
40 free(m->friendlist);
41 m->friendlist = NULL;
42 return 0;
43 }
44
45 Friend *newfriendlist = realloc(m->friendlist, num * sizeof(Friend));
46
47 if (newfriendlist == NULL)
48 return -1;
49
50 m->friendlist = newfriendlist;
51 return 0;
52}
53
54/* return the friend id associated to that public key.
55 return -1 if no such friend */
56int getfriend_id(Messenger *m, uint8_t *client_id)
57{
58 uint32_t i;
59
60 for (i = 0; i < m->numfriends; ++i) {
61 if (m->friendlist[i].status > 0)
62 if (memcmp(client_id, m->friendlist[i].client_id, crypto_box_PUBLICKEYBYTES) == 0)
63 return i;
64 }
65
66 return -1;
67}
68
69/* copies the public key associated to that friend id into client_id buffer.
70 make sure that client_id is of size CLIENT_ID_SIZE.
71 return 0 if success
72 return -1 if failure. */
73int getclient_id(Messenger *m, int friend_id, uint8_t *client_id)
74{
75 if (friend_id >= m->numfriends || friend_id < 0)
76 return -1;
77
78 if (m->friendlist[friend_id].status > 0) {
79 memcpy(client_id, m->friendlist[friend_id].client_id, CLIENT_ID_SIZE);
80 return 0;
81 }
82
83 return -1;
84}
85/*
86 * returns a uint16_t that represents the checksum of address of length len
87 *
88 * TODO: Another checksum algorithm might be better.
89 */
90static uint16_t address_checksum(uint8_t *address, uint32_t len)
91{
92 uint8_t checksum[2] = {0};
93 uint16_t check;
94 uint32_t i;
95
96 for (i = 0; i < len; ++i)
97 checksum[i % 2] ^= address[i];
98
99 memcpy(&check, checksum, sizeof(check));
100 return check;
101}
102
103/*
104 * returns a FRIEND_ADDRESS_SIZE byte address to give to others.
105 * format: [client_id (32 bytes)][nospam number (4 bytes)][checksum (2 bytes)]
106 *
107 */
108void getaddress(Messenger *m, uint8_t *address)
109{
110 memcpy(address, m->net_crypto->self_public_key, crypto_box_PUBLICKEYBYTES);
111 uint32_t nospam = get_nospam(&(m->fr));
112 memcpy(address + crypto_box_PUBLICKEYBYTES, &nospam, sizeof(nospam));
113 uint16_t checksum = address_checksum(address, FRIEND_ADDRESS_SIZE - sizeof(checksum));
114 memcpy(address + crypto_box_PUBLICKEYBYTES + sizeof(nospam), &checksum, sizeof(checksum));
115}
116
117/*
118 * add a friend
119 * set the data that will be sent along with friend request
120 * address is the address of the friend (returned by getaddress of the friend you wish to add) it must be FRIEND_ADDRESS_SIZE bytes. TODO: add checksum.
121 * data is the data and length is the length
122 * returns the friend number if success
123 * return FA_TOOLONG if message length is too long
124 * return FAERR_NOMESSAGE if no message (message length must be >= 1 byte)
125 * return FAERR_OWNKEY if user's own key
126 * return FAERR_ALREADYSENT if friend request already sent or already a friend
127 * return FAERR_UNKNOWN for unknown error
128 * return FAERR_BADCHECKSUM if bad checksum in address
129 * return FAERR_SETNEWNOSPAM if the friend was already there but the nospam was different
130 * (the nospam for that friend was set to the new one)
131 * return FAERR_NOMEM if increasing the friend list size fails
132 */
133int m_addfriend(Messenger *m, uint8_t *address, uint8_t *data, uint16_t length)
134{
135 if (length >= (MAX_DATA_SIZE - crypto_box_PUBLICKEYBYTES
136 - crypto_box_NONCEBYTES - crypto_box_BOXZEROBYTES
137 + crypto_box_ZEROBYTES))
138 return FAERR_TOOLONG;
139
140 uint8_t client_id[crypto_box_PUBLICKEYBYTES];
141 memcpy(client_id, address, crypto_box_PUBLICKEYBYTES);
142 uint16_t check, checksum = address_checksum(address, FRIEND_ADDRESS_SIZE - sizeof(checksum));
143 memcpy(&check, address + crypto_box_PUBLICKEYBYTES + sizeof(uint32_t), sizeof(check));
144
145 if (check != checksum)
146 return FAERR_BADCHECKSUM;
147
148 if (length < 1)
149 return FAERR_NOMESSAGE;
150
151 if (memcmp(client_id, m->net_crypto->self_public_key, crypto_box_PUBLICKEYBYTES) == 0)
152 return FAERR_OWNKEY;
153
154 int friend_id = getfriend_id(m, client_id);
155
156 if (friend_id != -1) {
157 uint32_t nospam;
158 memcpy(&nospam, address + crypto_box_PUBLICKEYBYTES, sizeof(nospam));
159
160 if (m->friendlist[friend_id].friendrequest_nospam == nospam)
161 return FAERR_ALREADYSENT;
162
163 m->friendlist[friend_id].friendrequest_nospam = nospam;
164 return FAERR_SETNEWNOSPAM;
165 }
166
167 /* resize the friend list if necessary */
168 if (realloc_friendlist(m, m->numfriends + 1) != 0)
169 return FAERR_NOMEM;
170
171 memset(&(m->friendlist[m->numfriends]), 0, sizeof(Friend));
172
173 uint32_t i;
174
175 for (i = 0; i <= m->numfriends; ++i) {
176 if (m->friendlist[i].status == NOFRIEND) {
177 DHT_addfriend(m->dht, client_id);
178 m->friendlist[i].status = FRIEND_ADDED;
179 m->friendlist[i].crypt_connection_id = -1;
180 m->friendlist[i].friendrequest_lastsent = 0;
181 m->friendlist[i].friendrequest_timeout = FRIENDREQUEST_TIMEOUT;
182 memcpy(m->friendlist[i].client_id, client_id, CLIENT_ID_SIZE);
183 m->friendlist[i].statusmessage = calloc(1, 1);
184 m->friendlist[i].statusmessage_length = 1;
185 m->friendlist[i].userstatus = USERSTATUS_NONE;
186 memcpy(m->friendlist[i].info, data, length);
187 m->friendlist[i].info_size = length;
188 m->friendlist[i].message_id = 0;
189 m->friendlist[i].receives_read_receipts = 1; /* default: YES */
190 memcpy(&(m->friendlist[i].friendrequest_nospam), address + crypto_box_PUBLICKEYBYTES, sizeof(uint32_t));
191
192 if (m->numfriends == i)
193 ++ m->numfriends;
194
195 return i;
196 }
197 }
198
199 return FAERR_UNKNOWN;
200}
201
202int m_addfriend_norequest(Messenger *m, uint8_t *client_id)
203{
204 if (getfriend_id(m, client_id) != -1)
205 return -1;
206
207 /* resize the friend list if necessary */
208 if (realloc_friendlist(m, m->numfriends + 1) != 0)
209 return FAERR_NOMEM;
210
211 memset(&(m->friendlist[m->numfriends]), 0, sizeof(Friend));
212
213 uint32_t i;
214
215 for (i = 0; i <= m->numfriends; ++i) {
216 if (m->friendlist[i].status == NOFRIEND) {
217 DHT_addfriend(m->dht, client_id);
218 m->friendlist[i].status = FRIEND_CONFIRMED;
219 m->friendlist[i].crypt_connection_id = -1;
220 m->friendlist[i].friendrequest_lastsent = 0;
221 memcpy(m->friendlist[i].client_id, client_id, CLIENT_ID_SIZE);
222 m->friendlist[i].statusmessage = calloc(1, 1);
223 m->friendlist[i].statusmessage_length = 1;
224 m->friendlist[i].userstatus = USERSTATUS_NONE;
225 m->friendlist[i].message_id = 0;
226 m->friendlist[i].receives_read_receipts = 1; /* default: YES */
227
228 if (m->numfriends == i)
229 ++ m->numfriends;
230
231 return i;
232 }
233 }
234
235 return -1;
236}
237
238/* remove a friend
239 return 0 if success
240 return -1 if failure */
241int m_delfriend(Messenger *m, int friendnumber)
242{
243 if (friendnumber >= m->numfriends || friendnumber < 0)
244 return -1;
245
246 DHT_delfriend(m->dht, m->friendlist[friendnumber].client_id);
247 crypto_kill(m->net_crypto, m->friendlist[friendnumber].crypt_connection_id);
248 free(m->friendlist[friendnumber].statusmessage);
249 memset(&(m->friendlist[friendnumber]), 0, sizeof(Friend));
250 uint32_t i;
251
252 for (i = m->numfriends; i != 0; --i) {
253 if (m->friendlist[i - 1].status != NOFRIEND)
254 break;
255 }
256
257 m->numfriends = i;
258
259 if (realloc_friendlist(m, m->numfriends) != 0)
260 return FAERR_NOMEM;
261
262 return 0;
263}
264
265/* return FRIEND_ONLINE if friend is online
266 return FRIEND_CONFIRMED if friend is confirmed
267 return FRIEND_REQUESTED if the friend request was sent
268 return FRIEND_ADDED if the friend was added
269 return NOFRIEND if there is no friend with that number */
270int m_friendstatus(Messenger *m, int friendnumber)
271{
272 if (friendnumber < 0 || friendnumber >= m->numfriends)
273 return NOFRIEND;
274
275 return m->friendlist[friendnumber].status;
276}
277
278/* send a text chat message to an online friend
279 return the message id if packet was successfully put into the send queue
280 return 0 if it was not */
281uint32_t m_sendmessage(Messenger *m, int friendnumber, uint8_t *message, uint32_t length)
282{
283 if (friendnumber < 0 || friendnumber >= m->numfriends)
284 return 0;
285
286 uint32_t msgid = ++m->friendlist[friendnumber].message_id;
287
288 if (msgid == 0)
289 msgid = 1; /* otherwise, false error */
290
291 if (m_sendmessage_withid(m, friendnumber, msgid, message, length)) {
292 return msgid;
293 }
294
295 return 0;
296}
297
298uint32_t m_sendmessage_withid(Messenger *m, int friendnumber, uint32_t theid, uint8_t *message, uint32_t length)
299{
300 if (length >= (MAX_DATA_SIZE - sizeof(theid)))
301 return 0;
302
303 uint8_t temp[MAX_DATA_SIZE];
304 theid = htonl(theid);
305 memcpy(temp, &theid, sizeof(theid));
306 memcpy(temp + sizeof(theid), message, length);
307 return write_cryptpacket_id(m, friendnumber, PACKET_ID_MESSAGE, temp, length + sizeof(theid));
308}
309
310/* send an action to an online friend
311 return 1 if packet was successfully put into the send queue
312 return 0 if it was not */
313int m_sendaction(Messenger *m, int friendnumber, uint8_t *action, uint32_t length)
314{
315 return write_cryptpacket_id(m, friendnumber, PACKET_ID_ACTION, action, length);
316}
317
318/* send a name packet to friendnumber
319 length is the length with the NULL terminator*/
320static int m_sendname(Messenger *m, int friendnumber, uint8_t *name, uint16_t length)
321{
322 if (length > MAX_NAME_LENGTH || length == 0)
323 return 0;
324
325 return write_cryptpacket_id(m, friendnumber, PACKET_ID_NICKNAME, name, length);
326}
327
328/* set the name of a friend
329 return 0 if success
330 return -1 if failure */
331static int setfriendname(Messenger *m, int friendnumber, uint8_t *name)
332{
333 if (friendnumber >= m->numfriends || friendnumber < 0)
334 return -1;
335
336 memcpy(m->friendlist[friendnumber].name, name, MAX_NAME_LENGTH);
337 return 0;
338}
339
340/* Set our nickname
341 name must be a string of maximum MAX_NAME_LENGTH length.
342 length must be at least 1 byte
343 length is the length of name with the NULL terminator
344 return 0 if success
345 return -1 if failure */
346int setname(Messenger *m, uint8_t *name, uint16_t length)
347{
348 if (length > MAX_NAME_LENGTH || length == 0)
349 return -1;
350
351 memcpy(m->name, name, length);
352 m->name_length = length;
353 uint32_t i;
354
355 for (i = 0; i < m->numfriends; ++i)
356 m->friendlist[i].name_sent = 0;
357
358 return 0;
359}
360
361/* get our nickname
362 put it in name
363 name needs to be a valid memory location with a size of at least MAX_NAME_LENGTH bytes.
364 return the length of the name */
365uint16_t getself_name(Messenger *m, uint8_t *name, uint16_t nlen)
366{
367 uint16_t len;
368
369 if (name == NULL || nlen == 0) {
370 return 0;
371 }
372
373 len = MIN(nlen, m->name_length);
374 memcpy(name, m->name, m->name_length);
375
376 return len;
377}
378
379/* get name of friendnumber
380 put it in name
381 name needs to be a valid memory location with a size of at least MAX_NAME_LENGTH bytes.
382 return 0 if success
383 return -1 if failure */
384int getname(Messenger *m, int friendnumber, uint8_t *name)
385{
386 if (friendnumber >= m->numfriends || friendnumber < 0)
387 return -1;
388
389 memcpy(name, m->friendlist[friendnumber].name, MAX_NAME_LENGTH);
390 return 0;
391}
392
393int m_set_statusmessage(Messenger *m, uint8_t *status, uint16_t length)
394{
395 if (length > MAX_STATUSMESSAGE_LENGTH)
396 return -1;
397
398 memcpy(m->statusmessage, status, length);
399 m->statusmessage_length = length;
400
401 uint32_t i;
402
403 for (i = 0; i < m->numfriends; ++i)
404 m->friendlist[i].statusmessage_sent = 0;
405
406 return 0;
407}
408
409int m_set_userstatus(Messenger *m, USERSTATUS status)
410{
411 if (status >= USERSTATUS_INVALID) {
412 return -1;
413 }
414
415 m->userstatus = status;
416 uint32_t i;
417
418 for (i = 0; i < m->numfriends; ++i)
419 m->friendlist[i].userstatus_sent = 0;
420
421 return 0;
422}
423
424/* return the size of friendnumber's user status
425 guaranteed to be at most MAX_STATUSMESSAGE_LENGTH */
426int m_get_statusmessage_size(Messenger *m, int friendnumber)
427{
428 if (friendnumber >= m->numfriends || friendnumber < 0)
429 return -1;
430
431 return m->friendlist[friendnumber].statusmessage_length;
432}
433
434/* copy the user status of friendnumber into buf, truncating if needed to maxlen
435 bytes, use m_get_statusmessage_size to find out how much you need to allocate */
436int m_copy_statusmessage(Messenger *m, int friendnumber, uint8_t *buf, uint32_t maxlen)
437{
438 if (friendnumber >= m->numfriends || friendnumber < 0)
439 return -1;
440
441 memset(buf, 0, maxlen);
442 memcpy(buf, m->friendlist[friendnumber].statusmessage, MIN(maxlen, MAX_STATUSMESSAGE_LENGTH) - 1);
443 return 0;
444}
445
446int m_copy_self_statusmessage(Messenger *m, uint8_t *buf, uint32_t maxlen)
447{
448 memset(buf, 0, maxlen);
449 memcpy(buf, m->statusmessage, MIN(maxlen, MAX_STATUSMESSAGE_LENGTH) - 1);
450 return 0;
451}
452
453USERSTATUS m_get_userstatus(Messenger *m, int friendnumber)
454{
455 if (friendnumber >= m->numfriends || friendnumber < 0)
456 return USERSTATUS_INVALID;
457
458 USERSTATUS status = m->friendlist[friendnumber].userstatus;
459
460 if (status >= USERSTATUS_INVALID) {
461 status = USERSTATUS_NONE;
462 }
463
464 return status;
465}
466
467USERSTATUS m_get_self_userstatus(Messenger *m)
468{
469 return m->userstatus;
470}
471
472static int send_statusmessage(Messenger *m, int friendnumber, uint8_t *status, uint16_t length)
473{
474 return write_cryptpacket_id(m, friendnumber, PACKET_ID_STATUSMESSAGE, status, length);
475}
476
477static int send_userstatus(Messenger *m, int friendnumber, USERSTATUS status)
478{
479 uint8_t stat = status;
480 return write_cryptpacket_id(m, friendnumber, PACKET_ID_USERSTATUS, &stat, sizeof(stat));
481}
482
483static int send_ping(Messenger *m, int friendnumber)
484{
485 m->friendlist[friendnumber].ping_lastsent = unix_time();
486 return write_cryptpacket_id(m, friendnumber, PACKET_ID_PING, 0, 0);
487}
488
489static int set_friend_statusmessage(Messenger *m, int friendnumber, uint8_t *status, uint16_t length)
490{
491 if (friendnumber >= m->numfriends || friendnumber < 0)
492 return -1;
493
494 uint8_t *newstatus = calloc(length, 1);
495 memcpy(newstatus, status, length);
496 free(m->friendlist[friendnumber].statusmessage);
497 m->friendlist[friendnumber].statusmessage = newstatus;
498 m->friendlist[friendnumber].statusmessage_length = length;
499 return 0;
500}
501
502static void set_friend_userstatus(Messenger *m, int friendnumber, USERSTATUS status)
503{
504 m->friendlist[friendnumber].userstatus = status;
505}
506
507/* Sets whether we send read receipts for friendnumber. */
508void m_set_sends_receipts(Messenger *m, int friendnumber, int yesno)
509{
510 if (yesno != 0 || yesno != 1)
511 return;
512
513 if (friendnumber >= m->numfriends || friendnumber < 0)
514 return;
515
516 m->friendlist[friendnumber].receives_read_receipts = yesno;
517}
518
519/* static void (*friend_request)(uint8_t *, uint8_t *, uint16_t); */
520/* set the function that will be executed when a friend request is received. */
521void m_callback_friendrequest(Messenger *m, void (*function)(uint8_t *, uint8_t *, uint16_t, void *), void *userdata)
522{
523 callback_friendrequest(&(m->fr), function, userdata);
524}
525
526/* set the function that will be executed when a message from a friend is received. */
527void m_callback_friendmessage(Messenger *m, void (*function)(Messenger *m, int, uint8_t *, uint16_t, void *),
528 void *userdata)
529{
530 m->friend_message = function;
531 m->friend_message_userdata = userdata;
532}
533
534void m_callback_action(Messenger *m, void (*function)(Messenger *m, int, uint8_t *, uint16_t, void *), void *userdata)
535{
536 m->friend_action = function;
537 m->friend_action_userdata = userdata;
538}
539
540void m_callback_namechange(Messenger *m, void (*function)(Messenger *m, int, uint8_t *, uint16_t, void *),
541 void *userdata)
542{
543 m->friend_namechange = function;
544 m->friend_namechange_userdata = userdata;
545}
546
547void m_callback_statusmessage(Messenger *m, void (*function)(Messenger *m, int, uint8_t *, uint16_t, void *),
548 void *userdata)
549{
550 m->friend_statusmessagechange = function;
551 m->friend_statuschange_userdata = userdata;
552}
553
554void m_callback_userstatus(Messenger *m, void (*function)(Messenger *m, int, USERSTATUS, void *), void *userdata)
555{
556 m->friend_userstatuschange = function;
557 m->friend_userstatuschange_userdata = userdata;
558}
559
560void m_callback_read_receipt(Messenger *m, void (*function)(Messenger *m, int, uint32_t, void *), void *userdata)
561{
562 m->read_receipt = function;
563 m->read_receipt_userdata = userdata;
564}
565
566void m_callback_connectionstatus(Messenger *m, void (*function)(Messenger *m, int, uint8_t, void *), void *userdata)
567{
568 m->friend_connectionstatuschange = function;
569 m->friend_connectionstatuschange_userdata = userdata;
570}
571
572static void check_friend_connectionstatus(Messenger *m, int friendnumber, uint8_t status)
573{
574 if (!m->friend_connectionstatuschange)
575 return;
576
577 if (status == NOFRIEND)
578 return;
579
580 const uint8_t was_connected = m->friendlist[friendnumber].status == FRIEND_ONLINE;
581 const uint8_t is_connected = status == FRIEND_ONLINE;
582
583 if (is_connected != was_connected)
584 m->friend_connectionstatuschange(m, friendnumber, is_connected, m->friend_connectionstatuschange_userdata);
585}
586
587void set_friend_status(Messenger *m, int friendnumber, uint8_t status)
588{
589 check_friend_connectionstatus(m, friendnumber, status);
590 m->friendlist[friendnumber].status = status;
591}
592
593int write_cryptpacket_id(Messenger *m, int friendnumber, uint8_t packet_id, uint8_t *data, uint32_t length)
594{
595 if (friendnumber < 0 || friendnumber >= m->numfriends)
596 return 0;
597
598 if (length >= MAX_DATA_SIZE || m->friendlist[friendnumber].status != FRIEND_ONLINE)
599 return 0;
600
601 uint8_t packet[length + 1];
602 packet[0] = packet_id;
603
604 if (length != 0)
605 memcpy(packet + 1, data, length);
606
607 return write_cryptpacket(m->net_crypto, m->friendlist[friendnumber].crypt_connection_id, packet, length + 1);
608}
609
610/*Interval in seconds between LAN discovery packet sending*/
611#define LAN_DISCOVERY_INTERVAL 60
612#define PORT 33445
613
614/*Send a LAN discovery packet every LAN_DISCOVERY_INTERVAL seconds*/
615static void LANdiscovery(Messenger *m)
616{
617 if (m->last_LANdiscovery + LAN_DISCOVERY_INTERVAL < unix_time()) {
618 send_LANdiscovery(htons(PORT), m->net_crypto);
619 m->last_LANdiscovery = unix_time();
620 }
621}
622
623/* run this at startup */
624Messenger *initMessenger(void)
625{
626 Messenger *m = calloc(1, sizeof(Messenger));
627
628 if ( ! m )
629 return NULL;
630
631 IP ip;
632 ip.i = 0;
633 m->net = new_networking(ip, PORT);
634
635 if (m->net == NULL) {
636 free(m);
637 return NULL;
638 }
639
640 m->net_crypto = new_net_crypto(m->net);
641
642 if (m->net_crypto == NULL) {
643 kill_networking(m->net);
644 free(m);
645 return NULL;
646 }
647
648 m->dht = new_DHT(m->net_crypto);
649
650 if (m->dht == NULL) {
651 kill_net_crypto(m->net_crypto);
652 kill_networking(m->net);
653 free(m);
654 return NULL;
655 }
656
657 new_keys(m->net_crypto);
658 m_set_statusmessage(m, (uint8_t *)"Online", sizeof("Online"));
659
660 friendreq_init(&(m->fr), m->net_crypto);
661 LANdiscovery_init(m->dht);
662 set_nospam(&(m->fr), random_int());
663
664 return m;
665}
666
667/* run this before closing shop */
668void cleanupMessenger(Messenger *m)
669{
670 /* FIXME TODO ideally cleanupMessenger will mirror initMessenger
671 * this requires the other modules to expose cleanup functions
672 */
673 kill_DHT(m->dht);
674 kill_net_crypto(m->net_crypto);
675 kill_networking(m->net);
676 free(m->friendlist);
677 free(m);
678}
679
680//TODO: make this function not suck.
681void doFriends(Messenger *m)
682{
683 /* TODO: add incoming connections and some other stuff. */
684 uint32_t i;
685 int len;
686 uint8_t temp[MAX_DATA_SIZE];
687 uint64_t temp_time = unix_time();
688
689 for (i = 0; i < m->numfriends; ++i) {
690 if (m->friendlist[i].status == FRIEND_ADDED) {
691 int fr = send_friendrequest(m->dht, m->friendlist[i].client_id, m->friendlist[i].friendrequest_nospam,
692 m->friendlist[i].info,
693 m->friendlist[i].info_size);
694
695 if (fr >= 0) {
696 set_friend_status(m, i, FRIEND_REQUESTED);
697 m->friendlist[i].friendrequest_lastsent = temp_time;
698 }
699 }
700
701 if (m->friendlist[i].status == FRIEND_REQUESTED
702 || m->friendlist[i].status == FRIEND_CONFIRMED) { /* friend is not online */
703 if (m->friendlist[i].status == FRIEND_REQUESTED) {
704 /* If we didn't connect to friend after successfully sending him a friend request the request is deemed
705 unsuccessful so we set the status back to FRIEND_ADDED and try again.*/
706 if (m->friendlist[i].friendrequest_lastsent + m->friendlist[i].friendrequest_timeout < temp_time) {
707 set_friend_status(m, i, FRIEND_ADDED);
708 /* Double the default timeout everytime if friendrequest is assumed to have been
709 sent unsuccessfully. */
710 m->friendlist[i].friendrequest_timeout *= 2;
711 }
712 }
713
714 IP_Port friendip = DHT_getfriendip(m->dht, m->friendlist[i].client_id);
715
716 switch (is_cryptoconnected(m->net_crypto, m->friendlist[i].crypt_connection_id)) {
717 case 0:
718 if (friendip.ip.i > 1)
719 m->friendlist[i].crypt_connection_id = crypto_connect(m->net_crypto, m->friendlist[i].client_id, friendip);
720
721 break;
722
723 case 3: /* Connection is established */
724 set_friend_status(m, i, FRIEND_ONLINE);
725 m->friendlist[i].name_sent = 0;
726 m->friendlist[i].userstatus_sent = 0;
727 m->friendlist[i].statusmessage_sent = 0;
728 m->friendlist[i].ping_lastrecv = temp_time;
729 break;
730
731 case 4:
732 crypto_kill(m->net_crypto, m->friendlist[i].crypt_connection_id);
733 m->friendlist[i].crypt_connection_id = -1;
734 break;
735
736 default:
737 break;
738 }
739 }
740
741 while (m->friendlist[i].status == FRIEND_ONLINE) { /* friend is online */
742 if (m->friendlist[i].name_sent == 0) {
743 if (m_sendname(m, i, m->name, m->name_length))
744 m->friendlist[i].name_sent = 1;
745 }
746
747 if (m->friendlist[i].statusmessage_sent == 0) {
748 if (send_statusmessage(m, i, m->statusmessage, m->statusmessage_length))
749 m->friendlist[i].statusmessage_sent = 1;
750 }
751
752 if (m->friendlist[i].userstatus_sent == 0) {
753 if (send_userstatus(m, i, m->userstatus))
754 m->friendlist[i].userstatus_sent = 1;
755 }
756
757 if (m->friendlist[i].ping_lastsent + FRIEND_PING_INTERVAL < temp_time) {
758 send_ping(m, i);
759 }
760
761 len = read_cryptpacket(m->net_crypto, m->friendlist[i].crypt_connection_id, temp);
762 uint8_t packet_id = temp[0];
763 uint8_t *data = temp + 1;
764 int data_length = len - 1;
765
766 if (len > 0) {
767 switch (packet_id) {
768 case PACKET_ID_PING: {
769 m->friendlist[i].ping_lastrecv = temp_time;
770 break;
771 }
772
773 case PACKET_ID_NICKNAME: {
774 if (data_length >= MAX_NAME_LENGTH || data_length == 0)
775 break;
776
777 if (m->friend_namechange)
778 m->friend_namechange(m, i, data, data_length, m->friend_namechange_userdata);
779
780 memcpy(m->friendlist[i].name, data, data_length);
781 m->friendlist[i].name[data_length - 1] = 0; /* make sure the NULL terminator is present. */
782 break;
783 }
784
785 case PACKET_ID_STATUSMESSAGE: {
786 if (data_length == 0)
787 break;
788
789 uint8_t *status = calloc(MIN(data_length, MAX_STATUSMESSAGE_LENGTH), 1);
790 memcpy(status, data, MIN(data_length, MAX_STATUSMESSAGE_LENGTH));
791
792 if (m->friend_statusmessagechange)
793 m->friend_statusmessagechange(m, i, status, MIN(data_length, MAX_STATUSMESSAGE_LENGTH),
794 m->friend_statuschange_userdata);
795
796 set_friend_statusmessage(m, i, status, MIN(data_length, MAX_STATUSMESSAGE_LENGTH));
797 free(status);
798 break;
799 }
800
801 case PACKET_ID_USERSTATUS: {
802 if (data_length != 1)
803 break;
804
805 USERSTATUS status = data[0];
806
807 if (m->friend_userstatuschange)
808 m->friend_userstatuschange(m, i, status, m->friend_userstatuschange_userdata);
809
810 set_friend_userstatus(m, i, status);
811 break;
812 }
813
814 case PACKET_ID_MESSAGE: {
815 uint8_t *message_id = data;
816 uint8_t message_id_length = 4;
817 uint8_t *message = data + message_id_length;
818 uint16_t message_length = data_length - message_id_length;
819
820 if (m->friendlist[i].receives_read_receipts) {
821 write_cryptpacket_id(m, i, PACKET_ID_RECEIPT, message_id, message_id_length);
822 }
823
824 if (m->friend_message)
825 (*m->friend_message)(m, i, message, message_length, m->friend_message_userdata);
826
827 break;
828 }
829
830 case PACKET_ID_ACTION: {
831 if (m->friend_action)
832 (*m->friend_action)(m, i, data, data_length, m->friend_action_userdata);
833
834 break;
835 }
836
837 case PACKET_ID_RECEIPT: {
838 uint32_t msgid;
839
840 if (data_length < sizeof(msgid))
841 break;
842
843 memcpy(&msgid, data, sizeof(msgid));
844 msgid = ntohl(msgid);
845
846 if (m->read_receipt)
847 (*m->read_receipt)(m, i, msgid, m->read_receipt_userdata);
848
849 break;
850 }
851 }
852 } else {
853 if (is_cryptoconnected(m->net_crypto,
854 m->friendlist[i].crypt_connection_id) == 4) { /* if the connection timed out, kill it */
855 crypto_kill(m->net_crypto, m->friendlist[i].crypt_connection_id);
856 m->friendlist[i].crypt_connection_id = -1;
857 set_friend_status(m, i, FRIEND_CONFIRMED);
858 }
859
860 break;
861 }
862
863 if (m->friendlist[i].ping_lastrecv + FRIEND_CONNECTION_TIMEOUT < temp_time) {
864 /* if we stopped recieving ping packets kill it */
865 crypto_kill(m->net_crypto, m->friendlist[i].crypt_connection_id);
866 m->friendlist[i].crypt_connection_id = -1;
867 set_friend_status(m, i, FRIEND_CONFIRMED);
868 }
869 }
870 }
871}
872
873void doInbound(Messenger *m)
874{
875 uint8_t secret_nonce[crypto_box_NONCEBYTES];
876 uint8_t public_key[crypto_box_PUBLICKEYBYTES];
877 uint8_t session_key[crypto_box_PUBLICKEYBYTES];
878 int inconnection = crypto_inbound(m->net_crypto, public_key, secret_nonce, session_key);
879
880 if (inconnection != -1) {
881 int friend_id = getfriend_id(m, public_key);
882
883 if (friend_id != -1) {
884 crypto_kill(m->net_crypto, m->friendlist[friend_id].crypt_connection_id);
885 m->friendlist[friend_id].crypt_connection_id =
886 accept_crypto_inbound(m->net_crypto, inconnection, public_key, secret_nonce, session_key);
887
888 set_friend_status(m, friend_id, FRIEND_CONFIRMED);
889 }
890 }
891}
892
893/* the main loop that needs to be run at least 20 times per second. */
894void doMessenger(Messenger *m)
895{
896 networking_poll(m->net);
897
898 do_DHT(m->dht);
899 do_net_crypto(m->net_crypto);
900 doInbound(m);
901 doFriends(m);
902 LANdiscovery(m);
903}
904
905/* returns the size of the messenger data (for saving) */
906uint32_t Messenger_size(Messenger *m)
907{
908 return crypto_box_PUBLICKEYBYTES + crypto_box_SECRETKEYBYTES
909 + sizeof(uint32_t) // nospam
910 + sizeof(uint32_t) // DHT size
911 + DHT_size(m->dht) // DHT itself
912 + sizeof(uint32_t) // Friendlist size
913 + sizeof(Friend) * m->numfriends // Friendlist itself
914 + sizeof(uint16_t) // Own nickname length
915 + m->name_length // Own nickname
916 ;
917}
918
919/* save the messenger in data of size Messenger_size() */
920void Messenger_save(Messenger *m, uint8_t *data)
921{
922 save_keys(m->net_crypto, data);
923 data += crypto_box_PUBLICKEYBYTES + crypto_box_SECRETKEYBYTES;
924 uint32_t nospam = get_nospam(&(m->fr));
925 memcpy(data, &nospam, sizeof(nospam));
926 data += sizeof(nospam);
927 uint32_t size = DHT_size(m->dht);
928 memcpy(data, &size, sizeof(size));
929 data += sizeof(size);
930 DHT_save(m->dht, data);
931 data += size;
932 size = sizeof(Friend) * m->numfriends;
933 memcpy(data, &size, sizeof(size));
934 data += sizeof(size);
935 memcpy(data, m->friendlist, sizeof(Friend) * m->numfriends);
936 data += size;
937 uint16_t small_size = m->name_length;
938 memcpy(data, &small_size, sizeof(small_size));
939 data += sizeof(small_size);
940 memcpy(data, m->name, small_size);
941}
942
943/* load the messenger from data of size length. */
944int Messenger_load(Messenger *m, uint8_t *data, uint32_t length)
945{
946 if (length == ~0)
947 return -1;
948
949 if (length < crypto_box_PUBLICKEYBYTES + crypto_box_SECRETKEYBYTES + sizeof(uint32_t) * 3)
950 return -1;
951
952 length -= crypto_box_PUBLICKEYBYTES + crypto_box_SECRETKEYBYTES + sizeof(uint32_t) * 3;
953 load_keys(m->net_crypto, data);
954 data += crypto_box_PUBLICKEYBYTES + crypto_box_SECRETKEYBYTES;
955 uint32_t nospam;
956 memcpy(&nospam, data, sizeof(nospam));
957 set_nospam(&(m->fr), nospam);
958 data += sizeof(nospam);
959 uint32_t size;
960 memcpy(&size, data, sizeof(size));
961 data += sizeof(size);
962
963 if (length < size)
964 return -1;
965
966 length -= size;
967
968 if (DHT_load(m->dht, data, size) == -1)
969 return -1;
970
971 data += size;
972 memcpy(&size, data, sizeof(size));
973 data += sizeof(size);
974
975 if (length < size || size % sizeof(Friend) != 0)
976 return -1;
977
978 Friend *temp = malloc(size);
979 memcpy(temp, data, size);
980
981 uint16_t num = size / sizeof(Friend);
982
983 uint32_t i;
984
985 for (i = 0; i < num; ++i) {
986 if (temp[i].status >= 3) {
987 int fnum = m_addfriend_norequest(m, temp[i].client_id);
988 setfriendname(m, fnum, temp[i].name);
989 /* set_friend_statusmessage(fnum, temp[i].statusmessage, temp[i].statusmessage_length); */
990 } else if (temp[i].status != 0) {
991 /* TODO: this is not a good way to do this. */
992 uint8_t address[FRIEND_ADDRESS_SIZE];
993 memcpy(address, temp[i].client_id, crypto_box_PUBLICKEYBYTES);
994 memcpy(address + crypto_box_PUBLICKEYBYTES, &(temp[i].friendrequest_nospam), sizeof(uint32_t));
995 uint16_t checksum = address_checksum(address, FRIEND_ADDRESS_SIZE - sizeof(checksum));
996 memcpy(address + crypto_box_PUBLICKEYBYTES + sizeof(uint32_t), &checksum, sizeof(checksum));
997 m_addfriend(m, address, temp[i].info, temp[i].info_size);
998 }
999 }
1000
1001 free(temp);
1002 data += size;
1003 length -= size;
1004
1005 uint16_t small_size;
1006
1007 if (length < sizeof(small_size))
1008 return -1;
1009
1010 memcpy(&small_size, data, sizeof(small_size));
1011 data += sizeof(small_size);
1012 length -= sizeof(small_size);
1013
1014 if (length != small_size)
1015 return -1;
1016
1017 setname(m, data, small_size);
1018
1019 return 0;
1020}
diff --git a/toxcore/Messenger.h b/toxcore/Messenger.h
new file mode 100644
index 00000000..e808529f
--- /dev/null
+++ b/toxcore/Messenger.h
@@ -0,0 +1,351 @@
1/* Messenger.h
2 *
3 * An implementation of a simple text chat only messenger on the tox network core.
4 *
5 * NOTE: All the text in the messages must be encoded using UTF-8
6 *
7 * Copyright (C) 2013 Tox project All Rights Reserved.
8 *
9 * This file is part of Tox.
10 *
11 * Tox is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation, either version 3 of the License, or
14 * (at your option) any later version.
15 *
16 * Tox is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with Tox. If not, see <http://www.gnu.org/licenses/>.
23 *
24 */
25
26#ifndef MESSENGER_H
27#define MESSENGER_H
28
29#include "net_crypto.h"
30#include "DHT.h"
31#include "friend_requests.h"
32#include "LAN_discovery.h"
33
34#ifdef __cplusplus
35extern "C" {
36#endif
37
38#define MAX_NAME_LENGTH 128
39#define MAX_STATUSMESSAGE_LENGTH 128
40
41#define FRIEND_ADDRESS_SIZE (crypto_box_PUBLICKEYBYTES + sizeof(uint32_t) + sizeof(uint16_t))
42
43#define PACKET_ID_PING 0
44#define PACKET_ID_NICKNAME 48
45#define PACKET_ID_STATUSMESSAGE 49
46#define PACKET_ID_USERSTATUS 50
47#define PACKET_ID_RECEIPT 65
48#define PACKET_ID_MESSAGE 64
49#define PACKET_ID_ACTION 63
50
51
52/* status definitions */
53enum {
54 NOFRIEND,
55 FRIEND_ADDED,
56 FRIEND_REQUESTED,
57 FRIEND_CONFIRMED,
58 FRIEND_ONLINE,
59};
60
61/* errors for m_addfriend
62 * FAERR - Friend Add Error */
63enum {
64 FAERR_TOOLONG = -1,
65 FAERR_NOMESSAGE = -2,
66 FAERR_OWNKEY = -3,
67 FAERR_ALREADYSENT = -4,
68 FAERR_UNKNOWN = -5,
69 FAERR_BADCHECKSUM = -6,
70 FAERR_SETNEWNOSPAM = -7,
71 FAERR_NOMEM = -8
72};
73
74/* don't assume MAX_STATUSMESSAGE_LENGTH will stay at 128, it may be increased
75 to an absurdly large number later */
76
77/* Default start timeout in seconds between friend requests */
78#define FRIENDREQUEST_TIMEOUT 5;
79
80/* interval between the sending of ping packets.*/
81#define FRIEND_PING_INTERVAL 5
82
83/* If no packets are recieved from friend in this time interval, kill the connection.*/
84#define FRIEND_CONNECTION_TIMEOUT (FRIEND_PING_INTERVAL * 2)
85
86/* USERSTATUS
87 * Represents userstatuses someone can have. */
88
89typedef enum {
90 USERSTATUS_NONE,
91 USERSTATUS_AWAY,
92 USERSTATUS_BUSY,
93 USERSTATUS_INVALID
94}
95USERSTATUS;
96
97typedef struct {
98 uint8_t client_id[CLIENT_ID_SIZE];
99 int crypt_connection_id;
100 uint64_t friendrequest_lastsent; /* time at which the last friend request was sent. */
101 uint32_t friendrequest_timeout; /* The timeout between successful friendrequest sending attempts */
102 uint8_t status; /* 0 if no friend, 1 if added, 2 if friend request sent, 3 if confirmed friend, 4 if online. */
103 uint8_t info[MAX_DATA_SIZE]; /* the data that is sent during the friend requests we do */
104 uint8_t name[MAX_NAME_LENGTH];
105 uint8_t name_sent; /* 0 if we didn't send our name to this friend 1 if we have. */
106 uint8_t *statusmessage;
107 uint16_t statusmessage_length;
108 uint8_t statusmessage_sent;
109 USERSTATUS userstatus;
110 uint8_t userstatus_sent;
111 uint16_t info_size; /* length of the info */
112 uint32_t message_id; /* a semi-unique id used in read receipts */
113 uint8_t receives_read_receipts; /* shall we send read receipts to this person? */
114 uint32_t friendrequest_nospam; /*The nospam number used in the friend request*/
115 uint64_t ping_lastrecv;
116 uint64_t ping_lastsent;
117} Friend;
118
119typedef struct Messenger {
120
121 Networking_Core *net;
122 Net_Crypto *net_crypto;
123 DHT *dht;
124 Friend_Requests fr;
125 uint8_t name[MAX_NAME_LENGTH];
126 uint16_t name_length;
127
128 uint8_t statusmessage[MAX_STATUSMESSAGE_LENGTH];
129 uint16_t statusmessage_length;
130
131 USERSTATUS userstatus;
132
133 Friend *friendlist;
134 uint32_t numfriends;
135
136 uint64_t last_LANdiscovery;
137
138 void (*friend_message)(struct Messenger *m, int, uint8_t *, uint16_t, void *);
139 void *friend_message_userdata;
140 void (*friend_action)(struct Messenger *m, int, uint8_t *, uint16_t, void *);
141 void *friend_action_userdata;
142 void (*friend_namechange)(struct Messenger *m, int, uint8_t *, uint16_t, void *);
143 void *friend_namechange_userdata;
144 void (*friend_statusmessagechange)(struct Messenger *m, int, uint8_t *, uint16_t, void *);
145 void *friend_statusmessagechange_userdata;
146 void (*friend_userstatuschange)(struct Messenger *m, int, USERSTATUS, void *);
147 void *friend_userstatuschange_userdata;
148 void (*read_receipt)(struct Messenger *m, int, uint32_t, void *);
149 void *read_receipt_userdata;
150 void (*friend_statuschange)(struct Messenger *m, int, uint8_t, void *);
151 void *friend_statuschange_userdata;
152 void (*friend_connectionstatuschange)(struct Messenger *m, int, uint8_t, void *);
153 void *friend_connectionstatuschange_userdata;
154
155
156} Messenger;
157
158/*
159 * returns a FRIEND_ADDRESS_SIZE byte address to give to others.
160 * format: [client_id (32 bytes)][nospam number (4 bytes)][checksum (2 bytes)]
161 *
162 */
163void getaddress(Messenger *m, uint8_t *address);
164
165/*
166 * add a friend
167 * set the data that will be sent along with friend request
168 * address is the address of the friend (returned by getaddress of the friend you wish to add) it must be FRIEND_ADDRESS_SIZE bytes. TODO: add checksum.
169 * data is the data and length is the length
170 * returns the friend number if success
171 * return -1 if message length is too long
172 * return -2 if no message (message length must be >= 1 byte)
173 * return -3 if user's own key
174 * return -4 if friend request already sent or already a friend
175 * return -5 for unknown error
176 * return -6 if bad checksum in address
177 * return -7 if the friend was already there but the nospam was different
178 * (the nospam for that friend was set to the new one)
179 * return -8 if increasing the friend list size fails
180 */
181int m_addfriend(Messenger *m, uint8_t *address, uint8_t *data, uint16_t length);
182
183
184/* add a friend without sending a friendrequest.
185 returns the friend number if success
186 return -1 if failure. */
187int m_addfriend_norequest(Messenger *m, uint8_t *client_id);
188
189/* return the friend id associated to that client id.
190 return -1 if no such friend */
191int getfriend_id(Messenger *m, uint8_t *client_id);
192
193/* copies the public key associated to that friend id into client_id buffer.
194 make sure that client_id is of size CLIENT_ID_SIZE.
195 return 0 if success
196 return -1 if failure */
197int getclient_id(Messenger *m, int friend_id, uint8_t *client_id);
198
199/* remove a friend */
200int m_delfriend(Messenger *m, int friendnumber);
201
202/* return 4 if friend is online
203 return 3 if friend is confirmed
204 return 2 if the friend request was sent
205 return 1 if the friend was added
206 return 0 if there is no friend with that number */
207int m_friendstatus(Messenger *m, int friendnumber);
208
209/* send a text chat message to an online friend
210 returns the message id if packet was successfully put into the send queue
211 return 0 if it was not
212 you will want to retain the return value, it will be passed to your read receipt callback
213 if one is received.
214 m_sendmessage_withid will send a message with the id of your choosing,
215 however we can generate an id for you by calling plain m_sendmessage. */
216uint32_t m_sendmessage(Messenger *m, int friendnumber, uint8_t *message, uint32_t length);
217uint32_t m_sendmessage_withid(Messenger *m, int friendnumber, uint32_t theid, uint8_t *message, uint32_t length);
218
219/* send an action to an online friend
220 returns 1 if packet was successfully put into the send queue
221 return 0 if it was not */
222int m_sendaction(Messenger *m, int friendnumber, uint8_t *action, uint32_t length);
223
224/* Set our nickname
225 name must be a string of maximum MAX_NAME_LENGTH length.
226 length must be at least 1 byte
227 length is the length of name with the NULL terminator
228 return 0 if success
229 return -1 if failure */
230int setname(Messenger *m, uint8_t *name, uint16_t length);
231
232/*
233 Get your nickname.
234 m The messanger context to use.
235 name Pointer to a string for the name.
236 nlen The length of the string buffer.
237 returns Return the length of the name, 0 on error.
238*/
239uint16_t getself_name(Messenger *m, uint8_t *name, uint16_t nlen);
240
241/* get name of friendnumber
242 put it in name
243 name needs to be a valid memory location with a size of at least MAX_NAME_LENGTH (128) bytes.
244 return 0 if success
245 return -1 if failure */
246int getname(Messenger *m, int friendnumber, uint8_t *name);
247
248/* set our user status
249 you are responsible for freeing status after
250 returns 0 on success, -1 on failure */
251int m_set_statusmessage(Messenger *m, uint8_t *status, uint16_t length);
252int m_set_userstatus(Messenger *m, USERSTATUS status);
253
254/* return the length of friendnumber's status message,
255 including null
256 pass it into malloc */
257int m_get_statusmessage_size(Messenger *m, int friendnumber);
258
259/* copy friendnumber's status message into buf, truncating if size is over maxlen
260 get the size you need to allocate from m_get_statusmessage_size
261 The self variant will copy our own status message. */
262int m_copy_statusmessage(Messenger *m, int friendnumber, uint8_t *buf, uint32_t maxlen);
263int m_copy_self_statusmessage(Messenger *m, uint8_t *buf, uint32_t maxlen);
264
265/* Return one of USERSTATUS values.
266 * Values unknown to your application should be represented as USERSTATUS_NONE.
267 * As above, the self variant will return our own USERSTATUS.
268 * If friendnumber is invalid, this shall return USERSTATUS_INVALID. */
269USERSTATUS m_get_userstatus(Messenger *m, int friendnumber);
270USERSTATUS m_get_self_userstatus(Messenger *m);
271
272/* Sets whether we send read receipts for friendnumber.
273 * This function is not lazy, and it will fail if yesno is not (0 or 1).*/
274void m_set_sends_receipts(Messenger *m, int friendnumber, int yesno);
275
276/* set the function that will be executed when a friend request is received.
277 function format is function(uint8_t * public_key, uint8_t * data, uint16_t length) */
278void m_callback_friendrequest(Messenger *m, void (*function)(uint8_t *, uint8_t *, uint16_t, void *), void *userdata);
279
280/* set the function that will be executed when a message from a friend is received.
281 function format is: function(int friendnumber, uint8_t * message, uint32_t length) */
282void m_callback_friendmessage(Messenger *m, void (*function)(Messenger *m, int, uint8_t *, uint16_t, void *),
283 void *userdata);
284
285/* set the function that will be executed when an action from a friend is received.
286 function format is: function(int friendnumber, uint8_t * action, uint32_t length) */
287void m_callback_action(Messenger *m, void (*function)(Messenger *m, int, uint8_t *, uint16_t, void *), void *userdata);
288
289/* set the callback for name changes
290 function(int friendnumber, uint8_t *newname, uint16_t length)
291 you are not responsible for freeing newname */
292void m_callback_namechange(Messenger *m, void (*function)(Messenger *m, int, uint8_t *, uint16_t, void *),
293 void *userdata);
294
295/* set the callback for status message changes
296 function(int friendnumber, uint8_t *newstatus, uint16_t length)
297 you are not responsible for freeing newstatus */
298void m_callback_statusmessage(Messenger *m, void (*function)(Messenger *m, int, uint8_t *, uint16_t, void *),
299 void *userdata);
300
301/* set the callback for status type changes
302 function(int friendnumber, USERSTATUS kind) */
303void m_callback_userstatus(Messenger *m, void (*function)(Messenger *m, int, USERSTATUS, void *), void *userdata);
304
305/* set the callback for read receipts
306 function(int friendnumber, uint32_t receipt)
307 if you are keeping a record of returns from m_sendmessage,
308 receipt might be one of those values, and that means the message
309 has been received on the other side. since core doesn't
310 track ids for you, receipt may not correspond to any message
311 in that case, you should discard it. */
312void m_callback_read_receipt(Messenger *m, void (*function)(Messenger *m, int, uint32_t, void *), void *userdata);
313
314/* set the callback for connection status changes
315 function(int friendnumber, uint8_t status)
316 status:
317 0 -- friend went offline after being previously online
318 1 -- friend went online
319 note that this callback is not called when adding friends, thus the "after
320 being previously online" part. it's assumed that when adding friends,
321 their connection status is offline. */
322void m_callback_connectionstatus(Messenger *m, void (*function)(Messenger *m, int, uint8_t, void *), void *userdata);
323
324/* run this at startup
325 * returns allocated instance of Messenger on success
326 * returns 0 if there are problems */
327Messenger *initMessenger(void);
328
329/* run this before closing shop
330 * free all datastructures */
331void cleanupMessenger(Messenger *M);
332
333/* the main loop that needs to be run at least 200 times per second */
334void doMessenger(Messenger *m);
335
336/* SAVING AND LOADING FUNCTIONS: */
337
338/* returns the size of the messenger data (for saving) */
339uint32_t Messenger_size(Messenger *m);
340
341/* save the messenger in data (must be allocated memory of size Messenger_size()) */
342void Messenger_save(Messenger *m, uint8_t *data);
343
344/* load the messenger from data of size length */
345int Messenger_load(Messenger *m, uint8_t *data, uint32_t length);
346
347#ifdef __cplusplus
348}
349#endif
350
351#endif
diff --git a/toxcore/friend_requests.c b/toxcore/friend_requests.c
new file mode 100644
index 00000000..d8c6858b
--- /dev/null
+++ b/toxcore/friend_requests.c
@@ -0,0 +1,141 @@
1/* friend_requests.c
2 *
3 * Handle friend requests.
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 "friend_requests.h"
25
26/* Try to send a friendrequest to peer with public_key
27 data is the data in the request and length is the length.
28 return -1 if failure.
29 return 0 if it sent the friend request directly to the friend.
30 return the number of peers it was routed through if it did not send it directly.*/
31int send_friendrequest(DHT *dht, uint8_t *public_key, uint32_t nospam_num, uint8_t *data, uint32_t length)
32{
33 if (length + sizeof(nospam_num) > MAX_DATA_SIZE)
34 return -1;
35
36 uint8_t temp[MAX_DATA_SIZE];
37 memcpy(temp, &nospam_num, sizeof(nospam_num));
38 memcpy(temp + sizeof(nospam_num), data, length);
39 uint8_t packet[MAX_DATA_SIZE];
40 int len = create_request(dht->c->self_public_key, dht->c->self_secret_key, packet, public_key, temp,
41 length + sizeof(nospam_num),
42 CRYPTO_PACKET_FRIEND_REQ);
43
44 if (len == -1)
45 return -1;
46
47 IP_Port ip_port = DHT_getfriendip(dht, public_key);
48
49 if (ip_port.ip.i == 1)
50 return -1;
51
52 if (ip_port.ip.i != 0) {
53 if (sendpacket(dht->c->lossless_udp->net->sock, ip_port, packet, len) != -1)
54 return 0;
55
56 return -1;
57 }
58
59 int num = route_tofriend(dht, public_key, packet, len);
60
61 if (num == 0)
62 return -1;
63
64 return num;
65}
66
67
68/*
69 * Set and get the nospam variable used to prevent one type of friend request spam
70 */
71void set_nospam(Friend_Requests *fr, uint32_t num)
72{
73 fr->nospam = num;
74}
75
76uint32_t get_nospam(Friend_Requests *fr)
77{
78 return fr->nospam;
79}
80
81
82/* set the function that will be executed when a friend request is received. */
83void callback_friendrequest(Friend_Requests *fr, void (*function)(uint8_t *, uint8_t *, uint16_t, void *),
84 void *userdata)
85{
86 fr->handle_friendrequest = function;
87 fr->handle_friendrequest_isset = 1;
88 fr->handle_friendrequest_userdata = userdata;
89}
90
91/*Add to list of received friend requests*/
92static void addto_receivedlist(Friend_Requests *fr, uint8_t *client_id)
93{
94 if (fr->received_requests_index >= MAX_RECEIVED_STORED)
95 fr->received_requests_index = 0;
96
97 memcpy(fr->received_requests[fr->received_requests_index], client_id, crypto_box_PUBLICKEYBYTES);
98 ++fr->received_requests_index;
99}
100
101/* Check if a friend request was already received
102 return 0 if not, 1 if we did */
103static int request_received(Friend_Requests *fr, uint8_t *client_id)
104{
105 uint32_t i;
106
107 for (i = 0; i < MAX_RECEIVED_STORED; ++i) {
108 if (memcmp(fr->received_requests[i], client_id, crypto_box_PUBLICKEYBYTES) == 0)
109 return 1;
110 }
111
112 return 0;
113}
114
115
116static int friendreq_handlepacket(void *object, IP_Port source, uint8_t *source_pubkey, uint8_t *packet,
117 uint32_t length)
118{
119 Friend_Requests *fr = object;
120
121 if (fr->handle_friendrequest_isset == 0)
122 return 1;
123
124 if (length <= sizeof(fr->nospam))
125 return 1;
126
127 if (request_received(fr, source_pubkey))
128 return 1;
129
130 if (memcmp(packet, &fr->nospam, sizeof(fr->nospam)) != 0)
131 return 1;
132
133 addto_receivedlist(fr, source_pubkey);
134 (*fr->handle_friendrequest)(source_pubkey, packet + 4, length - 4, fr->handle_friendrequest_userdata);
135 return 0;
136}
137
138void friendreq_init(Friend_Requests *fr, Net_Crypto *c)
139{
140 cryptopacket_registerhandler(c, CRYPTO_PACKET_FRIEND_REQ, &friendreq_handlepacket, fr);
141}
diff --git a/toxcore/friend_requests.h b/toxcore/friend_requests.h
new file mode 100644
index 00000000..2ebd557b
--- /dev/null
+++ b/toxcore/friend_requests.h
@@ -0,0 +1,70 @@
1/* friend_requests.h
2 *
3 * Handle friend requests.
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#ifndef FRIEND_REQUESTS_H
25#define FRIEND_REQUESTS_H
26
27#include "DHT.h"
28#include "net_crypto.h"
29
30#ifdef __cplusplus
31extern "C" {
32#endif
33
34typedef struct {
35 uint32_t nospam;
36 void (*handle_friendrequest)(uint8_t *, uint8_t *, uint16_t, void *);
37 uint8_t handle_friendrequest_isset;
38 void *handle_friendrequest_userdata;
39
40 /*NOTE: the following is just a temporary fix for the multiple friend requests received at the same time problem
41 TODO: Make this better (This will most likely tie in with the way we will handle spam.)*/
42
43#define MAX_RECEIVED_STORED 32
44
45 uint8_t received_requests[MAX_RECEIVED_STORED][crypto_box_PUBLICKEYBYTES];
46 uint16_t received_requests_index;
47} Friend_Requests;
48
49/* Try to send a friendrequest to peer with public_key
50 data is the data in the request and length is the length. */
51int send_friendrequest(DHT *dht, uint8_t *public_key, uint32_t nospam_num, uint8_t *data, uint32_t length);
52/*
53 * Set and get the nospam variable used to prevent one type of friend request spam
54 */
55void set_nospam(Friend_Requests *fr, uint32_t num);
56uint32_t get_nospam(Friend_Requests *fr);
57
58/* set the function that will be executed when a friend request for us is received.
59 function format is function(uint8_t * public_key, uint8_t * data, uint16_t length) */
60void callback_friendrequest(Friend_Requests *fr, void (*function)(uint8_t *, uint8_t *, uint16_t, void *),
61 void *userdata);
62
63/* sets up friendreq packet handlers */
64void friendreq_init(Friend_Requests *fr, Net_Crypto *c);
65
66#ifdef __cplusplus
67}
68#endif
69
70#endif
diff --git a/toxcore/net_crypto.c b/toxcore/net_crypto.c
new file mode 100644
index 00000000..f421c37d
--- /dev/null
+++ b/toxcore/net_crypto.c
@@ -0,0 +1,771 @@
1/* net_crypto.c
2 *
3 * Functions for the core network crypto.
4 * See also: http://wiki.tox.im/index.php/DHT
5 *
6 * NOTE: This code has to be perfect. We don't mess around with encryption.
7 *
8 * Copyright (C) 2013 Tox project All Rights Reserved.
9 *
10 * This file is part of Tox.
11 *
12 * Tox is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation, either version 3 of the License, or
15 * (at your option) any later version.
16 *
17 * Tox is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with Tox. If not, see <http://www.gnu.org/licenses/>.
24 *
25 */
26
27#include "net_crypto.h"
28
29#define CONN_NO_CONNECTION 0
30#define CONN_HANDSHAKE_SENT 1
31#define CONN_NOT_CONFIRMED 2
32#define CONN_ESTABLISHED 3
33#define CONN_TIMED_OUT 4
34
35/* Use this instead of memcmp; not vulnerable to timing attacks. */
36uint8_t crypto_iszero(uint8_t *mem, uint32_t length)
37{
38 uint8_t check = 0;
39 uint32_t i;
40
41 for (i = 0; i < length; ++i) {
42 check |= mem[i];
43 }
44
45 return check; // We return zero if mem is made out of zeroes.
46}
47
48/* Precomputes the shared key from their public_key and our secret_key.
49 This way we can avoid an expensive elliptic curve scalar multiply for each
50 encrypt/decrypt operation.
51 enc_key has to be crypto_box_BEFORENMBYTES bytes long. */
52void encrypt_precompute(uint8_t *public_key, uint8_t *secret_key, uint8_t *enc_key)
53{
54 crypto_box_beforenm(enc_key, public_key, secret_key);
55}
56
57/* Fast encrypt. Depends on enc_key from encrypt_precompute. */
58int encrypt_data_fast(uint8_t *enc_key, uint8_t *nonce,
59 uint8_t *plain, uint32_t length, uint8_t *encrypted)
60{
61 if (length + crypto_box_MACBYTES > MAX_DATA_SIZE || length == 0)
62 return -1;
63
64 uint8_t temp_plain[MAX_DATA_SIZE + crypto_box_ZEROBYTES] = {0};
65 uint8_t temp_encrypted[MAX_DATA_SIZE + crypto_box_BOXZEROBYTES];
66
67 memcpy(temp_plain + crypto_box_ZEROBYTES, plain, length); /* pad the message with 32 0 bytes. */
68
69 crypto_box_afternm(temp_encrypted, temp_plain, length + crypto_box_ZEROBYTES, nonce, enc_key);
70
71 if (crypto_iszero(temp_encrypted, crypto_box_BOXZEROBYTES) != 0)
72 return -1;
73
74 /* unpad the encrypted message */
75 memcpy(encrypted, temp_encrypted + crypto_box_BOXZEROBYTES, length + crypto_box_MACBYTES);
76 return length - crypto_box_BOXZEROBYTES + crypto_box_ZEROBYTES;
77}
78
79/* Fast decrypt. Depends on enc_ley from encrypt_precompute. */
80int decrypt_data_fast(uint8_t *enc_key, uint8_t *nonce,
81 uint8_t *encrypted, uint32_t length, uint8_t *plain)
82{
83 if (length > MAX_DATA_SIZE || length <= crypto_box_BOXZEROBYTES)
84 return -1;
85
86 uint8_t temp_plain[MAX_DATA_SIZE + crypto_box_ZEROBYTES];
87 uint8_t temp_encrypted[MAX_DATA_SIZE + crypto_box_BOXZEROBYTES] = {0};
88
89 memcpy(temp_encrypted + crypto_box_BOXZEROBYTES, encrypted, length); /* pad the message with 16 0 bytes. */
90
91 if (crypto_box_open_afternm(temp_plain, temp_encrypted, length + crypto_box_BOXZEROBYTES,
92 nonce, enc_key) == -1)
93 return -1;
94
95 /* if decryption is successful the first crypto_box_ZEROBYTES of the message will be zero
96 apparently memcmp should not be used so we do this instead:*/
97 if (crypto_iszero(temp_plain, crypto_box_ZEROBYTES) != 0)
98 return -1;
99
100 /* unpad the plain message */
101 memcpy(plain, temp_plain + crypto_box_ZEROBYTES, length - crypto_box_MACBYTES);
102 return length - crypto_box_ZEROBYTES + crypto_box_BOXZEROBYTES;
103}
104
105int encrypt_data(uint8_t *public_key, uint8_t *secret_key, uint8_t *nonce,
106 uint8_t *plain, uint32_t length, uint8_t *encrypted)
107{
108 uint8_t k[crypto_box_BEFORENMBYTES];
109 encrypt_precompute(public_key, secret_key, k);
110 return encrypt_data_fast(k, nonce, plain, length, encrypted);
111}
112
113int decrypt_data(uint8_t *public_key, uint8_t *secret_key, uint8_t *nonce,
114 uint8_t *encrypted, uint32_t length, uint8_t *plain)
115{
116 uint8_t k[crypto_box_BEFORENMBYTES];
117 encrypt_precompute(public_key, secret_key, k);
118 return decrypt_data_fast(k, nonce, encrypted, length, plain);
119}
120
121/* increment the given nonce by 1 */
122static void increment_nonce(uint8_t *nonce)
123{
124 uint32_t i;
125
126 for (i = 0; i < crypto_box_NONCEBYTES; ++i) {
127 ++nonce[i];
128
129 if (nonce[i] != 0)
130 break;
131 }
132}
133
134/* fill the given nonce with random bytes. */
135void random_nonce(uint8_t *nonce)
136{
137 uint32_t i, temp;
138
139 for (i = 0; i < crypto_box_NONCEBYTES / 4; ++i) {
140 temp = random_int();
141 memcpy(nonce + 4 * i, &temp, 4);
142 }
143}
144
145/* return 0 if there is no received data in the buffer
146 return -1 if the packet was discarded.
147 return length of received data if successful */
148int read_cryptpacket(Net_Crypto *c, int crypt_connection_id, uint8_t *data)
149{
150 if (crypt_connection_id < 0 || crypt_connection_id >= c->crypto_connections_length)
151 return 0;
152
153 if (c->crypto_connections[crypt_connection_id].status != CONN_ESTABLISHED)
154 return 0;
155
156 uint8_t temp_data[MAX_DATA_SIZE];
157 int length = read_packet(c->lossless_udp, c->crypto_connections[crypt_connection_id].number, temp_data);
158
159 if (length == 0)
160 return 0;
161
162 if (temp_data[0] != 3)
163 return -1;
164
165 int len = decrypt_data_fast(c->crypto_connections[crypt_connection_id].shared_key,
166 c->crypto_connections[crypt_connection_id].recv_nonce,
167 temp_data + 1, length - 1, data);
168
169 if (len != -1) {
170 increment_nonce(c->crypto_connections[crypt_connection_id].recv_nonce);
171 return len;
172 }
173
174 return -1;
175}
176
177/* return 0 if data could not be put in packet queue
178 return 1 if data was put into the queue */
179int write_cryptpacket(Net_Crypto *c, int crypt_connection_id, uint8_t *data, uint32_t length)
180{
181 if (crypt_connection_id < 0 || crypt_connection_id >= c->crypto_connections_length)
182 return 0;
183
184 if (length - crypto_box_BOXZEROBYTES + crypto_box_ZEROBYTES > MAX_DATA_SIZE - 1)
185 return 0;
186
187 if (c->crypto_connections[crypt_connection_id].status != CONN_ESTABLISHED)
188 return 0;
189
190 uint8_t temp_data[MAX_DATA_SIZE];
191 int len = encrypt_data_fast(c->crypto_connections[crypt_connection_id].shared_key,
192 c->crypto_connections[crypt_connection_id].sent_nonce,
193 data, length, temp_data + 1);
194
195 if (len == -1)
196 return 0;
197
198 temp_data[0] = 3;
199
200 if (write_packet(c->lossless_udp, c->crypto_connections[crypt_connection_id].number, temp_data, len + 1) == 0)
201 return 0;
202
203 increment_nonce(c->crypto_connections[crypt_connection_id].sent_nonce);
204 return 1;
205}
206
207/* create a request to peer.
208 send_public_key and send_secret_key are the pub/secret keys of the sender
209 recv_public_key is public key of reciever
210 packet must be an array of MAX_DATA_SIZE big.
211 Data represents the data we send with the request with length being the length of the data.
212 request_id is the id of the request (32 = friend request, 254 = ping request)
213 returns -1 on failure
214 returns the length of the created packet on success */
215int create_request(uint8_t *send_public_key, uint8_t *send_secret_key, uint8_t *packet, uint8_t *recv_public_key,
216 uint8_t *data, uint32_t length, uint8_t request_id)
217{
218 if (MAX_DATA_SIZE < length + 1 + crypto_box_PUBLICKEYBYTES * 2 + crypto_box_NONCEBYTES + 1 + ENCRYPTION_PADDING)
219 return -1;
220
221 uint8_t nonce[crypto_box_NONCEBYTES];
222 uint8_t temp[MAX_DATA_SIZE];
223 memcpy(temp + 1, data, length);
224 temp[0] = request_id;
225 random_nonce(nonce);
226 int len = encrypt_data(recv_public_key, send_secret_key, nonce, temp, length + 1,
227 1 + crypto_box_PUBLICKEYBYTES * 2 + crypto_box_NONCEBYTES + packet);
228
229 if (len == -1)
230 return -1;
231
232 packet[0] = NET_PACKET_CRYPTO;
233 memcpy(packet + 1, recv_public_key, crypto_box_PUBLICKEYBYTES);
234 memcpy(packet + 1 + crypto_box_PUBLICKEYBYTES, send_public_key, crypto_box_PUBLICKEYBYTES);
235 memcpy(packet + 1 + crypto_box_PUBLICKEYBYTES * 2, nonce, crypto_box_NONCEBYTES);
236
237 return len + 1 + crypto_box_PUBLICKEYBYTES * 2 + crypto_box_NONCEBYTES;
238}
239
240/* puts the senders public key in the request in public_key, the data from the request
241 in data if a friend or ping request was sent to us and returns the length of the data.
242 packet is the request packet and length is its length
243 return -1 if not valid request. */
244static int handle_request(Net_Crypto *c, uint8_t *public_key, uint8_t *data, uint8_t *request_id, uint8_t *packet,
245 uint16_t length)
246{
247
248 if (length > crypto_box_PUBLICKEYBYTES * 2 + crypto_box_NONCEBYTES + 1 + ENCRYPTION_PADDING &&
249 length <= MAX_DATA_SIZE + ENCRYPTION_PADDING &&
250 memcmp(packet + 1, c->self_public_key, crypto_box_PUBLICKEYBYTES) == 0) {
251 memcpy(public_key, packet + 1 + crypto_box_PUBLICKEYBYTES, crypto_box_PUBLICKEYBYTES);
252 uint8_t nonce[crypto_box_NONCEBYTES];
253 uint8_t temp[MAX_DATA_SIZE];
254 memcpy(nonce, packet + 1 + crypto_box_PUBLICKEYBYTES * 2, crypto_box_NONCEBYTES);
255 int len1 = decrypt_data(public_key, c->self_secret_key, nonce,
256 packet + 1 + crypto_box_PUBLICKEYBYTES * 2 + crypto_box_NONCEBYTES,
257 length - (crypto_box_PUBLICKEYBYTES * 2 + crypto_box_NONCEBYTES + 1), temp);
258
259 if (len1 == -1 || len1 == 0)
260 return -1;
261
262 request_id[0] = temp[0];
263 --len1;
264 memcpy(data, temp + 1, len1);
265 return len1;
266 } else
267 return -1;
268}
269
270void cryptopacket_registerhandler(Net_Crypto *c, uint8_t byte, cryptopacket_handler_callback cb, void *object)
271{
272 c->cryptopackethandlers[byte].function = cb;
273 c->cryptopackethandlers[byte].object = object;
274}
275
276static int cryptopacket_handle(void *object, IP_Port source, uint8_t *packet, uint32_t length)
277{
278 DHT *dht = object;
279
280 if (packet[0] == NET_PACKET_CRYPTO) {
281 if (length <= crypto_box_PUBLICKEYBYTES * 2 + crypto_box_NONCEBYTES + 1 + ENCRYPTION_PADDING ||
282 length > MAX_DATA_SIZE + ENCRYPTION_PADDING)
283 return 1;
284
285 if (memcmp(packet + 1, dht->c->self_public_key, crypto_box_PUBLICKEYBYTES) == 0) {// check if request is for us.
286 uint8_t public_key[crypto_box_PUBLICKEYBYTES];
287 uint8_t data[MAX_DATA_SIZE];
288 uint8_t number;
289 int len = handle_request(dht->c, public_key, data, &number, packet, length);
290
291 if (len == -1 || len == 0)
292 return 1;
293
294 if (!dht->c->cryptopackethandlers[number].function) return 1;
295
296 dht->c->cryptopackethandlers[number].function(dht->c->cryptopackethandlers[number].object, source, public_key, data,
297 len);
298
299 } else { /* if request is not for us, try routing it. */
300 if (route_packet(dht, packet + 1, packet, length) == length) //NOTE
301 return 0;
302 }
303 }
304
305 return 1;
306}
307
308/* Send a crypto handshake packet containing an encrypted secret nonce and session public key
309 to peer with connection_id and public_key
310 the packet is encrypted with a random nonce which is sent in plain text with the packet */
311static int send_cryptohandshake(Net_Crypto *c, int connection_id, uint8_t *public_key, uint8_t *secret_nonce,
312 uint8_t *session_key)
313{
314 uint8_t temp_data[MAX_DATA_SIZE];
315 uint8_t temp[crypto_box_NONCEBYTES + crypto_box_PUBLICKEYBYTES];
316 uint8_t nonce[crypto_box_NONCEBYTES];
317
318 random_nonce(nonce);
319 memcpy(temp, secret_nonce, crypto_box_NONCEBYTES);
320 memcpy(temp + crypto_box_NONCEBYTES, session_key, crypto_box_PUBLICKEYBYTES);
321
322 int len = encrypt_data(public_key, c->self_secret_key, nonce, temp, crypto_box_NONCEBYTES + crypto_box_PUBLICKEYBYTES,
323 1 + crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES + temp_data);
324
325 if (len == -1)
326 return 0;
327
328 temp_data[0] = 2;
329 memcpy(temp_data + 1, c->self_public_key, crypto_box_PUBLICKEYBYTES);
330 memcpy(temp_data + 1 + crypto_box_PUBLICKEYBYTES, nonce, crypto_box_NONCEBYTES);
331 return write_packet(c->lossless_udp, connection_id, temp_data,
332 len + 1 + crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES);
333}
334
335/* Extract secret nonce, session public key and public_key from a packet(data) with length length
336 return 1 if successful
337 return 0 if failure */
338static int handle_cryptohandshake(Net_Crypto *c, uint8_t *public_key, uint8_t *secret_nonce,
339 uint8_t *session_key, uint8_t *data, uint16_t length)
340{
341 int pad = (- crypto_box_BOXZEROBYTES + crypto_box_ZEROBYTES);
342
343 if (length != 1 + crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES
344 + crypto_box_NONCEBYTES + crypto_box_PUBLICKEYBYTES + pad) {
345 return 0;
346 }
347
348 if (data[0] != 2)
349 return 0;
350
351 uint8_t temp[crypto_box_NONCEBYTES + crypto_box_PUBLICKEYBYTES];
352
353 memcpy(public_key, data + 1, crypto_box_PUBLICKEYBYTES);
354
355 int len = decrypt_data(public_key, c->self_secret_key, data + 1 + crypto_box_PUBLICKEYBYTES,
356 data + 1 + crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES,
357 crypto_box_NONCEBYTES + crypto_box_PUBLICKEYBYTES + pad, temp);
358
359 if (len != crypto_box_NONCEBYTES + crypto_box_PUBLICKEYBYTES)
360 return 0;
361
362 memcpy(secret_nonce, temp, crypto_box_NONCEBYTES);
363 memcpy(session_key, temp + crypto_box_NONCEBYTES, crypto_box_PUBLICKEYBYTES);
364 return 1;
365}
366
367/* get crypto connection id from public key of peer
368 return -1 if there are no connections like we are looking for
369 return id if it found it */
370static int getcryptconnection_id(Net_Crypto *c, uint8_t *public_key)
371{
372 uint32_t i;
373
374 for (i = 0; i < c->crypto_connections_length; ++i) {
375 if (c->crypto_connections[i].status != CONN_NO_CONNECTION)
376 if (memcmp(public_key, c->crypto_connections[i].public_key, crypto_box_PUBLICKEYBYTES) == 0)
377 return i;
378 }
379
380 return -1;
381}
382
383/* set the size of the friend list to numfriends
384 return -1 if realloc fails */
385int realloc_cryptoconnection(Net_Crypto *c, uint32_t num)
386{
387 if (num == 0) {
388 free(c->crypto_connections);
389 c->crypto_connections = NULL;
390 return 0;
391 }
392
393 Crypto_Connection *newcrypto_connections = realloc(c->crypto_connections, num * sizeof(Crypto_Connection));
394
395 if (newcrypto_connections == NULL)
396 return -1;
397
398 c->crypto_connections = newcrypto_connections;
399 return 0;
400}
401
402/* Start a secure connection with other peer who has public_key and ip_port
403 returns -1 if failure
404 returns crypt_connection_id of the initialized connection if everything went well. */
405int crypto_connect(Net_Crypto *c, uint8_t *public_key, IP_Port ip_port)
406{
407 uint32_t i;
408 int id = getcryptconnection_id(c, public_key);
409
410 if (id != -1) {
411 IP_Port c_ip = connection_ip(c->lossless_udp, c->crypto_connections[id].number);
412
413 if (c_ip.ip.i == ip_port.ip.i && c_ip.port == ip_port.port)
414 return -1;
415 }
416
417 if (realloc_cryptoconnection(c, c->crypto_connections_length + 1) == -1)
418 return -1;
419
420 memset(&(c->crypto_connections[c->crypto_connections_length]), 0, sizeof(Crypto_Connection));
421 c->crypto_connections[c->crypto_connections_length].number = ~0;
422
423 for (i = 0; i <= c->crypto_connections_length; ++i) {
424 if (c->crypto_connections[i].status == CONN_NO_CONNECTION) {
425 int id = new_connection(c->lossless_udp, ip_port);
426
427 if (id == -1)
428 return -1;
429
430 c->crypto_connections[i].number = id;
431 c->crypto_connections[i].status = CONN_HANDSHAKE_SENT;
432 random_nonce(c->crypto_connections[i].recv_nonce);
433 memcpy(c->crypto_connections[i].public_key, public_key, crypto_box_PUBLICKEYBYTES);
434 crypto_box_keypair(c->crypto_connections[i].sessionpublic_key, c->crypto_connections[i].sessionsecret_key);
435
436 if (c->crypto_connections_length == i)
437 ++c->crypto_connections_length;
438
439 if (send_cryptohandshake(c, id, public_key, c->crypto_connections[i].recv_nonce,
440 c->crypto_connections[i].sessionpublic_key) == 1) {
441 increment_nonce(c->crypto_connections[i].recv_nonce);
442 return i;
443 }
444
445 return -1; /* this should never happen. */
446 }
447 }
448
449 return -1;
450}
451
452/* handle an incoming connection
453 return -1 if no crypto inbound connection
454 return incoming connection id (Lossless_UDP one) if there is an incoming crypto connection
455 Put the public key of the peer in public_key, the secret_nonce from the handshake into secret_nonce
456 and the session public key for the connection in session_key
457 to accept it see: accept_crypto_inbound(...)
458 to refuse it just call kill_connection(...) on the connection id */
459int crypto_inbound(Net_Crypto *c, uint8_t *public_key, uint8_t *secret_nonce, uint8_t *session_key)
460{
461 uint32_t i;
462
463 for (i = 0; i < MAX_INCOMING; ++i) {
464 if (c->incoming_connections[i] != -1) {
465 if (is_connected(c->lossless_udp, c->incoming_connections[i]) == 4
466 || is_connected(c->lossless_udp, c->incoming_connections[i]) == 0) {
467 kill_connection(c->lossless_udp, c->incoming_connections[i]);
468 c->incoming_connections[i] = -1;
469 continue;
470 }
471
472 if (id_packet(c->lossless_udp, c->incoming_connections[i]) == 2) {
473 uint8_t temp_data[MAX_DATA_SIZE];
474 uint16_t len = read_packet(c->lossless_udp, c->incoming_connections[i], temp_data);
475
476 if (handle_cryptohandshake(c, public_key, secret_nonce, session_key, temp_data, len)) {
477 int connection_id = c->incoming_connections[i];
478 c->incoming_connections[i] = -1; /* remove this connection from the incoming connection list. */
479 return connection_id;
480 }
481 }
482 }
483 }
484
485 return -1;
486}
487
488/* kill a crypto connection
489 return 0 if killed successfully
490 return 1 if there was a problem. */
491int crypto_kill(Net_Crypto *c, int crypt_connection_id)
492{
493 if (crypt_connection_id < 0 || crypt_connection_id >= c->crypto_connections_length)
494 return 1;
495
496 if (c->crypto_connections[crypt_connection_id].status != CONN_NO_CONNECTION) {
497 c->crypto_connections[crypt_connection_id].status = CONN_NO_CONNECTION;
498 kill_connection(c->lossless_udp, c->crypto_connections[crypt_connection_id].number);
499 memset(&(c->crypto_connections[crypt_connection_id]), 0 , sizeof(Crypto_Connection));
500 c->crypto_connections[crypt_connection_id].number = ~0;
501 uint32_t i;
502
503 for (i = c->crypto_connections_length; i != 0; --i) {
504 if (c->crypto_connections[i - 1].status != CONN_NO_CONNECTION)
505 break;
506 }
507
508 c->crypto_connections_length = i;
509 realloc_cryptoconnection(c, c->crypto_connections_length);
510 return 0;
511 }
512
513 return 1;
514}
515
516/* accept an incoming connection using the parameters provided by crypto_inbound
517 return -1 if not successful
518 returns the crypt_connection_id if successful */
519int accept_crypto_inbound(Net_Crypto *c, int connection_id, uint8_t *public_key, uint8_t *secret_nonce,
520 uint8_t *session_key)
521{
522 uint32_t i;
523
524 if (connection_id == -1)
525 return -1;
526
527 /*
528 if(getcryptconnection_id(public_key) != -1)
529 {
530 return -1;
531 }*/
532 if (realloc_cryptoconnection(c, c->crypto_connections_length + 1) == -1)
533 return -1;
534
535 memset(&(c->crypto_connections[c->crypto_connections_length]), 0, sizeof(Crypto_Connection));
536 c->crypto_connections[c->crypto_connections_length].number = ~0;
537
538 for (i = 0; i <= c->crypto_connections_length; ++i) {
539 if (c->crypto_connections[i].status == CONN_NO_CONNECTION) {
540 c->crypto_connections[i].number = connection_id;
541 c->crypto_connections[i].status = CONN_NOT_CONFIRMED;
542 random_nonce(c->crypto_connections[i].recv_nonce);
543 memcpy(c->crypto_connections[i].sent_nonce, secret_nonce, crypto_box_NONCEBYTES);
544 memcpy(c->crypto_connections[i].peersessionpublic_key, session_key, crypto_box_PUBLICKEYBYTES);
545 increment_nonce(c->crypto_connections[i].sent_nonce);
546 memcpy(c->crypto_connections[i].public_key, public_key, crypto_box_PUBLICKEYBYTES);
547
548 crypto_box_keypair(c->crypto_connections[i].sessionpublic_key, c->crypto_connections[i].sessionsecret_key);
549
550 if (c->crypto_connections_length == i)
551 ++c->crypto_connections_length;
552
553 if (send_cryptohandshake(c, connection_id, public_key, c->crypto_connections[i].recv_nonce,
554 c->crypto_connections[i].sessionpublic_key) == 1) {
555 increment_nonce(c->crypto_connections[i].recv_nonce);
556 uint32_t zero = 0;
557 encrypt_precompute(c->crypto_connections[i].peersessionpublic_key,
558 c->crypto_connections[i].sessionsecret_key,
559 c->crypto_connections[i].shared_key);
560 c->crypto_connections[i].status =
561 CONN_ESTABLISHED; /* connection status needs to be 3 for write_cryptpacket() to work */
562 write_cryptpacket(c, i, ((uint8_t *)&zero), sizeof(zero));
563 c->crypto_connections[i].status = CONN_NOT_CONFIRMED; /* set it to its proper value right after. */
564 return i;
565 }
566
567 return -1; /* this should never happen. */
568 }
569 }
570
571 return -1;
572}
573
574/* return 0 if no connection, 1 we have sent a handshake, 2 if connection is not confirmed yet
575 (we have received a handshake but no empty data packet), 3 if the connection is established.
576 4 if the connection is timed out and waiting to be killed */
577int is_cryptoconnected(Net_Crypto *c, int crypt_connection_id)
578{
579 if (crypt_connection_id >= 0 && crypt_connection_id < c->crypto_connections_length)
580 return c->crypto_connections[crypt_connection_id].status;
581
582 return CONN_NO_CONNECTION;
583}
584
585void new_keys(Net_Crypto *c)
586{
587 crypto_box_keypair(c->self_public_key, c->self_secret_key);
588}
589
590/* save the public and private keys to the keys array
591 Length must be crypto_box_PUBLICKEYBYTES + crypto_box_SECRETKEYBYTES */
592void save_keys(Net_Crypto *c, uint8_t *keys)
593{
594 memcpy(keys, c->self_public_key, crypto_box_PUBLICKEYBYTES);
595 memcpy(keys + crypto_box_PUBLICKEYBYTES, c->self_secret_key, crypto_box_SECRETKEYBYTES);
596}
597
598/* load the public and private keys from the keys array
599 Length must be crypto_box_PUBLICKEYBYTES + crypto_box_SECRETKEYBYTES */
600void load_keys(Net_Crypto *c, uint8_t *keys)
601{
602 memcpy(c->self_public_key, keys, crypto_box_PUBLICKEYBYTES);
603 memcpy(c->self_secret_key, keys + crypto_box_PUBLICKEYBYTES, crypto_box_SECRETKEYBYTES);
604}
605
606/* TODO: optimize this
607 adds an incoming connection to the incoming_connection list.
608 returns 0 if successful
609 returns 1 if failure */
610static int new_incoming(Net_Crypto *c, int id)
611{
612 uint32_t i;
613
614 for (i = 0; i < MAX_INCOMING; ++i) {
615 if (c->incoming_connections[i] == -1) {
616 c->incoming_connections[i] = id;
617 return 0;
618 }
619 }
620
621 return 1;
622}
623
624/* TODO: optimize this
625 handle all new incoming connections. */
626static void handle_incomings(Net_Crypto *c)
627{
628 int income;
629
630 while (1) {
631 income = incoming_connection(c->lossless_udp);
632
633 if (income == -1 || new_incoming(c, income) )
634 break;
635 }
636}
637
638/* handle received packets for not yet established crypto connections. */
639static void receive_crypto(Net_Crypto *c)
640{
641 uint32_t i;
642
643 for (i = 0; i < c->crypto_connections_length; ++i) {
644 if (c->crypto_connections[i].status == CONN_HANDSHAKE_SENT) {
645 uint8_t temp_data[MAX_DATA_SIZE];
646 uint8_t secret_nonce[crypto_box_NONCEBYTES];
647 uint8_t public_key[crypto_box_PUBLICKEYBYTES];
648 uint8_t session_key[crypto_box_PUBLICKEYBYTES];
649 uint16_t len;
650
651 if (id_packet(c->lossless_udp, c->crypto_connections[i].number) == 2) { /* handle handshake packet. */
652 len = read_packet(c->lossless_udp, c->crypto_connections[i].number, temp_data);
653
654 if (handle_cryptohandshake(c, public_key, secret_nonce, session_key, temp_data, len)) {
655 if (memcmp(public_key, c->crypto_connections[i].public_key, crypto_box_PUBLICKEYBYTES) == 0) {
656 memcpy(c->crypto_connections[i].sent_nonce, secret_nonce, crypto_box_NONCEBYTES);
657 memcpy(c->crypto_connections[i].peersessionpublic_key, session_key, crypto_box_PUBLICKEYBYTES);
658 increment_nonce(c->crypto_connections[i].sent_nonce);
659 uint32_t zero = 0;
660 encrypt_precompute(c->crypto_connections[i].peersessionpublic_key,
661 c->crypto_connections[i].sessionsecret_key,
662 c->crypto_connections[i].shared_key);
663 c->crypto_connections[i].status =
664 CONN_ESTABLISHED; /* connection status needs to be 3 for write_cryptpacket() to work */
665 write_cryptpacket(c, i, ((uint8_t *)&zero), sizeof(zero));
666 c->crypto_connections[i].status = CONN_NOT_CONFIRMED; /* set it to its proper value right after. */
667 }
668 }
669 } else if (id_packet(c->lossless_udp,
670 c->crypto_connections[i].number) != -1) { // This should not happen kill the connection if it does
671 crypto_kill(c, i);
672 return;
673 }
674 }
675
676 if (c->crypto_connections[i].status == CONN_NOT_CONFIRMED) {
677 if (id_packet(c->lossless_udp, c->crypto_connections[i].number) == 3) {
678 uint8_t temp_data[MAX_DATA_SIZE];
679 uint8_t data[MAX_DATA_SIZE];
680 int length = read_packet(c->lossless_udp, c->crypto_connections[i].number, temp_data);
681 int len = decrypt_data(c->crypto_connections[i].peersessionpublic_key,
682 c->crypto_connections[i].sessionsecret_key,
683 c->crypto_connections[i].recv_nonce, temp_data + 1, length - 1, data);
684 uint32_t zero = 0;
685
686 if (len == sizeof(uint32_t) && memcmp(((uint8_t *)&zero), data, sizeof(uint32_t)) == 0) {
687 increment_nonce(c->crypto_connections[i].recv_nonce);
688 encrypt_precompute(c->crypto_connections[i].peersessionpublic_key,
689 c->crypto_connections[i].sessionsecret_key,
690 c->crypto_connections[i].shared_key);
691 c->crypto_connections[i].status = CONN_ESTABLISHED;
692
693 /* connection is accepted so we disable the auto kill by setting it to about 1 month from now. */
694 kill_connection_in(c->lossless_udp, c->crypto_connections[i].number, 3000000);
695 } else {
696 crypto_kill(c, i); // This should not happen kill the connection if it does
697 return;
698 }
699 } else if (id_packet(c->lossless_udp, c->crypto_connections[i].number) != -1)
700 /* This should not happen
701 kill the connection if it does */
702 crypto_kill(c, i);
703
704 return;
705 }
706 }
707}
708
709/* run this to (re)initialize net_crypto
710 sets all the global connection variables to their default values. */
711Net_Crypto *new_net_crypto(Networking_Core *net)
712{
713 if (net == NULL)
714 return NULL;
715
716 Net_Crypto *temp = calloc(1, sizeof(Net_Crypto));
717
718 if (temp == NULL)
719 return NULL;
720
721 temp->lossless_udp = new_lossless_udp(net);
722
723 if (temp->lossless_udp == NULL)
724 return NULL;
725
726 memset(temp->incoming_connections, -1 , sizeof(int) * MAX_INCOMING);
727 return temp;
728}
729
730void init_cryptopackets(void *dht)
731{
732 DHT *s_dht = dht;
733 networking_registerhandler(s_dht->c->lossless_udp->net, NET_PACKET_CRYPTO, &cryptopacket_handle, s_dht);
734}
735
736static void kill_timedout(Net_Crypto *c)
737{
738 uint32_t i;
739
740 for (i = 0; i < c->crypto_connections_length; ++i) {
741 if (c->crypto_connections[i].status != CONN_NO_CONNECTION
742 && is_connected(c->lossless_udp, c->crypto_connections[i].number) == 4)
743 c->crypto_connections[i].status = CONN_TIMED_OUT;
744 else if (is_connected(c->lossless_udp, c->crypto_connections[i].number) == 4) {
745 kill_connection(c->lossless_udp, c->crypto_connections[i].number);
746 c->crypto_connections[i].number = ~0;
747 }
748 }
749}
750
751/* main loop */
752void do_net_crypto(Net_Crypto *c)
753{
754 do_lossless_udp(c->lossless_udp);
755 handle_incomings(c);
756 receive_crypto(c);
757 kill_timedout(c);
758}
759
760void kill_net_crypto(Net_Crypto *c)
761{
762 uint32_t i;
763
764 for (i = 0; i < c->crypto_connections_length; ++i) {
765 crypto_kill(c, i);
766 }
767
768 kill_lossless_udp(c->lossless_udp);
769 memset(c, 0, sizeof(Net_Crypto));
770 free(c);
771}
diff --git a/toxcore/net_crypto.h b/toxcore/net_crypto.h
new file mode 100644
index 00000000..81670993
--- /dev/null
+++ b/toxcore/net_crypto.h
@@ -0,0 +1,200 @@
1/* net_crypto.h
2 *
3 * Functions for the core network crypto.
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#ifndef NET_CRYPTO_H
25#define NET_CRYPTO_H
26
27#include "Lossless_UDP.h"
28
29#ifdef __cplusplus
30extern "C" {
31#endif
32
33#define MAX_INCOMING 64
34
35#define CRYPTO_PACKET_FRIEND_REQ 32 /* Friend request crypto packet ID */
36#define CRYPTO_PACKET_NAT_PING 254 /* NAT ping crypto packet ID */
37
38typedef struct {
39 uint8_t public_key[crypto_box_PUBLICKEYBYTES]; /* the real public key of the peer. */
40 uint8_t recv_nonce[crypto_box_NONCEBYTES]; /* nonce of received packets */
41 uint8_t sent_nonce[crypto_box_NONCEBYTES]; /* nonce of sent packets. */
42 uint8_t sessionpublic_key[crypto_box_PUBLICKEYBYTES]; /* our public key for this session. */
43 uint8_t sessionsecret_key[crypto_box_SECRETKEYBYTES]; /* our private key for this session. */
44 uint8_t peersessionpublic_key[crypto_box_PUBLICKEYBYTES]; /* The public key of the peer. */
45 uint8_t shared_key[crypto_box_BEFORENMBYTES]; /* the precomputed shared key from encrypt_precompute */
46 uint8_t status; /* 0 if no connection, 1 we have sent a handshake, 2 if connexion is not confirmed yet
47 (we have received a handshake but no empty data packet), 3 if the connection is established.
48 4 if the connection is timed out. */
49 uint16_t number; /* Lossless_UDP connection number corresponding to this connection. */
50
51} Crypto_Connection;
52
53typedef int (*cryptopacket_handler_callback)(void *object, IP_Port ip_port, uint8_t *source_pubkey, uint8_t *data,
54 uint32_t len);
55
56typedef struct {
57 cryptopacket_handler_callback function;
58 void *object;
59} Cryptopacket_Handles;
60
61typedef struct {
62 Lossless_UDP *lossless_udp;
63
64 Crypto_Connection *crypto_connections;
65
66 uint32_t crypto_connections_length; /* Length of connections array */
67
68 /* Our public and secret keys. */
69 uint8_t self_public_key[crypto_box_PUBLICKEYBYTES];
70 uint8_t self_secret_key[crypto_box_SECRETKEYBYTES];
71
72 /* keeps track of the connection numbers for friends request so we can check later if they were sent */
73 int incoming_connections[MAX_INCOMING];
74
75 Cryptopacket_Handles cryptopackethandlers[256];
76} Net_Crypto;
77
78#include "DHT.h"
79
80#define ENCRYPTION_PADDING (crypto_box_ZEROBYTES - crypto_box_BOXZEROBYTES)
81
82/* returns zero if the buffer contains only zeros */
83uint8_t crypto_iszero(uint8_t *buffer, uint32_t blen);
84
85/* encrypts plain of length length to encrypted of length + 16 using the
86 public key(32 bytes) of the receiver and the secret key of the sender and a 24 byte nonce
87 return -1 if there was a problem.
88 return length of encrypted data if everything was fine. */
89int encrypt_data(uint8_t *public_key, uint8_t *secret_key, uint8_t *nonce,
90 uint8_t *plain, uint32_t length, uint8_t *encrypted);
91
92
93/* decrypts encrypted of length length to plain of length length - 16 using the
94 public key(32 bytes) of the sender, the secret key of the receiver and a 24 byte nonce
95 return -1 if there was a problem(decryption failed)
96 return length of plain data if everything was fine. */
97int decrypt_data(uint8_t *public_key, uint8_t *secret_key, uint8_t *nonce,
98 uint8_t *encrypted, uint32_t length, uint8_t *plain);
99
100/* Fast encrypt/decrypt operations. Use if this is not a one-time communication.
101 encrypt_precompute does the shared-key generation once so it does not have
102 to be preformed on every encrypt/decrypt. */
103void encrypt_precompute(uint8_t *public_key, uint8_t *secret_key, uint8_t *enc_key);
104
105/* Fast encrypt. Depends on enc_key from encrypt_precompute. */
106int encrypt_data_fast(uint8_t *enc_key, uint8_t *nonce,
107 uint8_t *plain, uint32_t length, uint8_t *encrypted);
108
109/* Fast decrypt. Depends on enc_ley from encrypt_precompute. */
110int decrypt_data_fast(uint8_t *enc_key, uint8_t *nonce,
111 uint8_t *encrypted, uint32_t length, uint8_t *plain);
112
113
114/* fill the given nonce with random bytes. */
115void random_nonce(uint8_t *nonce);
116
117/* return 0 if there is no received data in the buffer
118 return -1 if the packet was discarded.
119 return length of received data if successful */
120int read_cryptpacket(Net_Crypto *c, int crypt_connection_id, uint8_t *data);
121
122/* return 0 if data could not be put in packet queue
123 return 1 if data was put into the queue */
124int write_cryptpacket(Net_Crypto *c, int crypt_connection_id, uint8_t *data, uint32_t length);
125
126/* create a request to peer.
127 send_public_key and send_secret_key are the pub/secret keys of the sender
128 recv_public_key is public key of reciever
129 packet must be an array of MAX_DATA_SIZE big.
130 Data represents the data we send with the request with length being the length of the data.
131 request_id is the id of the request (32 = friend request, 254 = ping request)
132 returns -1 on failure
133 returns the length of the created packet on success */
134int create_request(uint8_t *send_public_key, uint8_t *send_secret_key, uint8_t *packet, uint8_t *recv_public_key,
135 uint8_t *data, uint32_t length, uint8_t request_id);
136
137
138/* Function to call when request beginning with byte is received */
139void cryptopacket_registerhandler(Net_Crypto *c, uint8_t byte, cryptopacket_handler_callback cb, void *object);
140
141/* Start a secure connection with other peer who has public_key and ip_port
142 returns -1 if failure
143 returns crypt_connection_id of the initialized connection if everything went well. */
144int crypto_connect(Net_Crypto *c, uint8_t *public_key, IP_Port ip_port);
145
146/* kill a crypto connection
147 return 0 if killed successfully
148 return 1 if there was a problem. */
149int crypto_kill(Net_Crypto *c, int crypt_connection_id);
150
151/* handle an incoming connection
152 return -1 if no crypto inbound connection
153 return incoming connection id (Lossless_UDP one) if there is an incoming crypto connection
154 Put the public key of the peer in public_key, the secret_nonce from the handshake into secret_nonce
155 and the session public key for the connection in session_key
156 to accept it see: accept_crypto_inbound(...)
157 to refuse it just call kill_connection(...) on the connection id */
158int crypto_inbound(Net_Crypto *c, uint8_t *public_key, uint8_t *secret_nonce, uint8_t *session_key);
159
160/* accept an incoming connection using the parameters provided by crypto_inbound
161 return -1 if not successful
162 returns the crypt_connection_id if successful */
163int accept_crypto_inbound(Net_Crypto *c, int connection_id, uint8_t *public_key, uint8_t *secret_nonce,
164 uint8_t *session_key);
165
166/* return 0 if no connection, 1 we have sent a handshake, 2 if connexion is not confirmed yet
167 (we have received a handshake but no empty data packet), 3 if the connection is established.
168 4 if the connection is timed out and waiting to be killed */
169int is_cryptoconnected(Net_Crypto *c, int crypt_connection_id);
170
171
172/* Generate our public and private keys
173 Only call this function the first time the program starts. */
174void new_keys(Net_Crypto *c);
175
176/* save the public and private keys to the keys array
177 Length must be crypto_box_PUBLICKEYBYTES + crypto_box_SECRETKEYBYTES */
178void save_keys(Net_Crypto *c, uint8_t *keys);
179
180/* load the public and private keys from the keys array
181 Length must be crypto_box_PUBLICKEYBYTES + crypto_box_SECRETKEYBYTES */
182void load_keys(Net_Crypto *c, uint8_t *keys);
183
184/* create new instance of Net_Crypto
185 sets all the global connection variables to their default values. */
186Net_Crypto *new_net_crypto(Networking_Core *net);
187
188/* main loop */
189void do_net_crypto(Net_Crypto *c);
190
191void kill_net_crypto(Net_Crypto *c);
192
193/* Init the cryptopacket handling */
194void init_cryptopackets(void *dht);
195
196#ifdef __cplusplus
197}
198#endif
199
200#endif
diff --git a/toxcore/network.c b/toxcore/network.c
new file mode 100644
index 00000000..2bcf7d61
--- /dev/null
+++ b/toxcore/network.c
@@ -0,0 +1,218 @@
1/* network.h
2 *
3 * Functions for the core networking.
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 "network.h"
25
26/* returns current UNIX time in microseconds (us). */
27uint64_t current_time(void)
28{
29 uint64_t time;
30#ifdef WIN32
31 /* This probably works fine */
32 FILETIME ft;
33 GetSystemTimeAsFileTime(&ft);
34 time = ft.dwHighDateTime;
35 time <<= 32;
36 time |= ft.dwLowDateTime;
37 time -= 116444736000000000UL;
38 return time / 10;
39#else
40 struct timeval a;
41 gettimeofday(&a, NULL);
42 time = 1000000UL * a.tv_sec + a.tv_usec;
43 return time;
44#endif
45}
46
47/* return a random number
48 NOTE: this function should probably not be used where cryptographic randomness is absolutely necessary */
49uint32_t random_int(void)
50{
51#ifndef VANILLA_NACL
52 //NOTE: this function comes from libsodium
53 return randombytes_random();
54#else
55 return random();
56#endif
57}
58
59/* Basic network functions:
60 Function to send packet(data) of length length to ip_port */
61int sendpacket(int sock, IP_Port ip_port, uint8_t *data, uint32_t length)
62{
63 ADDR addr = {AF_INET, ip_port.port, ip_port.ip};
64 return sendto(sock, (char *) data, length, 0, (struct sockaddr *)&addr, sizeof(addr));
65}
66
67/* Function to receive data, ip and port of sender is put into ip_port
68 the packet data into data
69 the packet length into length.
70 dump all empty packets. */
71static int receivepacket(int sock, IP_Port *ip_port, uint8_t *data, uint32_t *length)
72{
73 ADDR addr;
74#ifdef WIN32
75 int addrlen = sizeof(addr);
76#else
77 uint32_t addrlen = sizeof(addr);
78#endif
79 (*(int32_t *)length) = recvfrom(sock, (char *) data, MAX_UDP_PACKET_SIZE, 0, (struct sockaddr *)&addr, &addrlen);
80
81 if (*(int32_t *)length <= 0)
82 return -1; /* nothing received or empty packet */
83
84 ip_port->ip = addr.ip;
85 ip_port->port = addr.port;
86 return 0;
87}
88
89void networking_registerhandler(Networking_Core *net, uint8_t byte, packet_handler_callback cb, void *object)
90{
91 net->packethandlers[byte].function = cb;
92 net->packethandlers[byte].object = object;
93}
94
95void networking_poll(Networking_Core *net)
96{
97 IP_Port ip_port;
98 uint8_t data[MAX_UDP_PACKET_SIZE];
99 uint32_t length;
100
101 while (receivepacket(net->sock, &ip_port, data, &length) != -1) {
102 if (length < 1) continue;
103
104 if (!(net->packethandlers[data[0]].function)) continue;
105
106 net->packethandlers[data[0]].function(net->packethandlers[data[0]].object, ip_port, data, length);
107 }
108}
109
110uint8_t at_startup_ran;
111static int at_startup(void)
112{
113 if (at_startup_ran != 0)
114 return 0;
115
116#ifdef WIN32
117 WSADATA wsaData;
118
119 if (WSAStartup(MAKEWORD(2, 2), &wsaData) != NO_ERROR)
120 return -1;
121
122#else
123 srandom((uint32_t)current_time());
124#endif
125 srand((uint32_t)current_time());
126 at_startup_ran = 1;
127 return 0;
128}
129
130/* TODO: put this somewhere
131static void at_shutdown(void)
132{
133#ifdef WIN32
134 WSACleanup();
135#endif
136}
137*/
138
139/* initialize networking
140 bind to ip and port
141 ip must be in network order EX: 127.0.0.1 = (7F000001)
142 port is in host byte order (this means don't worry about it)
143 returns Networking_Core object if no problems
144 returns NULL if there are problems */
145Networking_Core *new_networking(IP ip, uint16_t port)
146{
147 if (at_startup() != 0)
148 return NULL;
149
150 /* initialize our socket */
151 Networking_Core *temp = calloc(1, sizeof(Networking_Core));
152
153 if (temp == NULL)
154 return NULL;
155
156 temp->sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
157
158 /* Check for socket error */
159#ifdef WIN32
160
161 if (temp->sock == INVALID_SOCKET) { /* MSDN recommends this */
162 free(temp);
163 return NULL;
164 }
165
166#else
167
168 if (temp->sock < 0) {
169 free(temp);
170 return NULL;
171 }
172
173#endif
174
175 /* Functions to increase the size of the send and receive UDP buffers
176 NOTE: uncomment if necessary */
177 /*
178 int n = 1024 * 1024 * 2;
179 if(setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char*)&n, sizeof(n)) == -1)
180 {
181 return -1;
182 }
183
184 if(setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char*)&n, sizeof(n)) == -1)
185 return -1;
186 */
187
188 /* Enable broadcast on socket */
189 int broadcast = 1;
190 setsockopt(temp->sock, SOL_SOCKET, SO_BROADCAST, (char *)&broadcast, sizeof(broadcast));
191
192 /* Set socket nonblocking */
193#ifdef WIN32
194 /* I think this works for windows */
195 u_long mode = 1;
196 /* ioctl(sock, FIONBIO, &mode); */
197 ioctlsocket(temp->sock, FIONBIO, &mode);
198#else
199 fcntl(temp->sock, F_SETFL, O_NONBLOCK, 1);
200#endif
201
202 /* Bind our socket to port PORT and address 0.0.0.0 */
203 ADDR addr = {AF_INET, htons(port), ip};
204 bind(temp->sock, (struct sockaddr *)&addr, sizeof(addr));
205 return temp;
206}
207
208/* function to cleanup networking stuff */
209void kill_networking(Networking_Core *net)
210{
211#ifdef WIN32
212 closesocket(net->sock);
213#else
214 close(net->sock);
215#endif
216 free(net);
217 return;
218}
diff --git a/toxcore/network.h b/toxcore/network.h
new file mode 100644
index 00000000..3547f79b
--- /dev/null
+++ b/toxcore/network.h
@@ -0,0 +1,159 @@
1/* network.h
2 *
3 * Datatypes, functions and includes for the core networking.
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#ifndef NETWORK_H
25#define NETWORK_H
26
27#include <stdlib.h>
28#include <stdio.h>
29#include <stdint.h>
30#include <string.h>
31#include <time.h>
32
33#ifdef WIN32 /* Put win32 includes here */
34#ifndef WINVER
35//Windows XP
36#define WINVER 0x0501
37#endif
38#include <winsock2.h>
39#include <windows.h>
40#include <ws2tcpip.h>
41
42#undef VANILLA_NACL /* make sure on windows we use libsodium */
43
44#else //Linux includes
45
46#include <fcntl.h>
47#include <sys/socket.h>
48#include <netinet/in.h>
49#include <errno.h>
50#include <sys/time.h>
51#include <sys/types.h>
52#include <netdb.h>
53#include <unistd.h>
54
55#endif
56
57#ifndef VANILLA_NACL
58/* we use libsodium by default */
59#include <sodium.h>
60#else
61#include <crypto_box.h>
62#define crypto_box_MACBYTES (crypto_box_ZEROBYTES - crypto_box_BOXZEROBYTES)
63#endif
64
65#ifdef __cplusplus
66extern "C" {
67#endif
68
69#define MAX_UDP_PACKET_SIZE 65507
70
71#define NET_PACKET_PING_REQUEST 0 /* Ping request packet ID */
72#define NET_PACKET_PING_RESPONSE 1 /* Ping response packet ID */
73#define NET_PACKET_GET_NODES 2 /* Get nodes request packet ID */
74#define NET_PACKET_SEND_NODES 3 /* Send nodes response packet ID */
75#define NET_PACKET_HANDSHAKE 16 /* Handshake packet ID */
76#define NET_PACKET_SYNC 17 /* SYNC packet ID */
77#define NET_PACKET_DATA 18 /* Data packet ID */
78#define NET_PACKET_CRYPTO 32 /* Encrypted data packet ID */
79#define NET_PACKET_LAN_DISCOVERY 33 /* LAN discovery packet ID */
80
81
82/* Current time, unix format */
83#define unix_time() ((uint64_t)time(NULL))
84
85
86typedef union {
87 uint8_t c[4];
88 uint16_t s[2];
89 uint32_t i;
90} IP;
91
92typedef struct {
93 IP ip;
94 uint16_t port;
95 /* not used for anything right now */
96 uint16_t padding;
97} IP_Port;
98
99typedef struct {
100 int16_t family;
101 uint16_t port;
102 IP ip;
103 uint8_t zeroes[8];
104#ifdef ENABLE_IPV6
105 uint8_t zeroes2[12];
106#endif
107} ADDR;
108
109/* Function to receive data, ip and port of sender is put into ip_port
110 the packet data into data
111 the packet length into length. */
112typedef int (*packet_handler_callback)(void *object, IP_Port ip_port, uint8_t *data, uint32_t len);
113
114typedef struct {
115 packet_handler_callback function;
116 void *object;
117} Packet_Handles;
118
119typedef struct {
120 Packet_Handles packethandlers[256];
121 /* our UDP socket */
122 int sock;
123} Networking_Core;
124
125/* returns current time in milleseconds since the epoch. */
126uint64_t current_time(void);
127
128/* return a random number
129 NOTE: this function should probably not be used where cryptographic randomness is absolutely necessary */
130uint32_t random_int(void);
131
132/* Basic network functions: */
133
134/* Function to send packet(data) of length length to ip_port */
135int sendpacket(int sock, IP_Port ip_port, uint8_t *data, uint32_t length);
136
137/* Function to call when packet beginning with byte is received */
138void networking_registerhandler(Networking_Core *net, uint8_t byte, packet_handler_callback cb, void *object);
139
140/* call this several times a second */
141void networking_poll(Networking_Core *net);
142
143/* initialize networking
144 bind to ip and port
145 ip must be in network order EX: 127.0.0.1 = (7F000001)
146 port is in host byte order (this means don't worry about it)
147 returns 0 if no problems
148 returns -1 if there were problems */
149Networking_Core *new_networking(IP ip, uint16_t port);
150
151/* function to cleanup networking stuff(doesn't do much right now) */
152void kill_networking(Networking_Core *net);
153
154
155#ifdef __cplusplus
156}
157#endif
158
159#endif
diff --git a/toxcore/packets.h b/toxcore/packets.h
new file mode 100644
index 00000000..4f410fb3
--- /dev/null
+++ b/toxcore/packets.h
@@ -0,0 +1,31 @@
1/*
2 * packet.h -- Packet structure
3 *
4 * This file is donated to the Tox Project.
5 * Copyright 2013 plutooo
6 */
7
8typedef struct {
9 uint8_t id[CLIENT_ID_SIZE];
10
11} __attribute__((packed)) clientid_t;
12
13// Ping packet
14typedef struct {
15 uint8_t packet_id;
16 clientid_t client_id;
17 uint8_t nonce[crypto_box_NONCEBYTES];
18 uint64_t ping_id;
19 uint8_t padding[ENCRYPTION_PADDING];
20
21} __attribute__((packed)) pingreq_t;
22
23// Pong packet
24typedef struct {
25 uint8_t packet_id;
26 clientid_t client_id;
27 uint8_t nonce[crypto_box_NONCEBYTES];
28 uint64_t ping_id;
29 uint8_t padding[ENCRYPTION_PADDING];
30
31} __attribute__((packed)) pingres_t;
diff --git a/toxcore/ping.c b/toxcore/ping.c
new file mode 100644
index 00000000..55d4d261
--- /dev/null
+++ b/toxcore/ping.c
@@ -0,0 +1,229 @@
1/*
2 * ping.c -- Buffered pinging using cyclic arrays.
3 *
4 * This file is donated to the Tox Project.
5 * Copyright 2013 plutooo
6 */
7
8#include <stdbool.h>
9#include <stdint.h>
10
11#include "DHT.h"
12#include "net_crypto.h"
13#include "packets.h"
14#include "network.h"
15#include "util.h"
16
17#define PING_NUM_MAX 256
18#define PING_TIMEOUT 5 // 5s
19
20typedef struct {
21 IP_Port ipp;
22 uint64_t id;
23 uint64_t timestamp;
24} pinged_t;
25
26typedef struct {
27 pinged_t pings[PING_NUM_MAX];
28 size_t num_pings;
29 size_t pos_pings;
30} PING;
31
32void *new_ping(void)
33{
34 return calloc(1, sizeof(PING));
35}
36
37void kill_ping(void *ping)
38{
39 free(ping);
40}
41
42static bool is_timeout(uint64_t time)
43{
44 return (time + PING_TIMEOUT) < now();
45}
46
47static void remove_timeouts(void *ping) // O(n)
48{
49 PING *png = ping;
50 size_t i, id;
51 size_t new_pos = png->pos_pings;
52 size_t new_num = png->num_pings;
53
54 // Loop through buffer, oldest first
55 for (i = 0; i < png->num_pings; i++) {
56 id = (png->pos_pings + i) % PING_NUM_MAX;
57
58 if (is_timeout(png->pings[id].timestamp)) {
59 new_pos++;
60 new_num--;
61 }
62 // Break here because list is sorted.
63 else {
64 break;
65 }
66 }
67
68 png->num_pings = new_num;
69 png->pos_pings = new_pos % PING_NUM_MAX;
70}
71
72uint64_t add_ping(void *ping, IP_Port ipp) // O(n)
73{
74 PING *png = ping;
75 size_t p;
76
77 remove_timeouts(ping);
78
79 // Remove oldest ping if full buffer
80 if (png->num_pings == PING_NUM_MAX) {
81 png->num_pings--;
82 png->pos_pings = (png->pos_pings + 1) % PING_NUM_MAX;
83 }
84
85 // Insert new ping at end of list
86 p = (png->pos_pings + png->num_pings) % PING_NUM_MAX;
87
88 png->pings[p].ipp = ipp;
89 png->pings[p].timestamp = now();
90 png->pings[p].id = random_64b();
91
92 png->num_pings++;
93 return png->pings[p].id;
94}
95
96bool is_pinging(void *ping, IP_Port ipp, uint64_t ping_id) // O(n) TODO: replace this with something else.
97{
98 PING *png = ping;
99
100 if (ipp.ip.i == 0 && ping_id == 0)
101 return false;
102
103 size_t i, id;
104
105 remove_timeouts(ping);
106
107 for (i = 0; i < png->num_pings; i++) {
108 id = (png->pos_pings + i) % PING_NUM_MAX;
109
110 // ping_id = 0 means match any id
111 if ((ipp_eq(png->pings[id].ipp, ipp) || ipp.ip.i == 0) && (png->pings[id].id == ping_id || ping_id == 0)) {
112 return true;
113 }
114 }
115
116 return false;
117}
118
119int send_ping_request(void *ping, Net_Crypto *c, IP_Port ipp, clientid_t *client_id)
120{
121 pingreq_t pk;
122 int rc;
123 uint64_t ping_id;
124
125 if (is_pinging(ping, ipp, 0) || id_eq(client_id, (clientid_t *)c->self_public_key))
126 return 1;
127
128 // Generate random ping_id
129 ping_id = add_ping(ping, ipp);
130
131 pk.packet_id = NET_PACKET_PING_REQUEST;
132 id_cpy(&pk.client_id, (clientid_t *)c->self_public_key); // Our pubkey
133 random_nonce((uint8_t *) &pk.nonce); // Generate random nonce
134
135 // Encrypt ping_id using recipient privkey
136 rc = encrypt_data((uint8_t *) client_id,
137 c->self_secret_key,
138 (uint8_t *) &pk.nonce,
139 (uint8_t *) &ping_id, sizeof(ping_id),
140 (uint8_t *) &pk.ping_id);
141
142 if (rc != sizeof(ping_id) + ENCRYPTION_PADDING)
143 return 1;
144
145 return sendpacket(c->lossless_udp->net->sock, ipp, (uint8_t *) &pk, sizeof(pk));
146}
147
148int send_ping_response(Net_Crypto *c, IP_Port ipp, clientid_t *client_id, uint64_t ping_id)
149{
150 pingres_t pk;
151 int rc;
152
153 if (id_eq(client_id, (clientid_t *)c->self_public_key))
154 return 1;
155
156 pk.packet_id = NET_PACKET_PING_RESPONSE;
157 id_cpy(&pk.client_id, (clientid_t *)c->self_public_key); // Our pubkey
158 random_nonce((uint8_t *) &pk.nonce); // Generate random nonce
159
160 // Encrypt ping_id using recipient privkey
161 rc = encrypt_data((uint8_t *) client_id,
162 c->self_secret_key,
163 (uint8_t *) &pk.nonce,
164 (uint8_t *) &ping_id, sizeof(ping_id),
165 (uint8_t *) &pk.ping_id);
166
167 if (rc != sizeof(ping_id) + ENCRYPTION_PADDING)
168 return 1;
169
170 return sendpacket(c->lossless_udp->net->sock, ipp, (uint8_t *) &pk, sizeof(pk));
171}
172
173int handle_ping_request(void *object, IP_Port source, uint8_t *packet, uint32_t length)
174{
175 DHT *dht = object;
176 pingreq_t *p = (pingreq_t *) packet;
177 int rc;
178 uint64_t ping_id;
179
180 if (length != sizeof(pingreq_t) || id_eq(&p->client_id, (clientid_t *)dht->c->self_public_key))
181 return 1;
182
183 // Decrypt ping_id
184 rc = decrypt_data((uint8_t *) &p->client_id,
185 dht->c->self_secret_key,
186 (uint8_t *) &p->nonce,
187 (uint8_t *) &p->ping_id,
188 sizeof(ping_id) + ENCRYPTION_PADDING,
189 (uint8_t *) &ping_id);
190
191 if (rc != sizeof(ping_id))
192 return 1;
193
194 // Send response
195 send_ping_response(dht->c, source, &p->client_id, ping_id);
196 add_toping(dht, (uint8_t *) &p->client_id, source);
197
198 return 0;
199}
200
201int handle_ping_response(void *object, IP_Port source, uint8_t *packet, uint32_t length)
202{
203 DHT *dht = object;
204 pingres_t *p = (pingres_t *) packet;
205 int rc;
206 uint64_t ping_id;
207
208 if (length != sizeof(pingres_t) || id_eq(&p->client_id, (clientid_t *)dht->c->self_public_key))
209 return 1;
210
211 // Decrypt ping_id
212 rc = decrypt_data((uint8_t *) &p->client_id,
213 dht->c->self_secret_key,
214 (uint8_t *) &p->nonce,
215 (uint8_t *) &p->ping_id,
216 sizeof(ping_id) + ENCRYPTION_PADDING,
217 (uint8_t *) &ping_id);
218
219 if (rc != sizeof(ping_id))
220 return 1;
221
222 // Make sure ping_id is correct
223 if (!is_pinging(dht->ping, source, ping_id))
224 return 1;
225
226 // Associate source ip with client_id
227 addto_lists(dht, source, (uint8_t *) &p->client_id);
228 return 0;
229}
diff --git a/toxcore/ping.h b/toxcore/ping.h
new file mode 100644
index 00000000..c04ec80e
--- /dev/null
+++ b/toxcore/ping.h
@@ -0,0 +1,17 @@
1/*
2 * ping.h -- Buffered pinging using cyclic arrays.
3 *
4 * This file is donated to the Tox Project.
5 * Copyright 2013 plutooo
6 */
7
8#include <stdbool.h>
9
10void *new_ping(void);
11void kill_ping(void *ping);
12uint64_t add_ping(void *ping, IP_Port ipp);
13bool is_pinging(void *ping, IP_Port ipp, uint64_t ping_id);
14int send_ping_request(void *ping, Net_Crypto *c, IP_Port ipp, clientid_t *client_id);
15int send_ping_response(Net_Crypto *c, IP_Port ipp, clientid_t *client_id, uint64_t ping_id);
16int handle_ping_request(void *object, IP_Port source, uint8_t *packet, uint32_t length);
17int handle_ping_response(void *object, IP_Port source, uint8_t *packet, uint32_t length);
diff --git a/toxcore/tox.c b/toxcore/tox.c
new file mode 100644
index 00000000..a97e52bc
--- /dev/null
+++ b/toxcore/tox.c
@@ -0,0 +1,374 @@
1/* tox.c
2 *
3 * The Tox public API.
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 "Messenger.h"
25/*
26 * returns a FRIEND_ADDRESS_SIZE byte address to give to others.
27 * format: [client_id (32 bytes)][nospam number (4 bytes)][checksum (2 bytes)]
28 *
29 */
30void tox_getaddress(void *tox, uint8_t *address)
31{
32 Messenger *m = tox;
33 getaddress(m, address);
34}
35
36/*
37 * add a friend
38 * set the data that will be sent along with friend request
39 * address is the address of the friend (returned by getaddress of the friend you wish to add) it must be FRIEND_ADDRESS_SIZE bytes. TODO: add checksum.
40 * data is the data and length is the length
41 * returns the friend number if success
42 * return FA_TOOLONG if message length is too long
43 * return FAERR_NOMESSAGE if no message (message length must be >= 1 byte)
44 * return FAERR_OWNKEY if user's own key
45 * return FAERR_ALREADYSENT if friend request already sent or already a friend
46 * return FAERR_UNKNOWN for unknown error
47 * return FAERR_BADCHECKSUM if bad checksum in address
48 * return FAERR_SETNEWNOSPAM if the friend was already there but the nospam was different
49 * (the nospam for that friend was set to the new one)
50 * return FAERR_NOMEM if increasing the friend list size fails
51 */
52int tox_addfriend(void *tox, uint8_t *address, uint8_t *data, uint16_t length)
53{
54 Messenger *m = tox;
55 return m_addfriend(m, address, data, length);
56}
57
58/* add a friend without sending a friendrequest.
59 returns the friend number if success
60 return -1 if failure. */
61int tox_addfriend_norequest(void *tox, uint8_t *client_id)
62{
63 Messenger *m = tox;
64 return m_addfriend_norequest(m, client_id);
65}
66
67/* return the friend id associated to that client id.
68 return -1 if no such friend */
69int tox_getfriend_id(void *tox, uint8_t *client_id)
70{
71 Messenger *m = tox;
72 return getfriend_id(m, client_id);
73}
74
75/* copies the public key associated to that friend id into client_id buffer.
76 make sure that client_id is of size CLIENT_ID_SIZE.
77 return 0 if success
78 return -1 if failure */
79int tox_getclient_id(void *tox, int friend_id, uint8_t *client_id)
80{
81 Messenger *m = tox;
82 return getclient_id(m, friend_id, client_id);
83}
84
85/* remove a friend */
86int tox_delfriend(void *tox, int friendnumber)
87{
88 Messenger *m = tox;
89 return m_delfriend(m, friendnumber);
90}
91
92/* return 4 if friend is online
93 return 3 if friend is confirmed
94 return 2 if the friend request was sent
95 return 1 if the friend was added
96 return 0 if there is no friend with that number */
97int tox_friendstatus(void *tox, int friendnumber)
98{
99 Messenger *m = tox;
100 return m_friendstatus(m, friendnumber);
101}
102
103/* send a text chat message to an online friend
104 returns the message id if packet was successfully put into the send queue
105 return 0 if it was not
106 you will want to retain the return value, it will be passed to your read receipt callback
107 if one is received.
108 m_sendmessage_withid will send a message with the id of your choosing,
109 however we can generate an id for you by calling plain m_sendmessage. */
110uint32_t tox_sendmessage(void *tox, int friendnumber, uint8_t *message, uint32_t length)
111{
112 Messenger *m = tox;
113 return m_sendmessage(m, friendnumber, message, length);
114}
115
116uint32_t tox_sendmessage_withid(void *tox, int friendnumber, uint32_t theid, uint8_t *message, uint32_t length)
117{
118 Messenger *m = tox;
119 return m_sendmessage_withid(m, friendnumber, theid, message, length);
120}
121
122/* send an action to an online friend
123 returns 1 if packet was successfully put into the send queue
124 return 0 if it was not */
125int tox_sendaction(void *tox, int friendnumber, uint8_t *action, uint32_t length)
126{
127 Messenger *m = tox;
128 return m_sendaction(m, friendnumber, action, length);
129}
130
131/* Set our nickname
132 name must be a string of maximum MAX_NAME_LENGTH length.
133 length must be at least 1 byte
134 length is the length of name with the NULL terminator
135 return 0 if success
136 return -1 if failure */
137int tox_setname(void *tox, uint8_t *name, uint16_t length)
138{
139 Messenger *m = tox;
140 return setname(m, name, length);
141}
142
143/*
144 Get your nickname.
145 m The messanger context to use.
146 name Pointer to a string for the name.
147 nlen The length of the string buffer.
148 returns Return the length of the name, 0 on error.
149*/
150uint16_t tox_getselfname(void *tox, uint8_t *name, uint16_t nlen)
151{
152 Messenger *m = tox;
153 return getself_name(m, name, nlen);
154}
155
156/* get name of friendnumber
157 put it in name
158 name needs to be a valid memory location with a size of at least MAX_NAME_LENGTH (128) bytes.
159 return 0 if success
160 return -1 if failure */
161int tox_getname(void *tox, int friendnumber, uint8_t *name)
162{
163 Messenger *m = tox;
164 return getname(m, friendnumber, name);
165}
166
167/* set our user status
168 you are responsible for freeing status after
169 returns 0 on success, -1 on failure */
170int tox_set_statusmessage(void *tox, uint8_t *status, uint16_t length)
171{
172 Messenger *m = tox;
173 return m_set_statusmessage(m, status, length);
174}
175
176int tox_set_userstatus(void *tox, USERSTATUS status)
177{
178 Messenger *m = tox;
179 return m_set_userstatus(m, status);
180}
181
182/* return the length of friendnumber's status message,
183 including null
184 pass it into malloc */
185int tox_get_statusmessage_size(void *tox, int friendnumber)
186{
187 Messenger *m = tox;
188 return m_get_statusmessage_size(m, friendnumber);
189}
190
191/* copy friendnumber's status message into buf, truncating if size is over maxlen
192 get the size you need to allocate from m_get_statusmessage_size
193 The self variant will copy our own status message. */
194int tox_copy_statusmessage(void *tox, int friendnumber, uint8_t *buf, uint32_t maxlen)
195{
196 Messenger *m = tox;
197 return m_copy_statusmessage(m, friendnumber, buf, maxlen);
198}
199
200int tox_copy_self_statusmessage(void *tox, uint8_t *buf, uint32_t maxlen)
201{
202 Messenger *m = tox;
203 return m_copy_self_statusmessage(m, buf, maxlen);
204}
205
206/* Return one of USERSTATUS values.
207 * Values unknown to your application should be represented as USERSTATUS_NONE.
208 * As above, the self variant will return our own USERSTATUS.
209 * If friendnumber is invalid, this shall return USERSTATUS_INVALID. */
210USERSTATUS tox_get_userstatus(void *tox, int friendnumber)
211{
212 Messenger *m = tox;
213 return m_get_userstatus(m, friendnumber);
214}
215
216USERSTATUS tox_get_selfuserstatus(void *tox)
217{
218 Messenger *m = tox;
219 return m_get_self_userstatus(m);
220}
221
222
223/* Sets whether we send read receipts for friendnumber.
224 * This function is not lazy, and it will fail if yesno is not (0 or 1).*/
225void tox_set_sends_receipts(void *tox, int friendnumber, int yesno)
226{
227 Messenger *m = tox;
228 m_set_sends_receipts(m, friendnumber, yesno);
229}
230
231
232/* set the function that will be executed when a friend request is received.
233 function format is function(uint8_t * public_key, uint8_t * data, uint16_t length) */
234void tox_callback_friendrequest(void *tox, void (*function)(uint8_t *, uint8_t *, uint16_t, void *), void *userdata)
235{
236 Messenger *m = tox;
237 m_callback_friendrequest(m, function, userdata);
238}
239
240
241/* set the function that will be executed when a message from a friend is received.
242 function format is: function(int friendnumber, uint8_t * message, uint32_t length) */
243void tox_callback_friendmessage(void *tox, void (*function)(Messenger *tox, int, uint8_t *, uint16_t, void *),
244 void *userdata)
245{
246 Messenger *m = tox;
247 m_callback_friendmessage(m, function, userdata);
248}
249
250/* set the function that will be executed when an action from a friend is received.
251 function format is: function(int friendnumber, uint8_t * action, uint32_t length) */
252void tox_callback_action(void *tox, void (*function)(Messenger *tox, int, uint8_t *, uint16_t, void *), void *userdata)
253{
254 Messenger *m = tox;
255 m_callback_action(m, function, userdata);
256}
257
258/* set the callback for name changes
259 function(int friendnumber, uint8_t *newname, uint16_t length)
260 you are not responsible for freeing newname */
261void tox_callback_namechange(void *tox, void (*function)(Messenger *tox, int, uint8_t *, uint16_t, void *),
262 void *userdata)
263{
264 Messenger *m = tox;
265 m_callback_namechange(m, function, userdata);
266}
267
268/* set the callback for status message changes
269 function(int friendnumber, uint8_t *newstatus, uint16_t length)
270 you are not responsible for freeing newstatus */
271void tox_callback_statusmessage(void *tox, void (*function)(Messenger *tox, int, uint8_t *, uint16_t, void *),
272 void *userdata)
273{
274 Messenger *m = tox;
275 m_callback_statusmessage(m, function, userdata);
276}
277
278/* set the callback for status type changes
279 function(int friendnumber, USERSTATUS kind) */
280void tox_callback_userstatus(void *tox, void (*function)(Messenger *tox, int, USERSTATUS, void *), void *userdata)
281{
282 Messenger *m = tox;
283 m_callback_userstatus(m, function, userdata);
284}
285
286/* set the callback for read receipts
287 function(int friendnumber, uint32_t receipt)
288 if you are keeping a record of returns from m_sendmessage,
289 receipt might be one of those values, and that means the message
290 has been received on the other side. since core doesn't
291 track ids for you, receipt may not correspond to any message
292 in that case, you should discard it. */
293void tox_callback_read_receipt(void *tox, void (*function)(Messenger *tox, int, uint32_t, void *), void *userdata)
294{
295 Messenger *m = tox;
296 m_callback_read_receipt(m, function, userdata);
297}
298
299/* set the callback for connection status changes
300 function(int friendnumber, uint8_t status)
301 status:
302 0 -- friend went offline after being previously online
303 1 -- friend went online
304 note that this callback is not called when adding friends, thus the "after
305 being previously online" part. it's assumed that when adding friends,
306 their connection status is offline. */
307void tox_callback_connectionstatus(void *tox, void (*function)(Messenger *tox, int, uint8_t, void *), void *userdata)
308{
309 Messenger *m = tox;
310 m_callback_connectionstatus(m, function, userdata);
311}
312
313/* Use this function to bootstrap the client
314 Sends a get nodes request to the given node with ip port and public_key */
315void tox_bootstrap(void *tox, IP_Port ip_port, uint8_t *public_key)
316{
317 Messenger *m = tox;
318 DHT_bootstrap(m->dht, ip_port, public_key);
319}
320
321/* returns 0 if we are not connected to the DHT
322 returns 1 if we are */
323int tox_isconnected(void *tox)
324{
325 Messenger *m = tox;
326 return DHT_isconnected(m->dht);
327}
328
329/* run this at startup
330 * returns allocated instance of tox on success
331 * returns 0 if there are problems */
332void *tox_new(void)
333{
334 return initMessenger();
335}
336
337/* run this before closing shop
338 * free all datastructures */
339void tox_kill(void *tox)
340{
341 Messenger *m = tox;
342 cleanupMessenger(m);
343}
344
345/* the main loop that needs to be run at least 20 times per second */
346void tox_do(void *tox)
347{
348 Messenger *m = tox;
349 doMessenger(m);
350}
351
352/* SAVING AND LOADING FUNCTIONS: */
353
354/* returns the size of the messenger data (for saving) */
355uint32_t tox_size(void *tox)
356{
357 Messenger *m = tox;
358 return Messenger_size(m);
359}
360
361/* save the messenger in data (must be allocated memory of size Messenger_size()) */
362void tox_save(void *tox, uint8_t *data)
363{
364 Messenger *m = tox;
365 Messenger_save(m, data);
366}
367
368/* load the messenger from data of size length */
369int tox_load(void *tox, uint8_t *data, uint32_t length)
370{
371 Messenger *m = tox;
372 return Messenger_load(m, data, length);
373}
374
diff --git a/toxcore/tox.h b/toxcore/tox.h
new file mode 100644
index 00000000..bdfac1d6
--- /dev/null
+++ b/toxcore/tox.h
@@ -0,0 +1,289 @@
1/* tox.h
2 *
3 * The Tox public API.
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#ifndef TOX_H
25#define TOX_H
26
27#include <stdint.h>
28
29#ifdef __cplusplus
30extern "C" {
31#endif
32
33#define TOX_MAX_NAME_LENGTH 128
34#define TOX_MAX_STATUSMESSAGE_LENGTH 128
35#define TOX_CLIENT_ID_SIZE 32
36
37#define TOX_FRIEND_ADDRESS_SIZE (TOX_CLIENT_ID_SIZE + sizeof(uint32_t) + sizeof(uint16_t))
38
39
40typedef union {
41 uint8_t c[4];
42 uint16_t s[2];
43 uint32_t i;
44} tox_IP;
45
46typedef struct {
47 tox_IP ip;
48 uint16_t port;
49 /* not used for anything right now */
50 uint16_t padding;
51} tox_IP_Port;
52
53/* status definitions */
54enum {
55 TOX_NOFRIEND,
56 TOX_FRIEND_ADDED,
57 TOX_FRIEND_REQUESTED,
58 TOX_FRIEND_CONFIRMED,
59 TOX_FRIEND_ONLINE,
60};
61
62/* errors for m_addfriend
63 * FAERR - Friend Add Error */
64enum {
65 TOX_FAERR_TOOLONG = -1,
66 TOX_FAERR_NOMESSAGE = -2,
67 TOX_FAERR_OWNKEY = -3,
68 TOX_FAERR_ALREADYSENT = -4,
69 TOX_FAERR_UNKNOWN = -5,
70 TOX_FAERR_BADCHECKSUM = -6,
71 TOX_FAERR_SETNEWNOSPAM = -7,
72 TOX_FAERR_NOMEM = -8
73};
74/* USERSTATUS
75 * Represents userstatuses someone can have. */
76
77typedef enum {
78 TOX_USERSTATUS_NONE,
79 TOX_USERSTATUS_AWAY,
80 TOX_USERSTATUS_BUSY,
81 TOX_USERSTATUS_INVALID
82}
83TOX_USERSTATUS;
84
85typedef void Tox;
86
87/*
88 * returns a FRIEND_ADDRESS_SIZE byte address to give to others.
89 * format: [client_id (32 bytes)][nospam number (4 bytes)][checksum (2 bytes)]
90 *
91 */
92void tox_getaddress(Tox *tox, uint8_t *address);
93
94/*
95 * add a friend
96 * set the data that will be sent along with friend request
97 * address is the address of the friend (returned by getaddress of the friend you wish to add) it must be FRIEND_ADDRESS_SIZE bytes. TODO: add checksum.
98 * data is the data and length is the length
99 * returns the friend number if success
100 * return TOX_FA_TOOLONG if message length is too long
101 * return TOX_FAERR_NOMESSAGE if no message (message length must be >= 1 byte)
102 * return TOX_FAERR_OWNKEY if user's own key
103 * return TOX_FAERR_ALREADYSENT if friend request already sent or already a friend
104 * return TOX_FAERR_UNKNOWN for unknown error
105 * return TOX_FAERR_BADCHECKSUM if bad checksum in address
106 * return TOX_FAERR_SETNEWNOSPAM if the friend was already there but the nospam was different
107 * (the nospam for that friend was set to the new one)
108 * return TOX_FAERR_NOMEM if increasing the friend list size fails
109 */
110int tox_addfriend(Tox *tox, uint8_t *address, uint8_t *data, uint16_t length);
111
112
113/* add a friend without sending a friendrequest.
114 returns the friend number if success
115 return -1 if failure. */
116int tox_addfriend_norequest(Tox *tox, uint8_t *client_id);
117
118/* return the friend id associated to that client id.
119 return -1 if no such friend */
120int tox_getfriend_id(Tox *tox, uint8_t *client_id);
121
122/* copies the public key associated to that friend id into client_id buffer.
123 make sure that client_id is of size CLIENT_ID_SIZE.
124 return 0 if success
125 return -1 if failure */
126int tox_getclient_id(Tox *tox, int friend_id, uint8_t *client_id);
127
128/* remove a friend */
129int tox_delfriend(Tox *tox, int friendnumber);
130
131/* return TOX_FRIEND_ONLINE if friend is online
132 return TOX_FRIEND_CONFIRMED if friend is confirmed
133 return TOX_FRIEND_REQUESTED if the friend request was sent
134 return TOX_FRIEND_ADDED if the friend was added
135 return TOX_NOFRIEND if there is no friend with that number */
136int tox_friendstatus(Tox *tox, int friendnumber);
137
138/* send a text chat message to an online friend
139 returns the message id if packet was successfully put into the send queue
140 return 0 if it was not
141 you will want to retain the return value, it will be passed to your read receipt callback
142 if one is received.
143 m_sendmessage_withid will send a message with the id of your choosing,
144 however we can generate an id for you by calling plain m_sendmessage. */
145uint32_t tox_sendmessage(Tox *tox, int friendnumber, uint8_t *message, uint32_t length);
146uint32_t tox_sendmessage_withid(Tox *tox, int friendnumber, uint32_t theid, uint8_t *message, uint32_t length);
147
148/* send an action to an online friend
149 returns 1 if packet was successfully put into the send queue
150 return 0 if it was not */
151int tox_sendaction(Tox *tox, int friendnumber, uint8_t *action, uint32_t length);
152
153/* Set our nickname
154 name must be a string of maximum MAX_NAME_LENGTH length.
155 length must be at least 1 byte
156 length is the length of name with the NULL terminator
157 return 0 if success
158 return -1 if failure */
159int tox_setname(Tox *tox, uint8_t *name, uint16_t length);
160
161/*
162 Get your nickname.
163 m The messanger context to use.
164 name Pointer to a string for the name.
165 nlen The length of the string buffer.
166 returns Return the length of the name, 0 on error.
167*/
168uint16_t tox_getselfname(Tox *tox, uint8_t *name, uint16_t nlen);
169
170/* get name of friendnumber
171 put it in name
172 name needs to be a valid memory location with a size of at least MAX_NAME_LENGTH (128) bytes.
173 return 0 if success
174 return -1 if failure */
175int tox_getname(Tox *tox, int friendnumber, uint8_t *name);
176
177/* set our user status
178 you are responsible for freeing status after
179 returns 0 on success, -1 on failure */
180int tox_set_statusmessage(Tox *tox, uint8_t *status, uint16_t length);
181int tox_set_userstatus(Tox *tox, TOX_USERSTATUS status);
182
183/* return the length of friendnumber's status message,
184 including null
185 pass it into malloc */
186int tox_get_statusmessage_size(Tox *tox, int friendnumber);
187
188/* copy friendnumber's status message into buf, truncating if size is over maxlen
189 get the size you need to allocate from m_get_statusmessage_size
190 The self variant will copy our own status message. */
191int tox_copy_statusmessage(Tox *tox, int friendnumber, uint8_t *buf, uint32_t maxlen);
192int tox_copy_self_statusmessage(Tox *tox, uint8_t *buf, uint32_t maxlen);
193
194/* Return one of USERSTATUS values.
195 * Values unknown to your application should be represented as USERSTATUS_NONE.
196 * As above, the self variant will return our own USERSTATUS.
197 * If friendnumber is invalid, this shall return USERSTATUS_INVALID. */
198TOX_USERSTATUS tox_get_userstatus(Tox *tox, int friendnumber);
199TOX_USERSTATUS tox_get_selfuserstatus(Tox *tox);
200
201/* Sets whether we send read receipts for friendnumber.
202 * This function is not lazy, and it will fail if yesno is not (0 or 1).*/
203void tox_set_sends_receipts(Tox *tox, int friendnumber, int yesno);
204
205/* set the function that will be executed when a friend request is received.
206 function format is function(uint8_t * public_key, uint8_t * data, uint16_t length) */
207void tox_callback_friendrequest(Tox *tox, void (*function)(uint8_t *, uint8_t *, uint16_t, void *), void *userdata);
208
209/* set the function that will be executed when a message from a friend is received.
210 function format is: function(int friendnumber, uint8_t * message, uint32_t length) */
211void tox_callback_friendmessage(Tox *tox, void (*function)(Tox *tox, int, uint8_t *, uint16_t, void *),
212 void *userdata);
213
214/* set the function that will be executed when an action from a friend is received.
215 function format is: function(int friendnumber, uint8_t * action, uint32_t length) */
216void tox_callback_action(Tox *tox, void (*function)(Tox *tox, int, uint8_t *, uint16_t, void *), void *userdata);
217
218/* set the callback for name changes
219 function(int friendnumber, uint8_t *newname, uint16_t length)
220 you are not responsible for freeing newname */
221void tox_callback_namechange(Tox *tox, void (*function)(Tox *tox, int, uint8_t *, uint16_t, void *),
222 void *userdata);
223
224/* set the callback for status message changes
225 function(int friendnumber, uint8_t *newstatus, uint16_t length)
226 you are not responsible for freeing newstatus */
227void tox_callback_statusmessage(Tox *tox, void (*function)(Tox *tox, int, uint8_t *, uint16_t, void *),
228 void *userdata);
229
230/* set the callback for status type changes
231 function(int friendnumber, USERSTATUS kind) */
232void tox_callback_userstatus(Tox *tox, void (*function)(Tox *tox, int, TOX_USERSTATUS, void *), void *userdata);
233
234/* set the callback for read receipts
235 function(int friendnumber, uint32_t receipt)
236 if you are keeping a record of returns from m_sendmessage,
237 receipt might be one of those values, and that means the message
238 has been received on the other side. since core doesn't
239 track ids for you, receipt may not correspond to any message
240 in that case, you should discard it. */
241void tox_callback_read_receipt(Tox *tox, void (*function)(Tox *tox, int, uint32_t, void *), void *userdata);
242
243/* set the callback for connection status changes
244 function(int friendnumber, uint8_t status)
245 status:
246 0 -- friend went offline after being previously online
247 1 -- friend went online
248 note that this callback is not called when adding friends, thus the "after
249 being previously online" part. it's assumed that when adding friends,
250 their connection status is offline. */
251void tox_callback_connectionstatus(Tox *tox, void (*function)(Tox *tox, int, uint8_t, void *), void *userdata);
252
253/* Use this function to bootstrap the client
254 Sends a get nodes request to the given node with ip port and public_key */
255void tox_bootstrap(Tox *tox, tox_IP_Port ip_port, uint8_t *public_key);
256
257/* returns 0 if we are not connected to the DHT
258 returns 1 if we are */
259int tox_isconnected(Tox *tox);
260
261/* run this at startup
262 * returns allocated instance of tox on success
263 * returns 0 if there are problems */
264Tox *tox_new(void);
265
266/* run this before closing shop
267 * free all datastructures */
268void tox_kill(Tox *tox);
269
270/* the main loop that needs to be run at least 20 times per second */
271void tox_do(Tox *tox);
272
273/* SAVING AND LOADING FUNCTIONS: */
274
275/* returns the size of the messenger data (for saving) */
276uint32_t tox_size(Tox *tox);
277
278/* save the messenger in data (must be allocated memory of size Messenger_size()) */
279void tox_save(Tox *tox, uint8_t *data);
280
281/* load the messenger from data of size length */
282int tox_load(Tox *tox, uint8_t *data, uint32_t length);
283
284
285#ifdef __cplusplus
286}
287#endif
288
289#endif
diff --git a/toxcore/util.c b/toxcore/util.c
new file mode 100644
index 00000000..6f346db1
--- /dev/null
+++ b/toxcore/util.c
@@ -0,0 +1,45 @@
1/*
2 * util.c -- Utilities.
3 *
4 * This file is donated to the Tox Project.
5 * Copyright 2013 plutooo
6 */
7
8#include <time.h>
9#include <stdint.h>
10#include <stdbool.h>
11
12#include "DHT.h"
13#include "packets.h"
14
15uint64_t now()
16{
17 return time(NULL);
18}
19
20uint64_t random_64b()
21{
22 uint64_t r;
23
24 // This is probably not random enough?
25 r = random_int();
26 r <<= 32;
27 r |= random_int();
28
29 return r;
30}
31
32bool ipp_eq(IP_Port a, IP_Port b)
33{
34 return (a.ip.i == b.ip.i) && (a.port == b.port);
35}
36
37bool id_eq(clientid_t *dest, clientid_t *src)
38{
39 return memcmp(dest, src, sizeof(clientid_t)) == 0;
40}
41
42void id_cpy(clientid_t *dest, clientid_t *src)
43{
44 memcpy(dest, src, sizeof(clientid_t));
45}
diff --git a/toxcore/util.h b/toxcore/util.h
new file mode 100644
index 00000000..5209c2ca
--- /dev/null
+++ b/toxcore/util.h
@@ -0,0 +1,12 @@
1/*
2 * util.h -- Utilities.
3 *
4 * This file is donated to the Tox Project.
5 * Copyright 2013 plutooo
6 */
7
8uint64_t now();
9uint64_t random_64b();
10bool ipp_eq(IP_Port a, IP_Port b);
11bool id_eq(clientid_t *dest, clientid_t *src);
12void id_cpy(clientid_t *dest, clientid_t *src);