diff --git a/libi2pd/Profiling.h b/libi2pd/Profiling.h index 1846f08e..be674d95 100644 --- a/libi2pd/Profiling.h +++ b/libi2pd/Profiling.h @@ -11,6 +11,7 @@ #include #include +#include #include "Identity.h" namespace i2p @@ -67,6 +68,11 @@ namespace data bool IsUseful() const; bool IsDuplicated () const { return m_IsDuplicated; }; + + const boost::asio::ip::udp::endpoint& GetLastEndpoint () const { return m_LastEndpoint; } + void SetLastEndpoint (const boost::asio::ip::udp::endpoint& ep) { m_LastEndpoint = ep; } + bool HasLastEndpoint (bool v4) const { return !m_LastEndpoint.address ().is_unspecified () && m_LastEndpoint.port () && + ((v4 && m_LastEndpoint.address ().is_v4 ()) || (!v4 && m_LastEndpoint.address ().is_v6 ())); } private: @@ -90,6 +96,8 @@ namespace data uint32_t m_NumTimesRejected; bool m_HasConnected; // successful trusted(incoming or NTCP2) connection bool m_IsDuplicated; + // connectivity + boost::asio::ip::udp::endpoint m_LastEndpoint; // SSU2 for non-published addresses }; std::shared_ptr GetRouterProfile (const IdentHash& identHash); diff --git a/libi2pd/SSU2.cpp b/libi2pd/SSU2.cpp index e96a7233..871b5b05 100644 --- a/libi2pd/SSU2.cpp +++ b/libi2pd/SSU2.cpp @@ -833,6 +833,29 @@ namespace transport } } + bool SSU2Server::CheckPendingOutgoingSession (const boost::asio::ip::udp::endpoint& ep, bool peerTest) + { + auto s = FindPendingOutgoingSession (ep); + if (s) + { + if (peerTest) + { + // if peer test requested add it to the list for pending session + auto onEstablished = s->GetOnEstablished (); + if (onEstablished) + s->SetOnEstablished ([s, onEstablished]() + { + onEstablished (); + s->SendPeerTest (); + }); + else + s->SetOnEstablished ([s]() { s->SendPeerTest (); }); + } + return true; + } + return false; + } + bool SSU2Server::CreateSession (std::shared_ptr router, std::shared_ptr address, bool peerTest) { @@ -852,34 +875,28 @@ namespace transport if (isValidEndpoint) { if (i2p::transport::transports.IsInReservedRange(address->host)) return false; - auto s = FindPendingOutgoingSession (boost::asio::ip::udp::endpoint (address->host, address->port)); - if (s) - { - if (peerTest) - { - // if peer test requested add it to the list for pending session - auto onEstablished = s->GetOnEstablished (); - if (onEstablished) - s->SetOnEstablished ([s, onEstablished]() - { - onEstablished (); - s->SendPeerTest (); - }); - else - s->SetOnEstablished ([s]() { s->SendPeerTest (); }); - } - return false; - } + if (CheckPendingOutgoingSession (boost::asio::ip::udp::endpoint (address->host, address->port), peerTest)) return false; } auto session = std::make_shared (*this, router, address); + if (!isValidEndpoint && router->GetProfile ()->HasLastEndpoint (address->IsV4 ())) + { + // router doesn't publish endpoint, but we connected before and hole punch might be alive + const auto& ep = router->GetProfile ()->GetLastEndpoint (); + if (IsConnectedRecently (ep)) + { + if (CheckPendingOutgoingSession (ep, peerTest)) return false; + session->SetRemoteEndpoint (ep); + isValidEndpoint = true; + } + } if (peerTest) session->SetOnEstablished ([session]() {session->SendPeerTest (); }); - if (address->UsesIntroducer ()) - GetService ().post (std::bind (&SSU2Server::ConnectThroughIntroducer, this, session)); - else if (isValidEndpoint) // we can't connect without endpoint + if (isValidEndpoint) // we know endpoint GetService ().post ([session]() { session->Connect (); }); + else if (address->UsesIntroducer ()) // we don't know endpoint yet + GetService ().post (std::bind (&SSU2Server::ConnectThroughIntroducer, this, session)); else return false; } diff --git a/libi2pd/SSU2.h b/libi2pd/SSU2.h index 426c6a10..31783d58 100644 --- a/libi2pd/SSU2.h +++ b/libi2pd/SSU2.h @@ -147,6 +147,7 @@ namespace transport void ScheduleResend (bool more); void HandleResendTimer (const boost::system::error_code& ecode); + bool CheckPendingOutgoingSession (const boost::asio::ip::udp::endpoint& ep, bool peerTest); void ConnectThroughIntroducer (std::shared_ptr session); std::vector > FindIntroducers (int maxNumIntroducers, bool v4, const std::unordered_set& excluded); diff --git a/libi2pd/SSU2Session.cpp b/libi2pd/SSU2Session.cpp index 17d2c1da..20f21ce5 100644 --- a/libi2pd/SSU2Session.cpp +++ b/libi2pd/SSU2Session.cpp @@ -226,6 +226,13 @@ namespace transport if (m_Server.AddPendingOutgoingSession (shared_from_this ())) { m_Server.RemoveSession (GetConnID ()); + // update endpoint in profile because we know it now + auto identity = GetRemoteIdentity (); + if (identity) + { + auto profile = i2p::data::GetRouterProfile (identity->GetIdentHash ()); + if (profile) profile->SetLastEndpoint (m_RemoteEndpoint); + } // connect LogPrint (eLogDebug, "SSU2: Connecting after introduction to ", GetIdentHashBase64()); Connect (); @@ -1169,6 +1176,8 @@ namespace transport " and actual endpoint ", m_RemoteEndpoint.address (), " from ", i2p::data::GetIdentHashAbbreviation (ri->GetIdentHash ())); return false; } + if (!m_Address->published) + ri->GetProfile ()->SetLastEndpoint (m_RemoteEndpoint); SetRemoteIdentity (ri->GetRouterIdentity ()); AdjustMaxPayloadSize (); m_Server.AddSessionByRouterHash (shared_from_this ()); // we know remote router now