Instrument Neutral Distributed Interface INDI  2.0.2
NetIF.hpp
Go to the documentation of this file.
1 /*
2 Copyright (c) 2019,
3 Battelle Memorial Institute; Lawrence Livermore National Security, LLC; Alliance for Sustainable
4 Energy, LLC. See the top-level NOTICE for additional details. All rights reserved.
5 SPDX-License-Identifier: BSD-3-Clause
6 */
7 
8 #pragma once
9 
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string>
13 #include <vector>
14 
15 // other platforms might need different headers -- detecting with macros:
16 // https://sourceforge.net/p/predef/wiki/OperatingSystems/
17 // https://stackoverflow.com/questions/142508/how-do-i-check-os-with-a-preprocessor-directive
18 #if defined(_WIN32) || defined(__USE_W32_SOCKETS)
19  #include <winsock2.h>
20  #include <ws2tcpip.h>
21  #include <iphlpapi.h>
22  #ifdef _MSC_VER
23  #pragma comment(lib, "IPHLPAPI.lib")
24  #pragma comment(lib, "Ws2_32.lib")
25  #endif
26 #else
27  #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || \
28  defined(__bsdi__) || defined(__DragonFly__)
29  #include <sys/socket.h>
30  #include <netinet/in.h>
31  #endif
32  #include <arpa/inet.h>
33  #include <ifaddrs.h>
34 #endif
35 
36 namespace gmlc {
37 namespace netif {
38 #if defined(_WIN32) || defined(__USE_W32_SOCKETS)
39  using IF_ADDRS = PIP_ADAPTER_ADDRESSES;
40 #else
41  using IF_ADDRS = struct ifaddrs*;
42 #endif
43 
44 #if defined(_WIN32) || defined(__USE_W32_SOCKETS)
45  using IF_ADDRS_UNICAST = PIP_ADAPTER_UNICAST_ADDRESS;
46 #else
47  using IF_ADDRS_UNICAST = struct ifaddrs*;
48 #endif
49 
50 #ifdef __GNUC__
51  #pragma GCC diagnostic push
52  #pragma GCC diagnostic ignored "-Wstrict-aliasing"
53 #endif
54 
61  inline std::string addressToString(struct sockaddr* addr, int sa_len)
62  {
63 #if defined(__MINGW32__)
64  char addr_str[NI_MAXHOST];
65  if (getnameinfo(addr, sa_len, addr_str, NI_MAXHOST, 0, 0, NI_NUMERICHOST) != 0) {
66  return std::string();
67  }
68 #else
69  (void)sa_len;
70  int family = addr->sa_family;
71  char addr_str[INET6_ADDRSTRLEN];
72  void* src_addr = nullptr;
73  switch (family) {
74  case AF_INET:
75  src_addr = &(reinterpret_cast<struct sockaddr_in*>(addr)->sin_addr);
76  break;
77  case AF_INET6:
78  src_addr = &(reinterpret_cast<struct sockaddr_in6*>(addr)->sin6_addr);
79  break;
80  default: // Invalid address type for conversion to text
81  return std::string();
82  }
83  inet_ntop(family, src_addr, addr_str, INET6_ADDRSTRLEN);
84 #endif
85 
86  return std::string(addr_str);
87  }
88 
89 #ifdef __GNUC__
90  #pragma GCC diagnostic pop
91 #endif
92 
97  inline void freeAddresses(IF_ADDRS addrs)
98  {
99 #if defined(_WIN32) || defined(__USE_W32_SOCKETS)
100  HeapFree(GetProcessHeap(), 0, addrs);
101 #else
102  freeifaddrs(addrs);
103 #endif
104  }
105 
115  inline auto getAddresses(int family, IF_ADDRS* addrs)
116  {
117 #if defined(_WIN32) || defined(__USE_W32_SOCKETS)
118  // Windows API:
119  // https://docs.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-getadaptersaddresses
120  DWORD rv = 0;
121  ULONG bufLen = 15000; // recommended size from Windows API docs to avoid error
122  ULONG iter = 0;
123  do {
124  *addrs = (IP_ADAPTER_ADDRESSES*)HeapAlloc(GetProcessHeap(), 0, bufLen);
125  if (*addrs == NULL) {
126  return -1;
127  }
128 
129  rv = GetAdaptersAddresses(family, GAA_FLAG_INCLUDE_PREFIX, NULL, *addrs, &bufLen);
130  if (rv == ERROR_BUFFER_OVERFLOW) {
131  freeAddresses(*addrs);
132  *addrs = NULL;
133  bufLen = bufLen * 2; // Double buffer length for the next attempt
134  } else {
135  break;
136  }
137  iter++;
138  } while ((rv == ERROR_BUFFER_OVERFLOW) && (iter < 3));
139  if (rv != NO_ERROR) {
140  // May want to change this depending on the type of error
141  return -1;
142  }
143  return 0;
144 #else
145  (void)family;
146  return getifaddrs(addrs);
147 #endif
148  }
149 
155  inline auto getSockAddr(IF_ADDRS_UNICAST addr)
156  {
157 #if defined(_WIN32) || defined(__USE_W32_SOCKETS)
158  return addr->Address.lpSockaddr;
159 #else
160  return addr->ifa_addr;
161 #endif
162  }
163 
170  {
171 #if defined(_WIN32) || defined(__USE_W32_SOCKETS)
172  return addr->Address.iSockaddrLength;
173 #else
174  return sizeof(*addr->ifa_addr);
175 #endif
176  }
177 
187  {
188 #if defined(_WIN32) || defined(__USE_W32_SOCKETS)
189  (void)family;
190  return addrs->Next;
191 #else
192  auto next = addrs->ifa_next;
193  while (next != NULL) {
194  // Skip entries with a null sockaddr
195  auto next_sockaddr = getSockAddr(next);
196  if (next_sockaddr == NULL) {
197  next = next->ifa_next;
198  continue;
199  }
200 
201  int next_family = next_sockaddr->sa_family;
202  // Skip if the family is not IPv4 or IPv6
203  if (next_family != AF_INET && next_family != AF_INET6) {
204  next = next->ifa_next;
205  continue;
206  }
207  // Skip if a specific IP family was requested and the family doesn't match
208  if ((family == AF_INET || family == AF_INET6) && family != next_family) {
209  next = next->ifa_next;
210  continue;
211  }
212  // An address entry meeting the requirements was found, return it
213  return next;
214  }
215  return nullptr;
216 #endif
217  }
218 
226  std::vector<std::string> getInterfaceAddresses(int family)
227  {
228  std::vector<std::string> result_list;
229 
230  IF_ADDRS allAddrs = NULL;
231 
232  getAddresses(family, &allAddrs);
233 
234 #if defined(_WIN32) || defined(__USE_W32_SOCKETS)
235  WSADATA wsaData;
236  if (WSAStartup(0x202, &wsaData) != 0) {
237  return result_list;
238  }
239  auto winAddrs = allAddrs;
240  while (winAddrs) {
241  auto addrs = winAddrs->FirstUnicastAddress;
242 #else
243  auto addrs = allAddrs;
244 #endif
245 
246  for (auto a = addrs; a != NULL; a = getNextAddress(family, a)) {
247  std::string ipAddr = addressToString(getSockAddr(a), getSockAddrLen(a));
248  if (!ipAddr.empty()) {
249  result_list.push_back(ipAddr);
250  }
251  }
252 
253 #if defined(_WIN32) || defined(__USE_W32_SOCKETS)
254  winAddrs = winAddrs->Next;
255  }
256  WSACleanup();
257 #endif
258 
259  if (allAddrs) {
260  freeAddresses(allAddrs);
261  }
262  return result_list;
263  }
264 
269  std::vector<std::string> getInterfaceAddressesV4() { return getInterfaceAddresses(AF_INET); }
270 
275  std::vector<std::string> getInterfaceAddressesV6() { return getInterfaceAddresses(AF_INET6); }
276 
282  std::vector<std::string> getInterfaceAddressesAll() { return getInterfaceAddresses(AF_UNSPEC); }
283 } // namespace netif
284 } // namespace gmlc
struct ifaddrs * IF_ADDRS
Definition: NetIF.hpp:41
auto getAddresses(int family, IF_ADDRS *addrs)
Definition: NetIF.hpp:115
std::string addressToString(struct sockaddr *addr, int sa_len)
Definition: NetIF.hpp:61
auto getSockAddr(IF_ADDRS_UNICAST addr)
Definition: NetIF.hpp:155
std::vector< std::string > getInterfaceAddressesAll()
Definition: NetIF.hpp:282
struct ifaddrs * IF_ADDRS_UNICAST
Definition: NetIF.hpp:47
void freeAddresses(IF_ADDRS addrs)
Definition: NetIF.hpp:97
std::vector< std::string > getInterfaceAddressesV4()
Definition: NetIF.hpp:269
std::vector< std::string > getInterfaceAddresses(int family)
Definition: NetIF.hpp:226
int getSockAddrLen(IF_ADDRS_UNICAST addr)
Definition: NetIF.hpp:169
IF_ADDRS_UNICAST getNextAddress(int family, IF_ADDRS_UNICAST addrs)
Definition: NetIF.hpp:186
std::vector< std::string > getInterfaceAddressesV6()
Definition: NetIF.hpp:275
Definition: NetIF.hpp:36