/work/toxcore/group_announce.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 | | #include "group_announce.h" |
7 | | |
8 | | #include <string.h> |
9 | | |
10 | | #include "DHT.h" |
11 | | #include "attributes.h" |
12 | | #include "ccompat.h" |
13 | | #include "crypto_core.h" |
14 | | #include "logger.h" |
15 | | #include "mem.h" |
16 | | #include "mono_time.h" |
17 | | #include "network.h" |
18 | | |
19 | | /** |
20 | | * Removes `announces` from `gc_announces_list`. |
21 | | */ |
22 | | static void remove_announces(GC_Announces_List *_Nonnull gc_announces_list, GC_Announces *_Nonnull announces) |
23 | 242 | { |
24 | 242 | if (announces == nullptr || gc_announces_list == nullptr) { |
25 | 0 | return; |
26 | 0 | } |
27 | | |
28 | 242 | if (announces->prev_announce != nullptr) { |
29 | 67 | announces->prev_announce->next_announce = announces->next_announce; |
30 | 175 | } else { |
31 | 175 | gc_announces_list->root_announces = announces->next_announce; |
32 | 175 | } |
33 | | |
34 | 242 | if (announces->next_announce != nullptr) { |
35 | 113 | announces->next_announce->prev_announce = announces->prev_announce; |
36 | 113 | } |
37 | | |
38 | 242 | mem_delete(gc_announces_list->mem, announces); |
39 | 242 | } |
40 | | |
41 | | /** |
42 | | * Returns the announce designated by `chat_id`. |
43 | | * Returns null if no announce is found. |
44 | | */ |
45 | | static GC_Announces *_Nullable get_announces_by_chat_id(const GC_Announces_List *_Nonnull gc_announces_list, const uint8_t *_Nonnull chat_id) |
46 | 4.66k | { |
47 | 4.66k | GC_Announces *announces = gc_announces_list->root_announces; |
48 | | |
49 | 6.78k | while (announces != nullptr) { |
50 | 5.42k | if (memcmp(announces->chat_id, chat_id, CHAT_ID_SIZE) == 0) { |
51 | 3.30k | return announces; |
52 | 3.30k | } |
53 | | |
54 | 2.12k | announces = announces->next_announce; |
55 | 2.12k | } |
56 | | |
57 | 1.36k | return nullptr; |
58 | 4.66k | } |
59 | | |
60 | | int gca_get_announces(const GC_Announces_List *gc_announces_list, GC_Announce *gc_announces, uint8_t max_nodes, |
61 | | const uint8_t *chat_id, const uint8_t *except_public_key) |
62 | 2.05k | { |
63 | 2.05k | if (gc_announces == nullptr || gc_announces_list == nullptr || chat_id == nullptr || max_nodes == 0 |
64 | 2.05k | || except_public_key == nullptr) { |
65 | 99 | return -1; |
66 | 99 | } |
67 | | |
68 | 1.95k | const GC_Announces *announces = get_announces_by_chat_id(gc_announces_list, chat_id); |
69 | | |
70 | 1.95k | if (announces == nullptr) { |
71 | 429 | return 0; |
72 | 429 | } |
73 | | |
74 | 1.52k | uint16_t added_count = 0; |
75 | | |
76 | 15.2k | for (size_t i = 0; i < announces->index && i < GCA_MAX_SAVED_ANNOUNCES_PER_GC && added_count < max_nodes; ++i) { |
77 | 13.7k | const size_t index = i % GCA_MAX_SAVED_ANNOUNCES_PER_GC; |
78 | | |
79 | 13.7k | if (memcmp(except_public_key, announces->peer_announces[index].base_announce.peer_public_key, |
80 | 13.7k | ENC_PUBLIC_KEY_SIZE) == 0) { |
81 | 4.51k | continue; |
82 | 4.51k | } |
83 | | |
84 | 9.22k | bool already_added = false; |
85 | | |
86 | 20.4k | for (size_t j = 0; j < added_count; ++j) { |
87 | 16.1k | if (memcmp(gc_announces[j].peer_public_key, announces->peer_announces[index].base_announce.peer_public_key, |
88 | 16.1k | ENC_PUBLIC_KEY_SIZE) == 0) { |
89 | 4.93k | already_added = true; |
90 | 4.93k | break; |
91 | 4.93k | } |
92 | 16.1k | } |
93 | | |
94 | 9.22k | if (!already_added) { |
95 | 4.28k | gc_announces[added_count] = announces->peer_announces[index].base_announce; |
96 | 4.28k | ++added_count; |
97 | 4.28k | } |
98 | 9.22k | } |
99 | | |
100 | 1.52k | return added_count; |
101 | 1.95k | } |
102 | | |
103 | | uint16_t gca_pack_announces_list_size(uint16_t count) |
104 | 2 | { |
105 | 2 | return count * GCA_ANNOUNCE_MAX_SIZE; |
106 | 2 | } |
107 | | |
108 | | int gca_pack_announce(const Logger *log, uint8_t *data, uint16_t length, const GC_Announce *announce) |
109 | 4.00k | { |
110 | 4.00k | if (length < GCA_ANNOUNCE_MAX_SIZE) { |
111 | 4 | LOGGER_ERROR(log, "Invalid announce length: %u", length); |
112 | 4 | return -1; |
113 | 4 | } |
114 | | |
115 | 4.00k | if (data == nullptr) { |
116 | 0 | LOGGER_ERROR(log, "data is null"); |
117 | 0 | return -1; |
118 | 0 | } |
119 | | |
120 | 4.00k | if (announce == nullptr) { |
121 | 0 | LOGGER_ERROR(log, "announce is null"); |
122 | 0 | return -1; |
123 | 0 | } |
124 | | |
125 | 4.00k | uint16_t offset = 0; |
126 | 4.00k | memcpy(data + offset, announce->peer_public_key, ENC_PUBLIC_KEY_SIZE); |
127 | 4.00k | offset += ENC_PUBLIC_KEY_SIZE; |
128 | | |
129 | 4.00k | data[offset] = announce->ip_port_is_set ? 1 : 0; |
130 | 4.00k | ++offset; |
131 | | |
132 | 4.00k | data[offset] = announce->tcp_relays_count; |
133 | 4.00k | ++offset; |
134 | | |
135 | 4.00k | if (!announce->ip_port_is_set && announce->tcp_relays_count == 0) { |
136 | 52 | LOGGER_ERROR(log, "Failed to pack announce: no valid ip_port or tcp relay"); |
137 | 52 | return -1; |
138 | 52 | } |
139 | | |
140 | 3.95k | if (announce->ip_port_is_set) { |
141 | 3.61k | const int ip_port_length = pack_ip_port(log, data + offset, length - offset, &announce->ip_port); |
142 | | |
143 | 3.61k | if (ip_port_length == -1) { |
144 | 0 | LOGGER_ERROR(log, "Failed to pack ip_port"); |
145 | 0 | return -1; |
146 | 0 | } |
147 | | |
148 | 3.61k | offset += ip_port_length; |
149 | 3.61k | } |
150 | | |
151 | 3.95k | const int nodes_length = pack_nodes(log, data + offset, length - offset, announce->tcp_relays, |
152 | 3.95k | announce->tcp_relays_count); |
153 | | |
154 | 3.95k | if (nodes_length == -1) { |
155 | 0 | LOGGER_ERROR(log, "Failed to pack TCP nodes"); |
156 | 0 | return -1; |
157 | 0 | } |
158 | | |
159 | 3.95k | return nodes_length + offset; |
160 | 3.95k | } |
161 | | |
162 | | /** |
163 | | * Unpacks `announce` into `data` buffer of size `length`. |
164 | | * |
165 | | * Returns the size of the unpacked data on success. |
166 | | * Returns -1 on failure. |
167 | | */ |
168 | | static int gca_unpack_announce(const Logger *_Nonnull log, const uint8_t *_Nonnull data, uint16_t length, GC_Announce *_Nonnull announce) |
169 | 6.90k | { |
170 | 6.90k | if (length < ENC_PUBLIC_KEY_SIZE + 2) { |
171 | 201 | LOGGER_ERROR(log, "Invalid announce length: %u", length); |
172 | 201 | return -1; |
173 | 201 | } |
174 | | |
175 | 6.70k | if (data == nullptr) { |
176 | 0 | LOGGER_ERROR(log, "data is null"); |
177 | 0 | return -1; |
178 | 0 | } |
179 | | |
180 | 6.70k | if (announce == nullptr) { |
181 | 0 | LOGGER_ERROR(log, "announce is null"); |
182 | 0 | return -1; |
183 | 0 | } |
184 | | |
185 | 6.70k | uint16_t offset = 0; |
186 | 6.70k | memcpy(announce->peer_public_key, data + offset, ENC_PUBLIC_KEY_SIZE); |
187 | 6.70k | offset += ENC_PUBLIC_KEY_SIZE; |
188 | | |
189 | 6.70k | net_unpack_bool(&data[offset], &announce->ip_port_is_set); |
190 | 6.70k | ++offset; |
191 | | |
192 | 6.70k | announce->tcp_relays_count = data[offset]; |
193 | 6.70k | ++offset; |
194 | | |
195 | 6.70k | if (announce->tcp_relays_count > GCA_MAX_ANNOUNCED_TCP_RELAYS) { |
196 | 66 | return -1; |
197 | 66 | } |
198 | | |
199 | 6.63k | if (announce->ip_port_is_set) { |
200 | 5.16k | if (length - offset == 0) { |
201 | 195 | return -1; |
202 | 195 | } |
203 | | |
204 | 4.96k | const int ip_port_length = unpack_ip_port(&announce->ip_port, data + offset, length - offset, false); |
205 | | |
206 | 4.96k | if (ip_port_length == -1) { |
207 | 263 | LOGGER_ERROR(log, "Failed to unpack ip_port"); |
208 | 263 | return -1; |
209 | 263 | } |
210 | | |
211 | 4.70k | offset += ip_port_length; |
212 | 4.70k | } |
213 | | |
214 | 6.18k | uint16_t nodes_length; |
215 | 6.18k | const int nodes_count = unpack_nodes(announce->tcp_relays, announce->tcp_relays_count, &nodes_length, |
216 | 6.18k | data + offset, length - offset, true); |
217 | | |
218 | 6.18k | if (nodes_count != announce->tcp_relays_count) { |
219 | 219 | LOGGER_ERROR(log, "Failed to unpack TCP nodes"); |
220 | 219 | return -1; |
221 | 219 | } |
222 | | |
223 | 5.96k | return offset + nodes_length; |
224 | 6.18k | } |
225 | | |
226 | | int gca_pack_public_announce(const Logger *log, uint8_t *data, uint16_t length, |
227 | | const GC_Public_Announce *public_announce) |
228 | 138 | { |
229 | 138 | if (public_announce == nullptr || data == nullptr || length < CHAT_ID_SIZE) { |
230 | 1 | return -1; |
231 | 1 | } |
232 | | |
233 | 137 | memcpy(data, public_announce->chat_public_key, CHAT_ID_SIZE); |
234 | | |
235 | 137 | const int packed_size = gca_pack_announce(log, data + CHAT_ID_SIZE, length - CHAT_ID_SIZE, |
236 | 137 | &public_announce->base_announce); |
237 | | |
238 | 137 | if (packed_size < 0) { |
239 | 4 | LOGGER_ERROR(log, "Failed to pack public group announce"); |
240 | 4 | return -1; |
241 | 4 | } |
242 | | |
243 | 133 | return packed_size + CHAT_ID_SIZE; |
244 | 137 | } |
245 | | |
246 | | int gca_unpack_public_announce(const Logger *log, const uint8_t *data, uint16_t length, |
247 | | GC_Public_Announce *public_announce) |
248 | 2.98k | { |
249 | 2.98k | if (length < CHAT_ID_SIZE) { |
250 | 211 | LOGGER_ERROR(log, "invalid public announce length: %u", length); |
251 | 211 | return -1; |
252 | 211 | } |
253 | | |
254 | 2.77k | if (data == nullptr) { |
255 | 0 | LOGGER_ERROR(log, "data is null"); |
256 | 0 | return -1; |
257 | 0 | } |
258 | | |
259 | 2.77k | if (public_announce == nullptr) { |
260 | 0 | LOGGER_ERROR(log, "public_announce is null"); |
261 | 0 | return -1; |
262 | 0 | } |
263 | | |
264 | 2.77k | memcpy(public_announce->chat_public_key, data, CHAT_ID_SIZE); |
265 | | |
266 | 2.77k | const int base_announce_size = gca_unpack_announce(log, data + ENC_PUBLIC_KEY_SIZE, length - ENC_PUBLIC_KEY_SIZE, |
267 | 2.77k | &public_announce->base_announce); |
268 | | |
269 | 2.77k | if (base_announce_size == -1) { |
270 | 908 | LOGGER_ERROR(log, "Failed to unpack group announce"); |
271 | 908 | return -1; |
272 | 908 | } |
273 | | |
274 | 1.86k | return base_announce_size + CHAT_ID_SIZE; |
275 | 2.77k | } |
276 | | |
277 | | int gca_pack_announces_list(const Logger *log, uint8_t *data, uint16_t length, const GC_Announce *announces, |
278 | | uint8_t announces_count, size_t *processed) |
279 | 1.19k | { |
280 | 1.19k | if (data == nullptr) { |
281 | 0 | LOGGER_ERROR(log, "data is null"); |
282 | 0 | return -1; |
283 | 0 | } |
284 | | |
285 | 1.19k | if (announces == nullptr) { |
286 | 0 | LOGGER_ERROR(log, "announces is null"); |
287 | 0 | return -1; |
288 | 0 | } |
289 | | |
290 | 1.19k | uint16_t offset = 0; |
291 | | |
292 | 4.98k | for (size_t i = 0; i < announces_count; ++i) { |
293 | 3.83k | const int packed_length = gca_pack_announce(log, data + offset, length - offset, &announces[i]); |
294 | | |
295 | 3.83k | if (packed_length < 0) { |
296 | 52 | LOGGER_ERROR(log, "Failed to pack group announce"); |
297 | 52 | return -1; |
298 | 52 | } |
299 | | |
300 | 3.78k | offset += packed_length; |
301 | 3.78k | } |
302 | | |
303 | 1.14k | if (processed != nullptr) { |
304 | 1.14k | *processed = offset; |
305 | 1.14k | } |
306 | | |
307 | 1.14k | return announces_count; |
308 | 1.19k | } |
309 | | |
310 | | int gca_unpack_announces_list(const Logger *log, const uint8_t *data, uint16_t length, GC_Announce *announces, |
311 | | uint8_t max_count) |
312 | 1.19k | { |
313 | 1.19k | if (data == nullptr) { |
314 | 0 | LOGGER_ERROR(log, "data is null"); |
315 | 0 | return -1; |
316 | 0 | } |
317 | | |
318 | 1.19k | if (announces == nullptr) { |
319 | 0 | LOGGER_ERROR(log, "announces is null"); |
320 | 0 | return -1; |
321 | 0 | } |
322 | | |
323 | 1.19k | uint16_t offset = 0; |
324 | 1.19k | int announces_count = 0; |
325 | | |
326 | 5.29k | for (size_t i = 0; i < max_count && length > offset; ++i) { |
327 | 4.13k | const int unpacked_length = gca_unpack_announce(log, data + offset, length - offset, &announces[i]); |
328 | | |
329 | 4.13k | if (unpacked_length == -1) { |
330 | 36 | LOGGER_WARNING(log, "Failed to unpack group announce: %d %d", length, offset); |
331 | 36 | return -1; |
332 | 36 | } |
333 | | |
334 | 4.09k | offset += unpacked_length; |
335 | 4.09k | ++announces_count; |
336 | 4.09k | } |
337 | | |
338 | 1.16k | return announces_count; |
339 | 1.19k | } |
340 | | |
341 | | static GC_Announces *_Nullable gca_new_announces(const Memory *_Nonnull mem, GC_Announces_List *_Nonnull gc_announces_list, const GC_Public_Announce *_Nonnull public_announce) |
342 | 520 | { |
343 | 520 | GC_Announces *announces = (GC_Announces *)mem_alloc(mem, sizeof(GC_Announces)); |
344 | | |
345 | 520 | if (announces == nullptr) { |
346 | 1 | return nullptr; |
347 | 1 | } |
348 | | |
349 | 519 | announces->index = 0; |
350 | 519 | announces->prev_announce = nullptr; |
351 | | |
352 | 519 | if (gc_announces_list->root_announces != nullptr) { |
353 | 267 | gc_announces_list->root_announces->prev_announce = announces; |
354 | 267 | } |
355 | | |
356 | 519 | announces->next_announce = gc_announces_list->root_announces; |
357 | 519 | gc_announces_list->root_announces = announces; |
358 | 519 | memcpy(announces->chat_id, public_announce->chat_public_key, CHAT_ID_SIZE); |
359 | | |
360 | 519 | return announces; |
361 | 520 | } |
362 | | |
363 | | GC_Peer_Announce *gca_add_announce(const Memory *mem, const Mono_Time *mono_time, GC_Announces_List *gc_announces_list, |
364 | | const GC_Public_Announce *public_announce) |
365 | 2.05k | { |
366 | 2.05k | if (gc_announces_list == nullptr || public_announce == nullptr) { |
367 | 0 | return nullptr; |
368 | 0 | } |
369 | | |
370 | 2.05k | GC_Announces *announces = get_announces_by_chat_id(gc_announces_list, public_announce->chat_public_key); |
371 | | |
372 | | // No entry for this chat_id exists so we create one |
373 | 2.05k | if (announces == nullptr) { |
374 | 520 | announces = gca_new_announces(mem, gc_announces_list, public_announce); |
375 | | |
376 | 520 | if (announces == nullptr) { |
377 | 1 | return nullptr; |
378 | 1 | } |
379 | 520 | } |
380 | | |
381 | 2.05k | const uint64_t cur_time = mono_time_get(mono_time); |
382 | | |
383 | 2.05k | announces->last_announce_received_timestamp = cur_time; |
384 | | |
385 | 2.05k | const uint64_t index = announces->index % GCA_MAX_SAVED_ANNOUNCES_PER_GC; |
386 | | |
387 | 2.05k | GC_Peer_Announce *gc_peer_announce = &announces->peer_announces[index]; |
388 | | |
389 | 2.05k | gc_peer_announce->base_announce = public_announce->base_announce; |
390 | | |
391 | 2.05k | gc_peer_announce->timestamp = cur_time; |
392 | | |
393 | 2.05k | ++announces->index; |
394 | | |
395 | 2.05k | return gc_peer_announce; |
396 | 2.05k | } |
397 | | |
398 | | bool gca_is_valid_announce(const GC_Announce *announce) |
399 | 3.01k | { |
400 | 3.01k | if (announce == nullptr) { |
401 | 0 | return false; |
402 | 0 | } |
403 | | |
404 | 3.01k | return announce->tcp_relays_count > 0 || announce->ip_port_is_set; |
405 | 3.01k | } |
406 | | |
407 | | GC_Announces_List *new_gca_list(const Memory *mem) |
408 | 2.10k | { |
409 | 2.10k | GC_Announces_List *announces_list = (GC_Announces_List *)mem_alloc(mem, sizeof(GC_Announces_List)); |
410 | | |
411 | 2.10k | if (announces_list == nullptr) { |
412 | 3 | return nullptr; |
413 | 3 | } |
414 | | |
415 | 2.09k | announces_list->mem = mem; |
416 | | |
417 | 2.09k | return announces_list; |
418 | 2.10k | } |
419 | | |
420 | | void kill_gca(GC_Announces_List *announces_list) |
421 | 2.08k | { |
422 | 2.08k | if (announces_list == nullptr) { |
423 | 1 | return; |
424 | 1 | } |
425 | | |
426 | 2.08k | GC_Announces *root = announces_list->root_announces; |
427 | | |
428 | 2.34k | while (root != nullptr) { |
429 | 262 | GC_Announces *next = root->next_announce; |
430 | 262 | mem_delete(announces_list->mem, root); |
431 | 262 | root = next; |
432 | 262 | } |
433 | | |
434 | 2.08k | mem_delete(announces_list->mem, announces_list); |
435 | 2.08k | } |
436 | | |
437 | | |
438 | | /* How often we run do_gca() */ |
439 | 124k | #define GCA_DO_GCA_TIMEOUT 1 |
440 | | |
441 | | void do_gca(const Mono_Time *mono_time, GC_Announces_List *gc_announces_list) |
442 | 124k | { |
443 | 124k | if (gc_announces_list == nullptr) { |
444 | 0 | return; |
445 | 0 | } |
446 | | |
447 | 124k | if (!mono_time_is_timeout(mono_time, gc_announces_list->last_timeout_check, GCA_DO_GCA_TIMEOUT)) { |
448 | 118k | return; |
449 | 118k | } |
450 | | |
451 | 6.65k | gc_announces_list->last_timeout_check = mono_time_get(mono_time); |
452 | | |
453 | 6.65k | GC_Announces *announces = gc_announces_list->root_announces; |
454 | | |
455 | 7.42k | while (announces != nullptr) { |
456 | 765 | if (mono_time_is_timeout(mono_time, announces->last_announce_received_timestamp, GCA_ANNOUNCE_SAVE_TIMEOUT)) { |
457 | 1 | GC_Announces *to_delete = announces; |
458 | 1 | announces = announces->next_announce; |
459 | 1 | remove_announces(gc_announces_list, to_delete); |
460 | 1 | continue; |
461 | 1 | } |
462 | | |
463 | 764 | announces = announces->next_announce; |
464 | 764 | } |
465 | 6.65k | } |
466 | | |
467 | | void cleanup_gca(GC_Announces_List *gc_announces_list, const uint8_t *chat_id) |
468 | 652 | { |
469 | 652 | if (gc_announces_list == nullptr || chat_id == nullptr) { |
470 | 0 | return; |
471 | 0 | } |
472 | | |
473 | 652 | GC_Announces *announces = get_announces_by_chat_id(gc_announces_list, chat_id); |
474 | | |
475 | 652 | if (announces != nullptr) { |
476 | 241 | remove_announces(gc_announces_list, announces); |
477 | 241 | } |
478 | 652 | } |