Skip to content

Commit

Permalink
net: Use GetAdaptersAddresses to get local addresses on Windows
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
laanwj committed Oct 1, 2024
1 parent 5858e4c commit 0468e8a
Showing 1 changed file with 53 additions and 22 deletions.
75 changes: 53 additions & 22 deletions src/util/netif.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,16 @@

namespace {

std::optional<CNetAddr> FromSockAddr(const struct sockaddr* addr, socklen_t sa_len)
{
// Fill in a CService from the sockaddr, then drop the port part.
CService service;
if (service.SetSockAddr(addr, sa_len)) {
return (CNetAddr)service;
}
return std::nullopt;
}

// Linux and FreeBSD 14.0+. For FreeBSD 13.2 the code can be compiled but
// running it requires loading a special kernel module, otherwise socket(AF_NETLINK,...)
// will fail, so we skip that.
Expand Down Expand Up @@ -167,16 +177,6 @@ std::optional<CNetAddr> QueryDefaultGatewayImpl(sa_family_t family)
#define ROUNDUP32(a) \
((a) > 0 ? (1 + (((a) - 1) | (sizeof(uint32_t) - 1))) : sizeof(uint32_t))

std::optional<CNetAddr> FromSockAddr(const struct sockaddr* addr)
{
// Fill in a CService from the sockaddr, then drop the port part.
CService service;
if (service.SetSockAddr(addr, addr->sa_len)) {
return (CNetAddr)service;
}
return std::nullopt;
}

//! MacOS: Get default gateway from route table. See route(4) for the format.
std::optional<CNetAddr> QueryDefaultGatewayImpl(sa_family_t family)
{
Expand Down Expand Up @@ -210,9 +210,9 @@ std::optional<CNetAddr> QueryDefaultGatewayImpl(sa_family_t family)
const struct sockaddr* sa = (const struct sockaddr*)(buf.data() + sa_pos);
if ((sa_pos + sa->sa_len) > next_msg_pos) return std::nullopt;
if (i == RTAX_DST) {
dst = FromSockAddr(sa);
dst = FromSockAddr(sa, sa->sa_len);
} else if (i == RTAX_GATEWAY) {
gateway = FromSockAddr(sa);
gateway = FromSockAddr(sa, sa->sa_len);
}
// Skip sockaddr entries for bit flags we're not interested in,
// move cursor.
Expand Down Expand Up @@ -269,9 +269,44 @@ std::vector<CNetAddr> GetLocalAddresses()
{
std::vector<CNetAddr> addresses;
#ifdef WIN32
char pszHostName[256] = "";
if (gethostname(pszHostName, sizeof(pszHostName)) != SOCKET_ERROR) {
addresses = LookupHost(pszHostName, 0, true);
DWORD status = 0;
std::vector<std::byte> out_buf(15000, {}); // Start with 15KB allocation as recommended in GetAdaptersAddresses documentation.
do {
ULONG out_buf_len = out_buf.size();
status = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME,
nullptr, reinterpret_cast<PIP_ADAPTER_ADDRESSES>(out_buf.data()), &out_buf_len);
if (status == ERROR_BUFFER_OVERFLOW) {
// If status == ERROR_BUFFER_OVERFLOW, out_buf_len will contain the needed size.
// Unfortunately, this cannot be fully relied on, because another process may have added interfaces.
// So to avoid getting stuck due to a race condition, double the buffer size at least
// once before retrying.
do {
out_buf.resize(out_buf.size() * 2);
} while (out_buf.size() < out_buf_len);
} else {
break;
}
} while (status == ERROR_BUFFER_OVERFLOW);

if (status != NO_ERROR) {
// This includes ERROR_NO_DATA if there are no addresses.
LogPrintLevel(BCLog::NET, BCLog::Level::Error, "Could not get local addreses: %s\n", NetworkErrorString(status));
return addresses;
}

for (PIP_ADAPTER_ADDRESSES cur_adapter = reinterpret_cast<PIP_ADAPTER_ADDRESSES>(out_buf.data());
cur_adapter != nullptr; cur_adapter = cur_adapter->Next) {
if (cur_adapter->OperStatus != IfOperStatusUp) continue;
if (cur_adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK) continue;

for (PIP_ADAPTER_UNICAST_ADDRESS cur_address = cur_adapter->FirstUnicastAddress;
cur_address != nullptr; cur_address = cur_address->Next) {
// "The IP address is a cluster address and should not be used by most applications."
if ((cur_address->Flags & IP_ADAPTER_ADDRESS_TRANSIENT) != 0) continue;

std::optional<CNetAddr> addr = FromSockAddr(cur_address->Address.lpSockaddr, static_cast<socklen_t>(cur_address->Address.iSockaddrLength));
if (addr) addresses.push_back(*addr);
}
}
#elif (HAVE_DECL_GETIFADDRS && HAVE_DECL_FREEIFADDRS)
struct ifaddrs* myaddrs;
Expand All @@ -281,13 +316,9 @@ std::vector<CNetAddr> GetLocalAddresses()
if (ifa->ifa_addr == nullptr) continue;
if ((ifa->ifa_flags & IFF_UP) == 0) continue;
if ((ifa->ifa_flags & IFF_LOOPBACK) != 0) continue;
if (ifa->ifa_addr->sa_family == AF_INET) {
struct sockaddr_in* s4 = (struct sockaddr_in*)(ifa->ifa_addr);
addresses.emplace_back(s4->sin_addr);
} else if (ifa->ifa_addr->sa_family == AF_INET6) {
struct sockaddr_in6* s6 = (struct sockaddr_in6*)(ifa->ifa_addr);
addresses.emplace_back(s6->sin6_addr);
}

std::optional<CNetAddr> addr = FromSockAddr(ifa->ifa_addr, CService::ANY_LEN);
if (addr) addresses.push_back(*addr);
}
freeifaddrs(myaddrs);
}
Expand Down

0 comments on commit 0468e8a

Please sign in to comment.