round-robin over several destinations in client tunnel

This commit is contained in:
PobreGato
2026-08-14 03:26:43 +03:00
parent 8b9bc39b4d
commit df9e55be2c
2 changed files with 38 additions and 14 deletions
+33 -12
View File
@@ -704,10 +704,21 @@ namespace client
Done(shared_from_this());
}
// destination is a comma separated list, so one tunnel can serve several servers
static std::vector<std::string> SplitDestinations (const std::string& destination)
{
std::vector<std::string> destinations;
boost::split (destinations, destination, boost::is_any_of (","), boost::token_compress_on);
for (auto& it: destinations) boost::trim (it);
std::erase (destinations, "");
return destinations;
}
I2PClientTunnel::I2PClientTunnel (const std::string& name, const std::string& destination,
const std::string& address, uint16_t port, std::shared_ptr<ClientDestination> localDestination, uint16_t destinationPort):
TCPIPAcceptor (address, port, localDestination), m_Name (name), m_Destination (destination),
m_DestinationPort (destinationPort), m_KeepAliveInterval (0)
TCPIPAcceptor (address, port, localDestination), m_Name (name),
m_Destinations (SplitDestinations (destination)), m_Addresses (m_Destinations.size ()),
m_NextAddress (0), m_DestinationPort (destinationPort), m_KeepAliveInterval (0)
{
}
@@ -722,7 +733,7 @@ namespace client
void I2PClientTunnel::Stop ()
{
TCPIPAcceptor::Stop();
m_Address = nullptr;
for (auto& it: m_Addresses) it = nullptr;
if (m_KeepAliveTimer) m_KeepAliveTimer->cancel ();
}
@@ -736,13 +747,21 @@ namespace client
/* HACK: maybe we should create a caching IdentHash provider in AddressBook */
std::shared_ptr<const Address> I2PClientTunnel::GetAddress ()
{
if (!m_Address)
// round-robin over the destinations, skipping those we can't resolve,
// so one dead server doesn't block the tunnel
for (size_t tried = 0; tried < m_Destinations.size (); tried++)
{
m_Address = i2p::client::context.GetAddressBook ().GetAddress (m_Destination);
if (!m_Address)
LogPrint (eLogWarning, "I2PTunnel: Remote destination ", m_Destination, " not found");
auto i = m_NextAddress;
m_NextAddress = (m_NextAddress + 1) % m_Destinations.size ();
if (!m_Addresses[i])
{
m_Addresses[i] = i2p::client::context.GetAddressBook ().GetAddress (m_Destinations[i]);
if (!m_Addresses[i])
LogPrint (eLogWarning, "I2PTunnel: Remote destination ", m_Destinations[i], " not found");
}
if (m_Addresses[i]) return m_Addresses[i];
}
return m_Address;
return nullptr;
}
std::shared_ptr<I2PServiceHandler> I2PClientTunnel::CreateHandler(std::shared_ptr<boost::asio::ip::tcp::socket> socket)
@@ -768,12 +787,14 @@ namespace client
{
if (ecode != boost::asio::error::operation_aborted)
{
if (m_Address && m_Address->IsValid ())
// ping every resolved destination, otherwise the spare ones go cold
for (const auto& address: m_Addresses)
{
if (m_Address->IsIdentHash ())
GetLocalDestination ()->SendPing (m_Address->identHash);
if (!address || !address->IsValid ()) continue;
if (address->IsIdentHash ())
GetLocalDestination ()->SendPing (address->identHash);
else
GetLocalDestination ()->SendPing (m_Address->blindedPublicKey);
GetLocalDestination ()->SendPing (address->blindedPublicKey);
}
ScheduleKeepAliveTimer ();
}
+5 -2
View File
@@ -13,6 +13,7 @@
#include <string>
#include <string_view>
#include <set>
#include <vector>
#include <tuple>
#include <memory>
#include <sstream>
@@ -176,8 +177,10 @@ namespace client
private:
std::string m_Name, m_Destination;
std::shared_ptr<const Address> m_Address;
std::string m_Name;
std::vector<std::string> m_Destinations;
std::vector<std::shared_ptr<const Address> > m_Addresses; // resolved lazily, same order
size_t m_NextAddress;
uint16_t m_DestinationPort;
uint32_t m_KeepAliveInterval;
std::unique_ptr<boost::asio::steady_timer> m_KeepAliveTimer;