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..32639142 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); @@ -265,8 +262,14 @@ namespace garlic return ret; } - void GarlicRouting::HandleGarlicMessage (uint8_t * buf, size_t len, bool isFromTunnel) + void GarlicRouting::HandleGarlicMessage (I2NPMessage * msg) { + if (msg) m_Queue.Put (msg); + } + + void GarlicRouting::ProcessGarlicMessage (I2NPMessage * msg) + { + uint8_t * buf = msg->GetPayload (); uint32_t length = be32toh (*(uint32_t *)buf); buf += 4; std::string sessionTag((const char *)buf, 32); @@ -287,7 +290,7 @@ namespace garlic // new session ElGamalBlock elGamal; if (i2p::crypto::ElGamalDecrypt ( - isFromTunnel ? i2p::context.GetLeaseSetPrivateKey () : i2p::context.GetPrivateKey (), + msg->from ? i2p::context.GetLeaseSetPrivateKey () : i2p::context.GetPrivateKey (), buf, (uint8_t *)&elGamal, true)) { uint8_t iv[32]; // IV is first 16 bytes @@ -299,7 +302,7 @@ namespace garlic else LogPrint ("Failed to decrypt garlic"); } - + DeleteI2NPMessage (msg); } void GarlicRouting::HandleAESBlock (uint8_t * buf, size_t len, uint8_t * sessionKey) @@ -354,7 +357,7 @@ namespace garlic { case eGarlicDeliveryTypeLocal: LogPrint ("Garlic type local"); - i2p::HandleI2NPMessage (buf, len, false); + i2p::HandleI2NPMessage (buf, len); break; case eGarlicDeliveryTypeDestination: { @@ -413,5 +416,40 @@ namespace garlic LogPrint ("Garlic message ", be32toh (msg->msgID), " acknowledged"); } } + + void GarlicRouting::Start () + { + m_IsRunning = true; + m_Thread = new std::thread (std::bind (&GarlicRouting::Run, this)); + } + + void GarlicRouting::Stop () + { + m_IsRunning = false; + m_Queue.WakeUp (); + if (m_Thread) + { + m_Thread->join (); + delete m_Thread; + m_Thread = 0; + } + } + + void GarlicRouting::Run () + { + while (m_IsRunning) + { + try + { + I2NPMessage * msg = m_Queue.GetNext (); + if (msg) + ProcessGarlicMessage (msg); + } + catch (std::exception& ex) + { + LogPrint ("GarlicRouting: ", ex.what ()); + } + } + } } } diff --git a/Garlic.h b/Garlic.h index c640eee3..3fb8ba50 100644 --- a/Garlic.h +++ b/Garlic.h @@ -4,11 +4,14 @@ #include #include #include +#include #include #include #include #include "I2NPProtocol.h" #include "LeaseSet.h" +#include "Tunnel.h" +#include "Queue.h" namespace i2p { @@ -37,7 +40,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 +60,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; @@ -75,20 +78,28 @@ namespace garlic GarlicRouting (); ~GarlicRouting (); - void HandleGarlicMessage (uint8_t * buf, size_t len, bool isFromTunnel); + void Start (); + void Stop (); + + void HandleGarlicMessage (I2NPMessage * msg); 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: + void Run (); + void ProcessGarlicMessage (I2NPMessage * msg); void HandleAESBlock (uint8_t * buf, size_t len, uint8_t * sessionKey); void HandleGarlicPayload (uint8_t * buf, size_t len); private: + bool m_IsRunning; + std::thread * m_Thread; + i2p::util::Queue m_Queue; // outgoing sessions std::map m_Sessions; std::map m_CreatedSessions; // msgID -> session diff --git a/HTTPServer.cpp b/HTTPServer.cpp index 5aeac9ed..d71deb3c 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(), @@ -133,6 +144,7 @@ namespace util } s << "

Transports

"; + s << "NTCP
"; for (auto it: i2p::transports.GetNTCPSessions ()) { // RouterInfo of incoming connection doesn't have address @@ -146,17 +158,36 @@ namespace util s << "
"; } } + auto ssuServer = i2p::transports.GetSSUServer (); + if (ssuServer) + { + s << "
SSU
"; + for (auto it: ssuServer->GetSessions ()) + { + // incoming connections don't have remote router + bool outgoing = it.second->GetRemoteRouter (); + auto endpoint = it.second->GetRemoteEndpoint (); + if (outgoing) s << "-->"; + s << endpoint.address ().to_string () << ":" << endpoint.port (); + if (!outgoing) s << "-->"; + s << "
"; + } + } 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 +201,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 +224,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..8990da3b 100644 --- a/I2NPProtocol.cpp +++ b/I2NPProtocol.cpp @@ -22,6 +22,7 @@ namespace i2p I2NPMessage * msg = new I2NPMessage; msg->offset = 2; // reserve 2 bytes for NTCP header, should reserve more for SSU in future msg->len = sizeof (I2NPHeader) + 2; + msg->from = nullptr; return msg; } @@ -70,8 +71,16 @@ namespace i2p I2NPMessage * CreateDeliveryStatusMsg (uint32_t msgID) { I2NPDeliveryStatusMsg msg; - msg.msgID = htobe32 (msgID); - msg.timestamp = htobe64 (i2p::util::GetMillisecondsSinceEpoch ()); + if (msgID) + { + msg.msgID = htobe32 (msgID); + msg.timestamp = htobe64 (i2p::util::GetMillisecondsSinceEpoch ()); + } + else // for SSU establishment + { + msg.msgID = htobe32 (i2p::context.GetRandomNumberGenerator ().GenerateWord32 ()); + msg.timestamp = htobe64 (2); // netID = 2 + } return CreateI2NPMessage (eI2NPDeliveryStatus, (uint8_t *)&msg, sizeof (msg)); } @@ -212,7 +221,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 +401,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"); @@ -406,7 +415,7 @@ namespace i2p return be16toh (header->size) + sizeof (I2NPHeader); } - void HandleI2NPMessage (uint8_t * msg, size_t len, bool isFromTunnel) + void HandleI2NPMessage (uint8_t * msg, size_t len) { I2NPHeader * header = (I2NPHeader *)msg; uint32_t msgID = be32toh (header->msgID); @@ -415,12 +424,7 @@ namespace i2p uint8_t * buf = msg + sizeof (I2NPHeader); int size = be16toh (header->size); switch (header->typeID) - { - case eI2NPGarlic: - LogPrint ("Garlic"); - i2p::garlic::routing.HandleGarlicMessage (buf, size, isFromTunnel); - break; - break; + { case eI2NPDeliveryStatus: LogPrint ("DeliveryStatus"); // we assume DeliveryStatusMessage is sent with garlic only @@ -443,7 +447,7 @@ namespace i2p } } - void HandleI2NPMessage (I2NPMessage * msg, bool isFromTunnel) + void HandleI2NPMessage (I2NPMessage * msg) { if (msg) { @@ -465,8 +469,12 @@ namespace i2p LogPrint ("DatabaseSearchReply"); i2p::data::netdb.PostI2NPMsg (msg); break; + case eI2NPGarlic: + LogPrint ("Garlic"); + i2p::garlic::routing.HandleGarlicMessage (msg); + break; default: - HandleI2NPMessage (msg->GetBuffer (), msg->GetLength (), isFromTunnel); + HandleI2NPMessage (msg->GetBuffer (), msg->GetLength ()); DeleteI2NPMessage (msg); } } diff --git a/I2NPProtocol.h b/I2NPProtocol.h index 7f47601e..2030bdad 100644 --- a/I2NPProtocol.h +++ b/I2NPProtocol.h @@ -4,6 +4,7 @@ #include #include #include +#include "I2PEndian.h" #include "RouterInfo.h" namespace i2p @@ -19,6 +20,12 @@ namespace i2p uint8_t chks; }; + struct I2NPHeaderShort + { + uint8_t typeID; + uint32_t shortExpiration; + }; + struct I2NPDatabaseStoreMsg { uint8_t key[32]; @@ -84,11 +91,17 @@ namespace i2p eI2NPVariableTunnelBuildReply = 24 }; +namespace tunnel +{ + class InboundTunnel; +} + const int NTCP_MAX_MESSAGE_SIZE = 16384; struct I2NPMessage { uint8_t buf[NTCP_MAX_MESSAGE_SIZE]; size_t len, offset; + i2p::tunnel::InboundTunnel * from; I2NPHeader * GetHeader () { return (I2NPHeader *)(buf + offset); }; uint8_t * GetPayload () { return buf + offset + sizeof(I2NPHeader); }; @@ -99,8 +112,31 @@ namespace i2p { memcpy (buf + offset, other.buf + other.offset, other.GetLength ()); len = offset + other.GetLength (); + from = other.from; return *this; } + + // for SSU only + uint8_t * GetSSUHeader () { return buf + offset + sizeof(I2NPHeader) - sizeof(I2NPHeaderShort); }; + void FromSSU (uint32_t msgID) // we have received SSU message and convert it to regular + { + I2NPHeaderShort ssu = *(I2NPHeaderShort *)GetSSUHeader (); + I2NPHeader * header = GetHeader (); + header->typeID = ssu.typeID; + header->msgID = htobe32 (msgID); + header->expiration = htobe64 (be32toh (ssu.shortExpiration)*1000LL); + header->size = htobe16 (len - offset - sizeof (I2NPHeader)); + header->chks = 0; + } + uint32_t ToSSU () // return msgID + { + I2NPHeader header = *GetHeader (); + I2NPHeaderShort * ssu = (I2NPHeaderShort *)GetSSUHeader (); + ssu->typeID = header.typeID; + ssu->shortExpiration = htobe32 (be64toh (header.expiration)/1000LL); + len = offset + sizeof (I2NPHeaderShort) + be16toh (header.size); + return be32toh (header.msgID); + } }; I2NPMessage * NewI2NPMessage (); void DeleteI2NPMessage (I2NPMessage * msg); @@ -140,8 +176,8 @@ namespace i2p I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessage * msg); size_t GetI2NPMessageLength (uint8_t * msg); - void HandleI2NPMessage (uint8_t * msg, size_t len, bool isFromTunnel); - void HandleI2NPMessage (I2NPMessage * msg, bool isFromTunnel); + void HandleI2NPMessage (uint8_t * msg, size_t len); + void HandleI2NPMessage (I2NPMessage * msg); } #endif 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/Makefile b/Makefile index b5739c46..d9d9b4c9 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,8 @@ CFLAGS = -g -Wall -std=c++0x OBJECTS = obj/i2p.o obj/base64.o obj/NTCPSession.o obj/RouterInfo.o obj/Transports.o \ obj/RouterContext.o obj/NetDb.o obj/LeaseSet.o obj/Tunnel.o obj/TunnelEndpoint.o \ obj/TunnelGateway.o obj/TransitTunnel.o obj/I2NPProtocol.o obj/Log.o obj/Garlic.o \ - obj/HTTPServer.o obj/Streaming.o obj/Identity.o obj/SSU.o obj/util.o obj/Reseed.o + obj/HTTPServer.o obj/Streaming.o obj/Identity.o obj/SSU.o obj/util.o obj/Reseed.o \ + obj/UPnP.o INCFLAGS = LDFLAGS = -Wl,-rpath,/usr/local/lib -lcryptopp -lboost_system -lboost_filesystem -lboost_regex -lboost_program_options -lpthread LIBS = diff --git a/NTCPSession.cpp b/NTCPSession.cpp index e2686248..1a72fdbc 100644 --- a/NTCPSession.cpp +++ b/NTCPSession.cpp @@ -424,7 +424,7 @@ namespace ntcp if (m_NextMessageOffset >= m_NextMessage->len + 4) // +checksum { // we have a complete I2NP message - i2p::HandleI2NPMessage (m_NextMessage, false); + i2p::HandleI2NPMessage (m_NextMessage); m_NextMessage = nullptr; } } 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 01234ad7..1b115c63 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) { @@ -111,7 +111,7 @@ namespace data else // WTF? { LogPrint ("NetDb: unexpected message type ", msg->GetHeader ()->typeID); - i2p::HandleI2NPMessage (msg, false); + i2p::HandleI2NPMessage (msg); } msg = m_Queue.Get (); } @@ -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) @@ -448,10 +456,18 @@ namespace data { auto r = FindRouter (router); // do we have that floodfill router in our database? - if (r) + if (r) { + // we do if (!dest->IsExcluded (r->GetIdentHash ()) && dest->GetNumExcludedPeers () < 30) // TODO: fix TunnelGateway first { + // tell floodfill about us + msgs.push_back (i2p::tunnel::TunnelMessageBlock + { + i2p::tunnel::eDeliveryTypeRouter, + r->GetIdentHash (), 0, + CreateDatabaseStoreMsg () + }); // request destination auto msg = dest->CreateRequestMessage (r, dest->GetLastReplyTunnel ()); msgs.push_back (i2p::tunnel::TunnelMessageBlock @@ -478,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"); } @@ -506,16 +525,15 @@ namespace data auto inbound = i2p::tunnel::tunnels.GetNextInboundTunnel (); if (outbound && inbound) { - auto floodfill = GetRandomRouter (outbound->GetEndpointRouter (), true); + CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); + uint8_t randomHash[32]; + rnd.GenerateBlock (randomHash, 32); + RequestedDestination * dest = CreateRequestedDestination (IdentHash (randomHash), false, true); + dest->SetLastOutboundTunnel (outbound); + auto floodfill = GetClosestFloodfill (randomHash, dest->GetExcludedPeers ()); if (floodfill) - { + { LogPrint ("Exploring new routers ..."); - CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); - uint8_t randomHash[32]; - rnd.GenerateBlock (randomHash, 32); - RequestedDestination * dest = CreateRequestedDestination (IdentHash (randomHash), false, true); - dest->SetLastOutboundTunnel (outbound); - std::vector msgs; msgs.push_back (i2p::tunnel::TunnelMessageBlock { @@ -529,11 +547,24 @@ namespace data floodfill->GetIdentHash (), 0, dest->CreateRequestMessage (floodfill, inbound) // explore }); - outbound->SendTunnelDataMsg (msgs); + outbound->SendTunnelDataMsg (msgs); } + else + DeleteRequestedDestination (dest); } } + 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) { @@ -557,6 +588,15 @@ namespace data m_RequestedDestinations.erase (it); } } + + void NetDb::DeleteRequestedDestination (RequestedDestination * dest) + { + if (dest) + { + m_RequestedDestinations.erase (dest->GetDestination ()); + delete dest; + } + } const RouterInfo * NetDb::GetRandomNTCPRouter (bool floodfillOnly) const { @@ -626,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 bcf1f23c..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,17 +84,21 @@ 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, bool isLeaseSet, bool isExploratory = false); void DeleteRequestedDestination (const IdentHash& dest); + void DeleteRequestedDestination (RequestedDestination * dest); private: 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/Queue.h b/Queue.h index 449ff806..85114bc1 100644 --- a/Queue.h +++ b/Queue.h @@ -98,17 +98,18 @@ namespace util { public: - MsgQueue (): m_Thread (std::bind (&MsgQueue::Run, this)) {}; + MsgQueue (): m_Thread (std::bind (&MsgQueue::Run, this)) , running(1) {}; void Stop() { - m_Thread.detach(); + running = 0; + m_Thread.join(); } private: void Run () { Msg * msg = nullptr; - while ((msg = Queue::GetNext ()) != nullptr) + while ((msg = Queue::GetNext ()) != nullptr && running) { msg->Process (); delete msg; @@ -117,6 +118,7 @@ namespace util private: std::thread m_Thread; + volatile int running; }; } } diff --git a/RouterContext.cpp b/RouterContext.cpp index 68f6508a..af88c84a 100644 --- a/RouterContext.cpp +++ b/RouterContext.cpp @@ -25,25 +25,32 @@ namespace i2p m_Keys = i2p::data::CreateRandomKeys (); m_SigningPrivateKey.Initialize (i2p::crypto::dsap, i2p::crypto::dsaq, i2p::crypto::dsag, CryptoPP::Integer (m_Keys.signingPrivateKey, 20)); - + UpdateRouterInfo (); + } + + void RouterContext::UpdateRouterInfo () + { i2p::data::Identity ident; ident = m_Keys; - m_RouterInfo.SetRouterIdentity (ident); - m_RouterInfo.AddNTCPAddress ("127.0.0.1", 17007); // TODO: - m_RouterInfo.SetProperty ("caps", "LR"); - m_RouterInfo.SetProperty ("coreVersion", "0.9.8.1"); - m_RouterInfo.SetProperty ("netId", "2"); - m_RouterInfo.SetProperty ("router.version", "0.9.8.1"); - m_RouterInfo.SetProperty ("start_uptime", "90m"); - - m_RouterInfo.CreateBuffer (); - } + i2p::data::RouterInfo routerInfo; + routerInfo.SetRouterIdentity (ident); + routerInfo.AddSSUAddress ("127.0.0.1", 17007, routerInfo.GetIdentHash ()); + routerInfo.AddNTCPAddress ("127.0.0.1", 17007); // TODO: + routerInfo.SetProperty ("caps", "LR"); + routerInfo.SetProperty ("coreVersion", "0.9.8.1"); + routerInfo.SetProperty ("netId", "2"); + routerInfo.SetProperty ("router.version", "0.9.8.1"); + routerInfo.SetProperty ("start_uptime", "90m"); + routerInfo.CreateBuffer (); + m_RouterInfo = routerInfo; + } + void RouterContext::OverrideNTCPAddress (const char * host, int port) { m_RouterInfo.CreateBuffer (); - auto address = m_RouterInfo.GetNTCPAddress (); + auto address = const_cast(m_RouterInfo.GetNTCPAddress ()); if (address) { address->host = boost::asio::ip::address::from_string (host); @@ -51,6 +58,14 @@ namespace i2p } m_RouterInfo.CreateBuffer (); + Save (true); + } + + void RouterContext::UpdateAddress (const char * host) + { + for (auto& address : m_RouterInfo.GetAddresses ()) + address.host = boost::asio::ip::address::from_string (host); + m_RouterInfo.CreateBuffer (); } void RouterContext::Sign (uint8_t * buf, int len, uint8_t * signature) @@ -61,24 +76,49 @@ namespace i2p bool RouterContext::Load () { - std::ifstream fk (ROUTER_KEYS, std::ifstream::binary | std::ofstream::in); + std::string dataDir = i2p::util::filesystem::GetDataDir ().string (); +#ifndef _WIN32 + dataDir.append ("/"); +#else + dataDir.append ("\\"); +#endif + std::string router_keys = dataDir; + router_keys.append (ROUTER_KEYS); + std::string router_info = dataDir; + router_info.append (ROUTER_INFO); + + std::ifstream fk (router_keys.c_str (), std::ifstream::binary | std::ofstream::in); if (!fk.is_open ()) return false; fk.read ((char *)&m_Keys, sizeof (m_Keys)); m_SigningPrivateKey.Initialize (i2p::crypto::dsap, i2p::crypto::dsaq, i2p::crypto::dsag, CryptoPP::Integer (m_Keys.signingPrivateKey, 20)); - m_RouterInfo = i2p::data::RouterInfo (ROUTER_INFO); // TODO + m_RouterInfo = i2p::data::RouterInfo (router_info.c_str ()); // TODO return true; } - void RouterContext::Save () + void RouterContext::Save (bool infoOnly) { - std::ofstream fk (ROUTER_KEYS, std::ofstream::binary | std::ofstream::out); - fk.write ((char *)&m_Keys, sizeof (m_Keys)); - - std::ofstream fi (ROUTER_INFO, std::ofstream::binary | std::ofstream::out); + std::string dataDir = i2p::util::filesystem::GetDataDir ().string (); +#ifndef _WIN32 + dataDir.append ("/"); +#else + dataDir.append ("\\"); +#endif + std::string router_keys = dataDir; + router_keys.append (ROUTER_KEYS); + std::string router_info = dataDir; + router_info.append (ROUTER_INFO); + + if (!infoOnly) + { + std::ofstream fk (router_keys.c_str (), std::ofstream::binary | std::ofstream::out); + fk.write ((char *)&m_Keys, sizeof (m_Keys)); + } + + std::ofstream fi (router_info.c_str (), std::ofstream::binary | std::ofstream::out); fi.write ((char *)m_RouterInfo.GetBuffer (), m_RouterInfo.GetBufferLen ()); } } diff --git a/RouterContext.h b/RouterContext.h index f43094c1..092c50b5 100644 --- a/RouterContext.h +++ b/RouterContext.h @@ -28,12 +28,14 @@ namespace i2p void Sign (uint8_t * buf, int len, uint8_t * signature); void OverrideNTCPAddress (const char * host, int port); // temporary + void UpdateAddress (const char * host); // called from SSU private: void CreateNewRouter (); + void UpdateRouterInfo (); bool Load (); - void Save (); + void Save (bool infoOnly = false); private: diff --git a/RouterInfo.cpp b/RouterInfo.cpp index 3b0054a0..adae686c 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] - '0'; // 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); } @@ -159,6 +180,9 @@ namespace data CryptoPP::SHA256().CalculateDigest(m_IdentHash, (uint8_t *)&m_RouterIdentity, sizeof (m_RouterIdentity)); UpdateIdentHashBase64 (); UpdateRoutingKey (); + + if (!m_SupportedTransports) + SetUnreachable (true); } void RouterInfo::UpdateIdentHashBase64 () @@ -187,18 +211,36 @@ namespace data { s.write ((char *)&address.cost, sizeof (address.cost)); s.write ((char *)&address.date, sizeof (address.date)); + std::stringstream properties; if (address.transportStyle == eTransportNTCP) WriteString ("NTCP", s); else if (address.transportStyle == eTransportSSU) + { WriteString ("SSU", s); + // caps + WriteString ("caps", properties); + properties << '='; + WriteString ("B", properties); // TODO: should be 'BC' for introducers + properties << ';'; + } else WriteString ("", s); - std::stringstream properties; WriteString ("host", properties); properties << '='; WriteString (address.host.to_string (), properties); properties << ';'; + if (address.transportStyle == eTransportSSU) + { + // wtite intro key + WriteString ("key", properties); + properties << '='; + char value[64]; + size_t l = ByteStreamToBase64 (address.key, 32, value, 64); + value[l] = 0; + WriteString (value, properties); + properties << ';'; + } WriteString ("port", properties); properties << '='; WriteString (boost::lexical_cast(address.port), properties); @@ -264,8 +306,22 @@ namespace data addr.cost = 2; addr.date = 0; m_Addresses.push_back(addr); + m_SupportedTransports |= eNTCPV4; } + void RouterInfo::AddSSUAddress (const char * host, int port, const uint8_t * key) + { + Address addr; + addr.host = boost::asio::ip::address::from_string (host); + addr.port = port; + addr.transportStyle = eTransportSSU; + addr.cost = 10; // NTCP should have prioprity over SSU + addr.date = 0; + memcpy (addr.key, key, 32); + m_Addresses.push_back(addr); + m_SupportedTransports |= eSSUV4; + } + void RouterInfo::SetProperty (const char * key, const char * value) { m_Properties[key] = value; @@ -294,18 +350,33 @@ namespace data else return m_SupportedTransports & (eNTCPV4 | eNTCPV6); } + + bool RouterInfo::IsSSU (bool v4only) const + { + if (v4only) + return m_SupportedTransports & eSSUV4; + 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 (); + } - RouterInfo::Address * RouterInfo::GetNTCPAddress (bool v4only) + const RouterInfo::Address * RouterInfo::GetNTCPAddress (bool v4only) const { return GetAddress (eTransportNTCP, v4only); } - RouterInfo::Address * RouterInfo::GetSSUAddress (bool v4only) + const RouterInfo::Address * RouterInfo::GetSSUAddress (bool v4only) const { return GetAddress (eTransportSSU, v4only); } - RouterInfo::Address * RouterInfo::GetAddress (TransportStyle s, bool v4only) + const RouterInfo::Address * RouterInfo::GetAddress (TransportStyle s, bool v4only) const { for (auto& address : m_Addresses) { diff --git a/RouterInfo.h b/RouterInfo.h index d7fc6dd9..52a8d9e9 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); @@ -53,17 +63,20 @@ namespace data const char * GetIdentHashBase64 () const { return m_IdentHashBase64; }; const char * GetIdentHashAbbreviation () const { return m_IdentHashAbbreviation; }; uint64_t GetTimestamp () const { return m_Timestamp; }; - const std::vector
& GetAddresses () const { return m_Addresses; }; - Address * GetNTCPAddress (bool v4only = true); - Address * GetSSUAddress (bool v4only = true); + std::vector
& GetAddresses () { return m_Addresses; }; + const Address * GetNTCPAddress (bool v4only = true) const; + const Address * GetSSUAddress (bool v4only = true) const; const RoutingKey& GetRoutingKey () const { return m_RoutingKey; }; void AddNTCPAddress (const char * host, int port); + void AddSSUAddress (const char * host, int port, const uint8_t * key); void SetProperty (const char * key, const char * value); const char * GetProperty (const char * key) const; bool IsFloodfill () const; 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; }; @@ -90,7 +103,7 @@ namespace data size_t ReadString (char * str, std::istream& s); void WriteString (const std::string& str, std::ostream& s); void UpdateIdentHashBase64 (); - Address * GetAddress (TransportStyle s, bool v4only); + const Address * GetAddress (TransportStyle s, bool v4only) const; private: diff --git a/SSU.cpp b/SSU.cpp index 314a62da..7efd14e4 100644 --- a/SSU.cpp +++ b/SSU.cpp @@ -14,8 +14,8 @@ namespace i2p namespace ssu { - SSUSession::SSUSession (SSUServer * server, const boost::asio::ip::udp::endpoint& remoteEndpoint, - i2p::data::RouterInfo * router): m_Server (server), m_RemoteEndpoint (remoteEndpoint), + SSUSession::SSUSession (SSUServer * server, boost::asio::ip::udp::endpoint& remoteEndpoint, + const i2p::data::RouterInfo * router): m_Server (server), m_RemoteEndpoint (remoteEndpoint), m_RemoteRouter (router), m_State (eSessionStateUnknown) { } @@ -47,7 +47,8 @@ namespace ssu { switch (m_State) { - case eSessionStateEstablised: + case eSessionStateConfirmedSent: + case eSessionStateEstablished: // most common case ProcessMessage (buf, len); break; @@ -64,6 +65,21 @@ namespace ssu // session confirmed ProcessSessionConfirmed (buf, len); break; + case eSessionRelayRequestSent: + // relay response + ProcessRelayResponse (buf,len); + break; + case eSessionRelayResponseReceived: + // HolePunch received + LogPrint ("SSU HolePuch of ", len, " bytes received"); + m_State = eSessionStateEstablished; + Established (); + break; + case eSessionRelayRequestReceived: + // HolePunch + m_State = eSessionStateUnknown; + Connect (); + break; default: LogPrint ("SSU state not implemented yet"); } @@ -85,24 +101,43 @@ namespace ssu case PAYLOAD_TYPE_TEST: LogPrint ("SSU test received"); break; - case PAYLOAD_TYPE_SESSION_DESTROY: + case PAYLOAD_TYPE_SESSION_DESTROYED: + { LogPrint ("SSU session destroy received"); - break; + if (m_Server) + m_Server->DeleteSession (this); // delete this + break; + } + case PAYLOAD_TYPE_RELAY_INTRO: + LogPrint ("SSU relay intro received"); + // TODO: + break; default: LogPrint ("Unexpected SSU payload type ", (int)payloadType); } } - // TODO: try intro key as well else - LogPrint ("MAC verifcation failed"); + { + LogPrint ("MAC key failed. Trying intro key"); + auto introKey = GetIntroKey (); + if (introKey && Validate (buf, len, introKey)) + { + Decrypt (buf, len, introKey); + SSUHeader * header = (SSUHeader *)buf; + LogPrint ("Unexpected SSU payload type ", (int)(header->flag >> 4)); + // TODO: + } + else + LogPrint ("MAC verifcation failed"); + m_State = eSessionStateUnknown; + } } void SSUSession::ProcessSessionRequest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint) { LogPrint ("Process session request"); // use our intro key - if (ProcessIntroKeyEncryptedMessage (PAYLOAD_TYPE_SESSION_REQUEST, - i2p::context.GetRouterInfo (), buf, len)) + if (ProcessIntroKeyEncryptedMessage (PAYLOAD_TYPE_SESSION_REQUEST, buf, len)) { m_State = eSessionStateRequestReceived; LogPrint ("Session request received"); @@ -121,17 +156,42 @@ namespace ssu } // use remote intro key - if (ProcessIntroKeyEncryptedMessage (PAYLOAD_TYPE_SESSION_CREATED, *m_RemoteRouter, buf, len)) + if (ProcessIntroKeyEncryptedMessage (PAYLOAD_TYPE_SESSION_CREATED, buf, len)) { m_State = eSessionStateCreatedReceived; LogPrint ("Session created received"); - uint8_t * ourAddress = buf + sizeof (SSUHeader) + 257; - boost::asio::ip::address_v4 ourIP (be32toh (*(uint32_t* )(ourAddress))); - uint16_t ourPort = be16toh (*(uint16_t *)(ourAddress + 4)); + uint8_t signedData[532]; // x,y, our IP, our port, remote IP, remote port, relayTag, signed on time + uint8_t * payload = buf + sizeof (SSUHeader); + uint8_t * y = payload; + memcpy (signedData, i2p::context.GetRouterIdentity ().publicKey, 256); // x + memcpy (signedData + 256, y, 256); // y + payload += 256; + payload += 1; // size, assume 4 + uint8_t * ourAddress = payload; + boost::asio::ip::address_v4 ourIP (be32toh (*(uint32_t* )ourAddress)); + payload += 4; // address + uint16_t ourPort = be16toh (*(uint16_t *)payload); + payload += 2; // port + memcpy (signedData + 512, ourAddress, 6); // our IP and port LogPrint ("Our external address is ", ourIP.to_string (), ":", ourPort); - uint32_t relayTag = be32toh (*(uint32_t *)(buf + sizeof (SSUHeader) + 263)); - SendSessionConfirmed (buf + sizeof (SSUHeader), ourAddress, relayTag); - m_State = eSessionStateEstablised; + i2p::context.UpdateAddress (ourIP.to_string ().c_str ()); + *(uint32_t *)(signedData + 518) = htobe32 (m_RemoteEndpoint.address ().to_v4 ().to_ulong ()); // remote IP + *(uint16_t *)(signedData + 522) = htobe16 (m_RemoteEndpoint.port ()); // remote port + memcpy (signedData + 524, payload, 8); // relayTag and signed on time + uint32_t relayTag = be32toh (*(uint32_t *)payload); + payload += 4; // relayTag + payload += 4; // signed on time + // decrypt DSA signature + m_Decryption.SetKeyWithIV (m_SessionKey, 32, ((SSUHeader *)buf)->iv); + m_Decryption.ProcessData (payload, payload, 48); + // verify + CryptoPP::DSA::PublicKey pubKey; + pubKey.Initialize (i2p::crypto::dsap, i2p::crypto::dsaq, i2p::crypto::dsag, CryptoPP::Integer (m_RemoteRouter->GetRouterIdentity ().signingKey, 128)); + CryptoPP::DSA::Verifier verifier (pubKey); + if (!verifier.VerifyMessage (signedData, 532, payload, 40)) + LogPrint ("SSU signature verification failed"); + + SendSessionConfirmed (y, ourAddress, relayTag); } } @@ -145,9 +205,10 @@ namespace ssu if ((header->flag >> 4) == PAYLOAD_TYPE_SESSION_CONFIRMED) { m_State = eSessionStateConfirmedReceived; - LogPrint ("Session confirmed received"); - // TODO: - m_State = eSessionStateEstablised; + LogPrint ("Session confirmed received"); + m_State = eSessionStateEstablished; + SendI2NPMessage (CreateDeliveryStatusMsg (0)); + Established (); } else LogPrint ("Unexpected payload type ", (int)(header->flag >> 4)); @@ -158,10 +219,10 @@ namespace ssu void SSUSession::SendSessionRequest () { - auto address = m_RemoteRouter ? m_RemoteRouter->GetSSUAddress () : nullptr; - if (!address) + auto introKey = GetIntroKey (); + if (!introKey) { - LogPrint ("Missing remote SSU address"); + LogPrint ("SSU is not supported"); return; } @@ -174,18 +235,50 @@ namespace ssu uint8_t iv[16]; CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); rnd.GenerateBlock (iv, 16); // random iv - FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_REQUEST, buf, 304, address->key, iv, address->key); + FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_REQUEST, buf, 304, introKey, iv, introKey); m_State = eSessionStateRequestSent; m_Server->Send (buf, 304, m_RemoteEndpoint); } - void SSUSession::SendSessionCreated (const uint8_t * x) + void SSUSession::SendRelayRequest (const i2p::data::RouterInfo::Introducer& introducer) { - auto address = m_RemoteRouter ? m_RemoteRouter->GetSSUAddress () : nullptr; + auto address = i2p::context.GetRouterInfo ().GetSSUAddress (); if (!address) { - LogPrint ("Missing remote SSU 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 (); + auto address = i2p::context.GetRouterInfo ().GetSSUAddress (); + if (!introKey || !address) + { + LogPrint ("SSU is not supported"); return; } uint8_t signedData[532]; // x,y, remote IP, remote port, our IP, our port, relayTag, signed on time @@ -203,8 +296,8 @@ namespace ssu *(uint16_t *)(payload) = htobe16 (m_RemoteEndpoint.port ()); payload += 2; memcpy (signedData + 512, payload - 6, 6); // remote endpoint IP and port - *(uint32_t *)(signedData + 518) = htobe32 (m_Server->GetEndpoint ().address ().to_v4 ().to_ulong ()); // our IP - *(uint16_t *)(signedData + 522) = htobe16 (m_Server->GetEndpoint ().port ()); // our port + *(uint32_t *)(signedData + 518) = htobe32 (address->host.to_v4 ().to_ulong ()); // our IP + *(uint16_t *)(signedData + 522) = htobe16 (address->port); // our port *(uint32_t *)(payload) = 0; // relay tag, always 0 for now payload += 4; *(uint32_t *)(payload) = htobe32 (i2p::util::GetSecondsSinceEpoch ()); // signed on time @@ -221,20 +314,13 @@ namespace ssu m_Encryption.ProcessData (payload, payload, 48); // encrypt message with intro key - FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_CREATED, buf, 368, address->key, iv, address->key); - m_State = eSessionStateRequestSent; + FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_CREATED, buf, 368, introKey, iv, introKey); + m_State = eSessionStateCreatedSent; m_Server->Send (buf, 368, m_RemoteEndpoint); } void SSUSession::SendSessionConfirmed (const uint8_t * y, const uint8_t * ourAddress, uint32_t relayTag) { - auto address = m_RemoteRouter ? m_RemoteRouter->GetSSUAddress () : nullptr; - if (!address) - { - LogPrint ("Missing remote SSU address"); - return; - } - uint8_t buf[480 + 18]; uint8_t * payload = buf + sizeof (SSUHeader); *payload = 1; // 1 fragment @@ -272,15 +358,56 @@ namespace ssu m_Server->Send (buf, 480, m_RemoteEndpoint); } - bool SSUSession::ProcessIntroKeyEncryptedMessage (uint8_t expectedPayloadType, i2p::data::RouterInfo& r, uint8_t * buf, size_t len) + void SSUSession::ProcessRelayResponse (uint8_t * buf, size_t len) { - auto address = r.GetSSUAddress (); - if (address) + 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 ()); + m_State= eSessionRelayResponseReceived; + } + else + LogPrint ("Unexpected payload type ", (int)(header->flag >> 4)); + } + } + + bool SSUSession::ProcessIntroKeyEncryptedMessage (uint8_t expectedPayloadType, uint8_t * buf, size_t len) + { + auto introKey = GetIntroKey (); + if (introKey) { // use intro key for verification and decryption - if (Validate (buf, len, address->key)) + if (Validate (buf, len, introKey)) { - Decrypt (buf, len, address->key); + Decrypt (buf, len, introKey); SSUHeader * header = (SSUHeader *)buf; if ((header->flag >> 4) == expectedPayloadType) { @@ -291,14 +418,15 @@ 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 by ", r.GetIdentHashAbbreviation ()); + LogPrint ("SSU is not supported"); return false; } - void SSUSession::FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len, uint8_t * aesKey, uint8_t * iv, uint8_t * macKey) + void SSUSession::FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len, + const uint8_t * aesKey, const uint8_t * iv, const uint8_t * macKey) { if (len < sizeof (SSUHeader)) { @@ -319,7 +447,7 @@ namespace ssu i2p::crypto::HMACMD5Digest (encrypted, encryptedLen + 18, macKey, header->mac); } - void SSUSession::Decrypt (uint8_t * buf, size_t len, uint8_t * aesKey) + void SSUSession::Decrypt (uint8_t * buf, size_t len, const uint8_t * aesKey) { if (len < sizeof (SSUHeader)) { @@ -334,7 +462,7 @@ namespace ssu m_Decryption.ProcessData (encrypted, encrypted, encryptedLen); } - bool SSUSession::Validate (uint8_t * buf, size_t len, uint8_t * macKey) + bool SSUSession::Validate (uint8_t * buf, size_t len, const uint8_t * macKey) { if (len < sizeof (SSUHeader)) { @@ -357,17 +485,88 @@ namespace ssu SendSessionRequest (); } - void SSUSession::SendI2NPMessage (I2NPMessage * msg) + void SSUSession::ConnectThroughIntroducer (const i2p::data::RouterInfo::Introducer& introducer) + { + SendRelayRequest (introducer); + } + + void SSUSession::Close () { - // TODO: + SendSesionDestroyed (); + if (!m_DelayedMessages.empty ()) + { + for (auto it :m_DelayedMessages) + delete it; + m_DelayedMessages.clear (); + } + } + + void SSUSession::Established () + { + SendI2NPMessage (CreateDatabaseStoreMsg ()); + if (!m_DelayedMessages.empty ()) + { + for (auto it :m_DelayedMessages) + Send (it); + m_DelayedMessages.clear (); + } + } + + const uint8_t * SSUSession::GetIntroKey () const + { + if (m_RemoteRouter) + { + // we are client + auto address = m_RemoteRouter->GetSSUAddress (); + return address ? address->key : nullptr; + } + else + { + // we are server + auto address = i2p::context.GetRouterInfo ().GetSSUAddress (); + return address ? address->key : nullptr; + } } + void SSUSession::SendI2NPMessage (I2NPMessage * msg) + { + if (msg) + { + if (m_State == eSessionStateEstablished) + Send (msg); + else + m_DelayedMessages.push_back (msg); + } + } + void SSUSession::ProcessData (uint8_t * buf, size_t len) { //uint8_t * start = buf; uint8_t flag = *buf; buf++; LogPrint ("Process SSU data flags=", (int)flag); + if (flag & DATA_FLAG_EXPLICIT_ACKS_INCLUDED) + { + // explicit ACKs + uint8_t numAcks =*buf; + buf++; + // TODO: process ACKs + buf += numAcks*4; + } + if (flag & DATA_FLAG_ACK_BITFIELDS_INCLUDED) + { + // explicit ACK bitfields + uint8_t numBitfields =*buf; + buf++; + for (int i = 0; i < numBitfields; i++) + { + buf += 4; // msgID + // TODO: process ACH bitfields + while (*buf & 0x80) // not last + buf++; + buf++; // last byte + } + } uint8_t numFragments = *buf; // number of fragments buf++; for (int i = 0; i < numFragments; i++) @@ -382,14 +581,157 @@ namespace ssu uint16_t fragmentSize = fragmentInfo & 0x1FFF; // bits 0 - 13 bool isLast = fragmentInfo & 0x010000; // bit 16 uint8_t fragmentNum = fragmentInfo >> 17; // bits 23 - 17 - LogPrint ("SSU data fragment ", (int)fragmentNum, " of message ", msgID, " size=", (int)fragmentSize, isLast ? " last" : " non-last"); + LogPrint ("SSU data fragment ", (int)fragmentNum, " of message ", msgID, " size=", (int)fragmentSize, isLast ? " last" : " non-last"); + I2NPMessage * msg = nullptr; + if (fragmentNum > 0) // follow-up fragment + { + auto it = m_IncomleteMessages.find (msgID); + if (it != m_IncomleteMessages.end ()) + { + msg = it->second; + memcpy (msg->buf + msg->len, buf, fragmentSize); + msg->len += fragmentSize; + } + else + // TODO: + LogPrint ("Unexpected follow-on fragment ", fragmentNum, " of message ", msgID); + } + else // first fragment + { + msg = NewI2NPMessage (); + memcpy (msg->GetSSUHeader (), buf, fragmentSize); + msg->len += fragmentSize - sizeof (I2NPHeaderShort); + } + + if (msg) + { + if (!fragmentNum && !isLast) + m_IncomleteMessages[msgID] = msg; + if (isLast) + { + SendMsgAck (msgID); + if (fragmentNum > 0) + m_IncomleteMessages.erase (msgID); + msg->FromSSU (msgID); + if (m_State == eSessionStateEstablished) + i2p::HandleI2NPMessage (msg); + else + { + // we expect DeliveryStatus + if (msg->GetHeader ()->typeID == eI2NPDeliveryStatus) + { + LogPrint ("SSU session established"); + m_State = eSessionStateEstablished; + Established (); + } + else + LogPrint ("SSU unexpected message ", (int)msg->GetHeader ()->typeID); + DeleteI2NPMessage (msg); + } + } + } buf += fragmentSize; } } + void SSUSession::SendMsgAck (uint32_t msgID) + { + uint8_t buf[48 + 18]; // actual length is 44 = 37 + 7 but pad it to multiple of 16 + uint8_t iv[16]; + uint8_t * payload = buf + sizeof (SSUHeader); + *payload = DATA_FLAG_EXPLICIT_ACKS_INCLUDED; // flag + payload++; + *payload = 1; // number of ACKs + payload++; + *(uint32_t *)(payload) = htobe32 (msgID); // msgID + payload += 4; + *payload = 0; // number of fragments + + CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); + rnd.GenerateBlock (iv, 16); // random iv + // encrypt message with session key + FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, 48, m_SessionKey, iv, m_MacKey); + m_Server->Send (buf, 48, m_RemoteEndpoint); + } + + void SSUSession::SendSesionDestroyed () + { + uint8_t buf[48 + 18], iv[16]; + CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); + rnd.GenerateBlock (iv, 16); // random iv + if (m_State == eSessionStateEstablished) + // encrypt message with session key + FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_DESTROYED, buf, 48, m_SessionKey, iv, m_MacKey); + else + { + auto introKey = GetIntroKey (); + if (introKey) + // encrypt message with intro key + FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_DESTROYED, buf, 48, introKey, iv, introKey); + else + { + LogPrint ("SSU: can't send SessionDestroyed message"); + return; + } + } + m_Server->Send (buf, 48, m_RemoteEndpoint); + } + + void SSUSession::Send (i2p::I2NPMessage * msg) + { + uint32_t msgID = htobe32 (msg->ToSSU ()); + size_t payloadSize = SSU_MTU - sizeof (SSUHeader) - 9; // 9 = flag + #frg(1) + messageID(4) + frag info (3) + size_t len = msg->GetLength (); + uint8_t * msgBuf = msg->GetSSUHeader (); + + uint32_t fragmentNum = 0; + while (len > 0) + { + uint8_t buf[SSU_MTU + 18], iv[16], * payload = buf + sizeof (SSUHeader); + *payload = DATA_FLAG_WANT_REPLY; // for compatibility + payload++; + *payload = 1; // always 1 message fragment per message + payload++; + *(uint32_t *)payload = msgID; + payload += 4; + bool isLast = (len <= payloadSize); + size_t size = isLast ? len : payloadSize; + uint32_t fragmentInfo = (fragmentNum << 17); + if (isLast) + fragmentInfo |= 0x010000; + + fragmentInfo |= size; + fragmentInfo = htobe32 (fragmentInfo); + memcpy (payload, (uint8_t *)(&fragmentInfo) + 1, 3); + payload += 3; + memcpy (payload, msgBuf, size); + + size += payload - buf; + if (size % 16) // make sure 16 bytes boundary + size = (size/16 + 1)*16; + + CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); + rnd.GenerateBlock (iv, 16); // random iv + // encrypt message with session key + FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, size, m_SessionKey, iv, m_MacKey); + m_Server->Send (buf, size, m_RemoteEndpoint); + + if (!isLast) + { + len -= payloadSize; + msgBuf += payloadSize; + } + else + len = 0; + fragmentNum++; + } + } + SSUServer::SSUServer (boost::asio::io_service& service, int port): m_Endpoint (boost::asio::ip::udp::v4 (), port), m_Socket (service, m_Endpoint) { + m_Socket.set_option (boost::asio::socket_base::receive_buffer_size (65535)); + m_Socket.set_option (boost::asio::socket_base::send_buffer_size (65535)); } SSUServer::~SSUServer () @@ -405,12 +747,14 @@ namespace ssu void SSUServer::Stop () { + DeleteAllSessions (); m_Socket.close (); } void SSUServer::Send (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& to) { m_Socket.send_to (boost::asio::buffer (buf, len), to); + LogPrint ("SSU sent ", len, " bytes"); } void SSUServer::Receive () @@ -441,7 +785,7 @@ namespace ssu LogPrint ("SSU receive error: ", ecode.message ()); } - SSUSession * SSUServer::GetSession (i2p::data::RouterInfo * router) + SSUSession * SSUServer::GetSession (const i2p::data::RouterInfo * router) { SSUSession * session = nullptr; if (router) @@ -455,12 +799,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 @@ -468,6 +827,38 @@ namespace ssu } return session; } + + void SSUServer::DeleteSession (SSUSession * session) + { + if (session) + { + session->Close (); + m_Sessions.erase (session->GetRemoteEndpoint ()); + delete session; + } + } + + void SSUServer::DeleteAllSessions () + { + for (auto it: m_Sessions) + { + it.second->Close (); + 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 aee160ae..c61d09bb 100644 --- a/SSU.h +++ b/SSU.h @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -35,7 +36,15 @@ namespace ssu const uint8_t PAYLOAD_TYPE_RELAY_INTRO = 5; const uint8_t PAYLOAD_TYPE_DATA = 6; const uint8_t PAYLOAD_TYPE_TEST = 7; - const uint8_t PAYLOAD_TYPE_SESSION_DESTROY = 8; + const uint8_t PAYLOAD_TYPE_SESSION_DESTROYED = 8; + + // data flags + const uint8_t DATA_FLAG_EXTENDED_DATA_INCLUDED = 0x02; + const uint8_t DATA_FLAG_WANT_REPLY = 0x04; + const uint8_t DATA_FLAG_REQUEST_PREVIOUS_ACKS = 0x08; + const uint8_t DATA_FLAG_EXPLICIT_CONGESTION_NOTIFICATION = 0x10; + const uint8_t DATA_FLAG_ACK_BITFIELDS_INCLUDED = 0x40; + const uint8_t DATA_FLAG_EXPLICIT_ACKS_INCLUDED = 0x80; enum SessionState { @@ -46,7 +55,10 @@ namespace ssu eSessionStateCreatedReceived, eSessionStateConfirmedSent, eSessionStateConfirmedReceived, - eSessionStateEstablised + eSessionRelayRequestSent, + eSessionRelayRequestReceived, + eSessionRelayResponseReceived, + eSessionStateEstablished }; class SSUServer; @@ -54,13 +66,17 @@ namespace ssu { public: - SSUSession (SSUServer * server, const boost::asio::ip::udp::endpoint& remoteEndpoint, - i2p::data::RouterInfo * router = nullptr); + SSUSession (SSUServer * server, boost::asio::ip::udp::endpoint& remoteEndpoint, + const i2p::data::RouterInfo * router = nullptr); 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; }; + const i2p::data::RouterInfo * GetRemoteRouter () const { return m_RemoteRouter; }; void SendI2NPMessage (I2NPMessage * msg); - + private: void CreateAESandMacKey (uint8_t * pubKey, uint8_t * aesKey, uint8_t * macKey); @@ -68,26 +84,35 @@ 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); - - bool ProcessIntroKeyEncryptedMessage (uint8_t expectedPayloadType, i2p::data::RouterInfo& r, uint8_t * buf, size_t len); - void FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len, uint8_t * aesKey, uint8_t * iv, uint8_t * macKey); - void Decrypt (uint8_t * buf, size_t len, uint8_t * aesKey); - bool Validate (uint8_t * buf, size_t len, uint8_t * macKey); + void SendMsgAck (uint32_t msgID); + void SendSesionDestroyed (); + void Send (i2p::I2NPMessage * msg); + + bool ProcessIntroKeyEncryptedMessage (uint8_t expectedPayloadType, uint8_t * buf, size_t len); + void FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len, const uint8_t * aesKey, const uint8_t * iv, const uint8_t * macKey); + void Decrypt (uint8_t * buf, size_t len, const uint8_t * aesKey); + bool Validate (uint8_t * buf, size_t len, const uint8_t * macKey); + const uint8_t * GetIntroKey () const; private: SSUServer * m_Server; boost::asio::ip::udp::endpoint m_RemoteEndpoint; - i2p::data::RouterInfo * m_RemoteRouter; + const i2p::data::RouterInfo * m_RemoteRouter; SessionState m_State; CryptoPP::CBC_Mode::Encryption m_Encryption; CryptoPP::CBC_Mode::Decryption m_Decryption; uint8_t m_SessionKey[32], m_MacKey[32]; + std::map m_IncomleteMessages; + std::list m_DelayedMessages; }; class SSUServer @@ -98,10 +123,13 @@ namespace ssu ~SSUServer (); void Start (); void Stop (); - SSUSession * GetSession (i2p::data::RouterInfo * router); + SSUSession * GetSession (const i2p::data::RouterInfo * router); + void DeleteSession (SSUSession * session); + 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: @@ -115,6 +143,10 @@ namespace ssu boost::asio::ip::udp::endpoint m_SenderEndpoint; uint8_t m_ReceiveBuffer[2*SSU_MTU]; std::map m_Sessions; + + public: + // for HTTP only + const decltype(m_Sessions)& GetSessions () const { return m_Sessions; }; }; } } 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 c3b96d78..9dc733c4 100644 --- a/Transports.cpp +++ b/Transports.cpp @@ -25,6 +25,7 @@ namespace i2p { m_IsRunning = true; m_Thread = new std::thread (std::bind (&Transports::Run, this)); + m_Timer = new boost::asio::deadline_timer (m_Service); // create acceptors auto addresses = context.GetRouterInfo ().GetAddresses (); for (auto& address : addresses) @@ -46,6 +47,7 @@ namespace i2p m_SSUServer = new i2p::ssu::SSUServer (m_Service, address.port); LogPrint ("Start listening UDP port ", address.port); m_SSUServer->Start (); + DetectExternalIP (); } else LogPrint ("SSU server already exists"); @@ -60,6 +62,12 @@ namespace i2p m_NTCPSessions.clear (); delete m_NTCPAcceptor; + if (m_Timer) + { + m_Timer->cancel (); + delete m_Timer; + } + if (m_SSUServer) { m_SSUServer->Stop (); @@ -140,7 +148,7 @@ namespace i2p { if (ident == i2p::context.GetRouterInfo ().GetIdentHash ()) // we send it to ourself - i2p::HandleI2NPMessage (msg, false); + i2p::HandleI2NPMessage (msg); else m_Service.post (boost::bind (&Transports::PostMessage, this, ident, msg)); } @@ -160,7 +168,23 @@ namespace i2p AddNTCPSession (session); } else - LogPrint ("No NTCP addresses available"); + { + // SSU always have lower prioprity than NTCP + // TODO: it shouldn't + LogPrint ("No NTCP addresses available. Trying SSU"); + address = r->GetSSUAddress (); + if (address && m_SSUServer) + { + auto s = m_SSUServer->GetSession (r); + if (s) + { + s->SendI2NPMessage (msg); + return; + } + } + else + LogPrint ("No SSU addresses available"); + } } else { @@ -173,4 +197,30 @@ namespace i2p else LogPrint ("Session not found"); } + + void Transports::DetectExternalIP () + { + for (int i = 0; i < 5; i ++) + { + auto router = i2p::data::netdb.GetRandomRouter (); + if (router && router->IsSSU () && m_SSUServer) + m_SSUServer->GetSession (router); + } + if (m_Timer) + { + m_Timer->expires_from_now (boost::posix_time::seconds(5)); // 5 seconds + m_Timer->async_wait (boost::bind (&Transports::HandleTimer, this, boost::asio::placeholders::error)); + } + } + + void Transports::HandleTimer (const boost::system::error_code& ecode) + { + if (ecode != boost::asio::error::operation_aborted) + { + // end of external IP detection + if (m_SSUServer) + m_SSUServer->DeleteAllSessions (); + } + } + } diff --git a/Transports.h b/Transports.h index 03f6cf35..aedd51b2 100644 --- a/Transports.h +++ b/Transports.h @@ -38,6 +38,9 @@ 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); + + void DetectExternalIP (); + void HandleTimer (const boost::system::error_code& ecode); private: @@ -49,11 +52,13 @@ namespace i2p std::map m_NTCPSessions; i2p::ssu::SSUServer * m_SSUServer; + boost::asio::deadline_timer * m_Timer; public: // for HTTP only const decltype(m_NTCPSessions)& GetNTCPSessions () const { return m_NTCPSessions; }; + const i2p::ssu::SSUServer * GetSSUServer () const { return m_SSUServer; }; }; extern Transports transports; diff --git a/Tunnel.cpp b/Tunnel.cpp index 1163f47e..ed9708cb 100644 --- a/Tunnel.cpp +++ b/Tunnel.cpp @@ -130,34 +130,35 @@ namespace tunnel void InboundTunnel::HandleTunnelDataMsg (I2NPMessage * msg) { + msg->from = this; EncryptTunnelMsg (msg); m_Endpoint.HandleDecryptedTunnelDataMsg (msg); } 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 (); } @@ -389,7 +390,7 @@ namespace tunnel CreateTunnel ( new TunnelConfig (std::vector { - i2p::data::netdb.GetRandomNTCPRouter () + i2p::data::netdb.GetRandomRouter () }, inboundTunnel->GetTunnelConfig ())); } @@ -397,7 +398,7 @@ namespace tunnel { LogPrint ("Creating two hops outbound tunnel..."); - auto firstHop = i2p::data::netdb.GetRandomNTCPRouter (); // first hop must be NTCP + auto firstHop = i2p::data::netdb.GetRandomRouter (); CreateTunnel ( new TunnelConfig (std::vector { @@ -439,7 +440,7 @@ namespace tunnel CreateTunnel ( new TunnelConfig (std::vector { - i2p::data::netdb.GetRandomNTCPRouter () + i2p::data::netdb.GetRandomRouter () })); } else @@ -447,11 +448,12 @@ namespace tunnel OutboundTunnel * outboundTunnel = GetNextOutboundTunnel (); LogPrint ("Creating two hops inbound tunnel..."); auto router = outboundTunnel->GetTunnelConfig ()->GetFirstHop ()->router; + auto firstHop = i2p::data::netdb.GetRandomRouter (outboundTunnel->GetEndpointRouter ()); CreateTunnel ( new TunnelConfig (std::vector { - i2p::data::netdb.GetRandomRouter (outboundTunnel->GetEndpointRouter ()), - router != &i2p::context.GetRouterInfo () ? router : i2p::data::netdb.GetRandomNTCPRouter () // last hop must be NTCP + firstHop, + router != &i2p::context.GetRouterInfo () ? router : i2p::data::netdb.GetRandomRouter (firstHop) }), outboundTunnel); } 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/TunnelEndpoint.cpp b/TunnelEndpoint.cpp index 0e23a837..fc92ec29 100644 --- a/TunnelEndpoint.cpp +++ b/TunnelEndpoint.cpp @@ -147,7 +147,7 @@ namespace tunnel switch (msg.deliveryType) { case eDeliveryTypeLocal: - i2p::HandleI2NPMessage (msg.data, true); + i2p::HandleI2NPMessage (msg.data); break; case eDeliveryTypeTunnel: i2p::transports.SendMessage (msg.hash, i2p::CreateTunnelGatewayMsg (msg.tunnelID, msg.data)); 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; }; diff --git a/UPnP.cpp b/UPnP.cpp new file mode 100644 index 00000000..ebadc80f --- /dev/null +++ b/UPnP.cpp @@ -0,0 +1,68 @@ +#include +#include +#include +#include "Log.h" +#include "UPnP.h" + +namespace i2p +{ + UPnP::UPnP (): m_Timer (m_Service), + m_Endpoint (boost::asio::ip::udp::v4 (), UPNP_REPLY_PORT), + m_MulticastEndpoint (boost::asio::ip::address::from_string (UPNP_GROUP), UPNP_PORT), + m_Socket (m_Service, m_Endpoint.protocol ()) + { + m_Socket.set_option (boost::asio::socket_base::receive_buffer_size (65535)); + m_Socket.set_option (boost::asio::socket_base::send_buffer_size (65535)); + m_Socket.set_option(boost::asio::ip::udp::socket::reuse_address(true)); + } + + UPnP::~UPnP () + { + } + + void UPnP::Run () + { + DiscoverRouter (); + m_Service.run (); + } + + void UPnP::DiscoverRouter () + { + m_Timer.expires_from_now (boost::posix_time::seconds(5)); // 5 seconds + m_Timer.async_wait (boost::bind (&UPnP::HandleTimer, this, boost::asio::placeholders::error)); + + std::string address = UPNP_GROUP; + address += ":" + boost::lexical_cast(UPNP_PORT); + std::string request = "M-SEARCH * HTTP/1.1\r\n" + "HOST: " + address + "\r\n" + "ST:" + UPNP_ROUTER + "\r\n" + "MAN:\"ssdp:discover\"\r\n" + "MX:3\r\n" + "\r\n\r\n"; + m_Socket.send_to (boost::asio::buffer (request.c_str (), request.length ()), m_MulticastEndpoint); + Receive (); + } + + void UPnP::Receive () + { + m_Socket.async_receive_from (boost::asio::buffer (m_ReceiveBuffer, UPNP_MAX_PACKET_LEN), m_SenderEndpoint, + boost::bind (&UPnP::HandleReceivedFrom, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); + } + + void UPnP::HandleReceivedFrom (const boost::system::error_code& ecode, size_t bytes_transferred) + { + LogPrint ("UPnP: ", bytes_transferred, " received from ", m_SenderEndpoint.address ()); + std::string str (m_ReceiveBuffer, bytes_transferred); + LogPrint (str); + m_Timer.cancel (); + } + + void UPnP::HandleTimer (const boost::system::error_code& ecode) + { + if (ecode != boost::asio::error::operation_aborted) + { + LogPrint ("UPnP: timeout expired"); + m_Service.stop (); + } + } +} diff --git a/UPnP.h b/UPnP.h new file mode 100644 index 00000000..3b5122d1 --- /dev/null +++ b/UPnP.h @@ -0,0 +1,41 @@ +#ifndef UPNP_H__ +#define UPNP_H__ + +#include + +namespace i2p +{ + const int UPNP_MAX_PACKET_LEN = 1500; + const char UPNP_GROUP[] = "239.255.255.250"; + const int UPNP_PORT = 1900; + const int UPNP_REPLY_PORT = 1901; + const char UPNP_ROUTER[] = "urn:schemas-upnp-org:device:InternetGatewayDevice:1"; + + class UPnP + { + public: + + UPnP (); + ~UPnP (); + + void Run (); + + + private: + + void DiscoverRouter (); + void Receive (); + void HandleReceivedFrom (const boost::system::error_code& ecode, size_t bytes_transferred); + void HandleTimer (const boost::system::error_code& ecode); + + private: + + boost::asio::io_service m_Service; + boost::asio::deadline_timer m_Timer; + boost::asio::ip::udp::endpoint m_Endpoint, m_MulticastEndpoint, m_SenderEndpoint; + boost::asio::ip::udp::socket m_Socket; + char m_ReceiveBuffer[UPNP_MAX_PACKET_LEN]; + }; +} + +#endif diff --git a/hmac.h b/hmac.h index 6e9c042b..971b9f1a 100644 --- a/hmac.h +++ b/hmac.h @@ -13,7 +13,7 @@ namespace crypto const uint64_t IPAD = 0x3636363636363636; const uint64_t OPAD = 0x5C5C5C5C5C5C5C5C; - inline void HMACMD5Digest (uint8_t * msg, size_t len, uint8_t * key, uint8_t * digest) + inline void HMACMD5Digest (uint8_t * msg, size_t len, const uint8_t * key, uint8_t * digest) // key is 32 bytes // digest is 16 bytes // block size is 64 bytes diff --git a/i2p.cpp b/i2p.cpp index 355c2003..839e7c01 100644 --- a/i2p.cpp +++ b/i2p.cpp @@ -22,11 +22,13 @@ #include "Tunnel.h" #include "NetDb.h" #include "HTTPServer.h" +#include "Garlic.h" #include "util.h" // Global -int running = 1; +volatile int running = 1; +volatile int isDaemon; #ifndef _WIN32 void handle_signal(int sig) @@ -59,6 +61,7 @@ void handle_signal(int sig) int main( int argc, char* argv[] ) { i2p::util::config::OptionParser(argc,argv); + volatile int isDaemon = i2p::util::config::GetArg("-daemon", 0); #ifdef _WIN32 setlocale(LC_CTYPE, ""); SetConsoleCP(1251); @@ -71,8 +74,22 @@ int main( int argc, char* argv[] ) LogPrint("data directory: ", i2p::util::filesystem::GetDataDir().string()); i2p::util::filesystem::ReadConfigFile(i2p::util::config::mapArgs, i2p::util::config::mapMultiArgs); + volatile int isLogging = i2p::util::config::GetArg("-log", 0); + if (isLogging == 1) + { + std::string logfile = i2p::util::filesystem::GetDataDir().string(); +#ifndef _WIN32 + logfile.append("/debug.log"); +#else + logfile.append("\\debug.log"); +#endif + freopen(logfile.c_str(),"a",stdout); + LogPrint("Logging to file enabled."); + } + + #ifndef _WIN32 - if (i2p::util::config::GetArg("-daemon", 0) == 1) + if (isDaemon == 1) { pid_t pid; pid = fork(); @@ -125,18 +142,6 @@ int main( int argc, char* argv[] ) sigaction(SIGINT,&sa,0); #endif - if (i2p::util::config::GetArg("-log", 0) == 1) - { - std::string logfile = i2p::util::filesystem::GetDataDir().string(); -#ifndef _WIN32 - logfile.append("/debug.log"); -#else - logfile.append("\\debug.log"); -#endif - LogPrint("Logging to file enabled."); - freopen(logfile.c_str(),"a",stdout); - } - //TODO: This is an ugly workaround. fix it. //TODO: Autodetect public IP. i2p::context.OverrideNTCPAddress(i2p::util::config::GetCharArg("-host", "127.0.0.1"), @@ -148,6 +153,7 @@ int main( int argc, char* argv[] ) i2p::data::netdb.Start (); i2p::transports.Start (); i2p::tunnel::tunnels.Start (); + i2p::garlic::routing.Start (); while (running) { @@ -156,12 +162,13 @@ int main( int argc, char* argv[] ) } LogPrint("Shutdown started."); + i2p::garlic::routing.Stop (); i2p::tunnel::tunnels.Stop (); i2p::transports.Stop (); i2p::data::netdb.Stop (); httpServer.Stop (); - if (i2p::util::config::GetArg("-log", 0) == 1) + if (isLogging == 1) { fclose (stdout); } diff --git a/util.cpp b/util.cpp index 1917a984..262b6e25 100644 --- a/util.cpp +++ b/util.cpp @@ -99,11 +99,12 @@ namespace filesystem { static boost::filesystem::path path; - if (i2p::util::config::mapArgs.count("-datadir")) { + // TODO: datadir parameter is useless because GetDataDir is called before OptionParser + // and mapArgs is not initialized yet + /*if (i2p::util::config::mapArgs.count("-datadir")) path = boost::filesystem::system_complete(i2p::util::config::mapArgs["-datadir"]); - } else { + else */ path = GetDefaultDataDir(); - } if (!boost::filesystem::exists( path )) { @@ -115,9 +116,8 @@ namespace filesystem return path; } } - if (!boost::filesystem::is_directory(path)) { + if (!boost::filesystem::is_directory(path)) path = GetDefaultDataDir(); - } return path; }