diff --git a/libi2pd_client/Torrents.cpp b/libi2pd_client/Torrents.cpp index 87fab360..2fe3fec4 100644 --- a/libi2pd_client/Torrents.cpp +++ b/libi2pd_client/Torrents.cpp @@ -175,6 +175,16 @@ namespace torrents return s.str (); } + std::string CreateList (const std::vector& items) + { + std::stringstream s; + s << 'l'; + for (const auto& it: items) + s << it; + s << 'e'; + return s.str (); + } + //------------------------------------ bool TorrentFile::Save (size_t offset, const uint8_t * buf, size_t len) @@ -1066,6 +1076,18 @@ namespace torrents return ret; } + std::unordered_set Torrent::GetAllPeers () const + { + std::unordered_set ret; + for (const auto& it: m_TrackerStats) + { + const auto& peers = std::get<0>(it); + for (const auto& it1: peers) + ret.emplace (it1); + } + return ret; + } + uint64_t Torrent::GetNextTrackerRequestTime (size_t trackerID) const { if (trackerID < m_TrackerStats.size ()) return std::get<2>(m_TrackerStats[trackerID]); diff --git a/libi2pd_client/Torrents.h b/libi2pd_client/Torrents.h index 5ec3289b..136cfa75 100644 --- a/libi2pd_client/Torrents.h +++ b/libi2pd_client/Torrents.h @@ -44,6 +44,7 @@ namespace torrents std::string CreateByteString (std::string_view str); std::string CreateInteger (int64_t v); std::string CreateDictionary (const std::vector >& items); + std::string CreateList (const std::vector& items); constexpr size_t REQUEST_BLOCK_SIZE = 16384; @@ -284,8 +285,9 @@ namespace torrents Piece& GetPiece (int index) { return m_Pieces[index]; } std::pair, boost::logic::tribool> CreateBitfield () const; // (bitfield, true - all false - none) bool ApplyBitfield (const std::vector& bitfield); // return true if complete - std::unordered_set GetNonConnectedPeers (); - std::unordered_set GetNonConnectedPeers (size_t trackerID); + std::unordered_set GetNonConnectedPeers (); + std::unordered_set GetNonConnectedPeers (size_t trackerID); + std::unordered_set GetAllPeers () const; RequestedBlock GetNextBlockToRequest (std::shared_ptr conn, bool skipRequested = true); std::vector GetPieceFileFragments (int index) const; std::vector GetFilesCompleted () const; // completed size per file diff --git a/libi2pd_client/TorrentsDHT.cpp b/libi2pd_client/TorrentsDHT.cpp index b0484f07..19a89738 100644 --- a/libi2pd_client/TorrentsDHT.cpp +++ b/libi2pd_client/TorrentsDHT.cpp @@ -9,6 +9,7 @@ #ifndef NO_TORRENTS #include +#include #include "TorrentsTunnel.h" #include "TorrentsDHT.h" @@ -264,9 +265,9 @@ namespace torrents { // query char type = 0; - std::string transactionID, query, id; + std::string transactionID, query, id, infoHash; ParseDictionary (std::string_view ((const char *)buf, len), - [&type, &transactionID, &query, &id](std::string_view key, std::string_view buf)->size_t + [&type, &transactionID, &query, &id, &infoHash](std::string_view key, std::string_view buf)->size_t { if (key == "y") { @@ -289,7 +290,7 @@ namespace torrents else if (key == "a") { return ParseDictionary (buf, - [&id](std::string_view key, std::string_view buf)->size_t + [&id, &infoHash](std::string_view key, std::string_view buf)->size_t { if (key == "id") { @@ -297,25 +298,55 @@ namespace torrents if (l) id = value; return l; } + else if (key == "info_hash") + { + auto [value, l] = ExtractByteString (buf); + if (l) infoHash = value; + return l; + } return 0; }); } return 0; }); if (type == 'q') - HandleQuery (from.GetIdentHash (), fromPort, transactionID, query, id); + { + if (query == "ping") + HandlePingQuery (from.GetIdentHash (), fromPort, transactionID, id); + else if (query == "get_peers") + HandleGetPeersQuery (from.GetIdentHash (), fromPort, transactionID, id, infoHash); + else + LogPrint (eLogDebug, "TorrentsDHT: Unexpected query ", query); + } else if (type) LogPrint (eLogInfo, "TorrentsDHT: Unxpected msg type ", (int)type); } - void TorrentsDHT::HandleQuery (const i2p::data::IdentHash& fromIdent, uint16_t fromPort, - std::string_view transactionID, std::string_view query, std::string_view id) + void TorrentsDHT::HandlePingQuery (const i2p::data::IdentHash& fromIdent, uint16_t fromPort, + std::string_view transactionID, std::string_view id) { - LogPrint (eLogDebug, "TorrentsDHT: Query msg received"); - if (query == "ping") - SendPingResponse (transactionID, fromIdent, fromPort + 1); // to rport - else - LogPrint (eLogDebug, "TorrentsDHT: Unexpected query ", query); + LogPrint (eLogDebug, "TorrentsDHT: Ping query msg received"); + SendPingResponse (transactionID, fromIdent, fromPort + 1); // to rport + } + + void TorrentsDHT::HandleGetPeersQuery (const i2p::data::IdentHash& fromIdent, uint16_t fromPort, + std::string_view transactionID, std::string_view id, std::string_view infoHash) + { + LogPrint (eLogDebug, "TorrentsDHT: Get peers query msg received"); + Torrent::InfoHash hash; + if (infoHash.size () < hash.size ()) + { + LogPrint (eLogInfo, "TorrentsDHT: Requested info hash is too short ", infoHash.size ()); + return; + } + memcpy (hash.data (), infoHash.data (), hash.size ()); + auto torrent = m_Tunnel.FindTorrent (hash); + if (torrent) + { + auto peers = torrent->GetAllPeers (); + if (!peers.empty ()) + SendGetPeersResponse (transactionID, peers, fromIdent, fromPort + 1); // to rport + } } void TorrentsDHT::HandleResponse (std::string_view transactionID, std::string_view id) @@ -413,6 +444,21 @@ namespace torrents }), transactionID, toIdent, toPort); } + + void TorrentsDHT::SendGetPeersResponse (std::string_view transactionID, const std::unordered_set& peers, + const i2p::data::IdentHash& toIdent, uint16_t toPort) + { + std::vector values; + for (auto& it: peers) + values.emplace_back (CreateByteString (std::string_view ((const char *)it.data (), i2p::data::IdentHash::len))); + SendResponseMsg (CreateDictionary ({ + { "id", CreateByteString (std::string_view ((const char *)m_NodeID.data (), m_NodeID.size ())) }, + { "token", CreateByteString ("12345") }, // TODO: + { "values", CreateList (values) } + }), + transactionID, toIdent, toPort); + } + } } diff --git a/libi2pd_client/TorrentsDHT.h b/libi2pd_client/TorrentsDHT.h index c101fbf7..cdcc530f 100644 --- a/libi2pd_client/TorrentsDHT.h +++ b/libi2pd_client/TorrentsDHT.h @@ -127,8 +127,10 @@ namespace torrents void HandleDatagram (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len, const i2p::util::Mapping * options); - void HandleQuery (const i2p::data::IdentHash& fromIdent, uint16_t fromPort, - std::string_view transactionID, std::string_view query, std::string_view id); + void HandlePingQuery (const i2p::data::IdentHash& fromIdent, uint16_t fromPort, + std::string_view transactionID, std::string_view id); + void HandleGetPeersQuery (const i2p::data::IdentHash& fromIdent, uint16_t fromPort, + std::string_view transactionID, std::string_view id, std::string_view infoHash); void HandleResponse (std::string_view transactionID, std::string_view id); void SendDatagram (std::string_view msg, const i2p::data::IdentHash& toIdent, uint16_t toPort); @@ -136,6 +138,8 @@ namespace torrents void SendQueryMsg (std::string_view query, std::string_view arguments, const i2p::data::IdentHash& toIdent, uint16_t toPort); void SendResponseMsg (std::string_view response, std::string_view transactionID, const i2p::data::IdentHash& toIdent, uint16_t toPort); void SendPingResponse (std::string_view transactionID, const i2p::data::IdentHash& toIdent, uint16_t toPort); + void SendGetPeersResponse (std::string_view transactionID, const std::unordered_set& peers, + const i2p::data::IdentHash& toIdent, uint16_t toPort); private: