From 64aa588a9b01ca4b08ad7bcdb9f800db9448cb5e Mon Sep 17 00:00:00 2001 From: orignal Date: Fri, 21 Feb 2014 16:13:36 -0500 Subject: [PATCH] establish SSU session through relay --- RouterInfo.cpp | 7 +++ RouterInfo.h | 1 + SSU.cpp | 134 +++++++++++++++++++++++++++++++++++++++++++++---- SSU.h | 10 +++- 4 files changed, 141 insertions(+), 11 deletions(-) diff --git a/RouterInfo.cpp b/RouterInfo.cpp index 337ae7b3..24ca7bb9 100644 --- a/RouterInfo.cpp +++ b/RouterInfo.cpp @@ -323,6 +323,13 @@ namespace data else return m_SupportedTransports & (eSSUV4 | eSSUV6); } + + bool RouterInfo::UsesIntroducer () const + { + if (!IsSSU ()) return false; + auto address = GetSSUAddress (true); // no introducers for v6 + return address && !address->introducers.empty (); + } const RouterInfo::Address * RouterInfo::GetNTCPAddress (bool v4only) const { diff --git a/RouterInfo.h b/RouterInfo.h index be3d59a2..990a0db3 100644 --- a/RouterInfo.h +++ b/RouterInfo.h @@ -75,6 +75,7 @@ namespace data bool IsNTCP (bool v4only = true) const; bool IsSSU (bool v4only = true) const; bool IsCompatible (const RouterInfo& other) const { return m_SupportedTransports & other.m_SupportedTransports; }; + bool UsesIntroducer () const; void SetUnreachable (bool unreachable) { m_IsUnreachable = unreachable; }; bool IsUnreachable () const { return m_IsUnreachable; }; diff --git a/SSU.cpp b/SSU.cpp index edac2704..4417733b 100644 --- a/SSU.cpp +++ b/SSU.cpp @@ -65,6 +65,15 @@ namespace ssu // session confirmed ProcessSessionConfirmed (buf, len); break; + case eSessionRelayRequestSent: + // relay response + ProcessRelayResponse (buf,len); + break; + case eSessionRelayRequestReceived: + // HolePunch + m_State = eSessionStateUnknown; + Connect (); + break; default: LogPrint ("SSU state not implemented yet"); } @@ -91,8 +100,12 @@ namespace ssu LogPrint ("SSU session destroy received"); if (m_Server) m_Server->DeleteSession (this); // delete this - } - break; + break; + } + case PAYLOAD_TYPE_RELAY_INTRO: + LogPrint ("SSU relay intro received"); + // TODO: + break; default: LogPrint ("Unexpected SSU payload type ", (int)payloadType); } @@ -197,6 +210,37 @@ namespace ssu m_Server->Send (buf, 304, m_RemoteEndpoint); } + void SSUSession::SendRelayRequest (const i2p::data::RouterInfo::Introducer& introducer) + { + auto address = i2p::context.GetRouterInfo ().GetSSUAddress (); + if (!address) + { + LogPrint ("SSU is not supported"); + return; + } + + uint8_t buf[96 + 18]; + uint8_t * payload = buf + sizeof (SSUHeader); + *(uint32_t *)payload = htobe32 (introducer.iTag); + payload += 4; + *payload = 0; // no address + payload++; + *(uint16_t *)payload = 0; // port = 0 + payload += 2; + *payload = 0; // challenge + payload++; + memcpy (payload, address->key, 32); + payload += 32; + CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); + *(uint32_t *)payload = htobe32 (rnd.GenerateWord32 ()); // nonce + + uint8_t iv[16]; + rnd.GenerateBlock (iv, 16); // random iv + FillHeaderAndEncrypt (PAYLOAD_TYPE_RELAY_REQUEST, buf, 96, introducer.iKey, iv, introducer.iKey); + m_State = eSessionRelayRequestSent; + m_Server->Send (buf, 96, m_RemoteEndpoint); + } + void SSUSession::SendSessionCreated (const uint8_t * x) { auto introKey = GetIntroKey (); @@ -282,6 +326,46 @@ namespace ssu m_Server->Send (buf, 480, m_RemoteEndpoint); } + 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->flag >> 4) == 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++; + boost::asio::ip::address_v4 ourIP (be32toh (*(uint32_t* )(payload))); + payload += 4; + uint16_t ourPort = be16toh (*(uint16_t *)(payload)); + payload += 2; + 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)); + } + } + bool SSUSession::ProcessIntroKeyEncryptedMessage (uint8_t expectedPayloadType, uint8_t * buf, size_t len) { auto introKey = GetIntroKey (); @@ -301,7 +385,7 @@ namespace ssu LogPrint ("Unexpected payload type ", (int)(header->flag >> 4)); } else - LogPrint ("MAC verifcation failed"); + LogPrint ("MAC verification failed"); } else LogPrint ("SSU is not supported"); @@ -368,6 +452,11 @@ namespace ssu SendSessionRequest (); } + void SSUSession::ConnectThroughIntroducer (const i2p::data::RouterInfo::Introducer& introducer) + { + SendRelayRequest (introducer); + } + void SSUSession::Close () { SendSesionDestroyed (); @@ -677,12 +766,27 @@ namespace ssu session = it->second; else { - // otherwise create new session - session = new SSUSession (this, remoteEndpoint, router); - m_Sessions[remoteEndpoint] = session; - LogPrint ("New SSU session to [", router->GetIdentHashAbbreviation (), "] ", - remoteEndpoint.address ().to_string (), ":", remoteEndpoint.port (), " created"); - session->Connect (); + // otherwise create new session + if (!router->UsesIntroducer ()) + { + // connect directly + session = new SSUSession (this, remoteEndpoint, router); + m_Sessions[remoteEndpoint] = session; + LogPrint ("New SSU session to [", router->GetIdentHashAbbreviation (), "] ", + remoteEndpoint.address ().to_string (), ":", remoteEndpoint.port (), " created"); + session->Connect (); + } + else + { + // connect to introducer + auto& introducer = address->introducers[0]; // TODO: + boost::asio::ip::udp::endpoint introducerEndpoint (introducer.iHost, introducer.iPort); + session = new SSUSession (this, introducerEndpoint, router); + m_Sessions[introducerEndpoint] = session; + LogPrint ("New SSU session to [", router->GetIdentHashAbbreviation (), + "] created through introducer ", introducerEndpoint.address ().to_string (), ":", introducerEndpoint.port ()); + session->ConnectThroughIntroducer (introducer); + } } } else @@ -709,6 +813,18 @@ namespace ssu delete it.second; } 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 ()) + { + m_Sessions.erase (it); + m_Sessions[newEndpoint] = it->second; + LogPrint ("SSU session ressigned from ", oldEndpoint.address ().to_string (), ":", oldEndpoint.port (), + " to ", newEndpoint.address ().to_string (), ":", newEndpoint.port ()); + } } } } diff --git a/SSU.h b/SSU.h index fccd6a8f..f5efc862 100644 --- a/SSU.h +++ b/SSU.h @@ -55,6 +55,8 @@ namespace ssu eSessionStateCreatedReceived, eSessionStateConfirmedSent, eSessionStateConfirmedReceived, + eSessionRelayRequestSent, + eSessionRelayRequestReceived, eSessionStateEstablished }; @@ -68,6 +70,7 @@ namespace ssu void ProcessNextMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); void Connect (); + void ConnectThroughIntroducer (const i2p::data::RouterInfo::Introducer& introducer); void Close (); boost::asio::ip::udp::endpoint& GetRemoteEndpoint () { return m_RemoteEndpoint; }; void SendI2NPMessage (I2NPMessage * msg); @@ -79,10 +82,12 @@ namespace ssu void ProcessMessage (uint8_t * buf, size_t len); // call for established session 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 ProcessSessionCreated (uint8_t * buf, size_t len); void SendSessionCreated (const uint8_t * x); void ProcessSessionConfirmed (uint8_t * buf, size_t len); void SendSessionConfirmed (const uint8_t * y, const uint8_t * ourAddress, uint32_t relayTag); + void ProcessRelayResponse (uint8_t * buf, size_t len); void Established (); void ProcessData (uint8_t * buf, size_t len); void SendMsgAck (uint32_t msgID); @@ -118,10 +123,11 @@ namespace ssu void Stop (); SSUSession * GetSession (const i2p::data::RouterInfo * router); void DeleteSession (SSUSession * session); - void DeleteAllSessions (); - + void DeleteAllSessions (); + 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: