%PDF- %PDF-
Direktori : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/cares/src/lib/ |
Current File : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/cares/src/lib/ares__iface_ips.c |
/* MIT License * * Copyright (c) 2023 Brad House * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * SPDX-License-Identifier: MIT */ #include "ares_setup.h" #ifdef USE_WINSOCK # include <winsock2.h> # include <ws2tcpip.h> # if defined(HAVE_IPHLPAPI_H) # include <iphlpapi.h> # endif # if defined(HAVE_NETIOAPI_H) # include <netioapi.h> # endif #endif #ifdef HAVE_SYS_TYPES_H # include <sys/types.h> #endif #ifdef HAVE_SYS_SOCKET_H # include <sys/socket.h> #endif #ifdef HAVE_NET_IF_H # include <net/if.h> #endif #ifdef HAVE_IFADDRS_H # include <ifaddrs.h> #endif #ifdef HAVE_SYS_IOCTL_H # include <sys/ioctl.h> #endif #ifdef HAVE_NETINET_IN_H # include <netinet/in.h> #endif #include "ares.h" #include "ares_private.h" static ares_status_t ares__iface_ips_enumerate(ares__iface_ips_t *ips, const char *name); typedef struct { char *name; struct ares_addr addr; unsigned char netmask; unsigned int ll_scope; ares__iface_ip_flags_t flags; } ares__iface_ip_t; struct ares__iface_ips { ares__iface_ip_t *ips; size_t cnt; size_t alloc_size; ares__iface_ip_flags_t enum_flags; }; static ares__iface_ips_t *ares__iface_ips_alloc(ares__iface_ip_flags_t flags) { ares__iface_ips_t *ips = ares_malloc_zero(sizeof(*ips)); if (ips == NULL) { return NULL; } /* Prealloc 4 entries */ ips->alloc_size = 4; ips->ips = ares_malloc_zero(ips->alloc_size * sizeof(*ips->ips)); if (ips->ips == NULL) { ares_free(ips); return NULL; } ips->enum_flags = flags; return ips; } static void ares__iface_ip_destroy(ares__iface_ip_t *ip) { if (ip == NULL) { return; } ares_free(ip->name); memset(ip, 0, sizeof(*ip)); } void ares__iface_ips_destroy(ares__iface_ips_t *ips) { size_t i; if (ips == NULL) { return; } for (i = 0; i < ips->cnt; i++) { ares__iface_ip_destroy(&ips->ips[i]); } ares_free(ips->ips); ares_free(ips); } ares_status_t ares__iface_ips(ares__iface_ips_t **ips, ares__iface_ip_flags_t flags, const char *name) { ares_status_t status; if (ips == NULL) { return ARES_EFORMERR; } *ips = ares__iface_ips_alloc(flags); if (*ips == NULL) { return ARES_ENOMEM; } status = ares__iface_ips_enumerate(*ips, name); if (status != ARES_SUCCESS) { ares__iface_ips_destroy(*ips); *ips = NULL; return status; } return ARES_SUCCESS; } 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) { size_t idx; if (ips == NULL || name == NULL || addr == NULL) { return ARES_EFORMERR; } /* Don't want loopback */ if (flags & ARES_IFACE_IP_LOOPBACK && !(ips->enum_flags & ARES_IFACE_IP_LOOPBACK)) { return ARES_SUCCESS; } /* Don't want offline */ if (flags & ARES_IFACE_IP_OFFLINE && !(ips->enum_flags & ARES_IFACE_IP_OFFLINE)) { return ARES_SUCCESS; } /* Check for link-local */ if (ares__addr_is_linklocal(addr)) { flags |= ARES_IFACE_IP_LINKLOCAL; } if (flags & ARES_IFACE_IP_LINKLOCAL && !(ips->enum_flags & ARES_IFACE_IP_LINKLOCAL)) { return ARES_SUCCESS; } /* Set address flag based on address provided */ if (addr->family == AF_INET) { flags |= ARES_IFACE_IP_V4; } if (addr->family == AF_INET6) { flags |= ARES_IFACE_IP_V6; } /* If they specified either v4 or v6 validate flags otherwise assume they * want to enumerate both */ if (ips->enum_flags & (ARES_IFACE_IP_V4 | ARES_IFACE_IP_V6)) { if (flags & ARES_IFACE_IP_V4 && !(ips->enum_flags & ARES_IFACE_IP_V4)) { return ARES_SUCCESS; } if (flags & ARES_IFACE_IP_V6 && !(ips->enum_flags & ARES_IFACE_IP_V6)) { return ARES_SUCCESS; } } /* Allocate more ips */ if (ips->cnt + 1 > ips->alloc_size) { void *temp; size_t alloc_size; alloc_size = ares__round_up_pow2(ips->alloc_size + 1); temp = ares_realloc_zero(ips->ips, ips->alloc_size * sizeof(*ips->ips), alloc_size * sizeof(*ips->ips)); if (temp == NULL) { return ARES_ENOMEM; } ips->ips = temp; ips->alloc_size = alloc_size; } /* Add */ idx = ips->cnt++; ips->ips[idx].flags = flags; ips->ips[idx].netmask = netmask; ips->ips[idx].ll_scope = ll_scope; memcpy(&ips->ips[idx].addr, addr, sizeof(*addr)); ips->ips[idx].name = ares_strdup(name); if (ips->ips[idx].name == NULL) { return ARES_ENOMEM; } return ARES_SUCCESS; } size_t ares__iface_ips_cnt(const ares__iface_ips_t *ips) { if (ips == NULL) { return 0; } return ips->cnt; } const char *ares__iface_ips_get_name(const ares__iface_ips_t *ips, size_t idx) { if (ips == NULL || idx >= ips->cnt) { return NULL; } return ips->ips[idx].name; } const struct ares_addr *ares__iface_ips_get_addr(const ares__iface_ips_t *ips, size_t idx) { if (ips == NULL || idx >= ips->cnt) { return NULL; } return &ips->ips[idx].addr; } ares__iface_ip_flags_t ares__iface_ips_get_flags(const ares__iface_ips_t *ips, size_t idx) { if (ips == NULL || idx >= ips->cnt) { return 0; } return ips->ips[idx].flags; } unsigned char ares__iface_ips_get_netmask(const ares__iface_ips_t *ips, size_t idx) { if (ips == NULL || idx >= ips->cnt) { return 0; } return ips->ips[idx].netmask; } unsigned int ares__iface_ips_get_ll_scope(const ares__iface_ips_t *ips, size_t idx) { if (ips == NULL || idx >= ips->cnt) { return 0; } return ips->ips[idx].ll_scope; } #ifdef USE_WINSOCK # if 0 static char *wcharp_to_charp(const wchar_t *in) { char *out; int len; len = WideCharToMultiByte(CP_UTF8, 0, in, -1, NULL, 0, NULL, NULL); if (len == -1) { return NULL; } out = ares_malloc_zero((size_t)len + 1); if (WideCharToMultiByte(CP_UTF8, 0, in, -1, out, len, NULL, NULL) == -1) { ares_free(out); return NULL; } return out; } # endif static ares_bool_t name_match(const char *name, const char *adapter_name, unsigned int ll_scope) { if (name == NULL || *name == 0) { return ARES_TRUE; } if (strcasecmp(name, adapter_name) == 0) { return ARES_TRUE; } if (ares_str_isnum(name) && (unsigned int)atoi(name) == ll_scope) { return ARES_TRUE; } return ARES_FALSE; } static ares_status_t ares__iface_ips_enumerate(ares__iface_ips_t *ips, const char *name) { ULONG myflags = GAA_FLAG_INCLUDE_PREFIX /*|GAA_FLAG_INCLUDE_ALL_INTERFACES */; ULONG outBufLen = 0; DWORD retval; IP_ADAPTER_ADDRESSES *addresses = NULL; IP_ADAPTER_ADDRESSES *address = NULL; ares_status_t status = ARES_SUCCESS; /* Get necessary buffer size */ GetAdaptersAddresses(AF_UNSPEC, myflags, NULL, NULL, &outBufLen); if (outBufLen == 0) { status = ARES_EFILE; goto done; } addresses = ares_malloc_zero(outBufLen); if (addresses == NULL) { status = ARES_ENOMEM; goto done; } retval = GetAdaptersAddresses(AF_UNSPEC, myflags, NULL, addresses, &outBufLen); if (retval != ERROR_SUCCESS) { status = ARES_EFILE; goto done; } for (address = addresses; address != NULL; address = address->Next) { IP_ADAPTER_UNICAST_ADDRESS *ipaddr = NULL; ares__iface_ip_flags_t addrflag = 0; char ifname[64] = ""; # if defined(HAVE_CONVERTINTERFACEINDEXTOLUID) && \ defined(HAVE_CONVERTINTERFACELUIDTONAMEA) /* Retrieve name from interface index. * address->AdapterName appears to be a GUID/UUID of some sort, not a name. * address->FriendlyName is user-changeable. * That said, this doesn't appear to help us out on systems that don't * have if_nametoindex() or if_indextoname() as they don't have these * functions either! */ NET_LUID luid; ConvertInterfaceIndexToLuid(address->IfIndex, &luid); ConvertInterfaceLuidToNameA(&luid, ifname, sizeof(ifname)); # else ares_strcpy(ifname, address->AdapterName, sizeof(ifname)); # endif if (address->OperStatus != IfOperStatusUp) { addrflag |= ARES_IFACE_IP_OFFLINE; } if (address->IfType == IF_TYPE_SOFTWARE_LOOPBACK) { addrflag |= ARES_IFACE_IP_LOOPBACK; } for (ipaddr = address->FirstUnicastAddress; ipaddr != NULL; ipaddr = ipaddr->Next) { struct ares_addr addr; if (ipaddr->Address.lpSockaddr->sa_family == AF_INET) { const struct sockaddr_in *sockaddr_in = (const struct sockaddr_in *)((void *)ipaddr->Address.lpSockaddr); addr.family = AF_INET; memcpy(&addr.addr.addr4, &sockaddr_in->sin_addr, sizeof(addr.addr.addr4)); } else if (ipaddr->Address.lpSockaddr->sa_family == AF_INET6) { const struct sockaddr_in6 *sockaddr_in6 = (const struct sockaddr_in6 *)((void *)ipaddr->Address.lpSockaddr); addr.family = AF_INET6; memcpy(&addr.addr.addr6, &sockaddr_in6->sin6_addr, sizeof(addr.addr.addr6)); } else { /* Unknown */ continue; } /* Sometimes windows may use numerics to indicate a DNS server's adapter, * which corresponds to the index rather than the name. Check and * validate both. */ if (!name_match(name, ifname, address->Ipv6IfIndex)) { continue; } status = ares__iface_ips_add(ips, addrflag, ifname, &addr, ipaddr->OnLinkPrefixLength /* netmask */, address->Ipv6IfIndex /* ll_scope */); if (status != ARES_SUCCESS) { goto done; } } } done: ares_free(addresses); return status; } #elif defined(HAVE_GETIFADDRS) static unsigned char count_addr_bits(const unsigned char *addr, size_t addr_len) { size_t i; unsigned char count = 0; for (i = 0; i < addr_len; i++) { count += ares__count_bits_u8(addr[i]); } return count; } static ares_status_t ares__iface_ips_enumerate(ares__iface_ips_t *ips, const char *name) { struct ifaddrs *ifap = NULL; struct ifaddrs *ifa = NULL; ares_status_t status = ARES_SUCCESS; if (getifaddrs(&ifap) != 0) { status = ARES_EFILE; goto done; } for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { ares__iface_ip_flags_t addrflag = 0; struct ares_addr addr; unsigned char netmask = 0; unsigned int ll_scope = 0; if (ifa->ifa_addr == NULL) { continue; } if (!(ifa->ifa_flags & IFF_UP)) { addrflag |= ARES_IFACE_IP_OFFLINE; } if (ifa->ifa_flags & IFF_LOOPBACK) { addrflag |= ARES_IFACE_IP_LOOPBACK; } if (ifa->ifa_addr->sa_family == AF_INET) { const struct sockaddr_in *sockaddr_in = (const struct sockaddr_in *)((void *)ifa->ifa_addr); addr.family = AF_INET; memcpy(&addr.addr.addr4, &sockaddr_in->sin_addr, sizeof(addr.addr.addr4)); /* netmask */ sockaddr_in = (struct sockaddr_in *)((void *)ifa->ifa_netmask); netmask = count_addr_bits((const void *)&sockaddr_in->sin_addr, 4); } else if (ifa->ifa_addr->sa_family == AF_INET6) { const struct sockaddr_in6 *sockaddr_in6 = (const struct sockaddr_in6 *)((void *)ifa->ifa_addr); addr.family = AF_INET6; memcpy(&addr.addr.addr6, &sockaddr_in6->sin6_addr, sizeof(addr.addr.addr6)); /* netmask */ sockaddr_in6 = (struct sockaddr_in6 *)((void *)ifa->ifa_netmask); netmask = count_addr_bits((const void *)&sockaddr_in6->sin6_addr, 16); # ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID ll_scope = sockaddr_in6->sin6_scope_id; # endif } else { /* unknown */ continue; } /* Name mismatch */ if (strcasecmp(ifa->ifa_name, name) != 0) { continue; } status = ares__iface_ips_add(ips, addrflag, ifa->ifa_name, &addr, netmask, ll_scope); if (status != ARES_SUCCESS) { goto done; } } done: freeifaddrs(ifap); return status; } #else static ares_status_t ares__iface_ips_enumerate(ares__iface_ips_t *ips, const char *name) { (void)ips; (void)name; return ARES_ENOTIMP; } #endif unsigned int ares__if_nametoindex(const char *name) { #ifdef HAVE_IF_NAMETOINDEX return if_nametoindex(name); #else ares_status_t status; ares__iface_ips_t *ips = NULL; size_t i; unsigned int index = 0; status = ares__iface_ips(&ips, ARES_IFACE_IP_V6 | ARES_IFACE_IP_LINKLOCAL, name); if (status != ARES_SUCCESS) { goto done; } for (i = 0; i < ares__iface_ips_cnt(ips); i++) { if (ares__iface_ips_get_flags(ips, i) & ARES_IFACE_IP_LINKLOCAL) { index = ares__iface_ips_get_ll_scope(ips, i); goto done; } } done: ares__iface_ips_destroy(ips); return index; #endif } const char *ares__if_indextoname(unsigned int index, char *name, size_t name_len) { #ifdef HAVE_IF_INDEXTONAME if (name_len < IF_NAMESIZE) { return NULL; } return if_indextoname(index, name); #else ares_status_t status; ares__iface_ips_t *ips = NULL; size_t i; const char *ptr = NULL; if (name_len < IF_NAMESIZE) { goto done; } if (index == 0) { goto done; } status = ares__iface_ips(&ips, ARES_IFACE_IP_V6 | ARES_IFACE_IP_LINKLOCAL, NULL); if (status != ARES_SUCCESS) { goto done; } for (i = 0; i < ares__iface_ips_cnt(ips); i++) { if (ares__iface_ips_get_flags(ips, i) & ARES_IFACE_IP_LINKLOCAL && ares__iface_ips_get_ll_scope(ips, i) == index) { ares_strcpy(name, ares__iface_ips_get_name(ips, i), name_len); ptr = name; goto done; } } done: ares__iface_ips_destroy(ips); return ptr; #endif }