From 31f906240e1390f8d73e19c8825520a91f187ff8 Mon Sep 17 00:00:00 2001 From: orignal Date: Wed, 9 Apr 2014 11:58:57 -0400 Subject: [PATCH 01/10] 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/10] 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/10] 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/10] 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/10] 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/10] 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/10] 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/10] 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/10] 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/10] 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);