Coverage Report

Created: 2024-01-26 01:52

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