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_private.h"
27
28 #ifdef USE_WINSOCK
29 # include <winsock2.h>
30 # include <ws2tcpip.h>
31 # if defined(HAVE_IPHLPAPI_H)
32 # include <iphlpapi.h>
33 # endif
34 # if defined(HAVE_NETIOAPI_H)
35 # include <netioapi.h>
36 # endif
37 #endif
38
39 #ifdef HAVE_SYS_TYPES_H
40 # include <sys/types.h>
41 #endif
42 #ifdef HAVE_SYS_SOCKET_H
43 # include <sys/socket.h>
44 #endif
45 #ifdef HAVE_NET_IF_H
46 # include <net/if.h>
47 #endif
48 #ifdef HAVE_IFADDRS_H
49 # include <ifaddrs.h>
50 #endif
51 #ifdef HAVE_SYS_IOCTL_H
52 # include <sys/ioctl.h>
53 #endif
54 #ifdef HAVE_NETINET_IN_H
55 # include <netinet/in.h>
56 #endif
57 #ifdef HAVE_NETDB_H
58 # include <netdb.h>
59 #endif
60
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_array_t *ips; /*!< Type is ares_iface_ip_t */
75 ares_iface_ip_flags_t enum_flags;
76 };
77
ares_iface_ip_free_cb(void * arg)78 static void ares_iface_ip_free_cb(void *arg)
79 {
80 ares_iface_ip_t *ip = arg;
81 if (ip == NULL) {
82 return;
83 }
84 ares_free(ip->name);
85 }
86
ares_iface_ips_alloc(ares_iface_ip_flags_t flags)87 static ares_iface_ips_t *ares_iface_ips_alloc(ares_iface_ip_flags_t flags)
88 {
89 ares_iface_ips_t *ips = ares_malloc_zero(sizeof(*ips));
90 if (ips == NULL) {
91 return NULL; /* LCOV_EXCL_LINE: OutOfMemory */
92 }
93
94 ips->enum_flags = flags;
95 ips->ips = ares_array_create(sizeof(ares_iface_ip_t), ares_iface_ip_free_cb);
96 if (ips->ips == NULL) {
97 ares_free(ips); /* LCOV_EXCL_LINE: OutOfMemory */
98 return NULL; /* LCOV_EXCL_LINE: OutOfMemory */
99 }
100 return ips;
101 }
102
ares_iface_ips_destroy(ares_iface_ips_t * ips)103 void ares_iface_ips_destroy(ares_iface_ips_t *ips)
104 {
105 if (ips == NULL) {
106 return;
107 }
108
109 ares_array_destroy(ips->ips);
110 ares_free(ips);
111 }
112
ares_iface_ips(ares_iface_ips_t ** ips,ares_iface_ip_flags_t flags,const char * name)113 ares_status_t ares_iface_ips(ares_iface_ips_t **ips,
114 ares_iface_ip_flags_t flags, const char *name)
115 {
116 ares_status_t status;
117
118 if (ips == NULL) {
119 return ARES_EFORMERR;
120 }
121
122 *ips = ares_iface_ips_alloc(flags);
123 if (*ips == NULL) {
124 return ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
125 }
126
127 status = ares_iface_ips_enumerate(*ips, name);
128 if (status != ARES_SUCCESS) {
129 /* LCOV_EXCL_START: UntestablePath */
130 ares_iface_ips_destroy(*ips);
131 *ips = NULL;
132 return status;
133 /* LCOV_EXCL_STOP */
134 }
135
136 return ARES_SUCCESS;
137 }
138
139 __attribute__((unused)) 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)140 ares_iface_ips_add(ares_iface_ips_t *ips, ares_iface_ip_flags_t flags,
141 const char *name, const struct ares_addr *addr,
142 unsigned char netmask, unsigned int ll_scope)
143 {
144 ares_iface_ip_t *ip;
145 ares_status_t status;
146
147 if (ips == NULL || name == NULL || addr == NULL) {
148 return ARES_EFORMERR; /* LCOV_EXCL_LINE: DefensiveCoding */
149 }
150
151 /* Don't want loopback */
152 if (flags & ARES_IFACE_IP_LOOPBACK &&
153 !(ips->enum_flags & ARES_IFACE_IP_LOOPBACK)) {
154 return ARES_SUCCESS;
155 }
156
157 /* Don't want offline */
158 if (flags & ARES_IFACE_IP_OFFLINE &&
159 !(ips->enum_flags & ARES_IFACE_IP_OFFLINE)) {
160 return ARES_SUCCESS;
161 }
162
163 /* Check for link-local */
164 if (ares_addr_is_linklocal(addr)) {
165 flags |= ARES_IFACE_IP_LINKLOCAL;
166 }
167 if (flags & ARES_IFACE_IP_LINKLOCAL &&
168 !(ips->enum_flags & ARES_IFACE_IP_LINKLOCAL)) {
169 return ARES_SUCCESS;
170 }
171
172 /* Set address flag based on address provided */
173 if (addr->family == AF_INET) {
174 flags |= ARES_IFACE_IP_V4;
175 }
176
177 if (addr->family == AF_INET6) {
178 flags |= ARES_IFACE_IP_V6;
179 }
180
181 /* If they specified either v4 or v6 validate flags otherwise assume they
182 * want to enumerate both */
183 if (ips->enum_flags & (ARES_IFACE_IP_V4 | ARES_IFACE_IP_V6)) {
184 if (flags & ARES_IFACE_IP_V4 && !(ips->enum_flags & ARES_IFACE_IP_V4)) {
185 return ARES_SUCCESS;
186 }
187 if (flags & ARES_IFACE_IP_V6 && !(ips->enum_flags & ARES_IFACE_IP_V6)) {
188 return ARES_SUCCESS;
189 }
190 }
191
192 status = ares_array_insert_last((void **)&ip, ips->ips);
193 if (status != ARES_SUCCESS) {
194 return status;
195 }
196
197 ip->flags = flags;
198 ip->netmask = netmask;
199 if (flags & ARES_IFACE_IP_LINKLOCAL) {
200 ip->ll_scope = ll_scope;
201 }
202 memcpy(&ip->addr, addr, sizeof(*addr));
203 ip->name = ares_strdup(name);
204 if (ip->name == NULL) {
205 ares_array_remove_last(ips->ips);
206 return ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
207 }
208
209 return ARES_SUCCESS;
210 }
211
ares_iface_ips_cnt(const ares_iface_ips_t * ips)212 size_t ares_iface_ips_cnt(const ares_iface_ips_t *ips)
213 {
214 if (ips == NULL) {
215 return 0;
216 }
217 return ares_array_len(ips->ips);
218 }
219
ares_iface_ips_get_name(const ares_iface_ips_t * ips,size_t idx)220 const char *ares_iface_ips_get_name(const ares_iface_ips_t *ips, size_t idx)
221 {
222 const ares_iface_ip_t *ip;
223
224 if (ips == NULL) {
225 return NULL;
226 }
227
228 ip = ares_array_at_const(ips->ips, idx);
229 if (ip == NULL) {
230 return NULL;
231 }
232
233 return ip->name;
234 }
235
ares_iface_ips_get_addr(const ares_iface_ips_t * ips,size_t idx)236 const struct ares_addr *ares_iface_ips_get_addr(const ares_iface_ips_t *ips,
237 size_t idx)
238 {
239 const ares_iface_ip_t *ip;
240
241 if (ips == NULL) {
242 return NULL;
243 }
244
245 ip = ares_array_at_const(ips->ips, idx);
246 if (ip == NULL) {
247 return NULL;
248 }
249
250 return &ip->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 const ares_iface_ip_t *ip;
257
258 if (ips == NULL) {
259 return 0;
260 }
261
262 ip = ares_array_at_const(ips->ips, idx);
263 if (ip == NULL) {
264 return 0;
265 }
266
267 return ip->flags;
268 }
269
ares_iface_ips_get_netmask(const ares_iface_ips_t * ips,size_t idx)270 unsigned char ares_iface_ips_get_netmask(const ares_iface_ips_t *ips,
271 size_t idx)
272 {
273 const ares_iface_ip_t *ip;
274
275 if (ips == NULL) {
276 return 0;
277 }
278
279 ip = ares_array_at_const(ips->ips, idx);
280 if (ip == NULL) {
281 return 0;
282 }
283
284 return ip->netmask;
285 }
286
ares_iface_ips_get_ll_scope(const ares_iface_ips_t * ips,size_t idx)287 unsigned int ares_iface_ips_get_ll_scope(const ares_iface_ips_t *ips,
288 size_t idx)
289 {
290 const ares_iface_ip_t *ip;
291
292 if (ips == NULL) {
293 return 0;
294 }
295
296 ip = ares_array_at_const(ips->ips, idx);
297 if (ip == NULL) {
298 return 0;
299 }
300
301 return ip->ll_scope;
302 }
303
304
305 #ifdef USE_WINSOCK
306
307 # if 0
308 static char *wcharp_to_charp(const wchar_t *in)
309 {
310 char *out;
311 int len;
312
313 len = WideCharToMultiByte(CP_UTF8, 0, in, -1, NULL, 0, NULL, NULL);
314 if (len == -1) {
315 return NULL;
316 }
317
318 out = ares_malloc_zero((size_t)len + 1);
319
320 if (WideCharToMultiByte(CP_UTF8, 0, in, -1, out, len, NULL, NULL) == -1) {
321 ares_free(out);
322 return NULL;
323 }
324
325 return out;
326 }
327 # endif
328
name_match(const char * name,const char * adapter_name,unsigned int ll_scope)329 static ares_bool_t name_match(const char *name, const char *adapter_name,
330 unsigned int ll_scope)
331 {
332 if (name == NULL || *name == 0) {
333 return ARES_TRUE;
334 }
335
336 if (ares_strcaseeq(name, adapter_name)) {
337 return ARES_TRUE;
338 }
339
340 if (ares_str_isnum(name) && (unsigned int)atoi(name) == ll_scope) {
341 return ARES_TRUE;
342 }
343
344 return ARES_FALSE;
345 }
346
ares_iface_ips_enumerate(ares_iface_ips_t * ips,const char * name)347 static ares_status_t ares_iface_ips_enumerate(ares_iface_ips_t *ips,
348 const char *name)
349 {
350 ULONG myflags = GAA_FLAG_INCLUDE_PREFIX /*|GAA_FLAG_INCLUDE_ALL_INTERFACES */;
351 ULONG outBufLen = 0;
352 DWORD retval;
353 IP_ADAPTER_ADDRESSES *addresses = NULL;
354 IP_ADAPTER_ADDRESSES *address = NULL;
355 ares_status_t status = ARES_SUCCESS;
356
357 /* Get necessary buffer size */
358 GetAdaptersAddresses(AF_UNSPEC, myflags, NULL, NULL, &outBufLen);
359 if (outBufLen == 0) {
360 status = ARES_EFILE;
361 goto done;
362 }
363
364 addresses = ares_malloc_zero(outBufLen);
365 if (addresses == NULL) {
366 status = ARES_ENOMEM;
367 goto done;
368 }
369
370 retval =
371 GetAdaptersAddresses(AF_UNSPEC, myflags, NULL, addresses, &outBufLen);
372 if (retval != ERROR_SUCCESS) {
373 status = ARES_EFILE;
374 goto done;
375 }
376
377 for (address = addresses; address != NULL; address = address->Next) {
378 IP_ADAPTER_UNICAST_ADDRESS *ipaddr = NULL;
379 ares_iface_ip_flags_t addrflag = 0;
380 char ifname[64] = "";
381
382 # if defined(HAVE_CONVERTINTERFACEINDEXTOLUID) && \
383 defined(HAVE_CONVERTINTERFACELUIDTONAMEA)
384 /* Retrieve name from interface index.
385 * address->AdapterName appears to be a GUID/UUID of some sort, not a name.
386 * address->FriendlyName is user-changeable.
387 * That said, this doesn't appear to help us out on systems that don't
388 * have if_nametoindex() or if_indextoname() as they don't have these
389 * functions either! */
390 NET_LUID luid;
391 ConvertInterfaceIndexToLuid(address->IfIndex, &luid);
392 ConvertInterfaceLuidToNameA(&luid, ifname, sizeof(ifname));
393 # else
394 ares_strcpy(ifname, address->AdapterName, sizeof(ifname));
395 # endif
396
397 if (address->OperStatus != IfOperStatusUp) {
398 addrflag |= ARES_IFACE_IP_OFFLINE;
399 }
400
401 if (address->IfType == IF_TYPE_SOFTWARE_LOOPBACK) {
402 addrflag |= ARES_IFACE_IP_LOOPBACK;
403 }
404
405 for (ipaddr = address->FirstUnicastAddress; ipaddr != NULL;
406 ipaddr = ipaddr->Next) {
407 struct ares_addr addr;
408
409 if (ipaddr->Address.lpSockaddr->sa_family == AF_INET) {
410 const struct sockaddr_in *sockaddr_in =
411 (const struct sockaddr_in *)((void *)ipaddr->Address.lpSockaddr);
412 addr.family = AF_INET;
413 memcpy(&addr.addr.addr4, &sockaddr_in->sin_addr,
414 sizeof(addr.addr.addr4));
415 } else if (ipaddr->Address.lpSockaddr->sa_family == AF_INET6) {
416 const struct sockaddr_in6 *sockaddr_in6 =
417 (const struct sockaddr_in6 *)((void *)ipaddr->Address.lpSockaddr);
418 addr.family = AF_INET6;
419 memcpy(&addr.addr.addr6, &sockaddr_in6->sin6_addr,
420 sizeof(addr.addr.addr6));
421 } else {
422 /* Unknown */
423 continue;
424 }
425
426 /* Sometimes windows may use numerics to indicate a DNS server's adapter,
427 * which corresponds to the index rather than the name. Check and
428 * validate both. */
429 if (!name_match(name, ifname, address->Ipv6IfIndex)) {
430 continue;
431 }
432
433 status = ares_iface_ips_add(ips, addrflag, ifname, &addr,
434 #if _WIN32_WINNT >= 0x0600
435 ipaddr->OnLinkPrefixLength /* netmask */,
436 #else
437 ipaddr->Address.lpSockaddr->sa_family
438 == AF_INET?32:128,
439 #endif
440 address->Ipv6IfIndex /* ll_scope */
441 );
442
443 if (status != ARES_SUCCESS) {
444 goto done;
445 }
446 }
447 }
448
449 done:
450 ares_free(addresses);
451 return status;
452 }
453
454 #elif defined(HAVE_GETIFADDRS)
455
count_addr_bits(const unsigned char * addr,size_t addr_len)456 static unsigned char count_addr_bits(const unsigned char *addr, size_t addr_len)
457 {
458 size_t i;
459 unsigned char count = 0;
460
461 for (i = 0; i < addr_len; i++) {
462 count += ares_count_bits_u8(addr[i]);
463 }
464 return count;
465 }
466
ares_iface_ips_enumerate(ares_iface_ips_t * ips,const char * name)467 static ares_status_t ares_iface_ips_enumerate(ares_iface_ips_t *ips,
468 const char *name)
469 {
470 struct ifaddrs *ifap = NULL;
471 struct ifaddrs *ifa = NULL;
472 ares_status_t status = ARES_SUCCESS;
473
474 if (getifaddrs(&ifap) != 0) {
475 status = ARES_EFILE;
476 goto done;
477 }
478
479 for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
480 ares_iface_ip_flags_t addrflag = 0;
481 struct ares_addr addr;
482 unsigned char netmask = 0;
483 unsigned int ll_scope = 0;
484
485 if (ifa->ifa_addr == NULL) {
486 continue;
487 }
488
489 if (!(ifa->ifa_flags & IFF_UP)) {
490 addrflag |= ARES_IFACE_IP_OFFLINE;
491 }
492
493 if (ifa->ifa_flags & IFF_LOOPBACK) {
494 addrflag |= ARES_IFACE_IP_LOOPBACK;
495 }
496
497 if (ifa->ifa_addr->sa_family == AF_INET) {
498 const struct sockaddr_in *sockaddr_in =
499 (const struct sockaddr_in *)((void *)ifa->ifa_addr);
500 addr.family = AF_INET;
501 memcpy(&addr.addr.addr4, &sockaddr_in->sin_addr, sizeof(addr.addr.addr4));
502 /* netmask */
503 sockaddr_in = (struct sockaddr_in *)((void *)ifa->ifa_netmask);
504 netmask = count_addr_bits((const void *)&sockaddr_in->sin_addr, 4);
505 } else if (ifa->ifa_addr->sa_family == AF_INET6) {
506 const struct sockaddr_in6 *sockaddr_in6 =
507 (const struct sockaddr_in6 *)((void *)ifa->ifa_addr);
508 addr.family = AF_INET6;
509 memcpy(&addr.addr.addr6, &sockaddr_in6->sin6_addr,
510 sizeof(addr.addr.addr6));
511 /* netmask */
512 sockaddr_in6 = (struct sockaddr_in6 *)((void *)ifa->ifa_netmask);
513 netmask = count_addr_bits((const void *)&sockaddr_in6->sin6_addr, 16);
514 # ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID
515 ll_scope = sockaddr_in6->sin6_scope_id;
516 # endif
517 } else {
518 /* unknown */
519 continue;
520 }
521
522 /* Name mismatch */
523 if (name != NULL && !ares_strcaseeq(ifa->ifa_name, name)) {
524 continue;
525 }
526
527 status = ares_iface_ips_add(ips, addrflag, ifa->ifa_name, &addr, netmask,
528 ll_scope);
529 if (status != ARES_SUCCESS) {
530 goto done;
531 }
532 }
533
534 done:
535 freeifaddrs(ifap);
536 return status;
537 }
538
539 #else
540
ares_iface_ips_enumerate(ares_iface_ips_t * ips,const char * name)541 static ares_status_t ares_iface_ips_enumerate(ares_iface_ips_t *ips,
542 const char *name)
543 {
544 (void)ips;
545 (void)name;
546 return ARES_ENOTIMP;
547 }
548
549 #endif
550
551
ares_os_if_nametoindex(const char * name)552 unsigned int ares_os_if_nametoindex(const char *name)
553 {
554 #ifdef HAVE_IF_NAMETOINDEX
555 if (name == NULL) {
556 return 0;
557 }
558 return if_nametoindex(name);
559 #else
560 ares_status_t status;
561 ares_iface_ips_t *ips = NULL;
562 size_t i;
563 unsigned int index = 0;
564
565 if (name == NULL) {
566 return 0;
567 }
568
569 status =
570 ares_iface_ips(&ips, ARES_IFACE_IP_V6 | ARES_IFACE_IP_LINKLOCAL, name);
571 if (status != ARES_SUCCESS) {
572 goto done;
573 }
574
575 for (i = 0; i < ares_iface_ips_cnt(ips); i++) {
576 if (ares_iface_ips_get_flags(ips, i) & ARES_IFACE_IP_LINKLOCAL) {
577 index = ares_iface_ips_get_ll_scope(ips, i);
578 goto done;
579 }
580 }
581
582 done:
583 ares_iface_ips_destroy(ips);
584 return index;
585 #endif
586 }
587
ares_os_if_indextoname(unsigned int index,char * name,size_t name_len)588 const char *ares_os_if_indextoname(unsigned int index, char *name, size_t name_len)
589 {
590 #ifdef HAVE_IF_INDEXTONAME
591 if (name_len < IF_NAMESIZE) {
592 return NULL;
593 }
594 return if_indextoname(index, name);
595 #else
596 ares_status_t status;
597 ares_iface_ips_t *ips = NULL;
598 size_t i;
599 const char *ptr = NULL;
600
601 if (name == NULL || name_len < IF_NAMESIZE) {
602 goto done;
603 }
604
605 if (index == 0) {
606 goto done;
607 }
608
609 status =
610 ares_iface_ips(&ips, ARES_IFACE_IP_V6 | ARES_IFACE_IP_LINKLOCAL, NULL);
611 if (status != ARES_SUCCESS) {
612 goto done;
613 }
614
615 for (i = 0; i < ares_iface_ips_cnt(ips); i++) {
616 if (ares_iface_ips_get_flags(ips, i) & ARES_IFACE_IP_LINKLOCAL &&
617 ares_iface_ips_get_ll_scope(ips, i) == index) {
618 ares_strcpy(name, ares_iface_ips_get_name(ips, i), name_len);
619 ptr = name;
620 goto done;
621 }
622 }
623
624 done:
625 ares_iface_ips_destroy(ips);
626 return ptr;
627 #endif
628 }
629