Repository: bitcoin/bitcoin
File: src/netbase.cpp (C++)

Code before:
// Need ample time for negotiation for very slow proxies such as Tor
std::chrono::milliseconds g_socks5_recv_timeout = 20s;
CThreadInterrupt g_socks5_interrupt;

ReachableNets g_reachable_nets;

std::vector<CNetAddr> WrappedGetAddrInfo(const std::string& name, bool allow_lookup)
{
    addrinfo ai_hint{};
    // We want a TCP port, which is a streaming socket type
    ai_hint.ai_socktype = SOCK_STREAM;
    ai_hint.ai_protocol = IPPROTO_TCP;
    // We don't care which address family (IPv4 or IPv6) is returned
    ai_hint.ai_family = AF_UNSPEC;

    // If we allow lookups of hostnames, use the AI_ADDRCONFIG flag to only
    // return addresses whose family we have an address configured for.
    //
    // If we don't allow lookups, then use the AI_NUMERICHOST flag for
    // getaddrinfo to only decode numerical network addresses and suppress
    // hostname lookups.
    const int ai_flags = allow_lookup ? AI_ADDRCONFIG : AI_NUMERICHOST;

    std::vector<CNetAddr> resolved_addresses;

    auto callgetaddrinfo = [&](int flags) -> int {
        ai_hint.ai_flags = flags;
        addrinfo* ai_res{nullptr};
        const int n_err{getaddrinfo(name.c_str(), nullptr, &ai_hint, &ai_res)};
        if (n_err != 0) {
            return n_err;
        }

        // Traverse the linked list of addrinfo structures
        addrinfo* ai_trav{ai_res};
        while (ai_trav != nullptr) {
            if (ai_trav->ai_family == AF_INET) {
                assert(ai_trav->ai_addrlen >= sizeof(sockaddr_in));
                resolved_addresses.emplace_back(reinterpret_cast<sockaddr_in*>(ai_trav->ai_addr)->sin_addr);
            }
            if (ai_trav->ai_family == AF_INET6) {
                assert(ai_trav->ai_addrlen >= sizeof(sockaddr_in6));
                const sockaddr_in6* s6{reinterpret_cast<sockaddr_in6*>(ai_trav->ai_addr)};
                resolved_addresses.emplace_back(s6->sin6_addr, s6->sin6_scope_id);
            }
            ai_trav = ai_trav->ai_next;
        }
        freeaddrinfo(ai_res);
        return 0;
    };


Review comment (bug):
Unless I'm overlooking something, might be able to simplify/shorten the change by calling `getaddrinfo()` again just when needed.  `ai_res` should be set up leaving the `if` either way (or we return an empty vector).  Would avoid the lambda function and might also get rid of the copy/standalone `ai_flags`.

Here's a commit on a fork:
https://github.com/bitcoin/bitcoin/commit/bba1b42a8e533aa33319c7c9ca471dc0fab2ddd8

```diff
diff --git a/src/netbase.cpp b/src/netbase.cpp
index e231766487..8914bbb2c4 100644
--- a/src/netbase.cpp
+++ b/src/netbase.cpp
@@ -59,7 +59,17 @@ std::vector<CNetAddr> WrappedGetAddrInfo(const std::string& name, bool allow_loo
     addrinfo* ai_res{nullptr};
     const int n_err{getaddrinfo(name.c_str(), nullptr, &ai_hint, &ai_res)};
     if (n_err != 0) {
-        return {};
+        if ((ai_hint.ai_flags & AI_ADDRCONFIG) == AI_ADDRCONFIG) {
+            // AI_ADDRCONFIG on some systems may exclude loopback-only addresses
+            // If first lookup failed we perform a second lookup without AI_ADDRCONFIG
+            ai_hint.ai_flags = (ai_hint.ai_flags & ~AI_ADDRCONFIG);
+            const int n_err_retry{getaddrinfo(name.c_str(), nullptr, &ai_hint, &ai_res)};
+            if (n_err_retry != 0) {
+                return {};
+            }
+        } else {
+            return {};
+        }
     }
 
     // Traverse the linked list starting with ai_trav.
```

Note:  I did not heavily test this yet.

Code after:
// Need ample time for negotiation for very slow proxies such as Tor
std::chrono::milliseconds g_socks5_recv_timeout = 20s;
CThreadInterrupt g_socks5_interrupt;

ReachableNets g_reachable_nets;

std::vector<CNetAddr> WrappedGetAddrInfo(const std::string& name, bool allow_lookup)
{
    addrinfo ai_hint{};
    // We want a TCP port, which is a streaming socket type
    ai_hint.ai_socktype = SOCK_STREAM;
    ai_hint.ai_protocol = IPPROTO_TCP;
    // We don't care which address family (IPv4 or IPv6) is returned
    ai_hint.ai_family = AF_UNSPEC;

    // If we allow lookups of hostnames, use the AI_ADDRCONFIG flag to only
    // return addresses whose family we have an address configured for.
    //
    // If we don't allow lookups, then use the AI_NUMERICHOST flag for
    // getaddrinfo to only decode numerical network addresses and suppress
    // hostname lookups.
    ai_hint.ai_flags = allow_lookup ? AI_ADDRCONFIG : AI_NUMERICHOST;

    addrinfo* ai_res{nullptr};
    const int n_err{getaddrinfo(name.c_str(), nullptr, &ai_hint, &ai_res)};
    if (n_err != 0) {
        if ((ai_hint.ai_flags & AI_ADDRCONFIG) == AI_ADDRCONFIG) {
            // AI_ADDRCONFIG on some systems may exclude loopback-only addresses
            // If first lookup failed we perform a second lookup without AI_ADDRCONFIG
            ai_hint.ai_flags = (ai_hint.ai_flags & ~AI_ADDRCONFIG);
            const int n_err_retry{getaddrinfo(name.c_str(), nullptr, &ai_hint, &ai_res)};
            if (n_err_retry != 0) {
                return {};
            }
        } else {
            return {};
        }
    }

    // Traverse the linked list starting with ai_trav.
    addrinfo* ai_trav{ai_res};
    std::vector<CNetAddr> resolved_addresses;
    while (ai_trav != nullptr) {
        if (ai_trav->ai_family == AF_INET) {
            assert(ai_trav->ai_addrlen >= sizeof(sockaddr_in));
            resolved_addresses.emplace_back(reinterpret_cast<sockaddr_in*>(ai_trav->ai_addr)->sin_addr);
        }
        if (ai_trav->ai_family == AF_INET6) {
            assert(ai_trav->ai_addrlen >= sizeof(sockaddr_in6));
            const sockaddr_in6* s6{reinterpret_cast<sockaddr_in6*>(ai_trav->ai_addr)};
            resolved_addresses.emplace_back(s6->sin6_addr, s6->sin6_scope_id);