diff --git a/libi2pd_client/TorrentsDHT.cpp b/libi2pd_client/TorrentsDHT.cpp index c49fb1cd..999f23fd 100644 --- a/libi2pd_client/TorrentsDHT.cpp +++ b/libi2pd_client/TorrentsDHT.cpp @@ -394,6 +394,22 @@ namespace torrents return false; } + bool GetPeersRequestInfo::AddNode (std::shared_ptr node) + { + if (!torrent || !node || tried.contains (node->id)) return false; + return nodesToRequest.emplace (node->id ^ torrent->GetInfoHash (), node).second; + } + + std::shared_ptr GetPeersRequestInfo::GetNextNode () + { + if (nodesToRequest.empty ()) return nullptr; + auto node = nodesToRequest.begin ()->second; + nodesToRequest.erase (nodesToRequest.begin ()); + numAttempts++; + tried.emplace (node->id); + return node; + } + TorrentsDHT::TorrentsDHT (TorrentsTunnel& tunnel, uint16_t port): m_Tunnel (tunnel), m_DHTUpdateCheckTimer (tunnel.GetService ()), m_DHTExpirationCheckTimer (tunnel.GetService ()), @@ -842,19 +858,15 @@ namespace torrents auto node = UpdateNode (std::make_shared(nodeInfo)); if (info) { - info->tried.emplace (nodeID); + info->AddNode (node); info->token = token; if (!info->IsDone () && m_RoutingTable) { - auto torrent = info ? info->torrent.lock () : nullptr; - if (torrent) - { - auto nextNode = m_RoutingTable->FindClosestNode (torrent->GetInfoHash (), &info->tried); - if (nextNode) - SendGetPeersQuery (info, nextNode->peer, nextNode->port); - else - LogPrint (eLogDebug, "TorrentsDHT: No more nodes to send get_peers"); - } + auto nextNode = info->GetNextNode (); + if (nextNode) + SendGetPeersQuery (info, nextNode->peer, nextNode->port); + else + LogPrint (eLogDebug, "TorrentsDHT: No more nodes to send get_peers"); } else LogPrint (eLogDebug, "TorrentsDHT: Closest node not found after ", DHT_MAX_NUM_GET_PEERS_ATTEMPTS, " get_peers attempts"); @@ -864,24 +876,20 @@ namespace torrents void TorrentsDHT::HandleGetPeersResponsePeersAndAnnounce (std::shared_ptr info, const std::vector& peers, uint64_t token, const i2p::data::IdentHash& toIdent, uint16_t toPort) { - auto torrent = info ? info->torrent.lock () : nullptr; - if (torrent) + if (!info->torrent->IsComplete ()) { - if (!torrent->IsComplete ()) + std::unordered_set newPeers; + for (auto it: peers) + if (it.size () == i2p::data::IdentHash::len) + newPeers.emplace ((const uint8_t *)it.data ()); + if (!newPeers.empty ()) { - std::unordered_set newPeers; - for (auto it: peers) - if (it.size () == i2p::data::IdentHash::len) - newPeers.emplace ((const uint8_t *)it.data ()); - if (!newPeers.empty ()) - { - LogPrint (eLogDebug, "TorrentsDHT: ", newPeers.size (), " new peers received"); - m_Tunnel.ConnectToNewPeers (torrent, newPeers, ePeerConnectionOriginDHT); - } + LogPrint (eLogDebug, "TorrentsDHT: ", newPeers.size (), " new peers received"); + m_Tunnel.ConnectToNewPeers (info->torrent, newPeers, ePeerConnectionOriginDHT); } - LogPrint (eLogDebug, "TorrentsDHT: Send announce to ", toIdent.ToBase64 ()); - SendAnnouncePeerQuery (torrent->GetInfoHash (), token, toIdent, toPort); } + LogPrint (eLogDebug, "TorrentsDHT: Send announce to ", toIdent.ToBase64 ()); + SendAnnouncePeerQuery (info->torrent->GetInfoHash (), token, toIdent, toPort); } void TorrentsDHT::SendDatagram (std::string_view msg, const i2p::data::IdentHash& toIdent, uint16_t toPort) @@ -953,10 +961,8 @@ namespace torrents void TorrentsDHT::SendGetPeersQuery (std::shared_ptr info, const i2p::data::IdentHash& toIdent, uint16_t toPort) { - std::shared_ptr torrent; - if (info) torrent = info->torrent.lock (); - if (!torrent) return; - const auto& infoHash = torrent->GetInfoHash (); + if (!info || !info->torrent) return; + const auto& infoHash = info->torrent->GetInfoHash (); SendQueryMsg (eKRPCQueryGetPeers, CreateDictionary ({ { "id", CreateByteString (std::string_view ((const char *)m_NodeID.data (), m_NodeID.size ())) }, { "info_hash", CreateByteString (std::string_view ((const char *)infoHash.data (), infoHash.size ())) } @@ -1144,10 +1150,17 @@ namespace torrents void TorrentsDHT::GetPeersAndAnnounce (std::shared_ptr torrent) { if (!torrent || !m_RoutingTable) return; - auto queriedNode = m_RoutingTable->FindClosestNode (torrent->GetInfoHash ()); - if (!queriedNode) return; // DHT is empty - SendGetPeersQuery (std::make_shared(torrent, queriedNode->id), - queriedNode->peer, queriedNode->port); + auto request = std::make_shared(torrent); + auto bucket = m_RoutingTable->FindBucket (torrent->GetInfoHash ()); + if (!bucket || bucket->nodes.empty ()) return; // DHT is empty + for (auto it: bucket->nodes) + request->AddNode (it.second); + if (!request->IsDone ()) + { + auto queriedNode = request->GetNextNode (); + if (queriedNode) + SendGetPeersQuery (request, queriedNode->peer, queriedNode->port); + } } } } diff --git a/libi2pd_client/TorrentsDHT.h b/libi2pd_client/TorrentsDHT.h index b4335051..653f9ccd 100644 --- a/libi2pd_client/TorrentsDHT.h +++ b/libi2pd_client/TorrentsDHT.h @@ -52,7 +52,7 @@ namespace torrents constexpr int DHT_INCOMING_GET_PEERS_TOKEN_EXPIRATION_TIME = 600; // in seconds constexpr int DHT_EMPTY_TORRENT_EXPIRATION_TIME = 30; // in seconds constexpr int DHT_QUERY_EXPIRATION_TIME = 30; // in seconds - constexpr size_t DHT_MAX_NUM_GET_PEERS_ATTEMPTS = 12; + constexpr int DHT_MAX_NUM_GET_PEERS_ATTEMPTS = 22; using Distance = Torrent::InfoHash; struct NodeID: public Torrent::InfoHash @@ -128,6 +128,7 @@ namespace torrents void CleanUp (); size_t GetNumBuckets () const; size_t GetNumNodes () const; + Bucket * FindBucket (const Torrent::InfoHash& id) const; bool AddNode (std::shared_ptr node); void RemoveNode (const NodeID& id); @@ -141,10 +142,6 @@ namespace torrents std::list GetNodesToPing (uint64_t ts); void RemoveEmptyBuckets (); - private: - - Bucket * FindBucket (const Torrent::InfoHash& id) const; - private: Bucket * m_Buckets; @@ -187,13 +184,16 @@ namespace torrents struct GetPeersRequestInfo { - std::weak_ptr torrent; + std::shared_ptr torrent; + std::map > nodesToRequest; std::set tried; uint64_t token; + int numAttempts; - GetPeersRequestInfo (std::shared_ptr t, const NodeID& firstNode): - torrent (t), token (0) { tried.emplace (firstNode); } - bool IsDone () const { return tried.size () >= DHT_MAX_NUM_GET_PEERS_ATTEMPTS; } + GetPeersRequestInfo (std::shared_ptr t): torrent (t), token (0), numAttempts (0) { } + bool IsDone () const { return numAttempts >= DHT_MAX_NUM_GET_PEERS_ATTEMPTS || nodesToRequest.empty (); } + bool AddNode (std::shared_ptr node); + std::shared_ptr GetNextNode (); }; class TorrentsTunnel;