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