/work/toxcore/onion_client.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* SPDX-License-Identifier: GPL-3.0-or-later |
2 | | * Copyright © 2016-2026 The TokTok team. |
3 | | * Copyright © 2013 Tox project. |
4 | | */ |
5 | | |
6 | | /** |
7 | | * Implementation of the client part of docs/Prevent_Tracking.txt (The part that |
8 | | * uses the onion stuff to connect to the friend) |
9 | | */ |
10 | | #include "onion_client.h" |
11 | | |
12 | | #include <assert.h> |
13 | | #include <string.h> |
14 | | |
15 | | #include "DHT.h" |
16 | | #include "LAN_discovery.h" |
17 | | #include "TCP_connection.h" |
18 | | #include "attributes.h" |
19 | | #include "ccompat.h" |
20 | | #include "crypto_core.h" |
21 | | #include "group_announce.h" |
22 | | #include "group_onion_announce.h" |
23 | | #include "list.h" |
24 | | #include "logger.h" |
25 | | #include "mem.h" |
26 | | #include "mono_time.h" |
27 | | #include "net_crypto.h" |
28 | | #include "network.h" |
29 | | #include "onion.h" |
30 | | #include "onion_announce.h" |
31 | | #include "ping_array.h" |
32 | | #include "sort.h" |
33 | | #include "timed_auth.h" |
34 | | #include "util.h" |
35 | | |
36 | | /** @brief defines for the array size and timeout for onion announce packets. */ |
37 | 1.92k | #define ANNOUNCE_ARRAY_SIZE 256 |
38 | 1.92k | #define ANNOUNCE_TIMEOUT 10 |
39 | | |
40 | | typedef struct Onion_Node { |
41 | | uint8_t public_key[CRYPTO_PUBLIC_KEY_SIZE]; |
42 | | IP_Port ip_port; |
43 | | uint8_t ping_id[ONION_PING_ID_SIZE]; |
44 | | uint8_t data_public_key[CRYPTO_PUBLIC_KEY_SIZE]; |
45 | | uint8_t is_stored; // Tribool. |
46 | | |
47 | | uint64_t added_time; |
48 | | |
49 | | uint64_t timestamp; |
50 | | |
51 | | uint64_t last_pinged; |
52 | | |
53 | | uint8_t pings_since_last_response; |
54 | | |
55 | | uint32_t path_used; |
56 | | } Onion_Node; |
57 | | |
58 | | typedef struct Onion_Client_Paths { |
59 | | Onion_Path paths[NUMBER_ONION_PATHS]; |
60 | | uint64_t last_path_success[NUMBER_ONION_PATHS]; |
61 | | uint64_t last_path_used[NUMBER_ONION_PATHS]; |
62 | | uint64_t path_creation_time[NUMBER_ONION_PATHS]; |
63 | | /* number of times used without success. */ |
64 | | unsigned int last_path_used_times[NUMBER_ONION_PATHS]; |
65 | | } Onion_Client_Paths; |
66 | | |
67 | | typedef struct Last_Pinged { |
68 | | uint8_t public_key[CRYPTO_PUBLIC_KEY_SIZE]; |
69 | | uint64_t timestamp; |
70 | | } Last_Pinged; |
71 | | |
72 | | struct Onion_Friend { |
73 | | bool is_valid; |
74 | | bool is_online; |
75 | | |
76 | | bool know_dht_public_key; |
77 | | uint8_t dht_public_key[CRYPTO_PUBLIC_KEY_SIZE]; |
78 | | uint8_t real_public_key[CRYPTO_PUBLIC_KEY_SIZE]; |
79 | | |
80 | | Onion_Node clients_list[MAX_ONION_CLIENTS]; |
81 | | uint8_t temp_public_key[CRYPTO_PUBLIC_KEY_SIZE]; |
82 | | uint8_t temp_secret_key[CRYPTO_SECRET_KEY_SIZE]; |
83 | | |
84 | | uint64_t last_dht_pk_onion_sent; |
85 | | uint64_t last_dht_pk_dht_sent; |
86 | | |
87 | | uint64_t last_noreplay; |
88 | | |
89 | | uint64_t last_populated; // the last time we had a fully populated client nodes list |
90 | | uint64_t time_last_pinged; // the last time we pinged this friend with any node |
91 | | |
92 | | uint32_t run_count; |
93 | | uint32_t pings; // how many sucessful pings we've made for this friend |
94 | | |
95 | | Last_Pinged last_pinged[MAX_STORED_PINGED_NODES]; |
96 | | uint8_t last_pinged_index; |
97 | | |
98 | | recv_tcp_relay_cb *_Nullable tcp_relay_node_callback; |
99 | | void *_Nullable tcp_relay_node_callback_object; |
100 | | uint32_t tcp_relay_node_callback_number; |
101 | | |
102 | | onion_dht_pk_cb *_Nullable dht_pk_callback; |
103 | | void *_Nullable dht_pk_callback_object; |
104 | | uint32_t dht_pk_callback_number; |
105 | | |
106 | | uint8_t gc_data[GCA_PUBLIC_ANNOUNCE_MAX_SIZE]; |
107 | | uint8_t gc_public_key[ENC_PUBLIC_KEY_SIZE]; |
108 | | uint16_t gc_data_length; |
109 | | bool is_groupchat; |
110 | | }; |
111 | | |
112 | | static const Onion_Friend empty_onion_friend = {false}; |
113 | | |
114 | | typedef struct Onion_Data_Handler { |
115 | | oniondata_handler_cb *_Nullable function; |
116 | | void *_Nullable object; |
117 | | } Onion_Data_Handler; |
118 | | |
119 | | struct Onion_Client { |
120 | | const Mono_Time *_Nonnull mono_time; |
121 | | const Logger *_Nonnull logger; |
122 | | const Random *_Nonnull rng; |
123 | | const Memory *_Nonnull mem; |
124 | | |
125 | | DHT *_Nonnull dht; |
126 | | Net_Crypto *_Nonnull c; |
127 | | Networking_Core *_Nonnull net; |
128 | | Onion_Friend *_Nullable friends_list; |
129 | | uint32_t friends_list_capacity; |
130 | | uint32_t num_friends; |
131 | | |
132 | | BS_List friends_lookup; |
133 | | |
134 | | Onion_Node clients_announce_list[MAX_ONION_CLIENTS_ANNOUNCE]; |
135 | | uint64_t last_announce; |
136 | | |
137 | | Onion_Client_Paths onion_paths_self; |
138 | | Onion_Client_Paths onion_paths_friends; |
139 | | |
140 | | uint8_t secret_symmetric_key[CRYPTO_SYMMETRIC_KEY_SIZE]; |
141 | | uint64_t last_run; |
142 | | uint64_t first_run; |
143 | | uint64_t last_time_connected; |
144 | | |
145 | | uint8_t temp_public_key[CRYPTO_PUBLIC_KEY_SIZE]; |
146 | | uint8_t temp_secret_key[CRYPTO_SECRET_KEY_SIZE]; |
147 | | |
148 | | Last_Pinged last_pinged[MAX_STORED_PINGED_NODES]; |
149 | | |
150 | | Node_format path_nodes[MAX_PATH_NODES]; |
151 | | uint16_t path_nodes_index; |
152 | | |
153 | | Node_format path_nodes_bs[MAX_PATH_NODES]; |
154 | | uint16_t path_nodes_index_bs; |
155 | | |
156 | | Ping_Array *_Nonnull announce_ping_array; |
157 | | uint8_t last_pinged_index; |
158 | | Onion_Data_Handler onion_data_handlers[256]; |
159 | | |
160 | | uint64_t last_packet_recv; |
161 | | uint64_t last_populated; // the last time we had a fully populated path nodes list |
162 | | |
163 | | unsigned int onion_connected; |
164 | | bool udp_connected; |
165 | | |
166 | | onion_group_announce_cb *_Nullable group_announce_response; |
167 | | void *_Nullable group_announce_response_user_data; |
168 | | }; |
169 | | |
170 | | uint32_t onion_get_friend_count(const Onion_Client *const onion_c) |
171 | 124k | { |
172 | 124k | return onion_c->num_friends; |
173 | 124k | } |
174 | | |
175 | | Onion_Friend *onion_get_friend(const Onion_Client *const onion_c, uint32_t friend_num) |
176 | 184k | { |
177 | 184k | return &onion_c->friends_list[friend_num]; |
178 | 184k | } |
179 | | |
180 | | const uint8_t *onion_friend_get_gc_public_key(const Onion_Friend *const onion_friend) |
181 | 12.3k | { |
182 | 12.3k | return onion_friend->gc_public_key; |
183 | 12.3k | } |
184 | | |
185 | | const uint8_t *onion_friend_get_gc_public_key_num(const Onion_Client *const onion_c, uint32_t num) |
186 | 1.06k | { |
187 | 1.06k | return onion_c->friends_list[num].gc_public_key; |
188 | 1.06k | } |
189 | | |
190 | | void onion_friend_set_gc_public_key(Onion_Friend *const onion_friend, const uint8_t *public_key) |
191 | 120 | { |
192 | 120 | memcpy(onion_friend->gc_public_key, public_key, ENC_PUBLIC_KEY_SIZE); |
193 | 120 | } |
194 | | |
195 | | void onion_friend_set_gc_data(Onion_Friend *const onion_friend, const uint8_t *gc_data, uint16_t gc_data_length) |
196 | 245 | { |
197 | 245 | if (gc_data_length > 0 && gc_data != nullptr) { |
198 | 120 | assert(gc_data_length <= GCA_PUBLIC_ANNOUNCE_MAX_SIZE); |
199 | 120 | memcpy(onion_friend->gc_data, gc_data, gc_data_length); |
200 | 120 | } |
201 | | |
202 | 245 | onion_friend->gc_data_length = gc_data_length; |
203 | 245 | onion_friend->is_groupchat = true; |
204 | 245 | } |
205 | | |
206 | | bool onion_friend_is_groupchat(const Onion_Friend *const onion_friend) |
207 | 184k | { |
208 | 184k | return onion_friend->is_groupchat; |
209 | 184k | } |
210 | | |
211 | | /** @brief Add a node to the path_nodes bootstrap array. |
212 | | * |
213 | | * If a node with the given public key was already in the bootstrap array, this function has no |
214 | | * effect and returns successfully. There is currently no way to update the IP/port for a bootstrap |
215 | | * node, so if it changes, the Onion_Client must be recreated. |
216 | | * |
217 | | * @param onion_c The onion client object. |
218 | | * @param ip_port IP/port for the bootstrap node. |
219 | | * @param public_key DHT public key for the bootstrap node. |
220 | | * |
221 | | * @retval false on failure |
222 | | * @retval true on success |
223 | | */ |
224 | | bool onion_add_bs_path_node(Onion_Client *onion_c, const IP_Port *ip_port, const uint8_t *public_key) |
225 | 3.12k | { |
226 | 3.12k | if (!net_family_is_ipv4(ip_port->ip.family) && !net_family_is_ipv6(ip_port->ip.family)) { |
227 | 0 | return false; |
228 | 0 | } |
229 | | |
230 | 80.4k | for (unsigned int i = 0; i < MAX_PATH_NODES; ++i) { |
231 | 78.4k | if (pk_equal(public_key, onion_c->path_nodes_bs[i].public_key)) { |
232 | 1.16k | return true; |
233 | 1.16k | } |
234 | 78.4k | } |
235 | | |
236 | 1.96k | onion_c->path_nodes_bs[onion_c->path_nodes_index_bs % MAX_PATH_NODES].ip_port = *ip_port; |
237 | 1.96k | memcpy(onion_c->path_nodes_bs[onion_c->path_nodes_index_bs % MAX_PATH_NODES].public_key, public_key, |
238 | 1.96k | CRYPTO_PUBLIC_KEY_SIZE); |
239 | | |
240 | 1.96k | const uint16_t last = onion_c->path_nodes_index_bs; |
241 | 1.96k | ++onion_c->path_nodes_index_bs; |
242 | | |
243 | 1.96k | if (onion_c->path_nodes_index_bs < last) { |
244 | 0 | onion_c->path_nodes_index_bs = MAX_PATH_NODES + 1; |
245 | 0 | } |
246 | | |
247 | 1.96k | return true; |
248 | 3.12k | } |
249 | | |
250 | | /** @brief Add a node to the path_nodes array. |
251 | | * |
252 | | * return -1 on failure |
253 | | * return 0 on success |
254 | | */ |
255 | | static int onion_add_path_node(Onion_Client *_Nonnull onion_c, const IP_Port *_Nonnull ip_port, const uint8_t *_Nonnull public_key) |
256 | 166k | { |
257 | 166k | if (!net_family_is_ipv4(ip_port->ip.family) && !net_family_is_ipv6(ip_port->ip.family)) { |
258 | 6.19k | return -1; |
259 | 6.19k | } |
260 | | |
261 | 1.15M | for (unsigned int i = 0; i < MAX_PATH_NODES; ++i) { |
262 | 1.14M | if (pk_equal(public_key, onion_c->path_nodes[i].public_key)) { |
263 | 150k | return -1; |
264 | 150k | } |
265 | 1.14M | } |
266 | | |
267 | 9.82k | onion_c->path_nodes[onion_c->path_nodes_index % MAX_PATH_NODES].ip_port = *ip_port; |
268 | 9.82k | memcpy(onion_c->path_nodes[onion_c->path_nodes_index % MAX_PATH_NODES].public_key, public_key, |
269 | 9.82k | CRYPTO_PUBLIC_KEY_SIZE); |
270 | | |
271 | 9.82k | const uint16_t last = onion_c->path_nodes_index; |
272 | 9.82k | ++onion_c->path_nodes_index; |
273 | | |
274 | 9.82k | if (onion_c->path_nodes_index < last) { |
275 | 0 | onion_c->path_nodes_index = MAX_PATH_NODES + 1; |
276 | 0 | } |
277 | | |
278 | 9.82k | return 0; |
279 | 159k | } |
280 | | |
281 | | /** @brief Put up to max_num nodes in nodes. |
282 | | * |
283 | | * return the number of nodes. |
284 | | */ |
285 | | uint16_t onion_backup_nodes(const Onion_Client *onion_c, Node_format *nodes, uint16_t max_num) |
286 | 995 | { |
287 | 995 | if (max_num == 0) { |
288 | 0 | return 0; |
289 | 0 | } |
290 | | |
291 | 995 | const uint16_t num_nodes = min_u16(onion_c->path_nodes_index, MAX_PATH_NODES); |
292 | 995 | uint16_t i = 0; |
293 | | |
294 | 1.00k | while (i < max_num && i < num_nodes) { |
295 | 13 | nodes[i] = onion_c->path_nodes[(onion_c->path_nodes_index - (1 + i)) % num_nodes]; |
296 | 13 | ++i; |
297 | 13 | } |
298 | | |
299 | 1.19k | for (uint16_t j = 0; i < max_num && j < MAX_PATH_NODES && j < onion_c->path_nodes_index_bs; ++j) { |
300 | 199 | bool already_saved = false; |
301 | | |
302 | 205 | for (uint16_t k = 0; k < num_nodes; ++k) { |
303 | 10 | if (pk_equal(nodes[k].public_key, onion_c->path_nodes_bs[j].public_key)) { |
304 | 4 | already_saved = true; |
305 | 4 | break; |
306 | 4 | } |
307 | 10 | } |
308 | | |
309 | 199 | if (!already_saved) { |
310 | 195 | nodes[i] = onion_c->path_nodes_bs[j]; |
311 | 195 | ++i; |
312 | 195 | } |
313 | 199 | } |
314 | | |
315 | 995 | return i; |
316 | 995 | } |
317 | | |
318 | | /** @brief Put up to max_num random nodes in nodes. |
319 | | * |
320 | | * return the number of nodes. |
321 | | */ |
322 | | static uint16_t random_nodes_path_onion(const Onion_Client *_Nonnull onion_c, Node_format *_Nonnull nodes, uint16_t max_num) |
323 | 7.66k | { |
324 | 7.66k | if (max_num == 0) { |
325 | 0 | return 0; |
326 | 0 | } |
327 | | |
328 | 7.66k | const uint16_t num_nodes = min_u16(onion_c->path_nodes_index, MAX_PATH_NODES); |
329 | | |
330 | | // if (dht_non_lan_connected(onion_c->dht)) { |
331 | 7.66k | if (dht_isconnected(onion_c->dht)) { |
332 | 6.49k | if (num_nodes == 0) { |
333 | 0 | return 0; |
334 | 0 | } |
335 | | |
336 | 25.9k | for (unsigned int i = 0; i < max_num; ++i) { |
337 | 19.4k | const uint32_t rand_idx = random_range_u32(onion_c->rng, num_nodes); |
338 | 19.4k | nodes[i] = onion_c->path_nodes[rand_idx]; |
339 | 19.4k | } |
340 | 6.49k | } else { |
341 | 1.17k | const int random_tcp = get_random_tcp_con_number(onion_c->c); |
342 | | |
343 | 1.17k | if (random_tcp == -1) { |
344 | 685 | return 0; |
345 | 685 | } |
346 | | |
347 | 487 | if (num_nodes >= 2) { |
348 | 0 | nodes[0] = empty_node_format; |
349 | 0 | nodes[0].ip_port = tcp_connections_number_to_ip_port(random_tcp); |
350 | |
|
351 | 0 | for (unsigned int i = 1; i < max_num; ++i) { |
352 | 0 | const uint32_t rand_idx = random_range_u32(onion_c->rng, num_nodes); |
353 | 0 | nodes[i] = onion_c->path_nodes[rand_idx]; |
354 | 0 | } |
355 | 487 | } else { |
356 | 487 | const uint16_t num_nodes_bs = min_u16(onion_c->path_nodes_index_bs, MAX_PATH_NODES); |
357 | | |
358 | 487 | if (num_nodes_bs == 0) { |
359 | 0 | return 0; |
360 | 0 | } |
361 | | |
362 | 487 | nodes[0] = empty_node_format; |
363 | 487 | nodes[0].ip_port = tcp_connections_number_to_ip_port(random_tcp); |
364 | | |
365 | 1.46k | for (unsigned int i = 1; i < max_num; ++i) { |
366 | 976 | const uint32_t rand_idx = random_range_u32(onion_c->rng, num_nodes_bs); |
367 | 976 | nodes[i] = onion_c->path_nodes_bs[rand_idx]; |
368 | 976 | } |
369 | 487 | } |
370 | 487 | } |
371 | | |
372 | 6.98k | return max_num; |
373 | 7.66k | } |
374 | | |
375 | | /** |
376 | | * return -1 if nodes are suitable for creating a new path. |
377 | | * return path number of already existing similar path if one already exists. |
378 | | */ |
379 | | static int is_path_used(const Mono_Time *_Nonnull mono_time, const Onion_Client_Paths *_Nonnull onion_paths, const Node_format *_Nonnull nodes) |
380 | 6.98k | { |
381 | 36.3k | for (unsigned int i = 0; i < NUMBER_ONION_PATHS; ++i) { |
382 | 32.6k | if (mono_time_is_timeout(mono_time, onion_paths->last_path_success[i], ONION_PATH_TIMEOUT)) { |
383 | 18.3k | continue; |
384 | 18.3k | } |
385 | | |
386 | 14.2k | if (mono_time_is_timeout(mono_time, onion_paths->path_creation_time[i], ONION_PATH_MAX_LIFETIME)) { |
387 | 0 | continue; |
388 | 0 | } |
389 | | |
390 | | // TODO(irungentoo): do we really have to check it with the last node? |
391 | 14.2k | if (ipport_equal(&onion_paths->paths[i].ip_port1, &nodes[ONION_PATH_LENGTH - 1].ip_port)) { |
392 | 3.24k | return i; |
393 | 3.24k | } |
394 | 14.2k | } |
395 | | |
396 | 3.73k | return -1; |
397 | 6.98k | } |
398 | | |
399 | | /** is path timed out */ |
400 | | static bool path_timed_out(const Mono_Time *_Nonnull mono_time, const Onion_Client_Paths *_Nonnull onion_paths, uint32_t pathnum) |
401 | 65.6k | { |
402 | 65.6k | pathnum = pathnum % NUMBER_ONION_PATHS; |
403 | | |
404 | 65.6k | const bool is_new = onion_paths->last_path_success[pathnum] == onion_paths->path_creation_time[pathnum]; |
405 | 65.6k | const uint64_t timeout = is_new ? ONION_PATH_FIRST_TIMEOUT : ONION_PATH_TIMEOUT; |
406 | | |
407 | 65.6k | return (onion_paths->last_path_used_times[pathnum] >= ONION_PATH_MAX_NO_RESPONSE_USES |
408 | 65.6k | && mono_time_is_timeout(mono_time, onion_paths->last_path_used[pathnum], timeout)) |
409 | 65.6k | || mono_time_is_timeout(mono_time, onion_paths->path_creation_time[pathnum], ONION_PATH_MAX_LIFETIME); |
410 | 65.6k | } |
411 | | |
412 | | /** should node be considered to have timed out */ |
413 | | static bool onion_node_timed_out(const Onion_Node *_Nonnull node, const Mono_Time *_Nonnull mono_time) |
414 | 1.23M | { |
415 | 1.23M | return node->timestamp == 0 |
416 | 1.23M | || (node->pings_since_last_response >= ONION_NODE_MAX_PINGS |
417 | 598k | && mono_time_is_timeout(mono_time, node->last_pinged, ONION_NODE_TIMEOUT)); |
418 | 1.23M | } |
419 | | |
420 | | /** @brief Create a new path or use an old suitable one (if pathnum is valid) |
421 | | * or a random one from onion_paths. |
422 | | * |
423 | | * return -1 on failure |
424 | | * return 0 on success |
425 | | * |
426 | | * TODO(irungentoo): Make this function better, it currently probably is |
427 | | * vulnerable to some attacks that could deanonimize us. |
428 | | */ |
429 | | static int random_path(const Onion_Client *_Nonnull onion_c, Onion_Client_Paths *_Nonnull onion_paths, uint32_t pathnum, Onion_Path *_Nonnull path) |
430 | 38.0k | { |
431 | 38.0k | if (pathnum == UINT32_MAX) { |
432 | 33.2k | pathnum = random_range_u32(onion_c->rng, NUMBER_ONION_PATHS); |
433 | 33.2k | } else { |
434 | 4.88k | pathnum = pathnum % NUMBER_ONION_PATHS; |
435 | 4.88k | } |
436 | | |
437 | 38.0k | if (path_timed_out(onion_c->mono_time, onion_paths, pathnum)) { |
438 | 7.66k | Node_format nodes[ONION_PATH_LENGTH]; |
439 | | |
440 | 7.66k | if (random_nodes_path_onion(onion_c, nodes, ONION_PATH_LENGTH) != ONION_PATH_LENGTH) { |
441 | 685 | return -1; |
442 | 685 | } |
443 | | |
444 | 6.98k | const int n = is_path_used(onion_c->mono_time, onion_paths, nodes); |
445 | | |
446 | 6.98k | if (n == -1) { |
447 | 3.73k | if (create_onion_path(onion_c->rng, onion_c->dht, &onion_paths->paths[pathnum], nodes) == -1) { |
448 | 0 | return -1; |
449 | 0 | } |
450 | | |
451 | 3.73k | onion_paths->path_creation_time[pathnum] = mono_time_get(onion_c->mono_time); |
452 | 3.73k | onion_paths->last_path_success[pathnum] = onion_paths->path_creation_time[pathnum]; |
453 | 3.73k | onion_paths->last_path_used_times[pathnum] = ONION_PATH_MAX_NO_RESPONSE_USES / 2; |
454 | | |
455 | 3.73k | uint32_t path_num = random_u32(onion_c->rng); |
456 | 3.73k | path_num /= NUMBER_ONION_PATHS; |
457 | 3.73k | path_num *= NUMBER_ONION_PATHS; |
458 | 3.73k | path_num += pathnum; |
459 | | |
460 | 3.73k | onion_paths->paths[pathnum].path_num = path_num; |
461 | 3.73k | } else { |
462 | 3.24k | assert(0 <= n && n < NUMBER_ONION_PATHS); |
463 | 3.24k | pathnum = n; |
464 | 3.24k | } |
465 | 6.98k | } |
466 | | |
467 | 37.3k | if (onion_paths->last_path_used_times[pathnum] < ONION_PATH_MAX_NO_RESPONSE_USES) { |
468 | 31.7k | onion_paths->last_path_used[pathnum] = mono_time_get(onion_c->mono_time); |
469 | 31.7k | } |
470 | | |
471 | 37.3k | ++onion_paths->last_path_used_times[pathnum]; |
472 | 37.3k | *path = onion_paths->paths[pathnum]; |
473 | 37.3k | return 0; |
474 | 38.0k | } |
475 | | |
476 | | /** Set path timeouts, return the path number. */ |
477 | | static uint32_t set_path_timeouts(Onion_Client *_Nonnull onion_c, uint32_t num, uint32_t path_num) |
478 | 33.3k | { |
479 | 33.3k | if (num > onion_c->num_friends) { |
480 | 0 | return -1; |
481 | 0 | } |
482 | | |
483 | 33.3k | Onion_Client_Paths *onion_paths; |
484 | | |
485 | 33.3k | if (num == 0) { |
486 | 21.5k | onion_paths = &onion_c->onion_paths_self; |
487 | 21.5k | } else { |
488 | 11.8k | onion_paths = &onion_c->onion_paths_friends; |
489 | 11.8k | } |
490 | | |
491 | 33.3k | if (onion_paths->paths[path_num % NUMBER_ONION_PATHS].path_num == path_num) { |
492 | 33.0k | onion_paths->last_path_success[path_num % NUMBER_ONION_PATHS] = mono_time_get(onion_c->mono_time); |
493 | 33.0k | onion_paths->last_path_used_times[path_num % NUMBER_ONION_PATHS] = 0; |
494 | | |
495 | 33.0k | Node_format nodes[ONION_PATH_LENGTH]; |
496 | | |
497 | 33.0k | if (onion_path_to_nodes(nodes, ONION_PATH_LENGTH, &onion_paths->paths[path_num % NUMBER_ONION_PATHS]) == 0) { |
498 | 132k | for (unsigned int i = 0; i < ONION_PATH_LENGTH; ++i) { |
499 | 99.1k | onion_add_path_node(onion_c, &nodes[i].ip_port, nodes[i].public_key); |
500 | 99.1k | } |
501 | 33.0k | } |
502 | | |
503 | 33.0k | return path_num; |
504 | 33.0k | } |
505 | | |
506 | 301 | return -1; |
507 | 33.3k | } |
508 | | |
509 | | /** @brief Function to send onion packet via TCP and UDP. |
510 | | * |
511 | | * return -1 on failure. |
512 | | * return 0 on success. |
513 | | */ |
514 | | static int send_onion_packet_tcp_udp(const Onion_Client *_Nonnull onion_c, const Onion_Path *_Nonnull path, const IP_Port *_Nonnull dest, const uint8_t *_Nonnull data, uint16_t length) |
515 | 37.3k | { |
516 | 37.3k | if (net_family_is_ipv4(path->ip_port1.ip.family) || net_family_is_ipv6(path->ip_port1.ip.family)) { |
517 | 30.8k | uint8_t packet[ONION_MAX_PACKET_SIZE]; |
518 | 30.8k | const int len = create_onion_packet(onion_c->mem, onion_c->rng, packet, sizeof(packet), path, dest, data, length); |
519 | | |
520 | 30.8k | if (len == -1) { |
521 | 0 | return -1; |
522 | 0 | } |
523 | | |
524 | 30.8k | if (sendpacket(onion_c->net, &path->ip_port1, packet, len) != len) { |
525 | 0 | return -1; |
526 | 0 | } |
527 | | |
528 | 30.8k | return 0; |
529 | 30.8k | } |
530 | | |
531 | 6.54k | unsigned int tcp_connections_number; |
532 | | |
533 | 6.54k | if (ip_port_to_tcp_connections_number(&path->ip_port1, &tcp_connections_number)) { |
534 | 6.54k | uint8_t packet[ONION_MAX_PACKET_SIZE]; |
535 | 6.54k | const int len = create_onion_packet_tcp(onion_c->mem, onion_c->rng, packet, sizeof(packet), path, dest, data, length); |
536 | | |
537 | 6.54k | if (len == -1) { |
538 | 0 | return -1; |
539 | 0 | } |
540 | | |
541 | 6.54k | return send_tcp_onion_request(onion_c->c, tcp_connections_number, packet, len); |
542 | 6.54k | } |
543 | | |
544 | 6 | return -1; |
545 | 6.54k | } |
546 | | |
547 | | /** @brief Creates a sendback for use in an announce request. |
548 | | * |
549 | | * num is 0 if we used our secret public key for the announce |
550 | | * num is 1 + friendnum if we use a temporary one. |
551 | | * |
552 | | * Public key is the key we will be sending it to. |
553 | | * ip_port is the ip_port of the node we will be sending |
554 | | * it to. |
555 | | * |
556 | | * sendback must be at least ONION_ANNOUNCE_SENDBACK_DATA_LENGTH big |
557 | | * |
558 | | * return -1 on failure |
559 | | * return 0 on success |
560 | | * |
561 | | */ |
562 | | static int new_sendback(Onion_Client *_Nonnull onion_c, uint32_t num, const uint8_t *_Nonnull public_key, const IP_Port *_Nonnull ip_port, uint32_t path_num, uint64_t *_Nonnull sendback) |
563 | 35.2k | { |
564 | 35.2k | uint8_t data[sizeof(uint32_t) + CRYPTO_PUBLIC_KEY_SIZE + SIZE_IPPORT + sizeof(uint32_t)]; |
565 | 35.2k | memcpy(data, &num, sizeof(uint32_t)); |
566 | 35.2k | memcpy(&data[sizeof(uint32_t)], public_key, CRYPTO_PUBLIC_KEY_SIZE); |
567 | 35.2k | const int packed_len = pack_ip_port(onion_c->logger, &data[sizeof(uint32_t) + CRYPTO_PUBLIC_KEY_SIZE], SIZE_IPPORT, ip_port); |
568 | 35.2k | if (packed_len < 0) { |
569 | 0 | LOGGER_ERROR(onion_c->logger, "failed to pack IP/port"); |
570 | 0 | return -1; |
571 | 0 | } |
572 | 35.2k | assert(packed_len <= SIZE_IPPORT); |
573 | 35.2k | memzero(&data[sizeof(uint32_t) + CRYPTO_PUBLIC_KEY_SIZE + packed_len], SIZE_IPPORT - packed_len); |
574 | 35.2k | memcpy(&data[sizeof(uint32_t) + CRYPTO_PUBLIC_KEY_SIZE + SIZE_IPPORT], &path_num, sizeof(uint32_t)); |
575 | 35.2k | *sendback = ping_array_add(onion_c->announce_ping_array, onion_c->mono_time, onion_c->rng, data, sizeof(data)); |
576 | | |
577 | 35.2k | if (*sendback == 0) { |
578 | 0 | LOGGER_TRACE(onion_c->logger, "generating sendback in announce ping array failed"); |
579 | 0 | return -1; |
580 | 0 | } |
581 | | |
582 | 35.2k | return 0; |
583 | 35.2k | } |
584 | | |
585 | | /** @brief Checks if the sendback is valid and returns the public key contained in it in ret_pubkey and the |
586 | | * ip contained in it in ret_ip_port |
587 | | * |
588 | | * sendback is the sendback ONION_ANNOUNCE_SENDBACK_DATA_LENGTH big |
589 | | * ret_pubkey must be at least CRYPTO_PUBLIC_KEY_SIZE big |
590 | | * ret_ip_port must be at least 1 big |
591 | | * |
592 | | * return -1 on failure |
593 | | * return num (see new_sendback(...)) on success |
594 | | */ |
595 | | static uint32_t check_sendback(Onion_Client *_Nonnull onion_c, const uint8_t *_Nonnull sendback, uint8_t *_Nonnull ret_pubkey, IP_Port *_Nonnull ret_ip_port, uint32_t *_Nonnull path_num) |
596 | 33.9k | { |
597 | 33.9k | uint64_t sback; |
598 | 33.9k | memcpy(&sback, sendback, sizeof(uint64_t)); |
599 | 33.9k | uint8_t data[sizeof(uint32_t) + CRYPTO_PUBLIC_KEY_SIZE + SIZE_IPPORT + sizeof(uint32_t)]; |
600 | | |
601 | 33.9k | if (ping_array_check(onion_c->announce_ping_array, onion_c->mono_time, data, sizeof(data), sback) != sizeof(data)) { |
602 | 604 | return -1; |
603 | 604 | } |
604 | | |
605 | 33.3k | memcpy(ret_pubkey, data + sizeof(uint32_t), CRYPTO_PUBLIC_KEY_SIZE); |
606 | 33.3k | unpack_ip_port(ret_ip_port, data + sizeof(uint32_t) + CRYPTO_PUBLIC_KEY_SIZE, SIZE_IPPORT, false); |
607 | 33.3k | memcpy(path_num, data + sizeof(uint32_t) + CRYPTO_PUBLIC_KEY_SIZE + SIZE_IPPORT, sizeof(uint32_t)); |
608 | | |
609 | 33.3k | uint32_t num; |
610 | 33.3k | memcpy(&num, data, sizeof(uint32_t)); |
611 | 33.3k | return num; |
612 | 33.9k | } |
613 | | |
614 | | static int client_send_announce_request(Onion_Client *_Nonnull onion_c, uint32_t num, const IP_Port *_Nonnull dest, |
615 | | const uint8_t *_Nonnull dest_pubkey, const uint8_t *_Nullable ping_id, uint32_t pathnum) |
616 | 35.9k | { |
617 | 35.9k | if (num > onion_c->num_friends) { |
618 | 0 | LOGGER_TRACE(onion_c->logger, "not sending announce to out of bounds friend %u (num friends: %u)", num, onion_c->num_friends); |
619 | 0 | return -1; |
620 | 0 | } |
621 | | |
622 | 35.9k | uint64_t sendback; |
623 | 35.9k | Onion_Path path; |
624 | | |
625 | 35.9k | if (num == 0) { |
626 | 23.6k | if (random_path(onion_c, &onion_c->onion_paths_self, pathnum, &path) == -1) { |
627 | 685 | LOGGER_TRACE(onion_c->logger, "cannot find path to self"); |
628 | 685 | return -1; |
629 | 685 | } |
630 | 23.6k | } else { |
631 | 12.3k | if (random_path(onion_c, &onion_c->onion_paths_friends, pathnum, &path) == -1) { |
632 | 0 | LOGGER_TRACE(onion_c->logger, "cannot find path to friend"); |
633 | 0 | return -1; |
634 | 0 | } |
635 | 12.3k | } |
636 | | |
637 | 35.2k | if (new_sendback(onion_c, num, dest_pubkey, dest, path.path_num, &sendback) == -1) { |
638 | 0 | return -1; |
639 | 0 | } |
640 | | |
641 | 35.2k | uint8_t zero_ping_id[ONION_PING_ID_SIZE] = {0}; |
642 | | |
643 | 35.2k | if (ping_id == nullptr) { |
644 | 30.2k | ping_id = zero_ping_id; |
645 | 30.2k | } |
646 | | |
647 | 35.2k | uint8_t request[ONION_ANNOUNCE_REQUEST_MAX_SIZE]; |
648 | 35.2k | int len; |
649 | | |
650 | 35.2k | if (num == 0) { |
651 | 22.9k | len = create_announce_request( |
652 | 22.9k | onion_c->mem, onion_c->rng, request, sizeof(request), dest_pubkey, nc_get_self_public_key(onion_c->c), |
653 | 22.9k | nc_get_self_secret_key(onion_c->c), ping_id, nc_get_self_public_key(onion_c->c), |
654 | 22.9k | onion_c->temp_public_key, sendback); |
655 | 22.9k | } else { |
656 | 12.3k | Onion_Friend *onion_friend = &onion_c->friends_list[num - 1]; |
657 | | |
658 | 12.3k | if (onion_friend->gc_data_length == 0) { // contact is a friend |
659 | 11.1k | len = create_announce_request( |
660 | 11.1k | onion_c->mem, onion_c->rng, request, sizeof(request), dest_pubkey, onion_friend->temp_public_key, |
661 | 11.1k | onion_friend->temp_secret_key, ping_id, onion_friend->real_public_key, |
662 | 11.1k | zero_ping_id, sendback); |
663 | 11.1k | } else { // contact is a gc |
664 | 1.13k | onion_friend->is_groupchat = true; |
665 | | |
666 | 1.13k | len = create_gca_announce_request( |
667 | 1.13k | onion_c->mem, onion_c->rng, request, sizeof(request), dest_pubkey, onion_friend->temp_public_key, |
668 | 1.13k | onion_friend->temp_secret_key, ping_id, onion_friend->real_public_key, |
669 | 1.13k | zero_ping_id, sendback, onion_friend->gc_data, |
670 | 1.13k | onion_friend->gc_data_length); |
671 | 1.13k | } |
672 | 12.3k | } |
673 | | |
674 | 35.2k | if (len == -1) { |
675 | 0 | LOGGER_TRACE(onion_c->logger, "failed to create announce request"); |
676 | 0 | return -1; |
677 | 0 | } |
678 | | |
679 | 35.2k | Ip_Ntoa ip_str; |
680 | 35.2k | LOGGER_TRACE(onion_c->logger, "sending onion packet to %s:%d (%02x, %d bytes)", |
681 | 35.2k | net_ip_ntoa(&dest->ip, &ip_str), net_ntohs(dest->port), request[0], len); |
682 | 35.2k | return send_onion_packet_tcp_udp(onion_c, &path, dest, request, len); |
683 | 35.2k | } |
684 | | |
685 | | typedef struct Onion_Node_Cmp { |
686 | | const Memory *_Nonnull mem; |
687 | | const Mono_Time *_Nonnull mono_time; |
688 | | const uint8_t *_Nonnull comp_public_key; |
689 | | } Onion_Node_Cmp; |
690 | | |
691 | | static int onion_node_cmp(const Onion_Node_Cmp *_Nonnull cmp, const Onion_Node *_Nonnull entry1, const Onion_Node *_Nonnull entry2) |
692 | 372k | { |
693 | 372k | const bool t1 = onion_node_timed_out(entry1, cmp->mono_time); |
694 | 372k | const bool t2 = onion_node_timed_out(entry2, cmp->mono_time); |
695 | | |
696 | 372k | if (t1 && t2) { |
697 | 132k | return 0; |
698 | 132k | } |
699 | | |
700 | 240k | if (t1) { |
701 | 32.2k | return -1; |
702 | 32.2k | } |
703 | | |
704 | 208k | if (t2) { |
705 | 20.1k | return 1; |
706 | 20.1k | } |
707 | | |
708 | 188k | const int closest = id_closest(cmp->comp_public_key, entry1->public_key, entry2->public_key); |
709 | | |
710 | 188k | if (closest == 1) { |
711 | 158k | return 1; |
712 | 158k | } |
713 | | |
714 | 29.9k | if (closest == 2) { |
715 | 29.9k | return -1; |
716 | 29.9k | } |
717 | | |
718 | 18.4E | return 0; |
719 | 29.9k | } |
720 | | |
721 | | static bool onion_node_less_handler(const void *_Nonnull object, const void *_Nonnull a, const void *_Nonnull b) |
722 | 372k | { |
723 | 372k | const Onion_Node_Cmp *cmp = (const Onion_Node_Cmp *)object; |
724 | 372k | const Onion_Node *entry1 = (const Onion_Node *)a; |
725 | 372k | const Onion_Node *entry2 = (const Onion_Node *)b; |
726 | | |
727 | 372k | return onion_node_cmp(cmp, entry1, entry2) < 0; |
728 | 372k | } |
729 | | |
730 | | static const void *_Nonnull onion_node_get_handler(const void *_Nonnull arr, uint32_t index) |
731 | 754k | { |
732 | 754k | const Onion_Node *entries = (const Onion_Node *)arr; |
733 | 754k | return &entries[index]; |
734 | 754k | } |
735 | | |
736 | | static void onion_node_set_handler(void *_Nonnull arr, uint32_t index, const void *_Nonnull val) |
737 | 700k | { |
738 | 700k | Onion_Node *entries = (Onion_Node *)arr; |
739 | 700k | const Onion_Node *entry = (const Onion_Node *)val; |
740 | 700k | entries[index] = *entry; |
741 | 700k | } |
742 | | |
743 | | static void *_Nonnull onion_node_subarr_handler(void *_Nonnull arr, uint32_t index, uint32_t size) |
744 | 0 | { |
745 | 0 | Onion_Node *entries = (Onion_Node *)arr; |
746 | 0 | return &entries[index]; |
747 | 0 | } |
748 | | |
749 | | static void *_Nullable onion_node_alloc_handler(const void *_Nonnull object, uint32_t size) |
750 | 33.3k | { |
751 | 33.3k | const Onion_Node_Cmp *cmp = (const Onion_Node_Cmp *)object; |
752 | 33.3k | Onion_Node *tmp = (Onion_Node *)mem_valloc(cmp->mem, size, sizeof(Onion_Node)); |
753 | | |
754 | 33.3k | if (tmp == nullptr) { |
755 | 0 | return nullptr; |
756 | 0 | } |
757 | | |
758 | 33.3k | return tmp; |
759 | 33.3k | } |
760 | | |
761 | | static void onion_node_delete_handler(const void *_Nonnull object, void *_Nonnull arr, uint32_t size) |
762 | 33.3k | { |
763 | 33.3k | const Onion_Node_Cmp *cmp = (const Onion_Node_Cmp *)object; |
764 | 33.3k | mem_delete(cmp->mem, arr); |
765 | 33.3k | } |
766 | | |
767 | | static const Sort_Funcs onion_node_cmp_funcs = { |
768 | | onion_node_less_handler, |
769 | | onion_node_get_handler, |
770 | | onion_node_set_handler, |
771 | | onion_node_subarr_handler, |
772 | | onion_node_alloc_handler, |
773 | | onion_node_delete_handler, |
774 | | }; |
775 | | |
776 | | static void sort_onion_node_list(const Memory *_Nonnull mem, const Mono_Time *_Nonnull mono_time, Onion_Node *_Nonnull list, unsigned int length, const uint8_t *_Nonnull comp_public_key) |
777 | 33.3k | { |
778 | | // Pass comp_public_key to sort with each Onion_Node entry, so the |
779 | | // comparison function can use it as the base of comparison. |
780 | 33.3k | const Onion_Node_Cmp cmp = { |
781 | 33.3k | mem, |
782 | 33.3k | mono_time, |
783 | 33.3k | comp_public_key, |
784 | 33.3k | }; |
785 | | |
786 | 33.3k | merge_sort(list, length, &cmp, &onion_node_cmp_funcs); |
787 | 33.3k | } |
788 | | |
789 | | static int client_add_to_list(Onion_Client *_Nonnull onion_c, uint32_t num, const uint8_t *_Nonnull public_key, const IP_Port *_Nonnull ip_port, uint8_t is_stored, |
790 | | const uint8_t *_Nonnull pingid_or_key, uint32_t path_used) |
791 | 33.3k | { |
792 | 33.3k | if (num > onion_c->num_friends) { |
793 | 0 | return -1; |
794 | 0 | } |
795 | | |
796 | 33.3k | Onion_Node *node_list = nullptr; |
797 | 33.3k | const uint8_t *reference_id = nullptr; |
798 | 33.3k | unsigned int list_length; |
799 | | |
800 | 33.3k | if (num == 0) { |
801 | 21.5k | node_list = onion_c->clients_announce_list; |
802 | 21.5k | reference_id = nc_get_self_public_key(onion_c->c); |
803 | 21.5k | list_length = MAX_ONION_CLIENTS_ANNOUNCE; |
804 | | |
805 | 21.5k | if (is_stored == 1 && !pk_equal(pingid_or_key, onion_c->temp_public_key)) { |
806 | 0 | is_stored = 0; |
807 | 0 | } |
808 | 21.5k | } else { |
809 | 11.8k | if (is_stored >= 2) { |
810 | 0 | return -1; |
811 | 0 | } |
812 | | |
813 | 11.8k | node_list = onion_c->friends_list[num - 1].clients_list; |
814 | 11.8k | reference_id = onion_c->friends_list[num - 1].real_public_key; |
815 | 11.8k | list_length = MAX_ONION_CLIENTS; |
816 | 11.8k | } |
817 | | |
818 | 33.3k | sort_onion_node_list(onion_c->mem, onion_c->mono_time, node_list, list_length, reference_id); |
819 | | |
820 | 33.3k | int index = -1; |
821 | 33.3k | bool stored = false; |
822 | | |
823 | 33.3k | if (onion_node_timed_out(&node_list[0], onion_c->mono_time) |
824 | 33.3k | || id_closest(reference_id, node_list[0].public_key, public_key) == 2) { |
825 | 31.3k | index = 0; |
826 | 31.3k | } |
827 | | |
828 | 313k | for (unsigned int i = 0; i < list_length; ++i) { |
829 | 303k | if (pk_equal(node_list[i].public_key, public_key)) { |
830 | 22.5k | index = i; |
831 | 22.5k | stored = true; |
832 | 22.5k | break; |
833 | 22.5k | } |
834 | 303k | } |
835 | | |
836 | 33.3k | if (index == -1) { |
837 | 1.49k | return 0; |
838 | 1.49k | } |
839 | | |
840 | 31.8k | memcpy(node_list[index].public_key, public_key, CRYPTO_PUBLIC_KEY_SIZE); |
841 | 31.8k | node_list[index].ip_port = *ip_port; |
842 | | |
843 | | // TODO(irungentoo): remove this and find a better source of nodes to use for paths. |
844 | 31.8k | onion_add_path_node(onion_c, ip_port, public_key); |
845 | | |
846 | 31.8k | if (is_stored == 1) { |
847 | 8.81k | memcpy(node_list[index].data_public_key, pingid_or_key, CRYPTO_PUBLIC_KEY_SIZE); |
848 | 23.0k | } else { |
849 | 23.0k | memcpy(node_list[index].ping_id, pingid_or_key, ONION_PING_ID_SIZE); |
850 | 23.0k | } |
851 | | |
852 | 31.8k | node_list[index].is_stored = is_stored; |
853 | 31.8k | node_list[index].timestamp = mono_time_get(onion_c->mono_time); |
854 | 31.8k | node_list[index].pings_since_last_response = 0; |
855 | | |
856 | 31.8k | if (!stored) { |
857 | 9.28k | node_list[index].last_pinged = 0; |
858 | 9.28k | node_list[index].added_time = mono_time_get(onion_c->mono_time); |
859 | 9.28k | } |
860 | | |
861 | 31.8k | node_list[index].path_used = path_used; |
862 | 31.8k | return 0; |
863 | 33.3k | } |
864 | | |
865 | | static bool good_to_ping(const Mono_Time *_Nonnull mono_time, Last_Pinged *_Nonnull last_pinged, uint8_t *_Nonnull last_pinged_index, const uint8_t *_Nonnull public_key) |
866 | 27.3k | { |
867 | 160k | for (unsigned int i = 0; i < MAX_STORED_PINGED_NODES; ++i) { |
868 | 151k | if (!mono_time_is_timeout(mono_time, last_pinged[i].timestamp, MIN_NODE_PING_TIME)) { |
869 | 125k | if (pk_equal(last_pinged[i].public_key, public_key)) { |
870 | 18.9k | return false; |
871 | 18.9k | } |
872 | 125k | } |
873 | 151k | } |
874 | | |
875 | 8.37k | memcpy(last_pinged[*last_pinged_index % MAX_STORED_PINGED_NODES].public_key, public_key, CRYPTO_PUBLIC_KEY_SIZE); |
876 | 8.37k | last_pinged[*last_pinged_index % MAX_STORED_PINGED_NODES].timestamp = mono_time_get(mono_time); |
877 | 8.37k | ++*last_pinged_index; |
878 | 8.37k | return true; |
879 | 27.3k | } |
880 | | |
881 | | static int client_ping_nodes(Onion_Client *_Nonnull onion_c, uint32_t num, const Node_format *_Nonnull nodes, uint16_t num_nodes, const IP_Port *_Nonnull source) |
882 | 33.3k | { |
883 | 33.3k | if (num > onion_c->num_friends) { |
884 | 0 | return -1; |
885 | 0 | } |
886 | | |
887 | 33.3k | if (num_nodes == 0) { |
888 | 0 | return 0; |
889 | 0 | } |
890 | | |
891 | 33.3k | const Onion_Node *node_list = nullptr; |
892 | 33.3k | const uint8_t *reference_id = nullptr; |
893 | 33.3k | unsigned int list_length; |
894 | | |
895 | 33.3k | Last_Pinged *last_pinged = nullptr; |
896 | 33.3k | uint8_t *last_pinged_index = nullptr; |
897 | | |
898 | 33.3k | if (num == 0) { |
899 | 21.5k | node_list = onion_c->clients_announce_list; |
900 | 21.5k | reference_id = nc_get_self_public_key(onion_c->c); |
901 | 21.5k | list_length = MAX_ONION_CLIENTS_ANNOUNCE; |
902 | 21.5k | last_pinged = onion_c->last_pinged; |
903 | 21.5k | last_pinged_index = &onion_c->last_pinged_index; |
904 | 21.5k | } else { |
905 | 11.8k | node_list = onion_c->friends_list[num - 1].clients_list; |
906 | 11.8k | reference_id = onion_c->friends_list[num - 1].real_public_key; |
907 | 11.8k | list_length = MAX_ONION_CLIENTS; |
908 | 11.8k | last_pinged = onion_c->friends_list[num - 1].last_pinged; |
909 | 11.8k | last_pinged_index = &onion_c->friends_list[num - 1].last_pinged_index; |
910 | 11.8k | } |
911 | | |
912 | 33.3k | const bool lan_ips_accepted = ip_is_lan(&source->ip); |
913 | | |
914 | 137k | for (uint32_t i = 0; i < num_nodes; ++i) { |
915 | 104k | if (!lan_ips_accepted) { |
916 | 19.9k | if (ip_is_lan(&nodes[i].ip_port.ip)) { |
917 | 6.62k | continue; |
918 | 6.62k | } |
919 | 19.9k | } |
920 | | |
921 | 97.5k | if (onion_node_timed_out(&node_list[0], onion_c->mono_time) |
922 | 97.5k | || id_closest(reference_id, node_list[0].public_key, nodes[i].public_key) == 2 |
923 | 97.5k | || onion_node_timed_out(&node_list[1], onion_c->mono_time) |
924 | 97.5k | || id_closest(reference_id, node_list[1].public_key, nodes[i].public_key) == 2) { |
925 | 96.1k | uint32_t j; |
926 | | |
927 | | /* check if node is already in list. */ |
928 | 950k | for (j = 0; j < list_length; ++j) { |
929 | 923k | if (pk_equal(node_list[j].public_key, nodes[i].public_key)) { |
930 | 68.8k | break; |
931 | 68.8k | } |
932 | 923k | } |
933 | | |
934 | 96.1k | if (!net_socket_family_compatible(onion_c->net, &nodes[i].ip_port, false)) { |
935 | 0 | continue; |
936 | 0 | } |
937 | | |
938 | 96.1k | if (j == list_length && good_to_ping(onion_c->mono_time, last_pinged, last_pinged_index, nodes[i].public_key)) { |
939 | 8.38k | client_send_announce_request(onion_c, num, &nodes[i].ip_port, nodes[i].public_key, nullptr, -1); |
940 | 8.38k | } |
941 | 96.1k | } |
942 | 97.5k | } |
943 | | |
944 | 33.3k | return 0; |
945 | 33.3k | } |
946 | | |
947 | | static bool handle_group_announce_response(Onion_Client *_Nonnull onion_c, uint32_t num, const uint8_t *_Nonnull plain, size_t plain_size) |
948 | 1.06k | { |
949 | 1.06k | if (onion_c->group_announce_response == nullptr) { |
950 | 0 | return true; |
951 | 0 | } |
952 | | |
953 | 1.06k | return onion_c->group_announce_response(onion_c, num, plain, plain_size, onion_c->group_announce_response_user_data); |
954 | 1.06k | } |
955 | | |
956 | | static int handle_announce_response(void *_Nonnull object, const IP_Port *_Nonnull source, const uint8_t *_Nonnull packet, uint16_t length, |
957 | | void *_Nullable userdata) |
958 | 1.13k | { |
959 | 1.13k | Onion_Client *onion_c = (Onion_Client *)object; |
960 | 1.13k | if (length < ONION_ANNOUNCE_RESPONSE_MIN_SIZE || length > GCA_ANNOUNCE_RESPONSE_MAX_SIZE) { |
961 | 0 | LOGGER_TRACE(onion_c->logger, "invalid announce response length: %u (min: %u, max: %u)", |
962 | 0 | length, (unsigned int)ONION_ANNOUNCE_RESPONSE_MIN_SIZE, (unsigned int)GCA_ANNOUNCE_RESPONSE_MAX_SIZE); |
963 | 0 | return 1; |
964 | 0 | } |
965 | | |
966 | 1.13k | uint8_t public_key[CRYPTO_PUBLIC_KEY_SIZE] = {0}; |
967 | 1.13k | IP_Port ip_port = {{{0}}}; |
968 | 1.13k | uint32_t path_num = 0; |
969 | 1.13k | const uint32_t num = check_sendback(onion_c, packet + 1, public_key, &ip_port, &path_num); |
970 | | |
971 | 1.13k | if (num > onion_c->num_friends) { |
972 | 2 | return 1; |
973 | 2 | } |
974 | | |
975 | 1.13k | uint8_t plain[1 + ONION_PING_ID_SIZE + GCA_ANNOUNCE_RESPONSE_MAX_SIZE - ONION_ANNOUNCE_RESPONSE_MIN_SIZE]; |
976 | 1.13k | const uint32_t plain_size = 1 + ONION_PING_ID_SIZE + length - ONION_ANNOUNCE_RESPONSE_MIN_SIZE; |
977 | 1.13k | int len; |
978 | 1.13k | const uint16_t nonce_start = 1 + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH; |
979 | 1.13k | const uint16_t ciphertext_start = nonce_start + CRYPTO_NONCE_SIZE; |
980 | 1.13k | const uint16_t ciphertext_size = length - ciphertext_start; |
981 | | |
982 | 1.13k | if (num == 0) { |
983 | 2 | len = decrypt_data(onion_c->mem, public_key, nc_get_self_secret_key(onion_c->c), |
984 | 2 | &packet[nonce_start], &packet[ciphertext_start], ciphertext_size, plain); |
985 | 1.13k | } else { |
986 | 1.13k | if (!onion_c->friends_list[num - 1].is_valid) { |
987 | 0 | LOGGER_TRACE(onion_c->logger, "friend number %lu is invalid", (unsigned long)(num - 1)); |
988 | 0 | return 1; |
989 | 0 | } |
990 | | |
991 | 1.13k | len = decrypt_data(onion_c->mem, public_key, onion_c->friends_list[num - 1].temp_secret_key, |
992 | 1.13k | &packet[nonce_start], &packet[ciphertext_start], ciphertext_size, plain); |
993 | 1.13k | } |
994 | | |
995 | 1.13k | if (len < 0) { |
996 | | // This happens a lot, so don't log it. |
997 | 0 | return 1; |
998 | 0 | } |
999 | | |
1000 | 1.13k | if ((uint32_t)len != plain_size) { |
1001 | 0 | LOGGER_WARNING(onion_c->logger, "decrypted size (%lu) is not the expected plain text size (%lu)", (unsigned long)len, (unsigned long)plain_size); |
1002 | 0 | return 1; |
1003 | 0 | } |
1004 | | |
1005 | 1.13k | const uint32_t path_used = set_path_timeouts(onion_c, num, path_num); |
1006 | | |
1007 | 1.13k | if (client_add_to_list(onion_c, num, public_key, &ip_port, plain[0], plain + 1, path_used) == -1) { |
1008 | 0 | LOGGER_WARNING(onion_c->logger, "failed to add client to list"); |
1009 | 0 | return 1; |
1010 | 0 | } |
1011 | | |
1012 | 1.13k | uint16_t len_nodes = 0; |
1013 | | |
1014 | 1.13k | if (plain_size <= 1 + ONION_PING_ID_SIZE) { |
1015 | 2 | return 1; |
1016 | 2 | } |
1017 | | |
1018 | 1.13k | const uint8_t nodes_count = plain[1 + ONION_PING_ID_SIZE]; |
1019 | | |
1020 | 1.13k | if (nodes_count > 0) { |
1021 | 1.13k | if (nodes_count > MAX_SENT_NODES) { |
1022 | 0 | return 1; |
1023 | 0 | } |
1024 | | |
1025 | 1.13k | Node_format nodes[MAX_SENT_NODES]; |
1026 | 1.13k | const int num_nodes = unpack_nodes(nodes, nodes_count, &len_nodes, plain + 2 + ONION_PING_ID_SIZE, |
1027 | 1.13k | plain_size - 2 - ONION_PING_ID_SIZE, false); |
1028 | | |
1029 | 1.13k | if (num_nodes < 0) { |
1030 | 0 | LOGGER_WARNING(onion_c->logger, "no nodes to unpack in onion response"); |
1031 | 0 | return 1; |
1032 | 0 | } |
1033 | | |
1034 | 1.13k | if (client_ping_nodes(onion_c, num, nodes, num_nodes, source) == -1) { |
1035 | 0 | LOGGER_WARNING(onion_c->logger, "pinging %d nodes failed", num_nodes); |
1036 | 0 | return 1; |
1037 | 0 | } |
1038 | 1.13k | } |
1039 | | |
1040 | 1.13k | if (len_nodes + 1 < length - ONION_ANNOUNCE_RESPONSE_MIN_SIZE) { |
1041 | 1.06k | const uint16_t offset = 2 + ONION_PING_ID_SIZE + len_nodes; |
1042 | | |
1043 | 1.06k | if (plain_size < offset) { |
1044 | 0 | return 1; |
1045 | 0 | } |
1046 | | |
1047 | 1.06k | if (!handle_group_announce_response(onion_c, num, plain + offset, plain_size - offset)) { |
1048 | 0 | return 1; |
1049 | 0 | } |
1050 | 1.06k | } |
1051 | | |
1052 | | // TODO(irungentoo): LAN vs non LAN ips?, if we are connected only to LAN, are we offline? |
1053 | 1.13k | onion_c->last_packet_recv = mono_time_get(onion_c->mono_time); |
1054 | 1.13k | LOGGER_TRACE(onion_c->logger, "onion has received a packet at %llu", |
1055 | 1.13k | (unsigned long long)onion_c->last_packet_recv); |
1056 | | |
1057 | 1.13k | return 0; |
1058 | 1.13k | } |
1059 | | |
1060 | | /* TODO(jfreegman): DEPRECATE */ |
1061 | | static int handle_announce_response_old(void *_Nonnull object, const IP_Port *_Nonnull source, const uint8_t *_Nonnull packet, uint16_t length, |
1062 | | void *_Nullable userdata) |
1063 | 32.8k | { |
1064 | 32.8k | Onion_Client *onion_c = (Onion_Client *)object; |
1065 | 32.8k | if (length < ONION_ANNOUNCE_RESPONSE_MIN_SIZE || length > ONION_ANNOUNCE_RESPONSE_MAX_SIZE) { |
1066 | 0 | LOGGER_TRACE(onion_c->logger, "invalid announce response length: %u (min: %u, max: %u)", |
1067 | 0 | length, (unsigned int)ONION_ANNOUNCE_RESPONSE_MIN_SIZE, (unsigned int)ONION_ANNOUNCE_RESPONSE_MAX_SIZE); |
1068 | 0 | return 1; |
1069 | 0 | } |
1070 | | |
1071 | 32.8k | const uint16_t len_nodes = length - ONION_ANNOUNCE_RESPONSE_MIN_SIZE; |
1072 | | |
1073 | 32.8k | uint8_t public_key[CRYPTO_PUBLIC_KEY_SIZE] = {0}; |
1074 | 32.8k | IP_Port ip_port = {{{0}}}; |
1075 | 32.8k | uint32_t path_num = 0; |
1076 | 32.8k | const uint32_t num = check_sendback(onion_c, packet + 1, public_key, &ip_port, &path_num); |
1077 | | |
1078 | 32.8k | if (num > onion_c->num_friends) { |
1079 | 604 | return 1; |
1080 | 604 | } |
1081 | | |
1082 | 32.2k | const uint16_t plain_size = 1 + ONION_PING_ID_SIZE + len_nodes; |
1083 | 32.2k | VLA(uint8_t, plain, plain_size); |
1084 | 32.2k | int len; |
1085 | 32.2k | const uint16_t nonce_start = 1 + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH; |
1086 | 32.2k | const uint16_t ciphertext_start = nonce_start + CRYPTO_NONCE_SIZE; |
1087 | 32.2k | const uint16_t ciphertext_size = length - ciphertext_start; |
1088 | | |
1089 | 32.2k | if (num == 0) { |
1090 | 21.5k | len = decrypt_data(onion_c->mem, public_key, nc_get_self_secret_key(onion_c->c), |
1091 | 21.5k | &packet[nonce_start], &packet[ciphertext_start], ciphertext_size, plain); |
1092 | 21.5k | } else { |
1093 | 10.7k | if (!onion_c->friends_list[num - 1].is_valid) { |
1094 | 8 | LOGGER_TRACE(onion_c->logger, "friend number %lu is invalid", (unsigned long)(num - 1)); |
1095 | 8 | return 1; |
1096 | 8 | } |
1097 | | |
1098 | 10.7k | len = decrypt_data(onion_c->mem, public_key, onion_c->friends_list[num - 1].temp_secret_key, |
1099 | 10.7k | &packet[nonce_start], &packet[ciphertext_start], ciphertext_size, plain); |
1100 | 10.7k | } |
1101 | | |
1102 | 32.2k | if (len < 0) { |
1103 | | // This happens a lot, so don't log it. |
1104 | 51 | return 1; |
1105 | 51 | } |
1106 | | |
1107 | 32.2k | if ((uint32_t)len != plain_size) { |
1108 | 0 | LOGGER_WARNING(onion_c->logger, "decrypted size (%lu) is not the expected plain text size (%u)", (unsigned long)len, plain_size); |
1109 | 0 | return 1; |
1110 | 0 | } |
1111 | | |
1112 | 32.2k | const uint32_t path_used = set_path_timeouts(onion_c, num, path_num); |
1113 | | |
1114 | 32.2k | if (client_add_to_list(onion_c, num, public_key, &ip_port, plain[0], plain + 1, path_used) == -1) { |
1115 | 0 | LOGGER_WARNING(onion_c->logger, "failed to add client to list"); |
1116 | 0 | return 1; |
1117 | 0 | } |
1118 | | |
1119 | 32.2k | if (len_nodes != 0) { |
1120 | 32.2k | Node_format nodes[MAX_SENT_NODES]; |
1121 | 32.2k | const int num_nodes = unpack_nodes(nodes, MAX_SENT_NODES, nullptr, plain + 1 + ONION_PING_ID_SIZE, len_nodes, false); |
1122 | | |
1123 | 32.2k | if (num_nodes <= 0) { |
1124 | 0 | LOGGER_WARNING(onion_c->logger, "no nodes to unpack in onion response"); |
1125 | 0 | return 1; |
1126 | 0 | } |
1127 | | |
1128 | 32.2k | if (client_ping_nodes(onion_c, num, nodes, num_nodes, source) == -1) { |
1129 | 0 | LOGGER_WARNING(onion_c->logger, "pinging %d nodes failed", num_nodes); |
1130 | 0 | return 1; |
1131 | 0 | } |
1132 | 32.2k | } |
1133 | | |
1134 | | // TODO(irungentoo): LAN vs non LAN ips?, if we are connected only to LAN, are we offline? |
1135 | 32.2k | onion_c->last_packet_recv = mono_time_get(onion_c->mono_time); |
1136 | 32.2k | LOGGER_TRACE(onion_c->logger, "onion has received a packet at %llu", |
1137 | 32.2k | (unsigned long long)onion_c->last_packet_recv); |
1138 | | |
1139 | 32.2k | return 0; |
1140 | 32.2k | } |
1141 | | |
1142 | 27.8k | #define DATA_IN_RESPONSE_MIN_SIZE ONION_DATA_IN_RESPONSE_MIN_SIZE |
1143 | | |
1144 | | static int handle_data_response(void *_Nonnull object, const IP_Port *_Nonnull source, const uint8_t *_Nonnull packet, uint16_t length, void *_Nonnull userdata) |
1145 | 2.14k | { |
1146 | 2.14k | Onion_Client *onion_c = (Onion_Client *)object; |
1147 | | |
1148 | 2.14k | if (length <= (ONION_DATA_RESPONSE_MIN_SIZE + DATA_IN_RESPONSE_MIN_SIZE)) { |
1149 | 0 | return 1; |
1150 | 0 | } |
1151 | | |
1152 | 2.14k | if (length > MAX_DATA_REQUEST_SIZE) { |
1153 | 0 | return 1; |
1154 | 0 | } |
1155 | | |
1156 | 2.14k | const uint16_t temp_plain_size = length - ONION_DATA_RESPONSE_MIN_SIZE; |
1157 | 2.14k | VLA(uint8_t, temp_plain, temp_plain_size); |
1158 | 2.14k | int len = decrypt_data(onion_c->mem, packet + 1 + CRYPTO_NONCE_SIZE, onion_c->temp_secret_key, packet + 1, |
1159 | 2.14k | packet + 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE, |
1160 | 2.14k | length - (1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE), temp_plain); |
1161 | | |
1162 | 2.14k | if ((uint32_t)len != temp_plain_size) { |
1163 | 0 | return 1; |
1164 | 0 | } |
1165 | | |
1166 | 2.14k | const uint16_t plain_size = temp_plain_size - DATA_IN_RESPONSE_MIN_SIZE; |
1167 | 2.14k | VLA(uint8_t, plain, plain_size); |
1168 | 2.14k | len = decrypt_data(onion_c->mem, temp_plain, nc_get_self_secret_key(onion_c->c), |
1169 | 2.14k | packet + 1, temp_plain + CRYPTO_PUBLIC_KEY_SIZE, |
1170 | 2.14k | temp_plain_size - CRYPTO_PUBLIC_KEY_SIZE, plain); |
1171 | | |
1172 | 2.14k | if ((uint32_t)len != plain_size) { |
1173 | 0 | return 1; |
1174 | 0 | } |
1175 | | |
1176 | 2.14k | if (onion_c->onion_data_handlers[plain[0]].function == nullptr) { |
1177 | 0 | return 1; |
1178 | 0 | } |
1179 | | |
1180 | 2.14k | return onion_c->onion_data_handlers[plain[0]].function(onion_c->onion_data_handlers[plain[0]].object, temp_plain, plain, |
1181 | 2.14k | plain_size, userdata); |
1182 | 2.14k | } |
1183 | | |
1184 | 31.4k | #define DHTPK_DATA_MIN_LENGTH (1 + sizeof(uint64_t) + CRYPTO_PUBLIC_KEY_SIZE) |
1185 | 8.95k | #define DHTPK_DATA_MAX_LENGTH (DHTPK_DATA_MIN_LENGTH + sizeof(Node_format)*MAX_SENT_NODES) |
1186 | | static int handle_dhtpk_announce(void *_Nonnull object, const uint8_t *_Nonnull source_pubkey, const uint8_t *_Nonnull data, uint16_t length, |
1187 | | void *_Nullable userdata) |
1188 | 2.11k | { |
1189 | 2.11k | Onion_Client *onion_c = (Onion_Client *)object; |
1190 | 2.11k | if (length < DHTPK_DATA_MIN_LENGTH) { |
1191 | 0 | return 1; |
1192 | 0 | } |
1193 | | |
1194 | 2.11k | if (length > DHTPK_DATA_MAX_LENGTH) { |
1195 | 0 | return 1; |
1196 | 0 | } |
1197 | | |
1198 | 2.11k | const int friend_num = onion_friend_num(onion_c, source_pubkey); |
1199 | | |
1200 | 2.11k | if (friend_num == -1) { |
1201 | 7 | return 1; |
1202 | 7 | } |
1203 | | |
1204 | 2.10k | uint64_t no_replay; |
1205 | 2.10k | net_unpack_u64(data + 1, &no_replay); |
1206 | | |
1207 | 2.10k | if (no_replay <= onion_c->friends_list[friend_num].last_noreplay) { |
1208 | 1.58k | return 1; |
1209 | 1.58k | } |
1210 | | |
1211 | 527 | onion_c->friends_list[friend_num].last_noreplay = no_replay; |
1212 | | |
1213 | 527 | if (onion_c->friends_list[friend_num].dht_pk_callback != nullptr) { |
1214 | 527 | onion_c->friends_list[friend_num].dht_pk_callback(onion_c->friends_list[friend_num].dht_pk_callback_object, |
1215 | 527 | onion_c->friends_list[friend_num].dht_pk_callback_number, data + 1 + sizeof(uint64_t), userdata); |
1216 | 527 | } |
1217 | | |
1218 | 527 | onion_set_friend_dht_pubkey(onion_c, friend_num, data + 1 + sizeof(uint64_t)); |
1219 | | |
1220 | 527 | const uint16_t len_nodes = length - DHTPK_DATA_MIN_LENGTH; |
1221 | | |
1222 | 527 | if (len_nodes != 0) { |
1223 | 527 | Node_format nodes[MAX_SENT_NODES]; |
1224 | 527 | const int num_nodes = unpack_nodes(nodes, MAX_SENT_NODES, nullptr, data + 1 + sizeof(uint64_t) + CRYPTO_PUBLIC_KEY_SIZE, |
1225 | 527 | len_nodes, true); |
1226 | | |
1227 | 527 | if (num_nodes <= 0) { |
1228 | 0 | return 1; |
1229 | 0 | } |
1230 | | |
1231 | 1.82k | for (int i = 0; i < num_nodes; ++i) { |
1232 | 1.29k | const Family family = nodes[i].ip_port.ip.family; |
1233 | | |
1234 | 1.29k | if (net_family_is_ipv4(family) || net_family_is_ipv6(family)) { |
1235 | 1.10k | dht_send_nodes_request(onion_c->dht, &nodes[i].ip_port, nodes[i].public_key, onion_c->friends_list[friend_num].dht_public_key); |
1236 | 1.10k | } else if (net_family_is_tcp_ipv4(family) || net_family_is_tcp_ipv6(family)) { |
1237 | 187 | if (onion_c->friends_list[friend_num].tcp_relay_node_callback != nullptr) { |
1238 | 187 | void *obj = onion_c->friends_list[friend_num].tcp_relay_node_callback_object; |
1239 | 187 | const uint32_t number = onion_c->friends_list[friend_num].tcp_relay_node_callback_number; |
1240 | 187 | onion_c->friends_list[friend_num].tcp_relay_node_callback(obj, number, &nodes[i].ip_port, nodes[i].public_key); |
1241 | 187 | } |
1242 | 187 | } |
1243 | 1.29k | } |
1244 | 527 | } |
1245 | | |
1246 | 527 | return 0; |
1247 | 527 | } |
1248 | | |
1249 | | static int handle_tcp_onion(void *_Nonnull object, const uint8_t *_Nonnull data, uint16_t length, void *_Nonnull userdata) |
1250 | 6.53k | { |
1251 | 6.53k | if (length == 0) { |
1252 | 0 | return 1; |
1253 | 0 | } |
1254 | | |
1255 | 6.53k | IP_Port ip_port = {{{0}}}; |
1256 | 6.53k | ip_port.ip.family = net_family_tcp_server(); |
1257 | | |
1258 | 6.53k | if (data[0] == NET_PACKET_ANNOUNCE_RESPONSE) { |
1259 | 70 | return handle_announce_response(object, &ip_port, data, length, userdata); |
1260 | 70 | } |
1261 | | |
1262 | 6.46k | if (data[0] == NET_PACKET_ANNOUNCE_RESPONSE_OLD) { |
1263 | 6.12k | return handle_announce_response_old(object, &ip_port, data, length, userdata); |
1264 | 6.12k | } |
1265 | | |
1266 | 337 | if (data[0] == NET_PACKET_ONION_DATA_RESPONSE) { |
1267 | 337 | return handle_data_response(object, &ip_port, data, length, userdata); |
1268 | 337 | } |
1269 | | |
1270 | 0 | return 1; |
1271 | 337 | } |
1272 | | |
1273 | | /** @brief Send data of length length to friendnum. |
1274 | | * Maximum length of data is ONION_CLIENT_MAX_DATA_SIZE. |
1275 | | * This data will be received by the friend using the Onion_Data_Handlers callbacks. |
1276 | | * |
1277 | | * Even if this function succeeds, the friend might not receive any data. |
1278 | | * |
1279 | | * return the number of packets sent on success |
1280 | | * return -1 on failure. |
1281 | | */ |
1282 | | int send_onion_data(Onion_Client *onion_c, int friend_num, const uint8_t *data, uint16_t length) |
1283 | 18.3k | { |
1284 | 18.3k | if ((uint32_t)friend_num >= onion_c->num_friends) { |
1285 | 0 | return -1; |
1286 | 0 | } |
1287 | | |
1288 | 18.3k | if (length + DATA_IN_RESPONSE_MIN_SIZE > MAX_DATA_REQUEST_SIZE) { |
1289 | 0 | return -1; |
1290 | 0 | } |
1291 | | |
1292 | 18.3k | if (length == 0) { |
1293 | 0 | return -1; |
1294 | 0 | } |
1295 | | |
1296 | 18.3k | unsigned int good_nodes[MAX_ONION_CLIENTS]; |
1297 | 18.3k | unsigned int num_good = 0; |
1298 | 18.3k | unsigned int num_nodes = 0; |
1299 | 18.3k | const Onion_Node *node_list = onion_c->friends_list[friend_num].clients_list; |
1300 | | |
1301 | 164k | for (unsigned int i = 0; i < MAX_ONION_CLIENTS; ++i) { |
1302 | 146k | if (onion_node_timed_out(&node_list[i], onion_c->mono_time)) { |
1303 | 140k | continue; |
1304 | 140k | } |
1305 | | |
1306 | 5.90k | ++num_nodes; |
1307 | | |
1308 | 5.90k | if (node_list[i].is_stored != 0) { |
1309 | 2.17k | good_nodes[num_good] = i; |
1310 | 2.17k | ++num_good; |
1311 | 2.17k | } |
1312 | 5.90k | } |
1313 | | |
1314 | 18.3k | if (num_good < (num_nodes - 1) / 4 + 1) { |
1315 | 17.6k | return -1; |
1316 | 17.6k | } |
1317 | | |
1318 | 704 | uint8_t nonce[CRYPTO_NONCE_SIZE]; |
1319 | 704 | random_nonce(onion_c->rng, nonce); |
1320 | | |
1321 | 704 | const uint16_t packet_size = DATA_IN_RESPONSE_MIN_SIZE + length; |
1322 | 704 | VLA(uint8_t, packet, packet_size); |
1323 | 704 | memcpy(packet, nc_get_self_public_key(onion_c->c), CRYPTO_PUBLIC_KEY_SIZE); |
1324 | 704 | int len = encrypt_data(onion_c->mem, onion_c->friends_list[friend_num].real_public_key, |
1325 | 704 | nc_get_self_secret_key(onion_c->c), nonce, data, |
1326 | 704 | length, packet + CRYPTO_PUBLIC_KEY_SIZE); |
1327 | | |
1328 | 704 | if ((uint32_t)len + CRYPTO_PUBLIC_KEY_SIZE != packet_size) { |
1329 | 0 | return -1; |
1330 | 0 | } |
1331 | | |
1332 | 704 | unsigned int good = 0; |
1333 | | |
1334 | 2.85k | for (unsigned int i = 0; i < num_good; ++i) { |
1335 | 2.14k | Onion_Path path; |
1336 | | |
1337 | 2.14k | if (random_path(onion_c, &onion_c->onion_paths_friends, -1, &path) == -1) { |
1338 | 0 | continue; |
1339 | 0 | } |
1340 | | |
1341 | 2.14k | uint8_t o_packet[ONION_MAX_PACKET_SIZE]; |
1342 | 2.14k | len = create_data_request( |
1343 | 2.14k | onion_c->mem, onion_c->rng, o_packet, sizeof(o_packet), onion_c->friends_list[friend_num].real_public_key, |
1344 | 2.14k | node_list[good_nodes[i]].data_public_key, nonce, packet, packet_size); |
1345 | | |
1346 | 2.14k | if (len == -1) { |
1347 | 0 | continue; |
1348 | 0 | } |
1349 | | |
1350 | 2.14k | if (send_onion_packet_tcp_udp(onion_c, &path, &node_list[good_nodes[i]].ip_port, o_packet, len) == 0) { |
1351 | 2.14k | ++good; |
1352 | 2.14k | } |
1353 | 2.14k | } |
1354 | | |
1355 | 704 | return good; |
1356 | 704 | } |
1357 | | |
1358 | | /** @brief Try to send the dht public key via the DHT instead of onion |
1359 | | * |
1360 | | * Even if this function succeeds, the friend might not receive any data. |
1361 | | * |
1362 | | * return the number of packets sent on success |
1363 | | * return -1 on failure. |
1364 | | */ |
1365 | | static int send_dht_dhtpk(const Onion_Client *_Nonnull onion_c, int friend_num, const uint8_t *_Nonnull data, uint16_t length) |
1366 | 4.89k | { |
1367 | 4.89k | if ((uint32_t)friend_num >= onion_c->num_friends) { |
1368 | 0 | return -1; |
1369 | 0 | } |
1370 | | |
1371 | 4.89k | if (!onion_c->friends_list[friend_num].know_dht_public_key) { |
1372 | 1.38k | return -1; |
1373 | 1.38k | } |
1374 | | |
1375 | 3.50k | uint8_t nonce[CRYPTO_NONCE_SIZE]; |
1376 | 3.50k | random_nonce(onion_c->rng, nonce); |
1377 | | |
1378 | 3.50k | const uint16_t temp_size = DATA_IN_RESPONSE_MIN_SIZE + CRYPTO_NONCE_SIZE + length; |
1379 | 3.50k | VLA(uint8_t, temp, temp_size); |
1380 | 3.50k | memcpy(temp, nc_get_self_public_key(onion_c->c), CRYPTO_PUBLIC_KEY_SIZE); |
1381 | 3.50k | memcpy(temp + CRYPTO_PUBLIC_KEY_SIZE, nonce, CRYPTO_NONCE_SIZE); |
1382 | 3.50k | int len = encrypt_data(onion_c->mem, onion_c->friends_list[friend_num].real_public_key, |
1383 | 3.50k | nc_get_self_secret_key(onion_c->c), nonce, data, |
1384 | 3.50k | length, temp + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE); |
1385 | | |
1386 | 3.50k | if ((uint32_t)len + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE != temp_size) { |
1387 | 0 | return -1; |
1388 | 0 | } |
1389 | | |
1390 | 3.50k | uint8_t packet_data[MAX_CRYPTO_REQUEST_SIZE]; |
1391 | 3.50k | len = create_request( |
1392 | 3.50k | onion_c->mem, onion_c->rng, dht_get_self_public_key(onion_c->dht), dht_get_self_secret_key(onion_c->dht), packet_data, |
1393 | 3.50k | onion_c->friends_list[friend_num].dht_public_key, temp, temp_size, CRYPTO_PACKET_DHTPK); |
1394 | 3.50k | assert(len <= UINT16_MAX); |
1395 | 3.50k | const Net_Packet packet = {packet_data, (uint16_t)len}; |
1396 | | |
1397 | 3.50k | if (len == -1) { |
1398 | 0 | return -1; |
1399 | 0 | } |
1400 | | |
1401 | 3.50k | return route_to_friend(onion_c->dht, onion_c->friends_list[friend_num].dht_public_key, &packet); |
1402 | 3.50k | } |
1403 | | |
1404 | | static int handle_dht_dhtpk(void *_Nonnull object, const IP_Port *_Nonnull source, const uint8_t *_Nonnull source_pubkey, const uint8_t *_Nonnull packet, uint16_t length, |
1405 | | void *_Nonnull userdata) |
1406 | 346 | { |
1407 | 346 | Onion_Client *onion_c = (Onion_Client *)object; |
1408 | | |
1409 | 346 | if (length < DHTPK_DATA_MIN_LENGTH + DATA_IN_RESPONSE_MIN_SIZE + CRYPTO_NONCE_SIZE) { |
1410 | 0 | return 1; |
1411 | 0 | } |
1412 | | |
1413 | 346 | if (length > DHTPK_DATA_MAX_LENGTH + DATA_IN_RESPONSE_MIN_SIZE + CRYPTO_NONCE_SIZE) { |
1414 | 0 | return 1; |
1415 | 0 | } |
1416 | | |
1417 | 346 | uint8_t plain[DHTPK_DATA_MAX_LENGTH]; |
1418 | 346 | const int len = decrypt_data(onion_c->mem, packet, nc_get_self_secret_key(onion_c->c), |
1419 | 346 | packet + CRYPTO_PUBLIC_KEY_SIZE, |
1420 | 346 | packet + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE, |
1421 | 346 | length - (CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE), plain); |
1422 | | |
1423 | 346 | if (len != length - (DATA_IN_RESPONSE_MIN_SIZE + CRYPTO_NONCE_SIZE)) { |
1424 | 0 | return 1; |
1425 | 0 | } |
1426 | | |
1427 | 346 | if (!pk_equal(source_pubkey, plain + 1 + sizeof(uint64_t))) { |
1428 | 0 | return 1; |
1429 | 0 | } |
1430 | | |
1431 | 346 | return handle_dhtpk_announce(onion_c, packet, plain, len, userdata); |
1432 | 346 | } |
1433 | | |
1434 | | /** @brief Send the packets to tell our friends what our DHT public key is. |
1435 | | * |
1436 | | * if onion_dht_both is 0, use only the onion to send the packet. |
1437 | | * if it is 1, use only the dht. |
1438 | | * if it is something else, use both. |
1439 | | * |
1440 | | * return the number of packets sent on success |
1441 | | * return -1 on failure. |
1442 | | */ |
1443 | | static int send_dhtpk_announce(Onion_Client *_Nonnull onion_c, uint32_t friend_num, uint8_t onion_dht_both) |
1444 | 6.49k | { |
1445 | 6.49k | if (friend_num >= onion_c->num_friends) { |
1446 | 0 | return -1; |
1447 | 0 | } |
1448 | | |
1449 | 6.49k | uint8_t data[DHTPK_DATA_MAX_LENGTH]; |
1450 | 6.49k | data[0] = ONION_DATA_DHTPK; |
1451 | 6.49k | const uint64_t no_replay = mono_time_get(onion_c->mono_time); |
1452 | 6.49k | net_pack_u64(data + 1, no_replay); |
1453 | 6.49k | memcpy(data + 1 + sizeof(uint64_t), dht_get_self_public_key(onion_c->dht), CRYPTO_PUBLIC_KEY_SIZE); |
1454 | 6.49k | Node_format nodes[MAX_SENT_NODES]; |
1455 | 6.49k | const uint16_t num_relays = copy_connected_tcp_relays(onion_c->c, nodes, MAX_SENT_NODES / 2); |
1456 | 6.49k | uint16_t num_nodes = closelist_nodes(onion_c->dht, &nodes[num_relays], MAX_SENT_NODES - num_relays); |
1457 | 6.49k | num_nodes += num_relays; |
1458 | 6.49k | int nodes_len = 0; |
1459 | | |
1460 | 6.49k | if (num_nodes != 0) { |
1461 | 6.49k | nodes_len = pack_nodes(onion_c->logger, data + DHTPK_DATA_MIN_LENGTH, DHTPK_DATA_MAX_LENGTH - DHTPK_DATA_MIN_LENGTH, |
1462 | 6.49k | nodes, num_nodes); |
1463 | | |
1464 | 6.49k | if (nodes_len <= 0) { |
1465 | 0 | return -1; |
1466 | 0 | } |
1467 | 6.49k | } |
1468 | | |
1469 | 6.49k | int num1 = -1; |
1470 | 6.49k | int num2 = -1; |
1471 | | |
1472 | 6.49k | if (onion_dht_both != 1) { |
1473 | 1.60k | num1 = send_onion_data(onion_c, friend_num, data, DHTPK_DATA_MIN_LENGTH + nodes_len); |
1474 | 1.60k | } |
1475 | | |
1476 | 6.49k | if (onion_dht_both != 0) { |
1477 | 4.89k | num2 = send_dht_dhtpk(onion_c, friend_num, data, DHTPK_DATA_MIN_LENGTH + nodes_len); |
1478 | 4.89k | } |
1479 | | |
1480 | 6.49k | if (num1 == -1) { |
1481 | 6.02k | return num2; |
1482 | 6.02k | } |
1483 | | |
1484 | 474 | if (num2 == -1) { |
1485 | 474 | return num1; |
1486 | 474 | } |
1487 | | |
1488 | 18.4E | return num1 + num2; |
1489 | 473 | } |
1490 | | |
1491 | | /** @brief Get the friend_num of a friend. |
1492 | | * |
1493 | | * return -1 on failure. |
1494 | | * return friend number on success. |
1495 | | */ |
1496 | | int onion_friend_num(const Onion_Client *onion_c, const uint8_t *public_key) |
1497 | 3.10k | { |
1498 | 3.10k | return bs_list_find(&onion_c->friends_lookup, public_key); |
1499 | 3.10k | } |
1500 | | |
1501 | | /** @brief Set the size of the friend list to num. |
1502 | | * |
1503 | | * @retval -1 if mem_vrealloc fails. |
1504 | | * @retval 0 if it succeeds. |
1505 | | */ |
1506 | | static int realloc_onion_friends(Onion_Client *_Nonnull onion_c, uint32_t num) |
1507 | 2.89k | { |
1508 | 2.89k | if (num == 0) { |
1509 | 1.92k | mem_delete(onion_c->mem, onion_c->friends_list); |
1510 | 1.92k | onion_c->friends_list = nullptr; |
1511 | 1.92k | onion_c->friends_list_capacity = 0; |
1512 | 1.92k | return 0; |
1513 | 1.92k | } |
1514 | | |
1515 | 969 | if (num <= onion_c->friends_list_capacity) { |
1516 | 155 | return 0; |
1517 | 155 | } |
1518 | | |
1519 | | // Geometric growth: Double the capacity or set to num if starting. |
1520 | 814 | uint32_t new_capacity = onion_c->friends_list_capacity == 0 ? num : onion_c->friends_list_capacity * 2; |
1521 | 814 | if (new_capacity < num) { |
1522 | 0 | new_capacity = num; |
1523 | 0 | } |
1524 | | |
1525 | 814 | Onion_Friend *newonion_friends = (Onion_Friend *)mem_vrealloc(onion_c->mem, onion_c->friends_list, new_capacity, sizeof(Onion_Friend)); |
1526 | | |
1527 | 814 | if (newonion_friends == nullptr) { |
1528 | 0 | return -1; |
1529 | 0 | } |
1530 | | |
1531 | 814 | onion_c->friends_list = newonion_friends; |
1532 | 814 | onion_c->friends_list_capacity = new_capacity; |
1533 | 814 | return 0; |
1534 | 814 | } |
1535 | | |
1536 | | /** @brief Add a friend who we want to connect to. |
1537 | | * |
1538 | | * return -1 on failure. |
1539 | | * return the friend number on success or if the friend was already added. |
1540 | | */ |
1541 | | int onion_addfriend(Onion_Client *onion_c, const uint8_t *public_key) |
1542 | 983 | { |
1543 | 983 | const int num = onion_friend_num(onion_c, public_key); |
1544 | | |
1545 | 983 | if (num != -1) { |
1546 | 1 | return num; |
1547 | 1 | } |
1548 | | |
1549 | 982 | uint32_t index = (uint32_t) -1; |
1550 | | |
1551 | 2.59k | for (uint32_t i = 0; i < onion_c->num_friends; ++i) { |
1552 | 1.62k | if (!onion_c->friends_list[i].is_valid) { |
1553 | 13 | index = i; |
1554 | 13 | break; |
1555 | 13 | } |
1556 | 1.62k | } |
1557 | | |
1558 | 982 | if (index == (uint32_t) -1) { |
1559 | 969 | if (realloc_onion_friends(onion_c, onion_c->num_friends + 1) == -1) { |
1560 | 0 | return -1; |
1561 | 0 | } |
1562 | | |
1563 | 969 | index = onion_c->num_friends; |
1564 | 969 | onion_c->friends_list[onion_c->num_friends] = empty_onion_friend; |
1565 | 969 | ++onion_c->num_friends; |
1566 | 969 | } |
1567 | | |
1568 | 982 | onion_c->friends_list[index].is_valid = true; |
1569 | 982 | memcpy(onion_c->friends_list[index].real_public_key, public_key, CRYPTO_PUBLIC_KEY_SIZE); |
1570 | 982 | crypto_new_keypair(onion_c->rng, onion_c->friends_list[index].temp_public_key, |
1571 | 982 | onion_c->friends_list[index].temp_secret_key); |
1572 | | |
1573 | 982 | if (!bs_list_add(&onion_c->friends_lookup, public_key, index)) { |
1574 | 0 | LOGGER_ERROR(onion_c->logger, "Failed to add friend to lookup list (index: %u)", index); |
1575 | 0 | return -1; |
1576 | 0 | } |
1577 | | |
1578 | 982 | return index; |
1579 | 982 | } |
1580 | | |
1581 | | /** @brief Delete a friend. |
1582 | | * |
1583 | | * return -1 on failure. |
1584 | | * return the deleted friend number on success. |
1585 | | */ |
1586 | | int onion_delfriend(Onion_Client *onion_c, int friend_num) |
1587 | 975 | { |
1588 | 975 | if ((uint32_t)friend_num >= onion_c->num_friends) { |
1589 | 1 | return -1; |
1590 | 1 | } |
1591 | | |
1592 | | #if 0 |
1593 | | |
1594 | | if (onion_c->friends_list[friend_num].know_dht_public_key) { |
1595 | | dht_delfriend(onion_c->dht, onion_c->friends_list[friend_num].dht_public_key, 0); |
1596 | | } |
1597 | | |
1598 | | #endif /* 0 */ |
1599 | | |
1600 | 974 | if (!bs_list_remove(&onion_c->friends_lookup, onion_c->friends_list[friend_num].real_public_key, friend_num)) { |
1601 | 0 | LOGGER_ERROR(onion_c->logger, "Failed to remove friend from lookup list (index: %d)", friend_num); |
1602 | 0 | } |
1603 | | |
1604 | 974 | crypto_memzero(&onion_c->friends_list[friend_num], sizeof(Onion_Friend)); |
1605 | 974 | uint32_t i; |
1606 | | |
1607 | 1.93k | for (i = onion_c->num_friends; i != 0; --i) { |
1608 | 1.48k | if (onion_c->friends_list[i - 1].is_valid) { |
1609 | 523 | break; |
1610 | 523 | } |
1611 | 1.48k | } |
1612 | | |
1613 | 974 | onion_c->num_friends = i; |
1614 | | |
1615 | 974 | return friend_num; |
1616 | 975 | } |
1617 | | |
1618 | | /** @brief Set the function for this friend that will be callbacked with object and number |
1619 | | * when that friend gives us one of the TCP relays they are connected to. |
1620 | | * |
1621 | | * object and number will be passed as argument to this function. |
1622 | | * |
1623 | | * return -1 on failure. |
1624 | | * return 0 on success. |
1625 | | */ |
1626 | | int recv_tcp_relay_handler(Onion_Client *onion_c, int friend_num, |
1627 | | recv_tcp_relay_cb *callback, void *object, uint32_t number) |
1628 | 973 | { |
1629 | 973 | if ((uint32_t)friend_num >= onion_c->num_friends) { |
1630 | 0 | return -1; |
1631 | 0 | } |
1632 | | |
1633 | 973 | onion_c->friends_list[friend_num].tcp_relay_node_callback = callback; |
1634 | 973 | onion_c->friends_list[friend_num].tcp_relay_node_callback_object = object; |
1635 | 973 | onion_c->friends_list[friend_num].tcp_relay_node_callback_number = number; |
1636 | 973 | return 0; |
1637 | 973 | } |
1638 | | |
1639 | | /** @brief Set the function for this friend that will be callbacked with object and number |
1640 | | * when that friend gives us their DHT temporary public key. |
1641 | | * |
1642 | | * object and number will be passed as argument to this function. |
1643 | | * |
1644 | | * return -1 on failure. |
1645 | | * return 0 on success. |
1646 | | */ |
1647 | | int onion_dht_pk_callback(Onion_Client *onion_c, int friend_num, |
1648 | | onion_dht_pk_cb *function, void *object, uint32_t number) |
1649 | 975 | { |
1650 | 975 | if ((uint32_t)friend_num >= onion_c->num_friends) { |
1651 | 0 | return -1; |
1652 | 0 | } |
1653 | | |
1654 | 975 | onion_c->friends_list[friend_num].dht_pk_callback = function; |
1655 | 975 | onion_c->friends_list[friend_num].dht_pk_callback_object = object; |
1656 | 975 | onion_c->friends_list[friend_num].dht_pk_callback_number = number; |
1657 | 975 | return 0; |
1658 | 975 | } |
1659 | | |
1660 | | /** @brief Set a friend's DHT public key. |
1661 | | * |
1662 | | * return -1 on failure. |
1663 | | * return 0 on success. |
1664 | | */ |
1665 | | int onion_set_friend_dht_pubkey(Onion_Client *onion_c, int friend_num, const uint8_t *dht_key) |
1666 | 1.03k | { |
1667 | 1.03k | if ((uint32_t)friend_num >= onion_c->num_friends) { |
1668 | 1 | return -1; |
1669 | 1 | } |
1670 | | |
1671 | 1.03k | if (!onion_c->friends_list[friend_num].is_valid) { |
1672 | 0 | return -1; |
1673 | 0 | } |
1674 | | |
1675 | 1.03k | if (onion_c->friends_list[friend_num].know_dht_public_key) { |
1676 | 523 | if (pk_equal(dht_key, onion_c->friends_list[friend_num].dht_public_key)) { |
1677 | 521 | return -1; |
1678 | 521 | } |
1679 | 523 | } |
1680 | | |
1681 | 511 | onion_c->friends_list[friend_num].know_dht_public_key = true; |
1682 | 511 | memcpy(onion_c->friends_list[friend_num].dht_public_key, dht_key, CRYPTO_PUBLIC_KEY_SIZE); |
1683 | | |
1684 | 511 | return 0; |
1685 | 1.03k | } |
1686 | | |
1687 | | /** @brief Copy friends DHT public key into dht_key. |
1688 | | * |
1689 | | * return 0 on failure (no key copied). |
1690 | | * return 1 on success (key copied). |
1691 | | */ |
1692 | | unsigned int onion_getfriend_dht_pubkey(const Onion_Client *onion_c, int friend_num, uint8_t *dht_key) |
1693 | 4 | { |
1694 | 4 | if ((uint32_t)friend_num >= onion_c->num_friends) { |
1695 | 1 | return 0; |
1696 | 1 | } |
1697 | | |
1698 | 3 | if (!onion_c->friends_list[friend_num].is_valid) { |
1699 | 0 | return 0; |
1700 | 0 | } |
1701 | | |
1702 | 3 | if (!onion_c->friends_list[friend_num].know_dht_public_key) { |
1703 | 0 | return 0; |
1704 | 0 | } |
1705 | | |
1706 | 3 | memcpy(dht_key, onion_c->friends_list[friend_num].dht_public_key, CRYPTO_PUBLIC_KEY_SIZE); |
1707 | 3 | return 1; |
1708 | 3 | } |
1709 | | |
1710 | | /** @brief Get the ip of friend friendnum and put it in ip_port |
1711 | | * |
1712 | | * @retval -1 if public_key does NOT refer to a friend |
1713 | | * @retval 0 if public_key refers to a friend and we failed to find the friend (yet) |
1714 | | * @retval 1 if public_key refers to a friend and we found them |
1715 | | */ |
1716 | | int onion_getfriendip(const Onion_Client *onion_c, int friend_num, IP_Port *ip_port) |
1717 | 2 | { |
1718 | 2 | uint8_t dht_public_key[CRYPTO_PUBLIC_KEY_SIZE]; |
1719 | | |
1720 | 2 | if (onion_getfriend_dht_pubkey(onion_c, friend_num, dht_public_key) == 0) { |
1721 | 0 | return -1; |
1722 | 0 | } |
1723 | | |
1724 | 2 | return dht_getfriendip(onion_c->dht, dht_public_key, ip_port); |
1725 | 2 | } |
1726 | | |
1727 | | /** @brief Set if friend is online or not. |
1728 | | * |
1729 | | * NOTE: This function is there and should be used so that we don't send |
1730 | | * useless packets to the friend if they are online. |
1731 | | * |
1732 | | * return -1 on failure. |
1733 | | * return 0 on success. |
1734 | | */ |
1735 | | int onion_set_friend_online(Onion_Client *onion_c, int friend_num, bool is_online) |
1736 | 521 | { |
1737 | 521 | if ((uint32_t)friend_num >= onion_c->num_friends) { |
1738 | 1 | return -1; |
1739 | 1 | } |
1740 | | |
1741 | 520 | onion_c->friends_list[friend_num].is_online = is_online; |
1742 | | |
1743 | | /* This should prevent some clock related issues */ |
1744 | 520 | if (!is_online) { |
1745 | 17 | onion_c->friends_list[friend_num].last_noreplay = 0; |
1746 | 17 | onion_c->friends_list[friend_num].run_count = 0; |
1747 | 17 | } |
1748 | | |
1749 | 520 | return 0; |
1750 | 521 | } |
1751 | | |
1752 | | static void populate_path_nodes(Onion_Client *_Nonnull onion_c) |
1753 | 7.48k | { |
1754 | 7.48k | Node_format node_list[MAX_FRIEND_CLIENTS]; |
1755 | | |
1756 | 7.48k | const unsigned int num_nodes = randfriends_nodes(onion_c->dht, node_list, MAX_FRIEND_CLIENTS); |
1757 | | |
1758 | 42.6k | for (unsigned int i = 0; i < num_nodes; ++i) { |
1759 | 35.1k | onion_add_path_node(onion_c, &node_list[i].ip_port, node_list[i].public_key); |
1760 | 35.1k | } |
1761 | 7.48k | } |
1762 | | |
1763 | | /* How often we ping new friends per node */ |
1764 | 7.18k | #define ANNOUNCE_FRIEND_NEW_INTERVAL 3 |
1765 | | |
1766 | | /* How long we consider a friend new based on the value of their run_count */ |
1767 | 7.46k | #define ANNOUNCE_FRIEND_RUN_COUNT_BEGINNING 5 |
1768 | | |
1769 | | /* How often we try to re-populate the nodes lists if we don't meet a minimum threshhold of nodes */ |
1770 | 1.02k | #define ANNOUNCE_POPULATE_TIMEOUT (60 * 10) |
1771 | | |
1772 | | /* The max time between lookup requests for a friend per node */ |
1773 | 277 | #define ANNOUNCE_FRIEND_MAX_INTERVAL (60 * 60) |
1774 | | |
1775 | | /* Max exponent when calculating the announce request interval */ |
1776 | 277 | #define MAX_RUN_COUNT_EXPONENT 12 |
1777 | | |
1778 | | static void do_friend(Onion_Client *_Nonnull onion_c, uint32_t friendnum) |
1779 | 7.46k | { |
1780 | 7.46k | if (friendnum >= onion_c->num_friends) { |
1781 | 0 | return; |
1782 | 0 | } |
1783 | | |
1784 | 7.46k | Onion_Friend *o_friend = &onion_c->friends_list[friendnum]; |
1785 | | |
1786 | 7.46k | if (!o_friend->is_valid) { |
1787 | 2 | return; |
1788 | 2 | } |
1789 | | |
1790 | 7.46k | uint32_t interval; |
1791 | 7.46k | const uint64_t tm = mono_time_get(onion_c->mono_time); |
1792 | 7.46k | const bool friend_is_new = o_friend->run_count <= ANNOUNCE_FRIEND_RUN_COUNT_BEGINNING; |
1793 | | |
1794 | 7.46k | if (!friend_is_new) { |
1795 | | // how often we ping a node for a friend depends on how many times we've already tried. |
1796 | | // the interval increases exponentially, as the longer a friend has been offline, the less |
1797 | | // likely the case is that they're online and failed to find us |
1798 | 277 | const uint32_t c = 1 << min_u32(MAX_RUN_COUNT_EXPONENT, o_friend->run_count - 2); |
1799 | 277 | interval = min_u32(c, ANNOUNCE_FRIEND_MAX_INTERVAL); |
1800 | 7.18k | } else { |
1801 | 7.18k | interval = ANNOUNCE_FRIEND_NEW_INTERVAL; |
1802 | 7.18k | } |
1803 | | |
1804 | 7.46k | if (o_friend->is_online) { |
1805 | 2.53k | return; |
1806 | 2.53k | } |
1807 | | |
1808 | 4.92k | assert(interval >= ANNOUNCE_FRIEND_NEW_INTERVAL); // an int overflow would be devastating |
1809 | | |
1810 | | /* send packets to friend telling them our DHT public key. */ |
1811 | 4.92k | if (mono_time_is_timeout(onion_c->mono_time, onion_c->friends_list[friendnum].last_dht_pk_onion_sent, |
1812 | 4.92k | ONION_DHTPK_SEND_INTERVAL)) { |
1813 | 1.60k | if (send_dhtpk_announce(onion_c, friendnum, 0) >= 1) { |
1814 | 474 | onion_c->friends_list[friendnum].last_dht_pk_onion_sent = tm; |
1815 | 474 | } |
1816 | 1.60k | } |
1817 | | |
1818 | 4.92k | if (mono_time_is_timeout(onion_c->mono_time, onion_c->friends_list[friendnum].last_dht_pk_dht_sent, |
1819 | 4.92k | DHT_DHTPK_SEND_INTERVAL)) { |
1820 | 4.89k | if (send_dhtpk_announce(onion_c, friendnum, 1) >= 1) { |
1821 | 90 | onion_c->friends_list[friendnum].last_dht_pk_dht_sent = tm; |
1822 | 90 | } |
1823 | 4.89k | } |
1824 | | |
1825 | 4.92k | uint16_t count = 0; // number of alive path nodes |
1826 | | |
1827 | 4.92k | Onion_Node *node_list = o_friend->clients_list; |
1828 | | |
1829 | 44.3k | for (unsigned i = 0; i < MAX_ONION_CLIENTS; ++i) { |
1830 | 39.4k | if (onion_node_timed_out(&node_list[i], onion_c->mono_time)) { |
1831 | 30.7k | continue; |
1832 | 30.7k | } |
1833 | | |
1834 | 8.64k | ++count; |
1835 | | |
1836 | | // we don't want new nodes to be pinged immediately |
1837 | 8.64k | if (node_list[i].last_pinged == 0) { |
1838 | 1.83k | node_list[i].last_pinged = tm; |
1839 | 1.83k | continue; |
1840 | 1.83k | } |
1841 | | |
1842 | | // node hasn't responded in a while so we skip it |
1843 | 6.81k | if (node_list[i].pings_since_last_response >= ONION_NODE_MAX_PINGS) { |
1844 | 87 | continue; |
1845 | 87 | } |
1846 | | |
1847 | | // space requests out between nodes |
1848 | 6.72k | if (!mono_time_is_timeout(onion_c->mono_time, o_friend->time_last_pinged, interval / (MAX_ONION_CLIENTS / 2))) { |
1849 | 582 | continue; |
1850 | 582 | } |
1851 | | |
1852 | 6.14k | if (!mono_time_is_timeout(onion_c->mono_time, node_list[i].last_pinged, interval)) { |
1853 | 4.42k | continue; |
1854 | 4.42k | } |
1855 | | |
1856 | 1.72k | if (client_send_announce_request(onion_c, friendnum + 1, &node_list[i].ip_port, |
1857 | 1.72k | node_list[i].public_key, nullptr, -1) == 0) { |
1858 | 1.72k | node_list[i].last_pinged = tm; |
1859 | 1.72k | o_friend->time_last_pinged = tm; |
1860 | 1.72k | ++node_list[i].pings_since_last_response; |
1861 | 1.72k | ++o_friend->pings; |
1862 | | |
1863 | 1.72k | if (o_friend->pings % (MAX_ONION_CLIENTS / 2) == 0) { |
1864 | 375 | ++o_friend->run_count; |
1865 | 375 | } |
1866 | 1.72k | } |
1867 | 1.72k | } |
1868 | | |
1869 | 4.92k | if (count == MAX_ONION_CLIENTS) { |
1870 | 196 | if (!friend_is_new) { |
1871 | 0 | o_friend->last_populated = tm; |
1872 | 0 | } |
1873 | | |
1874 | 196 | return; |
1875 | 196 | } |
1876 | | |
1877 | | // check if path nodes list for this friend needs to be repopulated |
1878 | 4.73k | if (count <= MAX_ONION_CLIENTS / 2 |
1879 | 4.73k | || mono_time_is_timeout(onion_c->mono_time, o_friend->last_populated, ANNOUNCE_POPULATE_TIMEOUT)) { |
1880 | 4.22k | const uint16_t num_nodes = min_u16(onion_c->path_nodes_index, MAX_PATH_NODES); |
1881 | 4.22k | const uint16_t n = min_u16(num_nodes, MAX_PATH_NODES / 4); |
1882 | | |
1883 | 4.22k | if (n == 0) { |
1884 | 0 | return; |
1885 | 0 | } |
1886 | | |
1887 | 4.22k | o_friend->last_populated = tm; |
1888 | | |
1889 | 11.4k | for (uint16_t i = 0; i < n; ++i) { |
1890 | 7.27k | const uint32_t num = random_range_u32(onion_c->rng, num_nodes); |
1891 | 7.27k | client_send_announce_request(onion_c, friendnum + 1, &onion_c->path_nodes[num].ip_port, |
1892 | 7.27k | onion_c->path_nodes[num].public_key, nullptr, -1); |
1893 | 7.27k | } |
1894 | 4.22k | } |
1895 | 4.73k | } |
1896 | | |
1897 | | /** Function to call when onion data packet with contents beginning with byte is received. */ |
1898 | | void oniondata_registerhandler(Onion_Client *onion_c, uint8_t byte, oniondata_handler_cb *cb, void *object) |
1899 | 5.64k | { |
1900 | 5.64k | onion_c->onion_data_handlers[byte].function = cb; |
1901 | 5.64k | onion_c->onion_data_handlers[byte].object = object; |
1902 | 5.64k | } |
1903 | | |
1904 | | void onion_group_announce_register(Onion_Client *onion_c, onion_group_announce_cb *func, void *user_data) |
1905 | 3.62k | { |
1906 | 3.62k | onion_c->group_announce_response = func; |
1907 | 3.62k | onion_c->group_announce_response_user_data = user_data; |
1908 | 3.62k | } |
1909 | | |
1910 | 33.4k | #define ANNOUNCE_INTERVAL_NOT_ANNOUNCED 3 |
1911 | 27.4k | #define ANNOUNCE_INTERVAL_ANNOUNCED ONION_NODE_PING_INTERVAL |
1912 | | |
1913 | 27.5k | #define TIME_TO_STABLE (ONION_NODE_PING_INTERVAL * 6) |
1914 | 0 | #define ANNOUNCE_INTERVAL_STABLE (ONION_NODE_PING_INTERVAL * 8) |
1915 | | |
1916 | | static bool key_list_contains(const uint8_t *const *_Nonnull keys, uint16_t keys_size, const uint8_t *_Nonnull public_key) |
1917 | 32.0k | { |
1918 | 54.4k | for (uint16_t i = 0; i < keys_size; ++i) { |
1919 | 40.9k | if (memeq(keys[i], CRYPTO_PUBLIC_KEY_SIZE, public_key, CRYPTO_PUBLIC_KEY_SIZE)) { |
1920 | 18.5k | return true; |
1921 | 18.5k | } |
1922 | 40.9k | } |
1923 | | |
1924 | 13.5k | return false; |
1925 | 32.0k | } |
1926 | | |
1927 | | /** Does path with path_num exist. */ |
1928 | | static bool path_exists(const Mono_Time *_Nonnull mono_time, const Onion_Client_Paths *_Nonnull onion_paths, uint32_t path_num) |
1929 | 27.5k | { |
1930 | 27.5k | if (path_timed_out(mono_time, onion_paths, path_num)) { |
1931 | 31 | return false; |
1932 | 31 | } |
1933 | | |
1934 | 27.5k | return onion_paths->paths[path_num % NUMBER_ONION_PATHS].path_num == path_num; |
1935 | 27.5k | } |
1936 | | |
1937 | | /** |
1938 | | * A node/path is considered "stable" if it has survived for at least TIME_TO_STABLE |
1939 | | * and the latest packets sent to it are not timing out. |
1940 | | */ |
1941 | | static bool path_is_stable(const Mono_Time *_Nonnull mono_time, const Onion_Client_Paths *_Nonnull paths, uint32_t pathnum, const Onion_Node *_Nonnull node) |
1942 | 27.4k | { |
1943 | 27.4k | return mono_time_is_timeout(mono_time, node->added_time, TIME_TO_STABLE) |
1944 | 27.4k | && !(node->pings_since_last_response > 0 |
1945 | 0 | && mono_time_is_timeout(mono_time, node->last_pinged, ONION_NODE_TIMEOUT)) |
1946 | 27.4k | && mono_time_is_timeout(mono_time, paths->path_creation_time[pathnum], TIME_TO_STABLE) |
1947 | 27.4k | && !(paths->last_path_used_times[pathnum] > 0 |
1948 | 0 | && mono_time_is_timeout(mono_time, paths->last_path_used[pathnum], ONION_PATH_TIMEOUT)); |
1949 | 27.4k | } |
1950 | | |
1951 | | static void do_announce(Onion_Client *_Nonnull onion_c) |
1952 | 7.48k | { |
1953 | 7.48k | unsigned int count = 0; |
1954 | 7.48k | Onion_Node *node_list = onion_c->clients_announce_list; |
1955 | | |
1956 | 97.2k | for (unsigned int i = 0; i < MAX_ONION_CLIENTS_ANNOUNCE; ++i) { |
1957 | 89.7k | if (onion_node_timed_out(&node_list[i], onion_c->mono_time)) { |
1958 | 52.1k | continue; |
1959 | 52.1k | } |
1960 | | |
1961 | 37.6k | ++count; |
1962 | | |
1963 | | /* Don't announce ourselves the first time this is run to new peers */ |
1964 | 37.6k | if (node_list[i].last_pinged == 0) { |
1965 | 4.04k | node_list[i].last_pinged = 1; |
1966 | 4.04k | continue; |
1967 | 4.04k | } |
1968 | | |
1969 | 33.6k | if (node_list[i].pings_since_last_response >= ONION_NODE_MAX_PINGS) { |
1970 | 209 | continue; |
1971 | 209 | } |
1972 | | |
1973 | 33.4k | unsigned int interval = ANNOUNCE_INTERVAL_NOT_ANNOUNCED; |
1974 | | |
1975 | 33.4k | if (node_list[i].is_stored != 0 |
1976 | 33.4k | && path_exists(onion_c->mono_time, &onion_c->onion_paths_self, node_list[i].path_used)) { |
1977 | 27.4k | interval = ANNOUNCE_INTERVAL_ANNOUNCED; |
1978 | | |
1979 | 27.4k | const uint32_t pathnum = node_list[i].path_used % NUMBER_ONION_PATHS; |
1980 | | |
1981 | | /* If a node/path is considered "stable", it can be pinged less aggressively. */ |
1982 | 27.4k | if (path_is_stable(onion_c->mono_time, &onion_c->onion_paths_self, pathnum, &node_list[i])) { |
1983 | 0 | interval = ANNOUNCE_INTERVAL_STABLE; |
1984 | 0 | } |
1985 | 27.4k | } |
1986 | | |
1987 | 33.4k | if (mono_time_is_timeout(onion_c->mono_time, node_list[i].last_pinged, interval) |
1988 | 33.4k | || mono_time_is_timeout(onion_c->mono_time, onion_c->last_announce, ONION_NODE_PING_INTERVAL)) { |
1989 | 5.02k | uint32_t path_to_use = node_list[i].path_used; |
1990 | | |
1991 | 5.02k | if (node_list[i].pings_since_last_response == ONION_NODE_MAX_PINGS - 1 |
1992 | 5.02k | && mono_time_is_timeout(onion_c->mono_time, node_list[i].added_time, TIME_TO_STABLE)) { |
1993 | | /* Last chance for a long-lived node - try a random path */ |
1994 | 0 | path_to_use = -1; |
1995 | 0 | } |
1996 | | |
1997 | 5.02k | if (client_send_announce_request(onion_c, 0, &node_list[i].ip_port, node_list[i].public_key, |
1998 | 5.02k | node_list[i].ping_id, path_to_use) == 0) { |
1999 | 5.02k | node_list[i].last_pinged = mono_time_get(onion_c->mono_time); |
2000 | 5.02k | ++node_list[i].pings_since_last_response; |
2001 | 5.02k | onion_c->last_announce = mono_time_get(onion_c->mono_time); |
2002 | 5.02k | } |
2003 | 5.02k | } |
2004 | 33.4k | } |
2005 | | |
2006 | 7.48k | if (count == MAX_ONION_CLIENTS_ANNOUNCE) { |
2007 | 2.01k | onion_c->last_populated = mono_time_get(onion_c->mono_time); |
2008 | 2.01k | return; |
2009 | 2.01k | } |
2010 | | |
2011 | | // check if list needs to be re-populated |
2012 | 5.47k | if (count <= MAX_ONION_CLIENTS_ANNOUNCE / 2 |
2013 | 5.47k | || mono_time_is_timeout(onion_c->mono_time, onion_c->last_populated, ANNOUNCE_POPULATE_TIMEOUT)) { |
2014 | 5.47k | uint16_t num_nodes; |
2015 | 5.47k | const Node_format *path_nodes; |
2016 | | |
2017 | 5.47k | if (onion_c->path_nodes_index == 0) { |
2018 | 538 | num_nodes = min_u16(onion_c->path_nodes_index_bs, MAX_PATH_NODES); |
2019 | 538 | path_nodes = onion_c->path_nodes_bs; |
2020 | 4.93k | } else { |
2021 | 4.93k | num_nodes = min_u16(onion_c->path_nodes_index, MAX_PATH_NODES); |
2022 | 4.93k | path_nodes = onion_c->path_nodes; |
2023 | 4.93k | } |
2024 | | |
2025 | 5.47k | if (num_nodes == 0) { |
2026 | 132 | return; |
2027 | 132 | } |
2028 | | |
2029 | | // Don't send announces to the same node twice. If we don't have many nodes, |
2030 | | // the random selection below may have overlaps. This ensures that we deduplicate |
2031 | | // nodes before sending packets to save some bandwidth. |
2032 | | // |
2033 | | // TODO(iphydf): Figure out why on esp32, this is necessary for the onion |
2034 | | // connection to succeed. This is an optimisation and shouldn't be necessary. |
2035 | 5.34k | const uint8_t *targets[MAX_ONION_CLIENTS_ANNOUNCE / 2]; |
2036 | 5.34k | unsigned int targets_count = 0; |
2037 | | |
2038 | 37.3k | for (unsigned int i = 0; i < MAX_ONION_CLIENTS_ANNOUNCE / 2; ++i) { |
2039 | 32.0k | const uint32_t num = random_range_u32(onion_c->rng, num_nodes); |
2040 | 32.0k | const Node_format *target = &path_nodes[num]; |
2041 | | |
2042 | 32.0k | if (!key_list_contains(targets, targets_count, target->public_key)) { |
2043 | 13.5k | client_send_announce_request(onion_c, 0, &target->ip_port, target->public_key, nullptr, -1); |
2044 | | |
2045 | 13.5k | targets[targets_count] = target->public_key; |
2046 | 13.5k | ++targets_count; |
2047 | 13.5k | assert(targets_count <= MAX_ONION_CLIENTS_ANNOUNCE / 2); |
2048 | 18.5k | } else { |
2049 | 18.5k | Ip_Ntoa ip_str; |
2050 | 18.5k | LOGGER_TRACE(onion_c->logger, "not sending repeated announce request to %s:%d", |
2051 | 18.5k | net_ip_ntoa(&target->ip_port.ip, &ip_str), net_ntohs(target->ip_port.port)); |
2052 | 18.5k | } |
2053 | 32.0k | } |
2054 | 5.34k | } |
2055 | 5.47k | } |
2056 | | |
2057 | | /** |
2058 | | * @retval false if we are not connected to the network. |
2059 | | * @retval true if we are. |
2060 | | */ |
2061 | | static bool onion_isconnected(Onion_Client *_Nonnull onion_c) |
2062 | 8.35k | { |
2063 | 8.35k | unsigned int live = 0; |
2064 | 8.35k | unsigned int announced = 0; |
2065 | | |
2066 | 8.35k | if (mono_time_is_timeout(onion_c->mono_time, onion_c->last_packet_recv, ONION_OFFLINE_TIMEOUT)) { |
2067 | 1.99k | LOGGER_TRACE(onion_c->logger, "onion is NOT connected: last packet received at %llu (timeout=%u)", |
2068 | 1.99k | (unsigned long long)onion_c->last_packet_recv, (unsigned int)ONION_OFFLINE_TIMEOUT); |
2069 | 1.99k | onion_c->last_populated = 0; |
2070 | 1.99k | return false; |
2071 | 1.99k | } |
2072 | | |
2073 | 6.36k | if (onion_c->path_nodes_index == 0) { |
2074 | 0 | LOGGER_TRACE(onion_c->logger, "onion is NOT connected: no path nodes available"); |
2075 | 0 | onion_c->last_populated = 0; |
2076 | 0 | return false; |
2077 | 0 | } |
2078 | | |
2079 | 82.7k | for (unsigned int i = 0; i < MAX_ONION_CLIENTS_ANNOUNCE; ++i) { |
2080 | 76.3k | if (!onion_node_timed_out(&onion_c->clients_announce_list[i], onion_c->mono_time)) { |
2081 | 37.6k | ++live; |
2082 | | |
2083 | 37.6k | if (onion_c->clients_announce_list[i].is_stored != 0) { |
2084 | 27.7k | ++announced; |
2085 | 27.7k | } |
2086 | 37.6k | } |
2087 | 76.3k | } |
2088 | | |
2089 | 6.36k | unsigned int pnodes = onion_c->path_nodes_index; |
2090 | | |
2091 | 6.36k | if (pnodes > MAX_ONION_CLIENTS_ANNOUNCE) { |
2092 | 2.23k | pnodes = MAX_ONION_CLIENTS_ANNOUNCE; |
2093 | 2.23k | } |
2094 | | |
2095 | | /* Consider ourselves online if we are announced to half or more nodes |
2096 | | * we are connected to */ |
2097 | 6.36k | if (live != 0 && announced != 0) { |
2098 | 5.31k | if ((live / 2) <= announced && (pnodes / 2) <= live) { |
2099 | 5.12k | LOGGER_TRACE(onion_c->logger, "onion is connected: %u live nodes, %u announced, %d path nodes", |
2100 | 5.12k | live, announced, (int)pnodes); |
2101 | 5.12k | return true; |
2102 | 5.12k | } |
2103 | 5.31k | } |
2104 | | |
2105 | 1.24k | onion_c->last_populated = 0; |
2106 | | |
2107 | 1.24k | LOGGER_TRACE(onion_c->logger, "onion is NOT connected: %u live nodes, %u announced, %d path nodes", |
2108 | 1.24k | live, announced, (int)pnodes); |
2109 | 1.24k | return false; |
2110 | 6.36k | } |
2111 | | |
2112 | | static void reset_friend_run_counts(Onion_Client *_Nonnull onion_c) |
2113 | 440 | { |
2114 | 703 | for (uint32_t i = 0; i < onion_c->num_friends; ++i) { |
2115 | 263 | Onion_Friend *o_friend = &onion_c->friends_list[i]; |
2116 | | |
2117 | 263 | if (o_friend->is_valid) { |
2118 | 263 | o_friend->run_count = 0; |
2119 | 263 | } |
2120 | 263 | } |
2121 | 440 | } |
2122 | | |
2123 | 280k | #define ONION_CONNECTION_SECONDS 3 |
2124 | 5.11k | #define ONION_CONNECTED_TIMEOUT 10 |
2125 | | |
2126 | | Onion_Connection_Status onion_connection_status(const Onion_Client *onion_c) |
2127 | 259k | { |
2128 | 259k | if (onion_c->onion_connected >= ONION_CONNECTION_SECONDS) { |
2129 | 144k | if (onion_c->udp_connected) { |
2130 | 1.51k | return ONION_CONNECTION_STATUS_UDP; |
2131 | 1.51k | } |
2132 | | |
2133 | 142k | return ONION_CONNECTION_STATUS_TCP; |
2134 | 144k | } |
2135 | | |
2136 | 114k | return ONION_CONNECTION_STATUS_NONE; |
2137 | 259k | } |
2138 | | |
2139 | | void do_onion_client(Onion_Client *onion_c) |
2140 | 132k | { |
2141 | 132k | if (onion_c->last_run == mono_time_get(onion_c->mono_time)) { |
2142 | 123k | return; |
2143 | 123k | } |
2144 | | |
2145 | 8.30k | if (mono_time_is_timeout(onion_c->mono_time, onion_c->first_run, ONION_CONNECTION_SECONDS)) { |
2146 | 7.48k | populate_path_nodes(onion_c); |
2147 | 7.48k | do_announce(onion_c); |
2148 | 7.48k | } |
2149 | | |
2150 | 8.30k | if (onion_isconnected(onion_c)) { |
2151 | 5.11k | if (mono_time_is_timeout(onion_c->mono_time, onion_c->last_time_connected, ONION_CONNECTED_TIMEOUT)) { |
2152 | 440 | reset_friend_run_counts(onion_c); |
2153 | 440 | } |
2154 | | |
2155 | 5.11k | onion_c->last_time_connected = mono_time_get(onion_c->mono_time); |
2156 | | |
2157 | 5.11k | if (onion_c->onion_connected < ONION_CONNECTION_SECONDS * 2) { |
2158 | 2.26k | ++onion_c->onion_connected; |
2159 | 2.26k | } |
2160 | 5.11k | } else { |
2161 | 3.18k | if (onion_c->onion_connected != 0) { |
2162 | 1 | --onion_c->onion_connected; |
2163 | 1 | } |
2164 | 3.18k | } |
2165 | | |
2166 | 8.30k | onion_c->udp_connected = dht_non_lan_connected(onion_c->dht); |
2167 | | |
2168 | 8.30k | if (mono_time_is_timeout(onion_c->mono_time, onion_c->first_run, ONION_CONNECTION_SECONDS * 2)) { |
2169 | 6.25k | set_tcp_onion_status(nc_get_tcp_c(onion_c->c), !onion_c->udp_connected); |
2170 | 6.25k | } |
2171 | | |
2172 | 8.30k | if (onion_connection_status(onion_c) != ONION_CONNECTION_STATUS_NONE) { |
2173 | 11.7k | for (uint32_t i = 0; i < onion_c->num_friends; ++i) { |
2174 | 7.46k | do_friend(onion_c, i); |
2175 | 7.46k | } |
2176 | 4.24k | } |
2177 | | |
2178 | 8.30k | if (onion_c->last_run == 0) { |
2179 | 460 | onion_c->first_run = mono_time_get(onion_c->mono_time); |
2180 | 460 | } |
2181 | | |
2182 | 8.30k | onion_c->last_run = mono_time_get(onion_c->mono_time); |
2183 | 8.30k | } |
2184 | | |
2185 | | Onion_Client *new_onion_client(const Logger *logger, const Memory *mem, const Random *rng, const Mono_Time *mono_time, Net_Crypto *c, |
2186 | | DHT *dht, Networking_Core *net) |
2187 | 1.93k | { |
2188 | 1.93k | if (c == nullptr) { |
2189 | 0 | return nullptr; |
2190 | 0 | } |
2191 | | |
2192 | 1.93k | Onion_Client *onion_c = (Onion_Client *)mem_alloc(mem, sizeof(Onion_Client)); |
2193 | | |
2194 | 1.93k | if (onion_c == nullptr) { |
2195 | 3 | return nullptr; |
2196 | 3 | } |
2197 | | |
2198 | 1.92k | Ping_Array *const temp_ping_array = ping_array_new(mem, ANNOUNCE_ARRAY_SIZE, ANNOUNCE_TIMEOUT); |
2199 | | |
2200 | 1.92k | if (temp_ping_array == nullptr) { |
2201 | 3 | mem_delete(mem, onion_c); |
2202 | 3 | return nullptr; |
2203 | 3 | } |
2204 | 1.92k | onion_c->announce_ping_array = temp_ping_array; |
2205 | | |
2206 | 1.92k | onion_c->mono_time = mono_time; |
2207 | 1.92k | onion_c->logger = logger; |
2208 | 1.92k | onion_c->rng = rng; |
2209 | 1.92k | onion_c->mem = mem; |
2210 | 1.92k | onion_c->dht = dht; |
2211 | 1.92k | onion_c->net = net; |
2212 | 1.92k | onion_c->c = c; |
2213 | 1.92k | onion_c->friends_list_capacity = 0; |
2214 | 1.92k | bs_list_init(&onion_c->friends_lookup, mem, CRYPTO_PUBLIC_KEY_SIZE, 0, memcmp); |
2215 | | |
2216 | 1.92k | new_symmetric_key(rng, onion_c->secret_symmetric_key); |
2217 | 1.92k | crypto_new_keypair(rng, onion_c->temp_public_key, onion_c->temp_secret_key); |
2218 | 1.92k | networking_registerhandler(onion_c->net, NET_PACKET_ANNOUNCE_RESPONSE, &handle_announce_response, onion_c); |
2219 | 1.92k | networking_registerhandler(onion_c->net, NET_PACKET_ANNOUNCE_RESPONSE_OLD, &handle_announce_response_old, onion_c); |
2220 | 1.92k | networking_registerhandler(onion_c->net, NET_PACKET_ONION_DATA_RESPONSE, &handle_data_response, onion_c); |
2221 | 1.92k | oniondata_registerhandler(onion_c, ONION_DATA_DHTPK, &handle_dhtpk_announce, onion_c); |
2222 | 1.92k | cryptopacket_registerhandler(onion_c->dht, CRYPTO_PACKET_DHTPK, &handle_dht_dhtpk, onion_c); |
2223 | 1.92k | set_onion_packet_tcp_connection_callback(nc_get_tcp_c(onion_c->c), &handle_tcp_onion, onion_c); |
2224 | | |
2225 | 1.92k | return onion_c; |
2226 | 1.92k | } |
2227 | | |
2228 | | void kill_onion_client(Onion_Client *onion_c) |
2229 | 1.93k | { |
2230 | 1.93k | if (onion_c == nullptr) { |
2231 | 6 | return; |
2232 | 6 | } |
2233 | | |
2234 | 1.92k | const Memory *mem = onion_c->mem; |
2235 | | |
2236 | 1.92k | ping_array_kill(onion_c->announce_ping_array); |
2237 | 1.92k | realloc_onion_friends(onion_c, 0); |
2238 | 1.92k | bs_list_free(&onion_c->friends_lookup); |
2239 | 1.92k | networking_registerhandler(onion_c->net, NET_PACKET_ANNOUNCE_RESPONSE, nullptr, nullptr); |
2240 | 1.92k | networking_registerhandler(onion_c->net, NET_PACKET_ANNOUNCE_RESPONSE_OLD, nullptr, nullptr); |
2241 | 1.92k | networking_registerhandler(onion_c->net, NET_PACKET_ONION_DATA_RESPONSE, nullptr, nullptr); |
2242 | 1.92k | oniondata_registerhandler(onion_c, ONION_DATA_DHTPK, nullptr, nullptr); |
2243 | 1.92k | cryptopacket_registerhandler(onion_c->dht, CRYPTO_PACKET_DHTPK, nullptr, nullptr); |
2244 | 1.92k | set_onion_packet_tcp_connection_callback(nc_get_tcp_c(onion_c->c), nullptr, nullptr); |
2245 | 1.92k | crypto_memzero(onion_c, sizeof(Onion_Client)); |
2246 | 1.92k | mem_delete(mem, onion_c); |
2247 | 1.92k | } |
2248 | | |
2249 | | uint64_t onion_testonly_get_last_packet_recv(const Onion_Client *onion_c) |
2250 | 4 | { |
2251 | 4 | return onion_c->last_packet_recv; |
2252 | 4 | } |
2253 | | |
2254 | | void onion_testonly_get_temp_public_key(const Onion_Client *onion_c, uint8_t *public_key) |
2255 | 0 | { |
2256 | 0 | memcpy(public_key, onion_c->temp_public_key, CRYPTO_PUBLIC_KEY_SIZE); |
2257 | 0 | } |
2258 | | |
2259 | | void onion_testonly_get_secret_symmetric_key(const Onion_Client *onion_c, uint8_t *secret_key) |
2260 | 0 | { |
2261 | 0 | memcpy(secret_key, onion_c->secret_symmetric_key, CRYPTO_SYMMETRIC_KEY_SIZE); |
2262 | 0 | } |