diff --git a/src/common/netif.cpp b/src/common/netif.cpp index 7424f977c7e..7bfec08fdd0 100644 --- a/src/common/netif.cpp +++ b/src/common/netif.cpp @@ -31,6 +31,30 @@ namespace { +//! Return CNetAddr for the specified OS-level network address. +//! If a length is not given, it is taken to be sizeof(struct sockaddr_*) for the family. +std::optional FromSockAddr(const struct sockaddr* addr, std::optional sa_len_opt) +{ + socklen_t sa_len = 0; + if (sa_len_opt.has_value()) { + sa_len = *sa_len_opt; + } else { + // If sockaddr length was not specified, determine it from the family. + switch (addr->sa_family) { + case AF_INET: sa_len = sizeof(struct sockaddr_in); break; + case AF_INET6: sa_len = sizeof(struct sockaddr_in6); break; + default: + return std::nullopt; + } + } + // 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. @@ -167,16 +191,6 @@ std::optional QueryDefaultGatewayImpl(sa_family_t family) #define ROUNDUP32(a) \ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(uint32_t) - 1))) : sizeof(uint32_t)) -std::optional 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 QueryDefaultGatewayImpl(sa_family_t family) { @@ -210,9 +224,9 @@ std::optional 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. @@ -269,9 +283,47 @@ std::vector GetLocalAddresses() { std::vector addresses; #ifdef WIN32 - char pszHostName[256] = ""; - if (gethostname(pszHostName, sizeof(pszHostName)) != SOCKET_ERROR) { - addresses = LookupHost(pszHostName, 0, true); + DWORD status = 0; + 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. + std::vector out_buf(15000, {}); // Start with 15KB allocation as recommended in GetAdaptersAddresses documentation. + while (true) { + 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(out_buf.data()), &out_buf_len); + if (status == ERROR_BUFFER_OVERFLOW && out_buf.size() < MAX_ADAPTER_ADDR_SIZE) { + // 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 (but only up to the maximum allowed size). + out_buf.resize(std::min(std::max(out_buf_len, out_buf.size()) * 2, MAX_ADAPTER_ADDR_SIZE)); + } else { + break; + } + } + + if (status != NO_ERROR) { + // This includes ERROR_NO_DATA if there are no addresses and thus there's not even one PIP_ADAPTER_ADDRESSES + // record in the returned structure. + LogPrintLevel(BCLog::NET, BCLog::Level::Error, "Could not get local adapter addreses: %s\n", NetworkErrorString(status)); + return addresses; + } + + // Iterate over network adapters. + for (PIP_ADAPTER_ADDRESSES cur_adapter = reinterpret_cast(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; + + // Iterate over unicast addresses for adapter, the only address type we're interested in. + 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; + + if (std::optional addr = FromSockAddr(cur_address->Address.lpSockaddr, static_cast(cur_address->Address.iSockaddrLength))) { + addresses.push_back(*addr); + } + } } #elif (HAVE_DECL_GETIFADDRS && HAVE_DECL_FREEIFADDRS) struct ifaddrs* myaddrs; @@ -281,12 +333,9 @@ std::vector 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); + + if (std::optional addr = FromSockAddr(ifa->ifa_addr, std::nullopt)) { + addresses.push_back(*addr); } } freeifaddrs(myaddrs);