From 5d9f292506f70dfa9fdb0c93b2109a6fac75ce17 Mon Sep 17 00:00:00 2001
From: orignal <romakoshelkin@yandex.ru>
Date: Sun, 22 Dec 2013 11:29:57 -0500
Subject: [PATCH 01/16] remove dead routers

---
 Identity.cpp      |  8 ++++++++
 Identity.h        |  2 ++
 NTCPSession.cpp   |  9 ++++-----
 NTCPSession.h     | 11 ++++++++---
 NetDb.cpp         | 39 +++++++++++++++++++++++++++++++++++----
 NetDb.h           |  5 ++++-
 RouterContext.cpp |  4 +---
 7 files changed, 62 insertions(+), 16 deletions(-)

diff --git a/Identity.cpp b/Identity.cpp
index bbdfdbe8..b80d1cbf 100644
--- a/Identity.cpp
+++ b/Identity.cpp
@@ -9,6 +9,14 @@ namespace i2p
 {
 namespace data
 {
+	Identity& Identity::operator=(const Keys& keys)
+	{
+		// copy public and signing keys together
+		memcpy (publicKey, keys.publicKey, sizeof (publicKey) + sizeof (signingKey));
+		memset (certificate, 0, sizeof (certificate));		
+		return *this;
+	}	
+	
 	IdentHash CalculateIdentHash (const Identity& identity)
 	{
 		IdentHash hash;
diff --git a/Identity.h b/Identity.h
index 09af9332..549e9cea 100644
--- a/Identity.h
+++ b/Identity.h
@@ -23,6 +23,8 @@ namespace data
 		uint8_t publicKey[256];
 		uint8_t signingKey[128];
 		uint8_t certificate[3];
+
+		Identity& operator=(const Keys& keys);
 	};	
 	
 #pragma pack()
diff --git a/NTCPSession.cpp b/NTCPSession.cpp
index b52a6223..1d8a93f0 100644
--- a/NTCPSession.cpp
+++ b/NTCPSession.cpp
@@ -20,12 +20,10 @@ namespace i2p
 {
 namespace ntcp
 {
-	NTCPSession::NTCPSession (boost::asio::io_service& service, i2p::data::RouterInfo * in_RemoteRouterInfo): 
+	NTCPSession::NTCPSession (boost::asio::io_service& service, i2p::data::RouterInfo& in_RemoteRouterInfo): 
 		m_Socket (service), m_TerminationTimer (service), m_IsEstablished (false), 
-		m_ReceiveBufferOffset (0), m_NextMessage (nullptr)
+		m_RemoteRouterInfo (in_RemoteRouterInfo), m_ReceiveBufferOffset (0), m_NextMessage (nullptr)
 	{		
-		if (in_RemoteRouterInfo)
-			m_RemoteRouterInfo = *in_RemoteRouterInfo;
 	}
 	
 	void NTCPSession::CreateAESKey (uint8_t * pubKey, uint8_t * aesKey)
@@ -521,7 +519,8 @@ namespace ntcp
 		
 		
 	NTCPClient::NTCPClient (boost::asio::io_service& service, const char * address, 
-		int port, i2p::data::RouterInfo& in_RouterInfo): NTCPSession (service, &in_RouterInfo),
+		int port, i2p::data::RouterInfo& in_RouterInfo): 
+		NTCPSession (service, in_RouterInfo),
 		m_Endpoint (boost::asio::ip::address::from_string (address), port)	
 	{
 		Connect ();
diff --git a/NTCPSession.h b/NTCPSession.h
index 4ef0e3dd..925643b4 100644
--- a/NTCPSession.h
+++ b/NTCPSession.h
@@ -66,7 +66,7 @@ namespace ntcp
 	{
 		public:
 
-			NTCPSession (boost::asio::io_service& service, i2p::data::RouterInfo * in_RemoteRouterInfo = 0);
+			NTCPSession (boost::asio::io_service& service, i2p::data::RouterInfo& in_RemoteRouterInfo);
 			virtual ~NTCPSession () {};
 
 			boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; };
@@ -126,7 +126,7 @@ namespace ntcp
 			CryptoPP::CBC_Mode<CryptoPP::AES>::Encryption m_Encryption;
 			CryptoPP::Adler32 m_Adler;
 			
-			i2p::data::RouterInfo m_RemoteRouterInfo;
+			i2p::data::RouterInfo& m_RemoteRouterInfo;
 			
 			NTCPPhase1 m_Phase1;
 			NTCPPhase2 m_Phase2;
@@ -163,11 +163,16 @@ namespace ntcp
 	{
 		public:
 
-			NTCPServerConnection (boost::asio::io_service& service): NTCPSession (service) {};
+			NTCPServerConnection (boost::asio::io_service& service): 
+				NTCPSession (service, m_DummyRemoteRouterInfo) {};
 			
 		protected:
 
 			virtual void Connected ();
+			
+		private:	
+
+			i2p::data::RouterInfo m_DummyRemoteRouterInfo;
 	};	
 }	
 }	
diff --git a/NetDb.cpp b/NetDb.cpp
index 41440582..79cd903a 100644
--- a/NetDb.cpp
+++ b/NetDb.cpp
@@ -128,6 +128,15 @@ namespace data
 		else
 			return nullptr;
 	}
+
+	LeaseSet * NetDb::FindLeaseSet (const IdentHash& destination) const
+	{
+		auto it = m_LeaseSets.find (destination);
+		if (it != m_LeaseSets.end ())
+			return it->second;
+		else
+			return nullptr;
+	}	
 	
 	void NetDb::Load (const char * directory)
 	{
@@ -156,19 +165,36 @@ namespace data
 
 	void NetDb::SaveUpdated (const char * directory)
 	{	
-		int count = 0;
+		auto GetFilePath = [](const char * directory, const RouterInfo * routerInfo)
+		{
+			return std::string (directory) + "/r" +
+				routerInfo->GetIdentHashBase64 ()[0] + "/routerInfo-" + 
+				routerInfo->GetIdentHashBase64 () + ".dat";
+		};	
+			
+		int count = 0, deletedCount = 0;
 		for (auto it: m_RouterInfos)
+		{	
 			if (it.second->IsUpdated ())
 			{
-				std::ofstream r (std::string (directory) + "/r" +
-				    it.second->GetIdentHashBase64 ()[0] + "/routerInfo-" + 
-					it.second->GetIdentHashBase64 () + ".dat");
+				std::ofstream r (GetFilePath(directory, it.second));
 				r.write ((char *)it.second->GetBuffer (), it.second->GetBufferLen ());
 				it.second->SetUpdated (false);
 				count++;
 			}
+			else if (it.second->IsUnreachable ())
+			{
+				if (boost::filesystem::exists (GetFilePath (directory, it.second)))
+				{    
+				    boost::filesystem::remove (GetFilePath (directory, it.second));
+					deletedCount++;
+				}	
+			}	
+		}	
 		if (count > 0)
 			LogPrint (count," new/updated routers saved");
+		if (deletedCount > 0)
+			LogPrint (deletedCount," routers deleted");
 	}
 
 	void NetDb::RequestDestination (const char * b32, const uint8_t * router)
@@ -197,6 +223,11 @@ namespace data
 			LogPrint ("No outbound tunnels found");
 	}	
 
+	void NetDb::RequestDestination (const IdentHash& destination)
+	{
+		RequestDestination ((const uint8_t *)destination, GetRandomNTCPRouter (true)->GetIdentHash ());
+	}	
+	
 	void NetDb::HandleDatabaseStoreMsg (uint8_t * buf, size_t len)
 	{		
 		I2NPDatabaseStoreMsg * msg = (I2NPDatabaseStoreMsg *)buf;
diff --git a/NetDb.h b/NetDb.h
index c76bd9f7..4d9ebbb1 100644
--- a/NetDb.h
+++ b/NetDb.h
@@ -27,9 +27,12 @@ namespace data
 			void AddRouterInfo (uint8_t * buf, int len);
 			void AddLeaseSet (uint8_t * buf, int len);
 			RouterInfo * FindRouter (const IdentHash& ident) const;
-
+			LeaseSet * FindLeaseSet (const IdentHash& destination) const;
+			
 			void RequestDestination (const char * b32, const uint8_t * router); // in base32
 			void RequestDestination (const uint8_t * destination, const uint8_t * router);
+			void RequestDestination (const IdentHash& destination);
+			
 			void HandleDatabaseStoreMsg (uint8_t * buf, size_t len);
 			void HandleDatabaseSearchReplyMsg (I2NPMessage * msg);
 			
diff --git a/RouterContext.cpp b/RouterContext.cpp
index 84fac346..718a9f36 100644
--- a/RouterContext.cpp
+++ b/RouterContext.cpp
@@ -26,9 +26,7 @@ namespace i2p
 			CryptoPP::Integer (m_Keys.signingPrivateKey, 20));
 		
 		i2p::data::Identity ident;
-		// copy public and signing keys together
-		memcpy (ident.publicKey, m_Keys.publicKey, sizeof (ident.publicKey) + sizeof (ident.signingKey));
-		memset (ident.certificate, 0, sizeof (ident.certificate));		
+		ident = m_Keys;
 		m_RouterInfo.SetRouterIdentity (ident);
 
 		m_RouterInfo.AddNTCPAddress ("127.0.0.1", 17007); // TODO:

From ca51567eba5657a235d007115456f04f8a1cee0d Mon Sep 17 00:00:00 2001
From: orignal <romakoshelkin@yandex.ru>
Date: Wed, 25 Dec 2013 12:19:46 -0500
Subject: [PATCH 02/16] better exploratory

---
 NetDb.cpp | 20 ++++++++++++--------
 NetDb.h   |  3 +++
 2 files changed, 15 insertions(+), 8 deletions(-)

diff --git a/NetDb.cpp b/NetDb.cpp
index 79cd903a..3f05dd7d 100644
--- a/NetDb.cpp
+++ b/NetDb.cpp
@@ -270,8 +270,8 @@ namespace data
 		if (num > 0)
 		{
 			bool isExploratory = !memcmp (m_Exploratory, buf, 32) && m_LastFloodfill;			
-			i2p::tunnel::OutboundTunnel * outbound = i2p::tunnel::tunnels.GetNextOutboundTunnel ();
-			i2p::tunnel::InboundTunnel * inbound = i2p::tunnel::tunnels.GetNextInboundTunnel ();
+			i2p::tunnel::OutboundTunnel * outbound = isExploratory ? m_LastOutboundTunnel : i2p::tunnel::tunnels.GetNextOutboundTunnel ();
+			i2p::tunnel::InboundTunnel * inbound = isExploratory ? m_LastInboundTunnel : i2p::tunnel::tunnels.GetNextInboundTunnel ();
 			
 			for (int i = 0; i < num; i++)
 			{
@@ -327,9 +327,9 @@ namespace data
 	
 	void NetDb::Explore ()
 	{
-		i2p::tunnel::OutboundTunnel * outbound = i2p::tunnel::tunnels.GetNextOutboundTunnel ();
-		i2p::tunnel::InboundTunnel * inbound = i2p::tunnel::tunnels.GetNextInboundTunnel ();
-		if (outbound && inbound)
+		m_LastOutboundTunnel = i2p::tunnel::tunnels.GetNextOutboundTunnel ();
+		m_LastInboundTunnel = i2p::tunnel::tunnels.GetNextInboundTunnel ();
+		if (m_LastOutboundTunnel && m_LastInboundTunnel)
 		{
 			m_LastFloodfill = GetRandomNTCPRouter (true);
 			if (m_LastFloodfill)
@@ -337,9 +337,13 @@ namespace data
 				LogPrint ("Exploring new routers ...");
 				CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
 				rnd.GenerateBlock (m_Exploratory, 32);
-				I2NPMessage * msg = i2p::CreateDatabaseLookupMsg (m_Exploratory, inbound->GetNextIdentHash (), 
-					inbound->GetNextTunnelID (), true);
-				outbound->SendTunnelDataMsg (m_LastFloodfill->GetIdentHash (), 0, msg);
+				
+				m_LastOutboundTunnel->GetTunnelGateway ().PutTunnelDataMsg (m_LastFloodfill->GetIdentHash (), 0,
+					CreateDatabaseStoreMsg ()); // tell floodfill about us                                         
+				m_LastOutboundTunnel->GetTunnelGateway ().PutTunnelDataMsg (m_LastFloodfill->GetIdentHash (), 0, 
+					i2p::CreateDatabaseLookupMsg (m_Exploratory, m_LastInboundTunnel->GetNextIdentHash (), 
+					m_LastInboundTunnel->GetNextTunnelID (), true)); // explore
+				m_LastOutboundTunnel->GetTunnelGateway ().SendBuffer ();
 			}	
 		}
 	}	
diff --git a/NetDb.h b/NetDb.h
index 4d9ebbb1..3f644c7d 100644
--- a/NetDb.h
+++ b/NetDb.h
@@ -9,6 +9,7 @@
 #include "I2NPProtocol.h"
 #include "RouterInfo.h"
 #include "LeaseSet.h"
+#include "Tunnel.h"
 
 namespace i2p
 {
@@ -57,6 +58,8 @@ namespace data
 			std::thread * m_Thread;	
 			uint8_t m_Exploratory[32];
 			const RouterInfo * m_LastFloodfill;
+			i2p::tunnel::OutboundTunnel * m_LastOutboundTunnel;
+			i2p::tunnel::InboundTunnel * m_LastInboundTunnel;
 			i2p::util::Queue<I2NPMessage> m_Queue; // of I2NPDatabaseStoreMsg
 	};
 

From 808d6d1fbf978286b499d9d9990c94e3dea68617 Mon Sep 17 00:00:00 2001
From: orignal <romakoshelkin@yandex.ru>
Date: Sun, 29 Dec 2013 10:48:57 -0500
Subject: [PATCH 03/16] improved stability

---
 NTCPSession.cpp |  8 +++----
 NTCPSession.h   |  3 ---
 RouterInfo.cpp  |  8 ++++++-
 RouterInfo.h    |  2 ++
 Transports.cpp  | 56 ++++++++++++++++++++++++++++---------------------
 Transports.h    |  2 ++
 Tunnel.cpp      | 22 +++++++++++++------
 7 files changed, 62 insertions(+), 39 deletions(-)

diff --git a/NTCPSession.cpp b/NTCPSession.cpp
index 1d8a93f0..14b74c27 100644
--- a/NTCPSession.cpp
+++ b/NTCPSession.cpp
@@ -184,7 +184,8 @@ namespace ntcp
 	{
 		if (ecode)
         {
-			LogPrint ("Phase 2 read error: ", ecode.message ());
+			LogPrint ("Phase 2 read error: ", ecode.message (), ". Wrong ident assumed");
+			GetRemoteRouterInfo ().SetUnreachable (true); // this RouterInfo is not valid
 			Terminate ();
 		}
 		else
@@ -459,10 +460,7 @@ namespace ntcp
 		m_Adler.CalculateDigest (sendBuffer + len + 2 + padding, sendBuffer, len + 2+ padding);
 
 		int l = len + padding + 6;
-		{
-			std::lock_guard<std::mutex> lock (m_EncryptionMutex);
-			m_Encryption.ProcessData(sendBuffer, sendBuffer, l);
-		}	
+		m_Encryption.ProcessData(sendBuffer, sendBuffer, l);	
 
 		boost::asio::async_write (m_Socket, boost::asio::buffer (sendBuffer, l), boost::asio::transfer_all (),                      
         	boost::bind(&NTCPSession::HandleSent, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred, msg));	
diff --git a/NTCPSession.h b/NTCPSession.h
index 925643b4..5d33ef10 100644
--- a/NTCPSession.h
+++ b/NTCPSession.h
@@ -2,7 +2,6 @@
 #define NTCP_SESSION_H__
 
 #include <inttypes.h>
-#include <mutex>
 #include <list>
 #include <boost/asio.hpp>
 #include <cryptopp/modes.h>
@@ -139,8 +138,6 @@ namespace ntcp
 			i2p::I2NPMessage * m_NextMessage;
 			std::list<i2p::I2NPMessage *> m_DelayedMessages;
 			size_t m_NextMessageOffset;
-			
-			std::mutex m_EncryptionMutex;
 	};	
 
 	class NTCPClient: public NTCPSession
diff --git a/RouterInfo.cpp b/RouterInfo.cpp
index c6666dfd..417466c6 100644
--- a/RouterInfo.cpp
+++ b/RouterInfo.cpp
@@ -34,6 +34,7 @@ namespace data
 	{	
 		m_RouterIdentity = identity;
 		m_IdentHash = CalculateIdentHash (m_RouterIdentity);
+		UpdateIdentHashBase64 ();
 		m_Timestamp = i2p::util::GetMillisecondsSinceEpoch ();
 	}
 	
@@ -124,12 +125,17 @@ namespace data
 		}		
 		
 		CryptoPP::SHA256().CalculateDigest(m_IdentHash, (uint8_t *)&m_RouterIdentity, sizeof (m_RouterIdentity));
+		UpdateIdentHashBase64 ();
+	}	
+
+	void RouterInfo::UpdateIdentHashBase64 ()
+	{
 		size_t l = i2p::data::ByteStreamToBase64 (m_IdentHash, 32, m_IdentHashBase64, 48);
 		m_IdentHashBase64[l] = 0;
 		memcpy (m_IdentHashAbbreviation, m_IdentHashBase64, 4);
 		m_IdentHashAbbreviation[4] = 0;
 	}	
-
+		
 	void RouterInfo::WriteToStream (std::ostream& s)
 	{
 		s.write ((char *)&m_RouterIdentity, sizeof (m_RouterIdentity));
diff --git a/RouterInfo.h b/RouterInfo.h
index 780eb8c7..3111fbf3 100644
--- a/RouterInfo.h
+++ b/RouterInfo.h
@@ -36,6 +36,7 @@ namespace data
 			RouterInfo (const char * filename);
 			RouterInfo () = default;
 			RouterInfo (const RouterInfo& ) = default;
+			RouterInfo& operator=(const RouterInfo& ) = default;
 			RouterInfo (const uint8_t * buf, int len);
 			
 			const Identity& GetRouterIdentity () const { return m_RouterIdentity; };
@@ -74,6 +75,7 @@ namespace data
 			void WriteToStream (std::ostream& s);
 			size_t ReadString (char * str, std::istream& s);
 			void WriteString (const std::string& str, std::ostream& s);
+			void UpdateIdentHashBase64 ();
 			
 		private:
 
diff --git a/Transports.cpp b/Transports.cpp
index e938c85b..9204cea9 100644
--- a/Transports.cpp
+++ b/Transports.cpp
@@ -23,6 +23,7 @@ namespace i2p
 
 	void Transports::Start ()
 	{
+		m_IsRunning = true;
 		m_Thread = new std::thread (std::bind (&Transports::Run, this));
 		// create acceptors
 		auto addresses = context.GetRouterInfo ().GetAddresses ();
@@ -48,6 +49,7 @@ namespace i2p
 		m_NTCPSessions.clear ();
 		delete m_NTCPAcceptor;
 
+		m_IsRunning = false;
 		m_Service.stop ();
 		if (m_Thread)
 		{	
@@ -59,13 +61,16 @@ namespace i2p
 
 	void Transports::Run () 
 	{ 
-		try
-		{	
-			m_Service.run ();
-		}
-		catch (std::exception& ex)
+		while (m_IsRunning)
 		{
-			LogPrint ("Transports: ", ex.what ());
+			try
+			{	
+				m_Service.run ();
+			}
+			catch (std::exception& ex)
+			{
+				LogPrint ("Transports: ", ex.what ());
+			}	
 		}	
 	}
 		
@@ -120,25 +125,28 @@ namespace i2p
 			// we send it to ourself
 			i2p::HandleI2NPMessage (msg);
 		else
-		{	
-			auto session = FindNTCPSession (ident);
-			if (!session)
-			{
-				RouterInfo * r = netdb.FindRouter (ident);
-				if (r)
+			m_Service.post (boost::bind (&Transports::PostMessage, this, ident, msg));                             
+	}	
+
+	void Transports::PostMessage (const i2p::data::IdentHash& ident, i2p::I2NPMessage * msg)
+	{
+		auto session = FindNTCPSession (ident);
+		if (!session)
+		{
+			RouterInfo * r = netdb.FindRouter (ident);
+			if (r)
+			{	
+				auto address = r->GetNTCPAddress ();
+				if (address)
 				{	
-					auto address = r->GetNTCPAddress ();
-					if (address)
-					{	
-						session = new i2p::ntcp::NTCPClient (m_Service, address->host.c_str (), address->port, *r);
-						AddNTCPSession (session);
-					}	
-				}
-			}	
-			if (session)
-				session->SendI2NPMessage (msg);
-			else
-				LogPrint ("Session not found"); 
+					session = new i2p::ntcp::NTCPClient (m_Service, address->host.c_str (), address->port, *r);
+					AddNTCPSession (session);
+				}	
+			}
 		}	
+		if (session)
+			session->SendI2NPMessage (msg);
+		else
+			LogPrint ("Session not found"); 
 	}	
 }
diff --git a/Transports.h b/Transports.h
index ea5453cc..d6b08227 100644
--- a/Transports.h
+++ b/Transports.h
@@ -36,9 +36,11 @@ namespace i2p
 
 			void Run ();
 			void HandleAccept (i2p::ntcp::NTCPServerConnection * conn, const boost::system::error_code& error);
+			void PostMessage (const i2p::data::IdentHash& ident, i2p::I2NPMessage * msg);
 			
 		private:
 
+			bool m_IsRunning;
 			std::thread * m_Thread;	
 			boost::asio::io_service m_Service;
 			boost::asio::io_service::work m_Work;
diff --git a/Tunnel.cpp b/Tunnel.cpp
index f45ba1d4..eebb85fa 100644
--- a/Tunnel.cpp
+++ b/Tunnel.cpp
@@ -219,7 +219,17 @@ namespace tunnel
 	
 	OutboundTunnel * Tunnels::GetNextOutboundTunnel ()
 	{
-		OutboundTunnel * tunnel  = nullptr; 
+		CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
+		uint32_t ind = rnd.GenerateWord32 (0, m_OutboundTunnels.size () - 1), i = 0;
+		for (auto it: m_OutboundTunnels)
+		{	
+			if (i >= ind) return it;
+			else i++;
+		}	
+		return nullptr;
+		
+		// TODO: implement it base on profiling
+		/*OutboundTunnel * tunnel  = nullptr; 
 		size_t minSent = 0;
 		for (auto it : m_OutboundTunnels)
 			if (!tunnel || it->GetNumSentBytes () < minSent)
@@ -227,7 +237,7 @@ namespace tunnel
 				tunnel = it;
 				minSent = it->GetNumSentBytes ();
 			}		
-		return tunnel;
+		return tunnel;*/
 	}	
 	
 	void Tunnels::AddTransitTunnel (TransitTunnel * tunnel)
@@ -353,13 +363,13 @@ namespace tunnel
 			}	
 			else
 			{
-				OutboundTunnel * outboundTunnel =  GetNextOutboundTunnel ();
+				//OutboundTunnel * outboundTunnel =  GetNextOutboundTunnel ();
 				LogPrint ("Creating two hops outbound tunnel...");
 				CreateTunnel<OutboundTunnel> (
 				  	new TunnelConfig (i2p::data::netdb.GetRandomNTCPRouter (),
 						i2p::data::netdb.GetRandomNTCPRouter (),
-			     		inboundTunnel->GetTunnelConfig ()),
-				        	outboundTunnel);
+			     		inboundTunnel->GetTunnelConfig ())/*,
+				        	outboundTunnel*/);
 			}	
 		}
 	}
@@ -395,7 +405,7 @@ namespace tunnel
 			}
 			else
 			{
-				OutboundTunnel * outboundTunnel =  GetNextOutboundTunnel ();
+				OutboundTunnel * outboundTunnel = GetNextOutboundTunnel ();
 				LogPrint ("Creating two hops inbound tunnel...");
 				CreateTunnel<InboundTunnel> (
 					new TunnelConfig (i2p::data::netdb.GetRandomNTCPRouter (),

From 24ad2550539d1821e3fdfd5bb64a405d9e03f873 Mon Sep 17 00:00:00 2001
From: orignal <romakoshelkin@yandex.ru>
Date: Mon, 30 Dec 2013 20:46:33 -0500
Subject: [PATCH 04/16] Create and send local LeaseSet

---
 Garlic.cpp    | 78 +++++++++++++++++++++++++++++++++------------------
 Garlic.h      | 10 ++++---
 Streaming.cpp | 75 ++++++++++++++++++++++++++++++++++++++++++++-----
 Streaming.h   | 16 +++++++++--
 4 files changed, 137 insertions(+), 42 deletions(-)

diff --git a/Garlic.cpp b/Garlic.cpp
index 50da90a3..7a450c2c 100644
--- a/Garlic.cpp
+++ b/Garlic.cpp
@@ -29,7 +29,7 @@ namespace garlic
 		delete[] m_SessionTags;
 	}
 		
-	I2NPMessage * GarlicRoutingSession::WrapSingleMessage (I2NPMessage * msg)
+	I2NPMessage * GarlicRoutingSession::WrapSingleMessage (I2NPMessage * msg, I2NPMessage * leaseSet)
 	{
 		I2NPMessage * m = NewI2NPMessage ();
 		size_t len = 0;
@@ -46,7 +46,7 @@ namespace garlic
 			buf += 514;
 			// AES block
 			m_Encryption.SetKeyWithIV (m_SessionKey, 32, iv);
-			len += 514 + CreateAESBlock (buf, msg);	
+			len += 514 + CreateAESBlock (buf, msg, leaseSet);	
 		}
 		else // existing session
 		{	
@@ -57,7 +57,7 @@ namespace garlic
 			CryptoPP::SHA256().CalculateDigest(iv, m_SessionTags + m_NextTag*32, 32);
 			m_Encryption.SetKeyWithIV (m_SessionKey, 32, iv);
 			// AES block
-			len += 32 + CreateAESBlock (buf, msg);
+			len += 32 + CreateAESBlock (buf, msg, leaseSet);
 		}	
 		m_NextTag++;
 		*(uint32_t *)(m->GetPayload ()) = htobe32 (len);
@@ -67,7 +67,7 @@ namespace garlic
 		return m;
 	}	
 
-	size_t GarlicRoutingSession::CreateAESBlock (uint8_t * buf, I2NPMessage * msg)
+	size_t GarlicRoutingSession::CreateAESBlock (uint8_t * buf, I2NPMessage * msg, I2NPMessage * leaseSet)
 	{
 		size_t blockSize = 0;
 		*(uint16_t *)buf = htobe16 (m_NumTags); // tag count
@@ -80,7 +80,7 @@ namespace garlic
 		blockSize += 32;
 		buf[blockSize] = 0; // flag
 		blockSize++;
-		size_t len = CreateGarlicPayload (buf + blockSize, msg);
+		size_t len = CreateGarlicPayload (buf + blockSize, msg, leaseSet);
 		*payloadSize = htobe32 (len);
 		CryptoPP::SHA256().CalculateDigest(payloadHash, buf + blockSize, len);
 		blockSize += len;
@@ -91,32 +91,25 @@ namespace garlic
 		return blockSize;
 	}	
 
-	size_t GarlicRoutingSession::CreateGarlicPayload (uint8_t * payload, I2NPMessage * msg)
+	size_t GarlicRoutingSession::CreateGarlicPayload (uint8_t * payload, I2NPMessage * msg, I2NPMessage * leaseSet)
 	{
 		uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 5000; // 5 sec
 		size_t size = 0;
-		payload[size] = 1; // 1 clove
+		uint8_t * numCloves = payload + size;
+		*numCloves = 0;
 		size++;
-		if (m_Destination->IsDestination ())
-		{
-			payload[size] = eGarlicDeliveryTypeDestination << 5;//  delivery instructions flag destination
-			size++;
-			memcpy (payload + size, m_Destination->GetIdentHash (), 32);
-			size += 32;
-		}	
-		else	
+
+		if (leaseSet) // first clove is our leaseSet if presented
 		{	
-			payload[size] = 0;//  delivery instructions flag local
-			size++;
-		}
-		memcpy (payload + size, msg->GetBuffer (), msg->GetLength ());
-		size += msg->GetLength ();
-		*(uint32_t *)(payload + size) = htobe32 (m_Rnd.GenerateWord32 ()); // CloveID
-		size += 4;
-		*(uint64_t *)(payload + size) = htobe64 (ts); // Expiration of clove
-		size += 8;
-		memset (payload + size, 0, 3); // certificate of clove
-		size += 3;
+			size += CreateGarlicClove (payload + size, leaseSet, false);
+			(*numCloves)++;
+		}	
+		if (msg) // next clove message ifself if presented
+		{	
+			size += CreateGarlicClove (payload + size, msg, true);
+			(*numCloves)++;
+		}	
+		
 		memset (payload + size, 0, 3); // certificate of message
 		size += 3;
 		*(uint32_t *)(payload + size) = htobe32 (m_Rnd.GenerateWord32 ()); // MessageID
@@ -126,6 +119,34 @@ namespace garlic
 		return size;
 	}	
 
+	size_t GarlicRoutingSession::CreateGarlicClove (uint8_t * buf, I2NPMessage * msg, bool isDestination)
+	{
+		uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 5000; // 5 sec
+		size_t size = 0;
+		if (isDestination)
+		{
+			buf[size] = eGarlicDeliveryTypeDestination << 5;//  delivery instructions flag destination
+			size++;
+			memcpy (buf + size, m_Destination->GetIdentHash (), 32);
+			size += 32;
+		}	
+		else	
+		{	
+			buf[size] = 0;//  delivery instructions flag local
+			size++;
+		}
+		
+		memcpy (buf + size, msg->GetBuffer (), msg->GetLength ());
+		size += msg->GetLength ();
+		*(uint32_t *)(buf + size) = htobe32 (m_Rnd.GenerateWord32 ()); // CloveID
+		size += 4;
+		*(uint64_t *)(buf + size) = htobe64 (ts); // Expiration of clove
+		size += 8;
+		memset (buf + size, 0, 3); // certificate of clove
+		size += 3;
+		return size;
+	}	
+		
 	GarlicRouting routing;	
 	GarlicRouting::GarlicRouting ()
 	{
@@ -138,7 +159,8 @@ namespace garlic
 		m_Sessions.clear ();
 	}	
 
-	I2NPMessage * GarlicRouting::WrapSingleMessage (const i2p::data::RoutingDestination * destination, I2NPMessage * msg)
+	I2NPMessage * GarlicRouting::WrapSingleMessage (const i2p::data::RoutingDestination * destination, 
+		I2NPMessage * msg, I2NPMessage * leaseSet)
 	{
 		if (!destination) return nullptr;
 		auto it = m_Sessions.find (destination->GetIdentHash ());
@@ -151,7 +173,7 @@ namespace garlic
 			m_Sessions[destination->GetIdentHash ()] = session;
 		}	
 
-		I2NPMessage * ret = session->WrapSingleMessage (msg);
+		I2NPMessage * ret = session->WrapSingleMessage (msg, leaseSet);
 		if (session->GetNumRemainingSessionTags () <= 0)
 		{
 			m_Sessions.erase (destination->GetIdentHash ());
diff --git a/Garlic.h b/Garlic.h
index cdc251f6..db1f008d 100644
--- a/Garlic.h
+++ b/Garlic.h
@@ -40,13 +40,14 @@ namespace garlic
 
 			GarlicRoutingSession (const i2p::data::RoutingDestination * destination, int numTags);
 			~GarlicRoutingSession ();
-			I2NPMessage * WrapSingleMessage (I2NPMessage * msg);
+			I2NPMessage * WrapSingleMessage (I2NPMessage * msg, I2NPMessage * leaseSet);
 			int GetNumRemainingSessionTags () const { return m_NumTags - m_NextTag; };
 
 		private:
 
-			size_t CreateAESBlock (uint8_t * buf, I2NPMessage * msg);
-			size_t CreateGarlicPayload (uint8_t * payload, I2NPMessage * msg);
+			size_t CreateAESBlock (uint8_t * buf, I2NPMessage * msg, I2NPMessage * leaseSet);
+			size_t CreateGarlicPayload (uint8_t * payload, I2NPMessage * msg, I2NPMessage * leaseSet);
+			size_t CreateGarlicClove (uint8_t * buf, I2NPMessage * msg, bool isDestination);
 			
 		private:
 
@@ -68,7 +69,8 @@ namespace garlic
 
 			void HandleGarlicMessage (uint8_t * buf, size_t len);
 			
-			I2NPMessage * WrapSingleMessage (const i2p::data::RoutingDestination * destination, I2NPMessage * msg);
+			I2NPMessage * WrapSingleMessage (const i2p::data::RoutingDestination * destination, 
+				I2NPMessage * msg, I2NPMessage * leaseSet = nullptr);
 
 		private:
 
diff --git a/Streaming.cpp b/Streaming.cpp
index 7a8c18ef..84bd9a28 100644
--- a/Streaming.cpp
+++ b/Streaming.cpp
@@ -1,17 +1,21 @@
 #include <endian.h>
 #include <string>
 #include <cryptopp/gzip.h>
+#include <cryptopp/dsa.h>
 #include "Log.h"
 #include "RouterInfo.h"
 #include "RouterContext.h"
+#include "Tunnel.h"
+#include "Timestamp.h"
+#include "CryptoConst.h"
 #include "Streaming.h"
 
 namespace i2p
 {
 namespace stream
 {
-	Stream::Stream (const i2p::data::IdentHash& destination):
-		m_SendStreamID (0)
+	Stream::Stream (StreamingDestination * local, const i2p::data::IdentHash& remote):
+		m_SendStreamID (0), m_LocalDestination (local)
 	{
 		m_RecvStreamID = i2p::context.GetRandomNumberGenerator ().GenerateWord32 ();
 	}	
@@ -57,8 +61,16 @@ namespace stream
 		LogPrint ("Payload: ", str);
 	}	
 		
-	StreamingDestination m_SharedLocalDestination;	
-	
+	StreamingDestination * sharedLocalDestination = nullptr;	
+
+	StreamingDestination::StreamingDestination ()
+	{		
+		// TODO: read from file later
+		m_Keys = i2p::data::CreateRandomKeys ();
+		m_Identity = m_Keys;
+		m_IdentHash = i2p::data::CalculateIdentHash (m_Identity);
+	}
+		
 	void StreamingDestination::HandleNextPacket (const uint8_t * buf, size_t len)
 	{
 		uint32_t sendStreamID = *(uint32_t *)(buf);
@@ -83,11 +95,59 @@ namespace stream
 				return nullptr;
 			}	
 		}	*/
-		Stream * s = new Stream (destination);
+		Stream * s = new Stream (this, destination);
 		m_Streams[s->GetRecvStreamID ()] = s;
 		return s;
 	}	
-	
+
+	I2NPMessage * StreamingDestination::CreateLeaseSet () const
+	{
+		I2NPMessage * m = NewI2NPMessage ();
+		I2NPDatabaseStoreMsg * msg = (I2NPDatabaseStoreMsg *)m->GetPayload ();
+		memcpy (msg->key, (const uint8_t *)m_IdentHash, 32);
+		msg->type = 1; // LeaseSet
+		msg->replyToken = 0;
+		
+		uint8_t * buf = m->GetPayload () + sizeof (I2NPDatabaseStoreMsg);
+		size_t size = 0;
+		memcpy (buf + size, &m_Identity, sizeof (m_Identity));
+		size += sizeof (m_Identity); // destination
+		memcpy (buf + size, i2p::context.GetLeaseSetPublicKey (), 256);
+		size += 256; // encryption key
+		memset (buf + size, 0, 128);
+		size += 128; // signing key
+		auto tunnel = i2p::tunnel::tunnels.GetNextInboundTunnel ();
+		if (tunnel)
+		{	
+			buf[size] = 1; // 1 lease
+			size++; // num
+			memcpy (buf + size, (const uint8_t *)tunnel->GetNextIdentHash (), 32);
+			size += 32; // tunnel_gw
+			*(uint32_t *)(buf + size) = htobe32 (tunnel->GetNextTunnelID ());
+			size += 4; // tunnel_id
+			uint64_t ts = tunnel->GetCreationTime () + i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT;
+			ts *= 1000; // in milliseconds
+			*(uint64_t *)(buf + size) = htobe64 (ts);
+			size += 8; // end_date
+		}	
+		else
+		{
+			buf[size] = 0; // zero leases
+			size++; // num
+		}	
+
+		CryptoPP::DSA::PrivateKey signingPrivateKey;
+		signingPrivateKey.Initialize (i2p::crypto::dsap, i2p::crypto::dsaq, i2p::crypto::dsag, 
+			CryptoPP::Integer (m_Keys.signingPrivateKey, 20));
+		CryptoPP::DSA::Signer signer (signingPrivateKey);
+		signer.SignMessage (i2p::context.GetRandomNumberGenerator (), buf, size, buf+ size);
+		size += 40; // signature
+
+		m->len += size + sizeof (I2NPDatabaseStoreMsg);
+		FillI2NPMessageHeader (m, eI2NPDatabaseStore);
+		return m;
+	}	
+		
 	void HandleDataMessage (i2p::data::IdentHash * destination, const uint8_t * buf, size_t len)
 	{
 		uint32_t length = be32toh (*(uint32_t *)buf);
@@ -104,7 +164,8 @@ namespace stream
 			decompressor.Get (uncompressed, uncompressedSize);
 			// then forward to streaming engine
 			// TODO: we have onle one destination, might be more
-			m_SharedLocalDestination.HandleNextPacket (uncompressed, uncompressedSize);
+			if (sharedLocalDestination)
+				sharedLocalDestination->HandleNextPacket (uncompressed, uncompressedSize);
 		}	
 		else
 			LogPrint ("Data: protocol ", buf[9], " is not supported");
diff --git a/Streaming.h b/Streaming.h
index c75f0b13..6db5ecd0 100644
--- a/Streaming.h
+++ b/Streaming.h
@@ -3,7 +3,7 @@
 
 #include <inttypes.h>
 #include <map>
-#include "LeaseSet.h"
+#include "Identity.h"
 #include "I2NPProtocol.h"
 
 namespace i2p
@@ -22,11 +22,12 @@ namespace stream
 	const uint16_t PACKET_FLAG_ECHO = 0x0200;
 	const uint16_t PACKET_FLAG_NO_ACK = 0x0400;
 
+	class StreamingDestination;
 	class Stream
 	{	
 		public:
 
-			Stream (const i2p::data::IdentHash& destination);
+			Stream (StreamingDestination * local, const i2p::data::IdentHash& remote);
 			uint32_t GetSendStreamID () const { return m_SendStreamID; };
 			uint32_t GetRecvStreamID () const { return m_RecvStreamID; };
 
@@ -35,18 +36,27 @@ namespace stream
 		private:
 
 			uint32_t m_SendStreamID, m_RecvStreamID;
+			StreamingDestination * m_LocalDestination;
 	};
 	
 	class StreamingDestination
 	{
 		public:
 
+			StreamingDestination ();
+
+			const i2p::data::Keys GetKeys () const { return m_Keys; };
+			I2NPMessage * CreateLeaseSet () const;
+			
 			Stream * CreateNewStream (const i2p::data::IdentHash& destination);
 			void HandleNextPacket (const uint8_t * buf, size_t len);
-
+			
 		private:
 
 			std::map<uint32_t, Stream *> m_Streams;
+			i2p::data::Keys m_Keys;
+			i2p::data::Identity m_Identity;
+			i2p::data::IdentHash m_IdentHash;
 	};	
 	
 	// assuming data is I2CP message

From 1130287d28798ec7b0c47ff77bbecd6eee25f4d0 Mon Sep 17 00:00:00 2001
From: orignal <romakoshelkin@yandex.ru>
Date: Wed, 1 Jan 2014 18:19:03 -0500
Subject: [PATCH 05/16] send streaming protocol data

---
 Garlic.cpp    |   5 ++-
 LeaseSet.h    |   5 ++-
 Streaming.cpp | 102 ++++++++++++++++++++++++++++++++++++++------------
 Streaming.h   |  26 ++++++++++---
 4 files changed, 107 insertions(+), 31 deletions(-)

diff --git a/Garlic.cpp b/Garlic.cpp
index 7a450c2c..7d0e703f 100644
--- a/Garlic.cpp
+++ b/Garlic.cpp
@@ -63,7 +63,10 @@ namespace garlic
 		*(uint32_t *)(m->GetPayload ()) = htobe32 (len);
 		m->len += len + 4;
 		FillI2NPMessageHeader (m, eI2NPGarlic);
-		DeleteI2NPMessage (msg);
+		if (msg)
+			DeleteI2NPMessage (msg);
+		if (leaseSet)
+			DeleteI2NPMessage (leaseSet);
 		return m;
 	}	
 
diff --git a/LeaseSet.h b/LeaseSet.h
index 55a2e4b1..5bb75741 100644
--- a/LeaseSet.h
+++ b/LeaseSet.h
@@ -3,7 +3,7 @@
 
 #include <inttypes.h>
 #include <string.h>
-#include <list>
+#include <vector>
 #include "Identity.h"
 
 namespace i2p
@@ -31,12 +31,13 @@ namespace data
 			// implements RoutingDestination
 			const Identity& GetIdentity () const { return m_Identity; };
 			const IdentHash& GetIdentHash () const { return m_IdentHash; };
+			const std::vector<Lease>& GetLeases () const { return m_Leases; };
 			const uint8_t * GetEncryptionPublicKey () const { return m_EncryptionKey; };
 			bool IsDestination () const { return true; };
 			
 		private:
 
-			std::list<Lease> m_Leases;
+			std::vector<Lease> m_Leases;
 			Identity m_Identity;
 			IdentHash m_IdentHash;
 			uint8_t m_EncryptionKey[256];
diff --git a/Streaming.cpp b/Streaming.cpp
index 84bd9a28..e0d64a77 100644
--- a/Streaming.cpp
+++ b/Streaming.cpp
@@ -1,21 +1,21 @@
 #include <endian.h>
 #include <string>
 #include <cryptopp/gzip.h>
-#include <cryptopp/dsa.h>
 #include "Log.h"
 #include "RouterInfo.h"
 #include "RouterContext.h"
 #include "Tunnel.h"
 #include "Timestamp.h"
 #include "CryptoConst.h"
+#include "Garlic.h"
 #include "Streaming.h"
 
 namespace i2p
 {
 namespace stream
 {
-	Stream::Stream (StreamingDestination * local, const i2p::data::IdentHash& remote):
-		m_SendStreamID (0), m_LocalDestination (local)
+	Stream::Stream (StreamingDestination * local, const i2p::data::LeaseSet * remote):
+		m_SendStreamID (0), m_SequenceNumber (0), m_LocalDestination (local), m_RemoteLeaseSet (remote)
 	{
 		m_RecvStreamID = i2p::context.GetRandomNumberGenerator ().GenerateWord32 ();
 	}	
@@ -60,6 +60,49 @@ namespace stream
 		std::string str((const char *)buf, end-buf);
 		LogPrint ("Payload: ", str);
 	}	
+
+	size_t Stream::Send (uint8_t * buf, size_t len, int timeout)
+	{
+		uint8_t packet[STREAMING_MTU];
+		size_t size = 0;
+		*(uint32_t *)(packet + size) = htobe32 (m_SendStreamID);
+		size += 4; // sendStreamID
+		*(uint32_t *)(packet + size) = htobe32 (m_RecvStreamID);
+		size += 4; // receiveStreamID
+		*(uint32_t *)(packet + size) = htobe32 (m_SequenceNumber);
+		size += 4; // sequenceNum
+		*(uint32_t *)(packet + size) = 0; // TODO
+		size += 4; // ack Through
+		packet[size] = 0; 
+		size++; // NACK count
+		size++; // resend delay
+		// TODO: for initial packet only, following packets have different falgs
+		*(uint16_t *)(packet + size) = htobe16 (PACKET_FLAG_SYNCHRONIZE | 
+			PACKET_FLAG_FROM_INCLUDED | PACKET_FLAG_SIGNATURE_INCLUDED | PACKET_FLAG_NO_ACK);
+		size += 2; // flags
+		*(uint16_t *)(packet + size) = htobe16 (sizeof (i2p::data::Identity) + 40); // identity + signature
+		size += 2; // options size
+		memcpy (packet + size, &m_LocalDestination->GetIdentity (), sizeof (i2p::data::Identity)); 
+		size += sizeof (i2p::data::Identity); // from
+		uint8_t * signature = packet + size; // set it later
+		memset (signature, 0, 40); // zeroes for now
+		size += 40; // signature
+		memcpy (packet + size, buf, len); 
+		size += len; // payload
+		m_LocalDestination->Sign (packet, size, signature);
+		I2NPMessage * msg = i2p::garlic::routing.WrapSingleMessage (m_RemoteLeaseSet, 
+			CreateDataMessage (this, packet, size), m_LocalDestination->CreateLeaseSet ()); 
+
+		auto outbound = i2p::tunnel::tunnels.GetNextOutboundTunnel ();
+		if (outbound)
+		{
+			auto lease = m_RemoteLeaseSet->GetLeases ()[0]; // TODO:
+			outbound->SendTunnelDataMsg (lease.tunnelGateway, lease.tunnelID, msg);
+		}	
+		else
+			DeleteI2NPMessage (msg);
+		return len;
+	}	
 		
 	StreamingDestination * sharedLocalDestination = nullptr;	
 
@@ -69,6 +112,8 @@ namespace stream
 		m_Keys = i2p::data::CreateRandomKeys ();
 		m_Identity = m_Keys;
 		m_IdentHash = i2p::data::CalculateIdentHash (m_Identity);
+		m_SigningPrivateKey.Initialize (i2p::crypto::dsap, i2p::crypto::dsaq, i2p::crypto::dsag, 
+			CryptoPP::Integer (m_Keys.signingPrivateKey, 20));
 	}
 		
 	void StreamingDestination::HandleNextPacket (const uint8_t * buf, size_t len)
@@ -81,25 +126,22 @@ namespace stream
 			LogPrint ("Unknown stream ", sendStreamID);
 	}	
 
-	Stream * StreamingDestination::CreateNewStream (const i2p::data::IdentHash& destination)
+	Stream * StreamingDestination::CreateNewStream (const i2p::data::LeaseSet * remote)
 	{
-		/*i2p::data::LeaseSet * leaseSet = i2p::data::netdb.FindLeaseSet (destination);
-		if (!leaseSet)
-		{
-			i2p::data::netdb.RequestDestination (destination);
-			sleep (5); // wait for 5 seconds
-			leaseSet = i2p::data::netdb.FindLeaseSet (destination);
-			if (!leaseSet)
-			{
-				LogPrint ("Couldn't find LeaseSet");
-				return nullptr;
-			}	
-		}	*/
-		Stream * s = new Stream (this, destination);
+		Stream * s = new Stream (this, remote);
 		m_Streams[s->GetRecvStreamID ()] = s;
 		return s;
 	}	
 
+	void StreamingDestination::DeleteStream (Stream * stream)
+	{
+		if (stream)
+		{
+			m_Streams.erase (stream->GetRecvStreamID ());
+			delete stream;
+		}	
+	}	
+		
 	I2NPMessage * StreamingDestination::CreateLeaseSet () const
 	{
 		I2NPMessage * m = NewI2NPMessage ();
@@ -135,18 +177,32 @@ namespace stream
 			buf[size] = 0; // zero leases
 			size++; // num
 		}	
-
-		CryptoPP::DSA::PrivateKey signingPrivateKey;
-		signingPrivateKey.Initialize (i2p::crypto::dsap, i2p::crypto::dsaq, i2p::crypto::dsag, 
-			CryptoPP::Integer (m_Keys.signingPrivateKey, 20));
-		CryptoPP::DSA::Signer signer (signingPrivateKey);
-		signer.SignMessage (i2p::context.GetRandomNumberGenerator (), buf, size, buf+ size);
+		Sign (buf, size, buf+ size);
 		size += 40; // signature
 
 		m->len += size + sizeof (I2NPDatabaseStoreMsg);
 		FillI2NPMessageHeader (m, eI2NPDatabaseStore);
 		return m;
 	}	
+
+	void StreamingDestination::Sign (uint8_t * buf, int len, uint8_t * signature) const
+	{
+		CryptoPP::DSA::Signer signer (m_SigningPrivateKey);
+		signer.SignMessage (i2p::context.GetRandomNumberGenerator (), buf, len, signature);
+	}
+		
+	Stream * CreateStream (const i2p::data::LeaseSet * remote)
+	{
+		if (!sharedLocalDestination)
+			sharedLocalDestination = new StreamingDestination ();
+		return sharedLocalDestination->CreateNewStream (remote);
+	}
+		
+	void CloseStream (Stream * stream)
+	{
+		if (sharedLocalDestination)
+			sharedLocalDestination->DeleteStream (stream);
+	}	
 		
 	void HandleDataMessage (i2p::data::IdentHash * destination, const uint8_t * buf, size_t len)
 	{
diff --git a/Streaming.h b/Streaming.h
index 6db5ecd0..c3c97bd8 100644
--- a/Streaming.h
+++ b/Streaming.h
@@ -3,7 +3,9 @@
 
 #include <inttypes.h>
 #include <map>
+#include <cryptopp/dsa.h>
 #include "Identity.h"
+#include "LeaseSet.h"
 #include "I2NPProtocol.h"
 
 namespace i2p
@@ -22,21 +24,27 @@ namespace stream
 	const uint16_t PACKET_FLAG_ECHO = 0x0200;
 	const uint16_t PACKET_FLAG_NO_ACK = 0x0400;
 
+	const size_t STREAMING_MTU = 1730; 
+		
 	class StreamingDestination;
 	class Stream
 	{	
 		public:
 
-			Stream (StreamingDestination * local, const i2p::data::IdentHash& remote);
+			Stream (StreamingDestination * local, const i2p::data::LeaseSet * remote);
 			uint32_t GetSendStreamID () const { return m_SendStreamID; };
 			uint32_t GetRecvStreamID () const { return m_RecvStreamID; };
-
+			const i2p::data::LeaseSet * GetRemoteLeaseSet () const { return m_RemoteLeaseSet; };
+			bool IsEstablished () const { return !m_SendStreamID; };
+			
 			void HandleNextPacket (const uint8_t * buf, size_t len);
+			size_t Send (uint8_t * buf, size_t len, int timeout); // timeout in seconds
 			
 		private:
 
-			uint32_t m_SendStreamID, m_RecvStreamID;
+			uint32_t m_SendStreamID, m_RecvStreamID, m_SequenceNumber;
 			StreamingDestination * m_LocalDestination;
+			const i2p::data::LeaseSet * m_RemoteLeaseSet;
 	};
 	
 	class StreamingDestination
@@ -45,10 +53,13 @@ namespace stream
 
 			StreamingDestination ();
 
-			const i2p::data::Keys GetKeys () const { return m_Keys; };
+			const i2p::data::Keys& GetKeys () const { return m_Keys; };
+			const i2p::data::Identity& GetIdentity () const { return m_Identity; }; 
 			I2NPMessage * CreateLeaseSet () const;
+			void Sign (uint8_t * buf, int len, uint8_t * signature) const;
 			
-			Stream * CreateNewStream (const i2p::data::IdentHash& destination);
+			Stream * CreateNewStream (const i2p::data::LeaseSet * remote);
+			void DeleteStream (Stream * stream);
 			void HandleNextPacket (const uint8_t * buf, size_t len);
 			
 		private:
@@ -57,7 +68,12 @@ namespace stream
 			i2p::data::Keys m_Keys;
 			i2p::data::Identity m_Identity;
 			i2p::data::IdentHash m_IdentHash;
+
+			CryptoPP::DSA::PrivateKey m_SigningPrivateKey;
 	};	
+
+	Stream * CreateStream (const i2p::data::LeaseSet * remote);
+	void CloseStream (Stream * stream);
 	
 	// assuming data is I2CP message
 	void HandleDataMessage (i2p::data::IdentHash * destination, const uint8_t * buf, size_t len);

From 4f9a977022ae9dd09234600d5c4d196dde1a44a0 Mon Sep 17 00:00:00 2001
From: orignal <romakoshelkin@yandex.ru>
Date: Thu, 2 Jan 2014 21:22:48 -0500
Subject: [PATCH 06/16] wrap LeaseSet lookups into garlic

---
 Garlic.cpp |  2 +-
 NetDb.cpp  | 26 +++++++++++++++++---------
 NetDb.h    |  6 +++---
 3 files changed, 21 insertions(+), 13 deletions(-)

diff --git a/Garlic.cpp b/Garlic.cpp
index 7d0e703f..f9b88126 100644
--- a/Garlic.cpp
+++ b/Garlic.cpp
@@ -109,7 +109,7 @@ namespace garlic
 		}	
 		if (msg) // next clove message ifself if presented
 		{	
-			size += CreateGarlicClove (payload + size, msg, true);
+			size += CreateGarlicClove (payload + size, msg, m_Destination->IsDestination ());
 			(*numCloves)++;
 		}	
 		
diff --git a/NetDb.cpp b/NetDb.cpp
index 3f05dd7d..77e2ce72 100644
--- a/NetDb.cpp
+++ b/NetDb.cpp
@@ -8,6 +8,7 @@
 #include "I2NPProtocol.h"
 #include "Tunnel.h"
 #include "RouterContext.h"
+#include "Garlic.h"
 #include "NetDb.h"
 
 namespace i2p
@@ -197,15 +198,25 @@ namespace data
 			LogPrint (deletedCount," routers deleted");
 	}
 
-	void NetDb::RequestDestination (const char * b32, const uint8_t * router)
+	void NetDb::RequestDestination (const char * b32)
 	{
 		uint8_t destination[32];
 		Base32ToByteStream (b32, strlen(b32), destination, 32);
-		RequestDestination (destination, router);
+		RequestDestination (destination, true);
+	}	
+
+	void NetDb::RequestDestination (const IdentHash& destination, bool isLeaseSet)
+	{
+		auto floodfill= GetRandomNTCPRouter (true);
+		if (floodfill)
+			RequestDestination (destination, floodfill, isLeaseSet);
+		else
+			LogPrint ("No floodfill routers found");
 	}	
 	
-	void NetDb::RequestDestination (const uint8_t * destination, const uint8_t * router)
+	void NetDb::RequestDestination (const IdentHash& destination, const RouterInfo * floodfill, bool isLeaseSet)
 	{
+		if (!floodfill) return;
 		i2p::tunnel::OutboundTunnel * outbound = i2p::tunnel::tunnels.GetNextOutboundTunnel ();
 		if (outbound)
 		{
@@ -214,7 +225,9 @@ namespace data
 			{
 				I2NPMessage * msg = i2p::CreateDatabaseLookupMsg (destination, inbound->GetNextIdentHash (), 
 					inbound->GetNextTunnelID ());
-				outbound->SendTunnelDataMsg (router, 0, msg);
+				if (isLeaseSet) // wrap lookup message into garlic
+					msg = i2p::garlic::routing.WrapSingleMessage (floodfill, msg);
+				outbound->SendTunnelDataMsg (floodfill->GetIdentHash (), 0, msg);
 			}	
 			else
 				LogPrint ("No inbound tunnels found");	
@@ -222,11 +235,6 @@ namespace data
 		else
 			LogPrint ("No outbound tunnels found");
 	}	
-
-	void NetDb::RequestDestination (const IdentHash& destination)
-	{
-		RequestDestination ((const uint8_t *)destination, GetRandomNTCPRouter (true)->GetIdentHash ());
-	}	
 	
 	void NetDb::HandleDatabaseStoreMsg (uint8_t * buf, size_t len)
 	{		
diff --git a/NetDb.h b/NetDb.h
index 3f644c7d..ca63a239 100644
--- a/NetDb.h
+++ b/NetDb.h
@@ -30,9 +30,9 @@ namespace data
 			RouterInfo * FindRouter (const IdentHash& ident) const;
 			LeaseSet * FindLeaseSet (const IdentHash& destination) const;
 			
-			void RequestDestination (const char * b32, const uint8_t * router); // in base32
-			void RequestDestination (const uint8_t * destination, const uint8_t * router);
-			void RequestDestination (const IdentHash& destination);
+			void RequestDestination (const char * b32); // in base32
+			void RequestDestination (const IdentHash& destination, bool isLeaseSet = false);
+			void RequestDestination (const IdentHash& destination, const RouterInfo * floodfill, bool isLeaseSet = false);
 			
 			void HandleDatabaseStoreMsg (uint8_t * buf, size_t len);
 			void HandleDatabaseSearchReplyMsg (I2NPMessage * msg);

From 4f1f08b8057cfda0ac391ef78b38714d738c0436 Mon Sep 17 00:00:00 2001
From: orignal <romakoshelkin@yandex.ru>
Date: Fri, 3 Jan 2014 21:24:20 -0500
Subject: [PATCH 07/16] kademlia

---
 Identity.cpp   | 27 +++++++++++++++++++++++++++
 Identity.h     | 19 +++++++++++++++++++
 NetDb.cpp      | 21 +++++++++++++++++++++
 NetDb.h        |  1 +
 RouterInfo.cpp |  7 +++++++
 RouterInfo.h   |  3 +++
 6 files changed, 78 insertions(+)

diff --git a/Identity.cpp b/Identity.cpp
index b80d1cbf..f57ab97b 100644
--- a/Identity.cpp
+++ b/Identity.cpp
@@ -1,3 +1,5 @@
+#include <time.h>
+#include <stdio.h>
 #include <cryptopp/sha.h>
 #include <cryptopp/osrng.h>
 #include <cryptopp/dh.h>
@@ -43,5 +45,30 @@ namespace data
 		
 		return keys;
 	}	
+
+	RoutingKey CreateRoutingKey (const IdentHash& ident)
+	{
+		uint8_t buf[41]; // ident + yyyymmdd
+		memcpy (buf, (const uint8_t *)ident, 32);
+		time_t t = time (nullptr);
+		struct tm tm;
+		gmtime_r (&t, &tm);
+		sprintf ((char *)(buf + 32),"%4i%2i%2i", tm.tm_year, tm.tm_mon, tm.tm_mday);
+		
+		RoutingKey key;
+		CryptoPP::SHA256().CalculateDigest(key.hash, buf, 40);
+		return key;
+	}	
+	
+	XORMetric operator^(const RoutingKey& key1, const RoutingKey& key2)
+	{
+		// TODO: implementation depends on CPU
+		XORMetric m;
+		((uint64_t *)m.metric)[0] = ((uint64_t *)key1.hash)[0] ^ ((uint64_t *)key2.hash)[0];
+		((uint64_t *)m.metric)[1] = ((uint64_t *)key1.hash)[1] ^ ((uint64_t *)key2.hash)[1];
+		((uint64_t *)m.metric)[2] = ((uint64_t *)key1.hash)[2] ^ ((uint64_t *)key2.hash)[2];
+		((uint64_t *)m.metric)[3] = ((uint64_t *)key1.hash)[3] ^ ((uint64_t *)key2.hash)[3];
+		return m;
+	}	
 }
 }
diff --git a/Identity.h b/Identity.h
index 549e9cea..9e8b62ac 100644
--- a/Identity.h
+++ b/Identity.h
@@ -57,7 +57,26 @@ namespace data
 
 	IdentHash CalculateIdentHash (const Identity& identity);
 	Keys CreateRandomKeys ();
+
+	// kademlia
+	struct RoutingKey
+	{
+		uint8_t hash[32];
+	};	
+
+	struct XORMetric
+	{
+		uint8_t metric[32];
+
+		void SetMin () { memset (metric, 0, 32); };
+		void SetMax () { memset (metric, 0xFF, 32); };
+		bool operator< (const XORMetric& other) const { return memcmp (metric, other.metric, 32) < 0; };
+	};	
+
+	RoutingKey CreateRoutingKey (const IdentHash& ident);
+	XORMetric operator^(const RoutingKey& key1, const RoutingKey& key2); 	
 	
+	// destination for delivery instuctions
 	class RoutingDestination
 	{
 		public:
diff --git a/NetDb.cpp b/NetDb.cpp
index 77e2ce72..89364c1b 100644
--- a/NetDb.cpp
+++ b/NetDb.cpp
@@ -388,5 +388,26 @@ namespace data
 	{
 		if (msg) m_Queue.Put (msg);	
 	}	
+
+	const RouterInfo * NetDb::GetClosestFloodfill (const IdentHash& destination) const
+	{
+		RouterInfo * r = nullptr;
+		XORMetric minMetric;
+		RoutingKey destKey = CreateRoutingKey (destination);
+		minMetric.SetMax ();
+		for (auto it: m_RouterInfos)
+		{	
+			if (it.second->IsFloodfill () &&! it.second->IsUnreachable ())
+			{	
+				XORMetric m = destKey ^ it.second->GetRoutingKey ();
+				if (m < minMetric)
+				{
+					minMetric = m;
+					r = it.second;
+				}
+			}	
+		}	
+		return r;
+	}	
 }
 }
diff --git a/NetDb.h b/NetDb.h
index ca63a239..761d3a09 100644
--- a/NetDb.h
+++ b/NetDb.h
@@ -48,6 +48,7 @@ namespace data
 			void SaveUpdated (const char * directory);
 			void Run (); // exploratory thread
 			void Explore ();
+			const RouterInfo * GetClosestFloodfill (const IdentHash& destination) const;
 			
 		private:
 
diff --git a/RouterInfo.cpp b/RouterInfo.cpp
index 417466c6..45ab4c20 100644
--- a/RouterInfo.cpp
+++ b/RouterInfo.cpp
@@ -35,6 +35,7 @@ namespace data
 		m_RouterIdentity = identity;
 		m_IdentHash = CalculateIdentHash (m_RouterIdentity);
 		UpdateIdentHashBase64 ();
+		UpdateRoutingKey ();
 		m_Timestamp = i2p::util::GetMillisecondsSinceEpoch ();
 	}
 	
@@ -126,6 +127,7 @@ namespace data
 		
 		CryptoPP::SHA256().CalculateDigest(m_IdentHash, (uint8_t *)&m_RouterIdentity, sizeof (m_RouterIdentity));
 		UpdateIdentHashBase64 ();
+		UpdateRoutingKey ();
 	}	
 
 	void RouterInfo::UpdateIdentHashBase64 ()
@@ -135,6 +137,11 @@ namespace data
 		memcpy (m_IdentHashAbbreviation, m_IdentHashBase64, 4);
 		m_IdentHashAbbreviation[4] = 0;
 	}	
+
+	void RouterInfo::UpdateRoutingKey ()
+	{		
+		m_RoutingKey = CreateRoutingKey (m_IdentHash);
+	}
 		
 	void RouterInfo::WriteToStream (std::ostream& s)
 	{
diff --git a/RouterInfo.h b/RouterInfo.h
index 3111fbf3..ceda10cb 100644
--- a/RouterInfo.h
+++ b/RouterInfo.h
@@ -46,6 +46,7 @@ namespace data
 			uint64_t GetTimestamp () const { return m_Timestamp; };
 			const std::vector<Address>& GetAddresses () const { return m_Addresses; };
 			Address * GetNTCPAddress ();
+			const RoutingKey& GetRoutingKey () const { return m_RoutingKey; };
 			
 			void AddNTCPAddress (const char * host, int port);
 			void SetProperty (const char * key, const char * value);
@@ -56,6 +57,7 @@ namespace data
 			bool IsUnreachable () const { return m_IsUnreachable; };
 			
 			void CreateBuffer ();
+			void UpdateRoutingKey ();
 			const char * GetBuffer () const  { return m_Buffer; };
 			int GetBufferLen () const { return m_BufferLen; };
 
@@ -81,6 +83,7 @@ namespace data
 
 			Identity m_RouterIdentity;
 			IdentHash m_IdentHash;
+			RoutingKey m_RoutingKey;
 			char m_IdentHashBase64[48], m_IdentHashAbbreviation[5];
 			char m_Buffer[2048];
 			int m_BufferLen;

From 5997cb80bd17fe0f19dec911d7c3a37357bd5513 Mon Sep 17 00:00:00 2001
From: orignal <romakoshelkin@yandex.ru>
Date: Fri, 3 Jan 2014 22:25:22 -0500
Subject: [PATCH 08/16] differentiate garlic message received from tunnel and
 directly

---
 Garlic.cpp         | 8 +++++---
 Garlic.h           | 2 +-
 I2NPProtocol.cpp   | 8 ++++----
 I2NPProtocol.h     | 4 ++--
 NTCPSession.cpp    | 7 ++++---
 NetDb.cpp          | 2 +-
 Transports.cpp     | 2 +-
 TunnelEndpoint.cpp | 2 +-
 8 files changed, 19 insertions(+), 16 deletions(-)

diff --git a/Garlic.cpp b/Garlic.cpp
index f9b88126..202949bb 100644
--- a/Garlic.cpp
+++ b/Garlic.cpp
@@ -185,7 +185,7 @@ namespace garlic
 		return ret;
 	}	
 
-	void GarlicRouting::HandleGarlicMessage (uint8_t * buf, size_t len)
+	void GarlicRouting::HandleGarlicMessage (uint8_t * buf, size_t len, bool isFromTunnel)
 	{
 		uint32_t length = be32toh (*(uint32_t *)buf);
 		buf += 4;
@@ -203,7 +203,9 @@ namespace garlic
 		{
 			// new session
 			ElGamalBlock elGamal;
-			i2p::crypto::ElGamalDecrypt (i2p::context.GetLeaseSetPrivateKey (), buf, (uint8_t *)&elGamal, true);
+			i2p::crypto::ElGamalDecrypt (
+			   	isFromTunnel ? i2p::context.GetLeaseSetPrivateKey () : i2p::context.GetPrivateKey (), 
+				buf, (uint8_t *)&elGamal, true);
 			memcpy (m_SessionKey, elGamal.sessionKey, 32);
 			uint8_t iv[32]; // IV is first 16 bytes
 			CryptoPP::SHA256().CalculateDigest(iv, elGamal.preIV, 32); 
@@ -252,7 +254,7 @@ namespace garlic
 			{
 				case eGarlicDeliveryTypeLocal:
 					LogPrint ("Garlic type local");
-					i2p::HandleI2NPMessage (buf, len);
+					i2p::HandleI2NPMessage (buf, len, false);
 				break;	
 				case eGarlicDeliveryTypeDestination:
 				{	
diff --git a/Garlic.h b/Garlic.h
index db1f008d..536ed173 100644
--- a/Garlic.h
+++ b/Garlic.h
@@ -67,7 +67,7 @@ namespace garlic
 			GarlicRouting ();
 			~GarlicRouting ();
 
-			void HandleGarlicMessage (uint8_t * buf, size_t len);
+			void HandleGarlicMessage (uint8_t * buf, size_t len, bool isFromTunnel);
 			
 			I2NPMessage * WrapSingleMessage (const i2p::data::RoutingDestination * destination, 
 				I2NPMessage * msg, I2NPMessage * leaseSet = nullptr);
diff --git a/I2NPProtocol.cpp b/I2NPProtocol.cpp
index 58622fb8..76918bcd 100644
--- a/I2NPProtocol.cpp
+++ b/I2NPProtocol.cpp
@@ -370,7 +370,7 @@ namespace i2p
 		return be16toh (header->size) + sizeof (I2NPHeader);
 	}	
 	
-	void HandleI2NPMessage (uint8_t * msg, size_t len)
+	void HandleI2NPMessage (uint8_t * msg, size_t len, bool isFromTunnel)
 	{
 		I2NPHeader * header = (I2NPHeader *)msg;
 		uint32_t msgID = be32toh (header->msgID);	
@@ -382,7 +382,7 @@ namespace i2p
 		{
 			case eI2NPGarlic:
 				LogPrint ("Garlic");
-				i2p::garlic::routing.HandleGarlicMessage (buf, size);
+				i2p::garlic::routing.HandleGarlicMessage (buf, size, isFromTunnel);
 			break;	
 			break;	
 			case eI2NPDeliveryStatus:
@@ -401,7 +401,7 @@ namespace i2p
 		}	
 	}
 
-	void HandleI2NPMessage (I2NPMessage * msg)
+	void HandleI2NPMessage (I2NPMessage * msg, bool isFromTunnel)
 	{
 		if (msg)
 		{	
@@ -424,7 +424,7 @@ namespace i2p
 					i2p::data::netdb.PostI2NPMsg (msg);
 				break;	
 				default:
-					HandleI2NPMessage (msg->GetBuffer (), msg->GetLength ());
+					HandleI2NPMessage (msg->GetBuffer (), msg->GetLength (), isFromTunnel);
 					DeleteI2NPMessage (msg);
 			}	
 		}	
diff --git a/I2NPProtocol.h b/I2NPProtocol.h
index 2b85744c..597c00b0 100644
--- a/I2NPProtocol.h
+++ b/I2NPProtocol.h
@@ -132,8 +132,8 @@ namespace i2p
 	I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessage * msg);
 
 	size_t GetI2NPMessageLength (uint8_t * msg);
-	void HandleI2NPMessage (uint8_t * msg, size_t len);
-	void HandleI2NPMessage (I2NPMessage * msg);
+	void HandleI2NPMessage (uint8_t * msg, size_t len, bool isFromTunnel);
+	void HandleI2NPMessage (I2NPMessage * msg, bool isFromTunnel);
 }	
 
 #endif
diff --git a/NTCPSession.cpp b/NTCPSession.cpp
index 14b74c27..be50e02b 100644
--- a/NTCPSession.cpp
+++ b/NTCPSession.cpp
@@ -384,7 +384,8 @@ namespace ntcp
 				if (m_ReceiveBufferOffset > 0)
 					memcpy (m_ReceiveBuffer, nextBlock, m_ReceiveBufferOffset);
 			}	
-		
+			
+			ScheduleTermination (); // reset termination timer
 			Receive ();
 		}	
 	}	
@@ -423,7 +424,7 @@ namespace ntcp
 		if (m_NextMessageOffset >= m_NextMessage->len + 4) // +checksum
 		{	
 			// we have a complete I2NP message
-			i2p::HandleI2NPMessage (m_NextMessage);	
+			i2p::HandleI2NPMessage (m_NextMessage, false);	
 			m_NextMessage = nullptr;
 		}	
  	}	
@@ -464,7 +465,6 @@ namespace ntcp
 
 		boost::asio::async_write (m_Socket, boost::asio::buffer (sendBuffer, l), boost::asio::transfer_all (),                      
         	boost::bind(&NTCPSession::HandleSent, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred, msg));	
-		ScheduleTermination (); // reset termination timer
 	}
 		
 	void NTCPSession::HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, i2p::I2NPMessage * msg)
@@ -479,6 +479,7 @@ namespace ntcp
 		else
 		{	
 			LogPrint ("Msg sent: ", bytes_transferred);
+			ScheduleTermination (); // reset termination timer
 		}	
 	}
 
diff --git a/NetDb.cpp b/NetDb.cpp
index 89364c1b..264b1631 100644
--- a/NetDb.cpp
+++ b/NetDb.cpp
@@ -71,7 +71,7 @@ namespace data
 						else // WTF?
 						{
 							LogPrint ("NetDb: unexpected message type ", msg->GetHeader ()->typeID);
-							i2p::HandleI2NPMessage (msg);
+							i2p::HandleI2NPMessage (msg, false);
 						}	
 						msg = m_Queue.Get ();
 					}	
diff --git a/Transports.cpp b/Transports.cpp
index 9204cea9..e9953eca 100644
--- a/Transports.cpp
+++ b/Transports.cpp
@@ -123,7 +123,7 @@ namespace i2p
 	{
 		if (ident == i2p::context.GetRouterInfo ().GetIdentHash ())
 			// we send it to ourself
-			i2p::HandleI2NPMessage (msg);
+			i2p::HandleI2NPMessage (msg, false);
 		else
 			m_Service.post (boost::bind (&Transports::PostMessage, this, ident, msg));                             
 	}	
diff --git a/TunnelEndpoint.cpp b/TunnelEndpoint.cpp
index 1d856e4a..68e741a0 100644
--- a/TunnelEndpoint.cpp
+++ b/TunnelEndpoint.cpp
@@ -147,7 +147,7 @@ namespace tunnel
 		switch (msg.deliveryType)
 		{
 			case eDeliveryTypeLocal:
-				i2p::HandleI2NPMessage (msg.data);
+				i2p::HandleI2NPMessage (msg.data, true);
 			break;
 			case eDeliveryTypeTunnel:
 				i2p::transports.SendMessage (msg.hash, i2p::CreateTunnelGatewayMsg (msg.tunnelID, msg.data));

From e1716bc05ea6d025785f83a3aa0f40f336ea5a76 Mon Sep 17 00:00:00 2001
From: orignal <romakoshelkin@yandex.ru>
Date: Fri, 3 Jan 2014 22:56:28 -0500
Subject: [PATCH 09/16] manage transit tunnels

---
 Tunnel.cpp   | 24 ++++++++++++++++++++----
 Tunnel.h     | 14 +++++++-------
 TunnelBase.h | 10 ++++++++++
 3 files changed, 37 insertions(+), 11 deletions(-)

diff --git a/Tunnel.cpp b/Tunnel.cpp
index eebb85fa..0a75649a 100644
--- a/Tunnel.cpp
+++ b/Tunnel.cpp
@@ -13,8 +13,7 @@ namespace i2p
 namespace tunnel
 {		
 	
-	Tunnel::Tunnel (TunnelConfig * config): m_Config (config),
-		m_CreationTime (i2p::util::GetSecondsSinceEpoch ()), m_IsEstablished (false)
+	Tunnel::Tunnel (TunnelConfig * config): m_Config (config), m_IsEstablished (false)
 	{
 	}	
 
@@ -320,6 +319,7 @@ namespace tunnel
 		
 		ManageInboundTunnels ();
 		ManageOutboundTunnels ();
+		ManageTransitTunnels ();
 		
 	/*	if (!m_IsTunnelCreated)
 		{	
@@ -407,13 +407,29 @@ namespace tunnel
 			{
 				OutboundTunnel * outboundTunnel = GetNextOutboundTunnel ();
 				LogPrint ("Creating two hops inbound tunnel...");
+				auto router = outboundTunnel->GetTunnelConfig ()->GetFirstHop ()->router;
 				CreateTunnel<InboundTunnel> (
-					new TunnelConfig (i2p::data::netdb.GetRandomNTCPRouter (),
-				    	outboundTunnel->GetTunnelConfig ()->GetFirstHop ()->router),
+					new TunnelConfig (i2p::data::netdb.GetRandomNTCPRouter (), 
+							router != &i2p::context.GetRouterInfo () ? router : i2p::data::netdb.GetRandomNTCPRouter ()),
 				        	outboundTunnel);
 			}	
 		}
 	}	
+
+	void Tunnels::ManageTransitTunnels ()
+	{
+		uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
+		for (auto it = m_TransitTunnels.begin (); it != m_TransitTunnels.end ();)
+		{
+			if (ts > it->second->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT)
+			{
+				LogPrint ("Transit tunnel ", it->second->GetTunnelID (), " expired");
+				it = m_TransitTunnels.erase (it);
+			}	
+			else 
+				it++;
+		}
+	}	
 	
 	void Tunnels::PostTunnelData (I2NPMessage * msg)
 	{
diff --git a/Tunnel.h b/Tunnel.h
index 9ecf6ff3..cec82958 100644
--- a/Tunnel.h
+++ b/Tunnel.h
@@ -33,9 +33,7 @@ namespace tunnel
 
 			void Build (uint32_t replyMsgID, OutboundTunnel * outboundTunnel = 0);
 			
-			virtual uint32_t GetTunnelID () const = 0; // as known at our side
 			TunnelConfig * GetTunnelConfig () const { return m_Config; }
-			uint32_t GetCreationTime () const { return m_CreationTime; };
 			bool IsEstablished () const { return m_IsEstablished; };
 						
 			bool HandleTunnelBuildResponse (uint8_t * msg, size_t len);
@@ -54,7 +52,6 @@ namespace tunnel
 		private:
 
 			TunnelConfig * m_Config;
-			uint32_t m_CreationTime; // seconds since epoch
 			bool m_IsEstablished;
 
 			CryptoPP::ECB_Mode<CryptoPP::AES>::Decryption m_ECBDecryption;
@@ -70,9 +67,11 @@ namespace tunnel
 			void SendTunnelDataMsg (i2p::I2NPMessage * msg); //local
 			void SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, i2p::I2NPMessage * msg);
 
-			uint32_t GetTunnelID () const { return GetNextTunnelID (); };
 			TunnelGateway& GetTunnelGateway () { return m_Gateway; };
 			size_t GetNumSentBytes () const { return m_Gateway.GetNumSentBytes (); };
+
+			// implements TunnelBase
+			uint32_t GetTunnelID () const { return GetNextTunnelID (); };
 			
 		private:
 
@@ -85,10 +84,10 @@ namespace tunnel
 
 			InboundTunnel (TunnelConfig * config): Tunnel (config) {};
 			void HandleTunnelDataMsg (I2NPMessage * msg);
-
-			uint32_t GetTunnelID () const { return GetTunnelConfig ()->GetLastHop ()->nextTunnelID; };
 			size_t GetNumReceivedBytes () const { return m_Endpoint.GetNumReceivedBytes (); };
-			
+
+			// implements TunnelBase
+			uint32_t GetTunnelID () const { return GetTunnelConfig ()->GetLastHop ()->nextTunnelID; };
 		private:
 
 			TunnelEndpoint m_Endpoint; 
@@ -128,6 +127,7 @@ namespace tunnel
 			void ManageTunnels ();
 			void ManageOutboundTunnels ();
 			void ManageInboundTunnels ();
+			void ManageTransitTunnels ();
 			
 			void CreateZeroHopsInboundTunnel ();
 			
diff --git a/TunnelBase.h b/TunnelBase.h
index 829270ce..bd760989 100644
--- a/TunnelBase.h
+++ b/TunnelBase.h
@@ -2,6 +2,7 @@
 #define TUNNEL_BASE_H__
 
 #include <inttypes.h>
+#include "Timestamp.h"
 #include "I2NPProtocol.h"
 
 namespace i2p
@@ -30,11 +31,20 @@ namespace tunnel
 	{
 		public:
 
+			TunnelBase (): m_CreationTime (i2p::util::GetSecondsSinceEpoch ()) {};
 			virtual ~TunnelBase () {};
 			
 			virtual void EncryptTunnelMsg (I2NPMessage * tunnelMsg) = 0;
 			virtual uint32_t GetNextTunnelID () const = 0;
 			virtual const i2p::data::IdentHash& GetNextIdentHash () const = 0;
+			virtual uint32_t GetTunnelID () const = 0; // as known at our side
+
+			uint32_t GetCreationTime () const { return m_CreationTime; };
+			void SetCreationTime (uint32_t t) { m_CreationTime = t; };
+
+		private:
+			
+			uint32_t m_CreationTime; // seconds since epoch
 	};	
 }
 }

From c93402ab409cd9c3eb211d7854836df32b984ce8 Mon Sep 17 00:00:00 2001
From: orignal <romakoshelkin@yandex.ru>
Date: Sun, 5 Jan 2014 09:53:44 -0500
Subject: [PATCH 10/16] deep exploratory

---
 I2NPProtocol.cpp |  22 +++++-
 I2NPProtocol.h   |   5 +-
 NetDb.cpp        | 188 +++++++++++++++++++++++++++++++----------------
 NetDb.h          |  41 +++++++++--
 4 files changed, 182 insertions(+), 74 deletions(-)

diff --git a/I2NPProtocol.cpp b/I2NPProtocol.cpp
index 76918bcd..7993a79a 100644
--- a/I2NPProtocol.cpp
+++ b/I2NPProtocol.cpp
@@ -83,7 +83,7 @@ namespace i2p
 	}
 
 	I2NPMessage * CreateDatabaseLookupMsg (const uint8_t * key, const uint8_t * from, 
-		uint32_t replyTunnelID, bool exploratory)
+		uint32_t replyTunnelID, bool exploratory, std::set<i2p::data::IdentHash> * excludedPeers)
 	{
 		I2NPMessage * m = NewI2NPMessage ();
 		uint8_t * buf = m->GetPayload ();
@@ -113,9 +113,23 @@ namespace i2p
 		}
 		else
 		{
-			// nothing to exclude
-			*(uint16_t *)buf = htobe16 (0);
-			buf += 2;
+			if (excludedPeers)
+			{
+				int cnt = excludedPeers->size ();
+				*(uint16_t *)buf = htobe16 (cnt);
+				buf += 2;
+				for (auto& it: *excludedPeers)
+				{
+					memcpy (buf, it, 32);
+					buf += 32;
+				}	
+			}
+			else
+			{	
+				// nothing to exclude
+				*(uint16_t *)buf = htobe16 (0);
+				buf += 2;
+			}	
 		}	
 		m->len += (buf - m->GetPayload ()); 
 		FillI2NPMessageHeader (m, eI2NPDatabaseLookup);
diff --git a/I2NPProtocol.h b/I2NPProtocol.h
index 597c00b0..adcbd576 100644
--- a/I2NPProtocol.h
+++ b/I2NPProtocol.h
@@ -2,7 +2,7 @@
 #define I2NP_PROTOCOL_H__
 
 #include <inttypes.h>
-#include <vector>
+#include <set>
 #include <string.h>
 #include "RouterInfo.h"
 
@@ -105,7 +105,8 @@ namespace i2p
 	
 	I2NPMessage * CreateDeliveryStatusMsg ();
 	I2NPMessage * CreateDatabaseLookupMsg (const uint8_t * key, const uint8_t * from, 
-		uint32_t replyTunnelID, bool exploratory = false);
+		uint32_t replyTunnelID, bool exploratory = false, 
+	    std::set<i2p::data::IdentHash> * excludedPeers = nullptr);
 
 	I2NPMessage * CreateDatabaseStoreMsg ();
 	
diff --git a/NetDb.cpp b/NetDb.cpp
index 264b1631..395bd461 100644
--- a/NetDb.cpp
+++ b/NetDb.cpp
@@ -15,10 +15,23 @@ namespace i2p
 {
 namespace data
 {		
+
+	I2NPMessage * RequestedDestination::CreateRequestMessage (const RouterInfo * router,
+		const i2p::tunnel::InboundTunnel * replyTunnel)
+	{
+		I2NPMessage * msg = i2p::CreateDatabaseLookupMsg (m_Destination, 
+			replyTunnel->GetNextIdentHash (), replyTunnel->GetNextTunnelID (), m_IsExploratory, &m_ExcludedPeers);
+		if (m_IsLeaseSet) // wrap lookup message into garlic
+			msg = i2p::garlic::routing.WrapSingleMessage (router, msg);
+		m_ExcludedPeers.insert (router->GetIdentHash ());
+		m_LastRouter = router;
+		m_LastReplyTunnel = replyTunnel;
+		return msg;
+	}	
 	
 	NetDb netdb;
 
-	NetDb::NetDb (): m_IsRunning (false), m_Thread (0), m_LastFloodfill (0)
+	NetDb::NetDb (): m_IsRunning (false), m_Thread (0)
 	{
 	}
 	
@@ -29,6 +42,8 @@ namespace data
 			delete l.second;
 		for (auto r:m_RouterInfos)
 			delete r.second;
+		for (auto r:m_RequestedDestinations)
+			delete r.second;
 	}	
 
 	void NetDb::Start ()
@@ -97,6 +112,7 @@ namespace data
 	void NetDb::AddRouterInfo (uint8_t * buf, int len)
 	{
 		RouterInfo * r = new RouterInfo (buf, len);
+		DeleteRequestedDestination (r->GetIdentHash ());
 		auto it = m_RouterInfos.find(r->GetIdentHash ());
 		if (it != m_RouterInfos.end ())
 		{
@@ -118,6 +134,7 @@ namespace data
 	void NetDb::AddLeaseSet (uint8_t * buf, int len)
 	{
 		LeaseSet * l = new LeaseSet (buf, len);
+		DeleteRequestedDestination (l->GetIdentHash ());
 		m_LeaseSets[l->GetIdentHash ()] = l;
 	}	
 
@@ -223,10 +240,9 @@ namespace data
 			i2p::tunnel::InboundTunnel * inbound = i2p::tunnel::tunnels.GetNextInboundTunnel ();
 			if (inbound)
 			{
-				I2NPMessage * msg = i2p::CreateDatabaseLookupMsg (destination, inbound->GetNextIdentHash (), 
-					inbound->GetNextTunnelID ());
-				if (isLeaseSet) // wrap lookup message into garlic
-					msg = i2p::garlic::routing.WrapSingleMessage (floodfill, msg);
+				RequestedDestination * dest = CreateRequestedDestination (destination, isLeaseSet);
+				dest->SetLastOutboundTunnel (outbound);
+				auto msg = dest->CreateRequestMessage (floodfill, inbound);
 				outbound->SendTunnelDataMsg (floodfill->GetIdentHash (), 0, msg);
 			}	
 			else
@@ -275,87 +291,133 @@ namespace data
 		key[l] = 0;
 		int num = buf[32]; // num
 		LogPrint ("DatabaseSearchReply for ", key, " num=", num);
-		if (num > 0)
-		{
-			bool isExploratory = !memcmp (m_Exploratory, buf, 32) && m_LastFloodfill;			
-			i2p::tunnel::OutboundTunnel * outbound = isExploratory ? m_LastOutboundTunnel : i2p::tunnel::tunnels.GetNextOutboundTunnel ();
-			i2p::tunnel::InboundTunnel * inbound = isExploratory ? m_LastInboundTunnel : i2p::tunnel::tunnels.GetNextInboundTunnel ();
+		auto it = m_RequestedDestinations.find (IdentHash (buf));
+		if (it != m_RequestedDestinations.end ())
+		{	
+			RequestedDestination * dest = it->second;
+			if (num > 0)
+			{	
+				i2p::tunnel::OutboundTunnel * outbound = dest->GetLastOutboundTunnel ();
+				const i2p::tunnel::InboundTunnel * inbound = dest->GetLastReplyTunnel ();
 			
-			for (int i = 0; i < num; i++)
-			{
-				uint8_t * router = buf + 33 + i*32;
-				char peerHash[48];
-				int l1 = i2p::data::ByteStreamToBase64 (router, 32, peerHash, 48);
-				peerHash[l1] = 0;
-				LogPrint (i,": ", peerHash);
+				for (int i = 0; i < num; i++)
+				{
+					uint8_t * router = buf + 33 + i*32;
+					char peerHash[48];
+					int l1 = i2p::data::ByteStreamToBase64 (router, 32, peerHash, 48);
+					peerHash[l1] = 0;
+					LogPrint (i,": ", peerHash);
 
-				if (isExploratory)
-				{	
-					if (m_RouterInfos.find (IdentHash(router)) == m_RouterInfos.end ())
+					if (dest->IsExploratory ())
 					{	
-						LogPrint ("Found new router. Requesting RouterInfo ...");
+						if (!FindRouter (router)) // router with ident not found
+						{	
+							LogPrint ("Found new router. Requesting RouterInfo ...");
+							if (outbound && inbound)
+							{
+								RequestedDestination * d1 = CreateRequestedDestination (router, false, false);
+								d1->SetLastOutboundTunnel (outbound);
+								auto msg = d1->CreateRequestMessage (dest->GetLastRouter (), dest->GetLastReplyTunnel ());
+								outbound->GetTunnelGateway ().PutTunnelDataMsg (dest->GetLastRouter ()->GetIdentHash (), 0, msg);
+							}	
+						}
+						else
+							LogPrint ("Bayan");
+					}	
+					else
+					{	
+						// reply to our destination. Try other floodfills
 						if (outbound && inbound)
 						{
-							I2NPMessage * msg = i2p::CreateDatabaseLookupMsg (router, inbound->GetNextIdentHash (), 
-								inbound->GetNextTunnelID ());
-							outbound->GetTunnelGateway ().PutTunnelDataMsg (m_LastFloodfill->GetIdentHash (), 0, msg);
-						}	
-					}
-					else
-						LogPrint ("Bayan");
-				}	
-				else
-				{	
-					// reply to our destination. Try other floodfills
-					if (outbound && inbound)
-					{
-						// do we have that floodfill router in our database?
-						if (!FindRouter (router))
-						{	
-							// request router
-							LogPrint ("Found new floodfill. Request it");
-							msg = i2p::CreateDatabaseLookupMsg (router, inbound->GetNextIdentHash (), 
-								inbound->GetNextTunnelID ());
-							outbound->GetTunnelGateway ().PutTunnelDataMsg (
-								GetRandomNTCPRouter (true)->GetIdentHash (), 0, msg);
-							// request destination
-							I2NPMessage * msg = i2p::CreateDatabaseLookupMsg (buf, inbound->GetNextIdentHash (), 
-							inbound->GetNextTunnelID ());
-							outbound->GetTunnelGateway ().PutTunnelDataMsg (router, 0, msg);
+							auto r = FindRouter (router); 
+							// do we have that floodfill router in our database?
+							if (r)
+							{
+								if (!dest->IsExcluded (r->GetIdentHash ()) && dest->GetNumExcludedPeers () < 10) // TODO: fix TunnelGateway first
+								{	
+									// request destination
+									auto msg = dest->CreateRequestMessage (r, dest->GetLastReplyTunnel ());
+									outbound->GetTunnelGateway ().PutTunnelDataMsg (r->GetIdentHash (), 0, msg);
+								}	
+							}
+							else
+							{	
+								// request router
+								LogPrint ("Found new floodfill. Request it");
+								RequestedDestination * d2 = CreateRequestedDestination (router, false, false);
+								d2->SetLastOutboundTunnel (outbound);
+								I2NPMessage * msg = d2->CreateRequestMessage (dest->GetLastRouter (), inbound);
+								outbound->GetTunnelGateway ().PutTunnelDataMsg (
+									dest->GetLastRouter ()->GetIdentHash (), 0, msg);
+							}	
 						}	
 					}	
-				}	
-			}
+				}
 				
-			if (outbound)
-				outbound->GetTunnelGateway ().SendBuffer ();	
-		}	
+				if (outbound)
+					outbound->GetTunnelGateway ().SendBuffer ();	
+			}
+			else
+			{
+				// no more requests for detination possible. delete it
+				m_RequestedDestinations.erase (it);
+				delete it->second;
+			}	
+		}
+		else
+			LogPrint ("Requested destination for ", key, " not found");
 		i2p::DeleteI2NPMessage (msg);
 	}	
 	
 	void NetDb::Explore ()
 	{
-		m_LastOutboundTunnel = i2p::tunnel::tunnels.GetNextOutboundTunnel ();
-		m_LastInboundTunnel = i2p::tunnel::tunnels.GetNextInboundTunnel ();
-		if (m_LastOutboundTunnel && m_LastInboundTunnel)
+		auto outbound = i2p::tunnel::tunnels.GetNextOutboundTunnel ();
+		auto inbound = i2p::tunnel::tunnels.GetNextInboundTunnel ();
+		if (outbound && inbound)
 		{
-			m_LastFloodfill = GetRandomNTCPRouter (true);
-			if (m_LastFloodfill)
+			auto floodfill = GetRandomNTCPRouter (true);
+			if (floodfill)
 			{
 				LogPrint ("Exploring new routers ...");
 				CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
-				rnd.GenerateBlock (m_Exploratory, 32);
+				uint8_t randomHash[32];
+				rnd.GenerateBlock (randomHash, 32);
+				RequestedDestination * dest = CreateRequestedDestination (IdentHash (randomHash), false, true);
+				dest->SetLastOutboundTunnel (outbound);
 				
-				m_LastOutboundTunnel->GetTunnelGateway ().PutTunnelDataMsg (m_LastFloodfill->GetIdentHash (), 0,
+				outbound->GetTunnelGateway ().PutTunnelDataMsg (floodfill->GetIdentHash (), 0,
 					CreateDatabaseStoreMsg ()); // tell floodfill about us                                         
-				m_LastOutboundTunnel->GetTunnelGateway ().PutTunnelDataMsg (m_LastFloodfill->GetIdentHash (), 0, 
-					i2p::CreateDatabaseLookupMsg (m_Exploratory, m_LastInboundTunnel->GetNextIdentHash (), 
-					m_LastInboundTunnel->GetNextTunnelID (), true)); // explore
-				m_LastOutboundTunnel->GetTunnelGateway ().SendBuffer ();
+				outbound->GetTunnelGateway ().PutTunnelDataMsg (floodfill->GetIdentHash (), 0, 
+					dest->CreateRequestMessage (floodfill, inbound)); // explore
+				outbound->GetTunnelGateway ().SendBuffer ();
 			}	
 		}
 	}	
 
+	RequestedDestination * NetDb::CreateRequestedDestination (const IdentHash& dest,
+		bool isLeaseSet, bool isExploratory)
+	{
+		auto it = m_RequestedDestinations.find (dest);
+		if (it == m_RequestedDestinations.end ()) // not exist yet
+		{
+			RequestedDestination * d = new RequestedDestination (dest, isLeaseSet, isExploratory);
+			m_RequestedDestinations[dest] = d;
+			return d;
+		}	
+		else
+			return it->second;
+	}
+	
+	void NetDb::DeleteRequestedDestination (const IdentHash& dest)
+	{
+		auto it = m_RequestedDestinations.find (dest);
+		if (it != m_RequestedDestinations.end ())
+		{	
+			m_RequestedDestinations.erase (it);
+			delete it->second;
+		}	
+	}	
+	
 	const RouterInfo * NetDb::GetRandomNTCPRouter (bool floodfillOnly) const
 	{
 		CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
diff --git a/NetDb.h b/NetDb.h
index 761d3a09..02a585ef 100644
--- a/NetDb.h
+++ b/NetDb.h
@@ -2,6 +2,7 @@
 #define NETDB_H__
 
 #include <inttypes.h>
+#include <set>
 #include <map>
 #include <string>
 #include <thread>
@@ -15,6 +16,35 @@ namespace i2p
 {
 namespace data
 {		
+	class RequestedDestination
+	{
+		public:
+
+			RequestedDestination (const IdentHash& destination, bool isLeaseSet, bool isExploratory = false):
+				m_Destination (destination), m_IsLeaseSet (isLeaseSet), m_IsExploratory (isExploratory), 
+				m_LastRouter (nullptr), m_LastReplyTunnel (nullptr), m_LastOutboundTunnel (nullptr) {};
+			
+			const IdentHash& GetDestination () const { return m_Destination; };
+			int GetNumExcludedPeers () const { return m_ExcludedPeers.size (); };
+			const RouterInfo * GetLastRouter () const { return m_LastRouter; };
+			const i2p::tunnel::InboundTunnel * GetLastReplyTunnel () const { return m_LastReplyTunnel; };
+			bool IsExploratory () const { return m_IsExploratory; };
+			bool IsExcluded (const IdentHash& ident) const { return m_ExcludedPeers.count (ident); };
+			I2NPMessage * CreateRequestMessage (const RouterInfo * router, const i2p::tunnel::InboundTunnel * replyTunnel);
+
+			i2p::tunnel::OutboundTunnel * GetLastOutboundTunnel () const { return m_LastOutboundTunnel; };
+			void SetLastOutboundTunnel (i2p::tunnel::OutboundTunnel * tunnel) { m_LastOutboundTunnel = tunnel; };
+			
+		private:
+
+			IdentHash m_Destination;
+			bool m_IsLeaseSet, m_IsExploratory;
+			std::set<IdentHash> m_ExcludedPeers;
+			const RouterInfo * m_LastRouter;
+			const i2p::tunnel::InboundTunnel * m_LastReplyTunnel;
+			i2p::tunnel::OutboundTunnel * m_LastOutboundTunnel;
+	};	
+	
 	class NetDb
 	{
 		public:
@@ -49,18 +79,19 @@ namespace data
 			void Run (); // exploratory thread
 			void Explore ();
 			const RouterInfo * GetClosestFloodfill (const IdentHash& destination) const;
+
+			RequestedDestination * CreateRequestedDestination (const IdentHash& dest, 
+				bool isLeaseSet, bool isExploratory = false);
+			void DeleteRequestedDestination (const IdentHash& dest);
 			
 		private:
 
 			std::map<IdentHash, LeaseSet *> m_LeaseSets;
 			std::map<IdentHash, RouterInfo *> m_RouterInfos;
-
+			std::map<IdentHash, RequestedDestination *> m_RequestedDestinations;
+			
 			bool m_IsRunning;
 			std::thread * m_Thread;	
-			uint8_t m_Exploratory[32];
-			const RouterInfo * m_LastFloodfill;
-			i2p::tunnel::OutboundTunnel * m_LastOutboundTunnel;
-			i2p::tunnel::InboundTunnel * m_LastInboundTunnel;
 			i2p::util::Queue<I2NPMessage> m_Queue; // of I2NPDatabaseStoreMsg
 	};
 

From 4b9d2d60e2b2abc5723d0e6b427f01cceed8cfbc Mon Sep 17 00:00:00 2001
From: orignal <romakoshelkin@yandex.ru>
Date: Sun, 5 Jan 2014 21:25:48 -0500
Subject: [PATCH 11/16] long tunnel messages

---
 Tunnel.cpp        |   7 +-
 TunnelGateway.cpp | 288 +++++++++++++++++++---------------------------
 TunnelGateway.h   |  23 ++--
 3 files changed, 125 insertions(+), 193 deletions(-)

diff --git a/Tunnel.cpp b/Tunnel.cpp
index 0a75649a..b4cd41a0 100644
--- a/Tunnel.cpp
+++ b/Tunnel.cpp
@@ -65,14 +65,9 @@ namespace tunnel
 		FillI2NPMessageHeader (msg, eI2NPVariableTunnelBuild);
 		
 		if (outboundTunnel)
-		{	
-			outboundTunnel->SendTunnelDataMsg (GetNextIdentHash (), 0, msg);
-			DeleteI2NPMessage (msg);
-		}	
+			outboundTunnel->SendTunnelDataMsg (GetNextIdentHash (), 0, msg);	
 		else
-		{	
 			i2p::transports.SendMessage (GetNextIdentHash (), msg);
-		}	
 	}	
 		
 	bool Tunnel::HandleTunnelBuildResponse (uint8_t * msg, size_t len)
diff --git a/TunnelGateway.cpp b/TunnelGateway.cpp
index d70763e6..6e0db1f0 100644
--- a/TunnelGateway.cpp
+++ b/TunnelGateway.cpp
@@ -12,198 +12,144 @@ namespace tunnel
 {
 	void TunnelGatewayBuffer::PutI2NPMsg (const uint8_t * gwHash, uint32_t gwTunnel, I2NPMessage * msg)
 	{
-		TunnelMessageBlockExt * block = new TunnelMessageBlockExt;
-		block->deliveryInstructionsLen = 1; // flag
+		if (!m_CurrentTunnelDataMsg)
+			CreateCurrentTunnelDataMessage ();
+
+		// create delivery instructions
+		uint8_t di[40];
+		size_t diLen = 1;// flag
+		TunnelDeliveryType dt = eDeliveryTypeLocal;
 		if (gwHash)
 		{	
-			block->deliveryInstructionsLen += 32; // hash
-			memcpy (block->hash, gwHash, 32);
 			if (gwTunnel)
-			{	
-				block->deliveryType = eDeliveryTypeTunnel;
-				block->deliveryInstructionsLen += 4; // tunnelID
-				block->tunnelID = gwTunnel;
-			}	
-			else
-				block->deliveryType = eDeliveryTypeRouter;
-		}	
-		else	
-			block->deliveryType = eDeliveryTypeLocal;
-		block->deliveryInstructionsLen += 2; // size
-		// we don't reserve 4 bytes for msgID yet
-		block->totalLen = block->deliveryInstructionsLen + msg->GetLength ();
-		block->data = msg;
-		m_I2NPMsgs.push_back (block);
-
-		if (!m_Remaining) m_Remaining = TUNNEL_DATA_MAX_PAYLOAD_SIZE;
-		if (block->totalLen <= m_Remaining) // message fits
-		{
-			block->isFragmented = false;
-			m_Remaining -= block->totalLen;
-		}	
-		else // message doesn't fit
-		{
-			if (block->deliveryInstructionsLen + 4 <= m_Remaining)
 			{
-				// delivery instructions of first fragment fits
-				block->isFragmented = true;
-				block->deliveryInstructionsLen += 4;
-				block->totalLen += 4;
-				m_Remaining = m_Remaining + TUNNEL_DATA_MAX_PAYLOAD_SIZE - block->totalLen - 7; // TODO: handle case if more than two fragments
-			}	
-			else
-			{
-				// delivery instructions of first fragment don't fit
-				block->isFragmented = false;
-				m_Remaining = 0;
-			}	
-		}	
-	}	
-
-	std::vector<I2NPMessage *> TunnelGatewayBuffer::GetTunnelDataMsgs () 
-	{ 
-		m_Remaining = 0;
-		m_NextOffset = 0;
-		std::vector<I2NPMessage *> res;
-		int cnt = m_I2NPMsgs.size ();
-		if (cnt > 0)
-		{	
-			int ind = 0;
-			while (ind < cnt)
-			{
-				auto tunnelMsg = CreateNextTunnelMessage (ind);
-				if (!tunnelMsg) break;
-				res.push_back (tunnelMsg);
+				*(uint32_t *)(di + diLen) = htobe32 (gwTunnel);
+				diLen += 4; // tunnelID
+				dt = eDeliveryTypeTunnel;
 			}
-			for (auto msg: m_I2NPMsgs)
-				delete msg;
-			m_I2NPMsgs.clear ();
+			else
+				dt = eDeliveryTypeRouter;
+			
+			memcpy (di + diLen, gwHash, 32);
+			diLen += 32; //len
 		}	
-		
-		return res; 
-	}
+		di[0] = dt << 5; // set delivery type
 
-	size_t TunnelGatewayBuffer::CreateFirstFragment (TunnelMessageBlockExt * block, uint8_t * buf, size_t len)
-	{
-		size_t ret = 1;
-		buf[0] = block->deliveryType << 5; // flag
-		if (block->deliveryType == eDeliveryTypeTunnel)
+		// create fragments
+		if (diLen + msg->GetLength () + 2<= m_RemainingSize)
 		{
-			*(uint32_t *)(buf + ret) = htobe32 (block->tunnelID);
-			ret += 4;
-		}
-		if (block->deliveryType == eDeliveryTypeTunnel || block->deliveryType == eDeliveryTypeRouter)
-		{
-			memcpy (buf + ret, block->hash, 32);
-			ret += 32;
+			// message fits. First and last fragment
+			*(uint16_t *)(di + diLen) = htobe16 (msg->GetLength ());
+			diLen += 2; // size
+			memcpy (m_CurrentTunnelDataMsg->buf + m_CurrentTunnelDataMsg->len, di, diLen);
+			memcpy (m_CurrentTunnelDataMsg->buf + m_CurrentTunnelDataMsg->len + diLen, msg->GetBuffer (), msg->GetLength ());
+			m_CurrentTunnelDataMsg->len += diLen + msg->GetLength ();
+			m_RemainingSize -= diLen + msg->GetLength ();
+			if (!m_RemainingSize)
+				CompleteCurrentTunnelDataMessage ();
+			DeleteI2NPMessage (msg);
 		}	
-		size_t size = block->data->GetLength ();
-		if (block->totalLen > len) // entire message doesn't fit
-		{	
-			buf[0] |= 0x08; // set fragmented bit
-			m_NextMsgID = block->data->GetHeader ()->msgID;
-			*(uint32_t *)(buf + ret) = m_NextMsgID;
-			ret += 4; // msgID
-			m_NextSeqn = 1;
-			size = len - ret - 2; // 2 bytes for size field
-			m_NextOffset = size;
-		}	
-		*(uint16_t *)(buf + ret) = htobe16 (size); // size
-		ret += 2;
-		memcpy (buf + ret, block->data->GetBuffer (), size);
-		ret += size;
-		return ret;	
-	}	
-
-	size_t TunnelGatewayBuffer::CreateFollowOnFragment (TunnelMessageBlockExt * block, uint8_t * buf, size_t len)
-	{
-		int ret = 0;
-		buf[0] = 0x80 | (m_NextSeqn << 1);// follow-on flag and seqn
-		size_t fragmentLen = len - 7; // 7 bytes of header
-		if (fragmentLen >= block->data->GetLength () - m_NextOffset)
-		{
-			// fragment fits
-			fragmentLen = block->data->GetLength () - m_NextOffset;
-			buf[0] |= 0x01; // last fragment
-		}
 		else
-			m_NextSeqn++;
+		{
+			if (diLen + 6 <= m_RemainingSize)
+			{
+				// delivery instructions fit
+				uint32_t msgID = msg->GetHeader ()->msgID;
+				size_t size = m_RemainingSize - diLen - 6; // 6 = 4 (msgID) + 2 (size)
 
-		*(uint32_t *)(buf + 1) = m_NextMsgID; // msgID
-		*(uint16_t *)(buf + 5) = htobe16 (fragmentLen); // size
-		memcpy (buf + 7, block->data->GetBuffer () + m_NextOffset, fragmentLen);
-
-		m_NextOffset += fragmentLen;
-		ret += fragmentLen + 7;
-		
+				// first fragment
+				di[0] |= 0x08; // fragmented
+				*(uint32_t *)(di + diLen) = htobe32 (msgID);
+				diLen += 4; // Message ID
+				*(uint16_t *)(di + diLen) = htobe16 (size);
+				diLen += 2; // size
+				memcpy (m_CurrentTunnelDataMsg->buf + m_CurrentTunnelDataMsg->len, di, diLen);
+				memcpy (m_CurrentTunnelDataMsg->buf + m_CurrentTunnelDataMsg->len + diLen, msg->GetBuffer (), size);
+				m_CurrentTunnelDataMsg->len += diLen + size;
+				CompleteCurrentTunnelDataMessage ();
+				// follow on fragments
+				int fragmentNumber = 1;
+				while (size < msg->GetLength ())
+				{	
+					CreateCurrentTunnelDataMessage ();
+					uint8_t * buf = m_CurrentTunnelDataMsg->GetBuffer ();
+					buf[0] = 0x80 | (fragmentNumber << 1); // frag
+					bool isLastFragment = false;
+					size_t s = msg->GetLength () - size;
+					if (s > TUNNEL_DATA_MAX_PAYLOAD_SIZE - 7) // 7 follow on instructions
+						s = TUNNEL_DATA_MAX_PAYLOAD_SIZE - 7;	
+					else // last fragment
+					{	
+						buf[0] |= 0x01;
+						isLastFragment = true;
+					}	
+					*(uint32_t *)(buf + 1) = htobe32 (msgID); //Message ID
+					*(uint16_t *)(buf + 5) = htobe16 (s); // size
+					memcpy (buf + 7, msg->GetBuffer () + size, s);
+					m_CurrentTunnelDataMsg->len += s+7;
+					if (isLastFragment)
+					{
+						m_RemainingSize -= s+7; 
+						if (!m_RemainingSize)
+							CompleteCurrentTunnelDataMessage ();
+					}
+					else
+						CompleteCurrentTunnelDataMessage ();
+					size += s;
+					fragmentNumber++;
+				}
+				DeleteI2NPMessage (msg);
+			}	
+			else
+			{
+				// delivery instructions don't fit. Create new message
+				CompleteCurrentTunnelDataMessage ();
+				PutI2NPMsg (gwHash, gwTunnel, msg);
+				// don't delete msg because it's taken care inside
+			}	
+		}			
+	}
+	
+	std::vector<I2NPMessage *> TunnelGatewayBuffer::GetTunnelDataMsgs ()
+	{
+		CompleteCurrentTunnelDataMessage ();
+		std::vector<I2NPMessage *> ret = m_TunnelDataMsgs; // TODO: implement it better
+		m_TunnelDataMsgs.clear ();	
 		return ret;
 	}	
 
-	I2NPMessage * TunnelGatewayBuffer::CreateNextTunnelMessage (int& ind)
+	void TunnelGatewayBuffer::CreateCurrentTunnelDataMessage ()
 	{
-		int cnt = m_I2NPMsgs.size ();
-		if (ind > cnt - 1) return nullptr; // no more messages
-		// calculate payload size
-		size_t size = 0;
-		int i = ind;
-		if (m_NextOffset)
-		{	
-			size = m_I2NPMsgs[i]->data->GetLength () - m_NextOffset + 7; // including follow-on header
-			i++;
-		}	
-		while (i < cnt)
-		{	
-			auto msg = m_I2NPMsgs[i];
-			size += msg->totalLen;
-			if (size >= TUNNEL_DATA_MAX_PAYLOAD_SIZE)
-			{
-				size = TUNNEL_DATA_MAX_PAYLOAD_SIZE;
-				break;
-			}	
-			if (msg->isFragmented) break;
-			i++;
-		}
+		m_CurrentTunnelDataMsg = NewI2NPMessage ();
+		// we reserve space for padding
+		m_CurrentTunnelDataMsg->offset += TUNNEL_DATA_MSG_SIZE + sizeof (I2NPHeader);
+		m_CurrentTunnelDataMsg->len = m_CurrentTunnelDataMsg->offset;
+		m_RemainingSize = TUNNEL_DATA_MAX_PAYLOAD_SIZE;
+	}	
+	
+	void TunnelGatewayBuffer::CompleteCurrentTunnelDataMessage ()
+	{
+		if (!m_CurrentTunnelDataMsg) return;
+		uint8_t * payload = m_CurrentTunnelDataMsg->GetBuffer ();
+		size_t size = m_CurrentTunnelDataMsg->len - m_CurrentTunnelDataMsg->offset;
 		
-		I2NPMessage * tunnelMsg = NewI2NPMessage ();
-		uint8_t * buf = tunnelMsg->GetPayload ();
+		m_CurrentTunnelDataMsg->offset = m_CurrentTunnelDataMsg->len - TUNNEL_DATA_MSG_SIZE - sizeof (I2NPHeader);
+		uint8_t * buf = m_CurrentTunnelDataMsg->GetPayload ();
 		*(uint32_t *)(buf) = htobe32 (m_TunnelID);
 		CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
 		rnd.GenerateBlock (buf + 4, 16); // original IV	
-		memcpy (buf + TUNNEL_DATA_MSG_SIZE, buf + 4, 16); // copy IV for checksum 	
-		size_t zero  = TUNNEL_DATA_MSG_SIZE - size -1;
-		buf[zero] = 0; // zero
-		size_t s = 0;
-		while (ind < cnt)
-		{
-			auto msg = m_I2NPMsgs[ind];
-			if (m_NextOffset)	
-			{	
-				s += CreateFollowOnFragment (msg, buf + zero + 1 + s, size - s);
-				m_NextOffset = 0; // TODO:
-			}	
-			else
-			{	
-				s += CreateFirstFragment (msg, buf + zero + 1 + s, size - s);
-				if (msg->isFragmented) break; // payload is full, but we stay at the same message
-			}
-			ind++;
-			if (s >= size) break; //  payload is full but we moved to next message
-		}
-
-		if (s != size)
-		{	
-			LogPrint ("TunnelData payload size mismatch ", s, "!=", size);
-			return nullptr;
-		}	
-		
+		memcpy (payload + size, buf + 4, 16); // copy IV for checksum 
 		uint8_t hash[32];
-		CryptoPP::SHA256().CalculateDigest(hash, buf+zero+1, size+16);
-		memcpy (buf+20, hash, 4); // checksum
-		if (zero > 24)
-			memset (buf+24, 1, zero-24); // padding TODO: fill with random data
-		tunnelMsg->len += TUNNEL_DATA_MSG_SIZE;
+		CryptoPP::SHA256().CalculateDigest (hash, payload, size+16);
+		memcpy (buf+20, hash, 4); // checksum		
+		payload[-1] = 0; // zero	
+		ssize_t paddingSize = payload - buf - 25; // 25  = 24 + 1 
+		if (paddingSize > 0)
+			memset (buf + 24, 1, paddingSize); // padding TODO: fill with random data
+
 		// we can't fill message header yet because encryption is required
-		return tunnelMsg;
+		m_TunnelDataMsgs.push_back (m_CurrentTunnelDataMsg);
+		m_CurrentTunnelDataMsg = nullptr;
 	}	
 	
 	void TunnelGateway::SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, i2p::I2NPMessage * msg)
diff --git a/TunnelGateway.h b/TunnelGateway.h
index 9c679888..d68c8580 100644
--- a/TunnelGateway.h
+++ b/TunnelGateway.h
@@ -12,32 +12,23 @@ namespace tunnel
 {
 	class TunnelGatewayBuffer
 	{
-		struct TunnelMessageBlockExt: public TunnelMessageBlock
-		{
-			size_t deliveryInstructionsLen, totalLen;
-			bool isFragmented;
-		};	
-		
 		public:
-
-			TunnelGatewayBuffer (uint32_t tunnelID): m_TunnelID (tunnelID), m_Remaining (0) {}; 
-			
+			TunnelGatewayBuffer (uint32_t tunnelID): m_TunnelID (tunnelID), 
+				m_CurrentTunnelDataMsg (nullptr), m_RemainingSize (0) {};
 			void PutI2NPMsg (const uint8_t * gwHash, uint32_t gwTunnel, I2NPMessage * msg);	
 			std::vector<I2NPMessage *> GetTunnelDataMsgs ();
 
 		private:
 
-			size_t CreateFirstFragment (TunnelMessageBlockExt * block, uint8_t * buf, size_t len);
-			size_t CreateFollowOnFragment (TunnelMessageBlockExt * block, uint8_t * buf, size_t len);
-			I2NPMessage * CreateNextTunnelMessage (int& ind);
+			void CreateCurrentTunnelDataMessage ();
+			void CompleteCurrentTunnelDataMessage ();
 			
 		private:
 
 			uint32_t m_TunnelID;
-			std::vector<TunnelMessageBlockExt *> m_I2NPMsgs;
-			// for fragmented  messages
-			size_t m_NextOffset, m_NextSeqn, m_Remaining;
-			uint32_t m_NextMsgID;
+			std::vector<I2NPMessage *> m_TunnelDataMsgs;
+			I2NPMessage * m_CurrentTunnelDataMsg;
+			size_t m_RemainingSize;
 	};	
 
 	class TunnelGateway

From c85ae61cfcc9d7ff1457bf1bd118d5e9b7a013f1 Mon Sep 17 00:00:00 2001
From: orignal <romakoshelkin@yandex.ru>
Date: Sun, 5 Jan 2014 22:21:59 -0500
Subject: [PATCH 12/16] hadlle DatabaseLookup message

---
 I2NPProtocol.cpp | 36 +++++++++++++++++++++++++++++++++++-
 I2NPProtocol.h   |  4 +++-
 2 files changed, 38 insertions(+), 2 deletions(-)

diff --git a/I2NPProtocol.cpp b/I2NPProtocol.cpp
index 7993a79a..11ccd757 100644
--- a/I2NPProtocol.cpp
+++ b/I2NPProtocol.cpp
@@ -136,6 +136,36 @@ namespace i2p
 		return m; 
 	}	
 
+	void HandleDatabaseLookupMsg (uint8_t * buf, size_t len)
+	{
+		char key[48];
+		int l = i2p::data::ByteStreamToBase64 (buf, 32, key, 48);
+		key[l] = 0;
+		LogPrint ("DatabaseLookup for ", key, " recieved");
+		uint8_t flag = buf[64];
+		uint32_t replyTunnelID = 0;
+		if (flag & 0x01) //reply to yunnel
+			replyTunnelID = be32toh (*(uint32_t *)(buf + 64));
+		// TODO: implement search. We send non-found for now
+		I2NPMessage * replyMsg = CreateDatabaseSearchReply (buf);
+		if (replyTunnelID)
+			i2p::tunnel::tunnels.GetNextOutboundTunnel ()->SendTunnelDataMsg (buf+32, replyTunnelID, replyMsg);
+		else
+			i2p::transports.SendMessage (buf, replyMsg);
+	}	
+
+	I2NPMessage * CreateDatabaseSearchReply (const i2p::data::IdentHash& ident)
+	{
+		I2NPMessage * m = NewI2NPMessage ();
+		uint8_t * buf = m->GetPayload ();
+		memcpy (buf, ident, 32);
+		buf[32] = 0; // TODO:
+		memcpy (buf + 33, i2p::context.GetRouterInfo ().GetIdentHash (), 32);
+		m->len += 65;
+		FillI2NPMessageHeader (m, eI2NPDatabaseSearchReply);
+		return m; 
+	}	
+	
 	I2NPMessage * CreateDatabaseStoreMsg ()
 	{
 		I2NPMessage * m = NewI2NPMessage ();
@@ -409,7 +439,11 @@ namespace i2p
 			case eI2NPVariableTunnelBuildReply:
 				LogPrint ("VariableTunnelBuildReply");
 				HandleVariableTunnelBuildReplyMsg (msgID, buf, size);
-			break;		
+			break;	
+			case eI2NPDatabaseLookup:
+				LogPrint ("DatabaseLookup");
+				HandleDatabaseLookupMsg (buf, size);
+			break;	
 			default:
 				LogPrint ("Unexpected message ", (int)header->typeID);
 		}	
diff --git a/I2NPProtocol.h b/I2NPProtocol.h
index adcbd576..71b93525 100644
--- a/I2NPProtocol.h
+++ b/I2NPProtocol.h
@@ -107,7 +107,9 @@ namespace i2p
 	I2NPMessage * CreateDatabaseLookupMsg (const uint8_t * key, const uint8_t * from, 
 		uint32_t replyTunnelID, bool exploratory = false, 
 	    std::set<i2p::data::IdentHash> * excludedPeers = nullptr);
-
+	void HandleDatabaseLookupMsg (uint8_t * buf, size_t len);
+	I2NPMessage * CreateDatabaseSearchReply (const i2p::data::IdentHash& ident);
+	
 	I2NPMessage * CreateDatabaseStoreMsg ();
 	
 	I2NPBuildRequestRecordClearText CreateBuildRequestRecord (

From 040d0f5f58e88602e3da37a0f9adf73f6fa65048 Mon Sep 17 00:00:00 2001
From: orignal <romakoshelkin@yandex.ru>
Date: Sun, 5 Jan 2014 22:52:17 -0500
Subject: [PATCH 13/16] show trafic for transit tunnels

---
 HTTPServer.cpp    | 11 +++++++++--
 TransitTunnel.cpp |  4 +++-
 TransitTunnel.h   |  4 ++++
 3 files changed, 16 insertions(+), 3 deletions(-)

diff --git a/HTTPServer.cpp b/HTTPServer.cpp
index 88e7b272..577ef748 100644
--- a/HTTPServer.cpp
+++ b/HTTPServer.cpp
@@ -104,15 +104,22 @@ namespace util
 				s << "-->" << it.second->GetTunnelID ();
 			else
 				s << "-->" << it.second->GetTunnelID () << "-->";
-			s << "<BR>";
+			s << " " << it.second->GetNumTransmittedBytes () << "<BR>";
 		}	
 
 		s << "<P>Transports</P>";
 		for (auto it: i2p::transports.GetNTCPSessions ())
 		{	
+			// RouterInfo of incoming connection doesn't have address
+			bool outgoing = it.second->GetRemoteRouterInfo ().GetNTCPAddress ();
 			if (it.second->IsEstablished ())
+			{
+				if (outgoing) s << "-->";
 				s << it.second->GetRemoteRouterInfo ().GetIdentHashAbbreviation () <<  ": " 
-					<< it.second->GetSocket ().remote_endpoint().address ().to_string () << "<BR>";
+					<< it.second->GetSocket ().remote_endpoint().address ().to_string ();
+				if (!outgoing) s << "-->";
+				s << "<BR>";
+			}	
 		}	
 	}	
 
diff --git a/TransitTunnel.cpp b/TransitTunnel.cpp
index 5771c385..d92b62a9 100644
--- a/TransitTunnel.cpp
+++ b/TransitTunnel.cpp
@@ -14,7 +14,8 @@ namespace tunnel
 	TransitTunnel::TransitTunnel (uint32_t receiveTunnelID, 
 	    const uint8_t * nextIdent, uint32_t nextTunnelID, 
 		const uint8_t * layerKey,const uint8_t * ivKey): 
-			m_TunnelID (receiveTunnelID),  m_NextTunnelID (nextTunnelID), m_NextIdent (nextIdent)
+			m_TunnelID (receiveTunnelID),  m_NextTunnelID (nextTunnelID), 
+			m_NextIdent (nextIdent), m_NumTransmittedBytes (0)
 	{	
 		memcpy (m_LayerKey, layerKey, 32);
 		memcpy (m_IVKey, ivKey, 32);
@@ -43,6 +44,7 @@ namespace tunnel
 		FillI2NPMessageHeader (tunnelMsg, eI2NPTunnelData);
 	
 		i2p::transports.SendMessage (m_NextIdent, tunnelMsg);	
+		m_NumTransmittedBytes += tunnelMsg->GetLength ();
 	}
 
 	void TransitTunnel::SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, i2p::I2NPMessage * msg)
diff --git a/TransitTunnel.h b/TransitTunnel.h
index 2109ee31..570429f5 100644
--- a/TransitTunnel.h
+++ b/TransitTunnel.h
@@ -23,6 +23,7 @@ namespace tunnel
 			
 			virtual void HandleTunnelDataMsg (i2p::I2NPMessage * tunnelMsg);
 			virtual void SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, i2p::I2NPMessage * msg);
+			virtual size_t GetNumTransmittedBytes () const { return m_NumTransmittedBytes; };
 			
 			uint32_t GetTunnelID () const { return m_TunnelID; };
 
@@ -37,6 +38,7 @@ namespace tunnel
 			i2p::data::IdentHash m_NextIdent;
 			uint8_t m_LayerKey[32];
 			uint8_t m_IVKey[32];
+			size_t m_NumTransmittedBytes;
 			
 			CryptoPP::ECB_Mode<CryptoPP::AES>::Encryption m_ECBEncryption;
 			CryptoPP::CBC_Mode<CryptoPP::AES>::Encryption m_CBCEncryption;
@@ -53,6 +55,7 @@ namespace tunnel
 				layerKey, ivKey), m_Gateway(this) {};
 
 			void SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, i2p::I2NPMessage * msg);
+			size_t GetNumTransmittedBytes () const { return m_Gateway.GetNumSentBytes (); };
 			
 		private:
 
@@ -69,6 +72,7 @@ namespace tunnel
 				TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey) {};
 
 			void HandleTunnelDataMsg (i2p::I2NPMessage * tunnelMsg);
+			size_t GetNumTransmittedBytes () const { return m_Endpoint.GetNumReceivedBytes (); }
 			
 		private:
 

From cbe58c8bb1321141a4291af08d11a87f0559230f Mon Sep 17 00:00:00 2001
From: orignal <romakoshelkin@yandex.ru>
Date: Mon, 6 Jan 2014 20:56:44 -0500
Subject: [PATCH 14/16] fixed out of range bug

---
 Streaming.cpp     | 4 ++--
 TunnelGateway.cpp | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/Streaming.cpp b/Streaming.cpp
index e0d64a77..05d9933c 100644
--- a/Streaming.cpp
+++ b/Streaming.cpp
@@ -96,7 +96,7 @@ namespace stream
 		auto outbound = i2p::tunnel::tunnels.GetNextOutboundTunnel ();
 		if (outbound)
 		{
-			auto lease = m_RemoteLeaseSet->GetLeases ()[0]; // TODO:
+			auto& lease = m_RemoteLeaseSet->GetLeases ()[0]; // TODO:
 			outbound->SendTunnelDataMsg (lease.tunnelGateway, lease.tunnelID, msg);
 		}	
 		else
@@ -235,7 +235,7 @@ namespace stream
 		compressor.MessageEnd();
 		int size = compressor.MaxRetrievable ();
 		uint8_t * buf = msg->GetPayload ();
-		*(uint16_t *)buf = htobe32 (size); // length
+		*(uint32_t *)buf = htobe32 (size); // length
 		buf += 4;
 		compressor.Get (buf, size);
 		buf[9] = 6; // streaming protocol
diff --git a/TunnelGateway.cpp b/TunnelGateway.cpp
index 6e0db1f0..24f1a0ec 100644
--- a/TunnelGateway.cpp
+++ b/TunnelGateway.cpp
@@ -16,7 +16,7 @@ namespace tunnel
 			CreateCurrentTunnelDataMessage ();
 
 		// create delivery instructions
-		uint8_t di[40];
+		uint8_t di[43]; // max delivery instruction length is 43 for tunnel
 		size_t diLen = 1;// flag
 		TunnelDeliveryType dt = eDeliveryTypeLocal;
 		if (gwHash)

From 3aab091e9f0318f78431cc1584a7cfcb337a05ad Mon Sep 17 00:00:00 2001
From: orignal <romakoshelkin@yandex.ru>
Date: Wed, 8 Jan 2014 19:30:47 -0500
Subject: [PATCH 15/16] common constructor for TunnelConfig

---
 Tunnel.cpp     | 42 ++++++++++++++++++++++++++++++++----------
 TunnelConfig.h | 37 +++++++++++++++++--------------------
 2 files changed, 49 insertions(+), 30 deletions(-)

diff --git a/Tunnel.cpp b/Tunnel.cpp
index b4cd41a0..76f05a62 100644
--- a/Tunnel.cpp
+++ b/Tunnel.cpp
@@ -353,7 +353,10 @@ namespace tunnel
 			{	
 				LogPrint ("Creating one hop outbound tunnel...");
 				CreateTunnel<OutboundTunnel> (
-				  	new TunnelConfig (i2p::data::netdb.GetRandomNTCPRouter (),
+				  	new TunnelConfig (std::vector<const i2p::data::RouterInfo *> 
+					    { 
+							i2p::data::netdb.GetRandomNTCPRouter ()
+						},		
 			     		inboundTunnel->GetTunnelConfig ()));
 			}	
 			else
@@ -361,8 +364,11 @@ namespace tunnel
 				//OutboundTunnel * outboundTunnel =  GetNextOutboundTunnel ();
 				LogPrint ("Creating two hops outbound tunnel...");
 				CreateTunnel<OutboundTunnel> (
-				  	new TunnelConfig (i2p::data::netdb.GetRandomNTCPRouter (),
-						i2p::data::netdb.GetRandomNTCPRouter (),
+				  	new TunnelConfig (std::vector<const i2p::data::RouterInfo *>
+				    	{
+							i2p::data::netdb.GetRandomNTCPRouter (),
+							i2p::data::netdb.GetRandomNTCPRouter ()
+						},		
 			     		inboundTunnel->GetTunnelConfig ())/*,
 				        	outboundTunnel*/);
 			}	
@@ -396,7 +402,11 @@ namespace tunnel
 			if (m_OutboundTunnels.empty () || m_InboundTunnels.size () < 3)
 			{	
 				LogPrint ("Creating one hop inbound tunnel...");
-				CreateTunnel<InboundTunnel> (new TunnelConfig (i2p::data::netdb.GetRandomNTCPRouter ()));
+				CreateTunnel<InboundTunnel> (
+					new TunnelConfig (std::vector<const i2p::data::RouterInfo *>
+					    {              
+							i2p::data::netdb.GetRandomNTCPRouter ()
+						}));
 			}
 			else
 			{
@@ -404,9 +414,12 @@ namespace tunnel
 				LogPrint ("Creating two hops inbound tunnel...");
 				auto router = outboundTunnel->GetTunnelConfig ()->GetFirstHop ()->router;
 				CreateTunnel<InboundTunnel> (
-					new TunnelConfig (i2p::data::netdb.GetRandomNTCPRouter (), 
-							router != &i2p::context.GetRouterInfo () ? router : i2p::data::netdb.GetRandomNTCPRouter ()),
-				        	outboundTunnel);
+					new TunnelConfig (std::vector<const i2p::data::RouterInfo *>
+						{                
+				            i2p::data::netdb.GetRandomNTCPRouter (), 
+							router != &i2p::context.GetRouterInfo () ? router : i2p::data::netdb.GetRandomNTCPRouter ()
+		                }),                 
+				    outboundTunnel);
 			}	
 		}
 	}	
@@ -457,7 +470,10 @@ namespace tunnel
 	void Tunnels::CreateZeroHopsInboundTunnel ()
 	{
 		CreateTunnel<InboundTunnel> (
-			new TunnelConfig (&i2p::context.GetRouterInfo ()));
+			new TunnelConfig (std::vector<const i2p::data::RouterInfo *>
+			    { 
+					&i2p::context.GetRouterInfo ()
+				}));
 	}	
 	
 	OutboundTunnel * Tunnels::CreateOneHopOutboundTestTunnel (InboundTunnel * replyTunnel)
@@ -471,7 +487,9 @@ namespace tunnel
 		if (peer)
 		{
 			const i2p::data::RouterInfo& router = peer->GetRemoteRouterInfo ();
-			return CreateTunnel<InboundTunnel> (new TunnelConfig (&router), outboundTunnel);
+			return CreateTunnel<InboundTunnel> (
+				new TunnelConfig (std::vector<const i2p::data::RouterInfo *>{&router}), 
+			    outboundTunnel);
 		}	
 		else
 			LogPrint ("No established peers");
@@ -490,7 +508,11 @@ namespace tunnel
 		{
 			const i2p::data::RouterInfo& router = peer->GetRemoteRouterInfo ();
 			return  CreateTunnel<InboundTunnel> (
-						new TunnelConfig (&router, &i2p::context.GetRouterInfo ()),
+						new TunnelConfig (std::vector<const i2p::data::RouterInfo *>
+						    { 
+								&router, 
+								&i2p::context.GetRouterInfo ()
+							}),
 			            	outboundTunnel);		
 		}
 		else
diff --git a/TunnelConfig.h b/TunnelConfig.h
index 653fe56d..29fb8575 100644
--- a/TunnelConfig.h
+++ b/TunnelConfig.h
@@ -3,6 +3,7 @@
 
 #include <inttypes.h>
 #include <sstream>
+#include <vector>
 #include "RouterInfo.h"
 #include "RouterContext.h"
 
@@ -83,34 +84,30 @@ namespace tunnel
 	{
 		public:			
 			
-			TunnelConfig (const i2p::data::RouterInfo * peer, TunnelConfig * replyTunnelConfig = 0) // one hop 
+
+			TunnelConfig (std::vector<const i2p::data::RouterInfo *> peers, 
+				TunnelConfig * replyTunnelConfig = 0) // replyTunnelConfig=0 means inbound
 			{
-				m_FirstHop = new TunnelHopConfig (peer);
-				m_LastHop = m_FirstHop;	
+				TunnelHopConfig * prev = nullptr;
+				for (auto it: peers)
+				{
+					auto hop = new TunnelHopConfig (it);
+					if (prev)
+						prev->SetNext (hop);
+					else	
+						m_FirstHop = hop;
+					prev = hop;
+				}	
+				m_LastHop = prev;
 				
 				if (replyTunnelConfig) // outbound
-				{	
-					m_FirstHop->isGateway = false;
-					m_LastHop->SetReplyHop (replyTunnelConfig->GetFirstHop ());
-				}	
-				else
-					m_FirstHop->SetNextRouter (&i2p::context.GetRouterInfo ());
- 			}
-
-			TunnelConfig (const i2p::data::RouterInfo * peer1, const i2p::data::RouterInfo * peer2, TunnelConfig * replyTunnelConfig = 0) // two hops 
-			{
-				m_FirstHop = new TunnelHopConfig (peer1);
-				m_LastHop = new TunnelHopConfig (peer2);
-				m_FirstHop->SetNext (m_LastHop);
-				
-				if (replyTunnelConfig)
 				{
 					m_FirstHop->isGateway = false;
 					m_LastHop->SetReplyHop (replyTunnelConfig->GetFirstHop ());
 				}	
-				else
+				else // inbound
 					m_LastHop->SetNextRouter (&i2p::context.GetRouterInfo ());
-			}	
+			}
 			
 			~TunnelConfig ()
 			{

From 57c97208f9da13e7a6fba2e7c3da8110ff466959 Mon Sep 17 00:00:00 2001
From: orignal <romakoshelkin@yandex.ru>
Date: Wed, 8 Jan 2014 22:47:22 -0500
Subject: [PATCH 16/16] include DeliveryStatus to Garlic

---
 Garlic.cpp       | 54 ++++++++++++++++++++++++++++++++++++++++++------
 Garlic.h         |  1 +
 I2NPProtocol.cpp |  4 ++--
 I2NPProtocol.h   |  2 +-
 LeaseSet.cpp     | 16 +++++++++++++-
 Streaming.cpp    | 19 +++++++++++++++--
 Streaming.h      | 11 ++++++++--
 7 files changed, 93 insertions(+), 14 deletions(-)

diff --git a/Garlic.cpp b/Garlic.cpp
index 202949bb..ec52550c 100644
--- a/Garlic.cpp
+++ b/Garlic.cpp
@@ -4,6 +4,8 @@
 #include <string>
 #include "ElGamal.h"
 #include "RouterContext.h"
+#include "I2NPProtocol.h"
+#include "Tunnel.h"
 #include "Timestamp.h"
 #include "Streaming.h"
 #include "Garlic.h"
@@ -65,8 +67,6 @@ namespace garlic
 		FillI2NPMessageHeader (m, eI2NPGarlic);
 		if (msg)
 			DeleteI2NPMessage (msg);
-		if (leaseSet)
-			DeleteI2NPMessage (leaseSet);
 		return m;
 	}	
 
@@ -97,17 +97,23 @@ namespace garlic
 	size_t GarlicRoutingSession::CreateGarlicPayload (uint8_t * payload, I2NPMessage * msg, I2NPMessage * leaseSet)
 	{
 		uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 5000; // 5 sec
+		uint32_t msgID = m_Rnd.GenerateWord32 ();
 		size_t size = 0;
 		uint8_t * numCloves = payload + size;
 		*numCloves = 0;
 		size++;
+		
+		if (leaseSet) 
+		{
+			// clove is DeliveryStatus is LeaseSet is presented
+			size += CreateDeliveryStatusClove (payload + size, msgID);
+			(*numCloves)++;
 
-		if (leaseSet) // first clove is our leaseSet if presented
-		{	
+			// clove is our leaseSet if presented
 			size += CreateGarlicClove (payload + size, leaseSet, false);
 			(*numCloves)++;
 		}	
-		if (msg) // next clove message ifself if presented
+		if (msg) // clove message ifself if presented
 		{	
 			size += CreateGarlicClove (payload + size, msg, m_Destination->IsDestination ());
 			(*numCloves)++;
@@ -115,7 +121,7 @@ namespace garlic
 		
 		memset (payload + size, 0, 3); // certificate of message
 		size += 3;
-		*(uint32_t *)(payload + size) = htobe32 (m_Rnd.GenerateWord32 ()); // MessageID
+		*(uint32_t *)(payload + size) = htobe32 (msgID); // MessageID
 		size += 4;
 		*(uint64_t *)(payload + size) = htobe64 (ts); // Expiration of message
 		size += 8;
@@ -149,6 +155,42 @@ namespace garlic
 		size += 3;
 		return size;
 	}	
+
+	size_t GarlicRoutingSession::CreateDeliveryStatusClove (uint8_t * buf, uint32_t msgID)
+	{		
+		size_t size = 0;
+		auto tunnel = i2p::tunnel::tunnels.GetNextInboundTunnel ();
+		if (tunnel)
+		{	
+			buf[size] = eGarlicDeliveryTypeTunnel << 5; // delivery instructions flag tunnel
+			size++;
+			*(uint32_t *)(buf + size) = htobe32 (tunnel->GetNextTunnelID ()); // tunnelID
+			size += 4; 
+			memcpy (buf + size, tunnel->GetNextIdentHash (), 32); // To Hash
+			size += 32;
+		}
+		else	
+		{	
+			LogPrint ("No reply tunnels for garlic DeliveryStatus found");
+			buf[size] = 0;//  delivery instructions flag local
+			size++;
+		}
+			
+		
+		I2NPMessage * msg = CreateDeliveryStatusMsg (msgID);
+		memcpy (buf + size, msg->GetBuffer (), msg->GetLength ());
+		size += msg->GetLength ();
+		DeleteI2NPMessage (msg);
+		uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 5000; // 5 sec
+		*(uint32_t *)(buf + size) = htobe32 (m_Rnd.GenerateWord32 ()); // CloveID
+		size += 4;
+		*(uint64_t *)(buf + size) = htobe64 (ts); // Expiration of clove
+		size += 8;
+		memset (buf + size, 0, 3); // certificate of clove
+		size += 3;
+		
+		return size;
+	}
 		
 	GarlicRouting routing;	
 	GarlicRouting::GarlicRouting ()
diff --git a/Garlic.h b/Garlic.h
index 536ed173..81675fa9 100644
--- a/Garlic.h
+++ b/Garlic.h
@@ -48,6 +48,7 @@ namespace garlic
 			size_t CreateAESBlock (uint8_t * buf, I2NPMessage * msg, I2NPMessage * leaseSet);
 			size_t CreateGarlicPayload (uint8_t * payload, I2NPMessage * msg, I2NPMessage * leaseSet);
 			size_t CreateGarlicClove (uint8_t * buf, I2NPMessage * msg, bool isDestination);
+			size_t CreateDeliveryStatusClove (uint8_t * buf, uint32_t msgID);
 			
 		private:
 
diff --git a/I2NPProtocol.cpp b/I2NPProtocol.cpp
index 11ccd757..44862dc0 100644
--- a/I2NPProtocol.cpp
+++ b/I2NPProtocol.cpp
@@ -67,7 +67,7 @@ namespace i2p
 		return msg;
 	}	
 	
-	I2NPMessage * CreateDeliveryStatusMsg ()
+	I2NPMessage * CreateDeliveryStatusMsg (uint32_t msgID)
 	{
 #pragma pack(1)		
 		struct
@@ -77,7 +77,7 @@ namespace i2p
 		} msg;
 #pragma pack ()
 		
-		msg.msgID = 0;
+		msg.msgID = htobe32 (msgID);
 		msg.timestamp = htobe64 (i2p::util::GetMillisecondsSinceEpoch ());
 		return CreateI2NPMessage (eI2NPDeliveryStatus, (uint8_t *)&msg, sizeof (msg));
 	}
diff --git a/I2NPProtocol.h b/I2NPProtocol.h
index 71b93525..ebba0c5e 100644
--- a/I2NPProtocol.h
+++ b/I2NPProtocol.h
@@ -103,7 +103,7 @@ namespace i2p
 	I2NPMessage * CreateI2NPMessage (I2NPMessageType msgType, const uint8_t * buf, int len, uint32_t replyMsgID = 0);	
 	I2NPMessage * CreateI2NPMessage (const uint8_t * buf, int len);
 	
-	I2NPMessage * CreateDeliveryStatusMsg ();
+	I2NPMessage * CreateDeliveryStatusMsg (uint32_t msgID);
 	I2NPMessage * CreateDatabaseLookupMsg (const uint8_t * key, const uint8_t * from, 
 		uint32_t replyTunnelID, bool exploratory = false, 
 	    std::set<i2p::data::IdentHash> * excludedPeers = nullptr);
diff --git a/LeaseSet.cpp b/LeaseSet.cpp
index 9a646e98..d7939d2d 100644
--- a/LeaseSet.cpp
+++ b/LeaseSet.cpp
@@ -1,3 +1,5 @@
+#include <cryptopp/dsa.h>
+#include "CryptoConst.h"
 #include "Log.h"
 #include "LeaseSet.h"
 
@@ -24,10 +26,22 @@ namespace data
 		memcpy (m_EncryptionKey, header->encryptionKey, 256);
 		LogPrint ("LeaseSet num=", (int)header->num);
 
+		const uint8_t * leases = buf + sizeof (H);
 		for (int i = 0; i < header->num; i++)
 		{
-			m_Leases.push_back (*(Lease *)(buf + sizeof (H)));
+			Lease lease = *(Lease *)leases;
+			lease.tunnelID = be32toh (lease.tunnelID);
+			m_Leases.push_back (lease);
+			leases += sizeof (Lease);
 		}	
+
+		// verify
+		CryptoPP::DSA::PublicKey pubKey;
+		pubKey.Initialize (i2p::crypto::dsap, i2p::crypto::dsaq, i2p::crypto::dsag, 
+			CryptoPP::Integer (m_Identity.signingKey, 128));
+		CryptoPP::DSA::Verifier verifier (pubKey);
+		if (!verifier.VerifyMessage (buf, leases - buf, leases, 40))
+			LogPrint ("LeaseSet verification failed");
 	}	
 }		
 }	
diff --git a/Streaming.cpp b/Streaming.cpp
index 05d9933c..73f2ff8a 100644
--- a/Streaming.cpp
+++ b/Streaming.cpp
@@ -91,7 +91,7 @@ namespace stream
 		size += len; // payload
 		m_LocalDestination->Sign (packet, size, signature);
 		I2NPMessage * msg = i2p::garlic::routing.WrapSingleMessage (m_RemoteLeaseSet, 
-			CreateDataMessage (this, packet, size), m_LocalDestination->CreateLeaseSet ()); 
+			CreateDataMessage (this, packet, size), m_LocalDestination->GetLeaseSet ()); 
 
 		auto outbound = i2p::tunnel::tunnels.GetNextOutboundTunnel ();
 		if (outbound)
@@ -106,7 +106,7 @@ namespace stream
 		
 	StreamingDestination * sharedLocalDestination = nullptr;	
 
-	StreamingDestination::StreamingDestination ()
+	StreamingDestination::StreamingDestination (): m_LeaseSet (nullptr)
 	{		
 		// TODO: read from file later
 		m_Keys = i2p::data::CreateRandomKeys ();
@@ -115,6 +115,12 @@ namespace stream
 		m_SigningPrivateKey.Initialize (i2p::crypto::dsap, i2p::crypto::dsaq, i2p::crypto::dsag, 
 			CryptoPP::Integer (m_Keys.signingPrivateKey, 20));
 	}
+
+	StreamingDestination::~StreamingDestination ()
+	{
+		if (m_LeaseSet)
+			DeleteI2NPMessage (m_LeaseSet);
+	}	
 		
 	void StreamingDestination::HandleNextPacket (const uint8_t * buf, size_t len)
 	{
@@ -141,6 +147,15 @@ namespace stream
 			delete stream;
 		}	
 	}	
+
+	I2NPMessage * StreamingDestination::GetLeaseSet ()
+	{
+		if (!m_LeaseSet)
+			m_LeaseSet = CreateLeaseSet ();
+		else
+			FillI2NPMessageHeader (m_LeaseSet, eI2NPDatabaseStore); // refresh msgID
+		return m_LeaseSet;
+	}	
 		
 	I2NPMessage * StreamingDestination::CreateLeaseSet () const
 	{
diff --git a/Streaming.h b/Streaming.h
index c3c97bd8..86aeac31 100644
--- a/Streaming.h
+++ b/Streaming.h
@@ -52,15 +52,20 @@ namespace stream
 		public:
 
 			StreamingDestination ();
-
+			~StreamingDestination ();
+			
 			const i2p::data::Keys& GetKeys () const { return m_Keys; };
 			const i2p::data::Identity& GetIdentity () const { return m_Identity; }; 
-			I2NPMessage * CreateLeaseSet () const;
+			I2NPMessage * GetLeaseSet ();
 			void Sign (uint8_t * buf, int len, uint8_t * signature) const;
 			
 			Stream * CreateNewStream (const i2p::data::LeaseSet * remote);
 			void DeleteStream (Stream * stream);
 			void HandleNextPacket (const uint8_t * buf, size_t len);
+
+		private:
+
+			I2NPMessage * CreateLeaseSet () const;
 			
 		private:
 
@@ -69,6 +74,8 @@ namespace stream
 			i2p::data::Identity m_Identity;
 			i2p::data::IdentHash m_IdentHash;
 
+			I2NPMessage * m_LeaseSet;
+			
 			CryptoPP::DSA::PrivateKey m_SigningPrivateKey;
 	};