/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 | 34.8k | #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 | 0 | #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 | 71.8k | { |
38 | 71.8k | assert(array_entry != nullptr); |
39 | 71.8k | return array_entry->time_added == 0; |
40 | 71.8k | } |
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 | 988 | { |
45 | 988 | mem_delete(mem, array_entry->data); |
46 | | |
47 | 988 | *array_entry = (GC_Message_Array_Entry) { |
48 | 988 | nullptr |
49 | 988 | }; |
50 | 988 | } |
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 | 2.07k | { |
72 | 2.07k | return message_id % GCC_BUFFER_SIZE; |
73 | 2.07k | } |
74 | | |
75 | | void gcc_set_send_message_id(GC_Connection *gconn, uint64_t id) |
76 | 536 | { |
77 | 536 | gconn->send_message_id = id; |
78 | 536 | gconn->send_array_start = id % GCC_BUFFER_SIZE; |
79 | 536 | } |
80 | | |
81 | | void gcc_set_recv_message_id(GC_Connection *gconn, uint64_t id) |
82 | 1.17k | { |
83 | 1.17k | gconn->received_message_id = id; |
84 | 1.17k | } |
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 | 1.08k | { |
96 | 1.08k | 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 | 1.08k | if (length == 0) { |
102 | 302 | array_entry->data = nullptr; |
103 | 302 | array_entry->data_length = 0; |
104 | 781 | } else { |
105 | 781 | 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 | 781 | uint8_t *entry_data = (uint8_t *)mem_balloc(mem, length); |
112 | | |
113 | 781 | if (entry_data == nullptr) { |
114 | 0 | return false; |
115 | 0 | } |
116 | | |
117 | 781 | memcpy(entry_data, data, length); |
118 | 781 | array_entry->data = entry_data; |
119 | 781 | array_entry->data_length = length; |
120 | 781 | } |
121 | | |
122 | 1.08k | const uint64_t tm = mono_time_get(mono_time); |
123 | | |
124 | 1.08k | array_entry->packet_type = packet_type; |
125 | 1.08k | array_entry->message_id = message_id; |
126 | 1.08k | array_entry->time_added = tm; |
127 | 1.08k | array_entry->last_send_try = tm; |
128 | | |
129 | 1.08k | return true; |
130 | 1.08k | } |
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 | 1.08k | { |
139 | | /* check if send_array is full */ |
140 | 1.08k | 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 | 1.08k | const uint16_t idx = gcc_get_array_index(gconn->send_message_id); |
146 | 1.08k | GC_Message_Array_Entry *array_entry = &gconn->send_array[idx]; |
147 | | |
148 | 1.08k | if (!create_array_entry(log, mem, mono_time, array_entry, data, length, packet_type, gconn->send_message_id)) { |
149 | 0 | return false; |
150 | 0 | } |
151 | | |
152 | 1.08k | ++gconn->send_message_id; |
153 | | |
154 | 1.08k | return true; |
155 | 1.08k | } |
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 | 1.08k | { |
160 | 1.08k | const uint64_t message_id = gconn->send_message_id; |
161 | | |
162 | 1.08k | if (!add_to_send_array(chat->log, chat->mem, chat->mono_time, gconn, data, length, packet_type)) { |
163 | 0 | LOGGER_WARNING(chat->log, "Failed to add payload to send array: (type: 0x%02x, length: %d)", packet_type, length); |
164 | 0 | return -1; |
165 | 0 | } |
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 | 1.08k | if (gcc_encrypt_and_send_lossless_packet(chat, gconn, data, length, message_id, packet_type) == -1) { |
171 | 0 | const uint16_t idx = gcc_get_array_index(message_id); |
172 | 0 | GC_Message_Array_Entry *array_entry = &gconn->send_array[idx]; |
173 | 0 | clear_array_entry(chat->mem, array_entry); |
174 | 0 | gconn->send_message_id = message_id; |
175 | 0 | LOGGER_ERROR(chat->log, "Failed to encrypt payload: (type: 0x%02x, length: %d)", packet_type, length); |
176 | 0 | return -2; |
177 | 0 | } |
178 | | |
179 | 1.08k | return 0; |
180 | 1.08k | } |
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 | 0 | { |
185 | 0 | 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 | 0 | const uint16_t start_id = gconn->send_message_id; |
191 | | |
192 | | // First packet segment is comprised of packet type + first chunk of payload |
193 | 0 | uint8_t chunk[MAX_GC_PACKET_CHUNK_SIZE]; |
194 | 0 | chunk[0] = packet_type; |
195 | 0 | memcpy(chunk + 1, data, MAX_GC_PACKET_CHUNK_SIZE - 1); |
196 | |
|
197 | 0 | 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 | 0 | uint16_t processed = MAX_GC_PACKET_CHUNK_SIZE - 1; |
202 | | |
203 | | // The rest of the segments are added in chunks |
204 | 0 | while (length > processed) { |
205 | 0 | const uint16_t chunk_len = min_u16(MAX_GC_PACKET_CHUNK_SIZE, length - processed); |
206 | |
|
207 | 0 | memcpy(chunk, data + processed, chunk_len); |
208 | 0 | processed += chunk_len; |
209 | |
|
210 | 0 | 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 | 0 | } |
215 | | |
216 | | // empty packet signals the end of the sequence |
217 | 0 | 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 | 0 | const uint16_t start_idx = gcc_get_array_index(start_id); |
223 | 0 | const uint16_t end_idx = gcc_get_array_index(gconn->send_message_id); |
224 | |
|
225 | 0 | for (uint16_t i = start_idx; i != end_idx; i = (i + 1) % GCC_BUFFER_SIZE) { |
226 | 0 | const GC_Message_Array_Entry *entry = &gconn->send_array[i]; |
227 | |
|
228 | 0 | 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 | 0 | assert(entry->packet_type == GP_FRAGMENT); |
234 | | |
235 | 0 | gcc_encrypt_and_send_lossless_packet(chat, gconn, entry->data, entry->data_length, |
236 | 0 | entry->message_id, entry->packet_type); |
237 | 0 | } |
238 | | |
239 | 0 | return true; |
240 | 0 | } |
241 | | |
242 | | bool gcc_handle_ack(const Logger *log, const Memory *mem, GC_Connection *gconn, uint64_t message_id) |
243 | 988 | { |
244 | 988 | uint16_t idx = gcc_get_array_index(message_id); |
245 | 988 | GC_Message_Array_Entry *array_entry = &gconn->send_array[idx]; |
246 | | |
247 | 988 | if (array_entry_is_empty(array_entry)) { |
248 | 0 | return true; |
249 | 0 | } |
250 | | |
251 | 988 | 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 | 988 | clear_array_entry(mem, array_entry); |
257 | | |
258 | | /* Put send_array_start in proper position */ |
259 | 988 | if (idx == gconn->send_array_start) { |
260 | 979 | const uint16_t end = gconn->send_message_id % GCC_BUFFER_SIZE; |
261 | | |
262 | 1.95k | while (array_entry_is_empty(&gconn->send_array[idx]) && gconn->send_array_start != end) { |
263 | 979 | gconn->send_array_start = (gconn->send_array_start + 1) % GCC_BUFFER_SIZE; |
264 | 979 | idx = (idx + 1) % GCC_BUFFER_SIZE; |
265 | 979 | } |
266 | 979 | } |
267 | | |
268 | 988 | return true; |
269 | 988 | } |
270 | | |
271 | | bool gcc_ip_port_is_set(const GC_Connection *gconn) |
272 | 3.02k | { |
273 | 3.02k | return ipport_isset(&gconn->addr.ip_port); |
274 | 3.02k | } |
275 | | |
276 | | void gcc_set_ip_port(GC_Connection *gconn, const IP_Port *ipp) |
277 | 487 | { |
278 | 487 | if (ipp != nullptr && ipport_isset(ipp)) { |
279 | 301 | gconn->addr.ip_port = *ipp; |
280 | 301 | } |
281 | 487 | } |
282 | | |
283 | | bool gcc_copy_tcp_relay(const Random *rng, Node_format *tcp_node, const GC_Connection *gconn) |
284 | 300 | { |
285 | 300 | if (gconn == nullptr || tcp_node == nullptr) { |
286 | 0 | return false; |
287 | 0 | } |
288 | | |
289 | 300 | if (gconn->tcp_relays_count == 0) { |
290 | 286 | return false; |
291 | 286 | } |
292 | | |
293 | 14 | const uint32_t rand_idx = random_range_u32(rng, gconn->tcp_relays_count); |
294 | | |
295 | 14 | if (!ipport_isset(&gconn->connected_tcp_relays[rand_idx].ip_port)) { |
296 | 0 | return false; |
297 | 0 | } |
298 | | |
299 | 14 | *tcp_node = gconn->connected_tcp_relays[rand_idx]; |
300 | | |
301 | 14 | return true; |
302 | 14 | } |
303 | | |
304 | | int gcc_save_tcp_relay(const Random *rng, GC_Connection *gconn, const Node_format *tcp_node) |
305 | 5 | { |
306 | 5 | if (gconn == nullptr || tcp_node == nullptr) { |
307 | 0 | return -1; |
308 | 0 | } |
309 | | |
310 | 5 | if (!ipport_isset(&tcp_node->ip_port)) { |
311 | 0 | return -1; |
312 | 0 | } |
313 | | |
314 | 5 | 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 | 5 | uint32_t idx = gconn->tcp_relays_count; |
321 | | |
322 | 5 | if (gconn->tcp_relays_count >= MAX_FRIEND_TCP_CONNECTIONS) { |
323 | 0 | idx = random_range_u32(rng, gconn->tcp_relays_count); |
324 | 5 | } else { |
325 | 5 | ++gconn->tcp_relays_count; |
326 | 5 | } |
327 | | |
328 | 5 | gconn->connected_tcp_relays[idx] = *tcp_node; |
329 | | |
330 | 5 | return 0; |
331 | 5 | } |
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 | 0 | { |
341 | 0 | const uint16_t idx = gcc_get_array_index(message_id); |
342 | 0 | GC_Message_Array_Entry *ary_entry = &gconn->recv_array[idx]; |
343 | |
|
344 | 0 | return create_array_entry(log, mem, mono_time, ary_entry, data, length, packet_type, message_id); |
345 | 0 | } |
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 | 0 | { |
360 | 0 | uint16_t end_idx = gcc_get_array_index(message_id - 1); |
361 | 0 | uint16_t start_idx = end_idx; |
362 | 0 | uint16_t packet_length = 0; |
363 | |
|
364 | 0 | 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 | 0 | while (!array_entry_is_empty(entry) && entry->packet_type == GP_FRAGMENT) { |
368 | 0 | assert(entry->data != nullptr); |
369 | 0 | assert(entry->data_length <= MAX_GC_PACKET_INCOMING_CHUNK_SIZE); |
370 | | |
371 | 0 | const uint16_t diff = packet_length + entry->data_length; |
372 | |
|
373 | 0 | assert(diff > packet_length); // overflow check |
374 | 0 | packet_length = diff; |
375 | |
|
376 | 0 | 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 | 0 | start_idx = start_idx > 0 ? start_idx - 1 : GCC_BUFFER_SIZE - 1; |
382 | 0 | entry = &gconn->recv_array[start_idx]; |
383 | |
|
384 | 0 | if (start_idx == end_idx) { |
385 | 0 | LOGGER_ERROR(log, "Packet reassemble wrap-around"); |
386 | 0 | return 0; |
387 | 0 | } |
388 | 0 | } |
389 | | |
390 | 0 | if (packet_length == 0) { |
391 | 0 | return 0; |
392 | 0 | } |
393 | | |
394 | 0 | uint8_t *tmp_payload = (uint8_t *)mem_balloc(mem, packet_length); |
395 | |
|
396 | 0 | 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 | 0 | start_idx = (start_idx + 1) % GCC_BUFFER_SIZE; |
402 | 0 | end_idx = (end_idx + 1) % GCC_BUFFER_SIZE; |
403 | |
|
404 | 0 | uint16_t processed = 0; |
405 | |
|
406 | 0 | for (uint16_t i = start_idx; i != end_idx; i = (i + 1) % GCC_BUFFER_SIZE) { |
407 | 0 | entry = &gconn->recv_array[i]; |
408 | |
|
409 | 0 | assert(processed + entry->data_length <= packet_length); |
410 | 0 | memcpy(tmp_payload + processed, entry->data, entry->data_length); |
411 | 0 | processed += entry->data_length; |
412 | |
|
413 | 0 | clear_array_entry(mem, entry); |
414 | 0 | } |
415 | | |
416 | 0 | assert(*payload == nullptr); |
417 | 0 | *payload = tmp_payload; |
418 | |
|
419 | 0 | return processed; |
420 | 0 | } |
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 | 0 | { |
426 | 0 | if (length > 0) { |
427 | 0 | 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 | 0 | gcc_set_recv_message_id(gconn, gconn->received_message_id + 1); |
432 | 0 | gconn->last_chunk_id = message_id; |
433 | |
|
434 | 0 | return 1; |
435 | 0 | } |
436 | | |
437 | 0 | uint8_t sender_pk[ENC_PUBLIC_KEY_SIZE]; |
438 | 0 | memcpy(sender_pk, get_enc_key(&gconn->addr.public_key), ENC_PUBLIC_KEY_SIZE); |
439 | |
|
440 | 0 | uint8_t *payload = nullptr; |
441 | 0 | const uint16_t processed_len = reassemble_packet(chat->log, chat->mem, gconn, &payload, message_id); |
442 | |
|
443 | 0 | if (processed_len == 0) { |
444 | 0 | mem_delete(chat->mem, payload); |
445 | 0 | return -1; |
446 | 0 | } |
447 | | |
448 | 0 | 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 | 0 | peer_number = get_peer_number_of_enc_pk(chat, sender_pk, false); |
455 | 0 | GC_Connection *new_gconn = get_gc_connection(chat, peer_number); |
456 | |
|
457 | 0 | if (new_gconn == nullptr) { |
458 | 0 | mem_delete(chat->mem, payload); |
459 | 0 | return 0; |
460 | 0 | } |
461 | | |
462 | 0 | gconn = new_gconn; |
463 | |
|
464 | 0 | gcc_set_recv_message_id(gconn, gconn->received_message_id + 1); |
465 | 0 | gconn->last_chunk_id = 0; |
466 | |
|
467 | 0 | mem_delete(chat->mem, payload); |
468 | |
|
469 | 0 | return 0; |
470 | 0 | } |
471 | | |
472 | | int gcc_handle_received_message(const Logger *log, const Memory *mem, const Mono_Time *mono_time, GC_Connection *gconn, |
473 | | const uint8_t *data, uint16_t length, uint8_t packet_type, uint64_t message_id, |
474 | | bool direct_conn) |
475 | 1.03k | { |
476 | 1.03k | if (direct_conn) { |
477 | 998 | gconn->last_received_direct_time = mono_time_get(mono_time); |
478 | 998 | } |
479 | | |
480 | | /* Appears to be a duplicate packet so we discard it */ |
481 | 1.03k | if (message_id < gconn->received_message_id + 1) { |
482 | 3 | return 0; |
483 | 3 | } |
484 | | |
485 | 1.02k | if (packet_type == GP_FRAGMENT) { // we handle packet fragments as a special case |
486 | 0 | return 3; |
487 | 0 | } |
488 | | |
489 | | /* we're missing an older message from this peer so we store it in recv_array */ |
490 | 1.02k | if (message_id > gconn->received_message_id + 1) { |
491 | 0 | if (!store_in_recv_array(log, mem, mono_time, gconn, data, length, packet_type, message_id)) { |
492 | 0 | return -1; |
493 | 0 | } |
494 | | |
495 | 0 | return 1; |
496 | 0 | } |
497 | | |
498 | 1.02k | gcc_set_recv_message_id(gconn, gconn->received_message_id + 1); |
499 | | |
500 | 1.02k | return 2; |
501 | 1.02k | } |
502 | | |
503 | | /** @brief Handles peer_number's array entry with appropriate handler and clears it from array. |
504 | | * |
505 | | * This function increments the received message ID for `gconn`. |
506 | | * |
507 | | * Return true on success. |
508 | | */ |
509 | | static bool process_recv_array_entry(const GC_Session *_Nonnull c, GC_Chat *_Nonnull chat, GC_Connection *_Nonnull gconn, uint32_t peer_number, |
510 | | GC_Message_Array_Entry *_Nonnull array_entry, void *_Nullable userdata) |
511 | 0 | { |
512 | 0 | uint8_t sender_pk[ENC_PUBLIC_KEY_SIZE]; |
513 | 0 | memcpy(sender_pk, get_enc_key(&gconn->addr.public_key), ENC_PUBLIC_KEY_SIZE); |
514 | |
|
515 | 0 | const bool ret = handle_gc_lossless_helper(c, chat, peer_number, array_entry->data, array_entry->data_length, |
516 | 0 | array_entry->packet_type, userdata); |
517 | | |
518 | | /* peer number can change from peer add operations in packet handlers */ |
519 | 0 | peer_number = get_peer_number_of_enc_pk(chat, sender_pk, false); |
520 | 0 | gconn = get_gc_connection(chat, peer_number); |
521 | |
|
522 | 0 | clear_array_entry(chat->mem, array_entry); |
523 | |
|
524 | 0 | if (gconn == nullptr) { |
525 | 0 | return true; |
526 | 0 | } |
527 | | |
528 | 0 | if (!ret) { |
529 | 0 | gc_send_message_ack(chat, gconn, array_entry->message_id, GR_ACK_REQ); |
530 | 0 | return false; |
531 | 0 | } |
532 | | |
533 | 0 | gc_send_message_ack(chat, gconn, array_entry->message_id, GR_ACK_RECV); |
534 | |
|
535 | 0 | gcc_set_recv_message_id(gconn, gconn->received_message_id + 1); |
536 | |
|
537 | 0 | return true; |
538 | 0 | } |
539 | | |
540 | | void gcc_check_recv_array(const GC_Session *c, GC_Chat *chat, GC_Connection *gconn, uint32_t peer_number, |
541 | | void *userdata) |
542 | 32.5k | { |
543 | 32.5k | if (gconn->last_chunk_id != 0) { // dont check array if we have an unfinished fragment sequence |
544 | 0 | return; |
545 | 0 | } |
546 | | |
547 | 32.5k | const uint16_t idx = (gconn->received_message_id + 1) % GCC_BUFFER_SIZE; |
548 | 32.5k | GC_Message_Array_Entry *const array_entry = &gconn->recv_array[idx]; |
549 | | |
550 | 32.5k | if (!array_entry_is_empty(array_entry)) { |
551 | 0 | process_recv_array_entry(c, chat, gconn, peer_number, array_entry, userdata); |
552 | 0 | } |
553 | 32.5k | } |
554 | | |
555 | | void gcc_resend_packets(const GC_Chat *chat, GC_Connection *gconn) |
556 | 32.5k | { |
557 | 32.5k | const uint64_t tm = mono_time_get(chat->mono_time); |
558 | 32.5k | const uint16_t start = gconn->send_array_start; |
559 | 32.5k | const uint16_t end = gconn->send_message_id % GCC_BUFFER_SIZE; |
560 | | |
561 | 32.5k | const GC_Message_Array_Entry *array_entry = &gconn->send_array[start]; |
562 | | |
563 | 32.5k | if (array_entry_is_empty(array_entry)) { |
564 | 31.7k | return; |
565 | 31.7k | } |
566 | | |
567 | 769 | if (mono_time_is_timeout(chat->mono_time, array_entry->time_added, GC_CONFIRMED_PEER_TIMEOUT)) { |
568 | 0 | gcc_mark_for_deletion(gconn, chat->tcp_conn, GC_EXIT_TYPE_TIMEOUT, nullptr, 0); |
569 | 0 | LOGGER_DEBUG(chat->log, "Send array stuck; timing out peer"); |
570 | 0 | return; |
571 | 0 | } |
572 | | |
573 | 3.47k | for (uint16_t i = start; i != end; i = (i + 1) % GCC_BUFFER_SIZE) { |
574 | 2.70k | GC_Message_Array_Entry *const array_entry_loop = &gconn->send_array[i]; |
575 | | |
576 | 2.70k | if (array_entry_is_empty(array_entry_loop)) { |
577 | 873 | continue; |
578 | 873 | } |
579 | | |
580 | 1.83k | if (tm == array_entry_loop->last_send_try) { |
581 | 1.79k | continue; |
582 | 1.79k | } |
583 | | |
584 | 42 | const uint64_t delta = array_entry_loop->last_send_try - array_entry_loop->time_added; |
585 | 42 | array_entry_loop->last_send_try = tm; |
586 | | |
587 | | /* if this occurrs less than once per second this won't be reliable */ |
588 | 42 | if (delta > 1 && is_power_of_2(delta)) { |
589 | 6 | gcc_encrypt_and_send_lossless_packet(chat, gconn, array_entry_loop->data, array_entry_loop->data_length, |
590 | 6 | array_entry_loop->message_id, array_entry_loop->packet_type); |
591 | 6 | } |
592 | 42 | } |
593 | 769 | } |
594 | | |
595 | | bool gcc_send_packet(const GC_Chat *chat, GC_Connection *gconn, const uint8_t *packet, uint16_t length) |
596 | 2.18k | { |
597 | 2.18k | if (packet == nullptr || length == 0) { |
598 | 0 | return false; |
599 | 0 | } |
600 | | |
601 | 2.18k | bool direct_send_attempt = false; |
602 | | |
603 | 2.18k | if (gcc_direct_conn_is_possible(chat, gconn)) { |
604 | 2.11k | if (gcc_conn_is_direct(chat->mono_time, gconn)) { |
605 | 2.11k | return (uint16_t) sendpacket(chat->net, &gconn->addr.ip_port, packet, length) == length; |
606 | 2.11k | } |
607 | | |
608 | 0 | if (gcc_conn_should_try_direct(chat->mono_time, gconn)) { |
609 | 0 | gconn->last_sent_direct_try_time = mono_time_get(chat->mono_time); |
610 | |
|
611 | 0 | if ((uint16_t) sendpacket(chat->net, &gconn->addr.ip_port, packet, length) == length) { |
612 | 0 | direct_send_attempt = true; |
613 | 0 | } |
614 | 0 | } |
615 | 0 | } |
616 | | |
617 | 69 | if (chat->tcp_conn == nullptr) { |
618 | 0 | return false; |
619 | 0 | } |
620 | 69 | const int ret = send_packet_tcp_connection(chat->tcp_conn, gconn->tcp_connection_num, packet, length); |
621 | 69 | return ret == 0 || direct_send_attempt; |
622 | 69 | } |
623 | | |
624 | | int gcc_encrypt_and_send_lossless_packet(const GC_Chat *chat, GC_Connection *gconn, const uint8_t *data, |
625 | | uint16_t length, uint64_t message_id, uint8_t packet_type) |
626 | 1.08k | { |
627 | 1.08k | const uint16_t packet_size = gc_get_wrapped_packet_size(length, NET_PACKET_GC_LOSSLESS); |
628 | 1.08k | uint8_t *packet = (uint8_t *)mem_balloc(chat->mem, packet_size); |
629 | | |
630 | 1.08k | if (packet == nullptr) { |
631 | 0 | LOGGER_ERROR(chat->log, "Failed to allocate memory for packet buffer"); |
632 | 0 | return -1; |
633 | 0 | } |
634 | | |
635 | 1.08k | const int enc_len = group_packet_wrap( |
636 | 1.08k | chat->log, chat->mem, chat->rng, chat->self_public_key.enc, gconn->session_shared_key, packet, |
637 | 1.08k | packet_size, data, length, message_id, packet_type, NET_PACKET_GC_LOSSLESS); |
638 | | |
639 | 1.08k | if (enc_len < 0) { |
640 | 0 | LOGGER_ERROR(chat->log, "Failed to wrap packet (type: 0x%02x, error: %d)", packet_type, enc_len); |
641 | 0 | mem_delete(chat->mem, packet); |
642 | 0 | return -1; |
643 | 0 | } |
644 | | |
645 | 1.08k | if (!gcc_send_packet(chat, gconn, packet, (uint16_t)enc_len)) { |
646 | 0 | LOGGER_DEBUG(chat->log, "Failed to send packet (type: 0x%02x, enc_len: %d)", packet_type, enc_len); |
647 | 0 | mem_delete(chat->mem, packet); |
648 | 0 | return -2; |
649 | 0 | } |
650 | | |
651 | 1.08k | mem_delete(chat->mem, packet); |
652 | | |
653 | 1.08k | return 0; |
654 | 1.08k | } |
655 | | |
656 | | void gcc_make_session_shared_key(GC_Connection *gconn, const uint8_t *sender_pk) |
657 | 146 | { |
658 | 146 | encrypt_precompute(sender_pk, gconn->session_secret_key, gconn->session_shared_key); |
659 | 146 | } |
660 | | |
661 | | bool gcc_conn_is_direct(const Mono_Time *mono_time, const GC_Connection *gconn) |
662 | 34.8k | { |
663 | 34.8k | return GCC_UDP_DIRECT_TIMEOUT + gconn->last_received_direct_time > mono_time_get(mono_time); |
664 | 34.8k | } |
665 | | |
666 | | bool gcc_conn_should_try_direct(const Mono_Time *mono_time, const GC_Connection *gconn) |
667 | 0 | { |
668 | 0 | return mono_time_is_timeout(mono_time, gconn->last_sent_direct_try_time, GCC_UDP_DIRECT_RETRY); |
669 | 0 | } |
670 | | |
671 | | bool gcc_direct_conn_is_possible(const GC_Chat *chat, const GC_Connection *gconn) |
672 | 2.37k | { |
673 | 2.37k | return !net_family_is_unspec(gconn->addr.ip_port.ip.family) && !net_family_is_unspec(net_family(chat->net)); |
674 | 2.37k | } |
675 | | |
676 | | void gcc_mark_for_deletion(GC_Connection *gconn, TCP_Connections *tcp_conn, Group_Exit_Type type, |
677 | | const uint8_t *part_message, uint16_t length) |
678 | 77 | { |
679 | 77 | if (gconn == nullptr) { |
680 | 0 | return; |
681 | 0 | } |
682 | | |
683 | 77 | if (gconn->pending_delete) { |
684 | 0 | return; |
685 | 0 | } |
686 | | |
687 | 77 | gconn->pending_delete = true; |
688 | 77 | gconn->exit_info.exit_type = type; |
689 | | |
690 | 77 | if (tcp_conn != nullptr) { |
691 | 77 | kill_tcp_connection_to(tcp_conn, gconn->tcp_connection_num); |
692 | 77 | } |
693 | | |
694 | 77 | if (length > 0 && length <= MAX_GC_PART_MESSAGE_SIZE && part_message != nullptr) { |
695 | 1 | memcpy(gconn->exit_info.part_message, part_message, length); |
696 | 1 | gconn->exit_info.length = length; |
697 | 1 | } |
698 | 77 | } |
699 | | |
700 | | void gcc_peer_cleanup(const Memory *mem, GC_Connection *gconn) |
701 | 347 | { |
702 | 711k | for (size_t i = 0; i < GCC_BUFFER_SIZE; ++i) { |
703 | 710k | mem_delete(mem, gconn->send_array[i].data); |
704 | 710k | mem_delete(mem, gconn->recv_array[i].data); |
705 | 710k | } |
706 | | |
707 | 347 | mem_delete(mem, gconn->recv_array); |
708 | 347 | mem_delete(mem, gconn->send_array); |
709 | | |
710 | 347 | crypto_memunlock(gconn->session_secret_key, sizeof(gconn->session_secret_key)); |
711 | 347 | crypto_memunlock(gconn->session_shared_key, sizeof(gconn->session_shared_key)); |
712 | 347 | crypto_memzero(gconn, sizeof(GC_Connection)); |
713 | 347 | } |
714 | | |
715 | | void gcc_cleanup(const GC_Chat *chat) |
716 | 7.40k | { |
717 | 7.68k | for (uint32_t i = 0; i < chat->numpeers; ++i) { |
718 | 276 | GC_Connection *gconn = get_gc_connection(chat, i); |
719 | 276 | assert(gconn != nullptr); |
720 | | |
721 | 276 | gcc_peer_cleanup(chat->mem, gconn); |
722 | 276 | } |
723 | 7.40k | } |