From 55ca1a38f2eb8ede8d392daad7756a6fc59024ba Mon Sep 17 00:00:00 2001
From: orignal <i2porignal@yandex.ru>
Date: Fri, 17 Sep 2021 21:52:39 -0400
Subject: [PATCH] reuse receive buffer

(cherry picked from commit 5b2b9e00a2d5ddb673ea443c73d381251e54e4ea)
---
 libi2pd/NTCP2.cpp | 35 ++++++++++++++++++++++++++++++-----
 libi2pd/NTCP2.h   |  8 ++++++--
 2 files changed, 36 insertions(+), 7 deletions(-)

diff --git a/libi2pd/NTCP2.cpp b/libi2pd/NTCP2.cpp
index ed84b8e7..5b542187 100644
--- a/libi2pd/NTCP2.cpp
+++ b/libi2pd/NTCP2.cpp
@@ -332,8 +332,8 @@ namespace transport
 		m_SendMDCtx(nullptr), m_ReceiveMDCtx (nullptr),
 #endif
 		m_NextReceivedLen (0), m_NextReceivedBuffer (nullptr), m_NextSendBuffer (nullptr),
-		m_ReceiveSequenceNumber (0), m_SendSequenceNumber (0), m_IsSending (false),
-		m_NextPaddingSize (16)
+		m_NextReceivedBufferSize (0), m_ReceiveSequenceNumber (0), m_SendSequenceNumber (0), 
+		m_IsSending (false), m_IsReceiving (false), m_NextPaddingSize (16)
 	{
 		if (in_RemoteRouter) // Alice
 		{
@@ -405,7 +405,30 @@ namespace transport
 		htole64buf (nonce + 4, seqn);
 	}
 
+	void NTCP2Session::CreateNextReceivedBuffer (size_t size)
+	{		
+		if (m_NextReceivedBuffer)
+		{
+			if (size <= m_NextReceivedBufferSize)
+				return; // buffer is good, do nothing
+			else
+				delete[] m_NextReceivedBuffer;
+		}
+		m_NextReceivedBuffer = new uint8_t[size];
+		m_NextReceivedBufferSize = size;
+	}	
 
+	void NTCP2Session::DeleteNextReceiveBuffer (uint64_t ts)
+	{
+		if (m_NextReceivedBuffer && !m_IsReceiving && 
+		    ts > m_LastActivityTimestamp + NTCP2_RECEIVE_BUFFER_DELETION_TIMEOUT)
+		{
+			delete[] m_NextReceivedBuffer;
+			m_NextReceivedBuffer = nullptr;
+			m_NextReceivedBufferSize = 0;
+		}	
+	}	
+		
 	void NTCP2Session::KeyDerivationFunctionDataPhase ()
 	{
 		uint8_t k[64];
@@ -759,8 +782,7 @@ namespace transport
 			LogPrint (eLogDebug, "NTCP2: received length ", m_NextReceivedLen);
 			if (m_NextReceivedLen >= 16)
 			{
-				if (m_NextReceivedBuffer) delete[] m_NextReceivedBuffer;
-				m_NextReceivedBuffer = new uint8_t[m_NextReceivedLen];
+				CreateNextReceivedBuffer (m_NextReceivedLen);
 				boost::system::error_code ec;
 				size_t moreBytes = m_Socket.available(ec);
 				if (!ec && moreBytes >= m_NextReceivedLen)
@@ -787,6 +809,7 @@ namespace transport
 		const int one = 1;
 		setsockopt(m_Socket.native_handle(), IPPROTO_TCP, TCP_QUICKACK, &one, sizeof(one));
 #endif
+		m_IsReceiving = true;
 		boost::asio::async_read (m_Socket, boost::asio::buffer(m_NextReceivedBuffer, m_NextReceivedLen), boost::asio::transfer_all (),
 			std::bind(&NTCP2Session::HandleReceived, shared_from_this (), std::placeholders::_1, std::placeholders::_2));
 	}
@@ -810,7 +833,7 @@ namespace transport
 			{
 				LogPrint (eLogDebug, "NTCP2: received message decrypted");
 				ProcessNextFrame (m_NextReceivedBuffer, m_NextReceivedLen-16);
-				delete[] m_NextReceivedBuffer; m_NextReceivedBuffer = nullptr; // we don't need received buffer anymore
+				m_IsReceiving = false;
 				ReceiveLength ();
 			}
 			else
@@ -1448,6 +1471,8 @@ namespace transport
 					LogPrint (eLogDebug, "NTCP2: No activity for ", session->GetTerminationTimeout (), " seconds");
 					session->TerminateByTimeout (); // it doesn't change m_NTCP2Session right a way
 				}
+				else
+					it.second->DeleteNextReceiveBuffer (ts);
 			// pending
 			for (auto it = m_PendingIncomingSessions.begin (); it != m_PendingIncomingSessions.end ();)
 			{
diff --git a/libi2pd/NTCP2.h b/libi2pd/NTCP2.h
index bcee1b09..b54c1634 100644
--- a/libi2pd/NTCP2.h
+++ b/libi2pd/NTCP2.h
@@ -34,6 +34,7 @@ namespace transport
 	const int NTCP2_ESTABLISH_TIMEOUT = 10; // 10 seconds
 	const int NTCP2_TERMINATION_TIMEOUT = 120; // 2 minutes
 	const int NTCP2_TERMINATION_CHECK_TIMEOUT = 30; // 30 seconds
+	const int NTCP2_RECEIVE_BUFFER_DELETION_TIMEOUT = 3; // 3 seconds
 	const int NTCP2_ROUTERINFO_RESEND_INTERVAL = 25*60; // 25 minuntes in seconds
 	const int NTCP2_ROUTERINFO_RESEND_INTERVAL_THRESHOLD = 25*60; // 25 minuntes
 
@@ -132,7 +133,8 @@ namespace transport
 			void TerminateByTimeout ();
 			void Done ();
 			void Close () { m_Socket.close (); }; // for accept
-
+			void DeleteNextReceiveBuffer (uint64_t ts);
+			
 			boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; };
 			const boost::asio::ip::tcp::endpoint& GetRemoteEndpoint () { return m_RemoteEndpoint; };
 			void SetRemoteEndpoint (const boost::asio::ip::tcp::endpoint& ep) { m_RemoteEndpoint = ep; };
@@ -151,6 +153,7 @@ namespace transport
 			void Established ();
 
 			void CreateNonce (uint64_t seqn, uint8_t * nonce);
+			void CreateNextReceivedBuffer (size_t size);
 			void KeyDerivationFunctionDataPhase ();
 			void SetSipKeys (const uint8_t * sendSipKey, const uint8_t * receiveSipKey);
 
@@ -206,6 +209,7 @@ namespace transport
 #endif
 			uint16_t m_NextReceivedLen;
 			uint8_t * m_NextReceivedBuffer, * m_NextSendBuffer;
+			size_t m_NextReceivedBufferSize;
 			union
 			{
 				uint8_t buf[8];
@@ -215,7 +219,7 @@ namespace transport
 
 			i2p::I2NPMessagesHandler m_Handler;
 
-			bool m_IsSending;
+			bool m_IsSending, m_IsReceiving;
 			std::list<std::shared_ptr<I2NPMessage> > m_SendQueue;
 			uint64_t m_NextRouterInfoResendTime; // seconds since epoch