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