Coverage Report

Created: 2026-10-24 08:16

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