/work/toxcore/LAN_discovery.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 © 2013 Tox project. |
4 | | */ |
5 | | |
6 | | /** |
7 | | * LAN discovery implementation. |
8 | | */ |
9 | | #include "LAN_discovery.h" |
10 | | |
11 | | #if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) |
12 | | // The mingw32/64 Windows library warns about including winsock2.h after |
13 | | // windows.h even though with the above it's a valid thing to do. So, to make |
14 | | // mingw32 headers happy, we include winsock2.h first. |
15 | | #include <winsock2.h> |
16 | | |
17 | | #include <windows.h> |
18 | | #include <ws2tcpip.h> |
19 | | |
20 | | #include <iphlpapi.h> |
21 | | #endif /* WIN32 */ |
22 | | |
23 | | #if defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__) |
24 | | #include <netinet/in.h> |
25 | | #include <sys/ioctl.h> |
26 | | #include <sys/socket.h> |
27 | | #include <sys/types.h> |
28 | | #include <unistd.h> |
29 | | #endif /* Linux/BSD */ |
30 | | |
31 | | #ifdef __linux__ |
32 | | #include <linux/if.h> |
33 | | #endif /* Linux */ |
34 | | |
35 | | #if defined(__FreeBSD__) || defined(__DragonFly__) |
36 | | #include <net/if.h> |
37 | | #endif /* BSD */ |
38 | | |
39 | | #include "attributes.h" |
40 | | #include "ccompat.h" |
41 | | #include "crypto_core.h" |
42 | | #include "mem.h" |
43 | | #include "network.h" |
44 | | |
45 | 15 | #define MAX_INTERFACES 16 |
46 | | |
47 | | struct Broadcast_Info { |
48 | | const Memory *_Nonnull mem; |
49 | | |
50 | | uint32_t count; |
51 | | IP ips[MAX_INTERFACES]; |
52 | | }; |
53 | | |
54 | | #if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) |
55 | | |
56 | | static Broadcast_Info *_Nullable fetch_broadcast_info(const Memory *_Nonnull mem, const Network *_Nonnull ns) |
57 | | { |
58 | | Broadcast_Info *broadcast = (Broadcast_Info *)mem_alloc(mem, sizeof(Broadcast_Info)); |
59 | | |
60 | | if (broadcast == nullptr) { |
61 | | return nullptr; |
62 | | } |
63 | | |
64 | | broadcast->mem = mem; |
65 | | |
66 | | IP_ADAPTER_INFO *adapter_info = (IP_ADAPTER_INFO *)mem_balloc(mem, sizeof(IP_ADAPTER_INFO)); |
67 | | |
68 | | if (adapter_info == nullptr) { |
69 | | mem_delete(mem, broadcast); |
70 | | return nullptr; |
71 | | } |
72 | | |
73 | | unsigned long out_buf_len = sizeof(IP_ADAPTER_INFO); |
74 | | |
75 | | if (GetAdaptersInfo(adapter_info, &out_buf_len) == ERROR_BUFFER_OVERFLOW) { |
76 | | mem_delete(mem, adapter_info); |
77 | | IP_ADAPTER_INFO *new_adapter_info = (IP_ADAPTER_INFO *)mem_balloc(mem, out_buf_len); |
78 | | |
79 | | if (new_adapter_info == nullptr) { |
80 | | mem_delete(mem, broadcast); |
81 | | return nullptr; |
82 | | } |
83 | | |
84 | | adapter_info = new_adapter_info; |
85 | | } |
86 | | |
87 | | const int ret = GetAdaptersInfo(adapter_info, &out_buf_len); |
88 | | |
89 | | if (ret == NO_ERROR) { |
90 | | IP_ADAPTER_INFO *adapter = adapter_info; |
91 | | |
92 | | while (adapter != nullptr) { |
93 | | IP if_ip = {0}; |
94 | | IP subnet_mask = {0}; |
95 | | |
96 | | if (addr_parse_ip(adapter->IpAddressList.IpMask.String, &subnet_mask) |
97 | | && addr_parse_ip(adapter->IpAddressList.IpAddress.String, &if_ip)) { |
98 | | if (net_family_is_ipv4(if_ip.family) && net_family_is_ipv4(subnet_mask.family)) { |
99 | | if (if_ip.ip.v4.uint32 != 0 && subnet_mask.ip.v4.uint32 != 0) { |
100 | | IP *ip = &broadcast->ips[broadcast->count]; |
101 | | ip->family = net_family_ipv4(); |
102 | | ip->ip.v4.uint32 = if_ip.ip.v4.uint32 | ~subnet_mask.ip.v4.uint32; |
103 | | ++broadcast->count; |
104 | | |
105 | | if (broadcast->count >= MAX_INTERFACES) { |
106 | | break; |
107 | | } |
108 | | } |
109 | | } |
110 | | } |
111 | | |
112 | | adapter = adapter->Next; |
113 | | } |
114 | | } |
115 | | |
116 | | if (adapter_info != nullptr) { |
117 | | mem_delete(mem, adapter_info); |
118 | | } |
119 | | |
120 | | return broadcast; |
121 | | } |
122 | | |
123 | | #elif !defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) && (defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__)) |
124 | | |
125 | | static bool ip4_is_local(const IP4 *_Nonnull ip4); |
126 | | |
127 | | static Broadcast_Info *_Nullable fetch_broadcast_info(const Memory *_Nonnull mem, const Network *_Nonnull ns) |
128 | 39 | { |
129 | 39 | Broadcast_Info *broadcast = (Broadcast_Info *)mem_alloc(mem, sizeof(Broadcast_Info)); |
130 | | |
131 | 39 | if (broadcast == nullptr) { |
132 | 1 | return nullptr; |
133 | 1 | } |
134 | | |
135 | 38 | broadcast->mem = mem; |
136 | | |
137 | | /* Not sure how many platforms this will run on, |
138 | | * so it's wrapped in `__linux__` for now. |
139 | | * Definitely won't work like this on Windows... |
140 | | */ |
141 | 38 | const Socket sock = net_socket(ns, net_family_ipv4(), TOX_SOCK_STREAM, 0); |
142 | | |
143 | 38 | if (!sock_valid(sock)) { |
144 | 1 | mem_delete(mem, broadcast); |
145 | 1 | return nullptr; |
146 | 1 | } |
147 | | |
148 | | /* Configure ifconf for the ioctl call. */ |
149 | 37 | struct ifreq i_faces[MAX_INTERFACES] = {{{0}}}; |
150 | | |
151 | 37 | struct ifconf ifc; |
152 | 37 | ifc.ifc_buf = (char *)i_faces; |
153 | 37 | ifc.ifc_len = sizeof(i_faces); |
154 | | |
155 | 37 | if (ioctl(net_socket_to_native(sock), SIOCGIFCONF, &ifc) < 0) { |
156 | 29 | kill_sock(ns, sock); |
157 | 29 | mem_delete(mem, broadcast); |
158 | 29 | return nullptr; |
159 | 29 | } |
160 | | |
161 | | /* `ifc.ifc_len` is set by the `ioctl()` to the actual length used. |
162 | | * On usage of the complete array the call should be repeated with |
163 | | * a larger array, not done (640kB and 16 interfaces shall be |
164 | | * enough, for everybody!) |
165 | | */ |
166 | 8 | const int n = ifc.ifc_len / sizeof(struct ifreq); |
167 | | |
168 | 24 | for (int i = 0; i < n; ++i) { |
169 | | /* there are interfaces with are incapable of broadcast |
170 | | * on Linux, `lo` has no broadcast address, but this function returns `>=0` */ |
171 | 16 | if (ioctl(net_socket_to_native(sock), SIOCGIFBRDADDR, &i_faces[i]) < 0) { |
172 | 1 | continue; |
173 | 1 | } |
174 | | |
175 | | /* moot check: only AF_INET returned (backwards compat.) */ |
176 | 15 | if (i_faces[i].ifr_broadaddr.sa_family != AF_INET) { |
177 | 0 | continue; |
178 | 0 | } |
179 | | |
180 | 15 | const struct sockaddr_in *broadaddr4 = (const struct sockaddr_in *)(void *)&i_faces[i].ifr_broadaddr; |
181 | | |
182 | 15 | if (broadcast->count >= MAX_INTERFACES) { |
183 | 0 | break; |
184 | 0 | } |
185 | | |
186 | 15 | IP *ip = &broadcast->ips[broadcast->count]; |
187 | 15 | ip->family = net_family_ipv4(); |
188 | 15 | ip->ip.v4.uint32 = broadaddr4->sin_addr.s_addr; |
189 | | |
190 | | // if no broadcast address |
191 | 15 | if (ip->ip.v4.uint32 == 0) { |
192 | 6 | if (ioctl(net_socket_to_native(sock), SIOCGIFADDR, &i_faces[i]) < 0) { |
193 | 0 | continue; |
194 | 0 | } |
195 | | |
196 | 6 | const struct sockaddr_in *addr4 = (const struct sockaddr_in *)(void *)&i_faces[i].ifr_addr; |
197 | | |
198 | | |
199 | 6 | IP4 ip4_staging; |
200 | 6 | ip4_staging.uint32 = addr4->sin_addr.s_addr; |
201 | | |
202 | 6 | if (ip4_is_local(&ip4_staging)) { |
203 | | // this is 127.x.x.x |
204 | 6 | ip->ip.v4.uint32 = ip4_staging.uint32; |
205 | 6 | } else { |
206 | | // give up. |
207 | 0 | continue; |
208 | 0 | } |
209 | 6 | } |
210 | | |
211 | 15 | ++broadcast->count; |
212 | 15 | } |
213 | | |
214 | 8 | kill_sock(ns, sock); |
215 | | |
216 | 8 | return broadcast; |
217 | 37 | } |
218 | | |
219 | | #else // TODO(irungentoo): Other platforms? |
220 | | |
221 | | static Broadcast_Info *_Nullable fetch_broadcast_info(const Memory *_Nonnull mem, const Network *_Nonnull ns) |
222 | 1.44k | { |
223 | 1.44k | Broadcast_Info *broadcast = (Broadcast_Info *)mem_alloc(mem, sizeof(Broadcast_Info)); |
224 | | |
225 | 1.44k | if (broadcast == nullptr) { |
226 | 13 | return nullptr; |
227 | 13 | } |
228 | | |
229 | 1.43k | broadcast->mem = mem; |
230 | | |
231 | 1.43k | return broadcast; |
232 | 1.44k | } |
233 | | |
234 | | #endif /* platforms */ |
235 | | |
236 | | /** @brief Send packet to all IPv4 broadcast addresses |
237 | | * |
238 | | * @retval true if sent to at least one broadcast target. |
239 | | * @retval false on failure to find any valid broadcast target. |
240 | | */ |
241 | | static bool send_broadcasts(const Networking_Core *_Nonnull net, const Broadcast_Info *_Nonnull broadcast, uint16_t port, const uint8_t *_Nonnull data, uint16_t length) |
242 | 198 | { |
243 | 198 | if (broadcast->count == 0) { |
244 | 77 | return false; |
245 | 77 | } |
246 | | |
247 | 363 | for (uint32_t i = 0; i < broadcast->count; ++i) { |
248 | 242 | IP_Port ip_port; |
249 | 242 | ip_port.ip = broadcast->ips[i]; |
250 | 242 | ip_port.port = port; |
251 | 242 | sendpacket(net, &ip_port, data, length); |
252 | 242 | } |
253 | | |
254 | 121 | return true; |
255 | 198 | } |
256 | | |
257 | | /** Return the broadcast ip. */ |
258 | | static IP broadcast_ip(Family family_socket, Family family_broadcast) |
259 | 330 | { |
260 | 330 | IP ip; |
261 | 330 | ip_reset(&ip); |
262 | | |
263 | 330 | if (net_family_is_ipv6(family_socket)) { |
264 | 264 | if (net_family_is_ipv6(family_broadcast)) { |
265 | 132 | ip.family = net_family_ipv6(); |
266 | | /* `FF02::1` is - according to RFC 4291 - multicast all-nodes link-local */ |
267 | | /* `FE80::*:` MUST be exact, for that we would need to look over all |
268 | | * interfaces and check in which status they are */ |
269 | 132 | ip.ip.v6.uint8[0] = 0xFF; |
270 | 132 | ip.ip.v6.uint8[1] = 0x02; |
271 | 132 | ip.ip.v6.uint8[15] = 0x01; |
272 | 132 | } else if (net_family_is_ipv4(family_broadcast)) { |
273 | 132 | ip.family = net_family_ipv6(); |
274 | 132 | ip.ip.v6 = get_ip6_broadcast(); |
275 | 132 | } |
276 | 264 | } else if (net_family_is_ipv4(family_socket) && net_family_is_ipv4(family_broadcast)) { |
277 | 22 | ip.family = net_family_ipv4(); |
278 | 22 | ip.ip.v4 = get_ip4_broadcast(); |
279 | 22 | } |
280 | | |
281 | 330 | return ip; |
282 | 330 | } |
283 | | |
284 | | static bool ip4_is_local(const IP4 *ip4) |
285 | 3.28M | { |
286 | | /* Loopback. */ |
287 | 3.28M | return ip4->uint8[0] == 127; |
288 | 3.28M | } |
289 | | |
290 | | /** |
291 | | * Is IP a local ip or not. |
292 | | */ |
293 | | bool ip_is_local(const IP *ip) |
294 | 3.35M | { |
295 | 3.35M | if (net_family_is_ipv4(ip->family)) { |
296 | 3.28M | return ip4_is_local(&ip->ip.v4); |
297 | 3.28M | } |
298 | | |
299 | | /* embedded IPv4-in-IPv6 */ |
300 | 73.5k | if (ipv6_ipv4_in_v6(&ip->ip.v6)) { |
301 | 0 | IP4 ip4; |
302 | 0 | ip4.uint32 = ip->ip.v6.uint32[3]; |
303 | 0 | return ip4_is_local(&ip4); |
304 | 0 | } |
305 | | |
306 | | /* localhost in IPv6 (::1) */ |
307 | 73.5k | return ip->ip.v6.uint64[0] == 0 && ip->ip.v6.uint32[2] == 0 && ip->ip.v6.uint32[3] == net_htonl(1); |
308 | 73.5k | } |
309 | | |
310 | | static bool ip4_is_lan(const IP4 *_Nonnull ip4) |
311 | 265k | { |
312 | | /* 10.0.0.0 to 10.255.255.255 range. */ |
313 | 265k | if (ip4->uint8[0] == 10) { |
314 | 84 | return true; |
315 | 84 | } |
316 | | |
317 | | /* 172.16.0.0 to 172.31.255.255 range. */ |
318 | 265k | if (ip4->uint8[0] == 172 && ip4->uint8[1] >= 16 && ip4->uint8[1] <= 31) { |
319 | 23.2k | return true; |
320 | 23.2k | } |
321 | | |
322 | | /* 192.168.0.0 to 192.168.255.255 range. */ |
323 | 242k | if (ip4->uint8[0] == 192 && ip4->uint8[1] == 168) { |
324 | 0 | return true; |
325 | 0 | } |
326 | | |
327 | | /* 169.254.1.0 to 169.254.254.255 range. */ |
328 | 242k | if (ip4->uint8[0] == 169 && ip4->uint8[1] == 254 && ip4->uint8[2] != 0 |
329 | 242k | && ip4->uint8[2] != 255) { |
330 | 0 | return true; |
331 | 0 | } |
332 | | |
333 | | /* RFC 6598: 100.64.0.0 to 100.127.255.255 (100.64.0.0/10) |
334 | | * (shared address space to stack another layer of NAT) */ |
335 | 242k | return (ip4->uint8[0] == 100) && ((ip4->uint8[1] & 0xC0) == 0x40); |
336 | 242k | } |
337 | | |
338 | | bool ip_is_lan(const IP *ip) |
339 | 3.35M | { |
340 | 3.35M | if (ip_is_local(ip)) { |
341 | 3.01M | return true; |
342 | 3.01M | } |
343 | | |
344 | 339k | if (net_family_is_ipv4(ip->family)) { |
345 | 265k | return ip4_is_lan(&ip->ip.v4); |
346 | 265k | } |
347 | | |
348 | 73.3k | if (net_family_is_ipv6(ip->family)) { |
349 | | /* autogenerated for each interface: `FE80::*` (up to `FEBF::*`) |
350 | | * `FF02::1` is - according to RFC 4291 - multicast all-nodes link-local */ |
351 | 0 | if (((ip->ip.v6.uint8[0] == 0xFF) && (ip->ip.v6.uint8[1] < 3) && (ip->ip.v6.uint8[15] == 1)) || |
352 | 0 | ((ip->ip.v6.uint8[0] == 0xFE) && ((ip->ip.v6.uint8[1] & 0xC0) == 0x80))) { |
353 | 0 | return true; |
354 | 0 | } |
355 | | |
356 | | /* embedded IPv4-in-IPv6 */ |
357 | 0 | if (ipv6_ipv4_in_v6(&ip->ip.v6)) { |
358 | 0 | IP4 ip4; |
359 | 0 | ip4.uint32 = ip->ip.v6.uint32[3]; |
360 | 0 | return ip4_is_lan(&ip4); |
361 | 0 | } |
362 | 0 | } |
363 | | |
364 | 73.3k | return false; |
365 | 73.3k | } |
366 | | |
367 | | bool lan_discovery_send(const Networking_Core *net, const Broadcast_Info *broadcast, const uint8_t *dht_pk, |
368 | | uint16_t port) |
369 | 198 | { |
370 | 198 | if (broadcast == nullptr) { |
371 | 0 | return false; |
372 | 0 | } |
373 | | |
374 | 198 | uint8_t data[CRYPTO_PUBLIC_KEY_SIZE + 1]; |
375 | 198 | data[0] = NET_PACKET_LAN_DISCOVERY; |
376 | 198 | pk_copy(data + 1, dht_pk); |
377 | | |
378 | 198 | send_broadcasts(net, broadcast, port, data, 1 + CRYPTO_PUBLIC_KEY_SIZE); |
379 | | |
380 | 198 | bool res = false; |
381 | 198 | IP_Port ip_port; |
382 | 198 | ip_port.port = port; |
383 | | |
384 | | /* IPv6 multicast */ |
385 | 198 | if (net_family_is_ipv6(net_family(net))) { |
386 | 132 | ip_port.ip = broadcast_ip(net_family_ipv6(), net_family_ipv6()); |
387 | | |
388 | 132 | if (ip_isset(&ip_port.ip) && sendpacket(net, &ip_port, data, 1 + CRYPTO_PUBLIC_KEY_SIZE) > 0) { |
389 | 33 | res = true; |
390 | 33 | } |
391 | 132 | } |
392 | | |
393 | | /* IPv4 broadcast (has to be IPv4-in-IPv6 mapping if socket is IPv6 */ |
394 | 198 | ip_port.ip = broadcast_ip(net_family(net), net_family_ipv4()); |
395 | | |
396 | 198 | if (ip_isset(&ip_port.ip) && sendpacket(net, &ip_port, data, 1 + CRYPTO_PUBLIC_KEY_SIZE) > 0) { |
397 | 154 | res = true; |
398 | 154 | } |
399 | | |
400 | 198 | return res; |
401 | 198 | } |
402 | | |
403 | | Broadcast_Info *lan_discovery_init(const Memory *mem, const Network *ns) |
404 | 1.48k | { |
405 | 1.48k | return fetch_broadcast_info(mem, ns); |
406 | 1.48k | } |
407 | | |
408 | | void lan_discovery_kill(Broadcast_Info *broadcast) |
409 | 1.83k | { |
410 | 1.83k | if (broadcast == nullptr) { |
411 | 396 | return; |
412 | 396 | } |
413 | | |
414 | 1.44k | mem_delete(broadcast->mem, broadcast); |
415 | 1.44k | } |