/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 <stdlib.h> |
9 | | #include <string.h> |
10 | | |
11 | | #include "DHT.h" |
12 | | #include "ccompat.h" |
13 | | #include "logger.h" |
14 | | #include "mono_time.h" |
15 | | #include "network.h" |
16 | | |
17 | | /** |
18 | | * Removes `announces` from `gc_announces_list`. |
19 | | */ |
20 | | non_null() |
21 | | static void remove_announces(GC_Announces_List *gc_announces_list, GC_Announces *announces) |
22 | 187 | { |
23 | 187 | if (announces == nullptr || gc_announces_list == nullptr) { |
24 | 0 | return; |
25 | 0 | } |
26 | | |
27 | 187 | if (announces->prev_announce != nullptr) { |
28 | 11 | announces->prev_announce->next_announce = announces->next_announce; |
29 | 176 | } else { |
30 | 176 | gc_announces_list->root_announces = announces->next_announce; |
31 | 176 | } |
32 | | |
33 | 187 | if (announces->next_announce != nullptr) { |
34 | 103 | announces->next_announce->prev_announce = announces->prev_announce; |
35 | 103 | } |
36 | | |
37 | 187 | free(announces); |
38 | 187 | } |
39 | | |
40 | | /** |
41 | | * Returns the announce designated by `chat_id`. |
42 | | * Returns null if no announce is found. |
43 | | */ |
44 | | non_null() |
45 | | static GC_Announces *get_announces_by_chat_id(const GC_Announces_List *gc_announces_list, const uint8_t *chat_id) |
46 | 5.86k | { |
47 | 5.86k | GC_Announces *announces = gc_announces_list->root_announces; |
48 | | |
49 | 8.00k | while (announces != nullptr) { |
50 | 6.37k | if (memcmp(announces->chat_id, chat_id, CHAT_ID_SIZE) == 0) { |
51 | 4.23k | return announces; |
52 | 4.23k | } |
53 | | |
54 | 2.13k | announces = announces->next_announce; |
55 | 2.13k | } |
56 | | |
57 | 1.63k | return nullptr; |
58 | 5.86k | } |
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.65k | { |
63 | 2.65k | if (gc_announces == nullptr || gc_announces_list == nullptr || chat_id == nullptr || max_nodes == 0 |
64 | 2.65k | || except_public_key == nullptr) { |
65 | 169 | return -1; |
66 | 169 | } |
67 | | |
68 | 2.48k | const GC_Announces *announces = get_announces_by_chat_id(gc_announces_list, chat_id); |
69 | | |
70 | 2.48k | if (announces == nullptr) { |
71 | 606 | return 0; |
72 | 606 | } |
73 | | |
74 | 1.88k | uint16_t added_count = 0; |
75 | | |
76 | 20.1k | for (size_t i = 0; i < announces->index && i < GCA_MAX_SAVED_ANNOUNCES_PER_GC && added_count < max_nodes; ++i) { |
77 | 18.2k | const size_t index = i % GCA_MAX_SAVED_ANNOUNCES_PER_GC; |
78 | | |
79 | 18.2k | if (memcmp(except_public_key, &announces->peer_announces[index].base_announce.peer_public_key, |
80 | 18.2k | ENC_PUBLIC_KEY_SIZE) == 0) { |
81 | 5.43k | continue; |
82 | 5.43k | } |
83 | | |
84 | 12.8k | bool already_added = false; |
85 | | |
86 | 23.0k | for (size_t j = 0; j < added_count; ++j) { |
87 | 17.6k | if (memcmp(&gc_announces[j].peer_public_key, &announces->peer_announces[index].base_announce.peer_public_key, |
88 | 17.6k | ENC_PUBLIC_KEY_SIZE) == 0) { |
89 | 7.43k | already_added = true; |
90 | 7.43k | break; |
91 | 7.43k | } |
92 | 17.6k | } |
93 | | |
94 | 12.8k | if (!already_added) { |
95 | 5.38k | gc_announces[added_count] = announces->peer_announces[index].base_announce; |
96 | 5.38k | ++added_count; |
97 | 5.38k | } |
98 | 12.8k | } |
99 | | |
100 | 1.88k | return added_count; |
101 | 2.48k | } |
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 | 5.37k | { |
110 | 5.37k | if (length < GCA_ANNOUNCE_MAX_SIZE) { |
111 | 5 | LOGGER_ERROR(log, "Invalid announce length: %u", length); |
112 | 5 | return -1; |
113 | 5 | } |
114 | | |
115 | 5.37k | if (data == nullptr) { |
116 | 0 | LOGGER_ERROR(log, "data is null"); |
117 | 0 | return -1; |
118 | 0 | } |
119 | | |
120 | 5.37k | if (announce == nullptr) { |
121 | 0 | LOGGER_ERROR(log, "announce is null"); |
122 | 0 | return -1; |
123 | 0 | } |
124 | | |
125 | 5.37k | uint16_t offset = 0; |
126 | 5.37k | memcpy(data + offset, announce->peer_public_key, ENC_PUBLIC_KEY_SIZE); |
127 | 5.37k | offset += ENC_PUBLIC_KEY_SIZE; |
128 | | |
129 | 5.37k | data[offset] = announce->ip_port_is_set ? 1 : 0; |
130 | 5.37k | ++offset; |
131 | | |
132 | 5.37k | data[offset] = announce->tcp_relays_count; |
133 | 5.37k | ++offset; |
134 | | |
135 | 5.37k | if (!announce->ip_port_is_set && announce->tcp_relays_count == 0) { |
136 | 45 | LOGGER_ERROR(log, "Failed to pack announce: no valid ip_port or tcp relay"); |
137 | 45 | return -1; |
138 | 45 | } |
139 | | |
140 | 5.32k | if (announce->ip_port_is_set) { |
141 | 5.16k | const int ip_port_length = pack_ip_port(log, data + offset, length - offset, &announce->ip_port); |
142 | | |
143 | 5.16k | if (ip_port_length == -1) { |
144 | 0 | LOGGER_ERROR(log, "Failed to pack ip_port"); |
145 | 0 | return -1; |
146 | 0 | } |
147 | | |
148 | 5.16k | offset += ip_port_length; |
149 | 5.16k | } |
150 | | |
151 | 5.32k | const int nodes_length = pack_nodes(log, data + offset, length - offset, announce->tcp_relays, |
152 | 5.32k | announce->tcp_relays_count); |
153 | | |
154 | 5.32k | if (nodes_length == -1) { |
155 | 0 | LOGGER_ERROR(log, "Failed to pack TCP nodes"); |
156 | 0 | return -1; |
157 | 0 | } |
158 | | |
159 | 5.32k | return nodes_length + offset; |
160 | 5.32k | } |
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 | | non_null() |
169 | | static int gca_unpack_announce(const Logger *log, const uint8_t *data, uint16_t length, GC_Announce *announce) |
170 | 8.19k | { |
171 | 8.19k | if (length < ENC_PUBLIC_KEY_SIZE + 2) { |
172 | 173 | LOGGER_ERROR(log, "Invalid announce length: %u", length); |
173 | 173 | return -1; |
174 | 173 | } |
175 | | |
176 | 8.01k | if (data == nullptr) { |
177 | 0 | LOGGER_ERROR(log, "data is null"); |
178 | 0 | return -1; |
179 | 0 | } |
180 | | |
181 | 8.01k | if (announce == nullptr) { |
182 | 0 | LOGGER_ERROR(log, "announce is null"); |
183 | 0 | return -1; |
184 | 0 | } |
185 | | |
186 | 8.01k | uint16_t offset = 0; |
187 | 8.01k | memcpy(announce->peer_public_key, data + offset, ENC_PUBLIC_KEY_SIZE); |
188 | 8.01k | offset += ENC_PUBLIC_KEY_SIZE; |
189 | | |
190 | 8.01k | net_unpack_bool(&data[offset], &announce->ip_port_is_set); |
191 | 8.01k | ++offset; |
192 | | |
193 | 8.01k | announce->tcp_relays_count = data[offset]; |
194 | 8.01k | ++offset; |
195 | | |
196 | 8.01k | if (announce->tcp_relays_count > GCA_MAX_ANNOUNCED_TCP_RELAYS) { |
197 | 142 | return -1; |
198 | 142 | } |
199 | | |
200 | 7.87k | if (announce->ip_port_is_set) { |
201 | 6.64k | if (length - offset == 0) { |
202 | 3 | return -1; |
203 | 3 | } |
204 | | |
205 | 6.64k | const int ip_port_length = unpack_ip_port(&announce->ip_port, data + offset, length - offset, false); |
206 | | |
207 | 6.64k | if (ip_port_length == -1) { |
208 | 131 | LOGGER_ERROR(log, "Failed to unpack ip_port"); |
209 | 131 | return -1; |
210 | 131 | } |
211 | | |
212 | 6.51k | offset += ip_port_length; |
213 | 6.51k | } |
214 | | |
215 | 7.74k | uint16_t nodes_length; |
216 | 7.74k | const int nodes_count = unpack_nodes(announce->tcp_relays, announce->tcp_relays_count, &nodes_length, |
217 | 7.74k | data + offset, length - offset, true); |
218 | | |
219 | 7.74k | if (nodes_count != announce->tcp_relays_count) { |
220 | 148 | LOGGER_ERROR(log, "Failed to unpack TCP nodes"); |
221 | 148 | return -1; |
222 | 148 | } |
223 | | |
224 | 7.59k | return offset + nodes_length; |
225 | 7.74k | } |
226 | | |
227 | | int gca_pack_public_announce(const Logger *log, uint8_t *data, uint16_t length, |
228 | | const GC_Public_Announce *public_announce) |
229 | 283 | { |
230 | 283 | if (public_announce == nullptr || data == nullptr || length < CHAT_ID_SIZE) { |
231 | 1 | return -1; |
232 | 1 | } |
233 | | |
234 | 282 | memcpy(data, public_announce->chat_public_key, CHAT_ID_SIZE); |
235 | | |
236 | 282 | const int packed_size = gca_pack_announce(log, data + CHAT_ID_SIZE, length - CHAT_ID_SIZE, |
237 | 282 | &public_announce->base_announce); |
238 | | |
239 | 282 | if (packed_size < 0) { |
240 | 4 | LOGGER_ERROR(log, "Failed to pack public group announce"); |
241 | 4 | return -1; |
242 | 4 | } |
243 | | |
244 | 278 | return packed_size + CHAT_ID_SIZE; |
245 | 282 | } |
246 | | |
247 | | int gca_unpack_public_announce(const Logger *log, const uint8_t *data, uint16_t length, |
248 | | GC_Public_Announce *public_announce) |
249 | 6.25k | { |
250 | 6.25k | if (length < CHAT_ID_SIZE) { |
251 | 3.41k | LOGGER_ERROR(log, "invalid public announce length: %u", length); |
252 | 3.41k | return -1; |
253 | 3.41k | } |
254 | | |
255 | 2.84k | if (data == nullptr) { |
256 | 0 | LOGGER_ERROR(log, "data is null"); |
257 | 0 | return -1; |
258 | 0 | } |
259 | | |
260 | 2.84k | if (public_announce == nullptr) { |
261 | 0 | LOGGER_ERROR(log, "public_announce is null"); |
262 | 0 | return -1; |
263 | 0 | } |
264 | | |
265 | 2.84k | memcpy(public_announce->chat_public_key, data, CHAT_ID_SIZE); |
266 | | |
267 | 2.84k | const int base_announce_size = gca_unpack_announce(log, data + ENC_PUBLIC_KEY_SIZE, length - ENC_PUBLIC_KEY_SIZE, |
268 | 2.84k | &public_announce->base_announce); |
269 | | |
270 | 2.84k | if (base_announce_size == -1) { |
271 | 552 | LOGGER_ERROR(log, "Failed to unpack group announce"); |
272 | 552 | return -1; |
273 | 552 | } |
274 | | |
275 | 2.29k | return base_announce_size + CHAT_ID_SIZE; |
276 | 2.84k | } |
277 | | |
278 | | int gca_pack_announces_list(const Logger *log, uint8_t *data, uint16_t length, const GC_Announce *announces, |
279 | | uint8_t announces_count, size_t *processed) |
280 | 1.65k | { |
281 | 1.65k | if (data == nullptr) { |
282 | 0 | LOGGER_ERROR(log, "data is null"); |
283 | 0 | return -1; |
284 | 0 | } |
285 | | |
286 | 1.65k | if (announces == nullptr) { |
287 | 0 | LOGGER_ERROR(log, "announces is null"); |
288 | 0 | return -1; |
289 | 0 | } |
290 | | |
291 | 1.65k | uint16_t offset = 0; |
292 | | |
293 | 6.62k | for (size_t i = 0; i < announces_count; ++i) { |
294 | 5.01k | const int packed_length = gca_pack_announce(log, data + offset, length - offset, &announces[i]); |
295 | | |
296 | 5.01k | if (packed_length < 0) { |
297 | 46 | LOGGER_ERROR(log, "Failed to pack group announce"); |
298 | 46 | return -1; |
299 | 46 | } |
300 | | |
301 | 4.96k | offset += packed_length; |
302 | 4.96k | } |
303 | | |
304 | 1.60k | if (processed != nullptr) { |
305 | 1.60k | *processed = offset; |
306 | 1.60k | } |
307 | | |
308 | 1.60k | return announces_count; |
309 | 1.65k | } |
310 | | |
311 | | int gca_unpack_announces_list(const Logger *log, const uint8_t *data, uint16_t length, GC_Announce *announces, |
312 | | uint8_t max_count) |
313 | 1.71k | { |
314 | 1.71k | if (data == nullptr) { |
315 | 0 | LOGGER_ERROR(log, "data is null"); |
316 | 0 | return -1; |
317 | 0 | } |
318 | | |
319 | 1.71k | if (announces == nullptr) { |
320 | 0 | LOGGER_ERROR(log, "announces is null"); |
321 | 0 | return -1; |
322 | 0 | } |
323 | | |
324 | 1.71k | uint16_t offset = 0; |
325 | 1.71k | int announces_count = 0; |
326 | | |
327 | 7.01k | for (size_t i = 0; i < max_count && length > offset; ++i) { |
328 | 5.34k | const int unpacked_length = gca_unpack_announce(log, data + offset, length - offset, &announces[i]); |
329 | | |
330 | 5.34k | if (unpacked_length == -1) { |
331 | 45 | LOGGER_WARNING(log, "Failed to unpack group announce: %d %d", length, offset); |
332 | 45 | return -1; |
333 | 45 | } |
334 | | |
335 | 5.30k | offset += unpacked_length; |
336 | 5.30k | ++announces_count; |
337 | 5.30k | } |
338 | | |
339 | 1.66k | return announces_count; |
340 | 1.71k | } |
341 | | |
342 | | non_null() |
343 | | static GC_Announces *gca_new_announces( |
344 | | GC_Announces_List *gc_announces_list, |
345 | | const GC_Public_Announce *public_announce) |
346 | 402 | { |
347 | 402 | GC_Announces *announces = (GC_Announces *)calloc(1, sizeof(GC_Announces)); |
348 | | |
349 | 402 | if (announces == nullptr) { |
350 | 0 | return nullptr; |
351 | 0 | } |
352 | | |
353 | 402 | announces->index = 0; |
354 | 402 | announces->prev_announce = nullptr; |
355 | | |
356 | 402 | if (gc_announces_list->root_announces != nullptr) { |
357 | 211 | gc_announces_list->root_announces->prev_announce = announces; |
358 | 211 | } |
359 | | |
360 | 402 | announces->next_announce = gc_announces_list->root_announces; |
361 | 402 | gc_announces_list->root_announces = announces; |
362 | 402 | memcpy(announces->chat_id, public_announce->chat_public_key, CHAT_ID_SIZE); |
363 | | |
364 | 402 | return announces; |
365 | 402 | } |
366 | | |
367 | | GC_Peer_Announce *gca_add_announce(const Mono_Time *mono_time, GC_Announces_List *gc_announces_list, |
368 | | const GC_Public_Announce *public_announce) |
369 | 2.57k | { |
370 | 2.57k | if (gc_announces_list == nullptr || public_announce == nullptr) { |
371 | 0 | return nullptr; |
372 | 0 | } |
373 | | |
374 | 2.57k | GC_Announces *announces = get_announces_by_chat_id(gc_announces_list, public_announce->chat_public_key); |
375 | | |
376 | | // No entry for this chat_id exists so we create one |
377 | 2.57k | if (announces == nullptr) { |
378 | 402 | announces = gca_new_announces(gc_announces_list, public_announce); |
379 | | |
380 | 402 | if (announces == nullptr) { |
381 | 0 | return nullptr; |
382 | 0 | } |
383 | 402 | } |
384 | | |
385 | 2.57k | const uint64_t cur_time = mono_time_get(mono_time); |
386 | | |
387 | 2.57k | announces->last_announce_received_timestamp = cur_time; |
388 | | |
389 | 2.57k | const uint64_t index = announces->index % GCA_MAX_SAVED_ANNOUNCES_PER_GC; |
390 | | |
391 | 2.57k | GC_Peer_Announce *gc_peer_announce = &announces->peer_announces[index]; |
392 | | |
393 | 2.57k | gc_peer_announce->base_announce = public_announce->base_announce; |
394 | | |
395 | 2.57k | gc_peer_announce->timestamp = cur_time; |
396 | | |
397 | 2.57k | ++announces->index; |
398 | | |
399 | 2.57k | return gc_peer_announce; |
400 | 2.57k | } |
401 | | |
402 | | bool gca_is_valid_announce(const GC_Announce *announce) |
403 | 4.57k | { |
404 | 4.57k | if (announce == nullptr) { |
405 | 0 | return false; |
406 | 0 | } |
407 | | |
408 | 4.57k | return announce->tcp_relays_count > 0 || announce->ip_port_is_set; |
409 | 4.57k | } |
410 | | |
411 | | GC_Announces_List *new_gca_list(void) |
412 | 4.00k | { |
413 | 4.00k | return (GC_Announces_List *)calloc(1, sizeof(GC_Announces_List)); |
414 | 4.00k | } |
415 | | |
416 | | void kill_gca(GC_Announces_List *announces_list) |
417 | 2.93k | { |
418 | 2.93k | if (announces_list == nullptr) { |
419 | 1 | return; |
420 | 1 | } |
421 | | |
422 | 2.93k | GC_Announces *root = announces_list->root_announces; |
423 | | |
424 | 3.15k | while (root != nullptr) { |
425 | 215 | GC_Announces *next = root->next_announce; |
426 | 215 | free(root); |
427 | 215 | root = next; |
428 | 215 | } |
429 | | |
430 | 2.93k | free(announces_list); |
431 | 2.93k | } |
432 | | |
433 | | /* How long we save a peer's announce before we consider it stale and remove it. */ |
434 | 1.42k | #define GCA_ANNOUNCE_SAVE_TIMEOUT 30 |
435 | | |
436 | | /* How often we run do_gca() */ |
437 | 212k | #define GCA_DO_GCA_TIMEOUT 1 |
438 | | |
439 | | void do_gca(const Mono_Time *mono_time, GC_Announces_List *gc_announces_list) |
440 | 212k | { |
441 | 212k | if (gc_announces_list == nullptr) { |
442 | 0 | return; |
443 | 0 | } |
444 | | |
445 | 212k | if (!mono_time_is_timeout(mono_time, gc_announces_list->last_timeout_check, GCA_DO_GCA_TIMEOUT)) { |
446 | 176k | return; |
447 | 176k | } |
448 | | |
449 | 36.1k | gc_announces_list->last_timeout_check = mono_time_get(mono_time); |
450 | | |
451 | 36.1k | GC_Announces *announces = gc_announces_list->root_announces; |
452 | | |
453 | 37.5k | while (announces != nullptr) { |
454 | 1.42k | if (mono_time_is_timeout(mono_time, announces->last_announce_received_timestamp, GCA_ANNOUNCE_SAVE_TIMEOUT)) { |
455 | 1 | GC_Announces *to_delete = announces; |
456 | 1 | announces = announces->next_announce; |
457 | 1 | remove_announces(gc_announces_list, to_delete); |
458 | 1 | continue; |
459 | 1 | } |
460 | | |
461 | 1.42k | announces = announces->next_announce; |
462 | 1.42k | } |
463 | 36.1k | } |
464 | | |
465 | | void cleanup_gca(GC_Announces_List *gc_announces_list, const uint8_t *chat_id) |
466 | 809 | { |
467 | 809 | if (gc_announces_list == nullptr || chat_id == nullptr) { |
468 | 0 | return; |
469 | 0 | } |
470 | | |
471 | 809 | GC_Announces *announces = get_announces_by_chat_id(gc_announces_list, chat_id); |
472 | | |
473 | 809 | if (announces != nullptr) { |
474 | 186 | remove_announces(gc_announces_list, announces); |
475 | 186 | } |
476 | 809 | } |