diff --git a/src/AbstractCommand.cc b/src/AbstractCommand.cc index cba4e0b3..478dbf33 100644 --- a/src/AbstractCommand.cc +++ b/src/AbstractCommand.cc @@ -107,7 +107,11 @@ AbstractCommand::AbstractCommand requestGroup_->increaseStreamCommand(); requestGroup_->increaseNumCommand(); #ifdef ENABLE_ASYNC_DNS - if(e_->getOption()->getAsBool(PREF_DISABLE_IPV6)) { + if(!net::getIPv4AddrConfigured()) { + asyncNameResolverMan_->setIPv4(false); + } + if(!net::getIPv6AddrConfigured() || + e_->getOption()->getAsBool(PREF_DISABLE_IPV6)) { asyncNameResolverMan_->setIPv6(false); } #endif // ENABLE_ASYNC_DNS diff --git a/src/AsyncNameResolverMan.cc b/src/AsyncNameResolverMan.cc index 53f5daac..4e66cce1 100644 --- a/src/AsyncNameResolverMan.cc +++ b/src/AsyncNameResolverMan.cc @@ -69,14 +69,16 @@ void AsyncNameResolverMan::startAsync(const std::string& hostname, Command* command) { numResolver_ = 0; - if(ipv4_) { - startAsyncFamily(hostname, AF_INET, e, command); - ++numResolver_; - } + // Set IPv6 resolver first, so that we can push IPv6 address in + // front of IPv6 address in getResolvedAddress(). if(ipv6_) { startAsyncFamily(hostname, AF_INET6, e, command); ++numResolver_; } + if(ipv4_) { + startAsyncFamily(hostname, AF_INET, e, command); + ++numResolver_; + } A2_LOG_INFO(fmt(MSG_RESOLVING_HOSTNAME, command->getCuid(), hostname.c_str())); } @@ -150,11 +152,13 @@ void AsyncNameResolverMan::disableNameResolverCheck(size_t index, int AsyncNameResolverMan::getStatus() const { + size_t success = 0; size_t error = 0; for(size_t i = 0; i < numResolver_; ++i) { switch(asyncNameResolver_[i]->getStatus()) { case AsyncNameResolver::STATUS_SUCCESS: - return 1; + ++success; + break; case AsyncNameResolver::STATUS_ERROR: ++error; break; @@ -162,7 +166,13 @@ int AsyncNameResolverMan::getStatus() const break; } } - return error == numResolver_ ? -1 : 0; + if(success == numResolver_) { + return 1; + } else if(error == numResolver_) { + return -1; + } else { + return 0; + } } const std::string& AsyncNameResolverMan::getLastError() const diff --git a/src/SocketCore.cc b/src/SocketCore.cc index 619002be..11781aee 100644 --- a/src/SocketCore.cc +++ b/src/SocketCore.cc @@ -1546,6 +1546,86 @@ bool verifyHostname(const std::string& hostname, return false; } +namespace { +bool ipv4AddrConfigured = true; +bool ipv6AddrConfigured = true; +} // namespace + +void checkAddrconfig() +{ + // TODO Use GetAdaptersAddresses() for Mingw +#ifdef HAVE_GETIFADDRS + A2_LOG_INFO("Checking configured addresses"); + ipv4AddrConfigured = false; + ipv6AddrConfigured = false; + ifaddrs* ifaddr = 0; + int rv; + rv = getifaddrs(&ifaddr); + if(rv == -1) { + int errNum = SOCKET_ERRNO; + A2_LOG_INFO(fmt("getifaddrs failed. Cause: %s", errorMsg(errNum).c_str())); + return; + } + auto_delete ifaddrDeleter(ifaddr, freeifaddrs); + char host[NI_MAXHOST]; + sockaddr_union ad; + for(ifaddrs* ifa = ifaddr; ifa; ifa = ifa->ifa_next) { + if(!ifa->ifa_addr) { + continue; + } + bool found = false; + size_t addrlen = 0; + switch(ifa->ifa_addr->sa_family) { + case AF_INET: { + addrlen = sizeof(sockaddr_in); + memcpy(&ad.storage, ifa->ifa_addr, addrlen); + if(ad.in.sin_addr.s_addr != htonl(INADDR_LOOPBACK)) { + ipv4AddrConfigured = true; + found = true; + } + break; + } + case AF_INET6: { + addrlen = sizeof(sockaddr_in6); + memcpy(&ad.storage, ifa->ifa_addr, addrlen); + if(!IN6_IS_ADDR_LOOPBACK(&ad.in6.sin6_addr) && + !IN6_IS_ADDR_LINKLOCAL(&ad.in6.sin6_addr)) { + ipv6AddrConfigured = true; + found = true; + } + break; + } + default: + continue; + } + rv = getnameinfo(ifa->ifa_addr, addrlen, host, NI_MAXHOST, 0, 0, + NI_NUMERICHOST); + if(rv == 0) { + if(found) { + A2_LOG_INFO(fmt("Found configured address: %s", host)); + } else { + A2_LOG_INFO(fmt("Not considered: %s", host)); + } + } + } + A2_LOG_INFO(fmt("IPv4 configured=%d, IPv6 configured=%d", + ipv4AddrConfigured, ipv6AddrConfigured)); +#else // !HAVE_GETIFADDRS + A2_LOG_INFO("getifaddrs is not available. Assume IPv4 and IPv6 addresses" + " are configured."); +#endif // !HAVE_GETIFADDRS +} + +bool getIPv4AddrConfigured() +{ + return ipv4AddrConfigured; +} + +bool getIPv6AddrConfigured() +{ + return ipv6AddrConfigured; +} + } // namespace net } // namespace aria2 diff --git a/src/SocketCore.h b/src/SocketCore.h index f673657e..acabe77c 100644 --- a/src/SocketCore.h +++ b/src/SocketCore.h @@ -401,6 +401,12 @@ bool verifyHostname(const std::string& hostname, const std::vector& dnsNames, const std::vector& ipAddrs, const std::string& commonName); +// Checks public IP address are configured for each family: IPv4 and +// IPv6. The result can be obtained using getIpv4AddrConfigured() and +// getIpv6AddrConfigured() respectively. +void checkAddrconfig(); +bool getIPv4AddrConfigured(); +bool getIPv6AddrConfigured(); } // namespace net diff --git a/src/main.cc b/src/main.cc index 0f45f344..ed59f886 100644 --- a/src/main.cc +++ b/src/main.cc @@ -208,6 +208,7 @@ error_code::Value main(int argc, char* argv[]) // when none of network interface has IPv4 address. setDefaultAIFlags(0); } + net::checkAddrconfig(); // Bind interface if(!op->get(PREF_INTERFACE).empty()) { std::string iface = op->get(PREF_INTERFACE);