/* */ #include "HandshakeExtensionMessage.h" #include "Peer.h" #include "util.h" #include "DlAbortEx.h" #include "LogFactory.h" #include "Logger.h" #include "message.h" #include "StringFormat.h" #include "bencode2.h" #include "DownloadContext.h" #include "bittorrent_helper.h" #include "RequestGroup.h" #include "PieceStorage.h" namespace aria2 { const std::string HandshakeExtensionMessage::EXTENSION_NAME = "handshake"; HandshakeExtensionMessage::HandshakeExtensionMessage(): tcpPort_(0), metadataSize_(0), logger_(LogFactory::getInstance()) {} 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 = getExtensionName(); if(!clientVersion_.empty()) { strappend(s, " client=", util::percentEncode(clientVersion_)); } if(tcpPort_ > 0) { strappend(s, ", tcpPort=", util::uitos(tcpPort_)); } if(metadataSize_) { strappend(s, ", metadataSize=", util::uitos(metadataSize_)); } for(std::map::const_iterator itr = extensions_.begin(), eoi = extensions_.end(); itr != eoi; ++itr) { const std::map::value_type& vt = *itr; strappend(s, ", ", vt.first, "=", util::uitos(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() && !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(); pieceStorage->setEndGamePieceNum(0); } } else if(attrs->metadata.empty()) { 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 (StringFormat(MSG_TOO_SMALL_PAYLOAD_SIZE, EXTENSION_NAME.c_str(), length).str()); } HandshakeExtensionMessageHandle msg(new HandshakeExtensionMessage()); if(LogFactory::getInstance()->debug()) { LogFactory::getInstance()->debug ("Creating HandshakeExtensionMessage from %s", util::percentEncode(data, length).c_str()); } SharedHandle decoded = bencode2::decode(data+1, length-1); const Dict* dict = asDict(decoded); if(!dict) { throw DL_ABORT_EX ("Unexpected payload format for extended message handshake"); } const Integer* port = asInteger(dict->get("p")); if(port && 0 < port->i() && port->i() < 65536) { msg->tcpPort_ = port->i(); } const String* version = asString(dict->get("v")); if(version) { msg->clientVersion_ = version->s(); } const Dict* extDict = asDict(dict->get("m")); if(extDict) { for(Dict::ValueType::const_iterator i = extDict->begin(), eoi = extDict->end(); i != eoi; ++i) { const Integer* extId = asInteger((*i).second); if(extId) { msg->extensions_[(*i).first] = extId->i(); } } } const Integer* metadataSize = asInteger(dict->get("metadata_size")); // Only accept metadata smaller than 1MiB if(metadataSize && metadataSize->i() <= 1024*1024) { msg->metadataSize_ = metadataSize->i(); } return msg; } } // namespace aria2