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