/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-2025 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 | 135 | { |
19 | 138 | 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 | 135 | } |
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 | 29.6k | { |
33 | 29.6k | if (con->last_packet_length == 0) { |
34 | 29.2k | return 0; |
35 | 29.2k | } |
36 | | |
37 | 410 | const uint16_t left = con->last_packet_length - con->last_packet_sent; |
38 | 410 | const int len = net_send(con->ns, logger, con->sock, con->last_packet + con->last_packet_sent, left, &con->ip_port, |
39 | 410 | con->net_profile); |
40 | | |
41 | 410 | if (len <= 0) { |
42 | 347 | return -1; |
43 | 347 | } |
44 | | |
45 | 63 | if (len == left) { |
46 | 63 | con->last_packet_length = 0; |
47 | 63 | con->last_packet_sent = 0; |
48 | 63 | return 0; |
49 | 63 | } |
50 | | |
51 | 0 | con->last_packet_sent += len; |
52 | 0 | return -1; |
53 | 63 | } |
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 | 29.6k | { |
61 | | /* finish sending current non-priority packet */ |
62 | 29.6k | if (send_pending_data_nonpriority(logger, con) == -1) { |
63 | 347 | return -1; |
64 | 347 | } |
65 | | |
66 | 29.2k | TCP_Priority_List *p = con->priority_queue_start; |
67 | | |
68 | 29.2k | while (p != nullptr) { |
69 | 346 | const uint16_t left = p->size - p->sent; |
70 | 346 | const int len = net_send(con->ns, logger, con->sock, p->data + p->sent, left, &con->ip_port, con->net_profile); |
71 | | |
72 | 346 | if (len != left) { |
73 | 346 | if (len > 0) { |
74 | 0 | p->sent += len; |
75 | 0 | } |
76 | | |
77 | 346 | break; |
78 | 346 | } |
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 | 29.2k | con->priority_queue_start = p; |
87 | | |
88 | 29.2k | if (p == nullptr) { |
89 | 28.9k | con->priority_queue_end = nullptr; |
90 | 28.9k | return 0; |
91 | 28.9k | } |
92 | | |
93 | 346 | return -1; |
94 | 29.2k | } |
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 | 1 | p->next = new_list; |
125 | 2 | } else { |
126 | 2 | con->priority_queue_start = new_list; |
127 | 2 | } |
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 | 4.70k | { |
141 | 4.70k | if (length + CRYPTO_MAC_SIZE > MAX_PACKET_SIZE) { |
142 | 0 | return -1; |
143 | 0 | } |
144 | | |
145 | 4.70k | bool sendpriority = true; |
146 | | |
147 | 4.70k | if (send_pending_data(logger, con) == -1) { |
148 | 1 | if (priority) { |
149 | 1 | sendpriority = false; |
150 | 1 | } else { |
151 | 0 | return 0; |
152 | 0 | } |
153 | 1 | } |
154 | | |
155 | 4.70k | const uint16_t packet_size = sizeof(uint16_t) + length + CRYPTO_MAC_SIZE; |
156 | 4.70k | VLA(uint8_t, packet, packet_size); |
157 | | |
158 | 4.70k | uint16_t c_length = net_htons(length + CRYPTO_MAC_SIZE); |
159 | 4.70k | memcpy(packet, &c_length, sizeof(uint16_t)); |
160 | 4.70k | int len = encrypt_data_symmetric(con->mem, con->shared_key, con->sent_nonce, data, length, packet + sizeof(uint16_t)); |
161 | | |
162 | 4.70k | if ((unsigned int)len != (packet_size - sizeof(uint16_t))) { |
163 | 0 | return -1; |
164 | 0 | } |
165 | | |
166 | 4.70k | if (priority) { |
167 | 605 | len = sendpriority ? net_send(con->ns, logger, con->sock, packet, packet_size, &con->ip_port, |
168 | 604 | con->net_profile) : 0; |
169 | | |
170 | 605 | if (len <= 0) { |
171 | 3 | len = 0; |
172 | 3 | } |
173 | | |
174 | 605 | increment_nonce(con->sent_nonce); |
175 | | |
176 | 605 | if ((unsigned int)len == packet_size) { |
177 | 602 | return 1; |
178 | 602 | } |
179 | | |
180 | 3 | return add_priority(con, packet, packet_size, len) ? 1 : 0; |
181 | 605 | } |
182 | | |
183 | 4.09k | len = net_send(con->ns, logger, con->sock, packet, packet_size, &con->ip_port, con->net_profile); |
184 | | |
185 | 4.09k | if (len <= 0) { |
186 | 7 | return 0; |
187 | 7 | } |
188 | | |
189 | 4.09k | increment_nonce(con->sent_nonce); |
190 | | |
191 | 4.09k | if ((unsigned int)len == packet_size) { |
192 | 4.09k | return 1; |
193 | 4.09k | } |
194 | | |
195 | 0 | memcpy(con->last_packet, packet, packet_size); |
196 | 0 | con->last_packet_length = packet_size; |
197 | 0 | con->last_packet_sent = len; |
198 | 0 | return 1; |
199 | 4.09k | } |
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 | 4.62k | { |
209 | 4.62k | const uint16_t count = net_socket_data_recv_buffer(ns, sock); |
210 | | |
211 | 4.62k | if (count < length) { |
212 | 179 | 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 | 179 | return -1; |
218 | 179 | } |
219 | | |
220 | 4.44k | const int len = net_recv(ns, logger, sock, data, length, ip_port); |
221 | | |
222 | 4.44k | if (len != length) { |
223 | 0 | LOGGER_ERROR(logger, "FAIL recv packet"); |
224 | 0 | return -1; |
225 | 0 | } |
226 | | |
227 | 4.44k | return len; |
228 | 4.44k | } |
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 | 28.8k | { |
239 | 28.8k | const uint16_t count = net_socket_data_recv_buffer(ns, sock); |
240 | | |
241 | 28.8k | if (count >= sizeof(uint16_t)) { |
242 | 4.31k | uint8_t length_buf[sizeof(uint16_t)]; |
243 | 4.31k | const int len = net_recv(ns, logger, sock, length_buf, sizeof(length_buf), ip_port); |
244 | | |
245 | 4.31k | if (len != sizeof(uint16_t)) { |
246 | 0 | LOGGER_ERROR(logger, "FAIL recv packet"); |
247 | 0 | return 0; |
248 | 0 | } |
249 | | |
250 | 4.31k | uint16_t length; |
251 | 4.31k | net_unpack_u16(length_buf, &length); |
252 | | |
253 | 4.31k | 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 | 4.31k | return length; |
259 | 4.31k | } |
260 | | |
261 | 24.5k | return 0; |
262 | 28.8k | } |
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 | 28.9k | { |
275 | 28.9k | if (*next_packet_length == 0) { |
276 | 28.8k | const uint16_t len = read_tcp_length(logger, ns, sock, ip_port); |
277 | | |
278 | 28.8k | if (len == (uint16_t) -1) { |
279 | 0 | return -1; |
280 | 0 | } |
281 | | |
282 | 28.8k | if (len == 0) { |
283 | 24.5k | return 0; |
284 | 24.5k | } |
285 | | |
286 | 4.31k | *next_packet_length = len; |
287 | 4.31k | } |
288 | | |
289 | 4.33k | 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 | 4.33k | VLA(uint8_t, data_encrypted, (int) *next_packet_length); |
295 | 4.33k | const int len_packet = read_tcp_packet(logger, mem, ns, sock, data_encrypted, *next_packet_length, ip_port); |
296 | | |
297 | 4.33k | if (len_packet == -1) { |
298 | 18 | return 0; |
299 | 18 | } |
300 | | |
301 | 4.31k | if (len_packet != *next_packet_length) { |
302 | 0 | LOGGER_WARNING(logger, "invalid packet length: %d, expected %d", len_packet, *next_packet_length); |
303 | 0 | return 0; |
304 | 0 | } |
305 | | |
306 | 4.31k | *next_packet_length = 0; |
307 | | |
308 | 4.31k | const int len = decrypt_data_symmetric(mem, shared_key, recv_nonce, data_encrypted, len_packet, data); |
309 | | |
310 | 4.31k | if (len + CRYPTO_MAC_SIZE != len_packet) { |
311 | 0 | LOGGER_ERROR(logger, "decrypted length %d does not match expected length %d", len + CRYPTO_MAC_SIZE, len_packet); |
312 | 0 | return -1; |
313 | 0 | } |
314 | | |
315 | 4.31k | increment_nonce(recv_nonce); |
316 | | |
317 | 4.31k | return len; |
318 | 4.31k | } |