1 /* MIT License
2 *
3 * Copyright (c) 2023 Brad House
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to deal
7 * in the Software without restriction, including without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
14 * Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 *
24 * SPDX-License-Identifier: MIT
25 */
26 #include "ares_setup.h"
27
28
29 #ifdef USE_WINSOCK
30 # include <winsock2.h>
31 # include <ws2tcpip.h>
32 # if defined(HAVE_IPHLPAPI_H)
33 # include <iphlpapi.h>
34 # endif
35 # if defined(HAVE_NETIOAPI_H)
36 # include <netioapi.h>
37 # endif
38 #endif
39
40 #ifdef HAVE_SYS_TYPES_H
41 # include <sys/types.h>
42 #endif
43 #ifdef HAVE_SYS_SOCKET_H
44 # include <sys/socket.h>
45 #endif
46 #ifdef HAVE_NET_IF_H
47 # include <net/if.h>
48 #endif
49 #ifdef HAVE_IFADDRS_H
50 # include <ifaddrs.h>
51 #endif
52 #ifdef HAVE_SYS_IOCTL_H
53 # include <sys/ioctl.h>
54 #endif
55 #ifdef HAVE_NETINET_IN_H
56 # include <netinet/in.h>
57 #endif
58
59 #include "ares.h"
60 #include "ares_private.h"
61
62 static ares_status_t ares__iface_ips_enumerate(ares__iface_ips_t *ips,
63 const char *name);
64
65 typedef struct {
66 char *name;
67 struct ares_addr addr;
68 unsigned char netmask;
69 unsigned int ll_scope;
70 ares__iface_ip_flags_t flags;
71 } ares__iface_ip_t;
72
73 struct ares__iface_ips {
74 ares__iface_ip_t *ips;
75 size_t cnt;
76 size_t alloc_size;
77 ares__iface_ip_flags_t enum_flags;
78 };
79
ares__iface_ips_alloc(ares__iface_ip_flags_t flags)80 static ares__iface_ips_t *ares__iface_ips_alloc(ares__iface_ip_flags_t flags)
81 {
82 ares__iface_ips_t *ips = ares_malloc_zero(sizeof(*ips));
83 if (ips == NULL) {
84 return NULL;
85 }
86
87 /* Prealloc 4 entries */
88 ips->alloc_size = 4;
89 ips->ips = ares_malloc_zero(ips->alloc_size * sizeof(*ips->ips));
90 if (ips->ips == NULL) {
91 ares_free(ips);
92 return NULL;
93 }
94 ips->enum_flags = flags;
95 return ips;
96 }
97
ares__iface_ip_destroy(ares__iface_ip_t *ip)98 static void ares__iface_ip_destroy(ares__iface_ip_t *ip)
99 {
100 if (ip == NULL) {
101 return;
102 }
103 ares_free(ip->name);
104 memset(ip, 0, sizeof(*ip));
105 }
106
ares__iface_ips_destroy(ares__iface_ips_t *ips)107 void ares__iface_ips_destroy(ares__iface_ips_t *ips)
108 {
109 size_t i;
110
111 if (ips == NULL) {
112 return;
113 }
114
115 for (i = 0; i < ips->cnt; i++) {
116 ares__iface_ip_destroy(&ips->ips[i]);
117 }
118 ares_free(ips->ips);
119 ares_free(ips);
120 }
121
ares__iface_ips(ares__iface_ips_t **ips, ares__iface_ip_flags_t flags, const char *name)122 ares_status_t ares__iface_ips(ares__iface_ips_t **ips,
123 ares__iface_ip_flags_t flags, const char *name)
124 {
125 ares_status_t status;
126
127 if (ips == NULL) {
128 return ARES_EFORMERR;
129 }
130
131 *ips = ares__iface_ips_alloc(flags);
132 if (*ips == NULL) {
133 return ARES_ENOMEM;
134 }
135
136 status = ares__iface_ips_enumerate(*ips, name);
137 if (status != ARES_SUCCESS) {
138 ares__iface_ips_destroy(*ips);
139 *ips = NULL;
140 return status;
141 }
142
143 return ARES_SUCCESS;
144 }
145
146 static ares_status_t
ares__iface_ips_add(ares__iface_ips_t *ips, ares__iface_ip_flags_t flags, const char *name, const struct ares_addr *addr, unsigned char netmask, unsigned int ll_scope)147 ares__iface_ips_add(ares__iface_ips_t *ips, ares__iface_ip_flags_t flags,
148 const char *name, const struct ares_addr *addr,
149 unsigned char netmask, unsigned int ll_scope)
150 {
151 size_t idx;
152
153 if (ips == NULL || name == NULL || addr == NULL) {
154 return ARES_EFORMERR;
155 }
156
157 /* Don't want loopback */
158 if (flags & ARES_IFACE_IP_LOOPBACK &&
159 !(ips->enum_flags & ARES_IFACE_IP_LOOPBACK)) {
160 return ARES_SUCCESS;
161 }
162
163 /* Don't want offline */
164 if (flags & ARES_IFACE_IP_OFFLINE &&
165 !(ips->enum_flags & ARES_IFACE_IP_OFFLINE)) {
166 return ARES_SUCCESS;
167 }
168
169 /* Check for link-local */
170 if (ares__addr_is_linklocal(addr)) {
171 flags |= ARES_IFACE_IP_LINKLOCAL;
172 }
173 if (flags & ARES_IFACE_IP_LINKLOCAL &&
174 !(ips->enum_flags & ARES_IFACE_IP_LINKLOCAL)) {
175 return ARES_SUCCESS;
176 }
177
178 /* Set address flag based on address provided */
179 if (addr->family == AF_INET) {
180 flags |= ARES_IFACE_IP_V4;
181 }
182
183 if (addr->family == AF_INET6) {
184 flags |= ARES_IFACE_IP_V6;
185 }
186
187 /* If they specified either v4 or v6 validate flags otherwise assume they
188 * want to enumerate both */
189 if (ips->enum_flags & (ARES_IFACE_IP_V4 | ARES_IFACE_IP_V6)) {
190 if (flags & ARES_IFACE_IP_V4 && !(ips->enum_flags & ARES_IFACE_IP_V4)) {
191 return ARES_SUCCESS;
192 }
193 if (flags & ARES_IFACE_IP_V6 && !(ips->enum_flags & ARES_IFACE_IP_V6)) {
194 return ARES_SUCCESS;
195 }
196 }
197
198 /* Allocate more ips */
199 if (ips->cnt + 1 > ips->alloc_size) {
200 void *temp;
201 size_t alloc_size;
202
203 alloc_size = ares__round_up_pow2(ips->alloc_size + 1);
204 temp = ares_realloc_zero(ips->ips, ips->alloc_size * sizeof(*ips->ips),
205 alloc_size * sizeof(*ips->ips));
206 if (temp == NULL) {
207 return ARES_ENOMEM;
208 }
209 ips->ips = temp;
210 ips->alloc_size = alloc_size;
211 }
212
213 /* Add */
214 idx = ips->cnt++;
215
216 ips->ips[idx].flags = flags;
217 ips->ips[idx].netmask = netmask;
218 ips->ips[idx].ll_scope = ll_scope;
219 memcpy(&ips->ips[idx].addr, addr, sizeof(*addr));
220 ips->ips[idx].name = ares_strdup(name);
221 if (ips->ips[idx].name == NULL) {
222 return ARES_ENOMEM;
223 }
224
225 return ARES_SUCCESS;
226 }
227
ares__iface_ips_cnt(const ares__iface_ips_t *ips)228 size_t ares__iface_ips_cnt(const ares__iface_ips_t *ips)
229 {
230 if (ips == NULL) {
231 return 0;
232 }
233 return ips->cnt;
234 }
235
ares__iface_ips_get_name(const ares__iface_ips_t *ips, size_t idx)236 const char *ares__iface_ips_get_name(const ares__iface_ips_t *ips, size_t idx)
237 {
238 if (ips == NULL || idx >= ips->cnt) {
239 return NULL;
240 }
241 return ips->ips[idx].name;
242 }
243
ares__iface_ips_get_addr(const ares__iface_ips_t *ips, size_t idx)244 const struct ares_addr *ares__iface_ips_get_addr(const ares__iface_ips_t *ips,
245 size_t idx)
246 {
247 if (ips == NULL || idx >= ips->cnt) {
248 return NULL;
249 }
250 return &ips->ips[idx].addr;
251 }
252
ares__iface_ips_get_flags(const ares__iface_ips_t *ips, size_t idx)253 ares__iface_ip_flags_t ares__iface_ips_get_flags(const ares__iface_ips_t *ips,
254 size_t idx)
255 {
256 if (ips == NULL || idx >= ips->cnt) {
257 return 0;
258 }
259 return ips->ips[idx].flags;
260 }
261
ares__iface_ips_get_netmask(const ares__iface_ips_t *ips, size_t idx)262 unsigned char ares__iface_ips_get_netmask(const ares__iface_ips_t *ips,
263 size_t idx)
264 {
265 if (ips == NULL || idx >= ips->cnt) {
266 return 0;
267 }
268 return ips->ips[idx].netmask;
269 }
270
ares__iface_ips_get_ll_scope(const ares__iface_ips_t *ips, size_t idx)271 unsigned int ares__iface_ips_get_ll_scope(const ares__iface_ips_t *ips,
272 size_t idx)
273 {
274 if (ips == NULL || idx >= ips->cnt) {
275 return 0;
276 }
277 return ips->ips[idx].ll_scope;
278 }
279
280
281 #ifdef USE_WINSOCK
282
283 # if 0
284 static char *wcharp_to_charp(const wchar_t *in)
285 {
286 char *out;
287 int len;
288
289 len = WideCharToMultiByte(CP_UTF8, 0, in, -1, NULL, 0, NULL, NULL);
290 if (len == -1) {
291 return NULL;
292 }
293
294 out = ares_malloc_zero((size_t)len + 1);
295
296 if (WideCharToMultiByte(CP_UTF8, 0, in, -1, out, len, NULL, NULL) == -1) {
297 ares_free(out);
298 return NULL;
299 }
300
301 return out;
302 }
303 # endif
304
name_match(const char *name, const char *adapter_name, unsigned int ll_scope)305 static ares_bool_t name_match(const char *name, const char *adapter_name,
306 unsigned int ll_scope)
307 {
308 if (name == NULL || *name == 0) {
309 return ARES_TRUE;
310 }
311
312 if (strcasecmp(name, adapter_name) == 0) {
313 return ARES_TRUE;
314 }
315
316 if (ares_str_isnum(name) && (unsigned int)atoi(name) == ll_scope) {
317 return ARES_TRUE;
318 }
319
320 return ARES_FALSE;
321 }
322
ares__iface_ips_enumerate(ares__iface_ips_t *ips, const char *name)323 static ares_status_t ares__iface_ips_enumerate(ares__iface_ips_t *ips,
324 const char *name)
325 {
326 ULONG myflags = GAA_FLAG_INCLUDE_PREFIX /*|GAA_FLAG_INCLUDE_ALL_INTERFACES */;
327 ULONG outBufLen = 0;
328 DWORD retval;
329 IP_ADAPTER_ADDRESSES *addresses = NULL;
330 IP_ADAPTER_ADDRESSES *address = NULL;
331 ares_status_t status = ARES_SUCCESS;
332
333 /* Get necessary buffer size */
334 GetAdaptersAddresses(AF_UNSPEC, myflags, NULL, NULL, &outBufLen);
335 if (outBufLen == 0) {
336 status = ARES_EFILE;
337 goto done;
338 }
339
340 addresses = ares_malloc_zero(outBufLen);
341 if (addresses == NULL) {
342 status = ARES_ENOMEM;
343 goto done;
344 }
345
346 retval =
347 GetAdaptersAddresses(AF_UNSPEC, myflags, NULL, addresses, &outBufLen);
348 if (retval != ERROR_SUCCESS) {
349 status = ARES_EFILE;
350 goto done;
351 }
352
353 for (address = addresses; address != NULL; address = address->Next) {
354 IP_ADAPTER_UNICAST_ADDRESS *ipaddr = NULL;
355 ares__iface_ip_flags_t addrflag = 0;
356 char ifname[64] = "";
357
358 # if defined(HAVE_CONVERTINTERFACEINDEXTOLUID) && \
359 defined(HAVE_CONVERTINTERFACELUIDTONAMEA)
360 /* Retrieve name from interface index.
361 * address->AdapterName appears to be a GUID/UUID of some sort, not a name.
362 * address->FriendlyName is user-changeable.
363 * That said, this doesn't appear to help us out on systems that don't
364 * have if_nametoindex() or if_indextoname() as they don't have these
365 * functions either! */
366 NET_LUID luid;
367 ConvertInterfaceIndexToLuid(address->IfIndex, &luid);
368 ConvertInterfaceLuidToNameA(&luid, ifname, sizeof(ifname));
369 # else
370 ares_strcpy(ifname, address->AdapterName, sizeof(ifname));
371 # endif
372
373 if (address->OperStatus != IfOperStatusUp) {
374 addrflag |= ARES_IFACE_IP_OFFLINE;
375 }
376
377 if (address->IfType == IF_TYPE_SOFTWARE_LOOPBACK) {
378 addrflag |= ARES_IFACE_IP_LOOPBACK;
379 }
380
381 for (ipaddr = address->FirstUnicastAddress; ipaddr != NULL;
382 ipaddr = ipaddr->Next) {
383 struct ares_addr addr;
384
385 if (ipaddr->Address.lpSockaddr->sa_family == AF_INET) {
386 const struct sockaddr_in *sockaddr_in =
387 (const struct sockaddr_in *)((void *)ipaddr->Address.lpSockaddr);
388 addr.family = AF_INET;
389 memcpy(&addr.addr.addr4, &sockaddr_in->sin_addr,
390 sizeof(addr.addr.addr4));
391 } else if (ipaddr->Address.lpSockaddr->sa_family == AF_INET6) {
392 const struct sockaddr_in6 *sockaddr_in6 =
393 (const struct sockaddr_in6 *)((void *)ipaddr->Address.lpSockaddr);
394 addr.family = AF_INET6;
395 memcpy(&addr.addr.addr6, &sockaddr_in6->sin6_addr,
396 sizeof(addr.addr.addr6));
397 } else {
398 /* Unknown */
399 continue;
400 }
401
402 /* Sometimes windows may use numerics to indicate a DNS server's adapter,
403 * which corresponds to the index rather than the name. Check and
404 * validate both. */
405 if (!name_match(name, ifname, address->Ipv6IfIndex)) {
406 continue;
407 }
408
409 status = ares__iface_ips_add(ips, addrflag, ifname, &addr,
410 ipaddr->OnLinkPrefixLength /* netmask */,
411 address->Ipv6IfIndex /* ll_scope */);
412
413 if (status != ARES_SUCCESS) {
414 goto done;
415 }
416 }
417 }
418
419 done:
420 ares_free(addresses);
421 return status;
422 }
423
424 #elif defined(HAVE_GETIFADDRS)
425
count_addr_bits(const unsigned char *addr, size_t addr_len)426 static unsigned char count_addr_bits(const unsigned char *addr, size_t addr_len)
427 {
428 size_t i;
429 unsigned char count = 0;
430
431 for (i = 0; i < addr_len; i++) {
432 count += ares__count_bits_u8(addr[i]);
433 }
434 return count;
435 }
436
ares__iface_ips_enumerate(ares__iface_ips_t *ips, const char *name)437 static ares_status_t ares__iface_ips_enumerate(ares__iface_ips_t *ips,
438 const char *name)
439 {
440 struct ifaddrs *ifap = NULL;
441 struct ifaddrs *ifa = NULL;
442 ares_status_t status = ARES_SUCCESS;
443
444 if (getifaddrs(&ifap) != 0) {
445 status = ARES_EFILE;
446 goto done;
447 }
448
449 for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
450 ares__iface_ip_flags_t addrflag = 0;
451 struct ares_addr addr;
452 unsigned char netmask = 0;
453 unsigned int ll_scope = 0;
454
455 if (ifa->ifa_addr == NULL) {
456 continue;
457 }
458
459 if (!(ifa->ifa_flags & IFF_UP)) {
460 addrflag |= ARES_IFACE_IP_OFFLINE;
461 }
462
463 if (ifa->ifa_flags & IFF_LOOPBACK) {
464 addrflag |= ARES_IFACE_IP_LOOPBACK;
465 }
466
467 if (ifa->ifa_addr->sa_family == AF_INET) {
468 const struct sockaddr_in *sockaddr_in =
469 (const struct sockaddr_in *)((void *)ifa->ifa_addr);
470 addr.family = AF_INET;
471 memcpy(&addr.addr.addr4, &sockaddr_in->sin_addr, sizeof(addr.addr.addr4));
472 /* netmask */
473 sockaddr_in = (struct sockaddr_in *)((void *)ifa->ifa_netmask);
474 netmask = count_addr_bits((const void *)&sockaddr_in->sin_addr, 4);
475 } else if (ifa->ifa_addr->sa_family == AF_INET6) {
476 const struct sockaddr_in6 *sockaddr_in6 =
477 (const struct sockaddr_in6 *)((void *)ifa->ifa_addr);
478 addr.family = AF_INET6;
479 memcpy(&addr.addr.addr6, &sockaddr_in6->sin6_addr,
480 sizeof(addr.addr.addr6));
481 /* netmask */
482 sockaddr_in6 = (struct sockaddr_in6 *)((void *)ifa->ifa_netmask);
483 netmask = count_addr_bits((const void *)&sockaddr_in6->sin6_addr, 16);
484 # ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID
485 ll_scope = sockaddr_in6->sin6_scope_id;
486 # endif
487 } else {
488 /* unknown */
489 continue;
490 }
491
492 /* Name mismatch */
493 if (strcasecmp(ifa->ifa_name, name) != 0) {
494 continue;
495 }
496
497 status = ares__iface_ips_add(ips, addrflag, ifa->ifa_name, &addr, netmask,
498 ll_scope);
499 if (status != ARES_SUCCESS) {
500 goto done;
501 }
502 }
503
504 done:
505 freeifaddrs(ifap);
506 return status;
507 }
508
509 #else
510
ares__iface_ips_enumerate(ares__iface_ips_t *ips, const char *name)511 static ares_status_t ares__iface_ips_enumerate(ares__iface_ips_t *ips,
512 const char *name)
513 {
514 (void)ips;
515 (void)name;
516 return ARES_ENOTIMP;
517 }
518
519 #endif
520
521
ares__if_nametoindex(const char *name)522 unsigned int ares__if_nametoindex(const char *name)
523 {
524 #ifdef HAVE_IF_NAMETOINDEX
525 return if_nametoindex(name);
526 #else
527 ares_status_t status;
528 ares__iface_ips_t *ips = NULL;
529 size_t i;
530 unsigned int index = 0;
531
532 status =
533 ares__iface_ips(&ips, ARES_IFACE_IP_V6 | ARES_IFACE_IP_LINKLOCAL, name);
534 if (status != ARES_SUCCESS) {
535 goto done;
536 }
537
538 for (i = 0; i < ares__iface_ips_cnt(ips); i++) {
539 if (ares__iface_ips_get_flags(ips, i) & ARES_IFACE_IP_LINKLOCAL) {
540 index = ares__iface_ips_get_ll_scope(ips, i);
541 goto done;
542 }
543 }
544
545 done:
546 ares__iface_ips_destroy(ips);
547 return index;
548 #endif
549 }
550
ares__if_indextoname(unsigned int index, char *name, size_t name_len)551 const char *ares__if_indextoname(unsigned int index, char *name,
552 size_t name_len)
553 {
554 #ifdef HAVE_IF_INDEXTONAME
555 if (name_len < IF_NAMESIZE) {
556 return NULL;
557 }
558 return if_indextoname(index, name);
559 #else
560 ares_status_t status;
561 ares__iface_ips_t *ips = NULL;
562 size_t i;
563 const char *ptr = NULL;
564
565 if (name_len < IF_NAMESIZE) {
566 goto done;
567 }
568
569 if (index == 0) {
570 goto done;
571 }
572
573 status =
574 ares__iface_ips(&ips, ARES_IFACE_IP_V6 | ARES_IFACE_IP_LINKLOCAL, NULL);
575 if (status != ARES_SUCCESS) {
576 goto done;
577 }
578
579 for (i = 0; i < ares__iface_ips_cnt(ips); i++) {
580 if (ares__iface_ips_get_flags(ips, i) & ARES_IFACE_IP_LINKLOCAL &&
581 ares__iface_ips_get_ll_scope(ips, i) == index) {
582 ares_strcpy(name, ares__iface_ips_get_name(ips, i), name_len);
583 ptr = name;
584 goto done;
585 }
586 }
587
588 done:
589 ares__iface_ips_destroy(ips);
590 return ptr;
591 #endif
592 }
593