Instrument Neutral Distributed Interface INDI 2.2.5
NetIF.hpp
Go to the documentation of this file.
1/*
2Copyright (c) 2019,
3Battelle Memorial Institute; Lawrence Livermore National Security, LLC; Alliance for Sustainable
4Energy, LLC. See the top-level NOTICE for additional details. All rights reserved.
5SPDX-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
36namespace gmlc {
37namespace 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
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
std::vector< std::string > getInterfaceAddressesAll()
Definition NetIF.hpp:282
std::vector< std::string > getInterfaceAddresses(int family)
Definition NetIF.hpp:226
ifaddrs * IF_ADDRS
Definition NetIF.hpp:41
ifaddrs * IF_ADDRS_UNICAST
Definition NetIF.hpp:47
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
void freeAddresses(IF_ADDRS addrs)
Definition NetIF.hpp:97
std::vector< std::string > getInterfaceAddressesV4()
Definition NetIF.hpp:269
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