Explorar o código

Added --async-dns-server option.

This option accepts comma separated list of DNS server address used in
asynchronous DNS resolver. Usually asynchronous DNS resolver reads DNS
server addresses from /etc/resolv.conf. When this option is used, it
uses DNS servers specified in this option instead of ones in
/etc/resolv.conf. You can specify both IPv4 and IPv6 address. This
option is useful when the system does not have /etc/resolv.conf and
user does not have the permission to create it.
Tatsuhiro Tsujikawa %!s(int64=14) %!d(string=hai) anos
pai
achega
f0682a98c0

+ 2 - 1
src/AbstractCommand.cc

@@ -650,7 +650,8 @@ void AbstractCommand::initAsyncNameResolver(const std::string& hostname)
   } else {
     family = AF_INET;
   }
-  asyncNameResolver_.reset(new AsyncNameResolver(family));
+  asyncNameResolver_.reset
+    (new AsyncNameResolver(family, e_->getAsyncDNSServers()));
   A2_LOG_INFO(fmt(MSG_RESOLVING_HOSTNAME,
                   getCuid(),
                   hostname.c_str()));

+ 8 - 1
src/AsyncNameResolver.cc

@@ -37,6 +37,7 @@
 #include <cstring>
 
 #include "A2STR.h"
+#include "LogFactory.h"
 
 namespace aria2 {
 
@@ -77,12 +78,18 @@ void callback(void* arg, int status, int timeouts, struct hostent* host)
   }
 }
 
-AsyncNameResolver::AsyncNameResolver(int family):
+AsyncNameResolver::AsyncNameResolver(int family, ares_addr_node* servers):
   status_(STATUS_READY),
   family_(family)
 {
   // TODO evaluate return value
   ares_init(&channel_);
+  if(servers) {
+    // ares_set_servers has been added since c-ares 1.7.1
+    if(ares_set_servers(channel_, servers) != ARES_SUCCESS) {
+      A2_LOG_DEBUG("ares_set_servers failed");
+    }
+  }
 }
 
 AsyncNameResolver::~AsyncNameResolver()

+ 1 - 1
src/AsyncNameResolver.h

@@ -70,7 +70,7 @@ private:
   std::string error_;
   std::string hostname_;
 public:
-  AsyncNameResolver(int family);
+  AsyncNameResolver(int family, ares_addr_node* servers);
 
   ~AsyncNameResolver();
 

+ 1 - 1
src/DHTEntryPointNameResolveCommand.cc

@@ -84,7 +84,7 @@ bool DHTEntryPointNameResolveCommand::execute()
     } else {
       family = AF_INET;
     }
-    resolver_.reset(new AsyncNameResolver(family));
+    resolver_.reset(new AsyncNameResolver(family, e_->getAsyncDNSServers()));
   }
 #endif // ENABLE_ASYNC_DNS
   try {

+ 15 - 0
src/DownloadEngine.cc

@@ -95,6 +95,9 @@ DownloadEngine::DownloadEngine(const SharedHandle<EventPoll>& eventPoll)
 #ifdef ENABLE_BITTORRENT
     btRegistry_(new BtRegistry()),
 #endif // ENABLE_BITTORRENT
+#ifdef ENABLE_ASYNC_DNS
+    asyncDNSServers_(0),
+#endif // ENABLE_ASYNC_DNS
     dnsCache_(new DNSCache())
 {
   unsigned char sessionId[20];
@@ -104,6 +107,7 @@ DownloadEngine::DownloadEngine(const SharedHandle<EventPoll>& eventPoll)
 
 DownloadEngine::~DownloadEngine() {
   cleanQueue();
+  setAsyncDNSServers(0);
 }
 
 void DownloadEngine::cleanQueue() {
@@ -560,4 +564,15 @@ void DownloadEngine::setCheckIntegrityMan
   checkIntegrityMan_ = ciman;
 }
 
+void DownloadEngine::setAsyncDNSServers(ares_addr_node* asyncDNSServers)
+{
+  ares_addr_node* node = asyncDNSServers_;
+  while(node) {
+    ares_addr_node* next = node->next;
+    delete node;
+    node = next;
+  }
+  asyncDNSServers_ = asyncDNSServers;
+}
+
 } // namespace aria2

+ 11 - 0
src/DownloadEngine.h

@@ -135,6 +135,10 @@ private:
 
   CUIDCounter cuidCounter_;
 
+#ifdef ENABLE_ASYNC_DNS
+  ares_addr_node* asyncDNSServers_;
+#endif // ENABLE_ASYNC_DNS
+
   SharedHandle<DNSCache> dnsCache_;
 
   SharedHandle<AuthConfigFactory> authConfigFactory_;
@@ -326,6 +330,13 @@ public:
   {
     return sessionId_;
   }
+
+  void setAsyncDNSServers(ares_addr_node* asyncDNSServers);
+
+  ares_addr_node* getAsyncDNSServers() const
+  {
+    return asyncDNSServers_;
+  }
 };
 
 typedef SharedHandle<DownloadEngine> DownloadEngineHandle;

+ 49 - 0
src/MultiUrlRequestInfo.cc

@@ -36,6 +36,7 @@
 
 #include <signal.h>
 
+#include <cstring>
 #include <ostream>
 
 #include "RequestGroupMan.h"
@@ -90,6 +91,50 @@ void handler(int signal) {
 }
 } // namespace
 
+namespace {
+
+ares_addr_node* parseAsyncDNSServers(const std::string& serversOpt)
+{
+  std::vector<std::string> servers;
+  util::split(serversOpt,
+              std::back_inserter(servers),
+              A2STR::COMMA_C,
+              true /* doStrip */);
+  ares_addr_node root;
+  root.next = 0;
+  ares_addr_node* tail = &root;
+  for(std::vector<std::string>::const_iterator i = servers.begin(),
+        eoi = servers.end(); i != eoi; ++i) {
+    struct addrinfo* res;
+    int s = callGetaddrinfo(&res, (*i).c_str(), 0, AF_UNSPEC,
+                            0, AI_NUMERICHOST, 0);
+    if(s != 0) {
+      continue;
+    }
+    WSAAPI_AUTO_DELETE<struct addrinfo*> resDeleter(res, freeaddrinfo);
+    if(res) {
+      ares_addr_node* node = new ares_addr_node();
+      node->next = 0;
+      node->family = res->ai_family;
+      if(node->family == AF_INET) {
+        struct sockaddr_in* in =
+          reinterpret_cast<struct sockaddr_in*>(res->ai_addr);
+        memcpy(&node->addr.addr4, &(in->sin_addr), 4);
+      } else {
+        struct sockaddr_in6* in =
+          reinterpret_cast<struct sockaddr_in6*>(res->ai_addr);
+        memcpy(&node->addr.addr6, &(in->sin6_addr), 16);
+      }
+      tail->next = node;
+      tail = node;
+    }
+  }
+  return root.next;
+}
+
+} // namespace
+
+
 MultiUrlRequestInfo::MultiUrlRequestInfo
 (const std::vector<SharedHandle<RequestGroup> >& requestGroups,
  const SharedHandle<Option>& op,
@@ -170,6 +215,10 @@ error_code::Value MultiUrlRequestInfo::execute()
     }
     SocketCore::setTLSContext(tlsContext);
 #endif
+    ares_addr_node* asyncDNSServers =
+      parseAsyncDNSServers(option_->get(PREF_ASYNC_DNS_SERVER));
+    e->setAsyncDNSServers(asyncDNSServers);
+
     if(!Timer::monotonicClock()) {
       A2_LOG_WARN("Don't change system time while aria2c is running."
                   " Doing this may make aria2c hang for long time.");

+ 8 - 0
src/OptionHandlerFactory.cc

@@ -205,6 +205,14 @@ OptionHandlers OptionHandlerFactory::createOptionHandlers()
     op->addTag(TAG_ADVANCED);
     handlers.push_back(op);
   }
+  {
+    SharedHandle<OptionHandler> op(new DefaultOptionHandler
+                                   (PREF_ASYNC_DNS_SERVER,
+                                    TEXT_ASYNC_DNS_SERVER,
+                                    NO_DEFAULT_VALUE));
+    op->addTag(TAG_ADVANCED);
+    handlers.push_back(op);
+  }
 #endif // ENABLE_ASYNC_DNS
 #ifdef ENABLE_DIRECT_IO
   {

+ 2 - 0
src/prefs.cc

@@ -204,6 +204,8 @@ const std::string PREF_ENABLE_ASYNC_DNS6("enable-async-dns6");
 const std::string PREF_MAX_DOWNLOAD_RESULT("max-download-result");
 // value: 1*digit
 const std::string PREF_RETRY_WAIT("retry-wait");
+// value: string
+const std::string PREF_ASYNC_DNS_SERVER("async-dns-server");
 
 /**
  * FTP related preferences

+ 2 - 0
src/prefs.h

@@ -208,6 +208,8 @@ extern const std::string PREF_ENABLE_ASYNC_DNS6;
 extern const std::string PREF_MAX_DOWNLOAD_RESULT;
 // value: 1*digit
 extern const std::string PREF_RETRY_WAIT;
+// value: string
+extern const std::string PREF_ASYNC_DNS_SERVER;
 
 /**
  * FTP related preferences

+ 11 - 0
src/usage_text.h

@@ -753,3 +753,14 @@
     "                              option may result high memory consumption after\n" \
     "                              thousands of downloads. Specifying 0 means no\n" \
     "                              download result is kept.")
+#define TEXT_ASYNC_DNS_SERVER                   \
+  _(" --async-dns-server=IPADDRESS[,...] Comma separated list of DNS server address\n" \
+    "                              used in asynchronous DNS resolver. Usually\n" \
+    "                              asynchronous DNS resolver reads DNS server\n" \
+    "                              addresses from /etc/resolv.conf. When this option\n" \
+    "                              is used, it uses DNS servers specified in this\n" \
+    "                              option instead of ones in /etc/resolv.conf. You\n" \
+    "                              can specify both IPv4 and IPv6 address. This\n" \
+    "                              option is useful when the system does not have\n" \
+    "                              /etc/resolv.conf and user does not have the\n" \
+    "                              permission to create it.")