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