Browse Source

2007-12-22 Tatsuhiro Tsujikawa <tujikawa at rednoah dot com>

	Added uTorrent compatible Peer Exchange.
	* src/BencodeVisitor.{h, cc}
	* test/BencodeVisitorTest.cc
	* src/BtConstants.h
	* src/BtContext.h: Added 'private' flag.
	* src/BtExtendedMessage.{h, cc}
	* test/BtExtendedMessageTest.cc
	* src/BtHandshakeMessage.{h, cc}: Set extended messaging bit in
	reserved field.
	* test/BtHandshakeMessageTest.cc
	* src/BtMessageFactory.h
	* src/BtRegistry.h
	* src/BtRuntime.h: This class holds default extension message 
IDs for
	aria2. By default, aria2 uses ID 8 for ut_pex.
	* src/DefaultBtContext.cc
	* src/DefaultBtInteractive.{h, cc}: This class holds 
_utPexEnabled.
	When it is true, aria2 enables ut_pex. This value is set by
	PeerInteractionCommand.
	* src/DefaultBtMessageFactory.{h, cc}
	* test/DefaultBtMessageFactoryTest.cc
	* src/DefaultBtMessageReceiver.cc: Moved the code of fast 
extension
	handling to DefaultBtInteractive class.
	* src/DefaultExtensionMessageFactory.{h, cc}
	* test/DefaultExtensionMessageFactoryTest.cc
	* src/DefaultPeerStorage.cc: Returns false if a peer is already 
in
	the container(peers and incomingPeers. The equality is 
determined by
	Peer::id).
	* test/DefaultPeerStorageTest.cc
	* src/ExtensionMessage.h
	* test/MockExtensionMessage.h
	* src/ExtensionMessageFactory.h
	* test/MockExtensionMessageFactory.h
	* src/HandshakeExtensionMessage.{h, cc}
	* test/HandshakeExtensionMessageTest.cc
	* src/MetaEntry.h
	* src/Peer.{h, cc}
	* src/PeerInteractionCommand.cc
	* src/PeerReceiveHandshakeCommand.cc: Evaluate the return value 
of
	addIncomingPeer.
	* src/PeerMessageUtil.{h, cc}
	* src/PeerObject.h
	* src/UTPexExtensionMessage.{h, cc}
	* test/UTPexExtensionMessageTest.cc
	* src/message.h
	* src/prefs.h

	Fixed the bug that returns incomplete data when it contains null
	character. A convenient constructor was also added.
	* src/Data.{h, cc}

	Rewritten.
	* src/CompactPeerListProcessor.cc

	Fixed typos.
	* src/message.h
	* src/MetaFileUtil.cc
Tatsuhiro Tsujikawa 18 years ago
parent
commit
286f34cb3f
61 changed files with 2487 additions and 100 deletions
  1. 59 0
      ChangeLog
  2. 84 0
      src/BencodeVisitor.cc
  3. 64 0
      src/BencodeVisitor.h
  4. 43 0
      src/BtConstants.h
  5. 9 0
      src/BtContext.h
  6. 115 0
      src/BtExtendedMessage.cc
  7. 79 0
      src/BtExtendedMessage.h
  8. 7 0
      src/BtHandshakeMessage.cc
  9. 2 0
      src/BtHandshakeMessage.h
  10. 5 0
      src/BtMessageFactory.h
  11. 3 0
      src/BtRegistry.h
  12. 33 2
      src/BtRuntime.h
  13. 6 12
      src/CompactPeerListProcessor.cc
  14. 13 10
      src/Data.cc
  15. 3 0
      src/Data.h
  16. 7 0
      src/DefaultBtContext.cc
  17. 81 2
      src/DefaultBtInteractive.cc
  18. 11 1
      src/DefaultBtInteractive.h
  19. 18 0
      src/DefaultBtMessageFactory.cc
  20. 2 0
      src/DefaultBtMessageFactory.h
  21. 1 8
      src/DefaultBtMessageReceiver.cc
  22. 93 0
      src/DefaultExtensionMessageFactory.cc
  23. 70 0
      src/DefaultExtensionMessageFactory.h
  24. 17 12
      src/DefaultPeerStorage.cc
  25. 2 0
      src/DefaultPeerStorage.h
  26. 57 0
      src/ExtensionMessage.h
  27. 51 0
      src/ExtensionMessageFactory.h
  28. 174 0
      src/HandshakeExtensionMessage.cc
  29. 128 0
      src/HandshakeExtensionMessage.h
  30. 6 1
      src/Makefile.am
  31. 24 6
      src/Makefile.in
  32. 1 0
      src/MetaEntry.h
  33. 9 9
      src/MetaFileUtil.cc
  34. 30 1
      src/Peer.cc
  35. 32 2
      src/Peer.h
  36. 3 0
      src/PeerInteractionCommand.cc
  37. 14 0
      src/PeerMessageUtil.cc
  38. 10 0
      src/PeerMessageUtil.h
  39. 5 2
      src/PeerObject.h
  40. 15 14
      src/PeerReceiveHandshakeCommand.cc
  41. 149 0
      src/UTPexExtensionMessage.cc
  42. 99 0
      src/UTPexExtensionMessage.h
  43. 3 1
      src/message.h
  44. 1 0
      src/option_processing.cc
  45. 2 0
      src/prefs.h
  46. 70 0
      test/BencodeVisitorTest.cc
  47. 121 0
      test/BtExtendedMessageTest.cc
  48. 3 3
      test/BtHandshakeMessageTest.cc
  49. 78 0
      test/DefaultBtMessageFactoryTest.cc
  50. 113 0
      test/DefaultExtensionMessageFactoryTest.cc
  51. 3 3
      test/DefaultPeerListProcessorTest.cc
  52. 2 3
      test/DefaultPeerStorageTest.cc
  53. 155 0
      test/HandshakeExtensionMessageTest.cc
  54. 7 1
      test/Makefile.am
  55. 25 4
      test/Makefile.in
  56. 8 0
      test/MockBtMessageFactory.h
  57. 51 0
      test/MockExtensionMessage.h
  58. 20 0
      test/MockExtensionMessageFactory.h
  59. 3 2
      test/ShaVisitorTest.cc
  60. 187 0
      test/UTPexExtensionMessageTest.cc
  61. 1 1
      test/test.torrent

+ 59 - 0
ChangeLog

@@ -1,3 +1,62 @@
+2007-12-22  Tatsuhiro Tsujikawa  <tujikawa at rednoah dot com>
+
+	Added uTorrent compatible Peer Exchange.
+	* src/BencodeVisitor.{h, cc}
+	* test/BencodeVisitorTest.cc
+	* src/BtConstants.h
+	* src/BtContext.h: Added 'private' flag.
+	* src/BtExtendedMessage.{h, cc}
+	* test/BtExtendedMessageTest.cc
+	* src/BtHandshakeMessage.{h, cc}: Set extended messaging bit in
+	reserved field.
+	* test/BtHandshakeMessageTest.cc
+	* src/BtMessageFactory.h
+	* src/BtRegistry.h
+	* src/BtRuntime.h: This class holds default extension message IDs for
+	aria2. By default, aria2 uses ID 8 for ut_pex.
+	* src/DefaultBtContext.cc
+	* src/DefaultBtInteractive.{h, cc}: This class holds _utPexEnabled.
+	When it is true, aria2 enables ut_pex. This value is set by
+	PeerInteractionCommand.
+	* src/DefaultBtMessageFactory.{h, cc}
+	* test/DefaultBtMessageFactoryTest.cc
+	* src/DefaultBtMessageReceiver.cc: Moved the code of fast extension
+	handling to DefaultBtInteractive class.
+	* src/DefaultExtensionMessageFactory.{h, cc}
+	* test/DefaultExtensionMessageFactoryTest.cc
+	* src/DefaultPeerStorage.cc: Returns false if a peer is already in
+	the container(peers and incomingPeers. The equality is determined by
+	Peer::id).
+	* test/DefaultPeerStorageTest.cc
+	* src/ExtensionMessage.h
+	* test/MockExtensionMessage.h
+	* src/ExtensionMessageFactory.h
+	* test/MockExtensionMessageFactory.h
+	* src/HandshakeExtensionMessage.{h, cc}
+	* test/HandshakeExtensionMessageTest.cc
+	* src/MetaEntry.h
+	* src/Peer.{h, cc}
+	* src/PeerInteractionCommand.cc
+	* src/PeerReceiveHandshakeCommand.cc: Evaluate the return value of
+	addIncomingPeer.
+	* src/PeerMessageUtil.{h, cc}
+	* src/PeerObject.h
+	* src/UTPexExtensionMessage.{h, cc}
+	* test/UTPexExtensionMessageTest.cc
+	* src/message.h
+	* src/prefs.h
+
+	Fixed the bug that returns incomplete data when it contains null
+	character. A convenient constructor was also added.
+	* src/Data.{h, cc}
+
+	Rewritten.
+	* src/CompactPeerListProcessor.cc
+
+	Fixed typos.
+	* src/message.h
+	* src/MetaFileUtil.cc
+	
 2007-12-16  Tatsuhiro Tsujikawa  <tujikawa at rednoah dot com>
 	
 	Added "Status Legend" label to the explanation text of 'stat' in

+ 84 - 0
src/BencodeVisitor.cc

@@ -0,0 +1,84 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2006 Tatsuhiro Tsujikawa
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL.  If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so.  If you
+ * do not wish to do so, delete this exception statement from your
+ * version.  If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#include "BencodeVisitor.h"
+#include "Data.h"
+#include "List.h"
+#include "Dictionary.h"
+#include "Util.h"
+
+BencodeVisitor::BencodeVisitor() {}
+
+BencodeVisitor::~BencodeVisitor() {}
+
+void BencodeVisitor::visit(const Data* d)
+{
+  if(d->isNumber()) {
+    _bencodedData += "i"+d->toString()+"e";
+  } else {
+    _bencodedData += Util::itos(d->getLen())+":"+d->toString();
+  }
+}
+
+void BencodeVisitor::visit(const List* l)
+{
+  _bencodedData += "l";
+  for_each(l->getList().begin(), l->getList().end(),
+	   bind2nd(mem_fun(&MetaEntry::accept), this));
+  _bencodedData += "e";
+}
+
+void BencodeVisitor::visit(const Dictionary* d)
+{
+  _bencodedData += "d";
+
+  for(Order::const_iterator itr = d->getOrder().begin(); itr != d->getOrder().end(); ++itr) {
+    _bencodedData += Util::itos((int32_t)(*itr).size());
+    _bencodedData += ":";
+    _bencodedData += *itr;
+    d->get(*itr)->accept(this);
+  }
+  _bencodedData += "e";
+}
+
+void BencodeVisitor::visit(const MetaEntry* e)
+{
+  if(dynamic_cast<const Data*>(e) != 0) {
+    visit((const Data*)e);
+  } else if(dynamic_cast<const List*>(e) != 0) {
+    visit((const List*)e);
+  } else if(dynamic_cast<const Dictionary*>(e) != 0) {
+    visit((const Dictionary*)e);
+  }
+}

+ 64 - 0
src/BencodeVisitor.h

@@ -0,0 +1,64 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2006 Tatsuhiro Tsujikawa
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL.  If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so.  If you
+ * do not wish to do so, delete this exception statement from your
+ * version.  If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#ifndef _D_BENCODE_VISITOR_H_
+#define _D_BENCODE_VISITOR_H_
+
+#include "MetaEntryVisitor.h"
+
+class Data;
+class Dictionary;
+class List;
+class MetaEntry;
+
+class BencodeVisitor : public MetaEntryVisitor {
+private:
+  string _bencodedData;
+public:
+  BencodeVisitor();
+  ~BencodeVisitor();
+
+  void visit(const Data* d);
+  void visit(const Dictionary* d);
+  void visit(const List* l);
+
+  virtual void visit(const MetaEntry* e);
+
+  const string& getBencodedData() const
+  {
+    return _bencodedData;
+  }
+};
+
+#endif // _D_BENCODE_VISITOR_H_

+ 43 - 0
src/BtConstants.h

@@ -0,0 +1,43 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2006 Tatsuhiro Tsujikawa
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL.  If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so.  If you
+ * do not wish to do so, delete this exception statement from your
+ * version.  If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#ifndef _D_BT_CONSTANTS_
+#define _D_BT_CONSTANTS_
+
+#include "common.h"
+#include <map>
+
+typedef map<string, uint8_t> Extensions;
+
+#endif // _D_BT_CONSTANTS_

+ 9 - 0
src/BtContext.h

@@ -48,7 +48,11 @@ typedef deque<AnnounceTierHandle> AnnounceTiers;
 class RequestGroup;
 
 class BtContext:public DownloadContext {
+protected:
+  bool _private;
 public:
+  BtContext():_private(false) {}
+  
   virtual ~BtContext() {}
 
   virtual const unsigned char* getInfoHash() const = 0;
@@ -66,6 +70,11 @@ public:
    */
   virtual const unsigned char* getPeerId() = 0;
 
+  bool isPrivate() const
+  {
+    return _private;
+  }
+
   virtual Integers computeFastSet(const string& ipaddr, int32_t fastSetSize) = 0;
   
   virtual RequestGroup* getOwnerRequestGroup() = 0;

+ 115 - 0
src/BtExtendedMessage.cc

@@ -0,0 +1,115 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2006 Tatsuhiro Tsujikawa
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL.  If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so.  If you
+ * do not wish to do so, delete this exception statement from your
+ * version.  If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#include "BtExtendedMessage.h"
+#include "BtRegistry.h"
+#include "ExtensionMessage.h"
+#include "PeerMessageUtil.h"
+#include "DlAbortEx.h"
+#include "message.h"
+#include "Util.h"
+
+BtExtendedMessage::BtExtendedMessage(const ExtensionMessageHandle& extensionMessage):_extensionMessage(extensionMessage), _msg(0), _msgLength(0)
+{}
+
+BtExtendedMessage::~BtExtendedMessage()
+{
+  delete [] _msg;
+}
+
+const unsigned char* BtExtendedMessage::getMessage() {
+  if(!_msg) {
+    /**
+     * len --- 2+extpayload.length, 4bytes
+     * id --- 20, 1byte
+     * extmsgid --- extmsgid, 1byte
+     * extpayload --- extpayload, nbytes
+     * total: 6+extpayload.length bytes
+     */
+    string payload = _extensionMessage->getBencodedData();
+    _msgLength = 6+payload.size();
+    _msg = new unsigned char[_msgLength];
+    PeerMessageUtil::createPeerMessageString(_msg, _msgLength, 2+payload.size(), ID);
+    *(_msg+5) = _extensionMessage->getExtensionMessageID();
+    memcpy(_msg+6, payload.c_str(), payload.size());
+  }
+  return _msg;
+}
+
+int32_t BtExtendedMessage::getMessageLength() {
+  getMessage();
+  return _msgLength;
+}
+
+bool BtExtendedMessage::sendPredicate() const
+{
+  return peer->isExtendedMessagingEnabled();
+}
+
+string BtExtendedMessage::toString() const {
+  return "extended "+_extensionMessage->toString();
+}
+
+BtExtendedMessageHandle
+BtExtendedMessage::create(const BtContextHandle& btContext,
+			  const PeerHandle& peer,
+			  const char* data, size_t dataLength)
+{
+  if(dataLength < 2) {
+    throw new DlAbortEx(MSG_TOO_SMALL_PAYLOAD_SIZE, "extended", dataLength);
+  }
+  int8_t id = PeerMessageUtil::getId((const unsigned char*)data);
+  if(id != ID) {
+    throw new DlAbortEx(EX_INVALID_BT_MESSAGE_ID, id, "extended", ID);
+  }
+  ExtensionMessageFactoryHandle factory = EXTENSION_MESSAGE_FACTORY(btContext,
+								    peer);
+  assert(!factory.isNull());
+  ExtensionMessageHandle extmsg = factory->createMessage(data+1,
+							 dataLength-1);
+  BtExtendedMessageHandle message = new BtExtendedMessage(extmsg);
+  return message;
+}
+
+void BtExtendedMessage::doReceivedAction()
+{
+  if(!_extensionMessage.isNull()) {
+    _extensionMessage->doReceivedAction();
+  }
+}
+
+ExtensionMessageHandle BtExtendedMessage::getExtensionMessage() const
+{
+  return _extensionMessage;
+}

+ 79 - 0
src/BtExtendedMessage.h

@@ -0,0 +1,79 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2006 Tatsuhiro Tsujikawa
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL.  If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so.  If you
+ * do not wish to do so, delete this exception statement from your
+ * version.  If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#ifndef _D_BT_EXTENDED_MESSAGE_H_
+#define _D_BT_EXTENDED_MESSAGE_H_
+#include "SimpleBtMessage.h"
+
+class BtExtendedMessage;
+typedef SharedHandle<BtExtendedMessage> BtExtendedMessageHandle;
+class ExtensionMessage;
+typedef SharedHandle<ExtensionMessage> ExtensionMessageHandle;
+
+class BtExtendedMessage:public SimpleBtMessage
+{
+private:
+  ExtensionMessageHandle _extensionMessage;
+
+  unsigned char* _msg;
+
+  size_t _msgLength;
+public:
+  BtExtendedMessage(const ExtensionMessageHandle& extensionMessage = 0);
+
+  virtual ~BtExtendedMessage();
+
+  static const uint8_t ID = 20;
+
+  static BtExtendedMessageHandle create(const BtContextHandle& btContext,
+					const PeerHandle& peer,
+					const char* data,
+					size_t dataLength);
+
+  virtual int8_t getId() { return ID; }
+
+  virtual void doReceivedAction();
+
+  virtual const unsigned char* getMessage();
+
+  virtual int32_t getMessageLength();
+
+  virtual bool sendPredicate() const;
+
+  virtual string toString() const;
+
+  ExtensionMessageHandle getExtensionMessage() const;
+};
+
+#endif // _D_BT_EXTENDED_MESSAGE_H_

+ 7 - 0
src/BtHandshakeMessage.cc

@@ -61,6 +61,8 @@ void BtHandshakeMessage::init() {
   memset(this->reserved, 0, RESERVED_LENGTH);
   // fast extension
   this->reserved[7] |= 0x04;
+  // extended messaging
+  this->reserved[5] |= 0x10;
 }
 
 BtHandshakeMessageHandle BtHandshakeMessage::create(const unsigned char* data, int32_t dataLength) {
@@ -98,3 +100,8 @@ string BtHandshakeMessage::toString() const {
 bool BtHandshakeMessage::isFastExtensionSupported() const {
   return reserved[7]&0x04;
 }
+
+bool BtHandshakeMessage::isExtendedMessagingEnabled() const
+{
+  return reserved[5]&0x10;
+}

+ 2 - 0
src/BtHandshakeMessage.h

@@ -87,6 +87,8 @@ public:
 
   bool isFastExtensionSupported() const;
 
+  bool isExtendedMessagingEnabled() const;
+
   int8_t getPstrlen() const {
     return pstrlen;
   }

+ 5 - 0
src/BtMessageFactory.h

@@ -39,6 +39,9 @@
 #include "BtMessage.h"
 #include "Piece.h"
 
+class ExtensionMessage;
+typedef SharedHandle<ExtensionMessage> ExensionMessageHandle;
+
 class BtMessageFactory {
 public:
   virtual ~BtMessageFactory() {}
@@ -84,6 +87,8 @@ public:
   createRejectMessage(int32_t index, int32_t begin, int32_t length) = 0;
 
   virtual BtMessageHandle createAllowedFastMessage(int32_t index) = 0;
+
+  virtual BtMessageHandle createBtExtendedMessage(const ExensionMessageHandle& msg) = 0;
 };
 
 typedef SharedHandle<BtMessageFactory> BtMessageFactoryHandle;

+ 3 - 0
src/BtRegistry.h

@@ -146,4 +146,7 @@ PEER_OBJECT(btContext, peer)->btRequestFactory
 #define PEER_CONNECTION(btContext, peer) \
 PEER_OBJECT(btContext, peer)->peerConnection
 
+#define EXTENSION_MESSAGE_FACTORY(btContext, peer) \
+PEER_OBJECT(btContext, peer)->extensionMessageFactory
+
 #endif // _D_BT_REGISTRY_H_

+ 33 - 2
src/BtRuntime.h

@@ -36,7 +36,7 @@
 #define _D_BT_RUNTIME_H_
 
 #include "common.h"
-#include "SharedHandle.h"
+#include "BtConstants.h"
 
 #define MIN_PEERS 40
 
@@ -47,6 +47,8 @@ private:
   bool halt;
   int32_t connections;
   bool _ready;
+
+  Extensions _extensions;
 public:
   BtRuntime():
     uploadLengthAtStartup(0),
@@ -54,7 +56,9 @@ public:
     halt(false),
     connections(0),
     _ready(false)
-    {}
+  {
+    _extensions["ut_pex"] = 8;
+  }
   ~BtRuntime() {}
 
   int64_t getUploadLengthAtStartup() const {
@@ -90,6 +94,33 @@ public:
   bool ready() { return _ready; }
 
   void setReady(bool go) { _ready = go; }
+
+  const Extensions& getExtensions() const
+  {
+    return _extensions;
+  }
+
+  uint8_t getExtensionMessageID(const string& name)
+  {
+    Extensions::const_iterator itr = _extensions.find(name);
+    if(itr == _extensions.end()) {
+      return 0;
+    } else {
+      return (*itr).second;
+    }
+  }
+
+  string getExtensionName(uint8_t id)
+  {
+    for(Extensions::const_iterator itr = _extensions.begin();
+      itr != _extensions.end(); ++itr) {
+      const Extensions::value_type& p = *itr;
+      if(p.second == id) {
+	return p.first;
+      }
+    }
+    return "";
+  }
 };
 
 typedef SharedHandle<BtRuntime> BtRuntimeHandle;

+ 6 - 12
src/CompactPeerListProcessor.cc

@@ -43,19 +43,13 @@ Peers CompactPeerListProcessor::extractPeer(const MetaEntry* peersEntry) {
   Peers peers;
 
   const Data* peersData = (const Data*)peersEntry;
-  if(peersData->getLen() > 0) {
+  if(peersData->getLen()%6 == 0) {
     for(int32_t i = 0; i < peersData->getLen(); i += 6) {
-      uint32_t ipaddr1 = (unsigned char)*(peersData->getData()+i);
-      uint32_t ipaddr2 = (unsigned char)*(peersData->getData()+i+1);
-      uint32_t ipaddr3 = (unsigned char)*(peersData->getData()+i+2);
-      uint32_t ipaddr4 = (unsigned char)*(peersData->getData()+i+3);
-      int32_t port = ntohs(*(uint16_t*)(peersData->getData()+i+4));
-      char ipaddr[16];
-      
-      snprintf(ipaddr, sizeof(ipaddr), "%d.%d.%d.%d",
-	       ipaddr1, ipaddr2, ipaddr3, ipaddr4);
-      PeerHandle peer = new Peer(ipaddr, port, pieceLength, totalLength);
-      
+      struct in_addr in;
+      in.s_addr = *(uint32_t*)(peersData->getData()+i);
+      string ipaddr = inet_ntoa(in);
+      uint16_t port = ntohs(*(uint16_t*)(peersData->getData()+i+4));
+      PeerHandle peer = new Peer(ipaddr, port, pieceLength, totalLength);      
       peers.push_back(peer);
     }
   }

+ 13 - 10
src/Data.cc

@@ -46,21 +46,24 @@ Data::Data(const char* data, int32_t len, bool number):number(number) {
   }
 }
 
+Data::Data(const string& data, bool number):number(number)
+{
+  if(data.empty()) {
+    this->data = 0;
+    this->len = 0;
+  } else {
+    this->data = new char[data.size()];
+    memcpy(this->data, data.c_str(), data.size());
+    this->len = data.size();
+  }
+}
+
 Data::~Data() {
   delete [] data;
 }
 
 string Data::toString() const {
-  if(len == 0) {
-    return "";
-  } else {
-    char* temp = new char[len+1];
-    memcpy(temp, data, len);
-    temp[len] = '\0';
-    string str(temp);
-    delete [] temp;
-    return str;
-  }
+  return string(&data[0], &data[len]);
 }
 
 const char* Data::getData() const {

+ 3 - 0
src/Data.h

@@ -51,6 +51,9 @@ public:
    * memory of data.
    */
   Data(const char* data, int32_t len, bool number = false);
+
+  Data(const string& data, bool number = false);
+
   ~Data();
 
   string toString() const;

+ 7 - 0
src/DefaultBtContext.cc

@@ -84,6 +84,7 @@ void DefaultBtContext::clear() {
   numPieces = 0;
   name = "";
   announceTiers.clear();
+  _private = false;
 }
 
 void DefaultBtContext::extractPieceHash(const unsigned char* hashData,
@@ -240,6 +241,12 @@ void DefaultBtContext::processMetaInfo(const MetaEntry* rootEntry, const string&
   extractPieceHash((unsigned char*)pieceHashData->getData(),
 		   pieceHashData->getLen(),
 		   PIECE_HASH_LENGTH);
+  const Data* privateFlag = dynamic_cast<const Data*>(infoDic->get("private"));
+  if(privateFlag) {
+    if(privateFlag->toString() == "1") {
+      _private = true;
+    }
+  }
   // retrieve uri-list.
   // This implemantation obeys HTTP-Seeding specification:
   // see http://www.getright.com/seedtorrent.html

+ 81 - 2
src/DefaultBtInteractive.cc

@@ -43,10 +43,16 @@
 #include "BtRequestMessage.h"
 #include "BtPieceMessage.h"
 #include "DlAbortEx.h"
+#include "BtExtendedMessage.h"
+#include "HandshakeExtensionMessage.h"
+#include "UTPexExtensionMessage.h"
+#include "DefaultExtensionMessageFactory.h"
+#include "BtRegistry.h"
 
 void DefaultBtInteractive::initiateHandshake() {
-  BtMessageHandle message = messageFactory->createHandshakeMessage(btContext->getInfoHash(),
-								   btContext->getPeerId());
+  BtHandshakeMessageHandle message =
+    messageFactory->createHandshakeMessage(btContext->getInfoHash(),
+					   btContext->getPeerId());
   dispatcher->addMessageToQueue(message);
   dispatcher->sendMessages();
 }
@@ -58,6 +64,18 @@ BtMessageHandle DefaultBtInteractive::receiveHandshake(bool quickReply) {
       return 0;
     }
     peer->setPeerId(message->getPeerId());
+    
+    if(message->isFastExtensionSupported()) {
+      peer->setFastExtensionEnabled(true);
+      logger->info(MSG_FAST_EXTENSION_ENABLED, cuid);
+    }
+    if(message->isExtendedMessagingEnabled()) {
+      peer->setExtendedMessagingEnabled(true);
+      PEER_OBJECT(btContext, peer)->extensionMessageFactory =
+	new DefaultExtensionMessageFactory(btContext, peer);
+      logger->info(MSG_EXTENDED_MESSAGING_ENABLED, cuid);
+    }
+
     logger->info(MSG_RECEIVE_PEER_MESSAGE, cuid,
 		 peer->ipaddr.c_str(), peer->port,
 		 message->toString().c_str());
@@ -73,11 +91,28 @@ void DefaultBtInteractive::doPostHandshakeProcessing() {
   haveCheckPoint.reset();
   keepAliveCheckPoint.reset();
   floodingCheckPoint.reset();
+  _pexCheckPoint.setTimeInSec(0);
+  if(peer->isExtendedMessagingEnabled()) {
+    addHandshakeExtendedMessageToQueue();
+  }
   addBitfieldMessageToQueue();
   addAllowedFastMessageToQueue();  
   sendPendingMessage();
 }
 
+void DefaultBtInteractive::addHandshakeExtendedMessageToQueue()
+{
+  HandshakeExtensionMessageHandle m = new HandshakeExtensionMessage();
+  m->setClientVersion("aria2");
+  m->setTCPPort(btRuntime->getListenPort());
+  m->setExtensions(btRuntime->getExtensions());
+  
+  BtExtendedMessageHandle msg =
+    messageFactory->createBtExtendedMessage(m);
+  
+  dispatcher->addMessageToQueue(msg);
+}
+
 void DefaultBtInteractive::addBitfieldMessageToQueue() {
   if(peer->isFastExtensionEnabled()) {
     if(pieceStorage->allDownloadFinished()) {
@@ -283,6 +318,45 @@ void DefaultBtInteractive::checkActiveInteraction()
   }
 }
 
+void DefaultBtInteractive::addPeerExchangeMessage()
+{
+  time_t interval = 60;
+  if(_pexCheckPoint.elapsed(interval)) {
+    UTPexExtensionMessageHandle m =
+      new UTPexExtensionMessage(peer->getExtensionMessageID("ut_pex"));
+    const Peers& peers = peerStorage->getPeers();
+    {
+      size_t max = 30;
+      for(Peers::const_iterator i = peers.begin();
+	  i != peers.end() && max; ++i) {
+	const PeerHandle& cpeer = *i;
+	if(peer->ipaddr != cpeer->ipaddr &&
+	   !cpeer->getFirstContactTime().elapsed(interval) &&
+	   Util::isNumbersAndDotsNotation(cpeer->ipaddr)) {
+	  m->addFreshPeer(cpeer);
+	  --max;
+	}
+      }
+    }
+    {
+      size_t max = 10;
+      for(Peers::const_reverse_iterator i = peers.rbegin();
+	  i != peers.rend() && max; ++i) {
+	const PeerHandle& cpeer = *i;
+	if(peer->ipaddr != cpeer->ipaddr &&
+	   !cpeer->getBadConditionStartTime().elapsed(interval) &&
+	   Util::isNumbersAndDotsNotation(cpeer->ipaddr)) {
+	  m->addDroppedPeer(cpeer);
+	  --max;
+	}
+      }
+    }
+    BtExtendedMessageHandle msg = messageFactory->createBtExtendedMessage(m);
+    dispatcher->addMessageToQueue(msg);
+    _pexCheckPoint.reset();
+  }
+}
+
 void DefaultBtInteractive::doInteractionProcessing() {
   checkActiveInteraction();
 
@@ -304,5 +378,10 @@ void DefaultBtInteractive::doInteractionProcessing() {
   if(!pieceStorage->downloadFinished()) {
     addRequests();
   }
+
+  if(peer->getExtensionMessageID("ut_pex") && _utPexEnabled) {
+    addPeerExchangeMessage();
+  }
+
   sendPendingMessage();
 }

+ 11 - 1
src/DefaultBtInteractive.h

@@ -102,13 +102,16 @@ private:
   Time floodingCheckPoint;
   FloodingStat floodingStat;
   Time inactiveCheckPoint;
+  Time _pexCheckPoint;
   int32_t keepAliveInterval;
   int32_t maxDownloadSpeedLimit;
+  bool _utPexEnabled;
 
   static const int32_t FLOODING_CHECK_INTERVAL = 5;
 
   void addBitfieldMessageToQueue();
   void addAllowedFastMessageToQueue();
+  void addHandshakeExtendedMessageToQueue();
   void decideChoking();
   void checkHave();
   void sendKeepAlive();
@@ -117,6 +120,7 @@ private:
   void addRequests();
   void detectMessageFlooding();
   void checkActiveInteraction();
+  void addPeerExchangeMessage();
 
 public:
   DefaultBtInteractive():peer(0),
@@ -131,7 +135,8 @@ public:
 			 logger(LogFactory::getInstance()),
 			 allowedFastSetSize(10),
 			 keepAliveInterval(120),
-			 maxDownloadSpeedLimit(0)
+			 maxDownloadSpeedLimit(0),
+			 _utPexEnabled(false)
   {}
 
   virtual ~DefaultBtInteractive() {}
@@ -202,6 +207,11 @@ public:
   void setBtMessageFactory(const BtMessageFactoryWeakHandle& factory) {
     this->messageFactory = factory;
   }
+
+  void setUTPexEnabled(bool f)
+  {
+    _utPexEnabled = f;
+  }
 };
 
 typedef SharedHandle<DefaultBtInteractive> DefaultBtInteractiveHandle;

+ 18 - 0
src/DefaultBtMessageFactory.cc

@@ -61,6 +61,8 @@
 #include "BtAllowedFastMessageValidator.h"
 #include "BtHandshakeMessage.h"
 #include "BtHandshakeMessageValidator.h"
+#include "BtExtendedMessage.h"
+#include "ExtensionMessage.h"
 
 BtMessageHandle
 DefaultBtMessageFactory::createBtMessage(const unsigned char* data, int32_t dataLength)
@@ -161,6 +163,14 @@ DefaultBtMessageFactory::createBtMessage(const unsigned char* data, int32_t data
       msg = temp;
       break;
     }
+    case BtExtendedMessage::ID: {
+      if(peer->isExtendedMessagingEnabled()) {
+	msg = BtExtendedMessage::create(btContext, peer, (const char*)data, dataLength);
+      } else {
+	throw new DlAbortEx("Received extended message from peer during a session with extended messaging disabled.");
+      }
+      break;
+    }
     default:
       throw new DlAbortEx("Invalid message ID. id=%d", id);
     }
@@ -349,3 +359,11 @@ DefaultBtMessageFactory::createAllowedFastMessage(int32_t index)
   setCommonProperty(msg);
   return msg;
 }
+
+BtMessageHandle
+DefaultBtMessageFactory::createBtExtendedMessage(const ExensionMessageHandle& msg)
+{
+  BtExtendedMessageHandle m = new BtExtendedMessage(msg);
+  setCommonProperty(m);
+  return m;
+}

+ 2 - 0
src/DefaultBtMessageFactory.h

@@ -110,6 +110,8 @@ public:
 
   virtual BtMessageHandle createAllowedFastMessage(int32_t index);
 
+  virtual BtMessageHandle createBtExtendedMessage(const ExensionMessageHandle& msg);
+
   void setPeer(const PeerHandle& peer) {
     this->peer = peer;
   }

+ 1 - 8
src/DefaultBtMessageReceiver.cc

@@ -53,14 +53,7 @@ BtMessageHandle DefaultBtMessageReceiver::receiveHandshake(bool quickReply) {
   }
   BtHandshakeMessageHandle msg = messageFactory->createHandshakeMessage(data, dataLength);
   Errors errors;
-  if(msg->validate(errors)) {
-    if(msg->isFastExtensionSupported()) {
-      peer->setFastExtensionEnabled(true);
-      logger->info(MSG_FAST_EXTENSION_ENABLED, cuid);
-    }
-  } else {
-    // TODO throw exception here based on errors
-  }
+  msg->validate(errors);
   return msg;
 }
 

+ 93 - 0
src/DefaultExtensionMessageFactory.cc

@@ -0,0 +1,93 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2006 Tatsuhiro Tsujikawa
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL.  If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so.  If you
+ * do not wish to do so, delete this exception statement from your
+ * version.  If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#include "DefaultExtensionMessageFactory.h"
+#include "BtContext.h"
+#include "Peer.h"
+#include "DlAbortEx.h"
+#include "HandshakeExtensionMessage.h"
+#include "UTPexExtensionMessage.h"
+#include "LogFactory.h"
+#include "BtRegistry.h"
+
+DefaultExtensionMessageFactory::DefaultExtensionMessageFactory():
+  _btContext(0),
+  _peer(0),
+  _logger(LogFactory::getInstance()) {}
+
+DefaultExtensionMessageFactory::DefaultExtensionMessageFactory(const BtContextHandle& btContext,
+							       const PeerHandle& peer):
+  _btContext(btContext),
+  _peer(peer),
+  _logger(LogFactory::getInstance()) {}
+
+DefaultExtensionMessageFactory::~DefaultExtensionMessageFactory() {}
+
+ExtensionMessageHandle
+DefaultExtensionMessageFactory::createMessage(const char* data, size_t length)
+{
+  uint8_t extensionMessageID = *data;
+  if(extensionMessageID == 0) {
+    // handshake
+    HandshakeExtensionMessageHandle m = HandshakeExtensionMessage::create(data, length);
+    m->setBtContext(_btContext);
+    m->setPeer(_peer);
+    return m;
+  } else {
+    string extensionName = BT_RUNTIME(_btContext)->getExtensionName(extensionMessageID);
+    if(extensionName.empty()) {
+      throw new DlAbortEx("No extension registered for extended message ID %u",
+			  extensionMessageID);
+    }
+    if(extensionName == "ut_pex") {
+      // uTorrent compatible Peer-Exchange
+      UTPexExtensionMessageHandle m =
+	UTPexExtensionMessage::create(_btContext, data, length);
+      m->setBtContext(_btContext);
+      return m;
+    } else {
+      throw new DlAbortEx("Unsupported extension message received. extensionMessageID=%u, extensionName=%s", extensionMessageID, extensionName.c_str());
+    }
+  }
+}
+
+void DefaultExtensionMessageFactory::setBtContext(const BtContextHandle& btContext)
+{
+  _btContext = btContext;
+}
+
+void DefaultExtensionMessageFactory::setPeer(const PeerHandle& peer)
+{
+  _peer = peer;
+}

+ 70 - 0
src/DefaultExtensionMessageFactory.h

@@ -0,0 +1,70 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2006 Tatsuhiro Tsujikawa
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL.  If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so.  If you
+ * do not wish to do so, delete this exception statement from your
+ * version.  If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#ifndef _D_DEFAULT_EXTENSION_MESSAGE_FACTORY_H_
+#define _D_DEFAULT_EXTENSION_MESSAGE_FACTORY_H_
+
+#include "ExtensionMessageFactory.h"
+
+class BtContext;
+typedef SharedHandle<BtContext> BtContextHandle;
+class Peer;
+typedef SharedHandle<Peer> PeerHandle;
+class Logger;
+
+class DefaultExtensionMessageFactory:public ExtensionMessageFactory {
+private:
+  BtContextHandle _btContext;
+
+  PeerHandle _peer;
+
+  const Logger* _logger;
+
+public:
+  DefaultExtensionMessageFactory();
+
+  DefaultExtensionMessageFactory(const BtContextHandle& btContext,
+				 const PeerHandle& peer);
+
+  virtual ~DefaultExtensionMessageFactory();
+
+  virtual ExtensionMessageHandle createMessage(const char* data, size_t length);
+
+  void setBtContext(const BtContextHandle& btContext);
+
+  void setPeer(const PeerHandle& peer);
+};
+
+typedef SharedHandle<DefaultExtensionMessageFactory> DefaultExtensionMessageFactoryHandle;
+#endif // _D_DEFAULT_EXTENSION_MESSAGE_FACTORY_H_

+ 17 - 12
src/DefaultPeerStorage.cc

@@ -52,29 +52,34 @@ DefaultPeerStorage::DefaultPeerStorage(BtContextHandle btContext,
 
 DefaultPeerStorage::~DefaultPeerStorage() {}
 
+bool DefaultPeerStorage::isPeerAlreadyAdded(const PeerHandle& peer)
+{
+  return find(peers.begin(), peers.end(), peer) != peers.end() ||
+    find(incomingPeers.begin(), incomingPeers.end(), peer) != incomingPeers.end();
+}
+
 bool DefaultPeerStorage::addPeer(const PeerHandle& peer) {
-  Peers::iterator itr = find(peers.begin(), peers.end(), peer);
-  if(itr == peers.end()) {
+  if(isPeerAlreadyAdded(peer)) {
+    logger->debug("Adding %s:%u is rejected because it is already in PeerStorage.", peer->ipaddr.c_str(), peer->port);
+    return false;
+  } else {
     if(peers.size() >= (size_t)maxPeerListSize) {
       deleteUnusedPeer(peers.size()-maxPeerListSize+1);
     }
     peers.push_front(peer);
     return true;
-  } else {
-    const PeerHandle& peer = *itr;
-    if(!peer->isGood() || peer->cuid != 0) {
-      return false;
-    } else {
-      *itr = peer;
-      return true;
-    }      
   }
 }
 
 bool DefaultPeerStorage::addIncomingPeer(const PeerHandle& peer)
 {
-  incomingPeers.push_back(peer);
-  return true;
+  if(isPeerAlreadyAdded(peer)) {
+    logger->debug("Adding %s:%u is rejected because it is already in PeerStorage.", peer->ipaddr.c_str(), peer->port);
+    return false;
+  } else {
+    incomingPeers.push_back(peer);
+    return true;
+  }
 }
 
 void DefaultPeerStorage::addPeer(const Peers& peers) {

+ 2 - 0
src/DefaultPeerStorage.h

@@ -55,6 +55,8 @@ private:
   BtRuntimeHandle btRuntime;
   int64_t removedPeerSessionDownloadLength;
   int64_t removedPeerSessionUploadLength;
+
+  bool isPeerAlreadyAdded(const PeerHandle& peer);
 public:
   DefaultPeerStorage(BtContextHandle btContext, const Option* option);
   virtual ~DefaultPeerStorage();

+ 57 - 0
src/ExtensionMessage.h

@@ -0,0 +1,57 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2006 Tatsuhiro Tsujikawa
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL.  If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so.  If you
+ * do not wish to do so, delete this exception statement from your
+ * version.  If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#ifndef _D_EXTENSION_MESSAGE_H_
+#define _D_EXTENSION_MESSAGE_H_
+
+#include "common.h"
+
+class ExtensionMessage {
+public:
+  virtual ~ExtensionMessage() {}
+
+  virtual string getBencodedData() = 0;
+
+  virtual uint8_t getExtensionMessageID() = 0;
+  
+  virtual const string& getExtensionName() const = 0;
+
+  virtual string toString() const = 0;
+
+  virtual void doReceivedAction() = 0;
+};
+
+typedef SharedHandle<ExtensionMessage> ExtensionMessageHandle;
+
+#endif // _D_EXTENSION_MESSAGE_H_

+ 51 - 0
src/ExtensionMessageFactory.h

@@ -0,0 +1,51 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2006 Tatsuhiro Tsujikawa
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL.  If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so.  If you
+ * do not wish to do so, delete this exception statement from your
+ * version.  If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#ifndef _D_EXTENSION_MESSAGE_FACTORY_H_
+#define _D_EXTENSION_MESSAGE_FACTORY_H_
+
+#include "common.h"
+
+class ExtensionMessage;
+typedef SharedHandle<ExtensionMessage> ExtensionMessageHandle;
+
+class ExtensionMessageFactory {
+public:
+  virtual ~ExtensionMessageFactory() {}
+
+  virtual ExtensionMessageHandle createMessage(const char* data, size_t length) = 0;
+};
+
+typedef SharedHandle<ExtensionMessageFactory> ExtensionMessageFactoryHandle;
+#endif // _D_EXTENSION_MESSAGE_FACTORY_H_

+ 174 - 0
src/HandshakeExtensionMessage.cc

@@ -0,0 +1,174 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2006 Tatsuhiro Tsujikawa
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL.  If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so.  If you
+ * do not wish to do so, delete this exception statement from your
+ * version.  If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#include "HandshakeExtensionMessage.h"
+#include "Peer.h"
+#include "BtContext.h"
+#include "Dictionary.h"
+#include "Data.h"
+#include "Util.h"
+#include "BencodeVisitor.h"
+#include "BtRegistry.h"
+#include "MetaFileUtil.h"
+#include "DlAbortEx.h"
+#include "LogFactory.h"
+#include "message.h"
+
+const string HandshakeExtensionMessage::EXTENSION_NAME = "handshake";
+
+HandshakeExtensionMessage::HandshakeExtensionMessage():_tcpPort(0),
+						       _btContext(0),
+						       _peer(0),
+						       _logger(LogFactory::getInstance())
+{}
+
+HandshakeExtensionMessage::~HandshakeExtensionMessage() {}
+
+string HandshakeExtensionMessage::getBencodedData()
+{
+  SharedHandle<Dictionary> dic = new Dictionary();
+  if(!_clientVersion.empty()) {
+    Data* v = new Data(_clientVersion);
+    dic->put("v", v);
+  }
+  if(_tcpPort > 0) {
+    string portStr = Util::itos(_tcpPort);
+    Data* p = new Data(portStr, true);
+    dic->put("p", p);
+  }
+  Dictionary* exts = new Dictionary();
+  dic->put("m", exts);
+  for(map<string, uint8_t>::const_iterator itr = _extensions.begin();
+      itr != _extensions.end(); ++itr) {
+    const map<string, uint8_t>::value_type& vt = *itr;
+    string idStr = Util::uitos((uint32_t)vt.second);
+    exts->put(vt.first, new Data(idStr, true));
+  }
+  BencodeVisitor v;
+  dic->accept(&v);
+  return v.getBencodedData();
+}
+
+string HandshakeExtensionMessage::toString() const
+{
+  string s = getExtensionName();
+  if(!_clientVersion.empty()) {
+    s += " client="+Util::urlencode(_clientVersion);
+  }
+  if(_tcpPort > 0) {
+    s += ", tcpPort="+Util::itos(_tcpPort);
+  }
+  for(map<string, uint8_t>::const_iterator itr = _extensions.begin();
+      itr != _extensions.end(); ++itr) {
+    const map<string, uint8_t>::value_type& vt = *itr;
+    s += ", "+vt.first+"="+Util::uitos((uint32_t)vt.second);
+  }
+  return s;
+}
+
+void HandshakeExtensionMessage::doReceivedAction()
+{
+  if(_tcpPort > 0) {
+    _peer->port = _tcpPort;
+  }
+  for(map<string, uint8_t>::const_iterator itr = _extensions.begin();
+      itr != _extensions.end(); ++itr) {
+    const map<string, uint8_t>::value_type& vt = *itr;
+    _peer->setExtension(vt.first, vt.second);
+  }
+  if(_peer->port > 0) {
+    // This is needed when _peer is a connection initiator, listen port of
+    // _peer is now available, which is initially unknown.
+    // If _peer is a receiver or already its port is known, _peer has to be
+    // already added to PeerStorage using addPeer() and call
+    // PeerStorage::addPeer() here does nothing and just returns false.
+    PEER_STORAGE(_btContext)->addPeer(_peer);
+  }
+}
+
+void HandshakeExtensionMessage::setPeer(const PeerHandle& peer)
+{
+  _peer = peer;
+}
+
+void HandshakeExtensionMessage::setBtContext(const BtContextHandle& btContext)
+{
+  _btContext = btContext;
+}
+
+uint8_t HandshakeExtensionMessage::getExtensionMessageID(const string& name) const
+{
+  map<string, uint8_t>::const_iterator i = _extensions.find(name);
+  if(i == _extensions.end()) {
+    return 0;
+  } else {
+    return (*i).second;
+  }
+}
+
+HandshakeExtensionMessageHandle
+HandshakeExtensionMessage::create(const char* data, size_t length)
+{
+  if(length < 1) {
+    throw new DlAbortEx(MSG_TOO_SMALL_PAYLOAD_SIZE,
+			EXTENSION_NAME.c_str(), length);
+  }
+  HandshakeExtensionMessageHandle msg = new HandshakeExtensionMessage();
+  msg->_logger->debug("Creating HandshakeExtensionMessage from %s",
+		      Util::urlencode((const unsigned char*)data, length).c_str());
+  SharedHandle<MetaEntry> root = MetaFileUtil::bdecoding(data+1, length-1);
+  Dictionary* d = dynamic_cast<Dictionary*>(root.get());
+  if(d == 0) {
+    throw new DlAbortEx("Unexpected payload format for extended message handshake");
+  }
+  const Data* p = dynamic_cast<const Data*>(d->get("p"));
+  if(p) {
+    msg->_tcpPort = p->toInt();
+  }
+  const Data* v = dynamic_cast<const Data*>(d->get("v"));
+  if(v) {
+    msg->_clientVersion = v->toString();
+  }
+  const Dictionary* m = dynamic_cast<const Dictionary*>(d->get("m"));
+  if(m) {
+    const Order& order = m->getOrder();
+    for(Order::const_iterator i = order.begin(); i != order.end(); ++i) {
+      const Data* e = dynamic_cast<const Data*>(m->get(*i));
+      if(e) {
+	msg->_extensions[*i] = e->toInt();
+      }
+    }
+  }
+  return msg;
+}

+ 128 - 0
src/HandshakeExtensionMessage.h

@@ -0,0 +1,128 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2006 Tatsuhiro Tsujikawa
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL.  If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so.  If you
+ * do not wish to do so, delete this exception statement from your
+ * version.  If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#ifndef _D_HANDSHAKE_EXTENSION_MESSAGE_H_
+#define _D_HANDSHAKE_EXTENSION_MESSAGE_H_
+
+#include "ExtensionMessage.h"
+#include "BtConstants.h"
+
+class BtContext;
+typedef SharedHandle<BtContext> BtContextHandle;
+class Peer;
+typedef SharedHandle<Peer> PeerHandle;
+class HandshakeExtensionMessage;
+typedef SharedHandle<HandshakeExtensionMessage> HandshakeExtensionMessageHandle;
+class Logger;
+
+class HandshakeExtensionMessage:public ExtensionMessage {
+private:
+  string _clientVersion;
+
+  uint16_t _tcpPort;
+
+  map<string, uint8_t> _extensions;
+
+  BtContextHandle _btContext;
+
+  PeerHandle _peer;
+
+  const Logger* _logger;
+
+public:
+  HandshakeExtensionMessage();
+
+  virtual ~HandshakeExtensionMessage();
+
+  virtual string getBencodedData();
+
+  virtual uint8_t getExtensionMessageID()
+  {
+    return 0;
+  }
+  
+  virtual const string& getExtensionName() const
+  {
+    return EXTENSION_NAME;
+  }
+
+  static const string EXTENSION_NAME;
+
+  virtual string toString() const;
+
+  virtual void doReceivedAction();
+
+  void setClientVersion(const string& version)
+  {
+    _clientVersion = version;
+  }
+
+  const string& getClientVersion() const
+  {
+    return _clientVersion;
+  }
+
+  void setTCPPort(uint16_t port)
+  {
+    _tcpPort = port;
+  }
+
+  uint16_t getTCPPort() const
+  {
+    return _tcpPort;
+  }
+
+  void setExtension(const string& name, uint8_t id)
+  {
+    _extensions[name] = id;
+  }
+
+  void setExtensions(const Extensions& extensions)
+  {
+    _extensions = extensions;
+  }
+
+  uint8_t getExtensionMessageID(const string& name) const;
+
+  void setPeer(const PeerHandle& peer);
+
+  void setBtContext(const BtContextHandle& btContext);
+
+  static HandshakeExtensionMessageHandle create(const char* data,
+						size_t dataLength);
+
+};
+
+typedef SharedHandle<HandshakeExtensionMessage> HandshakeExtensionMessageHandle;
+#endif // _D_HANDSHAKE_EXTENSION_MESSAGE_H_

+ 6 - 1
src/Makefile.am

@@ -167,6 +167,7 @@ SRCS += MetaEntry.h\
 	MetaFileUtil.cc MetaFileUtil.h\
 	MetaEntryVisitor.h\
 	ShaVisitor.cc ShaVisitor.h\
+	BencodeVisitor.cc\
 	PeerMessageUtil.cc PeerMessageUtil.h\
 	PeerAbstractCommand.cc PeerAbstractCommand.h\
 	PeerInitiateConnectionCommand.cc PeerInitiateConnectionCommand.h\
@@ -248,7 +249,11 @@ SRCS += MetaEntry.h\
 	BtSetup.cc BtSetup.h\
 	BtFileAllocationEntry.cc BtFileAllocationEntry.h\
 	BtPostDownloadHandler.cc BtPostDownloadHandler.h\
-	BtCheckIntegrityEntry.cc BtCheckIntegrityEntry.h
+	BtCheckIntegrityEntry.cc BtCheckIntegrityEntry.h\
+	BtExtendedMessage.cc\
+	DefaultExtensionMessageFactory.cc\
+	HandshakeExtensionMessage.cc\
+	UTPexExtensionMessage.cc
 endif # ENABLE_BITTORRENT
 
 if ENABLE_METALINK

+ 24 - 6
src/Makefile.in

@@ -54,6 +54,7 @@ bin_PROGRAMS = aria2c$(EXEEXT)
 @ENABLE_BITTORRENT_TRUE@	MetaFileUtil.cc MetaFileUtil.h\
 @ENABLE_BITTORRENT_TRUE@	MetaEntryVisitor.h\
 @ENABLE_BITTORRENT_TRUE@	ShaVisitor.cc ShaVisitor.h\
+@ENABLE_BITTORRENT_TRUE@	BencodeVisitor.cc\
 @ENABLE_BITTORRENT_TRUE@	PeerMessageUtil.cc PeerMessageUtil.h\
 @ENABLE_BITTORRENT_TRUE@	PeerAbstractCommand.cc PeerAbstractCommand.h\
 @ENABLE_BITTORRENT_TRUE@	PeerInitiateConnectionCommand.cc PeerInitiateConnectionCommand.h\
@@ -135,7 +136,11 @@ bin_PROGRAMS = aria2c$(EXEEXT)
 @ENABLE_BITTORRENT_TRUE@	BtSetup.cc BtSetup.h\
 @ENABLE_BITTORRENT_TRUE@	BtFileAllocationEntry.cc BtFileAllocationEntry.h\
 @ENABLE_BITTORRENT_TRUE@	BtPostDownloadHandler.cc BtPostDownloadHandler.h\
-@ENABLE_BITTORRENT_TRUE@	BtCheckIntegrityEntry.cc BtCheckIntegrityEntry.h
+@ENABLE_BITTORRENT_TRUE@	BtCheckIntegrityEntry.cc BtCheckIntegrityEntry.h\
+@ENABLE_BITTORRENT_TRUE@	BtExtendedMessage.cc\
+@ENABLE_BITTORRENT_TRUE@	DefaultExtensionMessageFactory.cc\
+@ENABLE_BITTORRENT_TRUE@	HandshakeExtensionMessage.cc\
+@ENABLE_BITTORRENT_TRUE@	UTPexExtensionMessage.cc
 
 @ENABLE_METALINK_TRUE@am__append_3 = Metalinker.cc Metalinker.h\
 @ENABLE_METALINK_TRUE@	MetalinkEntry.cc MetalinkEntry.h\
@@ -303,8 +308,9 @@ am__libaria2c_a_SOURCES_DIST = Socket.h SocketCore.cc SocketCore.h \
 	MessageDigestHelper.h MetaEntry.h Data.cc Data.h Dictionary.cc \
 	Dictionary.h List.cc List.h MetaFileUtil.cc MetaFileUtil.h \
 	MetaEntryVisitor.h ShaVisitor.cc ShaVisitor.h \
-	PeerMessageUtil.cc PeerMessageUtil.h PeerAbstractCommand.cc \
-	PeerAbstractCommand.h PeerInitiateConnectionCommand.cc \
+	BencodeVisitor.cc PeerMessageUtil.cc PeerMessageUtil.h \
+	PeerAbstractCommand.cc PeerAbstractCommand.h \
+	PeerInitiateConnectionCommand.cc \
 	PeerInitiateConnectionCommand.h PeerInteractionCommand.cc \
 	PeerInteractionCommand.h PeerListenCommand.cc \
 	PeerListenCommand.h RequestSlot.cc RequestSlot.h Directory.cc \
@@ -355,8 +361,10 @@ am__libaria2c_a_SOURCES_DIST = Socket.h SocketCore.cc SocketCore.h \
 	PeerReceiveHandshakeCommand.h BtSetup.cc BtSetup.h \
 	BtFileAllocationEntry.cc BtFileAllocationEntry.h \
 	BtPostDownloadHandler.cc BtPostDownloadHandler.h \
-	BtCheckIntegrityEntry.cc BtCheckIntegrityEntry.h Metalinker.cc \
-	Metalinker.h MetalinkEntry.cc MetalinkEntry.h \
+	BtCheckIntegrityEntry.cc BtCheckIntegrityEntry.h \
+	BtExtendedMessage.cc DefaultExtensionMessageFactory.cc \
+	HandshakeExtensionMessage.cc UTPexExtensionMessage.cc \
+	Metalinker.cc Metalinker.h MetalinkEntry.cc MetalinkEntry.h \
 	MetalinkResource.cc MetalinkResource.h MetalinkProcessor.h \
 	MetalinkProcessorFactory.cc MetalinkParserController.cc \
 	MetalinkParserStateMachine.cc InitialMetalinkParserState.cc \
@@ -385,6 +393,7 @@ am__libaria2c_a_SOURCES_DIST = Socket.h SocketCore.cc SocketCore.h \
 @ENABLE_BITTORRENT_TRUE@	Dictionary.$(OBJEXT) List.$(OBJEXT) \
 @ENABLE_BITTORRENT_TRUE@	MetaFileUtil.$(OBJEXT) \
 @ENABLE_BITTORRENT_TRUE@	ShaVisitor.$(OBJEXT) \
+@ENABLE_BITTORRENT_TRUE@	BencodeVisitor.$(OBJEXT) \
 @ENABLE_BITTORRENT_TRUE@	PeerMessageUtil.$(OBJEXT) \
 @ENABLE_BITTORRENT_TRUE@	PeerAbstractCommand.$(OBJEXT) \
 @ENABLE_BITTORRENT_TRUE@	PeerInitiateConnectionCommand.$(OBJEXT) \
@@ -433,7 +442,11 @@ am__libaria2c_a_SOURCES_DIST = Socket.h SocketCore.cc SocketCore.h \
 @ENABLE_BITTORRENT_TRUE@	BtSetup.$(OBJEXT) \
 @ENABLE_BITTORRENT_TRUE@	BtFileAllocationEntry.$(OBJEXT) \
 @ENABLE_BITTORRENT_TRUE@	BtPostDownloadHandler.$(OBJEXT) \
-@ENABLE_BITTORRENT_TRUE@	BtCheckIntegrityEntry.$(OBJEXT)
+@ENABLE_BITTORRENT_TRUE@	BtCheckIntegrityEntry.$(OBJEXT) \
+@ENABLE_BITTORRENT_TRUE@	BtExtendedMessage.$(OBJEXT) \
+@ENABLE_BITTORRENT_TRUE@	DefaultExtensionMessageFactory.$(OBJEXT) \
+@ENABLE_BITTORRENT_TRUE@	HandshakeExtensionMessage.$(OBJEXT) \
+@ENABLE_BITTORRENT_TRUE@	UTPexExtensionMessage.$(OBJEXT)
 @ENABLE_METALINK_TRUE@am__objects_3 = Metalinker.$(OBJEXT) \
 @ENABLE_METALINK_TRUE@	MetalinkEntry.$(OBJEXT) \
 @ENABLE_METALINK_TRUE@	MetalinkResource.$(OBJEXT) \
@@ -935,6 +948,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/AuthConfigFactory.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/AutoSaveCommand.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Base64.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/BencodeVisitor.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/BitfieldMan.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/BitfieldManFactory.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/BtAllowedFastMessage.Po@am__quote@
@@ -944,6 +958,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/BtChokeMessage.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/BtContextAwareCommand.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/BtDependency.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/BtExtendedMessage.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/BtFileAllocationEntry.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/BtHandshakeMessage.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/BtHaveAllMessage.Po@am__quote@
@@ -987,6 +1002,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DefaultBtProgressInfoFile.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DefaultBtRequestFactory.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DefaultDiskWriter.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DefaultExtensionMessageFactory.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DefaultPeerListProcessor.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DefaultPeerStorage.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DefaultPieceStorage.Po@am__quote@
@@ -1022,6 +1038,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/FtpTunnelRequestCommand.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/FtpTunnelResponseCommand.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/GrowSegment.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/HandshakeExtensionMessage.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/HashMetalinkParserState.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/HaveEraseCommand.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/HttpConnection.Po@am__quote@
@@ -1111,6 +1128,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/TrackerWatcherCommand.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/TransferStat.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/URLMetalinkParserState.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/UTPexExtensionMessage.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/UnknownLengthPieceStorage.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/UriListParser.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Util.Po@am__quote@

+ 1 - 0
src/MetaEntry.h

@@ -49,4 +49,5 @@ public:
 
 };
 
+typedef SharedHandle<MetaEntry> MetaEntryHandle;
 #endif // _D_META_ENTRY_H_

+ 9 - 9
src/MetaFileUtil.cc

@@ -83,7 +83,7 @@ MetaEntry* MetaFileUtil::bdecoding(const char* buf, int32_t len) {
 
 MetaEntry* MetaFileUtil::bdecodingR(const char** pp, const char* end) {
   if(*pp >= end) {
-    throw new DlAbortEx("mulformed metainfo");
+    throw new DlAbortEx("Malformed metainfo");
   }
   MetaEntry* e;
   switch(**pp) {
@@ -107,7 +107,7 @@ MetaEntry* MetaFileUtil::bdecodingR(const char** pp, const char* end) {
 
 Dictionary* MetaFileUtil::parseDictionaryTree(const char** pp, const char* end) {
   if(*pp >= end) {
-    throw new DlAbortEx("mulformed metainfo");
+    throw new DlAbortEx("Malformed metainfo");
   }
   Dictionary* dic = new Dictionary();
   try {
@@ -129,7 +129,7 @@ Dictionary* MetaFileUtil::parseDictionaryTree(const char** pp, const char* end)
 
 List* MetaFileUtil::parseListTree(const char** pp, const char* end) {
   if(*pp >= end) {
-    throw new DlAbortEx("mulformed metainfo");
+    throw new DlAbortEx("Malformed metainfo");
   }
   List* lis = new List();
   try {
@@ -150,12 +150,12 @@ List* MetaFileUtil::parseListTree(const char** pp, const char* end) {
 
 Data* MetaFileUtil::decodeInt(const char** pp, const char* end) {
   if(*pp >= end) {
-    throw new DlAbortEx(EX_MULFORMED_META_INFO);
+    throw new DlAbortEx(EX_MALFORMED_META_INFO);
   }
   char* endTerm = (char*)memchr(*pp, 'e', end-*pp);
   // TODO if endTerm is null
   if(endTerm == NULL) {
-    throw new DlAbortEx(EX_MULFORMED_META_INFO);
+    throw new DlAbortEx(EX_MALFORMED_META_INFO);
   }
   int32_t numSize = endTerm-*pp;
 
@@ -166,12 +166,12 @@ Data* MetaFileUtil::decodeInt(const char** pp, const char* end) {
 
 Data* MetaFileUtil::decodeWord(const char** pp, const char* end) {
   if(*pp >= end) {
-    throw new DlAbortEx("mulformed metainfo");
+    throw new DlAbortEx("Malformed metainfo");
   }
   char* delim = (char*)memchr(*pp, ':', end-*pp);
   // TODO if delim is null
   if(delim == *pp || delim == NULL) {
-    throw new DlAbortEx(EX_MULFORMED_META_INFO);
+    throw new DlAbortEx(EX_MALFORMED_META_INFO);
   }
   int32_t numSize = delim-*pp;
   char* temp = new char[numSize+1];
@@ -181,12 +181,12 @@ Data* MetaFileUtil::decodeWord(const char** pp, const char* end) {
   int32_t size = strtol(temp, &endptr, 10);
   if(*endptr != '\0') {
     delete [] temp;
-    throw new DlAbortEx(EX_MULFORMED_META_INFO);
+    throw new DlAbortEx(EX_MALFORMED_META_INFO);
   }    
   delete [] temp;
 
   if(delim+1+size > end) {
-    throw new DlAbortEx(EX_MULFORMED_META_INFO);
+    throw new DlAbortEx(EX_MALFORMED_META_INFO);
   }
 
   Data* data = new Data(delim+1, size);

+ 30 - 1
src/Peer.cc

@@ -39,7 +39,7 @@
 # include "MessageDigestHelper.h"
 #endif // ENABLE_MESSAGE_DIGEST
 
-Peer::Peer(string ipaddr, int32_t port, int32_t pieceLength, int64_t totalLength):
+Peer::Peer(string ipaddr, uint16_t port, int32_t pieceLength, int64_t totalLength):
   ipaddr(ipaddr),
   port(port),
   sessionUploadLength(0),
@@ -113,6 +113,8 @@ void Peer::resetStatus() {
   optUnchoking = false;
   snubbing = false;
   fastExtensionEnabled = false;
+  _extendedMessagingEnabled = false;
+  _extensions.clear();
   latency = DEFAULT_LATENCY;
   peerAllowedIndexSet.clear();
   amAllowedIndexSet.clear();
@@ -158,3 +160,30 @@ bool Peer::isGood() const
 {
   return _badConditionStartTime.elapsed(_badConditionInterval);
 }
+
+uint8_t Peer::getExtensionMessageID(const string& name)
+{
+  Extensions::const_iterator itr = _extensions.find(name);
+  if(itr == _extensions.end()) {
+    return 0;
+  } else {
+    return (*itr).second;
+  }
+}
+
+string Peer::getExtensionName(uint8_t id)
+{
+  for(Extensions::const_iterator itr = _extensions.begin();
+      itr != _extensions.end(); ++itr) {
+    const Extensions::value_type& p = *itr;
+    if(p.second == id) {
+      return p.first;
+    }
+  }
+  return "";
+}
+
+void Peer::setExtension(const string& name, uint8_t id)
+{
+  _extensions[name] = id;
+}

+ 32 - 2
src/Peer.h

@@ -39,6 +39,7 @@
 #include "BitfieldMan.h"
 #include "PeerStat.h"
 #include "TimeA2.h"
+#include "BtConstants.h"
 #include <string.h>
 
 #define PEER_ID_LENGTH 20
@@ -49,7 +50,7 @@ class Peer {
   friend bool operator!=(const Peer& p1, const Peer& p2);
 public:
   string ipaddr;
-  int32_t port;
+  uint16_t port;
   bool amChoking;
   bool amInterested;
   bool peerChoking;
@@ -67,6 +68,8 @@ private:
   Integers peerAllowedIndexSet;
   // fast index set which localhost has sent to a peer.
   Integers amAllowedIndexSet;
+  bool _extendedMessagingEnabled;
+  Extensions _extensions;
   PeerStat peerStat;
   int64_t sessionUploadLength;
   int64_t sessionDownloadLength;
@@ -74,10 +77,11 @@ private:
   int32_t latency;
   bool active;
   string id;
+  Time _firstContactTime;
   Time _badConditionStartTime;
   int32_t _badConditionInterval;
 public:
-  Peer(string ipaddr, int32_t port, int32_t pieceLength, int64_t totalLength);
+  Peer(string ipaddr, uint16_t port, int32_t pieceLength, int64_t totalLength);
 
   ~Peer() {
     delete bitfield;
@@ -193,6 +197,16 @@ public:
   void addAmAllowedIndex(int32_t index);
   bool isInAmAllowedIndexSet(int32_t index) const;
 
+  void setExtendedMessagingEnabled(bool enabled)
+  {
+    _extendedMessagingEnabled = enabled;
+  }
+
+  bool isExtendedMessagingEnabled() const
+  {
+    return _extendedMessagingEnabled;
+  }
+
   bool shouldBeChoking() const;
 
   bool hasPiece(int32_t index) const;
@@ -211,6 +225,22 @@ public:
   bool isGood() const;
 
   void reconfigure(int32_t pieceLength, int64_t totalLength);
+
+  Time getFirstContactTime() const
+  {
+    return _firstContactTime;
+  }
+
+  Time getBadConditionStartTime() const
+  {
+    return _badConditionStartTime;
+  }
+
+  uint8_t getExtensionMessageID(const string& name);
+
+  string getExtensionName(uint8_t id);
+
+  void setExtension(const string& name, uint8_t id);
 };
 
 typedef SharedHandle<Peer> PeerHandle;

+ 3 - 0
src/PeerInteractionCommand.cc

@@ -112,6 +112,9 @@ PeerInteractionCommand::PeerInteractionCommand(int32_t cuid,
   btInteractive->setKeepAliveInterval(e->option->getAsInt(PREF_BT_KEEP_ALIVE_INTERVAL));
   btInteractive->setMaxDownloadSpeedLimit(e->option->getAsInt(PREF_MAX_DOWNLOAD_LIMIT));
   btInteractive->setBtMessageFactory(factory);
+  if(!btContext->isPrivate() && e->option->getAsBool(PREF_ENABLE_PEER_EXCHANGE)) {
+    btInteractive->setUTPexEnabled(true);
+  }
   this->btInteractive = btInteractive;
 
   // reverse depends

+ 14 - 0
src/PeerMessageUtil.cc

@@ -125,3 +125,17 @@ void PeerMessageUtil::createPeerMessageString(unsigned char* msg,
   setIntParam(msg, payloadLength);
   msg[4] = messageId;
 }
+
+bool PeerMessageUtil::createcompact(char* compact, const string& addr, uint16_t port)
+{
+  struct in_addr in;
+  if(inet_aton(addr.c_str(), &in) == 0) {
+    return false;
+  }
+  uint32_t* addrp = (uint32_t*)compact;
+  *addrp = in.s_addr;
+  uint16_t* portp = (uint16_t*)(compact+4);
+  *portp = htons(port);
+  return true;
+}
+

+ 10 - 0
src/PeerMessageUtil.h

@@ -65,6 +65,16 @@ public:
 				      int32_t msgLength,
 				      int32_t payloadLength,
 				      int8_t messageId);
+
+  /**
+   * Creates compact tracker format(6bytes for ipv4 address and port)
+   * and stores the results in compact.
+   * compact must be at least 6 bytes and pre-allocated.
+   * Returns true if creation is successful, otherwise returns false.
+   * The example of failure reason is that addr is not numbers-and-dots
+   * notation.
+   */
+  static bool createcompact(char* compact, const string& addr, uint16_t port);
 };
 
 #endif // _D_PEER_MESSAGE_UTIL_H_

+ 5 - 2
src/PeerObject.h

@@ -41,6 +41,7 @@
 #include "BtMessageDispatcher.h"
 #include "PeerConnection.h"
 #include "BtMessageReceiver.h"
+#include "ExtensionMessageFactory.h"
 
 class PeerObject {
 public:
@@ -48,13 +49,15 @@ public:
 	       btRequestFactory(0),
 	       btMessageDispatcher(0),
 	       btMessageReceiver(0),
-	       peerConnection(0) {}
-
+	       peerConnection(0),
+	       extensionMessageFactory(0) {}
+  
   BtMessageFactoryHandle btMessageFactory;
   BtRequestFactoryHandle btRequestFactory;
   BtMessageDispatcherHandle btMessageDispatcher;
   BtMessageReceiverHandle btMessageReceiver;
   PeerConnectionHandle peerConnection;
+  ExtensionMessageFactoryHandle extensionMessageFactory;
 };
 
 typedef SharedHandle<PeerObject> PeerObjectHandle;

+ 15 - 14
src/PeerReceiveHandshakeCommand.cc

@@ -77,21 +77,22 @@ bool PeerReceiveHandshakeCommand::executeInternal()
     if(!PIECE_STORAGE(btContext)->downloadFinished() && tstat.getDownloadSpeed() < _lowestSpeedLimit ||
        BT_RUNTIME(btContext)->getConnections() < MAX_PEERS) {
       peer->reconfigure(btContext->getPieceLength(), btContext->getTotalLength());
-      PEER_STORAGE(btContext)->addIncomingPeer(peer);
+      if(PEER_STORAGE(btContext)->addIncomingPeer(peer)) {
 
-      peer->cuid = cuid;
-
-      PeerInteractionCommand* command =
-	new PeerInteractionCommand(cuid,
-				   btContext->getOwnerRequestGroup(),
-				   peer,
-				   e,
-				   btContext,
-				   socket,
-				   PeerInteractionCommand::RECEIVER_WAIT_HANDSHAKE,
-				   _peerConnection);
-      e->commands.push_back(command);
-      logger->debug(MSG_INCOMING_PEER_CONNECTION, cuid, peer->cuid);
+	peer->cuid = cuid;
+	
+	PeerInteractionCommand* command =
+	  new PeerInteractionCommand(cuid,
+				     btContext->getOwnerRequestGroup(),
+				     peer,
+				     e,
+				     btContext,
+				     socket,
+				     PeerInteractionCommand::RECEIVER_WAIT_HANDSHAKE,
+				     _peerConnection);
+	e->commands.push_back(command);
+	logger->debug(MSG_INCOMING_PEER_CONNECTION, cuid, peer->cuid);
+      }
     }
     return true;
   } else {

+ 149 - 0
src/UTPexExtensionMessage.cc

@@ -0,0 +1,149 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2006 Tatsuhiro Tsujikawa
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL.  If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so.  If you
+ * do not wish to do so, delete this exception statement from your
+ * version.  If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#include "UTPexExtensionMessage.h"
+#include "Peer.h"
+#include "BtContext.h"
+#include "Dictionary.h"
+#include "Data.h"
+#include "BencodeVisitor.h"
+#include "a2netcompat.h"
+#include "Util.h"
+#include "PeerMessageUtil.h"
+#include "BtRegistry.h"
+#include "CompactPeerListProcessor.h"
+#include "MetaFileUtil.h"
+#include "DlAbortEx.h"
+#include "message.h"
+
+const string UTPexExtensionMessage::EXTENSION_NAME = "ut_pex";
+
+UTPexExtensionMessage::UTPexExtensionMessage(uint8_t extensionMessageID):
+  _extensionMessageID(extensionMessageID),
+  _btContext(0) {}
+
+UTPexExtensionMessage::~UTPexExtensionMessage() {}
+
+string UTPexExtensionMessage::getBencodedData()
+{
+  SharedHandle<Dictionary> d = new Dictionary();
+  pair<string, string> freshPeerPair = createCompactPeerListAndFlag(_freshPeers);
+  pair<string, string> droppedPeerPair = createCompactPeerListAndFlag(_droppedPeers);
+  d->put("added", new Data(freshPeerPair.first));
+  d->put("added.f", new Data(freshPeerPair.second));
+  d->put("dropped", new Data(droppedPeerPair.first));
+
+  BencodeVisitor v;
+  d->accept(&v);
+  return v.getBencodedData();
+}
+
+pair<string, string> UTPexExtensionMessage::createCompactPeerListAndFlag(const Peers& peers)
+{
+  string addrstring;
+  string flagstring;
+  for(Peers::const_iterator itr = peers.begin(); itr != peers.end(); ++itr) {
+    char compact[6];
+    if(PeerMessageUtil::createcompact(compact, (*itr)->ipaddr, (*itr)->port)) {
+      addrstring.append(&compact[0], &compact[6]);
+      flagstring += (*itr)->isSeeder() ? "2" : "0";
+    }
+  }
+  return pair<string, string>(addrstring, flagstring);
+}
+
+string UTPexExtensionMessage::toString() const
+{
+  return "ut_pex added="+Util::uitos(_freshPeers.size())+", dropped="+
+    Util::uitos(_droppedPeers.size());
+}
+
+void UTPexExtensionMessage::doReceivedAction()
+{
+  PEER_STORAGE(_btContext)->addPeer(_freshPeers);
+}
+
+void UTPexExtensionMessage::addFreshPeer(const PeerHandle& peer)
+{
+  _freshPeers.push_back(peer);
+}
+
+const Peers& UTPexExtensionMessage::getFreshPeers() const
+{
+  return _freshPeers;
+}
+
+void UTPexExtensionMessage::addDroppedPeer(const PeerHandle& peer)
+{
+  _droppedPeers.push_back(peer);
+}
+
+const Peers& UTPexExtensionMessage::getDroppedPeers() const
+{
+  return _droppedPeers;
+}
+
+void UTPexExtensionMessage::setBtContext(const BtContextHandle& btContext)
+{
+  _btContext = btContext;
+}
+
+UTPexExtensionMessageHandle
+UTPexExtensionMessage::create(const BtContextHandle& btContext,
+			      const char* data, size_t len)
+{
+  if(len < 1) {
+    throw new DlAbortEx(MSG_TOO_SMALL_PAYLOAD_SIZE,
+			EXTENSION_NAME.c_str(), len);
+  }
+  UTPexExtensionMessageHandle msg = new UTPexExtensionMessage(*data);
+  SharedHandle<MetaEntry> root = MetaFileUtil::bdecoding(data+1, len-1);
+  if(root.isNull()) {
+    return msg;
+  }
+  const Dictionary* d = dynamic_cast<const Dictionary*>(root.get());
+  if(d) {
+    CompactPeerListProcessor proc(btContext->getPieceLength(),
+				  btContext->getTotalLength());
+    const Data* added = dynamic_cast<const Data*>(d->get("added"));
+    if(added) {
+      msg->_freshPeers = proc.extractPeer(added);
+    }
+    const Data* dropped = dynamic_cast<const Data*>(d->get("dropped"));
+    if(dropped) {
+      msg->_droppedPeers = proc.extractPeer(dropped);
+    }
+  }
+  return msg;
+}

+ 99 - 0
src/UTPexExtensionMessage.h

@@ -0,0 +1,99 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2006 Tatsuhiro Tsujikawa
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL.  If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so.  If you
+ * do not wish to do so, delete this exception statement from your
+ * version.  If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#ifndef _D_UT_PEX_EXTENSION_MESSAGE_H_
+#define _D_UT_PEX_EXTENSION_MESSAGE_H_
+
+#include "ExtensionMessage.h"
+
+class BtContext;
+typedef SharedHandle<BtContext> BtContextHandle;
+class Peer;
+typedef SharedHandle<Peer> PeerHandle;
+typedef deque<PeerHandle> Peers;
+class UTPexExtensionMessage;
+typedef SharedHandle<UTPexExtensionMessage> UTPexExtensionMessageHandle;
+
+class UTPexExtensionMessage:public ExtensionMessage {
+private:
+  uint8_t _extensionMessageID;
+
+  Peers _freshPeers;
+
+  Peers _droppedPeers;
+
+  BtContextHandle _btContext;
+
+  pair<string, string> createCompactPeerListAndFlag(const Peers& peers);
+
+public:
+  UTPexExtensionMessage(uint8_t extensionMessageID);
+
+  virtual ~UTPexExtensionMessage();
+
+  virtual string getBencodedData();
+
+  virtual uint8_t getExtensionMessageID()
+  {
+    return _extensionMessageID;
+  }
+  
+  virtual const string& getExtensionName() const
+  {
+    return EXTENSION_NAME;
+  }
+
+  static const string EXTENSION_NAME;
+
+  virtual string toString() const;
+
+  virtual void doReceivedAction();
+
+  void addFreshPeer(const PeerHandle& peer);
+
+  const Peers& getFreshPeers() const;
+
+  void addDroppedPeer(const PeerHandle& peer);
+
+  const Peers& getDroppedPeers() const;
+
+  void setBtContext(const BtContextHandle& btContext);
+
+  static UTPexExtensionMessageHandle create(const BtContextHandle& btContext,
+					    const char* data, size_t len);
+};
+
+typedef SharedHandle<UTPexExtensionMessage> UTPexExtensionMessageHandle;
+
+#endif // _D_UT_PEX_EXTENSION_MESSAGE_H_

+ 3 - 1
src/message.h

@@ -78,6 +78,7 @@
 #define MSG_DELETING_REQUEST_SLOT_TIMEOUT _("CUID#%d - Deleting request slot blockIndex=%d because of time out")
 #define MSG_DELETING_REQUEST_SLOT_ACQUIRED _("CUID#%d - Deleting request slot blockIndex=%d because the block has been acquired.")
 #define MSG_FAST_EXTENSION_ENABLED _("CUID#%d - Fast extension enabled.")
+#define MSG_EXTENDED_MESSAGING_ENABLED _("CUID#%d - Extended Messaging enabled.")
 #define MSG_FILE_ALLOCATION_FAILURE _("CUID#%d - Exception caught while allocating file space.")
 #define MSG_CONTENT_DISPOSITION_DETECTED _("CUID#%d - Content-Disposition detected. Use %s as filename")
 #define MSG_PEER_BANNED _("CUID#%d - Peer %s:%d banned.")
@@ -131,6 +132,7 @@
 #define MSG_RESOURCE_NOT_FOUND _("Resource not found")
 #define MSG_FILE_RENAMED _("File already exists. Renamed to %s.")
 #define MSG_CANNOT_PARSE_METALINK _("Cannot parse metalink XML file. XML may be malformed.")
+#define MSG_TOO_SMALL_PAYLOAD_SIZE _("Too small payload size for %s, size=%d.")
 
 #define EX_TIME_OUT _("Timeout.")
 #define EX_INVALID_CHUNK_SIZE _("Invalid chunk size.")
@@ -154,7 +156,7 @@
 #define EX_AUTH_FAILED _("Authorization failed.")
 #define EX_GOT_EOF _("Got EOF from the server.")
 #define EX_EOF_FROM_PEER _("Got EOF from peer.")
-#define EX_MULFORMED_META_INFO _("Malformed meta info.")
+#define EX_MALFORMED_META_INFO _("Malformed meta info.")
 
 #define EX_FILE_OPEN _("Failed to open the file %s, cause: %s")
 #define EX_FILE_WRITE _("Failed to write into the file %s, cause: %s")

+ 1 - 0
src/option_processing.cc

@@ -129,6 +129,7 @@ Option* option_processing(int argc, char* const argv[])
   op->put(PREF_ENABLE_DIRECT_IO, V_FALSE);
   op->put(PREF_ALLOW_PIECE_LENGTH_CHANGE, V_FALSE);
   op->put(PREF_METALINK_PREFERRED_PROTOCOL, V_NONE);
+  op->put(PREF_ENABLE_PEER_EXCHANGE, V_TRUE);
   while(1) {
     int optIndex = 0;
     int lopt;

+ 2 - 0
src/prefs.h

@@ -207,6 +207,8 @@
 #define PREF_BT_KEEP_ALIVE_INTERVAL "bt-keep-alive-interval"
 // values: a string, less than or equals to 20 bytes length
 #define PREF_PEER_ID_PREFIX "peer-id-prefix"
+// values: true | false
+#define PREF_ENABLE_PEER_EXCHANGE "enable-peer-exchange"
 
 /**
  * Metalink related preferences

+ 70 - 0
test/BencodeVisitorTest.cc

@@ -0,0 +1,70 @@
+#include "BencodeVisitor.h"
+#include "Data.h"
+#include "List.h"
+#include "Dictionary.h"
+#include <cppunit/extensions/HelperMacros.h>
+
+class BencodeVisitorTest:public CppUnit::TestFixture {
+
+  CPPUNIT_TEST_SUITE(BencodeVisitorTest);
+  CPPUNIT_TEST(testVisit_data);
+  CPPUNIT_TEST(testVisit_list);
+  CPPUNIT_TEST(testVisit_dictionary);
+  CPPUNIT_TEST_SUITE_END();
+private:
+
+public:
+  void setUp() {
+  }
+
+  void testVisit_data();
+  void testVisit_list();
+  void testVisit_dictionary();
+};
+
+
+CPPUNIT_TEST_SUITE_REGISTRATION( BencodeVisitorTest );
+
+void BencodeVisitorTest::testVisit_data()
+{
+  {
+    BencodeVisitor v;
+    string str = "apple";
+    MetaEntryHandle m = new Data(str.c_str(), str.size());
+    m->accept(&v);
+    CPPUNIT_ASSERT_EQUAL(string("5:apple"), v.getBencodedData());
+  }
+  {
+    BencodeVisitor v;
+    string str = "123";
+    MetaEntryHandle m = new Data(str.c_str(), str.size(), true);
+    m->accept(&v);
+    CPPUNIT_ASSERT_EQUAL(string("i123e"), v.getBencodedData());
+  }
+}
+
+void BencodeVisitorTest::testVisit_list()
+{
+  BencodeVisitor v;
+  List l;
+  string s1 = "alpha";
+  l.add(new Data(s1.c_str(), s1.size()));
+  string s2 = "bravo";
+  l.add(new Data(s2.c_str(), s2.size()));
+  string s3 = "123";
+  l.add(new Data(s3.c_str(), s3.size(), true));
+  l.accept(&v);
+  CPPUNIT_ASSERT_EQUAL(string("l5:alpha5:bravoi123ee"), v.getBencodedData());
+}
+
+void BencodeVisitorTest::testVisit_dictionary()
+{
+  BencodeVisitor v;
+  Dictionary d;
+  string s1 = "alpha";
+  d.put("team", new Data(s1.c_str(), s1.size()));
+  string s2 = "123";
+  d.put("score", new Data(s2.c_str(), s2.size(), true));
+  d.accept(&v);
+  CPPUNIT_ASSERT_EQUAL(string("d4:team5:alpha5:scorei123ee"), v.getBencodedData());
+}

+ 121 - 0
test/BtExtendedMessageTest.cc

@@ -0,0 +1,121 @@
+#include "BtExtendedMessage.h"
+#include "PeerMessageUtil.h"
+#include "MockBtContext.h"
+#include "MockExtensionMessageFactory.h"
+#include <cppunit/extensions/HelperMacros.h>
+
+using namespace std;
+
+class BtExtendedMessageTest:public CppUnit::TestFixture {
+
+  CPPUNIT_TEST_SUITE(BtExtendedMessageTest);
+  CPPUNIT_TEST(testCreate);
+  CPPUNIT_TEST(testGetMessage);
+  CPPUNIT_TEST(testDoReceivedAction);
+  CPPUNIT_TEST(testToString);
+  CPPUNIT_TEST_SUITE_END();
+private:
+
+public:
+  void setUp()
+  {
+    BtRegistry::unregisterAll();
+  }
+
+  void tearDown()
+  {
+    BtRegistry::unregisterAll();
+  }
+
+  void testCreate();
+  void testGetMessage();
+  void testDoReceivedAction();
+  void testToString();
+};
+
+
+CPPUNIT_TEST_SUITE_REGISTRATION(BtExtendedMessageTest);
+
+void BtExtendedMessageTest::testCreate() {
+  PeerHandle peer = new Peer("192.168.0.1", 6969, 16*1024, 256*1024);
+  peer->setExtension("charlie", 1);
+  MockBtContextHandle ctx = new MockBtContext();
+  unsigned char infohash[20];
+  memset(infohash, 0, sizeof(infohash));
+  ctx->setInfoHash(infohash);
+  MockExtensionMessageFactoryHandle exmsgFactory = new MockExtensionMessageFactory();
+  
+
+  BtRegistry::registerPeerObjectCluster(ctx->getInfoHashAsString(), new PeerObjectCluster());
+  PeerObjectHandle peerObject = new PeerObject();
+  peerObject->extensionMessageFactory = exmsgFactory;
+
+  PEER_OBJECT_CLUSTER(ctx)->registerHandle(peer->getId(), peerObject);
+
+  // payload:{4:name3:foo}->11bytes
+  string payload = "4:name3:foo";
+  char msg[17];// 6+11bytes
+  PeerMessageUtil::createPeerMessageString((unsigned char*)msg, sizeof(msg), 13, 20);
+  msg[5] = 1; // Set dummy extended message ID 1
+  memcpy(msg+6, payload.c_str(), payload.size());
+  BtExtendedMessageHandle pm = BtExtendedMessage::create(ctx,
+							 peer,
+							 &msg[4], 13);
+  CPPUNIT_ASSERT_EQUAL((int8_t)20, pm->getId());
+  
+  // case: payload size is wrong
+  try {
+    char msg[5];
+    PeerMessageUtil::createPeerMessageString((unsigned char*)msg, sizeof(msg), 1, 20);
+    BtExtendedMessage::create(ctx, peer, &msg[4], 1);
+    CPPUNIT_FAIL("exception must be thrown.");
+  } catch(Exception* e) {
+    cerr << *e << endl;
+    delete e;
+  }
+  // case: id is wrong
+  try {
+    char msg[6];
+    PeerMessageUtil::createPeerMessageString((unsigned char*)msg, sizeof(msg), 2, 21);
+    BtExtendedMessage::create(ctx, peer, &msg[4], 2);
+    CPPUNIT_FAIL("exception must be thrown.");
+  } catch(Exception* e) {
+    cerr << *e << endl;
+    delete e;
+  }
+}
+
+void BtExtendedMessageTest::testGetMessage() {
+  string payload = "4:name3:foo";
+  uint8_t extendedMessageID = 1;
+  MockExtensionMessageHandle exmsg =
+    new MockExtensionMessage("charlie", extendedMessageID,
+			     payload.c_str(),
+			     payload.size());
+  BtExtendedMessage msg(exmsg);
+
+  char data[17];
+  PeerMessageUtil::createPeerMessageString((unsigned char*)data, sizeof(data), 13, 20);
+  *(data+5) = extendedMessageID;
+  memcpy(data+6, payload.c_str(), payload.size());
+  CPPUNIT_ASSERT(memcmp(msg.getMessage(), data, 17) == 0);
+}
+
+void BtExtendedMessageTest::testDoReceivedAction() {
+  MockExtensionMessageHandle exmsg =
+    new MockExtensionMessage("charlie", 1, "", 0);
+  BtExtendedMessage msg(exmsg);
+  msg.doReceivedAction();
+  CPPUNIT_ASSERT(exmsg->_doReceivedActionCalled);
+}
+  
+void BtExtendedMessageTest::testToString() {
+  string payload = "4:name3:foo";
+  uint8_t extendedMessageID = 1;
+  MockExtensionMessageHandle exmsg =
+    new MockExtensionMessage("charlie", extendedMessageID,
+			     payload.c_str(),
+			     payload.size());
+  BtExtendedMessage msg(exmsg);
+  CPPUNIT_ASSERT_EQUAL(string("extended charlie"), msg.toString());
+}

+ 3 - 3
test/BtHandshakeMessageTest.cc

@@ -34,7 +34,7 @@ void createHandshakeMessageData(unsigned char* msg) {
   msg[0] = 19;
   memcpy(&msg[1], BtHandshakeMessageTest::BTPSTR.c_str(),
 	 BtHandshakeMessageTest::BTPSTR.size());
-  unsigned char reserved[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04 };
+  unsigned char reserved[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x04 };
   memcpy(&msg[20], reserved, sizeof(reserved));
   unsigned char infoHash[] = { 0xff, 0xff, 0xff, 0xff, 0xff,
 			       0xff, 0xff, 0xff, 0xff, 0xff,
@@ -56,7 +56,7 @@ void BtHandshakeMessageTest::testCreate() {
   CPPUNIT_ASSERT_EQUAL((int8_t)19, message->getPstrlen());
   CPPUNIT_ASSERT_EQUAL(Util::toHex((const unsigned char*)BTPSTR.c_str(), BTPSTR.size()),
 		       Util::toHex(message->getPstr(), BtHandshakeMessage::PSTR_LENGTH));
-  CPPUNIT_ASSERT_EQUAL(string("0000000000000004"),
+  CPPUNIT_ASSERT_EQUAL(string("0000000000100004"),
 		       Util::toHex(message->getReserved(), BtHandshakeMessage::RESERVED_LENGTH));
   CPPUNIT_ASSERT_EQUAL(string("ffffffffffffffffffffffffffffffffffffffff"),
 		       Util::toHex(message->getInfoHash(), INFO_HASH_LENGTH));
@@ -98,5 +98,5 @@ void BtHandshakeMessageTest::testToString() {
   msg.setInfoHash(infoHash);
   msg.setPeerId(peerId);
 
-  CPPUNIT_ASSERT_EQUAL(string("handshake peerId=%f0%f0%f0%f0%f0%f0%f0%f0%f0%f0%f0%f0%f0%f0%f0%f0%f0%f0%f0%f0, reserved=0000000000000004"), msg.toString());
+  CPPUNIT_ASSERT_EQUAL(string("handshake peerId=%f0%f0%f0%f0%f0%f0%f0%f0%f0%f0%f0%f0%f0%f0%f0%f0%f0%f0%f0%f0, reserved=0000000000100004"), msg.toString());
 }

+ 78 - 0
test/DefaultBtMessageFactoryTest.cc

@@ -0,0 +1,78 @@
+#include "DefaultBtMessageFactory.h"
+#include "Peer.h"
+#include "PeerMessageUtil.h"
+#include "BtRegistry.h"
+#include "MockBtContext.h"
+#include "MockExtensionMessageFactory.h"
+#include "BtExtendedMessage.h"
+#include <cppunit/extensions/HelperMacros.h>
+
+class DefaultBtMessageFactoryTest:public CppUnit::TestFixture {
+
+  CPPUNIT_TEST_SUITE(DefaultBtMessageFactoryTest);
+  CPPUNIT_TEST(testCreateBtMessage_BtExtendedMessage);
+  CPPUNIT_TEST_SUITE_END();
+private:
+  MockBtContextHandle _btContext;
+  PeerHandle _peer;
+public:
+  DefaultBtMessageFactoryTest():_btContext(0), _peer(0) {}
+
+  void setUp()
+  {
+    BtRegistry::unregisterAll();
+    MockBtContextHandle btContext = new MockBtContext();
+    unsigned char infohash[20];
+    memset(infohash, 0, sizeof(infohash));
+    btContext->setInfoHash(infohash);
+    _btContext = btContext;
+
+    _peer = new Peer("192.168.0.1", 6969, 16*1024, 256*1024);
+    _peer->setExtendedMessagingEnabled(true);
+
+    MockExtensionMessageFactoryHandle exmsgFactory = new MockExtensionMessageFactory();
+    BtRegistry::registerPeerObjectCluster(_btContext->getInfoHashAsString(),
+					  new PeerObjectCluster());
+    PeerObjectHandle peerObject = new PeerObject();
+    peerObject->extensionMessageFactory = exmsgFactory;
+
+    PEER_OBJECT_CLUSTER(_btContext)->registerHandle(_peer->getId(), peerObject);
+  }
+
+  void tearDown()
+  {
+    BtRegistry::unregisterAll();
+  }
+
+  void testCreateBtMessage_BtExtendedMessage();
+};
+
+
+CPPUNIT_TEST_SUITE_REGISTRATION(DefaultBtMessageFactoryTest);
+
+void DefaultBtMessageFactoryTest::testCreateBtMessage_BtExtendedMessage()
+{
+  
+  DefaultBtMessageFactory factory;
+  factory.setBtContext(_btContext);
+  factory.setPeer(_peer);
+  
+  // payload:{4:name3:foo}->11bytes
+  string payload = "4:name3:foo";
+  char msg[17];// 6+11bytes
+  PeerMessageUtil::createPeerMessageString((unsigned char*)msg, sizeof(msg), 13, 20);
+  msg[5] = 1; // Set dummy extended message ID 1
+  memcpy(msg+6, payload.c_str(), payload.size());
+  
+  BtExtendedMessageHandle m = factory.createBtMessage((const unsigned char*)msg+4, sizeof(msg));
+
+  try {
+    // disable extended messaging
+    _peer->setExtendedMessagingEnabled(false);
+    factory.createBtMessage((const unsigned char*)msg+4, sizeof(msg));
+    CPPUNIT_FAIL("exception must be thrown.");
+  } catch(Exception* e) {
+    cerr << *e << endl;
+    delete e;
+  }
+}

+ 113 - 0
test/DefaultExtensionMessageFactoryTest.cc

@@ -0,0 +1,113 @@
+#include "DefaultExtensionMessageFactory.h"
+#include "Peer.h"
+#include "MockBtContext.h"
+#include "PeerMessageUtil.h"
+#include "HandshakeExtensionMessage.h"
+#include "UTPexExtensionMessage.h"
+#include "Exception.h"
+#include "BtRegistry.h"
+#include "BtRuntime.h"
+#include <cppunit/extensions/HelperMacros.h>
+
+class DefaultExtensionMessageFactoryTest:public CppUnit::TestFixture {
+
+  CPPUNIT_TEST_SUITE(DefaultExtensionMessageFactoryTest);
+  CPPUNIT_TEST(testCreateMessage_unknown);
+  CPPUNIT_TEST(testCreateMessage_Handshake);
+  CPPUNIT_TEST(testCreateMessage_UTPex);
+  CPPUNIT_TEST_SUITE_END();
+private:
+  MockBtContextHandle _btContext;
+  PeerHandle _peer;
+public:
+  DefaultExtensionMessageFactoryTest():_btContext(0), _peer(0) {}
+
+  void setUp()
+  {
+    BtRegistry::unregisterAll();
+    MockBtContextHandle btContext = new MockBtContext();
+    unsigned char infohash[20];
+    memset(infohash, 0, sizeof(infohash));
+    btContext->setInfoHash(infohash);
+    _btContext = btContext;
+
+    BtRuntimeHandle btRuntime = new BtRuntime();
+    BtRegistry::registerBtRuntime(_btContext->getInfoHashAsString(),
+				  btRuntime);
+
+    _peer = new Peer("192.168.0.1", 6969, 16*1024, 256*1024);
+    _peer->setExtension("ut_pex", 1);
+  }
+
+  void tearDown()
+  {
+    BtRegistry::unregisterAll();
+  }
+
+  void testCreateMessage_unknown();
+  void testCreateMessage_Handshake();
+  void testCreateMessage_UTPex();
+};
+
+
+CPPUNIT_TEST_SUITE_REGISTRATION(DefaultExtensionMessageFactoryTest);
+
+void DefaultExtensionMessageFactoryTest::testCreateMessage_unknown()
+{
+  DefaultExtensionMessageFactory factory;
+  factory.setBtContext(_btContext);
+  factory.setPeer(_peer);
+  _peer->setExtension("foo", 255);
+
+  char id[1] = { 255 };
+
+  string data = string(&id[0], &id[1]);
+  try {
+    factory.createMessage(data.c_str(), data.size());
+    CPPUNIT_FAIL("exception must be thrown.");
+  } catch(Exception* e) {
+    cerr << *e << endl;
+    delete e;
+  }
+}
+
+void DefaultExtensionMessageFactoryTest::testCreateMessage_Handshake()
+{
+  DefaultExtensionMessageFactory factory;
+  factory.setBtContext(_btContext);
+  factory.setPeer(_peer);
+
+  char id[1] = { 0 };
+
+  string data = string(&id[0], &id[1])+"d1:v5:aria2e";
+  HandshakeExtensionMessageHandle m = factory.createMessage(data.c_str(), data.size());
+  CPPUNIT_ASSERT_EQUAL(string("aria2"), m->getClientVersion());
+}
+
+void DefaultExtensionMessageFactoryTest::testCreateMessage_UTPex()
+{
+  DefaultExtensionMessageFactory factory;
+  factory.setBtContext(_btContext);
+  factory.setPeer(_peer);
+  
+  char c1[6];
+  char c2[6];
+  char c3[6];
+  char c4[6];
+  PeerMessageUtil::createcompact(c1, "192.168.0.1", 6881);
+  PeerMessageUtil::createcompact(c2, "10.1.1.2", 9999);
+  PeerMessageUtil::createcompact(c3, "192.168.0.2", 6882);
+  PeerMessageUtil::createcompact(c4, "10.1.1.3",10000);
+
+  char id[1] = { BT_RUNTIME(_btContext)->getExtensionMessageID("ut_pex") };
+
+  string data = string(&id[0], &id[1])+"d5:added12:"+
+    string(&c1[0], &c1[6])+string(&c2[0], &c2[6])+
+    "7:added.f2:207:dropped12:"+
+    string(&c3[0], &c3[6])+string(&c4[0], &c4[6])+
+    "e";
+
+  UTPexExtensionMessageHandle m = factory.createMessage(data.c_str(), data.size());
+  CPPUNIT_ASSERT_EQUAL(BT_RUNTIME(_btContext)->getExtensionMessageID("ut_pex"),
+		       m->getExtensionMessageID());
+}

+ 3 - 3
test/DefaultPeerListProcessorTest.cc

@@ -36,7 +36,7 @@ void DefaultPeerListProcessorTest::testExtractPeer() {
   CPPUNIT_ASSERT_EQUAL((size_t)1, peers.size());
   PeerHandle peer = *peers.begin();
   CPPUNIT_ASSERT_EQUAL(string("192.168.0.1"), peer->ipaddr);
-  CPPUNIT_ASSERT_EQUAL((int32_t)2006, peer->port);
+  CPPUNIT_ASSERT_EQUAL((uint16_t)2006, peer->port);
 }
 
 void DefaultPeerListProcessorTest::testExtract2Peers() {
@@ -49,9 +49,9 @@ void DefaultPeerListProcessorTest::testExtract2Peers() {
   CPPUNIT_ASSERT_EQUAL((size_t)2, peers.size());
   PeerHandle peer = *peers.begin();
   CPPUNIT_ASSERT_EQUAL(string("192.168.0.1"), peer->ipaddr);
-  CPPUNIT_ASSERT_EQUAL((int32_t)2006, peer->port);
+  CPPUNIT_ASSERT_EQUAL((uint16_t)2006, peer->port);
 
   peer = *(peers.begin()+1);
   CPPUNIT_ASSERT_EQUAL(string("192.168.0.2"), peer->ipaddr);
-  CPPUNIT_ASSERT_EQUAL((int32_t)2007, peer->port);
+  CPPUNIT_ASSERT_EQUAL((uint16_t)2007, peer->port);
 }

+ 2 - 3
test/DefaultPeerStorageTest.cc

@@ -113,9 +113,8 @@ void DefaultPeerStorageTest::testAddPeer() {
 
   CPPUNIT_ASSERT_EQUAL((int32_t)3, ps.countPeer());
 
-  // this is true, because peer1 in the container has no errors and
-  // it is replaced by peer1(self assignment).
-  CPPUNIT_ASSERT_EQUAL(true, ps.addPeer(peer1));
+  // this returns false, because peer1 is already in the container
+  CPPUNIT_ASSERT_EQUAL(false, ps.addPeer(peer1));
   // the number of peers doesn't change.
   CPPUNIT_ASSERT_EQUAL((int32_t)3, ps.countPeer());
 

+ 155 - 0
test/HandshakeExtensionMessageTest.cc

@@ -0,0 +1,155 @@
+#include "HandshakeExtensionMessage.h"
+#include "Peer.h"
+#include "MockBtContext.h"
+#include "MockPeerStorage.h"
+#include "BtRegistry.h"
+#include <cppunit/extensions/HelperMacros.h>
+
+class HandshakeExtensionMessageTest:public CppUnit::TestFixture {
+
+  CPPUNIT_TEST_SUITE(HandshakeExtensionMessageTest);
+  CPPUNIT_TEST(testGetExtensionMessageID);
+  CPPUNIT_TEST(testGetExtensionName);
+  CPPUNIT_TEST(testGetBencodedData);
+  CPPUNIT_TEST(testToString);
+  CPPUNIT_TEST(testDoReceivedAction);
+  CPPUNIT_TEST(testCreate);
+  CPPUNIT_TEST(testCreate_stringnum);
+  CPPUNIT_TEST_SUITE_END();
+private:
+  BtContextHandle _btContext;
+public:
+  HandshakeExtensionMessageTest():_btContext(0) {}
+
+  void setUp()
+  {
+    BtRegistry::unregisterAll();
+    MockBtContextHandle btContext = new MockBtContext();
+    unsigned char infohash[20];
+    memset(infohash, 0, sizeof(infohash));
+    btContext->setInfoHash(infohash);
+    _btContext = btContext;
+    MockPeerStorageHandle peerStorage = new MockPeerStorage();
+    BtRegistry::registerPeerStorage(_btContext->getInfoHashAsString(),
+				    peerStorage);
+  }
+
+  void tearDown()
+  {
+    BtRegistry::unregisterAll();
+  }
+
+  void testGetExtensionMessageID();
+  void testGetExtensionName();
+  void testGetBencodedData();
+  void testToString();
+  void testDoReceivedAction();
+  void testCreate();
+  void testCreate_stringnum();
+};
+
+
+CPPUNIT_TEST_SUITE_REGISTRATION(HandshakeExtensionMessageTest);
+
+void HandshakeExtensionMessageTest::testGetExtensionMessageID()
+{
+  HandshakeExtensionMessage msg;
+  CPPUNIT_ASSERT_EQUAL((uint8_t)0, msg.getExtensionMessageID());
+}
+
+void HandshakeExtensionMessageTest::testGetExtensionName()
+{
+  HandshakeExtensionMessage msg;
+  CPPUNIT_ASSERT_EQUAL(string("handshake"), msg.getExtensionName());
+}
+
+void HandshakeExtensionMessageTest::testGetBencodedData()
+{
+  HandshakeExtensionMessage msg;
+  msg.setClientVersion("aria2");
+  msg.setTCPPort(6889);
+  msg.setExtension("ut_pex", 1);
+  msg.setExtension("a2_dht", 2);
+  CPPUNIT_ASSERT_EQUAL(string("d1:v5:aria21:pi6889e1:md6:a2_dhti2e6:ut_pexi1eee"), msg.getBencodedData());
+}
+
+void HandshakeExtensionMessageTest::testToString()
+{
+  HandshakeExtensionMessage msg;
+  msg.setClientVersion("aria2");
+  msg.setTCPPort(6889);
+  msg.setExtension("ut_pex", 1);
+  msg.setExtension("a2_dht", 2);
+  CPPUNIT_ASSERT_EQUAL(string("handshake client=aria2, tcpPort=6889, a2_dht=2, ut_pex=1"), msg.toString());
+}
+
+void HandshakeExtensionMessageTest::testDoReceivedAction()
+{
+  PeerHandle peer = new Peer("192.168.0.1", 0, 1, 1);
+  HandshakeExtensionMessage msg;
+  msg.setClientVersion("aria2");
+  msg.setTCPPort(6889);
+  msg.setExtension("ut_pex", 1);
+  msg.setExtension("a2_dht", 2);
+  msg.setPeer(peer);
+  msg.setBtContext(_btContext);
+
+  msg.doReceivedAction();
+
+  CPPUNIT_ASSERT_EQUAL((uint16_t)6889, peer->port);
+  CPPUNIT_ASSERT_EQUAL((uint8_t)1, peer->getExtensionMessageID("ut_pex"));
+  CPPUNIT_ASSERT_EQUAL((uint8_t)2, peer->getExtensionMessageID("a2_dht"));
+
+  CPPUNIT_ASSERT_EQUAL((size_t)1, PEER_STORAGE(_btContext)->getPeers().size());
+  PeerHandle p1 = PEER_STORAGE(_btContext)->getPeers().front();
+  CPPUNIT_ASSERT_EQUAL(string("192.168.0.1"), p1->ipaddr);
+  CPPUNIT_ASSERT_EQUAL((uint16_t)6889, p1->port);
+}
+
+void HandshakeExtensionMessageTest::testCreate()
+{
+  string in = "0d1:pi6881e1:v5:aria21:md6:ut_pexi1eee";
+  HandshakeExtensionMessageHandle m =
+    HandshakeExtensionMessage::create(in.c_str(), in.size());
+  CPPUNIT_ASSERT_EQUAL(string("aria2"), m->getClientVersion());
+  CPPUNIT_ASSERT_EQUAL((uint16_t)6881, m->getTCPPort());
+  CPPUNIT_ASSERT_EQUAL((uint8_t)1, m->getExtensionMessageID("ut_pex"));
+
+  try {
+    // bad payload format
+    string in = "011:hello world";
+    HandshakeExtensionMessage::create(in.c_str(), in.size());
+    CPPUNIT_FAIL("exception must be thrown.");
+  } catch(Exception* e) {
+    cerr << *e << endl;
+    delete e;
+  }
+  try {
+    // malformed dencoded message
+    string in = "011:hello";
+    HandshakeExtensionMessage::create(in.c_str(), in.size());
+    CPPUNIT_FAIL("exception must be thrown.");
+  } catch(Exception* e) {
+    cerr << *e << endl;
+    delete e;
+  }
+  try {
+    // 0 length data
+    string in = "";
+    HandshakeExtensionMessage::create(in.c_str(), in.size());
+    CPPUNIT_FAIL("exception must be thrown.");
+  } catch(Exception* e) {
+    cerr << *e << endl;
+    delete e;
+  }    
+}
+
+void HandshakeExtensionMessageTest::testCreate_stringnum()
+{
+  string in = "0d1:p4:68811:v5:aria21:md6:ut_pex1:1ee";
+  HandshakeExtensionMessageHandle m =
+    HandshakeExtensionMessage::create(in.c_str(), in.size());
+  CPPUNIT_ASSERT_EQUAL(string("aria2"), m->getClientVersion());
+  CPPUNIT_ASSERT_EQUAL((uint16_t)6881, m->getTCPPort());
+  CPPUNIT_ASSERT_EQUAL((uint8_t)1, m->getExtensionMessageID("ut_pex"));
+}

+ 7 - 1
test/Makefile.am

@@ -95,7 +95,13 @@ aria2c_SOURCES += BtAllowedFastMessageTest.cc\
 	BtDependencyTest.cc\
 	BtPostDownloadHandlerTest.cc\
 	DownloadHandlerFactoryTest.cc\
-	TimeSeedCriteriaTest.cc
+	TimeSeedCriteriaTest.cc\
+	BencodeVisitorTest.cc\
+	BtExtendedMessageTest.cc\
+	HandshakeExtensionMessageTest.cc\
+	UTPexExtensionMessageTest.cc\
+	DefaultBtMessageFactoryTest.cc\
+	DefaultExtensionMessageFactoryTest.cc
 endif # ENABLE_BITTORRENT
 
 if ENABLE_METALINK

+ 25 - 4
test/Makefile.in

@@ -83,7 +83,13 @@ check_PROGRAMS = $(am__EXEEXT_1)
 @ENABLE_BITTORRENT_TRUE@	BtDependencyTest.cc\
 @ENABLE_BITTORRENT_TRUE@	BtPostDownloadHandlerTest.cc\
 @ENABLE_BITTORRENT_TRUE@	DownloadHandlerFactoryTest.cc\
-@ENABLE_BITTORRENT_TRUE@	TimeSeedCriteriaTest.cc
+@ENABLE_BITTORRENT_TRUE@	TimeSeedCriteriaTest.cc\
+@ENABLE_BITTORRENT_TRUE@	BencodeVisitorTest.cc\
+@ENABLE_BITTORRENT_TRUE@	BtExtendedMessageTest.cc\
+@ENABLE_BITTORRENT_TRUE@	HandshakeExtensionMessageTest.cc\
+@ENABLE_BITTORRENT_TRUE@	UTPexExtensionMessageTest.cc\
+@ENABLE_BITTORRENT_TRUE@	DefaultBtMessageFactoryTest.cc\
+@ENABLE_BITTORRENT_TRUE@	DefaultExtensionMessageFactoryTest.cc
 
 @ENABLE_METALINK_TRUE@am__append_3 = MetalinkerTest.cc\
 @ENABLE_METALINK_TRUE@	MetalinkEntryTest.cc\
@@ -156,8 +162,11 @@ am__aria2c_SOURCES_DIST = AllTest.cc Base64Test.cc SequenceTest.cc \
 	PeerMessageUtilTest.cc ShareRatioSeedCriteriaTest.cc \
 	BtRegistryTest.cc BtDependencyTest.cc \
 	BtPostDownloadHandlerTest.cc DownloadHandlerFactoryTest.cc \
-	TimeSeedCriteriaTest.cc MetalinkerTest.cc MetalinkEntryTest.cc \
-	Metalink2RequestGroupTest.cc \
+	TimeSeedCriteriaTest.cc BencodeVisitorTest.cc \
+	BtExtendedMessageTest.cc HandshakeExtensionMessageTest.cc \
+	UTPexExtensionMessageTest.cc DefaultBtMessageFactoryTest.cc \
+	DefaultExtensionMessageFactoryTest.cc MetalinkerTest.cc \
+	MetalinkEntryTest.cc Metalink2RequestGroupTest.cc \
 	MetalinkPostDownloadHandlerTest.cc MetalinkHelperTest.cc \
 	MetalinkParserControllerTest.cc MetalinkProcessorTest.cc
 @ENABLE_MESSAGE_DIGEST_TRUE@am__objects_1 =  \
@@ -203,7 +212,13 @@ am__aria2c_SOURCES_DIST = AllTest.cc Base64Test.cc SequenceTest.cc \
 @ENABLE_BITTORRENT_TRUE@	BtDependencyTest.$(OBJEXT) \
 @ENABLE_BITTORRENT_TRUE@	BtPostDownloadHandlerTest.$(OBJEXT) \
 @ENABLE_BITTORRENT_TRUE@	DownloadHandlerFactoryTest.$(OBJEXT) \
-@ENABLE_BITTORRENT_TRUE@	TimeSeedCriteriaTest.$(OBJEXT)
+@ENABLE_BITTORRENT_TRUE@	TimeSeedCriteriaTest.$(OBJEXT) \
+@ENABLE_BITTORRENT_TRUE@	BencodeVisitorTest.$(OBJEXT) \
+@ENABLE_BITTORRENT_TRUE@	BtExtendedMessageTest.$(OBJEXT) \
+@ENABLE_BITTORRENT_TRUE@	HandshakeExtensionMessageTest.$(OBJEXT) \
+@ENABLE_BITTORRENT_TRUE@	UTPexExtensionMessageTest.$(OBJEXT) \
+@ENABLE_BITTORRENT_TRUE@	DefaultBtMessageFactoryTest.$(OBJEXT) \
+@ENABLE_BITTORRENT_TRUE@	DefaultExtensionMessageFactoryTest.$(OBJEXT)
 @ENABLE_METALINK_TRUE@am__objects_3 = MetalinkerTest.$(OBJEXT) \
 @ENABLE_METALINK_TRUE@	MetalinkEntryTest.$(OBJEXT) \
 @ENABLE_METALINK_TRUE@	Metalink2RequestGroupTest.$(OBJEXT) \
@@ -518,12 +533,14 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/AnnounceListTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/AuthConfigFactoryTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Base64Test.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/BencodeVisitorTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/BitfieldManTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/BtAllowedFastMessageTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/BtBitfieldMessageTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/BtCancelMessageTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/BtChokeMessageTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/BtDependencyTest.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/BtExtendedMessageTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/BtHandshakeMessageTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/BtHaveAllMessageTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/BtHaveMessageTest.Po@am__quote@
@@ -549,9 +566,11 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DefaultBtAnnounceTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DefaultBtContextTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DefaultBtMessageDispatcherTest.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DefaultBtMessageFactoryTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DefaultBtProgressInfoFileTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DefaultBtRequestFactoryTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DefaultDiskWriterTest.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DefaultExtensionMessageFactoryTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DefaultPeerListProcessorTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DefaultPeerStorageTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DefaultPieceStorageTest.Po@am__quote@
@@ -562,6 +581,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/FileTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/FileUriListParserTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/GrowSegmentTest.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/HandshakeExtensionMessageTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/HttpHeaderProcessorTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/HttpHeaderTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/HttpRequestTest.Po@am__quote@
@@ -604,6 +624,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/SpeedCalcTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/StreamUriListParserTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/TimeSeedCriteriaTest.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/UTPexExtensionMessageTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/UtilTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/a2functionalTest.Po@am__quote@
 

+ 8 - 0
test/MockBtMessageFactory.h

@@ -3,6 +3,9 @@
 
 #include "BtMessageFactory.h"
 
+class ExtensionMessage;
+typedef SharedHandle<ExtensionMessage> ExensionMessageHandle;
+
 class MockBtMessageFactory : public BtMessageFactory {
 public:
   MockBtMessageFactory() {}
@@ -84,6 +87,11 @@ public:
   virtual BtMessageHandle createAllowedFastMessage(int32_t index) {
     return BtMessageHandle(0);
   }
+  
+  virtual BtMessageHandle createBtExtendedMessage(const ExensionMessageHandle&)
+  {
+    return BtMessageHandle(0);
+  }
 };
 
 typedef SharedHandle<MockBtMessageFactory> MockBtMessageFactoryHandle;

+ 51 - 0
test/MockExtensionMessage.h

@@ -0,0 +1,51 @@
+#ifndef _D_MOCK_EXTENSION_MESSAGE_H_
+#define _D_MOCK_EXTENSION_MESSAGE_H_
+
+#include "ExtensionMessage.h"
+
+class MockExtensionMessage:public ExtensionMessage {
+public:
+  string _extensionName;
+  uint8_t _extensionMessageID;
+  string _data;
+  bool _doReceivedActionCalled;
+public:
+  MockExtensionMessage(const string& extensionName,
+		       uint8_t extensionMessageID,
+		       const char* data,
+		       size_t length):_extensionName(extensionName),
+				      _extensionMessageID(extensionMessageID),
+				      _data(&data[0], &data[length]),
+				      _doReceivedActionCalled(false) {}
+
+  virtual ~MockExtensionMessage() {}
+
+  virtual string getBencodedData()
+  {
+    return _data;
+  }
+
+  virtual uint8_t getExtensionMessageID()
+  {
+    return _extensionMessageID;
+  }
+  
+  virtual const string& getExtensionName() const
+  {
+    return _extensionName;
+  }
+
+  virtual string toString() const
+  {
+    return _extensionName;
+  }
+
+  virtual void doReceivedAction()
+  {
+    _doReceivedActionCalled = true;
+  }
+};
+
+typedef SharedHandle<MockExtensionMessage> MockExtensionMessageHandle;
+
+#endif // _D_MOCK_EXTENSION_MESSAGE_H_

+ 20 - 0
test/MockExtensionMessageFactory.h

@@ -0,0 +1,20 @@
+#ifndef _D_MOCK_EXTENSION_MESSAGE_FACTORY_H_
+#define _D_MOCK_EXTENSION_MESSAGE_FACTORY_H_
+
+#include "ExtensionMessageFactory.h"
+#include "MockExtensionMessage.h"
+
+class MockExtensionMessageFactory:public ExtensionMessageFactory {
+public:
+  virtual ~MockExtensionMessageFactory() {}
+
+  virtual ExtensionMessageHandle createMessage(const char* data,
+					       size_t length)
+  {
+    return new MockExtensionMessage("a2_mock", *data, data+1, length-1);
+				    
+  }
+};
+
+typedef SharedHandle<MockExtensionMessageFactory> MockExtensionMessageFactoryHandle;
+#endif // _D_MOCK_EXTENSION_MESSAGE_FACTORY_H_

+ 3 - 2
test/ShaVisitorTest.cc

@@ -51,12 +51,13 @@ void ShaVisitorTest::testVisit() {
 
 void ShaVisitorTest::testVisitCompound() {
   ShaVisitor v;
-  MetaEntry* e = MetaFileUtil::parseMetaFile("test.torrent");
+  string data = "d4:name5:aria24:listli123eee";
+  MetaEntry* e = MetaFileUtil::bdecoding(data.c_str(), data.size());
   e->accept(&v);
   unsigned char md[20];
   int len = 0;
   v.getHash(md, len);
   string hashHex = hexHash(md, len);
-  CPPUNIT_ASSERT_EQUAL(string("5a2bf55fb6ec71a9cd3e06537aa7795cafccffab"),
+  CPPUNIT_ASSERT_EQUAL(string("75538fbac9a074bb98c6a19b6bca3bc87ef9bf8e"),
 		       hashHex);
 }

+ 187 - 0
test/UTPexExtensionMessageTest.cc

@@ -0,0 +1,187 @@
+#include "UTPexExtensionMessage.h"
+#include "Peer.h"
+#include "a2netcompat.h"
+#include "Util.h"
+#include "PeerMessageUtil.h"
+#include "BtRegistry.h"
+#include "MockBtContext.h"
+#include "MockPeerStorage.h"
+#include <cppunit/extensions/HelperMacros.h>
+
+class UTPexExtensionMessageTest:public CppUnit::TestFixture {
+
+  CPPUNIT_TEST_SUITE(UTPexExtensionMessageTest);
+  CPPUNIT_TEST(testGetExtensionMessageID);
+  CPPUNIT_TEST(testGetExtensionName);
+  CPPUNIT_TEST(testGetBencodedData);
+  CPPUNIT_TEST(testToString);
+  CPPUNIT_TEST(testDoReceivedAction);
+  CPPUNIT_TEST(testCreate);
+  CPPUNIT_TEST_SUITE_END();
+private:
+  MockBtContextHandle _btContext;
+public:
+  UTPexExtensionMessageTest():_btContext(0) {}
+
+  void setUp()
+  {
+    BtRegistry::unregisterAll();
+    MockBtContextHandle btContext = new MockBtContext();
+    unsigned char infohash[20];
+    memset(infohash, 0, sizeof(infohash));
+    btContext->setInfoHash(infohash);
+    _btContext = btContext;
+    MockPeerStorageHandle peerStorage = new MockPeerStorage();
+    BtRegistry::registerPeerStorage(_btContext->getInfoHashAsString(),
+				    peerStorage);
+  }
+
+  void tearDown()
+  {
+    BtRegistry::unregisterAll();
+  }
+
+  void testGetExtensionMessageID();
+  void testGetExtensionName();
+  void testGetBencodedData();
+  void testToString();
+  void testDoReceivedAction();
+  void testCreate();
+};
+
+
+CPPUNIT_TEST_SUITE_REGISTRATION(UTPexExtensionMessageTest);
+
+void UTPexExtensionMessageTest::testGetExtensionMessageID()
+{
+  UTPexExtensionMessage msg(1);
+  CPPUNIT_ASSERT_EQUAL((uint8_t)1, msg.getExtensionMessageID());
+}
+
+void UTPexExtensionMessageTest::testGetExtensionName()
+{
+  UTPexExtensionMessage msg(1);
+  CPPUNIT_ASSERT_EQUAL(string("ut_pex"), msg.getExtensionName());
+}
+
+void UTPexExtensionMessageTest::testGetBencodedData()
+{
+  UTPexExtensionMessage msg(1);
+  PeerHandle p1 = new Peer("192.168.0.1", 6881, 1, 1);
+  p1->setAllBitfield();
+  msg.addFreshPeer(p1);// added seeder, check add.f flag
+  PeerHandle p2 = new Peer("10.1.1.2", 9999, 1, 1);
+  msg.addFreshPeer(p2);
+  PeerHandle p3 = new Peer("192.168.0.2", 6882, 1, 1);
+  msg.addDroppedPeer(p3);
+  PeerHandle p4 = new Peer("10.1.1.3", 10000, 1, 1);
+  msg.addDroppedPeer(p4);
+
+  char c1[6];
+  char c2[6];
+  char c3[6];
+  char c4[6];
+  PeerMessageUtil::createcompact(c1, p1->ipaddr, p1->port);
+  PeerMessageUtil::createcompact(c2, p2->ipaddr, p2->port);
+  PeerMessageUtil::createcompact(c3, p3->ipaddr, p3->port);
+  PeerMessageUtil::createcompact(c4, p4->ipaddr, p4->port);
+
+  string expected = "d5:added12:"+
+    string(&c1[0], &c1[6])+string(&c2[0], &c2[6])+
+    "7:added.f2:207:dropped12:"+
+    string(&c3[0], &c3[6])+string(&c4[0], &c4[6])+
+    "e";
+  string bd = msg.getBencodedData();
+  CPPUNIT_ASSERT_EQUAL(Util::urlencode(expected),
+		       Util::urlencode(bd));
+}
+
+void UTPexExtensionMessageTest::testToString()
+{
+  UTPexExtensionMessage msg(1);
+  PeerHandle p1 = new Peer("192.168.0.1", 6881, 1, 1);
+  p1->setAllBitfield();
+  msg.addFreshPeer(p1);// added seeder, check add.f flag
+  PeerHandle p2 = new Peer("10.1.1.2", 9999, 1, 1);
+  msg.addFreshPeer(p2);
+  PeerHandle p3 = new Peer("192.168.0.2", 6882, 1, 1);
+  msg.addDroppedPeer(p3);
+  PeerHandle p4 = new Peer("10.1.1.3", 10000, 1, 1);
+  msg.addDroppedPeer(p4);
+  CPPUNIT_ASSERT_EQUAL(string("ut_pex added=2, dropped=2"), msg.toString());
+}
+
+void UTPexExtensionMessageTest::testDoReceivedAction()
+{
+  UTPexExtensionMessage msg(1);
+  PeerHandle p1 = new Peer("192.168.0.1", 6881, 1, 1);
+  p1->setAllBitfield();
+  msg.addFreshPeer(p1);// added seeder, check add.f flag
+  PeerHandle p2 = new Peer("10.1.1.2", 9999, 1, 1);
+  msg.addFreshPeer(p2);
+  PeerHandle p3 = new Peer("192.168.0.2", 6882, 1, 1);
+  msg.addDroppedPeer(p3);
+  PeerHandle p4 = new Peer("10.1.1.3", 10000, 1, 1);
+  msg.addDroppedPeer(p4);
+  msg.setBtContext(_btContext);
+
+  msg.doReceivedAction();
+
+  CPPUNIT_ASSERT_EQUAL((size_t)2, PEER_STORAGE(_btContext)->getPeers().size());
+  {
+    PeerHandle p = PEER_STORAGE(_btContext)->getPeers()[0];
+    CPPUNIT_ASSERT_EQUAL(string("192.168.0.1"), p->ipaddr);
+    CPPUNIT_ASSERT_EQUAL((uint16_t)6881, p->port);
+  }
+  {
+    PeerHandle p = PEER_STORAGE(_btContext)->getPeers()[1];
+    CPPUNIT_ASSERT_EQUAL(string("10.1.1.2"), p->ipaddr);
+    CPPUNIT_ASSERT_EQUAL((uint16_t)9999, p->port);
+  }
+}
+
+void UTPexExtensionMessageTest::testCreate()
+{
+  _btContext->setPieceLength(256*1024);
+  _btContext->setTotalLength(1024*1024);
+
+  char c1[6];
+  char c2[6];
+  char c3[6];
+  char c4[6];
+  PeerMessageUtil::createcompact(c1, "192.168.0.1", 6881);
+  PeerMessageUtil::createcompact(c2, "10.1.1.2", 9999);
+  PeerMessageUtil::createcompact(c3, "192.168.0.2", 6882);
+  PeerMessageUtil::createcompact(c4, "10.1.1.3",10000);
+
+  char id[1] = { 1 };
+
+  string data = string(&id[0], &id[1])+"d5:added12:"+
+    string(&c1[0], &c1[6])+string(&c2[0], &c2[6])+
+    "7:added.f2:207:dropped12:"+
+    string(&c3[0], &c3[6])+string(&c4[0], &c4[6])+
+    "e";
+  
+  UTPexExtensionMessageHandle msg =
+    UTPexExtensionMessage::create(_btContext, data.c_str(), data.size());
+  CPPUNIT_ASSERT_EQUAL((uint8_t)1, msg->getExtensionMessageID());
+  CPPUNIT_ASSERT_EQUAL((size_t)2, msg->getFreshPeers().size());
+  CPPUNIT_ASSERT_EQUAL(string("192.168.0.1"), msg->getFreshPeers()[0]->ipaddr);
+  CPPUNIT_ASSERT_EQUAL((uint16_t)6881, msg->getFreshPeers()[0]->port);
+  CPPUNIT_ASSERT_EQUAL(string("10.1.1.2"), msg->getFreshPeers()[1]->ipaddr);
+  CPPUNIT_ASSERT_EQUAL((uint16_t)9999, msg->getFreshPeers()[1]->port);
+  CPPUNIT_ASSERT_EQUAL((size_t)2, msg->getDroppedPeers().size());
+  CPPUNIT_ASSERT_EQUAL(string("192.168.0.2"), msg->getDroppedPeers()[0]->ipaddr);
+  CPPUNIT_ASSERT_EQUAL((uint16_t)6882, msg->getDroppedPeers()[0]->port);
+  CPPUNIT_ASSERT_EQUAL(string("10.1.1.3"), msg->getDroppedPeers()[1]->ipaddr);
+  CPPUNIT_ASSERT_EQUAL((uint16_t)10000, msg->getDroppedPeers()[1]->port);
+  try {
+    // 0 length data
+    string in = "";
+    UTPexExtensionMessage::create(_btContext, in.c_str(), in.size());
+    CPPUNIT_FAIL("exception must be thrown.");
+  } catch(Exception* e) {
+    cerr << *e << endl;
+    delete e;
+  }    
+}

+ 1 - 1
test/test.torrent

@@ -1 +1 @@
-d8:announce36:http://aria.rednoah.com/announce.php13:announce-listll16:http://tracker1 el15:http://tracker2el15:http://tracker3ee7:comment17:REDNOAH.COM RULES13:creation datei1123456789e4:infod5:filesld6:lengthi284e4:pathl5:aria23:src6:aria2ceed6:lengthi100e4:pathl19:aria2-0.2.2.tar.bz2eee4:name10:aria2-test12:piece lengthi128e6:pieces60:AAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCee
+d8:announce36:http://aria.rednoah.com/announce.php13:announce-listll16:http://tracker1 el15:http://tracker2el15:http://tracker3ee7:privatei1e7:comment17:REDNOAH.COM RULES13:creation datei1123456789e4:infod5:filesld6:lengthi284e4:pathl5:aria23:src6:aria2ceed6:lengthi100e4:pathl19:aria2-0.2.2.tar.bz2eee4:name10:aria2-test12:piece lengthi128e6:pieces60:AAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCee