Coverage Report

Created: 2024-01-26 01:52

/work/toxcore/group_connection.c
Line
Count
Source (jump to first uncovered line)
1
/* SPDX-License-Identifier: GPL-3.0-or-later
2
 * Copyright © 2016-2020 The TokTok team.
3
 * Copyright © 2015 Tox project.
4
 */
5
6
/**
7
 * An implementation of massive text only group chats.
8
 */
9
10
#include "group_connection.h"
11
12
#include <assert.h>
13
#include <stdint.h>
14
#include <stdlib.h>
15
#include <string.h>
16
17
#include "DHT.h"
18
#include "TCP_connection.h"
19
#include "ccompat.h"
20
#include "crypto_core.h"
21
#include "group_chats.h"
22
#include "group_common.h"
23
#include "logger.h"
24
#include "mono_time.h"
25
#include "network.h"
26
#include "util.h"
27
28
/** Seconds since last direct UDP packet was received before the connection is considered dead */
29
53.4k
#define GCC_UDP_DIRECT_TIMEOUT (GC_PING_TIMEOUT + 4)
30
31
/** Returns true if array entry does not contain an active packet. */
32
non_null()
33
static bool array_entry_is_empty(const GC_Message_Array_Entry *array_entry)
34
127k
{
35
127k
    assert(array_entry != nullptr);
36
127k
    return array_entry->time_added == 0;
37
127k
}
38
39
/** @brief Clears an array entry. */
40
non_null()
41
static void clear_array_entry(GC_Message_Array_Entry *const array_entry)
42
13.4k
{
43
13.4k
    if (array_entry->data != nullptr) {
44
12.3k
        free(array_entry->data);
45
12.3k
    }
46
47
13.4k
    *array_entry = (GC_Message_Array_Entry) {
48
13.4k
        nullptr
49
13.4k
    };
50
13.4k
}
51
52
/**
53
 * Clears every send array message from queue starting at the index designated by
54
 * `start_id` and ending at `end_id`, and sets the send_message_id for `gconn`
55
 * to `start_id`.
56
 */
57
non_null()
58
static void clear_send_queue_id_range(GC_Connection *gconn, uint64_t start_id, uint64_t end_id)
59
0
{
60
0
    const uint16_t start_idx = gcc_get_array_index(start_id);
61
0
    const uint16_t end_idx = gcc_get_array_index(end_id);
62
63
0
    for (uint16_t i = start_idx; i != end_idx; i = (i + 1) % GCC_BUFFER_SIZE) {
64
0
        GC_Message_Array_Entry *entry = &gconn->send_array[i];
65
0
        clear_array_entry(entry);
66
0
    }
67
68
0
    gconn->send_message_id = start_id;
69
0
}
70
71
uint16_t gcc_get_array_index(uint64_t message_id)
72
27.7k
{
73
27.7k
    return message_id % GCC_BUFFER_SIZE;
74
27.7k
}
75
76
void gcc_set_send_message_id(GC_Connection *gconn, uint64_t id)
77
1.34k
{
78
1.34k
    gconn->send_message_id = id;
79
1.34k
    gconn->send_array_start = id % GCC_BUFFER_SIZE;
80
1.34k
}
81
82
void gcc_set_recv_message_id(GC_Connection *gconn, uint64_t id)
83
13.7k
{
84
13.7k
    gconn->received_message_id = id;
85
13.7k
}
86
87
/** @brief Puts packet data in array_entry.
88
 *
89
 * Requires an empty array entry to be passed, and must not modify the passed
90
 * array entry on error.
91
 *
92
 * Return true on success.
93
 */
94
non_null(1, 2, 3) nullable(4)
95
static bool create_array_entry(const Logger *log, const Mono_Time *mono_time, GC_Message_Array_Entry *array_entry,
96
        const uint8_t *data, uint16_t length, uint8_t packet_type, uint64_t message_id)
97
14.1k
{
98
14.1k
    if (!array_entry_is_empty(array_entry)) {
99
2
        LOGGER_WARNING(log, "Failed to create array entry; entry is not empty.");
100
2
        return false;
101
2
    }
102
103
14.1k
    if (length == 0) {
104
1.18k
        array_entry->data = nullptr;
105
1.18k
        array_entry->data_length = 0;
106
12.9k
    } else {
107
12.9k
        if (data == nullptr) {  // should never happen
108
0
            LOGGER_FATAL(log, "Got null data with non-zero length (length: %u, type %u)",
109
0
                         length, packet_type);
110
0
            return false;
111
0
        }
112
113
12.9k
        uint8_t *entry_data = (uint8_t *)malloc(length);
114
115
12.9k
        if (entry_data == nullptr) {
116
11
            return false;
117
11
        }
118
119
12.9k
        memcpy(entry_data, data, length);
120
12.9k
        array_entry->data = entry_data;
121
12.9k
        array_entry->data_length = length;
122
12.9k
    }
123
124
14.1k
    const uint64_t tm = mono_time_get(mono_time);
125
126
14.1k
    array_entry->packet_type = packet_type;
127
14.1k
    array_entry->message_id = message_id;
128
14.1k
    array_entry->time_added = tm;
129
14.1k
    array_entry->last_send_try = tm;
130
131
14.1k
    return true;
132
14.1k
}
133
134
/** @brief Adds data of length to gconn's send_array.
135
 *
136
 * Returns true and increments gconn's send_message_id on success.
137
 */
138
non_null(1, 2, 3) nullable(4)
139
static bool add_to_send_array(const Logger *log, const Mono_Time *mono_time, GC_Connection *gconn, const uint8_t *data,
140
                              uint16_t length, uint8_t packet_type)
141
13.6k
{
142
    /* check if send_array is full */
143
13.6k
    if ((gconn->send_message_id % GCC_BUFFER_SIZE) == (uint16_t)(gconn->send_array_start - 1)) {
144
0
        LOGGER_DEBUG(log, "Send array overflow");
145
0
        return false;
146
0
    }
147
148
13.6k
    const uint16_t idx = gcc_get_array_index(gconn->send_message_id);
149
13.6k
    GC_Message_Array_Entry *array_entry = &gconn->send_array[idx];
150
151
13.6k
    if (!create_array_entry(log, mono_time, array_entry, data, length, packet_type, gconn->send_message_id)) {
152
12
        return false;
153
12
    }
154
155
13.6k
    ++gconn->send_message_id;
156
157
13.6k
    return true;
158
13.6k
}
159
160
int gcc_send_lossless_packet(const GC_Chat *chat, GC_Connection *gconn, const uint8_t *data, uint16_t length,
161
                             uint8_t packet_type)
162
13.0k
{
163
13.0k
    const uint64_t message_id = gconn->send_message_id;
164
165
13.0k
    if (!add_to_send_array(chat->log, chat->mono_time, gconn, data, length, packet_type)) {
166
12
        LOGGER_WARNING(chat->log, "Failed to add payload to send array: (type: 0x%02x, length: %d)", packet_type, length);
167
12
        return -1;
168
12
    }
169
170
    // If the packet fails to wrap/encrypt, we remove it from the send array, since trying to-resend
171
    // the same bad packet probably won't help much. Otherwise we don't care if it doesn't successfully
172
    // send through the wire as it will keep retrying until the connection times out.
173
13.0k
    if (gcc_encrypt_and_send_lossless_packet(chat, gconn, data, length, message_id, packet_type) == -1) {
174
39
        const uint16_t idx = gcc_get_array_index(message_id);
175
39
        GC_Message_Array_Entry *array_entry = &gconn->send_array[idx];
176
39
        clear_array_entry(array_entry);
177
39
        gconn->send_message_id = message_id;
178
39
        LOGGER_ERROR(chat->log, "Failed to encrypt payload: (type: 0x%02x, length: %d)", packet_type, length);
179
39
        return -2;
180
39
    }
181
182
13.0k
    return 0;
183
13.0k
}
184
185
186
bool gcc_send_lossless_packet_fragments(const GC_Chat *chat, GC_Connection *gconn, const uint8_t *data,
187
                                        uint16_t length, uint8_t packet_type)
188
185
{
189
185
    if (length <= MAX_GC_PACKET_CHUNK_SIZE || data == nullptr) {
190
0
        LOGGER_FATAL(chat->log, "invalid length or null data pointer");
191
0
        return false;
192
0
    }
193
194
185
    const uint16_t start_id = gconn->send_message_id;
195
196
    // First packet segment is comprised of packet type + first chunk of payload
197
185
    uint8_t chunk[MAX_GC_PACKET_CHUNK_SIZE];
198
185
    chunk[0] = packet_type;
199
185
    memcpy(chunk + 1, data, MAX_GC_PACKET_CHUNK_SIZE - 1);
200
201
185
    if (!add_to_send_array(chat->log, chat->mono_time, gconn, chunk, MAX_GC_PACKET_CHUNK_SIZE, GP_FRAGMENT)) {
202
0
        return false;
203
0
    }
204
205
185
    uint16_t processed = MAX_GC_PACKET_CHUNK_SIZE - 1;
206
207
    // The rest of the segments are added in chunks
208
448
    while (length > processed) {
209
263
        const uint16_t chunk_len = min_u16(MAX_GC_PACKET_CHUNK_SIZE, length - processed);
210
211
263
        memcpy(chunk, data + processed, chunk_len);
212
263
        processed += chunk_len;
213
214
263
        if (!add_to_send_array(chat->log, chat->mono_time, gconn, chunk, chunk_len, GP_FRAGMENT)) {
215
0
            clear_send_queue_id_range(gconn, start_id, gconn->send_message_id);
216
0
            return false;
217
0
        }
218
263
    }
219
220
    // empty packet signals the end of the sequence
221
185
    if (!add_to_send_array(chat->log, chat->mono_time, gconn, nullptr, 0, GP_FRAGMENT)) {
222
0
        clear_send_queue_id_range(gconn, start_id, gconn->send_message_id);
223
0
        return false;
224
0
    }
225
226
185
    const uint16_t start_idx = gcc_get_array_index(start_id);
227
185
    const uint16_t end_idx = gcc_get_array_index(gconn->send_message_id);
228
229
818
    for (uint16_t i = start_idx; i != end_idx; i = (i + 1) % GCC_BUFFER_SIZE) {
230
633
        const GC_Message_Array_Entry *entry = &gconn->send_array[i];
231
232
633
        if (array_entry_is_empty(entry)) {
233
0
            LOGGER_FATAL(chat->log, "array entry for packet chunk is empty");
234
0
            return false;
235
0
        }
236
237
633
        assert(entry->packet_type == GP_FRAGMENT);
238
239
633
        gcc_encrypt_and_send_lossless_packet(chat, gconn, entry->data, entry->data_length,
240
633
                                             entry->message_id, entry->packet_type);
241
633
    }
242
243
185
    return true;
244
185
}
245
246
bool gcc_handle_ack(const Logger *log, GC_Connection *gconn, uint64_t message_id)
247
13.0k
{
248
13.0k
    uint16_t idx = gcc_get_array_index(message_id);
249
13.0k
    GC_Message_Array_Entry *array_entry = &gconn->send_array[idx];
250
251
13.0k
    if (array_entry_is_empty(array_entry)) {
252
0
        return true;
253
0
    }
254
255
13.0k
    if (array_entry->message_id != message_id) {  // wrap-around indicates a connection problem
256
0
        LOGGER_DEBUG(log, "Wrap-around on message %llu", (unsigned long long)message_id);
257
0
        return false;
258
0
    }
259
260
13.0k
    clear_array_entry(array_entry);
261
262
    /* Put send_array_start in proper position */
263
13.0k
    if (idx == gconn->send_array_start) {
264
12.5k
        const uint16_t end = gconn->send_message_id % GCC_BUFFER_SIZE;
265
266
25.5k
        while (array_entry_is_empty(&gconn->send_array[idx]) && gconn->send_array_start != end) {
267
13.0k
            gconn->send_array_start = (gconn->send_array_start + 1) % GCC_BUFFER_SIZE;
268
13.0k
            idx = (idx + 1) % GCC_BUFFER_SIZE;
269
13.0k
        }
270
12.5k
    }
271
272
13.0k
    return true;
273
13.0k
}
274
275
bool gcc_ip_port_is_set(const GC_Connection *gconn)
276
86
{
277
86
    return ipport_isset(&gconn->addr.ip_port);
278
86
}
279
280
void gcc_set_ip_port(GC_Connection *gconn, const IP_Port *ipp)
281
1.00k
{
282
1.00k
    if (ipp != nullptr && ipport_isset(ipp)) {
283
423
        gconn->addr.ip_port = *ipp;
284
423
    }
285
1.00k
}
286
287
bool gcc_copy_tcp_relay(const Random *rng, Node_format *tcp_node, const GC_Connection *gconn)
288
895
{
289
895
    if (gconn == nullptr || tcp_node == nullptr) {
290
0
        return false;
291
0
    }
292
293
895
    if (gconn->tcp_relays_count == 0) {
294
895
        return false;
295
895
    }
296
297
0
    const uint32_t rand_idx = random_range_u32(rng, gconn->tcp_relays_count);
298
299
0
    if (!ipport_isset(&gconn->connected_tcp_relays[rand_idx].ip_port)) {
300
0
        return false;
301
0
    }
302
303
0
    *tcp_node = gconn->connected_tcp_relays[rand_idx];
304
305
0
    return true;
306
0
}
307
308
int gcc_save_tcp_relay(const Random *rng, GC_Connection *gconn, const Node_format *tcp_node)
309
0
{
310
0
    if (gconn == nullptr || tcp_node == nullptr) {
311
0
        return -1;
312
0
    }
313
314
0
    if (!ipport_isset(&tcp_node->ip_port)) {
315
0
        return -1;
316
0
    }
317
318
0
    for (uint16_t i = 0; i < gconn->tcp_relays_count; ++i) {
319
0
        if (pk_equal(gconn->connected_tcp_relays[i].public_key, tcp_node->public_key)) {
320
0
            return -2;
321
0
        }
322
0
    }
323
324
0
    uint32_t idx = gconn->tcp_relays_count;
325
326
0
    if (gconn->tcp_relays_count >= MAX_FRIEND_TCP_CONNECTIONS) {
327
0
        idx = random_range_u32(rng, gconn->tcp_relays_count);
328
0
    } else {
329
0
        ++gconn->tcp_relays_count;
330
0
    }
331
332
0
    gconn->connected_tcp_relays[idx] = *tcp_node;
333
334
0
    return 0;
335
0
}
336
337
/** @brief Stores `data` of length `length` in the receive array for `gconn`.
338
 *
339
 * Return true on success.
340
 */
341
non_null(1, 2, 3) nullable(4)
342
static bool store_in_recv_array(const Logger *log, const Mono_Time *mono_time, GC_Connection *gconn,
343
                                const uint8_t *data,
344
                                uint16_t length, uint8_t packet_type, uint64_t message_id)
345
450
{
346
450
    const uint16_t idx = gcc_get_array_index(message_id);
347
450
    GC_Message_Array_Entry *ary_entry = &gconn->recv_array[idx];
348
349
450
    return create_array_entry(log, mono_time, ary_entry, data, length, packet_type, message_id);
350
450
}
351
352
/**
353
 * Reassembles a fragmented packet sequence ending with the data in the receive
354
 * array at slot `message_id - 1` and starting with the last found slot containing
355
 * a GP_FRAGMENT packet when searching backwards in the array.
356
 *
357
 * The fully reassembled packet is stored in `payload`, which must be passed as a
358
 * null pointer, and must be free'd by the caller.
359
 *
360
 * Return the length of the fully reassembled packet on success.
361
 * Return 0 on failure.
362
 */
363
non_null(1, 3) nullable(2)
364
static uint16_t reassemble_packet(const Logger *log, GC_Connection *gconn, uint8_t **payload, uint64_t message_id)
365
185
{
366
185
    uint16_t end_idx = gcc_get_array_index(message_id - 1);
367
185
    uint16_t start_idx = end_idx;
368
185
    uint16_t packet_length = 0;
369
370
185
    GC_Message_Array_Entry *entry = &gconn->recv_array[end_idx];
371
372
    // search backwards in recv array until we find an empty slot or a non-fragment packet type
373
633
    while (!array_entry_is_empty(entry) && entry->packet_type == GP_FRAGMENT) {
374
448
        assert(entry->data != nullptr);
375
448
        assert(entry->data_length <= MAX_GC_PACKET_INCOMING_CHUNK_SIZE);
376
377
448
        const uint16_t diff = packet_length + entry->data_length;
378
379
448
        assert(diff > packet_length);  // overflow check
380
448
        packet_length = diff;
381
382
448
        if (packet_length > MAX_GC_PACKET_SIZE) {
383
0
            LOGGER_ERROR(log, "Payload of size %u exceeded max packet size", packet_length);  // should never happen
384
0
            return 0;
385
0
        }
386
387
448
        start_idx = start_idx > 0 ? start_idx - 1 : GCC_BUFFER_SIZE - 1;
388
448
        entry = &gconn->recv_array[start_idx];
389
390
448
        if (start_idx == end_idx) {
391
0
            LOGGER_ERROR(log, "Packet reassemble wrap-around");
392
0
            return 0;
393
0
        }
394
448
    }
395
396
185
    if (packet_length == 0) {
397
0
        return 0;
398
0
    }
399
400
185
    uint8_t *tmp_payload = (uint8_t *)malloc(packet_length);
401
402
185
    if (tmp_payload == nullptr) {
403
0
        LOGGER_ERROR(log, "Failed to allocate %u bytes for payload buffer", packet_length);
404
0
        return 0;
405
0
    }
406
407
185
    start_idx = (start_idx + 1) % GCC_BUFFER_SIZE;
408
185
    end_idx = (end_idx + 1) % GCC_BUFFER_SIZE;
409
410
185
    uint16_t processed = 0;
411
412
633
    for (uint16_t i = start_idx; i != end_idx; i = (i + 1) % GCC_BUFFER_SIZE) {
413
448
        entry = &gconn->recv_array[i];
414
415
448
        assert(processed + entry->data_length <= packet_length);
416
448
        memcpy(tmp_payload + processed, entry->data, entry->data_length);
417
448
        processed += entry->data_length;
418
419
448
        clear_array_entry(entry);
420
448
    }
421
422
185
    assert(*payload == nullptr);
423
185
    *payload = tmp_payload;
424
425
185
    return processed;
426
185
}
427
428
int gcc_handle_packet_fragment(const GC_Session *c, GC_Chat *chat, uint32_t peer_number,
429
                               GC_Connection *gconn, const uint8_t *chunk, uint16_t length, uint8_t packet_type,
430
                               uint64_t message_id, void *userdata)
431
633
{
432
633
    if (length > 0) {
433
448
        if (!store_in_recv_array(chat->log, chat->mono_time, gconn, chunk, length, packet_type, message_id)) {
434
0
            return -1;
435
0
        }
436
437
448
        gcc_set_recv_message_id(gconn, gconn->received_message_id + 1);
438
448
        gconn->last_chunk_id = message_id;
439
440
448
        return 1;
441
448
    }
442
443
185
    uint8_t sender_pk[ENC_PUBLIC_KEY_SIZE];
444
185
    memcpy(sender_pk, get_enc_key(gconn->addr.public_key), ENC_PUBLIC_KEY_SIZE);
445
446
185
    uint8_t *payload = nullptr;
447
185
    const uint16_t processed_len = reassemble_packet(chat->log, gconn, &payload, message_id);
448
449
185
    if (processed_len == 0) {
450
0
        free(payload);
451
0
        return -1;
452
0
    }
453
454
185
    if (!handle_gc_lossless_helper(c, chat, peer_number, payload + 1, processed_len - 1, payload[0], userdata)) {
455
0
        free(payload);
456
0
        return -1;
457
0
    }
458
459
    /* peer number can change from peer add operations in packet handlers */
460
185
    peer_number = get_peer_number_of_enc_pk(chat, sender_pk, false);
461
185
    gconn = get_gc_connection(chat, peer_number);
462
463
185
    if (gconn == nullptr) {
464
0
        free(payload);
465
0
        return 0;
466
0
    }
467
468
185
    gcc_set_recv_message_id(gconn, gconn->received_message_id + 1);
469
185
    gconn->last_chunk_id = 0;
470
471
185
    free(payload);
472
473
185
    return 0;
474
185
}
475
476
int gcc_handle_received_message(const Logger *log, const Mono_Time *mono_time, GC_Connection *gconn,
477
                                const uint8_t *data, uint16_t length, uint8_t packet_type, uint64_t message_id,
478
                                bool direct_conn)
479
13.8k
{
480
13.8k
    if (direct_conn) {
481
13.8k
        gconn->last_received_direct_time = mono_time_get(mono_time);
482
13.8k
    }
483
484
    /* Appears to be a duplicate packet so we discard it */
485
13.8k
    if (message_id < gconn->received_message_id + 1) {
486
454
        return 0;
487
454
    }
488
489
13.3k
    if (packet_type == GP_FRAGMENT) { // we handle packet fragments as a special case
490
633
        return 3;
491
633
    }
492
493
    /* we're missing an older message from this peer so we store it in recv_array */
494
12.7k
    if (message_id > gconn->received_message_id + 1) {
495
2
        if (!store_in_recv_array(log, mono_time, gconn, data, length, packet_type, message_id)) {
496
1
            return -1;
497
1
        }
498
499
1
        return 1;
500
2
    }
501
502
12.7k
    gcc_set_recv_message_id(gconn, gconn->received_message_id + 1);
503
504
12.7k
    return 2;
505
12.7k
}
506
507
/** @brief Handles peer_number's array entry with appropriate handler and clears it from array.
508
 *
509
 * This function increments the received message ID for `gconn`.
510
 *
511
 * Return true on success.
512
 */
513
non_null(1, 2, 3, 5) nullable(6)
514
static bool process_recv_array_entry(const GC_Session *c, GC_Chat *chat, GC_Connection *gconn, uint32_t peer_number,
515
                                     GC_Message_Array_Entry *const array_entry, void *userdata)
516
0
{
517
0
    uint8_t sender_pk[ENC_PUBLIC_KEY_SIZE];
518
0
    memcpy(sender_pk, get_enc_key(gconn->addr.public_key), ENC_PUBLIC_KEY_SIZE);
519
520
0
    const bool ret = handle_gc_lossless_helper(c, chat, peer_number, array_entry->data, array_entry->data_length,
521
0
                     array_entry->packet_type, userdata);
522
523
    /* peer number can change from peer add operations in packet handlers */
524
0
    peer_number = get_peer_number_of_enc_pk(chat, sender_pk, false);
525
0
    gconn = get_gc_connection(chat, peer_number);
526
527
0
    clear_array_entry(array_entry);
528
529
0
    if (gconn == nullptr) {
530
0
        return true;
531
0
    }
532
533
0
    if (!ret) {
534
0
        gc_send_message_ack(chat, gconn, array_entry->message_id, GR_ACK_REQ);
535
0
        return false;
536
0
    }
537
538
0
    gc_send_message_ack(chat, gconn, array_entry->message_id, GR_ACK_RECV);
539
540
0
    gcc_set_recv_message_id(gconn, gconn->received_message_id + 1);
541
542
0
    return true;
543
0
}
544
545
void gcc_check_recv_array(const GC_Session *c, GC_Chat *chat, GC_Connection *gconn, uint32_t peer_number,
546
                          void *userdata)
547
25.3k
{
548
25.3k
    if (gconn->last_chunk_id != 0) {  // dont check array if we have an unfinished fragment sequence
549
0
        return;
550
0
    }
551
552
25.3k
    const uint16_t idx = (gconn->received_message_id + 1) % GCC_BUFFER_SIZE;
553
25.3k
    GC_Message_Array_Entry *const array_entry = &gconn->recv_array[idx];
554
555
25.3k
    if (!array_entry_is_empty(array_entry)) {
556
0
        process_recv_array_entry(c, chat, gconn, peer_number, array_entry, userdata);
557
0
    }
558
25.3k
}
559
560
void gcc_resend_packets(const GC_Chat *chat, GC_Connection *gconn)
561
25.3k
{
562
25.3k
    const uint64_t tm = mono_time_get(chat->mono_time);
563
25.3k
    const uint16_t start = gconn->send_array_start;
564
25.3k
    const uint16_t end = gconn->send_message_id % GCC_BUFFER_SIZE;
565
566
25.3k
    GC_Message_Array_Entry *array_entry = &gconn->send_array[start];
567
568
25.3k
    if (array_entry_is_empty(array_entry)) {
569
22.4k
        return;
570
22.4k
    }
571
572
2.95k
    if (mono_time_is_timeout(chat->mono_time, array_entry->time_added, GC_CONFIRMED_PEER_TIMEOUT)) {
573
0
        gcc_mark_for_deletion(gconn, chat->tcp_conn, GC_EXIT_TYPE_TIMEOUT, nullptr, 0);
574
0
        LOGGER_DEBUG(chat->log, "Send array stuck; timing out peer");
575
0
        return;
576
0
    }
577
578
26.1k
    for (uint16_t i = start; i != end; i = (i + 1) % GCC_BUFFER_SIZE) {
579
23.1k
        array_entry = &gconn->send_array[i];
580
581
23.1k
        if (array_entry_is_empty(array_entry)) {
582
4.24k
            continue;
583
4.24k
        }
584
585
18.9k
        if (tm == array_entry->last_send_try) {
586
15.4k
            continue;
587
15.4k
        }
588
589
3.49k
        const uint64_t delta = array_entry->last_send_try - array_entry->time_added;
590
3.49k
        array_entry->last_send_try = tm;
591
592
        /* if this occurrs less than once per second this won't be reliable */
593
3.49k
        if (delta > 1 && is_power_of_2(delta)) {
594
481
            gcc_encrypt_and_send_lossless_packet(chat, gconn, array_entry->data, array_entry->data_length,
595
481
                                                 array_entry->message_id, array_entry->packet_type);
596
481
        }
597
3.49k
    }
598
2.95k
}
599
600
bool gcc_send_packet(const GC_Chat *chat, const GC_Connection *gconn, const uint8_t *packet, uint16_t length)
601
27.7k
{
602
27.7k
    if (packet == nullptr || length == 0) {
603
0
        return false;
604
0
    }
605
606
27.7k
    bool direct_send_attempt = false;
607
608
27.7k
    if (gcc_direct_conn_is_possible(chat, gconn)) {
609
27.7k
        if (gcc_conn_is_direct(chat->mono_time, gconn)) {
610
27.3k
            return (uint16_t) sendpacket(chat->net, &gconn->addr.ip_port, packet, length) == length;
611
27.3k
        }
612
613
380
        if ((uint16_t) sendpacket(chat->net, &gconn->addr.ip_port, packet, length) == length) {
614
379
            direct_send_attempt = true;
615
379
        }
616
380
    }
617
618
380
    const int ret = send_packet_tcp_connection(chat->tcp_conn, gconn->tcp_connection_num, packet, length);
619
380
    return ret == 0 || direct_send_attempt;
620
27.7k
}
621
622
int gcc_encrypt_and_send_lossless_packet(const GC_Chat *chat, const GC_Connection *gconn, const uint8_t *data,
623
        uint16_t length, uint64_t message_id, uint8_t packet_type)
624
14.1k
{
625
14.1k
    const uint16_t packet_size = gc_get_wrapped_packet_size(length, NET_PACKET_GC_LOSSLESS);
626
14.1k
    uint8_t *packet = (uint8_t *)malloc(packet_size);
627
628
14.1k
    if (packet == nullptr) {
629
9
        LOGGER_ERROR(chat->log, "Failed to allocate memory for packet buffer");
630
9
        return -1;
631
9
    }
632
633
14.1k
    const int enc_len = group_packet_wrap(
634
14.1k
                            chat->log, chat->rng, chat->self_public_key, gconn->session_shared_key, packet,
635
14.1k
                            packet_size, data, length, message_id, packet_type, NET_PACKET_GC_LOSSLESS);
636
637
14.1k
    if (enc_len < 0) {
638
30
        LOGGER_ERROR(chat->log, "Failed to wrap packet (type: 0x%02x, error: %d)", packet_type, enc_len);
639
30
        free(packet);
640
30
        return -1;
641
30
    }
642
643
14.1k
    if (!gcc_send_packet(chat, gconn, packet, (uint16_t)enc_len)) {
644
12
        LOGGER_DEBUG(chat->log, "Failed to send packet (type: 0x%02x, enc_len: %d)", packet_type, enc_len);
645
12
        free(packet);
646
12
        return -2;
647
12
    }
648
649
14.1k
    free(packet);
650
651
14.1k
    return 0;
652
14.1k
}
653
654
void gcc_make_session_shared_key(GC_Connection *gconn, const uint8_t *sender_pk)
655
372
{
656
372
    encrypt_precompute(sender_pk, gconn->session_secret_key, gconn->session_shared_key);
657
372
}
658
659
bool gcc_conn_is_direct(const Mono_Time *mono_time, const GC_Connection *gconn)
660
53.4k
{
661
53.4k
    return GCC_UDP_DIRECT_TIMEOUT + gconn->last_received_direct_time > mono_time_get(mono_time);
662
53.4k
}
663
664
bool gcc_direct_conn_is_possible(const GC_Chat *chat, const GC_Connection *gconn)
665
28.2k
{
666
28.2k
    return !net_family_is_unspec(gconn->addr.ip_port.ip.family) && !net_family_is_unspec(net_family(chat->net));
667
28.2k
}
668
669
void gcc_mark_for_deletion(GC_Connection *gconn, TCP_Connections *tcp_conn, Group_Exit_Type type,
670
                           const uint8_t *part_message, uint16_t length)
671
134
{
672
134
    if (gconn == nullptr) {
673
0
        return;
674
0
    }
675
676
134
    if (gconn->pending_delete) {
677
0
        return;
678
0
    }
679
680
134
    gconn->pending_delete = true;
681
134
    gconn->exit_info.exit_type = type;
682
683
134
    kill_tcp_connection_to(tcp_conn, gconn->tcp_connection_num);
684
685
134
    if (length > 0 && length <= MAX_GC_PART_MESSAGE_SIZE  && part_message != nullptr) {
686
1
        memcpy(gconn->exit_info.part_message, part_message, length);
687
1
        gconn->exit_info.length = length;
688
1
    }
689
134
}
690
691
void gcc_peer_cleanup(GC_Connection *gconn)
692
615
{
693
5.03M
    for (size_t i = 0; i < GCC_BUFFER_SIZE; ++i) {
694
5.03M
        free(gconn->send_array[i].data);
695
5.03M
        free(gconn->recv_array[i].data);
696
5.03M
    }
697
698
615
    free(gconn->recv_array);
699
615
    free(gconn->send_array);
700
701
615
    crypto_memunlock(gconn->session_secret_key, sizeof(gconn->session_secret_key));
702
615
    crypto_memunlock(gconn->session_shared_key, sizeof(gconn->session_shared_key));
703
615
    crypto_memzero(gconn, sizeof(GC_Connection));
704
615
}
705
706
void gcc_cleanup(const GC_Chat *chat)
707
2.61k
{
708
3.09k
    for (uint32_t i = 0; i < chat->numpeers; ++i) {
709
481
        GC_Connection *gconn = get_gc_connection(chat, i);
710
481
        assert(gconn != nullptr);
711
712
481
        gcc_peer_cleanup(gconn);
713
481
    }
714
2.61k
}