Skip to content

Commit b9d4d5f

Browse files
committed
net: Use GetAdaptersAddresses to get local addresses on Windows
Instead of a `gethostname` hack, use the official way of calling `GetAdaptersAddresses` to get local network addresses on Windows. As additional cleanup, move out `FromSockAddr` from MacOS and use it everywhere appropriate. Suggested by Ava Chow.
1 parent 42251e0 commit b9d4d5f

File tree

1 file changed

+70
-21
lines changed

1 file changed

+70
-21
lines changed

src/common/netif.cpp

Lines changed: 70 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,30 @@
3131

3232
namespace {
3333

34+
//! Return CNetAddr for the specified OS-level network address.
35+
//! If a length is not given, it is taken to be sizeof(struct sockaddr_*) for the family.
36+
std::optional<CNetAddr> FromSockAddr(const struct sockaddr* addr, std::optional<socklen_t> sa_len_opt)
37+
{
38+
socklen_t sa_len = 0;
39+
if (sa_len_opt.has_value()) {
40+
sa_len = *sa_len_opt;
41+
} else {
42+
// If sockaddr length was not specified, determine it from the family.
43+
switch (addr->sa_family) {
44+
case AF_INET: sa_len = sizeof(struct sockaddr_in); break;
45+
case AF_INET6: sa_len = sizeof(struct sockaddr_in6); break;
46+
default:
47+
return std::nullopt;
48+
}
49+
}
50+
// Fill in a CService from the sockaddr, then drop the port part.
51+
CService service;
52+
if (service.SetSockAddr(addr, sa_len)) {
53+
return (CNetAddr)service;
54+
}
55+
return std::nullopt;
56+
}
57+
3458
// Linux and FreeBSD 14.0+. For FreeBSD 13.2 the code can be compiled but
3559
// running it requires loading a special kernel module, otherwise socket(AF_NETLINK,...)
3660
// will fail, so we skip that.
@@ -167,16 +191,6 @@ std::optional<CNetAddr> QueryDefaultGatewayImpl(sa_family_t family)
167191
#define ROUNDUP32(a) \
168192
((a) > 0 ? (1 + (((a) - 1) | (sizeof(uint32_t) - 1))) : sizeof(uint32_t))
169193

170-
std::optional<CNetAddr> FromSockAddr(const struct sockaddr* addr)
171-
{
172-
// Fill in a CService from the sockaddr, then drop the port part.
173-
CService service;
174-
if (service.SetSockAddr(addr, addr->sa_len)) {
175-
return (CNetAddr)service;
176-
}
177-
return std::nullopt;
178-
}
179-
180194
//! MacOS: Get default gateway from route table. See route(4) for the format.
181195
std::optional<CNetAddr> QueryDefaultGatewayImpl(sa_family_t family)
182196
{
@@ -210,9 +224,9 @@ std::optional<CNetAddr> QueryDefaultGatewayImpl(sa_family_t family)
210224
const struct sockaddr* sa = (const struct sockaddr*)(buf.data() + sa_pos);
211225
if ((sa_pos + sa->sa_len) > next_msg_pos) return std::nullopt;
212226
if (i == RTAX_DST) {
213-
dst = FromSockAddr(sa);
227+
dst = FromSockAddr(sa, sa->sa_len);
214228
} else if (i == RTAX_GATEWAY) {
215-
gateway = FromSockAddr(sa);
229+
gateway = FromSockAddr(sa, sa->sa_len);
216230
}
217231
// Skip sockaddr entries for bit flags we're not interested in,
218232
// move cursor.
@@ -269,9 +283,47 @@ std::vector<CNetAddr> GetLocalAddresses()
269283
{
270284
std::vector<CNetAddr> addresses;
271285
#ifdef WIN32
272-
char pszHostName[256] = "";
273-
if (gethostname(pszHostName, sizeof(pszHostName)) != SOCKET_ERROR) {
274-
addresses = LookupHost(pszHostName, 0, true);
286+
DWORD status = 0;
287+
constexpr size_t MAX_ADAPTER_ADDR_SIZE = 4 * 1000 * 1000; // Absolute maximum size of adapter addresses structure we're willing to handle, as a precaution.
288+
std::vector<std::byte> out_buf(15000, {}); // Start with 15KB allocation as recommended in GetAdaptersAddresses documentation.
289+
while (true) {
290+
ULONG out_buf_len = out_buf.size();
291+
status = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME,
292+
nullptr, reinterpret_cast<PIP_ADAPTER_ADDRESSES>(out_buf.data()), &out_buf_len);
293+
if (status == ERROR_BUFFER_OVERFLOW && out_buf.size() < MAX_ADAPTER_ADDR_SIZE) {
294+
// If status == ERROR_BUFFER_OVERFLOW, out_buf_len will contain the needed size.
295+
// Unfortunately, this cannot be fully relied on, because another process may have added interfaces.
296+
// So to avoid getting stuck due to a race condition, double the buffer size at least
297+
// once before retrying (but only up to the maximum allowed size).
298+
out_buf.resize(std::min(std::max<size_t>(out_buf_len, out_buf.size()) * 2, MAX_ADAPTER_ADDR_SIZE));
299+
} else {
300+
break;
301+
}
302+
}
303+
304+
if (status != NO_ERROR) {
305+
// This includes ERROR_NO_DATA if there are no addresses and thus there's not even one PIP_ADAPTER_ADDRESSES
306+
// record in the returned structure.
307+
LogPrintLevel(BCLog::NET, BCLog::Level::Error, "Could not get local adapter addreses: %s\n", NetworkErrorString(status));
308+
return addresses;
309+
}
310+
311+
// Iterate over network adapters.
312+
for (PIP_ADAPTER_ADDRESSES cur_adapter = reinterpret_cast<PIP_ADAPTER_ADDRESSES>(out_buf.data());
313+
cur_adapter != nullptr; cur_adapter = cur_adapter->Next) {
314+
if (cur_adapter->OperStatus != IfOperStatusUp) continue;
315+
if (cur_adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK) continue;
316+
317+
// Iterate over unicast addresses for adapter, the only address type we're interested in.
318+
for (PIP_ADAPTER_UNICAST_ADDRESS cur_address = cur_adapter->FirstUnicastAddress;
319+
cur_address != nullptr; cur_address = cur_address->Next) {
320+
// "The IP address is a cluster address and should not be used by most applications."
321+
if ((cur_address->Flags & IP_ADAPTER_ADDRESS_TRANSIENT) != 0) continue;
322+
323+
if (std::optional<CNetAddr> addr = FromSockAddr(cur_address->Address.lpSockaddr, static_cast<socklen_t>(cur_address->Address.iSockaddrLength))) {
324+
addresses.push_back(*addr);
325+
}
326+
}
275327
}
276328
#elif (HAVE_DECL_GETIFADDRS && HAVE_DECL_FREEIFADDRS)
277329
struct ifaddrs* myaddrs;
@@ -281,12 +333,9 @@ std::vector<CNetAddr> GetLocalAddresses()
281333
if (ifa->ifa_addr == nullptr) continue;
282334
if ((ifa->ifa_flags & IFF_UP) == 0) continue;
283335
if ((ifa->ifa_flags & IFF_LOOPBACK) != 0) continue;
284-
if (ifa->ifa_addr->sa_family == AF_INET) {
285-
struct sockaddr_in* s4 = (struct sockaddr_in*)(ifa->ifa_addr);
286-
addresses.emplace_back(s4->sin_addr);
287-
} else if (ifa->ifa_addr->sa_family == AF_INET6) {
288-
struct sockaddr_in6* s6 = (struct sockaddr_in6*)(ifa->ifa_addr);
289-
addresses.emplace_back(s6->sin6_addr);
336+
337+
if (std::optional<CNetAddr> addr = FromSockAddr(ifa->ifa_addr, std::nullopt)) {
338+
addresses.push_back(*addr);
290339
}
291340
}
292341
freeifaddrs(myaddrs);

0 commit comments

Comments
 (0)