diff --git a/libi2pd_client/Torrents.h b/libi2pd_client/Torrents.h index 748a15bb..86d60bbe 100644 --- a/libi2pd_client/Torrents.h +++ b/libi2pd_client/Torrents.h @@ -69,8 +69,8 @@ namespace torrents constexpr uint16_t TORRENT_PORT = 6881; // not used by required by protocol constexpr int MIN_TRACKER_REQUESTS_INTERVAL = 15*1000; // in milliseconds constexpr int MAX_TRACKER_REQUESTS_INTERVAL = 24*3600*1000; // in milliseconds - constexpr int DHT_TORRENT_UPDATE_INTERVAL = 440; // in seconds - constexpr int DHT_TORRENT_UPDATE_INTERVAL_VARIANCE = 70; // in seconds + constexpr int DHT_TORRENT_UPDATE_INTERVAL = 880; // in seconds + constexpr int DHT_TORRENT_UPDATE_INTERVAL_VARIANCE = 130; // in seconds constexpr int DHT_TORRENT_INITIAL_UPDATE_INTERVAL = 100; // in seconds constexpr size_t PEER_CONNECTION_RECEIVE_BUFFER_SIZE = 65535; constexpr int PEER_CONNECTION_MAX_IDLE = 3600; // in seconds diff --git a/libi2pd_client/TorrentsDHT.cpp b/libi2pd_client/TorrentsDHT.cpp index 8b5c2e5a..04609c41 100644 --- a/libi2pd_client/TorrentsDHT.cpp +++ b/libi2pd_client/TorrentsDHT.cpp @@ -162,7 +162,7 @@ namespace torrents auto bucket = m_Buckets; while (bucket->next) { - if (id >= bucket->start && id < bucket->next->start) + if (id < bucket->next->start) break; bucket = bucket->next; } @@ -209,7 +209,7 @@ namespace torrents auto it = bucket->nodes.begin (); while (it != bucket->nodes.end ()) { - if (ts > it->second->lastUpdateTime + DHT_ROUTING_TABLE_NODE_EXPIRATION_TIME) + if (ts > it->second->lastUpdateTime + DHT_NODE_EXPIRATION_TIME) { numDeleted++; it = bucket->nodes.erase (it); @@ -282,21 +282,14 @@ namespace torrents RemoveEmptyBuckets (); } - std::list > > RoutingTable::GetExploratoryTargets (std::mt19937& rng) const + std::list > RoutingTable::GetExploratoryTargets (std::mt19937& rng) const { - std::list > > ret; + std::list > ret; auto bucket = m_Buckets; while (bucket) { if (!bucket->IsFull () || bucket->IsInBucket (m_OurNode)) - { - auto randomID = bucket->GetRandomID (rng); - Bucket * bucketToRequest = bucket; - if (bucket->IsEmpty () && bucket->next && !bucket->next->IsEmpty ()) - bucketToRequest = bucket->next; - for (auto it: bucketToRequest->nodes) - ret.emplace_back (std::make_pair (randomID, it.second)); - } + ret.emplace_back (std::make_pair (bucket->GetRandomID (rng), bucket)); bucket = bucket->next; } return ret; @@ -368,13 +361,13 @@ namespace torrents return false; } - bool GetPeersRequestInfo::AddNode (std::shared_ptr node) + bool RequestInfo::AddNode (std::shared_ptr node) { - if (!torrent || !node || tried.contains (node->id)) return false; - return nodesToRequest.emplace (node->id ^ torrent->GetInfoHash (), node).second; + if (!node || tried.contains (node->id)) return false; + return nodesToRequest.emplace (torrent ? node->id ^ torrent->GetInfoHash () : node->id ^ target, node).second; } - std::shared_ptr GetPeersRequestInfo::GetNextNode () + std::shared_ptr RequestInfo::GetNextNode () { if (nodesToRequest.empty ()) return nullptr; auto node = nodesToRequest.begin ()->second; @@ -822,7 +815,7 @@ namespace torrents case eKRPCQueryFindNode: { LogPrint (eLogDebug, "TorrentsDHT: find_node response received"); - HandleFindNodeResponse (nodes); + HandleFindNodeResponse (info, nodes); break; } case eKRPCQueryAnnouncePeer: @@ -838,7 +831,7 @@ namespace torrents LogPrint (eLogInfo, "TorrentsDHT: Query not found"); } - void TorrentsDHT::HandleGetPeersResponseNodes (std::shared_ptr info, + void TorrentsDHT::HandleGetPeersResponseNodes (std::shared_ptr info, const NodeID& nodeID, uint64_t token, std::string_view nodes) { NodeInfo nodeInfo; @@ -860,7 +853,7 @@ namespace torrents } } - void TorrentsDHT::HandleGetPeersResponsePeersAndAnnounce (std::shared_ptr info, + void TorrentsDHT::HandleGetPeersResponsePeersAndAnnounce (std::shared_ptr info, const std::vector& peers, uint64_t token, const i2p::data::IdentHash& toIdent, uint16_t toPort) { if (!info->torrent->IsComplete ()) @@ -879,14 +872,21 @@ namespace torrents SendAnnouncePeerQuery (info->torrent->GetInfoHash (), token, toIdent, toPort); } - void TorrentsDHT::HandleFindNodeResponse (std::string_view nodes) + void TorrentsDHT::HandleFindNodeResponse (std::shared_ptr info, std::string_view nodes) { NodeInfo nodeInfo; - while (nodes.size () >= nodeInfo.size ()) + if (nodes.size () >= nodeInfo.size ()) // at least one node { - memcpy (nodeInfo.data (), nodes.data (), nodeInfo.size ()); - UpdateNode (std::make_shared(nodeInfo)); - nodes = nodes.substr (nodeInfo.size ()); + while (nodes.size () >= nodeInfo.size ()) + { + memcpy (nodeInfo.data (), nodes.data (), nodeInfo.size ()); + auto node = UpdateNode (std::make_shared(nodeInfo)); + if (info && node) + info->AddNode (node); + nodes = nodes.substr (nodeInfo.size ()); + } + if (info) + SendNextFindNodeQuery (info); } } @@ -913,7 +913,7 @@ namespace torrents } void TorrentsDHT::SendQueryMsg (KRPCQuery query, std::string_view arguments, - const i2p::data::IdentHash& toIdent, uint16_t toPort, bool isRaw, std::shared_ptr info) + const i2p::data::IdentHash& toIdent, uint16_t toPort, bool isRaw, std::shared_ptr info) { uint16_t transactionID = m_Tunnel.GetLocalDestination () ? static_cast(m_Tunnel.GetLocalDestination ()->GetRng ()()) : 1; auto msg = CreateDictionary ({ @@ -957,7 +957,7 @@ namespace torrents transactionID, toIdent, toPort); } - void TorrentsDHT::SendGetPeersQuery (std::shared_ptr info, const i2p::data::IdentHash& toIdent, uint16_t toPort) + void TorrentsDHT::SendGetPeersQuery (std::shared_ptr info, const i2p::data::IdentHash& toIdent, uint16_t toPort) { if (!info || !info->torrent) return; const auto& infoHash = info->torrent->GetInfoHash (); @@ -968,7 +968,7 @@ namespace torrents toIdent, toPort, false, info); } - void TorrentsDHT::SendNextGetPeersQuery (std::shared_ptr info) + void TorrentsDHT::SendNextGetPeersQuery (std::shared_ptr info) { if (!info) return; if (!info->IsDone () && m_RoutingTable) @@ -1006,13 +1006,27 @@ namespace torrents transactionID, toIdent, toPort); } - void TorrentsDHT::SendFindNodeQuery (const NodeID& target, const i2p::data::IdentHash& toIdent, uint16_t toPort) + void TorrentsDHT::SendFindNodeQuery (std::shared_ptr info, const i2p::data::IdentHash& toIdent, uint16_t toPort) { + if (!info) return; SendQueryMsg (eKRPCQueryFindNode, CreateDictionary ({ { "id", CreateByteString (std::string_view ((const char *)m_NodeID.data (), m_NodeID.size ())) }, - { "target", CreateByteString (std::string_view ((const char *)target.data (), target.size ())) } + { "target", CreateByteString (std::string_view ((const char *)info->target.data (), info->target.size ())) } }), - toIdent, toPort); + toIdent, toPort, false, info); + } + + void TorrentsDHT::SendNextFindNodeQuery (std::shared_ptr info) + { + if (!info) return; + if (!info->IsDone () && m_RoutingTable) + { + auto nextNode = info->GetNextNode (); + if (nextNode) + SendFindNodeQuery (info, nextNode->peer, nextNode->port); + else + LogPrint (eLogDebug, "TorrentsDHT: No more nodes to send find_node"); + } } void TorrentsDHT::SendFindNodeResponse (std::string_view transactionID, std::string_view nodes, @@ -1041,8 +1055,22 @@ namespace torrents if (m_RoutingTable && m_Tunnel.GetLocalDestination ()) { auto targets = m_RoutingTable->GetExploratoryTargets (m_Tunnel.GetLocalDestination ()->GetRng ()); - for (auto [target, node]: targets) - SendFindNodeQuery (target, node->peer, node->port); + for (auto [target, bucket]: targets) + { + auto request = std::make_shared(nullptr, DHT_MAX_NUM_FIND_NODE_ATTEMPTS); + request->target = target; + if (bucket->IsEmpty ()) + { + if (bucket->next && !bucket->next->IsEmpty ()) + bucket = bucket->next; + else + return; + } + // fill initial list of nodes to request from bucket + for (auto it: bucket->nodes) + request->AddNode (it.second); + SendNextFindNodeQuery (request); + } } } @@ -1145,22 +1173,32 @@ namespace torrents { if (ecode != boost::asio::error::operation_aborted) { - std::list > requests; + std::list > getpeers, findnode; auto ts = i2p::util::GetMonotonicSeconds (); auto it = m_Queries.begin (); while (it != m_Queries.end ()) { if (ts > std::get<4>(it->second) + DHT_QUERY_EXPIRATION_TIME) { - if (std::get<2>(it->second) == eKRPCQueryGetPeers) - requests.push_back (std::get<3>(it->second)); + switch (std::get<2>(it->second)) + { + case eKRPCQueryGetPeers: + getpeers.push_back (std::get<3>(it->second)); + break; + case eKRPCQueryFindNode: + findnode.push_back (std::get<3>(it->second)); + break; + default: ; + } it = m_Queries.erase (it); } else it++; } - for (auto it1: requests) + for (auto it1: getpeers) SendNextGetPeersQuery (it1); + for (auto it1: findnode) + SendNextFindNodeQuery (it1); ScheduleDHTQueryExpirationCheck (); } } @@ -1168,7 +1206,7 @@ namespace torrents void TorrentsDHT::GetPeersAndAnnounce (std::shared_ptr torrent) { if (!torrent || !m_RoutingTable) return; - auto request = std::make_shared(torrent); + auto request = std::make_shared(torrent, DHT_MAX_NUM_GET_PEERS_ATTEMPTS); auto bucket = m_RoutingTable->FindBucket (torrent->GetInfoHash ()); if (!bucket) return; // DHT is empty if (bucket->IsEmpty ()) diff --git a/libi2pd_client/TorrentsDHT.h b/libi2pd_client/TorrentsDHT.h index 7a242c95..8c6880be 100644 --- a/libi2pd_client/TorrentsDHT.h +++ b/libi2pd_client/TorrentsDHT.h @@ -42,18 +42,18 @@ namespace torrents constexpr int DHT_EXPIRATION_CHECK_INTERVAL = 73; // in seconds constexpr int DHT_SEND_PING_CHECK_INTERVAL = 38; // in seconds constexpr int DHT_QUERY_EXPIRATION_CHECK_INTERVAL = 8; // in seconds - constexpr int DHT_EXPLORATORY_INTERVAL = 130; // in seconds + constexpr int DHT_EXPLORATORY_INTERVAL = 430; // in seconds constexpr int DHT_EXPLORATORY_INTERVAL_VARIANCE = 40; // in seconds constexpr int DHT_INITIAL_EXPLORATORY_INTERVAL = 90; // in seconds constexpr int DHT_NODE_SEND_PING_TIME = 740; // in seconds - constexpr int DHT_ROUTING_TABLE_NODE_EXPIRATION_TIME = 855; // in seconds - constexpr int DHT_NODE_EXPIRATION_TIME = 1315; // in seconds + constexpr int DHT_NODE_EXPIRATION_TIME = 855; // in seconds constexpr int DHT_BUCKET_EXPIRATION_THRESHOLD = 290; // in seconds constexpr int DHT_TORRENT_PEER_EXPIRATION_TIME = 3*3600; // in seconds 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 = 20; // in seconds constexpr int DHT_MAX_NUM_GET_PEERS_ATTEMPTS = 22; + constexpr int DHT_MAX_NUM_FIND_NODE_ATTEMPTS = 8; using Distance = Torrent::InfoHash; struct NodeID: public Torrent::InfoHash @@ -135,7 +135,7 @@ namespace torrents bool AddNode (std::shared_ptr node); void RemoveNode (const NodeID& id); - std::list > > GetExploratoryTargets (std::mt19937& rng) const; // (target, node to send find_node to) + std::list > GetExploratoryTargets (std::mt19937& rng) const; // (target, node to send find_node to) size_t DeleteExpiredNodes (uint64_t ts); std::list > GetNodesToPing (uint64_t ts); void RemoveEmptyBuckets (); @@ -181,16 +181,18 @@ namespace torrents "ping", "find_node", "get_peers", "announce_peer" }; - struct GetPeersRequestInfo + struct RequestInfo { - std::shared_ptr torrent; + std::shared_ptr torrent; // for get_peers + NodeID target; // for find_node std::map > nodesToRequest; std::set tried; uint64_t token; - int numAttempts; + int numAttempts, maxNumAttempts; - GetPeersRequestInfo (std::shared_ptr t): torrent (t), token (0), numAttempts (0) { } - bool IsDone () const { return numAttempts >= DHT_MAX_NUM_GET_PEERS_ATTEMPTS; } + RequestInfo (std::shared_ptr t, int maxNumAttempts1): torrent (t), + token (0), numAttempts (0), maxNumAttempts (maxNumAttempts1) { } + bool IsDone () const { return numAttempts >= maxNumAttempts; } bool AddNode (std::shared_ptr node); std::shared_ptr GetNextNode (); }; @@ -224,20 +226,21 @@ namespace torrents std::string_view transactionID, const NodeID& target); void HandleResponse (std::string_view transactionID, const NodeID& nodeID, uint64_t token, const std::vector& values, std::string_view nodes); - void HandleGetPeersResponseNodes (std::shared_ptr info, + void HandleGetPeersResponseNodes (std::shared_ptr info, const NodeID& nodeID, uint64_t token, std::string_view nodes); - void HandleGetPeersResponsePeersAndAnnounce (std::shared_ptr info, + void HandleGetPeersResponsePeersAndAnnounce (std::shared_ptr info, const std::vector& peers, uint64_t token, const i2p::data::IdentHash& toIdent, uint16_t toPort); - void HandleFindNodeResponse (std::string_view nodes); + void HandleFindNodeResponse (std::shared_ptr info, std::string_view nodes); void HandleAnnouncePeer (std::string_view transactionID, const Torrent::InfoHash& infoHash, uint64_t token); 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); void SendQueryMsg (KRPCQuery query, std::string_view arguments, const i2p::data::IdentHash& toIdent, - uint16_t toPort, bool isRaw = false, std::shared_ptr info = nullptr); - void SendFindNodeQuery (const NodeID& target, const i2p::data::IdentHash& toIdent, uint16_t toPort); - void SendGetPeersQuery (std::shared_ptr info, const i2p::data::IdentHash& toIdent, uint16_t toPort); - void SendNextGetPeersQuery (std::shared_ptr info); + uint16_t toPort, bool isRaw = false, std::shared_ptr info = nullptr); + void SendFindNodeQuery (std::shared_ptr info, const i2p::data::IdentHash& toIdent, uint16_t toPort); + void SendNextFindNodeQuery (std::shared_ptr info); + void SendGetPeersQuery (std::shared_ptr info, const i2p::data::IdentHash& toIdent, uint16_t toPort); + void SendNextGetPeersQuery (std::shared_ptr info); 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, std::shared_ptr torrent, @@ -274,8 +277,8 @@ namespace torrents uint16_t m_Port; NodeID m_NodeID; std::unique_ptr m_RoutingTable; - // transactionID -> (ident, port, query, get peers request info, time in monotonic seconds) - std::unordered_map, uint64_t > > m_Queries; + // transactionID -> (ident, port, query, request info, time in monotonic seconds) + std::unordered_map, uint64_t > > m_Queries; std::map > m_Torrents; uint64_t m_NextDHTExploratoryTime; // monotonic seconds };