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