Coverage Report

Created: 2025-10-08 19:34

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