/* */ #include "DefaultBtAnnounce.h" #include "LogFactory.h" #include "Logger.h" #include "util.h" #include "prefs.h" #include "DlAbortEx.h" #include "message.h" #include "SimpleRandomizer.h" #include "DownloadContext.h" #include "PieceStorage.h" #include "BtRuntime.h" #include "PeerStorage.h" #include "Peer.h" #include "Option.h" #include "fmt.h" #include "A2STR.h" #include "bencode2.h" #include "bittorrent_helper.h" #include "wallclock.h" #include "uri.h" #include "UDPTrackerRequest.h" #include "SocketCore.h" namespace aria2 { DefaultBtAnnounce::DefaultBtAnnounce (DownloadContext* downloadContext, const Option* option) : downloadContext_{downloadContext}, trackers_(0), prevAnnounceTimer_(0), interval_(DEFAULT_ANNOUNCE_INTERVAL), minInterval_(DEFAULT_ANNOUNCE_INTERVAL), userDefinedInterval_(0), complete_(0), incomplete_(0), announceList_(bittorrent::getTorrentAttrs(downloadContext)->announceList), option_(option), randomizer_(SimpleRandomizer::getInstance().get()), tcpPort_(0) {} DefaultBtAnnounce::~DefaultBtAnnounce() { } bool DefaultBtAnnounce::isDefaultAnnounceReady() { return (trackers_ == 0 && prevAnnounceTimer_. difference(global::wallclock()) >= (userDefinedInterval_==0? minInterval_:userDefinedInterval_) && !announceList_.allTiersFailed()); } bool DefaultBtAnnounce::isStoppedAnnounceReady() { return (trackers_ == 0 && btRuntime_->isHalt() && announceList_.countStoppedAllowedTier()); } bool DefaultBtAnnounce::isCompletedAnnounceReady() { return (trackers_ == 0 && pieceStorage_->allDownloadFinished() && announceList_.countCompletedAllowedTier()); } bool DefaultBtAnnounce::isAnnounceReady() { return isStoppedAnnounceReady() || isCompletedAnnounceReady() || isDefaultAnnounceReady(); } namespace { bool uriHasQuery(const std::string& uri) { uri_split_result us; if(uri_split(&us, uri.c_str()) == 0) { return (us.field_set & (1 << USR_QUERY)) && us.fields[USR_QUERY].len > 0; } else { return false; } } } // namespace bool DefaultBtAnnounce::adjustAnnounceList() { if(isStoppedAnnounceReady()) { if(!announceList_.currentTierAcceptsStoppedEvent()) { announceList_.moveToStoppedAllowedTier(); } announceList_.setEvent(AnnounceTier::STOPPED); } else if(isCompletedAnnounceReady()) { if(!announceList_.currentTierAcceptsCompletedEvent()) { announceList_.moveToCompletedAllowedTier(); } announceList_.setEvent(AnnounceTier::COMPLETED); } else if(isDefaultAnnounceReady()) { // If download completed before "started" event is sent to a tracker, // we change the event to something else to prevent us from // sending "completed" event. if(pieceStorage_->allDownloadFinished() && announceList_.getEvent() == AnnounceTier::STARTED) { announceList_.setEvent(AnnounceTier::STARTED_AFTER_COMPLETION); } } else { return false; } return true; } std::string DefaultBtAnnounce::getAnnounceUrl() { if(!adjustAnnounceList()) { return A2STR::NIL; } int numWant = 50; if(!btRuntime_->lessThanMinPeers() || btRuntime_->isHalt()) { numWant = 0; } NetStat& stat = downloadContext_->getNetStat(); int64_t left = pieceStorage_->getTotalLength()-pieceStorage_->getCompletedLength(); // Use last 8 bytes of peer ID as a key const size_t keyLen = 8; std::string uri = announceList_.getAnnounce(); uri += uriHasQuery(uri) ? "&" : "?"; uri += fmt("info_hash=%s&" "peer_id=%s&" "uploaded=%" PRId64 "&" "downloaded=%" PRId64 "&" "left=%" PRId64 "&" "compact=1&" "key=%s&" "numwant=%d&" "no_peer_id=1", util::torrentPercentEncode (bittorrent::getInfoHash(downloadContext_), INFO_HASH_LENGTH).c_str(), util::torrentPercentEncode (bittorrent::getStaticPeerId(), PEER_ID_LENGTH).c_str(), stat.getSessionUploadLength(), stat.getSessionDownloadLength(), left, util::torrentPercentEncode (bittorrent::getStaticPeerId()+PEER_ID_LENGTH-keyLen, keyLen).c_str(), numWant); if(tcpPort_) { uri += fmt("&port=%u", tcpPort_); } const char* event = announceList_.getEventString(); if(event[0]) { uri += "&event="; uri += event; } if(!trackerId_.empty()) { uri += "&trackerid="; uri += util::torrentPercentEncode(trackerId_); } if(option_->getAsBool(PREF_BT_REQUIRE_CRYPTO)) { uri += "&requirecrypto=1"; } else { uri += "&supportcrypto=1"; } if(!option_->blank(PREF_BT_EXTERNAL_IP)) { uri += "&ip="; uri += option_->get(PREF_BT_EXTERNAL_IP); } return uri; } std::shared_ptr DefaultBtAnnounce::createUDPTrackerRequest (const std::string& remoteAddr, uint16_t remotePort, uint16_t localPort) { if(!adjustAnnounceList()) { return nullptr; } NetStat& stat = downloadContext_->getNetStat(); int64_t left = pieceStorage_->getTotalLength()-pieceStorage_->getCompletedLength(); std::shared_ptr 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_; } void DefaultBtAnnounce::announceSuccess() { trackers_ = 0; announceList_.announceSuccess(); } void DefaultBtAnnounce::announceFailure() { trackers_ = 0; announceList_.announceFailure(); } bool DefaultBtAnnounce::isAllAnnounceFailed() { return announceList_.allTiersFailed(); } void DefaultBtAnnounce::resetAnnounce() { prevAnnounceTimer_ = global::wallclock(); announceList_.resetTier(); } void DefaultBtAnnounce::processAnnounceResponse(const unsigned char* trackerResponse, size_t trackerResponseLength) { A2_LOG_DEBUG("Now processing tracker response."); auto decodedValue = bencode2::decode(trackerResponse, trackerResponseLength); const Dict* dict = downcast(decodedValue); if(!dict) { throw DL_ABORT_EX(MSG_NULL_TRACKER_RESPONSE); } const String* failure = downcast(dict->get(BtAnnounce::FAILURE_REASON)); if(failure) { throw DL_ABORT_EX (fmt(EX_TRACKER_FAILURE, failure->s().c_str())); } const String* warn = downcast(dict->get(BtAnnounce::WARNING_MESSAGE)); if(warn) { A2_LOG_WARN(fmt(MSG_TRACKER_WARNING_MESSAGE, warn->s().c_str())); } const String* tid = downcast(dict->get(BtAnnounce::TRACKER_ID)); if(tid) { trackerId_ = tid->s(); A2_LOG_DEBUG(fmt("Tracker ID:%s", trackerId_.c_str())); } const Integer* ival = downcast(dict->get(BtAnnounce::INTERVAL)); if(ival && ival->i() > 0) { interval_ = ival->i(); A2_LOG_DEBUG(fmt("Interval:%ld", static_cast(interval_))); } const Integer* mival = downcast(dict->get(BtAnnounce::MIN_INTERVAL)); if(mival && mival->i() > 0) { minInterval_ = mival->i(); A2_LOG_DEBUG(fmt("Min interval:%ld", static_cast(minInterval_))); minInterval_ = std::min(minInterval_, interval_); } else { // Use interval as a minInterval if minInterval is not supplied. minInterval_ = interval_; } const Integer* comp = downcast(dict->get(BtAnnounce::COMPLETE)); if(comp) { complete_ = comp->i(); A2_LOG_DEBUG(fmt("Complete:%d", complete_)); } const Integer* incomp = downcast(dict->get(BtAnnounce::INCOMPLETE)); if(incomp) { incomplete_ = incomp->i(); A2_LOG_DEBUG(fmt("Incomplete:%d", incomplete_)); } auto peerData = dict->get(BtAnnounce::PEERS); if(!peerData) { A2_LOG_INFO(MSG_NO_PEER_LIST_RECEIVED); } else { if(!btRuntime_->isHalt() && btRuntime_->lessThanMinPeers()) { std::vector > peers; bittorrent::extractPeer(peerData, AF_INET, std::back_inserter(peers)); peerStorage_->addPeer(peers); } } auto peer6Data = dict->get(BtAnnounce::PEERS6); if(!peer6Data) { A2_LOG_INFO("No peers6 received."); } else { if(!btRuntime_->isHalt() && btRuntime_->lessThanMinPeers()) { std::vector > peers; bittorrent::extractPeer(peer6Data, AF_INET6, std::back_inserter(peers)); peerStorage_->addPeer(peers); } } } void DefaultBtAnnounce::processUDPTrackerResponse (const std::shared_ptr& req) { const std::shared_ptr& 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(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(auto & elem : reply->peers) { peerStorage_->addPeer(std::make_shared(elem.first, elem.second)); } } } bool DefaultBtAnnounce::noMoreAnnounce() { return (trackers_ == 0 && btRuntime_->isHalt() && !announceList_.countStoppedAllowedTier()); } void DefaultBtAnnounce::shuffleAnnounce() { announceList_.shuffle(); } void DefaultBtAnnounce::setRandomizer(Randomizer* randomizer) { randomizer_ = randomizer; } void DefaultBtAnnounce::setBtRuntime(const std::shared_ptr& btRuntime) { btRuntime_ = btRuntime; } void DefaultBtAnnounce::setPieceStorage(const std::shared_ptr& pieceStorage) { pieceStorage_ = pieceStorage; } void DefaultBtAnnounce::setPeerStorage (const std::shared_ptr& peerStorage) { peerStorage_ = peerStorage; } void DefaultBtAnnounce::overrideMinInterval(time_t interval) { minInterval_ = interval; } } // namespace aria2