Pārlūkot izejas kodu

2010-02-20 Tatsuhiro Tsujikawa <t-tujikawa@users.sourceforge.net>

	Added Local Peer Discovery. It is disabled by default. Use
	--bt-enable-lpd to enable the function.
	* src/BtConstants.h
	* src/BtSetup.cc
	* src/LpdDispatchMessageCommand.cc
	* src/LpdDispatchMessageCommand.h
	* src/LpdMessage.h
	* src/LpdMessageDispatcher.cc
	* src/LpdMessageDispatcher.h
	* src/LpdMessageReceiver.cc
	* src/LpdMessageReceiver.h
	* src/LpdReceiveMessageCommand.cc
	* src/LpdReceiveMessageCommand.h
	* src/Makefile.am
	* src/OptionHandlerFactory.cc
	* src/Peer.cc
	* src/Peer.h
	* src/PeerInteractionCommand.cc
	* src/SocketCore.cc
	* src/SocketCore.h
	* src/prefs.cc
	* src/prefs.h
	* src/usage_text.h
	* src/util.cc
	* src/util.h
	* test/LpdMessageDispatcherTest.cc
	* test/LpdMessageReceiverTest.cc
	* test/Makefile.am
Tatsuhiro Tsujikawa 15 gadi atpakaļ
vecāks
revīzija
9281f11264

+ 31 - 0
ChangeLog

@@ -1,3 +1,34 @@
+2010-02-20  Tatsuhiro Tsujikawa  <t-tujikawa@users.sourceforge.net>
+
+	Added Local Peer Discovery. It is disabled by default. Use
+	--bt-enable-lpd to enable the function.
+	* src/BtConstants.h
+	* src/BtSetup.cc
+	* src/LpdDispatchMessageCommand.cc
+	* src/LpdDispatchMessageCommand.h
+	* src/LpdMessage.h
+	* src/LpdMessageDispatcher.cc
+	* src/LpdMessageDispatcher.h
+	* src/LpdMessageReceiver.cc
+	* src/LpdMessageReceiver.h
+	* src/LpdReceiveMessageCommand.cc
+	* src/LpdReceiveMessageCommand.h
+	* src/Makefile.am
+	* src/OptionHandlerFactory.cc
+	* src/Peer.cc
+	* src/Peer.h
+	* src/PeerInteractionCommand.cc
+	* src/SocketCore.cc
+	* src/SocketCore.h
+	* src/prefs.cc
+	* src/prefs.h
+	* src/usage_text.h
+	* src/util.cc
+	* src/util.h
+	* test/LpdMessageDispatcherTest.cc
+	* test/LpdMessageReceiverTest.cc
+	* test/Makefile.am
+
 2010-02-20  Tatsuhiro Tsujikawa  <t-tujikawa@users.sourceforge.net>
 
 	User-defined custom request headers specified by --header option

+ 4 - 0
src/BtConstants.h

@@ -58,4 +58,8 @@ typedef std::map<std::string, uint8_t> Extensions;
 
 #define METADATA_PIECE_SIZE (16*1024)
 
+#define LPD_MULTICAST_ADDR "239.192.152.143"
+
+#define LPD_MULTICAST_PORT 6771
+
 #endif // _D_BT_CONSTANTS_

+ 39 - 0
src/BtSetup.cc

@@ -60,6 +60,12 @@
 #include "BtRuntime.h"
 #include "bittorrent_helper.h"
 #include "BtStopDownloadCommand.h"
+#include "LpdReceiveMessageCommand.h"
+#include "LpdDispatchMessageCommand.h"
+#include "LpdMessageReceiver.h"
+#include "LpdMessageDispatcher.h"
+#include "message.h"
+#include "SocketCore.h"
 
 namespace aria2 {
 
@@ -164,6 +170,39 @@ void BtSetup::setup(std::deque<Command*>& commands,
     PeerListenCommand* listenCommand = PeerListenCommand::getInstance(e);
     btRuntime->setListenPort(listenCommand->getPort());
   }
+  if(option->getAsBool(PREF_BT_ENABLE_LPD) &&
+     (metadataGetMode || torrentAttrs[bittorrent::PRIVATE].i() == 0)) {
+    if(LpdReceiveMessageCommand::getNumInstance() == 0) {
+      SharedHandle<LpdMessageReceiver> receiver
+        (new LpdMessageReceiver(LPD_MULTICAST_ADDR, LPD_MULTICAST_PORT));
+      try {
+        receiver->init();
+        receiver->getSocket()->setMulticastTtl(1);
+      } catch(RecoverableException& e) {
+        _logger->info(EX_EXCEPTION_CAUGHT, e);
+        receiver.reset();
+      }
+      if(!receiver.isNull()) {
+        LpdReceiveMessageCommand* cmd =
+          LpdReceiveMessageCommand::getInstance(e, receiver);
+        e->commands.push_back(cmd);
+      }
+    }
+    if(LpdReceiveMessageCommand::getNumInstance()) {
+      const unsigned char* infoHash =
+        bittorrent::getInfoHash(requestGroup->getDownloadContext());
+      SharedHandle<LpdMessageDispatcher> dispatcher
+        (new LpdMessageDispatcher
+         (std::string(&infoHash[0], &infoHash[INFO_HASH_LENGTH]),
+          btRuntime->getListenPort(),
+          LPD_MULTICAST_ADDR, LPD_MULTICAST_PORT,
+          LpdReceiveMessageCommand::getInstance()->getReceiverSocket()));
+      LpdDispatchMessageCommand* cmd =
+        new LpdDispatchMessageCommand(e->newCUID(), dispatcher, e);
+      cmd->setBtRuntime(btRuntime);
+      e->commands.push_back(cmd);
+    }
+  }
   time_t btStopTimeout = option->getAsInt(PREF_BT_STOP_TIMEOUT);
   if(btStopTimeout > 0) {
     BtStopDownloadCommand* stopDownloadCommand =

+ 88 - 0
src/LpdDispatchMessageCommand.cc

@@ -0,0 +1,88 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2010 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 "LpdDispatchMessageCommand.h"
+#include "LpdMessageDispatcher.h"
+#include "DownloadEngine.h"
+#include "BtRuntime.h"
+#include "Logger.h"
+#include "RecoverableException.h"
+#include "SocketCore.h"
+#include "util.h"
+
+namespace aria2 {
+
+LpdDispatchMessageCommand::LpdDispatchMessageCommand
+(int cuid,
+ const SharedHandle<LpdMessageDispatcher>& dispatcher,
+ DownloadEngine* e):
+  Command(cuid),
+  _dispatcher(dispatcher),
+  _e(e),
+  _tryCount(0) {}
+
+bool LpdDispatchMessageCommand::execute()
+{
+  if(_btRuntime->isHalt()) {
+    return true;
+  }
+  if(_dispatcher->isAnnounceReady()) {
+    try {
+      logger->info("Dispatching LPD message for infohash=%s",
+                   util::toHex(_dispatcher->getInfoHash()).c_str());
+      if(_dispatcher->sendMessage()) {
+        logger->info("Sending LPD message is complete.");
+        _dispatcher->resetAnnounceTimer();
+        _tryCount = 0;
+      } else {
+        ++_tryCount;
+        if(_tryCount >= 5) {
+          logger->info("Sending LPD message %u times but all failed.");
+          _dispatcher->resetAnnounceTimer();
+          _tryCount = 0;
+        } else {
+          logger->info("Could not send LPD message, retry shortly.");
+        }
+      }
+    } catch(RecoverableException& e) {
+      logger->info("Failed to send LPD message.", e);
+      _dispatcher->resetAnnounceTimer();
+      _tryCount = 0;
+    }
+  }
+  _e->commands.push_back(this);
+  return false;
+}
+
+} // namespace aria2

+ 69 - 0
src/LpdDispatchMessageCommand.h

@@ -0,0 +1,69 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2010 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_LPD_DISPATCH_MESSAGE_COMMAND_H_
+#define _D_LPD_DISPATCH_MESSAGE_COMMAND_H_
+
+#include "Command.h"
+#include "SharedHandle.h"
+
+namespace aria2 {
+
+class LpdMessageDispatcher;
+class DownloadEngine;
+class BtRuntime;
+
+class LpdDispatchMessageCommand:public Command {
+private:
+  SharedHandle<LpdMessageDispatcher> _dispatcher;
+  DownloadEngine* _e;
+  unsigned int _tryCount;
+  SharedHandle<BtRuntime> _btRuntime;
+public:
+  LpdDispatchMessageCommand
+  (int cuid,
+   const SharedHandle<LpdMessageDispatcher>& dispatcher,
+   DownloadEngine* e);
+
+  virtual bool execute();
+
+  void setBtRuntime(const SharedHandle<BtRuntime>& btRuntime)
+  {
+    _btRuntime = btRuntime;
+  }
+};
+
+} // namespace aria2
+
+#endif // _D_LPD_DISPATCH_MESSAGE_COMMAND_H_

+ 65 - 0
src/LpdMessage.h

@@ -0,0 +1,65 @@
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2010 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.
+ */
+#ifndef _D_LPD_MESSAGE_H_
+#define _D_LPD_MESSAGE_H_
+
+#include "common.h"
+
+#include <string>
+
+#include "Peer.h"
+
+namespace aria2 {
+
+class LpdMessage {
+private:
+  SharedHandle<Peer> _peer;
+  std::string _infoHash;
+public:
+  LpdMessage(const SharedHandle<Peer>& peer, const std::string& infoHash):
+    _peer(peer), _infoHash(infoHash) {}
+
+  const SharedHandle<Peer>& getPeer() const
+  {
+    return _peer;
+  }
+
+  const std::string& getInfoHash() const
+  {
+    return _infoHash;
+  }
+};
+
+} // namespace aria2
+
+#endif // _D_LPD_MESSAGE_H_

+ 95 - 0
src/LpdMessageDispatcher.cc

@@ -0,0 +1,95 @@
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2010 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.
+ */
+#include "LpdMessageDispatcher.h"
+#include "SocketCore.h"
+#include "A2STR.h"
+#include "util.h"
+#include "Logger.h"
+#include "LogFactory.h"
+#include "BtConstants.h"
+#include "RecoverableException.h"
+
+namespace aria2 {
+
+LpdMessageDispatcher::LpdMessageDispatcher
+(const std::string& infoHash, uint16_t port,
+ const std::string& multicastAddress, uint16_t multicastPort,
+ const SharedHandle<SocketCore>& socket,
+ time_t interval):
+  _infoHash(infoHash),
+  _port(port),
+  _socket(socket),
+  _multicastAddress(multicastAddress),
+  _multicastPort(multicastPort),
+  _timer(0),
+  _interval(interval),
+  _request(bittorrent::createLpdRequest(_multicastAddress, _multicastPort,
+                                        _infoHash, _port)),
+  _logger(LogFactory::getInstance()) {}
+
+bool LpdMessageDispatcher::sendMessage()
+{
+  return
+    _socket->writeData(_request.c_str(), _request.size(),
+                       _multicastAddress, _multicastPort)
+    == (ssize_t)_request.size();
+}
+
+bool LpdMessageDispatcher::isAnnounceReady() const
+{
+  return _timer.elapsed(_interval);
+}
+
+void LpdMessageDispatcher::resetAnnounceTimer()
+{
+  _timer.reset();
+}
+
+namespace bittorrent {
+
+std::string createLpdRequest
+(const std::string& multicastAddress, uint16_t multicastPort,
+ const std::string& infoHash, uint16_t port)
+{
+  std::string req = "BT-SEARCH * HTTP/1.1\r\n";
+  strappend(req, "Host: ", multicastAddress, A2STR::COLON_C,
+            util::uitos(multicastPort), A2STR::CRLF);
+  strappend(req, "Port: ", util::uitos(port), A2STR::CRLF);
+  strappend(req, "Infohash: ", util::toHex(infoHash), A2STR::CRLF);
+  req += "\r\n\r\n";
+  return req;
+}
+
+} // namespace bittorrent
+
+} // namespac aria2

+ 98 - 0
src/LpdMessageDispatcher.h

@@ -0,0 +1,98 @@
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2010 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.
+ */
+#ifndef _D_LPD_MESSAGE_DISPATCHER_H_
+#define _D_LPD_MESSAGE_DISPATCHER_H_
+
+#include "common.h"
+
+#include <string>
+
+#include "SharedHandle.h"
+#include "TimeA2.h"
+
+namespace aria2 {
+
+class SocketCore;
+class Logger;
+
+class LpdMessageDispatcher {
+private:
+  std::string _infoHash;
+  uint16_t _port;
+  SharedHandle<SocketCore> _socket;
+  std::string _multicastAddress;
+  uint16_t _multicastPort;
+  Time _timer;
+  time_t _interval;
+  std::string _request;
+  Logger* _logger;
+public:
+  LpdMessageDispatcher
+  (const std::string& infoHash, uint16_t port,
+   const std::string& multicastAddr, uint16_t multicastPort,
+   const SharedHandle<SocketCore>& socket,
+   time_t interval = 5*60);
+
+  // Returns true if _timer reached announce interval, which is by
+  // default 5mins.
+  bool isAnnounceReady() const;
+
+  // Sends LPD message. If message is sent returns true. Otherwise
+  // returns false.
+  bool sendMessage();
+
+  // Reset _timer to the current time.
+  void resetAnnounceTimer();
+
+  const std::string& getInfoHash() const
+  {
+    return _infoHash;
+  }
+
+  uint16_t getPort() const
+  {
+    return _port;
+  }
+};
+
+namespace bittorrent {
+
+std::string createLpdRequest
+(const std::string& multicastAddress, uint16_t multicastPort,
+ const std::string& infoHash, uint16_t port);
+
+} // namespace bittorrent
+
+} // namespace aria2
+
+#endif // _D_LPD_MESSAGE_DISPATCHER_H_

+ 107 - 0
src/LpdMessageReceiver.cc

@@ -0,0 +1,107 @@
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2010 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.
+ */
+#include "LpdMessageReceiver.h"
+#include "SocketCore.h"
+#include "Logger.h"
+#include "LogFactory.h"
+#include "HttpHeaderProcessor.h"
+#include "HttpHeader.h"
+#include "util.h"
+#include "LpdMessage.h"
+#include "RecoverableException.h"
+
+namespace aria2 {
+
+LpdMessageReceiver::LpdMessageReceiver
+(const std::string& multicastAddress, uint16_t multicastPort):
+  _multicastAddress(multicastAddress),
+  _multicastPort(multicastPort),
+  _logger(LogFactory::getInstance()) {}
+
+bool LpdMessageReceiver::init()
+{
+  try {
+    _socket.reset(new SocketCore(SOCK_DGRAM));
+    // SocketCore::bind(port, flags) cannot be used here, because it
+    // is affected by --interface option.
+    _socket->bindWithFamily(_multicastPort, AF_INET);
+    _socket->joinMulticastGroup(_multicastAddress, _multicastPort);
+    _socket->setNonBlockingMode();
+    _logger->info("Listening multicast group (%s:%u) packet",
+                  _multicastAddress.c_str(), _multicastPort);
+    return true;
+  } catch(RecoverableException& e) {
+    _logger->error("Failed to initialize LPD message receiver.", e);
+  }
+  return false;
+}
+
+SharedHandle<LpdMessage> LpdMessageReceiver::receiveMessage()
+{
+  SharedHandle<LpdMessage> msg;
+  try {
+    unsigned char buf[200];
+    std::pair<std::string, uint16_t> peerAddr;
+    ssize_t length = _socket->readDataFrom(buf, sizeof(buf), peerAddr);
+    if(length == 0) {
+      return msg;
+    }
+    HttpHeaderProcessor proc;
+    proc.update(buf, length);
+    if(!proc.eoh()) {
+      return msg;
+    }
+    SharedHandle<HttpHeader> header = proc.getHttpRequestHeader();
+    std::string infoHashString = header->getFirst("Infohash");
+    uint16_t port = header->getFirstAsUInt("Port");
+    _logger->info("LPD message received infohash=%s, port=%u from %s",
+                  infoHashString.c_str(), port, peerAddr.first.c_str());
+    std::string infoHash;
+    if(infoHashString.size() != 40 ||
+       (infoHash = util::fromHex(infoHashString)).empty() ||
+       port == 0) {
+      _logger->info("LPD bad request. infohash=%s", infoHashString.c_str());
+      return msg;
+    }
+    SharedHandle<Peer> peer(new Peer(peerAddr.first, port, false));
+    if(util::inPrivateAddress(peerAddr.first)) {
+      peer->setLocalPeer(true);
+    }
+    msg.reset(new LpdMessage(peer, infoHash));
+  } catch(RecoverableException& e) {
+    _logger->info("Failed to receive LPD message.", e);
+  }
+  return msg;
+}
+
+} // namespace aria2

+ 74 - 0
src/LpdMessageReceiver.h

@@ -0,0 +1,74 @@
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2010 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.
+ */
+#ifndef _LPD_MESSAGE_RECEIVER_H_
+#define _LPD_MESSAGE_RECEIVER_H_
+
+#include "common.h"
+
+#include <string>
+
+#include "SharedHandle.h"
+
+namespace aria2 {
+
+class SocketCore;
+class Logger;
+class LpdMessage;
+
+class LpdMessageReceiver {
+private:
+  SharedHandle<SocketCore> _socket;
+  std::string _multicastAddress;
+  uint16_t _multicastPort;
+  Logger* _logger;
+public:
+  // Currently only IPv4 multicastAddresses are supported.
+  LpdMessageReceiver
+  (const std::string& multicastAddress, uint16_t multicastPort);
+
+  // No throw.
+  bool init();
+
+  // Receives LPD message and process it.  Returns false if message is
+  // not available.
+  SharedHandle<LpdMessage> receiveMessage();
+
+  SharedHandle<SocketCore> getSocket() const
+  {
+    return _socket;
+  }
+};
+
+} // namespace aria2
+
+#endif // _LPD_MESSAGE_RECEIVER_H_

+ 154 - 0
src/LpdReceiveMessageCommand.cc

@@ -0,0 +1,154 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2010 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 "LpdReceiveMessageCommand.h"
+#include "DownloadEngine.h"
+#include "SocketCore.h"
+#include "LpdMessageReceiver.h"
+#include "RequestGroupMan.h"
+#include "DownloadContext.h"
+#include "PeerStorage.h"
+#include "Peer.h"
+#include "RequestGroup.h"
+#include "BtRegistry.h"
+#include "Logger.h"
+#include "PieceStorage.h"
+#include "BtRuntime.h"
+#include "BtProgressInfoFile.h"
+#include "BtAnnounce.h"
+#include "LpdMessage.h"
+#include "bittorrent_helper.h"
+
+namespace aria2 {
+
+unsigned int LpdReceiveMessageCommand::__numInstance = 0;
+
+LpdReceiveMessageCommand* LpdReceiveMessageCommand::__instance = 0;
+
+LpdReceiveMessageCommand::LpdReceiveMessageCommand
+(int32_t cuid, const SharedHandle<LpdMessageReceiver>& receiver,
+ DownloadEngine* e):Command(cuid), _receiver(receiver), _e(e)
+{
+  _e->addSocketForReadCheck(_receiver->getSocket(), this);
+  ++__numInstance;
+}
+
+LpdReceiveMessageCommand::~LpdReceiveMessageCommand()
+{
+  _e->deleteSocketForReadCheck(_receiver->getSocket(), this);
+  --__numInstance;
+  if(__numInstance == 0) {
+    __instance = 0;
+  }
+}
+
+bool LpdReceiveMessageCommand::execute()
+{
+  if(_e->_requestGroupMan->downloadFinished() || _e->isHaltRequested()) {
+    return true;
+  }
+  for(size_t i = 0; i < 20; ++i) {
+    SharedHandle<LpdMessage> m = _receiver->receiveMessage();
+    if(m.isNull()) {
+      break;
+    }
+    SharedHandle<BtRegistry> reg = _e->getBtRegistry();
+    SharedHandle<DownloadContext> dctx =
+      reg->getDownloadContext(m->getInfoHash());
+    if(dctx.isNull()) {
+      if(logger->debug()) {
+        logger->debug("Download Context is null for infohash=%s.",
+                      util::toHex(m->getInfoHash()).c_str());
+      }
+      continue;
+    }
+    const BDE& torrentAttrs = dctx->getAttribute(bittorrent::BITTORRENT);
+    if(torrentAttrs.containsKey(bittorrent::PRIVATE)) {
+      if(torrentAttrs[bittorrent::PRIVATE].i() == 1) {
+        if(logger->debug()) {
+          logger->debug("Ignore LPD message because the torrent is private.");
+        }
+        continue;
+      }
+    }
+    RequestGroup* group = dctx->getOwnerRequestGroup();
+    assert(group);
+    BtObject btobj = reg->get(group->getGID());
+    assert(!btobj.isNull());
+    SharedHandle<PeerStorage> peerStorage = btobj._peerStorage;
+    assert(!peerStorage.isNull());
+    SharedHandle<Peer> peer = m->getPeer();
+    if(peerStorage->addPeer(peer)) {
+      if(logger->debug()) {
+        logger->debug("LPD peer %s:%u local=%d added.",
+                      peer->ipaddr.c_str(), peer->port,
+                      peer->isLocalPeer()?1:0);
+      }
+    } else {
+      if(logger->debug()) {
+        logger->debug("LPD peer %s:%u local=%d not added.",
+                      peer->ipaddr.c_str(), peer->port,
+                      peer->isLocalPeer()?1:0);
+      }
+    }
+  }
+  _e->commands.push_back(this);
+  return false;
+}
+
+SharedHandle<SocketCore> LpdReceiveMessageCommand::getReceiverSocket() const
+{
+  return _receiver->getSocket();
+}
+
+LpdReceiveMessageCommand*
+LpdReceiveMessageCommand::getInstance
+(DownloadEngine* e, const SharedHandle<LpdMessageReceiver>& receiver)
+{
+  if(__numInstance == 0) {
+    __instance = new LpdReceiveMessageCommand(e->newCUID(), receiver, e);
+  }
+  return __instance;
+}
+
+LpdReceiveMessageCommand* LpdReceiveMessageCommand::getInstance()
+{
+  if(__numInstance == 0) {
+    return 0;
+  } else {
+    return __instance;
+  }
+}
+
+} // namespace aria2

+ 84 - 0
src/LpdReceiveMessageCommand.h

@@ -0,0 +1,84 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2010 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_LPD_RECEIVE_MESSAGE_COMMAND_H_
+#define _D_LPD_RECEIVE_MESSAGE_COMMAND_H_
+
+#include "Command.h"
+#include "SharedHandle.h"
+
+namespace aria2 {
+
+class LpdMessageReceiver;
+class DownloadEngine;
+class SocketCore;
+
+class LpdReceiveMessageCommand:public Command {
+private:
+  SharedHandle<LpdMessageReceiver> _receiver;
+
+  static unsigned int __numInstance;
+
+  static LpdReceiveMessageCommand* __instance;
+
+  LpdReceiveMessageCommand
+  (int32_t cuid, const SharedHandle<LpdMessageReceiver>& receiver,
+   DownloadEngine* e);
+protected:
+  DownloadEngine* _e;
+
+public:
+  virtual ~LpdReceiveMessageCommand();
+
+  virtual bool execute();
+
+  SharedHandle<SocketCore> getReceiverSocket() const;
+
+  static LpdReceiveMessageCommand*
+  getInstance
+  (DownloadEngine* e, const SharedHandle<LpdMessageReceiver>& receiver);
+
+  // If __numInstance is 0, then return 0. If __numInstance > 0, it
+  // returns __instance
+  static LpdReceiveMessageCommand* getInstance();
+
+  static unsigned int getNumInstance()
+  {
+    return __numInstance;
+  }
+};
+
+} // namespace aria2
+
+#endif // _D_LPD_RECEIVE_MESSAGE_COMMAND_H_

+ 6 - 1
src/Makefile.am

@@ -440,7 +440,12 @@ SRCS += PeerAbstractCommand.cc PeerAbstractCommand.h\
 	bencode.cc bencode.h\
 	bittorrent_helper.cc bittorrent_helper.h\
 	BtStopDownloadCommand.cc BtStopDownloadCommand.h\
-	PriorityPieceSelector.cc PriorityPieceSelector.h
+	PriorityPieceSelector.cc PriorityPieceSelector.h\
+	LpdMessageDispatcher.cc LpdMessageDispatcher.h\
+	LpdMessageReceiver.cc LpdMessageReceiver.h\
+	LpdMessage.h\
+	LpdReceiveMessageCommand.cc LpdReceiveMessageCommand.h\
+	LpdDispatchMessageCommand.cc LpdDispatchMessageCommand.h
 endif # ENABLE_BITTORRENT
 
 if ENABLE_METALINK

+ 21 - 4
src/Makefile.in

@@ -243,7 +243,12 @@ bin_PROGRAMS = aria2c$(EXEEXT)
 @ENABLE_BITTORRENT_TRUE@	bencode.cc bencode.h\
 @ENABLE_BITTORRENT_TRUE@	bittorrent_helper.cc bittorrent_helper.h\
 @ENABLE_BITTORRENT_TRUE@	BtStopDownloadCommand.cc BtStopDownloadCommand.h\
-@ENABLE_BITTORRENT_TRUE@	PriorityPieceSelector.cc PriorityPieceSelector.h
+@ENABLE_BITTORRENT_TRUE@	PriorityPieceSelector.cc PriorityPieceSelector.h\
+@ENABLE_BITTORRENT_TRUE@	LpdMessageDispatcher.cc LpdMessageDispatcher.h\
+@ENABLE_BITTORRENT_TRUE@	LpdMessageReceiver.cc LpdMessageReceiver.h\
+@ENABLE_BITTORRENT_TRUE@	LpdMessage.h\
+@ENABLE_BITTORRENT_TRUE@	LpdReceiveMessageCommand.cc LpdReceiveMessageCommand.h\
+@ENABLE_BITTORRENT_TRUE@	LpdDispatchMessageCommand.cc LpdDispatchMessageCommand.h
 
 @ENABLE_METALINK_TRUE@am__append_14 = Metalinker.cc Metalinker.h\
 @ENABLE_METALINK_TRUE@	MetalinkEntry.cc MetalinkEntry.h\
@@ -570,8 +575,12 @@ am__libaria2c_a_SOURCES_DIST = Socket.h SocketCore.cc SocketCore.h \
 	IndexBtMessageValidator.h ExtensionMessageRegistry.h \
 	bencode.cc bencode.h bittorrent_helper.cc bittorrent_helper.h \
 	BtStopDownloadCommand.cc BtStopDownloadCommand.h \
-	PriorityPieceSelector.cc PriorityPieceSelector.h Metalinker.cc \
-	Metalinker.h MetalinkEntry.cc MetalinkEntry.h \
+	PriorityPieceSelector.cc PriorityPieceSelector.h \
+	LpdMessageDispatcher.cc LpdMessageDispatcher.h \
+	LpdMessageReceiver.cc LpdMessageReceiver.h LpdMessage.h \
+	LpdReceiveMessageCommand.cc LpdReceiveMessageCommand.h \
+	LpdDispatchMessageCommand.cc LpdDispatchMessageCommand.h \
+	Metalinker.cc Metalinker.h MetalinkEntry.cc MetalinkEntry.h \
 	MetalinkResource.cc MetalinkResource.h MetalinkProcessor.h \
 	MetalinkParserController.cc MetalinkParserController.h \
 	MetalinkParserStateMachine.cc MetalinkParserStateMachine.h \
@@ -734,7 +743,11 @@ am__objects_6 =
 @ENABLE_BITTORRENT_TRUE@	bencode.$(OBJEXT) \
 @ENABLE_BITTORRENT_TRUE@	bittorrent_helper.$(OBJEXT) \
 @ENABLE_BITTORRENT_TRUE@	BtStopDownloadCommand.$(OBJEXT) \
-@ENABLE_BITTORRENT_TRUE@	PriorityPieceSelector.$(OBJEXT)
+@ENABLE_BITTORRENT_TRUE@	PriorityPieceSelector.$(OBJEXT) \
+@ENABLE_BITTORRENT_TRUE@	LpdMessageDispatcher.$(OBJEXT) \
+@ENABLE_BITTORRENT_TRUE@	LpdMessageReceiver.$(OBJEXT) \
+@ENABLE_BITTORRENT_TRUE@	LpdReceiveMessageCommand.$(OBJEXT) \
+@ENABLE_BITTORRENT_TRUE@	LpdDispatchMessageCommand.$(OBJEXT)
 @ENABLE_METALINK_TRUE@am__objects_14 = Metalinker.$(OBJEXT) \
 @ENABLE_METALINK_TRUE@	MetalinkEntry.$(OBJEXT) \
 @ENABLE_METALINK_TRUE@	MetalinkResource.$(OBJEXT) \
@@ -1465,6 +1478,10 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LogFactory.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Logger.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LongestSequencePieceSelector.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LpdDispatchMessageCommand.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LpdMessageDispatcher.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LpdMessageReceiver.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LpdReceiveMessageCommand.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MSEHandshake.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MemoryBufferPreDownloadHandler.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MessageDigestHelper.Po@am__quote@

+ 9 - 0
src/OptionHandlerFactory.cc

@@ -942,6 +942,15 @@ OptionHandlers OptionHandlerFactory::createOptionHandlers()
 #endif // ENABLE_BITTORRENT || ENABLE_METALINK
   // BitTorrent Specific Options
 #ifdef ENABLE_BITTORRENT
+  {
+    SharedHandle<OptionHandler> op(new BooleanOptionHandler
+                                   (PREF_BT_ENABLE_LPD,
+                                    TEXT_BT_ENABLE_LPD,
+                                    V_FALSE,
+                                    OptionHandler::OPT_ARG));
+    op->addTag(TAG_BITTORRENT);
+    handlers.push_back(op);
+  }
   {
     SharedHandle<OptionHandler> op(new DefaultOptionHandler
                                    (PREF_BT_EXTERNAL_IP,

+ 2 - 1
src/Peer.cc

@@ -55,7 +55,8 @@ Peer::Peer(std::string ipaddr, uint16_t port, bool incoming):
   _badConditionStartTime(0),
   _seeder(false),
   _res(0),
-  _incoming(incoming)
+  _incoming(incoming),
+  _localPeer(false)
 {
   memset(_peerId, 0, PEER_ID_LENGTH);
   resetStatus();

+ 13 - 0
src/Peer.h

@@ -78,6 +78,9 @@ private:
   // If true, port is assumed not to be a listening port.
   bool _incoming;
 
+  // If true, this peer is from local network.
+  bool _localPeer;
+
   // Before calling updateSeeder(),  make sure that
   // allocateSessionResource() is called and _res is created.
   // Otherwise assertion fails.
@@ -282,6 +285,16 @@ public:
 
   void setIncomingPeer(bool incoming);
 
+  bool isLocalPeer() const
+  {
+    return _localPeer;
+  }
+
+  void setLocalPeer(bool flag)
+  {
+    _localPeer = flag;
+  }
+
   void setBtMessageDispatcher(const WeakHandle<BtMessageDispatcher>& dpt);
 
   size_t countOutstandingUpload() const;

+ 2 - 1
src/PeerInteractionCommand.cc

@@ -189,7 +189,8 @@ PeerInteractionCommand::PeerInteractionCommand
     (getOption()->getAsInt(PREF_BT_KEEP_ALIVE_INTERVAL));
   btInteractive->setRequestGroupMan(e->_requestGroupMan);
   btInteractive->setBtMessageFactory(factory);
-  if(metadataGetMode || torrentAttrs[bittorrent::PRIVATE].i() == 0) {
+  if((metadataGetMode || torrentAttrs[bittorrent::PRIVATE].i() == 0) &&
+     !peer->isLocalPeer()) {
     if(getOption()->getAsBool(PREF_ENABLE_PEER_EXCHANGE)) {
       btInteractive->setUTPexEnabled(true);
     }

+ 34 - 0
src/SocketCore.cc

@@ -253,6 +253,18 @@ static sock_t bindTo
   return -1;
 }
 
+void SocketCore::bindWithFamily(uint16_t port, int family, int flags)
+{
+  closeConnection();
+  std::string error;
+  sock_t fd = bindTo(0, port, family, _sockType, flags, error);
+  if(fd == (sock_t) -1) {
+    throw DL_ABORT_EX(StringFormat(EX_SOCKET_BIND, error.c_str()).str());
+  } else {
+    sockfd = fd;
+  }
+}
+
 void SocketCore::bind(uint16_t port, int flags)
 {
   closeConnection();
@@ -403,6 +415,28 @@ void SocketCore::establishConnection(const std::string& host, uint16_t port)
   }
 }
 
+void SocketCore::setSockOpt
+(int level, int optname, void* optval, socklen_t optlen)
+{
+  if(setsockopt(sockfd, level, optname, optval, optlen) < 0) {
+    throw DL_ABORT_EX(StringFormat(EX_SOCKET_SET_OPT, errorMsg()).str());
+  }
+}   
+
+void SocketCore::setMulticastTtl(unsigned char ttl)
+{
+  setSockOpt(IPPROTO_IP, IP_MULTICAST_TTL, (a2_sockopt_t)&ttl, sizeof(ttl));
+}
+
+void SocketCore::joinMulticastGroup(const std::string& ipaddr, uint16_t port)
+{
+  struct ip_mreq mreq;
+  memset(&mreq, 0, sizeof(mreq));
+  mreq.imr_multiaddr.s_addr = inet_addr(ipaddr.c_str());
+  mreq.imr_interface.s_addr = htonl(INADDR_ANY);
+  setSockOpt(IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
+}
+
 void SocketCore::setNonBlockingMode()
 {
 #ifdef __MINGW32__

+ 8 - 0
src/SocketCore.h

@@ -135,6 +135,8 @@ private:
 
 #endif // HAVE_EPOLL
 
+  void setSockOpt(int level, int optname, void* optval, socklen_t optlen);
+
   SocketCore(sock_t sockfd, int sockType);
 public:
   SocketCore(int sockType = SOCK_STREAM);
@@ -144,6 +146,12 @@ public:
 
   bool isOpen() const { return sockfd != (sock_t) -1; }
 
+  void setMulticastTtl(unsigned char ttl);
+
+  void joinMulticastGroup(const std::string& ipaddr, uint16_t port);
+  
+  void bindWithFamily(uint16_t port, int family, int flags = AI_PASSIVE);
+
   /**
    * Creates a socket and bind it with locahost's address and port.
    * flags is set to struct addrinfo's ai_flags.

+ 2 - 0
src/prefs.cc

@@ -316,6 +316,8 @@ const std::string PREF_BT_PRIORITIZE_PIECE("bt-prioritize-piece");
 const std::string PREF_BT_SAVE_METADATA("bt-save-metadata");
 // values: true | false
 const std::string PREF_BT_METADATA_ONLY("bt-metadata-only");
+// values: true | false
+const std::string PREF_BT_ENABLE_LPD("bt-enable-lpd");
 
 /**
  * Metalink related preferences

+ 2 - 0
src/prefs.h

@@ -320,6 +320,8 @@ extern const std::string PREF_BT_PRIORITIZE_PIECE;
 extern const std::string PREF_BT_SAVE_METADATA;
 // values: true | false
 extern const std::string PREF_BT_METADATA_ONLY;
+// values: true | false
+extern const std::string PREF_BT_ENABLE_LPD;
 
 /**
  * Metalink related preferences

+ 2 - 0
src/usage_text.h

@@ -608,3 +608,5 @@
 #define TEXT_HUMAN_READABLE                     \
   _(" --human-readable[=true|false] Print sizes and speed in human readable format\n" \
     "                              (e.g., 1.2Ki, 3.4Mi) in the console readout.")
+#define TEXT_BT_ENABLE_LPD                      \
+  _(" --bt-enable-lpd[=true|false] Enable Local Peer Discovery.")

+ 22 - 0
src/util.cc

@@ -1172,6 +1172,28 @@ void generateRandomKey(unsigned char* key)
 #endif // !ENABLE_MESSAGE_DIGEST
 }
 
+// Returns true is given numeric ipv4addr is in Private Address Space.
+//
+// From Section.3 RFC1918
+// 10.0.0.0        -   10.255.255.255  (10/8 prefix)
+// 172.16.0.0      -   172.31.255.255  (172.16/12 prefix)
+// 192.168.0.0     -   192.168.255.255 (192.168/16 prefix)
+bool inPrivateAddress(const std::string& ipv4addr)
+{
+  if(util::startsWith(ipv4addr, "10.") ||
+     util::startsWith(ipv4addr, "192.168.")) {
+    return true;
+  }
+  if(util::startsWith(ipv4addr, "172.")) {
+    for(int i = 16; i <= 31; ++i) {
+      if(util::startsWith(ipv4addr, "172."+util::itos(i)+".")) {
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
 } // namespace util
 
 } // namespace aria2

+ 3 - 0
src/util.h

@@ -378,6 +378,9 @@ std::string fixTaintedBasename(const std::string& src);
 // by key.  Caller must allocate at least 20 bytes for generated key.
 void generateRandomKey(unsigned char* key);
 
+// Returns true is given numeric ipv4addr is in Private Address Space.
+bool inPrivateAddress(const std::string& ipv4addr);
+
 } // namespace util
 
 } // namespace aria2

+ 69 - 0
test/LpdMessageDispatcherTest.cc

@@ -0,0 +1,69 @@
+#include "LpdMessageDispatcher.h"
+
+#include <cstring>
+#include <sstream>
+
+#include <cppunit/extensions/HelperMacros.h>
+
+#include "Exception.h"
+#include "util.h"
+#include "LpdMessageDispatcher.h"
+#include "SocketCore.h"
+#include "BtConstants.h"
+
+namespace aria2 {
+
+class LpdMessageDispatcherTest:public CppUnit::TestFixture {
+
+  CPPUNIT_TEST_SUITE(LpdMessageDispatcherTest);
+  CPPUNIT_TEST(testCreateLpdRequest);
+  CPPUNIT_TEST(testSendMessage);
+  CPPUNIT_TEST_SUITE_END();
+public:
+  void testCreateLpdRequest();
+  void testSendMessage();
+};
+
+
+CPPUNIT_TEST_SUITE_REGISTRATION(LpdMessageDispatcherTest);
+
+void LpdMessageDispatcherTest::testCreateLpdRequest()
+{
+  std::string infoHashString = "cd41c7fdddfd034a15a04d7ff881216e01c4ceaf";
+  CPPUNIT_ASSERT_EQUAL
+    (std::string("BT-SEARCH * HTTP/1.1\r\n"
+                 "Host: 239.192.152.143:6771\r\n"
+                 "Port: 6000\r\n"
+                 "Infohash: cd41c7fdddfd034a15a04d7ff881216e01c4ceaf\r\n"
+                 "\r\n\r\n"),
+     bittorrent::createLpdRequest("239.192.152.143", 6771,
+                                  util::fromHex(infoHashString), 6000));
+}
+
+void LpdMessageDispatcherTest::testSendMessage()
+{
+  SharedHandle<SocketCore> recvsock(new SocketCore(SOCK_DGRAM));
+  recvsock->bind(LPD_MULTICAST_PORT);
+  recvsock->joinMulticastGroup(LPD_MULTICAST_ADDR, LPD_MULTICAST_PORT);
+  recvsock->setMulticastTtl(0);
+  LpdMessageDispatcher d("cd41c7fdddfd034a15a04d7ff881216e01c4ceaf", 6000,
+                         LPD_MULTICAST_ADDR, LPD_MULTICAST_PORT,
+                         recvsock);
+
+  CPPUNIT_ASSERT(d.sendMessage());
+
+  unsigned char buf[200];
+
+  std::pair<std::string, uint16_t> peer;
+  ssize_t nbytes = recvsock->readDataFrom(buf, sizeof(buf), peer);
+  buf[nbytes] = '\0';
+  std::stringstream temp;
+  temp << "BT-SEARCH * HTTP/1.1\r\n"
+       << "Host: " << LPD_MULTICAST_ADDR << ":" << LPD_MULTICAST_PORT << "\r\n"
+       << "Port: " << d.getPort() << "\r\n"
+       << "Infohash: " << util::toHex(d.getInfoHash()) << "\r\n"
+       << "\r\n\r\n";
+  CPPUNIT_ASSERT_EQUAL(temp.str(), std::string(&buf[0], &buf[nbytes]));
+}
+
+} // namespace aria2

+ 75 - 0
test/LpdMessageReceiverTest.cc

@@ -0,0 +1,75 @@
+#include "LpdMessageReceiver.h"
+
+#include <cstring>
+
+#include <cppunit/extensions/HelperMacros.h>
+
+#include "Exception.h"
+#include "util.h"
+#include "LpdMessageReceiver.h"
+#include "SocketCore.h"
+#include "BtConstants.h"
+#include "LpdMessage.h"
+#include "LpdMessageDispatcher.h"
+
+namespace aria2 {
+
+class LpdMessageReceiverTest:public CppUnit::TestFixture {
+
+  CPPUNIT_TEST_SUITE(LpdMessageReceiverTest);
+  CPPUNIT_TEST(testReceiveMessage);
+  CPPUNIT_TEST_SUITE_END();
+public:
+  void testReceiveMessage();
+};
+
+
+CPPUNIT_TEST_SUITE_REGISTRATION(LpdMessageReceiverTest);
+
+void LpdMessageReceiverTest::testReceiveMessage()
+{
+  LpdMessageReceiver rcv(LPD_MULTICAST_ADDR, LPD_MULTICAST_PORT);
+  CPPUNIT_ASSERT(rcv.init());
+
+  SharedHandle<SocketCore> sendsock = rcv.getSocket();
+  sendsock->setMulticastTtl(0);
+
+  std::string infoHashString = "cd41c7fdddfd034a15a04d7ff881216e01c4ceaf";
+  std::string infoHash = util::fromHex(infoHashString);
+  std::string request =
+    bittorrent::createLpdRequest(LPD_MULTICAST_ADDR, LPD_MULTICAST_PORT,
+                                 infoHash,
+                                 6000);
+
+  sendsock->writeData(request.c_str(), request.size(),
+                     LPD_MULTICAST_ADDR, LPD_MULTICAST_PORT);
+
+  SharedHandle<LpdMessage> msg = rcv.receiveMessage();
+  CPPUNIT_ASSERT(!msg.isNull());
+  CPPUNIT_ASSERT_EQUAL(std::string("cd41c7fdddfd034a15a04d7ff881216e01c4ceaf"),
+                       util::toHex(msg->getInfoHash()));
+  CPPUNIT_ASSERT_EQUAL((uint16_t)6000, msg->getPeer()->port);
+
+  // Bad infohash
+  std::string badInfoHashString = "cd41c7fdddfd034a15a04d7ff881216e01c4ce";
+  request =
+    bittorrent::createLpdRequest(LPD_MULTICAST_ADDR, LPD_MULTICAST_PORT,
+                                 util::fromHex(badInfoHashString),
+                                 6000);
+  sendsock->writeData(request.c_str(), request.size(),
+                     LPD_MULTICAST_ADDR, LPD_MULTICAST_PORT);
+
+  CPPUNIT_ASSERT(rcv.receiveMessage().isNull());
+
+  // Bad port
+  request =
+    bittorrent::createLpdRequest(LPD_MULTICAST_ADDR, LPD_MULTICAST_PORT,
+                                 infoHash,
+                                 0);
+  sendsock->writeData(request.c_str(), request.size(),
+                     LPD_MULTICAST_ADDR, LPD_MULTICAST_PORT);
+
+  CPPUNIT_ASSERT(rcv.receiveMessage().isNull());
+}
+
+} // namespace aria2

+ 3 - 1
test/Makefile.am

@@ -191,7 +191,9 @@ aria2c_SOURCES += BtAllowedFastMessageTest.cc\
 	BittorrentHelperTest.cc\
 	PriorityPieceSelectorTest.cc\
 	MockPieceSelector.h\
-	extension_message_test_helper.h
+	extension_message_test_helper.h\
+	LpdMessageDispatcherTest.cc\
+	LpdMessageReceiverTest.cc
 endif # ENABLE_BITTORRENT
 
 if ENABLE_METALINK

+ 10 - 3
test/Makefile.in

@@ -141,7 +141,9 @@ check_PROGRAMS = $(am__EXEEXT_1)
 @ENABLE_BITTORRENT_TRUE@	BittorrentHelperTest.cc\
 @ENABLE_BITTORRENT_TRUE@	PriorityPieceSelectorTest.cc\
 @ENABLE_BITTORRENT_TRUE@	MockPieceSelector.h\
-@ENABLE_BITTORRENT_TRUE@	extension_message_test_helper.h
+@ENABLE_BITTORRENT_TRUE@	extension_message_test_helper.h\
+@ENABLE_BITTORRENT_TRUE@	LpdMessageDispatcherTest.cc\
+@ENABLE_BITTORRENT_TRUE@	LpdMessageReceiverTest.cc
 
 @ENABLE_METALINK_TRUE@am__append_7 = MetalinkerTest.cc\
 @ENABLE_METALINK_TRUE@	MetalinkEntryTest.cc\
@@ -266,7 +268,8 @@ am__aria2c_SOURCES_DIST = AllTest.cc TestUtil.cc TestUtil.h \
 	MockExtensionMessage.h MockExtensionMessageFactory.h \
 	MockPieceStorage.h BencodeTest.cc BittorrentHelperTest.cc \
 	PriorityPieceSelectorTest.cc MockPieceSelector.h \
-	extension_message_test_helper.h MetalinkerTest.cc \
+	extension_message_test_helper.h LpdMessageDispatcherTest.cc \
+	LpdMessageReceiverTest.cc MetalinkerTest.cc \
 	MetalinkEntryTest.cc Metalink2RequestGroupTest.cc \
 	MetalinkPostDownloadHandlerTest.cc MetalinkHelperTest.cc \
 	MetalinkParserControllerTest.cc MetalinkProcessorTest.cc
@@ -356,7 +359,9 @@ am__aria2c_SOURCES_DIST = AllTest.cc TestUtil.cc TestUtil.h \
 @ENABLE_BITTORRENT_TRUE@	MSEHandshakeTest.$(OBJEXT) \
 @ENABLE_BITTORRENT_TRUE@	BencodeTest.$(OBJEXT) \
 @ENABLE_BITTORRENT_TRUE@	BittorrentHelperTest.$(OBJEXT) \
-@ENABLE_BITTORRENT_TRUE@	PriorityPieceSelectorTest.$(OBJEXT)
+@ENABLE_BITTORRENT_TRUE@	PriorityPieceSelectorTest.$(OBJEXT) \
+@ENABLE_BITTORRENT_TRUE@	LpdMessageDispatcherTest.$(OBJEXT) \
+@ENABLE_BITTORRENT_TRUE@	LpdMessageReceiverTest.$(OBJEXT)
 @ENABLE_METALINK_TRUE@am__objects_7 = MetalinkerTest.$(OBJEXT) \
 @ENABLE_METALINK_TRUE@	MetalinkEntryTest.$(OBJEXT) \
 @ENABLE_METALINK_TRUE@	Metalink2RequestGroupTest.$(OBJEXT) \
@@ -824,6 +829,8 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/IteratableChecksumValidatorTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/IteratableChunkChecksumValidatorTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LongestSequencePieceSelectorTest.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LpdMessageDispatcherTest.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LpdMessageReceiverTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MSEHandshakeTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MagnetTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MessageDigestHelperTest.Po@am__quote@