diff --git a/libi2pd/Destination.cpp b/libi2pd/Destination.cpp index 0287ae6b..df476bab 100644 --- a/libi2pd/Destination.cpp +++ b/libi2pd/Destination.cpp @@ -41,6 +41,7 @@ namespace client int numTags = DEFAULT_TAGS_TO_SEND; bool isHighBandwidth = true; std::shared_ptr > explicitPeers; + std::vector trustedRouters; try { if (params) @@ -63,6 +64,9 @@ namespace client auto explicitPeersStr = (*params)[I2CP_PARAM_EXPLICIT_PEERS]; if (!explicitPeersStr.empty ()) explicitPeers = std::make_shared > (i2p::data::ExtractIdentHashes (explicitPeersStr)); + auto trustedRoutersStr = (*params)[I2CP_PARAM_TRUSTED_ROUTERS]; + if (!trustedRoutersStr.empty ()) + trustedRouters = i2p::data::ExtractIdentHashes (trustedRoutersStr); m_Nickname = (*params)[I2CP_PARAM_INBOUND_NICKNAME]; if (m_Nickname.empty ()) // try outbound @@ -105,6 +109,8 @@ namespace client m_Pool = i2p::tunnel::tunnels.CreateTunnelPool (inLen, outLen, inQty, outQty, inVar, outVar, isHighBandwidth); if (explicitPeers) m_Pool->SetExplicitPeers (explicitPeers); + if (!trustedRouters.empty ()) + m_Pool->SetTrustedRouters (trustedRouters); if(params) { int maxLatency = 0; diff --git a/libi2pd/Destination.h b/libi2pd/Destination.h index c20c80c0..b1f667fc 100644 --- a/libi2pd/Destination.h +++ b/libi2pd/Destination.h @@ -64,6 +64,7 @@ namespace client const char I2CP_PARAM_OUTBOUND_TUNNELS_LENGTH_VARIANCE[] = "outbound.lengthVariance"; const int DEFAULT_OUTBOUND_TUNNELS_LENGTH_VARIANCE = 0; const char I2CP_PARAM_EXPLICIT_PEERS[] = "explicitPeers"; + const char I2CP_PARAM_TRUSTED_ROUTERS[] = "trustedRouters"; const int STREAM_REQUEST_TIMEOUT = 60; //in seconds const char I2CP_PARAM_TAGS_TO_SEND[] = "crypto.tagsToSend"; const int DEFAULT_TAGS_TO_SEND = 40; diff --git a/libi2pd/TunnelPool.cpp b/libi2pd/TunnelPool.cpp index 1af0bef2..bfed6a7d 100644 --- a/libi2pd/TunnelPool.cpp +++ b/libi2pd/TunnelPool.cpp @@ -88,6 +88,11 @@ namespace tunnel } } + void TunnelPool::SetTrustedRouters (std::vector routers) + { + m_TrustedRouters.swap (routers); + } + void TunnelPool::DetachTunnels () { { @@ -105,7 +110,7 @@ namespace tunnel { std::unique_lock l(m_TestsMutex); m_Tests.clear (); - } + } } bool TunnelPool::Reconfigure(int inHops, int outHops, int inQuant, int outQuant) @@ -152,7 +157,7 @@ namespace tunnel std::unique_lock l(m_TestsMutex); for (auto& it: m_Tests) if (it.second.second == expiredTunnel) it.second.second = nullptr; - } + } std::unique_lock l(m_InboundTunnelsMutex); m_InboundTunnels.erase (expiredTunnel); @@ -177,7 +182,7 @@ namespace tunnel std::unique_lock l(m_TestsMutex); for (auto& it: m_Tests) if (it.second.first == expiredTunnel) it.second.first = nullptr; - } + } std::unique_lock l(m_OutboundTunnelsMutex); m_OutboundTunnels.erase (expiredTunnel); @@ -280,10 +285,10 @@ namespace tunnel } if (!tunnel) - { + { tunnel = GetNextOutboundTunnel (); freshTunnel = true; - } + } return std::make_pair(tunnel, freshTunnel); } @@ -309,7 +314,7 @@ namespace tunnel for (const auto& it : m_InboundTunnels) if (it->IsEstablished ()) num++; } - if (!num && !m_OutboundTunnels.empty () && m_NumOutboundHops > 0 && + if (!num && !m_OutboundTunnels.empty () && m_NumOutboundHops > 0 && m_NumInboundHops == m_NumOutboundHops) { for (auto it: m_OutboundTunnels) @@ -353,10 +358,10 @@ namespace tunnel if (m_OutboundTunnels.size () > 1) // don't fail last tunnel m_OutboundTunnels.erase (it.second.first); else - { + { it.second.first->SetState (eTunnelStateTestFailed); CreateOutboundTunnel (); // create new tunnel immediately because last one failed - } + } } else if (it.second.first->GetState () != eTunnelStateExpiring) it.second.first->SetState (eTunnelStateTestFailed); @@ -371,15 +376,15 @@ namespace tunnel { std::unique_lock l(m_InboundTunnelsMutex); if (m_InboundTunnels.size () > 1) // don't fail last tunnel - { + { m_InboundTunnels.erase (it.second.second); - failed = true; - } + failed = true; + } else { it.second.second->SetState (eTunnelStateTestFailed); CreateInboundTunnel (); // create new tunnel immediately because last one failed - } + } } if (failed && m_LocalDestination) m_LocalDestination->SetLeaseSetUpdated (true); @@ -393,7 +398,7 @@ namespace tunnel } // new tests - if (!m_LocalDestination) return; + if (!m_LocalDestination) return; std::vector, std::shared_ptr > > newTests; std::vector > outboundTunnels; { @@ -447,7 +452,7 @@ namespace tunnel if (isECIES) { uint8_t key[32]; RAND_bytes (key, 32); - uint64_t tag; RAND_bytes ((uint8_t *)&tag, 8); + uint64_t tag; RAND_bytes ((uint8_t *)&tag, 8); m_LocalDestination->SubmitECIESx25519Key (key, tag); msg = i2p::garlic::WrapECIESX25519Message (msg, key, tag); } @@ -458,9 +463,9 @@ namespace tunnel m_LocalDestination->SubmitSessionKey (key, tag); i2p::garlic::ElGamalAESSession garlic (key, tag); msg = garlic.WrapSingleMessage (msg); - } + } outbound->SendTunnelDataMsgTo (it.second->GetNextIdentHash (), it.second->GetNextTunnelID (), msg); - } + } } void TunnelPool::ManageTunnels (uint64_t ts) @@ -544,14 +549,14 @@ namespace tunnel } } return found; - } - + } + bool TunnelPool::IsExploratory () const { return i2p::tunnel::tunnels.GetExploratoryPool () == shared_from_this (); } - std::shared_ptr TunnelPool::SelectNextHop (std::shared_ptr prevHop, + std::shared_ptr TunnelPool::SelectNextHop (std::shared_ptr prevHop, bool reverse, bool endpoint) const { bool tryClient = !IsExploratory () && !i2p::context.IsLimitedConnectivity (); @@ -560,7 +565,7 @@ namespace tunnel { hop = tryClient ? (m_IsHighBandwidth ? - i2p::data::netdb.GetHighBandwidthRandomRouter (prevHop, reverse, endpoint) : + i2p::data::netdb.GetHighBandwidthRandomRouter (prevHop, reverse, endpoint) : i2p::data::netdb.GetRandomRouter (prevHop, reverse, endpoint, true)): i2p::data::netdb.GetRandomRouter (prevHop, reverse, endpoint, false); if (hop) @@ -580,10 +585,11 @@ namespace tunnel { int start = 0; std::shared_ptr prevHop = i2p::context.GetSharedRouterInfo (); - if(i2p::transport::transports.RoutesRestricted()) + if(i2p::transport::transports.RoutesRestricted() || !m_TrustedRouters.empty ()) { - /** if routes are restricted prepend trusted first hop */ - auto hop = i2p::transport::transports.GetRestrictedPeer(); + /** if routes are restricted or trusted prepend trusted first hop */ + auto hop = (!m_TrustedRouters.empty ()) ? SelectTrustedRouter (inbound) : + i2p::transport::transports.GetRestrictedPeer (); if(!hop) return false; path.Add (hop); prevHop = hop; @@ -658,7 +664,7 @@ namespace tunnel if (m_CustomPeerSelector) return m_CustomPeerSelector->SelectPeers(path, numHops, isInbound); } - return StandardSelectPeers(path, numHops, isInbound, std::bind(&TunnelPool::SelectNextHop, this, + return StandardSelectPeers(path, numHops, isInbound, std::bind(&TunnelPool::SelectNextHop, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); } @@ -695,6 +701,37 @@ namespace tunnel return true; } + std::shared_ptr TunnelPool::SelectTrustedRouter (bool inbound) const + { + size_t count = m_TrustedRouters.size (); + if (!count) return nullptr; + std::shared_ptr r; + int ind = tunnels.GetRng ()() % count; + for (size_t i = 0; i < count; i++) + { + auto& ident = m_TrustedRouters[(ind + i) % count]; + if (inbound) + { + if (i2p::transport::transports.IsConnected (ident)) + { + r = i2p::data::netdb.FindRouter (ident); + break; + } + } + else + { + auto r1 = i2p::data::netdb.FindRouter (ident); + if (!r1) i2p::data::netdb.RequestDestination (ident, nullptr); // request one if not in NetDB + if (r1 && !r1->IsUnreachable () && r1->IsReachableFrom (i2p::context.GetRouterInfo ())) + { + r = r1; + break; + } + } + } + return r; + } + void TunnelPool::CreateInboundTunnel () { LogPrint (eLogDebug, "Tunnels: Creating destination inbound tunnel..."); @@ -734,9 +771,9 @@ namespace tunnel { auto peers = tunnel->GetPeers(); if (peers.size ()&& ValidatePeers (peers)) - config = std::make_shared(tunnel->GetPeers (), + config = std::make_shared(tunnel->GetPeers (), tunnel->IsShortBuildMessage (), tunnel->GetFarEndTransports ()); - } + } if (!m_NumInboundHops || config) { auto newTunnel = tunnels.CreateInboundTunnel (config, shared_from_this(), outboundTunnel); @@ -856,11 +893,11 @@ namespace tunnel { if (r->IsHighCongestion (highBandwidth)) return false; it = r->GetIdentity (); // use identity from updated RouterInfo - } - } + } + } return true; - } - + } + std::shared_ptr TunnelPool::GetLowestLatencyInboundTunnel(std::shared_ptr exclude) const { std::shared_ptr tun = nullptr; diff --git a/libi2pd/TunnelPool.h b/libi2pd/TunnelPool.h index 41400f8b..ef26f58d 100644 --- a/libi2pd/TunnelPool.h +++ b/libi2pd/TunnelPool.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2024, The PurpleI2P Project +* Copyright (c) 2013-2026, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -67,6 +67,7 @@ namespace tunnel std::shared_ptr GetLocalDestination () const { return m_LocalDestination; }; void SetLocalDestination (std::shared_ptr destination) { m_LocalDestination = destination; }; void SetExplicitPeers (std::shared_ptr > explicitPeers); + void SetTrustedRouters (std::vector routers); void CreateTunnels (); void TunnelCreated (std::shared_ptr createdTunnel); @@ -117,7 +118,7 @@ namespace tunnel // for overriding tunnel peer selection std::shared_ptr SelectNextHop (std::shared_ptr prevHop, bool reverse, bool endpoint) const; bool StandardSelectPeers(Path & path, int numHops, bool inbound, SelectHopFunc nextHop); - + private: void TestTunnels (); @@ -130,6 +131,7 @@ namespace tunnel bool SelectPeers (Path& path, bool isInbound); bool SelectExplicitPeers (Path& path, bool isInbound); bool ValidatePeers (std::vector >& peers) const; + std::shared_ptr SelectTrustedRouter (bool inbound) const; private: @@ -137,6 +139,7 @@ namespace tunnel int m_NumInboundHops, m_NumOutboundHops, m_NumInboundTunnels, m_NumOutboundTunnels, m_InboundVariance, m_OutboundVariance; std::shared_ptr > m_ExplicitPeers; + std::vector m_TrustedRouters; mutable std::mutex m_InboundTunnelsMutex; std::set, TunnelCreationTimeCmp> m_InboundTunnels; // recent tunnel appears first mutable std::mutex m_OutboundTunnelsMutex; @@ -150,7 +153,7 @@ namespace tunnel int m_MinLatency = 0; // if > 0 this tunnel pool will try building tunnels with minimum latency by ms int m_MaxLatency = 0; // if > 0 this tunnel pool will try building tunnels with maximum latency by ms - + public: // for HTTP only diff --git a/libi2pd_client/ClientContext.cpp b/libi2pd_client/ClientContext.cpp index 0199522d..969f9eb9 100644 --- a/libi2pd_client/ClientContext.cpp +++ b/libi2pd_client/ClientContext.cpp @@ -503,6 +503,8 @@ namespace client } std::string explicitPeers = GetI2CPStringOption(section, I2CP_PARAM_EXPLICIT_PEERS, ""); if (explicitPeers.length () > 0) options.Insert (I2CP_PARAM_EXPLICIT_PEERS, explicitPeers); + std::string trustedRouters = GetI2CPStringOption(section, I2CP_PARAM_TRUSTED_ROUTERS, ""); + if (trustedRouters.length () > 0) options.Insert (I2CP_PARAM_TRUSTED_ROUTERS, trustedRouters); std::string ratchetInboundTags = GetI2CPStringOption(section, I2CP_PARAM_RATCHET_INBOUND_TAGS, ""); if (ratchetInboundTags.length () > 0) options.Insert (I2CP_PARAM_RATCHET_INBOUND_TAGS, ratchetInboundTags); }