quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

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 }