diff --git a/Garlic.cpp b/Garlic.cpp index ecaed829..7a9585a8 100644 --- a/Garlic.cpp +++ b/Garlic.cpp @@ -4,6 +4,8 @@ #include #include "ElGamal.h" #include "RouterContext.h" +#include "I2NPProtocol.h" +#include "Tunnel.h" #include "Timestamp.h" #include "Streaming.h" #include "Garlic.h" @@ -29,7 +31,7 @@ namespace garlic delete[] m_SessionTags; } - I2NPMessage * GarlicRoutingSession::WrapSingleMessage (I2NPMessage * msg) + I2NPMessage * GarlicRoutingSession::WrapSingleMessage (I2NPMessage * msg, I2NPMessage * leaseSet) { I2NPMessage * m = NewI2NPMessage (); size_t len = 0; @@ -46,7 +48,7 @@ namespace garlic buf += 514; // AES block m_Encryption.SetKeyWithIV (m_SessionKey, 32, iv); - len += 514 + CreateAESBlock (buf, msg); + len += 514 + CreateAESBlock (buf, msg, leaseSet); } else // existing session { @@ -57,17 +59,18 @@ namespace garlic CryptoPP::SHA256().CalculateDigest(iv, m_SessionTags + m_NextTag*32, 32); m_Encryption.SetKeyWithIV (m_SessionKey, 32, iv); // AES block - len += 32 + CreateAESBlock (buf, msg); + len += 32 + CreateAESBlock (buf, msg, leaseSet); } m_NextTag++; *(uint32_t *)(m->GetPayload ()) = htobe32 (len); m->len += len + 4; FillI2NPMessageHeader (m, eI2NPGarlic); - DeleteI2NPMessage (msg); + if (msg) + DeleteI2NPMessage (msg); return m; } - size_t GarlicRoutingSession::CreateAESBlock (uint8_t * buf, I2NPMessage * msg) + size_t GarlicRoutingSession::CreateAESBlock (uint8_t * buf, I2NPMessage * msg, I2NPMessage * leaseSet) { size_t blockSize = 0; *(uint16_t *)buf = htobe16 (m_NumTags); // tag count @@ -80,7 +83,7 @@ namespace garlic blockSize += 32; buf[blockSize] = 0; // flag blockSize++; - size_t len = CreateGarlicPayload (buf + blockSize, msg); + size_t len = CreateGarlicPayload (buf + blockSize, msg, leaseSet); *payloadSize = htobe32 (len); CryptoPP::SHA256().CalculateDigest(payloadHash, buf + blockSize, len); blockSize += len; @@ -91,41 +94,104 @@ namespace garlic return blockSize; } - size_t GarlicRoutingSession::CreateGarlicPayload (uint8_t * payload, I2NPMessage * msg) + size_t GarlicRoutingSession::CreateGarlicPayload (uint8_t * payload, I2NPMessage * msg, I2NPMessage * leaseSet) { uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 5000; // 5 sec + uint32_t msgID = m_Rnd.GenerateWord32 (); size_t size = 0; - payload[size] = 1; // 1 clove + uint8_t * numCloves = payload + size; + *numCloves = 0; size++; - if (m_Destination->IsDestination ()) + + if (leaseSet) { - payload[size] = eGarlicDeliveryTypeDestination << 5;// delivery instructions flag destination + // clove is DeliveryStatus is LeaseSet is presented + size += CreateDeliveryStatusClove (payload + size, msgID); + (*numCloves)++; + + // clove is our leaseSet if presented + size += CreateGarlicClove (payload + size, leaseSet, false); + (*numCloves)++; + } + if (msg) // clove message ifself if presented + { + size += CreateGarlicClove (payload + size, msg, m_Destination->IsDestination ()); + (*numCloves)++; + } + + memset (payload + size, 0, 3); // certificate of message + size += 3; + *(uint32_t *)(payload + size) = htobe32 (msgID); // MessageID + size += 4; + *(uint64_t *)(payload + size) = htobe64 (ts); // Expiration of message + size += 8; + return size; + } + + size_t GarlicRoutingSession::CreateGarlicClove (uint8_t * buf, I2NPMessage * msg, bool isDestination) + { + uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 5000; // 5 sec + size_t size = 0; + if (isDestination) + { + buf[size] = eGarlicDeliveryTypeDestination << 5;// delivery instructions flag destination size++; - memcpy (payload + size, m_Destination->GetIdentHash (), 32); + memcpy (buf + size, m_Destination->GetIdentHash (), 32); size += 32; } else { - payload[size] = 0;// delivery instructions flag local + buf[size] = 0;// delivery instructions flag local size++; } - memcpy (payload + size, msg->GetBuffer (), msg->GetLength ()); + + memcpy (buf + size, msg->GetBuffer (), msg->GetLength ()); size += msg->GetLength (); - *(uint32_t *)(payload + size) = htobe32 (m_Rnd.GenerateWord32 ()); // CloveID + *(uint32_t *)(buf + size) = htobe32 (m_Rnd.GenerateWord32 ()); // CloveID size += 4; - *(uint64_t *)(payload + size) = htobe64 (ts); // Expiration of clove + *(uint64_t *)(buf + size) = htobe64 (ts); // Expiration of clove size += 8; - memset (payload + size, 0, 3); // certificate of clove + memset (buf + size, 0, 3); // certificate of clove size += 3; - memset (payload + size, 0, 3); // certificate of message - size += 3; - *(uint32_t *)(payload + size) = htobe32 (m_Rnd.GenerateWord32 ()); // MessageID - size += 4; - *(uint64_t *)(payload + size) = htobe64 (ts); // Expiration of message - size += 8; return size; } + size_t GarlicRoutingSession::CreateDeliveryStatusClove (uint8_t * buf, uint32_t msgID) + { + size_t size = 0; + auto tunnel = i2p::tunnel::tunnels.GetNextInboundTunnel (); + if (tunnel) + { + buf[size] = eGarlicDeliveryTypeTunnel << 5; // delivery instructions flag tunnel + size++; + *(uint32_t *)(buf + size) = htobe32 (tunnel->GetNextTunnelID ()); // tunnelID + size += 4; + memcpy (buf + size, tunnel->GetNextIdentHash (), 32); // To Hash + size += 32; + } + else + { + LogPrint ("No reply tunnels for garlic DeliveryStatus found"); + buf[size] = 0;// delivery instructions flag local + size++; + } + + + I2NPMessage * msg = CreateDeliveryStatusMsg (msgID); + memcpy (buf + size, msg->GetBuffer (), msg->GetLength ()); + size += msg->GetLength (); + DeleteI2NPMessage (msg); + uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 5000; // 5 sec + *(uint32_t *)(buf + size) = htobe32 (m_Rnd.GenerateWord32 ()); // CloveID + size += 4; + *(uint64_t *)(buf + size) = htobe64 (ts); // Expiration of clove + size += 8; + memset (buf + size, 0, 3); // certificate of clove + size += 3; + + return size; + } + GarlicRouting routing; GarlicRouting::GarlicRouting () { @@ -138,7 +204,8 @@ namespace garlic m_Sessions.clear (); } - I2NPMessage * GarlicRouting::WrapSingleMessage (const i2p::data::RoutingDestination * destination, I2NPMessage * msg) + I2NPMessage * GarlicRouting::WrapSingleMessage (const i2p::data::RoutingDestination * destination, + I2NPMessage * msg, I2NPMessage * leaseSet) { if (!destination) return nullptr; auto it = m_Sessions.find (destination->GetIdentHash ()); @@ -151,7 +218,7 @@ namespace garlic m_Sessions[destination->GetIdentHash ()] = session; } - I2NPMessage * ret = session->WrapSingleMessage (msg); + I2NPMessage * ret = session->WrapSingleMessage (msg, leaseSet); if (session->GetNumRemainingSessionTags () <= 0) { m_Sessions.erase (destination->GetIdentHash ()); @@ -160,7 +227,7 @@ namespace garlic return ret; } - void GarlicRouting::HandleGarlicMessage (uint8_t * buf, size_t len) + void GarlicRouting::HandleGarlicMessage (uint8_t * buf, size_t len, bool isFromTunnel) { uint32_t length = be32toh (*(uint32_t *)buf); buf += 4; @@ -178,7 +245,9 @@ namespace garlic { // new session ElGamalBlock elGamal; - i2p::crypto::ElGamalDecrypt (i2p::context.GetLeaseSetPrivateKey (), buf, (uint8_t *)&elGamal, true); + i2p::crypto::ElGamalDecrypt ( + isFromTunnel ? i2p::context.GetLeaseSetPrivateKey () : i2p::context.GetPrivateKey (), + buf, (uint8_t *)&elGamal, true); memcpy (m_SessionKey, elGamal.sessionKey, 32); uint8_t iv[32]; // IV is first 16 bytes CryptoPP::SHA256().CalculateDigest(iv, elGamal.preIV, 32); @@ -227,7 +296,7 @@ namespace garlic { case eGarlicDeliveryTypeLocal: LogPrint ("Garlic type local"); - i2p::HandleI2NPMessage (buf, len); + i2p::HandleI2NPMessage (buf, len, false); break; case eGarlicDeliveryTypeDestination: { diff --git a/Garlic.h b/Garlic.h index cdc251f6..81675fa9 100644 --- a/Garlic.h +++ b/Garlic.h @@ -40,13 +40,15 @@ namespace garlic GarlicRoutingSession (const i2p::data::RoutingDestination * destination, int numTags); ~GarlicRoutingSession (); - I2NPMessage * WrapSingleMessage (I2NPMessage * msg); + I2NPMessage * WrapSingleMessage (I2NPMessage * msg, I2NPMessage * leaseSet); int GetNumRemainingSessionTags () const { return m_NumTags - m_NextTag; }; private: - size_t CreateAESBlock (uint8_t * buf, I2NPMessage * msg); - size_t CreateGarlicPayload (uint8_t * payload, I2NPMessage * msg); + size_t CreateAESBlock (uint8_t * buf, I2NPMessage * msg, I2NPMessage * leaseSet); + size_t CreateGarlicPayload (uint8_t * payload, I2NPMessage * msg, I2NPMessage * leaseSet); + size_t CreateGarlicClove (uint8_t * buf, I2NPMessage * msg, bool isDestination); + size_t CreateDeliveryStatusClove (uint8_t * buf, uint32_t msgID); private: @@ -66,9 +68,10 @@ namespace garlic GarlicRouting (); ~GarlicRouting (); - void HandleGarlicMessage (uint8_t * buf, size_t len); + void HandleGarlicMessage (uint8_t * buf, size_t len, bool isFromTunnel); - I2NPMessage * WrapSingleMessage (const i2p::data::RoutingDestination * destination, I2NPMessage * msg); + I2NPMessage * WrapSingleMessage (const i2p::data::RoutingDestination * destination, + I2NPMessage * msg, I2NPMessage * leaseSet = nullptr); private: diff --git a/HTTPServer.cpp b/HTTPServer.cpp index 88e7b272..577ef748 100644 --- a/HTTPServer.cpp +++ b/HTTPServer.cpp @@ -104,15 +104,22 @@ namespace util s << "-->" << it.second->GetTunnelID (); else s << "-->" << it.second->GetTunnelID () << "-->"; - s << "
"; + s << " " << it.second->GetNumTransmittedBytes () << "
"; } s << "

Transports

"; for (auto it: i2p::transports.GetNTCPSessions ()) { + // RouterInfo of incoming connection doesn't have address + bool outgoing = it.second->GetRemoteRouterInfo ().GetNTCPAddress (); if (it.second->IsEstablished ()) + { + if (outgoing) s << "-->"; s << it.second->GetRemoteRouterInfo ().GetIdentHashAbbreviation () << ": " - << it.second->GetSocket ().remote_endpoint().address ().to_string () << "
"; + << it.second->GetSocket ().remote_endpoint().address ().to_string (); + if (!outgoing) s << "-->"; + s << "
"; + } } } diff --git a/I2NPProtocol.cpp b/I2NPProtocol.cpp index 87fced42..9395c40c 100644 --- a/I2NPProtocol.cpp +++ b/I2NPProtocol.cpp @@ -67,7 +67,7 @@ namespace i2p return msg; } - I2NPMessage * CreateDeliveryStatusMsg () + I2NPMessage * CreateDeliveryStatusMsg (uint32_t msgID) { #pragma pack(1) struct @@ -77,13 +77,13 @@ namespace i2p } msg; #pragma pack () - msg.msgID = 0; + msg.msgID = htobe32 (msgID); msg.timestamp = htobe64 (i2p::util::GetMillisecondsSinceEpoch ()); return CreateI2NPMessage (eI2NPDeliveryStatus, (uint8_t *)&msg, sizeof (msg)); } I2NPMessage * CreateDatabaseLookupMsg (const uint8_t * key, const uint8_t * from, - uint32_t replyTunnelID, bool exploratory) + uint32_t replyTunnelID, bool exploratory, std::set * excludedPeers) { I2NPMessage * m = NewI2NPMessage (); uint8_t * buf = m->GetPayload (); @@ -113,15 +113,59 @@ namespace i2p } else { - // nothing to exclude - *(uint16_t *)buf = htobe16 (0); - buf += 2; + if (excludedPeers) + { + int cnt = excludedPeers->size (); + *(uint16_t *)buf = htobe16 (cnt); + buf += 2; + for (auto& it: *excludedPeers) + { + memcpy (buf, it, 32); + buf += 32; + } + } + else + { + // nothing to exclude + *(uint16_t *)buf = htobe16 (0); + buf += 2; + } } m->len += (buf - m->GetPayload ()); FillI2NPMessageHeader (m, eI2NPDatabaseLookup); return m; } + void HandleDatabaseLookupMsg (uint8_t * buf, size_t len) + { + char key[48]; + int l = i2p::data::ByteStreamToBase64 (buf, 32, key, 48); + key[l] = 0; + LogPrint ("DatabaseLookup for ", key, " recieved"); + uint8_t flag = buf[64]; + uint32_t replyTunnelID = 0; + if (flag & 0x01) //reply to yunnel + replyTunnelID = be32toh (*(uint32_t *)(buf + 64)); + // TODO: implement search. We send non-found for now + I2NPMessage * replyMsg = CreateDatabaseSearchReply (buf); + if (replyTunnelID) + i2p::tunnel::tunnels.GetNextOutboundTunnel ()->SendTunnelDataMsg (buf+32, replyTunnelID, replyMsg); + else + i2p::transports.SendMessage (buf, replyMsg); + } + + I2NPMessage * CreateDatabaseSearchReply (const i2p::data::IdentHash& ident) + { + I2NPMessage * m = NewI2NPMessage (); + uint8_t * buf = m->GetPayload (); + memcpy (buf, ident, 32); + buf[32] = 0; // TODO: + memcpy (buf + 33, i2p::context.GetRouterInfo ().GetIdentHash (), 32); + m->len += 65; + FillI2NPMessageHeader (m, eI2NPDatabaseSearchReply); + return m; + } + I2NPMessage * CreateDatabaseStoreMsg () { I2NPMessage * m = NewI2NPMessage (); @@ -370,7 +414,7 @@ namespace i2p return be16toh (header->size) + sizeof (I2NPHeader); } - void HandleI2NPMessage (uint8_t * msg, size_t len) + void HandleI2NPMessage (uint8_t * msg, size_t len, bool isFromTunnel) { I2NPHeader * header = (I2NPHeader *)msg; uint32_t msgID = be32toh (header->msgID); @@ -382,7 +426,7 @@ namespace i2p { case eI2NPGarlic: LogPrint ("Garlic"); - i2p::garlic::routing.HandleGarlicMessage (buf, size); + i2p::garlic::routing.HandleGarlicMessage (buf, size, isFromTunnel); break; break; case eI2NPDeliveryStatus: @@ -395,13 +439,17 @@ namespace i2p case eI2NPVariableTunnelBuildReply: LogPrint ("VariableTunnelBuildReply"); HandleVariableTunnelBuildReplyMsg (msgID, buf, size); - break; + break; + case eI2NPDatabaseLookup: + LogPrint ("DatabaseLookup"); + HandleDatabaseLookupMsg (buf, size); + break; default: LogPrint ("Unexpected message ", (int)header->typeID); } } - void HandleI2NPMessage (I2NPMessage * msg) + void HandleI2NPMessage (I2NPMessage * msg, bool isFromTunnel) { if (msg) { @@ -424,7 +472,7 @@ namespace i2p i2p::data::netdb.PostI2NPMsg (msg); break; default: - HandleI2NPMessage (msg->GetBuffer (), msg->GetLength ()); + HandleI2NPMessage (msg->GetBuffer (), msg->GetLength (), isFromTunnel); DeleteI2NPMessage (msg); } } diff --git a/I2NPProtocol.h b/I2NPProtocol.h index 2b85744c..ebba0c5e 100644 --- a/I2NPProtocol.h +++ b/I2NPProtocol.h @@ -2,7 +2,7 @@ #define I2NP_PROTOCOL_H__ #include -#include +#include #include #include "RouterInfo.h" @@ -103,10 +103,13 @@ namespace i2p I2NPMessage * CreateI2NPMessage (I2NPMessageType msgType, const uint8_t * buf, int len, uint32_t replyMsgID = 0); I2NPMessage * CreateI2NPMessage (const uint8_t * buf, int len); - I2NPMessage * CreateDeliveryStatusMsg (); + I2NPMessage * CreateDeliveryStatusMsg (uint32_t msgID); I2NPMessage * CreateDatabaseLookupMsg (const uint8_t * key, const uint8_t * from, - uint32_t replyTunnelID, bool exploratory = false); - + uint32_t replyTunnelID, bool exploratory = false, + std::set * excludedPeers = nullptr); + void HandleDatabaseLookupMsg (uint8_t * buf, size_t len); + I2NPMessage * CreateDatabaseSearchReply (const i2p::data::IdentHash& ident); + I2NPMessage * CreateDatabaseStoreMsg (); I2NPBuildRequestRecordClearText CreateBuildRequestRecord ( @@ -132,8 +135,8 @@ namespace i2p I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessage * msg); size_t GetI2NPMessageLength (uint8_t * msg); - void HandleI2NPMessage (uint8_t * msg, size_t len); - void HandleI2NPMessage (I2NPMessage * msg); + void HandleI2NPMessage (uint8_t * msg, size_t len, bool isFromTunnel); + void HandleI2NPMessage (I2NPMessage * msg, bool isFromTunnel); } #endif diff --git a/Identity.cpp b/Identity.cpp index bbdfdbe8..f57ab97b 100644 --- a/Identity.cpp +++ b/Identity.cpp @@ -1,3 +1,5 @@ +#include +#include #include #include #include @@ -9,6 +11,14 @@ namespace i2p { namespace data { + Identity& Identity::operator=(const Keys& keys) + { + // copy public and signing keys together + memcpy (publicKey, keys.publicKey, sizeof (publicKey) + sizeof (signingKey)); + memset (certificate, 0, sizeof (certificate)); + return *this; + } + IdentHash CalculateIdentHash (const Identity& identity) { IdentHash hash; @@ -35,5 +45,30 @@ namespace data return keys; } + + RoutingKey CreateRoutingKey (const IdentHash& ident) + { + uint8_t buf[41]; // ident + yyyymmdd + memcpy (buf, (const uint8_t *)ident, 32); + time_t t = time (nullptr); + struct tm tm; + gmtime_r (&t, &tm); + sprintf ((char *)(buf + 32),"%4i%2i%2i", tm.tm_year, tm.tm_mon, tm.tm_mday); + + RoutingKey key; + CryptoPP::SHA256().CalculateDigest(key.hash, buf, 40); + return key; + } + + XORMetric operator^(const RoutingKey& key1, const RoutingKey& key2) + { + // TODO: implementation depends on CPU + XORMetric m; + ((uint64_t *)m.metric)[0] = ((uint64_t *)key1.hash)[0] ^ ((uint64_t *)key2.hash)[0]; + ((uint64_t *)m.metric)[1] = ((uint64_t *)key1.hash)[1] ^ ((uint64_t *)key2.hash)[1]; + ((uint64_t *)m.metric)[2] = ((uint64_t *)key1.hash)[2] ^ ((uint64_t *)key2.hash)[2]; + ((uint64_t *)m.metric)[3] = ((uint64_t *)key1.hash)[3] ^ ((uint64_t *)key2.hash)[3]; + return m; + } } } diff --git a/Identity.h b/Identity.h index 05a317e5..e17b27be 100644 --- a/Identity.h +++ b/Identity.h @@ -23,6 +23,8 @@ namespace data uint8_t publicKey[256]; uint8_t signingKey[128]; uint8_t certificate[3]; + + Identity& operator=(const Keys& keys); }; #pragma pack() @@ -59,7 +61,26 @@ namespace data IdentHash CalculateIdentHash (const Identity& identity); Keys CreateRandomKeys (); + + // kademlia + struct RoutingKey + { + uint8_t hash[32]; + }; + + struct XORMetric + { + uint8_t metric[32]; + + void SetMin () { memset (metric, 0, 32); }; + void SetMax () { memset (metric, 0xFF, 32); }; + bool operator< (const XORMetric& other) const { return memcmp (metric, other.metric, 32) < 0; }; + }; + + RoutingKey CreateRoutingKey (const IdentHash& ident); + XORMetric operator^(const RoutingKey& key1, const RoutingKey& key2); + // destination for delivery instuctions class RoutingDestination { public: diff --git a/LeaseSet.cpp b/LeaseSet.cpp index 9a646e98..d7939d2d 100644 --- a/LeaseSet.cpp +++ b/LeaseSet.cpp @@ -1,3 +1,5 @@ +#include +#include "CryptoConst.h" #include "Log.h" #include "LeaseSet.h" @@ -24,10 +26,22 @@ namespace data memcpy (m_EncryptionKey, header->encryptionKey, 256); LogPrint ("LeaseSet num=", (int)header->num); + const uint8_t * leases = buf + sizeof (H); for (int i = 0; i < header->num; i++) { - m_Leases.push_back (*(Lease *)(buf + sizeof (H))); + Lease lease = *(Lease *)leases; + lease.tunnelID = be32toh (lease.tunnelID); + m_Leases.push_back (lease); + leases += sizeof (Lease); } + + // verify + CryptoPP::DSA::PublicKey pubKey; + pubKey.Initialize (i2p::crypto::dsap, i2p::crypto::dsaq, i2p::crypto::dsag, + CryptoPP::Integer (m_Identity.signingKey, 128)); + CryptoPP::DSA::Verifier verifier (pubKey); + if (!verifier.VerifyMessage (buf, leases - buf, leases, 40)) + LogPrint ("LeaseSet verification failed"); } } } diff --git a/LeaseSet.h b/LeaseSet.h index 55a2e4b1..5bb75741 100644 --- a/LeaseSet.h +++ b/LeaseSet.h @@ -3,7 +3,7 @@ #include #include -#include +#include #include "Identity.h" namespace i2p @@ -31,12 +31,13 @@ namespace data // implements RoutingDestination const Identity& GetIdentity () const { return m_Identity; }; const IdentHash& GetIdentHash () const { return m_IdentHash; }; + const std::vector& GetLeases () const { return m_Leases; }; const uint8_t * GetEncryptionPublicKey () const { return m_EncryptionKey; }; bool IsDestination () const { return true; }; private: - std::list m_Leases; + std::vector m_Leases; Identity m_Identity; IdentHash m_IdentHash; uint8_t m_EncryptionKey[256]; diff --git a/NTCPSession.cpp b/NTCPSession.cpp index f8f48100..3174e4f5 100644 --- a/NTCPSession.cpp +++ b/NTCPSession.cpp @@ -20,12 +20,10 @@ namespace i2p { namespace ntcp { - NTCPSession::NTCPSession (boost::asio::io_service& service, i2p::data::RouterInfo * in_RemoteRouterInfo): + NTCPSession::NTCPSession (boost::asio::io_service& service, i2p::data::RouterInfo& in_RemoteRouterInfo): m_Socket (service), m_TerminationTimer (service), m_IsEstablished (false), - m_ReceiveBufferOffset (0), m_NextMessage (nullptr) + m_RemoteRouterInfo (in_RemoteRouterInfo), m_ReceiveBufferOffset (0), m_NextMessage (nullptr) { - if (in_RemoteRouterInfo) - m_RemoteRouterInfo = *in_RemoteRouterInfo; } void NTCPSession::CreateAESKey (uint8_t * pubKey, uint8_t * aesKey) @@ -186,7 +184,8 @@ namespace ntcp { if (ecode) { - LogPrint ("Phase 2 read error: ", ecode.message ()); + LogPrint ("Phase 2 read error: ", ecode.message (), ". Wrong ident assumed"); + GetRemoteRouterInfo ().SetUnreachable (true); // this RouterInfo is not valid Terminate (); } else @@ -385,7 +384,8 @@ namespace ntcp if (m_ReceiveBufferOffset > 0) memcpy (m_ReceiveBuffer, nextBlock, m_ReceiveBufferOffset); } - + + ScheduleTermination (); // reset termination timer Receive (); } } @@ -424,7 +424,7 @@ namespace ntcp if (m_NextMessageOffset >= m_NextMessage->len + 4) // +checksum { // we have a complete I2NP message - i2p::HandleI2NPMessage (m_NextMessage); + i2p::HandleI2NPMessage (m_NextMessage, false); m_NextMessage = nullptr; } } @@ -461,14 +461,10 @@ namespace ntcp m_Adler.CalculateDigest (sendBuffer + len + 2 + padding, sendBuffer, len + 2+ padding); int l = len + padding + 6; - { - std::lock_guard lock (m_EncryptionMutex); - m_Encryption.ProcessData(sendBuffer, sendBuffer, l); - } + m_Encryption.ProcessData(sendBuffer, sendBuffer, l); boost::asio::async_write (m_Socket, boost::asio::buffer (sendBuffer, l), boost::asio::transfer_all (), boost::bind(&NTCPSession::HandleSent, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred, msg)); - ScheduleTermination (); // reset termination timer } void NTCPSession::HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, i2p::I2NPMessage * msg) @@ -483,6 +479,7 @@ namespace ntcp else { LogPrint ("Msg sent: ", bytes_transferred); + ScheduleTermination (); // reset termination timer } } @@ -521,7 +518,8 @@ namespace ntcp NTCPClient::NTCPClient (boost::asio::io_service& service, const char * address, - int port, i2p::data::RouterInfo& in_RouterInfo): NTCPSession (service, &in_RouterInfo), + int port, i2p::data::RouterInfo& in_RouterInfo): + NTCPSession (service, in_RouterInfo), m_Endpoint (boost::asio::ip::address::from_string (address), port) { Connect (); diff --git a/NTCPSession.h b/NTCPSession.h index 4ef0e3dd..5d33ef10 100644 --- a/NTCPSession.h +++ b/NTCPSession.h @@ -2,7 +2,6 @@ #define NTCP_SESSION_H__ #include -#include #include #include #include @@ -66,7 +65,7 @@ namespace ntcp { public: - NTCPSession (boost::asio::io_service& service, i2p::data::RouterInfo * in_RemoteRouterInfo = 0); + NTCPSession (boost::asio::io_service& service, i2p::data::RouterInfo& in_RemoteRouterInfo); virtual ~NTCPSession () {}; boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; }; @@ -126,7 +125,7 @@ namespace ntcp CryptoPP::CBC_Mode::Encryption m_Encryption; CryptoPP::Adler32 m_Adler; - i2p::data::RouterInfo m_RemoteRouterInfo; + i2p::data::RouterInfo& m_RemoteRouterInfo; NTCPPhase1 m_Phase1; NTCPPhase2 m_Phase2; @@ -139,8 +138,6 @@ namespace ntcp i2p::I2NPMessage * m_NextMessage; std::list m_DelayedMessages; size_t m_NextMessageOffset; - - std::mutex m_EncryptionMutex; }; class NTCPClient: public NTCPSession @@ -163,11 +160,16 @@ namespace ntcp { public: - NTCPServerConnection (boost::asio::io_service& service): NTCPSession (service) {}; + NTCPServerConnection (boost::asio::io_service& service): + NTCPSession (service, m_DummyRemoteRouterInfo) {}; protected: virtual void Connected (); + + private: + + i2p::data::RouterInfo m_DummyRemoteRouterInfo; }; } } diff --git a/NetDb.cpp b/NetDb.cpp index 33ad940a..7aaa7f42 100644 --- a/NetDb.cpp +++ b/NetDb.cpp @@ -8,16 +8,30 @@ #include "I2NPProtocol.h" #include "Tunnel.h" #include "RouterContext.h" +#include "Garlic.h" #include "NetDb.h" namespace i2p { namespace data { + + I2NPMessage * RequestedDestination::CreateRequestMessage (const RouterInfo * router, + const i2p::tunnel::InboundTunnel * replyTunnel) + { + I2NPMessage * msg = i2p::CreateDatabaseLookupMsg (m_Destination, + replyTunnel->GetNextIdentHash (), replyTunnel->GetNextTunnelID (), m_IsExploratory, &m_ExcludedPeers); + if (m_IsLeaseSet) // wrap lookup message into garlic + msg = i2p::garlic::routing.WrapSingleMessage (router, msg); + m_ExcludedPeers.insert (router->GetIdentHash ()); + m_LastRouter = router; + m_LastReplyTunnel = replyTunnel; + return msg; + } NetDb netdb; - NetDb::NetDb (): m_IsRunning (false), m_Thread (0), m_LastFloodfill (0) + NetDb::NetDb (): m_IsRunning (false), m_Thread (0) { } @@ -28,6 +42,8 @@ namespace data delete l.second; for (auto r:m_RouterInfos) delete r.second; + for (auto r:m_RequestedDestinations) + delete r.second; } void NetDb::Start () @@ -70,7 +86,7 @@ namespace data else // WTF? { LogPrint ("NetDb: unexpected message type ", msg->GetHeader ()->typeID); - i2p::HandleI2NPMessage (msg); + i2p::HandleI2NPMessage (msg, false); } msg = m_Queue.Get (); } @@ -96,6 +112,7 @@ namespace data void NetDb::AddRouterInfo (uint8_t * buf, int len) { RouterInfo * r = new RouterInfo (buf, len); + DeleteRequestedDestination (r->GetIdentHash ()); auto it = m_RouterInfos.find(r->GetIdentHash ()); if (it != m_RouterInfos.end ()) { @@ -117,6 +134,7 @@ namespace data void NetDb::AddLeaseSet (uint8_t * buf, int len) { LeaseSet * l = new LeaseSet (buf, len); + DeleteRequestedDestination (l->GetIdentHash ()); m_LeaseSets[l->GetIdentHash ()] = l; } @@ -128,6 +146,15 @@ namespace data else return nullptr; } + + LeaseSet * NetDb::FindLeaseSet (const IdentHash& destination) const + { + auto it = m_LeaseSets.find (destination); + if (it != m_LeaseSets.end ()) + return it->second; + else + return nullptr; + } void NetDb::Load (const char * directory) { @@ -156,39 +183,67 @@ namespace data void NetDb::SaveUpdated (const char * directory) { - int count = 0; + auto GetFilePath = [](const char * directory, const RouterInfo * routerInfo) + { + return std::string (directory) + "/r" + + routerInfo->GetIdentHashBase64 ()[0] + "/routerInfo-" + + routerInfo->GetIdentHashBase64 () + ".dat"; + }; + + int count = 0, deletedCount = 0; for (auto it: m_RouterInfos) + { if (it.second->IsUpdated ()) { - std::ofstream r (std::string (directory) + "/r" + - it.second->GetIdentHashBase64 ()[0] + "/routerInfo-" + - it.second->GetIdentHashBase64 () + ".dat"); + std::ofstream r (GetFilePath(directory, it.second)); r.write ((char *)it.second->GetBuffer (), it.second->GetBufferLen ()); it.second->SetUpdated (false); count++; } + else if (it.second->IsUnreachable ()) + { + if (boost::filesystem::exists (GetFilePath (directory, it.second))) + { + boost::filesystem::remove (GetFilePath (directory, it.second)); + deletedCount++; + } + } + } if (count > 0) LogPrint (count," new/updated routers saved"); + if (deletedCount > 0) + LogPrint (deletedCount," routers deleted"); } - void NetDb::RequestDestination (const char * b32, const uint8_t * router) + void NetDb::RequestDestination (const char * b32) { uint8_t destination[32]; Base32ToByteStream (b32, strlen(b32), destination, 32); - RequestDestination (destination, router); + RequestDestination (destination, true); + } + + void NetDb::RequestDestination (const IdentHash& destination, bool isLeaseSet) + { + auto floodfill= GetRandomNTCPRouter (true); + if (floodfill) + RequestDestination (destination, floodfill, isLeaseSet); + else + LogPrint ("No floodfill routers found"); } - void NetDb::RequestDestination (const uint8_t * destination, const uint8_t * router) + void NetDb::RequestDestination (const IdentHash& destination, const RouterInfo * floodfill, bool isLeaseSet) { + if (!floodfill) return; i2p::tunnel::OutboundTunnel * outbound = i2p::tunnel::tunnels.GetNextOutboundTunnel (); if (outbound) { i2p::tunnel::InboundTunnel * inbound = i2p::tunnel::tunnels.GetNextInboundTunnel (); if (inbound) { - I2NPMessage * msg = i2p::CreateDatabaseLookupMsg (destination, inbound->GetNextIdentHash (), - inbound->GetNextTunnelID ()); - outbound->SendTunnelDataMsg (router, 0, msg); + RequestedDestination * dest = CreateRequestedDestination (destination, isLeaseSet); + dest->SetLastOutboundTunnel (outbound); + auto msg = dest->CreateRequestMessage (floodfill, inbound); + outbound->SendTunnelDataMsg (floodfill->GetIdentHash (), 0, msg); } else LogPrint ("No inbound tunnels found"); @@ -196,7 +251,7 @@ namespace data else LogPrint ("No outbound tunnels found"); } - + void NetDb::HandleDatabaseStoreMsg (uint8_t * buf, size_t len) { I2NPDatabaseStoreMsg * msg = (I2NPDatabaseStoreMsg *)buf; @@ -236,83 +291,133 @@ namespace data key[l] = 0; int num = buf[32]; // num LogPrint ("DatabaseSearchReply for ", key, " num=", num); - if (num > 0) - { - bool isExploratory = !memcmp (m_Exploratory, buf, 32) && m_LastFloodfill; - i2p::tunnel::OutboundTunnel * outbound = i2p::tunnel::tunnels.GetNextOutboundTunnel (); - i2p::tunnel::InboundTunnel * inbound = i2p::tunnel::tunnels.GetNextInboundTunnel (); + auto it = m_RequestedDestinations.find (IdentHash (buf)); + if (it != m_RequestedDestinations.end ()) + { + RequestedDestination * dest = it->second; + if (num > 0) + { + i2p::tunnel::OutboundTunnel * outbound = dest->GetLastOutboundTunnel (); + const i2p::tunnel::InboundTunnel * inbound = dest->GetLastReplyTunnel (); - for (int i = 0; i < num; i++) - { - uint8_t * router = buf + 33 + i*32; - char peerHash[48]; - int l1 = i2p::data::ByteStreamToBase64 (router, 32, peerHash, 48); - peerHash[l1] = 0; - LogPrint (i,": ", peerHash); + for (int i = 0; i < num; i++) + { + uint8_t * router = buf + 33 + i*32; + char peerHash[48]; + int l1 = i2p::data::ByteStreamToBase64 (router, 32, peerHash, 48); + peerHash[l1] = 0; + LogPrint (i,": ", peerHash); - if (isExploratory) - { - if (m_RouterInfos.find (IdentHash(router)) == m_RouterInfos.end ()) + if (dest->IsExploratory ()) { - LogPrint ("Found new router. Requesting RouterInfo ..."); + if (!FindRouter (router)) // router with ident not found + { + LogPrint ("Found new router. Requesting RouterInfo ..."); + if (outbound && inbound) + { + RequestedDestination * d1 = CreateRequestedDestination (router, false, false); + d1->SetLastOutboundTunnel (outbound); + auto msg = d1->CreateRequestMessage (dest->GetLastRouter (), dest->GetLastReplyTunnel ()); + outbound->GetTunnelGateway ().PutTunnelDataMsg (dest->GetLastRouter ()->GetIdentHash (), 0, msg); + } + } + else + LogPrint ("Bayan"); + } + else + { + // reply to our destination. Try other floodfills if (outbound && inbound) { - I2NPMessage * msg = i2p::CreateDatabaseLookupMsg (router, inbound->GetNextIdentHash (), - inbound->GetNextTunnelID ()); - outbound->GetTunnelGateway ().PutTunnelDataMsg (m_LastFloodfill->GetIdentHash (), 0, msg); - } - } - else - LogPrint ("Bayan"); - } - else - { - // reply to our destination. Try other floodfills - if (outbound && inbound) - { - // do we have that floodfill router in our database? - if (!FindRouter (router)) - { - // request router - LogPrint ("Found new floodfill. Request it"); - msg = i2p::CreateDatabaseLookupMsg (router, inbound->GetNextIdentHash (), - inbound->GetNextTunnelID ()); - outbound->GetTunnelGateway ().PutTunnelDataMsg ( - GetRandomNTCPRouter (true)->GetIdentHash (), 0, msg); - // request destination - I2NPMessage * msg = i2p::CreateDatabaseLookupMsg (buf, inbound->GetNextIdentHash (), - inbound->GetNextTunnelID ()); - outbound->GetTunnelGateway ().PutTunnelDataMsg (router, 0, msg); + auto r = FindRouter (router); + // do we have that floodfill router in our database? + if (r) + { + if (!dest->IsExcluded (r->GetIdentHash ()) && dest->GetNumExcludedPeers () < 10) // TODO: fix TunnelGateway first + { + // request destination + auto msg = dest->CreateRequestMessage (r, dest->GetLastReplyTunnel ()); + outbound->GetTunnelGateway ().PutTunnelDataMsg (r->GetIdentHash (), 0, msg); + } + } + else + { + // request router + LogPrint ("Found new floodfill. Request it"); + RequestedDestination * d2 = CreateRequestedDestination (router, false, false); + d2->SetLastOutboundTunnel (outbound); + I2NPMessage * msg = d2->CreateRequestMessage (dest->GetLastRouter (), inbound); + outbound->GetTunnelGateway ().PutTunnelDataMsg ( + dest->GetLastRouter ()->GetIdentHash (), 0, msg); + } } } - } - } + } - if (outbound) - outbound->GetTunnelGateway ().SendBuffer (); - } + if (outbound) + outbound->GetTunnelGateway ().SendBuffer (); + } + else + { + // no more requests for detination possible. delete it + m_RequestedDestinations.erase (it); + delete it->second; + } + } + else + LogPrint ("Requested destination for ", key, " not found"); i2p::DeleteI2NPMessage (msg); } void NetDb::Explore () { - i2p::tunnel::OutboundTunnel * outbound = i2p::tunnel::tunnels.GetNextOutboundTunnel (); - i2p::tunnel::InboundTunnel * inbound = i2p::tunnel::tunnels.GetNextInboundTunnel (); + auto outbound = i2p::tunnel::tunnels.GetNextOutboundTunnel (); + auto inbound = i2p::tunnel::tunnels.GetNextInboundTunnel (); if (outbound && inbound) { - m_LastFloodfill = GetRandomNTCPRouter (true); - if (m_LastFloodfill) + auto floodfill = GetRandomNTCPRouter (true); + if (floodfill) { LogPrint ("Exploring new routers ..."); CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); - rnd.GenerateBlock (m_Exploratory, 32); - I2NPMessage * msg = i2p::CreateDatabaseLookupMsg (m_Exploratory, inbound->GetNextIdentHash (), - inbound->GetNextTunnelID (), true); - outbound->SendTunnelDataMsg (m_LastFloodfill->GetIdentHash (), 0, msg); + uint8_t randomHash[32]; + rnd.GenerateBlock (randomHash, 32); + RequestedDestination * dest = CreateRequestedDestination (IdentHash (randomHash), false, true); + dest->SetLastOutboundTunnel (outbound); + + outbound->GetTunnelGateway ().PutTunnelDataMsg (floodfill->GetIdentHash (), 0, + CreateDatabaseStoreMsg ()); // tell floodfill about us + outbound->GetTunnelGateway ().PutTunnelDataMsg (floodfill->GetIdentHash (), 0, + dest->CreateRequestMessage (floodfill, inbound)); // explore + outbound->GetTunnelGateway ().SendBuffer (); } } } + RequestedDestination * NetDb::CreateRequestedDestination (const IdentHash& dest, + bool isLeaseSet, bool isExploratory) + { + auto it = m_RequestedDestinations.find (dest); + if (it == m_RequestedDestinations.end ()) // not exist yet + { + RequestedDestination * d = new RequestedDestination (dest, isLeaseSet, isExploratory); + m_RequestedDestinations[dest] = d; + return d; + } + else + return it->second; + } + + void NetDb::DeleteRequestedDestination (const IdentHash& dest) + { + auto it = m_RequestedDestinations.find (dest); + if (it != m_RequestedDestinations.end ()) + { + m_RequestedDestinations.erase (it); + delete it->second; + } + } + const RouterInfo * NetDb::GetRandomNTCPRouter (bool floodfillOnly) const { CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); @@ -345,5 +450,26 @@ namespace data { if (msg) m_Queue.Put (msg); } + + const RouterInfo * NetDb::GetClosestFloodfill (const IdentHash& destination) const + { + RouterInfo * r = nullptr; + XORMetric minMetric; + RoutingKey destKey = CreateRoutingKey (destination); + minMetric.SetMax (); + for (auto it: m_RouterInfos) + { + if (it.second->IsFloodfill () &&! it.second->IsUnreachable ()) + { + XORMetric m = destKey ^ it.second->GetRoutingKey (); + if (m < minMetric) + { + minMetric = m; + r = it.second; + } + } + } + return r; + } } } diff --git a/NetDb.h b/NetDb.h index c76bd9f7..02a585ef 100644 --- a/NetDb.h +++ b/NetDb.h @@ -2,6 +2,7 @@ #define NETDB_H__ #include +#include #include #include #include @@ -9,11 +10,41 @@ #include "I2NPProtocol.h" #include "RouterInfo.h" #include "LeaseSet.h" +#include "Tunnel.h" namespace i2p { namespace data { + class RequestedDestination + { + public: + + RequestedDestination (const IdentHash& destination, bool isLeaseSet, bool isExploratory = false): + m_Destination (destination), m_IsLeaseSet (isLeaseSet), m_IsExploratory (isExploratory), + m_LastRouter (nullptr), m_LastReplyTunnel (nullptr), m_LastOutboundTunnel (nullptr) {}; + + const IdentHash& GetDestination () const { return m_Destination; }; + int GetNumExcludedPeers () const { return m_ExcludedPeers.size (); }; + const RouterInfo * GetLastRouter () const { return m_LastRouter; }; + const i2p::tunnel::InboundTunnel * GetLastReplyTunnel () const { return m_LastReplyTunnel; }; + bool IsExploratory () const { return m_IsExploratory; }; + bool IsExcluded (const IdentHash& ident) const { return m_ExcludedPeers.count (ident); }; + I2NPMessage * CreateRequestMessage (const RouterInfo * router, const i2p::tunnel::InboundTunnel * replyTunnel); + + i2p::tunnel::OutboundTunnel * GetLastOutboundTunnel () const { return m_LastOutboundTunnel; }; + void SetLastOutboundTunnel (i2p::tunnel::OutboundTunnel * tunnel) { m_LastOutboundTunnel = tunnel; }; + + private: + + IdentHash m_Destination; + bool m_IsLeaseSet, m_IsExploratory; + std::set m_ExcludedPeers; + const RouterInfo * m_LastRouter; + const i2p::tunnel::InboundTunnel * m_LastReplyTunnel; + i2p::tunnel::OutboundTunnel * m_LastOutboundTunnel; + }; + class NetDb { public: @@ -27,9 +58,12 @@ namespace data void AddRouterInfo (uint8_t * buf, int len); void AddLeaseSet (uint8_t * buf, int len); RouterInfo * FindRouter (const IdentHash& ident) const; - - void RequestDestination (const char * b32, const uint8_t * router); // in base32 - void RequestDestination (const uint8_t * destination, const uint8_t * router); + LeaseSet * FindLeaseSet (const IdentHash& destination) const; + + void RequestDestination (const char * b32); // in base32 + void RequestDestination (const IdentHash& destination, bool isLeaseSet = false); + void RequestDestination (const IdentHash& destination, const RouterInfo * floodfill, bool isLeaseSet = false); + void HandleDatabaseStoreMsg (uint8_t * buf, size_t len); void HandleDatabaseSearchReplyMsg (I2NPMessage * msg); @@ -44,16 +78,20 @@ namespace data void SaveUpdated (const char * directory); void Run (); // exploratory thread void Explore (); + const RouterInfo * GetClosestFloodfill (const IdentHash& destination) const; + + RequestedDestination * CreateRequestedDestination (const IdentHash& dest, + bool isLeaseSet, bool isExploratory = false); + void DeleteRequestedDestination (const IdentHash& dest); private: std::map m_LeaseSets; std::map m_RouterInfos; - + std::map m_RequestedDestinations; + bool m_IsRunning; std::thread * m_Thread; - uint8_t m_Exploratory[32]; - const RouterInfo * m_LastFloodfill; i2p::util::Queue m_Queue; // of I2NPDatabaseStoreMsg }; diff --git a/RouterContext.cpp b/RouterContext.cpp index 84fac346..718a9f36 100644 --- a/RouterContext.cpp +++ b/RouterContext.cpp @@ -26,9 +26,7 @@ namespace i2p CryptoPP::Integer (m_Keys.signingPrivateKey, 20)); i2p::data::Identity ident; - // copy public and signing keys together - memcpy (ident.publicKey, m_Keys.publicKey, sizeof (ident.publicKey) + sizeof (ident.signingKey)); - memset (ident.certificate, 0, sizeof (ident.certificate)); + ident = m_Keys; m_RouterInfo.SetRouterIdentity (ident); m_RouterInfo.AddNTCPAddress ("127.0.0.1", 17007); // TODO: diff --git a/RouterInfo.cpp b/RouterInfo.cpp index 1f3f8fee..d804ecad 100644 --- a/RouterInfo.cpp +++ b/RouterInfo.cpp @@ -34,6 +34,8 @@ namespace data { m_RouterIdentity = identity; m_IdentHash = CalculateIdentHash (m_RouterIdentity); + UpdateIdentHashBase64 (); + UpdateRoutingKey (); m_Timestamp = i2p::util::GetMillisecondsSinceEpoch (); } @@ -124,12 +126,23 @@ namespace data } CryptoPP::SHA256().CalculateDigest(m_IdentHash, (uint8_t *)&m_RouterIdentity, sizeof (m_RouterIdentity)); + UpdateIdentHashBase64 (); + UpdateRoutingKey (); + } + + void RouterInfo::UpdateIdentHashBase64 () + { size_t l = i2p::data::ByteStreamToBase64 (m_IdentHash, 32, m_IdentHashBase64, 48); m_IdentHashBase64[l] = 0; memcpy (m_IdentHashAbbreviation, m_IdentHashBase64, 4); m_IdentHashAbbreviation[4] = 0; } + void RouterInfo::UpdateRoutingKey () + { + m_RoutingKey = CreateRoutingKey (m_IdentHash); + } + void RouterInfo::WriteToStream (std::ostream& s) { s.write ((char *)&m_RouterIdentity, sizeof (m_RouterIdentity)); diff --git a/RouterInfo.h b/RouterInfo.h index 780eb8c7..ceda10cb 100644 --- a/RouterInfo.h +++ b/RouterInfo.h @@ -36,6 +36,7 @@ namespace data RouterInfo (const char * filename); RouterInfo () = default; RouterInfo (const RouterInfo& ) = default; + RouterInfo& operator=(const RouterInfo& ) = default; RouterInfo (const uint8_t * buf, int len); const Identity& GetRouterIdentity () const { return m_RouterIdentity; }; @@ -45,6 +46,7 @@ namespace data uint64_t GetTimestamp () const { return m_Timestamp; }; const std::vector
& GetAddresses () const { return m_Addresses; }; Address * GetNTCPAddress (); + const RoutingKey& GetRoutingKey () const { return m_RoutingKey; }; void AddNTCPAddress (const char * host, int port); void SetProperty (const char * key, const char * value); @@ -55,6 +57,7 @@ namespace data bool IsUnreachable () const { return m_IsUnreachable; }; void CreateBuffer (); + void UpdateRoutingKey (); const char * GetBuffer () const { return m_Buffer; }; int GetBufferLen () const { return m_BufferLen; }; @@ -74,11 +77,13 @@ namespace data void WriteToStream (std::ostream& s); size_t ReadString (char * str, std::istream& s); void WriteString (const std::string& str, std::ostream& s); + void UpdateIdentHashBase64 (); private: Identity m_RouterIdentity; IdentHash m_IdentHash; + RoutingKey m_RoutingKey; char m_IdentHashBase64[48], m_IdentHashAbbreviation[5]; char m_Buffer[2048]; int m_BufferLen; diff --git a/Streaming.cpp b/Streaming.cpp index c718c0cf..4ca4b49b 100644 --- a/Streaming.cpp +++ b/Streaming.cpp @@ -4,14 +4,18 @@ #include "Log.h" #include "RouterInfo.h" #include "RouterContext.h" +#include "Tunnel.h" +#include "Timestamp.h" +#include "CryptoConst.h" +#include "Garlic.h" #include "Streaming.h" namespace i2p { namespace stream { - Stream::Stream (const i2p::data::IdentHash& destination): - m_SendStreamID (0) + Stream::Stream (StreamingDestination * local, const i2p::data::LeaseSet * remote): + m_SendStreamID (0), m_SequenceNumber (0), m_LocalDestination (local), m_RemoteLeaseSet (remote) { m_RecvStreamID = i2p::context.GetRandomNumberGenerator ().GenerateWord32 (); } @@ -56,9 +60,68 @@ namespace stream std::string str((const char *)buf, end-buf); LogPrint ("Payload: ", str); } + + size_t Stream::Send (uint8_t * buf, size_t len, int timeout) + { + uint8_t packet[STREAMING_MTU]; + size_t size = 0; + *(uint32_t *)(packet + size) = htobe32 (m_SendStreamID); + size += 4; // sendStreamID + *(uint32_t *)(packet + size) = htobe32 (m_RecvStreamID); + size += 4; // receiveStreamID + *(uint32_t *)(packet + size) = htobe32 (m_SequenceNumber); + size += 4; // sequenceNum + *(uint32_t *)(packet + size) = 0; // TODO + size += 4; // ack Through + packet[size] = 0; + size++; // NACK count + size++; // resend delay + // TODO: for initial packet only, following packets have different falgs + *(uint16_t *)(packet + size) = htobe16 (PACKET_FLAG_SYNCHRONIZE | + PACKET_FLAG_FROM_INCLUDED | PACKET_FLAG_SIGNATURE_INCLUDED | PACKET_FLAG_NO_ACK); + size += 2; // flags + *(uint16_t *)(packet + size) = htobe16 (sizeof (i2p::data::Identity) + 40); // identity + signature + size += 2; // options size + memcpy (packet + size, &m_LocalDestination->GetIdentity (), sizeof (i2p::data::Identity)); + size += sizeof (i2p::data::Identity); // from + uint8_t * signature = packet + size; // set it later + memset (signature, 0, 40); // zeroes for now + size += 40; // signature + memcpy (packet + size, buf, len); + size += len; // payload + m_LocalDestination->Sign (packet, size, signature); + I2NPMessage * msg = i2p::garlic::routing.WrapSingleMessage (m_RemoteLeaseSet, + CreateDataMessage (this, packet, size), m_LocalDestination->GetLeaseSet ()); + + auto outbound = i2p::tunnel::tunnels.GetNextOutboundTunnel (); + if (outbound) + { + auto& lease = m_RemoteLeaseSet->GetLeases ()[0]; // TODO: + outbound->SendTunnelDataMsg (lease.tunnelGateway, lease.tunnelID, msg); + } + else + DeleteI2NPMessage (msg); + return len; + } + + StreamingDestination * sharedLocalDestination = nullptr; + + StreamingDestination::StreamingDestination (): m_LeaseSet (nullptr) + { + // TODO: read from file later + m_Keys = i2p::data::CreateRandomKeys (); + m_Identity = m_Keys; + m_IdentHash = i2p::data::CalculateIdentHash (m_Identity); + m_SigningPrivateKey.Initialize (i2p::crypto::dsap, i2p::crypto::dsaq, i2p::crypto::dsag, + CryptoPP::Integer (m_Keys.signingPrivateKey, 20)); + } + + StreamingDestination::~StreamingDestination () + { + if (m_LeaseSet) + DeleteI2NPMessage (m_LeaseSet); + } - StreamingDestination m_SharedLocalDestination; - void StreamingDestination::HandleNextPacket (const uint8_t * buf, size_t len) { uint32_t sendStreamID = *(uint32_t *)(buf); @@ -69,25 +132,93 @@ namespace stream LogPrint ("Unknown stream ", sendStreamID); } - Stream * StreamingDestination::CreateNewStream (const i2p::data::IdentHash& destination) + Stream * StreamingDestination::CreateNewStream (const i2p::data::LeaseSet * remote) { - /*i2p::data::LeaseSet * leaseSet = i2p::data::netdb.FindLeaseSet (destination); - if (!leaseSet) - { - i2p::data::netdb.RequestDestination (destination); - sleep (5); // wait for 5 seconds - leaseSet = i2p::data::netdb.FindLeaseSet (destination); - if (!leaseSet) - { - LogPrint ("Couldn't find LeaseSet"); - return nullptr; - } - } */ - Stream * s = new Stream (destination); + Stream * s = new Stream (this, remote); m_Streams[s->GetRecvStreamID ()] = s; return s; } - + + void StreamingDestination::DeleteStream (Stream * stream) + { + if (stream) + { + m_Streams.erase (stream->GetRecvStreamID ()); + delete stream; + } + } + + I2NPMessage * StreamingDestination::GetLeaseSet () + { + if (!m_LeaseSet) + m_LeaseSet = CreateLeaseSet (); + else + FillI2NPMessageHeader (m_LeaseSet, eI2NPDatabaseStore); // refresh msgID + return m_LeaseSet; + } + + I2NPMessage * StreamingDestination::CreateLeaseSet () const + { + I2NPMessage * m = NewI2NPMessage (); + I2NPDatabaseStoreMsg * msg = (I2NPDatabaseStoreMsg *)m->GetPayload (); + memcpy (msg->key, (const uint8_t *)m_IdentHash, 32); + msg->type = 1; // LeaseSet + msg->replyToken = 0; + + uint8_t * buf = m->GetPayload () + sizeof (I2NPDatabaseStoreMsg); + size_t size = 0; + memcpy (buf + size, &m_Identity, sizeof (m_Identity)); + size += sizeof (m_Identity); // destination + memcpy (buf + size, i2p::context.GetLeaseSetPublicKey (), 256); + size += 256; // encryption key + memset (buf + size, 0, 128); + size += 128; // signing key + auto tunnel = i2p::tunnel::tunnels.GetNextInboundTunnel (); + if (tunnel) + { + buf[size] = 1; // 1 lease + size++; // num + memcpy (buf + size, (const uint8_t *)tunnel->GetNextIdentHash (), 32); + size += 32; // tunnel_gw + *(uint32_t *)(buf + size) = htobe32 (tunnel->GetNextTunnelID ()); + size += 4; // tunnel_id + uint64_t ts = tunnel->GetCreationTime () + i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT; + ts *= 1000; // in milliseconds + *(uint64_t *)(buf + size) = htobe64 (ts); + size += 8; // end_date + } + else + { + buf[size] = 0; // zero leases + size++; // num + } + Sign (buf, size, buf+ size); + size += 40; // signature + + m->len += size + sizeof (I2NPDatabaseStoreMsg); + FillI2NPMessageHeader (m, eI2NPDatabaseStore); + return m; + } + + void StreamingDestination::Sign (uint8_t * buf, int len, uint8_t * signature) const + { + CryptoPP::DSA::Signer signer (m_SigningPrivateKey); + signer.SignMessage (i2p::context.GetRandomNumberGenerator (), buf, len, signature); + } + + Stream * CreateStream (const i2p::data::LeaseSet * remote) + { + if (!sharedLocalDestination) + sharedLocalDestination = new StreamingDestination (); + return sharedLocalDestination->CreateNewStream (remote); + } + + void CloseStream (Stream * stream) + { + if (sharedLocalDestination) + sharedLocalDestination->DeleteStream (stream); + } + void HandleDataMessage (i2p::data::IdentHash * destination, const uint8_t * buf, size_t len) { uint32_t length = be32toh (*(uint32_t *)buf); @@ -104,7 +235,8 @@ namespace stream decompressor.Get (uncompressed, uncompressedSize); // then forward to streaming engine // TODO: we have onle one destination, might be more - m_SharedLocalDestination.HandleNextPacket (uncompressed, uncompressedSize); + if (sharedLocalDestination) + sharedLocalDestination->HandleNextPacket (uncompressed, uncompressedSize); } else LogPrint ("Data: protocol ", buf[9], " is not supported"); @@ -118,7 +250,7 @@ namespace stream compressor.MessageEnd(); int size = compressor.MaxRetrievable (); uint8_t * buf = msg->GetPayload (); - *(uint16_t *)buf = htobe32 (size); // length + *(uint32_t *)buf = htobe32 (size); // length buf += 4; compressor.Get (buf, size); buf[9] = 6; // streaming protocol diff --git a/Streaming.h b/Streaming.h index c75f0b13..86aeac31 100644 --- a/Streaming.h +++ b/Streaming.h @@ -3,6 +3,8 @@ #include #include +#include +#include "Identity.h" #include "LeaseSet.h" #include "I2NPProtocol.h" @@ -22,32 +24,63 @@ namespace stream const uint16_t PACKET_FLAG_ECHO = 0x0200; const uint16_t PACKET_FLAG_NO_ACK = 0x0400; + const size_t STREAMING_MTU = 1730; + + class StreamingDestination; class Stream { public: - Stream (const i2p::data::IdentHash& destination); + Stream (StreamingDestination * local, const i2p::data::LeaseSet * remote); uint32_t GetSendStreamID () const { return m_SendStreamID; }; uint32_t GetRecvStreamID () const { return m_RecvStreamID; }; - + const i2p::data::LeaseSet * GetRemoteLeaseSet () const { return m_RemoteLeaseSet; }; + bool IsEstablished () const { return !m_SendStreamID; }; + void HandleNextPacket (const uint8_t * buf, size_t len); + size_t Send (uint8_t * buf, size_t len, int timeout); // timeout in seconds private: - uint32_t m_SendStreamID, m_RecvStreamID; + uint32_t m_SendStreamID, m_RecvStreamID, m_SequenceNumber; + StreamingDestination * m_LocalDestination; + const i2p::data::LeaseSet * m_RemoteLeaseSet; }; class StreamingDestination { public: - Stream * CreateNewStream (const i2p::data::IdentHash& destination); + StreamingDestination (); + ~StreamingDestination (); + + const i2p::data::Keys& GetKeys () const { return m_Keys; }; + const i2p::data::Identity& GetIdentity () const { return m_Identity; }; + I2NPMessage * GetLeaseSet (); + void Sign (uint8_t * buf, int len, uint8_t * signature) const; + + Stream * CreateNewStream (const i2p::data::LeaseSet * remote); + void DeleteStream (Stream * stream); void HandleNextPacket (const uint8_t * buf, size_t len); + private: + + I2NPMessage * CreateLeaseSet () const; + private: std::map m_Streams; + i2p::data::Keys m_Keys; + i2p::data::Identity m_Identity; + i2p::data::IdentHash m_IdentHash; + + I2NPMessage * m_LeaseSet; + + CryptoPP::DSA::PrivateKey m_SigningPrivateKey; }; + + Stream * CreateStream (const i2p::data::LeaseSet * remote); + void CloseStream (Stream * stream); // assuming data is I2CP message void HandleDataMessage (i2p::data::IdentHash * destination, const uint8_t * buf, size_t len); diff --git a/TransitTunnel.cpp b/TransitTunnel.cpp index 3bb3295a..6c9a4f6f 100644 --- a/TransitTunnel.cpp +++ b/TransitTunnel.cpp @@ -14,7 +14,8 @@ namespace tunnel TransitTunnel::TransitTunnel (uint32_t receiveTunnelID, const uint8_t * nextIdent, uint32_t nextTunnelID, const uint8_t * layerKey,const uint8_t * ivKey): - m_TunnelID (receiveTunnelID), m_NextTunnelID (nextTunnelID), m_NextIdent (nextIdent) + m_TunnelID (receiveTunnelID), m_NextTunnelID (nextTunnelID), + m_NextIdent (nextIdent), m_NumTransmittedBytes (0) { memcpy (m_LayerKey, layerKey, 32); memcpy (m_IVKey, ivKey, 32); @@ -43,6 +44,7 @@ namespace tunnel FillI2NPMessageHeader (tunnelMsg, eI2NPTunnelData); i2p::transports.SendMessage (m_NextIdent, tunnelMsg); + m_NumTransmittedBytes += tunnelMsg->GetLength (); } void TransitTunnel::SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, i2p::I2NPMessage * msg) diff --git a/TransitTunnel.h b/TransitTunnel.h index 2109ee31..570429f5 100644 --- a/TransitTunnel.h +++ b/TransitTunnel.h @@ -23,6 +23,7 @@ namespace tunnel virtual void HandleTunnelDataMsg (i2p::I2NPMessage * tunnelMsg); virtual void SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, i2p::I2NPMessage * msg); + virtual size_t GetNumTransmittedBytes () const { return m_NumTransmittedBytes; }; uint32_t GetTunnelID () const { return m_TunnelID; }; @@ -37,6 +38,7 @@ namespace tunnel i2p::data::IdentHash m_NextIdent; uint8_t m_LayerKey[32]; uint8_t m_IVKey[32]; + size_t m_NumTransmittedBytes; CryptoPP::ECB_Mode::Encryption m_ECBEncryption; CryptoPP::CBC_Mode::Encryption m_CBCEncryption; @@ -53,6 +55,7 @@ namespace tunnel layerKey, ivKey), m_Gateway(this) {}; void SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, i2p::I2NPMessage * msg); + size_t GetNumTransmittedBytes () const { return m_Gateway.GetNumSentBytes (); }; private: @@ -69,6 +72,7 @@ namespace tunnel TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey) {}; void HandleTunnelDataMsg (i2p::I2NPMessage * tunnelMsg); + size_t GetNumTransmittedBytes () const { return m_Endpoint.GetNumReceivedBytes (); } private: diff --git a/Transports.cpp b/Transports.cpp index e938c85b..e9953eca 100644 --- a/Transports.cpp +++ b/Transports.cpp @@ -23,6 +23,7 @@ namespace i2p void Transports::Start () { + m_IsRunning = true; m_Thread = new std::thread (std::bind (&Transports::Run, this)); // create acceptors auto addresses = context.GetRouterInfo ().GetAddresses (); @@ -48,6 +49,7 @@ namespace i2p m_NTCPSessions.clear (); delete m_NTCPAcceptor; + m_IsRunning = false; m_Service.stop (); if (m_Thread) { @@ -59,13 +61,16 @@ namespace i2p void Transports::Run () { - try - { - m_Service.run (); - } - catch (std::exception& ex) + while (m_IsRunning) { - LogPrint ("Transports: ", ex.what ()); + try + { + m_Service.run (); + } + catch (std::exception& ex) + { + LogPrint ("Transports: ", ex.what ()); + } } } @@ -118,27 +123,30 @@ namespace i2p { if (ident == i2p::context.GetRouterInfo ().GetIdentHash ()) // we send it to ourself - i2p::HandleI2NPMessage (msg); + i2p::HandleI2NPMessage (msg, false); else - { - auto session = FindNTCPSession (ident); - if (!session) - { - RouterInfo * r = netdb.FindRouter (ident); - if (r) + m_Service.post (boost::bind (&Transports::PostMessage, this, ident, msg)); + } + + void Transports::PostMessage (const i2p::data::IdentHash& ident, i2p::I2NPMessage * msg) + { + auto session = FindNTCPSession (ident); + if (!session) + { + RouterInfo * r = netdb.FindRouter (ident); + if (r) + { + auto address = r->GetNTCPAddress (); + if (address) { - auto address = r->GetNTCPAddress (); - if (address) - { - session = new i2p::ntcp::NTCPClient (m_Service, address->host.c_str (), address->port, *r); - AddNTCPSession (session); - } - } - } - if (session) - session->SendI2NPMessage (msg); - else - LogPrint ("Session not found"); + session = new i2p::ntcp::NTCPClient (m_Service, address->host.c_str (), address->port, *r); + AddNTCPSession (session); + } + } } + if (session) + session->SendI2NPMessage (msg); + else + LogPrint ("Session not found"); } } diff --git a/Transports.h b/Transports.h index ea5453cc..d6b08227 100644 --- a/Transports.h +++ b/Transports.h @@ -36,9 +36,11 @@ namespace i2p void Run (); void HandleAccept (i2p::ntcp::NTCPServerConnection * conn, const boost::system::error_code& error); + void PostMessage (const i2p::data::IdentHash& ident, i2p::I2NPMessage * msg); private: + bool m_IsRunning; std::thread * m_Thread; boost::asio::io_service m_Service; boost::asio::io_service::work m_Work; diff --git a/Tunnel.cpp b/Tunnel.cpp index 26b4dab3..4e43e70d 100644 --- a/Tunnel.cpp +++ b/Tunnel.cpp @@ -14,8 +14,7 @@ namespace i2p namespace tunnel { - Tunnel::Tunnel (TunnelConfig * config): m_Config (config), - m_CreationTime (i2p::util::GetSecondsSinceEpoch ()), m_IsEstablished (false) + Tunnel::Tunnel (TunnelConfig * config): m_Config (config), m_IsEstablished (false) { } @@ -67,14 +66,9 @@ namespace tunnel FillI2NPMessageHeader (msg, eI2NPVariableTunnelBuild); if (outboundTunnel) - { - outboundTunnel->SendTunnelDataMsg (GetNextIdentHash (), 0, msg); - DeleteI2NPMessage (msg); - } + outboundTunnel->SendTunnelDataMsg (GetNextIdentHash (), 0, msg); else - { i2p::transports.SendMessage (GetNextIdentHash (), msg); - } } bool Tunnel::HandleTunnelBuildResponse (uint8_t * msg, size_t len) @@ -220,7 +214,17 @@ namespace tunnel OutboundTunnel * Tunnels::GetNextOutboundTunnel () { - OutboundTunnel * tunnel = nullptr; + CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); + uint32_t ind = rnd.GenerateWord32 (0, m_OutboundTunnels.size () - 1), i = 0; + for (auto it: m_OutboundTunnels) + { + if (i >= ind) return it; + else i++; + } + return nullptr; + + // TODO: implement it base on profiling + /*OutboundTunnel * tunnel = nullptr; size_t minSent = 0; for (auto it : m_OutboundTunnels) if (!tunnel || it->GetNumSentBytes () < minSent) @@ -228,7 +232,7 @@ namespace tunnel tunnel = it; minSent = it->GetNumSentBytes (); } - return tunnel; + return tunnel;*/ } void Tunnels::AddTransitTunnel (TransitTunnel * tunnel) @@ -311,6 +315,7 @@ namespace tunnel ManageInboundTunnels (); ManageOutboundTunnels (); + ManageTransitTunnels (); /* if (!m_IsTunnelCreated) { @@ -349,18 +354,24 @@ namespace tunnel { LogPrint ("Creating one hop outbound tunnel..."); CreateTunnel ( - new TunnelConfig (i2p::data::netdb.GetRandomNTCPRouter (), + new TunnelConfig (std::vector + { + i2p::data::netdb.GetRandomNTCPRouter () + }, inboundTunnel->GetTunnelConfig ())); } else { - OutboundTunnel * outboundTunnel = GetNextOutboundTunnel (); + //OutboundTunnel * outboundTunnel = GetNextOutboundTunnel (); LogPrint ("Creating two hops outbound tunnel..."); CreateTunnel ( - new TunnelConfig (i2p::data::netdb.GetRandomNTCPRouter (), - i2p::data::netdb.GetRandomNTCPRouter (), - inboundTunnel->GetTunnelConfig ()), - outboundTunnel); + new TunnelConfig (std::vector + { + i2p::data::netdb.GetRandomNTCPRouter (), + i2p::data::netdb.GetRandomNTCPRouter () + }, + inboundTunnel->GetTunnelConfig ())/*, + outboundTunnel*/); } } } @@ -392,19 +403,42 @@ namespace tunnel if (m_OutboundTunnels.empty () || m_InboundTunnels.size () < 3) { LogPrint ("Creating one hop inbound tunnel..."); - CreateTunnel (new TunnelConfig (i2p::data::netdb.GetRandomNTCPRouter ())); + CreateTunnel ( + new TunnelConfig (std::vector + { + i2p::data::netdb.GetRandomNTCPRouter () + })); } else { - OutboundTunnel * outboundTunnel = GetNextOutboundTunnel (); + OutboundTunnel * outboundTunnel = GetNextOutboundTunnel (); LogPrint ("Creating two hops inbound tunnel..."); + auto router = outboundTunnel->GetTunnelConfig ()->GetFirstHop ()->router; CreateTunnel ( - new TunnelConfig (i2p::data::netdb.GetRandomNTCPRouter (), - outboundTunnel->GetTunnelConfig ()->GetFirstHop ()->router), - outboundTunnel); + new TunnelConfig (std::vector + { + i2p::data::netdb.GetRandomNTCPRouter (), + router != &i2p::context.GetRouterInfo () ? router : i2p::data::netdb.GetRandomNTCPRouter () + }), + outboundTunnel); } } } + + void Tunnels::ManageTransitTunnels () + { + uint32_t ts = i2p::util::GetSecondsSinceEpoch (); + for (auto it = m_TransitTunnels.begin (); it != m_TransitTunnels.end ();) + { + if (ts > it->second->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT) + { + LogPrint ("Transit tunnel ", it->second->GetTunnelID (), " expired"); + it = m_TransitTunnels.erase (it); + } + else + it++; + } + } void Tunnels::PostTunnelData (I2NPMessage * msg) { @@ -437,7 +471,10 @@ namespace tunnel void Tunnels::CreateZeroHopsInboundTunnel () { CreateTunnel ( - new TunnelConfig (&i2p::context.GetRouterInfo ())); + new TunnelConfig (std::vector + { + &i2p::context.GetRouterInfo () + })); } OutboundTunnel * Tunnels::CreateOneHopOutboundTestTunnel (InboundTunnel * replyTunnel) @@ -451,7 +488,9 @@ namespace tunnel if (peer) { const i2p::data::RouterInfo& router = peer->GetRemoteRouterInfo (); - return CreateTunnel (new TunnelConfig (&router), outboundTunnel); + return CreateTunnel ( + new TunnelConfig (std::vector{&router}), + outboundTunnel); } else LogPrint ("No established peers"); @@ -470,7 +509,11 @@ namespace tunnel { const i2p::data::RouterInfo& router = peer->GetRemoteRouterInfo (); return CreateTunnel ( - new TunnelConfig (&router, &i2p::context.GetRouterInfo ()), + new TunnelConfig (std::vector + { + &router, + &i2p::context.GetRouterInfo () + }), outboundTunnel); } else diff --git a/Tunnel.h b/Tunnel.h index 9ecf6ff3..cec82958 100644 --- a/Tunnel.h +++ b/Tunnel.h @@ -33,9 +33,7 @@ namespace tunnel void Build (uint32_t replyMsgID, OutboundTunnel * outboundTunnel = 0); - virtual uint32_t GetTunnelID () const = 0; // as known at our side TunnelConfig * GetTunnelConfig () const { return m_Config; } - uint32_t GetCreationTime () const { return m_CreationTime; }; bool IsEstablished () const { return m_IsEstablished; }; bool HandleTunnelBuildResponse (uint8_t * msg, size_t len); @@ -54,7 +52,6 @@ namespace tunnel private: TunnelConfig * m_Config; - uint32_t m_CreationTime; // seconds since epoch bool m_IsEstablished; CryptoPP::ECB_Mode::Decryption m_ECBDecryption; @@ -70,9 +67,11 @@ namespace tunnel void SendTunnelDataMsg (i2p::I2NPMessage * msg); //local void SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, i2p::I2NPMessage * msg); - uint32_t GetTunnelID () const { return GetNextTunnelID (); }; TunnelGateway& GetTunnelGateway () { return m_Gateway; }; size_t GetNumSentBytes () const { return m_Gateway.GetNumSentBytes (); }; + + // implements TunnelBase + uint32_t GetTunnelID () const { return GetNextTunnelID (); }; private: @@ -85,10 +84,10 @@ namespace tunnel InboundTunnel (TunnelConfig * config): Tunnel (config) {}; void HandleTunnelDataMsg (I2NPMessage * msg); + size_t GetNumReceivedBytes () const { return m_Endpoint.GetNumReceivedBytes (); }; + // implements TunnelBase uint32_t GetTunnelID () const { return GetTunnelConfig ()->GetLastHop ()->nextTunnelID; }; - size_t GetNumReceivedBytes () const { return m_Endpoint.GetNumReceivedBytes (); }; - private: TunnelEndpoint m_Endpoint; @@ -128,6 +127,7 @@ namespace tunnel void ManageTunnels (); void ManageOutboundTunnels (); void ManageInboundTunnels (); + void ManageTransitTunnels (); void CreateZeroHopsInboundTunnel (); diff --git a/TunnelBase.h b/TunnelBase.h index 829270ce..bd760989 100644 --- a/TunnelBase.h +++ b/TunnelBase.h @@ -2,6 +2,7 @@ #define TUNNEL_BASE_H__ #include +#include "Timestamp.h" #include "I2NPProtocol.h" namespace i2p @@ -30,11 +31,20 @@ namespace tunnel { public: + TunnelBase (): m_CreationTime (i2p::util::GetSecondsSinceEpoch ()) {}; virtual ~TunnelBase () {}; virtual void EncryptTunnelMsg (I2NPMessage * tunnelMsg) = 0; virtual uint32_t GetNextTunnelID () const = 0; virtual const i2p::data::IdentHash& GetNextIdentHash () const = 0; + virtual uint32_t GetTunnelID () const = 0; // as known at our side + + uint32_t GetCreationTime () const { return m_CreationTime; }; + void SetCreationTime (uint32_t t) { m_CreationTime = t; }; + + private: + + uint32_t m_CreationTime; // seconds since epoch }; } } diff --git a/TunnelConfig.h b/TunnelConfig.h index 653fe56d..29fb8575 100644 --- a/TunnelConfig.h +++ b/TunnelConfig.h @@ -3,6 +3,7 @@ #include #include +#include #include "RouterInfo.h" #include "RouterContext.h" @@ -83,34 +84,30 @@ namespace tunnel { public: - TunnelConfig (const i2p::data::RouterInfo * peer, TunnelConfig * replyTunnelConfig = 0) // one hop - { - m_FirstHop = new TunnelHopConfig (peer); - m_LastHop = m_FirstHop; - - if (replyTunnelConfig) // outbound - { - m_FirstHop->isGateway = false; - m_LastHop->SetReplyHop (replyTunnelConfig->GetFirstHop ()); - } - else - m_FirstHop->SetNextRouter (&i2p::context.GetRouterInfo ()); - } - TunnelConfig (const i2p::data::RouterInfo * peer1, const i2p::data::RouterInfo * peer2, TunnelConfig * replyTunnelConfig = 0) // two hops + TunnelConfig (std::vector peers, + TunnelConfig * replyTunnelConfig = 0) // replyTunnelConfig=0 means inbound { - m_FirstHop = new TunnelHopConfig (peer1); - m_LastHop = new TunnelHopConfig (peer2); - m_FirstHop->SetNext (m_LastHop); + TunnelHopConfig * prev = nullptr; + for (auto it: peers) + { + auto hop = new TunnelHopConfig (it); + if (prev) + prev->SetNext (hop); + else + m_FirstHop = hop; + prev = hop; + } + m_LastHop = prev; - if (replyTunnelConfig) + if (replyTunnelConfig) // outbound { m_FirstHop->isGateway = false; m_LastHop->SetReplyHop (replyTunnelConfig->GetFirstHop ()); } - else + else // inbound m_LastHop->SetNextRouter (&i2p::context.GetRouterInfo ()); - } + } ~TunnelConfig () { diff --git a/TunnelEndpoint.cpp b/TunnelEndpoint.cpp index a53be126..c5ec73b4 100644 --- a/TunnelEndpoint.cpp +++ b/TunnelEndpoint.cpp @@ -147,7 +147,7 @@ namespace tunnel switch (msg.deliveryType) { case eDeliveryTypeLocal: - i2p::HandleI2NPMessage (msg.data); + i2p::HandleI2NPMessage (msg.data, true); break; case eDeliveryTypeTunnel: i2p::transports.SendMessage (msg.hash, i2p::CreateTunnelGatewayMsg (msg.tunnelID, msg.data)); diff --git a/TunnelGateway.cpp b/TunnelGateway.cpp index 7bff2cba..f19084b7 100644 --- a/TunnelGateway.cpp +++ b/TunnelGateway.cpp @@ -12,198 +12,144 @@ namespace tunnel { void TunnelGatewayBuffer::PutI2NPMsg (const uint8_t * gwHash, uint32_t gwTunnel, I2NPMessage * msg) { - TunnelMessageBlockExt * block = new TunnelMessageBlockExt; - block->deliveryInstructionsLen = 1; // flag + if (!m_CurrentTunnelDataMsg) + CreateCurrentTunnelDataMessage (); + + // create delivery instructions + uint8_t di[43]; // max delivery instruction length is 43 for tunnel + size_t diLen = 1;// flag + TunnelDeliveryType dt = eDeliveryTypeLocal; if (gwHash) { - block->deliveryInstructionsLen += 32; // hash - memcpy (block->hash, gwHash, 32); if (gwTunnel) - { - block->deliveryType = eDeliveryTypeTunnel; - block->deliveryInstructionsLen += 4; // tunnelID - block->tunnelID = gwTunnel; - } + { + *(uint32_t *)(di + diLen) = htobe32 (gwTunnel); + diLen += 4; // tunnelID + dt = eDeliveryTypeTunnel; + } else - block->deliveryType = eDeliveryTypeRouter; + dt = eDeliveryTypeRouter; + + memcpy (di + diLen, gwHash, 32); + diLen += 32; //len } - else - block->deliveryType = eDeliveryTypeLocal; - block->deliveryInstructionsLen += 2; // size - // we don't reserve 4 bytes for msgID yet - block->totalLen = block->deliveryInstructionsLen + msg->GetLength (); - block->data = msg; - m_I2NPMsgs.push_back (block); + di[0] = dt << 5; // set delivery type - if (!m_Remaining) m_Remaining = TUNNEL_DATA_MAX_PAYLOAD_SIZE; - if (block->totalLen <= m_Remaining) // message fits + // create fragments + if (diLen + msg->GetLength () + 2<= m_RemainingSize) { - block->isFragmented = false; - m_Remaining -= block->totalLen; + // message fits. First and last fragment + *(uint16_t *)(di + diLen) = htobe16 (msg->GetLength ()); + diLen += 2; // size + memcpy (m_CurrentTunnelDataMsg->buf + m_CurrentTunnelDataMsg->len, di, diLen); + memcpy (m_CurrentTunnelDataMsg->buf + m_CurrentTunnelDataMsg->len + diLen, msg->GetBuffer (), msg->GetLength ()); + m_CurrentTunnelDataMsg->len += diLen + msg->GetLength (); + m_RemainingSize -= diLen + msg->GetLength (); + if (!m_RemainingSize) + CompleteCurrentTunnelDataMessage (); + DeleteI2NPMessage (msg); } - else // message doesn't fit + else { - if (block->deliveryInstructionsLen + 4 <= m_Remaining) + if (diLen + 6 <= m_RemainingSize) { - // delivery instructions of first fragment fits - block->isFragmented = true; - block->deliveryInstructionsLen += 4; - block->totalLen += 4; - m_Remaining = m_Remaining + TUNNEL_DATA_MAX_PAYLOAD_SIZE - block->totalLen - 7; // TODO: handle case if more than two fragments + // delivery instructions fit + uint32_t msgID = msg->GetHeader ()->msgID; + size_t size = m_RemainingSize - diLen - 6; // 6 = 4 (msgID) + 2 (size) + + // first fragment + di[0] |= 0x08; // fragmented + *(uint32_t *)(di + diLen) = htobe32 (msgID); + diLen += 4; // Message ID + *(uint16_t *)(di + diLen) = htobe16 (size); + diLen += 2; // size + memcpy (m_CurrentTunnelDataMsg->buf + m_CurrentTunnelDataMsg->len, di, diLen); + memcpy (m_CurrentTunnelDataMsg->buf + m_CurrentTunnelDataMsg->len + diLen, msg->GetBuffer (), size); + m_CurrentTunnelDataMsg->len += diLen + size; + CompleteCurrentTunnelDataMessage (); + // follow on fragments + int fragmentNumber = 1; + while (size < msg->GetLength ()) + { + CreateCurrentTunnelDataMessage (); + uint8_t * buf = m_CurrentTunnelDataMsg->GetBuffer (); + buf[0] = 0x80 | (fragmentNumber << 1); // frag + bool isLastFragment = false; + size_t s = msg->GetLength () - size; + if (s > TUNNEL_DATA_MAX_PAYLOAD_SIZE - 7) // 7 follow on instructions + s = TUNNEL_DATA_MAX_PAYLOAD_SIZE - 7; + else // last fragment + { + buf[0] |= 0x01; + isLastFragment = true; + } + *(uint32_t *)(buf + 1) = htobe32 (msgID); //Message ID + *(uint16_t *)(buf + 5) = htobe16 (s); // size + memcpy (buf + 7, msg->GetBuffer () + size, s); + m_CurrentTunnelDataMsg->len += s+7; + if (isLastFragment) + { + m_RemainingSize -= s+7; + if (!m_RemainingSize) + CompleteCurrentTunnelDataMessage (); + } + else + CompleteCurrentTunnelDataMessage (); + size += s; + fragmentNumber++; + } + DeleteI2NPMessage (msg); } else { - // delivery instructions of first fragment don't fit - block->isFragmented = false; - m_Remaining = 0; + // delivery instructions don't fit. Create new message + CompleteCurrentTunnelDataMessage (); + PutI2NPMsg (gwHash, gwTunnel, msg); + // don't delete msg because it's taken care inside } - } - } - - std::vector TunnelGatewayBuffer::GetTunnelDataMsgs () - { - m_Remaining = 0; - m_NextOffset = 0; - std::vector res; - int cnt = m_I2NPMsgs.size (); - if (cnt > 0) - { - int ind = 0; - while (ind < cnt) - { - auto tunnelMsg = CreateNextTunnelMessage (ind); - if (!tunnelMsg) break; - res.push_back (tunnelMsg); - } - for (auto msg: m_I2NPMsgs) - delete msg; - m_I2NPMsgs.clear (); - } - - return res; + } } - - size_t TunnelGatewayBuffer::CreateFirstFragment (TunnelMessageBlockExt * block, uint8_t * buf, size_t len) + + std::vector TunnelGatewayBuffer::GetTunnelDataMsgs () { - size_t ret = 1; - buf[0] = block->deliveryType << 5; // flag - if (block->deliveryType == eDeliveryTypeTunnel) - { - *(uint32_t *)(buf + ret) = htobe32 (block->tunnelID); - ret += 4; - } - if (block->deliveryType == eDeliveryTypeTunnel || block->deliveryType == eDeliveryTypeRouter) - { - memcpy (buf + ret, block->hash, 32); - ret += 32; - } - size_t size = block->data->GetLength (); - if (block->totalLen > len) // entire message doesn't fit - { - buf[0] |= 0x08; // set fragmented bit - m_NextMsgID = block->data->GetHeader ()->msgID; - *(uint32_t *)(buf + ret) = m_NextMsgID; - ret += 4; // msgID - m_NextSeqn = 1; - size = len - ret - 2; // 2 bytes for size field - m_NextOffset = size; - } - *(uint16_t *)(buf + ret) = htobe16 (size); // size - ret += 2; - memcpy (buf + ret, block->data->GetBuffer (), size); - ret += size; - return ret; + CompleteCurrentTunnelDataMessage (); + std::vector ret = m_TunnelDataMsgs; // TODO: implement it better + m_TunnelDataMsgs.clear (); + return ret; } - size_t TunnelGatewayBuffer::CreateFollowOnFragment (TunnelMessageBlockExt * block, uint8_t * buf, size_t len) + void TunnelGatewayBuffer::CreateCurrentTunnelDataMessage () { - int ret = 0; - buf[0] = 0x80 | (m_NextSeqn << 1);// follow-on flag and seqn - size_t fragmentLen = len - 7; // 7 bytes of header - if (fragmentLen >= block->data->GetLength () - m_NextOffset) - { - // fragment fits - fragmentLen = block->data->GetLength () - m_NextOffset; - buf[0] |= 0x01; // last fragment - } - else - m_NextSeqn++; - - *(uint32_t *)(buf + 1) = m_NextMsgID; // msgID - *(uint16_t *)(buf + 5) = htobe16 (fragmentLen); // size - memcpy (buf + 7, block->data->GetBuffer () + m_NextOffset, fragmentLen); - - m_NextOffset += fragmentLen; - ret += fragmentLen + 7; - - return ret; + m_CurrentTunnelDataMsg = NewI2NPMessage (); + // we reserve space for padding + m_CurrentTunnelDataMsg->offset += TUNNEL_DATA_MSG_SIZE + sizeof (I2NPHeader); + m_CurrentTunnelDataMsg->len = m_CurrentTunnelDataMsg->offset; + m_RemainingSize = TUNNEL_DATA_MAX_PAYLOAD_SIZE; } - - I2NPMessage * TunnelGatewayBuffer::CreateNextTunnelMessage (int& ind) + + void TunnelGatewayBuffer::CompleteCurrentTunnelDataMessage () { - int cnt = m_I2NPMsgs.size (); - if (ind > cnt - 1) return nullptr; // no more messages - // calculate payload size - size_t size = 0; - int i = ind; - if (m_NextOffset) - { - size = m_I2NPMsgs[i]->data->GetLength () - m_NextOffset + 7; // including follow-on header - i++; - } - while (i < cnt) - { - auto msg = m_I2NPMsgs[i]; - size += msg->totalLen; - if (size >= TUNNEL_DATA_MAX_PAYLOAD_SIZE) - { - size = TUNNEL_DATA_MAX_PAYLOAD_SIZE; - break; - } - if (msg->isFragmented) break; - i++; - } + if (!m_CurrentTunnelDataMsg) return; + uint8_t * payload = m_CurrentTunnelDataMsg->GetBuffer (); + size_t size = m_CurrentTunnelDataMsg->len - m_CurrentTunnelDataMsg->offset; - I2NPMessage * tunnelMsg = NewI2NPMessage (); - uint8_t * buf = tunnelMsg->GetPayload (); + m_CurrentTunnelDataMsg->offset = m_CurrentTunnelDataMsg->len - TUNNEL_DATA_MSG_SIZE - sizeof (I2NPHeader); + uint8_t * buf = m_CurrentTunnelDataMsg->GetPayload (); *(uint32_t *)(buf) = htobe32 (m_TunnelID); CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); rnd.GenerateBlock (buf + 4, 16); // original IV - memcpy (buf + TUNNEL_DATA_MSG_SIZE, buf + 4, 16); // copy IV for checksum - size_t zero = TUNNEL_DATA_MSG_SIZE - size -1; - buf[zero] = 0; // zero - size_t s = 0; - while (ind < cnt) - { - auto msg = m_I2NPMsgs[ind]; - if (m_NextOffset) - { - s += CreateFollowOnFragment (msg, buf + zero + 1 + s, size - s); - m_NextOffset = 0; // TODO: - } - else - { - s += CreateFirstFragment (msg, buf + zero + 1 + s, size - s); - if (msg->isFragmented) break; // payload is full, but we stay at the same message - } - ind++; - if (s >= size) break; // payload is full but we moved to next message - } - - if (s != size) - { - LogPrint ("TunnelData payload size mismatch ", s, "!=", size); - return nullptr; - } - + memcpy (payload + size, buf + 4, 16); // copy IV for checksum uint8_t hash[32]; - CryptoPP::SHA256().CalculateDigest(hash, buf+zero+1, size+16); - memcpy (buf+20, hash, 4); // checksum - if (zero > 24) - memset (buf+24, 1, zero-24); // padding TODO: fill with random data - tunnelMsg->len += TUNNEL_DATA_MSG_SIZE; + CryptoPP::SHA256().CalculateDigest (hash, payload, size+16); + memcpy (buf+20, hash, 4); // checksum + payload[-1] = 0; // zero + ssize_t paddingSize = payload - buf - 25; // 25 = 24 + 1 + if (paddingSize > 0) + memset (buf + 24, 1, paddingSize); // padding TODO: fill with random data + // we can't fill message header yet because encryption is required - return tunnelMsg; + m_TunnelDataMsgs.push_back (m_CurrentTunnelDataMsg); + m_CurrentTunnelDataMsg = nullptr; } void TunnelGateway::SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, i2p::I2NPMessage * msg) diff --git a/TunnelGateway.h b/TunnelGateway.h index 9c679888..d68c8580 100644 --- a/TunnelGateway.h +++ b/TunnelGateway.h @@ -12,32 +12,23 @@ namespace tunnel { class TunnelGatewayBuffer { - struct TunnelMessageBlockExt: public TunnelMessageBlock - { - size_t deliveryInstructionsLen, totalLen; - bool isFragmented; - }; - public: - - TunnelGatewayBuffer (uint32_t tunnelID): m_TunnelID (tunnelID), m_Remaining (0) {}; - + TunnelGatewayBuffer (uint32_t tunnelID): m_TunnelID (tunnelID), + m_CurrentTunnelDataMsg (nullptr), m_RemainingSize (0) {}; void PutI2NPMsg (const uint8_t * gwHash, uint32_t gwTunnel, I2NPMessage * msg); std::vector GetTunnelDataMsgs (); private: - size_t CreateFirstFragment (TunnelMessageBlockExt * block, uint8_t * buf, size_t len); - size_t CreateFollowOnFragment (TunnelMessageBlockExt * block, uint8_t * buf, size_t len); - I2NPMessage * CreateNextTunnelMessage (int& ind); + void CreateCurrentTunnelDataMessage (); + void CompleteCurrentTunnelDataMessage (); private: uint32_t m_TunnelID; - std::vector m_I2NPMsgs; - // for fragmented messages - size_t m_NextOffset, m_NextSeqn, m_Remaining; - uint32_t m_NextMsgID; + std::vector m_TunnelDataMsgs; + I2NPMessage * m_CurrentTunnelDataMsg; + size_t m_RemainingSize; }; class TunnelGateway