diff --git a/Makefile b/Makefile index 1fca8ddc..d792360a 100644 --- a/Makefile +++ b/Makefile @@ -54,15 +54,15 @@ ifneq (, $(findstring darwin, $(SYS))) else include Makefile.osx endif +else ifneq (, $(findstring mingw, $(SYS))$(findstring windows-gnu, $(SYS))$(findstring cygwin, $(SYS))) + DAEMON_SRC += Win32/DaemonWin32.cpp Win32/Win32App.cpp Win32/Win32Service.cpp Win32/Win32NetState.cpp + include Makefile.mingw else ifneq (, $(findstring linux, $(SYS))$(findstring gnu, $(SYS))) DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp include Makefile.linux else ifneq (, $(findstring freebsd, $(SYS))$(findstring openbsd, $(SYS))) DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp include Makefile.bsd -else ifneq (, $(findstring mingw, $(SYS))$(findstring windows-gnu, $(SYS))$(findstring cygwin, $(SYS))) - DAEMON_SRC += Win32/DaemonWin32.cpp Win32/Win32App.cpp Win32/Win32Service.cpp Win32/Win32NetState.cpp - include Makefile.mingw else # not supported $(error Not supported platform) endif diff --git a/daemon/Daemon.cpp b/daemon/Daemon.cpp index 7b09feeb..2c120537 100644 --- a/daemon/Daemon.cpp +++ b/daemon/Daemon.cpp @@ -298,15 +298,14 @@ namespace util bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2); bool ssu2; i2p::config::GetOption("ssu2.enabled", ssu2); - bool ssu; i2p::config::GetOption("ssu", ssu); bool checkInReserved; i2p::config::GetOption("reservedrange", checkInReserved); LogPrint(eLogInfo, "Daemon: Starting Transports"); - if(!ssu) LogPrint(eLogInfo, "Daemon: SSU disabled"); + if(!ssu2) LogPrint(eLogInfo, "Daemon: SSU2 disabled"); if(!ntcp2) LogPrint(eLogInfo, "Daemon: NTCP2 disabled"); i2p::transport::transports.SetCheckReserved(checkInReserved); - i2p::transport::transports.Start(ntcp2, ssu, ssu2); - if (i2p::transport::transports.IsBoundSSU() || i2p::transport::transports.IsBoundSSU2() || i2p::transport::transports.IsBoundNTCP2()) + i2p::transport::transports.Start(ntcp2, ssu2); + if (i2p::transport::transports.IsBoundSSU2() || i2p::transport::transports.IsBoundNTCP2()) LogPrint(eLogInfo, "Daemon: Transports started"); else { diff --git a/daemon/HTTPServer.cpp b/daemon/HTTPServer.cpp index 8d57dedb..77254e61 100644 --- a/daemon/HTTPServer.cpp +++ b/daemon/HTTPServer.cpp @@ -311,12 +311,9 @@ namespace http { s << "\r\n"; switch (address->transportStyle) { - case i2p::data::RouterInfo::eTransportNTCP: + case i2p::data::RouterInfo::eTransportNTCP2: s << "NTCP2"; break; - case i2p::data::RouterInfo::eTransportSSU: - s << "SSU"; - break; case i2p::data::RouterInfo::eTransportSSU2: s << "SSU2"; break; @@ -843,46 +840,6 @@ namespace http { if (!sessions.empty ()) ShowTransportSessions (s, sessions, "NTCP2"); } - auto ssuServer = i2p::transport::transports.GetSSUServer (); - if (ssuServer) - { - auto sessions = ssuServer->GetSessions (); - if (!sessions.empty ()) - { - s << "
\r\n\r\n
"; - for (const auto& it: sessions) - { - s << "
\r\n"; - auto endpoint = it.second->GetRemoteEndpoint (); - if (it.second->IsOutgoing ()) s << " ⇒ "; - s << endpoint.address ().to_string () << ":" << endpoint.port (); - if (!it.second->IsOutgoing ()) s << " ⇒ "; - s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]"; - if (it.second->GetRelayTag ()) - s << " [itag:" << it.second->GetRelayTag () << "]"; - s << "
\r\n" << std::endl; - } - s << "
\r\n
\r\n"; - } - auto sessions6 = ssuServer->GetSessionsV6 (); - if (!sessions6.empty ()) - { - s << "
\r\n\r\n
"; - for (const auto& it: sessions6) - { - s << "
\r\n"; - auto endpoint = it.second->GetRemoteEndpoint (); - if (it.second->IsOutgoing ()) s << " ⇒ "; - s << "[" << endpoint.address ().to_string () << "]:" << endpoint.port (); - if (!it.second->IsOutgoing ()) s << " ⇒ "; - s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]"; - if (it.second->GetRelayTag ()) - s << " [itag:" << it.second->GetRelayTag () << "]"; - s << "
\r\n" << std::endl; - } - s << "
\r\n
\r\n"; - } - } auto ssu2Server = i2p::transport::transports.GetSSU2Server (); if (ssu2Server) { diff --git a/daemon/UPnP.cpp b/daemon/UPnP.cpp index 6ea4dc24..e48532b0 100644 --- a/daemon/UPnP.cpp +++ b/daemon/UPnP.cpp @@ -248,10 +248,10 @@ namespace transport { switch (address->transportStyle) { - case i2p::data::RouterInfo::eTransportNTCP: + case i2p::data::RouterInfo::eTransportNTCP2: return "TCP"; break; - case i2p::data::RouterInfo::eTransportSSU: + case i2p::data::RouterInfo::eTransportSSU2: default: return "UDP"; } diff --git a/i18n/I18N.cpp b/i18n/I18N.cpp new file mode 100644 index 00000000..fe04bcb6 --- /dev/null +++ b/i18n/I18N.cpp @@ -0,0 +1,36 @@ +/* +* Copyright (c) 2021-2022, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + +#include "ClientContext.h" +#include "I18N_langs.h" +#include "I18N.h" + +namespace i2p +{ +namespace i18n +{ + void SetLanguage(const std::string &lang) + { + const auto it = i2p::i18n::languages.find(lang); + if (it == i2p::i18n::languages.end()) // fallback + i2p::client::context.SetLanguage (i2p::i18n::english::GetLocale()); + else + i2p::client::context.SetLanguage (it->second.LocaleFunc()); + } + + std::string translate (const std::string& arg) + { + return i2p::client::context.GetLanguage ()->GetString (arg); + } + + std::string translate (const std::string& arg, const std::string& arg2, const int& n) + { + return i2p::client::context.GetLanguage ()->GetPlural (arg, arg2, n); + } +} // i18n +} // i2p diff --git a/i18n/I18N.h b/i18n/I18N.h index dd804926..27e043b9 100644 --- a/i18n/I18N.h +++ b/i18n/I18N.h @@ -9,30 +9,68 @@ #ifndef __I18N_H__ #define __I18N_H__ -#include "ClientContext.h" +#include +#include +#include +#include namespace i2p { namespace i18n { - inline void SetLanguage(const std::string &lang) + class Locale { - const auto it = i2p::i18n::languages.find(lang); - if (it == i2p::i18n::languages.end()) // fallback - i2p::client::context.SetLanguage (i2p::i18n::english::GetLocale()); - else - i2p::client::context.SetLanguage (it->second.LocaleFunc()); - } + public: + Locale ( + const std::string& language, + const std::map& strings, + const std::map>& plurals, + std::function formula + ): m_Language (language), m_Strings (strings), m_Plurals (plurals), m_Formula (formula) { }; - inline std::string translate (const std::string& arg) - { - return i2p::client::context.GetLanguage ()->GetString (arg); - } + // Get activated language name for webconsole + std::string GetLanguage() const + { + return m_Language; + } - inline std::string translate (const std::string& arg, const std::string& arg2, const int& n) - { - return i2p::client::context.GetLanguage ()->GetPlural (arg, arg2, n); - } + std::string GetString (const std::string& arg) const + { + const auto it = m_Strings.find(arg); + if (it == m_Strings.end()) + { + return arg; + } + else + { + return it->second; + } + } + + std::string GetPlural (const std::string& arg, const std::string& arg2, const int& n) const + { + const auto it = m_Plurals.find(arg2); + if (it == m_Plurals.end()) // not found, fallback to english + { + return n == 1 ? arg : arg2; + } + else + { + int form = m_Formula(n); + return it->second[form]; + } + } + + private: + const std::string m_Language; + const std::map m_Strings; + const std::map> m_Plurals; + std::function m_Formula; + }; + + void SetLanguage(const std::string &lang); + std::string translate (const std::string& arg); + std::string translate (const std::string& arg, const std::string& arg2, const int& n); } // i18n } // i2p diff --git a/i18n/I18N_langs.h b/i18n/I18N_langs.h index da70e578..42c7ba4e 100644 --- a/i18n/I18N_langs.h +++ b/i18n/I18N_langs.h @@ -9,60 +9,12 @@ #ifndef __I18N_LANGS_H__ #define __I18N_LANGS_H__ +#include "I18N.h" + namespace i2p { namespace i18n { - class Locale - { - public: - Locale ( - const std::string& language, - const std::map& strings, - const std::map>& plurals, - std::function formula - ): m_Language (language), m_Strings (strings), m_Plurals (plurals), m_Formula (formula) { }; - - // Get activated language name for webconsole - std::string GetLanguage() const - { - return m_Language; - } - - std::string GetString (const std::string& arg) const - { - const auto it = m_Strings.find(arg); - if (it == m_Strings.end()) - { - return arg; - } - else - { - return it->second; - } - } - - std::string GetPlural (const std::string& arg, const std::string& arg2, const int& n) const - { - const auto it = m_Plurals.find(arg2); - if (it == m_Plurals.end()) // not found, fallback to english - { - return n == 1 ? arg : arg2; - } - else - { - int form = m_Formula(n); - return it->second[form]; - } - } - - private: - const std::string m_Language; - const std::map m_Strings; - const std::map> m_Plurals; - std::function m_Formula; - }; - struct langData { std::string LocaleName; // localized name diff --git a/libi2pd/BloomFilter.cpp b/libi2pd/BloomFilter.cpp deleted file mode 100644 index de077e60..00000000 --- a/libi2pd/BloomFilter.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/* -* Copyright (c) 2013-2020, The PurpleI2P Project -* -* This file is part of Purple i2pd project and licensed under BSD3 -* -* See full license text in LICENSE file at top of project tree -*/ - -#include "BloomFilter.h" -#include "I2PEndian.h" -#include -#include - -namespace i2p -{ -namespace util -{ - - /** @brief decaying bloom filter implementation */ - class DecayingBloomFilter : public IBloomFilter - { - public: - - DecayingBloomFilter(const std::size_t size) - { - m_Size = size; - m_Data = new uint8_t[size]; - } - - /** @brief implements IBloomFilter::~IBloomFilter */ - ~DecayingBloomFilter() - { - delete [] m_Data; - } - - /** @brief implements IBloomFilter::Add */ - bool Add(const uint8_t * data, std::size_t len) - { - std::size_t idx; - uint8_t mask; - Get(data, len, idx, mask); - if(m_Data[idx] & mask) return false; // filter hit - m_Data[idx] |= mask; - return true; - } - - /** @brief implements IBloomFilter::Decay */ - void Decay() - { - // reset bloom filter buffer - memset(m_Data, 0, m_Size); - } - - private: - /** @brief get bit index for for data */ - void Get(const uint8_t * data, std::size_t len, std::size_t & idx, uint8_t & bm) - { - bm = 1; - uint8_t digest[32]; - // TODO: use blake2 because it's faster - SHA256(data, len, digest); - uint64_t i = buf64toh(digest); - idx = i % m_Size; - bm <<= (i % 8); - } - - uint8_t * m_Data; - std::size_t m_Size; - }; - - - BloomFilterPtr BloomFilter(std::size_t capacity) - { - return std::make_shared(capacity); - } -} -} diff --git a/libi2pd/BloomFilter.h b/libi2pd/BloomFilter.h deleted file mode 100644 index ade854e4..00000000 --- a/libi2pd/BloomFilter.h +++ /dev/null @@ -1,39 +0,0 @@ -/* -* Copyright (c) 2013-2020, The PurpleI2P Project -* -* This file is part of Purple i2pd project and licensed under BSD3 -* -* See full license text in LICENSE file at top of project tree -*/ - -#ifndef BLOOM_FILTER_H_ -#define BLOOM_FILTER_H_ -#include -#include - -namespace i2p -{ -namespace util -{ - - /** @brief interface for bloom filter */ - struct IBloomFilter - { - - /** @brief destructor */ - virtual ~IBloomFilter() {}; - /** @brief add entry to bloom filter, return false if filter hit otherwise return true */ - virtual bool Add(const uint8_t * data, std::size_t len) = 0; - /** @brief optionally decay old entries */ - virtual void Decay() = 0; - }; - - typedef std::shared_ptr BloomFilterPtr; - - /** @brief create bloom filter */ - BloomFilterPtr BloomFilter(std::size_t capacity = 1024 * 8); - -} -} - -#endif diff --git a/libi2pd/Config.cpp b/libi2pd/Config.cpp index b3563c9d..046f0a25 100644 --- a/libi2pd/Config.cpp +++ b/libi2pd/Config.cpp @@ -64,7 +64,7 @@ namespace config { ("bandwidth", value()->default_value(""), "Transit traffic bandwidth limit: integer in KBps or letters: L (32), O (256), P (2048), X (>9000)") ("share", value()->default_value(100), "Limit of transit traffic from max bandwidth in percents. (default: 100)") ("ntcp", bool_switch()->default_value(false), "Ignored. Always false") - ("ssu", bool_switch()->default_value(false), "Enable SSU transport (default: disabled)") + ("ssu", bool_switch()->default_value(false), "Ignored. Always false") ("ntcpproxy", value()->default_value(""), "Ignored") #ifdef _WIN32 ("svcctl", value()->default_value(""), "Ignored") diff --git a/libi2pd/Crypto.cpp b/libi2pd/Crypto.cpp index d7fb965e..b152cdd0 100644 --- a/libi2pd/Crypto.cpp +++ b/libi2pd/Crypto.cpp @@ -239,55 +239,6 @@ namespace crypto static BIGNUM * (* g_ElggTable)[255] = nullptr; -// DH - - DHKeys::DHKeys () - { - m_DH = DH_new (); - DH_set0_pqg (m_DH, BN_dup (elgp), NULL, BN_dup (elgg)); - DH_set0_key (m_DH, NULL, NULL); - } - - DHKeys::~DHKeys () - { - DH_free (m_DH); - } - - void DHKeys::GenerateKeys () - { - BIGNUM * priv_key = NULL, * pub_key = NULL; -#if !defined(__x86_64__) // use short exponent for non x64 - priv_key = BN_new (); - BN_rand (priv_key, ELGAMAL_SHORT_EXPONENT_NUM_BITS, 0, 1); -#endif - if (g_ElggTable) - { -#if defined(__x86_64__) - priv_key = BN_new (); - BN_rand (priv_key, ELGAMAL_FULL_EXPONENT_NUM_BITS, 0, 1); -#endif - auto ctx = BN_CTX_new (); - pub_key = ElggPow (priv_key, g_ElggTable, ctx); - DH_set0_key (m_DH, pub_key, priv_key); - BN_CTX_free (ctx); - } - else - { - DH_set0_key (m_DH, NULL, priv_key); - DH_generate_key (m_DH); - DH_get0_key (m_DH, (const BIGNUM **)&pub_key, (const BIGNUM **)&priv_key); - } - - bn2buf (pub_key, m_PublicKey, 256); - } - - void DHKeys::Agree (const uint8_t * pub, uint8_t * shared) - { - BIGNUM * pk = BN_bin2bn (pub, 256, NULL); - DH_compute_key (shared, pk, m_DH); - BN_free (pk); - } - // x25519 X25519Keys::X25519Keys () { @@ -601,77 +552,6 @@ namespace crypto BN_CTX_free (ctx); } -// HMAC - const uint64_t IPAD = 0x3636363636363636; - const uint64_t OPAD = 0x5C5C5C5C5C5C5C5C; - - - static const uint64_t ipads[] = { IPAD, IPAD, IPAD, IPAD }; - static const uint64_t opads[] = { OPAD, OPAD, OPAD, OPAD }; - - void HMACMD5Digest (uint8_t * msg, size_t len, const MACKey& key, uint8_t * digest) - // key is 32 bytes - // digest is 16 bytes - // block size is 64 bytes - { - uint64_t buf[256]; - uint64_t hash[12]; // 96 bytes -#if (defined(__x86_64__) || defined(__i386__)) && defined(__AVX__) // not all X86 targets supports AVX (like old Pentium, see #1600) - if(i2p::cpu::avx) - { - __asm__ - ( - "vmovups %[key], %%ymm0 \n" - "vmovups %[ipad], %%ymm1 \n" - "vmovups %%ymm1, 32(%[buf]) \n" - "vxorps %%ymm0, %%ymm1, %%ymm1 \n" - "vmovups %%ymm1, (%[buf]) \n" - "vmovups %[opad], %%ymm1 \n" - "vmovups %%ymm1, 32(%[hash]) \n" - "vxorps %%ymm0, %%ymm1, %%ymm1 \n" - "vmovups %%ymm1, (%[hash]) \n" - "vzeroall \n" // end of AVX - "movups %%xmm0, 80(%[hash]) \n" // zero last 16 bytes - : - : [key]"m"(*(const uint8_t *)key), [ipad]"m"(*ipads), [opad]"m"(*opads), - [buf]"r"(buf), [hash]"r"(hash) - : "memory", "%xmm0" // TODO: change to %ymm0 later - ); - } - else -#endif - { - // ikeypad - buf[0] = key.GetLL ()[0] ^ IPAD; - buf[1] = key.GetLL ()[1] ^ IPAD; - buf[2] = key.GetLL ()[2] ^ IPAD; - buf[3] = key.GetLL ()[3] ^ IPAD; - buf[4] = IPAD; - buf[5] = IPAD; - buf[6] = IPAD; - buf[7] = IPAD; - // okeypad - hash[0] = key.GetLL ()[0] ^ OPAD; - hash[1] = key.GetLL ()[1] ^ OPAD; - hash[2] = key.GetLL ()[2] ^ OPAD; - hash[3] = key.GetLL ()[3] ^ OPAD; - hash[4] = OPAD; - hash[5] = OPAD; - hash[6] = OPAD; - hash[7] = OPAD; - // fill last 16 bytes with zeros (first hash size assumed 32 bytes in I2P) - memset (hash + 10, 0, 16); - } - - // concatenate with msg - memcpy (buf + 8, msg, len); - // calculate first hash - MD5((uint8_t *)buf, len + 64, (uint8_t *)(hash + 8)); // 16 bytes - - // calculate digest - MD5((uint8_t *)hash, 96, digest); - } - // AES #ifdef __AES__ #define KeyExpansion256(round0,round1) \ diff --git a/libi2pd/Crypto.h b/libi2pd/Crypto.h index c6dcd2cc..d4cbda97 100644 --- a/libi2pd/Crypto.h +++ b/libi2pd/Crypto.h @@ -62,24 +62,6 @@ namespace crypto // RSA const BIGNUM * GetRSAE (); - // DH - class DHKeys - { - public: - - DHKeys (); - ~DHKeys (); - - void GenerateKeys (); - const uint8_t * GetPublicKey () const { return m_PublicKey; }; - void Agree (const uint8_t * pub, uint8_t * shared); - - private: - - DH * m_DH; - uint8_t m_PublicKey[256]; - }; - // x25519 class X25519Keys { @@ -121,10 +103,6 @@ namespace crypto bool ECIESDecrypt (const EC_GROUP * curve, const BIGNUM * key, const uint8_t * encrypted, uint8_t * data); // 514 bytes encrypted, 222 data void GenerateECIESKeyPair (const EC_GROUP * curve, BIGNUM *& priv, EC_POINT *& pub); - // HMAC - typedef i2p::data::Tag<32> MACKey; - void HMACMD5Digest (uint8_t * msg, size_t len, const MACKey& key, uint8_t * digest); - // AES struct ChipherBlock { diff --git a/libi2pd/NTCP2.cpp b/libi2pd/NTCP2.cpp index 3619e0c6..37553ceb 100644 --- a/libi2pd/NTCP2.cpp +++ b/libi2pd/NTCP2.cpp @@ -309,7 +309,7 @@ namespace transport KDF3Bob (); if (i2p::crypto::AEADChaCha20Poly1305 (m_SessionConfirmedBuffer + 48, m3p2Len - 16, GetH (), 32, GetK (), nonce, m3p2Buf, m3p2Len - 16, false)) // decrypt - // caclulate new h again for KDF data + // calculate new h again for KDF data MixHash (m_SessionConfirmedBuffer + 48, m3p2Len); // h = SHA256(h || ciphertext) else { diff --git a/libi2pd/NetDb.cpp b/libi2pd/NetDb.cpp index 193bfdc3..7dd32e30 100644 --- a/libi2pd/NetDb.cpp +++ b/libi2pd/NetDb.cpp @@ -628,8 +628,8 @@ namespace data (it.second->IsFloodfill () && totalFloodfills - deletedFloodfillsCount < NETDB_MIN_FLOODFILLS))) it.second->SetUnreachable (false); // find & mark expired routers - if (!it.second->IsReachable () && (it.second->GetCompatibleTransports (true) & (RouterInfo::eSSUV4 | RouterInfo::eSSU2V4))) - // non-reachable router, but reachable by ipv4 SSU or SSU2 means introducers + if (!it.second->IsReachable () && (it.second->GetCompatibleTransports (true) & RouterInfo::eSSU2V4)) + // non-reachable router, but reachable by ipv4 SSU2 means introducers { if (ts > it.second->GetTimestamp () + NETDB_INTRODUCEE_EXPIRATION_TIMEOUT*1000LL) // RouterInfo expires after 1 hour if uses introducer @@ -649,7 +649,8 @@ namespace data } // m_RouterInfos iteration m_RouterInfoBuffersPool.CleanUpMt (); - + m_RouterInfoAddressesPool.CleanUpMt (); + if (updatedCount > 0) LogPrint (eLogInfo, "NetDb: Saved ", updatedCount, " new/updated routers"); if (deletedCount > 0) @@ -1209,16 +1210,6 @@ namespace data }); } - std::shared_ptr NetDb::GetRandomPeerTestRouter (bool v4, const std::set& excluded) const - { - return GetRandomRouter ( - [v4, &excluded](std::shared_ptr router)->bool - { - return !router->IsHidden () && router->IsECIES () && - router->IsPeerTesting (v4) && !excluded.count (router->GetIdentHash ()); - }); - } - std::shared_ptr NetDb::GetRandomSSU2PeerTestRouter (bool v4, const std::set& excluded) const { return GetRandomRouter ( @@ -1229,25 +1220,6 @@ namespace data }); } - std::shared_ptr NetDb::GetRandomSSUV6Router () const - { - return GetRandomRouter ( - [](std::shared_ptr router)->bool - { - return !router->IsHidden () && router->IsECIES () && router->IsSSUV6 (); - }); - } - - std::shared_ptr NetDb::GetRandomIntroducer (bool v4, const std::set& excluded) const - { - return GetRandomRouter ( - [v4, &excluded](std::shared_ptr router)->bool - { - return !router->IsHidden () && router->IsECIES () && !router->IsFloodfill () && // floodfills don't send relay tag - router->IsIntroducer (v4) && !excluded.count (router->GetIdentHash ()); - }); - } - std::shared_ptr NetDb::GetRandomSSU2Introducer (bool v4, const std::set& excluded) const { return GetRandomRouter ( diff --git a/libi2pd/NetDb.hpp b/libi2pd/NetDb.hpp index 26e0a41b..8edf8af2 100644 --- a/libi2pd/NetDb.hpp +++ b/libi2pd/NetDb.hpp @@ -89,10 +89,7 @@ namespace data std::shared_ptr GetRandomRouter () const; std::shared_ptr GetRandomRouter (std::shared_ptr compatibleWith, bool reverse) const; std::shared_ptr GetHighBandwidthRandomRouter (std::shared_ptr compatibleWith, bool reverse) const; - std::shared_ptr GetRandomPeerTestRouter (bool v4, const std::set& excluded) const; std::shared_ptr GetRandomSSU2PeerTestRouter (bool v4, const std::set& excluded) const; - std::shared_ptr GetRandomSSUV6Router () const; // TODO: change to v6 peer test later - std::shared_ptr GetRandomIntroducer (bool v4, const std::set& excluded) const; std::shared_ptr GetRandomSSU2Introducer (bool v4, const std::set& excluded) const; std::shared_ptr GetClosestFloodfill (const IdentHash& destination, const std::set& excluded, bool closeThanUsOnly = false) const; std::vector GetClosestFloodfills (const IdentHash& destination, size_t num, @@ -126,6 +123,7 @@ namespace data void ClearRouterInfos () { m_RouterInfos.clear (); }; std::shared_ptr NewRouterInfoBuffer () { return m_RouterInfoBuffersPool.AcquireSharedMt (); }; void PopulateRouterInfoBuffer (std::shared_ptr r); + std::shared_ptr NewRouterInfoAddress () { return m_RouterInfoAddressesPool.AcquireSharedMt (); }; std::shared_ptr NewLease (const Lease& lease) { return m_LeasesPool.AcquireSharedMt (lease); }; uint32_t GetPublishReplyToken () const { return m_PublishReplyToken; }; @@ -183,6 +181,7 @@ namespace data uint32_t m_PublishReplyToken = 0; i2p::util::MemoryPoolMt m_RouterInfoBuffersPool; + i2p::util::MemoryPoolMt m_RouterInfoAddressesPool; i2p::util::MemoryPoolMt m_LeasesPool; }; diff --git a/libi2pd/RouterContext.cpp b/libi2pd/RouterContext.cpp index 6dd3a30d..c8000e2e 100644 --- a/libi2pd/RouterContext.cpp +++ b/libi2pd/RouterContext.cpp @@ -63,7 +63,6 @@ namespace i2p if (!port) port = SelectRandomPort (); bool ipv4; i2p::config::GetOption("ipv4", ipv4); bool ipv6; i2p::config::GetOption("ipv6", ipv6); - bool ssu; i2p::config::GetOption("ssu", ssu); bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2); bool ssu2; i2p::config::GetOption("ssu2.enabled", ssu2); bool ygg; i2p::config::GetOption("meshnets.yggdrasil", ygg); @@ -109,17 +108,12 @@ namespace i2p routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv); } } - if (ssu) - { - routerInfo.AddSSUAddress (host.c_str(), port, nullptr); - caps |= i2p::data::RouterInfo::eReachable; // R - } if (ssu2) { if (ssu2Published) { uint16_t ssu2Port; i2p::config::GetOption ("ssu2.port", ssu2Port); - if (!ssu2Port) ssu2Port = ssu ? (port + 1) : port; + if (!ssu2Port) ssu2Port = port; routerInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, boost::asio::ip::address_v4::from_string (host), ssu2Port); } else @@ -158,17 +152,12 @@ namespace i2p addressCaps |= i2p::data::RouterInfo::AddressCaps::eV6; } } - if (ssu) - { - routerInfo.AddSSUAddress (host.c_str(), port, nullptr); - caps |= i2p::data::RouterInfo::eReachable; // R - } if (ssu2) { if (ssu2Published) { uint16_t ssu2Port; i2p::config::GetOption ("ssu2.port", ssu2Port); - if (!ssu2Port) ssu2Port = ssu ? (port + 1) : port; + if (!ssu2Port) ssu2Port = port; routerInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, boost::asio::ip::address_v6::from_string (host), ssu2Port); } else @@ -236,13 +225,6 @@ namespace i2p fk.write ((char *)m_SSU2Keys.get (), sizeof (SSU2PrivateKeys)); } - bool RouterContext::IsSSU2Only () const - { - auto transports = m_RouterInfo.GetCompatibleTransports (false); - return (transports & (i2p::data::RouterInfo::eSSU2V4 | i2p::data::RouterInfo::eSSU2V6)) && - !(transports & (i2p::data::RouterInfo::eSSUV4 | i2p::data::RouterInfo::eSSUV6)); - } - void RouterContext::SetStatus (RouterStatus status) { if (status != m_Status) @@ -262,13 +244,7 @@ namespace i2p } } } - - void RouterContext::SetStatusSSU2 (RouterStatus status) - { - if (IsSSU2Only ()) - SetStatus (status); - } - + void RouterContext::SetStatusV6 (RouterStatus status) { if (status != m_StatusV6) @@ -288,19 +264,13 @@ namespace i2p } } } - - void RouterContext::SetStatusV6SSU2 (RouterStatus status) - { - if (IsSSU2Only ()) - SetStatusV6 (status); - } - + void RouterContext::UpdatePort (int port) { bool updated = false; for (auto& address : m_RouterInfo.GetAddresses ()) { - if (address->port != port && (address->transportStyle == i2p::data::RouterInfo::eTransportSSU || IsSSU2Only ())) + if (address->port != port && address->transportStyle == i2p::data::RouterInfo::eTransportSSU2) { address->port = port; updated = true; @@ -476,8 +446,6 @@ namespace i2p mtu = maxMTU; LogPrint(eLogWarning, "Router: MTU dropped to upper limit of ", maxMTU, " bytes"); } - if (mtu && !address->IsSSU2 ()) // SSU1 - mtu = (mtu >> 4) << 4; // round to multiple of 16 address->ssu->mtu = mtu; updated = true; } @@ -488,23 +456,8 @@ namespace i2p UpdateRouterInfo (); } - bool RouterContext::AddIntroducer (const i2p::data::RouterInfo::Introducer& introducer) - { - bool ret = m_RouterInfo.AddIntroducer (introducer); - if (ret) - UpdateRouterInfo (); - return ret; - } - - void RouterContext::RemoveIntroducer (const boost::asio::ip::udp::endpoint& e) - { - if (m_RouterInfo.RemoveIntroducer (e)) - UpdateRouterInfo (); - } - bool RouterContext::AddSSU2Introducer (const i2p::data::RouterInfo::Introducer& introducer, bool v4) { - if (!IsSSU2Only ()) return false; bool ret = m_RouterInfo.AddSSU2Introducer (introducer, v4); if (ret) UpdateRouterInfo (); @@ -513,7 +466,6 @@ namespace i2p void RouterContext::RemoveSSU2Introducer (const i2p::data::IdentHash& h, bool v4) { - if (!IsSSU2Only ()) return; if (m_RouterInfo.RemoveSSU2Introducer (h, v4)) UpdateRouterInfo (); } @@ -631,50 +583,6 @@ namespace i2p return m_RouterInfo.GetCaps () & i2p::data::RouterInfo::eUnreachable; } - void RouterContext::RemoveNTCPAddress (bool v4only) - { - bool updated = false; - auto& addresses = m_RouterInfo.GetAddresses (); - for (auto it = addresses.begin (); it != addresses.end ();) - { - if ((*it)->transportStyle == i2p::data::RouterInfo::eTransportNTCP && !(*it)->IsNTCP2 () && - (!v4only || (*it)->host.is_v4 ())) - { - it = addresses.erase (it); - updated = true; - if (v4only) break; // otherwise might be more than one address - } - else - ++it; - } - if (updated) - m_RouterInfo.UpdateSupportedTransports (); - } - - void RouterContext::RemoveSSUAddress () - { - bool updated = false; - auto& addresses = m_RouterInfo.GetAddresses (); - for (auto it = addresses.begin (); it != addresses.end ();) - { - if ((*it)->transportStyle == i2p::data::RouterInfo::eTransportSSU) - { - it = addresses.erase (it); - updated = true; - } - else - ++it; - } - if (updated) - m_RouterInfo.UpdateSupportedTransports (); - } - - void RouterContext::SetUnreachableSSU2 (bool v4, bool v6) - { - if (IsSSU2Only ()) - SetUnreachable (v4, v6); - } - void RouterContext::SetUnreachable (bool v4, bool v6) { if (v4 || (v6 && !SupportsV4 ())) @@ -691,8 +599,7 @@ namespace i2p // delete previous introducers auto& addresses = m_RouterInfo.GetAddresses (); for (auto& addr : addresses) - if (addr->ssu && (!addr->IsSSU2 () || IsSSU2Only ()) && - ((v4 && addr->IsV4 ()) || (v6 && addr->IsV6 ()))) + if (addr->ssu && ((v4 && addr->IsV4 ()) || (v6 && addr->IsV6 ()))) { addr->published = false; addr->caps &= ~i2p::data::RouterInfo::eSSUIntroducer; // can't be introducer @@ -722,19 +629,15 @@ namespace i2p } uint16_t port = 0; // delete previous introducers - bool isSSU2Published = IsSSU2Only (); // TODO - if (isSSU2Published) - i2p::config::GetOption ("ssu2.published", isSSU2Published); + bool isSSU2Published; i2p::config::GetOption ("ssu2.published", isSSU2Published); auto& addresses = m_RouterInfo.GetAddresses (); for (auto& addr : addresses) - if (addr->ssu && (!addr->IsSSU2 () || isSSU2Published) && - ((v4 && addr->IsV4 ()) || (v6 && addr->IsV6 ()))) + if (addr->ssu && isSSU2Published && ((v4 && addr->IsV4 ()) || (v6 && addr->IsV6 ()))) { addr->published = true; addr->caps |= i2p::data::RouterInfo::eSSUIntroducer; addr->ssu->introducers.clear (); - if (addr->port && (!addr->IsSSU2 () || IsSSU2Only ())) - port = addr->port; + if (addr->port) port = addr->port; } // publish NTCP2 bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2); @@ -758,7 +661,7 @@ namespace i2p if (supportsV6) { // insert v6 addresses if necessary - bool foundSSU = false, foundNTCP2 = false, foundSSU2 = false; + bool foundNTCP2 = false, foundSSU2 = false; uint16_t port = 0; auto& addresses = m_RouterInfo.GetAddresses (); for (auto& addr: addresses) @@ -767,10 +670,7 @@ namespace i2p { switch (addr->transportStyle) { - case i2p::data::RouterInfo::eTransportSSU: - foundSSU = true; - break; - case i2p::data::RouterInfo::eTransportNTCP: + case i2p::data::RouterInfo::eTransportNTCP2: foundNTCP2 = true; break; case i2p::data::RouterInfo::eTransportSSU2: @@ -786,13 +686,6 @@ namespace i2p i2p::config::GetOption("port", port); if (!port) port = SelectRandomPort (); } - // SSU - bool ssu; i2p::config::GetOption("ssu", ssu); - if (!foundSSU && ssu) - { - std::string host = "::1"; // TODO: read host - m_RouterInfo.AddSSUAddress (host.c_str (), port, nullptr); - } // NTCP2 if (!foundNTCP2) { @@ -825,7 +718,7 @@ namespace i2p if (ssu2Published) { uint16_t ssu2Port; i2p::config::GetOption ("ssu2.port", ssu2Port); - if (!ssu2Port) ssu2Port = ssu ? (port + 1) : port; + if (!ssu2Port) ssu2Port = port; m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, boost::asio::ip::address::from_string ("::1"), ssu2Port); } else @@ -847,7 +740,7 @@ namespace i2p // update if (supportsV4) { - bool foundSSU = false, foundNTCP2 = false, foundSSU2 = false; + bool foundNTCP2 = false, foundSSU2 = false; std::string host = "127.0.0.1"; uint16_t port = 0; auto& addresses = m_RouterInfo.GetAddresses (); @@ -857,10 +750,7 @@ namespace i2p { switch (addr->transportStyle) { - case i2p::data::RouterInfo::eTransportSSU: - foundSSU = true; - break; - case i2p::data::RouterInfo::eTransportNTCP: + case i2p::data::RouterInfo::eTransportNTCP2: foundNTCP2 = true; break; case i2p::data::RouterInfo::eTransportSSU2: @@ -876,11 +766,6 @@ namespace i2p i2p::config::GetOption("port", port); if (!port) port = SelectRandomPort (); } - // SSU - bool ssu; i2p::config::GetOption("ssu", ssu); - if (!foundSSU && ssu) - m_RouterInfo.AddSSUAddress (host.c_str (), port, nullptr); - // NTCP2 if (!foundNTCP2) { @@ -908,7 +793,7 @@ namespace i2p if (ssu2Published) { uint16_t ssu2Port; i2p::config::GetOption ("ssu2.port", ssu2Port); - if (!ssu2Port) ssu2Port = ssu ? (port + 1) : port; + if (!ssu2Port) ssu2Port = port; m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, boost::asio::ip::address::from_string ("127.0.0.1"), ssu2Port); } else @@ -956,32 +841,9 @@ namespace i2p for (auto& addr: addresses) { if (addr->ssu && ((v4 && addr->IsV4 ()) || (!v4 && addr->IsV6 ()))) - { - if (!addr->IsSSU2 ()) // SSU1 - { - // round to multiple of 16 - if (v4) - { - if (mtu > 1484) mtu = 1484; - else - { - mtu -= 12; - mtu = (mtu >> 4) << 4; - mtu += 12; - } - } - else - { - if (mtu > 1488) mtu = 1488; - else - mtu = (mtu >> 4) << 4; - } - } - if (mtu) - { - addr->ssu->mtu = mtu; - LogPrint (eLogDebug, "Router: MTU for ", v4 ? "ipv4" : "ipv6", " address ", addr->host.to_string(), " is set to ", mtu); - } + { + addr->ssu->mtu = mtu; + LogPrint (eLogDebug, "Router: MTU for ", v4 ? "ipv4" : "ipv6", " address ", addr->host.to_string(), " is set to ", mtu); } } } diff --git a/libi2pd/RouterContext.h b/libi2pd/RouterContext.h index cf394162..f115f026 100644 --- a/libi2pd/RouterContext.h +++ b/libi2pd/RouterContext.h @@ -102,12 +102,10 @@ namespace garlic uint64_t GetTransitBandwidthLimit () const { return (m_BandwidthLimit*m_ShareRatio)/100LL; }; RouterStatus GetStatus () const { return m_Status; }; void SetStatus (RouterStatus status); - void SetStatusSSU2 (RouterStatus status); RouterError GetError () const { return m_Error; }; void SetError (RouterError error) { m_Error = error; }; RouterStatus GetStatusV6 () const { return m_StatusV6; }; void SetStatusV6 (RouterStatus status); - void SetStatusV6SSU2 (RouterStatus status); RouterError GetErrorV6 () const { return m_ErrorV6; }; void SetErrorV6 (RouterError error) { m_ErrorV6 = error; }; int GetNetID () const { return m_NetID; }; @@ -121,16 +119,11 @@ namespace garlic void UpdateNTCP2Address (bool enable); void PublishSSU2Address (int port, bool publish, bool v4, bool v6); void UpdateSSU2Address (bool enable); - void RemoveNTCPAddress (bool v4only = true); // delete NTCP address for older routers. TODO: remove later - void RemoveSSUAddress (); // delete SSU address for older routers - bool AddIntroducer (const i2p::data::RouterInfo::Introducer& introducer); - void RemoveIntroducer (const boost::asio::ip::udp::endpoint& e); bool AddSSU2Introducer (const i2p::data::RouterInfo::Introducer& introducer, bool v4); void RemoveSSU2Introducer (const i2p::data::IdentHash& h, bool v4); void ClearSSU2Introducers (bool v4); bool IsUnreachable () const; void SetUnreachable (bool v4, bool v6); - void SetUnreachableSSU2 (bool v4, bool v6); void SetReachable (bool v4, bool v6); bool IsFloodfill () const { return m_IsFloodfill; }; void SetFloodfill (bool floodfill); diff --git a/libi2pd/RouterInfo.cpp b/libi2pd/RouterInfo.cpp index d9c966d7..580d1f74 100644 --- a/libi2pd/RouterInfo.cpp +++ b/libi2pd/RouterInfo.cpp @@ -206,26 +206,24 @@ namespace data s.read ((char *)&m_Timestamp, sizeof (m_Timestamp)); m_Timestamp = be64toh (m_Timestamp); // read addresses - auto addresses = boost::make_shared(); + if (!m_NewAddresses) m_NewAddresses = boost::make_shared(); uint8_t numAddresses; s.read ((char *)&numAddresses, sizeof (numAddresses)); - addresses->reserve (numAddresses); for (int i = 0; i < numAddresses; i++) { uint8_t supportedTransports = 0; - auto address = std::make_shared
(); + auto address = netdb.NewRouterInfoAddress (); uint8_t cost; // ignore s.read ((char *)&cost, sizeof (cost)); s.read ((char *)&address->date, sizeof (address->date)); - bool isHost = false, isIntroKey = false, isStaticKey = false, isV2 = false; - Tag<32> iV2; // for 'i' field in SSU, TODO: remove later + bool isHost = false, isStaticKey = false, isV2 = false; char transportStyle[6]; ReadString (transportStyle, 6, s); if (!strncmp (transportStyle, "NTCP", 4)) // NTCP or NTCP2 - address->transportStyle = eTransportNTCP; + address->transportStyle = eTransportNTCP2; else if (!strncmp (transportStyle, "SSU", 3)) // SSU or SSU2 { - address->transportStyle = (transportStyle[3] == '2') ? eTransportSSU2 : eTransportSSU; + address->transportStyle = eTransportSSU2; address->ssu.reset (new SSUExt ()); address->ssu->mtu = 0; } @@ -283,13 +281,6 @@ namespace data else LogPrint (eLogWarning, "RouterInfo: Unexpected field 'mtu' for NTCP2"); } - else if (!strcmp (key, "key")) - { - if (address->ssu) - isIntroKey = (Base64ToByteStream (value, strlen (value), address->i, 32) == 32); - else - LogPrint (eLogWarning, "RouterInfo: Unexpected field 'key' for NTCP2"); - } else if (!strcmp (key, "caps")) address->caps = ExtractAddressCaps (value); else if (!strcmp (key, "s")) // ntcp2 or ssu2 static key @@ -306,8 +297,6 @@ namespace data } else if (address->IsSSU2 ()) Base64ToByteStream (value, strlen (value), address->i, 32); - else - Base64ToByteStream (value, strlen (value), iV2, 32); } else if (!strcmp (key, "v")) { @@ -340,21 +329,9 @@ namespace data } Introducer& introducer = address->ssu->introducers.at (index); if (!strcmp (key, "ihost")) - { - boost::system::error_code ecode; - introducer.iHost = boost::asio::ip::address::from_string (value, ecode); - } + introducer.isH = false; // SSU1 else if (!strcmp (key, "iport")) - { - try - { - introducer.iPort = boost::lexical_cast(value); - } - catch (std::exception& ex) - { - LogPrint (eLogWarning, "RouterInfo: 'iport' exception ", ex.what ()); - } - } + introducer.isH = false; // SSU1 else if (!strcmp (key, "itag")) { try @@ -366,8 +343,11 @@ namespace data LogPrint (eLogWarning, "RouterInfo: 'itag' exception ", ex.what ()); } } - else if (!strcmp (key, "ikey") || !strcmp (key, "ih")) - Base64ToByteStream (value, strlen (value), introducer.iKey, 32); + else if (!strcmp (key, "ih")) + { + Base64ToByteStream (value, strlen (value), introducer.iH, 32); + introducer.isH = true; + } else if (!strcmp (key, "iexp")) { try @@ -382,7 +362,7 @@ namespace data } if (!s) return; } - if (address->transportStyle == eTransportNTCP) + if (address->transportStyle == eTransportNTCP2) { if (isStaticKey) { @@ -406,49 +386,7 @@ namespace data } } } - else if (address->transportStyle == eTransportSSU) - { - if (isIntroKey) - { - if (isHost) - supportedTransports |= address->host.is_v4 () ? eSSUV4 : eSSUV6; - else if (address->caps & AddressCaps::eV6) - { - supportedTransports |= eSSUV6; - if (address->caps & AddressCaps::eV4) supportedTransports |= eSSUV4; // in additional to v6 - } - else - supportedTransports |= eSSUV4; // in case if host or 6 caps is not preasented, we assume 4 - if (address->ssu && !address->ssu->introducers.empty ()) - { - // exclude invalid introducers - uint32_t ts = i2p::util::GetSecondsSinceEpoch (); - int numValid = 0; - for (auto& it: address->ssu->introducers) - { - if (!it.iExp) it.iExp = m_Timestamp/1000 + NETDB_INTRODUCEE_EXPIRATION_TIMEOUT; - if (ts <= it.iExp && it.iPort > 0 && - ((it.iHost.is_v4 () && address->IsV4 ()) || (it.iHost.is_v6 () && address->IsV6 ()))) - numValid++; - else - { - it.iPort = 0; - if (isV2) numValid++; - } - } - if (numValid) - m_ReachableTransports |= supportedTransports; - else - address->ssu->introducers.resize (0); - } - else if (isHost && address->port) - { - address->published = true; - m_ReachableTransports |= supportedTransports; - } - } - } - if (address->transportStyle == eTransportSSU2 || (isV2 && address->transportStyle == eTransportSSU)) + else if (address->transportStyle == eTransportSSU2 && isV2) { if (address->IsV4 ()) supportedTransports |= eSSU2V4; if (address->IsV6 ()) supportedTransports |= eSSU2V6; @@ -456,62 +394,42 @@ namespace data { if (address->host.is_v4 ()) m_ReachableTransports |= eSSU2V4; if (address->host.is_v6 ()) m_ReachableTransports |= eSSU2V6; + address->published = true; } - if (address->transportStyle == eTransportSSU2) + if (address->ssu && !address->ssu->introducers.empty ()) { - if (address->port) address->published = true; - if (address->ssu && !address->ssu->introducers.empty ()) - { - // exclude invalid introducers - uint32_t ts = i2p::util::GetSecondsSinceEpoch (); - int numValid = 0; - for (auto& it: address->ssu->introducers) - { - if (it.iTag && ts <= it.iExp) - numValid++; - else - it.iTag = 0; - } - if (numValid) - m_ReachableTransports |= supportedTransports; - else - address->ssu->introducers.resize (0); - } - } - else - { - // create additional SSU2 address. TODO: remove later - auto ssu2addr = std::make_shared
(); - ssu2addr->transportStyle = eTransportSSU2; - ssu2addr->host = address->host; ssu2addr->port = address->port; - ssu2addr->s = address->s; ssu2addr->i = iV2; - ssu2addr->date = address->date; ssu2addr->caps = address->caps; - ssu2addr->published = address->published; - ssu2addr->ssu.reset (new SSUExt ()); ssu2addr->ssu->mtu = address->ssu->mtu; + // exclude invalid introducers uint32_t ts = i2p::util::GetSecondsSinceEpoch (); - if (!address->ssu->introducers.empty ()) + int numValid = 0; + for (auto& it: address->ssu->introducers) { - for (const auto& introducer: address->ssu->introducers) - if (!introducer.iPort && introducer.iHost.is_unspecified () && ts < introducer.iExp) // SSU2 - ssu2addr->ssu->introducers.push_back (introducer); - if (!ssu2addr->ssu->introducers.empty ()) - m_ReachableTransports |= supportedTransports; + if (it.iTag && ts < it.iExp && it.isH) + numValid++; + else + it.iTag = 0; } - addresses->push_back(ssu2addr); + if (numValid) + m_ReachableTransports |= supportedTransports; + else + address->ssu->introducers.resize (0); } } if (supportedTransports) { if (!(m_SupportedTransports & supportedTransports)) // avoid duplicates - addresses->push_back(address); + m_NewAddresses->push_back(address); m_SupportedTransports |= supportedTransports; } } + // update addresses + auto prev = m_Addresses; #if (BOOST_VERSION >= 105300) - boost::atomic_store (&m_Addresses, addresses); + boost::atomic_store (&m_Addresses, m_NewAddresses); #else - m_Addresses = addresses; // race condition + m_Addresses = m_NewAddresses; // race condition #endif + if (prev) prev->clear (); + m_NewAddresses = prev; // read peers uint8_t numPeers; s.read ((char *)&numPeers, sizeof (numPeers)); if (!s) return; @@ -635,10 +553,10 @@ namespace data case CAPS_FLAG_V6: caps |= AddressCaps::eV6; break; - case CAPS_FLAG_SSU_TESTING: + case CAPS_FLAG_SSU2_TESTING: caps |= AddressCaps::eSSUTesting; break; - case CAPS_FLAG_SSU_INTRODUCER: + case CAPS_FLAG_SSU2_INTRODUCER: caps |= AddressCaps::eSSUIntroducer; break; default: ; @@ -701,36 +619,13 @@ namespace data return l+1; } - - void RouterInfo::AddSSUAddress (const char * host, int port, const uint8_t * key, int mtu) - { - auto addr = std::make_shared
(); - addr->host = boost::asio::ip::address::from_string (host); - addr->port = port; - addr->transportStyle = eTransportSSU; - addr->published = true; - addr->caps = i2p::data::RouterInfo::eSSUTesting | i2p::data::RouterInfo::eSSUIntroducer; // BC; - addr->date = 0; - addr->ssu.reset (new SSUExt ()); - addr->ssu->mtu = mtu; - if (key) - memcpy (addr->i, key, 32); - else - RAND_bytes (addr->i, 32); - for (const auto& it: *m_Addresses) // don't insert same address twice - if (*it == *addr) return; - m_SupportedTransports |= addr->host.is_v6 () ? eSSUV6 : eSSUV4; - m_ReachableTransports |= addr->host.is_v6 () ? eSSUV6 : eSSUV4; - m_Addresses->push_back(std::move(addr)); - } - void RouterInfo::AddNTCP2Address (const uint8_t * staticKey, const uint8_t * iv, const boost::asio::ip::address& host, int port, uint8_t caps) { auto addr = std::make_shared
(); addr->host = host; addr->port = port; - addr->transportStyle = eTransportNTCP; + addr->transportStyle = eTransportNTCP2; addr->caps = caps; addr->date = 0; if (port) addr->published = true; @@ -792,51 +687,6 @@ namespace data m_Addresses->push_back(std::move(addr)); } - bool RouterInfo::AddIntroducer (const Introducer& introducer) - { - for (auto& addr : *m_Addresses) - { - if (addr->transportStyle == eTransportSSU && - ((addr->IsV4 () && introducer.iHost.is_v4 ()) || (addr->IsV6 () && introducer.iHost.is_v6 ()))) - { - for (auto& intro: addr->ssu->introducers) - if (intro.iTag == introducer.iTag) return false; // already presented - addr->ssu->introducers.push_back (introducer); - m_ReachableTransports |= (addr->IsV4 () ? eSSUV4 : eSSUV6); - return true; - } - } - return false; - } - - bool RouterInfo::RemoveIntroducer (const boost::asio::ip::udp::endpoint& e) - { - for (auto& addr: *m_Addresses) - { - if (addr->transportStyle == eTransportSSU && - ((addr->IsV4 () && e.address ().is_v4 ()) || (addr->IsV6 () && e.address ().is_v6 ()))) - { - for (auto it = addr->ssu->introducers.begin (); it != addr->ssu->introducers.end (); ++it) - if (boost::asio::ip::udp::endpoint (it->iHost, it->iPort) == e) - { - addr->ssu->introducers.erase (it); - if (addr->ssu->introducers.empty ()) - m_ReachableTransports &= ~(addr->IsV4 () ? eSSUV4 : eSSUV6); - return true; - } - } - } - return false; - } - - bool RouterInfo::IsSSU (bool v4only) const - { - if (v4only) - return m_SupportedTransports & eSSUV4; - else - return m_SupportedTransports & (eSSUV4 | eSSUV6); - } - bool RouterInfo::IsNTCP2 (bool v4only) const { if (v4only) @@ -943,24 +793,6 @@ namespace data } } - std::shared_ptr RouterInfo::GetSSUAddress (bool v4only) const - { - return GetAddress ( - [v4only](std::shared_ptr address)->bool - { - return (address->transportStyle == eTransportSSU) && (!v4only || address->IsV4 ()); - }); - } - - std::shared_ptr RouterInfo::GetSSUV6Address () const - { - return GetAddress ( - [](std::shared_ptr address)->bool - { - return (address->transportStyle == eTransportSSU) && address->IsV6(); - }); - } - std::shared_ptr RouterInfo::GetSSU2V4Address () const { return GetAddress ( @@ -1075,21 +907,10 @@ namespace data bool RouterInfo::IsEligibleFloodfill () const { // floodfill must be reachable by ipv4, >= 0.9.38 and not DSA - return IsReachableBy (eNTCP2V4 | eSSUV4) && m_Version >= NETDB_MIN_FLOODFILL_VERSION && + return IsReachableBy (eNTCP2V4 | eSSU2V4) && m_Version >= NETDB_MIN_FLOODFILL_VERSION && GetIdentity ()->GetSigningKeyType () != SIGNING_KEY_TYPE_DSA_SHA1; } - bool RouterInfo::IsPeerTesting (bool v4) const - { - if (!(m_SupportedTransports & (v4 ? eSSUV4 : eSSUV6))) return false; - return (bool)GetAddress ( - [v4](std::shared_ptr address)->bool - { - return (address->transportStyle == eTransportSSU) && address->IsPeerTesting () && - ((v4 && address->IsV4 ()) || (!v4 && address->IsV6 ())) && address->IsReachableSSU (); - }); - } - bool RouterInfo::IsSSU2PeerTesting (bool v4) const { if (!(m_SupportedTransports & (v4 ? eSSU2V4 : eSSU2V6))) return false; @@ -1101,17 +922,6 @@ namespace data }); } - bool RouterInfo::IsIntroducer (bool v4) const - { - if (!(m_SupportedTransports & (v4 ? eSSUV4 : eSSUV6))) return false; - return (bool)GetAddress ( - [v4](std::shared_ptr address)->bool - { - return (address->transportStyle == eTransportSSU) && address->IsIntroducer () && - ((v4 && address->IsV4 ()) || (!v4 && address->IsV6 ())) && !address->host.is_unspecified (); - }); - } - bool RouterInfo::IsSSU2Introducer (bool v4) const { if (!(m_SupportedTransports & (v4 ? eSSU2V4 : eSSU2V6))) return false; @@ -1127,8 +937,7 @@ namespace data { for (auto& addr: *m_Addresses) { - // TODO: implement SSU - if (!addr->published && (addr->transportStyle == eTransportNTCP || addr->transportStyle == eTransportSSU2)) + if (!addr->published && (addr->transportStyle == eTransportNTCP2 || addr->transportStyle == eTransportSSU2)) { addr->caps &= ~(eV4 | eV6); addr->caps |= transports; @@ -1145,19 +954,13 @@ namespace data uint8_t transports = 0; switch (addr->transportStyle) { - case eTransportNTCP: + case eTransportNTCP2: if (addr->IsV4 ()) transports |= eNTCP2V4; if (addr->IsV6 ()) transports |= (i2p::util::net::IsYggdrasilAddress (addr->host) ? eNTCP2V6Mesh : eNTCP2V6); if (addr->IsPublishedNTCP2 ()) m_ReachableTransports |= transports; break; - case eTransportSSU: - if (addr->IsV4 ()) transports |= eSSUV4; - if (addr->IsV6 ()) transports |= eSSUV6; - if (addr->IsReachableSSU ()) - m_ReachableTransports |= transports; - break; case eTransportSSU2: if (addr->IsV4 ()) transports |= eSSU2V4; if (addr->IsV6 ()) transports |= eSSU2V6; @@ -1257,17 +1060,17 @@ namespace data const Address& address = *addr_ptr; // calculate cost uint8_t cost = 0x7f; - if (address.transportStyle == eTransportNTCP) + if (address.transportStyle == eTransportNTCP2) cost = address.published ? COST_NTCP2_PUBLISHED : COST_NTCP2_NON_PUBLISHED; - else if (address.transportStyle == eTransportSSU) - cost = address.published ? COST_SSU_DIRECT : COST_SSU_THROUGH_INTRODUCERS; else if (address.transportStyle == eTransportSSU2) cost = address.published ? COST_SSU2_DIRECT : COST_SSU2_NON_PUBLISHED; + else + continue; // skip unknown address s.write ((const char *)&cost, sizeof (cost)); s.write ((const char *)&address.date, sizeof (address.date)); std::stringstream properties; bool isPublished = false; - if (address.transportStyle == eTransportNTCP) + if (address.transportStyle == eTransportNTCP2) { if (address.IsNTCP2 ()) { @@ -1289,43 +1092,6 @@ namespace data else continue; // don't write NTCP address } - else if (address.transportStyle == eTransportSSU) - { - WriteString ("SSU", s); - // caps - WriteString ("caps", properties); - properties << '='; - std::string caps; - if (address.IsPeerTesting ()) caps += CAPS_FLAG_SSU_TESTING; - if (address.host.is_v4 ()) - { - if (address.published) - { - isPublished = true; - if (address.IsIntroducer ()) caps += CAPS_FLAG_SSU_INTRODUCER; - } - else - caps += CAPS_FLAG_V4; - } - else if (address.host.is_v6 ()) - { - if (address.published) - { - isPublished = true; - if (address.IsIntroducer ()) caps += CAPS_FLAG_SSU_INTRODUCER; - } - else - caps += CAPS_FLAG_V6; - } - else - { - if (address.IsV4 ()) caps += CAPS_FLAG_V4; - if (address.IsV6 ()) caps += CAPS_FLAG_V6; - if (caps.empty ()) caps += CAPS_FLAG_V4; - } - WriteString (caps, properties); - properties << ';'; - } else if (address.transportStyle == eTransportSSU2) { WriteString ("SSU2", s); @@ -1334,8 +1100,8 @@ namespace data if (address.published) { isPublished = true; - if (address.IsPeerTesting ()) caps += CAPS_FLAG_SSU_TESTING; - if (address.IsIntroducer ()) caps += CAPS_FLAG_SSU_INTRODUCER; + if (address.IsPeerTesting ()) caps += CAPS_FLAG_SSU2_TESTING; + if (address.IsIntroducer ()) caps += CAPS_FLAG_SSU2_INTRODUCER; } else { @@ -1368,7 +1134,7 @@ namespace data size_t len = address.IsSSU2 () ? 32 : 16; WriteString (address.i.ToBase64 (len), properties); properties << ';'; } - if (address.transportStyle == eTransportSSU || address.IsSSU2 ()) + if (address.transportStyle == eTransportSSU2) { // write introducers if any if (address.ssu && !address.ssu->introducers.empty()) @@ -1385,45 +1151,18 @@ namespace data } i++; } - if (address.transportStyle == eTransportSSU) - { - i = 0; - for (const auto& introducer: address.ssu->introducers) - { - WriteString ("ihost" + boost::lexical_cast(i), properties); - properties << '='; - WriteString (introducer.iHost.to_string (), properties); - properties << ';'; - i++; - } - } i = 0; for (const auto& introducer: address.ssu->introducers) { - if (address.IsSSU2 ()) - WriteString ("ih" + boost::lexical_cast(i), properties); - else - WriteString ("ikey" + boost::lexical_cast(i), properties); + WriteString ("ih" + boost::lexical_cast(i), properties); properties << '='; char value[64]; - size_t l = ByteStreamToBase64 (introducer.iKey, 32, value, 64); + size_t l = ByteStreamToBase64 (introducer.iH, 32, value, 64); value[l] = 0; WriteString (value, properties); properties << ';'; i++; } - if (address.transportStyle == eTransportSSU) - { - i = 0; - for (const auto& introducer: address.ssu->introducers) - { - WriteString ("iport" + boost::lexical_cast(i), properties); - properties << '='; - WriteString (boost::lexical_cast(introducer.iPort), properties); - properties << ';'; - i++; - } - } i = 0; for (const auto& introducer: address.ssu->introducers) { @@ -1435,18 +1174,8 @@ namespace data } } } - if (address.transportStyle == eTransportSSU) - { - // write intro key - WriteString ("key", properties); - properties << '='; - char value[64]; - size_t l = ByteStreamToBase64 (address.i, 32, value, 64); - value[l] = 0; - WriteString (value, properties); - properties << ';'; - } - if (address.transportStyle == eTransportSSU || address.IsSSU2 ()) + + if (address.transportStyle == eTransportSSU2) { // write mtu if (address.ssu && address.ssu->mtu) @@ -1457,7 +1186,7 @@ namespace data properties << ';'; } } - if ((isPublished || (address.ssu && !address.IsSSU2 ())) && address.port) + if (isPublished && address.port) { WriteString ("port", properties); properties << '='; @@ -1549,7 +1278,7 @@ namespace data if (addr->IsSSU2 () && ((v4 && addr->IsV4 ()) || (!v4 && addr->IsV6 ()))) { for (auto it = addr->ssu->introducers.begin (); it != addr->ssu->introducers.end (); ++it) - if (h == it->iKey) + if (h == it->iH) { addr->ssu->introducers.erase (it); if (addr->ssu->introducers.empty ()) diff --git a/libi2pd/RouterInfo.h b/libi2pd/RouterInfo.h index 8f76707c..6e122d95 100644 --- a/libi2pd/RouterInfo.h +++ b/libi2pd/RouterInfo.h @@ -47,14 +47,12 @@ namespace data const char CAPS_FLAG_V4 = '4'; const char CAPS_FLAG_V6 = '6'; - const char CAPS_FLAG_SSU_TESTING = 'B'; - const char CAPS_FLAG_SSU_INTRODUCER = 'C'; + const char CAPS_FLAG_SSU2_TESTING = 'B'; + const char CAPS_FLAG_SSU2_INTRODUCER = 'C'; const uint8_t COST_NTCP2_PUBLISHED = 3; const uint8_t COST_NTCP2_NON_PUBLISHED = 14; const uint8_t COST_SSU2_DIRECT = 8; - const uint8_t COST_SSU_DIRECT = 9; - const uint8_t COST_SSU_THROUGH_INTRODUCERS = 11; const uint8_t COST_SSU2_NON_PUBLISHED = 15; const size_t MAX_RI_BUFFER_SIZE = 3072; // if RouterInfo exceeds 3K we consider it as malformed, might extend later @@ -66,11 +64,9 @@ namespace data { eNTCP2V4 = 0x01, eNTCP2V6 = 0x02, - eSSUV4 = 0x04, - eSSUV6 = 0x08, + eSSU2V4 = 0x04, + eSSU2V6 = 0x08, eNTCP2V6Mesh = 0x10, - eSSU2V4 = 0x20, - eSSU2V6 = 0x40, eAllTransports = 0xFF }; typedef uint8_t CompatibleTransports; @@ -96,20 +92,17 @@ namespace data enum TransportStyle { eTransportUnknown = 0, - eTransportNTCP, - eTransportSSU, + eTransportNTCP2, eTransportSSU2 }; - typedef Tag<32> IntroKey; // should be castable to MacKey and AESKey struct Introducer { - Introducer (): iPort (0), iExp (0) {}; - boost::asio::ip::address iHost; - int iPort; - IntroKey iKey; // or ih for SSU2 + Introducer (): iTag (0), iExp (0), isH (false) {}; + IdentHash iH; uint32_t iTag; uint32_t iExp; + bool isH; // TODO: remove later }; struct SSUExt @@ -146,7 +139,7 @@ namespace data return !(*this == other); } - bool IsNTCP2 () const { return transportStyle == eTransportNTCP; }; + bool IsNTCP2 () const { return transportStyle == eTransportNTCP2; }; bool IsSSU2 () const { return transportStyle == eTransportSSU2; }; bool IsPublishedNTCP2 () const { return IsNTCP2 () && published; }; bool IsReachableSSU () const { return (bool)ssu && (published || UsesIntroducer ()); }; @@ -188,34 +181,27 @@ namespace data std::shared_ptr GetSSU2AddressWithStaticKey (const uint8_t * key, bool isV6) const; std::shared_ptr GetPublishedNTCP2V4Address () const; std::shared_ptr GetPublishedNTCP2V6Address () const; - std::shared_ptr GetSSUAddress (bool v4only = true) const; - std::shared_ptr GetSSUV6Address () const; std::shared_ptr GetYggdrasilAddress () const; std::shared_ptr GetSSU2V4Address () const; std::shared_ptr GetSSU2V6Address () const; std::shared_ptr GetSSU2Address (bool v4) const; - void AddSSUAddress (const char * host, int port, const uint8_t * key, int mtu = 0); void AddNTCP2Address (const uint8_t * staticKey, const uint8_t * iv, const boost::asio::ip::address& host = boost::asio::ip::address(), int port = 0, uint8_t caps = 0); void AddSSU2Address (const uint8_t * staticKey, const uint8_t * introKey, uint8_t caps = 0); // non published void AddSSU2Address (const uint8_t * staticKey, const uint8_t * introKey, const boost::asio::ip::address& host, int port); // published - bool AddIntroducer (const Introducer& introducer); - bool RemoveIntroducer (const boost::asio::ip::udp::endpoint& e); void SetUnreachableAddressesTransportCaps (uint8_t transports); // bitmask of AddressCaps void UpdateSupportedTransports (); bool IsFloodfill () const { return m_Caps & Caps::eFloodfill; }; bool IsReachable () const { return m_Caps & Caps::eReachable; }; bool IsECIES () const { return m_RouterIdentity->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD; }; - bool IsSSU (bool v4only = true) const; - bool IsSSUV6 () const { return m_SupportedTransports & eSSUV6; }; bool IsNTCP2 (bool v4only = true) const; bool IsNTCP2V6 () const { return m_SupportedTransports & eNTCP2V6; }; bool IsSSU2V4 () const { return m_SupportedTransports & eSSU2V4; }; bool IsSSU2V6 () const { return m_SupportedTransports & eSSU2V6; }; - bool IsV6 () const { return m_SupportedTransports & (eSSUV6 | eNTCP2V6 | eSSU2V6); }; - bool IsV4 () const { return m_SupportedTransports & (eSSUV4 | eNTCP2V4 | eSSU2V4); }; + bool IsV6 () const { return m_SupportedTransports & (eNTCP2V6 | eSSU2V6); }; + bool IsV4 () const { return m_SupportedTransports & (eNTCP2V4 | eSSU2V4); }; bool IsMesh () const { return m_SupportedTransports & eNTCP2V6Mesh; }; void EnableV6 (); void DisableV6 (); @@ -232,9 +218,7 @@ namespace data bool IsHighBandwidth () const { return m_Caps & RouterInfo::eHighBandwidth; }; bool IsExtraBandwidth () const { return m_Caps & RouterInfo::eExtraBandwidth; }; bool IsEligibleFloodfill () const; - bool IsPeerTesting (bool v4) const; bool IsSSU2PeerTesting (bool v4) const; - bool IsIntroducer (bool v4) const; bool IsSSU2Introducer (bool v4) const; uint8_t GetCaps () const { return m_Caps; }; @@ -298,7 +282,7 @@ namespace data std::shared_ptr m_Buffer; size_t m_BufferLen; uint64_t m_Timestamp; - boost::shared_ptr m_Addresses; // TODO: use std::shared_ptr and std::atomic_store for gcc >= 4.9 + boost::shared_ptr m_Addresses, m_NewAddresses; // TODO: use std::shared_ptr and std::atomic_store for gcc >= 4.9 bool m_IsUpdated, m_IsUnreachable; CompatibleTransports m_SupportedTransports, m_ReachableTransports; uint8_t m_Caps; @@ -311,6 +295,7 @@ namespace data public: LocalRouterInfo () = default; + LocalRouterInfo (const std::string& fullPath): RouterInfo (fullPath) {}; void CreateBuffer (const PrivateKeys& privateKeys); void UpdateCaps (uint8_t caps); diff --git a/libi2pd/SSU.cpp b/libi2pd/SSU.cpp deleted file mode 100644 index eec55857..00000000 --- a/libi2pd/SSU.cpp +++ /dev/null @@ -1,996 +0,0 @@ -/* -* Copyright (c) 2013-2022, The PurpleI2P Project -* -* This file is part of Purple i2pd project and licensed under BSD3 -* -* See full license text in LICENSE file at top of project tree -*/ - -#include -#include "Log.h" -#include "Timestamp.h" -#include "RouterContext.h" -#include "NetDb.hpp" -#include "Config.h" -#include "util.h" -#include "SSU.h" - -#if defined(__linux__) && !defined(_NETINET_IN_H) - #include -#endif - -#ifdef _WIN32 -#include -#endif - -namespace i2p -{ -namespace transport -{ - SSUServer::SSUServer (int port): - m_IsRunning(false), m_Thread (nullptr), - m_ReceiversThread (nullptr), m_ReceiversThreadV6 (nullptr), m_Work (m_Service), - m_ReceiversWork (m_ReceiversService), m_ReceiversWorkV6 (m_ReceiversServiceV6), - m_Endpoint (boost::asio::ip::udp::v4 (), port), m_EndpointV6 (boost::asio::ip::udp::v6 (), port), - m_Socket (m_ReceiversService), m_SocketV6 (m_ReceiversServiceV6), - m_IntroducersUpdateTimer (m_Service), m_IntroducersUpdateTimerV6 (m_Service), - m_PeerTestsCleanupTimer (m_Service), m_TerminationTimer (m_Service), m_TerminationTimerV6 (m_Service), - m_IsSyncClockFromPeers (true) - { - } - - SSUServer::~SSUServer () - { - } - - void SSUServer::OpenSocket () - { - try - { - m_Socket.open (boost::asio::ip::udp::v4()); - m_Socket.set_option (boost::asio::socket_base::receive_buffer_size (SSU_SOCKET_RECEIVE_BUFFER_SIZE)); - m_Socket.set_option (boost::asio::socket_base::send_buffer_size (SSU_SOCKET_SEND_BUFFER_SIZE)); - m_Socket.bind (m_Endpoint); - LogPrint (eLogInfo, "SSU: Start listening v4 port ", m_Endpoint.port()); - } - catch ( std::exception & ex ) - { - LogPrint (eLogError, "SSU: Failed to bind to v4 port ", m_Endpoint.port(), ": ", ex.what()); - ThrowFatal ("Unable to start IPv4 SSU transport at port ", m_Endpoint.port(), ": ", ex.what ()); - } - } - - void SSUServer::OpenSocketV6 () - { - try - { - m_SocketV6.open (boost::asio::ip::udp::v6()); - m_SocketV6.set_option (boost::asio::ip::v6_only (true)); - m_SocketV6.set_option (boost::asio::socket_base::receive_buffer_size (SSU_SOCKET_RECEIVE_BUFFER_SIZE)); - m_SocketV6.set_option (boost::asio::socket_base::send_buffer_size (SSU_SOCKET_SEND_BUFFER_SIZE)); -#if defined(__linux__) && !defined(_NETINET_IN_H) - if (m_EndpointV6.address() == boost::asio::ip::address().from_string("::")) // only if not binded to address - { - // Set preference to use public IPv6 address -- tested on linux, not works on windows, and not tested on others -#if (BOOST_VERSION >= 105500) - typedef boost::asio::detail::socket_option::integer ipv6PreferAddr; -#else - typedef boost::asio::detail::socket_option::integer ipv6PreferAddr; -#endif - m_SocketV6.set_option (ipv6PreferAddr(IPV6_PREFER_SRC_PUBLIC | IPV6_PREFER_SRC_HOME | IPV6_PREFER_SRC_NONCGA)); - } -#endif - m_SocketV6.bind (m_EndpointV6); - LogPrint (eLogInfo, "SSU: Start listening v6 port ", m_EndpointV6.port()); - } - catch ( std::exception & ex ) - { - LogPrint (eLogError, "SSU: Failed to bind to v6 port ", m_EndpointV6.port(), ": ", ex.what()); - ThrowFatal ("Unable to start IPv6 SSU transport at port ", m_Endpoint.port(), ": ", ex.what ()); - } - } - - void SSUServer::Start () - { - i2p::config::GetOption("nettime.frompeers", m_IsSyncClockFromPeers); - m_IsRunning = true; - m_Thread = new std::thread (std::bind (&SSUServer::Run, this)); - if (context.SupportsV4 ()) - { - OpenSocket (); - m_ReceiversThread = new std::thread (std::bind (&SSUServer::RunReceivers, this)); - m_ReceiversService.post (std::bind (&SSUServer::Receive, this)); - ScheduleTermination (); - ScheduleIntroducersUpdateTimer (); // wait for 30 seconds and decide if we need introducers - } - if (context.SupportsV6 ()) - { - OpenSocketV6 (); - m_ReceiversThreadV6 = new std::thread (std::bind (&SSUServer::RunReceiversV6, this)); - m_ReceiversServiceV6.post (std::bind (&SSUServer::ReceiveV6, this)); - ScheduleTerminationV6 (); - ScheduleIntroducersUpdateTimerV6 (); // wait for 30 seconds and decide if we need introducers - } - SchedulePeerTestsCleanupTimer (); - } - - void SSUServer::Stop () - { - DeleteAllSessions (); - m_IsRunning = false; - m_TerminationTimer.cancel (); - m_TerminationTimerV6.cancel (); - m_IntroducersUpdateTimer.cancel (); - m_IntroducersUpdateTimerV6.cancel (); - m_Service.stop (); - m_Socket.close (); - m_SocketV6.close (); - m_ReceiversService.stop (); - m_ReceiversServiceV6.stop (); - if (m_ReceiversThread) - { - m_ReceiversThread->join (); - delete m_ReceiversThread; - m_ReceiversThread = nullptr; - } - if (m_ReceiversThreadV6) - { - m_ReceiversThreadV6->join (); - delete m_ReceiversThreadV6; - m_ReceiversThreadV6 = nullptr; - } - if (m_Thread) - { - m_Thread->join (); - delete m_Thread; - m_Thread = nullptr; - } - } - - void SSUServer::Run () - { - i2p::util::SetThreadName("SSU"); - - while (m_IsRunning) - { - try - { - m_Service.run (); - } - catch (std::exception& ex) - { - LogPrint (eLogError, "SSU: Server runtime exception: ", ex.what ()); - } - } - } - - void SSUServer::RunReceivers () - { - i2p::util::SetThreadName("SSUv4"); - - while (m_IsRunning) - { - try - { - m_ReceiversService.run (); - } - catch (std::exception& ex) - { - LogPrint (eLogError, "SSU: Receivers runtime exception: ", ex.what ()); - if (m_IsRunning) - { - // restart socket - m_Socket.close (); - OpenSocket (); - Receive (); - } - } - } - } - - void SSUServer::RunReceiversV6 () - { - i2p::util::SetThreadName("SSUv6"); - - while (m_IsRunning) - { - try - { - m_ReceiversServiceV6.run (); - } - catch (std::exception& ex) - { - LogPrint (eLogError, "SSU: v6 receivers runtime exception: ", ex.what ()); - if (m_IsRunning) - { - m_SocketV6.close (); - OpenSocketV6 (); - ReceiveV6 (); - } - } - } - } - - void SSUServer::SetLocalAddress (const boost::asio::ip::address& localAddress) - { - if (localAddress.is_v6 ()) - m_EndpointV6.address (localAddress); - else if (localAddress.is_v4 ()) - m_Endpoint.address (localAddress); - } - - void SSUServer::AddRelay (uint32_t tag, std::shared_ptr relay) - { - m_Relays.emplace (tag, relay); - } - - void SSUServer::RemoveRelay (uint32_t tag) - { - m_Relays.erase (tag); - } - - std::shared_ptr SSUServer::FindRelaySession (uint32_t tag) - { - auto it = m_Relays.find (tag); - if (it != m_Relays.end ()) - { - if (it->second->GetState () == eSessionStateEstablished) - return it->second; - else - m_Relays.erase (it); - } - return nullptr; - } - - void SSUServer::Send (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& to) - { - boost::system::error_code ec; - if (to.protocol () == boost::asio::ip::udp::v4()) - m_Socket.send_to (boost::asio::buffer (buf, len), to, 0, ec); - else - m_SocketV6.send_to (boost::asio::buffer (buf, len), to, 0, ec); - - if (ec) - { - LogPrint (eLogError, "SSU: Send exception: ", ec.message (), " while trying to send data to ", to.address (), ":", to.port (), " (length: ", len, ")"); - } - } - - void SSUServer::Receive () - { - SSUPacket * packet = m_PacketsPool.AcquireMt (); - m_Socket.async_receive_from (boost::asio::buffer (packet->buf, SSU_MTU_V4), packet->from, - std::bind (&SSUServer::HandleReceivedFrom, this, std::placeholders::_1, std::placeholders::_2, packet)); - } - - void SSUServer::ReceiveV6 () - { - SSUPacket * packet = m_PacketsPool.AcquireMt (); - m_SocketV6.async_receive_from (boost::asio::buffer (packet->buf, SSU_MTU_V6), packet->from, - std::bind (&SSUServer::HandleReceivedFromV6, this, std::placeholders::_1, std::placeholders::_2, packet)); - } - - void SSUServer::HandleReceivedFrom (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet) - { - if (!ecode - || ecode == boost::asio::error::connection_refused - || ecode == boost::asio::error::connection_reset - || ecode == boost::asio::error::network_unreachable - || ecode == boost::asio::error::host_unreachable -#ifdef _WIN32 // windows can throw WinAPI error, which is not handled by ASIO - || ecode.value() == boost::winapi::ERROR_CONNECTION_REFUSED_ - || ecode.value() == boost::winapi::ERROR_NETWORK_UNREACHABLE_ - || ecode.value() == boost::winapi::ERROR_HOST_UNREACHABLE_ -#endif - ) - // just try continue reading when received ICMP response otherwise socket can crash, - // but better to find out which host were sent it and mark that router as unreachable - { - packet->len = bytes_transferred; - std::vector packets; - packets.push_back (packet); - - boost::system::error_code ec; - size_t moreBytes = m_Socket.available(ec); - if (!ec) - { - while (moreBytes && packets.size () < 25) - { - packet = m_PacketsPool.AcquireMt (); - packet->len = m_Socket.receive_from (boost::asio::buffer (packet->buf, SSU_MTU_V4), packet->from, 0, ec); - if (!ec) - { - packets.push_back (packet); - moreBytes = m_Socket.available(ec); - if (ec) break; - } - else - { - LogPrint (eLogError, "SSU: receive_from error: code ", ec.value(), ": ", ec.message ()); - m_PacketsPool.ReleaseMt (packet); - break; - } - } - } - - m_Service.post (std::bind (&SSUServer::HandleReceivedPackets, this, packets, &m_Sessions)); - Receive (); - } - else - { - m_PacketsPool.ReleaseMt (packet); - if (ecode != boost::asio::error::operation_aborted) - { - LogPrint (eLogError, "SSU: Receive error: code ", ecode.value(), ": ", ecode.message ()); - m_Socket.close (); - OpenSocket (); - Receive (); - } - } - } - - void SSUServer::HandleReceivedFromV6 (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet) - { - if (!ecode - || ecode == boost::asio::error::connection_refused - || ecode == boost::asio::error::connection_reset - || ecode == boost::asio::error::network_unreachable - || ecode == boost::asio::error::host_unreachable -#ifdef _WIN32 // windows can throw WinAPI error, which is not handled by ASIO - || ecode.value() == boost::winapi::ERROR_CONNECTION_REFUSED_ - || ecode.value() == boost::winapi::ERROR_NETWORK_UNREACHABLE_ - || ecode.value() == boost::winapi::ERROR_HOST_UNREACHABLE_ -#endif - ) - // just try continue reading when received ICMP response otherwise socket can crash, - // but better to find out which host were sent it and mark that router as unreachable - { - packet->len = bytes_transferred; - std::vector packets; - packets.push_back (packet); - - boost::system::error_code ec; - size_t moreBytes = m_SocketV6.available (ec); - if (!ec) - { - while (moreBytes && packets.size () < 25) - { - packet = m_PacketsPool.AcquireMt (); - packet->len = m_SocketV6.receive_from (boost::asio::buffer (packet->buf, SSU_MTU_V6), packet->from, 0, ec); - if (!ec) - { - packets.push_back (packet); - moreBytes = m_SocketV6.available(ec); - if (ec) break; - } - else - { - LogPrint (eLogError, "SSU: v6 receive_from error: code ", ec.value(), ": ", ec.message ()); - m_PacketsPool.ReleaseMt (packet);; - break; - } - } - } - - m_Service.post (std::bind (&SSUServer::HandleReceivedPackets, this, packets, &m_SessionsV6)); - ReceiveV6 (); - } - else - { - m_PacketsPool.ReleaseMt (packet); - if (ecode != boost::asio::error::operation_aborted) - { - LogPrint (eLogError, "SSU: v6 receive error: code ", ecode.value(), ": ", ecode.message ()); - m_SocketV6.close (); - OpenSocketV6 (); - ReceiveV6 (); - } - } - } - - void SSUServer::HandleReceivedPackets (std::vector packets, - std::map > * sessions) - { - if (!m_IsRunning) return; - std::shared_ptr session; - for (auto& packet: packets) - { - try - { - if (!session || session->GetRemoteEndpoint () != packet->from) // we received packet for other session than previous - { - if (session) - { - session->FlushData (); - session = nullptr; - } - auto it = sessions->find (packet->from); - if (it != sessions->end ()) - session = it->second; - if (!session && packet->len > 0) - { - session = std::make_shared (*this, packet->from); - session->WaitForConnect (); - (*sessions)[packet->from] = session; - LogPrint (eLogDebug, "SSU: New session from ", packet->from.address ().to_string (), ":", packet->from.port (), " created"); - } - } - if (session) - session->ProcessNextMessage (packet->buf, packet->len, packet->from); - } - catch (std::exception& ex) - { - LogPrint (eLogError, "SSU: HandleReceivedPackets ", ex.what ()); - if (session) session->FlushData (); - session = nullptr; - } - } - m_PacketsPool.ReleaseMt (packets); - if (session) session->FlushData (); - } - - std::shared_ptr SSUServer::FindSession (const boost::asio::ip::udp::endpoint& e) const - { - auto& sessions = e.address ().is_v6 () ? m_SessionsV6 : m_Sessions; - auto it = sessions.find (e); - if (it != sessions.end ()) - return it->second; - else - return nullptr; - } - - bool SSUServer::CreateSession (std::shared_ptr router, bool peerTest, bool v4only) - { - auto address = router->GetSSUAddress (v4only || !context.SupportsV6 ()); - if (address) - return CreateSession (router, address, peerTest); - else - LogPrint (eLogWarning, "SSU: Router ", i2p::data::GetIdentHashAbbreviation (router->GetIdentHash ()), " doesn't have SSU address"); - return false; - } - - bool SSUServer::CreateSession (std::shared_ptr router, - std::shared_ptr address, bool peerTest) - { - if (router && address) - { - if (address->UsesIntroducer ()) - m_Service.post (std::bind (&SSUServer::CreateSessionThroughIntroducer, this, router, address, peerTest)); // always V4 thread - else - { - if (address->host.is_unspecified () || !address->port) return false; - boost::asio::ip::udp::endpoint remoteEndpoint (address->host, address->port); - m_Service.post (std::bind (&SSUServer::CreateDirectSession, this, router, remoteEndpoint, peerTest)); - } - } - else - return false; - return true; - } - - void SSUServer::CreateDirectSession (std::shared_ptr router, boost::asio::ip::udp::endpoint remoteEndpoint, bool peerTest) - { - auto& sessions = remoteEndpoint.address ().is_v6 () ? m_SessionsV6 : m_Sessions; - auto it = sessions.find (remoteEndpoint); - if (it != sessions.end ()) - { - auto session = it->second; - if (peerTest && session->GetState () == eSessionStateEstablished) - session->SendPeerTest (); - } - else - { - // otherwise create new session - auto session = std::make_shared (*this, remoteEndpoint, router, peerTest); - sessions[remoteEndpoint] = session; - - // connect - LogPrint (eLogDebug, "SSU: Creating new session to [", i2p::data::GetIdentHashAbbreviation (router->GetIdentHash ()), "] ", - remoteEndpoint.address ().to_string (), ":", remoteEndpoint.port ()); - session->Connect (); - } - } - - void SSUServer::CreateSessionThroughIntroducer (std::shared_ptr router, - std::shared_ptr address, bool peerTest) - { - if (router && address && address->UsesIntroducer ()) - { - if (address->IsV4 () && !i2p::context.SupportsV4 ()) return; - if (address->IsV6 () && !i2p::context.SupportsV6 ()) return; - if (!address->host.is_unspecified () && address->port) - { - // we rarely come here - auto& sessions = address->host.is_v6 () ? m_SessionsV6 : m_Sessions; - boost::asio::ip::udp::endpoint remoteEndpoint (address->host, address->port); - auto it = sessions.find (remoteEndpoint); - // check if session is presented already - if (it != sessions.end ()) - { - auto session = it->second; - if (peerTest && session->GetState () == eSessionStateEstablished) - session->SendPeerTest (); - return; - } - } - // create new session - int numIntroducers = address->ssu->introducers.size (); - if (numIntroducers > 0) - { - uint32_t ts = i2p::util::GetSecondsSinceEpoch (); - std::shared_ptr introducerSession; - const i2p::data::RouterInfo::Introducer * introducer = nullptr; - // we might have a session to introducer already - auto offset = rand (); - for (int i = 0; i < numIntroducers; i++) - { - auto intr = &(address->ssu->introducers[(offset + i)%numIntroducers]); - if (!intr->iPort) continue; // skip invalid introducer - if (intr->iExp > 0 && ts > intr->iExp) continue; // skip expired introducer - boost::asio::ip::udp::endpoint ep (intr->iHost, intr->iPort); - if (ep.address ().is_v4 () && address->IsV4 ()) // ipv4 - { - if (!introducer) introducer = intr; - auto it = m_Sessions.find (ep); - if (it != m_Sessions.end ()) - { - introducerSession = it->second; - break; - } - } - if (ep.address ().is_v6 () && address->IsV6 ()) // ipv6 - { - if (!introducer) introducer = intr; - auto it = m_SessionsV6.find (ep); - if (it != m_SessionsV6.end ()) - { - introducerSession = it->second; - break; - } - } - } - if (!introducer) - { - LogPrint (eLogWarning, "SSU: Can't connect to unreachable router and no compatibe non-expired introducers presented"); - return; - } - - if (introducerSession) // session found - LogPrint (eLogWarning, "SSU: Session to introducer already exists"); - else // create new - { - LogPrint (eLogDebug, "SSU: Creating new session to introducer ", introducer->iHost); - boost::asio::ip::udp::endpoint introducerEndpoint (introducer->iHost, introducer->iPort); - introducerSession = std::make_shared (*this, introducerEndpoint, router); - if (introducerEndpoint.address ().is_v4 ()) - m_Sessions[introducerEndpoint] = introducerSession; - else if (introducerEndpoint.address ().is_v6 ()) - m_SessionsV6[introducerEndpoint] = introducerSession; - } - if (!address->host.is_unspecified () && address->port) - { - // create session - boost::asio::ip::udp::endpoint remoteEndpoint (address->host, address->port); - auto session = std::make_shared (*this, remoteEndpoint, router, peerTest); - if (address->host.is_v4 ()) - m_Sessions[remoteEndpoint] = session; - else if (address->host.is_v6 ()) - m_SessionsV6[remoteEndpoint] = session; - - // introduce - LogPrint (eLogInfo, "SSU: Introduce new session to [", i2p::data::GetIdentHashAbbreviation (router->GetIdentHash ()), - "] through introducer ", introducer->iHost, ":", introducer->iPort); - session->WaitForIntroduction (); - if ((address->host.is_v4 () && i2p::context.GetStatus () == eRouterStatusFirewalled) || - (address->host.is_v6 () && i2p::context.GetStatusV6 () == eRouterStatusFirewalled)) - { - uint8_t buf[1]; - Send (buf, 0, remoteEndpoint); // send HolePunch - } - } - introducerSession->Introduce (*introducer, router); - } - else - LogPrint (eLogWarning, "SSU: Can't connect to unreachable router and no introducers present"); - } - } - - void SSUServer::DeleteSession (std::shared_ptr session) - { - if (session) - { - session->Close (); - auto& ep = session->GetRemoteEndpoint (); - if (ep.address ().is_v6 ()) - m_SessionsV6.erase (ep); - else - m_Sessions.erase (ep); - } - } - - void SSUServer::DeleteAllSessions () - { - for (auto& it: m_Sessions) - it.second->Close (); - m_Sessions.clear (); - - for (auto& it: m_SessionsV6) - it.second->Close (); - m_SessionsV6.clear (); - } - - template - std::shared_ptr SSUServer::GetRandomV4Session (Filter filter) // v4 only - { - std::vector > filteredSessions; - for (const auto& s :m_Sessions) - if (filter (s.second)) filteredSessions.push_back (s.second); - if (filteredSessions.size () > 0) - { - auto ind = rand () % filteredSessions.size (); - return filteredSessions[ind]; - } - return nullptr; - } - - std::shared_ptr SSUServer::GetRandomEstablishedV4Session (std::shared_ptr excluded) // v4 only - { - return GetRandomV4Session ( - [excluded](std::shared_ptr session)->bool - { - return session->GetState () == eSessionStateEstablished && session != excluded; - } - ); - } - - template - std::shared_ptr SSUServer::GetRandomV6Session (Filter filter) // v6 only - { - std::vector > filteredSessions; - for (const auto& s :m_SessionsV6) - if (filter (s.second)) filteredSessions.push_back (s.second); - if (filteredSessions.size () > 0) - { - auto ind = rand () % filteredSessions.size (); - return filteredSessions[ind]; - } - return nullptr; - } - - std::shared_ptr SSUServer::GetRandomEstablishedV6Session (std::shared_ptr excluded) // v6 only - { - return GetRandomV6Session ( - [excluded](std::shared_ptr session)->bool - { - return session->GetState () == eSessionStateEstablished && session != excluded; - } - ); - } - - std::list > SSUServer::FindIntroducers (int maxNumIntroducers, - bool v4, std::set& excluded) - { - uint32_t ts = i2p::util::GetSecondsSinceEpoch (); - std::list > ret; - const auto& sessions = v4 ? m_Sessions : m_SessionsV6; - for (const auto& s : sessions) - { - if (s.second->GetRelayTag () && s.second->GetState () == eSessionStateEstablished && - ts < s.second->GetCreationTime () + SSU_TO_INTRODUCER_SESSION_EXPIRATION) - ret.push_back (s.second); - else if (s.second->GetRemoteIdentity ()) - excluded.insert (s.second->GetRemoteIdentity ()->GetIdentHash ()); - } - if ((int)ret.size () > maxNumIntroducers) - { - // shink ret randomly - int sz = ret.size () - maxNumIntroducers; - for (int i = 0; i < sz; i++) - { - auto ind = rand () % ret.size (); - auto it = ret.begin (); - std::advance (it, ind); - ret.erase (it); - } - } - return ret; - } - - void SSUServer::RescheduleIntroducersUpdateTimer () - { - m_IntroducersUpdateTimer.cancel (); - m_IntroducersUpdateTimer.expires_from_now (boost::posix_time::seconds(SSU_KEEP_ALIVE_INTERVAL/2)); - m_IntroducersUpdateTimer.async_wait (std::bind (&SSUServer::HandleIntroducersUpdateTimer, - this, std::placeholders::_1, true)); - } - - void SSUServer::ScheduleIntroducersUpdateTimer () - { - m_IntroducersUpdateTimer.expires_from_now (boost::posix_time::seconds(SSU_KEEP_ALIVE_INTERVAL)); - m_IntroducersUpdateTimer.async_wait (std::bind (&SSUServer::HandleIntroducersUpdateTimer, - this, std::placeholders::_1, true)); - } - - void SSUServer::RescheduleIntroducersUpdateTimerV6 () - { - m_IntroducersUpdateTimerV6.cancel (); - m_IntroducersUpdateTimerV6.expires_from_now (boost::posix_time::seconds(SSU_KEEP_ALIVE_INTERVAL/2)); - m_IntroducersUpdateTimerV6.async_wait (std::bind (&SSUServer::HandleIntroducersUpdateTimer, - this, std::placeholders::_1, false)); - } - - void SSUServer::ScheduleIntroducersUpdateTimerV6 () - { - m_IntroducersUpdateTimerV6.expires_from_now (boost::posix_time::seconds(SSU_KEEP_ALIVE_INTERVAL)); - m_IntroducersUpdateTimerV6.async_wait (std::bind (&SSUServer::HandleIntroducersUpdateTimer, - this, std::placeholders::_1, false)); - } - - void SSUServer::HandleIntroducersUpdateTimer (const boost::system::error_code& ecode, bool v4) - { - if (ecode != boost::asio::error::operation_aborted) - { - // timeout expired - if (v4) - { - if (i2p::context.GetStatus () == eRouterStatusTesting) - { - // we still don't know if we need introducers - ScheduleIntroducersUpdateTimer (); - return; - } - if (i2p::context.GetStatus () != eRouterStatusFirewalled) - { - // we don't need introducers - m_Introducers.clear (); - return; - } - // we are firewalled - if (!i2p::context.IsUnreachable ()) i2p::context.SetUnreachable (true, false); // v4 - } - else - { - if (i2p::context.GetStatusV6 () == eRouterStatusTesting) - { - // we still don't know if we need introducers - ScheduleIntroducersUpdateTimerV6 (); - return; - } - if (i2p::context.GetStatusV6 () != eRouterStatusFirewalled) - { - // we don't need introducers - m_IntroducersV6.clear (); - return; - } - // we are firewalled - auto addr = i2p::context.GetRouterInfo ().GetSSUV6Address (); - if (addr && addr->ssu && addr->ssu->introducers.empty ()) - i2p::context.SetUnreachable (false, true); // v6 - } - - std::list newList; - size_t numIntroducers = 0; - uint32_t ts = i2p::util::GetSecondsSinceEpoch (); - std::set excluded; - auto& introducers = v4 ? m_Introducers : m_IntroducersV6; - for (const auto& it : introducers) - { - auto session = FindSession (it); - if (session) - { - if (ts < session->GetCreationTime () + SSU_TO_INTRODUCER_SESSION_EXPIRATION) - session->SendKeepAlive (); - if (ts < session->GetCreationTime () + SSU_TO_INTRODUCER_SESSION_DURATION) - { - newList.push_back (it); - numIntroducers++; - if (session->GetRemoteIdentity ()) - excluded.insert (session->GetRemoteIdentity ()->GetIdentHash ()); - } - else - session = nullptr; - } - if (!session) - i2p::context.RemoveIntroducer (it); - } - if (numIntroducers < SSU_MAX_NUM_INTRODUCERS) - { - // create new - auto sessions = FindIntroducers (SSU_MAX_NUM_INTRODUCERS, v4, excluded); // try to find if duplicates - if (sessions.empty () && !introducers.empty ()) - { - // bump creation time for previous introducers if no new sessions found - LogPrint (eLogDebug, "SSU: No new introducers found. Trying to reuse existing"); - for (const auto& it : introducers) - { - auto session = FindSession (it); - if (session) - session->SetCreationTime (session->GetCreationTime () + SSU_TO_INTRODUCER_SESSION_DURATION); - } - // try again - excluded.clear (); - sessions = FindIntroducers (SSU_MAX_NUM_INTRODUCERS, v4, excluded); - } - for (const auto& it1: sessions) - { - const auto& ep = it1->GetRemoteEndpoint (); - i2p::data::RouterInfo::Introducer introducer; - introducer.iHost = ep.address (); - introducer.iPort = ep.port (); - introducer.iTag = it1->GetRelayTag (); - introducer.iKey = it1->GetIntroKey (); - introducer.iExp = it1->GetCreationTime () + SSU_TO_INTRODUCER_SESSION_EXPIRATION; - if (i2p::context.AddIntroducer (introducer)) - { - newList.push_back (ep); - if (newList.size () >= SSU_MAX_NUM_INTRODUCERS) break; - } - if (it1->GetRemoteIdentity ()) - excluded.insert (it1->GetRemoteIdentity ()->GetIdentHash ()); - } - } - introducers = newList; - if (introducers.size () < SSU_MAX_NUM_INTRODUCERS) - { - for (auto i = introducers.size (); i < SSU_MAX_NUM_INTRODUCERS; i++) - { - auto introducer = i2p::data::netdb.GetRandomIntroducer (v4, excluded); - if (introducer) - { - auto address = v4 ? introducer->GetSSUAddress (true) : introducer->GetSSUV6Address (); - if (address && !address->host.is_unspecified () && address->port) - { - boost::asio::ip::udp::endpoint ep (address->host, address->port); - if (std::find (introducers.begin (), introducers.end (), ep) == introducers.end ()) // not connected yet - { - CreateDirectSession (introducer, ep, false); - excluded.insert (introducer->GetIdentHash ()); - } - } - } - else - { - LogPrint (eLogDebug, "SSU: Can't find more introducers"); - break; - } - } - } - if (v4) - ScheduleIntroducersUpdateTimer (); - else - ScheduleIntroducersUpdateTimerV6 (); - } - } - - void SSUServer::NewPeerTest (uint32_t nonce, PeerTestParticipant role, std::shared_ptr session) - { - m_PeerTests[nonce] = { i2p::util::GetMillisecondsSinceEpoch (), role, session }; - } - - PeerTestParticipant SSUServer::GetPeerTestParticipant (uint32_t nonce) - { - auto it = m_PeerTests.find (nonce); - if (it != m_PeerTests.end ()) - return it->second.role; - else - return ePeerTestParticipantUnknown; - } - - std::shared_ptr SSUServer::GetPeerTestSession (uint32_t nonce) - { - auto it = m_PeerTests.find (nonce); - if (it != m_PeerTests.end ()) - return it->second.session; - else - return nullptr; - } - - void SSUServer::UpdatePeerTest (uint32_t nonce, PeerTestParticipant role) - { - auto it = m_PeerTests.find (nonce); - if (it != m_PeerTests.end ()) - it->second.role = role; - } - - void SSUServer::RemovePeerTest (uint32_t nonce) - { - m_PeerTests.erase (nonce); - } - - void SSUServer::SchedulePeerTestsCleanupTimer () - { - m_PeerTestsCleanupTimer.expires_from_now (boost::posix_time::seconds(SSU_PEER_TEST_TIMEOUT)); - m_PeerTestsCleanupTimer.async_wait (std::bind (&SSUServer::HandlePeerTestsCleanupTimer, - this, std::placeholders::_1)); - } - - void SSUServer::HandlePeerTestsCleanupTimer (const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - int numDeleted = 0; - uint64_t ts = i2p::util::GetMillisecondsSinceEpoch (); - for (auto it = m_PeerTests.begin (); it != m_PeerTests.end ();) - { - if (ts > it->second.creationTime + SSU_PEER_TEST_TIMEOUT*1000LL) - { - numDeleted++; - it = m_PeerTests.erase (it); - } - else - ++it; - } - if (numDeleted > 0) - LogPrint (eLogDebug, "SSU: ", numDeleted, " peer tests have been expired"); - // some cleaups. TODO: use separate timer - m_FragmentsPool.CleanUp (); - m_IncompleteMessagesPool.CleanUp (); - m_SentMessagesPool.CleanUp (); - - SchedulePeerTestsCleanupTimer (); - } - } - - void SSUServer::ScheduleTermination () - { - uint64_t timeout = SSU_TERMINATION_CHECK_TIMEOUT + (rand () % SSU_TERMINATION_CHECK_TIMEOUT)/5; - m_TerminationTimer.expires_from_now (boost::posix_time::seconds(timeout)); - m_TerminationTimer.async_wait (std::bind (&SSUServer::HandleTerminationTimer, - this, std::placeholders::_1)); - } - - void SSUServer::HandleTerminationTimer (const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - auto ts = i2p::util::GetSecondsSinceEpoch (); - for (auto& it: m_Sessions) - if (it.second->IsTerminationTimeoutExpired (ts)) - { - auto session = it.second; - if (it.first != session->GetRemoteEndpoint ()) - LogPrint (eLogWarning, "SSU: Remote endpoint ", session->GetRemoteEndpoint (), " doesn't match key ", it.first, " adjusted"); - m_Service.post ([session] - { - LogPrint (eLogWarning, "SSU: No activity with ", session->GetRemoteEndpoint (), " for ", session->GetTerminationTimeout (), " seconds"); - session->Failed (); - }); - } - else - it.second->CleanUp (ts); - ScheduleTermination (); - } - } - - void SSUServer::ScheduleTerminationV6 () - { - uint64_t timeout = SSU_TERMINATION_CHECK_TIMEOUT + (rand () % SSU_TERMINATION_CHECK_TIMEOUT)/5; - m_TerminationTimerV6.expires_from_now (boost::posix_time::seconds(timeout)); - m_TerminationTimerV6.async_wait (std::bind (&SSUServer::HandleTerminationTimerV6, - this, std::placeholders::_1)); - } - - void SSUServer::HandleTerminationTimerV6 (const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - auto ts = i2p::util::GetSecondsSinceEpoch (); - for (auto& it: m_SessionsV6) - if (it.second->IsTerminationTimeoutExpired (ts)) - { - auto session = it.second; - if (it.first != session->GetRemoteEndpoint ()) - LogPrint (eLogWarning, "SSU: Remote endpoint ", session->GetRemoteEndpoint (), " doesn't match key ", it.first); - m_Service.post ([session] - { - LogPrint (eLogWarning, "SSU: No activity with ", session->GetRemoteEndpoint (), " for ", session->GetTerminationTimeout (), " seconds"); - session->Failed (); - }); - } - else - it.second->CleanUp (ts); - ScheduleTerminationV6 (); - } - } -} -} diff --git a/libi2pd/SSU.h b/libi2pd/SSU.h deleted file mode 100644 index 25ce4d40..00000000 --- a/libi2pd/SSU.h +++ /dev/null @@ -1,159 +0,0 @@ -/* -* Copyright (c) 2013-2022, The PurpleI2P Project -* -* This file is part of Purple i2pd project and licensed under BSD3 -* -* See full license text in LICENSE file at top of project tree -*/ - -#ifndef SSU_H__ -#define SSU_H__ - -#include -#include -#include -#include -#include -#include -#include -#include -#include "Crypto.h" -#include "util.h" -#include "I2PEndian.h" -#include "Identity.h" -#include "RouterInfo.h" -#include "I2NPProtocol.h" -#include "SSUSession.h" - -namespace i2p -{ -namespace transport -{ - const int SSU_KEEP_ALIVE_INTERVAL = 30; // 30 seconds - const int SSU_PEER_TEST_TIMEOUT = 60; // 60 seconds - const int SSU_TO_INTRODUCER_SESSION_DURATION = 3600; // 1 hour - const int SSU_TO_INTRODUCER_SESSION_EXPIRATION = 4800; // 80 minutes - const int SSU_TERMINATION_CHECK_TIMEOUT = 30; // 30 seconds - const size_t SSU_MAX_NUM_INTRODUCERS = 3; - const size_t SSU_SOCKET_RECEIVE_BUFFER_SIZE = 0x1FFFF; // 128K - const size_t SSU_SOCKET_SEND_BUFFER_SIZE = 0x1FFFF; // 128K - - struct SSUPacket - { - i2p::crypto::AESAlignedBuffer buf; // max MTU + iv + size - boost::asio::ip::udp::endpoint from; - size_t len; - }; - - class SSUServer - { - public: - - SSUServer (int port); - ~SSUServer (); - void Start (); - void Stop (); - bool CreateSession (std::shared_ptr router, bool peerTest = false, bool v4only = false); - bool CreateSession (std::shared_ptr router, - std::shared_ptr address, bool peerTest = false); - void CreateDirectSession (std::shared_ptr router, boost::asio::ip::udp::endpoint remoteEndpoint, bool peerTest); - std::shared_ptr FindSession (const boost::asio::ip::udp::endpoint& e) const; - std::shared_ptr GetRandomEstablishedV4Session (std::shared_ptr excluded); - std::shared_ptr GetRandomEstablishedV6Session (std::shared_ptr excluded); - void DeleteSession (std::shared_ptr session); - void DeleteAllSessions (); - - boost::asio::io_service& GetService () { return m_Service; }; - i2p::util::MemoryPool& GetFragmentsPool () { return m_FragmentsPool; }; - i2p::util::MemoryPool& GetIncompleteMessagesPool () { return m_IncompleteMessagesPool; }; - i2p::util::MemoryPool& GetSentMessagesPool () { return m_SentMessagesPool; }; - - uint16_t GetPort () const { return m_Endpoint.port (); }; - bool IsSyncClockFromPeers () const { return m_IsSyncClockFromPeers; }; - void SetLocalAddress (const boost::asio::ip::address& localAddress); - - void Send (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& to); - void AddRelay (uint32_t tag, std::shared_ptr relay); - void RemoveRelay (uint32_t tag); - std::shared_ptr FindRelaySession (uint32_t tag); - void RescheduleIntroducersUpdateTimer (); - void RescheduleIntroducersUpdateTimerV6 (); - - void NewPeerTest (uint32_t nonce, PeerTestParticipant role, std::shared_ptr session = nullptr); - PeerTestParticipant GetPeerTestParticipant (uint32_t nonce); - std::shared_ptr GetPeerTestSession (uint32_t nonce); - void UpdatePeerTest (uint32_t nonce, PeerTestParticipant role); - void RemovePeerTest (uint32_t nonce); - - private: - - void OpenSocket (); - void OpenSocketV6 (); - void Run (); - void RunReceivers (); - void RunReceiversV6 (); - void Receive (); - void ReceiveV6 (); - void HandleReceivedFrom (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet); - void HandleReceivedFromV6 (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet); - void HandleReceivedPackets (std::vector packets, - std::map >* sessions); - - void CreateSessionThroughIntroducer (std::shared_ptr router, - std::shared_ptr address, bool peerTest = false); - template - std::shared_ptr GetRandomV4Session (Filter filter); - template - std::shared_ptr GetRandomV6Session (Filter filter); - - std::list > FindIntroducers (int maxNumIntroducers, bool v4, std::set& excluded); - void ScheduleIntroducersUpdateTimer (); - void ScheduleIntroducersUpdateTimerV6 (); - void HandleIntroducersUpdateTimer (const boost::system::error_code& ecode, bool v4); - - void SchedulePeerTestsCleanupTimer (); - void HandlePeerTestsCleanupTimer (const boost::system::error_code& ecode); - - // timer - void ScheduleTermination (); - void HandleTerminationTimer (const boost::system::error_code& ecode); - void ScheduleTerminationV6 (); - void HandleTerminationTimerV6 (const boost::system::error_code& ecode); - - private: - - struct PeerTest - { - uint64_t creationTime; - PeerTestParticipant role; - std::shared_ptr session; // for Bob to Alice - }; - - volatile bool m_IsRunning; - std::thread * m_Thread, * m_ReceiversThread, * m_ReceiversThreadV6; - boost::asio::io_service m_Service, m_ReceiversService, m_ReceiversServiceV6; - boost::asio::io_service::work m_Work, m_ReceiversWork, m_ReceiversWorkV6; - boost::asio::ip::udp::endpoint m_Endpoint, m_EndpointV6; - boost::asio::ip::udp::socket m_Socket, m_SocketV6; - boost::asio::deadline_timer m_IntroducersUpdateTimer, m_IntroducersUpdateTimerV6, - m_PeerTestsCleanupTimer, m_TerminationTimer, m_TerminationTimerV6; - bool m_IsSyncClockFromPeers; - std::list m_Introducers, m_IntroducersV6; // introducers we are connected to - std::map > m_Sessions, m_SessionsV6; - std::map > m_Relays; // we are introducer - std::map m_PeerTests; // nonce -> creation time in milliseconds - - i2p::util::MemoryPool m_FragmentsPool; - i2p::util::MemoryPool m_IncompleteMessagesPool; - i2p::util::MemoryPool m_SentMessagesPool; - i2p::util::MemoryPoolMt m_PacketsPool; - - public: - // for HTTP only - const decltype(m_Sessions)& GetSessions () const { return m_Sessions; }; - const decltype(m_SessionsV6)& GetSessionsV6 () const { return m_SessionsV6; }; - }; -} -} - -#endif diff --git a/libi2pd/SSU2.cpp b/libi2pd/SSU2.cpp index 65f6f4f1..4636762d 100644 --- a/libi2pd/SSU2.cpp +++ b/libi2pd/SSU2.cpp @@ -68,9 +68,8 @@ namespace transport if (ssu2Port) port = ssu2Port; else { - bool ssu; i2p::config::GetOption("ssu", ssu); uint16_t p; i2p::config::GetOption ("port", p); - if (p) port = ssu ? (p + 1) : p; + if (p) port = p; } } if (port) @@ -409,7 +408,7 @@ namespace transport return it->second; it++; } - // not found, try from begining + // not found, try from beginning it = m_Sessions.begin (); while (it != m_Sessions.end () && ind) { @@ -642,7 +641,7 @@ namespace transport // try to find existing session first for (auto& it: address->ssu->introducers) { - auto it1 = m_SessionsByRouterHash.find (it.iKey); + auto it1 = m_SessionsByRouterHash.find (it.iH); if (it1 != m_SessionsByRouterHash.end ()) { it1->second->Introduce (session, it.iTag); @@ -655,17 +654,17 @@ namespace transport uint32_t relayTag = 0; if (!address->ssu->introducers.empty ()) { - std::vector indicies; - for (int i = 0; i < (int)address->ssu->introducers.size (); i++) indicies.push_back(i); - if (indicies.size () > 1) - std::shuffle (indicies.begin(), indicies.end(), std::mt19937(std::random_device()())); + std::vector indices; + for (int i = 0; i < (int)address->ssu->introducers.size (); i++) indices.push_back(i); + if (indices.size () > 1) + std::shuffle (indices.begin(), indices.end(), std::mt19937(std::random_device()())); - for (auto i: indicies) + for (auto i: indices) { - const auto& introducer = address->ssu->introducers[indicies[i]]; + const auto& introducer = address->ssu->introducers[indices[i]]; if (introducer.iTag && ts < introducer.iExp) { - r = i2p::data::netdb.FindRouter (introducer.iKey); + r = i2p::data::netdb.FindRouter (introducer.iH); if (r && r->IsReachableFrom (i2p::context.GetRouterInfo ())) { relayTag = introducer.iTag; @@ -714,7 +713,7 @@ namespace transport // introducers not found, try to request them for (auto& it: address->ssu->introducers) if (it.iTag && ts < it.iExp) - i2p::data::netdb.RequestDestination (it.iKey); + i2p::data::netdb.RequestDestination (it.iH); } } @@ -962,7 +961,8 @@ namespace transport { i2p::data::RouterInfo::Introducer introducer; introducer.iTag = it->GetRelayTag (); - introducer.iKey = it->GetRemoteIdentity ()->GetIdentHash (); + introducer.iH = it->GetRemoteIdentity ()->GetIdentHash (); + introducer.isH = true; introducer.iExp = it->GetCreationTime () + SSU2_TO_INTRODUCER_SESSION_EXPIRATION; excluded.insert (it->GetRemoteIdentity ()->GetIdentHash ()); if (i2p::context.AddSSU2Introducer (introducer, v4)) @@ -1068,7 +1068,7 @@ namespace transport // we are firewalled auto addr = i2p::context.GetRouterInfo ().GetSSU2V4Address (); if (addr && addr->ssu && addr->ssu->introducers.empty ()) - i2p::context.SetUnreachableSSU2 (true, false); // v4 + i2p::context.SetUnreachable (true, false); // v4 UpdateIntroducers (true); ScheduleIntroducersUpdateTimer (); @@ -1091,7 +1091,7 @@ namespace transport // we are firewalled auto addr = i2p::context.GetRouterInfo ().GetSSU2V6Address (); if (addr && addr->ssu && addr->ssu->introducers.empty ()) - i2p::context.SetUnreachableSSU2 (false, true); // v6 + i2p::context.SetUnreachable (false, true); // v6 UpdateIntroducers (false); ScheduleIntroducersUpdateTimerV6 (); diff --git a/libi2pd/SSU2Session.cpp b/libi2pd/SSU2Session.cpp index 493a8a76..315d39b6 100644 --- a/libi2pd/SSU2Session.cpp +++ b/libi2pd/SSU2Session.cpp @@ -94,7 +94,10 @@ namespace transport if (!ecode) { // timeout expired - LogPrint (eLogWarning, "SSU2: Session with ", m_RemoteEndpoint, " was not established after ", SSU2_CONNECT_TIMEOUT, " seconds"); + if (m_State == eSSU2SessionStateIntroduced) // WaitForIntroducer + LogPrint (eLogWarning, "SSU2: Session was not introduced after ", SSU2_CONNECT_TIMEOUT, " seconds"); + else + LogPrint (eLogWarning, "SSU2: Session with ", m_RemoteEndpoint, " was not established after ", SSU2_CONNECT_TIMEOUT, " seconds"); Terminate (); } } @@ -103,7 +106,7 @@ namespace transport { // we are Alice if (!session || !relayTag) return false; - // find local adddress to introduce + // find local address to introduce auto localAddress = session->FindLocalAddress (); if (!localAddress) return false; // create nonce @@ -520,6 +523,11 @@ namespace transport case eSSU2PeerTest: { // TODO: remove later + if (len < 32) + { + LogPrint (eLogWarning, "SSU2: PeerTest message too short ", len); + break; + } const uint8_t nonce[12] = {0}; uint64_t headerX[2]; i2p::crypto::ChaCha20 (buf + 16, 16, i2p::context.GetSSU2IntroKey (), nonce, (uint8_t *)headerX); @@ -603,6 +611,11 @@ namespace transport void SSU2Session::ProcessSessionRequest (Header& header, uint8_t * buf, size_t len) { // we are Bob + if (len < 80) + { + LogPrint (eLogWarning, "SSU2: SessionRequest message too short ", len); + return; + } const uint8_t nonce[12] = {0}; uint8_t headerX[48]; i2p::crypto::ChaCha20 (buf + 16, 48, i2p::context.GetSSU2IntroKey (), nonce, headerX); @@ -723,6 +736,11 @@ namespace transport if (header.h.type != eSSU2SessionCreated) // this situation is valid, because it might be Retry with different encryption return false; + if (len < 80) + { + LogPrint (eLogWarning, "SSU2: SessionCreated message too short ", len); + return false; + } const uint8_t nonce[12] = {0}; uint8_t headerX[48]; i2p::crypto::ChaCha20 (buf + 16, 48, kh2, nonce, headerX); @@ -863,6 +881,12 @@ namespace transport LogPrint (eLogError, "SSU2: Too many fragments ", numFragments, " in SessionConfirmed"); return false; } + if (len < 32) + { + LogPrint (eLogWarning, "SSU2: SessionConfirmed fragment too short ", len); + if (m_SessionConfirmedFragment) m_SessionConfirmedFragment.reset (nullptr); + return false; + } if (!(header.h.flags[0] & 0xF0)) { // first fragment @@ -910,6 +934,12 @@ namespace transport len = m_SessionConfirmedFragment->payloadSize + 16; } } + if (len < 80) + { + LogPrint (eLogWarning, "SSU2: SessionConfirmed message too short ", len); + if (m_SessionConfirmedFragment) m_SessionConfirmedFragment.reset (nullptr); + return false; + } // KDF for Session Confirmed part 1 m_NoiseState->MixHash (header.buf, 16); // h = SHA256(h || header) // decrypt part1 @@ -1118,6 +1148,11 @@ namespace transport LogPrint (eLogWarning, "SSU2: Unexpected message type ", (int)header.h.type, " instead ", (int)eSSU2Retry); return false; } + if (len < 48) + { + LogPrint (eLogWarning, "SSU2: Retry message too short ", len); + return false; + } uint8_t nonce[12] = {0}; uint64_t headerX[2]; // sourceConnID, token i2p::crypto::ChaCha20 (buf + 16, 16, m_Address->i, nonce, (uint8_t *)headerX); @@ -1202,6 +1237,11 @@ namespace transport LogPrint (eLogWarning, "SSU2: Unexpected message type ", (int)header.h.type, " instead ", (int)eSSU2HolePunch); return false; } + if (len < 48) + { + LogPrint (eLogWarning, "SSU2: HolePunch message too short ", len); + return false; + } uint8_t nonce[12] = {0}; uint64_t headerX[2]; // sourceConnID, token i2p::crypto::ChaCha20 (buf + 16, 16, i2p::context.GetSSU2IntroKey (), nonce, (uint8_t *)headerX); @@ -1273,6 +1313,11 @@ namespace transport LogPrint (eLogWarning, "SSU2: Unexpected message type ", (int)header.h.type, " instead ", (int)eSSU2PeerTest); return false; } + if (len < 48) + { + LogPrint (eLogWarning, "SSU2: PeerTest message too short ", len); + return false; + } uint8_t nonce[12] = {0}; uint64_t headerX[2]; // sourceConnID, token i2p::crypto::ChaCha20 (buf + 16, 16, i2p::context.GetSSU2IntroKey (), nonce, (uint8_t *)headerX); @@ -1340,6 +1385,11 @@ namespace transport m_RemoteEndpoint = from; SendPathChallenge (); } + if (len < 32) + { + LogPrint (eLogWarning, "SSU2: Data message too short ", len); + return; + } uint8_t payload[SSU2_MAX_PACKET_SIZE]; size_t payloadSize = len - 32; uint32_t packetNum = be32toh (header.h.packetNum); @@ -2266,9 +2316,9 @@ namespace transport if (m_Address) { if (m_Address->IsV4 ()) - i2p::context.SetStatusSSU2 (status); + i2p::context.SetStatus (status); else if (m_Address->IsV6 ()) - i2p::context.SetStatusV6SSU2 (status); + i2p::context.SetStatusV6 (status); } } diff --git a/libi2pd/SSU2Session.h b/libi2pd/SSU2Session.h index a4306e68..a8c473a2 100644 --- a/libi2pd/SSU2Session.h +++ b/libi2pd/SSU2Session.h @@ -35,7 +35,7 @@ namespace transport const int SSU2_PEER_TEST_EXPIRATION_TIMEOUT = 60; // 60 seconds const size_t SSU2_MAX_PACKET_SIZE = 1500; const size_t SSU2_MIN_PACKET_SIZE = 1280; - const int SSU2_HANDSHAKE_RESEND_INTERVAL = 1000; // in millseconds + const int SSU2_HANDSHAKE_RESEND_INTERVAL = 1000; // in milliseconds const int SSU2_RESEND_INTERVAL = 300; // in milliseconds const int SSU2_MAX_NUM_RESENDS = 5; const int SSU2_INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT = 30; // in seconds diff --git a/libi2pd/SSUData.cpp b/libi2pd/SSUData.cpp deleted file mode 100644 index 6365381e..00000000 --- a/libi2pd/SSUData.cpp +++ /dev/null @@ -1,516 +0,0 @@ -/* -* Copyright (c) 2013-2022, The PurpleI2P Project -* -* This file is part of Purple i2pd project and licensed under BSD3 -* -* See full license text in LICENSE file at top of project tree -*/ - -#include -#include "Log.h" -#include "Timestamp.h" -#include "NetDb.hpp" -#include "SSU.h" -#include "SSUData.h" - -namespace i2p -{ -namespace transport -{ - void IncompleteMessage::AttachNextFragment (const uint8_t * fragment, size_t fragmentSize) - { - if (msg->len + fragmentSize > msg->maxLen) - { - LogPrint (eLogWarning, "SSU: I2NP message size ", msg->maxLen, " is not enough"); - auto newMsg = NewI2NPMessage (); - *newMsg = *msg; - msg = newMsg; - } - if (msg->Concat (fragment, fragmentSize) < fragmentSize) - LogPrint (eLogError, "SSU: I2NP buffer overflow ", msg->maxLen); - nextFragmentNum++; - } - - SSUData::SSUData (SSUSession& session): - m_Session (session), m_ResendTimer (session.GetService ()), - m_MaxPacketSize (session.IsV6 () ? SSU_V6_MAX_PACKET_SIZE : SSU_V4_MAX_PACKET_SIZE), - m_PacketSize (m_MaxPacketSize), m_LastMessageReceivedTime (0) - { - } - - SSUData::~SSUData () - { - } - - void SSUData::Start () - { - } - - void SSUData::Stop () - { - m_ResendTimer.cancel (); - m_IncompleteMessages.clear (); - m_SentMessages.clear (); - m_ReceivedMessages.clear (); - } - - void SSUData::AdjustPacketSize (std::shared_ptr remoteRouter) - { - if (!remoteRouter) return; - auto ssuAddress = remoteRouter->GetSSUAddress (); - if (ssuAddress && ssuAddress->ssu->mtu) - { - if (m_Session.IsV6 ()) - m_PacketSize = ssuAddress->ssu->mtu - IPV6_HEADER_SIZE - UDP_HEADER_SIZE; - else - m_PacketSize = ssuAddress->ssu->mtu - IPV4_HEADER_SIZE - UDP_HEADER_SIZE; - if (m_PacketSize > 0) - { - // make sure packet size multiple of 16 - m_PacketSize >>= 4; - m_PacketSize <<= 4; - if (m_PacketSize > m_MaxPacketSize) m_PacketSize = m_MaxPacketSize; - LogPrint (eLogDebug, "SSU: MTU=", ssuAddress->ssu->mtu, " packet size=", m_PacketSize); - } - else - { - LogPrint (eLogWarning, "SSU: Unexpected MTU ", ssuAddress->ssu->mtu); - m_PacketSize = m_MaxPacketSize; - } - } - } - - void SSUData::UpdatePacketSize (const i2p::data::IdentHash& remoteIdent) - { - auto routerInfo = i2p::data::netdb.FindRouter (remoteIdent); - if (routerInfo) - AdjustPacketSize (routerInfo); - } - - void SSUData::ProcessSentMessageAck (uint32_t msgID) - { - auto it = m_SentMessages.find (msgID); - if (it != m_SentMessages.end ()) - { - m_SentMessages.erase (it); - if (m_SentMessages.empty ()) - m_ResendTimer.cancel (); - } - } - - void SSUData::ProcessAcks (uint8_t *& buf, uint8_t flag) - { - if (flag & DATA_FLAG_EXPLICIT_ACKS_INCLUDED) - { - // explicit ACKs - uint8_t numAcks =*buf; - buf++; - for (int i = 0; i < numAcks; i++) - ProcessSentMessageAck (bufbe32toh (buf+i*4)); - 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++) - { - uint32_t msgID = bufbe32toh (buf); - buf += 4; // msgID - auto it = m_SentMessages.find (msgID); - // process individual Ack bitfields - bool isNonLast = false; - int fragment = 0; - do - { - uint8_t bitfield = *buf; - isNonLast = bitfield & 0x80; - bitfield &= 0x7F; // clear MSB - if (bitfield && it != m_SentMessages.end ()) - { - int numSentFragments = it->second->fragments.size (); - // process bits - uint8_t mask = 0x01; - for (int j = 0; j < 7; j++) - { - if (bitfield & mask) - { - if (fragment < numSentFragments) - it->second->fragments[fragment] = nullptr; - } - fragment++; - mask <<= 1; - } - } - buf++; - } - while (isNonLast); - } - } - } - - void SSUData::ProcessFragments (uint8_t * buf) - { - uint8_t numFragments = *buf; // number of fragments - buf++; - for (int i = 0; i < numFragments; i++) - { - uint32_t msgID = bufbe32toh (buf); // message ID - buf += 4; - uint8_t frag[4] = {0}; - memcpy (frag + 1, buf, 3); - buf += 3; - uint32_t fragmentInfo = bufbe32toh (frag); // fragment info - uint16_t fragmentSize = fragmentInfo & 0x3FFF; // bits 0 - 13 - bool isLast = fragmentInfo & 0x010000; // bit 16 - uint8_t fragmentNum = fragmentInfo >> 17; // bits 23 - 17 - if (fragmentSize >= SSU_V4_MAX_PACKET_SIZE) - { - LogPrint (eLogError, "SSU: Fragment size ", fragmentSize, " exceeds max SSU packet size"); - return; - } - - // find message with msgID - auto it = m_IncompleteMessages.find (msgID); - if (it == m_IncompleteMessages.end ()) - { - // create new message - auto msg = NewI2NPShortMessage (); - msg->len -= I2NP_SHORT_HEADER_SIZE; - it = m_IncompleteMessages.insert (std::make_pair (msgID, - m_Session.GetServer ().GetIncompleteMessagesPool ().AcquireShared (std::move (msg)))).first; - } - auto& incompleteMessage = it->second; - // mark fragment as received - if (fragmentNum < 64) - incompleteMessage->receivedFragmentsBits |= (uint64_t(0x01) << fragmentNum); - else - LogPrint (eLogWarning, "SSU: Fragment number ", fragmentNum, " exceeds 64"); - - // handle current fragment - if (fragmentNum == incompleteMessage->nextFragmentNum) - { - // expected fragment - incompleteMessage->AttachNextFragment (buf, fragmentSize); - if (!isLast && !incompleteMessage->savedFragments.empty ()) - { - // try saved fragments - for (auto it1 = incompleteMessage->savedFragments.begin (); it1 != incompleteMessage->savedFragments.end ();) - { - auto& savedFragment = *it1; - if (savedFragment->fragmentNum == incompleteMessage->nextFragmentNum) - { - incompleteMessage->AttachNextFragment (savedFragment->buf, savedFragment->len); - isLast = savedFragment->isLast; - incompleteMessage->savedFragments.erase (it1++); - } - else - break; - } - if (isLast) - LogPrint (eLogDebug, "SSU: Message ", msgID, " complete"); - } - } - else - { - if (fragmentNum < incompleteMessage->nextFragmentNum) - // duplicate fragment - LogPrint (eLogWarning, "SSU: Duplicate fragment ", (int)fragmentNum, " of message ", msgID, ", ignored"); - else - { - // missing fragment - LogPrint (eLogWarning, "SSU: Missing fragments from ", (int)incompleteMessage->nextFragmentNum, " to ", fragmentNum - 1, " of message ", msgID); - auto savedFragment = m_Session.GetServer ().GetFragmentsPool ().AcquireShared (fragmentNum, buf, fragmentSize, isLast); - if (incompleteMessage->savedFragments.insert (savedFragment).second) - incompleteMessage->lastFragmentInsertTime = i2p::util::GetSecondsSinceEpoch (); - else - LogPrint (eLogWarning, "SSU: Fragment ", (int)fragmentNum, " of message ", msgID, " already saved"); - } - isLast = false; - } - - if (isLast) - { - // delete incomplete message - auto msg = incompleteMessage->msg; - incompleteMessage->msg = nullptr; - m_IncompleteMessages.erase (msgID); - // process message - SendMsgAck (msgID); - msg->FromSSU (msgID); - if (m_Session.GetState () == eSessionStateEstablished) - { - if (!m_ReceivedMessages.count (msgID)) - { - m_LastMessageReceivedTime = i2p::util::GetSecondsSinceEpoch (); - m_ReceivedMessages.emplace (msgID, m_LastMessageReceivedTime); - if (!msg->IsExpired ()) - { - m_Handler.PutNextMessage (std::move (msg)); - } - else - LogPrint (eLogDebug, "SSU: message expired"); - } - else - LogPrint (eLogWarning, "SSU: Message ", msgID, " already received"); - } - else - { - // we expect DeliveryStatus - if (msg->GetTypeID () == eI2NPDeliveryStatus) - { - LogPrint (eLogDebug, "SSU: session established"); - m_Session.Established (); - } - else - LogPrint (eLogError, "SSU: unexpected message ", (int)msg->GetTypeID ()); - } - } - else - SendFragmentAck (msgID, incompleteMessage->receivedFragmentsBits); - buf += fragmentSize; - } - } - - void SSUData::FlushReceivedMessage () - { - m_Handler.Flush (); - } - - void SSUData::ProcessMessage (uint8_t * buf, size_t len) - { - //uint8_t * start = buf; - uint8_t flag = *buf; - buf++; - LogPrint (eLogDebug, "SSU: Process data, flags=", (int)flag, ", len=", len); - // process acks if presented - if (flag & (DATA_FLAG_ACK_BITFIELDS_INCLUDED | DATA_FLAG_EXPLICIT_ACKS_INCLUDED)) - ProcessAcks (buf, flag); - // extended data if presented - if (flag & DATA_FLAG_EXTENDED_DATA_INCLUDED) - { - uint8_t extendedDataSize = *buf; - buf++; // size - LogPrint (eLogDebug, "SSU: extended data of ", extendedDataSize, " bytes present"); - buf += extendedDataSize; - } - // process data - ProcessFragments (buf); - } - - void SSUData::Send (std::shared_ptr msg) - { - uint32_t msgID = msg->ToSSU (); - if (m_SentMessages.find (msgID) != m_SentMessages.end()) - { - LogPrint (eLogWarning, "SSU: message ", msgID, " already sent"); - return; - } - if (m_SentMessages.empty ()) // schedule resend at first message only - ScheduleResend (); - - auto ret = m_SentMessages.emplace (msgID, m_Session.GetServer ().GetSentMessagesPool ().AcquireShared ()); - auto& sentMessage = ret.first->second; - if (ret.second) - { - sentMessage->nextResendTime = i2p::util::GetSecondsSinceEpoch () + RESEND_INTERVAL; - sentMessage->numResends = 0; - } - auto& fragments = sentMessage->fragments; - size_t payloadSize = m_PacketSize - 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 && fragmentNum <= 127) - { - auto fragment = m_Session.GetServer ().GetFragmentsPool ().AcquireShared (); - fragment->fragmentNum = fragmentNum; - uint8_t * payload = fragment->buf + sizeof (SSUHeader); - *payload = DATA_FLAG_WANT_REPLY; // for compatibility - payload++; - *payload = 1; // always 1 message fragment per message - payload++; - htobe32buf (payload, msgID); - payload += 4; - bool isLast = (len <= payloadSize) || fragmentNum == 127; // 127 fragments max - 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 - fragment->buf; - uint8_t rem = size & 0x0F; - if (rem) // make sure 16 bytes boundary - { - auto padding = 16 - rem; - memset (fragment->buf + size, 0, padding); - size += padding; - } - fragment->len = size; - fragments.push_back (fragment); - - // encrypt message with session key - uint8_t buf[SSU_V4_MAX_PACKET_SIZE + 18]; - m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, fragment->buf, size, buf); - try - { - m_Session.Send (buf, size); - } - catch (boost::system::system_error& ec) - { - LogPrint (eLogWarning, "SSU: Can't send data fragment ", ec.what ()); - } - if (!isLast) - { - len -= payloadSize; - msgBuf += payloadSize; - } - else - len = 0; - fragmentNum++; - } - } - - void SSUData::SendMsgAck (uint32_t msgID) - { - uint8_t buf[48 + 18] = {0}; // actual length is 44 = 37 + 7 but pad it to multiple of 16 - uint8_t * payload = buf + sizeof (SSUHeader); - *payload = DATA_FLAG_EXPLICIT_ACKS_INCLUDED; // flag - payload++; - *payload = 1; // number of ACKs - payload++; - htobe32buf (payload, msgID); // msgID - payload += 4; - *payload = 0; // number of fragments - - // encrypt message with session key - m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, 48); - m_Session.Send (buf, 48); - } - - void SSUData::SendFragmentAck (uint32_t msgID, uint64_t bits) - { - if (!bits) return; - uint8_t buf[64 + 18] = {0}; - uint8_t * payload = buf + sizeof (SSUHeader); - *payload = DATA_FLAG_ACK_BITFIELDS_INCLUDED; // flag - payload++; - *payload = 1; // number of ACK bitfields - payload++; - // one ack - *(uint32_t *)(payload) = htobe32 (msgID); // msgID - payload += 4; - size_t len = 0; - while (bits) - { - *payload = (bits & 0x7F); // next 7 bits - bits >>= 7; - if (bits) *payload &= 0x80; // 0x80 means non-last - payload++; len++; - } - *payload = 0; // number of fragments - len = (len <= 4) ? 48 : 64; // 48 = 37 + 7 + 4 - // encrypt message with session key - m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, len); - m_Session.Send (buf, len); - } - - void SSUData::ScheduleResend() - { - m_ResendTimer.cancel (); - m_ResendTimer.expires_from_now (boost::posix_time::seconds(RESEND_INTERVAL)); - auto s = m_Session.shared_from_this(); - m_ResendTimer.async_wait ([s](const boost::system::error_code& ecode) - { s->m_Data.HandleResendTimer (ecode); }); - } - - void SSUData::HandleResendTimer (const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - uint8_t buf[SSU_V4_MAX_PACKET_SIZE + 18]; - uint32_t ts = i2p::util::GetSecondsSinceEpoch (); - int numResent = 0; - for (auto it = m_SentMessages.begin (); it != m_SentMessages.end ();) - { - if (ts >= it->second->nextResendTime) - { - if (it->second->numResends < MAX_NUM_RESENDS) - { - for (auto& f: it->second->fragments) - if (f) - { - try - { - m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, f->buf, f->len, buf); - m_Session.Send (buf, f->len); // resend - numResent++; - } - catch (boost::system::system_error& ec) - { - LogPrint (eLogWarning, "SSU: Can't resend message ", it->first, " data fragment: ", ec.what ()); - } - } - - it->second->numResends++; - it->second->nextResendTime += it->second->numResends*RESEND_INTERVAL; - ++it; - } - else - { - LogPrint (eLogInfo, "SSU: message ", it->first, " has not been ACKed after ", MAX_NUM_RESENDS, " attempts, deleted"); - it = m_SentMessages.erase (it); - } - } - else - ++it; - } - if (m_SentMessages.empty ()) return; // nothing to resend - if (numResent < MAX_OUTGOING_WINDOW_SIZE) - ScheduleResend (); - else - { - LogPrint (eLogError, "SSU: resend window exceeds max size. Session terminated"); - m_Session.Close (); - } - } - } - - void SSUData::CleanUp (uint64_t ts) - { - for (auto it = m_IncompleteMessages.begin (); it != m_IncompleteMessages.end ();) - { - if (ts > it->second->lastFragmentInsertTime + INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT) - { - LogPrint (eLogWarning, "SSU: message ", it->first, " was not completed in ", INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT, " seconds, deleted"); - it = m_IncompleteMessages.erase (it); - } - else - ++it; - } - - if (m_ReceivedMessages.size () > MAX_NUM_RECEIVED_MESSAGES || ts > m_LastMessageReceivedTime + DECAY_INTERVAL) - // decay - m_ReceivedMessages.clear (); - else - { - // delete old received messages - for (auto it = m_ReceivedMessages.begin (); it != m_ReceivedMessages.end ();) - { - if (ts > it->second + RECEIVED_MESSAGES_CLEANUP_TIMEOUT) - it = m_ReceivedMessages.erase (it); - else - ++it; - } - } - } -} -} diff --git a/libi2pd/SSUData.h b/libi2pd/SSUData.h deleted file mode 100644 index eba0fc28..00000000 --- a/libi2pd/SSUData.h +++ /dev/null @@ -1,131 +0,0 @@ -/* -* Copyright (c) 2013-2022, The PurpleI2P Project -* -* This file is part of Purple i2pd project and licensed under BSD3 -* -* See full license text in LICENSE file at top of project tree -*/ - -#ifndef SSU_DATA_H__ -#define SSU_DATA_H__ - -#include -#include -#include -#include -#include -#include -#include -#include "I2NPProtocol.h" -#include "Identity.h" -#include "RouterInfo.h" -#include "TransportSession.h" - -namespace i2p -{ -namespace transport -{ - const size_t SSU_MTU_V4 = 1484; - const size_t SSU_MTU_V6 = 1488; - const size_t SSU_V4_MAX_PACKET_SIZE = SSU_MTU_V4 - IPV4_HEADER_SIZE - UDP_HEADER_SIZE; // 1456 - const size_t SSU_V6_MAX_PACKET_SIZE = SSU_MTU_V6 - IPV6_HEADER_SIZE - UDP_HEADER_SIZE; // 1440 - const int RESEND_INTERVAL = 3; // in seconds - const int MAX_NUM_RESENDS = 5; - const int DECAY_INTERVAL = 20; // in seconds - const int INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT = 30; // in seconds - const int RECEIVED_MESSAGES_CLEANUP_TIMEOUT = 40; // in seconds - const unsigned int MAX_NUM_RECEIVED_MESSAGES = 1000; // how many msgID we store for duplicates check - const int MAX_OUTGOING_WINDOW_SIZE = 200; // how many unacked message we can store - // 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; - - struct Fragment - { - int fragmentNum; - size_t len; - bool isLast; - uint8_t buf[SSU_V4_MAX_PACKET_SIZE + 18]; // use biggest - - Fragment () = default; - Fragment (int n, const uint8_t * b, int l, bool last): - fragmentNum (n), len (l), isLast (last) { memcpy (buf, b, len); }; - }; - - struct FragmentCmp - { - bool operator() (const std::shared_ptr& f1, const std::shared_ptr& f2) const - { - return f1->fragmentNum < f2->fragmentNum; - }; - }; - - struct IncompleteMessage - { - std::shared_ptr msg; - int nextFragmentNum; - uint32_t lastFragmentInsertTime; // in seconds - uint64_t receivedFragmentsBits; - std::set, FragmentCmp> savedFragments; - - IncompleteMessage (std::shared_ptr&& m): msg (m), nextFragmentNum (0), - lastFragmentInsertTime (0), receivedFragmentsBits (0) {}; - void AttachNextFragment (const uint8_t * fragment, size_t fragmentSize); - }; - - struct SentMessage - { - std::vector > fragments; - uint32_t nextResendTime; // in seconds - int numResends; - }; - - class SSUSession; - class SSUData - { - public: - - SSUData (SSUSession& session); - ~SSUData (); - - void Start (); - void Stop (); - void CleanUp (uint64_t ts); - - void ProcessMessage (uint8_t * buf, size_t len); - void FlushReceivedMessage (); - void Send (std::shared_ptr msg); - - void AdjustPacketSize (std::shared_ptr remoteRouter); - void UpdatePacketSize (const i2p::data::IdentHash& remoteIdent); - - private: - - void SendMsgAck (uint32_t msgID); - void SendFragmentAck (uint32_t msgID, uint64_t bits); - void ProcessAcks (uint8_t *& buf, uint8_t flag); - void ProcessFragments (uint8_t * buf); - void ProcessSentMessageAck (uint32_t msgID); - - void ScheduleResend (); - void HandleResendTimer (const boost::system::error_code& ecode); - - private: - - SSUSession& m_Session; - std::map > m_IncompleteMessages; - std::map > m_SentMessages; - std::unordered_map m_ReceivedMessages; // msgID -> timestamp in seconds - boost::asio::deadline_timer m_ResendTimer; - int m_MaxPacketSize, m_PacketSize; - i2p::I2NPMessagesHandler m_Handler; - uint32_t m_LastMessageReceivedTime; // in second - }; -} -} - -#endif diff --git a/libi2pd/SSUSession.cpp b/libi2pd/SSUSession.cpp deleted file mode 100644 index 73f9cdb1..00000000 --- a/libi2pd/SSUSession.cpp +++ /dev/null @@ -1,1318 +0,0 @@ -/* -* Copyright (c) 2013-2022, The PurpleI2P Project -* -* This file is part of Purple i2pd project and licensed under BSD3 -* -* See full license text in LICENSE file at top of project tree -*/ - -#include "version.h" -#include "Crypto.h" -#include "Log.h" -#include "Timestamp.h" -#include "RouterContext.h" -#include "Transports.h" -#include "NetDb.hpp" -#include "SSU.h" -#include "SSUSession.h" - -namespace i2p -{ -namespace transport -{ - SSUSession::SSUSession (SSUServer& server, boost::asio::ip::udp::endpoint& remoteEndpoint, - std::shared_ptr router, bool peerTest ): - TransportSession (router, SSU_TERMINATION_TIMEOUT), - m_Server (server), m_RemoteEndpoint (remoteEndpoint), m_ConnectTimer (GetService ()), - m_IsPeerTest (peerTest),m_State (eSessionStateUnknown), m_IsSessionKey (false), - m_RelayTag (0), m_SentRelayTag (0), m_Data (*this), m_IsDataReceived (false) - { - if (router) - { - // we are client - auto address = IsV6 () ? router->GetSSUV6Address () : router->GetSSUAddress (true); - if (address) m_IntroKey = address->i; - m_Data.AdjustPacketSize (router); // mtu - } - else - { - // we are server - auto address = IsV6 () ? i2p::context.GetRouterInfo ().GetSSUV6Address () : - i2p::context.GetRouterInfo ().GetSSUAddress (true); - if (address) m_IntroKey = address->i; - } - } - - SSUSession::~SSUSession () - { - } - - boost::asio::io_service& SSUSession::GetService () - { - return m_Server.GetService (); - } - - void SSUSession::CreateAESandMacKey (const uint8_t * pubKey) - { - uint8_t sharedKey[256]; - m_DHKeysPair->Agree (pubKey, sharedKey); - - uint8_t * sessionKey = m_SessionKey, * macKey = m_MacKey; - if (sharedKey[0] & 0x80) - { - sessionKey[0] = 0; - memcpy (sessionKey + 1, sharedKey, 31); - memcpy (macKey, sharedKey + 31, 32); - } - else if (sharedKey[0]) - { - memcpy (sessionKey, sharedKey, 32); - memcpy (macKey, sharedKey + 32, 32); - } - else - { - // find first non-zero byte - uint8_t * nonZero = sharedKey + 1; - while (!*nonZero) - { - nonZero++; - if (nonZero - sharedKey > 32) - { - LogPrint (eLogWarning, "SSU: First 32 bytes of shared key is all zeros. Ignored"); - return; - } - } - - memcpy (sessionKey, nonZero, 32); - SHA256(nonZero, 64 - (nonZero - sharedKey), macKey); - } - m_IsSessionKey = true; - m_SessionKeyEncryption.SetKey (m_SessionKey); - m_SessionKeyDecryption.SetKey (m_SessionKey); - } - - void SSUSession::ProcessNextMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint) - { - m_NumReceivedBytes += len; - i2p::transport::transports.UpdateReceivedBytes (len); - if (m_State == eSessionStateIntroduced) - { - // HolePunch received - LogPrint (eLogDebug, "SSU: HolePunch of ", len, " bytes received"); - m_State = eSessionStateUnknown; - Connect (); - } - else - { - if (!len) return; // ignore zero-length packets - if (m_State == eSessionStateEstablished) - m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch (); - - if (m_IsSessionKey && Validate (buf, len, m_MacKey)) // try session key first - DecryptSessionKey (buf, len); - else - { - if (m_State == eSessionStateEstablished) Reset (); // new session key required - // try intro key depending on side - if (Validate (buf, len, m_IntroKey)) - Decrypt (buf, len, m_IntroKey); - else - { - // try own intro key - auto address = IsV6 () ? i2p::context.GetRouterInfo ().GetSSUV6Address () : - i2p::context.GetRouterInfo ().GetSSUAddress (true); - if (!address) - { - LogPrint (eLogInfo, "SSU: SSU is not supported"); - return; - } - if (Validate (buf, len, address->i)) - Decrypt (buf, len, address->i); - else - { - LogPrint (eLogWarning, "SSU: MAC verification failed ", len, " bytes from ", senderEndpoint); - m_Server.DeleteSession (shared_from_this ()); - return; - } - } - } - // successfully decrypted - ProcessMessage (buf, len, senderEndpoint); - } - } - - size_t SSUSession::GetSSUHeaderSize (const uint8_t * buf) const - { - size_t s = sizeof (SSUHeader); - if (((const SSUHeader *)buf)->IsExtendedOptions ()) - s += buf[s] + 1; // byte right after header is extended options length - return s; - } - - void SSUSession::ProcessMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint) - { - len -= (len & 0x0F); // %16, delete extra padding - if (len <= sizeof (SSUHeader)) return; // drop empty message - //TODO: since we are accessing a uint8_t this is unlikely to crash due to alignment but should be improved - auto headerSize = GetSSUHeaderSize (buf); - if (headerSize >= len) - { - LogPrint (eLogError, "SSU: SSU header size ", headerSize, " exceeds packet length ", len); - return; - } - SSUHeader * header = (SSUHeader *)buf; - switch (header->GetPayloadType ()) - { - case PAYLOAD_TYPE_DATA: - ProcessData (buf + headerSize, len - headerSize); - break; - case PAYLOAD_TYPE_SESSION_REQUEST: - ProcessSessionRequest (buf, len); // buf with header - break; - case PAYLOAD_TYPE_SESSION_CREATED: - ProcessSessionCreated (buf, len); // buf with header - break; - case PAYLOAD_TYPE_SESSION_CONFIRMED: - ProcessSessionConfirmed (buf, len); // buf with header - break; - case PAYLOAD_TYPE_PEER_TEST: - LogPrint (eLogDebug, "SSU: Peer test received"); - ProcessPeerTest (buf + headerSize, len - headerSize, senderEndpoint); - break; - case PAYLOAD_TYPE_SESSION_DESTROYED: - { - LogPrint (eLogDebug, "SSU: Session destroy received"); - m_Server.DeleteSession (shared_from_this ()); - break; - } - case PAYLOAD_TYPE_RELAY_RESPONSE: - ProcessRelayResponse (buf + headerSize, len - headerSize); - if (m_State != eSessionStateEstablished) - m_Server.DeleteSession (shared_from_this ()); - break; - case PAYLOAD_TYPE_RELAY_REQUEST: - LogPrint (eLogDebug, "SSU: Relay request received"); - ProcessRelayRequest (buf + headerSize, len - headerSize, senderEndpoint); - break; - case PAYLOAD_TYPE_RELAY_INTRO: - LogPrint (eLogDebug, "SSU: Relay intro received"); - ProcessRelayIntro (buf + headerSize, len - headerSize); - break; - default: - LogPrint (eLogWarning, "SSU: Unexpected payload type ", (int)header->GetPayloadType ()); - } - } - - void SSUSession::ProcessSessionRequest (const uint8_t * buf, size_t len) - { - LogPrint (eLogDebug, "SSU message: Session request"); - bool sendRelayTag = true; - auto headerSize = sizeof (SSUHeader); - if (((SSUHeader *)buf)->IsExtendedOptions ()) - { - uint8_t extendedOptionsLen = buf[headerSize]; - headerSize++; - if (extendedOptionsLen >= 2) // options are presented - { - uint16_t flags = bufbe16toh (buf + headerSize); - sendRelayTag = flags & EXTENDED_OPTIONS_FLAG_REQUEST_RELAY_TAG; - } - headerSize += extendedOptionsLen; - } - if (headerSize >= len) - { - LogPrint (eLogError, "SSU message: Session request header size ", headerSize, " exceeds packet length ", len); - return; - } - if (!m_DHKeysPair) - { - auto pair = std::make_shared (); - pair->GenerateKeys (); - m_DHKeysPair = pair; - } - CreateAESandMacKey (buf + headerSize); - SendSessionCreated (buf + headerSize, sendRelayTag); - } - - void SSUSession::ProcessSessionCreated (uint8_t * buf, size_t len) - { - if (!IsOutgoing () || !m_DHKeysPair) - { - LogPrint (eLogWarning, "SSU: Unsolicited session created message"); - return; - } - - LogPrint (eLogDebug, "SSU message: session created"); - m_ConnectTimer.cancel (); // connect timer - SignedData s; // x,y, our IP, our port, remote IP, remote port, relayTag, signed on time - auto headerSize = GetSSUHeaderSize (buf); - if (headerSize >= len) - { - LogPrint (eLogError, "SSU message: Session created header size ", headerSize, " exceeds packet length ", len); - return; - } - uint8_t * payload = buf + headerSize; - uint8_t * y = payload; - CreateAESandMacKey (y); - s.Insert (m_DHKeysPair->GetPublicKey (), 256); // x - s.Insert (y, 256); // y - payload += 256; - boost::asio::ip::address ourIP; - uint16_t ourPort = 0; - auto addressAndPortLen = ExtractIPAddressAndPort (payload, len, ourIP, ourPort); - if (!addressAndPortLen) return; - uint8_t * ourAddressAndPort = payload + 1; - payload += addressAndPortLen; - addressAndPortLen--; // -1 byte address size - s.Insert (ourAddressAndPort, addressAndPortLen); // address + port - if (m_RemoteEndpoint.address ().is_v4 ()) - s.Insert (m_RemoteEndpoint.address ().to_v4 ().to_bytes ().data (), 4); // remote IP v4 - else - s.Insert (m_RemoteEndpoint.address ().to_v6 ().to_bytes ().data (), 16); // remote IP v6 - s.Insert (htobe16 (m_RemoteEndpoint.port ())); // remote port - s.Insert (payload, 8); // relayTag and signed on time - m_RelayTag = bufbe32toh (payload); - payload += 4; // relayTag - uint32_t signedOnTime = bufbe32toh(payload); - payload += 4; // signed on time - // decrypt signature - size_t signatureLen = m_RemoteIdentity->GetSignatureLen (); - size_t paddingSize = signatureLen & 0x0F; // %16 - if (paddingSize > 0) signatureLen += (16 - paddingSize); - //TODO: since we are accessing a uint8_t this is unlikely to crash due to alignment but should be improved - m_SessionKeyDecryption.SetIV (((SSUHeader *)buf)->iv); - m_SessionKeyDecryption.Decrypt (payload, signatureLen, payload); // TODO: non-const payload - // verify signature - if (s.Verify (m_RemoteIdentity, payload)) - { - if (ourIP.is_v4 () && i2p::context.GetStatus () == eRouterStatusTesting) - { - auto ts = i2p::util::GetSecondsSinceEpoch (); - int offset = (int)ts - signedOnTime; - if (m_Server.IsSyncClockFromPeers ()) - { - if (std::abs (offset) > SSU_CLOCK_THRESHOLD) - { - LogPrint (eLogWarning, "SSU: Clock adjusted by ", -offset, " seconds"); - i2p::util::AdjustTimeOffset (-offset); - } - } - else if (std::abs (offset) > SSU_CLOCK_SKEW) - { - LogPrint (eLogError, "SSU: Clock skew detected ", offset, ". Check your clock"); - i2p::context.SetError (eRouterErrorClockSkew); - } - } - LogPrint (eLogInfo, "SSU: Our external address is ", ourIP.to_string (), ":", ourPort); - if (!i2p::util::net::IsInReservedRange (ourIP)) - { - i2p::context.UpdateAddress (ourIP); - SendSessionConfirmed (y, ourAddressAndPort, addressAndPortLen); - } - else - { - LogPrint (eLogError, "SSU: External address ", ourIP.to_string (), " is in reserved range"); - Failed (); - } - } - else - { - LogPrint (eLogError, "SSU: Message 'created' signature verification failed"); - Failed (); - } - } - - void SSUSession::ProcessSessionConfirmed (const uint8_t * buf, size_t len) - { - LogPrint (eLogDebug, "SSU: Session confirmed received"); - m_ConnectTimer.cancel (); - auto headerSize = GetSSUHeaderSize (buf); - if (headerSize >= len) - { - LogPrint (eLogError, "SSU: Session confirmed header size ", headerSize, " exceeds packet length ", len); - return; - } - const uint8_t * payload = buf + headerSize; - payload++; // identity fragment info - uint16_t identitySize = bufbe16toh (payload); - if (identitySize + headerSize + 7 > len) // 7 = fragment info + fragment size + signed on time - { - LogPrint (eLogError, "SSU: Session confirmed identity size ", identitySize, " exceeds packet length ", len); - return; - } - payload += 2; // size of identity fragment - auto identity = std::make_shared (payload, identitySize); - auto existing = i2p::data::netdb.FindRouter (identity->GetIdentHash ()); // check if exists already - SetRemoteIdentity (existing ? existing->GetRouterIdentity () : identity); - m_Data.UpdatePacketSize (m_RemoteIdentity->GetIdentHash ()); - payload += identitySize; // identity - auto ts = i2p::util::GetSecondsSinceEpoch (); - uint32_t signedOnTime = bufbe32toh(payload); - if (signedOnTime < ts - SSU_CLOCK_SKEW || signedOnTime > ts + SSU_CLOCK_SKEW) - { - LogPrint (eLogError, "SSU: Message 'confirmed' time difference ", (int)ts - signedOnTime, " exceeds clock skew"); - Failed (); - return; - } - if (m_SignedData) - m_SignedData->Insert (payload, 4); // insert Alice's signed on time - payload += 4; // signed-on time - size_t fullSize = (payload - buf) + m_RemoteIdentity->GetSignatureLen (); - size_t paddingSize = fullSize & 0x0F; // %16 - if (paddingSize > 0) paddingSize = 16 - paddingSize; - payload += paddingSize; - if (fullSize + paddingSize > len) - { - LogPrint (eLogError, "SSU: Session confirmed message is too short ", len); - return; - } - // verify signature - if (m_SignedData && m_SignedData->Verify (m_RemoteIdentity, payload)) - { - m_Data.Send (CreateDeliveryStatusMsg (0)); - Established (); - } - else - { - LogPrint (eLogError, "SSU: Message 'confirmed' signature verification failed"); - Failed (); - } - } - - void SSUSession::SendSessionRequest () - { - uint8_t buf[320 + 18] = {0}; // 304 bytes for ipv4, 320 for ipv6 - uint8_t * payload = buf + sizeof (SSUHeader); - uint8_t flag = 0; - // fill extended options, 3 bytes extended options don't change message size - bool isV4 = m_RemoteEndpoint.address ().is_v4 (); - if ((isV4 && i2p::context.GetStatus () == eRouterStatusOK) || - (!isV4 && i2p::context.GetStatusV6 () == eRouterStatusOK)) // we don't need relays - { - // tell out peer to now assign relay tag - flag = SSU_HEADER_EXTENDED_OPTIONS_INCLUDED; - *payload = 2; payload++; // 1 byte length - uint16_t flags = 0; // clear EXTENDED_OPTIONS_FLAG_REQUEST_RELAY_TAG - htobe16buf (payload, flags); - payload += 2; - } - // fill payload - memcpy (payload, m_DHKeysPair->GetPublicKey (), 256); // x - if (isV4) - { - payload[256] = 4; - memcpy (payload + 257, m_RemoteEndpoint.address ().to_v4 ().to_bytes ().data(), 4); - } - else - { - payload[256] = 16; - memcpy (payload + 257, m_RemoteEndpoint.address ().to_v6 ().to_bytes ().data(), 16); - } - // encrypt and send - uint8_t iv[16]; - RAND_bytes (iv, 16); // random iv - FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_REQUEST, buf, isV4 ? 304 : 320, m_IntroKey, iv, m_IntroKey, flag); - m_Server.Send (buf, isV4 ? 304 : 320, m_RemoteEndpoint); - } - - void SSUSession::SendRelayRequest (const i2p::data::RouterInfo::Introducer& introducer, uint32_t nonce) - { - auto address = IsV6 () ? i2p::context.GetRouterInfo ().GetSSUV6Address () : - i2p::context.GetRouterInfo ().GetSSUAddress (true); - if (!address) - { - LogPrint (eLogInfo, "SSU: SSU is not supported"); - return; - } - - uint8_t buf[96 + 18] = {0}; - uint8_t * payload = buf + sizeof (SSUHeader); - htobe32buf (payload, introducer.iTag); - payload += 4; - *payload = 0; // no address - payload++; - htobuf16(payload, 0); // port = 0 - payload += 2; - *payload = 0; // challenge - payload++; - memcpy (payload, (const uint8_t *)address->i, 32); - payload += 32; - htobe32buf (payload, nonce); // nonce - - uint8_t iv[16]; - RAND_bytes (iv, 16); // random iv - if (m_State == eSessionStateEstablished) - FillHeaderAndEncrypt (PAYLOAD_TYPE_RELAY_REQUEST, buf, 96, m_SessionKey, iv, m_MacKey); - else - FillHeaderAndEncrypt (PAYLOAD_TYPE_RELAY_REQUEST, buf, 96, introducer.iKey, iv, introducer.iKey); - m_Server.Send (buf, 96, m_RemoteEndpoint); - LogPrint (eLogDebug, "SSU: Relay request sent"); - } - - void SSUSession::SendSessionCreated (const uint8_t * x, bool sendRelayTag) - { - auto address = IsV6 () ? i2p::context.GetRouterInfo ().GetSSUV6Address () : - i2p::context.GetRouterInfo ().GetSSUAddress (true); //v4 only - if (!address) - { - LogPrint (eLogInfo, "SSU: SSU is not supported"); - return; - } - SignedData s; // x,y, remote IP, remote port, our IP, our port, relayTag, signed on time - s.Insert (x, 256); // x - - uint8_t buf[384 + 18] = {0}; - uint8_t * payload = buf + sizeof (SSUHeader); - memcpy (payload, m_DHKeysPair->GetPublicKey (), 256); - s.Insert (payload, 256); // y - payload += 256; - if (m_RemoteEndpoint.address ().is_v4 ()) - { - // ipv4 - *payload = 4; - payload++; - memcpy (payload, m_RemoteEndpoint.address ().to_v4 ().to_bytes ().data(), 4); - s.Insert (payload, 4); // remote endpoint IP V4 - payload += 4; - } - else - { - // ipv6 - *payload = 16; - payload++; - memcpy (payload, m_RemoteEndpoint.address ().to_v6 ().to_bytes ().data(), 16); - s.Insert (payload, 16); // remote endpoint IP V6 - payload += 16; - } - htobe16buf (payload, m_RemoteEndpoint.port ()); - s.Insert (payload, 2); // remote port - payload += 2; - if (address->host.is_v4 ()) - s.Insert (address->host.to_v4 ().to_bytes ().data (), 4); // our IP V4 - else - s.Insert (address->host.to_v6 ().to_bytes ().data (), 16); // our IP V6 - s.Insert (htobe16 (address->port)); // our port - if (sendRelayTag && i2p::context.GetRouterInfo ().IsIntroducer (!IsV6 ())) - { - RAND_bytes((uint8_t *)&m_SentRelayTag, 4); - if (!m_SentRelayTag) m_SentRelayTag = 1; - } - htobe32buf (payload, m_SentRelayTag); - payload += 4; // relay tag - htobe32buf (payload, i2p::util::GetSecondsSinceEpoch ()); // signed on time - payload += 4; - s.Insert (payload - 8, 4); // relayTag - // we have to store this signed data for session confirmed - // same data but signed on time, it will Alice's there - m_SignedData = std::unique_ptr(new SignedData (s)); - s.Insert (payload - 4, 4); // BOB's signed on time - s.Sign (i2p::context.GetPrivateKeys (), payload); // DSA signature - - uint8_t iv[16]; - RAND_bytes (iv, 16); // random iv - // encrypt signature and padding with newly created session key - size_t signatureLen = i2p::context.GetIdentity ()->GetSignatureLen (); - size_t paddingSize = signatureLen & 0x0F; // %16 - if (paddingSize > 0) - { - // fill random padding - RAND_bytes(payload + signatureLen, (16 - paddingSize)); - signatureLen += (16 - paddingSize); - } - m_SessionKeyEncryption.SetIV (iv); - m_SessionKeyEncryption.Encrypt (payload, signatureLen, payload); - payload += signatureLen; - size_t msgLen = payload - buf; - - // encrypt message with intro key - FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_CREATED, buf, msgLen, m_IntroKey, iv, m_IntroKey); - Send (buf, msgLen); - } - - void SSUSession::SendSessionConfirmed (const uint8_t * y, const uint8_t * ourAddress, size_t ourAddressLen) - { - uint8_t buf[512 + 18] = {0}; - uint8_t * payload = buf + sizeof (SSUHeader); - *payload = 1; // 1 fragment - payload++; // info - size_t identLen = i2p::context.GetIdentity ()->GetFullLen (); // 387+ bytes - htobe16buf (payload, identLen); - payload += 2; // cursize - i2p::context.GetIdentity ()->ToBuffer (payload, identLen); - payload += identLen; - uint32_t signedOnTime = i2p::util::GetSecondsSinceEpoch (); - htobe32buf (payload, signedOnTime); // signed on time - payload += 4; - auto signatureLen = i2p::context.GetIdentity ()->GetSignatureLen (); - size_t paddingSize = ((payload - buf) + signatureLen)%16; - if (paddingSize > 0) paddingSize = 16 - paddingSize; - RAND_bytes(payload, paddingSize); // fill padding with random - payload += paddingSize; // padding size - // signature - SignedData s; // x,y, our IP, our port, remote IP, remote port, relayTag, our signed on time - s.Insert (m_DHKeysPair->GetPublicKey (), 256); // x - s.Insert (y, 256); // y - s.Insert (ourAddress, ourAddressLen); // our address/port as seem by party - if (m_RemoteEndpoint.address ().is_v4 ()) - s.Insert (m_RemoteEndpoint.address ().to_v4 ().to_bytes ().data (), 4); // remote IP V4 - else - s.Insert (m_RemoteEndpoint.address ().to_v6 ().to_bytes ().data (), 16); // remote IP V6 - s.Insert (htobe16 (m_RemoteEndpoint.port ())); // remote port - s.Insert (htobe32 (m_RelayTag)); // relay tag - s.Insert (htobe32 (signedOnTime)); // signed on time - s.Sign (i2p::context.GetPrivateKeys (), payload); // DSA signature - payload += signatureLen; - - size_t msgLen = payload - buf; - uint8_t iv[16]; - RAND_bytes (iv, 16); // random iv - // encrypt message with session key - FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_CONFIRMED, buf, msgLen, m_SessionKey, iv, m_MacKey); - Send (buf, msgLen); - } - - void SSUSession::ProcessRelayRequest (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& from) - { - uint32_t relayTag = bufbe32toh (buf); - auto session = m_Server.FindRelaySession (relayTag); - if (session) - { - buf += 4; // relay tag - uint8_t size = *buf; - buf++; // size - buf += size; // address - buf += 2; // port - uint8_t challengeSize = *buf; - buf++; // challenge size - buf += challengeSize; - const uint8_t * introKey = buf; - buf += 32; // introkey - uint32_t nonce = bufbe32toh (buf); - SendRelayResponse (nonce, from, introKey, session->m_RemoteEndpoint); - SendRelayIntro (session, from); - } - } - - void SSUSession::SendRelayResponse (uint32_t nonce, const boost::asio::ip::udp::endpoint& from, - const uint8_t * introKey, const boost::asio::ip::udp::endpoint& to) - { - bool isV4 = to.address ().is_v4 (); // Charle's - bool isV4A = from.address ().is_v4 (); // Alice's - if ((isV4 && !isV4A) || (!isV4 && isV4A)) - { - LogPrint (eLogWarning, "SSU: Charlie's IP and Alice's IP belong to different networks for relay response"); - return; - } - uint8_t buf[80 + 18] = {0}; // 64 for ipv4 and 80 for ipv6 - uint8_t * payload = buf + sizeof (SSUHeader); - // Charlie - if (isV4) - { - *payload = 4; - payload++; // size - memcpy (payload, to.address ().to_v4 ().to_bytes ().data (), 4); // Charlie's IP V4 - payload += 4; // address - } - else - { - *payload = 16; - payload++; // size - memcpy (payload, to.address ().to_v6 ().to_bytes ().data (), 16); // Charlie's IP V6 - payload += 16; // address - } - htobe16buf (payload, to.port ()); // Charlie's port - payload += 2; // port - // Alice - if (isV4) - { - *payload = 4; - payload++; // size - memcpy (payload, from.address ().to_v4 ().to_bytes ().data (), 4); // Alice's IP V4 - payload += 4; // address - } - else - { - *payload = 16; - payload++; // size - memcpy (payload, from.address ().to_v6 ().to_bytes ().data (), 16); // Alice's IP V6 - payload += 16; // address - } - htobe16buf (payload, from.port ()); // Alice's port - payload += 2; // port - htobe32buf (payload, nonce); - - if (m_State == eSessionStateEstablished) - { - // encrypt with session key - FillHeaderAndEncrypt (PAYLOAD_TYPE_RELAY_RESPONSE, buf, isV4 ? 64 : 80); - Send (buf, isV4 ? 64 : 80); - } - else - { - // ecrypt with Alice's intro key - uint8_t iv[16]; - RAND_bytes (iv, 16); // random iv - FillHeaderAndEncrypt (PAYLOAD_TYPE_RELAY_RESPONSE, buf, isV4 ? 64 : 80, introKey, iv, introKey); - m_Server.Send (buf, isV4 ? 64 : 80, from); - } - LogPrint (eLogDebug, "SSU: Relay response sent"); - } - - void SSUSession::SendRelayIntro (std::shared_ptr session, const boost::asio::ip::udp::endpoint& from) - { - if (!session) return; - bool isV4 = from.address ().is_v4 (); // Alice's - bool isV4C = session->m_RemoteEndpoint.address ().is_v4 (); // Charlie's - if ((isV4 && !isV4C) || (!isV4 && isV4C)) - { - LogPrint (eLogWarning, "SSU: Charlie's IP and Alice's IP belong to different networks for relay intro"); - return; - } - uint8_t buf[64 + 18] = {0}; // 48 for ipv4 and 64 for ipv6 - uint8_t * payload = buf + sizeof (SSUHeader); - if (isV4) - { - *payload = 4; - payload++; // size - memcpy (payload, from.address ().to_v4 ().to_bytes ().data (), 4); // Alice's IP V4 - payload += 4; // address - } - else - { - *payload = 16; - payload++; // size - memcpy (payload, from.address ().to_v6 ().to_bytes ().data (), 16); // Alice's IP V6 - payload += 16; // address - } - htobe16buf (payload, from.port ()); // Alice's port - payload += 2; // port - *payload = 0; // challenge size - uint8_t iv[16]; - RAND_bytes (iv, 16); // random iv - FillHeaderAndEncrypt (PAYLOAD_TYPE_RELAY_INTRO, buf, isV4 ? 48 : 64, session->m_SessionKey, iv, session->m_MacKey); - m_Server.Send (buf, isV4 ? 48 : 64, session->m_RemoteEndpoint); - LogPrint (eLogDebug, "SSU: Relay intro sent"); - } - - void SSUSession::ProcessRelayResponse (const uint8_t * buf, size_t len) - { - LogPrint (eLogDebug, "SSU message: Relay response received"); - boost::asio::ip::address remoteIP; - uint16_t remotePort = 0; - auto remoteSize = ExtractIPAddressAndPort (buf, len, remoteIP, remotePort); - if (!remoteSize) return; - buf += remoteSize; len -= remoteSize; - boost::asio::ip::address ourIP; - uint16_t ourPort = 0; - auto ourSize = ExtractIPAddressAndPort (buf, len, ourIP, ourPort); - if (!ourSize) return; - buf += ourSize; len -= ourSize; - LogPrint (eLogInfo, "SSU: Our external address is ", ourIP.to_string (), ":", ourPort); - if (!i2p::util::net::IsInReservedRange (ourIP)) - i2p::context.UpdateAddress (ourIP); - else - LogPrint (eLogError, "SSU: External address ", ourIP.to_string (), " is in reserved range"); - if (ourIP.is_v4 ()) - { - if (ourPort != m_Server.GetPort ()) - { - if (i2p::context.GetStatus () == eRouterStatusTesting) - i2p::context.SetError (eRouterErrorSymmetricNAT); - } - else if (i2p::context.GetError () == eRouterErrorSymmetricNAT) - i2p::context.SetError (eRouterErrorNone); - } - uint32_t nonce = bufbe32toh (buf); - buf += 4; // nonce - auto it = m_RelayRequests.find (nonce); - if (it != m_RelayRequests.end ()) - { - // check if we are waiting for introduction - boost::asio::ip::udp::endpoint remoteEndpoint (remoteIP, remotePort); - if (!m_Server.FindSession (remoteEndpoint)) - { - // we didn't have correct endpoint when sent relay request - // now we do - LogPrint (eLogInfo, "SSU: RelayReponse connecting to endpoint ", remoteEndpoint); - if ((remoteIP.is_v4 () && i2p::context.GetStatus () == eRouterStatusFirewalled) || - (remoteIP.is_v6 () && i2p::context.GetStatusV6 () == eRouterStatusFirewalled)) - m_Server.Send (buf, 0, remoteEndpoint); // send HolePunch - // we assume that HolePunch has been sent by this time and our SessionRequest will go through - m_Server.CreateDirectSession (it->second.first, remoteEndpoint, false); - } - // delete request - m_RelayRequests.erase (it); - // cancel connect timer - m_ConnectTimer.cancel (); - } - else - LogPrint (eLogError, "SSU: Unsolicited RelayResponse, nonce=", nonce); - } - - void SSUSession::ProcessRelayIntro (const uint8_t * buf, size_t len) - { - boost::asio::ip::address ip; - uint16_t port = 0; - ExtractIPAddressAndPort (buf, len, ip, port); - if (!ip.is_unspecified () && port) - // send hole punch of 0 bytes - m_Server.Send (buf, 0, boost::asio::ip::udp::endpoint (ip, port)); - } - - void SSUSession::FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len, - const i2p::crypto::AESKey& aesKey, const uint8_t * iv, const i2p::crypto::MACKey& macKey, uint8_t flag) - { - if (len < sizeof (SSUHeader)) - { - LogPrint (eLogError, "SSU: Unexpected packet length ", len); - return; - } - SSUHeader * header = (SSUHeader *)buf; - memcpy (header->iv, iv, 16); - header->flag = flag | (payloadType << 4); // MSB is 0 - htobe32buf (header->time, i2p::util::GetSecondsSinceEpoch ()); - uint8_t * encrypted = &header->flag; - uint16_t encryptedLen = len - (encrypted - buf); - i2p::crypto::CBCEncryption encryption; - encryption.SetKey (aesKey); - encryption.SetIV (iv); - encryption.Encrypt (encrypted, encryptedLen, encrypted); - // assume actual buffer size is 18 (16 + 2) bytes more - memcpy (buf + len, iv, 16); - uint16_t netid = i2p::context.GetNetID (); - htobe16buf (buf + len + 16, (netid == I2PD_NET_ID) ? encryptedLen : encryptedLen ^ ((netid - 2) << 8)); - i2p::crypto::HMACMD5Digest (encrypted, encryptedLen + 18, macKey, header->mac); - } - - void SSUSession::FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len) - { - FillHeaderAndEncrypt (payloadType, buf, len, buf); - } - - void SSUSession::FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * in, size_t len, uint8_t * out) - { - if (len < sizeof (SSUHeader)) - { - LogPrint (eLogError, "SSU: Unexpected packet length ", len); - return; - } - SSUHeader * header = (SSUHeader *)out; - RAND_bytes (header->iv, 16); // random iv - m_SessionKeyEncryption.SetIV (header->iv); - SSUHeader * inHeader = (SSUHeader *)in; - inHeader->flag = payloadType << 4; // MSB is 0 - htobe32buf (inHeader->time, i2p::util::GetSecondsSinceEpoch ()); - uint8_t * encrypted = &header->flag, * clear = &inHeader->flag; - uint16_t encryptedLen = len - (encrypted - out); - m_SessionKeyEncryption.Encrypt (clear, encryptedLen, encrypted); - // assume actual out buffer size is 18 (16 + 2) bytes more - memcpy (out + len, header->iv, 16); - uint16_t netid = i2p::context.GetNetID (); - htobe16buf (out + len + 16, (netid == I2PD_NET_ID) ? encryptedLen : encryptedLen ^ ((netid - 2) << 8)); - i2p::crypto::HMACMD5Digest (encrypted, encryptedLen + 18, m_MacKey, header->mac); - } - - void SSUSession::Decrypt (uint8_t * buf, size_t len, const i2p::crypto::AESKey& aesKey) - { - if (len < sizeof (SSUHeader)) - { - LogPrint (eLogError, "SSU: Unexpected packet length ", len); - return; - } - SSUHeader * header = (SSUHeader *)buf; - uint8_t * encrypted = &header->flag; - uint16_t encryptedLen = len - (encrypted - buf); - i2p::crypto::CBCDecryption decryption; - decryption.SetKey (aesKey); - decryption.SetIV (header->iv); - decryption.Decrypt (encrypted, encryptedLen, encrypted); - } - - void SSUSession::DecryptSessionKey (uint8_t * buf, size_t len) - { - if (len < sizeof (SSUHeader)) - { - LogPrint (eLogError, "SSU: Unexpected packet length ", len); - return; - } - SSUHeader * header = (SSUHeader *)buf; - uint8_t * encrypted = &header->flag; - uint16_t encryptedLen = len - (encrypted - buf); - if (encryptedLen > 0) - { - m_SessionKeyDecryption.SetIV (header->iv); - m_SessionKeyDecryption.Decrypt (encrypted, encryptedLen, encrypted); - } - } - - bool SSUSession::Validate (uint8_t * buf, size_t len, const i2p::crypto::MACKey& macKey) - { - if (len < sizeof (SSUHeader)) - { - LogPrint (eLogError, "SSU: Unexpected packet length ", len); - return false; - } - SSUHeader * header = (SSUHeader *)buf; - uint8_t * encrypted = &header->flag; - uint16_t encryptedLen = len - (encrypted - buf); - // assume actual buffer size is 18 (16 + 2) bytes more - memcpy (buf + len, header->iv, 16); - uint16_t netid = i2p::context.GetNetID (); - htobe16buf (buf + len + 16, (netid == I2PD_NET_ID) ? encryptedLen : encryptedLen ^ ((netid - 2) << 8)); - uint8_t digest[16]; - i2p::crypto::HMACMD5Digest (encrypted, encryptedLen + 18, macKey, digest); - return !memcmp (header->mac, digest, 16); - } - - void SSUSession::Connect () - { - if (m_State == eSessionStateUnknown) - { - ScheduleConnectTimer (); // set connect timer - m_DHKeysPair = std::make_shared (); - m_DHKeysPair->GenerateKeys (); - SendSessionRequest (); - } - } - - void SSUSession::WaitForConnect () - { - if (!IsOutgoing ()) // incoming session - ScheduleConnectTimer (); - else - LogPrint (eLogError, "SSU: Wait for connect for outgoing session"); - } - - void SSUSession::ScheduleConnectTimer () - { - m_ConnectTimer.cancel (); - m_ConnectTimer.expires_from_now (boost::posix_time::seconds(SSU_CONNECT_TIMEOUT)); - m_ConnectTimer.async_wait (std::bind (&SSUSession::HandleConnectTimer, - shared_from_this (), std::placeholders::_1)); -} - - void SSUSession::HandleConnectTimer (const boost::system::error_code& ecode) - { - if (!ecode) - { - // timeout expired - LogPrint (eLogWarning, "SSU: Session with ", m_RemoteEndpoint, " was not established after ", SSU_CONNECT_TIMEOUT, " seconds"); - Failed (); - } - } - - void SSUSession::Introduce (const i2p::data::RouterInfo::Introducer& introducer, - std::shared_ptr to) - { - if (m_State == eSessionStateUnknown) - { - // set connect timer - m_ConnectTimer.expires_from_now (boost::posix_time::seconds(SSU_CONNECT_TIMEOUT)); - m_ConnectTimer.async_wait (std::bind (&SSUSession::HandleConnectTimer, - shared_from_this (), std::placeholders::_1)); - } - uint32_t nonce; - RAND_bytes ((uint8_t *)&nonce, 4); - auto ts = i2p::util::GetSecondsSinceEpoch (); - m_RelayRequests.emplace (nonce, std::make_pair (to, ts)); - SendRelayRequest (introducer, nonce); - } - - void SSUSession::WaitForIntroduction () - { - m_State = eSessionStateIntroduced; - // set connect timer - m_ConnectTimer.expires_from_now (boost::posix_time::seconds(SSU_CONNECT_TIMEOUT)); - m_ConnectTimer.async_wait (std::bind (&SSUSession::HandleConnectTimer, - shared_from_this (), std::placeholders::_1)); - } - - void SSUSession::Close () - { - SendSessionDestroyed (); - Reset (); - m_State = eSessionStateClosed; - } - - void SSUSession::Reset () - { - m_State = eSessionStateUnknown; - transports.PeerDisconnected (shared_from_this ()); - m_Data.Stop (); - m_ConnectTimer.cancel (); - if (m_SentRelayTag) - { - m_Server.RemoveRelay (m_SentRelayTag); // relay tag is not valid anymore - m_SentRelayTag = 0; - } - m_DHKeysPair = nullptr; - m_SignedData = nullptr; - m_IsSessionKey = false; - } - - void SSUSession::Done () - { - GetService ().post (std::bind (&SSUSession::Failed, shared_from_this ())); - } - - void SSUSession::Established () - { - m_State = eSessionStateEstablished; - m_DHKeysPair = nullptr; - m_SignedData = nullptr; - m_Data.Start (); - transports.PeerConnected (shared_from_this ()); - if (m_IsPeerTest) - SendPeerTest (); - if (m_SentRelayTag) - m_Server.AddRelay (m_SentRelayTag, shared_from_this ()); - m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch (); - } - - void SSUSession::Failed () - { - if (m_State != eSessionStateFailed) - { - m_State = eSessionStateFailed; - m_Server.DeleteSession (shared_from_this ()); - } - } - - void SSUSession::SendI2NPMessages (const std::vector >& msgs) - { - GetService ().post (std::bind (&SSUSession::PostI2NPMessages, shared_from_this (), msgs)); - } - - void SSUSession::PostI2NPMessages (std::vector > msgs) - { - if (m_State == eSessionStateEstablished) - { - for (const auto& it: msgs) - if (it) - { - if (it->GetLength () <= SSU_MAX_I2NP_MESSAGE_SIZE) - m_Data.Send (it); - else - LogPrint (eLogError, "SSU: I2NP message of size ", it->GetLength (), " can't be sent. Dropped"); - } - } - } - - void SSUSession::ProcessData (uint8_t * buf, size_t len) - { - m_Data.ProcessMessage (buf, len); - m_IsDataReceived = true; - } - - void SSUSession::FlushData () - { - if (m_IsDataReceived) - { - m_Data.FlushReceivedMessage (); - m_IsDataReceived = false; - } - } - - void SSUSession::CleanUp (uint64_t ts) - { - m_Data.CleanUp (ts); - for (auto it = m_RelayRequests.begin (); it != m_RelayRequests.end ();) - { - if (ts > it->second.second + SSU_CONNECT_TIMEOUT) - it = m_RelayRequests.erase (it); - else - ++it; - } - } - - void SSUSession::ProcessPeerTest (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint) - { - uint32_t nonce = bufbe32toh (buf); // 4 bytes - boost::asio::ip::address addr; // Alice's address - uint16_t port = 0; // and port - auto size = ExtractIPAddressAndPort (buf + 4, len - 4, addr, port); - if (port && (size != 7) && (size != 19)) - { - LogPrint (eLogWarning, "SSU: Address of ", size - 3, " bytes not supported"); - return; - } - const uint8_t * introKey = buf + 4 + size; - switch (m_Server.GetPeerTestParticipant (nonce)) - { - // existing test - case ePeerTestParticipantAlice1: - { - if (m_Server.GetPeerTestSession (nonce) == shared_from_this ()) // Alice-Bob - { - LogPrint (eLogDebug, "SSU: Peer test from Bob. We are Alice"); - if (IsV6 ()) - { - if (i2p::context.GetStatusV6 () == eRouterStatusTesting) - { - i2p::context.SetStatusV6 (eRouterStatusFirewalled); - m_Server.RescheduleIntroducersUpdateTimerV6 (); - } - } - else if (i2p::context.GetStatus () == eRouterStatusTesting) // still not OK - { - i2p::context.SetStatus (eRouterStatusFirewalled); - m_Server.RescheduleIntroducersUpdateTimer (); - } - } - else - { - LogPrint (eLogDebug, "SSU: First peer test from Charlie. We are Alice"); - if (m_State == eSessionStateEstablished) - LogPrint (eLogWarning, "SSU: First peer test from Charlie through established session. We are Alice"); - if (IsV6 ()) - i2p::context.SetStatusV6 (eRouterStatusOK); - else - i2p::context.SetStatus (eRouterStatusOK); - m_Server.UpdatePeerTest (nonce, ePeerTestParticipantAlice2); - SendPeerTest (nonce, senderEndpoint.address (), senderEndpoint.port (), introKey, true, false); // to Charlie - } - break; - } - case ePeerTestParticipantAlice2: - { - if (m_Server.GetPeerTestSession (nonce) == shared_from_this ()) // Alice-Bob - LogPrint (eLogDebug, "SSU: Peer test from Bob. We are Alice"); - else - { - // peer test successive - LogPrint (eLogDebug, "SSU: Second peer test from Charlie. We are Alice"); - if (IsV6 ()) - i2p::context.SetStatusV6 (eRouterStatusOK); - else - i2p::context.SetStatus (eRouterStatusOK); - m_Server.RemovePeerTest (nonce); - } - break; - } - case ePeerTestParticipantBob: - { - LogPrint (eLogDebug, "SSU: Peer test from Charlie. We are Bob"); - auto session = m_Server.GetPeerTestSession (nonce); // session with Alice from PeerTest - if (session && session->m_State == eSessionStateEstablished) - { - const auto& ep = session->GetRemoteEndpoint (); // Alice's endpoint as known to Bob - session->SendPeerTest (nonce, ep.address (), ep.port (), introKey, false, true); // send back to Alice - } - m_Server.RemovePeerTest (nonce); // nonce has been used - break; - } - case ePeerTestParticipantCharlie: - { - LogPrint (eLogDebug, "SSU: Peer test from Alice. We are Charlie"); - SendPeerTest (nonce, senderEndpoint.address (), senderEndpoint.port (), introKey); // to Alice with her actual address - m_Server.RemovePeerTest (nonce); // nonce has been used - break; - } - // test not found - case ePeerTestParticipantUnknown: - { - if (m_State == eSessionStateEstablished) - { - // new test - if (port) - { - LogPrint (eLogDebug, "SSU: Peer test from Bob. We are Charlie"); - Send (PAYLOAD_TYPE_PEER_TEST, buf, len); // back to Bob - if (!addr.is_unspecified () && !i2p::util::net::IsInReservedRange(addr)) - { - m_Server.NewPeerTest (nonce, ePeerTestParticipantCharlie); - SendPeerTest (nonce, addr, port, introKey); // to Alice with her address received from Bob - } - } - else - { - LogPrint (eLogDebug, "SSU: Peer test from Alice. We are Bob"); - auto session = senderEndpoint.address ().is_v4 () ? m_Server.GetRandomEstablishedV4Session (shared_from_this ()) : m_Server.GetRandomEstablishedV6Session (shared_from_this ()); // Charlie - if (session) - { - m_Server.NewPeerTest (nonce, ePeerTestParticipantBob, shared_from_this ()); - session->SendPeerTest (nonce, senderEndpoint.address (), senderEndpoint.port (), introKey, false); // to Charlie with Alice's actual address - } - } - } - else - LogPrint (eLogError, "SSU: Unexpected peer test"); - } - } - } - - void SSUSession::SendPeerTest (uint32_t nonce, const boost::asio::ip::address& address, uint16_t port, - const uint8_t * introKey, bool toAddress, bool sendAddress) - // toAddress is true for Alice<->Chalie communications only - // sendAddress is false if message comes from Alice - { - uint8_t buf[80 + 18] = {0}; - uint8_t iv[16]; - uint8_t * payload = buf + sizeof (SSUHeader); - htobe32buf (payload, nonce); - payload += 4; // nonce - // address and port - if (sendAddress) - { - if (address.is_v4 ()) - { - *payload = 4; - memcpy (payload + 1, address.to_v4 ().to_bytes ().data (), 4); // our IP V4 - } - else if (address.is_v6 ()) - { - *payload = 16; - memcpy (payload + 1, address.to_v6 ().to_bytes ().data (), 16); // our IP V6 - } - else - *payload = 0; - payload += (payload[0] + 1); - } - else - { - *payload = 0; - payload++; //size - } - htobe16buf (payload, port); - payload += 2; // port - // intro key - if (toAddress) - { - // send our intro key to address instead of its own - auto addr = address.is_v4 () ? i2p::context.GetRouterInfo ().GetSSUAddress (true) : // ipv4 - i2p::context.GetRouterInfo ().GetSSUV6Address (); - if (addr) - memcpy (payload, addr->i, 32); // intro key - else - LogPrint (eLogInfo, "SSU: SSU is not supported. Can't send peer test"); - } - else - memcpy (payload, introKey, 32); // intro key - - // send - RAND_bytes (iv, 16); // random iv - if (toAddress) - { - // encrypt message with specified intro key - FillHeaderAndEncrypt (PAYLOAD_TYPE_PEER_TEST, buf, 80, introKey, iv, introKey); - boost::asio::ip::udp::endpoint e (address, port); - m_Server.Send (buf, 80, e); - } - else - { - // encrypt message with session key - FillHeaderAndEncrypt (PAYLOAD_TYPE_PEER_TEST, buf, 80); - Send (buf, 80); - } - } - - void SSUSession::SendPeerTest () - { - // we are Alice - LogPrint (eLogDebug, "SSU: Sending peer test"); - auto address = IsV6 () ? i2p::context.GetRouterInfo ().GetSSUV6Address () : i2p::context.GetRouterInfo ().GetSSUAddress (true); - if (!address) - { - LogPrint (eLogInfo, "SSU: SSU is not supported. Can't send peer test"); - return; - } - uint32_t nonce; - RAND_bytes ((uint8_t *)&nonce, 4); - if (!nonce) nonce = 1; - m_IsPeerTest = false; - m_Server.NewPeerTest (nonce, ePeerTestParticipantAlice1, shared_from_this ()); - SendPeerTest (nonce, boost::asio::ip::address(), 0, address->i, false, false); // address and port always zero for Alice - } - - void SSUSession::SendKeepAlive () - { - if (m_State == eSessionStateEstablished) - { - uint8_t buf[48 + 18] = {0}; - uint8_t * payload = buf + sizeof (SSUHeader); - *payload = 0; // flags - payload++; - *payload = 0; // num fragments - // encrypt message with session key - FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, 48); - Send (buf, 48); - LogPrint (eLogDebug, "SSU: keep-alive sent"); - m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch (); - } - } - - void SSUSession::SendSessionDestroyed () - { - if (m_IsSessionKey) - { - uint8_t buf[48 + 18] = {0}; - // encrypt message with session key - FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_DESTROYED, buf, 48); - try - { - Send (buf, 48); - } - catch (std::exception& ex) - { - LogPrint (eLogWarning, "SSU: Exception while sending session destoroyed: ", ex.what ()); - } - LogPrint (eLogDebug, "SSU: Session destroyed sent"); - } - } - - void SSUSession::Send (uint8_t type, const uint8_t * payload, size_t len) - { - uint8_t buf[SSU_MTU_V4 + 18] = {0}; - size_t msgSize = len + sizeof (SSUHeader); - size_t paddingSize = msgSize & 0x0F; // %16 - if (paddingSize > 0) msgSize += (16 - paddingSize); - if (msgSize > SSU_MTU_V4) - { - LogPrint (eLogWarning, "SSU: Payload size ", msgSize, " exceeds MTU"); - return; - } - memcpy (buf + sizeof (SSUHeader), payload, len); - // encrypt message with session key - FillHeaderAndEncrypt (type, buf, msgSize); - Send (buf, msgSize); - } - - void SSUSession::Send (const uint8_t * buf, size_t size) - { - m_NumSentBytes += size; - i2p::transport::transports.UpdateSentBytes (size); - m_Server.Send (buf, size, m_RemoteEndpoint); - } - - size_t SSUSession::ExtractIPAddressAndPort (const uint8_t * buf, size_t len, boost::asio::ip::address& ip, uint16_t& port) - { - if (!len) return 0; - uint8_t size = *buf; - size_t s = 1 + size + 2; // size + address + port - if (len < s) - { - LogPrint (eLogWarning, "SSU: Address is too short ", len); - port = 0; - return len; - } - buf++; // size - if (size == 4) - { - boost::asio::ip::address_v4::bytes_type bytes; - memcpy (bytes.data (), buf, 4); - ip = boost::asio::ip::address_v4 (bytes); - } - else if (size == 16) - { - boost::asio::ip::address_v6::bytes_type bytes; - memcpy (bytes.data (), buf, 16); - ip = boost::asio::ip::address_v6 (bytes); - } - else - LogPrint (eLogWarning, "SSU: Address size ", int(size), " is not supported"); - buf += size; - port = bufbe16toh (buf); - return s; - } -} -} diff --git a/libi2pd/SSUSession.h b/libi2pd/SSUSession.h deleted file mode 100644 index e28b4991..00000000 --- a/libi2pd/SSUSession.h +++ /dev/null @@ -1,177 +0,0 @@ -/* -* Copyright (c) 2013-2022, The PurpleI2P Project -* -* This file is part of Purple i2pd project and licensed under BSD3 -* -* See full license text in LICENSE file at top of project tree -*/ - -#ifndef SSU_SESSION_H__ -#define SSU_SESSION_H__ - -#include -#include -#include -#include "Crypto.h" -#include "I2NPProtocol.h" -#include "TransportSession.h" -#include "SSUData.h" - -namespace i2p -{ -namespace transport -{ - const uint8_t SSU_HEADER_EXTENDED_OPTIONS_INCLUDED = 0x04; - struct SSUHeader - { - uint8_t mac[16]; - uint8_t iv[16]; - uint8_t flag; - uint8_t time[4]; - - uint8_t GetPayloadType () const { return flag >> 4; }; - bool IsExtendedOptions () const { return flag & SSU_HEADER_EXTENDED_OPTIONS_INCLUDED; }; - }; - - const int SSU_CONNECT_TIMEOUT = 5; // 5 seconds - const int SSU_TERMINATION_TIMEOUT = 330; // 5.5 minutes - const int SSU_CLOCK_SKEW = 60; // in seconds - const int SSU_CLOCK_THRESHOLD = 15; // in seconds, if more we should adjust - const size_t SSU_MAX_I2NP_MESSAGE_SIZE = 32768; - - // payload types (4 bits) - const uint8_t PAYLOAD_TYPE_SESSION_REQUEST = 0; - const uint8_t PAYLOAD_TYPE_SESSION_CREATED = 1; - const uint8_t PAYLOAD_TYPE_SESSION_CONFIRMED = 2; - const uint8_t PAYLOAD_TYPE_RELAY_REQUEST = 3; - const uint8_t PAYLOAD_TYPE_RELAY_RESPONSE = 4; - const uint8_t PAYLOAD_TYPE_RELAY_INTRO = 5; - const uint8_t PAYLOAD_TYPE_DATA = 6; - const uint8_t PAYLOAD_TYPE_PEER_TEST = 7; - const uint8_t PAYLOAD_TYPE_SESSION_DESTROYED = 8; - - // extended options - const uint16_t EXTENDED_OPTIONS_FLAG_REQUEST_RELAY_TAG = 0x0001; - - enum SessionState - { - eSessionStateUnknown, - eSessionStateIntroduced, - eSessionStateEstablished, - eSessionStateClosed, - eSessionStateFailed - }; - - enum PeerTestParticipant - { - ePeerTestParticipantUnknown = 0, - ePeerTestParticipantAlice1, - ePeerTestParticipantAlice2, - ePeerTestParticipantBob, - ePeerTestParticipantCharlie - }; - - class SSUServer; - class SSUSession: public TransportSession, public std::enable_shared_from_this - { - public: - - SSUSession (SSUServer& server, boost::asio::ip::udp::endpoint& remoteEndpoint, - std::shared_ptr router = nullptr, bool peerTest = false); - void ProcessNextMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); - ~SSUSession (); - - void Connect (); - void WaitForConnect (); - void Introduce (const i2p::data::RouterInfo::Introducer& introducer, - std::shared_ptr to); // Alice to Charlie - void WaitForIntroduction (); - void Close (); - void Done (); - void Failed (); - const boost::asio::ip::udp::endpoint& GetRemoteEndpoint () { return m_RemoteEndpoint; }; - SSUServer& GetServer () { return m_Server; }; - - bool IsV6 () const { return m_RemoteEndpoint.address ().is_v6 (); }; - void SendI2NPMessages (const std::vector >& msgs); - void SendPeerTest (); // Alice - - SessionState GetState () const { return m_State; }; - size_t GetNumSentBytes () const { return m_NumSentBytes; }; - size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; }; - - void SendKeepAlive (); - uint32_t GetRelayTag () const { return m_RelayTag; }; - const i2p::data::RouterInfo::IntroKey& GetIntroKey () const { return m_IntroKey; }; - - void FlushData (); - void CleanUp (uint64_t ts); - - private: - - boost::asio::io_service& GetService (); - void CreateAESandMacKey (const uint8_t * pubKey); - size_t GetSSUHeaderSize (const uint8_t * buf) const; - void PostI2NPMessages (std::vector > msgs); - void ProcessMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); // call for established session - void ProcessSessionRequest (const uint8_t * buf, size_t len); - void SendSessionRequest (); - void SendRelayRequest (const i2p::data::RouterInfo::Introducer& introducer, uint32_t nonce); - void ProcessSessionCreated (uint8_t * buf, size_t len); - void SendSessionCreated (const uint8_t * x, bool sendRelayTag = true); - void ProcessSessionConfirmed (const uint8_t * buf, size_t len); - void SendSessionConfirmed (const uint8_t * y, const uint8_t * ourAddress, size_t ourAddressLen); - void ProcessRelayRequest (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& from); - void SendRelayResponse (uint32_t nonce, const boost::asio::ip::udp::endpoint& from, - const uint8_t * introKey, const boost::asio::ip::udp::endpoint& to); - void SendRelayIntro (std::shared_ptr session, const boost::asio::ip::udp::endpoint& from); - void ProcessRelayResponse (const uint8_t * buf, size_t len); - void ProcessRelayIntro (const uint8_t * buf, size_t len); - void Established (); - void ScheduleConnectTimer (); - void HandleConnectTimer (const boost::system::error_code& ecode); - void ProcessPeerTest (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); - void SendPeerTest (uint32_t nonce, const boost::asio::ip::address& address, uint16_t port, const uint8_t * introKey, bool toAddress = true, bool sendAddress = true); - void ProcessData (uint8_t * buf, size_t len); - void SendSessionDestroyed (); - void Send (uint8_t type, const uint8_t * payload, size_t len); // with session key - void Send (const uint8_t * buf, size_t size); - - void FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len, const i2p::crypto::AESKey& aesKey, - const uint8_t * iv, const i2p::crypto::MACKey& macKey, uint8_t flag = 0); - void FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len); // with session key - void FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * in, size_t len, uint8_t * out); // with session key - void Decrypt (uint8_t * buf, size_t len, const i2p::crypto::AESKey& aesKey); - void DecryptSessionKey (uint8_t * buf, size_t len); - bool Validate (uint8_t * buf, size_t len, const i2p::crypto::MACKey& macKey); - - void Reset (); - - static size_t ExtractIPAddressAndPort (const uint8_t * buf, size_t len, boost::asio::ip::address& ip, uint16_t& port); // returns actual buf size - - private: - - friend class SSUData; // TODO: change in later - SSUServer& m_Server; - const boost::asio::ip::udp::endpoint m_RemoteEndpoint; - boost::asio::deadline_timer m_ConnectTimer; - bool m_IsPeerTest; - SessionState m_State; - bool m_IsSessionKey; - uint32_t m_RelayTag; // received from peer - uint32_t m_SentRelayTag; // sent by us - i2p::crypto::CBCEncryption m_SessionKeyEncryption; - i2p::crypto::CBCDecryption m_SessionKeyDecryption; - i2p::crypto::AESKey m_SessionKey; - i2p::crypto::MACKey m_MacKey; - i2p::data::RouterInfo::IntroKey m_IntroKey; - SSUData m_Data; - bool m_IsDataReceived; - std::unique_ptr m_SignedData; // we need it for SessionConfirmed only - std::map, uint64_t > > m_RelayRequests; // nonce->(Charlie, timestamp) - std::shared_ptr m_DHKeysPair; // X - for client and Y - for server - }; -} -} - -#endif diff --git a/libi2pd/Timestamp.h b/libi2pd/Timestamp.h index 995ea36f..ff777257 100644 --- a/libi2pd/Timestamp.h +++ b/libi2pd/Timestamp.h @@ -25,7 +25,7 @@ namespace util uint32_t GetHoursSinceEpoch (); void GetCurrentDate (char * date); // returns date as YYYYMMDD string, 9 bytes - void GetDateString (uint64_t timestamp, char * date); // timestap is seconds since epoch, returns date as YYYYMMDD string, 9 bytes + void GetDateString (uint64_t timestamp, char * date); // timestamp is seconds since epoch, returns date as YYYYMMDD string, 9 bytes void AdjustTimeOffset (int64_t offset); // in seconds from current class NTPTimeSync diff --git a/libi2pd/Transports.cpp b/libi2pd/Transports.cpp index 0d5764cf..0256e3d9 100644 --- a/libi2pd/Transports.cpp +++ b/libi2pd/Transports.cpp @@ -136,7 +136,7 @@ namespace transport Transports::Transports (): m_IsOnline (true), m_IsRunning (false), m_IsNAT (true), m_CheckReserved(true), m_Thread (nullptr), m_Service (nullptr), m_Work (nullptr), m_PeerCleanupTimer (nullptr), m_PeerTestTimer (nullptr), - m_SSUServer (nullptr), m_SSU2Server (nullptr), m_NTCP2Server (nullptr), + m_SSU2Server (nullptr), m_NTCP2Server (nullptr), m_X25519KeysPairSupplier (15), // 15 pre-generated keys m_TotalSentBytes(0), m_TotalReceivedBytes(0), m_TotalTransitTransmittedBytes (0), m_InBandwidth (0), m_OutBandwidth (0), m_TransitBandwidth(0), @@ -157,7 +157,7 @@ namespace transport } } - void Transports::Start (bool enableNTCP2, bool enableSSU, bool enableSSU2) + void Transports::Start (bool enableNTCP2, bool enableSSU2) { if (!m_Service) { @@ -205,22 +205,6 @@ namespace transport m_NTCP2Server = new NTCP2Server (); } - // create SSU server - int ssuPort = 0; - if (enableSSU) - { - auto& addresses = context.GetRouterInfo ().GetAddresses (); - for (const auto& address: addresses) - { - if (!address) continue; - if (address->transportStyle == RouterInfo::eTransportSSU) - { - ssuPort = address->port; - m_SSUServer = new SSUServer (address->port); - break; - } - } - } // create SSU2 server if (enableSSU2) { @@ -255,7 +239,6 @@ namespace transport if (!ec) { if (m_NTCP2Server) m_NTCP2Server->SetLocalAddress (addr); - if (m_SSUServer) m_SSUServer->SetLocalAddress (addr); if (m_SSU2Server) m_SSU2Server->SetLocalAddress (addr); } } @@ -282,7 +265,6 @@ namespace transport if (!ec) { if (m_NTCP2Server) m_NTCP2Server->SetLocalAddress (addr); - if (m_SSUServer) m_SSUServer->SetLocalAddress (addr); if (m_SSU2Server) m_SSU2Server->SetLocalAddress (addr); } } @@ -315,22 +297,7 @@ namespace transport // start servers if (m_NTCP2Server) m_NTCP2Server->Start (); if (m_SSU2Server) m_SSU2Server->Start (); - if (m_SSUServer) - { - LogPrint (eLogInfo, "Transports: Start listening UDP port ", ssuPort); - try - { - m_SSUServer->Start (); - } - catch (std::exception& ex ) - { - LogPrint(eLogError, "Transports: Failed to bind to UDP port", ssuPort); - m_SSUServer->Stop (); - delete m_SSUServer; - m_SSUServer = nullptr; - } - } - if (m_SSUServer || m_SSU2Server) DetectExternalIP (); + if (m_SSU2Server) DetectExternalIP (); m_PeerCleanupTimer->expires_from_now (boost::posix_time::seconds(5*SESSION_CREATION_TIMEOUT)); m_PeerCleanupTimer->async_wait (std::bind (&Transports::HandlePeerCleanupTimer, this, std::placeholders::_1)); @@ -347,12 +314,6 @@ namespace transport if (m_PeerCleanupTimer) m_PeerCleanupTimer->cancel (); if (m_PeerTestTimer) m_PeerTestTimer->cancel (); m_Peers.clear (); - if (m_SSUServer) - { - m_SSUServer->Stop (); - delete m_SSUServer; - m_SSUServer = nullptr; - } if (m_SSU2Server) { @@ -538,21 +499,6 @@ namespace transport } break; } - case i2p::data::RouterInfo::eSSUV4: - case i2p::data::RouterInfo::eSSUV6: - { - if (!m_SSUServer) continue; - std::shared_ptr address = (tr == i2p::data::RouterInfo::eSSUV6) ? - peer.router->GetSSUV6Address () : peer.router->GetSSUAddress (true); - if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host)) - address = nullptr; - if (address && address->IsReachableSSU ()) - { - if (m_SSUServer->CreateSession (peer.router, address)) - return true; - } - break; - } case i2p::data::RouterInfo::eNTCP2V6Mesh: { if (!m_NTCP2Server) continue; @@ -595,9 +541,7 @@ namespace transport i2p::data::RouterInfo::eNTCP2V4, i2p::data::RouterInfo::eSSU2V6, i2p::data::RouterInfo::eSSU2V4, - i2p::data::RouterInfo::eNTCP2V6Mesh, - i2p::data::RouterInfo::eSSUV6, - i2p::data::RouterInfo::eSSUV4 + i2p::data::RouterInfo::eNTCP2V6Mesh }, ssu2Priority = { @@ -605,9 +549,7 @@ namespace transport i2p::data::RouterInfo::eSSU2V4, i2p::data::RouterInfo::eNTCP2V6, i2p::data::RouterInfo::eNTCP2V4, - i2p::data::RouterInfo::eNTCP2V6Mesh, - i2p::data::RouterInfo::eSSUV6, - i2p::data::RouterInfo::eSSUV4 + i2p::data::RouterInfo::eNTCP2V6Mesh }; if (!peer.router) return; auto compatibleTransports = context.GetRouterInfo ().GetCompatibleTransports (false) & @@ -654,7 +596,7 @@ namespace transport i2p::context.SetStatus (eRouterStatusOK); return; } - if (m_SSUServer || m_SSU2Server) + if (m_SSU2Server) PeerTest (); else LogPrint (eLogWarning, "Transports: Can't detect external IP. SSU or SSU2 is not available"); @@ -662,103 +604,44 @@ namespace transport void Transports::PeerTest (bool ipv4, bool ipv6) { - if (RoutesRestricted() || (!m_SSUServer && !m_SSU2Server)) return; + if (RoutesRestricted() || !m_SSU2Server || m_SSU2Server->UsesProxy ()) return; if (ipv4 && i2p::context.SupportsV4 ()) { LogPrint (eLogInfo, "Transports: Started peer test IPv4"); std::set excluded; excluded.insert (i2p::context.GetIdentHash ()); // don't pick own router - if (m_SSUServer) + for (int i = 0; i < 5; i++) { - bool statusChanged = false; - for (int i = 0; i < 5; i++) + auto router = i2p::data::netdb.GetRandomSSU2PeerTestRouter (true, excluded); // v4 + if (router) { - auto router = i2p::data::netdb.GetRandomPeerTestRouter (true, excluded); // v4 - if (router) - { - auto addr = router->GetSSUAddress (true); // ipv4 - if (addr && !i2p::util::net::IsInReservedRange(addr->host)) - { - if (!statusChanged) - { - statusChanged = true; - i2p::context.SetStatus (eRouterStatusTesting); // first time only - } - m_SSUServer->CreateSession (router, addr, true); // peer test v4 - } - excluded.insert (router->GetIdentHash ()); - } - } - if (!statusChanged) - LogPrint (eLogWarning, "Transports: Can't find routers for peer test IPv4"); - } - // SSU2 - if (m_SSU2Server && !m_SSU2Server->UsesProxy ()) - { - excluded.clear (); - excluded.insert (i2p::context.GetIdentHash ()); - int numTests = m_SSUServer ? 3 : 5; - for (int i = 0; i < numTests; i++) - { - auto router = i2p::data::netdb.GetRandomSSU2PeerTestRouter (true, excluded); // v4 - if (router) - { - if (i2p::context.GetStatus () != eRouterStatusTesting) - i2p::context.SetStatusSSU2 (eRouterStatusTesting); - m_SSU2Server->StartPeerTest (router, true); - excluded.insert (router->GetIdentHash ()); - } + if (i2p::context.GetStatus () != eRouterStatusTesting) + i2p::context.SetStatus (eRouterStatusTesting); + m_SSU2Server->StartPeerTest (router, true); + excluded.insert (router->GetIdentHash ()); } } + if (excluded.size () <= 1) + LogPrint (eLogWarning, "Transports: Can't find routers for peer test IPv4"); } if (ipv6 && i2p::context.SupportsV6 ()) { LogPrint (eLogInfo, "Transports: Started peer test IPv6"); std::set excluded; excluded.insert (i2p::context.GetIdentHash ()); // don't pick own router - if (m_SSUServer) + for (int i = 0; i < 5; i++) { - bool statusChanged = false; - for (int i = 0; i < 5; i++) + auto router = i2p::data::netdb.GetRandomSSU2PeerTestRouter (false, excluded); // v6 + if (router) { - auto router = i2p::data::netdb.GetRandomPeerTestRouter (false, excluded); // v6 - if (router) - { - auto addr = router->GetSSUV6Address (); - if (addr && !i2p::util::net::IsInReservedRange(addr->host)) - { - if (!statusChanged) - { - statusChanged = true; - i2p::context.SetStatusV6 (eRouterStatusTesting); // first time only - } - m_SSUServer->CreateSession (router, addr, true); // peer test v6 - } - excluded.insert (router->GetIdentHash ()); - } - } - if (!statusChanged) - LogPrint (eLogWarning, "Transports: Can't find routers for peer test IPv6"); - } - - // SSU2 - if (m_SSU2Server && !m_SSU2Server->UsesProxy ()) - { - excluded.clear (); - excluded.insert (i2p::context.GetIdentHash ()); - int numTests = m_SSUServer ? 3 : 5; - for (int i = 0; i < numTests; i++) - { - auto router = i2p::data::netdb.GetRandomSSU2PeerTestRouter (false, excluded); // v6 - if (router) - { - if (i2p::context.GetStatusV6 () != eRouterStatusTesting) - i2p::context.SetStatusV6SSU2 (eRouterStatusTesting); - m_SSU2Server->StartPeerTest (router, false); - excluded.insert (router->GetIdentHash ()); - } + if (i2p::context.GetStatusV6 () != eRouterStatusTesting) + i2p::context.SetStatusV6 (eRouterStatusTesting); + m_SSU2Server->StartPeerTest (router, false); + excluded.insert (router->GetIdentHash ()); } } + if (excluded.size () <= 1) + LogPrint (eLogWarning, "Transports: Can't find routers for peer test IPv6"); } } @@ -1075,7 +958,6 @@ namespace transport i2p::context.SetSupportsV4 (ipv4); i2p::context.SetSupportsMesh (ygg, yggaddr); - i2p::context.RemoveNTCPAddress (!ipv6); // TODO: remove later bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2); if (ntcp2) { @@ -1108,15 +990,13 @@ namespace transport if (!ipv4 && !ipv6) i2p::context.SetStatus (eRouterStatusMesh); } - bool ssu; i2p::config::GetOption("ssu", ssu); - if (!ssu) i2p::context.RemoveSSUAddress (); // TODO: remove later bool ssu2; i2p::config::GetOption("ssu2.enabled", ssu2); if (ssu2 && i2p::config::IsDefault ("ssu2.enabled") && !ipv4 && !ipv6) ssu2 = false; // don't enable ssu2 for yggdrasil only router if (ssu2) { uint16_t ssu2port; i2p::config::GetOption("ssu2.port", ssu2port); - if (!ssu2port && port) ssu2port = ssu ? (port + 1) : port; + if (!ssu2port && port) ssu2port = port; bool published; i2p::config::GetOption("ssu2.published", published); if (published) i2p::context.PublishSSU2Address (ssu2port, true, ipv4, ipv6); // publish diff --git a/libi2pd/Transports.h b/libi2pd/Transports.h index 4ee08af2..05fc5458 100644 --- a/libi2pd/Transports.h +++ b/libi2pd/Transports.h @@ -21,7 +21,6 @@ #include #include #include "TransportSession.h" -#include "SSU.h" #include "SSU2.h" #include "NTCP2.h" #include "RouterInfo.h" @@ -96,10 +95,9 @@ namespace transport Transports (); ~Transports (); - void Start (bool enableNTCP2=true, bool enableSSU=true, bool enableSSU2=false); + void Start (bool enableNTCP2=true, bool enableSSU2=true); void Stop (); - bool IsBoundSSU() const { return m_SSUServer != nullptr; } bool IsBoundSSU2() const { return m_SSU2Server != nullptr; } bool IsBoundNTCP2() const { return m_NTCP2Server != nullptr; } @@ -170,7 +168,6 @@ namespace transport boost::asio::io_service::work * m_Work; boost::asio::deadline_timer * m_PeerCleanupTimer, * m_PeerTestTimer; - SSUServer * m_SSUServer; SSU2Server * m_SSU2Server; NTCP2Server * m_NTCP2Server; mutable std::mutex m_PeersMutex; @@ -196,7 +193,6 @@ namespace transport public: // for HTTP only - const SSUServer * GetSSUServer () const { return m_SSUServer; }; const NTCP2Server * GetNTCP2Server () const { return m_NTCP2Server; }; const SSU2Server * GetSSU2Server () const { return m_SSU2Server; }; const decltype(m_Peers)& GetPeers () const { return m_Peers; }; diff --git a/libi2pd/TunnelPool.cpp b/libi2pd/TunnelPool.cpp index 204ac294..b87ca048 100644 --- a/libi2pd/TunnelPool.cpp +++ b/libi2pd/TunnelPool.cpp @@ -76,12 +76,12 @@ namespace tunnel if (m_NumInboundHops > size) { m_NumInboundHops = size; - LogPrint (eLogInfo, "Tunnels: Inbound tunnel length has beed adjusted to ", size, " for explicit peers"); + LogPrint (eLogInfo, "Tunnels: Inbound tunnel length has been adjusted to ", size, " for explicit peers"); } if (m_NumOutboundHops > size) { m_NumOutboundHops = size; - LogPrint (eLogInfo, "Tunnels: Outbound tunnel length has beed adjusted to ", size, " for explicit peers"); + LogPrint (eLogInfo, "Tunnels: Outbound tunnel length has been adjusted to ", size, " for explicit peers"); } m_NumInboundTunnels = 1; m_NumOutboundTunnels = 1; diff --git a/libi2pd/util.cpp b/libi2pd/util.cpp index b8234b39..f0e9a7c6 100644 --- a/libi2pd/util.cpp +++ b/libi2pd/util.cpp @@ -84,7 +84,7 @@ const char *inet_ntop_xp(int af, const void *src, char *dst, socklen_t size) default: return NULL; } - /* cannot direclty use &size because of strict aliasing rules */ + /* cannot directly use &size because of strict aliasing rules */ return (WSAAddressToString((struct sockaddr *)&ss, sizeof(ss), NULL, dst, &s) == 0)? dst : NULL; } diff --git a/libi2pd_client/UDPTunnel.cpp b/libi2pd_client/UDPTunnel.cpp index 4f1b31f2..9495ddde 100644 --- a/libi2pd_client/UDPTunnel.cpp +++ b/libi2pd_client/UDPTunnel.cpp @@ -259,7 +259,7 @@ namespace client void I2PUDPClientTunnel::HandleRecvFromLocal (const boost::system::error_code & ec, std::size_t transferred) { if (m_cancel_resolve) { - LogPrint (eLogDebug, "UDP Client: Ignoring incomming data: stopping"); + LogPrint (eLogDebug, "UDP Client: Ignoring incoming data: stopping"); return; } if (ec) { diff --git a/tests/test-blinding.cpp b/tests/test-blinding.cpp index 5490acd4..d7c41809 100644 --- a/tests/test-blinding.cpp +++ b/tests/test-blinding.cpp @@ -16,28 +16,26 @@ void BlindTest (SigningKeyType sigType) auto timestamp = GetSecondsSinceEpoch (); char date[9]; GetDateString (timestamp, date); - uint8_t blindedPriv[64], blindedPub[128]; + uint8_t blindedPriv[32], blindedPub[32]; auto publicKeyLen = blindedKey.BlindPrivateKey (keys.GetSigningPrivateKey (), date, blindedPriv, blindedPub); - uint8_t blindedPub1[128]; + uint8_t blindedPub1[32]; blindedKey.GetBlindedKey (date, blindedPub1); // check if public key produced from private blinded key matches blided public key assert (!memcmp (blindedPub, blindedPub1, publicKeyLen)); // try to sign and verify - std::unique_ptr blindedSigner (PrivateKeys::CreateSigner (sigType, blindedPriv)); - uint8_t buf[100], signature[128]; + std::unique_ptr blindedSigner (PrivateKeys::CreateSigner (blindedKey.GetBlindedSigType (), blindedPriv)); + uint8_t buf[100], signature[64]; memset (buf, 1, 100); blindedSigner->Sign (buf, 100, signature); - std::unique_ptr blindedVerifier (IdentityEx::CreateVerifier (sigType)); - blindedVerifier->SetPublicKey (blindedPub1); + std::unique_ptr blindedVerifier (IdentityEx::CreateVerifier (blindedKey.GetBlindedSigType ())); + blindedVerifier->SetPublicKey (blindedPub); assert (blindedVerifier->Verify (buf, 100, signature)); } int main () { + // EdDSA test + BlindTest (SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519); // RedDSA test BlindTest (SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519); - // P256 test - BlindTest (SIGNING_KEY_TYPE_ECDSA_SHA256_P256); - // P384 test - BlindTest (SIGNING_KEY_TYPE_ECDSA_SHA384_P384); }