diff --git a/ElGamal.h b/ElGamal.h index 387e02bb..b3f0e44f 100644 --- a/ElGamal.h +++ b/ElGamal.h @@ -12,25 +12,47 @@ namespace i2p { namespace crypto { - inline void ElGamalEncrypt (const uint8_t * key, const uint8_t * data, int len, - uint8_t * encrypted, bool zeroPadding = false) // 514 with padding and 512 without + + class ElGamalEncryption { - CryptoPP::AutoSeededRandomPool rnd; - CryptoPP::Integer y(key, 256), k(rnd, CryptoPP::Integer::One(), elgp-1); - - if (zeroPadding) - { - encrypted[0] = 0; - encrypted[257] = 0; - } - a_exp_b_mod_c (elgg, k, elgp).Encode (zeroPadding ? encrypted + 1 : encrypted, 256); - uint8_t m[255]; - m[0] = 0xFF; - memcpy (m+33, data, len); - CryptoPP::SHA256().CalculateDigest(m+1, m+33, 222); - a_times_b_mod_c (a_exp_b_mod_c (y, k, elgp), - CryptoPP::Integer (m, 255), elgp).Encode (zeroPadding ? encrypted + 258 : encrypted + 256, 256); - } + public: + + ElGamalEncryption (const uint8_t * key): + y (key, 256), k (rnd, CryptoPP::Integer::One(), elgp-1), + a (a_exp_b_mod_c (elgg, k, elgp)), b1 (a_exp_b_mod_c (y, k, elgp)) + { + } + + void Encrypt (const uint8_t * data, int len, uint8_t * encrypted, bool zeroPadding = false) + { + // calculate b = b1*m mod p + uint8_t m[255]; + m[0] = 0xFF; + memcpy (m+33, data, len); + CryptoPP::SHA256().CalculateDigest(m+1, m+33, 222); + CryptoPP::Integer b (a_times_b_mod_c (b1, CryptoPP::Integer (m, 255), elgp)); + + // copy a and b + if (zeroPadding) + { + encrypted[0] = 0; + a.Encode (encrypted + 1, 256); + encrypted[257] = 0; + b.Encode (encrypted + 258, 256); + } + else + { + a.Encode (encrypted, 256); + b.Encode (encrypted + 256, 256); + } + } + + private: + + CryptoPP::AutoSeededRandomPool rnd; + CryptoPP::Integer y, k, a, b1; + bool m_ZeroPadding; + }; inline bool ElGamalDecrypt (const uint8_t * key, const uint8_t * encrypted, uint8_t * data, bool zeroPadding = false) diff --git a/Garlic.cpp b/Garlic.cpp index cdb8860e..e73a6f02 100644 --- a/Garlic.cpp +++ b/Garlic.cpp @@ -2,7 +2,6 @@ #include "I2PEndian.h" #include #include -#include "ElGamal.h" #include "RouterContext.h" #include "I2NPProtocol.h" #include "Tunnel.h" @@ -14,7 +13,7 @@ namespace i2p { namespace garlic { - GarlicRoutingSession::GarlicRoutingSession (const i2p::data::RoutingDestination * destination, int numTags): + GarlicRoutingSession::GarlicRoutingSession (const i2p::data::RoutingDestination& destination, int numTags): m_Destination (destination), m_FirstMsgID (0), m_IsAcknowledged (false), m_NumTags (numTags), m_NextTag (-1), m_SessionTags (0) { @@ -56,7 +55,7 @@ namespace garlic m_Rnd.GenerateBlock (elGamal.preIV, 32); // Pre-IV uint8_t iv[32]; // IV is first 16 bytes CryptoPP::SHA256().CalculateDigest(iv, elGamal.preIV, 32); - i2p::crypto::ElGamalEncrypt (m_Destination->GetEncryptionPublicKey (), (uint8_t *)&elGamal, sizeof(elGamal), buf, true); + m_Destination.GetElGamalEncryption ()->Encrypt ((uint8_t *)&elGamal, sizeof(elGamal), buf, true); m_Encryption.SetKeyWithIV (m_SessionKey, 32, iv); buf += 514; len += 514; @@ -140,7 +139,7 @@ namespace garlic } if (msg) // clove message ifself if presented { - size += CreateGarlicClove (payload + size, msg, m_Destination->IsDestination ()); + size += CreateGarlicClove (payload + size, msg, m_Destination.IsDestination ()); (*numCloves)++; } @@ -161,7 +160,7 @@ namespace garlic { buf[size] = eGarlicDeliveryTypeDestination << 5;// delivery instructions flag destination size++; - memcpy (buf + size, m_Destination->GetIdentHash (), 32); + memcpy (buf + size, m_Destination.GetIdentHash (), 32); size += 32; } else @@ -230,33 +229,31 @@ 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) { - if (!destination) return nullptr; - auto it = m_Sessions.find (destination->GetIdentHash ()); + auto it = m_Sessions.find (destination.GetIdentHash ()); if (it != m_Sessions.end ()) { delete it->second; m_Sessions.erase (it); } GarlicRoutingSession * session = new GarlicRoutingSession (destination, 0); // not follow-on messages expected - m_Sessions[destination->GetIdentHash ()] = session; + m_Sessions[destination.GetIdentHash ()] = session; return session->WrapSingleMessage (msg, nullptr); } - I2NPMessage * GarlicRouting::WrapMessage (const i2p::data::RoutingDestination * destination, + I2NPMessage * GarlicRouting::WrapMessage (const i2p::data::RoutingDestination& destination, I2NPMessage * msg, I2NPMessage * leaseSet) { - if (!destination) return nullptr; - auto it = m_Sessions.find (destination->GetIdentHash ()); + auto it = m_Sessions.find (destination.GetIdentHash ()); GarlicRoutingSession * session = nullptr; if (it != m_Sessions.end ()) session = it->second; if (!session) { session = new GarlicRoutingSession (destination, 4); // TODO: change it later - m_Sessions[destination->GetIdentHash ()] = session; + m_Sessions[destination.GetIdentHash ()] = session; } I2NPMessage * ret = session->WrapSingleMessage (msg, leaseSet); diff --git a/Garlic.h b/Garlic.h index c640eee3..0383b030 100644 --- a/Garlic.h +++ b/Garlic.h @@ -37,7 +37,7 @@ namespace garlic { public: - GarlicRoutingSession (const i2p::data::RoutingDestination * destination, int numTags); + GarlicRoutingSession (const i2p::data::RoutingDestination& destination, int numTags); ~GarlicRoutingSession (); I2NPMessage * WrapSingleMessage (I2NPMessage * msg, I2NPMessage * leaseSet); int GetNextTag () const { return m_NextTag; }; @@ -57,7 +57,7 @@ namespace garlic private: - const i2p::data::RoutingDestination * m_Destination; + const i2p::data::RoutingDestination& m_Destination; uint8_t m_SessionKey[32]; uint32_t m_FirstMsgID; // first message ID bool m_IsAcknowledged; @@ -78,8 +78,8 @@ namespace garlic void HandleGarlicMessage (uint8_t * buf, size_t len, bool isFromTunnel); void HandleDeliveryStatusMessage (uint8_t * buf, size_t len); - I2NPMessage * WrapSingleMessage (const i2p::data::RoutingDestination * destination, I2NPMessage * msg); - I2NPMessage * WrapMessage (const i2p::data::RoutingDestination * destination, + I2NPMessage * WrapSingleMessage (const i2p::data::RoutingDestination& destination, I2NPMessage * msg); + I2NPMessage * WrapMessage (const i2p::data::RoutingDestination& destination, I2NPMessage * msg, I2NPMessage * leaseSet = nullptr); private: diff --git a/HTTPServer.cpp b/HTTPServer.cpp index 5aeac9ed..833dca51 100644 --- a/HTTPServer.cpp +++ b/HTTPServer.cpp @@ -60,9 +60,20 @@ namespace util { m_Buffer[bytes_transferred] = 0; auto address = ExtractAddress (); - LogPrint (address); if (address.length () > 1) // not just '/' - HandleDestinationRequest (address.substr (1)); // exclude '/' + { + std::string uri ("/"), b32; + size_t pos = address.find ('/', 1); + if (pos == std::string::npos) + b32 = address.substr (1); // excluding leading '/' to end of line + else + { + b32 = address.substr (1, pos - 1); // excluding leading '/' to next '/' + uri = address.substr (pos); // rest of line + } + + HandleDestinationRequest (b32, uri); + } else HandleRequest (); boost::asio::async_write (*m_Socket, m_Reply.to_buffers(), @@ -149,14 +160,18 @@ namespace util s << "

Flibusta

"; } - void HTTPConnection::HandleDestinationRequest (std::string b32) + void HTTPConnection::HandleDestinationRequest (const std::string& b32, const std::string& uri) { uint8_t destination[32]; - i2p::data::Base32ToByteStream (b32.c_str (), b32.length (), destination, 32); + if (i2p::data::Base32ToByteStream (b32.c_str (), b32.length (), destination, 32) != 32) + { + LogPrint ("Invalid Base32 address ", b32); + return; + } auto leaseSet = i2p::data::netdb.FindLeaseSet (destination); if (!leaseSet || !leaseSet->HasNonExpiredLeases ()) { - i2p::data::netdb.RequestDestination (i2p::data::IdentHash (destination), true); + i2p::data::netdb.Subscribe(destination); std::this_thread::sleep_for (std::chrono::seconds(10)); // wait for 10 seconds leaseSet = i2p::data::netdb.FindLeaseSet (destination); if (!leaseSet || !leaseSet->HasNonExpiredLeases ()) // still no LeaseSet @@ -170,17 +185,10 @@ namespace util return; } } - // we found LeaseSet - if (leaseSet->HasExpiredLeases ()) - { - // we should re-request LeaseSet - LogPrint ("LeaseSet re-requested"); - i2p::data::netdb.RequestDestination (i2p::data::IdentHash (destination), true); - } - auto s = i2p::stream::CreateStream (leaseSet); + auto s = i2p::stream::CreateStream (*leaseSet); if (s) { - std::string request = "GET / HTTP/1.1\n Host:" + b32 + ".b32.i2p\n"; + std::string request = "GET " + uri + " HTTP/1.1\n Host:" + b32 + ".b32.i2p\n"; s->Send ((uint8_t *)request.c_str (), request.length (), 10); std::stringstream ss; uint8_t buf[8192]; @@ -200,7 +208,7 @@ namespace util else // nothing received ss << "Not responding"; s->Close (); - //DeleteStream (s); + DeleteStream (s); m_Reply.content = ss.str (); m_Reply.headers.resize(2); diff --git a/HTTPServer.h b/HTTPServer.h index 90aacfc0..a62a2074 100644 --- a/HTTPServer.h +++ b/HTTPServer.h @@ -48,7 +48,7 @@ namespace util void HandleWrite(const boost::system::error_code& ecode); void HandleRequest (); - void HandleDestinationRequest (std::string b32); + void HandleDestinationRequest (const std::string& b32, const std::string& uri); void FillContent (std::stringstream& s); std::string ExtractAddress (); diff --git a/I2NPProtocol.cpp b/I2NPProtocol.cpp index 6503d70f..25549a09 100644 --- a/I2NPProtocol.cpp +++ b/I2NPProtocol.cpp @@ -212,7 +212,7 @@ namespace i2p const I2NPBuildRequestRecordClearText& clearText, I2NPBuildRequestRecordElGamalEncrypted& record) { - i2p::crypto::ElGamalEncrypt (router.GetRouterIdentity ().publicKey, (uint8_t *)&clearText, sizeof(clearText), record.encrypted); + router.GetElGamalEncryption ()->Encrypt ((uint8_t *)&clearText, sizeof(clearText), record.encrypted); memcpy (record.toPeer, (const uint8_t *)router.GetIdentHash (), 16); } @@ -392,7 +392,7 @@ namespace i2p LogPrint ("TunnelGateway of ", (int)len, " bytes for tunnel ", (unsigned int)tunnelID, ". Msg type ", (int)msg->GetHeader()->typeID); i2p::tunnel::TransitTunnel * tunnel = i2p::tunnel::tunnels.GetTransitTunnel (tunnelID); if (tunnel) - tunnel->SendTunnelDataMsg (nullptr, 0, msg); + tunnel->SendTunnelDataMsg (msg); else { LogPrint ("Tunnel ", (unsigned int)tunnelID, " not found"); diff --git a/Identity.h b/Identity.h index e17b27be..10af227c 100644 --- a/Identity.h +++ b/Identity.h @@ -3,6 +3,7 @@ #include #include +#include "ElGamal.h" namespace i2p { @@ -84,9 +85,24 @@ namespace data class RoutingDestination { public: + + RoutingDestination (): m_ElGamalEncryption (nullptr) {}; + virtual ~RoutingDestination () { delete m_ElGamalEncryption; }; + virtual const IdentHash& GetIdentHash () const = 0; virtual const uint8_t * GetEncryptionPublicKey () const = 0; virtual bool IsDestination () const = 0; // for garlic + + i2p::crypto::ElGamalEncryption * GetElGamalEncryption () const + { + if (!m_ElGamalEncryption) + m_ElGamalEncryption = new i2p::crypto::ElGamalEncryption (GetEncryptionPublicKey ()); + return m_ElGamalEncryption; + } + + private: + + mutable i2p::crypto::ElGamalEncryption * m_ElGamalEncryption; // use lazy initialization }; } } diff --git a/NTCPSession.h b/NTCPSession.h index 71cf5af9..eb31951d 100644 --- a/NTCPSession.h +++ b/NTCPSession.h @@ -60,7 +60,7 @@ namespace ntcp #pragma pack() - const int TERMINATION_TIMEOUT = 60; // 1 minute + const int TERMINATION_TIMEOUT = 120; // 2 minutes class NTCPSession { public: diff --git a/NetDb.cpp b/NetDb.cpp index aa040758..14a33353 100644 --- a/NetDb.cpp +++ b/NetDb.cpp @@ -25,7 +25,7 @@ namespace data 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); + msg = i2p::garlic::routing.WrapSingleMessage (*router, msg); m_ExcludedPeers.insert (router->GetIdentHash ()); m_LastRouter = router; m_LastReplyTunnel = replyTunnel; @@ -90,7 +90,7 @@ namespace data void NetDb::Run () { - uint32_t lastTs = 0; + uint32_t lastSave = 0, lastPublish = 0; m_IsRunning = true; while (m_IsRunning) { @@ -120,11 +120,19 @@ namespace data Explore (); uint64_t ts = i2p::util::GetSecondsSinceEpoch (); - if (ts - lastTs >= 60) // save routers every minute + if (ts - lastSave >= 60) // save routers and validate subscriptions every minute { - if (lastTs) + if (lastSave) + { SaveUpdated (m_NetDbPath); - lastTs = ts; + ValidateSubscriptions (); + } + lastSave = ts; + } + if (ts - lastPublish >= 600) // publish every 10 minutes + { + Publish (); + lastPublish = ts; } } catch (std::exception& ex) @@ -486,7 +494,10 @@ namespace data else // we should send directly { if (!dest->IsLeaseSet ()) // if not LeaseSet - i2p::transports.SendMessage (router, dest->CreateRequestMessage (router)); + { + if (!dest->IsExcluded (router) && dest->GetNumExcludedPeers () < 30) + i2p::transports.SendMessage (router, dest->CreateRequestMessage (router)); + } else LogPrint ("Can't request LeaseSet"); } @@ -543,6 +554,17 @@ namespace data } } + void NetDb::Publish () + { + std::set excluded; // TODO: fill up later + auto floodfill = GetClosestFloodfill (i2p::context.GetRouterInfo ().GetIdentHash (), excluded); + if (floodfill) + { + LogPrint ("Publishing our RouterInfo to ", floodfill->GetIdentHashAbbreviation ()); + transports.SendMessage (floodfill->GetIdentHash (), CreateDatabaseStoreMsg ()); + } + } + RequestedDestination * NetDb::CreateRequestedDestination (const IdentHash& dest, bool isLeaseSet, bool isExploratory) { @@ -644,5 +666,33 @@ namespace data return r; } + void NetDb::Subscribe (const IdentHash& ident) + { + LeaseSet * leaseSet = FindLeaseSet (ident); + if (!leaseSet) + { + LogPrint ("LeaseSet requested"); + RequestDestination (ident, true); + } + m_Subscriptions.insert (ident); + } + + void NetDb::Unsubscribe (const IdentHash& ident) + { + m_Subscriptions.erase (ident); + } + + void NetDb::ValidateSubscriptions () + { + for (auto it : m_Subscriptions) + { + LeaseSet * leaseSet = FindLeaseSet (it); + if (!leaseSet || leaseSet->HasExpiredLeases ()) + { + LogPrint ("LeaseSet re-requested"); + RequestDestination (it, true); + } + } + } } } diff --git a/NetDb.h b/NetDb.h index 8f0e4125..39f87df0 100644 --- a/NetDb.h +++ b/NetDb.h @@ -63,7 +63,9 @@ namespace data void AddLeaseSet (uint8_t * buf, int len); RouterInfo * FindRouter (const IdentHash& ident) const; LeaseSet * FindLeaseSet (const IdentHash& destination) const; - + void Subscribe (const IdentHash& ident); // keep LeaseSets upto date + void Unsubscribe (const IdentHash& ident); + void RequestDestination (const char * b32); // in base32 void RequestDestination (const IdentHash& destination, bool isLeaseSet = false); @@ -82,6 +84,8 @@ namespace data void SaveUpdated (const char * directory); void Run (); // exploratory thread void Explore (); + void Publish (); + void ValidateSubscriptions (); const RouterInfo * GetClosestFloodfill (const IdentHash& destination, const std::set& excluded) const; RequestedDestination * CreateRequestedDestination (const IdentHash& dest, @@ -94,6 +98,7 @@ namespace data std::map m_LeaseSets; std::map m_RouterInfos; std::map m_RequestedDestinations; + std::set m_Subscriptions; bool m_IsRunning; int m_ReseedRetries; diff --git a/RouterInfo.cpp b/RouterInfo.cpp index e1ae0a2d..24ca7bb9 100644 --- a/RouterInfo.cpp +++ b/RouterInfo.cpp @@ -129,6 +129,27 @@ namespace data address.port = boost::lexical_cast(value); else if (!strcmp (key, "key")) Base64ToByteStream (value, strlen (value), address.key, 32); + else if (key[0] == 'i') + { + // introducers + size_t l = strlen(key); + unsigned char index = key[l-1]; // TODO: + key[l-1] = 0; + if (index >= address.introducers.size ()) + address.introducers.resize (index + 1); + Introducer& introducer = address.introducers.at (index); + if (!strcmp (key, "ihost")) + { + boost::system::error_code ecode; + introducer.iHost = boost::asio::ip::address::from_string (value, ecode); + } + else if (!strcmp (key, "iport")) + introducer.iPort = boost::lexical_cast(value); + else if (!strcmp (key, "itag")) + introducer.iTag = boost::lexical_cast(value); + else if (!strcmp (key, "ikey")) + Base64ToByteStream (value, strlen (value), introducer.iKey, 32); + } } m_Addresses.push_back(address); } @@ -302,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 3c08d8d1..990a0db3 100644 --- a/RouterInfo.h +++ b/RouterInfo.h @@ -32,6 +32,14 @@ namespace data eTransportSSU }; + struct Introducer + { + boost::asio::ip::address iHost; + int iPort; + uint8_t iKey[32]; + uint32_t iTag; + }; + struct Address { TransportStyle transportStyle; @@ -39,7 +47,9 @@ namespace data int port; uint64_t date; uint8_t cost; - uint8_t key[32]; // into key for SSU + // SSU only + uint8_t key[32]; // intro key for SSU + std::vector introducers; }; RouterInfo (const char * filename); @@ -65,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: diff --git a/Streaming.cpp b/Streaming.cpp index 05f838fa..6caa2a0f 100644 --- a/Streaming.cpp +++ b/Streaming.cpp @@ -14,7 +14,7 @@ namespace i2p { namespace stream { - Stream::Stream (StreamingDestination * local, const i2p::data::LeaseSet * remote): + Stream::Stream (StreamingDestination * local, const i2p::data::LeaseSet& remote): m_SendStreamID (0), m_SequenceNumber (0), m_LastReceivedSequenceNumber (0), m_IsOpen (false), m_LocalDestination (local), m_RemoteLeaseSet (remote), m_OutboundTunnel (nullptr) { @@ -172,7 +172,7 @@ namespace stream if (!m_OutboundTunnel) m_OutboundTunnel = i2p::tunnel::tunnels.GetNextOutboundTunnel (); - auto leases = m_RemoteLeaseSet->GetNonExpiredLeases (); + auto leases = m_RemoteLeaseSet.GetNonExpiredLeases (); if (m_OutboundTunnel && !leases.empty ()) { auto& lease = *leases.begin (); // TODO: @@ -206,7 +206,7 @@ namespace stream CreateDataMessage (this, packet, size)); if (m_OutboundTunnel) { - auto leases = m_RemoteLeaseSet->GetNonExpiredLeases (); + auto leases = m_RemoteLeaseSet.GetNonExpiredLeases (); if (!leases.empty ()) { auto& lease = *leases.begin (); // TODO: @@ -252,7 +252,7 @@ namespace stream I2NPMessage * msg = i2p::garlic::routing.WrapSingleMessage (m_RemoteLeaseSet, CreateDataMessage (this, packet, size)); - auto leases = m_RemoteLeaseSet->GetNonExpiredLeases (); + auto leases = m_RemoteLeaseSet.GetNonExpiredLeases (); if (m_OutboundTunnel && !leases.empty ()) { auto& lease = *leases.begin (); // TODO: @@ -318,7 +318,7 @@ namespace stream void StreamingDestination::HandleNextPacket (Packet * packet) { - uint32_t sendStreamID = be32toh (*(uint32_t *)(packet->buf)); + uint32_t sendStreamID = packet->GetSendStreamID (); auto it = m_Streams.find (sendStreamID); if (it != m_Streams.end ()) it->second->HandleNextPacket (packet); @@ -329,7 +329,7 @@ namespace stream } } - Stream * StreamingDestination::CreateNewStream (const i2p::data::LeaseSet * remote) + Stream * StreamingDestination::CreateNewStream (const i2p::data::LeaseSet& remote) { Stream * s = new Stream (this, remote); m_Streams[s->GetRecvStreamID ()] = s; @@ -399,7 +399,7 @@ namespace stream signer.SignMessage (i2p::context.GetRandomNumberGenerator (), buf, len, signature); } - Stream * CreateStream (const i2p::data::LeaseSet * remote) + Stream * CreateStream (const i2p::data::LeaseSet& remote) { if (!sharedLocalDestination) sharedLocalDestination = new StreamingDestination (); diff --git a/Streaming.h b/Streaming.h index 18d09b5d..4e33d111 100644 --- a/Streaming.h +++ b/Streaming.h @@ -65,11 +65,11 @@ namespace stream { public: - Stream (StreamingDestination * local, const i2p::data::LeaseSet * remote); + Stream (StreamingDestination * local, const i2p::data::LeaseSet& remote); ~Stream (); uint32_t GetSendStreamID () const { return m_SendStreamID; }; uint32_t GetRecvStreamID () const { return m_RecvStreamID; }; - const i2p::data::LeaseSet * GetRemoteLeaseSet () const { return m_RemoteLeaseSet; }; + const i2p::data::LeaseSet& GetRemoteLeaseSet () const { return m_RemoteLeaseSet; }; bool IsOpen () const { return m_IsOpen; }; bool IsEstablished () const { return m_SendStreamID; }; @@ -91,7 +91,7 @@ namespace stream uint32_t m_SendStreamID, m_RecvStreamID, m_SequenceNumber, m_LastReceivedSequenceNumber; bool m_IsOpen; StreamingDestination * m_LocalDestination; - const i2p::data::LeaseSet * m_RemoteLeaseSet; + const i2p::data::LeaseSet& m_RemoteLeaseSet; i2p::util::Queue m_ReceiveQueue; std::set m_SavedPackets; i2p::tunnel::OutboundTunnel * m_OutboundTunnel; @@ -109,7 +109,7 @@ namespace stream I2NPMessage * GetLeaseSet (); void Sign (uint8_t * buf, int len, uint8_t * signature) const; - Stream * CreateNewStream (const i2p::data::LeaseSet * remote); + Stream * CreateNewStream (const i2p::data::LeaseSet& remote); void DeleteStream (Stream * stream); void HandleNextPacket (Packet * packet); @@ -129,7 +129,7 @@ namespace stream CryptoPP::DSA::PrivateKey m_SigningPrivateKey; }; - Stream * CreateStream (const i2p::data::LeaseSet * remote); + Stream * CreateStream (const i2p::data::LeaseSet& remote); void DeleteStream (Stream * stream); // assuming data is I2CP message diff --git a/TransitTunnel.cpp b/TransitTunnel.cpp index 6c9a4f6f..a96f3d05 100644 --- a/TransitTunnel.cpp +++ b/TransitTunnel.cpp @@ -47,15 +47,18 @@ namespace tunnel m_NumTransmittedBytes += tunnelMsg->GetLength (); } - void TransitTunnel::SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, i2p::I2NPMessage * msg) + void TransitTunnel::SendTunnelDataMsg (i2p::I2NPMessage * msg) { LogPrint ("We are not a gateway for transit tunnel ", m_TunnelID); i2p::DeleteI2NPMessage (msg); } - void TransitTunnelGateway::SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, i2p::I2NPMessage * msg) + void TransitTunnelGateway::SendTunnelDataMsg (i2p::I2NPMessage * msg) { - m_Gateway.SendTunnelDataMsg (gwHash, gwTunnel, msg); + TunnelMessageBlock block; + block.deliveryType = eDeliveryTypeLocal; + block.data = msg; + m_Gateway.SendTunnelDataMsg (block); } void TransitTunnelEndpoint::HandleTunnelDataMsg (i2p::I2NPMessage * tunnelMsg) diff --git a/TransitTunnel.h b/TransitTunnel.h index 570429f5..feeeaa58 100644 --- a/TransitTunnel.h +++ b/TransitTunnel.h @@ -22,7 +22,7 @@ namespace tunnel const uint8_t * layerKey,const uint8_t * ivKey); virtual void HandleTunnelDataMsg (i2p::I2NPMessage * tunnelMsg); - virtual void SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, i2p::I2NPMessage * msg); + virtual void SendTunnelDataMsg (i2p::I2NPMessage * msg); virtual size_t GetNumTransmittedBytes () const { return m_NumTransmittedBytes; }; uint32_t GetTunnelID () const { return m_TunnelID; }; @@ -54,7 +54,7 @@ namespace tunnel TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey), m_Gateway(this) {}; - void SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, i2p::I2NPMessage * msg); + void SendTunnelDataMsg (i2p::I2NPMessage * msg); size_t GetNumTransmittedBytes () const { return m_Gateway.GetNumSentBytes (); }; private: diff --git a/Transports.cpp b/Transports.cpp index ac5085fa..cc56a86f 100644 --- a/Transports.cpp +++ b/Transports.cpp @@ -64,9 +64,12 @@ namespace i2p delete session.second; m_NTCPSessions.clear (); delete m_NTCPAcceptor; - - m_Timer->cancel (); - delete m_Timer; + + if (m_Timer) + { + m_Timer->cancel (); + delete m_Timer; + } if (m_SSUServer) { diff --git a/Tunnel.cpp b/Tunnel.cpp index 1163f47e..9d58c875 100644 --- a/Tunnel.cpp +++ b/Tunnel.cpp @@ -136,28 +136,28 @@ namespace tunnel void OutboundTunnel::SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, i2p::I2NPMessage * msg) { - m_Gateway.SendTunnelDataMsg (gwHash, gwTunnel, msg); + TunnelMessageBlock block; + if (gwHash) + { + block.hash = gwHash; + if (gwTunnel) + { + block.deliveryType = eDeliveryTypeTunnel; + block.tunnelID = gwTunnel; + } + else + block.deliveryType = eDeliveryTypeRouter; + } + else + block.deliveryType = eDeliveryTypeLocal; + block.data = msg; + m_Gateway.SendTunnelDataMsg (block); } void OutboundTunnel::SendTunnelDataMsg (std::vector msgs) { for (auto& it : msgs) - { - switch (it.deliveryType) - { - case eDeliveryTypeLocal: - m_Gateway.SendTunnelDataMsg (nullptr, 0, it.data); - break; - case eDeliveryTypeTunnel: - m_Gateway.SendTunnelDataMsg (it.hash, it.tunnelID, it.data); - break; - case eDeliveryTypeRouter: - m_Gateway.SendTunnelDataMsg (it.hash, 0, it.data); - break; - default: - LogPrint ("Unexpected delivery type ", (int)it.deliveryType); - } - } + m_Gateway.PutTunnelDataMsg (it); m_Gateway.SendBuffer (); } diff --git a/Tunnel.h b/Tunnel.h index 3498eae3..7e7f94d5 100644 --- a/Tunnel.h +++ b/Tunnel.h @@ -21,7 +21,7 @@ namespace i2p { namespace tunnel { - const int TUNNEL_EXPIRATION_TIMEOUT = 600; // 10 minutes + const int TUNNEL_EXPIRATION_TIMEOUT = 660; // 11 minutes class OutboundTunnel; class InboundTunnel; diff --git a/TunnelGateway.cpp b/TunnelGateway.cpp index 55d78326..cc1189e0 100644 --- a/TunnelGateway.cpp +++ b/TunnelGateway.cpp @@ -10,7 +10,7 @@ namespace i2p { namespace tunnel { - void TunnelGatewayBuffer::PutI2NPMsg (const uint8_t * gwHash, uint32_t gwTunnel, I2NPMessage * msg) + void TunnelGatewayBuffer::PutI2NPMsg (const TunnelMessageBlock& block) { if (!m_CurrentTunnelDataMsg) CreateCurrentTunnelDataMessage (); @@ -18,24 +18,21 @@ namespace tunnel // create delivery instructions uint8_t di[43]; // max delivery instruction length is 43 for tunnel size_t diLen = 1;// flag - TunnelDeliveryType dt = eDeliveryTypeLocal; - if (gwHash) + if (block.deliveryType != eDeliveryTypeLocal) // tunnel or router { - if (gwTunnel) + if (block.deliveryType == eDeliveryTypeTunnel) { - *(uint32_t *)(di + diLen) = htobe32 (gwTunnel); + *(uint32_t *)(di + diLen) = htobe32 (block.tunnelID); diLen += 4; // tunnelID - dt = eDeliveryTypeTunnel; } - else - dt = eDeliveryTypeRouter; - memcpy (di + diLen, gwHash, 32); + memcpy (di + diLen, block.hash, 32); diLen += 32; //len } - di[0] = dt << 5; // set delivery type + di[0] = block.deliveryType << 5; // set delivery type // create fragments + I2NPMessage * msg = block.data; if (diLen + msg->GetLength () + 2<= m_RemainingSize) { // message fits. First and last fragment @@ -104,7 +101,7 @@ namespace tunnel { // delivery instructions don't fit. Create new message CompleteCurrentTunnelDataMessage (); - PutI2NPMsg (gwHash, gwTunnel, msg); + PutI2NPMsg (block); // don't delete msg because it's taken care inside } } @@ -152,15 +149,15 @@ namespace tunnel m_CurrentTunnelDataMsg = nullptr; } - void TunnelGateway::SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, i2p::I2NPMessage * msg) + void TunnelGateway::SendTunnelDataMsg (const TunnelMessageBlock& block) { - PutTunnelDataMsg (gwHash, gwTunnel, msg); + PutTunnelDataMsg (block); SendBuffer (); } - void TunnelGateway::PutTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, i2p::I2NPMessage * msg) + void TunnelGateway::PutTunnelDataMsg (const TunnelMessageBlock& block) { - m_Buffer.PutI2NPMsg (gwHash, gwTunnel, msg); + m_Buffer.PutI2NPMsg (block); } void TunnelGateway::SendBuffer () diff --git a/TunnelGateway.h b/TunnelGateway.h index d68c8580..8c390eef 100644 --- a/TunnelGateway.h +++ b/TunnelGateway.h @@ -15,7 +15,7 @@ namespace tunnel public: TunnelGatewayBuffer (uint32_t tunnelID): m_TunnelID (tunnelID), m_CurrentTunnelDataMsg (nullptr), m_RemainingSize (0) {}; - void PutI2NPMsg (const uint8_t * gwHash, uint32_t gwTunnel, I2NPMessage * msg); + void PutI2NPMsg (const TunnelMessageBlock& block); std::vector GetTunnelDataMsgs (); private: @@ -37,8 +37,8 @@ namespace tunnel TunnelGateway (TunnelBase * tunnel): m_Tunnel (tunnel), m_Buffer (tunnel->GetNextTunnelID ()), m_NumSentBytes (0) {}; - void SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, i2p::I2NPMessage * msg); - void PutTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, i2p::I2NPMessage * msg); + void SendTunnelDataMsg (const TunnelMessageBlock& block); + void PutTunnelDataMsg (const TunnelMessageBlock& block); void SendBuffer (); size_t GetNumSentBytes () const { return m_NumSentBytes; };