summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>2013-02-25 00:56:49 +0900
committerTatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>2013-02-25 00:56:49 +0900
commitd68741697ad93e4099b16c7fbfedc7ed4b98b75f (patch)
treee59f740db763e412818141cb72e62a0e7b26b108 /src
parentb782a56b1c178795fda4ca7ed2ee4d9621927aab (diff)
Support UDP tracker
It shares UDP listening port with IPv4 DHT. At the moment, in order to enable UDP tracker support, enable IPv4 DHT.
Diffstat (limited to 'src')
-rw-r--r--src/BtAnnounce.h10
-rw-r--r--src/BtRegistry.cc10
-rw-r--r--src/BtRegistry.h19
-rw-r--r--src/BtSetup.cc1
-rw-r--r--src/DHTInteractionCommand.cc73
-rw-r--r--src/DHTInteractionCommand.h9
-rw-r--r--src/DHTMessageReceiver.cc23
-rw-r--r--src/DHTMessageReceiver.h4
-rw-r--r--src/DHTSetup.cc15
-rw-r--r--src/DefaultBtAnnounce.cc89
-rw-r--r--src/DefaultBtAnnounce.h9
-rw-r--r--src/DownloadEngine.cc6
-rw-r--r--src/DownloadEngine.h7
-rw-r--r--src/Makefile.am5
-rw-r--r--src/NameResolveCommand.cc193
-rw-r--r--src/NameResolveCommand.h87
-rw-r--r--src/TrackerWatcherCommand.cc249
-rw-r--r--src/TrackerWatcherCommand.h60
-rw-r--r--src/UDPTrackerClient.cc616
-rw-r--r--src/UDPTrackerClient.h171
-rw-r--r--src/UDPTrackerRequest.cc51
-rw-r--r--src/UDPTrackerRequest.h112
-rw-r--r--src/bittorrent_helper.cc13
-rw-r--r--src/bittorrent_helper.h9
24 files changed, 1740 insertions, 101 deletions
diff --git a/src/BtAnnounce.h b/src/BtAnnounce.h
index 6cdd6804..47523af2 100644
--- a/src/BtAnnounce.h
+++ b/src/BtAnnounce.h
@@ -40,9 +40,12 @@
#include <string>
#include "a2time.h"
+#include "SharedHandle.h"
namespace aria2 {
+class UDPTrackerRequest;
+
class BtAnnounce {
public:
virtual ~BtAnnounce() {}
@@ -65,6 +68,10 @@ public:
*/
virtual std::string getAnnounceUrl() = 0;
+ virtual SharedHandle<UDPTrackerRequest>
+ createUDPTrackerRequest(const std::string& remoteAddr, uint16_t remotePort,
+ uint16_t localPort) = 0;
+
/**
* Tells that the announce process has just started.
*/
@@ -96,6 +103,9 @@ public:
virtual void processAnnounceResponse(const unsigned char* trackerResponse,
size_t trackerResponseLength) = 0;
+ virtual void processUDPTrackerResponse
+ (const SharedHandle<UDPTrackerRequest>& req) = 0;
+
/**
* Returns true if no more announce is needed.
*/
diff --git a/src/BtRegistry.cc b/src/BtRegistry.cc
index 1189a652..273f0130 100644
--- a/src/BtRegistry.cc
+++ b/src/BtRegistry.cc
@@ -42,12 +42,14 @@
#include "BtProgressInfoFile.h"
#include "bittorrent_helper.h"
#include "LpdMessageReceiver.h"
+#include "UDPTrackerClient.h"
#include "NullHandle.h"
namespace aria2 {
BtRegistry::BtRegistry()
- : tcpPort_(0)
+ : tcpPort_(0),
+ udpPort_(0)
{}
BtRegistry::~BtRegistry() {}
@@ -107,6 +109,12 @@ void BtRegistry::setLpdMessageReceiver
lpdMessageReceiver_ = receiver;
}
+void BtRegistry::setUDPTrackerClient
+(const SharedHandle<UDPTrackerClient>& tracker)
+{
+ udpTrackerClient_ = tracker;
+}
+
BtObject::BtObject
(const SharedHandle<DownloadContext>& downloadContext,
const SharedHandle<PieceStorage>& pieceStorage,
diff --git a/src/BtRegistry.h b/src/BtRegistry.h
index a4791210..06c0bfa4 100644
--- a/src/BtRegistry.h
+++ b/src/BtRegistry.h
@@ -51,6 +51,7 @@ class BtRuntime;
class BtProgressInfoFile;
class DownloadContext;
class LpdMessageReceiver;
+class UDPTrackerClient;
struct BtObject {
SharedHandle<DownloadContext> downloadContext;
@@ -80,7 +81,10 @@ class BtRegistry {
private:
std::map<a2_gid_t, SharedHandle<BtObject> > pool_;
uint16_t tcpPort_;
+ // This is IPv4 port for DHT and UDP tracker. No IPv6 udpPort atm.
+ uint16_t udpPort_;
SharedHandle<LpdMessageReceiver> lpdMessageReceiver_;
+ SharedHandle<UDPTrackerClient> udpTrackerClient_;
public:
BtRegistry();
~BtRegistry();
@@ -118,11 +122,26 @@ public:
return tcpPort_;
}
+ void setUdpPort(uint16_t port)
+ {
+ udpPort_ = port;
+ }
+ uint16_t getUdpPort() const
+ {
+ return udpPort_;
+ }
+
void setLpdMessageReceiver(const SharedHandle<LpdMessageReceiver>& receiver);
const SharedHandle<LpdMessageReceiver>& getLpdMessageReceiver() const
{
return lpdMessageReceiver_;
}
+
+ void setUDPTrackerClient(const SharedHandle<UDPTrackerClient>& tracker);
+ const SharedHandle<UDPTrackerClient>& getUDPTrackerClient() const
+ {
+ return udpTrackerClient_;
+ }
};
} // namespace aria2
diff --git a/src/BtSetup.cc b/src/BtSetup.cc
index 1a8b6403..ce7f3bf8 100644
--- a/src/BtSetup.cc
+++ b/src/BtSetup.cc
@@ -67,6 +67,7 @@
#include "DHTMessageReceiver.h"
#include "DHTMessageFactory.h"
#include "DHTMessageCallback.h"
+#include "UDPTrackerClient.h"
#include "BtProgressInfoFile.h"
#include "BtAnnounce.h"
#include "BtRuntime.h"
diff --git a/src/DHTInteractionCommand.cc b/src/DHTInteractionCommand.cc
index 4fc3f371..096812a8 100644
--- a/src/DHTInteractionCommand.cc
+++ b/src/DHTInteractionCommand.cc
@@ -46,10 +46,16 @@
#include "LogFactory.h"
#include "DHTMessageCallback.h"
#include "DHTNode.h"
+#include "DHTConnection.h"
+#include "UDPTrackerClient.h"
+#include "UDPTrackerRequest.h"
#include "fmt.h"
+#include "wallclock.h"
namespace aria2 {
+// TODO This name of this command is misleading, because now it also
+// handles UDP trackers as well as DHT.
DHTInteractionCommand::DHTInteractionCommand(cuid_t cuid, DownloadEngine* e)
: Command(cuid),
e_(e)
@@ -77,23 +83,60 @@ void DHTInteractionCommand::disableReadCheckSocket(const SharedHandle<SocketCore
bool DHTInteractionCommand::execute()
{
- if(e_->getRequestGroupMan()->downloadFinished() || e_->isHaltRequested()) {
+ // We need to keep this command alive while TrackerWatcherCommand
+ // needs this.
+ if(e_->getRequestGroupMan()->downloadFinished() ||
+ (e_->isHaltRequested() && udpTrackerClient_->getNumWatchers() == 0)) {
+ return true;
+ } else if(e_->isForceHaltRequested()) {
+ udpTrackerClient_->failAll();
return true;
}
taskQueue_->executeTask();
- while(1) {
- SharedHandle<DHTMessage> m = receiver_->receiveMessage();
- if(!m) {
- break;
+ std::string remoteAddr;
+ uint16_t remotePort;
+ unsigned char data[64*1024];
+ try {
+ while(1) {
+ ssize_t length = connection_->receiveMessage(data, sizeof(data),
+ remoteAddr, remotePort);
+ if(length <= 0) {
+ break;
+ }
+ if(data[0] == 'd') {
+ // udp tracker response does not start with 'd', so assume
+ // this message belongs to DHT. nothrow.
+ receiver_->receiveMessage(remoteAddr, remotePort, data, length);
+ } else {
+ // this may be udp tracker response. nothrow.
+ udpTrackerClient_->receiveReply(data, length, remoteAddr, remotePort,
+ global::wallclock());
+ }
}
+ } catch(RecoverableException& e) {
+ A2_LOG_INFO_EX("Exception thrown while receiving UDP message.", e);
}
receiver_->handleTimeout();
- try {
- dispatcher_->sendMessages();
- } catch(RecoverableException& e) {
- A2_LOG_ERROR_EX(EX_EXCEPTION_CAUGHT, e);
+ udpTrackerClient_->handleTimeout(global::wallclock());
+ dispatcher_->sendMessages();
+ while(!udpTrackerClient_->getPendingRequests().empty()) {
+ // no throw
+ ssize_t length = udpTrackerClient_->createRequest(data, sizeof(data),
+ remoteAddr, remotePort,
+ global::wallclock());
+ if(length == -1) {
+ break;
+ }
+ try {
+ // throw
+ connection_->sendMessage(data, length, remoteAddr, remotePort);
+ udpTrackerClient_->requestSent(global::wallclock());
+ } catch(RecoverableException& e) {
+ A2_LOG_INFO_EX("Exception thrown while sending UDP tracker request.", e);
+ udpTrackerClient_->requestFail(UDPT_ERR_NETWORK);
+ }
}
e_->addCommand(this);
return false;
@@ -114,4 +157,16 @@ void DHTInteractionCommand::setTaskQueue(const SharedHandle<DHTTaskQueue>& taskQ
taskQueue_ = taskQueue;
}
+void DHTInteractionCommand::setConnection
+(const SharedHandle<DHTConnection>& connection)
+{
+ connection_ = connection;
+}
+
+void DHTInteractionCommand::setUDPTrackerClient
+(const SharedHandle<UDPTrackerClient>& udpTrackerClient)
+{
+ udpTrackerClient_ = udpTrackerClient;
+}
+
} // namespace aria2
diff --git a/src/DHTInteractionCommand.h b/src/DHTInteractionCommand.h
index f00df78a..e90a1dd6 100644
--- a/src/DHTInteractionCommand.h
+++ b/src/DHTInteractionCommand.h
@@ -45,6 +45,8 @@ class DHTMessageReceiver;
class DHTTaskQueue;
class DownloadEngine;
class SocketCore;
+class DHTConnection;
+class UDPTrackerClient;
class DHTInteractionCommand:public Command {
private:
@@ -53,6 +55,8 @@ private:
SharedHandle<DHTMessageReceiver> receiver_;
SharedHandle<DHTTaskQueue> taskQueue_;
SharedHandle<SocketCore> readCheckSocket_;
+ SharedHandle<DHTConnection> connection_;
+ SharedHandle<UDPTrackerClient> udpTrackerClient_;
public:
DHTInteractionCommand(cuid_t cuid, DownloadEngine* e);
@@ -69,6 +73,11 @@ public:
void setMessageReceiver(const SharedHandle<DHTMessageReceiver>& receiver);
void setTaskQueue(const SharedHandle<DHTTaskQueue>& taskQueue);
+
+ void setConnection(const SharedHandle<DHTConnection>& connection);
+
+ void setUDPTrackerClient
+ (const SharedHandle<UDPTrackerClient>& udpTrackerClient);
};
} // namespace aria2
diff --git a/src/DHTMessageReceiver.cc b/src/DHTMessageReceiver.cc
index e5558d9d..36e40639 100644
--- a/src/DHTMessageReceiver.cc
+++ b/src/DHTMessageReceiver.cc
@@ -63,18 +63,11 @@ DHTMessageReceiver::DHTMessageReceiver
DHTMessageReceiver::~DHTMessageReceiver() {}
-SharedHandle<DHTMessage> DHTMessageReceiver::receiveMessage()
+SharedHandle<DHTMessage> DHTMessageReceiver::receiveMessage
+(const std::string& remoteAddr, uint16_t remotePort, unsigned char *data,
+ size_t length)
{
- std::string remoteAddr;
- uint16_t remotePort;
- unsigned char data[64*1024];
try {
- ssize_t length = connection_->receiveMessage(data, sizeof(data),
- remoteAddr,
- remotePort);
- if(length <= 0) {
- return SharedHandle<DHTMessage>();
- }
bool isReply = false;
SharedHandle<ValueBase> decoded = bencode2::decode(data, length);
const Dict* dict = downcast<Dict>(decoded);
@@ -87,13 +80,13 @@ SharedHandle<DHTMessage> DHTMessageReceiver::receiveMessage()
} else {
A2_LOG_INFO(fmt("Malformed DHT message. Missing 'y' key. From:%s:%u",
remoteAddr.c_str(), remotePort));
- return handleUnknownMessage(data, sizeof(data), remoteAddr, remotePort);
+ return handleUnknownMessage(data, length, remoteAddr, remotePort);
}
} else {
A2_LOG_INFO(fmt("Malformed DHT message. This is not a bencoded directory."
" From:%s:%u",
remoteAddr.c_str(), remotePort));
- return handleUnknownMessage(data, sizeof(data), remoteAddr, remotePort);
+ return handleUnknownMessage(data, length, remoteAddr, remotePort);
}
if(isReply) {
std::pair<SharedHandle<DHTResponseMessage>,
@@ -101,7 +94,7 @@ SharedHandle<DHTMessage> DHTMessageReceiver::receiveMessage()
tracker_->messageArrived(dict, remoteAddr, remotePort);
if(!p.first) {
// timeout or malicious? message
- return handleUnknownMessage(data, sizeof(data), remoteAddr, remotePort);
+ return handleUnknownMessage(data, length, remoteAddr, remotePort);
}
onMessageReceived(p.first);
if(p.second) {
@@ -114,14 +107,14 @@ SharedHandle<DHTMessage> DHTMessageReceiver::receiveMessage()
if(*message->getLocalNode() == *message->getRemoteNode()) {
// drop message from localnode
A2_LOG_INFO("Received DHT message from localnode.");
- return handleUnknownMessage(data, sizeof(data), remoteAddr, remotePort);
+ return handleUnknownMessage(data, length, remoteAddr, remotePort);
}
onMessageReceived(message);
return message;
}
} catch(RecoverableException& e) {
A2_LOG_INFO_EX("Exception thrown while receiving DHT message.", e);
- return handleUnknownMessage(data, sizeof(data), remoteAddr, remotePort);
+ return handleUnknownMessage(data, length, remoteAddr, remotePort);
}
}
diff --git a/src/DHTMessageReceiver.h b/src/DHTMessageReceiver.h
index 4e2e15fe..f8ed5018 100644
--- a/src/DHTMessageReceiver.h
+++ b/src/DHTMessageReceiver.h
@@ -69,7 +69,9 @@ public:
~DHTMessageReceiver();
- SharedHandle<DHTMessage> receiveMessage();
+ SharedHandle<DHTMessage> receiveMessage
+ (const std::string& remoteAddr, uint16_t remotePort, unsigned char *data,
+ size_t length);
void handleTimeout();
diff --git a/src/DHTSetup.cc b/src/DHTSetup.cc
index 17bd035d..145e0316 100644
--- a/src/DHTSetup.cc
+++ b/src/DHTSetup.cc
@@ -61,6 +61,8 @@
#include "DHTRegistry.h"
#include "DHTBucketRefreshTask.h"
#include "DHTMessageCallback.h"
+#include "UDPTrackerClient.h"
+#include "BtRegistry.h"
#include "prefs.h"
#include "Option.h"
#include "SocketCore.h"
@@ -176,6 +178,8 @@ void DHTSetup::setup
factory->setTokenTracker(tokenTracker.get());
factory->setLocalNode(localNode);
+ // For now, UDPTrackerClient was enabled along with DHT
+ SharedHandle<UDPTrackerClient> udpTrackerClient(new UDPTrackerClient());
// assign them into DHTRegistry
if(family == AF_INET) {
DHTRegistry::getMutableData().localNode = localNode;
@@ -187,6 +191,8 @@ void DHTSetup::setup
DHTRegistry::getMutableData().messageDispatcher = dispatcher;
DHTRegistry::getMutableData().messageReceiver = receiver;
DHTRegistry::getMutableData().messageFactory = factory;
+ e->getBtRegistry()->setUDPTrackerClient(udpTrackerClient);
+ e->getBtRegistry()->setUdpPort(localNode->getPort());
} else {
DHTRegistry::getMutableData6().localNode = localNode;
DHTRegistry::getMutableData6().routingTable = routingTable;
@@ -244,6 +250,8 @@ void DHTSetup::setup
command->setMessageReceiver(receiver);
command->setTaskQueue(taskQueue);
command->setReadCheckSocket(connection->getSocket());
+ command->setConnection(connection);
+ command->setUDPTrackerClient(udpTrackerClient);
tempCommands->push_back(command);
}
{
@@ -282,12 +290,15 @@ void DHTSetup::setup
}
commands.insert(commands.end(), tempCommands->begin(), tempCommands->end());
tempCommands->clear();
- } catch(RecoverableException& e) {
+ } catch(RecoverableException& ex) {
A2_LOG_ERROR_EX(fmt("Exception caught while initializing DHT functionality."
" DHT is disabled."),
- e);
+ ex);
if(family == AF_INET) {
DHTRegistry::clearData();
+ e->getBtRegistry()->setUDPTrackerClient
+ (SharedHandle<UDPTrackerClient>());
+ e->getBtRegistry()->setUdpPort(0);
} else {
DHTRegistry::clearData6();
}
diff --git a/src/DefaultBtAnnounce.cc b/src/DefaultBtAnnounce.cc
index 6a9cc713..a41f002d 100644
--- a/src/DefaultBtAnnounce.cc
+++ b/src/DefaultBtAnnounce.cc
@@ -52,6 +52,8 @@
#include "bittorrent_helper.h"
#include "wallclock.h"
#include "uri.h"
+#include "UDPTrackerRequest.h"
+#include "SocketCore.h"
namespace aria2 {
@@ -115,7 +117,7 @@ bool uriHasQuery(const std::string& uri)
}
} // namespace
-std::string DefaultBtAnnounce::getAnnounceUrl() {
+bool DefaultBtAnnounce::adjustAnnounceList() {
if(isStoppedAnnounceReady()) {
if(!announceList_.currentTierAcceptsStoppedEvent()) {
announceList_.moveToStoppedAllowedTier();
@@ -135,6 +137,13 @@ std::string DefaultBtAnnounce::getAnnounceUrl() {
announceList_.setEvent(AnnounceTier::STARTED_AFTER_COMPLETION);
}
} else {
+ return false;
+ }
+ return true;
+}
+
+std::string DefaultBtAnnounce::getAnnounceUrl() {
+ if(!adjustAnnounceList()) {
return A2STR::NIL;
}
int numWant = 50;
@@ -193,6 +202,60 @@ std::string DefaultBtAnnounce::getAnnounceUrl() {
return uri;
}
+SharedHandle<UDPTrackerRequest> DefaultBtAnnounce::createUDPTrackerRequest
+(const std::string& remoteAddr, uint16_t remotePort, uint16_t localPort)
+{
+ if(!adjustAnnounceList()) {
+ return SharedHandle<UDPTrackerRequest>();
+ }
+ NetStat& stat = downloadContext_->getNetStat();
+ int64_t left =
+ pieceStorage_->getTotalLength()-pieceStorage_->getCompletedLength();
+ SharedHandle<UDPTrackerRequest> req(new UDPTrackerRequest());
+ req->remoteAddr = remoteAddr;
+ req->remotePort = remotePort;
+ req->action = UDPT_ACT_ANNOUNCE;
+ req->infohash = bittorrent::getTorrentAttrs(downloadContext_)->infoHash;
+ const unsigned char* peerId = bittorrent::getStaticPeerId();
+ req->peerId.assign(peerId, peerId + PEER_ID_LENGTH);
+ req->downloaded = stat.getSessionDownloadLength();
+ req->left = left;
+ req->uploaded = stat.getSessionUploadLength();
+ switch(announceList_.getEvent()) {
+ case AnnounceTier::STARTED:
+ case AnnounceTier::STARTED_AFTER_COMPLETION:
+ req->event = UDPT_EVT_STARTED;
+ break;
+ case AnnounceTier::STOPPED:
+ req->event = UDPT_EVT_STOPPED;
+ break;
+ case AnnounceTier::COMPLETED:
+ req->event = UDPT_EVT_COMPLETED;
+ break;
+ default:
+ req->event = 0;
+ }
+ if(!option_->blank(PREF_BT_EXTERNAL_IP)) {
+ unsigned char dest[16];
+ if(net::getBinAddr(dest, option_->get(PREF_BT_EXTERNAL_IP)) == 4) {
+ memcpy(&req->ip, dest, 4);
+ } else {
+ req->ip = 0;
+ }
+ } else {
+ req->ip = 0;
+ }
+ req->key = randomizer_->getRandomNumber(INT32_MAX);
+ int numWant = 50;
+ if(!btRuntime_->lessThanMinPeers() || btRuntime_->isHalt()) {
+ numWant = 0;
+ }
+ req->numWant = numWant;
+ req->port = localPort;
+ req->extensions = 0;
+ return req;
+}
+
void DefaultBtAnnounce::announceStart() {
++trackers_;
}
@@ -287,6 +350,30 @@ DefaultBtAnnounce::processAnnounceResponse(const unsigned char* trackerResponse,
}
}
+void DefaultBtAnnounce::processUDPTrackerResponse
+(const SharedHandle<UDPTrackerRequest>& req)
+{
+ const SharedHandle<UDPTrackerReply>& reply = req->reply;
+ A2_LOG_DEBUG("Now processing UDP tracker response.");
+ if(reply->interval > 0) {
+ minInterval_ = reply->interval;
+ A2_LOG_DEBUG(fmt("Min interval:%ld", static_cast<long int>(minInterval_)));
+ interval_ = minInterval_;
+ }
+ complete_ = reply->seeders;
+ A2_LOG_DEBUG(fmt("Complete:%d", reply->seeders));
+ incomplete_ = reply->leechers;
+ A2_LOG_DEBUG(fmt("Incomplete:%d", reply->leechers));
+ if(!btRuntime_->isHalt() && btRuntime_->lessThanMinPeers()) {
+ for(std::vector<std::pair<std::string, uint16_t> >::iterator i =
+ reply->peers.begin(), eoi = reply->peers.end(); i != eoi;
+ ++i) {
+ peerStorage_->addPeer(SharedHandle<Peer>(new Peer((*i).first,
+ (*i).second)));
+ }
+ }
+}
+
bool DefaultBtAnnounce::noMoreAnnounce() {
return (trackers_ == 0 &&
btRuntime_->isHalt() &&
diff --git a/src/DefaultBtAnnounce.h b/src/DefaultBtAnnounce.h
index a760a140..5428a42c 100644
--- a/src/DefaultBtAnnounce.h
+++ b/src/DefaultBtAnnounce.h
@@ -66,6 +66,8 @@ private:
SharedHandle<PieceStorage> pieceStorage_;
SharedHandle<PeerStorage> peerStorage_;
uint16_t tcpPort_;
+
+ bool adjustAnnounceList();
public:
DefaultBtAnnounce(const SharedHandle<DownloadContext>& downloadContext,
const Option* option);
@@ -103,6 +105,10 @@ public:
virtual std::string getAnnounceUrl();
+ virtual SharedHandle<UDPTrackerRequest>
+ createUDPTrackerRequest(const std::string& remoteAddr, uint16_t remotePort,
+ uint16_t localPort);
+
virtual void announceStart();
virtual void announceSuccess();
@@ -116,6 +122,9 @@ public:
virtual void processAnnounceResponse(const unsigned char* trackerResponse,
size_t trackerResponseLength);
+ virtual void processUDPTrackerResponse
+ (const SharedHandle<UDPTrackerRequest>& req);
+
virtual bool noMoreAnnounce();
virtual void shuffleAnnounce();
diff --git a/src/DownloadEngine.cc b/src/DownloadEngine.cc
index 9b42bf70..05478fe8 100644
--- a/src/DownloadEngine.cc
+++ b/src/DownloadEngine.cc
@@ -85,7 +85,7 @@ volatile sig_atomic_t globalHaltRequested = 0;
DownloadEngine::DownloadEngine(const SharedHandle<EventPoll>& eventPoll)
: eventPoll_(eventPoll),
- haltRequested_(false),
+ haltRequested_(0),
noWait_(false),
refreshInterval_(DEFAULT_REFRESH_INTERVAL),
cookieStorage_(new CookieStorage()),
@@ -239,13 +239,13 @@ void DownloadEngine::afterEachIteration()
void DownloadEngine::requestHalt()
{
- haltRequested_ = true;
+ haltRequested_ = std::max(haltRequested_, 1);
requestGroupMan_->halt();
}
void DownloadEngine::requestForceHalt()
{
- haltRequested_ = true;
+ haltRequested_ = std::max(haltRequested_, 2);
requestGroupMan_->forceHalt();
}
diff --git a/src/DownloadEngine.h b/src/DownloadEngine.h
index 05a8b03a..d949bff6 100644
--- a/src/DownloadEngine.h
+++ b/src/DownloadEngine.h
@@ -79,7 +79,7 @@ private:
SharedHandle<StatCalc> statCalc_;
- bool haltRequested_;
+ int haltRequested_;
class SocketPoolEntry {
private:
@@ -230,6 +230,11 @@ public:
return haltRequested_;
}
+ bool isForceHaltRequested() const
+ {
+ return haltRequested_ >= 2;
+ }
+
void requestHalt();
void requestForceHalt();
diff --git a/src/Makefile.am b/src/Makefile.am
index bf5805b7..5388ba35 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -522,7 +522,10 @@ SRCS += PeerAbstractCommand.cc PeerAbstractCommand.h\
ValueBaseBencodeParser.h\
BencodeDiskWriter.h\
BencodeDiskWriterFactory.h\
- MemoryBencodePreDownloadHandler.h
+ MemoryBencodePreDownloadHandler.h\
+ UDPTrackerClient.cc UDPTrackerClient.h\
+ UDPTrackerRequest.cc UDPTrackerRequest.h\
+ NameResolveCommand.cc NameResolveCommand.h
endif # ENABLE_BITTORRENT
if ENABLE_METALINK
diff --git a/src/NameResolveCommand.cc b/src/NameResolveCommand.cc
new file mode 100644
index 00000000..cf09ebb6
--- /dev/null
+++ b/src/NameResolveCommand.cc
@@ -0,0 +1,193 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2013 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 "NameResolveCommand.h"
+#include "DownloadEngine.h"
+#include "NameResolver.h"
+#include "prefs.h"
+#include "message.h"
+#include "util.h"
+#include "Option.h"
+#include "RequestGroupMan.h"
+#include "Logger.h"
+#include "LogFactory.h"
+#include "fmt.h"
+#include "UDPTrackerRequest.h"
+#include "UDPTrackerClient.h"
+#include "BtRegistry.h"
+#ifdef ENABLE_ASYNC_DNS
+#include "AsyncNameResolver.h"
+#endif // ENABLE_ASYNC_DNS
+
+namespace aria2 {
+
+NameResolveCommand::NameResolveCommand
+(cuid_t cuid, DownloadEngine* e,
+ const SharedHandle<UDPTrackerRequest>& req)
+ : Command(cuid),
+ e_(e),
+ req_(req)
+{
+ setStatus(Command::STATUS_ONESHOT_REALTIME);
+}
+
+NameResolveCommand::~NameResolveCommand()
+{
+#ifdef ENABLE_ASYNC_DNS
+ disableNameResolverCheck(resolver_);
+#endif // ENABLE_ASYNC_DNS
+}
+
+bool NameResolveCommand::execute()
+{
+ // This is UDP tracker specific, but we need to keep this command
+ // alive until force shutdown is
+ // commencing. RequestGroupMan::downloadFinished() is useless here
+ // at the moment.
+ if(e_->isForceHaltRequested()) {
+ onShutdown();
+ return true;
+ }
+#ifdef ENABLE_ASYNC_DNS
+ if(!resolver_) {
+ int family = AF_INET;
+ resolver_.reset(new AsyncNameResolver(family
+#ifdef HAVE_ARES_ADDR_NODE
+ , e_->getAsyncDNSServers()
+#endif // HAVE_ARES_ADDR_NODE
+ ));
+ }
+#endif // ENABLE_ASYNC_DNS
+ std::string hostname = req_->remoteAddr;
+ std::vector<std::string> res;
+ if(util::isNumericHost(hostname)) {
+ res.push_back(hostname);
+ } else {
+#ifdef ENABLE_ASYNC_DNS
+ if(e_->getOption()->getAsBool(PREF_ASYNC_DNS)) {
+ try {
+ if(resolveHostname(hostname, resolver_)) {
+ res = resolver_->getResolvedAddresses();
+ } else {
+ e_->addCommand(this);
+ return false;
+ }
+ } catch(RecoverableException& e) {
+ A2_LOG_ERROR_EX(EX_EXCEPTION_CAUGHT, e);
+ }
+ } else
+#endif // ENABLE_ASYNC_DNS
+ {
+ NameResolver resolver;
+ resolver.setSocktype(SOCK_DGRAM);
+ try {
+ resolver.resolve(res, hostname);
+ } catch(RecoverableException& e) {
+ A2_LOG_ERROR_EX(EX_EXCEPTION_CAUGHT, e);
+ }
+ }
+ }
+ if(res.empty()) {
+ onFailure();
+ } else {
+ onSuccess(res, e_);
+ }
+ return true;
+}
+
+void NameResolveCommand::onShutdown()
+{
+ req_->state = UDPT_STA_COMPLETE;
+ req_->error = UDPT_ERR_SHUTDOWN;
+}
+
+void NameResolveCommand::onFailure()
+{
+ req_->state = UDPT_STA_COMPLETE;
+ req_->error = UDPT_ERR_NETWORK;
+}
+
+void NameResolveCommand::onSuccess
+(const std::vector<std::string>& addrs, DownloadEngine* e)
+{
+ req_->remoteAddr = addrs[0];
+ e->getBtRegistry()->getUDPTrackerClient()->addRequest(req_);
+}
+
+#ifdef ENABLE_ASYNC_DNS
+
+bool NameResolveCommand::resolveHostname
+(const std::string& hostname,
+ const SharedHandle<AsyncNameResolver>& resolver)
+{
+ switch(resolver->getStatus()) {
+ case AsyncNameResolver::STATUS_READY:
+ A2_LOG_INFO(fmt(MSG_RESOLVING_HOSTNAME,
+ getCuid(),
+ hostname.c_str()));
+ resolver->resolve(hostname);
+ setNameResolverCheck(resolver);
+ return false;
+ case AsyncNameResolver::STATUS_SUCCESS:
+ A2_LOG_INFO(fmt(MSG_NAME_RESOLUTION_COMPLETE,
+ getCuid(),
+ resolver->getHostname().c_str(),
+ resolver->getResolvedAddresses().front().c_str()));
+ return true;
+ break;
+ case AsyncNameResolver::STATUS_ERROR:
+ throw DL_ABORT_EX
+ (fmt(MSG_NAME_RESOLUTION_FAILED,
+ getCuid(),
+ hostname.c_str(),
+ resolver->getError().c_str()));
+ default:
+ return false;
+ }
+}
+
+void NameResolveCommand::setNameResolverCheck
+(const SharedHandle<AsyncNameResolver>& resolver)
+{
+ e_->addNameResolverCheck(resolver, this);
+}
+
+void NameResolveCommand::disableNameResolverCheck
+(const SharedHandle<AsyncNameResolver>& resolver)
+{
+ e_->deleteNameResolverCheck(resolver, this);
+}
+#endif // ENABLE_ASYNC_DNS
+
+} // namespace aria2
diff --git a/src/NameResolveCommand.h b/src/NameResolveCommand.h
new file mode 100644
index 00000000..0d3aa8a8
--- /dev/null
+++ b/src/NameResolveCommand.h
@@ -0,0 +1,87 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2013 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_NAME_RESOLVE_COMMAND_H
+#define D_NAME_RESOLVE_COMMAND_H
+
+#include "Command.h"
+
+#include <string>
+#include <vector>
+
+#include "SharedHandle.h"
+
+// TODO Make this class generic.
+namespace aria2 {
+
+class DownloadEngine;
+#ifdef ENABLE_ASYNC_DNS
+class AsyncNameResolver;
+#endif // ENABLE_ASYNC_DNS
+class UDPTrackerRequest;
+
+class NameResolveCommand:public Command {
+private:
+ DownloadEngine* e_;
+
+#ifdef ENABLE_ASYNC_DNS
+ SharedHandle<AsyncNameResolver> resolver_;
+#endif // ENABLE_ASYNC_DNS
+
+#ifdef ENABLE_ASYNC_DNS
+ bool resolveHostname(const std::string& hostname,
+ const SharedHandle<AsyncNameResolver>& resolver);
+
+ void setNameResolverCheck(const SharedHandle<AsyncNameResolver>& resolver);
+
+ void disableNameResolverCheck(const SharedHandle<AsyncNameResolver>& resolver);
+#endif // ENABLE_ASYNC_DNS
+
+ SharedHandle<UDPTrackerRequest> req_;
+ void onShutdown();
+ void onFailure();
+ void onSuccess
+ (const std::vector<std::string>& addrs, DownloadEngine* e);
+public:
+ NameResolveCommand(cuid_t cuid, DownloadEngine* e,
+ const SharedHandle<UDPTrackerRequest>& req);
+
+ virtual ~NameResolveCommand();
+
+ virtual bool execute();
+};
+
+} // namespace aria2
+
+#endif // D_NAME_RESOVE_COMMAND_H
diff --git a/src/TrackerWatcherCommand.cc b/src/TrackerWatcherCommand.cc
index 0eba057f..06d7fc3e 100644
--- a/src/TrackerWatcherCommand.cc
+++ b/src/TrackerWatcherCommand.cc
@@ -63,32 +63,156 @@
#include "a2functional.h"
#include "util.h"
#include "fmt.h"
+#include "UDPTrackerRequest.h"
+#include "UDPTrackerClient.h"
+#include "BtRegistry.h"
+#include "NameResolveCommand.h"
namespace aria2 {
+HTTPAnnRequest::HTTPAnnRequest(const SharedHandle<RequestGroup>& rg)
+ : rg_(rg)
+{}
+
+HTTPAnnRequest::~HTTPAnnRequest()
+{}
+
+bool HTTPAnnRequest::stopped() const
+{
+ return rg_->getNumCommand() == 0;
+}
+
+bool HTTPAnnRequest::success() const
+{
+ return rg_->downloadFinished();
+}
+
+void HTTPAnnRequest::stop(DownloadEngine* e)
+{
+ rg_->setForceHaltRequested(true);
+}
+
+bool HTTPAnnRequest::issue(DownloadEngine* e)
+{
+ try {
+ std::vector<Command*>* commands = new std::vector<Command*>();
+ auto_delete_container<std::vector<Command*> > commandsDel(commands);
+ rg_->createInitialCommand(*commands, e);
+ e->addCommand(*commands);
+ e->setNoWait(true);
+ commands->clear();
+ A2_LOG_DEBUG("added tracker request command");
+ return true;
+ } catch(RecoverableException& ex) {
+ A2_LOG_ERROR_EX(EX_EXCEPTION_CAUGHT, ex);
+ return false;
+ }
+}
+
+bool HTTPAnnRequest::processResponse
+(const SharedHandle<BtAnnounce>& btAnnounce)
+{
+ try {
+ std::stringstream strm;
+ unsigned char data[2048];
+ rg_->getPieceStorage()->getDiskAdaptor()->openFile();
+ while(1) {
+ ssize_t dataLength = rg_->getPieceStorage()->
+ getDiskAdaptor()->readData(data, sizeof(data), strm.tellp());
+ if(dataLength == 0) {
+ break;
+ }
+ strm.write(reinterpret_cast<const char*>(data), dataLength);
+ }
+ std::string res = strm.str();
+ btAnnounce->processAnnounceResponse
+ (reinterpret_cast<const unsigned char*>(res.c_str()), res.size());
+ return true;
+ } catch(RecoverableException& e) {
+ A2_LOG_ERROR_EX(EX_EXCEPTION_CAUGHT, e);
+ return false;
+ }
+}
+
+UDPAnnRequest::UDPAnnRequest(const SharedHandle<UDPTrackerRequest>& req)
+ : req_(req)
+{}
+
+UDPAnnRequest::~UDPAnnRequest()
+{}
+
+bool UDPAnnRequest::stopped() const
+{
+ return !req_ || req_->state == UDPT_STA_COMPLETE;
+}
+
+bool UDPAnnRequest::success() const
+{
+ return req_ && req_->state == UDPT_STA_COMPLETE &&
+ req_->error == UDPT_ERR_SUCCESS;
+}
+
+void UDPAnnRequest::stop(DownloadEngine* e)
+{
+ if(req_) {
+ req_.reset();
+ }
+}
+
+bool UDPAnnRequest::issue(DownloadEngine* e)
+{
+ if(req_) {
+ NameResolveCommand* command = new NameResolveCommand
+ (e->newCUID(), e, req_);
+ e->addCommand(command);
+ e->setNoWait(true);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool UDPAnnRequest::processResponse
+(const SharedHandle<BtAnnounce>& btAnnounce)
+{
+ if(req_) {
+ btAnnounce->processUDPTrackerResponse(req_);
+ return true;
+ } else {
+ return false;
+ }
+}
+
TrackerWatcherCommand::TrackerWatcherCommand
(cuid_t cuid, RequestGroup* requestGroup, DownloadEngine* e)
: Command(cuid),
requestGroup_(requestGroup),
- e_(e)
+ e_(e),
+ udpTrackerClient_(e_->getBtRegistry()->getUDPTrackerClient())
{
requestGroup_->increaseNumCommand();
+ if(udpTrackerClient_) {
+ udpTrackerClient_->increaseWatchers();
+ }
}
TrackerWatcherCommand::~TrackerWatcherCommand()
{
requestGroup_->decreaseNumCommand();
+ if(udpTrackerClient_) {
+ udpTrackerClient_->decreaseWatchers();
+ }
}
bool TrackerWatcherCommand::execute() {
if(requestGroup_->isForceHaltRequested()) {
- if(!trackerRequestGroup_) {
+ if(!trackerRequest_) {
return true;
- } else if(trackerRequestGroup_->getNumCommand() == 0 ||
- trackerRequestGroup_->downloadFinished()) {
+ } else if(trackerRequest_->stopped() ||
+ trackerRequest_->success()) {
return true;
} else {
- trackerRequestGroup_->setForceHaltRequested(true);
+ trackerRequest_->stop(e_);
e_->setRefreshInterval(0);
e_->addCommand(this);
return false;
@@ -98,44 +222,32 @@ bool TrackerWatcherCommand::execute() {
A2_LOG_DEBUG("no more announce");
return true;
}
- if(!trackerRequestGroup_) {
- trackerRequestGroup_ = createAnnounce();
- if(trackerRequestGroup_) {
- try {
- std::vector<Command*>* commands = new std::vector<Command*>();
- auto_delete_container<std::vector<Command*> > commandsDel(commands);
- trackerRequestGroup_->createInitialCommand(*commands, e_);
- e_->addCommand(*commands);
- commands->clear();
- A2_LOG_DEBUG("added tracker request command");
- } catch(RecoverableException& ex) {
- A2_LOG_ERROR_EX(EX_EXCEPTION_CAUGHT, ex);
- }
+ if(!trackerRequest_) {
+ trackerRequest_ = createAnnounce(e_);
+ if(trackerRequest_) {
+ trackerRequest_->issue(e_);
}
- } else if(trackerRequestGroup_->getNumCommand() == 0) {
+ } else if(trackerRequest_->stopped()) {
// We really want to make sure that tracker request has finished
// by checking getNumCommand() == 0. Because we reset
// trackerRequestGroup_, if it is still used in other Command, we
// will get Segmentation fault.
- if(trackerRequestGroup_->downloadFinished()) {
- try {
- std::string trackerResponse = getTrackerResponse(trackerRequestGroup_);
-
- processTrackerResponse(trackerResponse);
+ if(trackerRequest_->success()) {
+ if(trackerRequest_->processResponse(btAnnounce_)) {
btAnnounce_->announceSuccess();
btAnnounce_->resetAnnounce();
- } catch(RecoverableException& ex) {
- A2_LOG_ERROR_EX(EX_EXCEPTION_CAUGHT, ex);
+ addConnection();
+ } else {
btAnnounce_->announceFailure();
if(btAnnounce_->isAllAnnounceFailed()) {
btAnnounce_->resetAnnounce();
}
}
- trackerRequestGroup_.reset();
+ trackerRequest_.reset();
} else {
// handle errors here
btAnnounce_->announceFailure(); // inside it, trackers = 0.
- trackerRequestGroup_.reset();
+ trackerRequest_.reset();
if(btAnnounce_->isAllAnnounceFailed()) {
btAnnounce_->resetAnnounce();
}
@@ -145,30 +257,8 @@ bool TrackerWatcherCommand::execute() {
return false;
}
-std::string TrackerWatcherCommand::getTrackerResponse
-(const SharedHandle<RequestGroup>& requestGroup)
-{
- std::stringstream strm;
- unsigned char data[2048];
- requestGroup->getPieceStorage()->getDiskAdaptor()->openFile();
- while(1) {
- ssize_t dataLength = requestGroup->getPieceStorage()->
- getDiskAdaptor()->readData(data, sizeof(data), strm.tellp());
- if(dataLength == 0) {
- break;
- }
- strm.write(reinterpret_cast<const char*>(data), dataLength);
- }
- return strm.str();
-}
-
-// TODO we have to deal with the exception thrown By BtAnnounce
-void TrackerWatcherCommand::processTrackerResponse
-(const std::string& trackerResponse)
+void TrackerWatcherCommand::addConnection()
{
- btAnnounce_->processAnnounceResponse
- (reinterpret_cast<const unsigned char*>(trackerResponse.c_str()),
- trackerResponse.size());
while(!btRuntime_->isHalt() && btRuntime_->lessThanMinPeers()) {
if(!peerStorage_->isPeerAvailable()) {
break;
@@ -180,8 +270,8 @@ void TrackerWatcherCommand::processTrackerResponse
break;
}
PeerInitiateConnectionCommand* command;
- command = new PeerInitiateConnectionCommand(ncuid, requestGroup_, peer, e_,
- btRuntime_);
+ command = new PeerInitiateConnectionCommand(ncuid, requestGroup_, peer,
+ e_, btRuntime_);
command->setPeerStorage(peerStorage_);
command->setPieceStorage(pieceStorage_);
e_->addCommand(command);
@@ -190,13 +280,48 @@ void TrackerWatcherCommand::processTrackerResponse
}
}
-SharedHandle<RequestGroup> TrackerWatcherCommand::createAnnounce() {
- SharedHandle<RequestGroup> rg;
- if(btAnnounce_->isAnnounceReady()) {
- rg = createRequestGroup(btAnnounce_->getAnnounceUrl());
- btAnnounce_->announceStart(); // inside it, trackers++.
+SharedHandle<AnnRequest>
+TrackerWatcherCommand::createAnnounce(DownloadEngine* e)
+{
+ SharedHandle<AnnRequest> treq;
+ while(!btAnnounce_->isAllAnnounceFailed() &&
+ btAnnounce_->isAnnounceReady()) {
+ std::string uri = btAnnounce_->getAnnounceUrl();
+ uri_split_result res;
+ memset(&res, 0, sizeof(res));
+ if(uri_split(&res, uri.c_str()) == 0) {
+ // Without UDP tracker support, send it to normal tracker flow
+ // and make it fail.
+ if(udpTrackerClient_ &&
+ uri::getFieldString(res, USR_SCHEME, uri.c_str()) == "udp") {
+ uint16_t localPort;
+ localPort = e->getBtRegistry()->getUdpPort();
+ treq = createUDPAnnRequest
+ (uri::getFieldString(res, USR_HOST, uri.c_str()), res.port,
+ localPort);
+ } else {
+ treq = createHTTPAnnRequest(btAnnounce_->getAnnounceUrl());
+ }
+ btAnnounce_->announceStart(); // inside it, trackers++.
+ break;
+ } else {
+ btAnnounce_->announceFailure();
+ }
}
- return rg;
+ if(btAnnounce_->isAllAnnounceFailed()) {
+ btAnnounce_->resetAnnounce();
+ }
+ return treq;
+}
+
+SharedHandle<AnnRequest>
+TrackerWatcherCommand::createUDPAnnRequest(const std::string& host,
+ uint16_t port,
+ uint16_t localPort)
+{
+ SharedHandle<UDPTrackerRequest> req =
+ btAnnounce_->createUDPTrackerRequest(host, port, localPort);
+ return SharedHandle<AnnRequest>(new UDPAnnRequest(req));
}
namespace {
@@ -219,8 +344,8 @@ bool backupTrackerIsAvailable
}
} // namespace
-SharedHandle<RequestGroup>
-TrackerWatcherCommand::createRequestGroup(const std::string& uri)
+SharedHandle<AnnRequest>
+TrackerWatcherCommand::createHTTPAnnRequest(const std::string& uri)
{
std::vector<std::string> uris;
uris.push_back(uri);
@@ -261,7 +386,7 @@ TrackerWatcherCommand::createRequestGroup(const std::string& uri)
dctx->setAcceptMetalink(false);
A2_LOG_INFO(fmt("Creating tracker request group GID#%s",
GroupId::toHex(rg->getGID()).c_str()));
- return rg;
+ return SharedHandle<AnnRequest>(new HTTPAnnRequest(rg));
}
void TrackerWatcherCommand::setBtRuntime
diff --git a/src/TrackerWatcherCommand.h b/src/TrackerWatcherCommand.h
index 73887f8a..99ff91b4 100644
--- a/src/TrackerWatcherCommand.h
+++ b/src/TrackerWatcherCommand.h
@@ -50,6 +50,50 @@ class PieceStorage;
class BtRuntime;
class BtAnnounce;
class Option;
+class UDPTrackerRequest;
+class UDPTrackerClient;
+
+class AnnRequest {
+public:
+ virtual ~AnnRequest() {}
+ // Returns true if tracker request is finished, regardless of the
+ // outcome.
+ virtual bool stopped() const = 0;
+ // Returns true if tracker request is successful.
+ virtual bool success() const = 0;
+ // Returns true if issuing request is successful.
+ virtual bool issue(DownloadEngine* e) = 0;
+ // Stop this request.
+ virtual void stop(DownloadEngine* e) = 0;
+ // Returns true if processing tracker response is successful.
+ virtual bool processResponse(const SharedHandle<BtAnnounce>& btAnnounce) = 0;
+};
+
+class HTTPAnnRequest:public AnnRequest {
+public:
+ HTTPAnnRequest(const SharedHandle<RequestGroup>& rg);
+ virtual ~HTTPAnnRequest();
+ virtual bool stopped() const;
+ virtual bool success() const;
+ virtual bool issue(DownloadEngine* e);
+ virtual void stop(DownloadEngine* e);
+ virtual bool processResponse(const SharedHandle<BtAnnounce>& btAnnounce);
+private:
+ SharedHandle<RequestGroup> rg_;
+};
+
+class UDPAnnRequest:public AnnRequest {
+public:
+ UDPAnnRequest(const SharedHandle<UDPTrackerRequest>& req);
+ virtual ~UDPAnnRequest();
+ virtual bool stopped() const;
+ virtual bool success() const;
+ virtual bool issue(DownloadEngine* e);
+ virtual void stop(DownloadEngine* e);
+ virtual bool processResponse(const SharedHandle<BtAnnounce>& btAnnounce);
+private:
+ SharedHandle<UDPTrackerRequest> req_;
+};
class TrackerWatcherCommand : public Command
{
@@ -58,6 +102,8 @@ private:
DownloadEngine* e_;
+ SharedHandle<UDPTrackerClient> udpTrackerClient_;
+
SharedHandle<PeerStorage> peerStorage_;
SharedHandle<PieceStorage> pieceStorage_;
@@ -66,16 +112,20 @@ private:
SharedHandle<BtAnnounce> btAnnounce_;
- SharedHandle<RequestGroup> trackerRequestGroup_;
+ SharedHandle<AnnRequest> trackerRequest_;
+
/**
* Returns a command for announce request. Returns 0 if no announce request
* is needed.
*/
- SharedHandle<RequestGroup> createRequestGroup(const std::string& url);
+ SharedHandle<AnnRequest>
+ createHTTPAnnRequest(const std::string& uri);
- std::string getTrackerResponse(const SharedHandle<RequestGroup>& requestGroup);
+ SharedHandle<AnnRequest>
+ createUDPAnnRequest(const std::string& host, uint16_t port,
+ uint16_t localPort);
- void processTrackerResponse(const std::string& response);
+ void addConnection();
const SharedHandle<Option>& getOption() const;
public:
@@ -85,7 +135,7 @@ public:
virtual ~TrackerWatcherCommand();
- SharedHandle<RequestGroup> createAnnounce();
+ SharedHandle<AnnRequest> createAnnounce(DownloadEngine* e);
virtual bool execute();
diff --git a/src/UDPTrackerClient.cc b/src/UDPTrackerClient.cc
new file mode 100644
index 00000000..81d6e8e2
--- /dev/null
+++ b/src/UDPTrackerClient.cc
@@ -0,0 +1,616 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2013 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 "UDPTrackerClient.h"
+
+#include "UDPTrackerRequest.h"
+#include "bittorrent_helper.h"
+#include "util.h"
+#include "LogFactory.h"
+#include "SimpleRandomizer.h"
+#include "fmt.h"
+
+namespace aria2 {
+
+UDPTrackerClient::UDPTrackerClient()
+{}
+
+namespace {
+template<typename InputIterator>
+void failRequest(InputIterator first, InputIterator last, int error)
+{
+ for(; first != last; ++first) {
+ (*first)->state = UDPT_STA_COMPLETE;
+ (*first)->error = error;
+ }
+}
+} // namespace
+
+namespace {
+int32_t generateTransactionId()
+{
+ return SimpleRandomizer::getInstance()->getRandomNumber(INT32_MAX);
+}
+} // namespace
+
+namespace {
+void logInvalidLength(const std::string& remoteAddr, uint16_t remotePort,
+ int action, unsigned long expected, unsigned long actual)
+{
+ A2_LOG_INFO(fmt("UDPT received %s reply from %s:%u invalid length "
+ "expected:%lu, actual:%lu",
+ getUDPTrackerActionStr(action),
+ remoteAddr.c_str(), remotePort, expected, actual));
+}
+} // namespace
+
+namespace {
+void logInvalidTransaction(const std::string& remoteAddr, uint16_t remotePort,
+ int action, int32_t transactionId)
+{
+ A2_LOG_INFO(fmt("UDPT received %s reply from %s:%u invalid transaction_id=%d",
+ getUDPTrackerActionStr(action),
+ remoteAddr.c_str(), remotePort, transactionId));
+}
+} // namespace
+
+namespace {
+void logTooShortLength(const std::string& remoteAddr, uint16_t remotePort,
+ int action,
+ unsigned long minLength, unsigned long actual)
+{
+ A2_LOG_INFO(fmt("UDPT received %s reply from %s:%u length too short "
+ "min:%lu, actual:%lu",
+ getUDPTrackerActionStr(action),
+ remoteAddr.c_str(), remotePort, minLength, actual));
+}
+} // namespace
+
+UDPTrackerClient::~UDPTrackerClient()
+{
+ // Make all contained requests fail
+ int error = UDPT_ERR_SHUTDOWN;
+ failRequest(inflightRequests_.begin(), inflightRequests_.end(), error);
+ failRequest(pendingRequests_.begin(), pendingRequests_.end(), error);
+ failRequest(connectRequests_.begin(), connectRequests_.end(), error);
+}
+
+namespace {
+struct CollectAddrPortMatch {
+ bool operator()(const SharedHandle<UDPTrackerRequest>& req) const
+ {
+ if(req->remoteAddr == remoteAddr && req->remotePort == remotePort) {
+ dest.push_back(req);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ std::vector<SharedHandle<UDPTrackerRequest> >& dest;
+ std::string remoteAddr;
+ uint16_t remotePort;
+ CollectAddrPortMatch(std::vector<SharedHandle<UDPTrackerRequest> >& dest,
+ const std::string& remoteAddr, uint16_t remotePort)
+ : dest(dest), remoteAddr(remoteAddr), remotePort(remotePort)
+ {}
+};
+} // namespace
+
+int UDPTrackerClient::receiveReply
+(const unsigned char* data, size_t length, const std::string& remoteAddr,
+ uint16_t remotePort, const Timer& now)
+{
+ int32_t action = bittorrent::getIntParam(data, 0);
+ switch(action) {
+ case UDPT_ACT_CONNECT: {
+ if(length != 16) {
+ logInvalidLength(remoteAddr, remotePort, action, 16, length);
+ return -1;
+ }
+ int32_t transactionId = bittorrent::getIntParam(data, 4);
+ SharedHandle<UDPTrackerRequest> req =
+ findInflightRequest(remoteAddr, remotePort, transactionId, true);
+ if(!req) {
+ logInvalidTransaction(remoteAddr, remotePort, action, transactionId);
+ return -1;
+ }
+ req->state = UDPT_STA_COMPLETE;
+
+ int64_t connectionId = bittorrent::getLLIntParam(data, 8);
+ A2_LOG_INFO(fmt("UDPT received CONNECT reply from %s:%u transaction_id=%u,"
+ "connection_id=%"PRId64, remoteAddr.c_str(), remotePort,
+ transactionId, connectionId));
+ UDPTrackerConnection c(UDPT_CST_CONNECTED, connectionId, now);
+ connectionIdCache_[std::make_pair(remoteAddr, remotePort)] = c;
+ // Now we have connecion ID, push requests which are waiting for
+ // it.
+ std::vector<SharedHandle<UDPTrackerRequest> > reqs;
+ connectRequests_.erase(std::remove_if
+ (connectRequests_.begin(), connectRequests_.end(),
+ CollectAddrPortMatch(reqs, remoteAddr, remotePort)),
+ connectRequests_.end());
+ pendingRequests_.insert(pendingRequests_.begin(),
+ reqs.begin(), reqs.end());
+ break;
+ }
+ case UDPT_ACT_ANNOUNCE: {
+ if(length < 20) {
+ logTooShortLength(remoteAddr, remotePort, action, 20, length);
+ return - 1;
+ }
+ int32_t transactionId = bittorrent::getIntParam(data, 4);
+ SharedHandle<UDPTrackerRequest> req =
+ findInflightRequest(remoteAddr, remotePort, transactionId, true);
+ if(!req) {
+ logInvalidTransaction(remoteAddr, remotePort, action, transactionId);
+ return -1;
+ }
+ req->state = UDPT_STA_COMPLETE;
+
+ req->reply.reset(new UDPTrackerReply());
+ req->reply->action = action;
+ req->reply->transactionId = transactionId;
+ req->reply->interval = bittorrent::getIntParam(data, 8);
+ req->reply->leechers = bittorrent::getIntParam(data, 12);
+ req->reply->seeders = bittorrent::getIntParam(data, 16);
+
+ int numPeers = 0;
+ for(size_t i = 20; i < length; i += 6) {
+ std::pair<std::string, uint16_t> hostport =
+ bittorrent::unpackcompact(data+i, AF_INET);
+ if(!hostport.first.empty()) {
+ req->reply->peers.push_back(hostport);
+ ++numPeers;
+ }
+ }
+
+ A2_LOG_INFO(fmt("UDPT received ANNOUNCE reply from %s:%u transaction_id=%u,"
+ "connection_id=%"PRId64", event=%s, infohash=%s, "
+ "interval=%d, leechers=%d, "
+ "seeders=%d, num_peers=%d", remoteAddr.c_str(), remotePort,
+ transactionId, req->connectionId,
+ getUDPTrackerEventStr(req->event),
+ util::toHex(req->infohash).c_str(),
+ req->reply->interval, req->reply->leechers,
+ req->reply->seeders, numPeers));
+ break;
+ }
+ case UDPT_ACT_ERROR: {
+ if(length < 8) {
+ logTooShortLength(remoteAddr, remotePort, action, 8, length);
+ return -1;
+ }
+ int32_t transactionId = bittorrent::getIntParam(data, 4);
+ SharedHandle<UDPTrackerRequest> req =
+ findInflightRequest(remoteAddr, remotePort, transactionId, true);
+ if(!req) {
+ logInvalidTransaction(remoteAddr, remotePort, action, transactionId);
+ return -1;
+ }
+ std::string errorString(data+8, data+length);
+ errorString = util::encodeNonUtf8(errorString);
+
+ req->state = UDPT_STA_COMPLETE;
+ req->error = UDPT_ERR_TRACKER;
+
+ A2_LOG_INFO(fmt("UDPT received ERROR reply from %s:%u transaction_id=%u,"
+ "connection_id=%"PRId64", action=%d, error_string=%s",
+ remoteAddr.c_str(),
+ remotePort, transactionId, req->connectionId, action,
+ errorString.c_str()));
+ if(req->action == UDPT_ACT_CONNECT) {
+ failConnect(req->remoteAddr, req->remotePort, UDPT_ERR_TRACKER);
+ }
+ break;
+ }
+ case UDPT_ACT_SCRAPE:
+ A2_LOG_INFO("unexpected scrape action reply");
+ return -1;
+ default:
+ A2_LOG_INFO("unknown action reply");
+ return -1;
+ }
+ return 0;
+}
+
+ssize_t UDPTrackerClient::createRequest
+(unsigned char* data, size_t length, std::string& remoteAddr,
+ uint16_t& remotePort, const Timer& now)
+{
+ if(pendingRequests_.empty()) {
+ return -1;
+ }
+ while(!pendingRequests_.empty()) {
+ const SharedHandle<UDPTrackerRequest>& req = pendingRequests_.front();
+ if(req->action == UDPT_ACT_CONNECT) {
+ ssize_t rv;
+ rv = createUDPTrackerConnect(data, length, remoteAddr, remotePort, req);
+ return rv;
+ }
+ UDPTrackerConnection* c = getConnectionId(req->remoteAddr,
+ req->remotePort,
+ now);
+ if(!c) {
+ SharedHandle<UDPTrackerRequest> creq(new UDPTrackerRequest());
+ creq->action = UDPT_ACT_CONNECT;
+ creq->remoteAddr = req->remoteAddr;
+ creq->remotePort = req->remotePort;
+ creq->transactionId = generateTransactionId();
+ pendingRequests_.push_front(creq);
+ ssize_t rv;
+ rv = createUDPTrackerConnect(data, length, remoteAddr, remotePort, creq);
+ return rv;
+ }
+ if(c->state == UDPT_CST_CONNECTING) {
+ connectRequests_.push_back(req);
+ pendingRequests_.pop_front();
+ continue;
+ }
+ req->connectionId = c->connectionId;
+ req->transactionId = generateTransactionId();
+ ssize_t rv;
+ rv = createUDPTrackerAnnounce(data, length, remoteAddr, remotePort, req);
+ return rv;
+ }
+ return -1;
+}
+
+void UDPTrackerClient::requestSent(const Timer& now)
+{
+ if(pendingRequests_.empty()) {
+ A2_LOG_WARN("pendingRequests_ is empty");
+ return;
+ }
+ const SharedHandle<UDPTrackerRequest>& req = pendingRequests_.front();
+ switch(req->action) {
+ case UDPT_ACT_CONNECT:
+ A2_LOG_INFO(fmt("UDPT sent CONNECT to %s:%u transaction_id=%u",
+ req->remoteAddr.c_str(), req->remotePort,
+ req->transactionId));
+ break;
+ case UDPT_ACT_ANNOUNCE:
+ A2_LOG_INFO(fmt("UDPT sent ANNOUNCE to %s:%u transaction_id=%u, "
+ "connection_id=%"PRId64", event=%s, infohash=%s",
+ req->remoteAddr.c_str(), req->remotePort,
+ req->transactionId, req->connectionId,
+ getUDPTrackerEventStr(req->event),
+ util::toHex(req->infohash).c_str()));
+ break;
+ default:
+ // unreachable
+ assert(0);
+ }
+ req->dispatched = now;
+ switch(req->action) {
+ case UDPT_ACT_CONNECT: {
+ connectionIdCache_[std::make_pair(req->remoteAddr, req->remotePort)]
+ = UDPTrackerConnection();
+ break;
+ }
+ }
+ inflightRequests_.push_back(req);
+ pendingRequests_.pop_front();
+}
+
+void UDPTrackerClient::requestFail(int error)
+{
+ if(pendingRequests_.empty()) {
+ A2_LOG_WARN("pendingRequests_ is empty");
+ return;
+ }
+ const SharedHandle<UDPTrackerRequest>& req = pendingRequests_.front();
+ switch(req->action) {
+ case UDPT_ACT_CONNECT:
+ A2_LOG_INFO(fmt("UDPT fail CONNECT to %s:%u transaction_id=%u",
+ req->remoteAddr.c_str(), req->remotePort,
+ req->transactionId));
+ failConnect(req->remoteAddr, req->remotePort, error);
+ break;
+ case UDPT_ACT_ANNOUNCE:
+ A2_LOG_INFO(fmt("UDPT fail ANNOUNCE to %s:%u transaction_id=%u, "
+ "connection_id=%"PRId64", event=%s, infohash=%s",
+ req->remoteAddr.c_str(), req->remotePort,
+ req->transactionId, req->connectionId,
+ getUDPTrackerEventStr(req->event),
+ util::toHex(req->infohash).c_str()));
+ break;
+ default:
+ // unreachable
+ assert(0);
+ }
+ req->state = UDPT_STA_COMPLETE;
+ req->error = error;
+ pendingRequests_.pop_front();
+}
+
+void UDPTrackerClient::addRequest(const SharedHandle<UDPTrackerRequest>& req)
+{
+ req->state = UDPT_STA_PENDING;
+ req->error = UDPT_ERR_SUCCESS;
+ pendingRequests_.push_back(req);
+}
+
+namespace {
+struct TimeoutCheck {
+ bool operator()(const SharedHandle<UDPTrackerRequest>& req) const
+ {
+ int t = req->dispatched.difference(now);
+ if(req->failCount == 0) {
+ if(t >= 15) {
+ switch(req->action) {
+ case UDPT_ACT_CONNECT:
+ A2_LOG_INFO(fmt("UDPT resend CONNECT to %s:%u transaction_id=%u",
+ req->remoteAddr.c_str(), req->remotePort,
+ req->transactionId));
+ break;
+ case UDPT_ACT_ANNOUNCE:
+ A2_LOG_INFO(fmt("UDPT resend ANNOUNCE to %s:%u transaction_id=%u, "
+ "connection_id=%"PRId64", event=%s, infohash=%s",
+ req->remoteAddr.c_str(), req->remotePort,
+ req->transactionId, req->connectionId,
+ getUDPTrackerEventStr(req->event),
+ util::toHex(req->infohash).c_str()));
+ break;
+ default:
+ // unreachable
+ assert(0);
+ }
+ ++req->failCount;
+ dest.push_back(req);
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ if(t >= 60) {
+ switch(req->action) {
+ case UDPT_ACT_CONNECT:
+ A2_LOG_INFO(fmt("UDPT timeout CONNECT to %s:%u transaction_id=%u",
+ req->remoteAddr.c_str(), req->remotePort,
+ req->transactionId));
+ client->failConnect(req->remoteAddr, req->remotePort,
+ UDPT_ERR_TIMEOUT);
+ break;
+ case UDPT_ACT_ANNOUNCE:
+ A2_LOG_INFO(fmt("UDPT timeout ANNOUNCE to %s:%u transaction_id=%u, "
+ "connection_id=%"PRId64", event=%s, infohash=%s",
+ req->remoteAddr.c_str(), req->remotePort,
+ req->transactionId, req->connectionId,
+ getUDPTrackerEventStr(req->event),
+ util::toHex(req->infohash).c_str()));
+ break;
+ default:
+ // unreachable
+ assert(0);
+ }
+ ++req->failCount;
+ req->state = UDPT_STA_COMPLETE;
+ req->error = UDPT_ERR_TIMEOUT;
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+ std::vector<SharedHandle<UDPTrackerRequest> >& dest;
+ UDPTrackerClient* client;
+ const Timer& now;
+ TimeoutCheck(std::vector<SharedHandle<UDPTrackerRequest> >& dest,
+ UDPTrackerClient* client,
+ const Timer& now)
+ : dest(dest), client(client), now(now)
+ {}
+};
+} // namespace
+
+void UDPTrackerClient::handleTimeout(const Timer& now)
+{
+ std::vector<SharedHandle<UDPTrackerRequest> > dest;
+ inflightRequests_.erase(std::remove_if(inflightRequests_.begin(),
+ inflightRequests_.end(),
+ TimeoutCheck(dest, this, now)),
+ inflightRequests_.end());
+ pendingRequests_.insert(pendingRequests_.begin(), dest.begin(), dest.end());
+}
+
+SharedHandle<UDPTrackerRequest> UDPTrackerClient::findInflightRequest
+(const std::string& remoteAddr, uint16_t remotePort, int32_t transactionId,
+ bool remove)
+{
+ SharedHandle<UDPTrackerRequest> res;
+ for(std::deque<SharedHandle<UDPTrackerRequest> >::iterator i =
+ inflightRequests_.begin(), eoi = inflightRequests_.end(); i != eoi;
+ ++i) {
+ if((*i)->remoteAddr == remoteAddr && (*i)->remotePort == remotePort &&
+ (*i)->transactionId == transactionId) {
+ res = *i;
+ if(remove) {
+ inflightRequests_.erase(i);
+ }
+ break;
+ }
+ }
+ return res;
+}
+
+UDPTrackerConnection* UDPTrackerClient::getConnectionId
+(const std::string& remoteAddr, uint16_t remotePort, const Timer& now)
+{
+ std::map<std::pair<std::string, uint16_t>,
+ UDPTrackerConnection>::iterator i =
+ connectionIdCache_.find(std::make_pair(remoteAddr, remotePort));
+ if(i == connectionIdCache_.end()) {
+ return 0;
+ }
+ if((*i).second.state == UDPT_CST_CONNECTED &&
+ (*i).second.lastUpdated.difference(now) > 60) {
+ connectionIdCache_.erase(i);
+ return 0;
+ } else {
+ return &(*i).second;
+ }
+}
+
+namespace {
+struct FailConnectDelete {
+ bool operator()(const SharedHandle<UDPTrackerRequest>& req) const
+ {
+ if(req->action == UDPT_ACT_ANNOUNCE &&
+ req->remoteAddr == remoteAddr && req->remotePort == remotePort) {
+ A2_LOG_INFO(fmt("Force fail infohash=%s",
+ util::toHex(req->infohash).c_str()));
+ req->state = UDPT_STA_COMPLETE;
+ req->error = error;
+ return true;
+ } else {
+ return false;
+ }
+ }
+ std::string remoteAddr;
+ uint16_t remotePort;
+ int error;
+ FailConnectDelete(const std::string& remoteAddr, uint16_t remotePort,
+ int error)
+ : remoteAddr(remoteAddr), remotePort(remotePort), error(error)
+ {}
+};
+} // namespace
+
+void UDPTrackerClient::failConnect(const std::string& remoteAddr,
+ uint16_t remotePort, int error)
+{
+ connectionIdCache_.erase(std::make_pair(remoteAddr, remotePort));
+ // Fail all requests which are waiting for connecion ID of the host.
+ connectRequests_.erase(std::remove_if(connectRequests_.begin(),
+ connectRequests_.end(),
+ FailConnectDelete
+ (remoteAddr, remotePort, error)),
+ connectRequests_.end());
+ pendingRequests_.erase(std::remove_if(pendingRequests_.begin(),
+ pendingRequests_.end(),
+ FailConnectDelete
+ (remoteAddr, remotePort, error)),
+ pendingRequests_.end());
+}
+
+void UDPTrackerClient::failAll()
+{
+ int error = UDPT_ERR_SHUTDOWN;
+ failRequest(inflightRequests_.begin(), inflightRequests_.end(), error);
+ failRequest(pendingRequests_.begin(), pendingRequests_.end(), error);
+ failRequest(connectRequests_.begin(), connectRequests_.end(), error);
+}
+
+void UDPTrackerClient::increaseWatchers()
+{
+ ++numWatchers_;
+}
+
+void UDPTrackerClient::decreaseWatchers()
+{
+ --numWatchers_;
+}
+
+ssize_t createUDPTrackerConnect
+(unsigned char* data, size_t length,
+ std::string& remoteAddr, uint16_t& remotePort,
+ const SharedHandle<UDPTrackerRequest>& req)
+{
+ assert(length >= 16);
+ remoteAddr = req->remoteAddr;
+ remotePort = req->remotePort;
+ bittorrent::setLLIntParam(data, UDPT_INITIAL_CONNECTION_ID);
+ bittorrent::setIntParam(data+8, req->action);
+ bittorrent::setIntParam(data+12, req->transactionId);
+ return 16;
+}
+
+ssize_t createUDPTrackerAnnounce
+(unsigned char* data, size_t length,
+ std::string& remoteAddr, uint16_t& remotePort,
+ const SharedHandle<UDPTrackerRequest>& req)
+{
+ assert(length >= 100);
+ remoteAddr = req->remoteAddr;
+ remotePort = req->remotePort;
+ bittorrent::setLLIntParam(data, req->connectionId);
+ bittorrent::setIntParam(data+8, req->action);
+ bittorrent::setIntParam(data+12, req->transactionId);
+ memcpy(data+16, req->infohash.c_str(), req->infohash.size());
+ memcpy(data+36, req->peerId.c_str(), req->peerId.size());
+ bittorrent::setLLIntParam(data+56, req->downloaded);
+ bittorrent::setLLIntParam(data+64, req->left);
+ bittorrent::setLLIntParam(data+72, req->uploaded);
+ bittorrent::setIntParam(data+80, req->event);
+ // ip is already network-byte order
+ memcpy(data+84, &req->ip, sizeof(req->ip));
+ bittorrent::setIntParam(data+88, req->key);
+ bittorrent::setIntParam(data+92, req->numWant);
+ bittorrent::setShortIntParam(data+96, req->port);
+ // extensions is always 0
+ bittorrent::setShortIntParam(data+98, 0);
+ return 100;
+}
+
+const char* getUDPTrackerActionStr(int action)
+{
+ switch(action) {
+ case UDPT_ACT_CONNECT:
+ return "CONNECT";
+ case UDPT_ACT_ANNOUNCE:
+ return "ANNOUNCE";
+ case UDPT_ACT_ERROR:
+ return "ERROR";
+ default:
+ return "(unknown)";
+ }
+}
+
+const char* getUDPTrackerEventStr(int event)
+{
+ switch(event) {
+ case UDPT_EVT_NONE:
+ return "NONE";
+ case UDPT_EVT_COMPLETED:
+ return "COMPLETED";
+ case UDPT_EVT_STARTED:
+ return "STARTED";
+ case UDPT_EVT_STOPPED:
+ return "STOPPED";
+ default:
+ return "(unknown)";
+ }
+}
+
+} // namespace aria2
diff --git a/src/UDPTrackerClient.h b/src/UDPTrackerClient.h
new file mode 100644
index 00000000..a1e9fc9a
--- /dev/null
+++ b/src/UDPTrackerClient.h
@@ -0,0 +1,171 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2013 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_UDP_TRACKER_CLIENT_H
+#define D_UDP_TRACKER_CLIENT_H
+
+#include "common.h"
+
+#include <string>
+#include <deque>
+#include <map>
+
+#include "SharedHandle.h"
+#include "TimerA2.h"
+
+namespace aria2 {
+
+#define UDPT_INITIAL_CONNECTION_ID 0x41727101980LL
+
+class UDPTrackerRequest;
+
+enum UDPTrackerConnectionState {
+ UDPT_CST_CONNECTING,
+ UDPT_CST_CONNECTED
+};
+
+struct UDPTrackerConnection {
+ int state;
+ int64_t connectionId;
+ Timer lastUpdated;
+ UDPTrackerConnection()
+ : state(UDPT_CST_CONNECTING),
+ connectionId(UDPT_INITIAL_CONNECTION_ID),
+ lastUpdated(0)
+ {}
+ UDPTrackerConnection(int state, int64_t connectionId,
+ const Timer& lastUpdated)
+ : state(state),
+ connectionId(connectionId),
+ lastUpdated(lastUpdated)
+ {}
+};
+
+class UDPTrackerClient {
+public:
+ UDPTrackerClient();
+ ~UDPTrackerClient();
+
+ int receiveReply
+ (const unsigned char* data, size_t length, const std::string& remoteAddr,
+ uint16_t remotePort, const Timer& now);
+
+ // Creates data frame for the next pending request. This function
+ // always processes first entry of pendingRequests_. If the data is
+ // sent successfully, call requestSent(). Otherwise call
+ // requestFail().
+ ssize_t createRequest
+ (unsigned char* data, size_t length, std::string& remoteAddr,
+ uint16_t& remotePort, const Timer& now);
+
+ // Tells this object that first entry of pendingRequests_ is
+ // successfully sent.
+ void requestSent(const Timer& now);
+ // Tells this object that first entry of pendingRequests_ is not
+ // successfully sent. The |error| should indicate error situation.
+ void requestFail(int error);
+
+ void addRequest(const SharedHandle<UDPTrackerRequest>& req);
+
+ // Handles timeout for inflight requests.
+ void handleTimeout(const Timer& now);
+
+ const std::deque<SharedHandle<UDPTrackerRequest> >&
+ getPendingRequests() const
+ {
+ return pendingRequests_;
+ }
+ const std::deque<SharedHandle<UDPTrackerRequest> >&
+ getConnectRequests() const
+ {
+ return connectRequests_;
+ }
+ const std::deque<SharedHandle<UDPTrackerRequest> >&
+ getInflightRequests() const
+ {
+ return inflightRequests_;
+ }
+
+ bool noRequest() const
+ {
+ return pendingRequests_.empty() && connectRequests_.empty() &&
+ getInflightRequests().empty();
+ }
+
+ // Makes all contained requests fail.
+ void failAll();
+
+ int getNumWatchers() const
+ {
+ return numWatchers_;
+ }
+
+ void increaseWatchers();
+ void decreaseWatchers();
+
+ // Actually private function, but made public, to be used by unnamed
+ // function.
+ void failConnect(const std::string& remoteAddr, uint16_t remotePort,
+ int error);
+private:
+ SharedHandle<UDPTrackerRequest> findInflightRequest
+ (const std::string& remoteAddr, uint16_t remotePort, int32_t transactionId,
+ bool remove);
+
+ UDPTrackerConnection* getConnectionId
+ (const std::string& remoteAddr, uint16_t remotePort, const Timer& now);
+
+ std::map<std::pair<std::string, uint16_t>,
+ UDPTrackerConnection> connectionIdCache_;
+ std::deque<SharedHandle<UDPTrackerRequest> > inflightRequests_;
+ std::deque<SharedHandle<UDPTrackerRequest> > pendingRequests_;
+ std::deque<SharedHandle<UDPTrackerRequest> > connectRequests_;
+ int numWatchers_;
+};
+
+ssize_t createUDPTrackerConnect
+(unsigned char* data, size_t length, std::string& remoteAddr,
+ uint16_t& remotePort, const SharedHandle<UDPTrackerRequest>& req);
+
+ssize_t createUDPTrackerAnnounce
+(unsigned char* data, size_t length, std::string& remoteAddr,
+ uint16_t& remotePort, const SharedHandle<UDPTrackerRequest>& req);
+
+const char* getUDPTrackerActionStr(int action);
+
+const char* getUDPTrackerEventStr(int event);
+
+} // namespace aria2
+
+#endif // D_UDP_TRACKER_CLIENT_H
diff --git a/src/UDPTrackerRequest.cc b/src/UDPTrackerRequest.cc
new file mode 100644
index 00000000..94ae0f47
--- /dev/null
+++ b/src/UDPTrackerRequest.cc
@@ -0,0 +1,51 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2013 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 "UDPTrackerRequest.h"
+
+namespace aria2 {
+
+UDPTrackerReply::UDPTrackerReply()
+ : action(0), transactionId(0), interval(0), leechers(0), seeders(0)
+{}
+
+UDPTrackerRequest::UDPTrackerRequest()
+ : remotePort(0), action(UDPT_ACT_CONNECT), transactionId(0), downloaded(0),
+ left(0), uploaded(0), event(UDPT_EVT_NONE), ip(0), key(0), numWant(0),
+ port(0), extensions(0), state(UDPT_STA_PENDING), error(UDPT_ERR_SUCCESS),
+ dispatched(0),
+ failCount(0)
+{}
+
+} // namespace aria2
diff --git a/src/UDPTrackerRequest.h b/src/UDPTrackerRequest.h
new file mode 100644
index 00000000..8012b182
--- /dev/null
+++ b/src/UDPTrackerRequest.h
@@ -0,0 +1,112 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2013 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_UDP_TRACKER_REQUEST_H
+#define D_UDP_TRACKER_REQUEST_H
+
+#include "common.h"
+
+#include <string>
+#include <vector>
+
+#include "SharedHandle.h"
+#include "TimerA2.h"
+
+namespace aria2 {
+
+enum UDPTrackerAction {
+ UDPT_ACT_CONNECT = 0,
+ UDPT_ACT_ANNOUNCE = 1,
+ UDPT_ACT_SCRAPE = 2,
+ UDPT_ACT_ERROR = 3
+};
+
+enum UDPTrackerError {
+ UDPT_ERR_SUCCESS,
+ UDPT_ERR_TRACKER,
+ UDPT_ERR_TIMEOUT,
+ UDPT_ERR_NETWORK,
+ UDPT_ERR_SHUTDOWN
+};
+
+enum UDPTrackerState {
+ UDPT_STA_PENDING,
+ UDPT_STA_COMPLETE
+};
+
+enum UDPTrackerEvent {
+ UDPT_EVT_NONE = 0,
+ UDPT_EVT_COMPLETED = 1,
+ UDPT_EVT_STARTED = 2,
+ UDPT_EVT_STOPPED = 3
+};
+
+struct UDPTrackerReply {
+ int32_t action;
+ int32_t transactionId;
+ int32_t interval;
+ int32_t leechers;
+ int32_t seeders;
+ std::vector<std::pair<std::string, uint16_t> > peers;
+ UDPTrackerReply();
+};
+
+struct UDPTrackerRequest {
+ std::string remoteAddr;
+ uint16_t remotePort;
+ int64_t connectionId;
+ int32_t action;
+ int32_t transactionId;
+ std::string infohash;
+ std::string peerId;
+ int64_t downloaded;
+ int64_t left;
+ int64_t uploaded;
+ int32_t event;
+ uint32_t ip;
+ uint32_t key;
+ int32_t numWant;
+ uint16_t port;
+ uint16_t extensions;
+ int state;
+ int error;
+ Timer dispatched;
+ int failCount;
+ SharedHandle<UDPTrackerReply> reply;
+ UDPTrackerRequest();
+};
+
+} // namespace aria2
+
+#endif // D_UDP_TRACKER_REQUEST_H
diff --git a/src/bittorrent_helper.cc b/src/bittorrent_helper.cc
index f24f4ae7..7924b2db 100644
--- a/src/bittorrent_helper.cc
+++ b/src/bittorrent_helper.cc
@@ -731,6 +731,13 @@ uint8_t getId(const unsigned char* msg)
return msg[0];
}
+uint64_t getLLIntParam(const unsigned char* msg, size_t pos)
+{
+ uint64_t nParam;
+ memcpy(&nParam, msg+pos, sizeof(nParam));
+ return ntoh64(nParam);
+}
+
uint32_t getIntParam(const unsigned char* msg, size_t pos)
{
uint32_t nParam;
@@ -798,6 +805,12 @@ void checkBitfield
}
}
+void setLLIntParam(unsigned char* dest, uint64_t param)
+{
+ uint64_t nParam = hton64(param);
+ memcpy(dest, &nParam, sizeof(nParam));
+}
+
void setIntParam(unsigned char* dest, uint32_t param)
{
uint32_t nParam = htonl(param);
diff --git a/src/bittorrent_helper.h b/src/bittorrent_helper.h
index a8cef945..95d0d6a6 100644
--- a/src/bittorrent_helper.h
+++ b/src/bittorrent_helper.h
@@ -160,6 +160,11 @@ getInfoHash(const SharedHandle<DownloadContext>& downloadContext);
std::string
getInfoHashString(const SharedHandle<DownloadContext>& downloadContext);
+// Returns 8bytes unsigned integer located at offset pos. The integer
+// in msg is network byte order. This function converts it into host
+// byte order and returns it.
+uint64_t getLLIntParam(const unsigned char* msg, size_t pos);
+
// Returns 4bytes unsigned integer located at offset pos. The integer
// in msg is network byte order. This function converts it into host
// byte order and returns it.
@@ -172,6 +177,10 @@ uint16_t getShortIntParam(const unsigned char* msg, size_t pos);
// Put param at location pointed by dest. param is converted into
// network byte order.
+void setLLIntParam(unsigned char* dest, uint64_t param);
+
+// Put param at location pointed by dest. param is converted into
+// network byte order.
void setIntParam(unsigned char* dest, uint32_t param);
// Put param at location pointed by dest. param is converted into