/* */ #include "HandshakeExtensionMessage.h" #include "Peer.h" #include "util.h" #include "DlAbortEx.h" #include "LogFactory.h" #include "Logger.h" #include "message.h" #include "fmt.h" #include "bencode2.h" #include "DownloadContext.h" #include "bittorrent_helper.h" #include "RequestGroup.h" #include "PieceStorage.h" #include "FileEntry.h" namespace aria2 { const char HandshakeExtensionMessage::EXTENSION_NAME[] = "handshake"; HandshakeExtensionMessage::HandshakeExtensionMessage() : tcpPort_(0), metadataSize_(0) {} HandshakeExtensionMessage::~HandshakeExtensionMessage() {} std::string HandshakeExtensionMessage::getPayload() { Dict dict; if(!clientVersion_.empty()) { dict.put("v", clientVersion_); } if(tcpPort_ > 0) { dict.put("p", Integer::g(tcpPort_)); } SharedHandle extDict = Dict::g(); for(std::map::const_iterator itr = extensions_.begin(), eoi = extensions_.end(); itr != eoi; ++itr) { const std::map::value_type& vt = *itr; extDict->put(vt.first, Integer::g(vt.second)); } dict.put("m", extDict); if(metadataSize_) { dict.put("metadata_size", Integer::g(metadataSize_)); } return bencode2::encode(&dict); } std::string HandshakeExtensionMessage::toString() const { std::string s(fmt("%s client=%s, tcpPort=%u, metadataSize=%lu", getExtensionName(), util::percentEncode(clientVersion_).c_str(), tcpPort_, static_cast(metadataSize_))); for(std::map::const_iterator itr = extensions_.begin(), eoi = extensions_.end(); itr != eoi; ++itr) { const std::map::value_type& vt = *itr; s += fmt(", %s=%u", vt.first.c_str(), vt.second); } return s; } void HandshakeExtensionMessage::doReceivedAction() { if(tcpPort_ > 0) { peer_->setPort(tcpPort_); peer_->setIncomingPeer(false); } for(std::map::const_iterator itr = extensions_.begin(), eoi = extensions_.end(); itr != eoi; ++itr) { const std::map::value_type& vt = *itr; peer_->setExtension(vt.first, vt.second); } SharedHandle attrs = bittorrent::getTorrentAttrs(dctx_); if(attrs->metadata.empty()) { if(!peer_->getExtensionMessageID("ut_metadata")) { // TODO In metadataGetMode, if peer don't support metadata // transfer, should we drop connection? There is a possibility // that peer can still tell us peers using PEX. throw DL_ABORT_EX("Peer doesn't support ut_metadata extension. Goodbye."); } if(metadataSize_ > 0) { if(attrs->metadataSize) { if(metadataSize_ != attrs->metadataSize) { throw DL_ABORT_EX("Wrong metadata_size. Which one is correct!?"); } } else { attrs->metadataSize = metadataSize_; dctx_->getFirstFileEntry()->setLength(metadataSize_); dctx_->markTotalLengthIsKnown(); dctx_->getOwnerRequestGroup()->initPieceStorage(); SharedHandle pieceStorage = dctx_->getOwnerRequestGroup()->getPieceStorage(); // We enter 'end game' mode from the start to get metadata // quickly. pieceStorage->enterEndGame(); } peer_->reconfigureSessionResource(dctx_->getPieceLength(), dctx_->getTotalLength()); peer_->setAllBitfield(); } else { throw DL_ABORT_EX("Peer didn't provide metadata_size." " It seems that it doesn't have whole metadata."); } } } void HandshakeExtensionMessage::setPeer(const SharedHandle& peer) { peer_ = peer; } uint8_t HandshakeExtensionMessage::getExtensionMessageID(const std::string& name) const { std::map::const_iterator i = extensions_.find(name); if(i == extensions_.end()) { return 0; } else { return (*i).second; } } HandshakeExtensionMessageHandle HandshakeExtensionMessage::create(const unsigned char* data, size_t length) { if(length < 1) { throw DL_ABORT_EX (fmt(MSG_TOO_SMALL_PAYLOAD_SIZE, EXTENSION_NAME, static_cast(length))); } HandshakeExtensionMessageHandle msg(new HandshakeExtensionMessage()); A2_LOG_DEBUG(fmt("Creating HandshakeExtensionMessage from %s", util::percentEncode(data, length).c_str())); SharedHandle decoded = bencode2::decode(data+1, length - 1); const Dict* dict = downcast(decoded); if(!dict) { throw DL_ABORT_EX ("Unexpected payload format for extended message handshake"); } const Integer* port = downcast(dict->get("p")); if(port && 0 < port->i() && port->i() < 65536) { msg->tcpPort_ = port->i(); } const String* version = downcast(dict->get("v")); if(version) { msg->clientVersion_ = version->s(); } const Dict* extDict = downcast(dict->get("m")); if(extDict) { for(Dict::ValueType::const_iterator i = extDict->begin(), eoi = extDict->end(); i != eoi; ++i) { const Integer* extId = downcast((*i).second); if(extId) { msg->extensions_[(*i).first] = extId->i(); } } } const Integer* metadataSize = downcast(dict->get("metadata_size")); // Only accept metadata smaller than 1MiB if(metadataSize && metadataSize->i() <= 1024*1024) { msg->metadataSize_ = metadataSize->i(); } return msg; } } // namespace aria2