From 31f906240e1390f8d73e19c8825520a91f187ff8 Mon Sep 17 00:00:00 2001 From: orignal Date: Wed, 9 Apr 2014 11:58:57 -0400 Subject: [PATCH 01/15] introducer session --- SSU.cpp | 97 ++++++++++++++++++++++++++------------------------------- SSU.h | 8 ++--- 2 files changed, 48 insertions(+), 57 deletions(-) diff --git a/SSU.cpp b/SSU.cpp index a036d1ff..1af2aa7c 100644 --- a/SSU.cpp +++ b/SSU.cpp @@ -75,18 +75,14 @@ namespace ssu case eSessionRelayRequestSent: // relay response ProcessRelayResponse (buf,len); + m_Server.DeleteSession (this); break; - case eSessionRelayResponseReceived: + case eSessionIntroduced: // HolePunch received LogPrint ("SSU HolePuch of ", len, " bytes received"); - m_State = eSessionStateEstablished; - Established (); - break; - case eSessionRelayRequestReceived: - // HolePunch m_State = eSessionStateUnknown; Connect (); - break; + break; default: LogPrint ("SSU state not implemented yet"); } @@ -151,7 +147,7 @@ namespace ssu if (!Validate (buf, len, introKey)) { LogPrint ("MAC verification intro key failed"); - Failed (); + m_Server.DeleteSession (this); return; } @@ -273,7 +269,7 @@ namespace ssu m_Server.Send (buf, 304, m_RemoteEndpoint); } - void SSUSession::SendRelayRequest (const i2p::data::RouterInfo::Introducer& introducer) + void SSUSession::SendRelayRequest (uint32_t iTag, const uint8_t * iKey) { auto address = i2p::context.GetRouterInfo ().GetSSUAddress (); if (!address) @@ -284,7 +280,7 @@ namespace ssu uint8_t buf[96 + 18]; uint8_t * payload = buf + sizeof (SSUHeader); - *(uint32_t *)payload = htobe32 (introducer.iTag); + *(uint32_t *)payload = htobe32 (iTag); payload += 4; *payload = 0; // no address payload++; @@ -299,7 +295,7 @@ namespace ssu uint8_t iv[16]; rnd.GenerateBlock (iv, 16); // random iv - FillHeaderAndEncrypt (PAYLOAD_TYPE_RELAY_REQUEST, buf, 96, introducer.iKey, iv, introducer.iKey); + FillHeaderAndEncrypt (PAYLOAD_TYPE_RELAY_REQUEST, buf, 96, iKey, iv, iKey); m_State = eSessionRelayRequestSent; m_Server.Send (buf, 96, m_RemoteEndpoint); } @@ -404,27 +400,22 @@ namespace ssu { Decrypt (buf, len, address->key); SSUHeader * header = (SSUHeader *)buf; - if ((header->flag >> 4) == PAYLOAD_TYPE_RELAY_RESPONSE) + if (header->GetPayloadType () == PAYLOAD_TYPE_RELAY_RESPONSE) { LogPrint ("Relay response received"); - m_State = eSessionRelayRequestReceived; uint8_t * payload = buf + sizeof (SSUHeader); - payload++; - boost::asio::ip::address_v4 remoteIP (be32toh (*(uint32_t* )(payload))); - payload += 4; - uint16_t remotePort = be16toh (*(uint16_t *)(payload)); - payload += 2; - boost::asio::ip::udp::endpoint newRemoteEndpoint(remoteIP, remotePort); - m_Server.ReassignSession (m_RemoteEndpoint, newRemoteEndpoint); - m_RemoteEndpoint = newRemoteEndpoint; - payload++; + payload++; // remote size + //boost::asio::ip::address_v4 remoteIP (be32toh (*(uint32_t* )(payload))); + payload += 4; // remote address + //uint16_t remotePort = be16toh (*(uint16_t *)(payload)); + payload += 2; // remote port + payload++; // our size boost::asio::ip::address_v4 ourIP (be32toh (*(uint32_t* )(payload))); - payload += 4; + payload += 4; // our address uint16_t ourPort = be16toh (*(uint16_t *)(payload)); - payload += 2; + payload += 2; // our port LogPrint ("Our external address is ", ourIP.to_string (), ":", ourPort); i2p::context.UpdateAddress (ourIP.to_string ().c_str ()); - m_State= eSessionRelayResponseReceived; } else LogPrint ("Unexpected payload type ", (int)(header->flag >> 4)); @@ -524,7 +515,7 @@ namespace ssu } } - void SSUSession::ConnectThroughIntroducer (const i2p::data::RouterInfo::Introducer& introducer) + void SSUSession::Introduce (uint32_t iTag, const uint8_t * iKey) { if (m_State == eSessionStateUnknown) { @@ -532,10 +523,19 @@ namespace ssu m_Timer.expires_from_now (boost::posix_time::seconds(SSU_CONNECT_TIMEOUT)); m_Timer.async_wait (boost::bind (&SSUSession::HandleConnectTimer, this, boost::asio::placeholders::error)); - SendRelayRequest (introducer); + SendRelayRequest (iTag, iKey); } } + void SSUSession::WaitForIntroduction () + { + m_State = eSessionIntroduced; + // set connect timer + m_Timer.expires_from_now (boost::posix_time::seconds(SSU_CONNECT_TIMEOUT)); + m_Timer.async_wait (boost::bind (&SSUSession::HandleConnectTimer, + this, boost::asio::placeholders::error)); + } + void SSUSession::Close () { SendSesionDestroyed (); @@ -977,11 +977,12 @@ namespace ssu else { // otherwise create new session + session = new SSUSession (*this, remoteEndpoint, router, peerTest); + m_Sessions[remoteEndpoint] = session; + if (!router->UsesIntroducer ()) { - // connect directly - session = new SSUSession (*this, remoteEndpoint, router, peerTest); - m_Sessions[remoteEndpoint] = session; + // connect directly LogPrint ("Creating new SSU session to [", router->GetIdentHashAbbreviation (), "] ", remoteEndpoint.address ().to_string (), ":", remoteEndpoint.port ()); session->Connect (); @@ -989,24 +990,27 @@ namespace ssu else { // connect through introducer + session->WaitForIntroduction (); if (address->introducers.size () > 0) { auto& introducer = address->introducers[0]; // TODO: boost::asio::ip::udp::endpoint introducerEndpoint (introducer.iHost, introducer.iPort); - it = m_Sessions.find (introducerEndpoint); - if (it == m_Sessions.end ()) - { - session = new SSUSession (*this, introducerEndpoint, router); - m_Sessions[introducerEndpoint] = session; - LogPrint ("Creating new SSU session to [", router->GetIdentHashAbbreviation (), + LogPrint ("Creating new SSU session to [", router->GetIdentHashAbbreviation (), "] through introducer ", introducerEndpoint.address ().to_string (), ":", introducerEndpoint.port ()); - session->ConnectThroughIntroducer (introducer); - } - else + it = m_Sessions.find (introducerEndpoint); + SSUSession * introducerSession = nullptr; + if (it != m_Sessions.end ()) { LogPrint ("Session to introducer already exists"); - // TODO: + introducerSession = it->second; } + else + { + LogPrint ("New session to introducer created"); + introducerSession = new SSUSession (*this, introducerEndpoint, router); + m_Sessions[introducerEndpoint] = introducerSession; + } + introducerSession->Introduce (introducer.iTag, introducer.iKey); } else LogPrint ("Router is unreachable, but not introducers presentd. Ignored"); @@ -1038,19 +1042,6 @@ namespace ssu } m_Sessions.clear (); } - - void SSUServer::ReassignSession (const boost::asio::ip::udp::endpoint& oldEndpoint, const boost::asio::ip::udp::endpoint& newEndpoint) - { - auto it = m_Sessions.find (oldEndpoint); - if (it != m_Sessions.end ()) - { - auto session = it->second; - m_Sessions.erase (it); - m_Sessions[newEndpoint] = session; - LogPrint ("SSU session reassigned from ", oldEndpoint.address ().to_string (), ":", oldEndpoint.port (), - " to ", newEndpoint.address ().to_string (), ":", newEndpoint.port ()); - } - } } } diff --git a/SSU.h b/SSU.h index ab60c71b..e41131b7 100644 --- a/SSU.h +++ b/SSU.h @@ -62,7 +62,7 @@ namespace ssu eSessionStateConfirmedReceived, eSessionRelayRequestSent, eSessionRelayRequestReceived, - eSessionRelayResponseReceived, + eSessionIntroduced, eSessionStateEstablished, eSessionStateFailed }; @@ -78,7 +78,8 @@ namespace ssu ~SSUSession (); void Connect (); - void ConnectThroughIntroducer (const i2p::data::RouterInfo::Introducer& introducer); + void Introduce (uint32_t iTag, const uint8_t * iKey); + void WaitForIntroduction (); void Close (); boost::asio::ip::udp::endpoint& GetRemoteEndpoint () { return m_RemoteEndpoint; }; const i2p::data::RouterInfo * GetRemoteRouter () const { return m_RemoteRouter; }; @@ -94,7 +95,7 @@ namespace ssu void ProcessSessionRequest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); void SendSessionRequest (); - void SendRelayRequest (const i2p::data::RouterInfo::Introducer& introducer); + void SendRelayRequest (uint32_t iTag, const uint8_t * iKey); void ProcessSessionCreated (uint8_t * buf, size_t len); void SendSessionCreated (const uint8_t * x); void ProcessSessionConfirmed (uint8_t * buf, size_t len); @@ -153,7 +154,6 @@ namespace ssu boost::asio::io_service& GetService () { return m_Socket.get_io_service(); }; const boost::asio::ip::udp::endpoint& GetEndpoint () const { return m_Endpoint; }; void Send (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& to); - void ReassignSession (const boost::asio::ip::udp::endpoint& oldEndpoint, const boost::asio::ip::udp::endpoint& newEndpoint); private: From 73066eb6c67cc10b0d7b80a521e2529862c71f86 Mon Sep 17 00:00:00 2001 From: orignal Date: Wed, 9 Apr 2014 12:25:40 -0400 Subject: [PATCH 02/15] send relay request through connected session --- SSU.cpp | 21 +++++++++++++++------ SSU.h | 6 +++--- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/SSU.cpp b/SSU.cpp index 1af2aa7c..c8db6437 100644 --- a/SSU.cpp +++ b/SSU.cpp @@ -72,12 +72,12 @@ namespace ssu // session confirmed ProcessSessionConfirmed (buf, len); break; - case eSessionRelayRequestSent: + case eSessionStateRelayRequestSent: // relay response - ProcessRelayResponse (buf,len); + ProcessRelayResponse (buf, len); m_Server.DeleteSession (this); break; - case eSessionIntroduced: + case eSessionStateIntroduced: // HolePunch received LogPrint ("SSU HolePuch of ", len, " bytes received"); m_State = eSessionStateUnknown; @@ -110,6 +110,10 @@ namespace ssu m_Server.DeleteSession (this); // delete this break; } + case PAYLOAD_TYPE_RELAY_RESPONSE: + LogPrint ("SSU relay response received though established session"); + // Ignore it for now + break; case PAYLOAD_TYPE_RELAY_INTRO: LogPrint ("SSU relay intro received"); ProcessRelayIntro (buf + sizeof (SSUHeader), len - sizeof (SSUHeader)); @@ -295,8 +299,13 @@ namespace ssu uint8_t iv[16]; rnd.GenerateBlock (iv, 16); // random iv - FillHeaderAndEncrypt (PAYLOAD_TYPE_RELAY_REQUEST, buf, 96, iKey, iv, iKey); - m_State = eSessionRelayRequestSent; + if (m_State == eSessionStateEstablished) + FillHeaderAndEncrypt (PAYLOAD_TYPE_RELAY_REQUEST, buf, 96, m_SessionKey, iv, m_MacKey); + else + { + FillHeaderAndEncrypt (PAYLOAD_TYPE_RELAY_REQUEST, buf, 96, iKey, iv, iKey); + m_State = eSessionStateRelayRequestSent; + } m_Server.Send (buf, 96, m_RemoteEndpoint); } @@ -529,7 +538,7 @@ namespace ssu void SSUSession::WaitForIntroduction () { - m_State = eSessionIntroduced; + m_State = eSessionStateIntroduced; // set connect timer m_Timer.expires_from_now (boost::posix_time::seconds(SSU_CONNECT_TIMEOUT)); m_Timer.async_wait (boost::bind (&SSUSession::HandleConnectTimer, diff --git a/SSU.h b/SSU.h index e41131b7..03e889ea 100644 --- a/SSU.h +++ b/SSU.h @@ -60,9 +60,9 @@ namespace ssu eSessionStateCreatedReceived, eSessionStateConfirmedSent, eSessionStateConfirmedReceived, - eSessionRelayRequestSent, - eSessionRelayRequestReceived, - eSessionIntroduced, + eSessionStateRelayRequestSent, + eSessionStateRelayRequestReceived, + eSessionStateIntroduced, eSessionStateEstablished, eSessionStateFailed }; From 5099b997ad53a9041b63423c1b98169b60585c26 Mon Sep 17 00:00:00 2001 From: orignal Date: Wed, 9 Apr 2014 13:55:31 -0400 Subject: [PATCH 03/15] delete session with introducer --- SSU.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/SSU.cpp b/SSU.cpp index c8db6437..d2efc892 100644 --- a/SSU.cpp +++ b/SSU.cpp @@ -425,6 +425,7 @@ namespace ssu payload += 2; // our port LogPrint ("Our external address is ", ourIP.to_string (), ":", ourPort); i2p::context.UpdateAddress (ourIP.to_string ().c_str ()); + m_Server.DeleteSession (this); // we don't need this session anymore } else LogPrint ("Unexpected payload type ", (int)(header->flag >> 4)); From d09bafa4a75547fac59995cbab0e3bb6d43eec3b Mon Sep 17 00:00:00 2001 From: orignal Date: Wed, 9 Apr 2014 14:58:30 -0400 Subject: [PATCH 04/15] save peer test nonces --- SSU.cpp | 37 +++++++++++++++++++++++++++++-------- SSU.h | 2 ++ 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/SSU.cpp b/SSU.cpp index d2efc892..3932b37f 100644 --- a/SSU.cpp +++ b/SSU.cpp @@ -733,19 +733,40 @@ namespace ssu uint16_t port = *(uint16_t *)buf; // use it as is buf += 2; // port uint8_t * introKey = buf; - if (port) + if (port && !address) { - LogPrint ("SSU peer test. We are Charlie"); - Send (PAYLOAD_TYPE_PEER_TEST, buf1, len); // back to Bob - if (address) - SendPeerTest (nonce, be32toh (*(uint32_t *)address), be16toh (port), introKey); // to Alice + LogPrint ("Address of ", size, " bytes not supported"); + return; + } + if (m_PeerTestNonces.count (nonce) > 0) + { + // existing test + if (port) + { + LogPrint ("SSU peer test from Charlie. We are Bob"); + // TODO: back to Alice + } else - LogPrint ("Address of ", size, " bytes not supported"); + { + LogPrint ("SSU peer test from Alice. We are Charlie"); + SendPeerTest (nonce, be32toh (*(uint32_t *)address), be16toh (port), introKey); // to Alice + } } else { - LogPrint ("SSU peer test. We are Bob"); - // TODO: + // new test + m_PeerTestNonces.insert (nonce); + if (port) + { + LogPrint ("SSU peer test from Bob. We are Charlie"); + Send (PAYLOAD_TYPE_PEER_TEST, buf1, len); // back to Bob + SendPeerTest (nonce, be32toh (*(uint32_t *)address), be16toh (port), introKey); // to Alice + } + else + { + LogPrint ("SSU peer test from Alice. We are Bob"); + // TODO: find Charlie + } } } diff --git a/SSU.h b/SSU.h index 03e889ea..334fd710 100644 --- a/SSU.h +++ b/SSU.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -131,6 +132,7 @@ namespace ssu bool m_PeerTest; SessionState m_State; uint32_t m_RelayTag; + std::set m_PeerTestNonces; CryptoPP::CBC_Mode::Encryption m_Encryption; CryptoPP::CBC_Mode::Decryption m_Decryption; uint8_t m_SessionKey[32], m_MacKey[32]; From e96c55bd98fb5a9a8eb94e565ed67d82bb02aed6 Mon Sep 17 00:00:00 2001 From: orignal Date: Wed, 9 Apr 2014 18:17:13 -0400 Subject: [PATCH 05/15] fixed crash --- SSU.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/SSU.cpp b/SSU.cpp index 3932b37f..efcf3a74 100644 --- a/SSU.cpp +++ b/SSU.cpp @@ -425,7 +425,6 @@ namespace ssu payload += 2; // our port LogPrint ("Our external address is ", ourIP.to_string (), ":", ourPort); i2p::context.UpdateAddress (ourIP.to_string ().c_str ()); - m_Server.DeleteSession (this); // we don't need this session anymore } else LogPrint ("Unexpected payload type ", (int)(header->flag >> 4)); @@ -532,9 +531,9 @@ namespace ssu // set connect timer m_Timer.expires_from_now (boost::posix_time::seconds(SSU_CONNECT_TIMEOUT)); m_Timer.async_wait (boost::bind (&SSUSession::HandleConnectTimer, - this, boost::asio::placeholders::error)); - SendRelayRequest (iTag, iKey); - } + this, boost::asio::placeholders::error)); + } + SendRelayRequest (iTag, iKey); } void SSUSession::WaitForIntroduction () From 2140f49665ce2c6622d7f90452fbacc4ff8206f7 Mon Sep 17 00:00:00 2001 From: orignal Date: Wed, 9 Apr 2014 22:35:35 -0400 Subject: [PATCH 06/15] fixed crash --- SSU.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SSU.cpp b/SSU.cpp index efcf3a74..ac9a02b0 100644 --- a/SSU.cpp +++ b/SSU.cpp @@ -748,7 +748,7 @@ namespace ssu else { LogPrint ("SSU peer test from Alice. We are Charlie"); - SendPeerTest (nonce, be32toh (*(uint32_t *)address), be16toh (port), introKey); // to Alice + //SendPeerTest (nonce, be32toh (*(uint32_t *)address), be16toh (port), introKey); // to Alice } } else From 747383a5e651e6380f3df7ed9ad4992701123c95 Mon Sep 17 00:00:00 2001 From: orignal Date: Thu, 10 Apr 2014 14:13:15 -0400 Subject: [PATCH 07/15] check if session key is available --- SSU.cpp | 8 ++++---- SSU.h | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/SSU.cpp b/SSU.cpp index ac9a02b0..30298269 100644 --- a/SSU.cpp +++ b/SSU.cpp @@ -843,11 +843,11 @@ namespace ssu void SSUSession::SendSesionDestroyed () { - uint8_t buf[48 + 18], iv[16]; - CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); - rnd.GenerateBlock (iv, 16); // random iv - if (m_State == eSessionStateEstablished) + if (HasSessionKey ()) { + uint8_t buf[48 + 18], iv[16]; + CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); + rnd.GenerateBlock (iv, 16); // random iv // encrypt message with session key FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_DESTROYED, buf, 48, m_SessionKey, iv, m_MacKey); m_Server.Send (buf, 48, m_RemoteEndpoint); diff --git a/SSU.h b/SSU.h index 334fd710..3f7ac96b 100644 --- a/SSU.h +++ b/SSU.h @@ -118,6 +118,7 @@ namespace ssu void Decrypt (uint8_t * buf, size_t len, const uint8_t * aesKey); bool Validate (uint8_t * buf, size_t len, const uint8_t * macKey); const uint8_t * GetIntroKey () const; + bool HasSessionKey () const { return m_State == eSessionStateCreatedReceived || m_State == eSessionStateRequestReceived; }; void ScheduleTermination (); void HandleTerminationTimer (const boost::system::error_code& ecode); From 9c7039cf783e18a82047875123e7894589fb28a7 Mon Sep 17 00:00:00 2001 From: orignal Date: Thu, 10 Apr 2014 14:41:34 -0400 Subject: [PATCH 08/15] recognize PeerTest back from Bob to Alice --- SSU.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/SSU.cpp b/SSU.cpp index 30298269..2c84430f 100644 --- a/SSU.cpp +++ b/SSU.cpp @@ -740,7 +740,13 @@ namespace ssu if (m_PeerTestNonces.count (nonce) > 0) { // existing test - if (port) + if (m_PeerTest) + { + LogPrint ("SSU peer test from Bob. We are Alice"); + m_PeerTestNonces.erase (nonce); + m_PeerTest = false; + } + else if (port) { LogPrint ("SSU peer test from Charlie. We are Bob"); // TODO: back to Alice @@ -807,6 +813,7 @@ namespace ssu CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); uint32_t nonce = 0; rnd.GenerateWord32 (nonce); + m_PeerTestNonces.insert (nonce); *(uint32_t *)payload = htobe32 (nonce); payload += 4; // nonce *payload = 4; From c9c38cccf6c070f3cd477c3e6dfeebb2c69e1d40 Mon Sep 17 00:00:00 2001 From: orignal Date: Thu, 10 Apr 2014 14:46:52 -0400 Subject: [PATCH 09/15] send PeerTest if partly has 'B' capability only --- SSU.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SSU.cpp b/SSU.cpp index 2c84430f..d033ec36 100644 --- a/SSU.cpp +++ b/SSU.cpp @@ -565,7 +565,7 @@ namespace ssu Send (it); m_DelayedMessages.clear (); } - if (m_PeerTest) + if (m_PeerTest && (m_RemoteRouter && m_RemoteRouter->IsPeerTesting ())) SendPeerTest (); ScheduleTermination (); } From 3cf256b7694c0581ff65cd83c205340a849e2c72 Mon Sep 17 00:00:00 2001 From: orignal Date: Thu, 10 Apr 2014 15:34:51 -0400 Subject: [PATCH 10/15] removed sync Receive operation --- Streaming.cpp | 14 -------------- Streaming.h | 2 +- 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/Streaming.cpp b/Streaming.cpp index 090a0d04..f8c63378 100644 --- a/Streaming.cpp +++ b/Streaming.cpp @@ -242,20 +242,6 @@ namespace stream m_ReceiveQueue.WakeUp (); } } - - size_t Stream::Receive (uint8_t * buf, size_t len, int timeout) - { - if (!m_IsOpen) return 0; - if (m_ReceiveQueue.IsEmpty ()) - { - if (!timeout) return 0; - if (!m_ReceiveQueue.Wait (timeout, 0)) - return 0; - } - - // either non-empty or we have received something - return ConcatenatePackets (buf, len); - } size_t Stream::ConcatenatePackets (uint8_t * buf, size_t len) { diff --git a/Streaming.h b/Streaming.h index b1706e4a..4d597345 100644 --- a/Streaming.h +++ b/Streaming.h @@ -80,7 +80,7 @@ namespace stream void HandleNextPacket (Packet * packet); size_t Send (uint8_t * buf, size_t len, int timeout); // timeout in seconds - size_t Receive (uint8_t * buf, size_t len, int timeout = 0); // returns 0 if timeout expired + template void AsyncReceive (const Buffer& buffer, ReceiveHandler handler, int timeout = 0); From e1027ffb7b2ca580de68b0b8804bbcbad28e7931 Mon Sep 17 00:00:00 2001 From: orignal Date: Fri, 11 Apr 2014 21:13:52 -0400 Subject: [PATCH 11/15] replace ReceiveQueue to std::queue --- Streaming.cpp | 35 ++++++++++++++++------------------- Streaming.h | 6 +++--- 2 files changed, 19 insertions(+), 22 deletions(-) diff --git a/Streaming.cpp b/Streaming.cpp index f8c63378..cb0cc285 100644 --- a/Streaming.cpp +++ b/Streaming.cpp @@ -28,8 +28,12 @@ namespace stream Stream::~Stream () { m_ReceiveTimer.cancel (); - while (auto packet = m_ReceiveQueue.Get ()) + while (!m_ReceiveQueue.empty ()) + { + auto packet = m_ReceiveQueue.front (); + m_ReceiveQueue.pop (); delete packet; + } for (auto it: m_SavedPackets) delete it; } @@ -118,7 +122,7 @@ namespace stream packet->offset = packet->GetPayload () - packet->buf; if (packet->GetLength () > 0) { - m_ReceiveQueue.Put (packet); + m_ReceiveQueue.push (packet); m_ReceiveTimer.cancel (); } else @@ -131,7 +135,6 @@ namespace stream LogPrint ("Closed"); SendQuickAck (); // send ack for close explicitly? m_IsOpen = false; - m_ReceiveQueue.WakeUp (); } } @@ -239,30 +242,24 @@ namespace stream if (SendPacket (packet, size)) LogPrint ("FIN sent"); - m_ReceiveQueue.WakeUp (); } } size_t Stream::ConcatenatePackets (uint8_t * buf, size_t len) { size_t pos = 0; - while (pos < len) + while (pos < len && !m_ReceiveQueue.empty ()) { - Packet * packet = m_ReceiveQueue.Peek (); - if (packet) + Packet * packet = m_ReceiveQueue.front (); + size_t l = std::min (packet->GetLength (), len - pos); + memcpy (buf + pos, packet->GetBuffer (), l); + pos += l; + packet->offset += l; + if (!packet->GetLength ()) { - size_t l = std::min (packet->GetLength (), len - pos); - memcpy (buf + pos, packet->GetBuffer (), l); - pos += l; - packet->offset += l; - if (!packet->GetLength ()) - { - m_ReceiveQueue.Get (); - delete packet; - } - } - else // no more data available - break; + m_ReceiveQueue.pop (); + delete packet; + } } return pos; } diff --git a/Streaming.h b/Streaming.h index 4d597345..3a0e5114 100644 --- a/Streaming.h +++ b/Streaming.h @@ -5,12 +5,12 @@ #include #include #include +#include #include #include #include #include #include "I2PEndian.h" -#include "Queue.h" #include "Identity.h" #include "LeaseSet.h" #include "I2NPProtocol.h" @@ -112,7 +112,7 @@ namespace stream StreamingDestination * m_LocalDestination; const i2p::data::LeaseSet& m_RemoteLeaseSet; i2p::data::Lease m_CurrentRemoteLease; - i2p::util::Queue m_ReceiveQueue; + std::queue m_ReceiveQueue; std::set m_SavedPackets; i2p::tunnel::OutboundTunnel * m_OutboundTunnel; boost::asio::deadline_timer m_ReceiveTimer; @@ -204,7 +204,7 @@ namespace stream template void Stream::AsyncReceive (const Buffer& buffer, ReceiveHandler handler, int timeout) { - if (!m_ReceiveQueue.IsEmpty ()) + if (!m_ReceiveQueue.empty ()) { size_t received = ConcatenatePackets (boost::asio::buffer_cast(buffer), boost::asio::buffer_size(buffer)); if (received) From a510e7c2c653dacd3ed753ba126db3319163e1d9 Mon Sep 17 00:00:00 2001 From: orignal Date: Sat, 12 Apr 2014 21:13:30 -0400 Subject: [PATCH 12/15] check for duplicate and missing fragments --- SSU.cpp | 36 ++++++++++++++++++++++++++++++------ SSU.h | 10 +++++++++- 2 files changed, 39 insertions(+), 7 deletions(-) diff --git a/SSU.cpp b/SSU.cpp index d033ec36..69b105f8 100644 --- a/SSU.cpp +++ b/SSU.cpp @@ -27,6 +27,12 @@ namespace ssu SSUSession::~SSUSession () { delete m_DHKeysPair; + for (auto it: m_IncomleteMessages) + if (it.second) + { + DeleteI2NPMessage (it.second->msg); + delete it.second; + } } void SSUSession::CreateAESandMacKey (uint8_t * pubKey, uint8_t * aesKey, uint8_t * macKey) @@ -673,9 +679,29 @@ namespace ssu auto it = m_IncomleteMessages.find (msgID); if (it != m_IncomleteMessages.end ()) { - msg = it->second; - memcpy (msg->buf + msg->len, buf, fragmentSize); - msg->len += fragmentSize; + if (fragmentNum == it->second->nextFragmentNum) + { + // expected fragment + msg = it->second->msg; + memcpy (msg->buf + msg->len, buf, fragmentSize); + msg->len += fragmentSize; + it->second->nextFragmentNum++; + } + else if (fragmentNum < it->second->nextFragmentNum) + // duplicate fragment + LogPrint ("Duplicate fragment ", fragmentNum, " of message ", msgID, ". Ignored"); + else + { + // missing fragment + LogPrint ("Missing fragments from ", it->second->nextFragmentNum, " to ", fragmentNum - 1, " of message ", msgID); + //TODO + } + + if (isLast) + { + delete it->second; + m_IncomleteMessages.erase (it); + } } else // TODO: @@ -691,12 +717,10 @@ namespace ssu if (msg) { if (!fragmentNum && !isLast) - m_IncomleteMessages[msgID] = msg; + m_IncomleteMessages[msgID] = new IncompleteMessage (msg); if (isLast) { SendMsgAck (msgID); - if (fragmentNum > 0) - m_IncomleteMessages.erase (msgID); msg->FromSSU (msgID); if (m_State == eSessionStateEstablished) i2p::HandleI2NPMessage (msg); diff --git a/SSU.h b/SSU.h index 3f7ac96b..8ad3fecf 100644 --- a/SSU.h +++ b/SSU.h @@ -124,6 +124,14 @@ namespace ssu void HandleTerminationTimer (const boost::system::error_code& ecode); private: + + struct IncompleteMessage + { + I2NPMessage * msg; + uint8_t nextFragmentNum; + + IncompleteMessage (I2NPMessage * m): msg (m), nextFragmentNum (1) {}; + }; SSUServer& m_Server; boost::asio::ip::udp::endpoint m_RemoteEndpoint; @@ -137,7 +145,7 @@ namespace ssu CryptoPP::CBC_Mode::Encryption m_Encryption; CryptoPP::CBC_Mode::Decryption m_Decryption; uint8_t m_SessionKey[32], m_MacKey[32]; - std::map m_IncomleteMessages; + std::map m_IncomleteMessages; std::list m_DelayedMessages; }; From 8694ed16fd147384b7de9a61f2b991696e6d4a02 Mon Sep 17 00:00:00 2001 From: orignal Date: Sun, 13 Apr 2014 16:59:54 -0400 Subject: [PATCH 13/15] decrypt message first --- SSU.cpp | 237 ++++++++++++++++++++------------------------------------ SSU.h | 8 +- 2 files changed, 89 insertions(+), 156 deletions(-) diff --git a/SSU.cpp b/SSU.cpp index 69b105f8..9946fa28 100644 --- a/SSU.cpp +++ b/SSU.cpp @@ -19,7 +19,7 @@ namespace ssu const i2p::data::RouterInfo * router, bool peerTest ): m_Server (server), m_RemoteEndpoint (remoteEndpoint), m_RemoteRouter (router), m_Timer (m_Server.GetService ()), m_PeerTest (peerTest), m_State (eSessionStateUnknown), - m_RelayTag (0) + m_IsSessionKey (false), m_RelayTag (0) { m_DHKeysPair = i2p::transports.GetNextDHKeysPair (); } @@ -35,7 +35,7 @@ namespace ssu } } - void SSUSession::CreateAESandMacKey (uint8_t * pubKey, uint8_t * aesKey, uint8_t * macKey) + void SSUSession::CreateAESandMacKey (const uint8_t * pubKey, uint8_t * aesKey, uint8_t * macKey) { CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg); CryptoPP::SecByteBlock secretKey(dh.AgreedValueLength()); @@ -56,128 +56,92 @@ namespace ssu memcpy (aesKey, secretKey, 32); memcpy (macKey, secretKey + 32, 32); } + m_IsSessionKey = true; } void SSUSession::ProcessNextMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint) { - switch (m_State) + if (m_State == eSessionStateIntroduced) { - case eSessionStateConfirmedSent: - case eSessionStateEstablished: - // most common case - ScheduleTermination (); - ProcessMessage (buf, len, senderEndpoint); - break; - // establishing or testing - case eSessionStateUnknown: - case eSessionStateRequestSent: - // we must use intro key - ProcessIntroKeyMessage (buf, len, senderEndpoint); - break; - case eSessionStateCreatedSent: - // session confirmed - ProcessSessionConfirmed (buf, len); - break; - case eSessionStateRelayRequestSent: - // relay response - ProcessRelayResponse (buf, len); - m_Server.DeleteSession (this); - break; - case eSessionStateIntroduced: - // HolePunch received - LogPrint ("SSU HolePuch of ", len, " bytes received"); - m_State = eSessionStateUnknown; - Connect (); - break; - default: - LogPrint ("SSU state not implemented yet"); - } - } - - void SSUSession::ProcessMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint) - { - if (Validate (buf, len, m_MacKey)) - { - Decrypt (buf, len, m_SessionKey); - SSUHeader * header = (SSUHeader *)buf; - switch (header->GetPayloadType ()) - { - case PAYLOAD_TYPE_DATA: - LogPrint ("SSU data received"); - ProcessData (buf + sizeof (SSUHeader), len - sizeof (SSUHeader)); - break; - case PAYLOAD_TYPE_PEER_TEST: - LogPrint ("SSU peer test received"); - ProcessPeerTest (buf + sizeof (SSUHeader), len - sizeof (SSUHeader), senderEndpoint); - break; - case PAYLOAD_TYPE_SESSION_DESTROYED: - { - LogPrint ("SSU session destroy received"); - m_Server.DeleteSession (this); // delete this - break; - } - case PAYLOAD_TYPE_RELAY_RESPONSE: - LogPrint ("SSU relay response received though established session"); - // Ignore it for now - break; - case PAYLOAD_TYPE_RELAY_INTRO: - LogPrint ("SSU relay intro received"); - ProcessRelayIntro (buf + sizeof (SSUHeader), len - sizeof (SSUHeader)); - break; - default: - LogPrint ("Unexpected SSU payload type ", (int)header->GetPayloadType ()); - } + // HolePunch received + LogPrint ("SSU HolePuch of ", len, " bytes received"); + m_State = eSessionStateUnknown; + Connect (); } else { - LogPrint ("MAC key failed. Trying intro key"); - auto introKey = GetIntroKey (); - if (introKey && Validate (buf, len, introKey)) + ScheduleTermination (); + if (m_IsSessionKey && Validate (buf, len, m_MacKey)) // try session key first + Decrypt (buf, len, m_SessionKey); + else { - Decrypt (buf, len, introKey); - SSUHeader * header = (SSUHeader *)buf; - LogPrint ("Unexpected SSU payload type ", (int)(header->flag >> 4)); - // TODO: + // try intro key depending on side + auto introKey = GetIntroKey (); + if (introKey && Validate (buf, len, introKey)) + Decrypt (buf, len, introKey); + else + { + // try own intro key + auto address = i2p::context.GetRouterInfo ().GetSSUAddress (); + if (!address) + { + LogPrint ("SSU is not supported"); + return; + } + if (Validate (buf, len, address->key)) + Decrypt (buf, len, address->key); + else + { + LogPrint ("MAC verifcation failed"); + m_Server.DeleteSession (this); + return; + } + } } - else - LogPrint ("MAC verifcation failed"); - m_State = eSessionStateUnknown; - } + // successfully decrypted + ProcessMessage (buf, len, senderEndpoint); + } } - void SSUSession::ProcessIntroKeyMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint) + void SSUSession::ProcessMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint) { - auto introKey = GetIntroKey (); - if (!introKey) - { - LogPrint ("SSU is not supported"); - return; - } - // use intro key for verification and decryption - if (!Validate (buf, len, introKey)) - { - LogPrint ("MAC verification intro key failed"); - m_Server.DeleteSession (this); - return; - } - - Decrypt (buf, len, introKey); - CreateAESandMacKey (buf + sizeof (SSUHeader), m_SessionKey, m_MacKey); SSUHeader * header = (SSUHeader *)buf; switch (header->GetPayloadType ()) { + case PAYLOAD_TYPE_DATA: + LogPrint ("SSU data received"); + ProcessData (buf + sizeof (SSUHeader), len - sizeof (SSUHeader)); + break; case PAYLOAD_TYPE_SESSION_REQUEST: ProcessSessionRequest (buf, len, senderEndpoint); break; case PAYLOAD_TYPE_SESSION_CREATED: ProcessSessionCreated (buf, len); break; + case PAYLOAD_TYPE_SESSION_CONFIRMED: + ProcessSessionConfirmed (buf, len); + break; case PAYLOAD_TYPE_PEER_TEST: LogPrint ("SSU peer test received"); - // TODO: - break; - default: ; - } + ProcessPeerTest (buf + sizeof (SSUHeader), len - sizeof (SSUHeader), senderEndpoint); + break; + case PAYLOAD_TYPE_SESSION_DESTROYED: + { + LogPrint ("SSU session destroy received"); + m_Server.DeleteSession (this); // delete this + break; + } + case PAYLOAD_TYPE_RELAY_RESPONSE: + ProcessRelayResponse (buf, len); + m_Server.DeleteSession (this); + break; + case PAYLOAD_TYPE_RELAY_INTRO: + LogPrint ("SSU relay intro received"); + ProcessRelayIntro (buf + sizeof (SSUHeader), len - sizeof (SSUHeader)); + break; + default: + LogPrint ("Unexpected SSU payload type ", (int)header->GetPayloadType ()); + } } void SSUSession::ProcessSessionRequest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint) @@ -185,6 +149,7 @@ namespace ssu m_State = eSessionStateRequestReceived; LogPrint ("Session request received"); m_RemoteEndpoint = senderEndpoint; + CreateAESandMacKey (buf + sizeof (SSUHeader), m_SessionKey, m_MacKey); SendSessionCreated (buf + sizeof (SSUHeader)); } @@ -202,6 +167,7 @@ namespace ssu uint8_t signedData[532]; // x,y, our IP, our port, remote IP, remote port, relayTag, signed on time uint8_t * payload = buf + sizeof (SSUHeader); uint8_t * y = payload; + CreateAESandMacKey (y, m_SessionKey, m_MacKey); memcpy (signedData, m_DHKeysPair->publicKey, 256); // x memcpy (signedData + 256, y, 256); // y payload += 256; @@ -235,24 +201,11 @@ namespace ssu void SSUSession::ProcessSessionConfirmed (uint8_t * buf, size_t len) { - LogPrint ("Process session confirmed"); - if (Validate (buf, len, m_MacKey)) - { - Decrypt (buf, len, m_SessionKey); - SSUHeader * header = (SSUHeader *)buf; - if (header->GetPayloadType () == PAYLOAD_TYPE_SESSION_CONFIRMED) - { - m_State = eSessionStateConfirmedReceived; - LogPrint ("Session confirmed received"); - m_State = eSessionStateEstablished; - SendI2NPMessage (CreateDeliveryStatusMsg (0)); - Established (); - } - else - LogPrint ("Unexpected payload type ", (int)(header->flag >> 4)); - } - else - LogPrint ("MAC verifcation failed"); + m_State = eSessionStateConfirmedReceived; + LogPrint ("Session confirmed received"); + m_State = eSessionStateEstablished; + SendI2NPMessage (CreateDeliveryStatusMsg (0)); + Established (); } void SSUSession::SendSessionRequest () @@ -403,38 +356,20 @@ namespace ssu void SSUSession::ProcessRelayResponse (uint8_t * buf, size_t len) { - LogPrint ("Process relay response"); - auto address = i2p::context.GetRouterInfo ().GetSSUAddress (); - if (!address) - { - LogPrint ("SSU is not supported"); - return; - } - - if (Validate (buf, len, address->key)) - { - Decrypt (buf, len, address->key); - SSUHeader * header = (SSUHeader *)buf; - if (header->GetPayloadType () == PAYLOAD_TYPE_RELAY_RESPONSE) - { - LogPrint ("Relay response received"); - uint8_t * payload = buf + sizeof (SSUHeader); - payload++; // remote size - //boost::asio::ip::address_v4 remoteIP (be32toh (*(uint32_t* )(payload))); - payload += 4; // remote address - //uint16_t remotePort = be16toh (*(uint16_t *)(payload)); - payload += 2; // remote port - payload++; // our size - boost::asio::ip::address_v4 ourIP (be32toh (*(uint32_t* )(payload))); - payload += 4; // our address - uint16_t ourPort = be16toh (*(uint16_t *)(payload)); - payload += 2; // our port - LogPrint ("Our external address is ", ourIP.to_string (), ":", ourPort); - i2p::context.UpdateAddress (ourIP.to_string ().c_str ()); - } - else - LogPrint ("Unexpected payload type ", (int)(header->flag >> 4)); - } + LogPrint ("Relay response received"); + uint8_t * payload = buf + sizeof (SSUHeader); + payload++; // remote size + //boost::asio::ip::address_v4 remoteIP (be32toh (*(uint32_t* )(payload))); + payload += 4; // remote address + //uint16_t remotePort = be16toh (*(uint16_t *)(payload)); + payload += 2; // remote port + payload++; // our size + boost::asio::ip::address_v4 ourIP (be32toh (*(uint32_t* )(payload))); + payload += 4; // our address + uint16_t ourPort = be16toh (*(uint16_t *)(payload)); + payload += 2; // our port + LogPrint ("Our external address is ", ourIP.to_string (), ":", ourPort); + i2p::context.UpdateAddress (ourIP.to_string ().c_str ()); } void SSUSession::ProcessRelayIntro (uint8_t * buf, size_t len) @@ -874,7 +809,7 @@ namespace ssu void SSUSession::SendSesionDestroyed () { - if (HasSessionKey ()) + if (m_IsSessionKey) { uint8_t buf[48 + 18], iv[16]; CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); diff --git a/SSU.h b/SSU.h index 8ad3fecf..f648f5db 100644 --- a/SSU.h +++ b/SSU.h @@ -66,7 +66,7 @@ namespace ssu eSessionStateIntroduced, eSessionStateEstablished, eSessionStateFailed - }; + }; class SSUServer; class SSUSession @@ -89,11 +89,9 @@ namespace ssu private: - void CreateAESandMacKey (uint8_t * pubKey, uint8_t * aesKey, uint8_t * macKey); + void CreateAESandMacKey (const uint8_t * pubKey, uint8_t * aesKey, uint8_t * macKey); void ProcessMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); // call for established session - void ProcessIntroKeyMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); // call for non-established session - void ProcessSessionRequest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); void SendSessionRequest (); void SendRelayRequest (uint32_t iTag, const uint8_t * iKey); @@ -118,7 +116,6 @@ namespace ssu void Decrypt (uint8_t * buf, size_t len, const uint8_t * aesKey); bool Validate (uint8_t * buf, size_t len, const uint8_t * macKey); const uint8_t * GetIntroKey () const; - bool HasSessionKey () const { return m_State == eSessionStateCreatedReceived || m_State == eSessionStateRequestReceived; }; void ScheduleTermination (); void HandleTerminationTimer (const boost::system::error_code& ecode); @@ -140,6 +137,7 @@ namespace ssu i2p::data::DHKeysPair * m_DHKeysPair; // X - for client and Y - for server bool m_PeerTest; SessionState m_State; + bool m_IsSessionKey; uint32_t m_RelayTag; std::set m_PeerTestNonces; CryptoPP::CBC_Mode::Encryption m_Encryption; From b0adb6bd92f002bd146efa87fc1de878bee7c5e4 Mon Sep 17 00:00:00 2001 From: user Date: Mon, 14 Apr 2014 01:07:54 +0400 Subject: [PATCH 14/15] fix hosts.txt loading bug --- AddressBook.cpp | 2 +- util.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/AddressBook.cpp b/AddressBook.cpp index dfd788bf..c6a06490 100644 --- a/AddressBook.cpp +++ b/AddressBook.cpp @@ -43,7 +43,7 @@ void AddressBook::LoadHostsFromI2P () content = i2p::util::http::httpRequest(url_ss.str()); // TODO: check http errors - if (! boost::starts_with(content, "")) + if (! boost::starts_with(content, "") && content.size() > 0) break; std::this_thread::sleep_for(std::chrono::seconds(5)); } diff --git a/util.cpp b/util.cpp index 8b95e2e3..05073c82 100644 --- a/util.cpp +++ b/util.cpp @@ -262,13 +262,14 @@ namespace http // code for parser tests //{ + // i2p::util::http::url u_0("http://127.0.0.1:7070/asdasd?qqqqqqqqqqqq"); // i2p::util::http::url u_1("http://user:password@site.com:8080/asdasd?qqqqqqqqqqqqq"); // i2p::util::http::url u_2("http://user:password@site.com/asdasd?qqqqqqqqqqqqqq"); // i2p::util::http::url u_3("http://user:@site.com/asdasd?qqqqqqqqqqqqq"); // i2p::util::http::url u_4("http://user@site.com/asdasd?qqqqqqqqqqqq"); // i2p::util::http::url u_5("http://@site.com:800/asdasd?qqqqqqqqqqqq"); // i2p::util::http::url u_6("http://@site.com:err_port/asdasd?qqqqqqqqqqqq"); - // i2p::util::http::url u_7("http://user:password@site.com:err_port/asdasd?qqqqqqqqqqqq"); + // i2p::util::http::url u_7("http://user:password@site.com:err_port/asdasd?qqqqqqqqqqqq"); //} void url::parse(const std::string& url_s) { From 030bebf080257c54431871c9d781e7d8fab95d37 Mon Sep 17 00:00:00 2001 From: user Date: Mon, 14 Apr 2014 01:08:41 +0400 Subject: [PATCH 15/15] add qcreator project --- build/i2pd.pro | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 build/i2pd.pro diff --git a/build/i2pd.pro b/build/i2pd.pro new file mode 100644 index 00000000..2200ea89 --- /dev/null +++ b/build/i2pd.pro @@ -0,0 +1,83 @@ +TEMPLATE = app +CONFIG += console +CONFIG -= app_bundle +CONFIG -= qt + +TARGET = ./../../i2pd_qt + +QMAKE_CXXFLAGS += -std=c++0x + +LIBS += -lcrypto++ +LIBS += \ + -lboost_system\ + -lboost_filesystem\ + -lboost_regex\ + -lboost_program_options\ + -lpthread + +SOURCES += \ + ../LeaseSet.cpp \ + ../i2p.cpp \ + ../HTTPServer.cpp \ + ../HTTPProxy.cpp \ + ../Garlic.cpp \ + ../base64.cpp \ + ../AddressBook.cpp \ + ../util.cpp \ + ../UPnP.cpp \ + ../TunnelPool.cpp \ + ../TunnelGateway.cpp \ + ../TunnelEndpoint.cpp \ + ../Tunnel.cpp \ + ../Transports.cpp \ + ../TransitTunnel.cpp \ + ../Streaming.cpp \ + ../SSU.cpp \ + ../RouterInfo.cpp \ + ../RouterContext.cpp \ + ../Reseed.cpp \ + ../NTCPSession.cpp \ + ../NetDb.cpp \ + ../Log.cpp \ + ../Identity.cpp \ + ../I2NPProtocol.cpp + +HEADERS += \ + ../LeaseSet.h \ + ../Identity.h \ + ../HTTPServer.h \ + ../HTTPProxy.h \ + ../hmac.h \ + ../Garlic.h \ + ../ElGamal.h \ + ../CryptoConst.h \ + ../base64.h \ + ../AddressBook.h \ + ../util.h \ + ../UPnP.h \ + ../TunnelPool.h \ + ../TunnelGateway.h \ + ../TunnelEndpoint.h \ + ../TunnelConfig.h \ + ../TunnelBase.h \ + ../Tunnel.h \ + ../Transports.h \ + ../TransitTunnel.h \ + ../Timestamp.h \ + ../Streaming.h \ + ../SSU.h \ + ../RouterInfo.h \ + ../RouterContext.h \ + ../Reseed.h \ + ../Queue.h \ + ../NTCPSession.h \ + ../NetDb.h \ + ../Log.h \ + ../LittleBigEndian.h \ + ../I2PEndian.h \ + ../I2NPProtocol.h + +OTHER_FILES += \ + ../README.md \ + ../Makefile \ + ../LICENSE