From 643a94a441c78425798057792e97dbe13a86449e Mon Sep 17 00:00:00 2001 From: orignal Date: Sun, 3 Aug 2025 16:33:07 -0400 Subject: [PATCH 01/16] increased number of floodfills threshold --- libi2pd/NetDb.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libi2pd/NetDb.hpp b/libi2pd/NetDb.hpp index 8d17628f..df831be8 100644 --- a/libi2pd/NetDb.hpp +++ b/libi2pd/NetDb.hpp @@ -41,7 +41,7 @@ namespace data const int NETDB_MIN_ROUTERS = 90; const int NETDB_MIN_FLOODFILLS = 5; const int NETDB_MIN_TRANSPORTS = 10 ; // otherwise assume offline - const int NETDB_NUM_FLOODFILLS_THRESHOLD = 1500; + const int NETDB_NUM_FLOODFILLS_THRESHOLD = 1800; const int NETDB_NUM_ROUTERS_THRESHOLD = 4*NETDB_NUM_FLOODFILLS_THRESHOLD; const int NETDB_TUNNEL_CREATION_RATE_THRESHOLD = 10; // in % const int NETDB_CHECK_FOR_EXPIRATION_UPTIME = 600; // 10 minutes, in seconds From aeca4c3fc7e97b17a70b972f1e8c664ab091a87a Mon Sep 17 00:00:00 2001 From: orignal Date: Sun, 3 Aug 2025 16:34:31 -0400 Subject: [PATCH 02/16] fixed #2215. Check streaming destination during cleanup --- libi2pd_client/SAM.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/libi2pd_client/SAM.cpp b/libi2pd_client/SAM.cpp index 52ef1b10..09c2dfe9 100644 --- a/libi2pd_client/SAM.cpp +++ b/libi2pd_client/SAM.cpp @@ -1590,14 +1590,17 @@ namespace client if (dest) { auto streamingDest = dest->GetStreamingDestination (); - auto numStreams = streamingDest->GetNumStreams (); - if (numStreams > 0) - { - LogPrint (eLogInfo, "SAM: Session ", session->Name, " still has ", numStreams, " streams"); - ScheduleSessionCleanupTimer (session); + if (streamingDest) + { + auto numStreams = streamingDest->GetNumStreams (); + if (numStreams > 0) + { + LogPrint (eLogInfo, "SAM: Session ", session->Name, " still has ", numStreams, " streams"); + ScheduleSessionCleanupTimer (session); + } + else + LogPrint (eLogDebug, "SAM: Session ", session->Name, " terminated"); } - else - LogPrint (eLogDebug, "SAM: Session ", session->Name, " terminated"); } } // session's destructor is called here unless rescheduled From 28996583e49a4dea59b012296d5822a0ee0639cc Mon Sep 17 00:00:00 2001 From: orignal Date: Sun, 3 Aug 2025 16:35:23 -0400 Subject: [PATCH 03/16] Handle links with UTF8 SAM session name --- daemon/HTTPServer.cpp | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/daemon/HTTPServer.cpp b/daemon/HTTPServer.cpp index c9371ab6..58166e56 100644 --- a/daemon/HTTPServer.cpp +++ b/daemon/HTTPServer.cpp @@ -947,7 +947,8 @@ namespace http { for (auto& it: sam->GetSessions ()) { auto& name = it.second->GetLocalDestination ()->GetNickname (); - s << "\r\n"; @@ -959,13 +960,26 @@ namespace http { void ShowSAMSession (std::stringstream& s, const std::string& id) { auto sam = i2p::client::context.GetSAMBridge (); - if (!sam) { + if (!sam) + { ShowError(s, tr("SAM disabled")); return; } - - auto session = sam->FindSession (id); - if (!session) { + if (id.empty ()) + { + ShowError(s, tr("No sam_id")); + return; + } + std::vector sam_id(id.length ()); // id is in base64 + size_t l = i2p::data::Base64ToByteStream (id, sam_id.data (), sam_id.size ()); + if (!l) + { + ShowError(s, tr("Invalid sam_id")); + return; + } + auto session = sam->FindSession ( { (const char *)sam_id.data (), l }); + if (!session) + { ShowError(s, tr("SAM session not found")); return; } @@ -977,7 +991,7 @@ namespace http { s << i2p::client::context.GetAddressBook ().ToAddress(ident) << "\r\n"; s << "
\r\n"; s << "" << tr("Streams") << ":
\r\n
\r\n"; - for (const auto& it: sam->ListSockets(id)) + for (const auto& it: sam->ListSockets({ (const char *)sam_id.data (), l })) { s << "
"; switch (it->GetSocketType ()) From f0c4203f5a9ef0d55c497bddded409c060314799 Mon Sep 17 00:00:00 2001 From: orignal Date: Mon, 4 Aug 2025 18:25:07 -0400 Subject: [PATCH 04/16] read bool param --- libi2pd/Destination.cpp | 33 +++++++++++++++++++++------------ libi2pd/Destination.h | 1 + 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/libi2pd/Destination.cpp b/libi2pd/Destination.cpp index 1493df6b..0be5890a 100644 --- a/libi2pd/Destination.cpp +++ b/libi2pd/Destination.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include "Crypto.h" #include "ECIESX25519AEADRatchetSession.h" @@ -93,10 +94,8 @@ namespace client } it = params->find (I2CP_PARAM_DONT_PUBLISH_LEASESET); if (it != params->end ()) - { // override isPublic - m_IsPublic = (it->second != "true"); - } + m_IsPublic = GetBoolParamValue (it->second); it = params->find (I2CP_PARAM_LEASESET_TYPE); if (it != params->end ()) m_LeaseSetType = std::stoi(it->second); @@ -188,6 +187,24 @@ namespace client CleanUp (); // GarlicDestination } + bool LeaseSetDestination::GetBoolParamValue (std::string_view value) + { + bool ret = false; + if (value == "true") + ret = true; + else if (value == "false") + ret = false; + else + { + int v = 0; + auto res = std::from_chars(value.data(), value.data() + value.size(), v); + if (res.ec != std::errc()) + LogPrint (eLogError, "Destination: Unable to parse bool param value ", value, ": ", std::make_error_code (res.ec).message ()); + ret = v; + } + return ret; + } + bool LeaseSetDestination::Reconfigure(std::map params) { auto itr = params.find("i2cp.dontPublishLeaseSet"); @@ -1096,15 +1113,7 @@ namespace client } it = params->find (I2CP_PARAM_STREAMING_ANSWER_PINGS); if (it != params->end ()) - { - LogPrint (eLogDebug, "Destination: Reading parameter ", I2CP_PARAM_STREAMING_ANSWER_PINGS, " value ", it->second); - if (it->second == "true") - m_IsStreamingAnswerPings = true; - else if (it->second == "false") - m_IsStreamingAnswerPings = false; - else - m_IsStreamingAnswerPings = std::stoi (it->second); // 1 for true - } + m_IsStreamingAnswerPings = GetBoolParamValue (it->second); if (GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2) { diff --git a/libi2pd/Destination.h b/libi2pd/Destination.h index 87859657..d387a900 100644 --- a/libi2pd/Destination.h +++ b/libi2pd/Destination.h @@ -174,6 +174,7 @@ namespace client int GetLeaseSetType () const { return m_LeaseSetType; }; void SetLeaseSetType (int leaseSetType) { m_LeaseSetType = leaseSetType; }; int GetAuthType () const { return m_AuthType; }; + static bool GetBoolParamValue (std::string_view value); virtual void CleanupDestination () {}; // additional clean up in derived classes virtual i2p::data::CryptoKeyType GetPreferredCryptoType () const = 0; // I2CP From f5b57283b365c9c2079583d3d551ae344f593ee1 Mon Sep 17 00:00:00 2001 From: orignal Date: Tue, 5 Aug 2025 19:21:07 -0400 Subject: [PATCH 05/16] i2p.streaming.dontSign tunnel param --- libi2pd/Destination.cpp | 11 ++-- libi2pd/Destination.h | 5 +- libi2pd/Streaming.cpp | 98 ++++++++++++++++++++------------ libi2pd/Streaming.h | 20 ++----- libi2pd_client/ClientContext.cpp | 1 + 5 files changed, 77 insertions(+), 58 deletions(-) diff --git a/libi2pd/Destination.cpp b/libi2pd/Destination.cpp index 0be5890a..c24ef1db 100644 --- a/libi2pd/Destination.cpp +++ b/libi2pd/Destination.cpp @@ -1027,9 +1027,9 @@ namespace client m_StreamingInboundSpeed (DEFAULT_MAX_INBOUND_SPEED), m_StreamingMaxConcurrentStreams (DEFAULT_MAX_CONCURRENT_STREAMS), m_StreamingMaxWindowSize (i2p::stream::MAX_WINDOW_SIZE), - m_IsStreamingAnswerPings (DEFAULT_ANSWER_PINGS), m_LastPort (0), - m_DatagramDestination (nullptr), m_RefCounter (0), m_LastPublishedTimestamp (0), - m_ReadyChecker(service) + m_IsStreamingAnswerPings (DEFAULT_ANSWER_PINGS), m_IsStreamingDontSign (DEFAULT_DONT_SIGN), + m_LastPort (0), m_DatagramDestination (nullptr), m_RefCounter (0), + m_LastPublishedTimestamp (0), m_ReadyChecker(service) { if (keys.IsOfflineSignature () && GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_LEASESET) SetLeaseSetType (i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2); // offline keys can be published with LS2 only @@ -1114,7 +1114,10 @@ namespace client it = params->find (I2CP_PARAM_STREAMING_ANSWER_PINGS); if (it != params->end ()) m_IsStreamingAnswerPings = GetBoolParamValue (it->second); - + it = params->find (I2CP_PARAM_STREAMING_DONT_SIGN); + if (it != params->end ()) + m_IsStreamingDontSign = GetBoolParamValue (it->second); + if (GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2) { // authentication for encrypted LeaseSet diff --git a/libi2pd/Destination.h b/libi2pd/Destination.h index d387a900..f7ba269f 100644 --- a/libi2pd/Destination.h +++ b/libi2pd/Destination.h @@ -102,6 +102,8 @@ namespace client const char I2CP_PARAM_STREAMING_MAX_CONCURRENT_STREAMS[] = "i2p.streaming.maxConcurrentStreams"; const int DEFAULT_MAX_CONCURRENT_STREAMS = 2048; const char I2CP_PARAM_STREAMING_MAX_WINDOW_SIZE[] = "i2p.streaming.maxWindowSize"; + const char I2CP_PARAM_STREAMING_DONT_SIGN[] = "i2p.streaming.dontSign"; + const int DEFAULT_DONT_SIGN = false; typedef std::function stream)> StreamRequestComplete; @@ -273,6 +275,7 @@ namespace client int GetStreamingInboundSpeed () const { return m_StreamingInboundSpeed; } int GetStreamingMaxConcurrentStreams () const { return m_StreamingMaxConcurrentStreams; } bool IsStreamingAnswerPings () const { return m_IsStreamingAnswerPings; } + bool IsStreamingDontSign () const { return m_IsStreamingDontSign; } int GetStreamingMaxWindowSize () const { return m_StreamingMaxWindowSize; } // datagram @@ -315,7 +318,7 @@ namespace client i2p::data::CryptoKeyType m_PreferredCryptoType; int m_StreamingAckDelay,m_StreamingOutboundSpeed, m_StreamingInboundSpeed, m_StreamingMaxConcurrentStreams, m_StreamingMaxWindowSize; - bool m_IsStreamingAnswerPings; + bool m_IsStreamingAnswerPings, m_IsStreamingDontSign; std::shared_ptr m_StreamingDestination; // default std::map > m_StreamingDestinationsByPorts; std::shared_ptr m_LastStreamingDestination; uint16_t m_LastPort; // for server tunnels diff --git a/libi2pd/Streaming.cpp b/libi2pd/Streaming.cpp index df284788..a2018a4e 100644 --- a/libi2pd/Streaming.cpp +++ b/libi2pd/Streaming.cpp @@ -91,16 +91,18 @@ namespace stream m_IsResendNeeded (false), m_IsFirstRttSample (false), m_IsSendTime (true), m_IsWinDropped (true), m_IsChoking2 (false), m_IsClientChoked (false), m_IsClientChoked2 (false), m_IsTimeOutResend (false), m_IsImmediateAckRequested (false), m_IsRemoteLeaseChangeInProgress (false), - m_IsBufferEmpty (false), m_IsJavaClient (false), m_LocalDestination (local), - m_RemoteLeaseSet (remote), m_ReceiveTimer (m_Service), m_SendTimer (m_Service), m_ResendTimer (m_Service), - m_AckSendTimer (m_Service), m_NumSentBytes (0), m_NumReceivedBytes (0), m_Port (port), - m_RTT (INITIAL_RTT), m_MinRTT (INITIAL_RTT), m_SlowRTT (INITIAL_RTT), m_FastRTT (INITIAL_RTT), - m_WindowSize (INITIAL_WINDOW_SIZE), m_MaxWindowSize (local.GetOwner ()->GetStreamingMaxWindowSize ()), - m_LastWindowDropSize (0), m_WindowDropTargetSize (0), m_WindowIncCounter (0), m_RTO (INITIAL_RTO), + m_IsBufferEmpty (false), m_IsJavaClient (false), m_DontSign (local.GetOwner ()->IsStreamingDontSign ()), + m_LocalDestination (local), m_RemoteLeaseSet (remote), m_ReceiveTimer (m_Service), + m_SendTimer (m_Service), m_ResendTimer (m_Service), m_AckSendTimer (m_Service), m_NumSentBytes (0), + m_NumReceivedBytes (0), m_Port (port), m_RTT (INITIAL_RTT), m_MinRTT (INITIAL_RTT), + m_SlowRTT (INITIAL_RTT), m_FastRTT (INITIAL_RTT), m_WindowSize (INITIAL_WINDOW_SIZE), + m_MaxWindowSize (local.GetOwner ()->GetStreamingMaxWindowSize ()), m_LastWindowDropSize (0), + m_WindowDropTargetSize (0), m_WindowIncCounter (0), m_RTO (INITIAL_RTO), m_AckDelay (local.GetOwner ()->GetStreamingAckDelay ()), m_PrevRTTSample (INITIAL_RTT), - m_Jitter (0), m_MinPacingTime (0), - m_PacingTime (INITIAL_PACING_TIME), m_PacingTimeRem (0), m_LastSendTime (0), m_LastACKRecieveTime (0), m_ACKRecieveInterval (local.GetOwner ()->GetStreamingAckDelay ()), m_RemoteLeaseChangeTime (0), m_LastWindowIncTime (0), m_LastACKRequestTime (0), - m_LastACKSendTime (0), m_PacketACKInterval (1), m_PacketACKIntervalRem (0), // for limit inbound speed + m_Jitter (0), m_MinPacingTime (0), m_PacingTime (INITIAL_PACING_TIME), m_PacingTimeRem (0), + m_LastSendTime (0), m_LastACKRecieveTime (0), m_ACKRecieveInterval (local.GetOwner ()->GetStreamingAckDelay ()), + m_RemoteLeaseChangeTime (0), m_LastWindowIncTime (0), m_LastACKRequestTime (0), m_LastACKSendTime (0), + m_PacketACKInterval (1), m_PacketACKIntervalRem (0), // for limit inbound speed m_NumResendAttempts (0), m_NumPacketsToSend (0), m_JitterAccum (0), m_JitterDiv (1), m_MTU (STREAMING_MTU) { RAND_bytes ((uint8_t *)&m_RecvStreamID, 4); @@ -122,14 +124,16 @@ namespace stream m_IsResendNeeded (false), m_IsFirstRttSample (false), m_IsSendTime (true), m_IsWinDropped (true), m_IsChoking2 (false), m_IsClientChoked (false), m_IsClientChoked2 (false), m_IsTimeOutResend (false), m_IsImmediateAckRequested (false), m_IsRemoteLeaseChangeInProgress (false), - m_IsBufferEmpty (false), m_IsJavaClient (false), m_LocalDestination (local), - m_ReceiveTimer (m_Service), m_SendTimer (m_Service), m_ResendTimer (m_Service), m_AckSendTimer (m_Service), - m_NumSentBytes (0), m_NumReceivedBytes (0), m_Port (0), m_RTT (INITIAL_RTT), m_MinRTT (INITIAL_RTT), m_SlowRTT (INITIAL_RTT), m_FastRTT (INITIAL_RTT), + m_IsBufferEmpty (false), m_IsJavaClient (false), m_DontSign (local.GetOwner ()->IsStreamingDontSign ()), + m_LocalDestination (local),m_ReceiveTimer (m_Service), m_SendTimer (m_Service), + m_ResendTimer (m_Service), m_AckSendTimer (m_Service),m_NumSentBytes (0), m_NumReceivedBytes (0), + m_Port (0), m_RTT (INITIAL_RTT), m_MinRTT (INITIAL_RTT), m_SlowRTT (INITIAL_RTT), m_FastRTT (INITIAL_RTT), m_WindowSize (INITIAL_WINDOW_SIZE), m_MaxWindowSize (local.GetOwner ()->GetStreamingMaxWindowSize ()), - m_LastWindowDropSize (0), m_WindowDropTargetSize (0), m_WindowIncCounter (0), - m_RTO (INITIAL_RTO), m_AckDelay (local.GetOwner ()->GetStreamingAckDelay ()), - m_PrevRTTSample (INITIAL_RTT), m_Jitter (0), m_MinPacingTime (0), - m_PacingTime (INITIAL_PACING_TIME), m_PacingTimeRem (0), m_LastSendTime (0), m_LastACKRecieveTime (0), m_ACKRecieveInterval (local.GetOwner ()->GetStreamingAckDelay ()), m_RemoteLeaseChangeTime (0), m_LastWindowIncTime (0), m_LastACKRequestTime (0), + m_LastWindowDropSize (0), m_WindowDropTargetSize (0), m_WindowIncCounter (0), m_RTO (INITIAL_RTO), + m_AckDelay (local.GetOwner ()->GetStreamingAckDelay ()),m_PrevRTTSample (INITIAL_RTT), m_Jitter (0), + m_MinPacingTime (0), m_PacingTime (INITIAL_PACING_TIME), m_PacingTimeRem (0), m_LastSendTime (0), + m_LastACKRecieveTime (0), m_ACKRecieveInterval (local.GetOwner ()->GetStreamingAckDelay ()), + m_RemoteLeaseChangeTime (0), m_LastWindowIncTime (0), m_LastACKRequestTime (0), m_LastACKSendTime (0), m_PacketACKInterval (1), m_PacketACKIntervalRem (0), // for limit inbound speed m_NumResendAttempts (0), m_NumPacketsToSend (0), m_JitterAccum (0), m_JitterDiv (1), m_MTU (STREAMING_MTU) { @@ -487,7 +491,9 @@ namespace stream return false; } sessionVerified = true; - } + if (!(flags & PACKET_FLAG_SIGNATURE_INCLUDED)) + m_DontSign = true; // don't sign if the remote didn't sign + } } if (flags & PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED) @@ -942,8 +948,8 @@ namespace stream m_RoutingSession = m_LocalDestination.GetOwner ()->GetRoutingSession (m_RemoteLeaseSet, true, !m_IsIncoming); m_MTU = (m_RoutingSession && m_RoutingSession->IsRatchets ()) ? STREAMING_MTU_RATCHETS : STREAMING_MTU; } - uint16_t flags = PACKET_FLAG_SYNCHRONIZE | PACKET_FLAG_FROM_INCLUDED | - PACKET_FLAG_SIGNATURE_INCLUDED | PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED; + uint16_t flags = PACKET_FLAG_SYNCHRONIZE | PACKET_FLAG_FROM_INCLUDED | PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED; + if (!m_DontSign) flags |= PACKET_FLAG_SIGNATURE_INCLUDED; if (isNoAck) flags |= PACKET_FLAG_NO_ACK; bool isOfflineSignature = m_LocalDestination.GetOwner ()->GetPrivateKeys ().IsOfflineSignature (); if (isOfflineSignature) flags |= PACKET_FLAG_OFFLINE_SIGNATURE; @@ -957,18 +963,26 @@ namespace stream size += identityLen; // from htobe16buf (packet + size, m_MTU); size += 2; // max packet size - if (isOfflineSignature) + if (m_DontSign) { - const auto& offlineSignature = m_LocalDestination.GetOwner ()->GetPrivateKeys ().GetOfflineSignature (); - memcpy (packet + size, offlineSignature.data (), offlineSignature.size ()); - size += offlineSignature.size (); // offline signature + htobe16buf (optionsSize, packet + size - 2 - optionsSize); // actual options size + size += m_SendBuffer.Get (packet + size, m_MTU); // payload } - uint8_t * signature = packet + size; // set it later - memset (signature, 0, signatureLen); // zeroes for now - size += signatureLen; // signature - htobe16buf (optionsSize, packet + size - 2 - optionsSize); // actual options size - size += m_SendBuffer.Get (packet + size, m_MTU); // payload - m_LocalDestination.GetOwner ()->Sign (packet, size, signature); + else + { + if (isOfflineSignature) + { + const auto& offlineSignature = m_LocalDestination.GetOwner ()->GetPrivateKeys ().GetOfflineSignature (); + memcpy (packet + size, offlineSignature.data (), offlineSignature.size ()); + size += offlineSignature.size (); // offline signature + } + uint8_t * signature = packet + size; // set it later + memset (signature, 0, signatureLen); // zeroes for now + size += signatureLen; // signature + htobe16buf (optionsSize, packet + size - 2 - optionsSize); // actual options size + size += m_SendBuffer.Get (packet + size, m_MTU); // payload + m_LocalDestination.GetOwner ()->Sign (packet, size, signature); + } } else { @@ -1253,15 +1267,25 @@ namespace stream size++; // NACK count packet[size] = 0; size++; // resend delay - htobe16buf (packet + size, PACKET_FLAG_CLOSE | PACKET_FLAG_SIGNATURE_INCLUDED); + uint16_t flags = PACKET_FLAG_CLOSE; + if (!m_DontSign) flags |= PACKET_FLAG_SIGNATURE_INCLUDED; + htobe16buf (packet + size, flags); size += 2; // flags - size_t signatureLen = m_LocalDestination.GetOwner ()->GetPrivateKeys ().GetSignatureLen (); - htobe16buf (packet + size, signatureLen); // signature only - size += 2; // options size - uint8_t * signature = packet + size; - memset (packet + size, 0, signatureLen); - size += signatureLen; // signature - m_LocalDestination.GetOwner ()->Sign (packet, size, signature); + if (m_DontSign) + { + memset (packet + size, 0, 2); // no options + size += 2; // options size + } + else + { + size_t signatureLen = m_LocalDestination.GetOwner ()->GetPrivateKeys ().GetSignatureLen (); + htobe16buf (packet + size, signatureLen); // signature only + size += 2; // options size + uint8_t * signature = packet + size; + memset (packet + size, 0, signatureLen); + size += signatureLen; // signature + m_LocalDestination.GetOwner ()->Sign (packet, size, signature); + } p->len = size; boost::asio::post (m_Service, std::bind (&Stream::SendPacket, shared_from_this (), p)); diff --git a/libi2pd/Streaming.h b/libi2pd/Streaming.h index b33102e4..5aeced26 100644 --- a/libi2pd/Streaming.h +++ b/libi2pd/Streaming.h @@ -273,22 +273,10 @@ namespace stream int32_t m_PreviousReceivedSequenceNumber; int32_t m_LastConfirmedReceivedSequenceNumber; // for limit inbound speed StreamStatus m_Status; - bool m_IsIncoming; - bool m_IsAckSendScheduled; - bool m_IsNAcked; - bool m_IsFirstACK; - bool m_IsResendNeeded; - bool m_IsFirstRttSample; - bool m_IsSendTime; - bool m_IsWinDropped; - bool m_IsChoking2; - bool m_IsClientChoked; - bool m_IsClientChoked2; - bool m_IsTimeOutResend; - bool m_IsImmediateAckRequested; - bool m_IsRemoteLeaseChangeInProgress; - bool m_IsBufferEmpty; - bool m_IsJavaClient; + bool m_IsIncoming, m_IsAckSendScheduled, m_IsNAcked, m_IsFirstACK, m_IsResendNeeded, + m_IsFirstRttSample, m_IsSendTime, m_IsWinDropped, m_IsChoking2, m_IsClientChoked, + m_IsClientChoked2, m_IsTimeOutResend, m_IsImmediateAckRequested, + m_IsRemoteLeaseChangeInProgress, m_IsBufferEmpty, m_IsJavaClient, m_DontSign; StreamingDestination& m_LocalDestination; std::shared_ptr m_RemoteIdentity; std::shared_ptr m_TransientVerifier; // in case of offline key diff --git a/libi2pd_client/ClientContext.cpp b/libi2pd_client/ClientContext.cpp index 58c8d431..8bee52ae 100644 --- a/libi2pd_client/ClientContext.cpp +++ b/libi2pd_client/ClientContext.cpp @@ -476,6 +476,7 @@ namespace client options[I2CP_PARAM_STREAMING_MAX_INBOUND_SPEED] = GetI2CPOption(section, I2CP_PARAM_STREAMING_MAX_INBOUND_SPEED, DEFAULT_MAX_INBOUND_SPEED); options[I2CP_PARAM_STREAMING_MAX_CONCURRENT_STREAMS] = GetI2CPOption(section, I2CP_PARAM_STREAMING_MAX_CONCURRENT_STREAMS, DEFAULT_MAX_CONCURRENT_STREAMS); options[I2CP_PARAM_STREAMING_ANSWER_PINGS] = GetI2CPOption(section, I2CP_PARAM_STREAMING_ANSWER_PINGS, isServer ? DEFAULT_ANSWER_PINGS : false); + options[I2CP_PARAM_STREAMING_DONT_SIGN] = GetI2CPOption(section, I2CP_PARAM_STREAMING_DONT_SIGN, DEFAULT_DONT_SIGN); options[I2CP_PARAM_STREAMING_PROFILE] = GetI2CPOption(section, I2CP_PARAM_STREAMING_PROFILE, DEFAULT_STREAMING_PROFILE); options[I2CP_PARAM_STREAMING_MAX_WINDOW_SIZE] = GetI2CPOption(section, I2CP_PARAM_STREAMING_MAX_WINDOW_SIZE, i2p::stream::MAX_WINDOW_SIZE); options[I2CP_PARAM_LEASESET_TYPE] = GetI2CPOption(section, I2CP_PARAM_LEASESET_TYPE, DEFAULT_LEASESET_TYPE); From aca5f35fa027e6622bd88574ce182b10e5470d33 Mon Sep 17 00:00:00 2001 From: orignal Date: Wed, 6 Aug 2025 10:50:10 -0400 Subject: [PATCH 06/16] don't verify signature for close and reset packets if came from ECIESx25519 session --- libi2pd/Streaming.cpp | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/libi2pd/Streaming.cpp b/libi2pd/Streaming.cpp index a2018a4e..8fefb0f2 100644 --- a/libi2pd/Streaming.cpp +++ b/libi2pd/Streaming.cpp @@ -461,9 +461,10 @@ namespace stream optionData += 2; } - bool sessionVerified = false; + bool verified = true; if (flags & PACKET_FLAG_FROM_INCLUDED) { + verified = false; if (m_RemoteLeaseSet) m_RemoteIdentity = m_RemoteLeaseSet->GetIdentity (); if (!m_RemoteIdentity) m_RemoteIdentity = std::make_shared(optionData, optionSize); @@ -490,7 +491,7 @@ namespace stream m_RemoteIdentity->GetIdentHash ().ToBase32 ()); return false; } - sessionVerified = true; + verified = true; if (!(flags & PACKET_FLAG_SIGNATURE_INCLUDED)) m_DontSign = true; // don't sign if the remote didn't sign } @@ -503,6 +504,27 @@ namespace stream optionData += 2; } + if (flags & (PACKET_FLAG_CLOSE | PACKET_FLAG_RESET)) + { + verified = false; + if (packet->from && !m_RemoteLeaseSet && m_RemoteIdentity) + m_RemoteLeaseSet = m_LocalDestination.GetOwner ()->FindLeaseSet (m_RemoteIdentity->GetIdentHash ()); + if (m_RemoteLeaseSet) + { + uint8_t staticKey[32]; + m_RemoteLeaseSet->Encrypt (nullptr, staticKey); + if (memcmp (packet->from->GetRemoteStaticKey (), staticKey, 32)) + { + LogPrint (eLogError, "Streaming: Remote LeaseSet static key mismatch for stream from ", + m_RemoteIdentity->GetIdentHash ().ToBase32 ()); + return false; + } + verified = true; + } + else // invalid stream, safe to close + verified = true; + } + if (flags & PACKET_FLAG_OFFLINE_SIGNATURE) { if (!m_RemoteIdentity) @@ -510,7 +532,7 @@ namespace stream LogPrint (eLogInfo, "Streaming: offline signature without identity"); return false; } - if (sessionVerified) + if (verified) { // skip offline signature optionData += 4; // timestamp @@ -558,7 +580,6 @@ namespace stream LogPrint (eLogError, "Streaming: Signature too big, ", signatureLen, " bytes"); return false; } - bool verified = sessionVerified; if (!verified) // packet was not verified through session { // verify actual signature @@ -591,8 +612,13 @@ namespace stream { LogPrint (eLogError, "Streaming: Signature verification failed, sSID=", m_SendStreamID, ", rSID=", m_RecvStreamID); return false; - } + } } + if (!verified) + { + LogPrint (eLogError, "Streaming: Missing signature, sSID=", m_SendStreamID, ", rSID=", m_RecvStreamID); + return false; + } if (immediateAckRequested) SendQuickAck (); return true; From 20ba95ee52f7c5c7cc487b4ef656c628379d9cde Mon Sep 17 00:00:00 2001 From: orignal Date: Wed, 6 Aug 2025 13:44:46 -0400 Subject: [PATCH 07/16] don't add signature to ping message if i2p.streaming.dontSign --- libi2pd/Streaming.cpp | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/libi2pd/Streaming.cpp b/libi2pd/Streaming.cpp index 8fefb0f2..f9e2e60c 100644 --- a/libi2pd/Streaming.cpp +++ b/libi2pd/Streaming.cpp @@ -1218,7 +1218,8 @@ namespace stream size += 4; // sendStreamID memset (packet + size, 0, 14); size += 14; // all zeroes - uint16_t flags = PACKET_FLAG_ECHO | PACKET_FLAG_SIGNATURE_INCLUDED | PACKET_FLAG_FROM_INCLUDED; + uint16_t flags = PACKET_FLAG_ECHO | PACKET_FLAG_FROM_INCLUDED; + if (!m_DontSign) flags |= PACKET_FLAG_SIGNATURE_INCLUDED; bool isOfflineSignature = m_LocalDestination.GetOwner ()->GetPrivateKeys ().IsOfflineSignature (); if (isOfflineSignature) flags |= PACKET_FLAG_OFFLINE_SIGNATURE; htobe16buf (packet + size, flags); @@ -1229,17 +1230,22 @@ namespace stream size += 2; // options size m_LocalDestination.GetOwner ()->GetIdentity ()->ToBuffer (packet + size, identityLen); size += identityLen; // from - if (isOfflineSignature) - { - const auto& offlineSignature = m_LocalDestination.GetOwner ()->GetPrivateKeys ().GetOfflineSignature (); - memcpy (packet + size, offlineSignature.data (), offlineSignature.size ()); - size += offlineSignature.size (); // offline signature - } - uint8_t * signature = packet + size; // set it later - memset (signature, 0, signatureLen); // zeroes for now - size += signatureLen; // signature - htobe16buf (optionsSize, packet + size - 2 - optionsSize); // actual options size - m_LocalDestination.GetOwner ()->Sign (packet, size, signature); + if (m_DontSign) + htobe16buf (optionsSize, packet + size - 2 - optionsSize); // actual options size + else + { + if (isOfflineSignature) + { + const auto& offlineSignature = m_LocalDestination.GetOwner ()->GetPrivateKeys ().GetOfflineSignature (); + memcpy (packet + size, offlineSignature.data (), offlineSignature.size ()); + size += offlineSignature.size (); // offline signature + } + uint8_t * signature = packet + size; // set it later + memset (signature, 0, signatureLen); // zeroes for now + size += signatureLen; // signature + htobe16buf (optionsSize, packet + size - 2 - optionsSize); // actual options size + m_LocalDestination.GetOwner ()->Sign (packet, size, signature); + } p.len = size; SendPackets (std::vector { &p }); LogPrint (eLogDebug, "Streaming: Ping of ", p.len, " bytes sent"); From 9276042078249c9d60917518f95e26e5b831c0bd Mon Sep 17 00:00:00 2001 From: orignal Date: Wed, 6 Aug 2025 15:31:34 -0400 Subject: [PATCH 08/16] offline signature for close packet --- libi2pd/Streaming.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/libi2pd/Streaming.cpp b/libi2pd/Streaming.cpp index f9e2e60c..b1e10ae6 100644 --- a/libi2pd/Streaming.cpp +++ b/libi2pd/Streaming.cpp @@ -1225,7 +1225,6 @@ namespace stream htobe16buf (packet + size, flags); size += 2; // flags size_t identityLen = m_LocalDestination.GetOwner ()->GetIdentity ()->GetFullLen (); - size_t signatureLen = m_LocalDestination.GetOwner ()->GetPrivateKeys ().GetSignatureLen (); uint8_t * optionsSize = packet + size; // set options size later size += 2; // options size m_LocalDestination.GetOwner ()->GetIdentity ()->ToBuffer (packet + size, identityLen); @@ -1240,6 +1239,7 @@ namespace stream memcpy (packet + size, offlineSignature.data (), offlineSignature.size ()); size += offlineSignature.size (); // offline signature } + size_t signatureLen = m_LocalDestination.GetOwner ()->GetPrivateKeys ().GetSignatureLen (); uint8_t * signature = packet + size; // set it later memset (signature, 0, signatureLen); // zeroes for now size += signatureLen; // signature @@ -1301,6 +1301,8 @@ namespace stream size++; // resend delay uint16_t flags = PACKET_FLAG_CLOSE; if (!m_DontSign) flags |= PACKET_FLAG_SIGNATURE_INCLUDED; + bool isOfflineSignature = m_LocalDestination.GetOwner ()->GetPrivateKeys ().IsOfflineSignature (); + if (isOfflineSignature) flags |= PACKET_FLAG_OFFLINE_SIGNATURE; htobe16buf (packet + size, flags); size += 2; // flags if (m_DontSign) @@ -1310,6 +1312,12 @@ namespace stream } else { + if (isOfflineSignature) + { + const auto& offlineSignature = m_LocalDestination.GetOwner ()->GetPrivateKeys ().GetOfflineSignature (); + memcpy (packet + size, offlineSignature.data (), offlineSignature.size ()); + size += offlineSignature.size (); // offline signature + } size_t signatureLen = m_LocalDestination.GetOwner ()->GetPrivateKeys ().GetSignatureLen (); htobe16buf (packet + size, signatureLen); // signature only size += 2; // options size From 2fb8ca9cc782d7113ee07b29f532b5541117337e Mon Sep 17 00:00:00 2001 From: orignal Date: Sat, 9 Aug 2025 19:35:55 -0400 Subject: [PATCH 09/16] send Datagram2 --- libi2pd/Datagram.cpp | 57 ++++++++++++++++++++++++++++++-------------- 1 file changed, 39 insertions(+), 18 deletions(-) diff --git a/libi2pd/Datagram.cpp b/libi2pd/Datagram.cpp index 1ccf67c3..eff2533a 100644 --- a/libi2pd/Datagram.cpp +++ b/libi2pd/Datagram.cpp @@ -60,28 +60,49 @@ namespace datagram { if (session) { - if (session->GetVersion () == eDatagramV3) + std::shared_ptr msg; + switch (session->GetVersion ()) { - constexpr uint8_t flags[] = { 0x00, 0x03 }; // datagram3, no options - auto msg = CreateDataMessage ({{m_Owner->GetIdentity ()->GetIdentHash (), 32}, - {flags, 2}, {payload, len}}, fromPort, toPort, i2p::client::PROTOCOL_TYPE_DATAGRAM3, false); // datagram3 - session->SendMsg(msg); - } - else - { - if (m_Owner->GetIdentity ()->GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1) + case eDatagramV3: { - uint8_t hash[32]; - SHA256(payload, len, hash); - m_Owner->Sign (hash, 32, m_Signature.data ()); + constexpr uint8_t flags[] = { 0x00, 0x03 }; // datagram3, no options + msg = CreateDataMessage ({{m_Owner->GetIdentity ()->GetIdentHash (), 32}, + {flags, 2}, {payload, len}}, fromPort, toPort, i2p::client::PROTOCOL_TYPE_DATAGRAM3, false); // datagram3 + break; } - else - m_Owner->Sign (payload, len, m_Signature.data ()); + case eDatagramV1: + { + if (m_Owner->GetIdentity ()->GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1) + { + uint8_t hash[32]; + SHA256(payload, len, hash); + m_Owner->Sign (hash, 32, m_Signature.data ()); + } + else + m_Owner->Sign (payload, len, m_Signature.data ()); + msg = CreateDataMessage ({{m_From.data (), m_From.size ()}, {m_Signature.data (), m_Signature.size ()}, {payload, len}}, + fromPort, toPort, i2p::client::PROTOCOL_TYPE_DATAGRAM, !session->IsRatchets ()); // datagram1 + break; + } + case eDatagramV2: + { + constexpr uint8_t flags[] = { 0x00, 0x02 }; // datagram2, no options + // signature + std::vector signedData (len + 32 + 2); + memcpy (signedData.data (), m_Owner->GetIdentity ()->GetIdentHash (), 32); + memcpy (signedData.data () + 32, flags, 2); + memcpy (signedData.data () + 34, payload, len); + m_Owner->Sign (signedData.data (), signedData.size (), m_Signature.data ()); + // TODO: offline signatures and options + msg = CreateDataMessage ({{m_From.data (), m_From.size ()}, {flags, 2}, {payload, len}, + {m_Signature.data (), m_Signature.size ()}}, fromPort, toPort, i2p::client::PROTOCOL_TYPE_DATAGRAM2, false); // datagram2 - auto msg = CreateDataMessage ({{m_From.data (), m_From.size ()}, {m_Signature.data (), m_Signature.size ()}, {payload, len}}, - fromPort, toPort, i2p::client::PROTOCOL_TYPE_DATAGRAM, !session->IsRatchets ()); // datagram1 - session->SendMsg(msg); - } + break; + } + default: + LogPrint (eLogError, "Datagram: datagram type ", (int)session->GetVersion (), " is not supported"); + } + if (msg) session->SendMsg(msg); } } From 91027168f9152c132bf687d2593e260a37566d53 Mon Sep 17 00:00:00 2001 From: orignal Date: Mon, 11 Aug 2025 13:14:55 -0400 Subject: [PATCH 10/16] correct max UDP buffer size for openbsd --- libi2pd/SSU2.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libi2pd/SSU2.h b/libi2pd/SSU2.h index b7214480..3be9f2d1 100644 --- a/libi2pd/SSU2.h +++ b/libi2pd/SSU2.h @@ -34,7 +34,11 @@ namespace transport const int SSU2_RESEND_CHECK_MORE_TIMEOUT_VARIANCE = 9; // in milliseconds const size_t SSU2_MAX_RESEND_PACKETS = 128; // packets to resend at the time const uint64_t SSU2_SOCKET_MIN_BUFFER_SIZE = 128 * 1024; +#if defined(__OpenBSD__) + const uint64_t SSU2_SOCKET_MAX_BUFFER_SIZE = 2 * 1024 * 1024; +#else const uint64_t SSU2_SOCKET_MAX_BUFFER_SIZE = 4 * 1024 * 1024; +#endif const size_t SSU2_MAX_NUM_INTRODUCERS = 3; const size_t SSU2_MIN_RECEIVED_PACKET_SIZE = 40; // 16 byte short header + 8 byte minimum payload + 16 byte MAC const size_t SSU2_MAX_RECEIVED_QUEUE_SIZE = 2500; // in packets From d496b15249e782cb4e10b082022de47478be9dc5 Mon Sep 17 00:00:00 2001 From: orignal Date: Wed, 13 Aug 2025 17:16:56 -0400 Subject: [PATCH 11/16] handle SESSION ADD without FROM_PORT --- libi2pd_client/SAM.cpp | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/libi2pd_client/SAM.cpp b/libi2pd_client/SAM.cpp index 09c2dfe9..1f5e313d 100644 --- a/libi2pd_client/SAM.cpp +++ b/libi2pd_client/SAM.cpp @@ -893,11 +893,17 @@ namespace client SendSessionI2PError("Unsupported STYLE"); return; } - auto fromPort = std::stoi(std::string (params[SAM_PARAM_FROM_PORT])); - if (fromPort == -1) + uint16_t fromPort = 0; + auto it = params.find (SAM_PARAM_FROM_PORT); + if (it != params.end ()) { - SendSessionI2PError("Invalid from port"); - return; + auto p = it->second; + auto res = std::from_chars(p.data(), p.data() + p.size(), fromPort); + if (res.ec != std::errc()) + { + SendSessionI2PError("Invalid from port"); + return; + } } auto subsession = std::make_shared(masterSession, id, type, fromPort); if (m_Owner.AddSession (subsession)) @@ -1364,9 +1370,10 @@ namespace client SAMSubSession::SAMSubSession (std::shared_ptr master, std::string_view name, SAMSessionType type, uint16_t port): SAMSession (master->m_Bridge, name, type), masterSession (master), inPort (port) - { - if (Type == SAMSessionType::eSAMSessionTypeStream) + { + if (Type == SAMSessionType::eSAMSessionTypeStream && port) { + // additional streaming destination, use default if port is 0 auto d = masterSession->GetLocalDestination ()->CreateStreamingDestination (inPort); if (d) d->Start (); } From 7dd174d32caf38f11e9a836fc169ec7ac3e255e6 Mon Sep 17 00:00:00 2001 From: orignal Date: Thu, 14 Aug 2025 15:44:48 -0400 Subject: [PATCH 12/16] extract and check VERSION --- libi2pd_client/SAM.cpp | 140 ++++++++++++++++++++--------------------- libi2pd_client/SAM.h | 23 ++++--- 2 files changed, 83 insertions(+), 80 deletions(-) diff --git a/libi2pd_client/SAM.cpp b/libi2pd_client/SAM.cpp index 1f5e313d..94bac10f 100644 --- a/libi2pd_client/SAM.cpp +++ b/libi2pd_client/SAM.cpp @@ -26,9 +26,9 @@ namespace client { SAMSocket::SAMSocket (SAMBridge& owner): m_Owner (owner), m_Socket(owner.GetService()), m_Timer (m_Owner.GetService ()), - m_BufferOffset (0), - m_SocketType (SAMSocketType::eSAMSocketTypeUnknown), m_IsSilent (false), - m_IsAccepting (false), m_IsReceiving (false) + m_BufferOffset (0), m_SocketType (SAMSocketType::eSAMSocketTypeUnknown), + m_IsSilent (false), m_IsAccepting (false), m_IsReceiving (false), + m_Version (MIN_SAM_VERSION) { } @@ -81,21 +81,26 @@ namespace client std::placeholders::_1, std::placeholders::_2)); } - static bool SAMVersionAcceptable(const std::string & ver) + static int ExtractVersion (std::string_view ver) { - return ver == "3.0" || ver == "3.1"; - } + int version = 0; + for (auto ch: ver) + { + if (ch >= '0' && ch <= '9') + { + version *= 10; + version += (ch - '0'); + } + } + return version; + } - static bool SAMVersionTooLow(const std::string & ver) + static std::string CreateVersion (int ver) { - return ver.size() && ver[0] < '3'; - } - - static bool SAMVersionTooHigh(const std::string & ver) - { - return ver.size() && ver > "3.1"; - } - + auto d = div (ver, 10); + return std::to_string (d.quot) + "." + std::to_string (d.rem); + } + void SAMSocket::HandleHandshakeReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred) { if (ecode) @@ -121,8 +126,7 @@ namespace client if (!strcmp (m_Buffer, SAM_HANDSHAKE)) { - std::string maxver("3.1"); - std::string minver("3.0"); + int minVer = 0, maxVer = 0; // try to find MIN and MAX, 3.0 if not found if (separator) { @@ -130,39 +134,33 @@ namespace client auto params = ExtractParams (separator); auto it = params.find (SAM_PARAM_MAX); if (it != params.end ()) - maxver = it->second; + maxVer = ExtractVersion (it->second); it = params.find(SAM_PARAM_MIN); if (it != params.end ()) - minver = it->second; + minVer = ExtractVersion (it->second); } // version negotiation - std::string version; - if (SAMVersionAcceptable(maxver)) - { - version = maxver; - } - else if (SAMVersionAcceptable(minver)) - { - version = minver; - } - else if (SAMVersionTooLow(minver) && SAMVersionTooHigh(maxver)) - { - version = "3.0"; - } - - if (SAMVersionAcceptable(version)) - { -#ifdef _MSC_VER - size_t l = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_HANDSHAKE_REPLY, version.c_str ()); -#else - size_t l = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_HANDSHAKE_REPLY, version.c_str ()); -#endif - boost::asio::async_write (m_Socket, boost::asio::buffer (m_Buffer, l), boost::asio::transfer_all (), - std::bind(&SAMSocket::HandleHandshakeReplySent, shared_from_this (), - std::placeholders::_1, std::placeholders::_2)); - } + if (maxVer && maxVer <= MAX_SAM_VERSION) + m_Version = maxVer; + else if (minVer && minVer >= MIN_SAM_VERSION && minVer <= MAX_SAM_VERSION) + m_Version = minVer; + else if (!maxVer && !minVer) + m_Version = MIN_SAM_VERSION; else - SendMessageReply (SAM_HANDSHAKE_NOVERSION, strlen (SAM_HANDSHAKE_NOVERSION), true); + { + LogPrint (eLogError, "SAM: Handshake version mismatch ", minVer, " ", maxVer); + SendMessageReply (SAM_HANDSHAKE_NOVERSION, true); + return; + } + // send reply +#ifdef _MSC_VER + size_t l = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_HANDSHAKE_REPLY, CreateVersion (m_Version).c_str ()); +#else + size_t l = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_HANDSHAKE_REPLY, CreateVersion (m_Version).c_str ()); +#endif + boost::asio::async_write (m_Socket, boost::asio::buffer (m_Buffer, l), boost::asio::transfer_all (), + std::bind(&SAMSocket::HandleHandshakeReplySent, shared_from_this (), + std::placeholders::_1, std::placeholders::_2)); } else { @@ -193,12 +191,12 @@ namespace client } } - void SAMSocket::SendMessageReply (const char * msg, size_t len, bool close) + void SAMSocket::SendMessageReply (std::string_view msg, bool close) { LogPrint (eLogDebug, "SAMSocket::SendMessageReply, close=",close?"true":"false", " reason: ", msg); if (!m_IsSilent || m_SocketType == SAMSocketType::eSAMSocketTypeForward) - boost::asio::async_write (m_Socket, boost::asio::buffer (msg, len), boost::asio::transfer_all (), + boost::asio::async_write (m_Socket, boost::asio::buffer (msg.data (), msg.size ()), boost::asio::transfer_all (), std::bind(&SAMSocket::HandleMessageReplySent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, close)); else @@ -341,14 +339,14 @@ namespace client if(!IsAcceptableSessionName(id)) { // invalid session id - SendMessageReply (SAM_SESSION_CREATE_INVALID_ID, strlen(SAM_SESSION_CREATE_INVALID_ID), true); + SendMessageReply (SAM_SESSION_CREATE_INVALID_ID, true); return; } m_ID = id; if (m_Owner.FindSession (id)) { // session exists - SendMessageReply (SAM_SESSION_CREATE_DUPLICATED_ID, strlen(SAM_SESSION_CREATE_DUPLICATED_ID), true); + SendMessageReply (SAM_SESSION_CREATE_DUPLICATED_ID, true); return; } @@ -412,7 +410,7 @@ namespace client //ensure we actually received a destination if (destination.empty()) { - SendMessageReply (SAM_SESSION_STATUS_INVALID_KEY, strlen(SAM_SESSION_STATUS_INVALID_KEY), true); + SendMessageReply (SAM_SESSION_STATUS_INVALID_KEY, true); return; } @@ -422,7 +420,7 @@ namespace client i2p::data::PrivateKeys keys; if (!keys.FromBase64(destination)) { - SendMessageReply(SAM_SESSION_STATUS_INVALID_KEY, strlen(SAM_SESSION_STATUS_INVALID_KEY), true); + SendMessageReply(SAM_SESSION_STATUS_INVALID_KEY, true); return; } } @@ -465,7 +463,7 @@ namespace client } } else - SendMessageReply (SAM_SESSION_CREATE_DUPLICATED_DEST, strlen(SAM_SESSION_CREATE_DUPLICATED_DEST), true); + SendMessageReply (SAM_SESSION_CREATE_DUPLICATED_DEST, true); } void SAMSocket::HandleSessionReadinessCheckTimer (const boost::system::error_code& ecode) @@ -503,7 +501,7 @@ namespace client #else size_t l2 = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_CREATE_REPLY_OK, priv.c_str ()); #endif - SendMessageReply (m_Buffer, l2, false); + SendMessageReply ({m_Buffer, l2}, false); } } @@ -571,10 +569,10 @@ namespace client shared_from_this(), std::placeholders::_1)); } else - SendMessageReply (SAM_STREAM_STATUS_INVALID_KEY, strlen(SAM_STREAM_STATUS_INVALID_KEY), true); + SendMessageReply (SAM_STREAM_STATUS_INVALID_KEY, true); } else - SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true); + SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, true); } void SAMSocket::Connect (std::shared_ptr remote, std::shared_ptr session) @@ -591,16 +589,16 @@ namespace client m_Stream->Send ((uint8_t *)m_Buffer, m_BufferOffset); // connect and send m_BufferOffset = 0; I2PReceive (); - SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false); + SendMessageReply (SAM_STREAM_STATUS_OK, false); } else - SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true); + SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, true); } else SendStreamCantReachPeer ("Incompatible crypto"); } else - SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true); + SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, true); } void SAMSocket::HandleConnectLeaseSetRequestComplete (std::shared_ptr leaseSet) @@ -634,7 +632,7 @@ namespace client if (!session->GetLocalDestination ()->IsAcceptingStreams ()) { m_IsAccepting = true; - SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false); + SendMessageReply (SAM_STREAM_STATUS_OK, false); session->GetLocalDestination ()->AcceptOnce (std::bind (&SAMSocket::HandleI2PAccept, shared_from_this (), std::placeholders::_1)); } else @@ -650,7 +648,7 @@ namespace client if (session->acceptQueue.size () < SAM_SESSION_MAX_ACCEPT_QUEUE_SIZE) { // already accepting, queue up - SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false); + SendMessageReply (SAM_STREAM_STATUS_OK, false); session->acceptQueue.push_back (std::make_pair(shared_from_this(), ts)); } else @@ -661,7 +659,7 @@ namespace client } } else - SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true); + SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, true); } void SAMSocket::ProcessStreamForward (std::string_view buf) @@ -680,7 +678,7 @@ namespace client auto session = m_Owner.FindSession(id); if (!session) { - SendMessageReply(SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true); + SendMessageReply(SAM_STREAM_STATUS_INVALID_ID, true); return; } if (session->GetLocalDestination()->IsAcceptingStreams()) @@ -748,7 +746,7 @@ namespace client session->GetLocalDestination()->AcceptStreams( std::bind(&SAMSocket::HandleI2PForward, shared_from_this(), std::placeholders::_1, ep)); - SendMessageReply(SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false); + SendMessageReply(SAM_STREAM_STATUS_OK, false); } @@ -823,7 +821,7 @@ namespace client size_t l = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_DEST_REPLY, keys.GetPublic ()->ToBase64 ().c_str (), keys.ToBase64 ().c_str ()); #endif - SendMessageReply (m_Buffer, l, false); + SendMessageReply ({m_Buffer, l}, false); } void SAMSocket::ProcessNamingLookup (std::string_view buf) @@ -864,7 +862,7 @@ namespace client #else size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str()); #endif - SendMessageReply (m_Buffer, len, false); + SendMessageReply ({m_Buffer, len}, false); } } @@ -880,7 +878,7 @@ namespace client if (masterSession->subsessions.count (id) > 1) { // session exists - SendMessageReply (SAM_SESSION_CREATE_DUPLICATED_ID, strlen(SAM_SESSION_CREATE_DUPLICATED_ID), false); + SendMessageReply (SAM_SESSION_CREATE_DUPLICATED_ID, false); return; } std::string_view style = params[SAM_PARAM_STYLE]; @@ -912,7 +910,7 @@ namespace client SendSessionCreateReplyOk (); } else - SendMessageReply (SAM_SESSION_CREATE_DUPLICATED_ID, strlen(SAM_SESSION_CREATE_DUPLICATED_ID), false); + SendMessageReply (SAM_SESSION_CREATE_DUPLICATED_ID, false); } else SendSessionI2PError ("Wrong session type"); @@ -929,7 +927,7 @@ namespace client std::string id(params[SAM_PARAM_ID]); if (!masterSession->subsessions.erase (id)) { - SendMessageReply (SAM_SESSION_STATUS_INVALID_KEY, strlen(SAM_SESSION_STATUS_INVALID_KEY), false); + SendMessageReply (SAM_SESSION_STATUS_INVALID_KEY, false); return; } m_Owner.CloseSession (id); @@ -946,7 +944,7 @@ namespace client #else size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, reply, msg.c_str()); #endif - SendMessageReply (m_Buffer, len, true); + SendMessageReply ({m_Buffer, len}, true); } void SAMSocket::SendSessionI2PError(const std::string & msg) @@ -981,7 +979,7 @@ namespace client #else size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str()); #endif - SendMessageReply (m_Buffer, len, false); + SendMessageReply ({m_Buffer, len}, false); } } @@ -993,7 +991,7 @@ namespace client #else size_t l = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY, name.c_str (), base64.c_str ()); #endif - SendMessageReply (m_Buffer, l, false); + SendMessageReply ({m_Buffer, l}, false); } const std::map SAMSocket::ExtractParams (std::string_view buf) diff --git a/libi2pd_client/SAM.h b/libi2pd_client/SAM.h index a0ea924d..7ffafb61 100644 --- a/libi2pd_client/SAM.h +++ b/libi2pd_client/SAM.h @@ -39,21 +39,21 @@ namespace client const char SAM_HANDSHAKE[] = "HELLO VERSION"; const char SAM_HANDSHAKE_REPLY[] = "HELLO REPLY RESULT=OK VERSION=%s\n"; - const char SAM_HANDSHAKE_NOVERSION[] = "HELLO REPLY RESULT=NOVERSION\n"; + constexpr std::string_view SAM_HANDSHAKE_NOVERSION { "HELLO REPLY RESULT=NOVERSION\n" }; const char SAM_HANDSHAKE_I2P_ERROR[] = "HELLO REPLY RESULT=I2P_ERROR\n"; const char SAM_SESSION_CREATE[] = "SESSION CREATE"; const char SAM_SESSION_CREATE_REPLY_OK[] = "SESSION STATUS RESULT=OK DESTINATION=%s\n"; - const char SAM_SESSION_CREATE_DUPLICATED_ID[] = "SESSION STATUS RESULT=DUPLICATED_ID\n"; - const char SAM_SESSION_CREATE_DUPLICATED_DEST[] = "SESSION STATUS RESULT=DUPLICATED_DEST\n"; - const char SAM_SESSION_CREATE_INVALID_ID[] = "SESSION STATUS RESULT=INVALID_ID\n"; - const char SAM_SESSION_STATUS_INVALID_KEY[] = "SESSION STATUS RESULT=INVALID_KEY\n"; + constexpr std::string_view SAM_SESSION_CREATE_DUPLICATED_ID { "SESSION STATUS RESULT=DUPLICATED_ID\n" }; + constexpr std::string_view SAM_SESSION_CREATE_DUPLICATED_DEST { "SESSION STATUS RESULT=DUPLICATED_DEST\n" }; + constexpr std::string_view SAM_SESSION_CREATE_INVALID_ID { "SESSION STATUS RESULT=INVALID_ID\n" }; + constexpr std::string_view SAM_SESSION_STATUS_INVALID_KEY { "SESSION STATUS RESULT=INVALID_KEY\n" }; const char SAM_SESSION_STATUS_I2P_ERROR[] = "SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"%s\"\n"; const char SAM_SESSION_ADD[] = "SESSION ADD"; const char SAM_SESSION_REMOVE[] = "SESSION REMOVE"; const char SAM_STREAM_CONNECT[] = "STREAM CONNECT"; - const char SAM_STREAM_STATUS_OK[] = "STREAM STATUS RESULT=OK\n"; - const char SAM_STREAM_STATUS_INVALID_ID[] = "STREAM STATUS RESULT=INVALID_ID\n"; - const char SAM_STREAM_STATUS_INVALID_KEY[] = "STREAM STATUS RESULT=INVALID_KEY\n"; + constexpr std::string_view SAM_STREAM_STATUS_OK { "STREAM STATUS RESULT=OK\n" }; + constexpr std::string_view SAM_STREAM_STATUS_INVALID_ID { "STREAM STATUS RESULT=INVALID_ID\n" }; + constexpr std::string_view SAM_STREAM_STATUS_INVALID_KEY { "STREAM STATUS RESULT=INVALID_KEY\n" }; const char SAM_STREAM_STATUS_CANT_REACH_PEER[] = "STREAM STATUS RESULT=CANT_REACH_PEER MESSAGE=\"%s\"\n"; const char SAM_STREAM_STATUS_I2P_ERROR[] = "STREAM STATUS RESULT=I2P_ERROR MESSAGE=\"%s\"\n"; const char SAM_STREAM_ACCEPT[] = "STREAM ACCEPT"; @@ -90,6 +90,10 @@ namespace client constexpr std::string_view SAM_VALUE_DATAGRAM { "DATAGRAM" }; constexpr std::string_view SAM_VALUE_RAW { "RAW" }; constexpr std::string_view SAM_VALUE_MASTER { "MASTER" }; + + constexpr int MAKE_SAM_VERSION_NUMBER (int major, int minor) { return major*10 + minor; } + constexpr int MIN_SAM_VERSION = MAKE_SAM_VERSION_NUMBER (3, 0); + constexpr int MAX_SAM_VERSION = MAKE_SAM_VERSION_NUMBER (3, 3); enum class SAMSocketType { @@ -127,7 +131,7 @@ namespace client void HandleHandshakeReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); void HandleHandshakeReplySent (const boost::system::error_code& ecode, std::size_t bytes_transferred); void HandleMessage (const boost::system::error_code& ecode, std::size_t bytes_transferred); - void SendMessageReply (const char * msg, size_t len, bool close); + void SendMessageReply (std::string_view msg, bool close); void HandleMessageReplySent (const boost::system::error_code& ecode, std::size_t bytes_transferred, bool close); void Receive (); void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); @@ -182,6 +186,7 @@ namespace client bool m_IsAccepting; // for eSAMSocketTypeAcceptor only bool m_IsReceiving; // for eSAMSocketTypeStream only std::shared_ptr m_Stream; + int m_Version; }; enum class SAMSessionType From 83f9e1098d66d9a01c61c67535ae70ca9b2a8151 Mon Sep 17 00:00:00 2001 From: orignal Date: Thu, 14 Aug 2025 15:58:42 -0400 Subject: [PATCH 13/16] decline master session if SAM version is less than 3.3 --- libi2pd_client/SAM.cpp | 20 +++++++++++++++++++- libi2pd_client/SAM.h | 1 + 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/libi2pd_client/SAM.cpp b/libi2pd_client/SAM.cpp index 94bac10f..22279001 100644 --- a/libi2pd_client/SAM.cpp +++ b/libi2pd_client/SAM.cpp @@ -373,7 +373,15 @@ namespace client } } else if (style == SAM_VALUE_RAW) type = SAMSessionType::eSAMSessionTypeRaw; - else if (style == SAM_VALUE_MASTER) type = SAMSessionType::eSAMSessionTypeMaster; + else if (style == SAM_VALUE_MASTER) + { + if (m_Version < SAM_VERSION_33) // < SAM 3.3 + { + SendSessionI2PError("MASTER session is not supported"); + return; + } + type = SAMSessionType::eSAMSessionTypeMaster; + } if (type == SAMSessionType::eSAMSessionTypeUnknown) { // unknown style @@ -868,6 +876,11 @@ namespace client void SAMSocket::ProcessSessionAdd (std::string_view buf) { + if (m_Version < SAM_VERSION_33) // < SAM 3.3 + { + SendSessionI2PError("SESSION ADD is not supported"); + return; + } auto session = m_Owner.FindSession(m_ID); if (session && session->Type == SAMSessionType::eSAMSessionTypeMaster) { @@ -918,6 +931,11 @@ namespace client void SAMSocket::ProcessSessionRemove (std::string_view buf) { + if (m_Version < SAM_VERSION_33) // < SAM 3.3 + { + SendSessionI2PError("SESSION REMOVE is not supported"); + return; + } auto session = m_Owner.FindSession(m_ID); if (session && session->Type == SAMSessionType::eSAMSessionTypeMaster) { diff --git a/libi2pd_client/SAM.h b/libi2pd_client/SAM.h index 7ffafb61..00b7ed03 100644 --- a/libi2pd_client/SAM.h +++ b/libi2pd_client/SAM.h @@ -94,6 +94,7 @@ namespace client constexpr int MAKE_SAM_VERSION_NUMBER (int major, int minor) { return major*10 + minor; } constexpr int MIN_SAM_VERSION = MAKE_SAM_VERSION_NUMBER (3, 0); constexpr int MAX_SAM_VERSION = MAKE_SAM_VERSION_NUMBER (3, 3); + constexpr int SAM_VERSION_33 = MAKE_SAM_VERSION_NUMBER (3, 3); // SAM 3.3 enum class SAMSocketType { From 27f2c5285da9bec537caeba9f7df6920b9f21c87 Mon Sep 17 00:00:00 2001 From: orignal Date: Thu, 14 Aug 2025 18:51:54 -0400 Subject: [PATCH 14/16] support boost 1.89 --- Makefile.bsd | 2 +- Makefile.homebrew | 2 +- Makefile.osx | 2 +- libi2pd/Timestamp.h | 3 ++- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Makefile.bsd b/Makefile.bsd index 1c911802..9c6de7f0 100644 --- a/Makefile.bsd +++ b/Makefile.bsd @@ -3,7 +3,7 @@ CXXFLAGS ?= ${CXX_DEBUG} -Wall -Wextra -Wno-unused-parameter -pedantic -Wno-misl DEFINES = -D_GLIBCXX_USE_NANOSLEEP=1 INCFLAGS = -I/usr/include/ -I/usr/local/include/ LDFLAGS = ${LD_DEBUG} -Wl,-rpath,/usr/local/lib -L/usr/local/lib -LDLIBS = -lssl -lcrypto -lz -lpthread -lboost_system -lboost_program_options +LDLIBS = -lssl -lcrypto -lz -lpthread -lboost_program_options ## NOTE: NEEDED_CXXFLAGS is here so that custom CXXFLAGS can be specified at build time ## **without** overwriting the CXXFLAGS which we need in order to build. diff --git a/Makefile.homebrew b/Makefile.homebrew index 706f9811..9d8d17cd 100644 --- a/Makefile.homebrew +++ b/Makefile.homebrew @@ -18,7 +18,7 @@ endif LDLIBS += -lpthread -ldl else LDFLAGS += -L${SSLROOT}/lib -L${BOOSTROOT}/lib - LDLIBS = -lz -lssl -lcrypto -lboost_system -lboost_filesystem -lboost_program_options -lpthread + LDLIBS = -lz -lssl -lcrypto -lboost_program_options -lpthread ifeq ($(USE_UPNP),yes) LDFLAGS += -L${UPNPROOT}/lib LDLIBS += -lminiupnpc diff --git a/Makefile.osx b/Makefile.osx index 52282307..a2ad515e 100644 --- a/Makefile.osx +++ b/Makefile.osx @@ -9,7 +9,7 @@ LDFLAGS += -Wl,-dead_strip_dylibs ifeq ($(USE_STATIC),yes) LDLIBS = -lz /usr/local/lib/libssl.a /usr/local/lib/libcrypto.a /usr/local/lib/libboost_system.a /usr/local/lib/libboost_filesystem.a /usr/local/lib/libboost_program_options.a -lpthread else - LDLIBS = -lz -lssl -lcrypto -lboost_system -lboost_filesystem -lboost_program_options -lpthread + LDLIBS = -lz -lssl -lcrypto -lboost_program_options -lpthread endif ifeq ($(USE_UPNP),yes) diff --git a/libi2pd/Timestamp.h b/libi2pd/Timestamp.h index 00c60433..6b224685 100644 --- a/libi2pd/Timestamp.h +++ b/libi2pd/Timestamp.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2024, The PurpleI2P Project +* Copyright (c) 2013-2025, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -14,6 +14,7 @@ #include #include #include +#include // for boost 1.89 namespace i2p { From be05fa0fe143e4a9315689a21e5848ff009365cc Mon Sep 17 00:00:00 2001 From: orignal Date: Fri, 15 Aug 2025 09:01:53 -0400 Subject: [PATCH 15/16] verify session for close packet onlly if ECIESx25519 --- libi2pd/Streaming.cpp | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/libi2pd/Streaming.cpp b/libi2pd/Streaming.cpp index b1e10ae6..2651fd55 100644 --- a/libi2pd/Streaming.cpp +++ b/libi2pd/Streaming.cpp @@ -507,22 +507,25 @@ namespace stream if (flags & (PACKET_FLAG_CLOSE | PACKET_FLAG_RESET)) { verified = false; - if (packet->from && !m_RemoteLeaseSet && m_RemoteIdentity) - m_RemoteLeaseSet = m_LocalDestination.GetOwner ()->FindLeaseSet (m_RemoteIdentity->GetIdentHash ()); - if (m_RemoteLeaseSet) - { - uint8_t staticKey[32]; - m_RemoteLeaseSet->Encrypt (nullptr, staticKey); - if (memcmp (packet->from->GetRemoteStaticKey (), staticKey, 32)) + if (packet->from) + { + if (!m_RemoteLeaseSet && m_RemoteIdentity) + m_RemoteLeaseSet = m_LocalDestination.GetOwner ()->FindLeaseSet (m_RemoteIdentity->GetIdentHash ()); + if (m_RemoteLeaseSet) { - LogPrint (eLogError, "Streaming: Remote LeaseSet static key mismatch for stream from ", - m_RemoteIdentity->GetIdentHash ().ToBase32 ()); - return false; + uint8_t staticKey[32]; + m_RemoteLeaseSet->Encrypt (nullptr, staticKey); + if (memcmp (packet->from->GetRemoteStaticKey (), staticKey, 32)) + { + LogPrint (eLogError, "Streaming: Remote LeaseSet static key mismatch for stream from ", + m_RemoteIdentity->GetIdentHash ().ToBase32 ()); + return false; + } + verified = true; } - verified = true; - } - else // invalid stream, safe to close - verified = true; + else // invalid stream, safe to close + verified = true; + } } if (flags & PACKET_FLAG_OFFLINE_SIGNATURE) From c7ad247929cf98309345fcfa8d9b8e54920baec3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9F=9C=83=20Ezor=20Kael?= <60944239+wipedlifepotato@users.noreply.github.com> Date: Sat, 16 Aug 2025 12:06:46 +0700 Subject: [PATCH 16/16] Update HTTPServer.cpp. fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Починка style.css если нет кастомных тем --- daemon/HTTPServer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daemon/HTTPServer.cpp b/daemon/HTTPServer.cpp index 18fecce7..4e5d815e 100644 --- a/daemon/HTTPServer.cpp +++ b/daemon/HTTPServer.cpp @@ -42,7 +42,7 @@ namespace i2p { namespace http { - static void LoadExtCSS (std::string fileName = "style.css") + static void LoadExtCSS (std::string fileName = "style") { std::stringstream s; std::string styleFile = i2p::fs::DataDirPath ("webconsole/"+fileName+".css");