/work/toxcore/TCP_common.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* SPDX-License-Identifier: GPL-3.0-or-later |
2 | | * Copyright © 2016-2026 The TokTok team. |
3 | | * Copyright © 2014 Tox project. |
4 | | */ |
5 | | |
6 | | #include "TCP_common.h" |
7 | | |
8 | | #include <string.h> |
9 | | |
10 | | #include "attributes.h" |
11 | | #include "ccompat.h" |
12 | | #include "crypto_core.h" |
13 | | #include "logger.h" |
14 | | #include "mem.h" |
15 | | #include "network.h" |
16 | | |
17 | | void wipe_priority_list(const Memory *mem, TCP_Priority_List *p) |
18 | 167 | { |
19 | 170 | while (p != nullptr) { |
20 | 3 | TCP_Priority_List *pp = p; |
21 | 3 | p = p->next; |
22 | 3 | mem_delete(mem, pp->data); |
23 | 3 | mem_delete(mem, pp); |
24 | 3 | } |
25 | 167 | } |
26 | | |
27 | | /** |
28 | | * @retval 0 if pending data was sent completely |
29 | | * @retval -1 if it wasn't |
30 | | */ |
31 | | int send_pending_data_nonpriority(const Logger *logger, TCP_Connection *con) |
32 | 59.7k | { |
33 | 59.7k | if (con->last_packet_length == 0) { |
34 | 59.1k | return 0; |
35 | 59.1k | } |
36 | | |
37 | 637 | const uint16_t left = con->last_packet_length - con->last_packet_sent; |
38 | 637 | const int len = net_send(con->ns, logger, con->sock, con->last_packet + con->last_packet_sent, left, &con->ip_port, |
39 | 637 | con->net_profile); |
40 | | |
41 | 637 | if (len <= 0) { |
42 | 596 | return -1; |
43 | 596 | } |
44 | | |
45 | 67 | if (len == left) { |
46 | 67 | con->last_packet_length = 0; |
47 | 67 | con->last_packet_sent = 0; |
48 | 67 | return 0; |
49 | 67 | } |
50 | | |
51 | 18.4E | con->last_packet_sent += len; |
52 | 18.4E | return -1; |
53 | 41 | } |
54 | | |
55 | | /** |
56 | | * @retval 0 if pending data was sent completely |
57 | | * @retval -1 if it wasn't |
58 | | */ |
59 | | int send_pending_data(const Logger *logger, TCP_Connection *con) |
60 | 59.7k | { |
61 | | /* finish sending current non-priority packet */ |
62 | 59.7k | if (send_pending_data_nonpriority(logger, con) == -1) { |
63 | 596 | return -1; |
64 | 596 | } |
65 | | |
66 | 59.1k | TCP_Priority_List *p = con->priority_queue_start; |
67 | | |
68 | 59.1k | while (p != nullptr) { |
69 | 2 | const uint16_t left = p->size - p->sent; |
70 | 2 | const int len = net_send(con->ns, logger, con->sock, p->data + p->sent, left, &con->ip_port, con->net_profile); |
71 | | |
72 | 2 | if (len != left) { |
73 | 2 | if (len > 0) { |
74 | 0 | p->sent += len; |
75 | 0 | } |
76 | | |
77 | 2 | break; |
78 | 2 | } |
79 | | |
80 | 0 | TCP_Priority_List *pp = p; |
81 | 0 | p = p->next; |
82 | 0 | mem_delete(con->mem, pp->data); |
83 | 0 | mem_delete(con->mem, pp); |
84 | 0 | } |
85 | | |
86 | 59.1k | con->priority_queue_start = p; |
87 | | |
88 | 59.1k | if (p == nullptr) { |
89 | 59.1k | con->priority_queue_end = nullptr; |
90 | 59.1k | return 0; |
91 | 59.1k | } |
92 | | |
93 | 18.4E | return -1; |
94 | 59.1k | } |
95 | | |
96 | | /** |
97 | | * @retval false on failure (only if mem_alloc fails) |
98 | | * @retval true on success |
99 | | */ |
100 | | static bool add_priority(TCP_Connection *_Nonnull con, const uint8_t *_Nonnull packet, uint16_t size, uint16_t sent) |
101 | 3 | { |
102 | 3 | TCP_Priority_List *p = con->priority_queue_end; |
103 | 3 | TCP_Priority_List *new_list = (TCP_Priority_List *)mem_alloc(con->mem, sizeof(TCP_Priority_List)); |
104 | | |
105 | 3 | if (new_list == nullptr) { |
106 | 0 | return false; |
107 | 0 | } |
108 | | |
109 | 3 | uint8_t *data = (uint8_t *)mem_balloc(con->mem, size); |
110 | | |
111 | 3 | if (data == nullptr) { |
112 | 0 | mem_delete(con->mem, new_list); |
113 | 0 | return false; |
114 | 0 | } |
115 | | |
116 | 3 | memcpy(data, packet, size); |
117 | 3 | new_list->data = data; |
118 | 3 | new_list->size = size; |
119 | | |
120 | 3 | new_list->next = nullptr; |
121 | 3 | new_list->sent = sent; |
122 | | |
123 | 3 | if (p != nullptr) { |
124 | 2 | p->next = new_list; |
125 | 2 | } else { |
126 | 1 | con->priority_queue_start = new_list; |
127 | 1 | } |
128 | | |
129 | 3 | con->priority_queue_end = new_list; |
130 | 3 | return true; |
131 | 3 | } |
132 | | |
133 | | /** |
134 | | * @retval 1 on success. |
135 | | * @retval 0 if could not send packet. |
136 | | * @retval -1 on failure (connection must be killed). |
137 | | */ |
138 | | int write_packet_tcp_secure_connection(const Logger *logger, TCP_Connection *con, const uint8_t *data, uint16_t length, |
139 | | bool priority) |
140 | 16.8k | { |
141 | 16.8k | if (length + CRYPTO_MAC_SIZE > MAX_PACKET_SIZE) { |
142 | 0 | return -1; |
143 | 0 | } |
144 | | |
145 | 16.8k | bool sendpriority = true; |
146 | | |
147 | 16.8k | if (send_pending_data(logger, con) == -1) { |
148 | 2 | if (priority) { |
149 | 2 | sendpriority = false; |
150 | 2 | } else { |
151 | 0 | return 0; |
152 | 0 | } |
153 | 2 | } |
154 | | |
155 | 16.8k | const uint16_t packet_size = sizeof(uint16_t) + length + CRYPTO_MAC_SIZE; |
156 | 16.8k | VLA(uint8_t, packet, packet_size); |
157 | | |
158 | 16.8k | uint16_t c_length = net_htons(length + CRYPTO_MAC_SIZE); |
159 | 16.8k | memcpy(packet, &c_length, sizeof(uint16_t)); |
160 | 16.8k | int len = encrypt_data_symmetric(con->mem, con->shared_key, con->sent_nonce, data, length, packet + sizeof(uint16_t)); |
161 | | |
162 | 16.8k | if ((unsigned int)len != (packet_size - sizeof(uint16_t))) { |
163 | 0 | return -1; |
164 | 0 | } |
165 | | |
166 | 16.8k | if (priority) { |
167 | 758 | len = sendpriority ? net_send(con->ns, logger, con->sock, packet, packet_size, &con->ip_port, |
168 | 756 | con->net_profile) : 0; |
169 | | |
170 | 758 | if (len <= 0) { |
171 | 3 | len = 0; |
172 | 3 | } |
173 | | |
174 | 758 | increment_nonce(con->sent_nonce); |
175 | | |
176 | 758 | if ((unsigned int)len == packet_size) { |
177 | 755 | return 1; |
178 | 755 | } |
179 | | |
180 | 3 | return add_priority(con, packet, packet_size, len) ? 1 : 0; |
181 | 758 | } |
182 | | |
183 | 16.0k | len = net_send(con->ns, logger, con->sock, packet, packet_size, &con->ip_port, con->net_profile); |
184 | | |
185 | 16.0k | if (len <= 0) { |
186 | 0 | return 0; |
187 | 0 | } |
188 | | |
189 | 16.0k | increment_nonce(con->sent_nonce); |
190 | | |
191 | 16.0k | if ((unsigned int)len == packet_size) { |
192 | 16.0k | return 1; |
193 | 16.0k | } |
194 | | |
195 | 3 | memcpy(con->last_packet, packet, packet_size); |
196 | 3 | con->last_packet_length = packet_size; |
197 | 3 | con->last_packet_sent = len; |
198 | 3 | return 1; |
199 | 16.0k | } |
200 | | |
201 | | /** @brief Read length bytes from socket. |
202 | | * |
203 | | * return length on success |
204 | | * return -1 on failure/no data in buffer. |
205 | | */ |
206 | | int read_tcp_packet( |
207 | | const Logger *logger, const Memory *mem, const Network *ns, Socket sock, uint8_t *data, uint16_t length, const IP_Port *ip_port) |
208 | 16.8k | { |
209 | 16.8k | const uint16_t count = net_socket_data_recv_buffer(ns, sock); |
210 | | |
211 | 16.8k | if (count < length) { |
212 | 94 | if (count != 0) { |
213 | | // Only log when there are some bytes available, as empty buffer |
214 | | // is a very common case and this spams our logs. |
215 | 22 | LOGGER_TRACE(logger, "recv buffer has %d bytes, but requested %d bytes", count, length); |
216 | 22 | } |
217 | 94 | return -1; |
218 | 94 | } |
219 | | |
220 | 16.7k | const int len = net_recv(ns, logger, sock, data, length, ip_port); |
221 | | |
222 | 16.7k | if (len != length) { |
223 | 0 | LOGGER_ERROR(logger, "FAIL recv packet"); |
224 | 0 | return -1; |
225 | 0 | } |
226 | | |
227 | 16.7k | return len; |
228 | 16.7k | } |
229 | | |
230 | | /** @brief Read the next two bytes in TCP stream then convert them to |
231 | | * length (host byte order). |
232 | | * |
233 | | * return length on success |
234 | | * return 0 if nothing has been read from socket. |
235 | | * return -1 on failure. |
236 | | */ |
237 | | static uint16_t read_tcp_length(const Logger *_Nonnull logger, const Network *_Nonnull ns, Socket sock, const IP_Port *_Nonnull ip_port) |
238 | 60.3k | { |
239 | 60.3k | const uint16_t count = net_socket_data_recv_buffer(ns, sock); |
240 | | |
241 | 60.3k | if (count >= sizeof(uint16_t)) { |
242 | 16.5k | uint8_t length_buf[sizeof(uint16_t)]; |
243 | 16.5k | const int len = net_recv(ns, logger, sock, length_buf, sizeof(length_buf), ip_port); |
244 | | |
245 | 16.5k | if (len != sizeof(uint16_t)) { |
246 | 0 | LOGGER_ERROR(logger, "FAIL recv packet"); |
247 | 0 | return 0; |
248 | 0 | } |
249 | | |
250 | 16.5k | uint16_t length; |
251 | 16.5k | net_unpack_u16(length_buf, &length); |
252 | | |
253 | 16.5k | if (length > MAX_PACKET_SIZE) { |
254 | 0 | LOGGER_ERROR(logger, "TCP packet too large: %d > %d", length, MAX_PACKET_SIZE); |
255 | 0 | return -1; |
256 | 0 | } |
257 | | |
258 | 16.5k | return length; |
259 | 16.5k | } |
260 | | |
261 | 43.7k | return 0; |
262 | 60.3k | } |
263 | | |
264 | | /** |
265 | | * @return length of received packet on success. |
266 | | * @retval 0 if could not read any packet. |
267 | | * @retval -1 on failure (connection must be killed). |
268 | | */ |
269 | | int read_packet_tcp_secure_connection( |
270 | | const Logger *logger, const Memory *mem, const Network *ns, |
271 | | Socket sock, uint16_t *next_packet_length, |
272 | | const uint8_t *shared_key, uint8_t *recv_nonce, uint8_t *data, |
273 | | uint16_t max_len, const IP_Port *ip_port) |
274 | 60.2k | { |
275 | 60.3k | if (*next_packet_length == 0) { |
276 | 60.3k | const uint16_t len = read_tcp_length(logger, ns, sock, ip_port); |
277 | | |
278 | 60.3k | if (len == (uint16_t) -1) { |
279 | 0 | return -1; |
280 | 0 | } |
281 | | |
282 | 60.3k | if (len == 0) { |
283 | 43.7k | return 0; |
284 | 43.7k | } |
285 | | |
286 | 16.6k | *next_packet_length = len; |
287 | 16.6k | } |
288 | | |
289 | 16.5k | if (max_len + CRYPTO_MAC_SIZE < *next_packet_length) { |
290 | 0 | LOGGER_DEBUG(logger, "packet too large"); |
291 | 0 | return -1; |
292 | 0 | } |
293 | | |
294 | 16.5k | VLA(uint8_t, data_encrypted, (int) *next_packet_length); |
295 | 16.5k | const int len_packet = read_tcp_packet(logger, mem, ns, sock, data_encrypted, *next_packet_length, ip_port); |
296 | | |
297 | 16.5k | if (len_packet == -1) { |
298 | 18 | return 0; |
299 | 18 | } |
300 | | |
301 | 16.5k | if (len_packet != *next_packet_length) { |
302 | | // TODO(Green-Sky): this is probably unrecoverable |
303 | 0 | LOGGER_WARNING(logger, "invalid packet length: %d, expected %d", len_packet, *next_packet_length); |
304 | 0 | return 0; |
305 | 0 | } |
306 | | |
307 | 16.5k | *next_packet_length = 0; |
308 | | |
309 | 16.5k | const int len = decrypt_data_symmetric(mem, shared_key, recv_nonce, data_encrypted, len_packet, data); |
310 | | |
311 | 16.5k | if (len + CRYPTO_MAC_SIZE != len_packet) { |
312 | 0 | LOGGER_ERROR(logger, "decrypted length %d does not match expected length %d", len + CRYPTO_MAC_SIZE, len_packet); |
313 | 0 | return -1; |
314 | 0 | } |
315 | | |
316 | 16.5k | increment_nonce(recv_nonce); |
317 | | |
318 | 16.5k | return len; |
319 | 16.5k | } |
320 | | |
321 | | const char *tcp_packet_type_to_string(Tcp_Packet type) |
322 | 0 | { |
323 | 0 | switch (type) { |
324 | 0 | case TCP_PACKET_ROUTING_REQUEST: |
325 | 0 | return "TCP_PACKET_ROUTING_REQUEST"; |
326 | | |
327 | 0 | case TCP_PACKET_ROUTING_RESPONSE: |
328 | 0 | return "TCP_PACKET_ROUTING_RESPONSE"; |
329 | | |
330 | 0 | case TCP_PACKET_CONNECTION_NOTIFICATION: |
331 | 0 | return "TCP_PACKET_CONNECTION_NOTIFICATION"; |
332 | | |
333 | 0 | case TCP_PACKET_DISCONNECT_NOTIFICATION: |
334 | 0 | return "TCP_PACKET_DISCONNECT_NOTIFICATION"; |
335 | | |
336 | 0 | case TCP_PACKET_PING: |
337 | 0 | return "TCP_PACKET_PING"; |
338 | | |
339 | 0 | case TCP_PACKET_PONG: |
340 | 0 | return "TCP_PACKET_PONG"; |
341 | | |
342 | 0 | case TCP_PACKET_OOB_SEND: |
343 | 0 | return "TCP_PACKET_OOB_SEND"; |
344 | | |
345 | 0 | case TCP_PACKET_OOB_RECV: |
346 | 0 | return "TCP_PACKET_OOB_RECV"; |
347 | | |
348 | 0 | case TCP_PACKET_ONION_REQUEST: |
349 | 0 | return "TCP_PACKET_ONION_REQUEST"; |
350 | | |
351 | 0 | case TCP_PACKET_ONION_RESPONSE: |
352 | 0 | return "TCP_PACKET_ONION_RESPONSE"; |
353 | | |
354 | 0 | case TCP_PACKET_FORWARD_REQUEST: |
355 | 0 | return "TCP_PACKET_FORWARD_REQUEST"; |
356 | | |
357 | 0 | case TCP_PACKET_FORWARDING: |
358 | 0 | return "TCP_PACKET_FORWARDING"; |
359 | 0 | } |
360 | | |
361 | 0 | return "<invalid Tcp_Packet>"; |
362 | 0 | } |
363 | | |
364 | | bool tcp_packet_from_int(uint32_t value, Tcp_Packet *out_enum) |
365 | 0 | { |
366 | 0 | switch (value) { |
367 | 0 | case TCP_PACKET_ROUTING_REQUEST: |
368 | 0 | case TCP_PACKET_ROUTING_RESPONSE: |
369 | 0 | case TCP_PACKET_CONNECTION_NOTIFICATION: |
370 | 0 | case TCP_PACKET_DISCONNECT_NOTIFICATION: |
371 | 0 | case TCP_PACKET_PING: |
372 | 0 | case TCP_PACKET_PONG: |
373 | 0 | case TCP_PACKET_OOB_SEND: |
374 | 0 | case TCP_PACKET_OOB_RECV: |
375 | 0 | case TCP_PACKET_ONION_REQUEST: |
376 | 0 | case TCP_PACKET_ONION_RESPONSE: |
377 | 0 | case TCP_PACKET_FORWARD_REQUEST: |
378 | 0 | case TCP_PACKET_FORWARDING: { |
379 | 0 | *out_enum = (Tcp_Packet)value; |
380 | 0 | return true; |
381 | 0 | } |
382 | 0 | } |
383 | | |
384 | 0 | return false; |
385 | 0 | } |