diff --git a/libi2pd/SSU2.cpp b/libi2pd/SSU2.cpp index 6e0a7d79..cfb9f1bc 100644 --- a/libi2pd/SSU2.cpp +++ b/libi2pd/SSU2.cpp @@ -244,6 +244,31 @@ namespace transport return it->second; return nullptr; } + + std::shared_ptr SSU2Server::GetRandomSession (i2p::data::RouterInfo::CompatibleTransports remoteTransports) const + { + if (m_Sessions.empty ()) return nullptr; + uint16_t ind; + RAND_bytes ((uint8_t *)&ind, sizeof (ind)); + ind %= m_Sessions.size (); + auto it = m_Sessions.begin (); + std::advance (it, ind); + while (it != m_Sessions.end ()) + { + if (it->second->GetRemoteTransports () & remoteTransports) + return it->second; + it++; + } + // not found, try from begining + it = m_Sessions.begin (); + while (it != m_Sessions.end () && ind) + { + if (it->second->GetRemoteTransports () & remoteTransports) + return it->second; + it++; ind--; + } + return nullptr; + } void SSU2Server::AddRelay (uint32_t tag, std::shared_ptr relay) { diff --git a/libi2pd/SSU2.h b/libi2pd/SSU2.h index d5939a31..e13524c4 100644 --- a/libi2pd/SSU2.h +++ b/libi2pd/SSU2.h @@ -54,6 +54,7 @@ namespace transport void AddSessionByRouterHash (std::shared_ptr session); void AddPendingOutgoingSession (std::shared_ptr session); std::shared_ptr FindSession (const i2p::data::IdentHash& ident) const; + std::shared_ptr GetRandomSession (i2p::data::RouterInfo::CompatibleTransports remoteTransports) const; void AddRelay (uint32_t tag, std::shared_ptr relay); void RemoveRelay (uint32_t tag); diff --git a/libi2pd/SSU2Session.cpp b/libi2pd/SSU2Session.cpp index 66cc63f4..2304ec11 100644 --- a/libi2pd/SSU2Session.cpp +++ b/libi2pd/SSU2Session.cpp @@ -22,9 +22,10 @@ namespace transport SSU2Session::SSU2Session (SSU2Server& server, std::shared_ptr in_RemoteRouter, std::shared_ptr addr): TransportSession (in_RemoteRouter, SSU2_CONNECT_TIMEOUT), - m_Server (server), m_Address (addr), m_DestConnID (0), m_SourceConnID (0), - m_State (eSSU2SessionStateUnknown), m_SendPacketNum (0), m_ReceivePacketNum (0), - m_IsDataReceived (false), m_WindowSize (SSU2_MAX_WINDOW_SIZE), m_RelayTag (0) + m_Server (server), m_Address (addr), m_RemoteTransports (0), + m_DestConnID (0), m_SourceConnID (0), m_State (eSSU2SessionStateUnknown), + m_SendPacketNum (0), m_ReceivePacketNum (0), m_IsDataReceived (false), + m_WindowSize (SSU2_MAX_WINDOW_SIZE), m_RelayTag (0) { m_NoiseState.reset (new i2p::crypto::NoiseSymmetricState); if (in_RemoteRouter && m_Address) @@ -32,6 +33,7 @@ namespace transport // outgoing InitNoiseXKState1 (*m_NoiseState, m_Address->s); m_RemoteEndpoint = boost::asio::ip::udp::endpoint (m_Address->host, m_Address->port); + m_RemoteTransports = in_RemoteRouter->GetCompatibleTransports (false); RAND_bytes ((uint8_t *)&m_DestConnID, 8); RAND_bytes ((uint8_t *)&m_SourceConnID, 8); } @@ -612,6 +614,7 @@ namespace transport LogPrint (eLogError, "SSU2: No SSU2 address with static key found in SessionConfirmed"); return false; } + m_RemoteTransports = ri->GetCompatibleTransports (false); i2p::data::netdb.PostI2NPMsg (CreateI2NPMessage (eI2NPDummyMsg, ri->GetBuffer (), ri->GetBufferLen ())); // TODO: should insert ri // handle other blocks HandlePayload (decryptedPayload.data () + riSize + 3, decryptedPayload.size () - riSize - 3); @@ -1333,12 +1336,37 @@ namespace transport { case 1: // Bob from Alice { - // TODO: find Charlie - uint8_t payload[SSU2_MAX_PAYLOAD_SIZE], zeroHash[32] = {0}; - size_t payloadSize = CreatePeerTestBlock (payload, SSU2_MAX_PAYLOAD_SIZE, 4, - eSSU2PeerTestCodeBobNoCharlieAvailable, zeroHash, buf + 3, len - 3); - payloadSize += CreatePaddingBlock (payload + payloadSize, SSU2_MAX_PAYLOAD_SIZE - payloadSize); - SendData (payload, payloadSize); + auto session = m_Server.GetRandomSession ((buf[12] == 6) ? i2p::data::RouterInfo::eSSU2V4 : i2p::data::RouterInfo::eSSU2V6); + if (session) // session with Charlie + { + session->m_PeerTests.emplace (nonce, std::make_pair (shared_from_this (), i2p::util::GetSecondsSinceEpoch ())); + uint8_t payload[SSU2_MAX_PAYLOAD_SIZE]; + // Alice's RouterInfo + auto r = i2p::data::netdb.FindRouter (GetRemoteIdentity ()->GetIdentHash ()); + size_t payloadSize = r ? CreateRouterInfoBlock (payload, SSU2_MAX_PAYLOAD_SIZE - len - 32, r) : 0; + if (!payloadSize && r) + session->SendFragmentedMessage (CreateDatabaseStoreMsg (r)); + if (payloadSize + len + 48 > SSU2_MAX_PAYLOAD_SIZE) + { + // doesn't fit one message, send RouterInfo in separate message + session->SendData (payload, payloadSize); + payloadSize = 0; + } + // PeerTest to Charlie + payloadSize += CreatePeerTestBlock (payload, SSU2_MAX_PAYLOAD_SIZE - payloadSize, 2, + eSSU2PeerTestCodeAccept, GetRemoteIdentity ()->GetIdentHash (), buf + 3, len - 3); + payloadSize += CreatePaddingBlock (payload + payloadSize, SSU2_MAX_PAYLOAD_SIZE - payloadSize); + session->SendData (payload, payloadSize); + } + else + { + // Charlie not found, send error back to Alice + uint8_t payload[SSU2_MAX_PAYLOAD_SIZE], zeroHash[32] = {0}; + size_t payloadSize = CreatePeerTestBlock (payload, SSU2_MAX_PAYLOAD_SIZE, 4, + eSSU2PeerTestCodeBobNoCharlieAvailable, zeroHash, buf + 3, len - 3); + payloadSize += CreatePaddingBlock (payload + payloadSize, SSU2_MAX_PAYLOAD_SIZE - payloadSize); + SendData (payload, payloadSize); + } break; } case 2: // Charlie from Bob @@ -1401,7 +1429,19 @@ namespace transport if (it != m_PeerTests.end () && it->second.first) { uint8_t payload[SSU2_MAX_PAYLOAD_SIZE]; - size_t payloadSize = CreatePeerTestBlock (payload, SSU2_MAX_PAYLOAD_SIZE, 4, + // Charlie's RouterInfo + auto r = i2p::data::netdb.FindRouter (GetRemoteIdentity ()->GetIdentHash ()); + size_t payloadSize = r ? CreateRouterInfoBlock (payload, SSU2_MAX_PAYLOAD_SIZE - len - 32, r) : 0; + if (!payloadSize && r) + it->second.first->SendFragmentedMessage (CreateDatabaseStoreMsg (r)); + if (payloadSize + len + 16 > SSU2_MAX_PAYLOAD_SIZE) + { + // doesn't fit one message, send RouterInfo in separate message + it->second.first->SendData (payload, payloadSize); + payloadSize = 0; + } + // PeerTest to Alice + payloadSize = CreatePeerTestBlock (payload, SSU2_MAX_PAYLOAD_SIZE, 4, (SSU2PeerTestCode)buf[1], GetRemoteIdentity ()->GetIdentHash (), buf + 3, len - 3); if (payloadSize < SSU2_MAX_PAYLOAD_SIZE) payloadSize += CreatePaddingBlock (payload + payloadSize, SSU2_MAX_PAYLOAD_SIZE - payloadSize); diff --git a/libi2pd/SSU2Session.h b/libi2pd/SSU2Session.h index 6be69080..1fc09d76 100644 --- a/libi2pd/SSU2Session.h +++ b/libi2pd/SSU2Session.h @@ -160,6 +160,7 @@ namespace transport void SetRemoteEndpoint (const boost::asio::ip::udp::endpoint& ep) { m_RemoteEndpoint = ep; }; const boost::asio::ip::udp::endpoint& GetRemoteEndpoint () const { return m_RemoteEndpoint; }; + i2p::data::RouterInfo::CompatibleTransports GetRemoteTransports () const { return m_RemoteTransports; }; void SetOnEstablished (OnEstablished e) { m_OnEstablished = e; }; void Connect (); @@ -244,6 +245,7 @@ namespace transport std::unique_ptr m_SessionConfirmedFragment1; // for Bob if applicable std::shared_ptr m_Address; boost::asio::ip::udp::endpoint m_RemoteEndpoint; + i2p::data::RouterInfo::CompatibleTransports m_RemoteTransports; // for peer tests uint64_t m_DestConnID, m_SourceConnID; SSU2SessionState m_State; uint8_t m_KeyDataSend[64], m_KeyDataReceive[64];