/work/toxcore/forwarding.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* SPDX-License-Identifier: GPL-3.0-or-later |
2 | | * Copyright © 2019-2026 The TokTok team. |
3 | | */ |
4 | | |
5 | | #include "forwarding.h" |
6 | | |
7 | | #include <assert.h> |
8 | | #include <string.h> |
9 | | |
10 | | #include "DHT.h" |
11 | | #include "attributes.h" |
12 | | #include "ccompat.h" |
13 | | #include "crypto_core.h" |
14 | | #include "logger.h" |
15 | | #include "mem.h" |
16 | | #include "mono_time.h" |
17 | | #include "network.h" |
18 | | #include "timed_auth.h" |
19 | | |
20 | | struct Forwarding { |
21 | | const Logger *_Nonnull log; |
22 | | const Memory *_Nonnull mem; |
23 | | const Random *_Nonnull rng; |
24 | | DHT *_Nonnull dht; |
25 | | const Mono_Time *_Nonnull mono_time; |
26 | | Networking_Core *_Nonnull net; |
27 | | |
28 | | uint8_t hmac_key[CRYPTO_HMAC_KEY_SIZE]; |
29 | | |
30 | | forward_reply_cb *_Nullable forward_reply_callback; |
31 | | void *_Nullable forward_reply_callback_object; |
32 | | |
33 | | forwarded_request_cb *_Nullable forwarded_request_callback; |
34 | | void *_Nullable forwarded_request_callback_object; |
35 | | |
36 | | forwarded_response_cb *_Nullable forwarded_response_callback; |
37 | | void *_Nullable forwarded_response_callback_object; |
38 | | }; |
39 | | |
40 | 109 | #define SENDBACK_TIMEOUT 3600 |
41 | | |
42 | | bool send_forward_request(const Networking_Core *net, const IP_Port *forwarder, |
43 | | const uint8_t *chain_keys, uint16_t chain_length, |
44 | | const uint8_t *data, uint16_t data_length) |
45 | 33 | { |
46 | 33 | if (chain_length == 0 || chain_length > MAX_FORWARD_CHAIN_LENGTH |
47 | 33 | || data_length > MAX_FORWARD_DATA_SIZE) { |
48 | 3 | return false; |
49 | 3 | } |
50 | | |
51 | 30 | const uint16_t len = forward_chain_packet_size(chain_length, data_length); |
52 | 30 | VLA(uint8_t, packet, len); |
53 | | |
54 | 30 | return create_forward_chain_packet(chain_keys, chain_length, data, data_length, packet) |
55 | 30 | && sendpacket(net, forwarder, packet, len) == len; |
56 | 33 | } |
57 | | |
58 | | uint16_t forward_chain_packet_size(uint16_t chain_length, uint16_t data_length) |
59 | 34 | { |
60 | 34 | return chain_length * (1 + CRYPTO_PUBLIC_KEY_SIZE) + data_length; |
61 | 34 | } |
62 | | |
63 | | bool create_forward_chain_packet(const uint8_t *chain_keys, uint16_t chain_length, |
64 | | const uint8_t *data, uint16_t data_length, |
65 | | uint8_t *packet) |
66 | 34 | { |
67 | 34 | if (chain_length == 0 || chain_length > MAX_FORWARD_CHAIN_LENGTH |
68 | 34 | || data_length > MAX_FORWARD_DATA_SIZE) { |
69 | 0 | return false; |
70 | 0 | } |
71 | | |
72 | 34 | uint16_t offset = 0; |
73 | | |
74 | 109 | for (uint16_t j = 0; j < chain_length; ++j) { |
75 | 75 | packet[offset] = NET_PACKET_FORWARD_REQUEST; |
76 | 75 | ++offset; |
77 | 75 | memcpy(packet + offset, chain_keys + j * CRYPTO_PUBLIC_KEY_SIZE, CRYPTO_PUBLIC_KEY_SIZE); |
78 | 75 | offset += CRYPTO_PUBLIC_KEY_SIZE; |
79 | 75 | } |
80 | | |
81 | 34 | memcpy(packet + offset, data, data_length); |
82 | 34 | return true; |
83 | 34 | } |
84 | | |
85 | | static uint16_t forwarding_packet_length(uint16_t sendback_data_len, uint16_t data_length) |
86 | 70 | { |
87 | 70 | const uint16_t sendback_len = sendback_data_len == 0 ? 0 : TIMED_AUTH_SIZE + sendback_data_len; |
88 | 70 | return 1 + 1 + sendback_len + data_length; |
89 | 70 | } |
90 | | |
91 | | static bool create_forwarding_packet(const Forwarding *_Nonnull forwarding, |
92 | | const uint8_t *_Nullable sendback_data, uint16_t sendback_data_len, |
93 | | const uint8_t *_Nonnull data, uint16_t length, |
94 | | uint8_t *_Nonnull packet) |
95 | 70 | { |
96 | 70 | packet[0] = NET_PACKET_FORWARDING; |
97 | 70 | if (sendback_data_len == 0) { |
98 | 15 | packet[1] = 0; |
99 | 15 | memcpy(packet + 1 + 1, data, length); |
100 | 55 | } else { |
101 | 55 | const uint16_t sendback_len = TIMED_AUTH_SIZE + sendback_data_len; |
102 | | |
103 | 55 | if (sendback_len > MAX_SENDBACK_SIZE) { |
104 | 0 | return false; |
105 | 0 | } |
106 | | |
107 | 55 | packet[1] = sendback_len; |
108 | 55 | generate_timed_auth(forwarding->mono_time, SENDBACK_TIMEOUT, forwarding->hmac_key, sendback_data, |
109 | 55 | sendback_data_len, packet + 1 + 1); |
110 | | |
111 | 55 | if (sendback_data_len != 0) { |
112 | 55 | assert(sendback_data != nullptr); |
113 | 55 | memcpy(packet + 1 + 1 + TIMED_AUTH_SIZE, sendback_data, sendback_data_len); |
114 | 55 | } |
115 | | |
116 | 55 | memcpy(packet + 1 + 1 + sendback_len, data, length); |
117 | 55 | } |
118 | | |
119 | 70 | return true; |
120 | 70 | } |
121 | | |
122 | | bool send_forwarding(const Forwarding *forwarding, const IP_Port *dest, |
123 | | const uint8_t *sendback_data, uint16_t sendback_data_len, |
124 | | const uint8_t *data, uint16_t length) |
125 | 20 | { |
126 | 20 | if (length > MAX_FORWARD_DATA_SIZE) { |
127 | 0 | return false; |
128 | 0 | } |
129 | | |
130 | 20 | const uint16_t len = forwarding_packet_length(sendback_data_len, length); |
131 | 20 | VLA(uint8_t, packet, len); |
132 | 20 | create_forwarding_packet(forwarding, sendback_data, sendback_data_len, data, length, packet); |
133 | 20 | return sendpacket(forwarding->net, dest, packet, len) == len; |
134 | 20 | } |
135 | | |
136 | 50 | #define FORWARD_REQUEST_MIN_PACKET_SIZE (1 + CRYPTO_PUBLIC_KEY_SIZE) |
137 | | |
138 | | static bool handle_forward_request_dht(const Forwarding *_Nonnull forwarding, |
139 | | const uint8_t *_Nullable sendback_data, uint16_t sendback_data_len, |
140 | | const uint8_t *_Nullable packet, uint16_t length) |
141 | 50 | { |
142 | 50 | if (length < FORWARD_REQUEST_MIN_PACKET_SIZE) { |
143 | 0 | return false; |
144 | 0 | } |
145 | | |
146 | 50 | const uint8_t *const public_key = packet + 1; |
147 | 50 | const uint8_t *const forward_data = packet + (1 + CRYPTO_PUBLIC_KEY_SIZE); |
148 | 50 | const uint16_t forward_data_len = length - (1 + CRYPTO_PUBLIC_KEY_SIZE); |
149 | | |
150 | 50 | if (TIMED_AUTH_SIZE + sendback_data_len > MAX_SENDBACK_SIZE || |
151 | 50 | forward_data_len > MAX_FORWARD_DATA_SIZE) { |
152 | 0 | return false; |
153 | 0 | } |
154 | | |
155 | 50 | const uint16_t len = forwarding_packet_length(sendback_data_len, forward_data_len); |
156 | 50 | VLA(uint8_t, forwarding_packet, len); |
157 | | |
158 | 50 | create_forwarding_packet(forwarding, sendback_data, sendback_data_len, forward_data, forward_data_len, |
159 | 50 | forwarding_packet); |
160 | | |
161 | 50 | return route_packet(forwarding->dht, public_key, forwarding_packet, len) == len; |
162 | 50 | } |
163 | | |
164 | | static int handle_forward_request(void *_Nonnull object, const IP_Port *_Nonnull source, const uint8_t *_Nullable packet, uint16_t length, |
165 | | void *_Nullable userdata) |
166 | 16 | { |
167 | 16 | const Forwarding *forwarding = (const Forwarding *)object; |
168 | 16 | uint8_t sendback_data[1 + MAX_PACKED_IPPORT_SIZE]; |
169 | 16 | sendback_data[0] = SENDBACK_IPPORT; |
170 | | |
171 | 16 | const int ipport_length = pack_ip_port(forwarding->log, sendback_data + 1, MAX_PACKED_IPPORT_SIZE, source); |
172 | | |
173 | 16 | if (ipport_length == -1) { |
174 | 0 | return 1; |
175 | 0 | } |
176 | | |
177 | 16 | return handle_forward_request_dht(forwarding, sendback_data, 1 + ipport_length, packet, length) ? 0 : 1; |
178 | 16 | } |
179 | | |
180 | 54 | #define MIN_NONEMPTY_SENDBACK_SIZE TIMED_AUTH_SIZE |
181 | 54 | #define FORWARD_REPLY_MIN_PACKET_SIZE (1 + 1 + MIN_NONEMPTY_SENDBACK_SIZE) |
182 | | |
183 | | static int handle_forward_reply(void *_Nonnull object, const IP_Port *_Nonnull source, const uint8_t *_Nullable packet, uint16_t length, |
184 | | void *_Nullable userdata) |
185 | 54 | { |
186 | 54 | const Forwarding *forwarding = (const Forwarding *)object; |
187 | 54 | if (length < FORWARD_REPLY_MIN_PACKET_SIZE) { |
188 | 0 | return 1; |
189 | 0 | } |
190 | | |
191 | 54 | const uint8_t sendback_len = packet[1]; |
192 | 54 | const uint8_t *const sendback_auth = packet + 1 + 1; |
193 | 54 | const uint8_t *const sendback_data = sendback_auth + TIMED_AUTH_SIZE; |
194 | | |
195 | 54 | if (sendback_len > MAX_SENDBACK_SIZE) { |
196 | | /* value 0xff is reserved for possible future expansion */ |
197 | 0 | return 1; |
198 | 0 | } |
199 | | |
200 | 54 | if (sendback_len < TIMED_AUTH_SIZE + 1) { |
201 | 0 | return 1; |
202 | 0 | } |
203 | | |
204 | 54 | const uint16_t sendback_data_len = sendback_len - TIMED_AUTH_SIZE; |
205 | | |
206 | 54 | if (length < 1 + 1 + sendback_len) { |
207 | 0 | return 1; |
208 | 0 | } |
209 | | |
210 | 54 | const uint8_t *const to_forward = packet + (1 + 1 + sendback_len); |
211 | 54 | const uint16_t to_forward_len = length - (1 + 1 + sendback_len); |
212 | | |
213 | 54 | if (!check_timed_auth(forwarding->mono_time, SENDBACK_TIMEOUT, forwarding->hmac_key, sendback_data, sendback_data_len, |
214 | 54 | sendback_auth)) { |
215 | 0 | return 1; |
216 | 0 | } |
217 | | |
218 | 54 | if (sendback_data[0] == SENDBACK_IPPORT) { |
219 | 15 | IP_Port dest; |
220 | | |
221 | 15 | if (unpack_ip_port(&dest, sendback_data + 1, sendback_data_len - 1, false) |
222 | 15 | != sendback_data_len - 1) { |
223 | 0 | return 1; |
224 | 0 | } |
225 | | |
226 | 15 | return send_forwarding(forwarding, &dest, nullptr, 0, to_forward, to_forward_len) ? 0 : 1; |
227 | 15 | } |
228 | | |
229 | 39 | if (sendback_data[0] == SENDBACK_FORWARD) { |
230 | 34 | IP_Port forwarder; |
231 | 34 | const int ipport_length = unpack_ip_port(&forwarder, sendback_data + 1, sendback_data_len - 1, false); |
232 | | |
233 | 34 | if (ipport_length == -1) { |
234 | 0 | return 1; |
235 | 0 | } |
236 | | |
237 | 34 | const uint8_t *const forward_sendback = sendback_data + (1 + ipport_length); |
238 | 34 | const uint16_t forward_sendback_len = sendback_data_len - (1 + ipport_length); |
239 | | |
240 | 34 | return forward_reply(forwarding->net, &forwarder, forward_sendback, forward_sendback_len, to_forward, |
241 | 34 | to_forward_len) ? 0 : 1; |
242 | 34 | } |
243 | | |
244 | 5 | if (forwarding->forward_reply_callback == nullptr) { |
245 | 0 | return 1; |
246 | 0 | } |
247 | | |
248 | 5 | return forwarding->forward_reply_callback(forwarding->forward_reply_callback_object, |
249 | 5 | sendback_data, sendback_data_len, |
250 | 5 | to_forward, to_forward_len) ? 0 : 1; |
251 | 5 | } |
252 | | |
253 | 69 | #define FORWARDING_MIN_PACKET_SIZE (1 + 1) |
254 | | |
255 | | static int handle_forwarding(void *_Nonnull object, const IP_Port *_Nonnull source, const uint8_t *_Nullable packet, uint16_t length, |
256 | | void *_Nullable userdata) |
257 | 69 | { |
258 | 69 | const Forwarding *forwarding = (const Forwarding *)object; |
259 | 69 | if (length < FORWARDING_MIN_PACKET_SIZE) { |
260 | 0 | return 1; |
261 | 0 | } |
262 | | |
263 | 69 | const uint8_t sendback_len = packet[1]; |
264 | | |
265 | 69 | if (length < 1 + 1 + sendback_len) { |
266 | 0 | return 1; |
267 | 0 | } |
268 | | |
269 | 69 | const uint8_t *const sendback = packet + 1 + 1; |
270 | | |
271 | 69 | const uint8_t *const forwarded = sendback + sendback_len; |
272 | 69 | const uint16_t forwarded_len = length - (1 + 1 + sendback_len); |
273 | | |
274 | 69 | if (forwarded_len >= 1 && forwarded[0] == NET_PACKET_FORWARD_REQUEST) { |
275 | 34 | VLA(uint8_t, sendback_data, 1 + MAX_PACKED_IPPORT_SIZE + sendback_len); |
276 | 34 | sendback_data[0] = SENDBACK_FORWARD; |
277 | | |
278 | 34 | const int ipport_length = pack_ip_port(forwarding->log, sendback_data + 1, MAX_PACKED_IPPORT_SIZE, source); |
279 | | |
280 | 34 | if (ipport_length == -1) { |
281 | 0 | return 1; |
282 | 0 | } |
283 | | |
284 | 34 | memcpy(sendback_data + 1 + ipport_length, sendback, sendback_len); |
285 | | |
286 | 34 | return handle_forward_request_dht(forwarding, sendback_data, 1 + ipport_length + sendback_len, forwarded, |
287 | 34 | forwarded_len) ? 0 : 1; |
288 | 34 | } |
289 | | |
290 | 35 | if (sendback_len > 0) { |
291 | 20 | if (forwarding->forwarded_request_callback == nullptr) { |
292 | 0 | return 1; |
293 | 0 | } |
294 | | |
295 | 20 | forwarding->forwarded_request_callback(forwarding->forwarded_request_callback_object, |
296 | 20 | source, sendback, sendback_len, |
297 | 20 | forwarded, forwarded_len, userdata); |
298 | 20 | return 0; |
299 | 20 | } else { |
300 | 15 | if (forwarding->forwarded_response_callback == nullptr) { |
301 | 0 | return 1; |
302 | 0 | } |
303 | | |
304 | 15 | forwarding->forwarded_response_callback(forwarding->forwarded_response_callback_object, |
305 | 15 | forwarded, forwarded_len, userdata); |
306 | 15 | return 0; |
307 | 15 | } |
308 | 35 | } |
309 | | |
310 | | bool forward_reply(const Networking_Core *net, const IP_Port *forwarder, |
311 | | const uint8_t *sendback, uint16_t sendback_length, |
312 | | const uint8_t *data, uint16_t length) |
313 | 67 | { |
314 | 67 | if (sendback_length > MAX_SENDBACK_SIZE || |
315 | 67 | length > MAX_FORWARD_DATA_SIZE) { |
316 | 2 | return false; |
317 | 2 | } |
318 | | |
319 | 65 | const uint16_t len = 1 + 1 + sendback_length + length; |
320 | 65 | VLA(uint8_t, packet, len); |
321 | 65 | packet[0] = NET_PACKET_FORWARD_REPLY; |
322 | 65 | packet[1] = (uint8_t) sendback_length; |
323 | 65 | memcpy(packet + 1 + 1, sendback, sendback_length); |
324 | 65 | memcpy(packet + 1 + 1 + sendback_length, data, length); |
325 | 65 | return sendpacket(net, forwarder, packet, len) == len; |
326 | 67 | } |
327 | | |
328 | | void set_callback_forwarded_request(Forwarding *forwarding, forwarded_request_cb *function, void *object) |
329 | 3.70k | { |
330 | 3.70k | forwarding->forwarded_request_callback = function; |
331 | 3.70k | forwarding->forwarded_request_callback_object = object; |
332 | 3.70k | } |
333 | | |
334 | | void set_callback_forwarded_response(Forwarding *forwarding, forwarded_response_cb *function, void *object) |
335 | 20 | { |
336 | 20 | forwarding->forwarded_response_callback = function; |
337 | 20 | forwarding->forwarded_response_callback_object = object; |
338 | 20 | } |
339 | | |
340 | | void set_callback_forward_reply(Forwarding *forwarding, forward_reply_cb *function, void *object) |
341 | 8 | { |
342 | 8 | forwarding->forward_reply_callback = function; |
343 | 8 | forwarding->forward_reply_callback_object = object; |
344 | 8 | } |
345 | | |
346 | | Forwarding *new_forwarding(const Logger *log, const Memory *mem, const Random *rng, |
347 | | const Mono_Time *mono_time, DHT *dht, Networking_Core *net) |
348 | 1.86k | { |
349 | 1.86k | if (log == nullptr || mono_time == nullptr || dht == nullptr) { |
350 | 0 | return nullptr; |
351 | 0 | } |
352 | | |
353 | 1.86k | Forwarding *forwarding = (Forwarding *)mem_alloc(mem, sizeof(Forwarding)); |
354 | | |
355 | 1.86k | if (forwarding == nullptr) { |
356 | 18 | return nullptr; |
357 | 18 | } |
358 | | |
359 | 1.84k | forwarding->log = log; |
360 | 1.84k | forwarding->mem = mem; |
361 | 1.84k | forwarding->rng = rng; |
362 | 1.84k | forwarding->mono_time = mono_time; |
363 | 1.84k | forwarding->dht = dht; |
364 | 1.84k | forwarding->net = net; |
365 | | |
366 | 1.84k | networking_registerhandler(forwarding->net, NET_PACKET_FORWARD_REQUEST, &handle_forward_request, forwarding); |
367 | 1.84k | networking_registerhandler(forwarding->net, NET_PACKET_FORWARD_REPLY, &handle_forward_reply, forwarding); |
368 | 1.84k | networking_registerhandler(forwarding->net, NET_PACKET_FORWARDING, &handle_forwarding, forwarding); |
369 | | |
370 | 1.84k | new_hmac_key(forwarding->rng, forwarding->hmac_key); |
371 | | |
372 | 1.84k | return forwarding; |
373 | 1.86k | } |
374 | | |
375 | | void kill_forwarding(Forwarding *forwarding) |
376 | 1.86k | { |
377 | 1.86k | if (forwarding == nullptr) { |
378 | 18 | return; |
379 | 18 | } |
380 | | |
381 | 1.84k | networking_registerhandler(forwarding->net, NET_PACKET_FORWARD_REQUEST, nullptr, nullptr); |
382 | 1.84k | networking_registerhandler(forwarding->net, NET_PACKET_FORWARD_REPLY, nullptr, nullptr); |
383 | 1.84k | networking_registerhandler(forwarding->net, NET_PACKET_FORWARDING, nullptr, nullptr); |
384 | | |
385 | 1.84k | crypto_memzero(forwarding->hmac_key, CRYPTO_HMAC_KEY_SIZE); |
386 | | |
387 | 1.84k | mem_delete(forwarding->mem, forwarding); |
388 | 1.84k | } |