• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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