Browse Source

Check non-loopback (and non-linklocak for IPv6) address is configured

Issue A record query only when non-loopback IPv4 address is
configured.  Likewise, issue AAA record query only when non-loopback
and non-linklocak IPv6 address is configured.
Tatsuhiro Tsujikawa 12 years ago
parent
commit
f4a0987544
5 changed files with 108 additions and 7 deletions
  1. 5 1
      src/AbstractCommand.cc
  2. 16 6
      src/AsyncNameResolverMan.cc
  3. 80 0
      src/SocketCore.cc
  4. 6 0
      src/SocketCore.h
  5. 1 0
      src/main.cc

+ 5 - 1
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

+ 16 - 6
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

+ 80 - 0
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<ifaddrs*> 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

+ 6 - 0
src/SocketCore.h

@@ -401,6 +401,12 @@ bool verifyHostname(const std::string& hostname,
                     const std::vector<std::string>& dnsNames,
                     const std::vector<std::string>& 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
 

+ 1 - 0
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);