31
31
32
32
namespace {
33
33
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
+
34
58
// Linux and FreeBSD 14.0+. For FreeBSD 13.2 the code can be compiled but
35
59
// running it requires loading a special kernel module, otherwise socket(AF_NETLINK,...)
36
60
// will fail, so we skip that.
@@ -167,16 +191,6 @@ std::optional<CNetAddr> QueryDefaultGatewayImpl(sa_family_t family)
167
191
#define ROUNDUP32 (a ) \
168
192
((a) > 0 ? (1 + (((a) - 1 ) | (sizeof (uint32_t ) - 1 ))) : sizeof (uint32_t ))
169
193
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
-
180
194
// ! MacOS: Get default gateway from route table. See route(4) for the format.
181
195
std::optional<CNetAddr> QueryDefaultGatewayImpl (sa_family_t family)
182
196
{
@@ -210,9 +224,9 @@ std::optional<CNetAddr> QueryDefaultGatewayImpl(sa_family_t family)
210
224
const struct sockaddr * sa = (const struct sockaddr *)(buf.data () + sa_pos);
211
225
if ((sa_pos + sa->sa_len ) > next_msg_pos) return std::nullopt;
212
226
if (i == RTAX_DST) {
213
- dst = FromSockAddr (sa);
227
+ dst = FromSockAddr (sa, sa-> sa_len );
214
228
} else if (i == RTAX_GATEWAY) {
215
- gateway = FromSockAddr (sa);
229
+ gateway = FromSockAddr (sa, sa-> sa_len );
216
230
}
217
231
// Skip sockaddr entries for bit flags we're not interested in,
218
232
// move cursor.
@@ -269,9 +283,47 @@ std::vector<CNetAddr> GetLocalAddresses()
269
283
{
270
284
std::vector<CNetAddr> addresses;
271
285
#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
+ }
275
327
}
276
328
#elif (HAVE_DECL_GETIFADDRS && HAVE_DECL_FREEIFADDRS)
277
329
struct ifaddrs * myaddrs;
@@ -281,12 +333,9 @@ std::vector<CNetAddr> GetLocalAddresses()
281
333
if (ifa->ifa_addr == nullptr ) continue ;
282
334
if ((ifa->ifa_flags & IFF_UP) == 0 ) continue ;
283
335
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);
290
339
}
291
340
}
292
341
freeifaddrs (myaddrs);
0 commit comments