From a65400471aeefb356cfdc61bbf0b2b2a0b866653 Mon Sep 17 00:00:00 2001 From: orignal Date: Wed, 22 Jan 2014 07:44:41 -0500 Subject: [PATCH 1/8] use kademlia to pick floodfill for destination --- NetDb.cpp | 43 ++++++++++++++++++++++++++++--------------- NetDb.h | 8 ++++---- 2 files changed, 32 insertions(+), 19 deletions(-) diff --git a/NetDb.cpp b/NetDb.cpp index 9b454598..6cf9e251 100644 --- a/NetDb.cpp +++ b/NetDb.cpp @@ -239,16 +239,6 @@ namespace data 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 IdentHash& destination, const RouterInfo * floodfill, bool isLeaseSet) - { - if (!floodfill) return; i2p::tunnel::OutboundTunnel * outbound = i2p::tunnel::tunnels.GetNextOutboundTunnel (); if (outbound) { @@ -256,9 +246,31 @@ namespace data if (inbound) { RequestedDestination * dest = CreateRequestedDestination (destination, isLeaseSet); - dest->SetLastOutboundTunnel (outbound); - auto msg = dest->CreateRequestMessage (floodfill, inbound); - outbound->SendTunnelDataMsg (floodfill->GetIdentHash (), 0, msg); + auto floodfill = GetClosestFloodfill (destination, dest->GetExcludedPeers ()); + if (floodfill) + { + std::vector msgs; + // our RouterInfo + msgs.push_back (i2p::tunnel::TunnelMessageBlock + { + i2p::tunnel::eDeliveryTypeRouter, + floodfill->GetIdentHash (), 0, + CreateDatabaseStoreMsg () + }); + + // DatabaseLookup message + dest->SetLastOutboundTunnel (outbound); + msgs.push_back (i2p::tunnel::TunnelMessageBlock + { + i2p::tunnel::eDeliveryTypeRouter, + floodfill->GetIdentHash (), 0, + dest->CreateRequestMessage (floodfill, inbound) + }); + + outbound->SendTunnelDataMsg (msgs); + } + else + LogPrint ("No more floodfills found"); } else LogPrint ("No inbound tunnels found"); @@ -487,7 +499,8 @@ namespace data if (msg) m_Queue.Put (msg); } - const RouterInfo * NetDb::GetClosestFloodfill (const IdentHash& destination) const + const RouterInfo * NetDb::GetClosestFloodfill (const IdentHash& destination, + const std::set& excluded) const { RouterInfo * r = nullptr; XORMetric minMetric; @@ -495,7 +508,7 @@ namespace data minMetric.SetMax (); for (auto it: m_RouterInfos) { - if (it.second->IsFloodfill () &&! it.second->IsUnreachable ()) + if (it.second->IsFloodfill () &&! it.second->IsUnreachable () && !excluded.count (it.first)) { XORMetric m = destKey ^ it.second->GetRoutingKey (); if (m < minMetric) diff --git a/NetDb.h b/NetDb.h index 02a585ef..75c2c81b 100644 --- a/NetDb.h +++ b/NetDb.h @@ -26,7 +26,8 @@ namespace data const IdentHash& GetDestination () const { return m_Destination; }; int GetNumExcludedPeers () const { return m_ExcludedPeers.size (); }; - const RouterInfo * GetLastRouter () const { return m_LastRouter; }; + const std::set& GetExcludedPeers () { return m_ExcludedPeers; }; + 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); }; @@ -62,8 +63,7 @@ namespace data 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); @@ -78,7 +78,7 @@ namespace data void SaveUpdated (const char * directory); void Run (); // exploratory thread void Explore (); - const RouterInfo * GetClosestFloodfill (const IdentHash& destination) const; + const RouterInfo * GetClosestFloodfill (const IdentHash& destination, const std::set& excluded) const; RequestedDestination * CreateRequestedDestination (const IdentHash& dest, bool isLeaseSet, bool isExploratory = false); From 1afc493e90467947383ceffffacd4e3951eb9a68 Mon Sep 17 00:00:00 2001 From: orignal Date: Wed, 22 Jan 2014 07:51:55 -0500 Subject: [PATCH 2/8] bigger buffer size temporary for win32 --- RouterInfo.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/RouterInfo.cpp b/RouterInfo.cpp index 41d57117..ee9a60c2 100644 --- a/RouterInfo.cpp +++ b/RouterInfo.cpp @@ -128,7 +128,13 @@ namespace data size = be16toh (size); while (r < size) { +#ifdef _WIN32 char key[500], value[500]; + // TODO: investigate why properties get read as one long string under Windows + // length should not be more than 44 +#else + char key[50], value[50]; +#endif r += ReadString (key, s); s.seekg (1, std::ios_base::cur); r++; // = r += ReadString (value, s); From dc8dac51f7a878c926bac44edba3b1c84cf43132 Mon Sep 17 00:00:00 2001 From: orignal Date: Wed, 22 Jan 2014 15:32:50 -0500 Subject: [PATCH 3/8] download RouterInfo through HTTP --- NetDb.cpp | 38 ++++++++++++++++++++++++++++++++++++++ NetDb.h | 1 + 2 files changed, 39 insertions(+) diff --git a/NetDb.cpp b/NetDb.cpp index 6cf9e251..a39d7ee1 100644 --- a/NetDb.cpp +++ b/NetDb.cpp @@ -1,6 +1,7 @@ #include "I2PEndian.h" #include #include +#include #include #include #include "base64.h" @@ -520,5 +521,42 @@ namespace data } return r; } + + void NetDb::DownloadRouterInfo (const std::string& address, const std::string& filename) + { + try + { + boost::asio::ip::tcp::iostream site(address, "http"); + if (!site) + { + site.expires_from_now (boost::posix_time::seconds (10)); // wait for 10 seconds + site << "GET " << filename << "HTTP/1.0\nHost: " << address << "\nAccept: */*\nConnection: close\n\n"; + // read response + std::string version, statusMessage; + site >> version; // HTTP version + int status; + site >> status; // status + std::getline (site, statusMessage); + if (status == 200) // OK + { + std::string header; + while (header != "\n") + std::getline (site, header); + // read content + std::stringstream ss; + ss << site.rdbuf(); + AddRouterInfo ((uint8_t *)ss.str ().c_str (), ss.str ().size ()); + } + else + LogPrint ("HTTP response ", status); + } + else + LogPrint ("Can't connect to ", address); + } + catch (std::exception& ex) + { + LogPrint ("Failed to download ", filename, " : ", ex.what ()); + } + } } } diff --git a/NetDb.h b/NetDb.h index 75c2c81b..03be5ed6 100644 --- a/NetDb.h +++ b/NetDb.h @@ -76,6 +76,7 @@ namespace data void Load (const char * directory); void SaveUpdated (const char * directory); + void DownloadRouterInfo (const std::string& address, const std::string& filename); // for reseed void Run (); // exploratory thread void Explore (); const RouterInfo * GetClosestFloodfill (const IdentHash& destination, const std::set& excluded) const; From 2b87a58685da4ecf0725a1ce60dc6568d6e0f720 Mon Sep 17 00:00:00 2001 From: orignal Date: Wed, 22 Jan 2014 17:19:54 -0500 Subject: [PATCH 4/8] fixed build with boost 1.46 --- NetDb.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NetDb.cpp b/NetDb.cpp index a39d7ee1..17b6f9b8 100644 --- a/NetDb.cpp +++ b/NetDb.cpp @@ -529,7 +529,7 @@ namespace data boost::asio::ip::tcp::iostream site(address, "http"); if (!site) { - site.expires_from_now (boost::posix_time::seconds (10)); // wait for 10 seconds + //site.expires_from_now (boost::posix_time::seconds (10)); // wait for 10 seconds site << "GET " << filename << "HTTP/1.0\nHost: " << address << "\nAccept: */*\nConnection: close\n\n"; // read response std::string version, statusMessage; From 6c6d013a7642a8e7b8c64ea8611386a2cf1faca0 Mon Sep 17 00:00:00 2001 From: orignal Date: Wed, 22 Jan 2014 20:19:39 -0500 Subject: [PATCH 5/8] clean-up from obsolete RouterInfos --- NetDb.cpp | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/NetDb.cpp b/NetDb.cpp index 17b6f9b8..084b10ab 100644 --- a/NetDb.cpp +++ b/NetDb.cpp @@ -207,6 +207,8 @@ namespace data }; int count = 0, deletedCount = 0; + auto total = m_RouterInfos.size (); + uint64_t ts = i2p::util::GetMillisecondsSinceEpoch (); for (auto it: m_RouterInfos) { if (it.second->IsUpdated ()) @@ -216,13 +218,20 @@ namespace data it.second->SetUpdated (false); count++; } - else if (it.second->IsUnreachable ()) + else { - if (boost::filesystem::exists (GetFilePath (directory, it.second))) - { - boost::filesystem::remove (GetFilePath (directory, it.second)); - deletedCount++; - } + // RouterInfo expires in 72 hours if more than 300 + if (total > 300 && ts > it.second->GetTimestamp () + 3*24*3600*1000LL) // 3 days + it.second->SetUnreachable (true); + + if (it.second->IsUnreachable ()) + { + if (boost::filesystem::exists (GetFilePath (directory, it.second))) + { + boost::filesystem::remove (GetFilePath (directory, it.second)); + deletedCount++; + } + } } } if (count > 0) From 6385f4554e5b49edba4d01ae0aebd392f40a9867 Mon Sep 17 00:00:00 2001 From: orignal Date: Wed, 22 Jan 2014 20:48:08 -0500 Subject: [PATCH 6/8] make sure not all routers get deleted after long break --- NetDb.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/NetDb.cpp b/NetDb.cpp index 084b10ab..a00e900d 100644 --- a/NetDb.cpp +++ b/NetDb.cpp @@ -222,7 +222,10 @@ namespace data { // RouterInfo expires in 72 hours if more than 300 if (total > 300 && ts > it.second->GetTimestamp () + 3*24*3600*1000LL) // 3 days + { + total--; it.second->SetUnreachable (true); + } if (it.second->IsUnreachable ()) { From d7d3fcef94cec4ae05f1c5e03780ffb8647a2f7e Mon Sep 17 00:00:00 2001 From: orignal Date: Thu, 23 Jan 2014 13:03:37 -0500 Subject: [PATCH 7/8] implementation of HMAC-MD5-128 --- hmac.h | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 hmac.h diff --git a/hmac.h b/hmac.h new file mode 100644 index 00000000..7b77af28 --- /dev/null +++ b/hmac.h @@ -0,0 +1,60 @@ +#ifndef HMAC_H__ +#define HMAC_H__ + +#include +#include +#define CRYPTOPP_ENABLE_NAMESPACE_WEAK 1 +#include + +namespace i2p +{ +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) + // key is 32 bytes + // digest is 16 bytes + // block size is 64 bytes + { + size_t totalLen = len + 64 + 32; + uint8_t * buf = new uint8_t[totalLen]; // TODO: reuse buffers + // ikeypad + ((uint64_t *)buf)[0] = ((uint64_t *)key)[0] ^ IPAD; + ((uint64_t *)buf)[1] = ((uint64_t *)key)[1] ^ IPAD; + ((uint64_t *)buf)[2] = ((uint64_t *)key)[2] ^ IPAD; + ((uint64_t *)buf)[3] = ((uint64_t *)key)[3] ^ IPAD; + ((uint64_t *)buf)[4] = IPAD; + ((uint64_t *)buf)[5] = IPAD; + ((uint64_t *)buf)[6] = IPAD; + ((uint64_t *)buf)[7] = IPAD; + // concatenate with msg + memcpy (buf + 64, msg, len); + // calculate first hash + uint8_t hash[16]; // MD5 + CryptoPP::Weak1::MD5().CalculateDigest (hash, buf, len + 64); + + // okeypad + ((uint64_t *)buf)[0] = ((uint64_t *)key)[0] ^ OPAD; + ((uint64_t *)buf)[1] = ((uint64_t *)key)[1] ^ OPAD; + ((uint64_t *)buf)[2] = ((uint64_t *)key)[2] ^ OPAD; + ((uint64_t *)buf)[3] = ((uint64_t *)key)[3] ^ OPAD; + ((uint64_t *)buf)[4] = OPAD; + ((uint64_t *)buf)[5] = OPAD; + ((uint64_t *)buf)[6] = OPAD; + ((uint64_t *)buf)[7] = OPAD; + // copy first hash after okeypad + memcpy (buf + 64, hash, 16); + // fill next 16 bytes with zeros (first hash size assumed 32 bytes in I2P) + memset (buf + 72, 0, 16); + + // calculate digest + CryptoPP::Weak1::MD5().CalculateDigest (digest, buf, totalLen); + delete[] buf; + } +} +} + +#endif + From 3cf3e69aef309bb1096d1f16655a747a661622c0 Mon Sep 17 00:00:00 2001 From: orignal Date: Thu, 23 Jan 2014 16:10:33 -0500 Subject: [PATCH 8/8] SSU added --- Makefile | 2 +- SSU.cpp | 44 ++++++++++++++++++++++++++++++++++++++++++++ SSU.h | 36 ++++++++++++++++++++++++++++++++++++ Transports.cpp | 21 +++++++++++++++++++-- Transports.h | 2 ++ 5 files changed, 102 insertions(+), 3 deletions(-) create mode 100644 SSU.cpp create mode 100644 SSU.h diff --git a/Makefile b/Makefile index 1fec1950..84c7a32f 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ CC = g++ CFLAGS = -g -Wall -std=c++0x OBJECTS = i2p.o base64.o NTCPSession.o RouterInfo.o Transports.o RouterContext.o \ NetDb.o LeaseSet.o Tunnel.o TunnelEndpoint.o TunnelGateway.o TransitTunnel.o \ - I2NPProtocol.o Log.o Garlic.o HTTPServer.o Streaming.o Identity.o + I2NPProtocol.o Log.o Garlic.o HTTPServer.o Streaming.o Identity.o SSU.o INCFLAGS = LDFLAGS = -Wl,-rpath,/usr/local/lib -lcryptopp -lboost_system -lboost_filesystem LIBS = diff --git a/SSU.cpp b/SSU.cpp new file mode 100644 index 00000000..45788ed4 --- /dev/null +++ b/SSU.cpp @@ -0,0 +1,44 @@ +#include +#include "Log.h" +#include "hmac.h" +#include "SSU.h" + +namespace i2p +{ +namespace ssu +{ + SSUServer::SSUServer (boost::asio::io_service& service, int port): + m_Socket (service, boost::asio::ip::udp::v4 (), port) + { + } + + void SSUServer::Start () + { + Receive (); + } + + void SSUServer::Stop () + { + m_Socket.close (); + } + + void SSUServer::Receive () + { + m_Socket.async_receive_from (boost::asio::buffer (m_ReceiveBuffer, SSU_MTU), m_SenderEndpoint, + boost::bind (&SSUServer::HandleReceivedFrom, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); + } + + void SSUServer::HandleReceivedFrom (const boost::system::error_code& ecode, std::size_t bytes_transferred) + { + if (!ecode) + { + LogPrint ("SSU received ", bytes_transferred, " bytes"); + // Handle + Receive (); + } + else + LogPrint ("SSU receive error: ", ecode.message ()); + } +} +} + diff --git a/SSU.h b/SSU.h new file mode 100644 index 00000000..0cbc31f1 --- /dev/null +++ b/SSU.h @@ -0,0 +1,36 @@ +#ifndef SSU_H__ +#define SSU_H__ + +#include +#include + +namespace i2p +{ +namespace ssu +{ + const int SSU_MTU = 1484; + + class SSUServer + { + public: + + SSUServer (boost::asio::io_service& service, int port); + void Start (); + void Stop (); + + private: + + void Receive (); + void HandleReceivedFrom (const boost::system::error_code& ecode, std::size_t bytes_transferred); + + private: + + boost::asio::ip::udp::socket m_Socket; + boost::asio::ip::udp::endpoint m_SenderEndpoint; + uint8_t m_ReceiveBuffer[SSU_MTU]; + }; +} +} + +#endif + diff --git a/Transports.cpp b/Transports.cpp index 31d0325f..c3b96d78 100644 --- a/Transports.cpp +++ b/Transports.cpp @@ -12,7 +12,7 @@ namespace i2p Transports transports; Transports::Transports (): - m_Thread (0), m_Work (m_Service),m_NTCPAcceptor (0) + m_Thread (nullptr), m_Work (m_Service),m_NTCPAcceptor (nullptr), m_SSUServer (nullptr) { } @@ -34,11 +34,22 @@ namespace i2p m_NTCPAcceptor = new boost::asio::ip::tcp::acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), address.port)); - LogPrint ("Start listening port ", address.port); + LogPrint ("Start listening TCP port ", address.port); auto conn = new i2p::ntcp::NTCPServerConnection (m_Service); m_NTCPAcceptor->async_accept(conn->GetSocket (), boost::bind (&Transports::HandleAccept, this, conn, boost::asio::placeholders::error)); } + else if (address.transportStyle == RouterInfo::eTransportSSU) + { + if (!m_SSUServer) + { + m_SSUServer = new i2p::ssu::SSUServer (m_Service, address.port); + LogPrint ("Start listening UDP port ", address.port); + m_SSUServer->Start (); + } + else + LogPrint ("SSU server already exists"); + } } } @@ -49,6 +60,12 @@ namespace i2p m_NTCPSessions.clear (); delete m_NTCPAcceptor; + if (m_SSUServer) + { + m_SSUServer->Stop (); + delete m_SSUServer; + } + m_IsRunning = false; m_Service.stop (); if (m_Thread) diff --git a/Transports.h b/Transports.h index d6b08227..03f6cf35 100644 --- a/Transports.h +++ b/Transports.h @@ -7,6 +7,7 @@ #include #include #include "NTCPSession.h" +#include "SSU.h" #include "RouterInfo.h" #include "I2NPProtocol.h" @@ -47,6 +48,7 @@ namespace i2p boost::asio::ip::tcp::acceptor * m_NTCPAcceptor; std::map m_NTCPSessions; + i2p::ssu::SSUServer * m_SSUServer; public: