ares_iface_ips.c (15943B)
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 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 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 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 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 static ares_status_t 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 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 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 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 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 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 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 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 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 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 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 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 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 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 }