1 /* SPDX-License-Identifier: GPL-3.0-or-later
2 * Copyright © 2016-2018 The TokTok team.
3 * Copyright © 2014 Tox project.
4 */
5
6 /*
7 * Connection to friends.
8 */
9 #ifdef HAVE_CONFIG_H
10 #include "config.h"
11 #endif
12
13 #include "friend_connection.h"
14
15 #include <stdlib.h>
16 #include <string.h>
17
18 #include "mono_time.h"
19 #include "util.h"
20
21 #define PORTS_PER_DISCOVERY 10
22
23 typedef struct Friend_Conn_Callbacks {
24 fc_status_cb *status_callback;
25 fc_data_cb *data_callback;
26 fc_lossy_data_cb *lossy_data_callback;
27
28 void *callback_object;
29 int callback_id;
30 } Friend_Conn_Callbacks;
31
32 typedef struct Friend_Conn {
33 uint8_t status;
34
35 uint8_t real_public_key[CRYPTO_PUBLIC_KEY_SIZE];
36 uint8_t dht_temp_pk[CRYPTO_PUBLIC_KEY_SIZE];
37 uint16_t dht_lock;
38 IP_Port dht_ip_port;
39 uint64_t dht_pk_lastrecv;
40 uint64_t dht_ip_port_lastrecv;
41
42 int onion_friendnum;
43 int crypt_connection_id;
44
45 uint64_t ping_lastrecv;
46 uint64_t ping_lastsent;
47 uint64_t share_relays_lastsent;
48
49 Friend_Conn_Callbacks callbacks[MAX_FRIEND_CONNECTION_CALLBACKS];
50
51 uint16_t lock_count;
52
53 Node_format tcp_relays[FRIEND_MAX_STORED_TCP_RELAYS];
54 uint16_t tcp_relay_counter;
55
56 bool hosting_tcp_relay;
57 } Friend_Conn;
58
59
60 struct Friend_Connections {
61 const Mono_Time *mono_time;
62 const Logger *logger;
63 Net_Crypto *net_crypto;
64 DHT *dht;
65 Onion_Client *onion_c;
66
67 Friend_Conn *conns;
68 uint32_t num_cons;
69
70 fr_request_cb *fr_request_callback;
71 void *fr_request_object;
72
73 global_status_cb *global_status_callback;
74 void *global_status_callback_object;
75
76 uint64_t last_lan_discovery;
77 uint16_t next_lan_port;
78
79 bool local_discovery_enabled;
80 };
81
friendconn_net_crypto(const Friend_Connections * fr_c)82 Net_Crypto *friendconn_net_crypto(const Friend_Connections *fr_c)
83 {
84 return fr_c->net_crypto;
85 }
86
87
88 /* return true if the friendcon_id is valid.
89 * return false if the friendcon_id is not valid.
90 */
friendconn_id_valid(const Friend_Connections * fr_c,int friendcon_id)91 static bool friendconn_id_valid(const Friend_Connections *fr_c, int friendcon_id)
92 {
93 return (unsigned int)friendcon_id < fr_c->num_cons &&
94 fr_c->conns != nullptr &&
95 fr_c->conns[friendcon_id].status != FRIENDCONN_STATUS_NONE;
96 }
97
98
99 /* Set the size of the friend connections list to num.
100 *
101 * return false if realloc fails.
102 * return true if it succeeds.
103 */
realloc_friendconns(Friend_Connections * fr_c,uint32_t num)104 static bool realloc_friendconns(Friend_Connections *fr_c, uint32_t num)
105 {
106 if (num == 0) {
107 free(fr_c->conns);
108 fr_c->conns = nullptr;
109 return true;
110 }
111
112 Friend_Conn *newgroup_cons = (Friend_Conn *)realloc(fr_c->conns, num * sizeof(Friend_Conn));
113
114 if (newgroup_cons == nullptr) {
115 return false;
116 }
117
118 fr_c->conns = newgroup_cons;
119 return true;
120 }
121
122 /* Create a new empty friend connection.
123 *
124 * return -1 on failure.
125 * return friendcon_id on success.
126 */
create_friend_conn(Friend_Connections * fr_c)127 static int create_friend_conn(Friend_Connections *fr_c)
128 {
129 for (uint32_t i = 0; i < fr_c->num_cons; ++i) {
130 if (fr_c->conns[i].status == FRIENDCONN_STATUS_NONE) {
131 return i;
132 }
133 }
134
135 if (!realloc_friendconns(fr_c, fr_c->num_cons + 1)) {
136 return -1;
137 }
138
139 const int id = fr_c->num_cons;
140 ++fr_c->num_cons;
141 memset(&fr_c->conns[id], 0, sizeof(Friend_Conn));
142
143 return id;
144 }
145
146 /* Wipe a friend connection.
147 *
148 * return -1 on failure.
149 * return 0 on success.
150 */
wipe_friend_conn(Friend_Connections * fr_c,int friendcon_id)151 static int wipe_friend_conn(Friend_Connections *fr_c, int friendcon_id)
152 {
153 if (!friendconn_id_valid(fr_c, friendcon_id)) {
154 return -1;
155 }
156
157 memset(&fr_c->conns[friendcon_id], 0, sizeof(Friend_Conn));
158
159 uint32_t i;
160
161 for (i = fr_c->num_cons; i != 0; --i) {
162 if (fr_c->conns[i - 1].status != FRIENDCONN_STATUS_NONE) {
163 break;
164 }
165 }
166
167 if (fr_c->num_cons != i) {
168 fr_c->num_cons = i;
169 realloc_friendconns(fr_c, fr_c->num_cons);
170 }
171
172 return 0;
173 }
174
get_conn(const Friend_Connections * fr_c,int friendcon_id)175 static Friend_Conn *get_conn(const Friend_Connections *fr_c, int friendcon_id)
176 {
177 if (!friendconn_id_valid(fr_c, friendcon_id)) {
178 return nullptr;
179 }
180
181 return &fr_c->conns[friendcon_id];
182 }
183
184 /* return friendcon_id corresponding to the real public key on success.
185 * return -1 on failure.
186 */
getfriend_conn_id_pk(Friend_Connections * fr_c,const uint8_t * real_pk)187 int getfriend_conn_id_pk(Friend_Connections *fr_c, const uint8_t *real_pk)
188 {
189 for (uint32_t i = 0; i < fr_c->num_cons; ++i) {
190 Friend_Conn *friend_con = get_conn(fr_c, i);
191
192 if (friend_con) {
193 if (public_key_cmp(friend_con->real_public_key, real_pk) == 0) {
194 return i;
195 }
196 }
197 }
198
199 return -1;
200 }
201
202 /* Add a TCP relay associated to the friend.
203 *
204 * return -1 on failure.
205 * return 0 on success.
206 */
friend_add_tcp_relay(Friend_Connections * fr_c,int friendcon_id,IP_Port ip_port,const uint8_t * public_key)207 int friend_add_tcp_relay(Friend_Connections *fr_c, int friendcon_id, IP_Port ip_port, const uint8_t *public_key)
208 {
209 Friend_Conn *const friend_con = get_conn(fr_c, friendcon_id);
210
211 if (!friend_con) {
212 return -1;
213 }
214
215 /* Local ip and same pk means that they are hosting a TCP relay. */
216 if (ip_is_local(ip_port.ip) && public_key_cmp(friend_con->dht_temp_pk, public_key) == 0) {
217 if (!net_family_is_unspec(friend_con->dht_ip_port.ip.family)) {
218 ip_port.ip = friend_con->dht_ip_port.ip;
219 } else {
220 friend_con->hosting_tcp_relay = 0;
221 }
222 }
223
224 const uint16_t index = friend_con->tcp_relay_counter % FRIEND_MAX_STORED_TCP_RELAYS;
225
226 for (unsigned i = 0; i < FRIEND_MAX_STORED_TCP_RELAYS; ++i) {
227 if (!net_family_is_unspec(friend_con->tcp_relays[i].ip_port.ip.family)
228 && public_key_cmp(friend_con->tcp_relays[i].public_key, public_key) == 0) {
229 memset(&friend_con->tcp_relays[i], 0, sizeof(Node_format));
230 }
231 }
232
233 friend_con->tcp_relays[index].ip_port = ip_port;
234 memcpy(friend_con->tcp_relays[index].public_key, public_key, CRYPTO_PUBLIC_KEY_SIZE);
235 ++friend_con->tcp_relay_counter;
236
237 return add_tcp_relay_peer(fr_c->net_crypto, friend_con->crypt_connection_id, ip_port, public_key);
238 }
239
240 /* Connect to number saved relays for friend. */
connect_to_saved_tcp_relays(Friend_Connections * fr_c,int friendcon_id,unsigned int number)241 static void connect_to_saved_tcp_relays(Friend_Connections *fr_c, int friendcon_id, unsigned int number)
242 {
243 const Friend_Conn *const friend_con = get_conn(fr_c, friendcon_id);
244
245 if (!friend_con) {
246 return;
247 }
248
249 for (unsigned i = 0; (i < FRIEND_MAX_STORED_TCP_RELAYS) && (number != 0); ++i) {
250 const uint16_t index = (friend_con->tcp_relay_counter - (i + 1)) % FRIEND_MAX_STORED_TCP_RELAYS;
251
252 if (!net_family_is_unspec(friend_con->tcp_relays[index].ip_port.ip.family)) {
253 if (add_tcp_relay_peer(fr_c->net_crypto, friend_con->crypt_connection_id, friend_con->tcp_relays[index].ip_port,
254 friend_con->tcp_relays[index].public_key) == 0) {
255 --number;
256 }
257 }
258 }
259 }
260
send_relays(Friend_Connections * fr_c,int friendcon_id)261 static unsigned int send_relays(Friend_Connections *fr_c, int friendcon_id)
262 {
263 Friend_Conn *const friend_con = get_conn(fr_c, friendcon_id);
264
265 if (!friend_con) {
266 return 0;
267 }
268
269 Node_format nodes[MAX_SHARED_RELAYS];
270 uint8_t data[1024];
271
272 const int n = copy_connected_tcp_relays(fr_c->net_crypto, nodes, MAX_SHARED_RELAYS);
273
274 for (int i = 0; i < n; ++i) {
275 /* Associated the relays being sent with this connection.
276 * On receiving the peer will do the same which will establish the connection. */
277 friend_add_tcp_relay(fr_c, friendcon_id, nodes[i].ip_port, nodes[i].public_key);
278 }
279
280 int length = pack_nodes(data + 1, sizeof(data) - 1, nodes, n);
281
282 if (length <= 0) {
283 return 0;
284 }
285
286 data[0] = PACKET_ID_SHARE_RELAYS;
287 ++length;
288
289 if (write_cryptpacket(fr_c->net_crypto, friend_con->crypt_connection_id, data, length, 0) != -1) {
290 friend_con->share_relays_lastsent = mono_time_get(fr_c->mono_time);
291 return 1;
292 }
293
294 return 0;
295 }
296
297 /* callback for recv TCP relay nodes. */
tcp_relay_node_callback(void * object,uint32_t number,IP_Port ip_port,const uint8_t * public_key)298 static int tcp_relay_node_callback(void *object, uint32_t number, IP_Port ip_port, const uint8_t *public_key)
299 {
300 Friend_Connections *fr_c = (Friend_Connections *)object;
301 const Friend_Conn *friend_con = get_conn(fr_c, number);
302
303 if (!friend_con) {
304 return -1;
305 }
306
307 if (friend_con->crypt_connection_id != -1) {
308 return friend_add_tcp_relay(fr_c, number, ip_port, public_key);
309 }
310
311 return add_tcp_relay(fr_c->net_crypto, ip_port, public_key);
312 }
313
314 static int friend_new_connection(Friend_Connections *fr_c, int friendcon_id);
315 /* Callback for DHT ip_port changes. */
dht_ip_callback(void * object,int32_t number,IP_Port ip_port)316 static void dht_ip_callback(void *object, int32_t number, IP_Port ip_port)
317 {
318 Friend_Connections *const fr_c = (Friend_Connections *)object;
319 Friend_Conn *const friend_con = get_conn(fr_c, number);
320
321 if (!friend_con) {
322 return;
323 }
324
325 if (friend_con->crypt_connection_id == -1) {
326 friend_new_connection(fr_c, number);
327 }
328
329 set_direct_ip_port(fr_c->net_crypto, friend_con->crypt_connection_id, ip_port, 1);
330 friend_con->dht_ip_port = ip_port;
331 friend_con->dht_ip_port_lastrecv = mono_time_get(fr_c->mono_time);
332
333 if (friend_con->hosting_tcp_relay) {
334 friend_add_tcp_relay(fr_c, number, ip_port, friend_con->dht_temp_pk);
335 friend_con->hosting_tcp_relay = 0;
336 }
337 }
338
change_dht_pk(Friend_Connections * fr_c,int friendcon_id,const uint8_t * dht_public_key)339 static void change_dht_pk(Friend_Connections *fr_c, int friendcon_id, const uint8_t *dht_public_key)
340 {
341 Friend_Conn *const friend_con = get_conn(fr_c, friendcon_id);
342
343 if (!friend_con) {
344 return;
345 }
346
347 friend_con->dht_pk_lastrecv = mono_time_get(fr_c->mono_time);
348
349 if (friend_con->dht_lock) {
350 if (dht_delfriend(fr_c->dht, friend_con->dht_temp_pk, friend_con->dht_lock) != 0) {
351 LOGGER_ERROR(fr_c->logger, "a. Could not delete dht peer. Please report this.");
352 return;
353 }
354
355 friend_con->dht_lock = 0;
356 }
357
358 dht_addfriend(fr_c->dht, dht_public_key, dht_ip_callback, fr_c, friendcon_id, &friend_con->dht_lock);
359 memcpy(friend_con->dht_temp_pk, dht_public_key, CRYPTO_PUBLIC_KEY_SIZE);
360 }
361
handle_status(void * object,int number,uint8_t status,void * userdata)362 static int handle_status(void *object, int number, uint8_t status, void *userdata)
363 {
364 Friend_Connections *const fr_c = (Friend_Connections *)object;
365 Friend_Conn *const friend_con = get_conn(fr_c, number);
366
367 if (!friend_con) {
368 return -1;
369 }
370
371 bool status_changed = 0;
372
373 if (status) { /* Went online. */
374 status_changed = 1;
375 friend_con->status = FRIENDCONN_STATUS_CONNECTED;
376 friend_con->ping_lastrecv = mono_time_get(fr_c->mono_time);
377 friend_con->share_relays_lastsent = 0;
378 onion_set_friend_online(fr_c->onion_c, friend_con->onion_friendnum, status);
379 } else { /* Went offline. */
380 if (friend_con->status != FRIENDCONN_STATUS_CONNECTING) {
381 status_changed = 1;
382 friend_con->dht_pk_lastrecv = mono_time_get(fr_c->mono_time);
383 onion_set_friend_online(fr_c->onion_c, friend_con->onion_friendnum, status);
384 }
385
386 friend_con->status = FRIENDCONN_STATUS_CONNECTING;
387 friend_con->crypt_connection_id = -1;
388 friend_con->hosting_tcp_relay = 0;
389 }
390
391 if (status_changed) {
392 if (fr_c->global_status_callback) {
393 fr_c->global_status_callback(fr_c->global_status_callback_object, number, status, userdata);
394 }
395
396 for (unsigned i = 0; i < MAX_FRIEND_CONNECTION_CALLBACKS; ++i) {
397 if (friend_con->callbacks[i].status_callback) {
398 friend_con->callbacks[i].status_callback(
399 friend_con->callbacks[i].callback_object,
400 friend_con->callbacks[i].callback_id, status, userdata);
401 }
402 }
403 }
404
405 return 0;
406 }
407
408 /* Callback for dht public key changes. */
dht_pk_callback(void * object,int32_t number,const uint8_t * dht_public_key,void * userdata)409 static void dht_pk_callback(void *object, int32_t number, const uint8_t *dht_public_key, void *userdata)
410 {
411 Friend_Connections *const fr_c = (Friend_Connections *)object;
412 Friend_Conn *const friend_con = get_conn(fr_c, number);
413
414 if (!friend_con) {
415 return;
416 }
417
418 if (public_key_cmp(friend_con->dht_temp_pk, dht_public_key) == 0) {
419 return;
420 }
421
422 change_dht_pk(fr_c, number, dht_public_key);
423
424 /* if pk changed, create a new connection.*/
425 if (friend_con->crypt_connection_id != -1) {
426 crypto_kill(fr_c->net_crypto, friend_con->crypt_connection_id);
427 friend_con->crypt_connection_id = -1;
428 handle_status(object, number, 0, userdata); /* Going offline. */
429 }
430
431 friend_new_connection(fr_c, number);
432 onion_set_friend_DHT_pubkey(fr_c->onion_c, friend_con->onion_friendnum, dht_public_key);
433 }
434
handle_packet(void * object,int number,const uint8_t * data,uint16_t length,void * userdata)435 static int handle_packet(void *object, int number, const uint8_t *data, uint16_t length, void *userdata)
436 {
437 if (length == 0) {
438 return -1;
439 }
440
441 Friend_Connections *const fr_c = (Friend_Connections *)object;
442 Friend_Conn *friend_con = get_conn(fr_c, number);
443
444 if (!friend_con) {
445 return -1;
446 }
447
448 if (data[0] == PACKET_ID_FRIEND_REQUESTS) {
449 if (fr_c->fr_request_callback) {
450 fr_c->fr_request_callback(fr_c->fr_request_object, friend_con->real_public_key, data, length, userdata);
451 }
452
453 return 0;
454 }
455
456 if (data[0] == PACKET_ID_ALIVE) {
457 friend_con->ping_lastrecv = mono_time_get(fr_c->mono_time);
458 return 0;
459 }
460
461 if (data[0] == PACKET_ID_SHARE_RELAYS) {
462 Node_format nodes[MAX_SHARED_RELAYS];
463 const int n = unpack_nodes(nodes, MAX_SHARED_RELAYS, nullptr, data + 1, length - 1, 1);
464
465 if (n == -1) {
466 return -1;
467 }
468
469 for (int j = 0; j < n; ++j) {
470 friend_add_tcp_relay(fr_c, number, nodes[j].ip_port, nodes[j].public_key);
471 }
472
473 return 0;
474 }
475
476 for (unsigned i = 0; i < MAX_FRIEND_CONNECTION_CALLBACKS; ++i) {
477 if (friend_con->callbacks[i].data_callback) {
478 friend_con->callbacks[i].data_callback(
479 friend_con->callbacks[i].callback_object,
480 friend_con->callbacks[i].callback_id, data, length, userdata);
481 }
482
483 friend_con = get_conn(fr_c, number);
484
485 if (!friend_con) {
486 return -1;
487 }
488 }
489
490 return 0;
491 }
492
handle_lossy_packet(void * object,int number,const uint8_t * data,uint16_t length,void * userdata)493 static int handle_lossy_packet(void *object, int number, const uint8_t *data, uint16_t length, void *userdata)
494 {
495 if (length == 0) {
496 return -1;
497 }
498
499 Friend_Connections *const fr_c = (Friend_Connections *)object;
500 const Friend_Conn *friend_con = get_conn(fr_c, number);
501
502 if (!friend_con) {
503 return -1;
504 }
505
506 for (unsigned i = 0; i < MAX_FRIEND_CONNECTION_CALLBACKS; ++i) {
507 if (friend_con->callbacks[i].lossy_data_callback) {
508 friend_con->callbacks[i].lossy_data_callback(
509 friend_con->callbacks[i].callback_object,
510 friend_con->callbacks[i].callback_id, data, length, userdata);
511 }
512
513 friend_con = get_conn(fr_c, number);
514
515 if (!friend_con) {
516 return -1;
517 }
518 }
519
520 return 0;
521 }
522
handle_new_connections(void * object,New_Connection * n_c)523 static int handle_new_connections(void *object, New_Connection *n_c)
524 {
525 Friend_Connections *const fr_c = (Friend_Connections *)object;
526 const int friendcon_id = getfriend_conn_id_pk(fr_c, n_c->public_key);
527 Friend_Conn *const friend_con = get_conn(fr_c, friendcon_id);
528
529 if (!friend_con) {
530 return -1;
531 }
532
533 if (friend_con->crypt_connection_id != -1) {
534 return -1;
535 }
536
537 const int id = accept_crypto_connection(fr_c->net_crypto, n_c);
538
539 if (id == -1) {
540 return -1;
541 }
542
543 connection_status_handler(fr_c->net_crypto, id, &handle_status, fr_c, friendcon_id);
544 connection_data_handler(fr_c->net_crypto, id, &handle_packet, fr_c, friendcon_id);
545 connection_lossy_data_handler(fr_c->net_crypto, id, &handle_lossy_packet, fr_c, friendcon_id);
546 friend_con->crypt_connection_id = id;
547
548 if (!net_family_is_ipv4(n_c->source.ip.family) && !net_family_is_ipv6(n_c->source.ip.family)) {
549 set_direct_ip_port(fr_c->net_crypto, friend_con->crypt_connection_id, friend_con->dht_ip_port, 0);
550 } else {
551 friend_con->dht_ip_port = n_c->source;
552 friend_con->dht_ip_port_lastrecv = mono_time_get(fr_c->mono_time);
553 }
554
555 if (public_key_cmp(friend_con->dht_temp_pk, n_c->dht_public_key) != 0) {
556 change_dht_pk(fr_c, friendcon_id, n_c->dht_public_key);
557 }
558
559 nc_dht_pk_callback(fr_c->net_crypto, id, &dht_pk_callback, fr_c, friendcon_id);
560 return 0;
561 }
562
friend_new_connection(Friend_Connections * fr_c,int friendcon_id)563 static int friend_new_connection(Friend_Connections *fr_c, int friendcon_id)
564 {
565 Friend_Conn *const friend_con = get_conn(fr_c, friendcon_id);
566
567 if (!friend_con) {
568 return -1;
569 }
570
571 if (friend_con->crypt_connection_id != -1) {
572 return -1;
573 }
574
575 /* If dht_temp_pk does not contains a pk. */
576 if (!friend_con->dht_lock) {
577 return -1;
578 }
579
580 const int id = new_crypto_connection(fr_c->net_crypto, friend_con->real_public_key, friend_con->dht_temp_pk);
581
582 if (id == -1) {
583 return -1;
584 }
585
586 friend_con->crypt_connection_id = id;
587 connection_status_handler(fr_c->net_crypto, id, &handle_status, fr_c, friendcon_id);
588 connection_data_handler(fr_c->net_crypto, id, &handle_packet, fr_c, friendcon_id);
589 connection_lossy_data_handler(fr_c->net_crypto, id, &handle_lossy_packet, fr_c, friendcon_id);
590 nc_dht_pk_callback(fr_c->net_crypto, id, &dht_pk_callback, fr_c, friendcon_id);
591
592 return 0;
593 }
594
send_ping(const Friend_Connections * fr_c,int friendcon_id)595 static int send_ping(const Friend_Connections *fr_c, int friendcon_id)
596 {
597 Friend_Conn *const friend_con = get_conn(fr_c, friendcon_id);
598
599 if (!friend_con) {
600 return -1;
601 }
602
603 const uint8_t ping = PACKET_ID_ALIVE;
604 const int64_t ret = write_cryptpacket(fr_c->net_crypto, friend_con->crypt_connection_id, &ping, sizeof(ping), 0);
605
606 if (ret != -1) {
607 friend_con->ping_lastsent = mono_time_get(fr_c->mono_time);
608 return 0;
609 }
610
611 return -1;
612 }
613
614 /* Increases lock_count for the connection with friendcon_id by 1.
615 *
616 * return 0 on success.
617 * return -1 on failure.
618 */
friend_connection_lock(Friend_Connections * fr_c,int friendcon_id)619 int friend_connection_lock(Friend_Connections *fr_c, int friendcon_id)
620 {
621 Friend_Conn *const friend_con = get_conn(fr_c, friendcon_id);
622
623 if (!friend_con) {
624 return -1;
625 }
626
627 ++friend_con->lock_count;
628 return 0;
629 }
630
631 /* return FRIENDCONN_STATUS_CONNECTED if the friend is connected.
632 * return FRIENDCONN_STATUS_CONNECTING if the friend isn't connected.
633 * return FRIENDCONN_STATUS_NONE on failure.
634 */
friend_con_connected(Friend_Connections * fr_c,int friendcon_id)635 unsigned int friend_con_connected(Friend_Connections *fr_c, int friendcon_id)
636 {
637 const Friend_Conn *const friend_con = get_conn(fr_c, friendcon_id);
638
639 if (!friend_con) {
640 return 0;
641 }
642
643 return friend_con->status;
644 }
645
646 /* Copy public keys associated to friendcon_id.
647 *
648 * return 0 on success.
649 * return -1 on failure.
650 */
get_friendcon_public_keys(uint8_t * real_pk,uint8_t * dht_temp_pk,Friend_Connections * fr_c,int friendcon_id)651 int get_friendcon_public_keys(uint8_t *real_pk, uint8_t *dht_temp_pk, Friend_Connections *fr_c, int friendcon_id)
652 {
653 const Friend_Conn *const friend_con = get_conn(fr_c, friendcon_id);
654
655 if (!friend_con) {
656 return -1;
657 }
658
659 if (real_pk) {
660 memcpy(real_pk, friend_con->real_public_key, CRYPTO_PUBLIC_KEY_SIZE);
661 }
662
663 if (dht_temp_pk) {
664 memcpy(dht_temp_pk, friend_con->dht_temp_pk, CRYPTO_PUBLIC_KEY_SIZE);
665 }
666
667 return 0;
668 }
669
670 /* Set temp dht key for connection.
671 */
set_dht_temp_pk(Friend_Connections * fr_c,int friendcon_id,const uint8_t * dht_temp_pk,void * userdata)672 void set_dht_temp_pk(Friend_Connections *fr_c, int friendcon_id, const uint8_t *dht_temp_pk, void *userdata)
673 {
674 dht_pk_callback(fr_c, friendcon_id, dht_temp_pk, userdata);
675 }
676
677 /* Set the callbacks for the friend connection.
678 * index is the index (0 to (MAX_FRIEND_CONNECTION_CALLBACKS - 1)) we want the callback to set in the array.
679 *
680 * return 0 on success.
681 * return -1 on failure
682 */
friend_connection_callbacks(Friend_Connections * fr_c,int friendcon_id,unsigned int index,fc_status_cb * status_callback,fc_data_cb * data_callback,fc_lossy_data_cb * lossy_data_callback,void * object,int number)683 int friend_connection_callbacks(Friend_Connections *fr_c, int friendcon_id, unsigned int index,
684 fc_status_cb *status_callback,
685 fc_data_cb *data_callback,
686 fc_lossy_data_cb *lossy_data_callback,
687 void *object, int number)
688 {
689 Friend_Conn *const friend_con = get_conn(fr_c, friendcon_id);
690
691 if (!friend_con) {
692 return -1;
693 }
694
695 if (index >= MAX_FRIEND_CONNECTION_CALLBACKS) {
696 return -1;
697 }
698
699 friend_con->callbacks[index].status_callback = status_callback;
700 friend_con->callbacks[index].data_callback = data_callback;
701 friend_con->callbacks[index].lossy_data_callback = lossy_data_callback;
702
703 friend_con->callbacks[index].callback_object = object;
704 friend_con->callbacks[index].callback_id = number;
705
706 return 0;
707 }
708
709 /* Set global status callback for friend connections. */
set_global_status_callback(Friend_Connections * fr_c,global_status_cb * global_status_callback,void * object)710 void set_global_status_callback(Friend_Connections *fr_c, global_status_cb *global_status_callback, void *object)
711 {
712 fr_c->global_status_callback = global_status_callback;
713 fr_c->global_status_callback_object = object;
714 }
715
716 /* return the crypt_connection_id for the connection.
717 *
718 * return crypt_connection_id on success.
719 * return -1 on failure.
720 */
friend_connection_crypt_connection_id(Friend_Connections * fr_c,int friendcon_id)721 int friend_connection_crypt_connection_id(Friend_Connections *fr_c, int friendcon_id)
722 {
723 const Friend_Conn *const friend_con = get_conn(fr_c, friendcon_id);
724
725 if (!friend_con) {
726 return -1;
727 }
728
729 return friend_con->crypt_connection_id;
730 }
731
732 /* Create a new friend connection.
733 * If one to that real public key already exists, increase lock count and return it.
734 *
735 * return -1 on failure.
736 * return connection id on success.
737 */
new_friend_connection(Friend_Connections * fr_c,const uint8_t * real_public_key)738 int new_friend_connection(Friend_Connections *fr_c, const uint8_t *real_public_key)
739 {
740 int friendcon_id = getfriend_conn_id_pk(fr_c, real_public_key);
741
742 if (friendcon_id != -1) {
743 ++fr_c->conns[friendcon_id].lock_count;
744 return friendcon_id;
745 }
746
747 friendcon_id = create_friend_conn(fr_c);
748
749 if (friendcon_id == -1) {
750 return -1;
751 }
752
753 const int32_t onion_friendnum = onion_addfriend(fr_c->onion_c, real_public_key);
754
755 if (onion_friendnum == -1) {
756 return -1;
757 }
758
759 Friend_Conn *const friend_con = &fr_c->conns[friendcon_id];
760
761 friend_con->crypt_connection_id = -1;
762 friend_con->status = FRIENDCONN_STATUS_CONNECTING;
763 memcpy(friend_con->real_public_key, real_public_key, CRYPTO_PUBLIC_KEY_SIZE);
764 friend_con->onion_friendnum = onion_friendnum;
765
766 recv_tcp_relay_handler(fr_c->onion_c, onion_friendnum, &tcp_relay_node_callback, fr_c, friendcon_id);
767 onion_dht_pk_callback(fr_c->onion_c, onion_friendnum, &dht_pk_callback, fr_c, friendcon_id);
768
769 return friendcon_id;
770 }
771
772 /* Kill a friend connection.
773 *
774 * return -1 on failure.
775 * return 0 on success.
776 */
kill_friend_connection(Friend_Connections * fr_c,int friendcon_id)777 int kill_friend_connection(Friend_Connections *fr_c, int friendcon_id)
778 {
779 Friend_Conn *const friend_con = get_conn(fr_c, friendcon_id);
780
781 if (!friend_con) {
782 return -1;
783 }
784
785 if (friend_con->lock_count) {
786 --friend_con->lock_count;
787 return 0;
788 }
789
790 onion_delfriend(fr_c->onion_c, friend_con->onion_friendnum);
791 crypto_kill(fr_c->net_crypto, friend_con->crypt_connection_id);
792
793 if (friend_con->dht_lock) {
794 dht_delfriend(fr_c->dht, friend_con->dht_temp_pk, friend_con->dht_lock);
795 }
796
797 return wipe_friend_conn(fr_c, friendcon_id);
798 }
799
800
801 /* Set friend request callback.
802 *
803 * This function will be called every time a friend request packet is received.
804 */
set_friend_request_callback(Friend_Connections * fr_c,fr_request_cb * fr_request_callback,void * object)805 void set_friend_request_callback(Friend_Connections *fr_c, fr_request_cb *fr_request_callback, void *object)
806 {
807 fr_c->fr_request_callback = fr_request_callback;
808 fr_c->fr_request_object = object;
809 oniondata_registerhandler(fr_c->onion_c, CRYPTO_PACKET_FRIEND_REQ, fr_request_callback, object);
810 }
811
812 /* Send a Friend request packet.
813 *
814 * return -1 if failure.
815 * return 0 if it sent the friend request directly to the friend.
816 * return the number of peers it was routed through if it did not send it directly.
817 */
send_friend_request_packet(Friend_Connections * fr_c,int friendcon_id,uint32_t nospam_num,const uint8_t * data,uint16_t length)818 int send_friend_request_packet(Friend_Connections *fr_c, int friendcon_id, uint32_t nospam_num, const uint8_t *data,
819 uint16_t length)
820 {
821 if (1 + sizeof(nospam_num) + length > ONION_CLIENT_MAX_DATA_SIZE || length == 0) {
822 return -1;
823 }
824
825 const Friend_Conn *const friend_con = get_conn(fr_c, friendcon_id);
826
827 if (!friend_con) {
828 return -1;
829 }
830
831 VLA(uint8_t, packet, 1 + sizeof(nospam_num) + length);
832 memcpy(packet + 1, &nospam_num, sizeof(nospam_num));
833 memcpy(packet + 1 + sizeof(nospam_num), data, length);
834
835 if (friend_con->status == FRIENDCONN_STATUS_CONNECTED) {
836 packet[0] = PACKET_ID_FRIEND_REQUESTS;
837 return write_cryptpacket(fr_c->net_crypto, friend_con->crypt_connection_id, packet, SIZEOF_VLA(packet), 0) != -1;
838 }
839
840 packet[0] = CRYPTO_PACKET_FRIEND_REQ;
841 const int num = send_onion_data(fr_c->onion_c, friend_con->onion_friendnum, packet, SIZEOF_VLA(packet));
842
843 if (num <= 0) {
844 return -1;
845 }
846
847 return num;
848 }
849
850 /* Create new friend_connections instance. */
new_friend_connections(const Logger * logger,const Mono_Time * mono_time,Onion_Client * onion_c,bool local_discovery_enabled)851 Friend_Connections *new_friend_connections(const Logger *logger, const Mono_Time *mono_time, Onion_Client *onion_c,
852 bool local_discovery_enabled)
853 {
854 if (onion_c == nullptr) {
855 return nullptr;
856 }
857
858 Friend_Connections *const temp = (Friend_Connections *)calloc(1, sizeof(Friend_Connections));
859
860 if (temp == nullptr) {
861 return nullptr;
862 }
863
864 temp->mono_time = mono_time;
865 temp->logger = logger;
866 temp->dht = onion_get_dht(onion_c);
867 temp->net_crypto = onion_get_net_crypto(onion_c);
868 temp->onion_c = onion_c;
869 temp->local_discovery_enabled = local_discovery_enabled;
870 // Don't include default port in port range
871 temp->next_lan_port = TOX_PORTRANGE_FROM + 1;
872
873 new_connection_handler(temp->net_crypto, &handle_new_connections, temp);
874
875 if (temp->local_discovery_enabled) {
876 lan_discovery_init(temp->dht);
877 }
878
879 return temp;
880 }
881
882 /* Send a LAN discovery packet every LAN_DISCOVERY_INTERVAL seconds. */
lan_discovery(Friend_Connections * fr_c)883 static void lan_discovery(Friend_Connections *fr_c)
884 {
885 if (fr_c->last_lan_discovery + LAN_DISCOVERY_INTERVAL < mono_time_get(fr_c->mono_time)) {
886 const uint16_t first = fr_c->next_lan_port;
887 uint16_t last = first + PORTS_PER_DISCOVERY;
888 last = last > TOX_PORTRANGE_TO ? TOX_PORTRANGE_TO : last;
889
890 // Always send to default port
891 lan_discovery_send(net_htons(TOX_PORT_DEFAULT), fr_c->dht);
892
893 // And check some extra ports
894 for (uint16_t port = first; port < last; ++port) {
895 lan_discovery_send(net_htons(port), fr_c->dht);
896 }
897
898 // Don't include default port in port range
899 fr_c->next_lan_port = last != TOX_PORTRANGE_TO ? last : TOX_PORTRANGE_FROM + 1;
900 fr_c->last_lan_discovery = mono_time_get(fr_c->mono_time);
901 }
902 }
903
904 /* main friend_connections loop. */
do_friend_connections(Friend_Connections * fr_c,void * userdata)905 void do_friend_connections(Friend_Connections *fr_c, void *userdata)
906 {
907 const uint64_t temp_time = mono_time_get(fr_c->mono_time);
908
909 for (uint32_t i = 0; i < fr_c->num_cons; ++i) {
910 Friend_Conn *const friend_con = get_conn(fr_c, i);
911
912 if (friend_con) {
913 if (friend_con->status == FRIENDCONN_STATUS_CONNECTING) {
914 if (friend_con->dht_pk_lastrecv + FRIEND_DHT_TIMEOUT < temp_time) {
915 if (friend_con->dht_lock) {
916 dht_delfriend(fr_c->dht, friend_con->dht_temp_pk, friend_con->dht_lock);
917 friend_con->dht_lock = 0;
918 memset(friend_con->dht_temp_pk, 0, CRYPTO_PUBLIC_KEY_SIZE);
919 }
920 }
921
922 if (friend_con->dht_ip_port_lastrecv + FRIEND_DHT_TIMEOUT < temp_time) {
923 friend_con->dht_ip_port.ip.family = net_family_unspec;
924 }
925
926 if (friend_con->dht_lock) {
927 if (friend_new_connection(fr_c, i) == 0) {
928 set_direct_ip_port(fr_c->net_crypto, friend_con->crypt_connection_id, friend_con->dht_ip_port, 0);
929 connect_to_saved_tcp_relays(fr_c, i, (MAX_FRIEND_TCP_CONNECTIONS / 2)); /* Only fill it half up. */
930 }
931 }
932 } else if (friend_con->status == FRIENDCONN_STATUS_CONNECTED) {
933 if (friend_con->ping_lastsent + FRIEND_PING_INTERVAL < temp_time) {
934 send_ping(fr_c, i);
935 }
936
937 if (friend_con->share_relays_lastsent + SHARE_RELAYS_INTERVAL < temp_time) {
938 send_relays(fr_c, i);
939 }
940
941 if (friend_con->ping_lastrecv + FRIEND_CONNECTION_TIMEOUT < temp_time) {
942 /* If we stopped receiving ping packets, kill it. */
943 crypto_kill(fr_c->net_crypto, friend_con->crypt_connection_id);
944 friend_con->crypt_connection_id = -1;
945 handle_status(fr_c, i, 0, userdata); /* Going offline. */
946 }
947 }
948 }
949 }
950
951 if (fr_c->local_discovery_enabled) {
952 lan_discovery(fr_c);
953 }
954 }
955
956 /* Free everything related with friend_connections. */
kill_friend_connections(Friend_Connections * fr_c)957 void kill_friend_connections(Friend_Connections *fr_c)
958 {
959 if (!fr_c) {
960 return;
961 }
962
963 for (uint32_t i = 0; i < fr_c->num_cons; ++i) {
964 kill_friend_connection(fr_c, i);
965 }
966
967 if (fr_c->local_discovery_enabled) {
968 lan_discovery_kill(fr_c->dht);
969 }
970
971 free(fr_c);
972 }
973