From fc42730f8b82eb762efbc7d296962a0e10f2cd68 Mon Sep 17 00:00:00 2001 From: orignal Date: Wed, 23 Sep 2026 20:49:11 -0400 Subject: [PATCH] handle get_peers response --- libi2pd_client/TorrentsDHT.cpp | 114 ++++++++++++++++++++++++++++----- libi2pd_client/TorrentsDHT.h | 17 +++-- 2 files changed, 111 insertions(+), 20 deletions(-) diff --git a/libi2pd_client/TorrentsDHT.cpp b/libi2pd_client/TorrentsDHT.cpp index f194f47b..1155184f 100644 --- a/libi2pd_client/TorrentsDHT.cpp +++ b/libi2pd_client/TorrentsDHT.cpp @@ -17,6 +17,15 @@ namespace i2p { namespace torrents { + NodeInfo Node::GetNodeInfo () const + { + NodeInfo nodeInfo; + memcpy (nodeInfo.data (), id.data (), id.size ()); + memcpy (nodeInfo.data () + id.size (), peer, peer.len); + htobe16buf (nodeInfo.data () + nodeInfo.size () - 2, port); + return nodeInfo; + } + std::shared_ptr Bucket::FindNode (const NodeID& id) const { auto it = std::find_if (nodes.begin (), nodes.end (), @@ -138,6 +147,13 @@ namespace torrents return node; } + std::shared_ptr RoutingTable::FindNode (const NodeID& id) const + { + auto bucket = FindBucket (id); + if (!bucket) return nullptr; + return bucket->FindNode (id); + } + std::list, Distance> > RoutingTable::FindClosestNodes (const Torrent::InfoHash& infoHash, size_t num) const { std::list, Distance> > ret; @@ -176,6 +192,12 @@ namespace torrents m_IncomingGetPeers.emplace (token, node); } + void DHTTorrent::AddOutgoingGetPeerNode (GetPeersToken token, std::shared_ptr node) + { + if (!node) return; + m_OutgoingGetPeers.emplace (token, node); + } + TorrentsDHT::TorrentsDHT (TorrentsTunnel& tunnel, uint16_t port): m_Tunnel (tunnel), m_Port (port) { @@ -185,9 +207,7 @@ namespace torrents memcpy (m_NodeID.data (), dest->GetIdentHash (), m_NodeID.size ()); m_NodeID[4] ^= (port >> 8); m_NodeID[5] ^= (port & 0xFF); - memcpy (m_NodeInfo.data (), m_NodeID.data (), m_NodeID.size ()); - memcpy (m_NodeInfo.data () + m_NodeID.size (), dest->GetIdentHash (), i2p::data::IdentHash::len); - htobe16buf (m_NodeInfo.data () + m_NodeInfo.size () - 2, port); + m_NodeInfo = Node (m_NodeID, dest->GetIdentHash (), port).GetNodeInfo (); m_RoutingTable = std::make_unique (m_NodeID); } else @@ -224,8 +244,10 @@ namespace torrents // response or error char type = 0; std::string transactionID, id; + uint64_t token = 0; + std::vector values; ParseDictionary (std::string_view ((const char *)buf, len), - [&type, &transactionID, &id](std::string_view key, std::string_view buf)->size_t + [&type, &transactionID, &id, &values, &token](std::string_view key, std::string_view buf)->size_t { if (key == "y") { @@ -239,10 +261,17 @@ namespace torrents if (l) transactionID = value; return l; } + else if (key == "token") + { + auto [value, l] = ExtractByteString (buf); + if (l && value.size () <= 8) + memcpy (&token, value.data (), 8); + return l; + } else if (key == "r") { return ParseDictionary (buf, - [&id](std::string_view key, std::string_view buf)->size_t + [&id, &values](std::string_view key, std::string_view buf)->size_t { if (key == "id") { @@ -250,6 +279,12 @@ namespace torrents if (l) id = value; return l; } + else if (key == "values") + { + auto [v, l] = ParseStringList (buf); + if (l) values = v; + return l; + } return 0; }); } @@ -260,7 +295,7 @@ namespace torrents switch (type) { case 'r': - HandleResponse (transactionID, id); + HandleResponse (transactionID, id, token, values); break; case 'e': LogPrint (eLogDebug, "TorrentsDHT: Error msg received"); @@ -374,9 +409,19 @@ namespace torrents it->second->AddIncomingGetPeerNode (token, node); SendGetPeersResponse (transactionID, it->second, token, fromIdent, fromPort + 1); // to rport } + else if (m_RoutingTable) + { + auto nodes = m_RoutingTable->FindClosestNodes (hash); + if (!nodes.empty ()) + { + uint64_t token = m_Tunnel.GetLocalDestination () ? m_Tunnel.GetLocalDestination ()->GetRng ()() : 1; + SendGetPeersResponse (transactionID, nodes.front ().first, token, fromIdent, fromPort + 1); // to rport + } + } } - void TorrentsDHT::HandleResponse (std::string_view transactionID, std::string_view id) + void TorrentsDHT::HandleResponse (std::string_view transactionID, std::string_view id, + uint64_t token, const std::vector& values) { LogPrint (eLogDebug, "TorrentsDHT: Response msg received"); uint16_t t = 0; @@ -385,20 +430,46 @@ namespace torrents auto it = m_Queries.find (t); if (t && it != m_Queries.end ()) { - // assume ping for now if (id.size () < NodeID::len) { LogPrint (eLogInfo, "TorrentsDHT: received id is too short ", id.size ()); return; } + NodeID nodeID; + memcpy (nodeID.data (), id.data (), NodeID::len); if (m_RoutingTable) { - NodeID nodeID; - memcpy (nodeID.data (), id.data (), NodeID::len); - if (m_RoutingTable->AddNode (nodeID, it->second.first, it->second.second)) - LogPrint (eLogDebug, "TorrentsDHT: Node ", it->second.first.ToBase64 (), ":", it->second.second, " added"); - else - LogPrint (eLogError, "TorrentsDHT: Failed to add node ", it->second.first.ToBase64 ()); + const auto& [ident, port, query, torrentw] = it->second; + if (query == "ping") + { + LogPrint (eLogDebug, "TorrentsDHT: Ping response received"); + if (m_RoutingTable->AddNode (nodeID, ident, port)) + LogPrint (eLogDebug, "TorrentsDHT: Node ", ident.ToBase64 (), ":", port, " added"); + else + LogPrint (eLogError, "TorrentsDHT: Failed to add node ", ident.ToBase64 ()); + } + else if (query == "get_peers") + { + LogPrint (eLogDebug, "TorrentsDHT: get_peers response received"); + auto torrent = torrentw.lock (); + if (!torrent) + { + auto node = m_RoutingTable->FindNode (nodeID); + if (node) + { + std::shared_ptr dhtTorrent; + auto it1 = m_Torrents.find (torrent->GetInfoHash ()); + if (it1 != m_Torrents.end ()) + dhtTorrent = it1->second; + else + { + dhtTorrent = std::make_shared(); + m_Torrents.emplace (torrent->GetInfoHash (), dhtTorrent); + } + dhtTorrent->AddOutgoingGetPeerNode (token, node); + } + } + } } } else @@ -437,7 +508,7 @@ namespace torrents { "t", CreateByteString (std::string_view ((const char *)&transactionID, 2)) }, { "y", CreateByteString ("q") } }); - m_Queries.insert_or_assign (transactionID, std::make_pair (toIdent, toPort)); + m_Queries.insert_or_assign (transactionID, std::make_tuple (toIdent, toPort, query, std::shared_ptr{})); SendDatagram (msg, toIdent, toPort); } @@ -471,6 +542,7 @@ namespace torrents void TorrentsDHT::SendGetPeersResponse (std::string_view transactionID, std::shared_ptr torrent, uint64_t token, const i2p::data::IdentHash& toIdent, uint16_t toPort) { + if (!torrent) return; SendResponseMsg (CreateDictionary ({ { "id", CreateByteString (std::string_view ((const char *)m_NodeID.data (), m_NodeID.size ())) }, { "token", CreateByteString (std::string_view ((const char *)&token, 8)) }, @@ -479,6 +551,18 @@ namespace torrents transactionID, toIdent, toPort); } + void TorrentsDHT::SendGetPeersResponse (std::string_view transactionID, std::shared_ptr node, + uint64_t token, const i2p::data::IdentHash& toIdent, uint16_t toPort) + { + if (!node) return; + NodeInfo nodeInfo = node->GetNodeInfo (); + SendResponseMsg (CreateDictionary ({ + { "id", CreateByteString (std::string_view ((const char *)m_NodeID.data (), m_NodeID.size ())) }, + { "token", CreateByteString (std::string_view ((const char *)&token, 8)) }, + { "nodes", CreateByteString (std::string_view ((const char *)nodeInfo.data (), nodeInfo.size ())) } + }), + transactionID, toIdent, toPort); + } } } diff --git a/libi2pd_client/TorrentsDHT.h b/libi2pd_client/TorrentsDHT.h index 8e0785bd..b9b0a68e 100644 --- a/libi2pd_client/TorrentsDHT.h +++ b/libi2pd_client/TorrentsDHT.h @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -59,6 +60,7 @@ namespace torrents } }; + using NodeInfo = std::array; struct Node { NodeID id; @@ -67,6 +69,8 @@ namespace torrents Node (const NodeID& id1, const i2p::data::IdentHash& peer1, uint16_t port1): id (id1), peer (peer1), port (port1) {} + + NodeInfo GetNodeInfo () const; }; constexpr size_t MAX_BUCKET_CAPACITY = 8; @@ -93,7 +97,8 @@ namespace torrents ~RoutingTable (); std::shared_ptr AddNode (const NodeID& id, const i2p::data::IdentHash& peer, uint16_t port); - std::list, Distance> > FindClosestNodes (const Torrent::InfoHash& infoHash, size_t num) const; + std::shared_ptr FindNode (const NodeID& id) const; + std::list, Distance> > FindClosestNodes (const Torrent::InfoHash& infoHash, size_t num = 1) const; private: @@ -115,6 +120,7 @@ namespace torrents std::string GetBEncodedPeers () const; void AddIncomingGetPeerNode (GetPeersToken token, std::shared_ptr node); + void AddOutgoingGetPeerNode (GetPeersToken token, std::shared_ptr node); private: @@ -123,8 +129,6 @@ namespace torrents std::unordered_map > m_OutgoingGetPeers; // we recive announces from }; - using NodeInfo = std::array; - class TorrentsTunnel; class TorrentsDHT { @@ -149,7 +153,8 @@ namespace torrents 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 HandleResponse (std::string_view transactionID, std::string_view id, uint64_t token, + const std::vector& values); void SendDatagram (std::string_view msg, const i2p::data::IdentHash& toIdent, uint16_t toPort); void SendRawDatagram (std::string_view msg, const i2p::data::IdentHash& toIdent, uint16_t toPort); @@ -158,6 +163,8 @@ namespace torrents void SendPingResponse (std::string_view transactionID, const i2p::data::IdentHash& toIdent, uint16_t toPort); void SendGetPeersResponse (std::string_view transactionID, std::shared_ptr torrent, uint64_t token, const i2p::data::IdentHash& toIdent, uint16_t toPort); + void SendGetPeersResponse (std::string_view transactionID, std::shared_ptr node, + uint64_t token, const i2p::data::IdentHash& toIdent, uint16_t toPort); private: @@ -166,7 +173,7 @@ namespace torrents NodeID m_NodeID; NodeInfo m_NodeInfo; // 20 byte Node ID + 32 byte IdentHash + 2 byte port std::unique_ptr m_RoutingTable; - std::unordered_map > m_Queries; + std::unordered_map > > m_Queries; std::map > m_Torrents; }; }