Line | Count | Source (jump to first uncovered line) |
1 | | /* SPDX-License-Identifier: GPL-3.0-or-later |
2 | | * Copyright © 2016-2026 The TokTok team. |
3 | | * Copyright © 2013 Tox project. |
4 | | */ |
5 | | |
6 | | /** |
7 | | * Functions for the core networking. |
8 | | */ |
9 | | |
10 | | #include "network.h" |
11 | | |
12 | | #include <assert.h> |
13 | | #include <limits.h> |
14 | | #include <stdio.h> |
15 | | #include <stdlib.h> |
16 | | #include <string.h> |
17 | | |
18 | | #include "attributes.h" |
19 | | #include "bin_pack.h" |
20 | | #include "ccompat.h" |
21 | | #include "logger.h" |
22 | | #include "mem.h" |
23 | | #include "net.h" |
24 | | #include "net_log.h" |
25 | | #include "net_profile.h" |
26 | | #include "os_network.h" |
27 | | #include "util.h" |
28 | | |
29 | | static_assert(sizeof(IP4) == SIZE_IP4, "IP4 size must be 4"); |
30 | | |
31 | | // TODO(iphydf): Stop relying on this. We memcpy this struct (and IP4 above) |
32 | | // into packets but really should be serialising it properly. |
33 | | static_assert(sizeof(IP6) == SIZE_IP6, "IP6 size must be 16"); |
34 | | |
35 | | IP4 get_ip4_broadcast(void) |
36 | 31 | { |
37 | 31 | return net_get_ip4_broadcast(); |
38 | 31 | } |
39 | | |
40 | | IP6 get_ip6_broadcast(void) |
41 | 132 | { |
42 | 132 | return net_get_ip6_broadcast(); |
43 | 132 | } |
44 | | |
45 | | IP4 get_ip4_loopback(void) |
46 | 155 | { |
47 | 155 | return net_get_ip4_loopback(); |
48 | 155 | } |
49 | | |
50 | | IP6 get_ip6_loopback(void) |
51 | 3 | { |
52 | 3 | return net_get_ip6_loopback(); |
53 | 3 | } |
54 | | |
55 | | bool sock_valid(Socket sock) |
56 | 2.38k | { |
57 | 2.38k | const Socket invalid_socket = net_invalid_socket(); |
58 | 2.38k | return sock.value != invalid_socket.value; |
59 | 2.38k | } |
60 | | |
61 | | int net_send(const Network *ns, const Logger *log, |
62 | | Socket sock, const uint8_t *buf, size_t len, const IP_Port *ip_port, Net_Profile *net_profile) |
63 | 17.6k | { |
64 | 17.6k | const int res = ns_send(ns, sock, buf, len); |
65 | | |
66 | 17.6k | if (res > 0) { |
67 | 17.0k | netprof_record_packet(net_profile, buf[0], res, PACKET_DIRECTION_SEND); |
68 | 17.0k | } |
69 | | |
70 | 17.6k | net_log_data(log, "T=>", buf, len, ip_port, res); |
71 | 17.6k | return res; |
72 | 17.6k | } |
73 | | |
74 | | int net_recv(const Network *ns, const Logger *log, |
75 | | Socket sock, uint8_t *buf, size_t len, const IP_Port *ip_port) |
76 | 33.3k | { |
77 | 33.3k | const int res = ns_recv(ns, sock, buf, len); |
78 | 33.3k | net_log_data(log, "=>T", buf, len, ip_port, res); |
79 | 33.3k | return res; |
80 | 33.3k | } |
81 | | |
82 | | int net_listen(const Network *ns, Socket sock, int backlog) |
83 | 36 | { |
84 | 36 | return ns_listen(ns, sock, backlog); |
85 | 36 | } |
86 | | |
87 | | Socket net_accept(const Network *ns, Socket sock) |
88 | 90 | { |
89 | 90 | return ns_accept(ns, sock); |
90 | 90 | } |
91 | | |
92 | | /** Close the socket. */ |
93 | | void kill_sock(const Network *ns, Socket sock) |
94 | 2.12k | { |
95 | 2.12k | ns_close(ns, sock); |
96 | 2.12k | } |
97 | | |
98 | | bool set_socket_nonblock(const Network *ns, Socket sock) |
99 | 2.21k | { |
100 | 2.21k | return net_set_socket_nonblock(ns, sock); |
101 | 2.21k | } |
102 | | |
103 | | bool set_socket_nosigpipe(const Network *ns, Socket sock) |
104 | 2.17k | { |
105 | 2.17k | return net_set_socket_nosigpipe(ns, sock); |
106 | 2.17k | } |
107 | | |
108 | | bool set_socket_reuseaddr(const Network *ns, Socket sock) |
109 | 34 | { |
110 | 34 | return net_set_socket_reuseaddr(ns, sock); |
111 | 34 | } |
112 | | |
113 | | bool set_socket_dualstack(const Network *ns, Socket sock) |
114 | 1.49k | { |
115 | 1.49k | return net_set_socket_dualstack(ns, sock); |
116 | 1.49k | } |
117 | | |
118 | | typedef struct Packet_Handler { |
119 | | packet_handler_cb *_Nullable function; |
120 | | void *_Nullable object; |
121 | | } Packet_Handler; |
122 | | |
123 | | struct Networking_Core { |
124 | | const Logger *_Nonnull log; |
125 | | const Memory *_Nonnull mem; |
126 | | Packet_Handler packethandlers[256]; |
127 | | const Network *_Nonnull ns; |
128 | | |
129 | | Family family; |
130 | | uint16_t port; |
131 | | /* Our UDP socket. */ |
132 | | Socket sock; |
133 | | |
134 | | Net_Profile *_Nullable udp_net_profile; |
135 | | }; |
136 | | |
137 | | Family net_family(const Networking_Core *net) |
138 | 2.69k | { |
139 | 2.69k | return net->family; |
140 | 2.69k | } |
141 | | |
142 | | uint16_t net_port(const Networking_Core *net) |
143 | 113k | { |
144 | 113k | return net->port; |
145 | 113k | } |
146 | | |
147 | | /* Basic network functions: |
148 | | */ |
149 | | |
150 | | bool net_socket_family_compatible(const Networking_Core *net, const IP_Port *ip_port, bool log_warning) |
151 | 833k | { |
152 | 833k | if (!net_family_is_ipv4(net->family) || net_family_is_ipv4(ip_port->ip.family)) { |
153 | 832k | return true; |
154 | 832k | } |
155 | 546 | Ip_Ntoa ip_str; |
156 | 546 | if (log_warning) { |
157 | 3 | LOGGER_WARNING(net->log, "attempted to send message with network family %d (probably IPv6) on IPv4 socket (%s)", |
158 | 3 | ip_port->ip.family.value, net_ip_ntoa(&ip_port->ip, &ip_str)); |
159 | 543 | } else { |
160 | 543 | LOGGER_TRACE(net->log, "attempted to send message with network family %d (probably IPv6) on IPv4 socket (%s)", |
161 | 543 | ip_port->ip.family.value, net_ip_ntoa(&ip_port->ip, &ip_str)); |
162 | 543 | } |
163 | 546 | return false; |
164 | 833k | } |
165 | | |
166 | | int net_send_packet(const Networking_Core *net, const IP_Port *ip_port, Net_Packet packet) |
167 | 561k | { |
168 | 561k | IP_Port ipp_copy = *ip_port; |
169 | | |
170 | 561k | if (net_family_is_unspec(ip_port->ip.family)) { |
171 | | // TODO(iphydf): Make this an error. Currently this fails sometimes when |
172 | | // called from DHT.c:do_ping_and_sendnode_requests. |
173 | 4 | return -1; |
174 | 4 | } |
175 | | |
176 | 561k | if (net_family_is_unspec(net->family)) { /* Socket not initialized */ |
177 | | // TODO(iphydf): Make this an error. Currently, the onion client calls |
178 | | // this via DHT nodes requests. |
179 | 2 | LOGGER_WARNING(net->log, "attempted to send message of length %u on uninitialised socket", packet.length); |
180 | 2 | return -1; |
181 | 2 | } |
182 | | |
183 | | /* socket TOX_AF_INET, but target IP NOT: can't send */ |
184 | 561k | if (!net_socket_family_compatible(net, ip_port, true)) { |
185 | | // TODO(iphydf): Make this an error. Occasionally we try to send to an |
186 | | // all-zero ip_port. |
187 | 3 | return -1; |
188 | 3 | } |
189 | | |
190 | 561k | if (net_family_is_ipv4(ipp_copy.ip.family) && net_family_is_ipv6(net->family)) { |
191 | | /* must convert to IPV4-in-IPV6 address */ |
192 | 40.5k | IP6 ip6; |
193 | | |
194 | | /* there should be a macro for this in a standards compliant |
195 | | * environment, not found */ |
196 | 40.5k | ip6.uint32[0] = 0; |
197 | 40.5k | ip6.uint32[1] = 0; |
198 | 40.5k | ip6.uint32[2] = net_htonl(0xFFFF); |
199 | 40.5k | ip6.uint32[3] = ipp_copy.ip.ip.v4.uint32; |
200 | | |
201 | 40.5k | ipp_copy.ip.family = net_family_ipv6(); |
202 | 40.5k | ipp_copy.ip.ip.v6 = ip6; |
203 | 40.5k | } |
204 | | |
205 | 561k | const long res = ns_sendto(net->ns, net->sock, packet.data, packet.length, &ipp_copy); |
206 | 561k | net_log_data(net->log, "O=>", packet.data, packet.length, ip_port, res); |
207 | | |
208 | 561k | assert(res <= INT_MAX); |
209 | | |
210 | 560k | if (res == packet.length && packet.data != nullptr) { |
211 | 560k | netprof_record_packet(net->udp_net_profile, packet.data[0], packet.length, PACKET_DIRECTION_SEND); |
212 | 560k | } |
213 | | |
214 | 560k | return (int)res; |
215 | 561k | } |
216 | | |
217 | | /** |
218 | | * Function to send packet(data) of length length to ip_port. |
219 | | * |
220 | | * @deprecated Use net_send_packet instead. |
221 | | */ |
222 | | int sendpacket(const Networking_Core *net, const IP_Port *ip_port, const uint8_t *data, uint16_t length) |
223 | 560k | { |
224 | 560k | const Net_Packet packet = {data, length}; |
225 | 560k | return net_send_packet(net, ip_port, packet); |
226 | 560k | } |
227 | | |
228 | | /** @brief Function to receive data |
229 | | * ip and port of sender is put into ip_port. |
230 | | * Packet data is put into data. |
231 | | * Packet length is put into length. |
232 | | */ |
233 | | static int receivepacket(const Network *_Nonnull ns, const Logger *_Nonnull log, Socket sock, IP_Port *_Nonnull ip_port, uint8_t *_Nonnull data, uint32_t *_Nonnull length) |
234 | 631k | { |
235 | 631k | memset(ip_port, 0, sizeof(IP_Port)); |
236 | 631k | *length = 0; |
237 | | |
238 | 631k | const int fail_or_len = ns_recvfrom(ns, sock, data, MAX_UDP_PACKET_SIZE, ip_port); |
239 | | |
240 | 631k | if (fail_or_len < 0) { |
241 | 102k | const int error = net_error(); |
242 | | |
243 | 102k | if (!net_should_ignore_recv_error(error)) { |
244 | 4 | Net_Strerror error_str; |
245 | 4 | LOGGER_ERROR(log, "unexpected error reading from socket: %u, %s", (unsigned int)error, net_strerror(error, &error_str)); |
246 | 4 | } |
247 | | |
248 | 102k | return -1; /* Nothing received. */ |
249 | 102k | } |
250 | | |
251 | 528k | *length = (uint32_t)fail_or_len; |
252 | | |
253 | 528k | if (net_family_is_ipv6(ip_port->ip.family) && ipv6_ipv4_in_v6(&ip_port->ip.ip.v6)) { |
254 | 40.1k | ip_port->ip.family = net_family_ipv4(); |
255 | 40.1k | ip_port->ip.ip.v4.uint32 = ip_port->ip.ip.v6.uint32[3]; |
256 | 40.1k | } |
257 | | |
258 | 528k | net_log_data(log, "=>O", data, MAX_UDP_PACKET_SIZE, ip_port, *length); |
259 | | |
260 | 528k | return 0; |
261 | 631k | } |
262 | | |
263 | | void networking_registerhandler(Networking_Core *net, uint8_t byte, packet_handler_cb *cb, void *object) |
264 | 121k | { |
265 | 121k | net->packethandlers[byte].function = cb; |
266 | 121k | net->packethandlers[byte].object = object; |
267 | 121k | } |
268 | | |
269 | | void networking_poll(const Networking_Core *net, void *userdata) |
270 | 103k | { |
271 | 103k | if (net_family_is_unspec(net->family)) { |
272 | | /* Socket not initialized */ |
273 | 620 | return; |
274 | 620 | } |
275 | | |
276 | 102k | IP_Port ip_port; |
277 | 102k | uint8_t data[MAX_UDP_PACKET_SIZE] = {0}; |
278 | 102k | uint32_t length; |
279 | | |
280 | 631k | while (receivepacket(net->ns, net->log, net->sock, &ip_port, data, &length) != -1) { |
281 | 528k | if (length < 1) { |
282 | 0 | continue; |
283 | 0 | } |
284 | | |
285 | 528k | netprof_record_packet(net->udp_net_profile, data[0], length, PACKET_DIRECTION_RECV); |
286 | | |
287 | 528k | const Packet_Handler *const handler = &net->packethandlers[data[0]]; |
288 | | |
289 | 528k | if (handler->function == nullptr) { |
290 | | // TODO(https://github.com/TokTok/c-toxcore/issues/1115): Make this |
291 | | // a warning or error again. |
292 | 1.80k | LOGGER_DEBUG(net->log, "[%02u] -- Packet has no handler", data[0]); |
293 | 1.80k | continue; |
294 | 1.80k | } |
295 | | |
296 | 527k | handler->function(handler->object, &ip_port, data, length, userdata); |
297 | 527k | } |
298 | 102k | } |
299 | | |
300 | | /** @brief Initialize networking. |
301 | | * Bind to ip and port. |
302 | | * ip must be in network order EX: 127.0.0.1 = (7F000001). |
303 | | * port is in host byte order (this means don't worry about it). |
304 | | * |
305 | | * @return Networking_Core object if no problems |
306 | | * @retval NULL if there are problems. |
307 | | * |
308 | | * If error is non NULL it is set to 0 if no issues, 1 if socket related error, 2 if other. |
309 | | */ |
310 | | Networking_Core *new_networking_ex( |
311 | | const Logger *log, const Memory *mem, const Network *ns, const IP *ip, |
312 | | uint16_t port_from, uint16_t port_to, unsigned int *error) |
313 | 2.01k | { |
314 | | /* If both from and to are 0, use default port range |
315 | | * If one is 0 and the other is non-0, use the non-0 value as only port |
316 | | * If from > to, swap |
317 | | */ |
318 | 2.01k | if (port_from == 0 && port_to == 0) { |
319 | 1.43k | port_from = TOX_PORTRANGE_FROM; |
320 | 1.43k | port_to = TOX_PORTRANGE_TO; |
321 | 1.43k | } else if (port_from == 0 && port_to != 0) { |
322 | 7 | port_from = port_to; |
323 | 580 | } else if (port_from != 0 && port_to == 0) { |
324 | 1 | port_to = port_from; |
325 | 579 | } else if (port_from > port_to) { |
326 | 4 | const uint16_t temp_port = port_from; |
327 | 4 | port_from = port_to; |
328 | 4 | port_to = temp_port; |
329 | 4 | } |
330 | | |
331 | 2.01k | if (error != nullptr) { |
332 | 1.81k | *error = 2; |
333 | 1.81k | } |
334 | | |
335 | | /* maybe check for invalid IPs like 224+.x.y.z? if there is any IP set ever */ |
336 | 2.01k | if (!net_family_is_ipv4(ip->family) && !net_family_is_ipv6(ip->family)) { |
337 | 2 | LOGGER_ERROR(log, "invalid address family: %u", ip->family.value); |
338 | 2 | return nullptr; |
339 | 2 | } |
340 | | |
341 | 2.01k | Networking_Core *temp = (Networking_Core *)mem_alloc(mem, sizeof(Networking_Core)); |
342 | | |
343 | 2.01k | if (temp == nullptr) { |
344 | 4 | return nullptr; |
345 | 4 | } |
346 | | |
347 | 2.01k | Net_Profile *np = netprof_new(log, mem); |
348 | | |
349 | 2.01k | if (np == nullptr) { |
350 | 4 | mem_delete(mem, temp); |
351 | 4 | return nullptr; |
352 | 4 | } |
353 | | |
354 | 2.00k | temp->udp_net_profile = np; |
355 | 2.00k | temp->ns = ns; |
356 | 2.00k | temp->log = log; |
357 | 2.00k | temp->mem = mem; |
358 | 2.00k | temp->family = ip->family; |
359 | 2.00k | temp->port = 0; |
360 | | |
361 | | /* Initialize our socket. */ |
362 | | /* add log message what we're creating */ |
363 | 2.00k | temp->sock = net_socket(ns, temp->family, TOX_SOCK_DGRAM, TOX_PROTO_UDP); |
364 | | |
365 | | /* Check for socket error. */ |
366 | 2.00k | if (!sock_valid(temp->sock)) { |
367 | 3 | const int neterror = net_error(); |
368 | 3 | Net_Strerror error_str; |
369 | 3 | LOGGER_ERROR(log, "failed to get a socket?! %d, %s", neterror, net_strerror(neterror, &error_str)); |
370 | 3 | netprof_kill(mem, temp->udp_net_profile); |
371 | 3 | mem_delete(mem, temp); |
372 | | |
373 | 3 | if (error != nullptr) { |
374 | 1 | *error = 1; |
375 | 1 | } |
376 | | |
377 | 3 | return nullptr; |
378 | 3 | } |
379 | | |
380 | | /* Functions to increase the size of the send and receive UDP buffers. |
381 | | */ |
382 | 2.00k | if (!net_set_socket_buffer_size(ns, temp->sock, 1024 * 1024 * 2)) { |
383 | 3 | LOGGER_WARNING(log, "failed to set socket buffer size"); |
384 | 3 | } |
385 | | |
386 | | /* Enable broadcast on socket */ |
387 | 2.00k | if (!net_set_socket_broadcast(ns, temp->sock)) { |
388 | 3 | LOGGER_ERROR(log, "failed to set socket broadcast"); |
389 | 3 | } |
390 | | |
391 | | /* iOS UDP sockets are weird and apparently can SIGPIPE */ |
392 | 2.00k | if (!set_socket_nosigpipe(ns, temp->sock)) { |
393 | 0 | kill_networking(temp); |
394 | |
|
395 | 0 | if (error != nullptr) { |
396 | 0 | *error = 1; |
397 | 0 | } |
398 | |
|
399 | 0 | return nullptr; |
400 | 0 | } |
401 | | |
402 | | /* Set socket nonblocking. */ |
403 | 2.00k | if (!set_socket_nonblock(ns, temp->sock)) { |
404 | 0 | kill_networking(temp); |
405 | |
|
406 | 0 | if (error != nullptr) { |
407 | 0 | *error = 1; |
408 | 0 | } |
409 | |
|
410 | 0 | return nullptr; |
411 | 0 | } |
412 | | |
413 | | /* Bind our socket to port PORT and the given IP address (usually 0.0.0.0 or ::) */ |
414 | 2.00k | uint16_t *portptr = nullptr; |
415 | 2.00k | IP_Port addr; |
416 | 2.00k | ip_init(&addr.ip, net_family_is_ipv6(temp->family)); |
417 | | |
418 | 2.00k | if (net_family_is_ipv4(temp->family) || net_family_is_ipv6(temp->family)) { |
419 | 2.00k | ip_copy(&addr.ip, ip); |
420 | 2.00k | addr.port = 0; |
421 | 2.00k | portptr = &addr.port; |
422 | 2.00k | } else { |
423 | 0 | mem_delete(mem, temp); |
424 | 0 | return nullptr; |
425 | 0 | } |
426 | | |
427 | 2.00k | if (net_family_is_ipv6(ip->family)) { |
428 | 1.47k | const bool is_dualstack = set_socket_dualstack(ns, temp->sock); |
429 | | |
430 | 1.47k | if (is_dualstack) { |
431 | 1.47k | LOGGER_TRACE(log, "Dual-stack socket: enabled"); |
432 | 1.47k | } else { |
433 | 1 | LOGGER_ERROR(log, "Dual-stack socket failed to enable, won't be able to receive from/send to IPv4 addresses"); |
434 | 1 | } |
435 | | |
436 | 1.47k | if (!net_join_multicast(ns, temp->sock, ip->family)) { |
437 | 30 | const int neterror = net_error(); |
438 | 30 | Net_Strerror error_str; |
439 | 30 | LOGGER_INFO(log, "Failed to activate local multicast membership in FF02::1. (%d, %s)", neterror, net_strerror(neterror, &error_str)); |
440 | 1.44k | } else { |
441 | 1.44k | const int neterror = net_error(); |
442 | 1.44k | Net_Strerror error_str; |
443 | 1.44k | LOGGER_TRACE(log, "Local multicast group joined successfully. (%d, %s)", neterror, net_strerror(neterror, &error_str)); |
444 | 1.44k | } |
445 | 1.47k | } |
446 | | |
447 | | /* A hanging program or a different user might block the standard port. |
448 | | * As long as it isn't a parameter coming from the commandline, |
449 | | * try a few ports after it, to see if we can find a "free" one. |
450 | | * |
451 | | * If we go on without binding, the first sendto() automatically binds to |
452 | | * a free port chosen by the system (i.e. anything from 1024 to 65535). |
453 | | * |
454 | | * Returning NULL after bind fails has both advantages and disadvantages: |
455 | | * advantage: |
456 | | * we can rely on getting the port in the range 33445..33450, which |
457 | | * enables us to tell joe user to open their firewall to a small range |
458 | | * |
459 | | * disadvantage: |
460 | | * some clients might not test return of tox_new(), blindly assuming that |
461 | | * it worked ok (which it did previously without a successful bind) |
462 | | */ |
463 | 2.00k | uint16_t port_to_try = port_from; |
464 | 2.00k | *portptr = net_htons(port_to_try); |
465 | | |
466 | 34.4k | for (uint16_t tries = port_from; tries <= port_to; ++tries) { |
467 | 34.4k | const int res = ns_bind(ns, temp->sock, &addr); |
468 | | |
469 | 34.4k | if (res == 0) { |
470 | 2.00k | temp->port = *portptr; |
471 | | |
472 | 2.00k | Ip_Ntoa ip_str; |
473 | 2.00k | LOGGER_DEBUG(log, "Bound successfully to %s:%u", net_ip_ntoa(ip, &ip_str), |
474 | 2.00k | net_ntohs(temp->port)); |
475 | | |
476 | | /* errno isn't reset on success, only set on failure, the failed |
477 | | * binds with parallel clients yield a -EPERM to the outside if |
478 | | * errno isn't cleared here */ |
479 | 2.00k | if (tries > 0) { |
480 | 2.00k | net_clear_error(); |
481 | 2.00k | } |
482 | | |
483 | 2.00k | if (error != nullptr) { |
484 | 1.81k | *error = 0; |
485 | 1.81k | } |
486 | | |
487 | 2.00k | return temp; |
488 | 2.00k | } |
489 | | |
490 | 32.4k | ++port_to_try; |
491 | | |
492 | 32.4k | if (port_to_try > port_to) { |
493 | 3 | port_to_try = port_from; |
494 | 3 | } |
495 | | |
496 | 32.4k | *portptr = net_htons(port_to_try); |
497 | 32.4k | } |
498 | | |
499 | 3 | Ip_Ntoa ip_str; |
500 | 3 | const int neterror = net_error(); |
501 | 3 | Net_Strerror error_str; |
502 | 3 | LOGGER_ERROR(log, "failed to bind socket: %d, %s IP: %s port_from: %u port_to: %u", |
503 | 3 | neterror, net_strerror(neterror, &error_str), net_ip_ntoa(ip, &ip_str), port_from, port_to); |
504 | 3 | kill_networking(temp); |
505 | | |
506 | 3 | if (error != nullptr) { |
507 | 1 | *error = 1; |
508 | 1 | } |
509 | | |
510 | 3 | return nullptr; |
511 | 2.00k | } |
512 | | |
513 | | Networking_Core *new_networking_no_udp(const Logger *log, const Memory *mem, const Network *ns) |
514 | 125 | { |
515 | | /* this is the easiest way to completely disable UDP without changing too much code. */ |
516 | 125 | Networking_Core *net = (Networking_Core *)mem_alloc(mem, sizeof(Networking_Core)); |
517 | | |
518 | 125 | if (net == nullptr) { |
519 | 1 | return nullptr; |
520 | 1 | } |
521 | | |
522 | 124 | net->ns = ns; |
523 | 124 | net->log = log; |
524 | 124 | net->mem = mem; |
525 | | |
526 | 124 | return net; |
527 | 125 | } |
528 | | |
529 | | /** Function to cleanup networking stuff (doesn't do much right now). */ |
530 | | void kill_networking(Networking_Core *net) |
531 | 2.08k | { |
532 | 2.08k | if (net == nullptr) { |
533 | 0 | return; |
534 | 0 | } |
535 | | |
536 | 2.08k | if (!net_family_is_unspec(net->family)) { |
537 | | /* Socket is initialized, so we close it. */ |
538 | 1.96k | kill_sock(net->ns, net->sock); |
539 | 1.96k | } |
540 | | |
541 | 2.08k | netprof_kill(net->mem, net->udp_net_profile); |
542 | 2.08k | mem_delete(net->mem, net); |
543 | 2.08k | } |
544 | | |
545 | | bool ip_equal(const IP *a, const IP *b) |
546 | 243k | { |
547 | 243k | if (a == nullptr || b == nullptr) { |
548 | 9 | return false; |
549 | 9 | } |
550 | | |
551 | | /* same family */ |
552 | 243k | if (a->family.value == b->family.value) { |
553 | 243k | if (net_family_is_ipv4(a->family) || net_family_is_tcp_ipv4(a->family)) { |
554 | 243k | return a->ip.v4.uint32 == b->ip.v4.uint32; |
555 | 243k | } |
556 | | |
557 | 10 | if (net_family_is_ipv6(a->family) || net_family_is_tcp_ipv6(a->family)) { |
558 | 6 | return a->ip.v6.uint64[0] == b->ip.v6.uint64[0] && |
559 | 6 | a->ip.v6.uint64[1] == b->ip.v6.uint64[1]; |
560 | 6 | } |
561 | | |
562 | 4 | return false; |
563 | 10 | } |
564 | | |
565 | | /* different family: check on the IPv6 one if it is the IPv4 one embedded */ |
566 | 14 | if (net_family_is_ipv4(a->family) && net_family_is_ipv6(b->family)) { |
567 | 6 | if (ipv6_ipv4_in_v6(&b->ip.v6)) { |
568 | 3 | return a->ip.v4.uint32 == b->ip.v6.uint32[3]; |
569 | 3 | } |
570 | 8 | } else if (net_family_is_ipv6(a->family) && net_family_is_ipv4(b->family)) { |
571 | 0 | if (ipv6_ipv4_in_v6(&a->ip.v6)) { |
572 | 0 | return a->ip.v6.uint32[3] == b->ip.v4.uint32; |
573 | 0 | } |
574 | 0 | } |
575 | | |
576 | 11 | return false; |
577 | 14 | } |
578 | | |
579 | | bool ipport_equal(const IP_Port *a, const IP_Port *b) |
580 | 19.3M | { |
581 | 19.3M | if (a == nullptr || b == nullptr) { |
582 | 0 | return false; |
583 | 0 | } |
584 | | |
585 | 19.3M | if (a->port == 0 || (a->port != b->port)) { |
586 | 19.0M | return false; |
587 | 19.0M | } |
588 | | |
589 | 231k | return ip_equal(&a->ip, &b->ip); |
590 | 19.3M | } |
591 | | |
592 | | static int ip4_cmp(const IP4 *_Nonnull a, const IP4 *_Nonnull b) |
593 | 139k | { |
594 | 139k | return cmp_uint(a->uint32, b->uint32); |
595 | 139k | } |
596 | | |
597 | | static int ip6_cmp(const IP6 *_Nonnull a, const IP6 *_Nonnull b) |
598 | 2 | { |
599 | 2 | const int res = cmp_uint(a->uint64[0], b->uint64[0]); |
600 | 2 | if (res != 0) { |
601 | 1 | return res; |
602 | 1 | } |
603 | 1 | return cmp_uint(a->uint64[1], b->uint64[1]); |
604 | 2 | } |
605 | | |
606 | | static int ip_cmp(const IP *_Nonnull a, const IP *_Nonnull b) |
607 | 141k | { |
608 | 141k | const int res = cmp_uint(a->family.value, b->family.value); |
609 | 141k | if (res != 0) { |
610 | 1.39k | return res; |
611 | 1.39k | } |
612 | 139k | switch (a->family.value) { |
613 | 1 | case TOX_AF_UNSPEC: |
614 | 1 | return 0; |
615 | 139k | case TOX_AF_INET: |
616 | 139k | case TCP_INET: |
617 | 139k | case TOX_TCP_INET: |
618 | 139k | return ip4_cmp(&a->ip.v4, &b->ip.v4); |
619 | 2 | case TOX_AF_INET6: |
620 | 2 | case TCP_INET6: |
621 | 2 | case TOX_TCP_INET6: |
622 | 2 | case TCP_SERVER_FAMILY: // these happen to be ipv6 according to TCP_server.c. |
623 | 2 | case TCP_CLIENT_FAMILY: |
624 | 2 | return ip6_cmp(&a->ip.v6, &b->ip.v6); |
625 | 139k | } |
626 | | // Invalid, we don't compare any further and consider them equal. |
627 | 1 | return 0; |
628 | 139k | } |
629 | | |
630 | | int ipport_cmp_handler(const void *a, const void *b, size_t size) |
631 | 141k | { |
632 | 141k | const IP_Port *ipp_a = (const IP_Port *)a; |
633 | 141k | const IP_Port *ipp_b = (const IP_Port *)b; |
634 | 141k | assert(size == sizeof(IP_Port)); |
635 | | |
636 | 141k | const int ip_res = ip_cmp(&ipp_a->ip, &ipp_b->ip); |
637 | 141k | if (ip_res != 0) { |
638 | 1.39k | return ip_res; |
639 | 1.39k | } |
640 | | |
641 | 139k | return cmp_uint(ipp_a->port, ipp_b->port); |
642 | 141k | } |
643 | | |
644 | | static const IP empty_ip = {{0}}; |
645 | | |
646 | | /** nulls out ip */ |
647 | | void ip_reset(IP *ip) |
648 | 73.2k | { |
649 | 73.2k | if (ip == nullptr) { |
650 | 0 | return; |
651 | 0 | } |
652 | | |
653 | 73.2k | *ip = empty_ip; |
654 | 73.2k | } |
655 | | |
656 | | static const IP_Port empty_ip_port = {{{0}}}; |
657 | | |
658 | | /** nulls out ip_port */ |
659 | | void ipport_reset(IP_Port *ipport) |
660 | 444k | { |
661 | 444k | if (ipport == nullptr) { |
662 | 0 | return; |
663 | 0 | } |
664 | | |
665 | 444k | *ipport = empty_ip_port; |
666 | 444k | } |
667 | | |
668 | | /** nulls out ip, sets family according to flag */ |
669 | | void ip_init(IP *ip, bool ipv6enabled) |
670 | 25.2k | { |
671 | 25.2k | if (ip == nullptr) { |
672 | 0 | return; |
673 | 0 | } |
674 | | |
675 | 25.2k | ip_reset(ip); |
676 | 25.2k | ip->family = ipv6enabled ? net_family_ipv6() : net_family_ipv4(); |
677 | 25.2k | } |
678 | | |
679 | | /** checks if ip is valid */ |
680 | | bool ip_isset(const IP *ip) |
681 | 1.60M | { |
682 | 1.60M | if (ip == nullptr) { |
683 | 0 | return false; |
684 | 0 | } |
685 | | |
686 | 1.60M | return !net_family_is_unspec(ip->family); |
687 | 1.60M | } |
688 | | |
689 | | /** checks if ip is valid */ |
690 | | bool ipport_isset(const IP_Port *ipport) |
691 | 290k | { |
692 | 290k | if (ipport == nullptr) { |
693 | 0 | return false; |
694 | 0 | } |
695 | | |
696 | 290k | if (ipport->port == 0) { |
697 | 58.8k | return false; |
698 | 58.8k | } |
699 | | |
700 | 231k | return ip_isset(&ipport->ip); |
701 | 290k | } |
702 | | |
703 | | /** copies an ip structure (careful about direction) */ |
704 | | void ip_copy(IP *target, const IP *source) |
705 | 2.02k | { |
706 | 2.02k | if (source == nullptr || target == nullptr) { |
707 | 0 | return; |
708 | 0 | } |
709 | | |
710 | 2.02k | *target = *source; |
711 | 2.02k | } |
712 | | |
713 | | /** copies an ip_port structure (careful about direction) */ |
714 | | void ipport_copy(IP_Port *target, const IP_Port *source) |
715 | 5.87k | { |
716 | 5.87k | if (source == nullptr || target == nullptr) { |
717 | 0 | return; |
718 | 0 | } |
719 | | |
720 | | // Write to a temporary object first, so that padding bytes are |
721 | | // uninitialised and msan can catch mistakes in downstream code. |
722 | 5.87k | IP_Port tmp; |
723 | 5.87k | tmp.ip.family = source->ip.family; |
724 | 5.87k | tmp.ip.ip = source->ip.ip; |
725 | 5.87k | tmp.port = source->port; |
726 | | |
727 | 5.87k | *target = tmp; |
728 | 5.87k | } |
729 | | |
730 | | /** @brief Packs an IP structure. |
731 | | * |
732 | | * It's the caller's responsibility to make sure `is_ipv4` tells the truth. This |
733 | | * function is an implementation detail of @ref bin_pack_ip_port. |
734 | | * |
735 | | * @param is_ipv4 whether this IP is an IP4 or IP6. |
736 | | * |
737 | | * @retval true on success. |
738 | | */ |
739 | | static bool bin_pack_ip(Bin_Pack *_Nonnull bp, const IP *_Nonnull ip, bool is_ipv4) |
740 | 1.03M | { |
741 | 1.03M | if (is_ipv4) { |
742 | 1.03M | return bin_pack_bin_b(bp, ip->ip.v4.uint8, SIZE_IP4); |
743 | 1.03M | } else { |
744 | 1.86k | return bin_pack_bin_b(bp, ip->ip.v6.uint8, SIZE_IP6); |
745 | 1.86k | } |
746 | 1.03M | } |
747 | | |
748 | | /** @brief Packs an IP_Port structure. |
749 | | * |
750 | | * @retval true on success. |
751 | | */ |
752 | | bool bin_pack_ip_port(Bin_Pack *bp, const Logger *logger, const IP_Port *ip_port) |
753 | 1.03M | { |
754 | 1.03M | bool is_ipv4; |
755 | 1.03M | uint8_t family; |
756 | | |
757 | 1.03M | if (net_family_is_ipv4(ip_port->ip.family)) { |
758 | | // TODO(irungentoo): use functions to convert endianness |
759 | 1.02M | is_ipv4 = true; |
760 | 1.02M | family = TOX_AF_INET; |
761 | 1.02M | } else if (net_family_is_tcp_ipv4(ip_port->ip.family)) { |
762 | 8.79k | is_ipv4 = true; |
763 | 8.79k | family = TOX_TCP_INET; |
764 | 8.79k | } else if (net_family_is_ipv6(ip_port->ip.family)) { |
765 | 1.24k | is_ipv4 = false; |
766 | 1.24k | family = TOX_AF_INET6; |
767 | 1.24k | } else if (net_family_is_tcp_ipv6(ip_port->ip.family)) { |
768 | 640 | is_ipv4 = false; |
769 | 640 | family = TOX_TCP_INET6; |
770 | 640 | } else { |
771 | 364 | Ip_Ntoa ip_str; |
772 | | // TODO(iphydf): Find out why we're trying to pack invalid IPs, stop |
773 | | // doing that, and turn this into an error. |
774 | 364 | LOGGER_TRACE(logger, "cannot pack invalid IP: %s", net_ip_ntoa(&ip_port->ip, &ip_str)); |
775 | 364 | return false; |
776 | 364 | } |
777 | | |
778 | 1.03M | return bin_pack_u08_b(bp, family) |
779 | 1.03M | && bin_pack_ip(bp, &ip_port->ip, is_ipv4) |
780 | 1.03M | && bin_pack_u16_b(bp, net_ntohs(ip_port->port)); |
781 | 1.03M | } |
782 | | |
783 | | static bool bin_pack_ip_port_handler(const void *_Nonnull obj, const Logger *_Nonnull logger, Bin_Pack *_Nonnull bp) |
784 | 158k | { |
785 | 158k | const IP_Port *ip_port = (const IP_Port *)obj; |
786 | 158k | return bin_pack_ip_port(bp, logger, ip_port); |
787 | 158k | } |
788 | | |
789 | | int pack_ip_port(const Logger *logger, uint8_t *data, uint16_t length, const IP_Port *ip_port) |
790 | 79.3k | { |
791 | 79.3k | const uint32_t size = bin_pack_obj_size(bin_pack_ip_port_handler, ip_port, logger); |
792 | | |
793 | 79.3k | if (size > length) { |
794 | 0 | return -1; |
795 | 0 | } |
796 | | |
797 | 79.3k | if (!bin_pack_obj(bin_pack_ip_port_handler, ip_port, logger, data, length)) { |
798 | 0 | return -1; |
799 | 0 | } |
800 | | |
801 | 79.3k | assert(size < INT_MAX); |
802 | 79.3k | return (int)size; |
803 | 79.3k | } |
804 | | |
805 | | int unpack_ip_port(IP_Port *ip_port, const uint8_t *data, uint16_t length, bool tcp_enabled) |
806 | 445k | { |
807 | 445k | if (data == nullptr || length == 0 || ip_port == nullptr) { |
808 | 3 | return -1; |
809 | 3 | } |
810 | | |
811 | 445k | bool is_ipv4; |
812 | 445k | Family host_family; |
813 | | |
814 | 445k | if (data[0] == TOX_AF_INET) { |
815 | 441k | is_ipv4 = true; |
816 | 441k | host_family = net_family_ipv4(); |
817 | 441k | } else if (data[0] == TOX_TCP_INET) { |
818 | 1.41k | if (!tcp_enabled) { |
819 | 177 | return -1; |
820 | 177 | } |
821 | | |
822 | 1.23k | is_ipv4 = true; |
823 | 1.23k | host_family = net_family_tcp_ipv4(); |
824 | 2.96k | } else if (data[0] == TOX_AF_INET6) { |
825 | 1.71k | is_ipv4 = false; |
826 | 1.71k | host_family = net_family_ipv6(); |
827 | 1.71k | } else if (data[0] == TOX_TCP_INET6) { |
828 | 466 | if (!tcp_enabled) { |
829 | 78 | return -1; |
830 | 78 | } |
831 | | |
832 | 388 | is_ipv4 = false; |
833 | 388 | host_family = net_family_tcp_ipv6(); |
834 | 775 | } else { |
835 | 775 | return -1; |
836 | 775 | } |
837 | | |
838 | 444k | ipport_reset(ip_port); |
839 | | |
840 | 444k | if (is_ipv4) { |
841 | 442k | const uint32_t size = 1 + SIZE_IP4 + sizeof(uint16_t); |
842 | | |
843 | 442k | if (size > length) { |
844 | 143 | return -1; |
845 | 143 | } |
846 | | |
847 | 442k | ip_port->ip.family = host_family; |
848 | 442k | memcpy(ip_port->ip.ip.v4.uint8, data + 1, SIZE_IP4); |
849 | 442k | memcpy(&ip_port->port, data + 1 + SIZE_IP4, sizeof(uint16_t)); |
850 | 442k | return size; |
851 | 442k | } else { |
852 | 2.11k | const uint32_t size = 1 + SIZE_IP6 + sizeof(uint16_t); |
853 | | |
854 | 2.11k | if (size > length) { |
855 | 205 | return -1; |
856 | 205 | } |
857 | | |
858 | 1.91k | ip_port->ip.family = host_family; |
859 | 1.91k | memcpy(ip_port->ip.ip.v6.uint8, data + 1, SIZE_IP6); |
860 | 1.91k | memcpy(&ip_port->port, data + 1 + SIZE_IP6, sizeof(uint16_t)); |
861 | 1.91k | return size; |
862 | 2.11k | } |
863 | 444k | } |
864 | | |
865 | | const char *net_ip_ntoa(const IP *ip, Ip_Ntoa *ip_str) |
866 | 1.60M | { |
867 | 1.60M | assert(ip_str != nullptr); |
868 | | |
869 | 1.60M | ip_str->ip_is_valid = false; |
870 | | |
871 | 1.60M | if (ip == nullptr) { |
872 | 0 | snprintf(ip_str->buf, sizeof(ip_str->buf), "(IP invalid: NULL)"); |
873 | 0 | ip_str->length = (uint16_t)strlen(ip_str->buf); |
874 | 0 | return ip_str->buf; |
875 | 0 | } |
876 | | |
877 | 1.60M | if (!ip_parse_addr(ip, ip_str->buf, sizeof(ip_str->buf))) { |
878 | 25.9k | snprintf(ip_str->buf, sizeof(ip_str->buf), "(IP invalid, family %u)", ip->family.value); |
879 | 25.9k | ip_str->length = (uint16_t)strlen(ip_str->buf); |
880 | 25.9k | return ip_str->buf; |
881 | 25.9k | } |
882 | | |
883 | | /* brute force protection against lacking termination */ |
884 | 1.57M | ip_str->buf[sizeof(ip_str->buf) - 1] = '\0'; |
885 | 1.57M | ip_str->length = (uint16_t)strlen(ip_str->buf); |
886 | 1.57M | ip_str->ip_is_valid = true; |
887 | | |
888 | 1.57M | return ip_str->buf; |
889 | 1.60M | } |
890 | | |
891 | | bool ip_parse_addr(const IP *ip, char *address, size_t length) |
892 | 1.60M | { |
893 | 1.60M | if (address == nullptr || ip == nullptr) { |
894 | 0 | return false; |
895 | 0 | } |
896 | | |
897 | 1.60M | if (net_family_is_ipv4(ip->family) || net_family_is_tcp_ipv4(ip->family)) { |
898 | 1.57M | return net_inet_ntop4(&ip->ip.v4, address, length) != nullptr; |
899 | 1.57M | } |
900 | | |
901 | 27.8k | if (net_family_is_ipv6(ip->family) || net_family_is_tcp_ipv6(ip->family)) { |
902 | 1.73k | return net_inet_ntop6(&ip->ip.v6, address, length) != nullptr; |
903 | 1.73k | } |
904 | | |
905 | 26.1k | return false; |
906 | 27.8k | } |
907 | | |
908 | | bool addr_parse_ip(const char *address, IP *to) |
909 | 28.6k | { |
910 | 28.6k | if (address == nullptr || to == nullptr) { |
911 | 0 | return false; |
912 | 0 | } |
913 | | |
914 | 28.6k | if (net_inet_pton4(address, &to->ip.v4) == 1) { |
915 | 28.6k | to->family = net_family_ipv4(); |
916 | 28.6k | return true; |
917 | 28.6k | } |
918 | | |
919 | 13 | if (net_inet_pton6(address, &to->ip.v6) == 1) { |
920 | 0 | to->family = net_family_ipv6(); |
921 | 0 | return true; |
922 | 0 | } |
923 | | |
924 | 13 | return false; |
925 | 13 | } |
926 | | |
927 | | /** addr_resolve return values */ |
928 | 0 | #define TOX_ADDR_RESOLVE_INET 1 |
929 | 0 | #define TOX_ADDR_RESOLVE_INET6 2 |
930 | | |
931 | | /** |
932 | | * Uses getaddrinfo to resolve an address into an IP address. |
933 | | * |
934 | | * Uses the first IPv4/IPv6 addresses returned by getaddrinfo. |
935 | | * |
936 | | * @param address a hostname (or something parseable to an IP address) |
937 | | * @param to to.family MUST be initialized, either set to a specific IP version |
938 | | * (TOX_AF_INET/TOX_AF_INET6) or to the unspecified TOX_AF_UNSPEC (0), if both |
939 | | * IP versions are acceptable |
940 | | * @param extra can be NULL and is only set in special circumstances, see returns |
941 | | * |
942 | | * Returns in `*to` a valid IPAny (v4/v6), |
943 | | * prefers v6 if `ip.family` was TOX_AF_UNSPEC and both available |
944 | | * Returns in `*extra` an IPv4 address, if family was TOX_AF_UNSPEC and `*to` is TOX_AF_INET6 |
945 | | * |
946 | | * @return false on failure, true on success. |
947 | | */ |
948 | | static bool addr_resolve(const Network *_Nonnull ns, const Memory *_Nonnull mem, const char *_Nonnull address, IP *_Nonnull to, IP *_Nullable extra) |
949 | 71 | { |
950 | 71 | #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION |
951 | 71 | if ((true)) { |
952 | 71 | return false; |
953 | 71 | } |
954 | 0 | #endif /* FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */ |
955 | | |
956 | 0 | if (address == nullptr || to == nullptr) { |
957 | 0 | return false; |
958 | 0 | } |
959 | | |
960 | 0 | const int family = to->family.value; |
961 | |
|
962 | 0 | IP_Port *addrs = nullptr; |
963 | 0 | const int rc = ns_getaddrinfo(ns, mem, address, family, 0, &addrs); |
964 | | |
965 | | // Lookup failed / empty. |
966 | 0 | if (rc <= 0) { |
967 | 0 | return false; |
968 | 0 | } |
969 | | |
970 | 0 | assert(addrs != nullptr); |
971 | | |
972 | 0 | IP ip4; |
973 | 0 | ip_init(&ip4, false); // ipv6enabled = false |
974 | 0 | IP ip6; |
975 | 0 | ip_init(&ip6, true); // ipv6enabled = true |
976 | |
|
977 | 0 | int result = 0; |
978 | 0 | bool done = false; |
979 | |
|
980 | 0 | for (int i = 0; i < rc && !done; ++i) { |
981 | 0 | if (net_family_is_ipv4(addrs[i].ip.family)) { |
982 | 0 | if (addrs[i].ip.family.value == to->family.value) { /* AF_INET requested, done */ |
983 | 0 | ip_copy(to, &addrs[i].ip); |
984 | 0 | result = TOX_ADDR_RESOLVE_INET; |
985 | 0 | done = true; |
986 | 0 | } else if ((result & TOX_ADDR_RESOLVE_INET) == 0) { /* AF_UNSPEC requested, store away */ |
987 | 0 | ip_copy(&ip4, &addrs[i].ip); |
988 | 0 | result |= TOX_ADDR_RESOLVE_INET; |
989 | 0 | } |
990 | 0 | } else if (net_family_is_ipv6(addrs[i].ip.family)) { |
991 | 0 | if (addrs[i].ip.family.value == to->family.value) { /* AF_INET6 requested, done */ |
992 | 0 | ip_copy(to, &addrs[i].ip); |
993 | 0 | result = TOX_ADDR_RESOLVE_INET6; |
994 | 0 | done = true; |
995 | 0 | } else if ((result & TOX_ADDR_RESOLVE_INET6) == 0) { /* AF_UNSPEC requested, store away */ |
996 | 0 | ip_copy(&ip6, &addrs[i].ip); |
997 | 0 | result |= TOX_ADDR_RESOLVE_INET6; |
998 | 0 | } |
999 | 0 | } |
1000 | 0 | } |
1001 | |
|
1002 | 0 | if (family == TOX_AF_UNSPEC) { |
1003 | 0 | if ((result & TOX_ADDR_RESOLVE_INET6) != 0) { |
1004 | 0 | ip_copy(to, &ip6); |
1005 | |
|
1006 | 0 | if ((result & TOX_ADDR_RESOLVE_INET) != 0 && (extra != nullptr)) { |
1007 | 0 | ip_copy(extra, &ip4); |
1008 | 0 | } |
1009 | 0 | } else if ((result & TOX_ADDR_RESOLVE_INET) != 0) { |
1010 | 0 | ip_copy(to, &ip4); |
1011 | 0 | } else { |
1012 | 0 | result = 0; |
1013 | 0 | } |
1014 | 0 | } |
1015 | |
|
1016 | 0 | ns_freeaddrinfo(ns, mem, addrs); |
1017 | 0 | return result != 0; |
1018 | 0 | } |
1019 | | |
1020 | | bool addr_resolve_or_parse_ip(const Network *ns, const Memory *mem, const char *address, IP *to, IP *extra, bool dns_enabled) |
1021 | 84 | { |
1022 | 84 | if (dns_enabled && addr_resolve(ns, mem, address, to, extra)) { |
1023 | 13 | return true; |
1024 | 13 | } |
1025 | | |
1026 | 71 | return addr_parse_ip(address, to); |
1027 | 84 | } |
1028 | | |
1029 | | const char *net_err_connect_to_string(Net_Err_Connect err) |
1030 | 0 | { |
1031 | 0 | switch (err) { |
1032 | 0 | case NET_ERR_CONNECT_OK: |
1033 | 0 | return "NET_ERR_CONNECT_OK"; |
1034 | 0 | case NET_ERR_CONNECT_INVALID_FAMILY: |
1035 | 0 | return "NET_ERR_CONNECT_INVALID_FAMILY"; |
1036 | 0 | case NET_ERR_CONNECT_FAILED: |
1037 | 0 | return "NET_ERR_CONNECT_FAILED"; |
1038 | 0 | } |
1039 | | |
1040 | 0 | return "<invalid Net_Err_Connect>"; |
1041 | 0 | } |
1042 | | |
1043 | | bool net_connect(const Network *ns, const Memory *mem, const Logger *log, Socket sock, const IP_Port *ip_port, Net_Err_Connect *err) |
1044 | 22 | { |
1045 | 22 | if (!(net_family_is_ipv4(ip_port->ip.family) || net_family_is_ipv6(ip_port->ip.family))) { |
1046 | 0 | Ip_Ntoa ip_str; |
1047 | 0 | LOGGER_ERROR(log, "cannot connect to %s:%d which is neither IPv4 nor IPv6", |
1048 | 0 | net_ip_ntoa(&ip_port->ip, &ip_str), net_ntohs(ip_port->port)); |
1049 | 0 | *err = NET_ERR_CONNECT_INVALID_FAMILY; |
1050 | 0 | return false; |
1051 | 0 | } |
1052 | | |
1053 | 22 | #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION |
1054 | 22 | if ((true)) { |
1055 | 22 | *err = NET_ERR_CONNECT_OK; |
1056 | 22 | return true; |
1057 | 22 | } |
1058 | 0 | #endif /* FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */ |
1059 | | |
1060 | 0 | Ip_Ntoa ip_str; |
1061 | 0 | LOGGER_DEBUG(log, "connecting socket %d to %s:%d", |
1062 | 0 | net_socket_to_native(sock), net_ip_ntoa(&ip_port->ip, &ip_str), net_ntohs(ip_port->port)); |
1063 | 0 | net_clear_error(); |
1064 | |
|
1065 | 0 | if (ns_connect(ns, sock, ip_port) == -1) { |
1066 | 0 | const int error = net_error(); |
1067 | | |
1068 | | // Non-blocking socket: "Operation in progress" means it's connecting. |
1069 | 0 | if (!net_should_ignore_connect_error(error)) { |
1070 | 0 | Net_Strerror error_str; |
1071 | 0 | LOGGER_WARNING(log, "failed to connect to %s:%d: %d (%s)", |
1072 | 0 | net_ip_ntoa(&ip_port->ip, &ip_str), net_ntohs(ip_port->port), error, net_strerror(error, &error_str)); |
1073 | 0 | *err = NET_ERR_CONNECT_FAILED; |
1074 | 0 | return false; |
1075 | 0 | } |
1076 | 0 | } |
1077 | | |
1078 | 0 | *err = NET_ERR_CONNECT_OK; |
1079 | 0 | return true; |
1080 | 0 | } |
1081 | | |
1082 | | int32_t net_getipport(const Network *ns, const Memory *mem, const char *node, IP_Port **res, int tox_type, bool dns_enabled) |
1083 | 28.5k | { |
1084 | 28.5k | assert(node != nullptr); |
1085 | | |
1086 | | // Try parsing as IP address first. |
1087 | 28.5k | IP_Port parsed = {{{0}}}; |
1088 | | // Initialise to nullptr. In error paths, at least we initialise the out |
1089 | | // parameter. |
1090 | 28.5k | *res = nullptr; |
1091 | | |
1092 | 28.5k | if (addr_parse_ip(node, &parsed.ip)) { |
1093 | 28.5k | IP_Port *tmp = (IP_Port *)mem_alloc(mem, sizeof(IP_Port)); |
1094 | | |
1095 | 28.5k | if (tmp == nullptr) { |
1096 | 13 | return -1; |
1097 | 13 | } |
1098 | | |
1099 | 28.5k | tmp[0] = parsed; |
1100 | 28.5k | *res = tmp; |
1101 | 28.5k | return 1; |
1102 | 28.5k | } |
1103 | | |
1104 | 26 | if (!dns_enabled) { |
1105 | 0 | return -1; |
1106 | 0 | } |
1107 | | |
1108 | | #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION |
1109 | 0 | if ((true)) { |
1110 | 0 | IP_Port *ip_port = (IP_Port *)mem_alloc(mem, sizeof(IP_Port)); |
1111 | 0 | if (ip_port == nullptr) { |
1112 | 0 | abort(); |
1113 | 0 | } |
1114 | 0 | ip_port->ip.ip.v4.uint32 = net_htonl(0x7F000003); // 127.0.0.3 |
1115 | 0 | ip_port->ip.family = net_family_ipv4(); |
1116 | |
|
1117 | 0 | *res = ip_port; |
1118 | 0 | return 1; |
1119 | 0 | } |
1120 | 0 | #endif /* FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */ |
1121 | | |
1122 | 26 | int type = tox_type; |
1123 | | // ugly |
1124 | 26 | if (tox_type == -1) { |
1125 | 0 | type = 0; |
1126 | 0 | } |
1127 | | |
1128 | | // It's not an IP address, so now we try doing a DNS lookup. |
1129 | 26 | IP_Port *addrs = nullptr; |
1130 | 26 | const int rc = ns_getaddrinfo(ns, mem, node, TOX_AF_UNSPEC, type, &addrs); |
1131 | | |
1132 | | // Lookup failed / empty. |
1133 | 26 | if (rc <= 0) { |
1134 | 0 | return -1; |
1135 | 0 | } |
1136 | | |
1137 | 26 | assert(addrs != nullptr); |
1138 | | |
1139 | 5 | *res = addrs; |
1140 | 5 | return rc; |
1141 | 26 | } Line | Count | Source | 1083 | 60 | { | 1084 | 60 | assert(node != nullptr); | 1085 | | | 1086 | | // Try parsing as IP address first. | 1087 | 60 | IP_Port parsed = {{{0}}}; | 1088 | | // Initialise to nullptr. In error paths, at least we initialise the out | 1089 | | // parameter. | 1090 | 60 | *res = nullptr; | 1091 | | | 1092 | 60 | if (addr_parse_ip(node, &parsed.ip)) { | 1093 | 60 | IP_Port *tmp = (IP_Port *)mem_alloc(mem, sizeof(IP_Port)); | 1094 | | | 1095 | 60 | if (tmp == nullptr) { | 1096 | 13 | return -1; | 1097 | 13 | } | 1098 | | | 1099 | 47 | tmp[0] = parsed; | 1100 | 47 | *res = tmp; | 1101 | 47 | return 1; | 1102 | 60 | } | 1103 | | | 1104 | 0 | if (!dns_enabled) { | 1105 | 0 | return -1; | 1106 | 0 | } | 1107 | | | 1108 | 0 | #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION | 1109 | 0 | if ((true)) { | 1110 | 0 | IP_Port *ip_port = (IP_Port *)mem_alloc(mem, sizeof(IP_Port)); | 1111 | 0 | if (ip_port == nullptr) { | 1112 | 0 | abort(); | 1113 | 0 | } | 1114 | 0 | ip_port->ip.ip.v4.uint32 = net_htonl(0x7F000003); // 127.0.0.3 | 1115 | 0 | ip_port->ip.family = net_family_ipv4(); | 1116 | |
| 1117 | 0 | *res = ip_port; | 1118 | 0 | return 1; | 1119 | 0 | } | 1120 | 0 | #endif /* FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */ | 1121 | | | 1122 | 0 | int type = tox_type; | 1123 | | // ugly | 1124 | 0 | if (tox_type == -1) { | 1125 | 0 | type = 0; | 1126 | 0 | } | 1127 | | | 1128 | | // It's not an IP address, so now we try doing a DNS lookup. | 1129 | 0 | IP_Port *addrs = nullptr; | 1130 | 0 | const int rc = ns_getaddrinfo(ns, mem, node, TOX_AF_UNSPEC, type, &addrs); | 1131 | | | 1132 | | // Lookup failed / empty. | 1133 | 0 | if (rc <= 0) { | 1134 | 0 | return -1; | 1135 | 0 | } | 1136 | | | 1137 | 0 | assert(addrs != nullptr); | 1138 | | | 1139 | 0 | *res = addrs; | 1140 | 0 | return rc; | 1141 | 0 | } |
Line | Count | Source | 1083 | 28.5k | { | 1084 | 28.5k | assert(node != nullptr); | 1085 | | | 1086 | | // Try parsing as IP address first. | 1087 | 28.5k | IP_Port parsed = {{{0}}}; | 1088 | | // Initialise to nullptr. In error paths, at least we initialise the out | 1089 | | // parameter. | 1090 | 28.5k | *res = nullptr; | 1091 | | | 1092 | 28.5k | if (addr_parse_ip(node, &parsed.ip)) { | 1093 | 28.4k | IP_Port *tmp = (IP_Port *)mem_alloc(mem, sizeof(IP_Port)); | 1094 | | | 1095 | 28.4k | if (tmp == nullptr) { | 1096 | 0 | return -1; | 1097 | 0 | } | 1098 | | | 1099 | 28.4k | tmp[0] = parsed; | 1100 | 28.4k | *res = tmp; | 1101 | 28.4k | return 1; | 1102 | 28.4k | } | 1103 | | | 1104 | 26 | if (!dns_enabled) { | 1105 | 0 | return -1; | 1106 | 0 | } | 1107 | | | 1108 | | #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION | 1109 | | if ((true)) { | 1110 | | IP_Port *ip_port = (IP_Port *)mem_alloc(mem, sizeof(IP_Port)); | 1111 | | if (ip_port == nullptr) { | 1112 | | abort(); | 1113 | | } | 1114 | | ip_port->ip.ip.v4.uint32 = net_htonl(0x7F000003); // 127.0.0.3 | 1115 | | ip_port->ip.family = net_family_ipv4(); | 1116 | | | 1117 | | *res = ip_port; | 1118 | | return 1; | 1119 | | } | 1120 | | #endif /* FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */ | 1121 | | | 1122 | 26 | int type = tox_type; | 1123 | | // ugly | 1124 | 26 | if (tox_type == -1) { | 1125 | 0 | type = 0; | 1126 | 0 | } | 1127 | | | 1128 | | // It's not an IP address, so now we try doing a DNS lookup. | 1129 | 26 | IP_Port *addrs = nullptr; | 1130 | 26 | const int rc = ns_getaddrinfo(ns, mem, node, TOX_AF_UNSPEC, type, &addrs); | 1131 | | | 1132 | | // Lookup failed / empty. | 1133 | 26 | if (rc <= 0) { | 1134 | 0 | return -1; | 1135 | 0 | } | 1136 | | | 1137 | 26 | assert(addrs != nullptr); | 1138 | | | 1139 | 5 | *res = addrs; | 1140 | 5 | return rc; | 1141 | 26 | } |
|
1142 | | |
1143 | | void net_freeipport(const Memory *mem, IP_Port *ip_ports) |
1144 | 28.5k | { |
1145 | 28.5k | mem_delete(mem, ip_ports); |
1146 | 28.5k | } |
1147 | | |
1148 | | bool bind_to_port(const Network *ns, Socket sock, Family family, uint16_t port) |
1149 | 36 | { |
1150 | 36 | IP_Port addr; |
1151 | 36 | ip_init(&addr.ip, net_family_is_ipv6(family)); |
1152 | 36 | addr.ip.family = family; |
1153 | | |
1154 | 36 | if (net_family_is_ipv4(family)) { |
1155 | 19 | addr.ip.ip.v4.uint32 = 0; |
1156 | 19 | } else { |
1157 | 17 | memset(addr.ip.ip.v6.uint8, 0, 16); |
1158 | 17 | } |
1159 | | |
1160 | 36 | addr.port = net_htons(port); |
1161 | | |
1162 | 36 | return ns_bind(ns, sock, &addr) == 0; |
1163 | 36 | } |
1164 | | |
1165 | | Socket net_socket(const Network *ns, Family domain, int type, int protocol) |
1166 | 2.18k | { |
1167 | 2.18k | return ns_socket(ns, domain.value, type, protocol); |
1168 | 2.18k | } |
1169 | | |
1170 | | uint16_t net_socket_data_recv_buffer(const Network *ns, Socket sock) |
1171 | 77.1k | { |
1172 | 77.1k | const int count = ns_recvbuf(ns, sock); |
1173 | 77.1k | return (uint16_t)max_s32(0, min_s32(count, UINT16_MAX)); |
1174 | 77.1k | } |
1175 | | |
1176 | | bool ipv6_ipv4_in_v6(const IP6 *a) |
1177 | 113k | { |
1178 | 113k | return a->uint64[0] == 0 && a->uint32[2] == net_htonl(0xffff); |
1179 | 113k | } |
1180 | | |
1181 | | const Net_Profile *net_get_net_profile(const Networking_Core *net) |
1182 | 2 | { |
1183 | 2 | if (net == nullptr) { |
1184 | 0 | return nullptr; |
1185 | 0 | } |
1186 | | |
1187 | 2 | return net->udp_net_profile; |
1188 | 2 | } |