xref: /libuv/src/unix/bsd-ifaddrs.c (revision 7ae0c954)
1 /* Copyright libuv project contributors. All rights reserved.
2  *
3  * Permission is hereby granted, free of charge, to any person obtaining a copy
4  * of this software and associated documentation files (the "Software"), to
5  * deal in the Software without restriction, including without limitation the
6  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7  * sell copies of the Software, and to permit persons to whom the Software is
8  * furnished to do so, subject to the following conditions:
9  *
10  * The above copyright notice and this permission notice shall be included in
11  * all copies or substantial portions of the Software.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19  * IN THE SOFTWARE.
20  */
21 
22 #include "uv.h"
23 #include "internal.h"
24 
25 #include <errno.h>
26 #include <stddef.h>
27 
28 #include <ifaddrs.h>
29 #include <net/if.h>
30 #if !defined(__CYGWIN__) && !defined(__MSYS__) && !defined(__GNU__)
31 #include <net/if_dl.h>
32 #endif
33 
34 #if defined(__HAIKU__)
35 #define IFF_RUNNING IFF_LINK
36 #endif
37 
uv__ifaddr_exclude(struct ifaddrs * ent,int exclude_type)38 static int uv__ifaddr_exclude(struct ifaddrs *ent, int exclude_type) {
39   if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING)))
40     return 1;
41   if (ent->ifa_addr == NULL)
42     return 1;
43 #if !defined(__CYGWIN__) && !defined(__MSYS__) && !defined(__GNU__)
44   /*
45    * If `exclude_type` is `UV__EXCLUDE_IFPHYS`, return whether `sa_family`
46    * equals `AF_LINK`. Otherwise, the result depends on the operating
47    * system with `AF_LINK` or `PF_INET`.
48    */
49   if (exclude_type == UV__EXCLUDE_IFPHYS)
50     return (ent->ifa_addr->sa_family != AF_LINK);
51 #endif
52 #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__) || \
53     defined(__HAIKU__)
54   /*
55    * On BSD getifaddrs returns information related to the raw underlying
56    * devices. We're not interested in this information.
57    */
58   if (ent->ifa_addr->sa_family == AF_LINK)
59     return 1;
60 #elif defined(__NetBSD__) || defined(__OpenBSD__)
61   if (ent->ifa_addr->sa_family != PF_INET &&
62       ent->ifa_addr->sa_family != PF_INET6)
63     return 1;
64 #endif
65   return 0;
66 }
67 
uv_interface_addresses(uv_interface_address_t ** addresses,int * count)68 int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
69   struct ifaddrs* addrs;
70   struct ifaddrs* ent;
71   uv_interface_address_t* address;
72 #if !(defined(__CYGWIN__) || defined(__MSYS__)) && !defined(__GNU__)
73   int i;
74 #endif
75 
76   *count = 0;
77   *addresses = NULL;
78 
79   if (getifaddrs(&addrs) != 0)
80     return UV__ERR(errno);
81 
82   /* Count the number of interfaces */
83   for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
84     if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR))
85       continue;
86     (*count)++;
87   }
88 
89   if (*count == 0) {
90     freeifaddrs(addrs);
91     return 0;
92   }
93 
94   /* Make sure the memory is initiallized to zero using calloc() */
95   *addresses = uv__calloc(*count, sizeof(**addresses));
96 
97   if (*addresses == NULL) {
98     freeifaddrs(addrs);
99     return UV_ENOMEM;
100   }
101 
102   address = *addresses;
103 
104   for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
105     if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR))
106       continue;
107 
108     address->name = uv__strdup(ent->ifa_name);
109 
110     if (ent->ifa_addr->sa_family == AF_INET6) {
111       address->address.address6 = *((struct sockaddr_in6*) ent->ifa_addr);
112     } else {
113       address->address.address4 = *((struct sockaddr_in*) ent->ifa_addr);
114     }
115 
116     if (ent->ifa_netmask == NULL) {
117       memset(&address->netmask, 0, sizeof(address->netmask));
118     } else if (ent->ifa_netmask->sa_family == AF_INET6) {
119       address->netmask.netmask6 = *((struct sockaddr_in6*) ent->ifa_netmask);
120     } else {
121       address->netmask.netmask4 = *((struct sockaddr_in*) ent->ifa_netmask);
122     }
123 
124     address->is_internal = !!(ent->ifa_flags & IFF_LOOPBACK);
125 
126     address++;
127   }
128 
129 #if !(defined(__CYGWIN__) || defined(__MSYS__)) && !defined(__GNU__)
130   /* Fill in physical addresses for each interface */
131   for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
132     if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFPHYS))
133       continue;
134 
135     address = *addresses;
136 
137     for (i = 0; i < *count; i++) {
138       if (strcmp(address->name, ent->ifa_name) == 0) {
139         struct sockaddr_dl* sa_addr;
140         sa_addr = (struct sockaddr_dl*)(ent->ifa_addr);
141         memcpy(address->phys_addr, LLADDR(sa_addr), sizeof(address->phys_addr));
142       }
143       address++;
144     }
145   }
146 #endif
147 
148   freeifaddrs(addrs);
149 
150   return 0;
151 }
152 
153 
uv_free_interface_addresses(uv_interface_address_t * addresses,int count)154 void uv_free_interface_addresses(uv_interface_address_t* addresses,
155                                  int count) {
156   int i;
157 
158   for (i = 0; i < count; i++) {
159     uv__free(addresses[i].name);
160   }
161 
162   uv__free(addresses);
163 }
164