Переглянути джерело

2010-08-07 Tatsuhiro Tsujikawa <t-tujikawa@users.sourceforge.net>

	Added IPv6 DHT. Added --dht-entry-porint6, --dht-file-path6,
	--dht-listen-addr6 and --enable-dht6 option.  IPv6 DHT is disabled
	by default. To use IPv6 DHT, you need to use --enable-dht6 and
	specify a global unicast address to --dht-listen-addr6.  IPv6 DHT
	is highly experimental.
	* src/BtSetup.cc
	* src/DHTAutoSaveCommand.cc
	* src/DHTAutoSaveCommand.h
	* src/DHTConnectionImpl.cc
	* src/DHTConnectionImpl.h
	* src/DHTEntryPointNameResolveCommand.cc
	* src/DHTFindNodeReplyMessage.cc
	* src/DHTFindNodeReplyMessage.h
	* src/DHTGetPeersMessage.cc
	* src/DHTGetPeersReplyMessage.cc
	* src/DHTGetPeersReplyMessage.h
	* src/DHTMessageFactory.h
	* src/DHTMessageFactoryImpl.cc
	* src/DHTMessageFactoryImpl.h
	* src/DHTMessageTracker.cc
	* src/DHTRegistry.cc
	* src/DHTRegistry.h
	* src/DHTRoutingTableDeserializer.cc
	* src/DHTRoutingTableDeserializer.h
	* src/DHTRoutingTableSerializer.cc
	* src/DHTRoutingTableSerializer.h
	* src/DHTSetup.cc
	* src/DHTSetup.h
	* src/FtpConnection.cc
	* src/LpdMessageReceiver.cc
	* src/OptionHandlerFactory.cc
	* src/OptionHandlerImpl.h
	* src/PeerInteractionCommand.cc
	* src/RequestGroup.cc
	* src/SocketCore.cc
	* src/SocketCore.h
	* src/bittorrent_helper.cc
	* src/bittorrent_helper.h
	* src/prefs.cc
	* src/prefs.h
	* src/usage_text.h
	* test/DHTConnectionImplTest.cc
	* test/DHTFindNodeReplyMessageTest.cc
	* test/DHTGetPeersMessageTest.cc
	* test/DHTGetPeersReplyMessageTest.cc
	* test/DHTMessageFactoryImplTest.cc
	* test/DHTRoutingTableDeserializerTest.cc
	* test/DHTRoutingTableSerializerTest.cc
	* test/LpdMessageDispatcherTest.cc
	* test/MockDHTMessageFactory.h
Tatsuhiro Tsujikawa 15 роки тому
батько
коміт
26d6692376
46 змінених файлів з 1093 додано та 410 видалено
  1. 54 1
      ChangeLog
  2. 28 9
      src/BtSetup.cc
  3. 6 4
      src/DHTAutoSaveCommand.cc
  4. 4 1
      src/DHTAutoSaveCommand.h
  5. 16 8
      src/DHTConnectionImpl.cc
  6. 13 8
      src/DHTConnectionImpl.h
  7. 6 1
      src/DHTEntryPointNameResolveCommand.cc
  8. 16 8
      src/DHTFindNodeReplyMessage.cc
  9. 6 1
      src/DHTFindNodeReplyMessage.h
  10. 5 12
      src/DHTGetPeersMessage.cc
  11. 30 16
      src/DHTGetPeersReplyMessage.cc
  12. 6 1
      src/DHTGetPeersReplyMessage.h
  13. 0 6
      src/DHTMessageFactory.h
  14. 50 71
      src/DHTMessageFactoryImpl.cc
  15. 8 15
      src/DHTMessageFactoryImpl.h
  16. 16 11
      src/DHTMessageTracker.cc
  17. 22 9
      src/DHTRegistry.cc
  18. 40 0
      src/DHTRegistry.h
  19. 18 22
      src/DHTRoutingTableDeserializer.cc
  20. 3 1
      src/DHTRoutingTableDeserializer.h
  21. 12 14
      src/DHTRoutingTableSerializer.cc
  22. 3 1
      src/DHTRoutingTableSerializer.h
  23. 58 31
      src/DHTSetup.cc
  24. 1 6
      src/DHTSetup.h
  25. 1 1
      src/FtpConnection.cc
  26. 1 1
      src/LpdMessageReceiver.cc
  27. 48 0
      src/OptionHandlerFactory.cc
  28. 8 7
      src/OptionHandlerImpl.h
  29. 41 10
      src/PeerInteractionCommand.cc
  30. 37 11
      src/RequestGroup.cc
  31. 26 14
      src/SocketCore.cc
  32. 3 2
      src/SocketCore.h
  33. 11 0
      src/bittorrent_helper.cc
  34. 5 0
      src/bittorrent_helper.h
  35. 14 0
      src/prefs.cc
  36. 14 0
      src/prefs.h
  37. 20 8
      src/usage_text.h
  38. 9 6
      test/DHTConnectionImplTest.cc
  39. 48 3
      test/DHTFindNodeReplyMessageTest.cc
  40. 7 21
      test/DHTGetPeersMessageTest.cc
  41. 76 13
      test/DHTGetPeersReplyMessageTest.cc
  42. 118 16
      test/DHTMessageFactoryImplTest.cc
  43. 45 2
      test/DHTRoutingTableDeserializerTest.cc
  44. 136 25
      test/DHTRoutingTableSerializerTest.cc
  45. 1 1
      test/LpdMessageDispatcherTest.cc
  46. 3 12
      test/MockDHTMessageFactory.h

+ 54 - 1
ChangeLog

@@ -1,4 +1,57 @@
-2010-08-05  Tatsuhiro Tsujikawa  <t-tujikawa@users.sourceforge.net>
+2010-08-07  Tatsuhiro Tsujikawa  <t-tujikawa@users.sourceforge.net>
+
+	Added IPv6 DHT. Added --dht-entry-porint6, --dht-file-path6,
+	--dht-listen-addr6 and --enable-dht6 option.  IPv6 DHT is disabled
+	by default. To use IPv6 DHT, you need to use --enable-dht6 and
+	specify a global unicast address to --dht-listen-addr6.  IPv6 DHT
+	is highly experimental.
+	* src/BtSetup.cc
+	* src/DHTAutoSaveCommand.cc
+	* src/DHTAutoSaveCommand.h
+	* src/DHTConnectionImpl.cc
+	* src/DHTConnectionImpl.h
+	* src/DHTEntryPointNameResolveCommand.cc
+	* src/DHTFindNodeReplyMessage.cc
+	* src/DHTFindNodeReplyMessage.h
+	* src/DHTGetPeersMessage.cc
+	* src/DHTGetPeersReplyMessage.cc
+	* src/DHTGetPeersReplyMessage.h
+	* src/DHTMessageFactory.h
+	* src/DHTMessageFactoryImpl.cc
+	* src/DHTMessageFactoryImpl.h
+	* src/DHTMessageTracker.cc
+	* src/DHTRegistry.cc
+	* src/DHTRegistry.h
+	* src/DHTRoutingTableDeserializer.cc
+	* src/DHTRoutingTableDeserializer.h
+	* src/DHTRoutingTableSerializer.cc
+	* src/DHTRoutingTableSerializer.h
+	* src/DHTSetup.cc
+	* src/DHTSetup.h
+	* src/FtpConnection.cc
+	* src/LpdMessageReceiver.cc
+	* src/OptionHandlerFactory.cc
+	* src/OptionHandlerImpl.h
+	* src/PeerInteractionCommand.cc
+	* src/RequestGroup.cc
+	* src/SocketCore.cc
+	* src/SocketCore.h
+	* src/bittorrent_helper.cc
+	* src/bittorrent_helper.h
+	* src/prefs.cc
+	* src/prefs.h
+	* src/usage_text.h
+	* test/DHTConnectionImplTest.cc
+	* test/DHTFindNodeReplyMessageTest.cc
+	* test/DHTGetPeersMessageTest.cc
+	* test/DHTGetPeersReplyMessageTest.cc
+	* test/DHTMessageFactoryImplTest.cc
+	* test/DHTRoutingTableDeserializerTest.cc
+	* test/DHTRoutingTableSerializerTest.cc
+	* test/LpdMessageDispatcherTest.cc
+	* test/MockDHTMessageFactory.h
+
+2010-08-07  Tatsuhiro Tsujikawa  <t-tujikawa@users.sourceforge.net>
 
 	Removed AI_V4MAPPED from DEFAULT_AI_FLAGS
 	* src/a2netcompat.h

+ 28 - 9
src/BtSetup.cc

@@ -58,6 +58,15 @@
 #include "DHTPeerAnnounceStorage.h"
 #include "DHTSetup.h"
 #include "DHTRegistry.h"
+#include "DHTNode.h"
+#include "DHTRoutingTable.h"
+#include "DHTTaskQueue.h"
+#include "DHTTaskFactory.h"
+#include "DHTTokenTracker.h"
+#include "DHTMessageDispatcher.h"
+#include "DHTMessageReceiver.h"
+#include "DHTMessageFactory.h"
+#include "DHTMessageCallback.h"
 #include "BtProgressInfoFile.h"
 #include "BtAnnounce.h"
 #include "BtRuntime.h"
@@ -126,15 +135,25 @@ void BtSetup::setup(std::vector<Command*>& commands,
     commands.push_back(c);
   }
 
-  if((metadataGetMode || !torrentAttrs->privateTorrent) &&
-     DHTSetup::initialized()) {
-    DHTGetPeersCommand* command =
-      new DHTGetPeersCommand(e->newCUID(), requestGroup, e);
-    command->setTaskQueue(DHTRegistry::getData().taskQueue);
-    command->setTaskFactory(DHTRegistry::getData().taskFactory);
-    command->setBtRuntime(btRuntime);
-    command->setPeerStorage(peerStorage);
-    commands.push_back(command);
+  if(metadataGetMode || !torrentAttrs->privateTorrent) {
+    if(DHTRegistry::isInitialized()) {
+      DHTGetPeersCommand* command =
+        new DHTGetPeersCommand(e->newCUID(), requestGroup, e);
+      command->setTaskQueue(DHTRegistry::getData().taskQueue);
+      command->setTaskFactory(DHTRegistry::getData().taskFactory);
+      command->setBtRuntime(btRuntime);
+      command->setPeerStorage(peerStorage);
+      commands.push_back(command);
+    }
+    if(DHTRegistry::isInitialized6()) {
+      DHTGetPeersCommand* command =
+        new DHTGetPeersCommand(e->newCUID(), requestGroup, e);
+      command->setTaskQueue(DHTRegistry::getData6().taskQueue);
+      command->setTaskFactory(DHTRegistry::getData6().taskFactory);
+      command->setBtRuntime(btRuntime);
+      command->setPeerStorage(peerStorage);
+      commands.push_back(command);
+    }
   }
   if(!metadataGetMode) {
     SharedHandle<UnionSeedCriteria> unionCri(new UnionSeedCriteria());

+ 6 - 4
src/DHTAutoSaveCommand.cc

@@ -62,8 +62,9 @@
 namespace aria2 {
 
 DHTAutoSaveCommand::DHTAutoSaveCommand
-(cuid_t cuid, DownloadEngine* e, time_t interval):
-  TimeBasedCommand(cuid, e, interval) {}
+(cuid_t cuid, DownloadEngine* e, int family, time_t interval):
+  TimeBasedCommand(cuid, e, interval),
+  family_(family) {}
 
 DHTAutoSaveCommand::~DHTAutoSaveCommand() {}
 
@@ -84,7 +85,8 @@ void DHTAutoSaveCommand::process()
 void DHTAutoSaveCommand::save()
 {
   std::string dhtFile =
-    getDownloadEngine()->getOption()->get(PREF_DHT_FILE_PATH);
+    getDownloadEngine()->getOption()->
+    get(family_ == AF_INET? PREF_DHT_FILE_PATH : PREF_DHT_FILE_PATH6);
   getLogger()->info("Saving DHT routing table to %s.", dhtFile.c_str());
 
   std::string tempFile = dhtFile;
@@ -116,7 +118,7 @@ void DHTAutoSaveCommand::save()
     nodes.insert(nodes.end(), goodNodes.begin(), goodNodes.end());
   }
 
-  DHTRoutingTableSerializer serializer;
+  DHTRoutingTableSerializer serializer(family_);
   serializer.setLocalNode(localNode_);
   serializer.setNodes(nodes);
 

+ 4 - 1
src/DHTAutoSaveCommand.h

@@ -46,13 +46,16 @@ class DHTNode;
 class DHTAutoSaveCommand : public TimeBasedCommand
 {
 private:
+  int family_;
+
   SharedHandle<DHTNode> localNode_;
   
   SharedHandle<DHTRoutingTable> routingTable_;
 
   void save();
 public:
-  DHTAutoSaveCommand(cuid_t cuid, DownloadEngine* e, time_t interval);
+  DHTAutoSaveCommand
+  (cuid_t cuid, DownloadEngine* e, int family, time_t interval);
 
   virtual ~DHTAutoSaveCommand();
 

+ 16 - 8
src/DHTConnectionImpl.cc

@@ -46,12 +46,15 @@
 
 namespace aria2 {
 
-DHTConnectionImpl::DHTConnectionImpl():socket_(new SocketCore(SOCK_DGRAM)),
-                                       logger_(LogFactory::getInstance()) {}
+DHTConnectionImpl::DHTConnectionImpl(int family):
+  socket_(new SocketCore(SOCK_DGRAM)),
+  family_(family),
+  logger_(LogFactory::getInstance()) {}
 
 DHTConnectionImpl::~DHTConnectionImpl() {}
 
-bool DHTConnectionImpl::bind(uint16_t& port, IntSequence& ports)
+bool DHTConnectionImpl::bind
+(uint16_t& port, const std::string& addr, IntSequence& ports)
 {
   std::vector<int32_t> randPorts = ports.flush();
   std::random_shuffle(randPorts.begin(), randPorts.end(),
@@ -63,25 +66,30 @@ bool DHTConnectionImpl::bind(uint16_t& port, IntSequence& ports)
       continue;
     }
     port = (*portItr);
-    if(bind(port)) {
+    if(bind(port, addr)) {
       return true;
     }
   }
   return false;
 }
 
-bool DHTConnectionImpl::bind(uint16_t& port)
+bool DHTConnectionImpl::bind(uint16_t& port, const std::string& addr)
 {
+  int ipv = family_ == AF_INET?4:6;
   try {
-    socket_->bind(port);
+    if(addr.empty()) {
+      socket_->bind(A2STR::NIL, port, family_);
+    } else {
+      socket_->bind(addr, port, family_);
+    }
     socket_->setNonBlockingMode();
     std::pair<std::string, uint16_t> svaddr;
     socket_->getAddrInfo(svaddr);
     port = svaddr.second;
-    logger_->notice("DHT: listening to port %d", port);
+    logger_->notice("IPv%d DHT: listening to port %d", ipv, port);
     return true;
   } catch(RecoverableException& e) {
-    logger_->error("Failed to bind for DHT. port=%u", e, port);
+    logger_->error("Failed to bind for IPv%d DHT. port=%u", e, ipv, port);
   }
   return false;
 }

+ 13 - 8
src/DHTConnectionImpl.h

@@ -48,28 +48,33 @@ class DHTConnectionImpl:public DHTConnection {
 private:
   SharedHandle<SocketCore> socket_;
 
+  int family_;
+
   Logger* logger_;
 public:
-  DHTConnectionImpl();
+  DHTConnectionImpl(int family);
 
   virtual ~DHTConnectionImpl();
 
   /**
-   * Binds port. All number in ports are tried.
-   * If successful, the binded port is assigned to port and returns true.
-   * Otherwise return false and port is undefined in this case.
+   * Binds port. All number in ports are tried.  If successful, the
+   * binded port is assigned to port and returns true.  Otherwise
+   * return false and port is undefined in this case.  If non-empty
+   * string addr is given, the socket is associated to the address.
    */
-  bool bind(uint16_t& port, IntSequence& ports);
+  bool bind(uint16_t& port, const std::string& addr, IntSequence& ports);
   
   /**
    * Binds port. The port number specified by port is used to bind.
-   * If successful, the binded port is assigned to port and returns true.
-   * Otherwise return false and port is undefined in this case.
+   * If successful, the binded port is assigned to port and returns
+   * true.  Otherwise return false and port is undefined in this case.
+   * If non-empty string addr is given, the socket is associated to
+   * the address.
    *
    * If you want to bind arbitrary port, give 0 as port and if successful,
    * the binded port is assigned to port.
    */
-  bool bind(uint16_t& port);
+  bool bind(uint16_t& port, const std::string& addr);
 
   virtual ssize_t receiveMessage(unsigned char* data, size_t len,
                                  std::string& host, uint16_t& port);

+ 6 - 1
src/DHTEntryPointNameResolveCommand.cc

@@ -96,7 +96,12 @@ bool DHTEntryPointNameResolveCommand::execute()
       while(!entryPoints_.empty()) {
         std::string hostname = entryPoints_.front().first;
         try {
-          if(resolveHostname(hostname, resolver_)) {
+          if(util::isNumericHost(hostname)) {
+            std::pair<std::string, uint16_t> p
+              (hostname, entryPoints_.front().second);
+            resolvedEntryPoints_.push_back(p);
+            addPingTask(p);
+          } else if(resolveHostname(hostname, resolver_)) {
             hostname = resolver_->getResolvedAddresses().front();
             std::pair<std::string, uint16_t> p(hostname,
                                                entryPoints_.front().second);

+ 16 - 8
src/DHTFindNodeReplyMessage.cc

@@ -51,11 +51,15 @@ const std::string DHTFindNodeReplyMessage::FIND_NODE("find_node");
 
 const std::string DHTFindNodeReplyMessage::NODES("nodes");
 
+const std::string DHTFindNodeReplyMessage::NODES6("nodes6");
+
 DHTFindNodeReplyMessage::DHTFindNodeReplyMessage
-(const SharedHandle<DHTNode>& localNode,
+(int family,
+ const SharedHandle<DHTNode>& localNode,
  const SharedHandle<DHTNode>& remoteNode,
  const std::string& transactionID):
-  DHTResponseMessage(localNode, remoteNode, transactionID) {}
+  DHTResponseMessage(localNode, remoteNode, transactionID),
+  family_(family) {}
 
 DHTFindNodeReplyMessage::~DHTFindNodeReplyMessage() {}
 
@@ -73,23 +77,27 @@ SharedHandle<Dict> DHTFindNodeReplyMessage::getResponse()
 {
   SharedHandle<Dict> aDict = Dict::g();
   aDict->put(DHTMessage::ID, String::g(getLocalNode()->getID(), DHT_ID_LENGTH));
+  unsigned char buffer[DHTBucket::K*38];
+  const int clen = bittorrent::getCompactLength(family_);
+  const int unit = clen+20;
+  assert(unit <= 38);
   size_t offset = 0;
-  unsigned char buffer[DHTBucket::K*26];
-  // TODO if closestKNodes_.size() > DHTBucket::K ??
+  size_t k = 0;
   for(std::vector<SharedHandle<DHTNode> >::const_iterator i =
         closestKNodes_.begin(), eoi = closestKNodes_.end();
-      i != eoi && offset < DHTBucket::K*26; ++i) {
+      i != eoi && k < DHTBucket::K; ++i) {
     SharedHandle<DHTNode> node = *i;
     memcpy(buffer+offset, node->getID(), DHT_ID_LENGTH);
     unsigned char compact[COMPACT_LEN_IPV6];
     int compactlen = bittorrent::packcompact
       (compact, node->getIPAddress(), node->getPort());
-    if(compactlen == COMPACT_LEN_IPV4) {
+    if(compactlen == clen) {
       memcpy(buffer+20+offset, compact, compactlen);
-      offset += 26;
+      offset += unit;
+      ++k;
     }
   }
-  aDict->put(NODES, String::g(buffer, offset));
+  aDict->put(family_ == AF_INET?NODES:NODES6, String::g(buffer, offset));
   return aDict;
 }
 

+ 6 - 1
src/DHTFindNodeReplyMessage.h

@@ -42,11 +42,14 @@ namespace aria2 {
 
 class DHTFindNodeReplyMessage:public DHTResponseMessage {
 private:
+  int family_;
+
   std::vector<SharedHandle<DHTNode> > closestKNodes_;
 protected:
   virtual std::string toStringOptional() const;
 public:
-  DHTFindNodeReplyMessage(const SharedHandle<DHTNode>& localNode,
+  DHTFindNodeReplyMessage(int family,
+                          const SharedHandle<DHTNode>& localNode,
                           const SharedHandle<DHTNode>& remoteNode,
                           const std::string& transactionID);
 
@@ -71,6 +74,8 @@ public:
   static const std::string FIND_NODE;
   
   static const std::string NODES;
+  
+  static const std::string NODES6;
 };
 
 } // namespace aria2

+ 5 - 12
src/DHTGetPeersMessage.cc

@@ -70,18 +70,11 @@ void DHTGetPeersMessage::doReceivedAction()
   // Check to see localhost has the contents which has same infohash
   std::vector<SharedHandle<Peer> > peers;
   peerAnnounceStorage_->getPeers(peers, infoHash_);
-  SharedHandle<DHTMessage> reply;
-  if(peers.empty()) {
-    std::vector<SharedHandle<DHTNode> > nodes;
-    getRoutingTable()->getClosestKNodes(nodes, infoHash_);
-    reply =
-      getMessageFactory()->createGetPeersReplyMessage
-      (getRemoteNode(), nodes, token, getTransactionID());
-  } else {
-    reply =
-      getMessageFactory()->createGetPeersReplyMessage
-      (getRemoteNode(), peers, token, getTransactionID());
-  }
+  std::vector<SharedHandle<DHTNode> > nodes;
+  getRoutingTable()->getClosestKNodes(nodes, infoHash_);
+  SharedHandle<DHTMessage> reply =
+    getMessageFactory()->createGetPeersReplyMessage
+    (getRemoteNode(), nodes, peers, token, getTransactionID());
   getMessageDispatcher()->addMessageToQueue(reply);
 }
 

+ 30 - 16
src/DHTGetPeersReplyMessage.cc

@@ -57,12 +57,16 @@ const std::string DHTGetPeersReplyMessage::VALUES("values");
 
 const std::string DHTGetPeersReplyMessage::NODES("nodes");
 
+const std::string DHTGetPeersReplyMessage::NODES6("nodes6");
+
 DHTGetPeersReplyMessage::DHTGetPeersReplyMessage
-(const SharedHandle<DHTNode>& localNode,
+(int family,
+ const SharedHandle<DHTNode>& localNode,
  const SharedHandle<DHTNode>& remoteNode,
  const std::string& token,
  const std::string& transactionID):
   DHTResponseMessage(localNode, remoteNode, transactionID),
+  family_(family),
   token_(token) {}
 
 DHTGetPeersReplyMessage::~DHTGetPeersReplyMessage() {}
@@ -77,59 +81,69 @@ SharedHandle<Dict> DHTGetPeersReplyMessage::getResponse()
   SharedHandle<Dict> rDict = Dict::g();
   rDict->put(DHTMessage::ID, String::g(getLocalNode()->getID(), DHT_ID_LENGTH));
   rDict->put(TOKEN, token_);
-  if(values_.empty()) {
+  // TODO want parameter
+  if(!closestKNodes_.empty()) {
+    unsigned char buffer[DHTBucket::K*38];
+    const int clen = bittorrent::getCompactLength(family_);
+    const int unit = clen+20;
     size_t offset = 0;
-    unsigned char buffer[DHTBucket::K*26];
+    size_t k = 0;
     for(std::vector<SharedHandle<DHTNode> >::const_iterator i =
           closestKNodes_.begin(), eoi = closestKNodes_.end();
-        i != eoi && offset < DHTBucket::K*26; ++i) {
+        i != eoi && k < DHTBucket::K; ++i) {
       SharedHandle<DHTNode> node = *i;
       memcpy(buffer+offset, node->getID(), DHT_ID_LENGTH);
       unsigned char compact[COMPACT_LEN_IPV6];
       int compactlen = bittorrent::packcompact
         (compact, node->getIPAddress(), node->getPort());
-      if(compactlen == COMPACT_LEN_IPV4) {
+      if(compactlen == clen) {
         memcpy(buffer+20+offset, compact, compactlen);
-        offset += 26;
+        offset += unit;
+        ++k;
       }
     }
-    rDict->put(NODES, String::g(buffer, offset));
-  } else {
+    rDict->put(family_ == AF_INET?NODES:NODES6, String::g(buffer, offset));
+  }
+  if(!values_.empty()) {
     // Limit the size of values list.  The maxmum size of UDP datagram
     // is limited to 65535 bytes. aria2 uses 20bytes token and 2byte
     // transaction ID. The size of get_peers reply message without
-    // values list is 87bytes:
+    // values list and nodes is 87bytes:
     //
     // d1:rd2:id20:aaaaaaaaaaaaaaaaaaaa5:token20:aaaaaaaaaaaaaaaaaaaa
     // 6:valueslee1:t2:bb1:y1:re
     //
+    // nodes are 38 bytes per host for IPv6 and the number of hosts is
+    // K(=8) max. So without values list, we already 87+38*8+4 = 395.
+    //
     // Because of Path MTU Discovery, UDP packet size which need not
     // to be fragmented is much smaller. Since Linux uses Path MTU
     // Dicoverry by default and returning ICMP message might be
     // filtered, we should avoid fragmentation.  MTU of pppoe is 1492
     // max according to RFC2516.  We use maximum packet size to be
-    // 1000. Since it contains 20 bytes IP header and 8 bytes UDP
-    // header and 87 bytes reply message template described above, We
-    // can carry (1000-28-87)/8 = 110 peer info. Since DHT spec
+    // 1024. Since it contains 20 bytes IP header and 8 bytes UDP
+    // header and 395 bytes reply message template described above, We
+    // can carry (1024-28-395)/(18+3) = 28 peer info. Since DHT spec
     // doesn't specify the maximum size of token, reply message
-    // template may get bigger than 87 bytes. So we use 100 as maximum
+    // template may get bigger than 395 bytes. So we use 25 as maximum
     // number of peer info that a message can carry.
-    static const size_t MAX_VALUES_SIZE = 100;
+    static const size_t MAX_VALUES_SIZE = 25;
     SharedHandle<List> valuesList = List::g();
     for(std::vector<SharedHandle<Peer> >::const_iterator i = values_.begin(),
           eoi = values_.end(); i != eoi && valuesList->size() < MAX_VALUES_SIZE;
         ++i) {
       const SharedHandle<Peer>& peer = *i;
       unsigned char compact[COMPACT_LEN_IPV6];
+      const int clen = bittorrent::getCompactLength(family_);
       int compactlen = bittorrent::packcompact
         (compact, peer->getIPAddress(), peer->getPort());
-      if(compactlen == COMPACT_LEN_IPV4) {
+      if(compactlen == clen) {
         valuesList->append(String::g(compact, compactlen));
       }
     }
     rDict->put(VALUES, valuesList);
   }
-  return rDict;  
+  return rDict;
 }
 
 const std::string& DHTGetPeersReplyMessage::getMessageType() const

+ 6 - 1
src/DHTGetPeersReplyMessage.h

@@ -47,6 +47,8 @@ class Peer;
 
 class DHTGetPeersReplyMessage:public DHTResponseMessage {
 private:
+  int family_;
+
   std::string token_;
 
   std::vector<SharedHandle<DHTNode> > closestKNodes_;
@@ -55,7 +57,8 @@ private:
 protected:
   virtual std::string toStringOptional() const;
 public:
-  DHTGetPeersReplyMessage(const SharedHandle<DHTNode>& localNode,
+  DHTGetPeersReplyMessage(int family,
+                          const SharedHandle<DHTNode>& localNode,
                           const SharedHandle<DHTNode>& remoteNode,
                           const std::string& token,
                           const std::string& transactionID);
@@ -103,6 +106,8 @@ public:
   static const std::string VALUES;
 
   static const std::string NODES;
+
+  static const std::string NODES6;
 };
 
 } // namespace aria2

+ 0 - 6
src/DHTMessageFactory.h

@@ -94,12 +94,6 @@ public:
   createGetPeersReplyMessage
   (const SharedHandle<DHTNode>& remoteNode,
    const std::vector<SharedHandle<DHTNode> >& closestKNodes,
-   const std::string& token,
-   const std::string& transactionID) = 0;
-
-  virtual SharedHandle<DHTResponseMessage>
-  createGetPeersReplyMessage
-  (const SharedHandle<DHTNode>& remoteNode,
    const std::vector<SharedHandle<Peer> >& peers,
    const std::string& token,
    const std::string& transactionID) = 0;

+ 50 - 71
src/DHTMessageFactoryImpl.cc

@@ -64,7 +64,8 @@
 
 namespace aria2 {
 
-DHTMessageFactoryImpl::DHTMessageFactoryImpl():
+DHTMessageFactoryImpl::DHTMessageFactoryImpl(int family):
+  family_(family),
   logger_(LogFactory::getInstance()) {}
 
 DHTMessageFactoryImpl::~DHTMessageFactoryImpl() {}
@@ -261,21 +262,7 @@ DHTMessageFactoryImpl::createResponseMessage
   } else if(messageType == DHTFindNodeReplyMessage::FIND_NODE) {
     msg = createFindNodeReplyMessage(remoteNode, dict, transactionID->s());
   } else if(messageType == DHTGetPeersReplyMessage::GET_PEERS) {
-    const List* valuesList =
-      asList(rDict->get(DHTGetPeersReplyMessage::VALUES));
-    if(valuesList) {
-      msg = createGetPeersReplyMessageWithValues
-        (remoteNode, dict, transactionID->s());
-    } else {
-      const String* nodes = asString
-        (rDict->get(DHTGetPeersReplyMessage::NODES));
-      if(nodes) {
-        msg = createGetPeersReplyMessageWithNodes
-          (remoteNode, dict, transactionID->s());
-      } else {
-        throw DL_ABORT_EX("Malformed DHT message: missing nodes/values");
-      }
-    }
+    msg = createGetPeersReplyMessage(remoteNode, dict, transactionID->s());
   } else if(messageType == DHTAnnouncePeerReplyMessage::ANNOUNCE_PEER) {
     msg = createAnnouncePeerReplyMessage(remoteNode, transactionID->s());
   } else {
@@ -349,23 +336,26 @@ DHTMessageFactoryImpl::createFindNodeReplyMessage
  const std::string& transactionID)
 {
   SharedHandle<DHTFindNodeReplyMessage> m
-    (new DHTFindNodeReplyMessage(localNode_, remoteNode, transactionID));
+    (new DHTFindNodeReplyMessage
+     (family_, localNode_, remoteNode, transactionID));
   m->setClosestKNodes(closestKNodes);
   setCommonProperty(m);
   return m;
 }
 
-std::vector<SharedHandle<DHTNode> >
-DHTMessageFactoryImpl::extractNodes(const unsigned char* src, size_t length)
+void DHTMessageFactoryImpl::extractNodes
+(std::vector<SharedHandle<DHTNode> >& nodes,
+ const unsigned char* src, size_t length)
 {
-  if(length%26 != 0) {
-    throw DL_ABORT_EX("Nodes length is not multiple of 26");
+  int unit = bittorrent::getCompactLength(family_)+20;
+  if(length%unit != 0) {
+    throw DL_ABORT_EX
+      (StringFormat("Nodes length is not multiple of %d", unit).str());
   }
-  std::vector<SharedHandle<DHTNode> > nodes;
-  for(size_t offset = 0; offset < length; offset += 26) {
+  for(size_t offset = 0; offset < length; offset += unit) {
     SharedHandle<DHTNode> node(new DHTNode(src+offset));
     std::pair<std::string, uint16_t> addr =
-      bittorrent::unpackcompact(src+offset+DHT_ID_LENGTH, AF_INET);
+      bittorrent::unpackcompact(src+offset+DHT_ID_LENGTH, family_);
     if(addr.first.empty()) {
       continue;
     }
@@ -373,7 +363,6 @@ DHTMessageFactoryImpl::extractNodes(const unsigned char* src, size_t length)
     node->setPort(addr.second);
     nodes.push_back(node);
   }
-  return nodes;
 }
 
 SharedHandle<DHTResponseMessage>
@@ -383,10 +372,13 @@ DHTMessageFactoryImpl::createFindNodeReplyMessage
  const std::string& transactionID)
 {
   const String* nodesData =
-    getString(getDictionary(dict, DHTResponseMessage::R),
-              DHTFindNodeReplyMessage::NODES);
-  std::vector<SharedHandle<DHTNode> > nodes =
-    extractNodes(nodesData->uc(), nodesData->s().size());
+    asString(getDictionary(dict, DHTResponseMessage::R)->
+             get(family_ == AF_INET?DHTFindNodeReplyMessage::NODES:
+                 DHTFindNodeReplyMessage::NODES6));
+  std::vector<SharedHandle<DHTNode> > nodes;
+  if(nodesData) {
+    extractNodes(nodes, nodesData->uc(), nodesData->s().size());
+  }
   return createFindNodeReplyMessage(remoteNode, nodes, transactionID);
 }
 
@@ -404,69 +396,56 @@ DHTMessageFactoryImpl::createGetPeersMessage
   return m;
 }
 
-SharedHandle<DHTResponseMessage>
-DHTMessageFactoryImpl::createGetPeersReplyMessageWithNodes
-(const SharedHandle<DHTNode>& remoteNode,
- const Dict* dict,
- const std::string& transactionID)
-{
-  const Dict* rDict = getDictionary(dict, DHTResponseMessage::R);
-  const String* nodesData = getString(rDict, DHTGetPeersReplyMessage::NODES);
-  std::vector<SharedHandle<DHTNode> > nodes = extractNodes
-    (nodesData->uc(), nodesData->s().size());
-  const String* token = getString(rDict, DHTGetPeersReplyMessage::TOKEN);
-  return createGetPeersReplyMessage
-    (remoteNode, nodes, token->s(), transactionID);
-}
-
 SharedHandle<DHTResponseMessage>
 DHTMessageFactoryImpl::createGetPeersReplyMessage
-(const SharedHandle<DHTNode>& remoteNode,
- const std::vector<SharedHandle<DHTNode> >& closestKNodes,
- const std::string& token,
- const std::string& transactionID)
-{
-  SharedHandle<DHTGetPeersReplyMessage> m
-    (new DHTGetPeersReplyMessage(localNode_, remoteNode, token, transactionID));
-  m->setClosestKNodes(closestKNodes);
-  setCommonProperty(m);
-  return m;
-}
-
-SharedHandle<DHTResponseMessage>
-DHTMessageFactoryImpl::createGetPeersReplyMessageWithValues
 (const SharedHandle<DHTNode>& remoteNode,
  const Dict* dict,
  const std::string& transactionID)
 {
   const Dict* rDict = getDictionary(dict, DHTResponseMessage::R);
-  const List* valuesList = getList(rDict,
-                                  DHTGetPeersReplyMessage::VALUES);
+  const String* nodesData =
+    asString(rDict->get(family_ == AF_INET?DHTGetPeersReplyMessage::NODES:
+                        DHTGetPeersReplyMessage::NODES6));
+  std::vector<SharedHandle<DHTNode> > nodes;
+  if(nodesData) {
+    extractNodes(nodes, nodesData->uc(), nodesData->s().size());
+  }
+  const List* valuesList =
+    asList(rDict->get(DHTGetPeersReplyMessage::VALUES));
   std::vector<SharedHandle<Peer> > peers;
-  for(List::ValueType::const_iterator i = valuesList->begin(),
-        eoi = valuesList->end(); i != eoi; ++i) {
-    const String* data = asString(*i);
-    if(data && data->s().size() == 6) {
-      std::pair<std::string, uint16_t> addr =
-        bittorrent::unpackcompact(data->uc(), AF_INET);
-      SharedHandle<Peer> peer(new Peer(addr.first, addr.second));
-      peers.push_back(peer);
+  size_t clen = bittorrent::getCompactLength(family_);
+  if(valuesList) {
+    for(List::ValueType::const_iterator i = valuesList->begin(),
+          eoi = valuesList->end(); i != eoi; ++i) {
+      const String* data = asString(*i);
+      if(data && data->s().size() == clen) {
+        std::pair<std::string, uint16_t> addr =
+          bittorrent::unpackcompact(data->uc(), family_);
+        if(addr.first.empty()) {
+          continue;
+        }
+        SharedHandle<Peer> peer(new Peer(addr.first, addr.second));
+        peers.push_back(peer);
+      }
     }
-  }
+  }  
   const String* token = getString(rDict, DHTGetPeersReplyMessage::TOKEN);
   return createGetPeersReplyMessage
-    (remoteNode, peers, token->s(), transactionID);
+    (remoteNode, nodes, peers, token->s(), transactionID);
 }
 
 SharedHandle<DHTResponseMessage>
 DHTMessageFactoryImpl::createGetPeersReplyMessage
 (const SharedHandle<DHTNode>& remoteNode,
+ const std::vector<SharedHandle<DHTNode> >& closestKNodes,
  const std::vector<SharedHandle<Peer> >& values,
  const std::string& token,
  const std::string& transactionID)
 {
   SharedHandle<DHTGetPeersReplyMessage> m
-    (new DHTGetPeersReplyMessage(localNode_, remoteNode, token, transactionID));
+    (new DHTGetPeersReplyMessage
+     (family_, localNode_, remoteNode, token, transactionID));
+  m->setClosestKNodes(closestKNodes);
   m->setValues(values);
   setCommonProperty(m);
   return m;

+ 8 - 15
src/DHTMessageFactoryImpl.h

@@ -51,6 +51,8 @@ class DHTAbstractMessage;
 
 class DHTMessageFactoryImpl:public DHTMessageFactory {
 private:
+  int family_;
+
   SharedHandle<DHTNode> localNode_;
 
   WeakHandle<DHTConnection> connection_;
@@ -73,13 +75,14 @@ private:
 
   void validatePort(const Integer* i) const;
 
-  std::vector<SharedHandle<DHTNode> >
-  extractNodes(const unsigned char* src, size_t length);
+  void extractNodes
+  (std::vector<SharedHandle<DHTNode> >& nodes,
+   const unsigned char* src, size_t length);
 
   void setCommonProperty(const SharedHandle<DHTAbstractMessage>& m);
 
 public:
-  DHTMessageFactoryImpl();
+  DHTMessageFactoryImpl(int family);
 
   virtual ~DHTMessageFactoryImpl();
 
@@ -127,26 +130,16 @@ public:
   createGetPeersReplyMessage
   (const SharedHandle<DHTNode>& remoteNode,
    const std::vector<SharedHandle<DHTNode> >& closestKNodes,
+   const std::vector<SharedHandle<Peer> >& peers,
    const std::string& token,
    const std::string& transactionID);
 
   SharedHandle<DHTResponseMessage>
-  createGetPeersReplyMessageWithNodes(const SharedHandle<DHTNode>& remoteNode,
-                                      const Dict* dict,
-                                      const std::string& transactionID);
-
-  virtual SharedHandle<DHTResponseMessage>
   createGetPeersReplyMessage
   (const SharedHandle<DHTNode>& remoteNode,
-   const std::vector<SharedHandle<Peer> >& peers,
-   const std::string& token,
+   const Dict* dict,
    const std::string& transactionID);
 
-  SharedHandle<DHTResponseMessage>
-  createGetPeersReplyMessageWithValues(const SharedHandle<DHTNode>& remoteNode,
-                                       const Dict* dict,
-                                       const std::string& transactionID);
-
   virtual SharedHandle<DHTQueryMessage>
   createAnnouncePeerMessage(const SharedHandle<DHTNode>& remoteNode,
                             const unsigned char* infoHash,

+ 16 - 11
src/DHTMessageTracker.cc

@@ -84,19 +84,24 @@ DHTMessageTracker::messageArrived
         logger_->debug("Tracker entry found.");
       }
       SharedHandle<DHTNode> targetNode = entry->getTargetNode();
+      try {
+        SharedHandle<DHTResponseMessage> message =
+          factory_->createResponseMessage(entry->getMessageType(), dict,
+                                          targetNode->getIPAddress(),
+                                          targetNode->getPort());
 
-      SharedHandle<DHTResponseMessage> message =
-        factory_->createResponseMessage(entry->getMessageType(), dict,
-                                        targetNode->getIPAddress(),
-                                        targetNode->getPort());
-
-      int64_t rtt = entry->getElapsedMillis();
-      if(logger_->debug()) {
-        logger_->debug("RTT is %s", util::itos(rtt).c_str());
+        int64_t rtt = entry->getElapsedMillis();
+        if(logger_->debug()) {
+          logger_->debug("RTT is %s", util::itos(rtt).c_str());
+        }
+        message->getRemoteNode()->updateRTT(rtt);
+        SharedHandle<DHTMessageCallback> callback = entry->getCallback();
+        return std::make_pair(message, callback);
+      } catch(RecoverableException& e) {
+        entry->getCallback()->onTimeout(targetNode);
+        return std::pair<SharedHandle<DHTResponseMessage>,
+                         SharedHandle<DHTMessageCallback> >();
       }
-      message->getRemoteNode()->updateRTT(rtt);
-      SharedHandle<DHTMessageCallback> callback = entry->getCallback();
-      return std::make_pair(message, callback);
     }
   }
   if(logger_->debug()) {

+ 22 - 9
src/DHTRegistry.cc

@@ -49,17 +49,30 @@ namespace aria2 {
 
 DHTRegistry::Data DHTRegistry::data_;
 
+DHTRegistry::Data DHTRegistry::data6_;
+
+void DHTRegistry::clear(DHTRegistry::Data& data)
+{
+  data.initialized = false;
+  data.localNode.reset();
+  data.routingTable.reset();
+  data.taskQueue.reset();
+  data.taskFactory.reset();
+  data.peerAnnounceStorage.reset();
+  data.tokenTracker.reset();
+  data.messageDispatcher.reset();
+  data.messageReceiver.reset();
+  data.messageFactory.reset();
+}
+
 void DHTRegistry::clearData()
 {
-  data_.localNode.reset();
-  data_.routingTable.reset();
-  data_.taskQueue.reset();
-  data_.taskFactory.reset();
-  data_.peerAnnounceStorage.reset();
-  data_.tokenTracker.reset();
-  data_.messageDispatcher.reset();
-  data_.messageReceiver.reset();
-  data_.messageFactory.reset();
+  clear(data_);
+}
+
+void DHTRegistry::clearData6()
+{
+  clear(data6_);
 }
 
 } // namespace aria2

+ 40 - 0
src/DHTRegistry.h

@@ -53,6 +53,8 @@ class DHTMessageFactory;
 class DHTRegistry {
 private:
   struct Data {
+    bool initialized;
+
     SharedHandle<DHTNode> localNode;
 
     SharedHandle<DHTRoutingTable> routingTable;
@@ -70,9 +72,15 @@ private:
     SharedHandle<DHTMessageReceiver> messageReceiver;
 
     SharedHandle<DHTMessageFactory> messageFactory;
+
+    Data():initialized(false) {}
   };
 
   static Data data_;
+  static Data data6_;
+
+  static void clear(Data& data);
+
   DHTRegistry();
 public:
   static const Data& getData()
@@ -86,6 +94,38 @@ public:
   }
 
   static void clearData();
+
+  static bool isInitialized()
+  {
+    return data_.initialized;
+  }
+
+  static void setInitialized(bool f)
+  {
+    data_.initialized = f;
+  }
+
+  static const Data& getData6()
+  {
+    return data6_;
+  }
+
+  static Data& getMutableData6()
+  {
+    return data6_;
+  }
+
+  static void clearData6();
+
+  static bool isInitialized6()
+  {
+    return data6_.initialized;
+  }
+
+  static void setInitialized6(bool f)
+  {
+    data6_.initialized = f;
+  }
 };
 
 } // namespace aria2

+ 18 - 22
src/DHTRoutingTableDeserializer.cc

@@ -52,7 +52,8 @@
 
 namespace aria2 {
 
-DHTRoutingTableDeserializer::DHTRoutingTableDeserializer() {}
+DHTRoutingTableDeserializer::DHTRoutingTableDeserializer(int family):
+  family_(family) {}
 
 DHTRoutingTableDeserializer::~DHTRoutingTableDeserializer() {}
 
@@ -99,7 +100,7 @@ void DHTRoutingTableDeserializer::deserialize(std::istream& in)
   headerCompat[6] = 0;
   headerCompat[7] = 0x02;
 
-  char zero[8];
+  char zero[18];
   memset(zero, 0, sizeof(zero));
 
   int version;
@@ -159,44 +160,39 @@ void DHTRoutingTableDeserializer::deserialize(std::istream& in)
 
   std::vector<SharedHandle<DHTNode> > nodes;
   // nodes
+  const int compactlen = bittorrent::getCompactLength(family_);
   for(size_t i = 0; i < numNodes; ++i) {
-    // Currently, only IPv4 addresses are supported.
     // 1byte compact peer info length
     uint8_t peerInfoLen;
     in >> peerInfoLen;
-    if(peerInfoLen != 6) {
+    if(peerInfoLen != compactlen) {
       // skip this entry
-      readBytes(buf, buf.size(), in, 42+7+6);
-      CHECK_STREAM(in, 42+7+6);
+      readBytes(buf, buf.size(), in, 7+48);
+      CHECK_STREAM(in, 7+48);
       continue;
     }
     // 7bytes reserved
     readBytes(buf, buf.size(), in, 7);
     CHECK_STREAM(in, 7);
-    // 6bytes compact peer info
-    readBytes(buf, buf.size(), in, 6);
-    CHECK_STREAM(in, 6);
-    if(memcmp(zero, buf, 6) == 0) {
+    // compactlen bytes compact peer info
+    readBytes(buf, buf.size(), in, compactlen);
+    CHECK_STREAM(in, compactlen);
+    if(memcmp(zero, buf, compactlen) == 0) {
       // skip this entry
-      readBytes(buf, buf.size(), in, 42);
-      CHECK_STREAM(in, 42);
+      readBytes(buf, buf.size(), in, 48-compactlen);
+      CHECK_STREAM(in, 48-compactlen);
       continue;
     }
-    // TODO DHT6 protocol family should be configurable.
     std::pair<std::string, uint16_t> peer =
-      bittorrent::unpackcompact(buf, AF_INET);
+      bittorrent::unpackcompact(buf, family_);
     if(peer.first.empty()) {
       // skip this entry
-      readBytes(buf, buf.size(), in, 42);
-      CHECK_STREAM(in, 42);
+      readBytes(buf, buf.size(), in, 48-compactlen);
+      CHECK_STREAM(in, 48-compactlen);
       continue;
     }
-    // 2bytes reserved
-    readBytes(buf, buf.size(), in, 2);
-    CHECK_STREAM(in, 2);
-    // 16byte reserved
-    readBytes(buf, buf.size(), in, 16);
-    CHECK_STREAM(in, 16);
+    // 24-compactlen bytes reserved
+    readBytes(buf, buf.size(), in, 24-compactlen);
     // node ID
     readBytes(buf, buf.size(), in, DHT_ID_LENGTH);
     CHECK_STREAM(in, DHT_ID_LENGTH);

+ 3 - 1
src/DHTRoutingTableDeserializer.h

@@ -49,13 +49,15 @@ class DHTNode;
 
 class DHTRoutingTableDeserializer {
 private:
+  int family_;
+
   SharedHandle<DHTNode> localNode_;
 
   std::vector<SharedHandle<DHTNode> > nodes_;
 
   Time serializedTime_;
 public:
-  DHTRoutingTableDeserializer();
+  DHTRoutingTableDeserializer(int family);
 
   ~DHTRoutingTableDeserializer();
 

+ 12 - 14
src/DHTRoutingTableSerializer.cc

@@ -50,7 +50,8 @@
 
 namespace aria2 {
 
-DHTRoutingTableSerializer::DHTRoutingTableSerializer() {}
+DHTRoutingTableSerializer::DHTRoutingTableSerializer(int family):
+  family_(family) {}
 
 DHTRoutingTableSerializer::~DHTRoutingTableSerializer() {}
 
@@ -79,7 +80,7 @@ void DHTRoutingTableSerializer::serialize(std::ostream& o)
   header[6] = 0;
   header[7] = 0x03;
   
-  char zero[16];
+  char zero[18];
   memset(zero, 0, sizeof(zero));
 
   o.write(header, 8);
@@ -101,29 +102,26 @@ void DHTRoutingTableSerializer::serialize(std::ostream& o)
   // 4bytes reserved
   o.write(zero, 4);
 
+  const int clen = bittorrent::getCompactLength(family_);
   // nodes
   for(std::vector<SharedHandle<DHTNode> >::const_iterator i = nodes_.begin(),
         eoi = nodes_.end(); i != eoi; ++i) {
     const SharedHandle<DHTNode>& node = *i;
-    // Currently, only IPv4 addresses are saved.
-    // 6bytes: write IP address + port in Compact IP-address/port info form.
+    // Write IP address + port in Compact IP-address/port info form.
     unsigned char compactPeer[COMPACT_LEN_IPV6];
     int compactlen = bittorrent::packcompact
       (compactPeer, node->getIPAddress(), node->getPort());
-    if(compactlen != COMPACT_LEN_IPV4) {
-      compactlen = COMPACT_LEN_IPV4;
-      memset(compactPeer, 0, COMPACT_LEN_IPV4);
+    if(compactlen != clen) {
+      memset(compactPeer, 0, clen);
     }
     // 1byte compact peer format length
-    o << static_cast<uint8_t>(compactlen);
+    o << static_cast<uint8_t>(clen);
     // 7bytes reserved
     o.write(zero, 7);
-    // 6 bytes compact peer
-    o.write(reinterpret_cast<const char*>(compactPeer), compactlen);
-    // 2bytes reserved
-    o.write(zero, 2);
-    // 16bytes reserved
-    o.write(zero, 16);
+    // clen bytes compact peer
+    o.write(reinterpret_cast<const char*>(compactPeer), clen);
+    // 24-clen bytes reserved
+    o.write(zero, 24-clen);
     // 20bytes: node ID
     o.write(reinterpret_cast<const char*>(node->getID()), DHT_ID_LENGTH);
     // 4bytes reserved

+ 3 - 1
src/DHTRoutingTableSerializer.h

@@ -48,11 +48,13 @@ class DHTNode;
 
 class DHTRoutingTableSerializer {
 private:
+  int family_;
+
   SharedHandle<DHTNode> localNode_;
 
   std::vector<SharedHandle<DHTNode> > nodes_;
 public:
-  DHTRoutingTableSerializer();
+  DHTRoutingTableSerializer(int family);
 
   ~DHTRoutingTableSerializer();
 

+ 58 - 31
src/DHTSetup.cc

@@ -79,16 +79,18 @@
 
 namespace aria2 {
 
-// TODO DownloadEngine should hold this flag.
-bool DHTSetup::initialized_ = false;
-
 DHTSetup::DHTSetup():logger_(LogFactory::getInstance()) {}
 
 DHTSetup::~DHTSetup() {}
 
-void DHTSetup::setup(std::vector<Command*>& commands, DownloadEngine* e)
+void DHTSetup::setup
+(std::vector<Command*>& commands, DownloadEngine* e, int family)
 {
-  if(initialized_) {
+  if(family != AF_INET && family != AF_INET6) {
+    return;
+  }
+  if((family == AF_INET && DHTRegistry::isInitialized()) ||
+     (family == AF_INET6 && DHTRegistry::isInitialized6())) {
     return;
   }
   try {
@@ -98,8 +100,10 @@ void DHTSetup::setup(std::vector<Command*>& commands, DownloadEngine* e)
 
     SharedHandle<DHTNode> localNode;
 
-    DHTRoutingTableDeserializer deserializer;
-    std::string dhtFile = e->getOption()->get(PREF_DHT_FILE_PATH);
+    DHTRoutingTableDeserializer deserializer(family);
+    const std::string& dhtFile =
+      e->getOption()->get(family == AF_INET?PREF_DHT_FILE_PATH:
+                          PREF_DHT_FILE_PATH6);
     try {
       std::ifstream in(dhtFile.c_str(), std::ios::binary);
       if(!in) {
@@ -115,12 +119,15 @@ void DHTSetup::setup(std::vector<Command*>& commands, DownloadEngine* e)
       localNode.reset(new DHTNode());
     }
 
-    SharedHandle<DHTConnectionImpl> connection(new DHTConnectionImpl());
+    SharedHandle<DHTConnectionImpl> connection(new DHTConnectionImpl(family));
     {
       IntSequence seq =
         util::parseIntRange(e->getOption()->get(PREF_DHT_LISTEN_PORT));
       uint16_t port;
-      if(!connection->bind(port, seq)) {
+      const std::string& addr =
+        e->getOption()->get(family == AF_INET?PREF_DHT_LISTEN_ADDR:
+                            PREF_DHT_LISTEN_ADDR6);
+      if(!connection->bind(port, addr, seq)) {
         throw DL_ABORT_EX("Error occurred while binding port for DHT");
       }
       localNode->setPort(port);
@@ -131,7 +138,8 @@ void DHTSetup::setup(std::vector<Command*>& commands, DownloadEngine* e)
     }
     SharedHandle<DHTRoutingTable> routingTable(new DHTRoutingTable(localNode));
 
-    SharedHandle<DHTMessageFactoryImpl> factory(new DHTMessageFactoryImpl());
+    SharedHandle<DHTMessageFactoryImpl> factory
+      (new DHTMessageFactoryImpl(family));
 
     SharedHandle<DHTMessageTracker> tracker(new DHTMessageTracker());
 
@@ -179,16 +187,27 @@ void DHTSetup::setup(std::vector<Command*>& commands, DownloadEngine* e)
     factory->setLocalNode(localNode);
 
     // assign them into DHTRegistry
-    DHTRegistry::getMutableData().localNode = localNode;
-    DHTRegistry::getMutableData().routingTable = routingTable;
-    DHTRegistry::getMutableData().taskQueue = taskQueue;
-    DHTRegistry::getMutableData().taskFactory = taskFactory;
-    DHTRegistry::getMutableData().peerAnnounceStorage = peerAnnounceStorage;
-    DHTRegistry::getMutableData().tokenTracker = tokenTracker;
-    DHTRegistry::getMutableData().messageDispatcher = dispatcher;
-    DHTRegistry::getMutableData().messageReceiver = receiver;
-    DHTRegistry::getMutableData().messageFactory = factory;
-
+    if(family == AF_INET) {
+      DHTRegistry::getMutableData().localNode = localNode;
+      DHTRegistry::getMutableData().routingTable = routingTable;
+      DHTRegistry::getMutableData().taskQueue = taskQueue;
+      DHTRegistry::getMutableData().taskFactory = taskFactory;
+      DHTRegistry::getMutableData().peerAnnounceStorage = peerAnnounceStorage;
+      DHTRegistry::getMutableData().tokenTracker = tokenTracker;
+      DHTRegistry::getMutableData().messageDispatcher = dispatcher;
+      DHTRegistry::getMutableData().messageReceiver = receiver;
+      DHTRegistry::getMutableData().messageFactory = factory;
+    } else {
+      DHTRegistry::getMutableData6().localNode = localNode;
+      DHTRegistry::getMutableData6().routingTable = routingTable;
+      DHTRegistry::getMutableData6().taskQueue = taskQueue;
+      DHTRegistry::getMutableData6().taskFactory = taskFactory;
+      DHTRegistry::getMutableData6().peerAnnounceStorage = peerAnnounceStorage;
+      DHTRegistry::getMutableData6().tokenTracker = tokenTracker;
+      DHTRegistry::getMutableData6().messageDispatcher = dispatcher;
+      DHTRegistry::getMutableData6().messageReceiver = receiver;
+      DHTRegistry::getMutableData6().messageFactory = factory;
+    }
     // add deserialized nodes to routing table
     const std::vector<SharedHandle<DHTNode> >& desnodes =
       deserializer.getNodes();
@@ -206,11 +225,16 @@ void DHTSetup::setup(std::vector<Command*>& commands, DownloadEngine* e)
       taskQueue->addPeriodicTask1(task);
     }
 
-    if(!e->getOption()->get(PREF_DHT_ENTRY_POINT_HOST).empty()) {
+    const std::string& prefEntryPointHost =
+      family == AF_INET?PREF_DHT_ENTRY_POINT_HOST:PREF_DHT_ENTRY_POINT_HOST6;
+    if(!e->getOption()->get(prefEntryPointHost).empty()) {
       {
+        const std::string& prefEntryPointPort =
+          family == AF_INET?PREF_DHT_ENTRY_POINT_PORT:
+          PREF_DHT_ENTRY_POINT_PORT6;
         std::pair<std::string, uint16_t> addr
-          (e->getOption()->get(PREF_DHT_ENTRY_POINT_HOST),
-           e->getOption()->getAsInt(PREF_DHT_ENTRY_POINT_PORT));
+          (e->getOption()->get(prefEntryPointHost),
+           e->getOption()->getAsInt(prefEntryPointPort));
         std::vector<std::pair<std::string, uint16_t> > entryPoints;
         entryPoints.push_back(addr);
         DHTEntryPointNameResolveCommand* command =
@@ -258,24 +282,27 @@ void DHTSetup::setup(std::vector<Command*>& commands, DownloadEngine* e)
     }
     {
       DHTAutoSaveCommand* command =
-        new DHTAutoSaveCommand(e->newCUID(), e, 30*60);
+        new DHTAutoSaveCommand(e->newCUID(), e, family, 30*60);
       command->setLocalNode(localNode);
       command->setRoutingTable(routingTable);
       tempCommands->push_back(command);
     }
-    initialized_ = true;
+    if(family == AF_INET) {
+      DHTRegistry::setInitialized(true);
+    } else {
+      DHTRegistry::setInitialized6(true);
+    }
     commands.insert(commands.end(), tempCommands->begin(), tempCommands->end());
     tempCommands->clear();
   } catch(RecoverableException& e) {
     logger_->error("Exception caught while initializing DHT functionality."
                    " DHT is disabled.", e);
-    DHTRegistry::clearData();
+    if(family == AF_INET) {
+      DHTRegistry::clearData();
+    } else {
+      DHTRegistry::clearData6();
+    }
   }
 }
 
-bool DHTSetup::initialized()
-{
-  return initialized_;
-}
-
 } // namespace aria2

+ 1 - 6
src/DHTSetup.h

@@ -45,18 +45,13 @@ class Command;
 
 class DHTSetup {
 private:
-  static bool initialized_;
-
   Logger* logger_;
-
 public:
   DHTSetup();
 
   ~DHTSetup();
 
-  void setup(std::vector<Command*>& commands, DownloadEngine* e);
-
-  static bool initialized();
+  void setup(std::vector<Command*>& commands, DownloadEngine* e, int family);
 };
 
 } // namespace aria2

+ 1 - 1
src/FtpConnection.cc

@@ -223,7 +223,7 @@ SharedHandle<SocketCore> FtpConnection::createServerSocket()
   std::pair<std::string, uint16_t> addrinfo;
   socket_->getAddrInfo(addrinfo);
   SharedHandle<SocketCore> serverSocket(new SocketCore());
-  serverSocket->bind(addrinfo.first, 0);
+  serverSocket->bind(addrinfo.first, 0, AF_UNSPEC);
   serverSocket->beginListen();
   serverSocket->setNonBlockingMode();
   return serverSocket;

+ 1 - 1
src/LpdMessageReceiver.cc

@@ -56,7 +56,7 @@ bool LpdMessageReceiver::init(const std::string& localAddr)
     // Binding multicast address fails under Windows.
     socket_->bindWithFamily(multicastPort_, AF_INET);
 #else // !__MINGW32__
-    socket_->bind(multicastAddress_, multicastPort_);
+    socket_->bind(multicastAddress_, multicastPort_, AF_INET);
 #endif // !__MINGW32__
     if(logger_->debug()) {
       logger_->debug("Joining multicast group. %s:%u, localAddr=%s",

+ 48 - 0
src/OptionHandlerFactory.cc

@@ -1325,6 +1325,16 @@ OptionHandlers OptionHandlerFactory::createOptionHandlers()
     op->addTag(TAG_BITTORRENT);
     handlers.push_back(op);
   }
+  {
+    SharedHandle<OptionHandler> op(new HostPortOptionHandler
+                                   (PREF_DHT_ENTRY_POINT6,
+                                    TEXT_DHT_ENTRY_POINT6,
+                                    NO_DEFAULT_VALUE,
+                                    PREF_DHT_ENTRY_POINT_HOST6,
+                                    PREF_DHT_ENTRY_POINT_PORT6));
+    op->addTag(TAG_BITTORRENT);
+    handlers.push_back(op);
+  }
   {
     SharedHandle<OptionHandler> op(new DefaultOptionHandler
                                    (PREF_DHT_FILE_PATH,
@@ -1334,6 +1344,34 @@ OptionHandlers OptionHandlerFactory::createOptionHandlers()
     op->addTag(TAG_BITTORRENT);
     handlers.push_back(op);
   }
+  {
+    SharedHandle<OptionHandler> op(new DefaultOptionHandler
+                                   (PREF_DHT_FILE_PATH6,
+                                    TEXT_DHT_FILE_PATH6,
+                                    util::getHomeDir()+"/.aria2/dht6.dat",
+                                    "/PATH/TO/DHT_DAT"));
+    op->addTag(TAG_BITTORRENT);
+    handlers.push_back(op);
+  }
+  {
+    SharedHandle<OptionHandler> op(new DefaultOptionHandler
+                                   (PREF_DHT_LISTEN_ADDR,
+                                    NO_DESCRIPTION,
+                                    NO_DEFAULT_VALUE));
+    op->hide();
+    op->addTag(TAG_BASIC);
+    op->addTag(TAG_BITTORRENT);
+    handlers.push_back(op);
+  }
+  {
+    SharedHandle<OptionHandler> op(new DefaultOptionHandler
+                                   (PREF_DHT_LISTEN_ADDR6,
+                                    TEXT_DHT_LISTEN_ADDR6,
+                                    NO_DEFAULT_VALUE));
+    op->addTag(TAG_BASIC);
+    op->addTag(TAG_BITTORRENT);
+    handlers.push_back(op);
+  }
   {
     SharedHandle<OptionHandler> op(new IntegerRangeOptionHandler
                                    (PREF_DHT_LISTEN_PORT,
@@ -1363,6 +1401,16 @@ OptionHandlers OptionHandlerFactory::createOptionHandlers()
     op->addTag(TAG_BITTORRENT);
     handlers.push_back(op);
   }
+  {
+    SharedHandle<OptionHandler> op(new BooleanOptionHandler
+                                   (PREF_ENABLE_DHT6,
+                                    TEXT_ENABLE_DHT6,
+                                    V_FALSE,
+                                    OptionHandler::OPT_ARG));
+    op->addTag(TAG_BASIC);
+    op->addTag(TAG_BITTORRENT);
+    handlers.push_back(op);
+  }
   {
     SharedHandle<OptionHandler> op(new BooleanOptionHandler
                                    (PREF_ENABLE_PEER_EXCHANGE,

+ 8 - 7
src/OptionHandlerImpl.h

@@ -522,17 +522,18 @@ public:
 
   virtual void parseArg(Option& option, const std::string& optarg)
   {
-    std::pair<std::string, std::string> proxy = util::split(optarg, ":");
-    int32_t port = util::parseInt(proxy.second);
-    if(proxy.first.empty() || proxy.second.empty() ||
-       port <= 0 || 65535 < port) {
-      throw DL_ABORT_EX(_("unrecognized proxy format"));
+    std::string uri = "http://";
+    uri += optarg;
+    Request req;
+    if(!req.setUri(uri)) {
+      throw DL_ABORT_EX(_("Unrecognized format"));
     }
     option.put(optName_, optarg);
-    setHostAndPort(option, proxy.first, port);
+    setHostAndPort(option, req.getHost(), req.getPort());
   }
 
-  void setHostAndPort(Option& option, const std::string& hostname, uint16_t port)
+  void setHostAndPort
+  (Option& option, const std::string& hostname, uint16_t port)
   {
     option.put(hostOptionName_, hostname);
     option.put(portOptionName_, util::uitos(port));

+ 41 - 10
src/PeerInteractionCommand.cc

@@ -60,8 +60,13 @@
 #include "DHTTaskQueue.h"
 #include "DHTTaskFactory.h"
 #include "DHTNode.h"
-#include "DHTSetup.h"
 #include "DHTRegistry.h"
+#include "DHTPeerAnnounceStorage.h"
+#include "DHTTokenTracker.h"
+#include "DHTMessageDispatcher.h"
+#include "DHTMessageReceiver.h"
+#include "DHTMessageFactory.h"
+#include "DHTMessageCallback.h"
 #include "PieceStorage.h"
 #include "RequestGroup.h"
 #include "BtAnnounce.h"
@@ -102,6 +107,17 @@ PeerInteractionCommand::PeerInteractionCommand
     setWriteCheckSocket(getSocket());
     setTimeout(getOption()->getAsInt(PREF_PEER_CONNECTION_TIMEOUT));
   }
+
+  int family;
+  unsigned char compact[COMPACT_LEN_IPV6];
+  int compactlen = bittorrent::packcompact
+    (compact, getPeer()->getIPAddress(), getPeer()->getPort());
+  if(compactlen == COMPACT_LEN_IPV6) {
+    family = AF_INET6;
+  } else {
+    family = AF_INET;
+  }
+ 
   SharedHandle<TorrentAttribute> torrentAttrs =
     bittorrent::getTorrentAttrs(requestGroup_->getDownloadContext());
   bool metadataGetMode = torrentAttrs->metadata.empty();
@@ -131,11 +147,18 @@ PeerInteractionCommand::PeerInteractionCommand
   factory->setPieceStorage(pieceStorage);
   factory->setPeerStorage(peerStorage);
   factory->setExtensionMessageFactory(extensionMessageFactory);
-  factory->setPeer(getPeer());
-  factory->setLocalNode(DHTRegistry::getData().localNode);
-  factory->setRoutingTable(DHTRegistry::getData().routingTable);
-  factory->setTaskQueue(DHTRegistry::getData().taskQueue);
-  factory->setTaskFactory(DHTRegistry::getData().taskFactory);
+  factory->setPeer(getPeer());  
+  if(family == AF_INET) {
+    factory->setLocalNode(DHTRegistry::getData().localNode);
+    factory->setRoutingTable(DHTRegistry::getData().routingTable);
+    factory->setTaskQueue(DHTRegistry::getData().taskQueue);
+    factory->setTaskFactory(DHTRegistry::getData().taskFactory);
+  } else {
+    factory->setLocalNode(DHTRegistry::getData6().localNode);
+    factory->setRoutingTable(DHTRegistry::getData6().routingTable);
+    factory->setTaskQueue(DHTRegistry::getData6().taskQueue);
+    factory->setTaskFactory(DHTRegistry::getData6().taskFactory);
+  }
   if(metadataGetMode) {
     factory->enableMetadataGetMode();
   }
@@ -192,10 +215,18 @@ PeerInteractionCommand::PeerInteractionCommand
     if(getOption()->getAsBool(PREF_ENABLE_PEER_EXCHANGE)) {
       btInteractive->setUTPexEnabled(true);
     }
-    if(DHTSetup::initialized()) {
-      btInteractive->setDHTEnabled(true);
-      btInteractive->setLocalNode(DHTRegistry::getData().localNode);
-      factory->setDHTEnabled(true);
+    if(family == AF_INET) {
+      if(DHTRegistry::isInitialized()) {
+        btInteractive->setDHTEnabled(true);
+        factory->setDHTEnabled(true);
+        btInteractive->setLocalNode(DHTRegistry::getData().localNode);
+      }
+    } else {
+      if(DHTRegistry::isInitialized6()) {
+        btInteractive->setDHTEnabled(true);
+        factory->setDHTEnabled(true);
+        btInteractive->setLocalNode(DHTRegistry::getData6().localNode);
+      }
     }
   }
   btInteractive->setUTMetadataRequestFactory(utMetadataRequestFactory);

+ 37 - 11
src/RequestGroup.cc

@@ -94,6 +94,15 @@
 # include "BtPostDownloadHandler.h"
 # include "DHTSetup.h"
 # include "DHTRegistry.h"
+# include "DHTNode.h"
+# include "DHTRoutingTable.h"
+# include "DHTTaskQueue.h"
+# include "DHTTaskFactory.h"
+# include "DHTTokenTracker.h"
+# include "DHTMessageDispatcher.h"
+# include "DHTMessageReceiver.h"
+# include "DHTMessageFactory.h"
+# include "DHTMessageCallback.h"
 # include "BtMessageFactory.h"
 # include "BtRequestFactory.h"
 # include "BtMessageDispatcher.h"
@@ -283,10 +292,18 @@ void RequestGroup::createInitialCommand
                                 SharedHandle<BtProgressInfoFile>
                                 (progressInfoFile))));
       if(metadataGetMode) {
-        if(option_->getAsBool(PREF_ENABLE_DHT)) {
-          std::vector<Command*> dhtCommands;
-          DHTSetup().setup(dhtCommands, e);
-          e->addCommand(dhtCommands);
+        if(option_->getAsBool(PREF_ENABLE_DHT) ||
+           option_->getAsBool(PREF_ENABLE_DHT6)) {
+          if(option_->getAsBool(PREF_ENABLE_DHT)) {
+            std::vector<Command*> dhtCommands;
+            DHTSetup().setup(dhtCommands, e, AF_INET);
+            e->addCommand(dhtCommands);
+          }
+          if(option_->getAsBool(PREF_ENABLE_DHT6)) {
+            std::vector<Command*> dhtCommands;
+            DHTSetup().setup(dhtCommands, e, AF_INET6);
+            e->addCommand(dhtCommands);
+          }
         } else {
           logger_->notice("For BitTorrent Magnet URI, enabling DHT is strongly"
                           " recommended. See --enable-dht option.");
@@ -343,16 +360,25 @@ void RequestGroup::createInitialCommand
       }
       progressInfoFile_ = progressInfoFile;
 
-      if(!torrentAttrs->privateTorrent && option_->getAsBool(PREF_ENABLE_DHT)) {
-        std::vector<Command*> dhtCommands;
-        DHTSetup().setup(dhtCommands, e);
-        e->addCommand(dhtCommands);
+      if(!torrentAttrs->privateTorrent &&
+         (option_->getAsBool(PREF_ENABLE_DHT) ||
+          option_->getAsBool(PREF_ENABLE_DHT6))) {
+        if(option_->getAsBool(PREF_ENABLE_DHT)) {
+          std::vector<Command*> dhtCommands;
+          DHTSetup().setup(dhtCommands, e, AF_INET);
+          e->addCommand(dhtCommands);
+        }
+        if(option_->getAsBool(PREF_ENABLE_DHT6)) {
+          std::vector<Command*> dhtCommands;
+          DHTSetup().setup(dhtCommands, e, AF_INET6);
+          e->addCommand(dhtCommands);
+        }
         const std::vector<std::pair<std::string, uint16_t> >& nodes =
           torrentAttrs->nodes;
-        if(!nodes.empty() && DHTSetup::initialized()) {
-          std::vector<std::pair<std::string, uint16_t> > entryPoints(nodes);
+        // TODO Are nodes in torrent IPv4 only?
+        if(!nodes.empty() && DHTRegistry::isInitialized()) {
           DHTEntryPointNameResolveCommand* command =
-            new DHTEntryPointNameResolveCommand(e->newCUID(), e, entryPoints);
+            new DHTEntryPointNameResolveCommand(e->newCUID(), e, nodes);
           command->setTaskQueue(DHTRegistry::getData().taskQueue);
           command->setTaskFactory(DHTRegistry::getData().taskFactory);
           command->setRoutingTable(DHTRegistry::getData().routingTable);

+ 26 - 14
src/SocketCore.cc

@@ -54,6 +54,7 @@
 #include "TimeA2.h"
 #include "a2functional.h"
 #include "LogFactory.h"
+#include "A2STR.h"
 #ifdef ENABLE_SSL
 # include "TLSContext.h"
 #endif // ENABLE_SSL
@@ -215,6 +216,14 @@ static sock_t bindInternal(int family, int socktype, int protocol,
     CLOSE(fd);
     return -1;
   }
+  if(family == AF_INET6) {
+    int sockopt = 1;
+    if(setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (a2_sockopt_t) &sockopt,
+                  sizeof(sockopt)) < 0) {
+      CLOSE(fd);
+      return -1;
+    }
+  }
   if(::bind(fd, addr, addrlen) == -1) {
     error = errorMsg();
     CLOSE(fd);
@@ -258,25 +267,19 @@ void SocketCore::bindWithFamily(uint16_t port, int family, int flags)
   }
 }
 
-void SocketCore::bind(const std::string& addr, uint16_t port, int flags)
+void SocketCore::bind
+(const std::string& addr, uint16_t port, int family, int flags)
 {
   closeConnection();
   std::string error;
-  sock_t fd =
-    bindTo(addr.c_str(), port, protocolFamily_, sockType_, flags, error);
-  if(fd == (sock_t)-1) {
-    throw DL_ABORT_EX(StringFormat(EX_SOCKET_BIND, error.c_str()).str());
+  const char* addrp;
+  if(addr.empty()) {
+    addrp = 0;
   } else {
-    sockfd_ = fd;
+    addrp = addr.c_str();
   }
-}
-
-void SocketCore::bind(uint16_t port, int flags)
-{
-  closeConnection();
-  std::string error;
   if(!(flags&AI_PASSIVE) || bindAddrs_.empty()) {
-    sock_t fd = bindTo(0, port, protocolFamily_, sockType_, flags, error);
+    sock_t fd = bindTo(addrp, port, family, sockType_, flags, error);
     if(fd != (sock_t) -1) {
       sockfd_ = fd;
     }
@@ -294,7 +297,11 @@ void SocketCore::bind(uint16_t port, int flags)
         error = gai_strerror(s);
         continue;
       }
-      sock_t fd = bindTo(host, port, protocolFamily_, sockType_, flags, error);
+      if(addrp && strcmp(host, addrp) != 0) {
+        // TODO we should assign something to error?
+        continue;
+      }
+      sock_t fd = bindTo(addrp, port, family, sockType_, flags, error);
       if(fd != (sock_t)-1) {
         sockfd_ = fd;
         break;
@@ -306,6 +313,11 @@ void SocketCore::bind(uint16_t port, int flags)
   }
 }
 
+void SocketCore::bind(uint16_t port, int flags)
+{
+  bind(A2STR::NIL, port, protocolFamily_, flags);
+}
+
 void SocketCore::bind(const struct sockaddr* addr, socklen_t addrlen)
 {
   closeConnection();

+ 3 - 2
src/SocketCore.h

@@ -137,8 +137,6 @@ public:
 
   void bindWithFamily(uint16_t port, int family, int flags = AI_PASSIVE);
 
-  void bind(const std::string& addr, uint16_t port, int flags = AI_PASSIVE);
-
   /**
    * Creates a socket and bind it with locahost's address and port.
    * flags is set to struct addrinfo's ai_flags.
@@ -147,6 +145,9 @@ public:
    */
   void bind(uint16_t port, int flags = AI_PASSIVE);
 
+  void bind
+  (const std::string& addr, uint16_t port, int family, int flags = AI_PASSIVE);
+
   /**
    * Listens form connection on it.
    * Call bind(uint16_t) before calling this function.

+ 11 - 0
src/bittorrent_helper.cc

@@ -994,6 +994,17 @@ std::string torrent2Magnet(const SharedHandle<TorrentAttribute>& attrs)
   return uri;
 }
 
+int getCompactLength(int family)
+{
+  if(family == AF_INET) {
+    return COMPACT_LEN_IPV4;
+  } else if(family == AF_INET6) {
+    return COMPACT_LEN_IPV6;
+  } else {
+    return 0;
+  }
+}
+
 } // namespace bittorrent
 
 } // namespace aria2

+ 5 - 0
src/bittorrent_helper.h

@@ -246,6 +246,9 @@ void extractPeer(const ValueBase* peerData, int family, OutputIterator dest)
         const unsigned char* end = base+length;
         for(; base != end; base += unit) {
           std::pair<std::string, uint16_t> p = unpackcompact(base, family_);
+          if(p.first.empty()) {
+            continue;
+          }
           *dest_++ = SharedHandle<Peer>(new Peer(p.first, p.second));
         }
       }
@@ -288,6 +291,8 @@ void extractPeer
   return extractPeer(peerData.get(), family, dest);
 }
 
+int getCompactLength(int family);
+
 } // namespace bittorrent
 
 } // namespace aria2

+ 14 - 0
src/prefs.cc

@@ -308,6 +308,8 @@ const std::string PREF_PEER_ID_PREFIX("peer-id-prefix");
 const std::string PREF_ENABLE_PEER_EXCHANGE("enable-peer-exchange");
 // values: true | false
 const std::string PREF_ENABLE_DHT("enable-dht");
+// values: a string
+const std::string PREF_DHT_LISTEN_ADDR("dht-listen-addr");
 // values: 1*digit
 const std::string PREF_DHT_LISTEN_PORT("dht-listen-port");
 // values: a string
@@ -318,6 +320,18 @@ const std::string PREF_DHT_ENTRY_POINT_PORT("dht-entry-point-port");
 const std::string PREF_DHT_ENTRY_POINT("dht-entry-point");
 // values: a string
 const std::string PREF_DHT_FILE_PATH("dht-file-path");
+// values: true | false
+const std::string PREF_ENABLE_DHT6("enable-dht6");
+// values: a string
+const std::string PREF_DHT_LISTEN_ADDR6("dht-listen-addr6");
+// values: a string
+const std::string PREF_DHT_ENTRY_POINT_HOST6("dht-entry-point-host6");
+// values: 1*digit
+const std::string PREF_DHT_ENTRY_POINT_PORT6("dht-entry-point-port6");
+// values: a string (hostname:port)
+const std::string PREF_DHT_ENTRY_POINT6("dht-entry-point6");
+// values: a string
+const std::string PREF_DHT_FILE_PATH6("dht-file-path6");
 // values: plain | arc4
 const std::string PREF_BT_MIN_CRYPTO_LEVEL("bt-min-crypto-level");
 const std::string V_PLAIN("plain");

+ 14 - 0
src/prefs.h

@@ -312,6 +312,8 @@ extern const std::string PREF_PEER_ID_PREFIX;
 extern const std::string PREF_ENABLE_PEER_EXCHANGE;
 // values: true | false
 extern const std::string PREF_ENABLE_DHT;
+// values: a string
+extern const std::string PREF_DHT_LISTEN_ADDR;
 // values: 1*digit
 extern const std::string PREF_DHT_LISTEN_PORT;
 // values: a string
@@ -322,6 +324,18 @@ extern const std::string PREF_DHT_ENTRY_POINT_PORT;
 extern const std::string PREF_DHT_ENTRY_POINT;
 // values: a string
 extern const std::string PREF_DHT_FILE_PATH;
+// values: true | false
+extern const std::string PREF_ENABLE_DHT6;
+// values: a string
+extern const std::string PREF_DHT_LISTEN_ADDR6;
+// values: a string
+extern const std::string PREF_DHT_ENTRY_POINT_HOST6;
+// values: 1*digit
+extern const std::string PREF_DHT_ENTRY_POINT_PORT6;
+// values: a string (hostname:port)
+extern const std::string PREF_DHT_ENTRY_POINT6;
+// values: a string
+extern const std::string PREF_DHT_FILE_PATH6;
 // values: plain | arc4
 extern const std::string PREF_BT_MIN_CRYPTO_LEVEL;
 extern const std::string V_PLAIN;

+ 20 - 8
src/usage_text.h

@@ -297,18 +297,18 @@
 #define TEXT_ENABLE_PEER_EXCHANGE                                       \
   _(" --enable-peer-exchange[=true|false] Enable Peer Exchange extension.")
 #define TEXT_ENABLE_DHT                                         \
-  _(" --enable-dht[=true|false]    Enable DHT functionality.")
+  _(" --enable-dht[=true|false]    Enable IPv4 DHT functionality.")
 #define TEXT_DHT_LISTEN_PORT                                            \
-  _(" --dht-listen-port=PORT...    Set UDP listening port for DHT.\n"   \
-    "                              Multiple ports can be specified by using ',',\n" \
-    "                              for example: \"6881,6885\". You can also use '-'\n" \
-    "                              to specify a range: \"6881-6999\". ',' and '-' can\n" \
-    "                              be used together.")
+  _(" --dht-listen-port=PORT...    Set UDP listening port for both IPv4 and IPv6\n"   \
+    "                              DHT. Multiple ports can be specified by using\n" \
+    "                              ',', for example: \"6881,6885\". You can also\n" \
+    "                              use '-' to specify a range: \"6881-6999\". ','\n" \
+    "                              and '-' can be used together.")
 #define TEXT_DHT_ENTRY_POINT                                            \
-  _(" --dht-entry-point=HOST:PORT  Set host and port as an entry point to DHT\n" \
+  _(" --dht-entry-point=HOST:PORT  Set host and port as an entry point to IPv4 DHT\n" \
     "                              network.")
 #define TEXT_DHT_FILE_PATH                                              \
-  _(" --dht-file-path=PATH         Change the DHT routing table file to PATH.")
+  _(" --dht-file-path=PATH         Change the IPv4 DHT routing table file to PATH.")
 #define TEXT_BT_MIN_CRYPTO_LEVEL                                        \
   _(" --bt-min-crypto-level=plain|arc4 Set minimum level of encryption method.\n" \
     "                              If several encryption methods are provided by a\n" \
@@ -714,3 +714,15 @@
   _(" --enable-async-dns6[=true|false] Enable IPv6 name resolution in asynchronous\n" \
     "                              DNS resolver. This option will be ignored when\n" \
     "                              --async-dns=false.")
+#define TEXT_ENABLE_DHT6                        \
+  _(" --enable-dht6[=true|false]   Enable IPv6 DHT functionality. See also\n" \
+    "                              --dht-listen-addr6 option.")
+#define TEXT_DHT_LISTEN_ADDR6                   \
+  _(" --dht-listen-addr6=ADDR      Specify address to bind socket for IPv6 DHT. \n" \
+    "                              It should be a global unicast IPv6 address of the\n" \
+    "                              host.")
+#define TEXT_DHT_ENTRY_POINT6                   \
+  _(" --dht-entry-point6=HOST:PORT Set host and port as an entry point to IPv6 DHT\n" \
+    "                              network.")
+#define TEXT_DHT_FILE_PATH6                     \
+  _(" --dht-file-path6=PATH        Change the IPv6 DHT routing table file to PATH.")

+ 9 - 6
test/DHTConnectionImplTest.cc

@@ -1,9 +1,12 @@
 #include "DHTConnectionImpl.h"
-#include "Exception.h"
-#include "SocketCore.h"
+
 #include <iostream>
 #include <cppunit/extensions/HelperMacros.h>
 
+#include "Exception.h"
+#include "SocketCore.h"
+#include "A2STR.h"
+
 namespace aria2 {
 
 class DHTConnectionImplTest:public CppUnit::TestFixture {
@@ -25,13 +28,13 @@ CPPUNIT_TEST_SUITE_REGISTRATION(DHTConnectionImplTest);
 void DHTConnectionImplTest::testWriteAndReadData()
 {
   try {
-    DHTConnectionImpl con1;
+    DHTConnectionImpl con1(AF_INET);
     uint16_t con1port = 0;
-    CPPUNIT_ASSERT(con1.bind(con1port));
+    CPPUNIT_ASSERT(con1.bind(con1port, A2STR::NIL));
 
-    DHTConnectionImpl con2;
+    DHTConnectionImpl con2(AF_INET);
     uint16_t con2port = 0;
-    CPPUNIT_ASSERT(con2.bind(con2port));
+    CPPUNIT_ASSERT(con2.bind(con2port, A2STR::NIL));
 
     std::string message1 = "hello world.";
     // hostname should be "localhost", not 127.0.0.1. Test failed on Mac OSX10.5

+ 48 - 3
test/DHTFindNodeReplyMessageTest.cc

@@ -15,6 +15,7 @@ class DHTFindNodeReplyMessageTest:public CppUnit::TestFixture {
 
   CPPUNIT_TEST_SUITE(DHTFindNodeReplyMessageTest);
   CPPUNIT_TEST(testGetBencodedMessage);
+  CPPUNIT_TEST(testGetBencodedMessage6);
   CPPUNIT_TEST_SUITE_END();
 public:
   void setUp() {}
@@ -22,6 +23,8 @@ public:
   void tearDown() {}
 
   void testGetBencodedMessage();
+
+  void testGetBencodedMessage6();
 };
 
 
@@ -36,7 +39,7 @@ void DHTFindNodeReplyMessageTest::testGetBencodedMessage()
   util::generateRandomData(tid, DHT_TRANSACTION_ID_LENGTH);
   std::string transactionID(&tid[0], &tid[DHT_TRANSACTION_ID_LENGTH]);
 
-  DHTFindNodeReplyMessage msg(localNode, remoteNode, transactionID);
+  DHTFindNodeReplyMessage msg(AF_INET, localNode, remoteNode, transactionID);
   msg.setVersion("A200");
   std::string compactNodeInfo;
   SharedHandle<DHTNode> nodes[8];
@@ -46,8 +49,8 @@ void DHTFindNodeReplyMessageTest::testGetBencodedMessage()
     nodes[i]->setPort(6881+i);
 
     unsigned char buf[COMPACT_LEN_IPV6];
-    bittorrent::packcompact
-      (buf, nodes[i]->getIPAddress(), nodes[i]->getPort());
+    CPPUNIT_ASSERT_EQUAL(COMPACT_LEN_IPV4, bittorrent::packcompact
+                         (buf, nodes[i]->getIPAddress(), nodes[i]->getPort()));
     compactNodeInfo +=
       std::string(&nodes[i]->getID()[0], &nodes[i]->getID()[DHT_ID_LENGTH])+
       std::string(&buf[0], &buf[COMPACT_LEN_IPV4]);
@@ -69,4 +72,46 @@ void DHTFindNodeReplyMessageTest::testGetBencodedMessage()
   CPPUNIT_ASSERT_EQUAL(bencode2::encode(&dict), msgbody);
 }
 
+void DHTFindNodeReplyMessageTest::testGetBencodedMessage6()
+{
+  SharedHandle<DHTNode> localNode(new DHTNode());
+  SharedHandle<DHTNode> remoteNode(new DHTNode());
+
+  unsigned char tid[DHT_TRANSACTION_ID_LENGTH];
+  util::generateRandomData(tid, DHT_TRANSACTION_ID_LENGTH);
+  std::string transactionID(&tid[0], &tid[DHT_TRANSACTION_ID_LENGTH]);
+
+  DHTFindNodeReplyMessage msg(AF_INET6, localNode, remoteNode, transactionID);
+  msg.setVersion("A200");
+  std::string compactNodeInfo;
+  SharedHandle<DHTNode> nodes[8];
+  for(size_t i = 0; i < DHTBucket::K; ++i) {
+    nodes[i].reset(new DHTNode());
+    nodes[i]->setIPAddress("2001::000"+util::uitos(i+1));
+    nodes[i]->setPort(6881+i);
+
+    unsigned char buf[COMPACT_LEN_IPV6];
+    CPPUNIT_ASSERT_EQUAL(COMPACT_LEN_IPV6, bittorrent::packcompact
+                         (buf, nodes[i]->getIPAddress(), nodes[i]->getPort()));
+    compactNodeInfo +=
+      std::string(&nodes[i]->getID()[0], &nodes[i]->getID()[DHT_ID_LENGTH])+
+      std::string(&buf[0], &buf[COMPACT_LEN_IPV6]);
+  }
+  msg.setClosestKNodes
+    (std::vector<SharedHandle<DHTNode> >(&nodes[0], &nodes[DHTBucket::K]));
+
+  std::string msgbody = msg.getBencodedMessage();
+
+  Dict dict;
+  dict.put("t", transactionID);
+  dict.put("v", "A200");
+  dict.put("y", "r");
+  SharedHandle<Dict> rDict = Dict::g();
+  rDict->put("id", String::g(localNode->getID(), DHT_ID_LENGTH));
+  rDict->put("nodes6", compactNodeInfo);
+  dict.put("r", rDict);
+
+  CPPUNIT_ASSERT_EQUAL(bencode2::encode(&dict), msgbody);
+}
+
 } // namespace aria2

+ 7 - 21
test/DHTGetPeersMessageTest.cc

@@ -31,24 +31,11 @@ public:
 
   class MockDHTMessageFactory2:public MockDHTMessageFactory {
   public:
-    virtual SharedHandle<DHTResponseMessage>
-    createGetPeersReplyMessage(const SharedHandle<DHTNode>& remoteNode,
-                               const std::vector<SharedHandle<Peer> >& peers,
-                               const std::string& token,
-                               const std::string& transactionID)
-    {
-      SharedHandle<MockDHTResponseMessage> m
-        (new MockDHTResponseMessage
-         (localNode_, remoteNode, "get_peers", transactionID));
-      m->peers_ = peers;
-      m->token_ = token;
-      return m;
-    }
-
     virtual SharedHandle<DHTResponseMessage>
     createGetPeersReplyMessage
     (const SharedHandle<DHTNode>& remoteNode,
      const std::vector<SharedHandle<DHTNode> >& closestKNodes,
+     const std::vector<SharedHandle<Peer> >& peers,
      const std::string& token,
      const std::string& transactionID)
     {
@@ -56,10 +43,10 @@ public:
         (new MockDHTResponseMessage
          (localNode_, remoteNode, "get_peers", transactionID));
       m->nodes_ = closestKNodes;
+      m->peers_ = peers;
       m->token_ = token;
       return m;
     }
-
   };
 };
 
@@ -115,14 +102,13 @@ void DHTGetPeersMessageTest::testDoReceivedAction()
   MockDHTMessageDispatcher dispatcher;
   MockDHTMessageFactory2 factory;
   factory.setLocalNode(localNode);
+  DHTRoutingTable routingTable(localNode);
 
   DHTGetPeersMessage msg(localNode, remoteNode, infoHash, transactionID);
-  msg.setTokenTracker(WeakHandle<DHTTokenTracker>
-                      (&tokenTracker));
-  msg.setMessageDispatcher(WeakHandle<DHTMessageDispatcher>
-                           (&dispatcher));
-  msg.setMessageFactory(WeakHandle<DHTMessageFactory>
-                        (&factory));
+  msg.setRoutingTable(WeakHandle<DHTRoutingTable>(&routingTable));
+  msg.setTokenTracker(WeakHandle<DHTTokenTracker>(&tokenTracker));
+  msg.setMessageDispatcher(WeakHandle<DHTMessageDispatcher>(&dispatcher));
+  msg.setMessageFactory(WeakHandle<DHTMessageFactory>(&factory));
   {
     // localhost has peer contact information for that infohash.
     DHTPeerAnnounceStorage peerAnnounceStorage;

+ 76 - 13
test/DHTGetPeersReplyMessageTest.cc

@@ -16,6 +16,7 @@ class DHTGetPeersReplyMessageTest:public CppUnit::TestFixture {
 
   CPPUNIT_TEST_SUITE(DHTGetPeersReplyMessageTest);
   CPPUNIT_TEST(testGetBencodedMessage);
+  CPPUNIT_TEST(testGetBencodedMessage6);
   CPPUNIT_TEST_SUITE_END();
 public:
   void setUp() {}
@@ -23,6 +24,8 @@ public:
   void tearDown() {}
 
   void testGetBencodedMessage();
+
+  void testGetBencodedMessage6();
 };
 
 
@@ -39,7 +42,8 @@ void DHTGetPeersReplyMessageTest::testGetBencodedMessage()
 
   std::string token = "token";
 
-  DHTGetPeersReplyMessage msg(localNode, remoteNode, token, transactionID);
+  DHTGetPeersReplyMessage msg
+    (AF_INET, localNode, remoteNode, token, transactionID);
   msg.setVersion("A200");
   Dict dict;
   dict.put("t", transactionID);
@@ -58,38 +62,97 @@ void DHTGetPeersReplyMessageTest::testGetBencodedMessage()
       nodes[i]->setPort(6881+i);
       
       unsigned char buf[COMPACT_LEN_IPV6];
-      bittorrent::packcompact
-        (buf, nodes[i]->getIPAddress(), nodes[i]->getPort());
+      CPPUNIT_ASSERT_EQUAL
+        (COMPACT_LEN_IPV4,
+         bittorrent::packcompact
+         (buf, nodes[i]->getIPAddress(), nodes[i]->getPort()));
       compactNodeInfo +=
         std::string(&nodes[i]->getID()[0], &nodes[i]->getID()[DHT_ID_LENGTH])+
         std::string(&buf[0], &buf[COMPACT_LEN_IPV4]);
     }
     msg.setClosestKNodes
       (std::vector<SharedHandle<DHTNode> >(&nodes[0], &nodes[DHTBucket::K]));
-
-    std::string msgbody = msg.getBencodedMessage();
-
     rDict->put("nodes", compactNodeInfo);
 
-    CPPUNIT_ASSERT_EQUAL(util::percentEncode(bencode2::encode(&dict)),
-                         util::percentEncode(msgbody));
-  }
-  rDict->removeKey("nodes");
-  {
     std::vector<SharedHandle<Peer> > peers;
     SharedHandle<List> valuesList = List::g();
     for(size_t i = 0; i < 4; ++i) {
       SharedHandle<Peer> peer(new Peer("192.168.0."+util::uitos(i+1), 6881+i));
       unsigned char buffer[COMPACT_LEN_IPV6];
-      bittorrent::packcompact(buffer, peer->getIPAddress(), peer->getPort());
+      CPPUNIT_ASSERT_EQUAL
+        (COMPACT_LEN_IPV4,
+         bittorrent::packcompact
+         (buffer, peer->getIPAddress(), peer->getPort()));
       valuesList->append(String::g(buffer, COMPACT_LEN_IPV4));
       peers.push_back(peer);
     }
+    msg.setValues(peers);
     rDict->put("values", valuesList);
 
+    std::string msgbody = msg.getBencodedMessage();
+    CPPUNIT_ASSERT_EQUAL(util::percentEncode(bencode2::encode(&dict)),
+                         util::percentEncode(msgbody));
+  }
+}
+
+void DHTGetPeersReplyMessageTest::testGetBencodedMessage6()
+{
+  SharedHandle<DHTNode> localNode(new DHTNode());
+  SharedHandle<DHTNode> remoteNode(new DHTNode());
+
+  unsigned char tid[DHT_TRANSACTION_ID_LENGTH];
+  util::generateRandomData(tid, DHT_TRANSACTION_ID_LENGTH);
+  std::string transactionID(&tid[0], &tid[DHT_TRANSACTION_ID_LENGTH]);
+
+  std::string token = "token";
+
+  DHTGetPeersReplyMessage msg
+    (AF_INET6, localNode, remoteNode, token, transactionID);
+  msg.setVersion("A200");
+  Dict dict;
+  dict.put("t", transactionID);
+  dict.put("v", "A200");
+  dict.put("y", "r");
+  SharedHandle<Dict> rDict = Dict::g();
+  rDict->put("id", String::g(localNode->getID(), DHT_ID_LENGTH));
+  rDict->put("token", token);
+  dict.put("r", rDict);
+  {
+    std::string compactNodeInfo;
+    SharedHandle<DHTNode> nodes[8];
+    for(size_t i = 0; i < DHTBucket::K; ++i) {
+      nodes[i].reset(new DHTNode());
+      nodes[i]->setIPAddress("2001::000"+util::uitos(i+1));
+      nodes[i]->setPort(6881+i);
+      
+      unsigned char buf[COMPACT_LEN_IPV6];
+      CPPUNIT_ASSERT_EQUAL
+        (COMPACT_LEN_IPV6, bittorrent::packcompact
+         (buf, nodes[i]->getIPAddress(), nodes[i]->getPort()));
+      compactNodeInfo +=
+        std::string(&nodes[i]->getID()[0], &nodes[i]->getID()[DHT_ID_LENGTH])+
+        std::string(&buf[0], &buf[COMPACT_LEN_IPV6]);
+    }
+    msg.setClosestKNodes
+      (std::vector<SharedHandle<DHTNode> >(&nodes[0], &nodes[DHTBucket::K]));
+    rDict->put("nodes6", compactNodeInfo);
+
+    std::vector<SharedHandle<Peer> > peers;
+    SharedHandle<List> valuesList = List::g();
+    for(size_t i = 0; i < 4; ++i) {
+      SharedHandle<Peer> peer(new Peer("2001::100"+util::uitos(i+1), 6881+i));
+      unsigned char buffer[COMPACT_LEN_IPV6];
+      CPPUNIT_ASSERT_EQUAL
+        (COMPACT_LEN_IPV6,
+         bittorrent::packcompact
+         (buffer, peer->getIPAddress(), peer->getPort()));
+      valuesList->append(String::g(buffer, COMPACT_LEN_IPV6));
+      peers.push_back(peer);
+    }
     msg.setValues(peers);
-    std::string msgbody  = msg.getBencodedMessage();
+    rDict->put("values", valuesList);
 
+    std::string msgbody = msg.getBencodedMessage();
     CPPUNIT_ASSERT_EQUAL(util::percentEncode(bencode2::encode(&dict)),
                          util::percentEncode(msgbody));
   }

+ 118 - 16
test/DHTMessageFactoryImplTest.cc

@@ -31,9 +31,10 @@ class DHTMessageFactoryImplTest:public CppUnit::TestFixture {
   CPPUNIT_TEST(testCreatePingReplyMessage);
   CPPUNIT_TEST(testCreateFindNodeMessage);
   CPPUNIT_TEST(testCreateFindNodeReplyMessage);
+  CPPUNIT_TEST(testCreateFindNodeReplyMessage6);
   CPPUNIT_TEST(testCreateGetPeersMessage);
-  CPPUNIT_TEST(testCreateGetPeersReplyMessage_nodes);
-  CPPUNIT_TEST(testCreateGetPeersReplyMessage_values);
+  CPPUNIT_TEST(testCreateGetPeersReplyMessage);
+  CPPUNIT_TEST(testCreateGetPeersReplyMessage6);
   CPPUNIT_TEST(testCreateAnnouncePeerMessage);
   CPPUNIT_TEST(testCreateAnnouncePeerReplyMessage);
   CPPUNIT_TEST(testReceivedErrorMessage);
@@ -52,7 +53,7 @@ public:
   void setUp()
   {
     localNode.reset(new DHTNode());
-    factory.reset(new DHTMessageFactoryImpl());
+    factory.reset(new DHTMessageFactoryImpl(AF_INET));
     factory->setLocalNode(localNode);
     memset(transactionID, 0xff, DHT_TRANSACTION_ID_LENGTH);
     memset(remoteNodeID, 0x0f, DHT_ID_LENGTH);
@@ -66,9 +67,10 @@ public:
   void testCreatePingReplyMessage();
   void testCreateFindNodeMessage();
   void testCreateFindNodeReplyMessage();
+  void testCreateFindNodeReplyMessage6();
   void testCreateGetPeersMessage();
-  void testCreateGetPeersReplyMessage_nodes();
-  void testCreateGetPeersReplyMessage_values();
+  void testCreateGetPeersReplyMessage();
+  void testCreateGetPeersReplyMessage6();
   void testCreateAnnouncePeerMessage();
   void testCreateAnnouncePeerReplyMessage();
   void testReceivedErrorMessage();
@@ -169,8 +171,10 @@ void DHTMessageFactoryImplTest::testCreateFindNodeReplyMessage()
       nodes[i]->setPort(6881+i);
 
       unsigned char buf[COMPACT_LEN_IPV6];
-      bittorrent::packcompact
-        (buf, nodes[i]->getIPAddress(), nodes[i]->getPort());
+      CPPUNIT_ASSERT_EQUAL
+        (COMPACT_LEN_IPV4,
+         bittorrent::packcompact
+         (buf, nodes[i]->getIPAddress(), nodes[i]->getPort()));
       compactNodeInfo +=
         std::string(&nodes[i]->getID()[0], &nodes[i]->getID()[DHT_ID_LENGTH])+
         std::string(&buf[0], &buf[COMPACT_LEN_IPV4]);
@@ -200,6 +204,58 @@ void DHTMessageFactoryImplTest::testCreateFindNodeReplyMessage()
   }
 }
 
+void DHTMessageFactoryImplTest::testCreateFindNodeReplyMessage6()
+{
+  factory.reset(new DHTMessageFactoryImpl(AF_INET6));
+  factory->setLocalNode(localNode);
+  factory->setRoutingTable(routingTable);
+  try {
+    Dict dict;
+    dict.put("t", String::g(transactionID, DHT_TRANSACTION_ID_LENGTH));
+    dict.put("y", "r");
+    SharedHandle<Dict> rDict = Dict::g();
+    rDict->put("id", String::g(remoteNodeID, DHT_ID_LENGTH));
+    std::string compactNodeInfo;
+    SharedHandle<DHTNode> nodes[8];
+    for(size_t i = 0; i < DHTBucket::K; ++i) {
+      nodes[i].reset(new DHTNode());
+      nodes[i]->setIPAddress("2001::000"+util::uitos(i+1));
+      nodes[i]->setPort(6881+i);
+
+      unsigned char buf[COMPACT_LEN_IPV6];
+      CPPUNIT_ASSERT_EQUAL
+        (COMPACT_LEN_IPV6,
+         bittorrent::packcompact
+         (buf, nodes[i]->getIPAddress(), nodes[i]->getPort()));
+      compactNodeInfo +=
+        std::string(&nodes[i]->getID()[0], &nodes[i]->getID()[DHT_ID_LENGTH])+
+        std::string(&buf[0], &buf[COMPACT_LEN_IPV6]);
+    }
+    rDict->put("nodes6", compactNodeInfo);
+    dict.put("r", rDict);
+
+    SharedHandle<DHTNode> remoteNode(new DHTNode(remoteNodeID));
+    remoteNode->setIPAddress("2001::2001");
+    remoteNode->setPort(6881);
+  
+    SharedHandle<DHTFindNodeReplyMessage> m
+      (dynamic_pointer_cast<DHTFindNodeReplyMessage>
+       (factory->createResponseMessage("find_node", &dict,
+                                       remoteNode->getIPAddress(),
+                                       remoteNode->getPort())));
+
+    CPPUNIT_ASSERT(localNode == m->getLocalNode());
+    CPPUNIT_ASSERT(remoteNode == m->getRemoteNode());
+    CPPUNIT_ASSERT_EQUAL((size_t)DHTBucket::K, m->getClosestKNodes().size());
+    CPPUNIT_ASSERT(nodes[0] == m->getClosestKNodes()[0]);
+    CPPUNIT_ASSERT(nodes[7] == m->getClosestKNodes()[7]);
+    CPPUNIT_ASSERT_EQUAL(util::toHex(transactionID, DHT_TRANSACTION_ID_LENGTH),
+                         util::toHex(m->getTransactionID()));
+  } catch(Exception& e) {
+    CPPUNIT_FAIL(e.stackTrace());
+  }
+}
+
 void DHTMessageFactoryImplTest::testCreateGetPeersMessage()
 {
   Dict dict;
@@ -228,7 +284,7 @@ void DHTMessageFactoryImplTest::testCreateGetPeersMessage()
                        util::toHex(m->getInfoHash(), DHT_ID_LENGTH));
 }
 
-void DHTMessageFactoryImplTest::testCreateGetPeersReplyMessage_nodes()
+void DHTMessageFactoryImplTest::testCreateGetPeersReplyMessage()
 {
   try {
     Dict dict;
@@ -244,13 +300,30 @@ void DHTMessageFactoryImplTest::testCreateGetPeersReplyMessage_nodes()
       nodes[i]->setPort(6881+i);
 
       unsigned char buf[COMPACT_LEN_IPV6];
-      bittorrent::packcompact
-        (buf, nodes[i]->getIPAddress(), nodes[i]->getPort());
+      CPPUNIT_ASSERT_EQUAL
+        (COMPACT_LEN_IPV4,
+         bittorrent::packcompact
+         (buf, nodes[i]->getIPAddress(), nodes[i]->getPort()));
       compactNodeInfo +=
         std::string(&nodes[i]->getID()[0], &nodes[i]->getID()[DHT_ID_LENGTH])+
         std::string(&buf[0], &buf[COMPACT_LEN_IPV4]);
     }
     rDict->put("nodes", compactNodeInfo);
+
+    std::deque<SharedHandle<Peer> > peers;
+    SharedHandle<List> valuesList = List::g();
+    for(size_t i = 0; i < 4; ++i) {
+      SharedHandle<Peer> peer(new Peer("192.168.0."+util::uitos(i+1), 6881+i));
+      unsigned char buffer[COMPACT_LEN_IPV6];
+      CPPUNIT_ASSERT_EQUAL
+        (COMPACT_LEN_IPV4,
+         bittorrent::packcompact
+         (buffer, peer->getIPAddress(), peer->getPort()));
+      valuesList->append(String::g(buffer, COMPACT_LEN_IPV4));
+      peers.push_back(peer);
+    }
+    rDict->put("values", valuesList);
+
     rDict->put("token", "token");
     dict.put("r", rDict);
 
@@ -270,6 +343,9 @@ void DHTMessageFactoryImplTest::testCreateGetPeersReplyMessage_nodes()
     CPPUNIT_ASSERT_EQUAL((size_t)DHTBucket::K, m->getClosestKNodes().size());
     CPPUNIT_ASSERT(nodes[0] == m->getClosestKNodes()[0]);
     CPPUNIT_ASSERT(nodes[7] == m->getClosestKNodes()[7]);
+    CPPUNIT_ASSERT_EQUAL((size_t)4, m->getValues().size());
+    CPPUNIT_ASSERT(peers[0] == m->getValues()[0]);
+    CPPUNIT_ASSERT(peers[3] == m->getValues()[3]);
     CPPUNIT_ASSERT_EQUAL(util::toHex(transactionID, DHT_TRANSACTION_ID_LENGTH),
                          util::toHex(m->getTransactionID()));
   } catch(Exception& e) {
@@ -277,31 +353,54 @@ void DHTMessageFactoryImplTest::testCreateGetPeersReplyMessage_nodes()
   }
 }
 
-void DHTMessageFactoryImplTest::testCreateGetPeersReplyMessage_values()
+void DHTMessageFactoryImplTest::testCreateGetPeersReplyMessage6()
 {
+  factory.reset(new DHTMessageFactoryImpl(AF_INET6));
+  factory->setLocalNode(localNode);
+  factory->setRoutingTable(routingTable);
   try {
     Dict dict;
     dict.put("t", String::g(transactionID, DHT_TRANSACTION_ID_LENGTH));
     dict.put("y", "r");
     SharedHandle<Dict> rDict = Dict::g();
     rDict->put("id", String::g(remoteNodeID, DHT_ID_LENGTH));
+    std::string compactNodeInfo;
+    SharedHandle<DHTNode> nodes[8];
+    for(size_t i = 0; i < DHTBucket::K; ++i) {
+      nodes[i].reset(new DHTNode());
+      nodes[i]->setIPAddress("2001::000"+util::uitos(i+1));
+      nodes[i]->setPort(6881+i);
+
+      unsigned char buf[COMPACT_LEN_IPV6];
+      CPPUNIT_ASSERT_EQUAL
+        (COMPACT_LEN_IPV6,
+         bittorrent::packcompact
+         (buf, nodes[i]->getIPAddress(), nodes[i]->getPort()));
+      compactNodeInfo +=
+        std::string(&nodes[i]->getID()[0], &nodes[i]->getID()[DHT_ID_LENGTH])+
+        std::string(&buf[0], &buf[COMPACT_LEN_IPV6]);
+    }
+    rDict->put("nodes6", compactNodeInfo);
 
     std::deque<SharedHandle<Peer> > peers;
     SharedHandle<List> valuesList = List::g();
     for(size_t i = 0; i < 4; ++i) {
-      SharedHandle<Peer> peer(new Peer("192.168.0."+util::uitos(i+1), 6881+i));
+      SharedHandle<Peer> peer(new Peer("2001::100"+util::uitos(i+1), 6881+i));
       unsigned char buffer[COMPACT_LEN_IPV6];
-      bittorrent::packcompact
-        (buffer, peer->getIPAddress(), peer->getPort());
-      valuesList->append(String::g(buffer, COMPACT_LEN_IPV4));
+      CPPUNIT_ASSERT_EQUAL
+        (COMPACT_LEN_IPV6,
+         bittorrent::packcompact
+         (buffer, peer->getIPAddress(), peer->getPort()));
+      valuesList->append(String::g(buffer, COMPACT_LEN_IPV6));
       peers.push_back(peer);
     }
     rDict->put("values", valuesList);
+
     rDict->put("token", "token");
     dict.put("r", rDict);
 
     SharedHandle<DHTNode> remoteNode(new DHTNode(remoteNodeID));
-    remoteNode->setIPAddress("192.168.0.1");
+    remoteNode->setIPAddress("2001::2001");
     remoteNode->setPort(6881);
   
     SharedHandle<DHTGetPeersReplyMessage> m
@@ -313,6 +412,9 @@ void DHTMessageFactoryImplTest::testCreateGetPeersReplyMessage_values()
     CPPUNIT_ASSERT(localNode == m->getLocalNode());
     CPPUNIT_ASSERT(remoteNode == m->getRemoteNode());
     CPPUNIT_ASSERT_EQUAL(std::string("token"), m->getToken());
+    CPPUNIT_ASSERT_EQUAL((size_t)DHTBucket::K, m->getClosestKNodes().size());
+    CPPUNIT_ASSERT(nodes[0] == m->getClosestKNodes()[0]);
+    CPPUNIT_ASSERT(nodes[7] == m->getClosestKNodes()[7]);
     CPPUNIT_ASSERT_EQUAL((size_t)4, m->getValues().size());
     CPPUNIT_ASSERT(peers[0] == m->getValues()[0]);
     CPPUNIT_ASSERT(peers[3] == m->getValues()[3]);

+ 45 - 2
test/DHTRoutingTableDeserializerTest.cc

@@ -20,6 +20,7 @@ class DHTRoutingTableDeserializerTest:public CppUnit::TestFixture {
 
   CPPUNIT_TEST_SUITE(DHTRoutingTableDeserializerTest);
   CPPUNIT_TEST(testDeserialize);
+  CPPUNIT_TEST(testDeserialize6);
   CPPUNIT_TEST_SUITE_END();
 public:
   void setUp() {}
@@ -27,6 +28,8 @@ public:
   void tearDown() {}
 
   void testDeserialize();
+
+  void testDeserialize6();
 };
 
 
@@ -45,14 +48,14 @@ void DHTRoutingTableDeserializerTest::testDeserialize()
   nodesSrc[1]->setIPAddress("non-numerical-name");
   std::vector<SharedHandle<DHTNode> > nodes(vbegin(nodesSrc), vend(nodesSrc));
   
-  DHTRoutingTableSerializer s;
+  DHTRoutingTableSerializer s(AF_INET);
   s.setLocalNode(localNode);
   s.setNodes(nodes);
 
   std::stringstream ss;
   s.serialize(ss);
 
-  DHTRoutingTableDeserializer d;
+  DHTRoutingTableDeserializer d(AF_INET);
   d.deserialize(ss);
 
   CPPUNIT_ASSERT(memcmp(localNode->getID(), d.getLocalNode()->getID(),
@@ -70,4 +73,44 @@ void DHTRoutingTableDeserializerTest::testDeserialize()
   CPPUNIT_ASSERT(memcmp(nodes[2]->getID(), dsnodes[1]->getID(), DHT_ID_LENGTH) == 0);
 }
 
+void DHTRoutingTableDeserializerTest::testDeserialize6()
+{
+  SharedHandle<DHTNode> localNode(new DHTNode());
+
+  SharedHandle<DHTNode> nodesSrc[3];
+  for(size_t i = 0; i < A2_ARRAY_LEN(nodesSrc); ++i) {
+    nodesSrc[i].reset(new DHTNode());
+    nodesSrc[i]->setIPAddress("2001::100"+util::uitos(i+1));
+    nodesSrc[i]->setPort(6881+i);
+  }
+  nodesSrc[1]->setIPAddress("non-numerical-name");
+  std::vector<SharedHandle<DHTNode> > nodes(vbegin(nodesSrc), vend(nodesSrc));
+  
+  DHTRoutingTableSerializer s(AF_INET6);
+  s.setLocalNode(localNode);
+  s.setNodes(nodes);
+
+  std::stringstream ss;
+  s.serialize(ss);
+
+  DHTRoutingTableDeserializer d(AF_INET6);
+  d.deserialize(ss);
+
+  CPPUNIT_ASSERT(memcmp(localNode->getID(), d.getLocalNode()->getID(),
+                        DHT_ID_LENGTH) == 0);
+
+  std::cout << d.getSerializedTime().getTime() << std::endl;
+
+  CPPUNIT_ASSERT_EQUAL((size_t)2, d.getNodes().size());
+  const std::vector<SharedHandle<DHTNode> >& dsnodes = d.getNodes();
+  CPPUNIT_ASSERT_EQUAL(std::string("2001::1001"), dsnodes[0]->getIPAddress());
+  CPPUNIT_ASSERT_EQUAL((uint16_t)6881, dsnodes[0]->getPort());
+  CPPUNIT_ASSERT(memcmp(nodes[0]->getID(), dsnodes[0]->getID(),
+                        DHT_ID_LENGTH) == 0);
+  CPPUNIT_ASSERT_EQUAL(std::string("2001::1003"), dsnodes[1]->getIPAddress());
+  CPPUNIT_ASSERT_EQUAL((uint16_t)6883, dsnodes[1]->getPort());
+  CPPUNIT_ASSERT(memcmp(nodes[2]->getID(), dsnodes[1]->getID(),
+                        DHT_ID_LENGTH) == 0);
+}
+
 } // namespace aria2

+ 136 - 25
test/DHTRoutingTableSerializerTest.cc

@@ -20,42 +20,35 @@ class DHTRoutingTableSerializerTest:public CppUnit::TestFixture {
 
   CPPUNIT_TEST_SUITE(DHTRoutingTableSerializerTest);
   CPPUNIT_TEST(testSerialize);
+  CPPUNIT_TEST(testSerialize6);
   CPPUNIT_TEST_SUITE_END();
+private:
+  char zero[256];
+  char buf[20];
+
+  void checkToLocalnode
+  (std::istream& ss, const SharedHandle<DHTNode>& localNode);
+
+  void checkNumNodes(std::istream& ss, size_t expected);
 public:
-  void setUp() {}
+  void setUp()
+  {
+    memset(zero, 0, sizeof(zero));
+  }
 
   void tearDown() {}
 
   void testSerialize();
+
+  void testSerialize6();
 };
 
 
 CPPUNIT_TEST_SUITE_REGISTRATION(DHTRoutingTableSerializerTest);
 
-void DHTRoutingTableSerializerTest::testSerialize()
+void DHTRoutingTableSerializerTest::checkToLocalnode
+(std::istream& ss, const SharedHandle<DHTNode>& localNode)
 {
-  SharedHandle<DHTNode> localNode(new DHTNode());
-
-  SharedHandle<DHTNode> nodesSrc[3];
-  for(size_t i = 0; i < A2_ARRAY_LEN(nodesSrc); ++i) {
-    nodesSrc[i].reset(new DHTNode());
-    nodesSrc[i]->setIPAddress("192.168.0."+util::uitos(i+1));
-    nodesSrc[i]->setPort(6881+i);
-  }
-  nodesSrc[1]->setIPAddress("non-numerical-name");
-  std::vector<SharedHandle<DHTNode> > nodes(vbegin(nodesSrc), vend(nodesSrc));
-  
-  DHTRoutingTableSerializer s;
-  s.setLocalNode(localNode);
-  s.setNodes(nodes);
-
-  std::stringstream ss;
-  s.serialize(ss);
-
-  char zero[16];
-  memset(zero, 0, sizeof(zero));
-
-  char buf[20];
   // header
   ss.read(buf, 8);
   // magic
@@ -89,14 +82,44 @@ void DHTRoutingTableSerializerTest::testSerialize()
   // 4bytes reserved
   ss.read(buf, 4);
   CPPUNIT_ASSERT(memcmp(zero, buf, 4) == 0);
+}
 
+void DHTRoutingTableSerializerTest::checkNumNodes
+(std::istream& ss, size_t expected)
+{
   // number of nodes saved
   ss.read(buf, 4);
   uint32_t numNodes;
   memcpy(&numNodes, buf, sizeof(numNodes));
   numNodes = ntohl(numNodes);
 
-  CPPUNIT_ASSERT_EQUAL((uint32_t)3, numNodes);
+  CPPUNIT_ASSERT_EQUAL((uint32_t)expected, numNodes);
+}
+
+void DHTRoutingTableSerializerTest::testSerialize()
+{
+  SharedHandle<DHTNode> localNode(new DHTNode());
+
+  SharedHandle<DHTNode> nodesSrc[3];
+  for(size_t i = 0; i < A2_ARRAY_LEN(nodesSrc); ++i) {
+    nodesSrc[i].reset(new DHTNode());
+    nodesSrc[i]->setIPAddress("192.168.0."+util::uitos(i+1));
+    nodesSrc[i]->setPort(6881+i);
+  }
+  nodesSrc[1]->setIPAddress("non-numerical-name");
+  std::vector<SharedHandle<DHTNode> > nodes(vbegin(nodesSrc), vend(nodesSrc));
+  
+  DHTRoutingTableSerializer s(AF_INET);
+  s.setLocalNode(localNode);
+  s.setNodes(nodes);
+
+  std::stringstream ss;
+  s.serialize(ss);
+
+  checkToLocalnode(ss, localNode);
+  size_t numNodes = 3;
+  checkNumNodes(ss, numNodes);
+
   // 4bytes reserved
   ss.read(buf, 4);
   CPPUNIT_ASSERT(memcmp(zero, buf, 4) == 0);
@@ -199,4 +222,92 @@ void DHTRoutingTableSerializerTest::testSerialize()
   CPPUNIT_ASSERT(ss.eof());
 }
 
+void DHTRoutingTableSerializerTest::testSerialize6()
+{
+  SharedHandle<DHTNode> localNode(new DHTNode());
+
+  SharedHandle<DHTNode> nodesSrc[2];
+  for(size_t i = 0; i < A2_ARRAY_LEN(nodesSrc); ++i) {
+    nodesSrc[i].reset(new DHTNode());
+    nodesSrc[i]->setIPAddress("2001::100"+util::uitos(i+1));
+    nodesSrc[i]->setPort(6881+i);
+  }
+  nodesSrc[1]->setIPAddress("non-numerical-name");
+  std::vector<SharedHandle<DHTNode> > nodes(vbegin(nodesSrc), vend(nodesSrc));
+  
+  DHTRoutingTableSerializer s(AF_INET6);
+  s.setLocalNode(localNode);
+  s.setNodes(nodes);
+
+  std::stringstream ss;
+  s.serialize(ss);
+
+  checkToLocalnode(ss, localNode);
+  size_t numNodes = 2;
+  checkNumNodes(ss, numNodes);
+
+  // 4bytes reserved
+  ss.read(buf, 4);
+  CPPUNIT_ASSERT(memcmp(zero, buf, 4) == 0);
+
+  // node[0]
+  // 1byte compatc peer format length
+  {
+    uint8_t len;
+    ss >> len;
+    CPPUNIT_ASSERT_EQUAL((uint8_t)18, len);
+  }
+  // 7bytes reserved
+  ss.read(buf, 7);
+  CPPUNIT_ASSERT(memcmp(zero, buf, 7) == 0);
+  // 18 bytes compact peer info
+  ss.read(buf, 18);
+  {
+    std::pair<std::string, uint16_t> peer =
+      bittorrent::unpackcompact(reinterpret_cast<const unsigned char*>(buf),
+                                AF_INET6);
+    CPPUNIT_ASSERT_EQUAL(std::string("2001::1001"), peer.first);
+    CPPUNIT_ASSERT_EQUAL((uint16_t)6881, peer.second);
+  }
+  // 6bytes reserved
+  ss.read(buf, 6);
+  CPPUNIT_ASSERT(memcmp(zero, buf, 6) == 0);
+  // localnode ID
+  ss.read(buf, DHT_ID_LENGTH);
+  CPPUNIT_ASSERT(memcmp(nodes[0]->getID(), buf, DHT_ID_LENGTH) == 0);
+  // 4bytes reserved
+  ss.read(buf, 4);
+  CPPUNIT_ASSERT(memcmp(zero, buf, 4) == 0);
+
+  // node[1]
+  // 1byte compatc peer format length
+  {
+    uint8_t len;
+    ss >> len;
+    CPPUNIT_ASSERT_EQUAL((uint8_t)18, len);
+  }
+  // 7bytes reserved
+  ss.read(buf, 7);
+  CPPUNIT_ASSERT(memcmp(zero, buf, 7) == 0);
+  // 18bytes compact peer info
+  ss.read(buf, 18);
+  // zero filled because node[1]'s hostname is not numerical form
+  // deserializer should skip this entry
+  CPPUNIT_ASSERT(memcmp(zero, buf, 18) == 0);
+  // 6bytes reserved
+  ss.read(buf, 6);
+  CPPUNIT_ASSERT(memcmp(zero, buf, 6) == 0);
+  // localnode ID
+  ss.read(buf, DHT_ID_LENGTH);
+  CPPUNIT_ASSERT(memcmp(nodes[1]->getID(), buf, DHT_ID_LENGTH) == 0);
+  // 4bytes reserved
+  ss.read(buf, 4);
+  CPPUNIT_ASSERT(memcmp(zero, buf, 4) == 0);
+
+  // check to see stream ends
+  ss.read(buf, 1);
+  CPPUNIT_ASSERT_EQUAL((std::streamsize)0, ss.gcount());
+  CPPUNIT_ASSERT(ss.eof());
+}
+
 } // namespace aria2

+ 1 - 1
test/LpdMessageDispatcherTest.cc

@@ -46,7 +46,7 @@ void LpdMessageDispatcherTest::testSendMessage()
 #ifdef __MINGW32__
   recvsock->bindWithFamily(LPD_MULTICAST_PORT, AF_INET);
 #else // !__MINGW32__
-  recvsock->bind(LPD_MULTICAST_ADDR, LPD_MULTICAST_PORT);
+  recvsock->bind(LPD_MULTICAST_ADDR, LPD_MULTICAST_PORT, AF_INET);
 #endif // !__MINGW32__
   recvsock->joinMulticastGroup(LPD_MULTICAST_ADDR, LPD_MULTICAST_PORT, "");
   

+ 3 - 12
test/MockDHTMessageFactory.h

@@ -81,18 +81,9 @@ public:
   createGetPeersReplyMessage
   (const SharedHandle<DHTNode>& remoteNode,
    const std::vector<SharedHandle<DHTNode> >& closestKNodes,
-                             const std::string& token,
-                             const std::string& transactionID)
-  {
-    return SharedHandle<DHTResponseMessage>();
-  }
-
-
-  virtual SharedHandle<DHTResponseMessage>
-  createGetPeersReplyMessage(const SharedHandle<DHTNode>& remoteNode,
-                             const std::vector<SharedHandle<Peer> >& peers,
-                             const std::string& token,
-                             const std::string& transactionID)
+   const std::vector<SharedHandle<Peer> >& peers,
+   const std::string& token,
+   const std::string& transactionID)
   {
     return SharedHandle<DHTResponseMessage>();
   }