diff --git a/.github/workflows/build-windows-msvc.yml b/.github/workflows/build-windows-msvc.yml
index 356bb466..172e0596 100644
--- a/.github/workflows/build-windows-msvc.yml
+++ b/.github/workflows/build-windows-msvc.yml
@@ -29,7 +29,7 @@ jobs:
- name: Install Boost
uses: crazy-max/ghaction-chocolatey@v2
with:
- args: install boost-msvc-14.3
+ args: install boost-msvc-14.3 --version=1.81.0
- name: Install OpenSSL
uses: crazy-max/ghaction-chocolatey@v2
diff --git a/Win32/Win32App.cpp b/Win32/Win32App.cpp
index fc61a8ac..9f750c4c 100644
--- a/Win32/Win32App.cpp
+++ b/Win32/Win32App.cpp
@@ -145,18 +145,19 @@ namespace win32
s << bytes << " Bytes\n";
}
- static void ShowNetworkStatus (std::stringstream& s, RouterStatus status)
+ static void ShowNetworkStatus (std::stringstream& s, RouterStatus status, bool testing)
{
switch (status)
{
case eRouterStatusOK: s << "OK"; break;
- case eRouterStatusTesting: s << "Test"; break;
case eRouterStatusFirewalled: s << "FW"; break;
case eRouterStatusUnknown: s << "Unk"; break;
case eRouterStatusProxy: s << "Proxy"; break;
case eRouterStatusMesh: s << "Mesh"; break;
default: s << "Unk";
};
+ if (testing)
+ s << " (Test)";
if (i2p::context.GetError () != eRouterErrorNone)
{
switch (i2p::context.GetError ())
@@ -179,11 +180,11 @@ namespace win32
{
s << "\n";
s << "Status: ";
- ShowNetworkStatus (s, i2p::context.GetStatus ());
+ ShowNetworkStatus (s, i2p::context.GetStatus (), i2p::context.GetTesting ());
if (i2p::context.SupportsV6 ())
{
s << " / ";
- ShowNetworkStatus (s, i2p::context.GetStatusV6 ());
+ ShowNetworkStatus (s, i2p::context.GetStatusV6 (), i2p::context.GetTestingV6 ());
}
s << "; ";
s << "Success Rate: " << i2p::tunnel::tunnels.GetTunnelCreationSuccessRate() << "%\n";
diff --git a/daemon/HTTPServer.cpp b/daemon/HTTPServer.cpp
index 50666940..2d6800b4 100644
--- a/daemon/HTTPServer.cpp
+++ b/daemon/HTTPServer.cpp
@@ -222,18 +222,19 @@ namespace http {
s << "" << tr("ERROR") << ": " << string << "
\r\n";
}
- static void ShowNetworkStatus (std::stringstream& s, RouterStatus status, RouterError error)
+ static void ShowNetworkStatus (std::stringstream& s, RouterStatus status, bool testing, RouterError error)
{
switch (status)
{
case eRouterStatusOK: s << tr("OK"); break;
- case eRouterStatusTesting: s << tr("Testing"); break;
case eRouterStatusFirewalled: s << tr("Firewalled"); break;
case eRouterStatusUnknown: s << tr("Unknown"); break;
case eRouterStatusProxy: s << tr("Proxy"); break;
case eRouterStatusMesh: s << tr("Mesh"); break;
default: s << tr("Unknown");
}
+ if (testing)
+ s << " (" << tr("Testing") << ")";
if (error != eRouterErrorNone)
{
switch (error)
@@ -264,12 +265,12 @@ namespace http {
ShowUptime(s, i2p::context.GetUptime ());
s << "
\r\n";
s << "" << tr("Network status") << ": ";
- ShowNetworkStatus (s, i2p::context.GetStatus (), i2p::context.GetError ());
+ ShowNetworkStatus (s, i2p::context.GetStatus (), i2p::context.GetTesting(), i2p::context.GetError ());
s << "
\r\n";
if (i2p::context.SupportsV6 ())
{
s << "" << tr("Network status v6") << ": ";
- ShowNetworkStatus (s, i2p::context.GetStatusV6 (), i2p::context.GetErrorV6 ());
+ ShowNetworkStatus (s, i2p::context.GetStatusV6 (), i2p::context.GetTestingV6(), i2p::context.GetErrorV6 ());
s << "
\r\n";
}
#if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY))
diff --git a/libi2pd/Crypto.cpp b/libi2pd/Crypto.cpp
index 4a78f2b1..12087443 100644
--- a/libi2pd/Crypto.cpp
+++ b/libi2pd/Crypto.cpp
@@ -991,7 +991,7 @@ namespace crypto
EVP_EncryptInit_ex(ctx, NULL, NULL, key, nonce);
EVP_EncryptUpdate(ctx, NULL, &outlen, ad, adLen);
EVP_EncryptUpdate(ctx, buf, &outlen, msg, msgLen);
- EVP_EncryptFinal_ex(ctx, buf, &outlen);
+ EVP_EncryptFinal_ex(ctx, buf + outlen, &outlen);
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, 16, buf + msgLen);
}
else
diff --git a/libi2pd/NTCP2.cpp b/libi2pd/NTCP2.cpp
index 5e1cbaf6..bbd93435 100644
--- a/libi2pd/NTCP2.cpp
+++ b/libi2pd/NTCP2.cpp
@@ -375,7 +375,16 @@ namespace transport
m_Server.RemoveNTCP2Session (shared_from_this ());
m_SendQueue.clear ();
m_SendQueueSize = 0;
- LogPrint (eLogDebug, "NTCP2: Session terminated");
+ auto remoteIdentity = GetRemoteIdentity ();
+ if (remoteIdentity)
+ {
+ LogPrint (eLogDebug, "NTCP2: Session with ", GetRemoteEndpoint (),
+ " (", i2p::data::GetIdentHashAbbreviation (GetRemoteIdentity ()->GetIdentHash ()), ") terminated");
+ }
+ else
+ {
+ LogPrint (eLogDebug, "NTCP2: Session with ", GetRemoteEndpoint (), " terminated");
+ }
}
}
@@ -691,16 +700,25 @@ namespace transport
i2p::data::RouterInfo ri (buf.data () + 4, size - 1); // 1 byte block type + 2 bytes size + 1 byte flag
if (ri.IsUnreachable ())
{
- LogPrint (eLogError, "NTCP2: Signature verification failed in SessionConfirmed");
+ LogPrint (eLogError, "NTCP2: RouterInfo verification failed in SessionConfirmed from ", GetRemoteEndpoint ());
SendTerminationAndTerminate (eNTCP2RouterInfoSignatureVerificationFail);
return;
}
- if (i2p::util::GetMillisecondsSinceEpoch () > ri.GetTimestamp () + i2p::data::NETDB_MIN_EXPIRATION_TIMEOUT*1000LL) // 90 minutes
+ LogPrint(eLogDebug, "NTCP2: SessionConfirmed from ", GetRemoteEndpoint (),
+ " (", i2p::data::GetIdentHashAbbreviation (ri.GetIdentHash ()), ")");
+ auto ts = i2p::util::GetMillisecondsSinceEpoch ();
+ if (ts > ri.GetTimestamp () + i2p::data::NETDB_MIN_EXPIRATION_TIMEOUT*1000LL) // 90 minutes
{
- LogPrint (eLogError, "NTCP2: RouterInfo is too old in SessionConfirmed");
+ LogPrint (eLogError, "NTCP2: RouterInfo is too old in SessionConfirmed for ", (ts - ri.GetTimestamp ())/1000LL, " seconds");
SendTerminationAndTerminate (eNTCP2Message3Error);
return;
}
+ if (ts + i2p::data::NETDB_EXPIRATION_TIMEOUT_THRESHOLD*1000LL < ri.GetTimestamp ()) // 2 minutes
+ {
+ LogPrint (eLogError, "NTCP2: RouterInfo is from future for ", (ri.GetTimestamp () - ts)/1000LL, " seconds");
+ SendTerminationAndTerminate (eNTCP2Message3Error);
+ return;
+ }
auto addr = m_RemoteEndpoint.address ().is_v4 () ? ri.GetNTCP2V4Address () :
(i2p::util::net::IsYggdrasilAddress (m_RemoteEndpoint.address ()) ? ri.GetYggdrasilAddress () : ri.GetNTCP2V6Address ());
if (!addr || memcmp (m_Establisher->m_RemoteStaticKey, addr->s, 32))
@@ -884,7 +902,7 @@ namespace transport
auto size = bufbe16toh (frame + offset);
offset += 2;
LogPrint (eLogDebug, "NTCP2: Block type ", (int)blk, " of size ", size);
- if (size > len)
+ if (offset + size > len)
{
LogPrint (eLogError, "NTCP2: Unexpected block length ", size);
break;
@@ -1402,7 +1420,8 @@ namespace transport
LogPrint (eLogError, "NTCP2: Can't connect to unspecified address");
return;
}
- LogPrint (eLogDebug, "NTCP2: Connecting to ", conn->GetRemoteEndpoint ());
+ LogPrint (eLogDebug, "NTCP2: Connecting to ", conn->GetRemoteEndpoint (),
+ " (", i2p::data::GetIdentHashAbbreviation (conn->GetRemoteIdentity ()->GetIdentHash ()), ")");
GetService ().post([this, conn]()
{
if (this->AddNTCP2Session (conn))
@@ -1458,7 +1477,8 @@ namespace transport
}
else
{
- LogPrint (eLogDebug, "NTCP2: Connected to ", conn->GetRemoteEndpoint ());
+ LogPrint (eLogDebug, "NTCP2: Connected to ", conn->GetRemoteEndpoint (),
+ " (", i2p::data::GetIdentHashAbbreviation (conn->GetRemoteIdentity ()->GetIdentHash ()), ")");
conn->ClientLogin ();
}
}
diff --git a/libi2pd/NetDb.cpp b/libi2pd/NetDb.cpp
index 5134dbe0..061ccc23 100644
--- a/libi2pd/NetDb.cpp
+++ b/libi2pd/NetDb.cpp
@@ -59,7 +59,7 @@ namespace data
{
Reseed ();
}
- else if (!GetRandomRouter (i2p::context.GetSharedRouterInfo (), false))
+ else if (!GetRandomRouter (i2p::context.GetSharedRouterInfo (), false, false))
Reseed (); // we don't have a router we can connect to. Trying to reseed
auto it = m_RouterInfos.find (i2p::context.GetIdentHash ());
@@ -424,12 +424,9 @@ namespace data
if (r)
{
r->SetUnreachable (unreachable);
- if (unreachable)
- {
- auto profile = r->GetProfile ();
- if (profile)
- profile->Unreachable ();
- }
+ auto profile = r->GetProfile ();
+ if (profile)
+ profile->Unreachable (unreachable);
}
}
@@ -1199,15 +1196,17 @@ namespace data
});
}
- std::shared_ptr NetDb::GetRandomRouter (std::shared_ptr compatibleWith, bool reverse) const
+ std::shared_ptr NetDb::GetRandomRouter (std::shared_ptr compatibleWith,
+ bool reverse, bool endpoint) const
{
return GetRandomRouter (
- [compatibleWith, reverse](std::shared_ptr router)->bool
+ [compatibleWith, reverse, endpoint](std::shared_ptr router)->bool
{
return !router->IsHidden () && router != compatibleWith &&
- (reverse ? compatibleWith->IsReachableFrom (*router) :
+ (reverse ? (compatibleWith->IsReachableFrom (*router) && router->GetCompatibleTransports (true)):
router->IsReachableFrom (*compatibleWith)) &&
- router->IsECIES () && !router->IsHighCongestion (false);
+ router->IsECIES () && !router->IsHighCongestion (false) &&
+ (!endpoint || (router->IsV4 () && (!reverse || router->IsPublished (true)))); // endpoint must be ipv4 and published if inbound(reverse)
});
}
@@ -1231,17 +1230,20 @@ namespace data
});
}
- std::shared_ptr NetDb::GetHighBandwidthRandomRouter (std::shared_ptr compatibleWith, bool reverse) const
+ std::shared_ptr NetDb::GetHighBandwidthRandomRouter (std::shared_ptr compatibleWith,
+ bool reverse, bool endpoint) const
{
return GetRandomRouter (
- [compatibleWith, reverse](std::shared_ptr router)->bool
+ [compatibleWith, reverse, endpoint](std::shared_ptr router)->bool
{
return !router->IsHidden () && router != compatibleWith &&
- (reverse ? compatibleWith->IsReachableFrom (*router) :
+ (reverse ? (compatibleWith->IsReachableFrom (*router) && router->GetCompatibleTransports (true)) :
router->IsReachableFrom (*compatibleWith)) &&
(router->GetCaps () & RouterInfo::eHighBandwidth) &&
router->GetVersion () >= NETDB_MIN_HIGHBANDWIDTH_VERSION &&
- router->IsECIES () && !router->IsHighCongestion (true);
+ router->IsECIES () && !router->IsHighCongestion (true) &&
+ (!endpoint || (router->IsV4 () && (!reverse || router->IsPublished (true)))); // endpoint must be ipv4 and published if inbound(reverse)
+
});
}
diff --git a/libi2pd/NetDb.hpp b/libi2pd/NetDb.hpp
index adfdcde7..b7d9a5b3 100644
--- a/libi2pd/NetDb.hpp
+++ b/libi2pd/NetDb.hpp
@@ -84,8 +84,8 @@ namespace data
void HandleNTCP2RouterInfoMsg (std::shared_ptr m);
std::shared_ptr GetRandomRouter () const;
- std::shared_ptr GetRandomRouter (std::shared_ptr compatibleWith, bool reverse) const;
- std::shared_ptr GetHighBandwidthRandomRouter (std::shared_ptr compatibleWith, bool reverse) const;
+ std::shared_ptr GetRandomRouter (std::shared_ptr compatibleWith, bool reverse, bool endpoint) const;
+ std::shared_ptr GetHighBandwidthRandomRouter (std::shared_ptr compatibleWith, bool reverse, bool endpoint) const;
std::shared_ptr GetRandomSSU2PeerTestRouter (bool v4, const std::set& excluded) const;
std::shared_ptr GetRandomSSU2Introducer (bool v4, const std::set& excluded) const;
std::shared_ptr GetClosestFloodfill (const IdentHash& destination, const std::set& excluded) const;
diff --git a/libi2pd/Profiling.cpp b/libi2pd/Profiling.cpp
index 47fc15e7..879aea29 100644
--- a/libi2pd/Profiling.cpp
+++ b/libi2pd/Profiling.cpp
@@ -165,12 +165,12 @@ namespace data
}
}
- void RouterProfile::Unreachable ()
+ void RouterProfile::Unreachable (bool unreachable)
{
- m_LastUnreachableTime = i2p::util::GetSecondsSinceEpoch ();
+ m_LastUnreachableTime = unreachable ? i2p::util::GetSecondsSinceEpoch () : 0;
UpdateTime ();
}
-
+
void RouterProfile::Connected ()
{
m_HasConnected = true;
diff --git a/libi2pd/Profiling.h b/libi2pd/Profiling.h
index 6b814893..c351b41d 100644
--- a/libi2pd/Profiling.h
+++ b/libi2pd/Profiling.h
@@ -55,7 +55,7 @@ namespace data
void TunnelBuildResponse (uint8_t ret);
void TunnelNonReplied ();
- void Unreachable ();
+ void Unreachable (bool unreachable);
void Connected ();
boost::posix_time::ptime GetLastUpdateTime () const { return m_LastUpdateTime; };
diff --git a/libi2pd/RouterContext.cpp b/libi2pd/RouterContext.cpp
index 3e7d7a2c..fa3ba7bd 100644
--- a/libi2pd/RouterContext.cpp
+++ b/libi2pd/RouterContext.cpp
@@ -31,7 +31,8 @@ namespace i2p
RouterContext::RouterContext ():
m_LastUpdateTime (0), m_AcceptsTunnels (true), m_IsFloodfill (false),
m_ShareRatio (100), m_Status (eRouterStatusUnknown), m_StatusV6 (eRouterStatusUnknown),
- m_Error (eRouterErrorNone), m_ErrorV6 (eRouterErrorNone), m_NetID (I2PD_NET_ID),
+ m_Error (eRouterErrorNone), m_ErrorV6 (eRouterErrorNone),
+ m_Testing (false), m_TestingV6 (false), m_NetID (I2PD_NET_ID),
m_PublishReplyToken (0), m_IsHiddenMode (false)
{
}
@@ -277,8 +278,29 @@ namespace i2p
fk.write ((char *)m_SSU2Keys.get (), sizeof (SSU2PrivateKeys));
}
+ void RouterContext::SetTesting (bool testing)
+ {
+ if (testing != m_Testing)
+ {
+ m_Testing = testing;
+ if (m_Testing)
+ m_Error = eRouterErrorNone;
+ }
+ }
+
+ void RouterContext::SetTestingV6 (bool testing)
+ {
+ if (testing != m_TestingV6)
+ {
+ m_TestingV6 = testing;
+ if (m_TestingV6)
+ m_ErrorV6 = eRouterErrorNone;
+ }
+ }
+
void RouterContext::SetStatus (RouterStatus status)
{
+ SetTesting (false);
if (status != m_Status)
{
m_Status = status;
@@ -290,9 +312,6 @@ namespace i2p
case eRouterStatusFirewalled:
SetUnreachable (true, false); // ipv4
break;
- case eRouterStatusTesting:
- m_Error = eRouterErrorNone;
- break;
default:
;
}
@@ -301,6 +320,7 @@ namespace i2p
void RouterContext::SetStatusV6 (RouterStatus status)
{
+ SetTestingV6 (false);
if (status != m_StatusV6)
{
m_StatusV6 = status;
@@ -312,9 +332,6 @@ namespace i2p
case eRouterStatusFirewalled:
SetUnreachable (false, true); // ipv6
break;
- case eRouterStatusTesting:
- m_ErrorV6 = eRouterErrorNone;
- break;
default:
;
}
diff --git a/libi2pd/RouterContext.h b/libi2pd/RouterContext.h
index b8183339..d49b5523 100644
--- a/libi2pd/RouterContext.h
+++ b/libi2pd/RouterContext.h
@@ -42,11 +42,10 @@ namespace garlic
enum RouterStatus
{
eRouterStatusOK = 0,
- eRouterStatusTesting = 1,
- eRouterStatusFirewalled = 2,
- eRouterStatusUnknown = 3,
- eRouterStatusProxy = 4,
- eRouterStatusMesh = 5
+ eRouterStatusFirewalled = 1,
+ eRouterStatusUnknown = 2,
+ eRouterStatusProxy = 3,
+ eRouterStatusMesh = 4
};
enum RouterError
@@ -121,10 +120,14 @@ namespace garlic
uint64_t GetLastUpdateTime () const { return m_LastUpdateTime; };
uint64_t GetBandwidthLimit () const { return m_BandwidthLimit; };
uint64_t GetTransitBandwidthLimit () const { return (m_BandwidthLimit*m_ShareRatio)/100LL; };
+ bool GetTesting () const { return m_Testing; };
+ void SetTesting (bool testing);
RouterStatus GetStatus () const { return m_Status; };
void SetStatus (RouterStatus status);
RouterError GetError () const { return m_Error; };
void SetError (RouterError error) { m_Error = error; };
+ bool GetTestingV6 () const { return m_TestingV6; };
+ void SetTestingV6 (bool testing);
RouterStatus GetStatusV6 () const { return m_StatusV6; };
void SetStatusV6 (RouterStatus status);
RouterError GetErrorV6 () const { return m_ErrorV6; };
@@ -231,6 +234,7 @@ namespace garlic
int m_ShareRatio;
RouterStatus m_Status, m_StatusV6;
RouterError m_Error, m_ErrorV6;
+ bool m_Testing, m_TestingV6;
int m_NetID;
std::unique_ptr m_NTCP2Keys;
std::unique_ptr m_SSU2Keys;
diff --git a/libi2pd/RouterInfo.cpp b/libi2pd/RouterInfo.cpp
index 5ba8a5b1..63cb79ef 100644
--- a/libi2pd/RouterInfo.cpp
+++ b/libi2pd/RouterInfo.cpp
@@ -579,7 +579,7 @@ namespace data
int numValid = 0;
for (auto& it: address->ssu->introducers)
{
- if (it.iTag && ts < it.iExp)
+ if (it.iTag && ts < it.iExp && !it.iH.IsZero ())
numValid++;
else
it.iTag = 0;
@@ -995,6 +995,7 @@ namespace data
bool RouterInfo::IsPublished (bool v4) const
{
+ if (m_Caps & (eUnreachable | eHidden)) return false; // if router sets U or H we assume that all addreses are not published
auto addr = GetAddresses ();
if (v4)
return ((*addr)[eNTCP2V4Idx] && ((*addr)[eNTCP2V4Idx])->published) ||
@@ -1320,6 +1321,7 @@ namespace data
int i = 0;
for (const auto& introducer: address.ssu->introducers)
{
+ if (!introducer.iTag) continue;
if (introducer.iExp) // expiration is specified
{
WriteString ("iexp" + boost::lexical_cast(i), properties);
@@ -1332,6 +1334,7 @@ namespace data
i = 0;
for (const auto& introducer: address.ssu->introducers)
{
+ if (!introducer.iTag) continue;
WriteString ("ih" + boost::lexical_cast(i), properties);
properties << '=';
char value[64];
@@ -1344,6 +1347,7 @@ namespace data
i = 0;
for (const auto& introducer: address.ssu->introducers)
{
+ if (!introducer.iTag) continue;
WriteString ("itag" + boost::lexical_cast(i), properties);
properties << '=';
WriteString (boost::lexical_cast(introducer.iTag), properties);
diff --git a/libi2pd/RouterInfo.h b/libi2pd/RouterInfo.h
index 47ee1dae..9aff2240 100644
--- a/libi2pd/RouterInfo.h
+++ b/libi2pd/RouterInfo.h
@@ -125,7 +125,7 @@ namespace data
struct Introducer
{
- Introducer (): iTag (0), iExp (0) {};
+ Introducer (): iTag (0), iExp (0) { iH.Fill(0); };
IdentHash iH;
uint32_t iTag;
uint32_t iExp;
diff --git a/libi2pd/SSU2.cpp b/libi2pd/SSU2.cpp
index a9e04790..7099ba0c 100644
--- a/libi2pd/SSU2.cpp
+++ b/libi2pd/SSU2.cpp
@@ -983,7 +983,7 @@ namespace transport
void SSU2Server::UpdateIntroducers (bool v4)
{
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
- std::list newList;
+ std::list newList, impliedList;
auto& introducers = v4 ? m_Introducers : m_IntroducersV6;
std::set excluded;
for (const auto& it : introducers)
@@ -995,12 +995,19 @@ namespace transport
session = it1->second;
excluded.insert (it);
}
- if (session && session->IsEstablished ())
+ if (session && session->IsEstablished () && session->GetRelayTag () && session->IsOutgoing ()) // still session with introducer?
{
if (ts < session->GetCreationTime () + SSU2_TO_INTRODUCER_SESSION_EXPIRATION)
+ {
session->SendKeepAlive ();
- if (ts < session->GetCreationTime () + SSU2_TO_INTRODUCER_SESSION_DURATION)
- newList.push_back (it);
+ if (ts < session->GetCreationTime () + SSU2_TO_INTRODUCER_SESSION_DURATION)
+ newList.push_back (it);
+ else
+ {
+ impliedList.push_back (it); // keep in introducers list, but not publish
+ session = nullptr;
+ }
+ }
else
session = nullptr;
}
@@ -1014,20 +1021,18 @@ namespace transport
{
// bump creation time for previous introducers if no new sessions found
LogPrint (eLogDebug, "SSU2: No new introducers found. Trying to reuse existing");
+ impliedList.clear ();
for (auto& it : introducers)
{
auto it1 = m_SessionsByRouterHash.find (it);
if (it1 != m_SessionsByRouterHash.end ())
{
auto session = it1->second;
- if (session->IsEstablished ())
+ if (session->IsEstablished () && session->GetRelayTag () && session->IsOutgoing ())
{
session->SetCreationTime (session->GetCreationTime () + SSU2_TO_INTRODUCER_SESSION_DURATION);
if (std::find (newList.begin (), newList.end (), it) == newList.end ())
- {
- newList.push_back (it);
sessions.push_back (session);
- }
}
}
}
@@ -1035,10 +1040,14 @@ namespace transport
for (const auto& it : sessions)
{
+ uint32_t tag = it->GetRelayTag ();
+ uint32_t exp = it->GetCreationTime () + SSU2_TO_INTRODUCER_SESSION_EXPIRATION;
+ if (!tag || ts + SSU2_TO_INTRODUCER_SESSION_DURATION/2 > exp)
+ continue; // don't pick too old session for introducer
i2p::data::RouterInfo::Introducer introducer;
- introducer.iTag = it->GetRelayTag ();
+ introducer.iTag = tag;
introducer.iH = it->GetRemoteIdentity ()->GetIdentHash ();
- introducer.iExp = it->GetCreationTime () + SSU2_TO_INTRODUCER_SESSION_EXPIRATION;
+ introducer.iExp = exp;
excluded.insert (it->GetRemoteIdentity ()->GetIdentHash ());
if (i2p::context.AddSSU2Introducer (introducer, v4))
{
@@ -1072,13 +1081,15 @@ namespace transport
}
}
}
+ introducers.splice (introducers.end (), impliedList); // insert non-published, but non-expired introducers back
}
void SSU2Server::ScheduleIntroducersUpdateTimer ()
{
if (m_IsPublished)
{
- m_IntroducersUpdateTimer.expires_from_now (boost::posix_time::seconds(SSU2_KEEP_ALIVE_INTERVAL));
+ m_IntroducersUpdateTimer.expires_from_now (boost::posix_time::seconds(
+ SSU2_KEEP_ALIVE_INTERVAL + rand () % SSU2_KEEP_ALIVE_INTERVAL_VARIANCE));
m_IntroducersUpdateTimer.async_wait (std::bind (&SSU2Server::HandleIntroducersUpdateTimer,
this, std::placeholders::_1, true));
}
@@ -1091,7 +1102,8 @@ namespace transport
m_IntroducersUpdateTimer.cancel ();
i2p::context.ClearSSU2Introducers (true);
m_Introducers.clear ();
- m_IntroducersUpdateTimer.expires_from_now (boost::posix_time::seconds(SSU2_KEEP_ALIVE_INTERVAL/2));
+ m_IntroducersUpdateTimer.expires_from_now (boost::posix_time::seconds(
+ (SSU2_KEEP_ALIVE_INTERVAL + rand () % SSU2_KEEP_ALIVE_INTERVAL_VARIANCE)/2));
m_IntroducersUpdateTimer.async_wait (std::bind (&SSU2Server::HandleIntroducersUpdateTimer,
this, std::placeholders::_1, true));
}
@@ -1101,7 +1113,8 @@ namespace transport
{
if (m_IsPublished)
{
- m_IntroducersUpdateTimerV6.expires_from_now (boost::posix_time::seconds(SSU2_KEEP_ALIVE_INTERVAL));
+ m_IntroducersUpdateTimerV6.expires_from_now (boost::posix_time::seconds(
+ SSU2_KEEP_ALIVE_INTERVAL + rand () % SSU2_KEEP_ALIVE_INTERVAL_VARIANCE));
m_IntroducersUpdateTimerV6.async_wait (std::bind (&SSU2Server::HandleIntroducersUpdateTimer,
this, std::placeholders::_1, false));
}
@@ -1114,7 +1127,8 @@ namespace transport
m_IntroducersUpdateTimerV6.cancel ();
i2p::context.ClearSSU2Introducers (false);
m_IntroducersV6.clear ();
- m_IntroducersUpdateTimerV6.expires_from_now (boost::posix_time::seconds(SSU2_KEEP_ALIVE_INTERVAL/2));
+ m_IntroducersUpdateTimerV6.expires_from_now (boost::posix_time::seconds(
+ (SSU2_KEEP_ALIVE_INTERVAL + rand () % SSU2_KEEP_ALIVE_INTERVAL_VARIANCE)/2));
m_IntroducersUpdateTimerV6.async_wait (std::bind (&SSU2Server::HandleIntroducersUpdateTimer,
this, std::placeholders::_1, false));
}
@@ -1127,7 +1141,7 @@ namespace transport
// timeout expired
if (v4)
{
- if (i2p::context.GetStatus () == eRouterStatusTesting)
+ if (i2p::context.GetTesting ())
{
// we still don't know if we need introducers
ScheduleIntroducersUpdateTimer ();
@@ -1150,7 +1164,7 @@ namespace transport
}
else
{
- if (i2p::context.GetStatusV6 () == eRouterStatusTesting)
+ if (i2p::context.GetTestingV6 ())
{
// we still don't know if we need introducers
ScheduleIntroducersUpdateTimerV6 ();
diff --git a/libi2pd/SSU2.h b/libi2pd/SSU2.h
index 2e652786..a1fafc63 100644
--- a/libi2pd/SSU2.h
+++ b/libi2pd/SSU2.h
@@ -29,7 +29,8 @@ namespace transport
const size_t SSU2_MAX_NUM_INTRODUCERS = 3;
const int SSU2_TO_INTRODUCER_SESSION_DURATION = 3600; // 1 hour
const int SSU2_TO_INTRODUCER_SESSION_EXPIRATION = 4800; // 80 minutes
- const int SSU2_KEEP_ALIVE_INTERVAL = 30; // in seconds
+ const int SSU2_KEEP_ALIVE_INTERVAL = 15; // in seconds
+ const int SSU2_KEEP_ALIVE_INTERVAL_VARIANCE = 4; // in seconds
const int SSU2_PROXY_CONNECT_RETRY_TIMEOUT = 30; // in seconds
class SSU2Server: private i2p::util::RunnableServiceWithWork
diff --git a/libi2pd/SSU2Session.cpp b/libi2pd/SSU2Session.cpp
index 49004437..f0e539c7 100644
--- a/libi2pd/SSU2Session.cpp
+++ b/libi2pd/SSU2Session.cpp
@@ -114,6 +114,8 @@ namespace transport
{
if (m_State == eSSU2SessionStateUnknown || m_State == eSSU2SessionStateTokenReceived)
{
+ LogPrint(eLogDebug, "SSU2: Connecting to ", GetRemoteEndpoint (),
+ " (", i2p::data::GetIdentHashAbbreviation (GetRemoteIdentity ()->GetIdentHash ()), ")");
ScheduleConnectTimer ();
auto token = m_Server.FindOutgoingToken (m_RemoteEndpoint);
if (token)
@@ -243,7 +245,7 @@ namespace transport
if (IsEstablished ())
{
uint8_t payload[20];
- size_t payloadSize = CreatePaddingBlock (payload, 20, 5);
+ size_t payloadSize = CreatePaddingBlock (payload, 20, 8);
SendData (payload, payloadSize);
}
}
@@ -269,7 +271,16 @@ namespace transport
m_ReceivedI2NPMsgIDs.clear ();
m_Server.RemoveSession (m_SourceConnID);
transports.PeerDisconnected (shared_from_this ());
- LogPrint (eLogDebug, "SSU2: Session terminated");
+ auto remoteIdentity = GetRemoteIdentity ();
+ if (remoteIdentity)
+ {
+ LogPrint (eLogDebug, "SSU2: Session with ", GetRemoteEndpoint (),
+ " (", i2p::data::GetIdentHashAbbreviation (GetRemoteIdentity ()->GetIdentHash ()), ") terminated");
+ }
+ else
+ {
+ LogPrint (eLogDebug, "SSU2: Session with ", GetRemoteEndpoint (), " terminated");
+ }
}
}
@@ -298,6 +309,8 @@ namespace transport
m_OnEstablished ();
m_OnEstablished = nullptr;
}
+ LogPrint(eLogDebug, "SSU2: Session with ", GetRemoteEndpoint (),
+ " (", i2p::data::GetIdentHashAbbreviation (GetRemoteIdentity ()->GetIdentHash ()), ") established");
}
void SSU2Session::Done ()
@@ -1053,6 +1066,17 @@ namespace transport
LogPrint (eLogError, "SSU2: SessionConfirmed malformed RouterInfo block");
return false;
}
+ auto ts = i2p::util::GetMillisecondsSinceEpoch();
+ if (ts > ri->GetTimestamp () + i2p::data::NETDB_MIN_EXPIRATION_TIMEOUT*1000LL) // 90 minutes
+ {
+ LogPrint (eLogError, "SSU2: RouterInfo in SessionConfirmed is too old for ", (ts - ri->GetTimestamp ())/1000LL, " seconds");
+ return false;
+ }
+ if (ts + i2p::data::NETDB_EXPIRATION_TIMEOUT_THRESHOLD*1000LL < ri->GetTimestamp ()) // 2 minutes
+ {
+ LogPrint (eLogError, "SSU2: RouterInfo in SessionConfirmed is from future for ", (ri->GetTimestamp () - ts)/1000LL, " seconds");
+ return false;
+ }
m_Address = m_RemoteEndpoint.address ().is_v6 () ? ri->GetSSU2V6Address () : ri->GetSSU2V4Address ();
if (!m_Address || memcmp (S, m_Address->s, 32))
{
@@ -1486,7 +1510,7 @@ namespace transport
auto size = bufbe16toh (buf + offset);
offset += 2;
LogPrint (eLogDebug, "SSU2: Block type ", (int)blk, " of size ", size);
- if (size > len)
+ if (offset + size > len)
{
LogPrint (eLogError, "SSU2: Unexpected block length ", size);
break;
@@ -1532,16 +1556,21 @@ namespace transport
break;
case eSSU2BlkTermination:
{
- uint8_t rsn = buf[11]; // reason
- LogPrint (eLogDebug, "SSU2: Termination reason=", (int)rsn);
- if (IsEstablished () && rsn != eSSU2TerminationReasonTerminationReceived)
- RequestTermination (eSSU2TerminationReasonTerminationReceived);
- else if (m_State != eSSU2SessionStateTerminated)
+ if (size >= 9)
{
- if (m_State == eSSU2SessionStateClosing && rsn == eSSU2TerminationReasonTerminationReceived)
- m_State = eSSU2SessionStateClosingConfirmed;
- Done ();
+ uint8_t rsn = buf[offset + 8]; // reason
+ LogPrint (eLogDebug, "SSU2: Termination reason=", (int)rsn);
+ if (IsEstablished () && rsn != eSSU2TerminationReasonTerminationReceived)
+ RequestTermination (eSSU2TerminationReasonTerminationReceived);
+ else if (m_State != eSSU2SessionStateTerminated)
+ {
+ if (m_State == eSSU2SessionStateClosing && rsn == eSSU2TerminationReasonTerminationReceived)
+ m_State = eSSU2SessionStateClosingConfirmed;
+ Done ();
+ }
}
+ else
+ LogPrint(eLogWarning, "SSU2: Unexpected termination block size ", size);
break;
}
case eSSU2BlkRelayRequest:
@@ -1635,8 +1664,8 @@ namespace transport
break;
case eSSU2SessionStateSessionCreatedReceived:
case eSSU2SessionStateTokenReceived:
- if ((m_RemoteEndpoint.address ().is_v4 () && i2p::context.GetStatus () == eRouterStatusTesting) ||
- (m_RemoteEndpoint.address ().is_v6 () && i2p::context.GetStatusV6 () == eRouterStatusTesting))
+ if ((m_RemoteEndpoint.address ().is_v4 () && i2p::context.GetTesting ()) ||
+ (m_RemoteEndpoint.address ().is_v6 () && i2p::context.GetTestingV6 ()))
{
if (m_Server.IsSyncClockFromPeers ())
{
@@ -1734,14 +1763,14 @@ namespace transport
LogPrint (eLogInfo, "SSU2: Our port ", ep.port (), " received from ", m_RemoteEndpoint, " is different from ", m_Server.GetPort (isV4));
if (isV4)
{
- if (i2p::context.GetStatus () == eRouterStatusTesting)
+ if (i2p::context.GetTesting ())
i2p::context.SetError (eRouterErrorSymmetricNAT);
else if (m_State == eSSU2SessionStatePeerTest)
i2p::context.SetError (eRouterErrorFullConeNAT);
}
else
{
- if (i2p::context.GetStatusV6 () == eRouterStatusTesting)
+ if (i2p::context.GetTestingV6 ())
i2p::context.SetErrorV6 (eRouterErrorSymmetricNAT);
else if (m_State == eSSU2SessionStatePeerTest)
i2p::context.SetErrorV6 (eRouterErrorFullConeNAT);
@@ -2214,7 +2243,7 @@ namespace transport
if (buf[1] == eSSU2PeerTestCodeAccept)
{
if (GetRouterStatus () == eRouterStatusUnknown)
- SetRouterStatus (eRouterStatusTesting);
+ SetTestingState (true);
auto r = i2p::data::netdb.FindRouter (buf + 3); // find Charlie
if (r && it->second.first)
{
@@ -2240,13 +2269,17 @@ namespace transport
}
else
{
- if (GetRouterStatus () == eRouterStatusTesting)
+ if (GetTestingState ())
{
- SetRouterStatus (eRouterStatusFirewalled);
- if (m_Address->IsV4 ())
- m_Server.RescheduleIntroducersUpdateTimer ();
- else
- m_Server.RescheduleIntroducersUpdateTimerV6 ();
+ SetTestingState (false);
+ if (GetRouterStatus () != eRouterStatusFirewalled)
+ {
+ SetRouterStatus (eRouterStatusFirewalled);
+ if (m_Address->IsV4 ())
+ m_Server.RescheduleIntroducersUpdateTimer ();
+ else
+ m_Server.RescheduleIntroducersUpdateTimerV6 ();
+ }
}
}
LogPrint (eLogDebug, "SSU2: Peer test 4 received from ", i2p::data::GetIdentHashAbbreviation (GetRemoteIdentity ()->GetIdentHash ()),
@@ -2275,7 +2308,7 @@ namespace transport
{
LogPrint (eLogInfo, "SSU2: Peer test 4 error code ", (int)buf[1], " from ",
i2p::data::GetIdentHashAbbreviation (buf[1] < 64 ? GetRemoteIdentity ()->GetIdentHash () : i2p::data::IdentHash (buf + 3)));
- if (GetRouterStatus () == eRouterStatusTesting)
+ if (GetTestingState ())
SetRouterStatus (eRouterStatusUnknown);
it->second.first->Done ();
}
@@ -2429,6 +2462,29 @@ namespace transport
}
}
+ bool SSU2Session::GetTestingState () const
+ {
+ if (m_Address)
+ {
+ if (m_Address->IsV4 ())
+ return i2p::context.GetTesting ();
+ if (m_Address->IsV6 ())
+ return i2p::context.GetTestingV6 ();
+ }
+ return false;
+ }
+
+ void SSU2Session::SetTestingState (bool testing) const
+ {
+ if (m_Address)
+ {
+ if (m_Address->IsV4 ())
+ i2p::context.SetTesting (testing);
+ else if (m_Address->IsV6 ())
+ i2p::context.SetTestingV6 (testing);
+ }
+ }
+
size_t SSU2Session::CreateAddressBlock (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& ep)
{
if (len < 9) return 0;
@@ -2841,7 +2897,7 @@ namespace transport
void SSU2Session::SendPathResponse (const uint8_t * data, size_t len)
{
- if (len < 8 || len > m_MaxPayloadSize - 3)
+ if (len > m_MaxPayloadSize - 3)
{
LogPrint (eLogWarning, "SSU2: Incorrect data size for path response ", len);
return;
@@ -2850,7 +2906,10 @@ namespace transport
payload[0] = eSSU2BlkPathResponse;
htobe16buf (payload + 1, len);
memcpy (payload + 3, data, len);
- SendData (payload, len + 3);
+ size_t payloadSize = len + 3;
+ if (payloadSize < m_MaxPayloadSize)
+ payloadSize += CreatePaddingBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize, payloadSize < 8 ? 8 : 0);
+ SendData (payload, payloadSize);
}
void SSU2Session::SendPathChallenge ()
@@ -2868,7 +2927,7 @@ namespace transport
}
len += 3;
if (len < m_MaxPayloadSize)
- len += CreatePaddingBlock (payload + len, m_MaxPayloadSize - len);
+ len += CreatePaddingBlock (payload + len, m_MaxPayloadSize - len, len < 8 ? 8 : 0);
SendData (payload, len);
}
diff --git a/libi2pd/SSU2Session.h b/libi2pd/SSU2Session.h
index 304efdab..14d76971 100644
--- a/libi2pd/SSU2Session.h
+++ b/libi2pd/SSU2Session.h
@@ -308,6 +308,8 @@ namespace transport
void AdjustMaxPayloadSize ();
RouterStatus GetRouterStatus () const;
void SetRouterStatus (RouterStatus status) const;
+ bool GetTestingState () const;
+ void SetTestingState(bool testing) const;
std::shared_ptr ExtractRouterInfo (const uint8_t * buf, size_t size);
void CreateNonce (uint64_t seqn, uint8_t * nonce);
bool UpdateReceivePacketNum (uint32_t packetNum); // for Ack, returns false if duplicate
diff --git a/libi2pd/Transports.cpp b/libi2pd/Transports.cpp
index 38d6e270..b452c05e 100644
--- a/libi2pd/Transports.cpp
+++ b/libi2pd/Transports.cpp
@@ -650,8 +650,8 @@ namespace transport
auto router = i2p::data::netdb.GetRandomSSU2PeerTestRouter (true, excluded); // v4
if (router)
{
- if (i2p::context.GetStatus () != eRouterStatusTesting)
- i2p::context.SetStatus (eRouterStatusTesting);
+ if (!i2p::context.GetTesting ())
+ i2p::context.SetTesting (true);
m_SSU2Server->StartPeerTest (router, true);
excluded.insert (router->GetIdentHash ());
}
@@ -669,8 +669,8 @@ namespace transport
auto router = i2p::data::netdb.GetRandomSSU2PeerTestRouter (false, excluded); // v6
if (router)
{
- if (i2p::context.GetStatusV6 () != eRouterStatusTesting)
- i2p::context.SetStatusV6 (eRouterStatusTesting);
+ if (!i2p::context.GetTestingV6 ())
+ i2p::context.SetTestingV6 (true);
m_SSU2Server->StartPeerTest (router, false);
excluded.insert (router->GetIdentHash ());
}
@@ -716,6 +716,7 @@ namespace transport
if (transport == i2p::data::RouterInfo::eNTCP2V4 ||
transport == i2p::data::RouterInfo::eNTCP2V6 || transport == i2p::data::RouterInfo::eNTCP2V6Mesh)
it->second.router->GetProfile ()->Connected (); // outgoing NTCP2 connection if always real
+ i2p::data::netdb.SetUnreachable (ident, false); // clear unreachable
}
it->second.numAttempts = 0;
it->second.router = nullptr; // we don't need RouterInfo after successive connect
@@ -831,8 +832,8 @@ namespace transport
++it;
}
}
- bool ipv4Testing = i2p::context.GetStatus () == eRouterStatusTesting;
- bool ipv6Testing = i2p::context.GetStatusV6 () == eRouterStatusTesting;
+ bool ipv4Testing = i2p::context.GetTesting ();
+ bool ipv6Testing = i2p::context.GetTestingV6 ();
// if still testing, repeat peer test
if (ipv4Testing || ipv6Testing)
PeerTest (ipv4Testing, ipv6Testing);
diff --git a/libi2pd/Tunnel.cpp b/libi2pd/Tunnel.cpp
index db6f74f9..6234ceb4 100644
--- a/libi2pd/Tunnel.cpp
+++ b/libi2pd/Tunnel.cpp
@@ -709,7 +709,7 @@ namespace tunnel
auto inboundTunnel = GetNextInboundTunnel ();
auto router = i2p::transport::transports.RoutesRestricted() ?
i2p::transport::transports.GetRestrictedPeer() :
- i2p::data::netdb.GetRandomRouter (i2p::context.GetSharedRouterInfo (), false); // reachable by us
+ i2p::data::netdb.GetRandomRouter (i2p::context.GetSharedRouterInfo (), false, true); // reachable by us
if (!inboundTunnel || !router) return;
LogPrint (eLogDebug, "Tunnel: Creating one hop outbound tunnel");
CreateTunnel (
@@ -781,7 +781,7 @@ namespace tunnel
auto router = i2p::transport::transports.RoutesRestricted() ?
i2p::transport::transports.GetRestrictedPeer() :
// should be reachable by us because we send build request directly
- i2p::data::netdb.GetRandomRouter (i2p::context.GetSharedRouterInfo (), false);
+ i2p::data::netdb.GetRandomRouter (i2p::context.GetSharedRouterInfo (), false, true);
if (!router) {
LogPrint (eLogWarning, "Tunnel: Can't find any router, skip creating tunnel");
return;
diff --git a/libi2pd/TunnelPool.cpp b/libi2pd/TunnelPool.cpp
index a2168f64..23cc53e3 100644
--- a/libi2pd/TunnelPool.cpp
+++ b/libi2pd/TunnelPool.cpp
@@ -296,10 +296,12 @@ 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)
{
+ // try to create inbound tunnel through the same path as succesive outbound
CreatePairedInboundTunnel (it);
num++;
if (num >= m_NumInboundTunnels) break;
@@ -470,13 +472,14 @@ namespace tunnel
return i2p::tunnel::tunnels.GetExploratoryPool () == shared_from_this ();
}
- std::shared_ptr TunnelPool::SelectNextHop (std::shared_ptr prevHop, bool reverse) const
+ std::shared_ptr TunnelPool::SelectNextHop (std::shared_ptr prevHop,
+ bool reverse, bool endpoint) const
{
- auto hop = IsExploratory () ? i2p::data::netdb.GetRandomRouter (prevHop, reverse):
- i2p::data::netdb.GetHighBandwidthRandomRouter (prevHop, reverse);
+ auto hop = IsExploratory () ? i2p::data::netdb.GetRandomRouter (prevHop, reverse, endpoint):
+ i2p::data::netdb.GetHighBandwidthRandomRouter (prevHop, reverse, endpoint);
if (!hop || hop->GetProfile ()->IsBad ())
- hop = i2p::data::netdb.GetRandomRouter (prevHop, reverse);
+ hop = i2p::data::netdb.GetRandomRouter (prevHop, reverse, endpoint);
return hop;
}
@@ -508,7 +511,7 @@ namespace tunnel
for(int i = start; i < numHops; i++ )
{
- auto hop = nextHop (prevHop, inbound);
+ auto hop = nextHop (prevHop, inbound, i == numHops - 1);
if (!hop && !i) // if no suitable peer found for first hop, try already connected
{
LogPrint (eLogInfo, "Tunnels: Can't select first hop for a tunnel. Trying already connected");
@@ -520,11 +523,6 @@ namespace tunnel
LogPrint (eLogError, "Tunnels: Can't select next hop for ", prevHop->GetIdentHashBase64 ());
return false;
}
- if ((i == numHops - 1) && (!hop->IsV4 () || (inbound && !hop->IsPublished (true)))) // IBGW is not published ipv4
- {
- auto hop1 = nextHop (prevHop, inbound);
- if (hop1) hop = hop1;
- }
prevHop = hop;
path.Add (hop);
}
@@ -566,14 +564,15 @@ namespace tunnel
if (m_CustomPeerSelector)
return m_CustomPeerSelector->SelectPeers(path, numHops, isInbound);
}
- return StandardSelectPeers(path, numHops, isInbound, std::bind(&TunnelPool::SelectNextHop, this, std::placeholders::_1, std::placeholders::_2));
+ return StandardSelectPeers(path, numHops, isInbound, std::bind(&TunnelPool::SelectNextHop, this,
+ std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
}
bool TunnelPool::SelectExplicitPeers (Path& path, bool isInbound)
{
+ if (!m_ExplicitPeers->size ()) return false;
int numHops = isInbound ? m_NumInboundHops : m_NumOutboundHops;
if (numHops > (int)m_ExplicitPeers->size ()) numHops = m_ExplicitPeers->size ();
- if (!numHops) return false;
for (int i = 0; i < numHops; i++)
{
auto& ident = (*m_ExplicitPeers)[i];
diff --git a/libi2pd/TunnelPool.h b/libi2pd/TunnelPool.h
index e9dd142e..c1fd19cd 100644
--- a/libi2pd/TunnelPool.h
+++ b/libi2pd/TunnelPool.h
@@ -56,7 +56,7 @@ namespace tunnel
class TunnelPool: public std::enable_shared_from_this // per local destination
{
- typedef std::function(std::shared_ptr, bool)> SelectHopFunc;
+ typedef std::function(std::shared_ptr, bool, bool)> SelectHopFunc;
public:
TunnelPool (int numInboundHops, int numOutboundHops, int numInboundTunnels,
@@ -112,7 +112,7 @@ namespace tunnel
std::shared_ptr GetLowestLatencyOutboundTunnel(std::shared_ptr exclude = nullptr) const;
// for overriding tunnel peer selection
- std::shared_ptr SelectNextHop (std::shared_ptr prevHop, bool reverse) const;
+ std::shared_ptr SelectNextHop (std::shared_ptr prevHop, bool reverse, bool endpoint) const;
bool StandardSelectPeers(Path & path, int numHops, bool inbound, SelectHopFunc nextHop);
private:
diff --git a/libi2pd_client/MatchedDestination.cpp b/libi2pd_client/MatchedDestination.cpp
index 1e2e8275..40752f5b 100644
--- a/libi2pd_client/MatchedDestination.cpp
+++ b/libi2pd_client/MatchedDestination.cpp
@@ -73,7 +73,8 @@ namespace client
{
auto pool = GetTunnelPool();
if(!pool || !pool->StandardSelectPeers(path, hops, inbound,
- std::bind(&i2p::tunnel::TunnelPool::SelectNextHop, pool, std::placeholders::_1, std::placeholders::_2)))
+ std::bind(&i2p::tunnel::TunnelPool::SelectNextHop, pool, std::placeholders::_1,
+ std::placeholders::_2, std::placeholders::_3)))
return false;
// more here for outbound tunnels
if(!inbound && m_RemoteLeaseSet)