From f70ee480baa71af9aebfb8024b4d0f981571914d Mon Sep 17 00:00:00 2001 From: orignal Date: Mon, 1 Mar 2021 19:02:27 -0500 Subject: [PATCH 01/37] check connectivity between peers for tunnel --- libi2pd/NetDb.cpp | 18 ++++++++++-------- libi2pd/NetDb.hpp | 6 +++--- libi2pd/RouterInfo.cpp | 25 ++++++++++++++++++++----- libi2pd/RouterInfo.h | 8 +++++--- libi2pd/Transports.cpp | 4 ++-- libi2pd/TunnelPool.cpp | 16 ++++++++-------- libi2pd/TunnelPool.h | 6 +++--- libi2pd_client/MatchedDestination.cpp | 5 +++-- 8 files changed, 54 insertions(+), 34 deletions(-) diff --git a/libi2pd/NetDb.cpp b/libi2pd/NetDb.cpp index 66d15cae..7f71619e 100644 --- a/libi2pd/NetDb.cpp +++ b/libi2pd/NetDb.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2021, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -57,7 +57,7 @@ namespace data uint16_t threshold; i2p::config::GetOption("reseed.threshold", threshold); if (m_RouterInfos.size () < threshold) // reseed if # of router less than threshold Reseed (); - else if (!GetRandomRouter (i2p::context.GetSharedRouterInfo ())) + else if (!GetRandomRouter (i2p::context.GetSharedRouterInfo (), false)) Reseed (); // we don't have a router we can connect to. Trying to reseed i2p::config::GetOption("persist.profiles", m_PersistProfiles); @@ -1135,13 +1135,14 @@ namespace data }); } - std::shared_ptr NetDb::GetRandomRouter (std::shared_ptr compatibleWith) const + std::shared_ptr NetDb::GetRandomRouter (std::shared_ptr compatibleWith, bool reverse) const { return GetRandomRouter ( - [compatibleWith](std::shared_ptr router)->bool + [compatibleWith, reverse](std::shared_ptr router)->bool { return !router->IsHidden () && router != compatibleWith && - router->IsCompatible (*compatibleWith); + (reverse ? compatibleWith->IsReachableFrom (*router) : + router->IsReachableFrom (*compatibleWith)); }); } @@ -1172,13 +1173,14 @@ namespace data }); } - std::shared_ptr NetDb::GetHighBandwidthRandomRouter (std::shared_ptr compatibleWith) const + std::shared_ptr NetDb::GetHighBandwidthRandomRouter (std::shared_ptr compatibleWith, bool reverse) const { return GetRandomRouter ( - [compatibleWith](std::shared_ptr router)->bool + [compatibleWith, reverse](std::shared_ptr router)->bool { return !router->IsHidden () && router != compatibleWith && - router->IsCompatible (*compatibleWith) && + (reverse ? compatibleWith->IsReachableFrom (*router) : + router->IsReachableFrom (*compatibleWith)) && (router->GetCaps () & RouterInfo::eHighBandwidth) && router->GetVersion () >= NETDB_MIN_HIGHBANDWIDTH_VERSION; }); diff --git a/libi2pd/NetDb.hpp b/libi2pd/NetDb.hpp index 845217e1..5dbef743 100644 --- a/libi2pd/NetDb.hpp +++ b/libi2pd/NetDb.hpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2021, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -83,8 +83,8 @@ namespace data void HandleDeliveryStatusMsg (std::shared_ptr msg); std::shared_ptr GetRandomRouter () const; - std::shared_ptr GetRandomRouter (std::shared_ptr compatibleWith) const; - std::shared_ptr GetHighBandwidthRandomRouter (std::shared_ptr compatibleWith) 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 v4only = true) const; std::shared_ptr GetRandomSSUV6Router () const; // TODO: change to v6 peer test later std::shared_ptr GetRandomIntroducer () const; diff --git a/libi2pd/RouterInfo.cpp b/libi2pd/RouterInfo.cpp index 4ea66add..03186c54 100644 --- a/libi2pd/RouterInfo.cpp +++ b/libi2pd/RouterInfo.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2021, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -1079,22 +1079,37 @@ namespace data auto supportedTransports = m_SupportedTransports & (eSSUV4 | eSSUV6); if (!supportedTransports) return false; // no SSU if (v4only && !(supportedTransports & eSSUV4)) return false; // no SSU v4 - return GetAddress ( + return (bool)GetAddress ( [](std::shared_ptr address)->bool { return (address->transportStyle == eTransportSSU) && address->IsPeerTesting (); - }) != nullptr; + }); } bool RouterInfo::IsIntroducer () const { // TODO: support ipv6 if (!(m_SupportedTransports & eSSUV4)) return false; - return GetAddress ( + return (bool)GetAddress ( [](std::shared_ptr address)->bool { return (address->transportStyle == eTransportSSU) && address->IsIntroducer (); - }) != nullptr; + }); + } + + bool RouterInfo::IsReachableFrom (const RouterInfo& other) const + { + auto commonTransports = m_SupportedTransports & other.m_SupportedTransports; + if (!commonTransports) return false; + if (commonTransports & eNTCP2V6Mesh) return true; + return (bool)GetAddress ( + [commonTransports](std::shared_ptr address)->bool + { + // TODO:check v4 and v6 separately based on caps + if ((commonTransports & (eNTCP2V4 | eNTCP2V6)) && address->IsPublishedNTCP2 ()) return true; + if ((commonTransports & (eSSUV4 | eSSUV6)) && address->IsReachableSSU ()) return true; + return false; + }); } } } diff --git a/libi2pd/RouterInfo.h b/libi2pd/RouterInfo.h index 9e9f00cd..2dbaefe2 100644 --- a/libi2pd/RouterInfo.h +++ b/libi2pd/RouterInfo.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2021, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -147,7 +147,8 @@ namespace data bool IsNTCP2 () const { return (bool)ntcp2; }; bool IsPublishedNTCP2 () const { return IsNTCP2 () && ntcp2->isPublished; }; - + bool IsReachableSSU () const { return (bool)ssu && (!host.is_unspecified () || !ssu->introducers.empty ()); }; + bool IsIntroducer () const { return caps & eSSUIntroducer; }; bool IsPeerTesting () const { return caps & eSSUTesting; }; }; @@ -196,7 +197,8 @@ namespace data void DisableV4 (); void EnableMesh (); void DisableMesh (); - bool IsCompatible (const RouterInfo& other) const { return m_SupportedTransports & other.m_SupportedTransports; }; + bool IsCompatible (const RouterInfo& other) const { return m_SupportedTransports & other.m_SupportedTransports; }; + bool IsReachableFrom (const RouterInfo& other) const; bool HasValidAddresses () const { return m_SupportedTransports; }; bool UsesIntroducer () const; bool IsHidden () const { return m_Caps & eHidden; }; diff --git a/libi2pd/Transports.cpp b/libi2pd/Transports.cpp index 705754f0..b9a53905 100644 --- a/libi2pd/Transports.cpp +++ b/libi2pd/Transports.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2021, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -503,7 +503,7 @@ namespace transport } peer.numAttempts++; } - if (address) + if (address && address->IsReachableSSU ()) { m_SSUServer->CreateSession (peer.router, address); return true; diff --git a/libi2pd/TunnelPool.cpp b/libi2pd/TunnelPool.cpp index 22563e2d..f7603652 100644 --- a/libi2pd/TunnelPool.cpp +++ b/libi2pd/TunnelPool.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2021, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -393,14 +393,14 @@ namespace tunnel } } - std::shared_ptr TunnelPool::SelectNextHop (std::shared_ptr prevHop) const + std::shared_ptr TunnelPool::SelectNextHop (std::shared_ptr prevHop, bool reverse) const { bool isExploratory = (i2p::tunnel::tunnels.GetExploratoryPool () == shared_from_this ()); - auto hop = isExploratory ? i2p::data::netdb.GetRandomRouter (prevHop): - i2p::data::netdb.GetHighBandwidthRandomRouter (prevHop); + auto hop = isExploratory ? i2p::data::netdb.GetRandomRouter (prevHop, reverse): + i2p::data::netdb.GetHighBandwidthRandomRouter (prevHop, reverse); if (!hop || hop->GetProfile ()->IsBad ()) - hop = i2p::data::netdb.GetRandomRouter (prevHop); + hop = i2p::data::netdb.GetRandomRouter (prevHop, reverse); return hop; } @@ -429,7 +429,7 @@ namespace tunnel for(int i = 0; i < numHops; i++ ) { - auto hop = nextHop (prevHop); + auto hop = nextHop (prevHop, inbound); if (!hop) { LogPrint (eLogError, "Tunnels: Can't select next hop for ", prevHop->GetIdentHashBase64 ()); @@ -438,7 +438,7 @@ namespace tunnel if (inbound && (i == numHops - 1) && !hop->IsReachable ()) { // if first is not reachable try again - auto hop1 = nextHop (prevHop); + auto hop1 = nextHop (prevHop, true); if (hop1) hop = hop1; } prevHop = hop; @@ -460,7 +460,7 @@ namespace tunnel } // explicit peers in use if (m_ExplicitPeers) return SelectExplicitPeers (peers, isInbound); - return StandardSelectPeers(peers, numHops, isInbound, std::bind(&TunnelPool::SelectNextHop, this, std::placeholders::_1)); + return StandardSelectPeers(peers, numHops, isInbound, std::bind(&TunnelPool::SelectNextHop, this, std::placeholders::_1, std::placeholders::_2)); } bool TunnelPool::SelectExplicitPeers (std::vector >& peers, bool isInbound) diff --git a/libi2pd/TunnelPool.h b/libi2pd/TunnelPool.h index 4742a1f6..5fd1d83c 100644 --- a/libi2pd/TunnelPool.h +++ b/libi2pd/TunnelPool.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2021, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -46,7 +46,7 @@ namespace tunnel }; - typedef std::function(std::shared_ptr)> SelectHopFunc; + typedef std::function(std::shared_ptr, bool)> SelectHopFunc; // standard peer selection algorithm bool StandardSelectPeers(Path & path, int hops, bool inbound, SelectHopFunc nextHop); @@ -104,7 +104,7 @@ namespace tunnel std::shared_ptr GetLowestLatencyOutboundTunnel(std::shared_ptr exclude = nullptr) const; // for overriding tunnel peer selection - std::shared_ptr SelectNextHop (std::shared_ptr prevHop) const; + std::shared_ptr SelectNextHop (std::shared_ptr prevHop, bool reverse) const; private: diff --git a/libi2pd_client/MatchedDestination.cpp b/libi2pd_client/MatchedDestination.cpp index 4ffa7442..c0766096 100644 --- a/libi2pd_client/MatchedDestination.cpp +++ b/libi2pd_client/MatchedDestination.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2021, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -72,7 +72,8 @@ namespace client bool MatchedTunnelDestination::SelectPeers(i2p::tunnel::Path & path, int hops, bool inbound) { auto pool = GetTunnelPool(); - if(!i2p::tunnel::StandardSelectPeers(path, hops, inbound, std::bind(&i2p::tunnel::TunnelPool::SelectNextHop, pool, std::placeholders::_1))) + if(!i2p::tunnel::StandardSelectPeers(path, hops, inbound, + std::bind(&i2p::tunnel::TunnelPool::SelectNextHop, pool, std::placeholders::_1, std::placeholders::_2))) return false; // more here for outbound tunnels if(!inbound && m_RemoteLeaseSet) From 876375f2c3bc757696d3ba2274d37e708916704d Mon Sep 17 00:00:00 2001 From: orignal Date: Mon, 1 Mar 2021 22:13:17 -0500 Subject: [PATCH 02/37] precise bandwidth limit --- libi2pd/RouterContext.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libi2pd/RouterContext.cpp b/libi2pd/RouterContext.cpp index a7273376..6b217ce7 100644 --- a/libi2pd/RouterContext.cpp +++ b/libi2pd/RouterContext.cpp @@ -388,6 +388,7 @@ namespace i2p else if (limit > 48) { SetBandwidth('M'); } else if (limit > 12) { SetBandwidth('L'); } else { SetBandwidth('K'); } + m_BandwidthLimit = limit; // set precise limit } void RouterContext::SetShareRatio (int percents) From ef85277a1be90f8db21e9149fa104c14ebdf915d Mon Sep 17 00:00:00 2001 From: orignal Date: Tue, 2 Mar 2021 08:46:13 -0500 Subject: [PATCH 03/37] select reachable routers for one hop tunnels --- libi2pd/Tunnel.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libi2pd/Tunnel.cpp b/libi2pd/Tunnel.cpp index 5355a7b6..e016e9e4 100644 --- a/libi2pd/Tunnel.cpp +++ b/libi2pd/Tunnel.cpp @@ -698,7 +698,7 @@ namespace tunnel auto inboundTunnel = GetNextInboundTunnel (); auto router = i2p::transport::transports.RoutesRestricted() ? i2p::transport::transports.GetRestrictedPeer() : - i2p::data::netdb.GetRandomRouter (); + i2p::data::netdb.GetRandomRouter (i2p::context.GetSharedRouterInfo (), false); // reachable by us if (!inboundTunnel || !router) return; LogPrint (eLogDebug, "Tunnel: creating one hop outbound tunnel"); CreateTunnel ( @@ -771,7 +771,8 @@ namespace tunnel // trying to create one more inbound tunnel auto router = i2p::transport::transports.RoutesRestricted() ? i2p::transport::transports.GetRestrictedPeer() : - i2p::data::netdb.GetRandomRouter (); + // should be reachable by us because we send build request directly + i2p::data::netdb.GetRandomRouter (i2p::context.GetSharedRouterInfo (), false); if (!router) { LogPrint (eLogWarning, "Tunnel: can't find any router, skip creating tunnel"); return; From 924a7bc533c0dff7954fbd47919a0e4eba5e3add Mon Sep 17 00:00:00 2001 From: orignal Date: Tue, 2 Mar 2021 12:29:51 -0500 Subject: [PATCH 04/37] use connected peers if others not available --- libi2pd/TunnelPool.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libi2pd/TunnelPool.cpp b/libi2pd/TunnelPool.cpp index f7603652..07d9a205 100644 --- a/libi2pd/TunnelPool.cpp +++ b/libi2pd/TunnelPool.cpp @@ -430,6 +430,11 @@ namespace tunnel for(int i = 0; i < numHops; i++ ) { auto hop = nextHop (prevHop, inbound); + if (!hop && !i) // if no suitable peer found for first hop, try already connected + { + LogPrint (eLogInfo, "Tunnels: Can't select first hop for a tunnel. Trying already connected"); + hop = i2p::transport::transports.GetRandomPeer (); + } if (!hop) { LogPrint (eLogError, "Tunnels: Can't select next hop for ", prevHop->GetIdentHashBase64 ()); From 618aa26454c8435f94f9971cbd66c871571faa99 Mon Sep 17 00:00:00 2001 From: orignal Date: Tue, 2 Mar 2021 14:13:28 -0500 Subject: [PATCH 05/37] allow some unreachable floodfills --- libi2pd/RouterInfo.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libi2pd/RouterInfo.cpp b/libi2pd/RouterInfo.cpp index 03186c54..e3597d28 100644 --- a/libi2pd/RouterInfo.cpp +++ b/libi2pd/RouterInfo.cpp @@ -1069,8 +1069,9 @@ namespace data bool RouterInfo::IsEligibleFloodfill () const { - // floodfill must be reachable, >= 0.9.28 and not DSA - return IsReachable () && m_Version >= NETDB_MIN_FLOODFILL_VERSION && + // floodfill must be reachable somehow, >= 0.9.28 and not DSA + return (IsReachable () || (m_SupportedTransports & eSSUV4)) && + m_Version >= NETDB_MIN_FLOODFILL_VERSION && GetIdentity ()->GetSigningKeyType () != SIGNING_KEY_TYPE_DSA_SHA1; } From def9873a7093f5103d1f19fc5332d37531f8b605 Mon Sep 17 00:00:00 2001 From: orignal Date: Tue, 2 Mar 2021 21:10:19 -0500 Subject: [PATCH 06/37] request multiple introducers at the time --- libi2pd/NetDb.cpp | 2 +- libi2pd/SSU.cpp | 18 ++++++++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/libi2pd/NetDb.cpp b/libi2pd/NetDb.cpp index 7f71619e..f6632d2a 100644 --- a/libi2pd/NetDb.cpp +++ b/libi2pd/NetDb.cpp @@ -1169,7 +1169,7 @@ namespace data return GetRandomRouter ( [](std::shared_ptr router)->bool { - return !router->IsHidden () && router->IsIntroducer (); + return router->IsIntroducer () && !router->IsHidden () && !router->IsFloodfill (); // floodfills don't send relay tag }); } diff --git a/libi2pd/SSU.cpp b/libi2pd/SSU.cpp index be5ac9c7..f1de3893 100644 --- a/libi2pd/SSU.cpp +++ b/libi2pd/SSU.cpp @@ -671,7 +671,7 @@ namespace transport ScheduleIntroducersUpdateTimer (); return; } - if (i2p::context.GetStatus () == eRouterStatusOK) return; // we don't need introducers anymore + if (i2p::context.GetStatus () != eRouterStatusFirewalled) return; // we don't need introducers // we are firewalled if (!i2p::context.IsUnreachable ()) i2p::context.SetUnreachable (); std::list newList; @@ -712,9 +712,19 @@ namespace transport m_Introducers = newList; if (m_Introducers.size () < SSU_MAX_NUM_INTRODUCERS) { - auto introducer = i2p::data::netdb.GetRandomIntroducer (); - if (introducer) - CreateSession (introducer); + for (auto i = m_Introducers.size (); i < SSU_MAX_NUM_INTRODUCERS; i++) + { + auto introducer = i2p::data::netdb.GetRandomIntroducer (); + if (introducer) + { + auto address = introducer->GetSSUAddress (true); // v4 + if (address && !address->host.is_unspecified ()) + { + boost::asio::ip::udp::endpoint ep (address->host, address->port); + CreateDirectSession (introducer, ep, false); + } + } + } } ScheduleIntroducersUpdateTimer (); } From 065cfe3b9dc5122275d5554b063678f81c708c9d Mon Sep 17 00:00:00 2001 From: orignal Date: Wed, 3 Mar 2021 15:30:13 -0500 Subject: [PATCH 07/37] separate ratchet session for ECIES router --- libi2pd/ECIESX25519AEADRatchetSession.cpp | 66 ++++++++++++----------- libi2pd/ECIESX25519AEADRatchetSession.h | 19 +++++-- libi2pd/RouterContext.cpp | 7 ++- libi2pd/RouterContext.h | 8 ++- 4 files changed, 64 insertions(+), 36 deletions(-) diff --git a/libi2pd/ECIESX25519AEADRatchetSession.cpp b/libi2pd/ECIESX25519AEADRatchetSession.cpp index 81b83477..f956fa67 100644 --- a/libi2pd/ECIESX25519AEADRatchetSession.cpp +++ b/libi2pd/ECIESX25519AEADRatchetSession.cpp @@ -227,7 +227,7 @@ namespace garlic if (!GetOwner ()) return false; // we are Bob // KDF1 - i2p::crypto::InitNoiseIKState (*this, GetOwner ()->GetEncryptionPublicKey (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)); // bpk + i2p::crypto::InitNoiseIKState (GetNoiseState (), GetOwner ()->GetEncryptionPublicKey (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)); // bpk if (!i2p::crypto::GetElligator ()->Decode (buf, m_Aepk)) { @@ -460,7 +460,7 @@ namespace garlic offset += 32; // KDF1 - i2p::crypto::InitNoiseIKState (*this, m_RemoteStaticKey); // bpk + i2p::crypto::InitNoiseIKState (GetNoiseState (), m_RemoteStaticKey); // bpk MixHash (m_EphemeralKeys->GetPublicKey (), 32); // h = SHA256(h || aepk) uint8_t sharedSecret[32]; if (!m_EphemeralKeys->Agree (m_RemoteStaticKey, sharedSecret)) // x25519(aesk, bpk) @@ -520,7 +520,7 @@ namespace garlic bool ECIESX25519AEADRatchetSession::NewOutgoingMessageForRouter (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen) { // we are Alice, router's bpk is m_RemoteStaticKey - i2p::crypto::InitNoiseNState (*this, m_RemoteStaticKey); + i2p::crypto::InitNoiseNState (GetNoiseState (), m_RemoteStaticKey); size_t offset = 0; m_EphemeralKeys = i2p::transport::transports.GetNextX25519KeysPair (); memcpy (out + offset, m_EphemeralKeys->GetPublicKey (), 32); @@ -656,7 +656,7 @@ namespace garlic } buf += 32; len -= 32; // KDF for Reply Key Section - i2p::util::SaveStateHelper s(*this); // restore noise state on exit + i2p::util::SaveStateHelper s(GetNoiseState ()); // restore noise state on exit MixHash (tag, 8); // h = SHA256(h || tag) MixHash (bepk, 32); // h = SHA256(h || bepk) uint8_t sharedSecret[32]; @@ -820,32 +820,6 @@ namespace garlic } return true; } - - bool ECIESX25519AEADRatchetSession::HandleNextMessageForRouter (const uint8_t * buf, size_t len) - { - if (!GetOwner ()) return false; - // we are Bob - i2p::crypto::InitNoiseNState (*this, GetOwner ()->GetEncryptionPublicKey (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)); // bpk - MixHash (buf, 32); - uint8_t sharedSecret[32]; - if (!GetOwner ()->Decrypt (buf, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) // x25519(bsk, aepk) - { - LogPrint (eLogWarning, "Garlic: Incorrect N ephemeral public key"); - return false; - } - MixKey (sharedSecret); - buf += 32; len -= 32; - uint8_t nonce[12]; - CreateNonce (0, nonce); - std::vector payload (len - 16); - if (!i2p::crypto::AEADChaCha20Poly1305 (buf, len - 16, m_H, 32, m_CK + 32, nonce, payload.data (), len - 16, false)) // decrypt - { - LogPrint (eLogWarning, "Garlic: Payload for router AEAD verification failed"); - return false; - } - HandlePayload (payload.data (), len - 16, nullptr, 0); - return true; - } std::shared_ptr ECIESX25519AEADRatchetSession::WrapSingleMessage (std::shared_ptr msg) { @@ -1124,6 +1098,38 @@ namespace garlic ts*1000 > m_LastSentTimestamp + ECIESX25519_SEND_EXPIRATION_TIMEOUT*1000; // milliseconds } + RouterIncomingRatchetSession::RouterIncomingRatchetSession (const i2p::crypto::NoiseSymmetricState& initState): + ECIESX25519AEADRatchetSession (&i2p::context, false) + { + SetNoiseState (initState); + } + + bool RouterIncomingRatchetSession::HandleNextMessage (const uint8_t * buf, size_t len) + { + if (!GetOwner ()) return false; + i2p::crypto::NoiseSymmetricState state (GetNoiseState ()); + // we are Bob + state.MixHash (buf, 32); + uint8_t sharedSecret[32]; + if (!GetOwner ()->Decrypt (buf, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) // x25519(bsk, aepk) + { + LogPrint (eLogWarning, "Garlic: Incorrect N ephemeral public key"); + return false; + } + state.MixKey (sharedSecret); + buf += 32; len -= 32; + uint8_t nonce[12]; + CreateNonce (0, nonce); + std::vector payload (len - 16); + if (!i2p::crypto::AEADChaCha20Poly1305 (buf, len - 16, state.m_H, 32, state.m_CK + 32, nonce, payload.data (), len - 16, false)) // decrypt + { + LogPrint (eLogWarning, "Garlic: Payload for router AEAD verification failed"); + return false; + } + HandlePayload (payload.data (), len - 16, nullptr, 0); + return true; + } + std::shared_ptr WrapECIESX25519AEADRatchetMessage (std::shared_ptr msg, const uint8_t * key, uint64_t tag) { auto m = NewI2NPMessage (); diff --git a/libi2pd/ECIESX25519AEADRatchetSession.h b/libi2pd/ECIESX25519AEADRatchetSession.h index 3aaa3d62..bd6d55e3 100644 --- a/libi2pd/ECIESX25519AEADRatchetSession.h +++ b/libi2pd/ECIESX25519AEADRatchetSession.h @@ -164,7 +164,6 @@ namespace garlic ~ECIESX25519AEADRatchetSession (); bool HandleNextMessage (uint8_t * buf, size_t len, std::shared_ptr receiveTagset, int index = 0); - bool HandleNextMessageForRouter (const uint8_t * buf, size_t len); std::shared_ptr WrapSingleMessage (std::shared_ptr msg); std::shared_ptr WrapOneTimeMessage (std::shared_ptr msg, bool isForRouter = false); @@ -186,16 +185,21 @@ namespace garlic bool IsTerminated () const { return m_IsTerminated; } uint64_t GetLastActivityTimestamp () const { return m_LastActivityTimestamp; }; + protected: + + i2p::crypto::NoiseSymmetricState& GetNoiseState () { return *this; }; + void SetNoiseState (const i2p::crypto::NoiseSymmetricState& state) { GetNoiseState () = state; }; + void CreateNonce (uint64_t seqn, uint8_t * nonce); + void HandlePayload (const uint8_t * buf, size_t len, const std::shared_ptr& receiveTagset, int index); + private: - void CreateNonce (uint64_t seqn, uint8_t * nonce); bool GenerateEphemeralKeysAndEncode (uint8_t * buf); // buf is 32 bytes void InitNewSessionTagset (std::shared_ptr tagsetNsr) const; bool HandleNewIncomingSession (const uint8_t * buf, size_t len); bool HandleNewOutgoingSessionReply (uint8_t * buf, size_t len); bool HandleExistingSessionMessage (uint8_t * buf, size_t len, std::shared_ptr receiveTagset, int index); - void HandlePayload (const uint8_t * buf, size_t len, const std::shared_ptr& receiveTagset, int index); void HandleNextKey (const uint8_t * buf, size_t len, const std::shared_ptr& receiveTagset); bool NewOutgoingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen, bool isStatic = true); @@ -237,6 +241,15 @@ namespace garlic } }; + // single session for all incoming messages + class RouterIncomingRatchetSession: public ECIESX25519AEADRatchetSession + { + public: + + RouterIncomingRatchetSession (const i2p::crypto::NoiseSymmetricState& initState); + bool HandleNextMessage (const uint8_t * buf, size_t len); + }; + std::shared_ptr WrapECIESX25519AEADRatchetMessage (std::shared_ptr msg, const uint8_t * key, uint64_t tag); } } diff --git a/libi2pd/RouterContext.cpp b/libi2pd/RouterContext.cpp index 6b217ce7..52be93d3 100644 --- a/libi2pd/RouterContext.cpp +++ b/libi2pd/RouterContext.cpp @@ -48,6 +48,7 @@ namespace i2p auto initState = new i2p::crypto::NoiseSymmetricState (); i2p::crypto::InitNoiseNState (*initState, GetIdentity ()->GetEncryptionPublicKey ()); m_InitialNoiseState.reset (initState); + m_ECIESSession = std::make_shared(*initState); } } @@ -739,8 +740,10 @@ namespace i2p return; } buf += 4; - auto session = std::make_shared(this, false); - session->HandleNextMessageForRouter (buf, len); + if (m_ECIESSession) + m_ECIESSession->HandleNextMessage (buf, len); + else + LogPrint (eLogError, "Router: Session is not set for ECIES router"); } else i2p::garlic::GarlicDestination::ProcessGarlicMessage (msg); diff --git a/libi2pd/RouterContext.h b/libi2pd/RouterContext.h index 228da788..afca407a 100644 --- a/libi2pd/RouterContext.h +++ b/libi2pd/RouterContext.h @@ -21,6 +21,11 @@ namespace i2p { +namespace garlic +{ + class RouterIncomingRatchetSession; +} + const char ROUTER_INFO[] = "router.info"; const char ROUTER_KEYS[] = "router.keys"; const char NTCP2_KEYS[] = "ntcp2.keys"; @@ -42,7 +47,7 @@ namespace i2p eRouterErrorOffline = 2, eRouterErrorSymmetricNAT = 3 }; - + class RouterContext: public i2p::garlic::GarlicDestination { private: @@ -155,6 +160,7 @@ namespace i2p i2p::data::RouterInfo m_RouterInfo; i2p::data::PrivateKeys m_Keys; std::shared_ptr m_Decryptor, m_TunnelDecryptor; + std::shared_ptr m_ECIESSession; uint64_t m_LastUpdateTime; // in seconds bool m_AcceptsTunnels, m_IsFloodfill; std::chrono::time_point m_StartupTime; From e70ffc9d7c57aaa4b739d71bb34ca86f77e06875 Mon Sep 17 00:00:00 2001 From: orignal Date: Thu, 4 Mar 2021 15:55:51 -0500 Subject: [PATCH 08/37] re-shedule introducers updates if router becomes firewalled --- libi2pd/SSU.cpp | 24 +++++++++++++++++++++--- libi2pd/SSU.h | 3 ++- libi2pd/SSUSession.cpp | 3 +++ 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/libi2pd/SSU.cpp b/libi2pd/SSU.cpp index f1de3893..3b2dc122 100644 --- a/libi2pd/SSU.cpp +++ b/libi2pd/SSU.cpp @@ -653,6 +653,14 @@ namespace transport 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)); + } + void SSUServer::ScheduleIntroducersUpdateTimer () { m_IntroducersUpdateTimer.expires_from_now (boost::posix_time::seconds(SSU_KEEP_ALIVE_INTERVAL)); @@ -671,7 +679,12 @@ namespace transport ScheduleIntroducersUpdateTimer (); return; } - if (i2p::context.GetStatus () != eRouterStatusFirewalled) return; // we don't need introducers + if (i2p::context.GetStatus () != eRouterStatusFirewalled) + { + // we don't need introducers + m_Introducers.clear (); + return; + } // we are firewalled if (!i2p::context.IsUnreachable ()) i2p::context.SetUnreachable (); std::list newList; @@ -712,16 +725,21 @@ namespace transport m_Introducers = newList; if (m_Introducers.size () < SSU_MAX_NUM_INTRODUCERS) { + std::set > requested; for (auto i = m_Introducers.size (); i < SSU_MAX_NUM_INTRODUCERS; i++) { auto introducer = i2p::data::netdb.GetRandomIntroducer (); - if (introducer) + if (introducer && !requested.count (introducer)) // not requested already { auto address = introducer->GetSSUAddress (true); // v4 if (address && !address->host.is_unspecified ()) { boost::asio::ip::udp::endpoint ep (address->host, address->port); - CreateDirectSession (introducer, ep, false); + if (std::find (m_Introducers.begin (), m_Introducers.end (), ep) == m_Introducers.end ()) // not connected yet + { + CreateDirectSession (introducer, ep, false); + requested.insert (introducer); + } } } } diff --git a/libi2pd/SSU.h b/libi2pd/SSU.h index bfcfed4e..daaa83f3 100644 --- a/libi2pd/SSU.h +++ b/libi2pd/SSU.h @@ -70,7 +70,8 @@ namespace transport void AddRelay (uint32_t tag, std::shared_ptr relay); void RemoveRelay (uint32_t tag); std::shared_ptr FindRelaySession (uint32_t tag); - + void RescheduleIntroducersUpdateTimer (); + void NewPeerTest (uint32_t nonce, PeerTestParticipant role, std::shared_ptr session = nullptr); PeerTestParticipant GetPeerTestParticipant (uint32_t nonce); std::shared_ptr GetPeerTestSession (uint32_t nonce); diff --git a/libi2pd/SSUSession.cpp b/libi2pd/SSUSession.cpp index e1e48b75..9685caf7 100644 --- a/libi2pd/SSUSession.cpp +++ b/libi2pd/SSUSession.cpp @@ -999,7 +999,10 @@ namespace transport { LogPrint (eLogDebug, "SSU: peer test from Bob. We are Alice"); if (i2p::context.GetStatus () == eRouterStatusTesting) // still not OK + { i2p::context.SetStatus (eRouterStatusFirewalled); + m_Server.RescheduleIntroducersUpdateTimer (); + } } else { From 3bf6db1c08d2b0d6eeac14095d1dc5fef074cb09 Mon Sep 17 00:00:00 2001 From: R4SAS Date: Fri, 5 Mar 2021 03:19:30 +0300 Subject: [PATCH 09/37] enable yggdrasil address finding for android Signed-off-by: R4SAS --- libi2pd/util.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/libi2pd/util.cpp b/libi2pd/util.cpp index 8c9ffae7..e75b7c19 100644 --- a/libi2pd/util.cpp +++ b/libi2pd/util.cpp @@ -67,8 +67,12 @@ int inet_pton_xp (int af, const char *src, void *dst) } #else /* !_WIN32 => UNIX */ #include +#ifdef ANDROID +#include "ifaddrs.h" +#else #include #endif +#endif #define address_pair_v4(a,b) { boost::asio::ip::address_v4::from_string (a).to_ulong (), boost::asio::ip::address_v4::from_string (b).to_ulong () } #define address_pair_v6(a,b) { boost::asio::ip::address_v6::from_string (a).to_bytes (), boost::asio::ip::address_v6::from_string (b).to_bytes () } @@ -380,11 +384,11 @@ namespace net return boost::asio::ip::address::from_string("127.0.0.1"); #else int af = (ipv6 ? AF_INET6 : AF_INET); - ifaddrs * addrs = nullptr; + ifaddrs *addrs, *cur = nullptr; if(getifaddrs(&addrs) == 0) { // got ifaddrs - ifaddrs * cur = addrs; + cur = addrs; while(cur) { std::string cur_ifname(cur->ifa_name); @@ -431,10 +435,7 @@ namespace net boost::asio::ip::address_v6 GetYggdrasilAddress () { -#if defined(ANDROID) - // TODO: implement - return boost::asio::ip::address_v6 (); -#elif defined(_WIN32) +#if defined(_WIN32) ULONG outBufLen = 0; PIP_ADAPTER_ADDRESSES pAddresses = nullptr; PIP_ADAPTER_ADDRESSES pCurrAddresses = nullptr; @@ -482,11 +483,11 @@ namespace net FREE(pAddresses); return boost::asio::ip::address_v6 (); #else - ifaddrs * addrs = nullptr; + ifaddrs *addrs, *cur = nullptr; auto err = getifaddrs(&addrs); if (!err) { - ifaddrs * cur = addrs; + cur = addrs; while(cur) { if (cur->ifa_addr && cur->ifa_addr->sa_family == AF_INET6) From 1f6cde652eff565ecd55deecf036179b9101eb96 Mon Sep 17 00:00:00 2001 From: orignal Date: Thu, 4 Mar 2021 22:47:56 -0500 Subject: [PATCH 10/37] check caps for SSU address --- libi2pd/RouterInfo.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/libi2pd/RouterInfo.cpp b/libi2pd/RouterInfo.cpp index e3597d28..ae68d4ee 100644 --- a/libi2pd/RouterInfo.cpp +++ b/libi2pd/RouterInfo.cpp @@ -193,7 +193,6 @@ namespace data auto addresses = boost::make_shared(); uint8_t numAddresses; s.read ((char *)&numAddresses, sizeof (numAddresses)); if (!s) return; - bool introducers = false; for (int i = 0; i < numAddresses; i++) { uint8_t supportedTransports = 0; @@ -271,7 +270,6 @@ namespace data LogPrint (eLogError, "RouterInfo: Introducer is presented for non-SSU address. Skipped"); continue; } - introducers = true; size_t l = strlen(key); unsigned char index = key[l-1] - '0'; // TODO: key[l-1] = 0; @@ -328,8 +326,13 @@ namespace data { 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 - if (introducers) supportedTransports |= eSSUV4; // in case if host is not presented + supportedTransports |= eSSUV4; // in case if host or 6 caps is not preasented, we assume 4 } } if (supportedTransports) @@ -403,7 +406,7 @@ namespace data if (!s) return; } - if (!m_SupportedTransports || !m_Addresses->size() || (UsesIntroducer () && !introducers)) + if (!m_SupportedTransports) SetUnreachable (true); } From b994af920959a204448e28e2e0c990732ddac241 Mon Sep 17 00:00:00 2001 From: orignal Date: Fri, 5 Mar 2021 08:41:44 -0500 Subject: [PATCH 11/37] check reachability of floodfill to request from --- libi2pd/NetDb.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/libi2pd/NetDb.cpp b/libi2pd/NetDb.cpp index f6632d2a..076a8665 100644 --- a/libi2pd/NetDb.cpp +++ b/libi2pd/NetDb.cpp @@ -645,7 +645,9 @@ namespace data auto floodfill = GetClosestFloodfill (destination, dest->GetExcludedPeers ()); if (floodfill) { - if (direct && !floodfill->IsCompatible (i2p::context.GetRouterInfo ())) direct = false; // check if fllodfill is reachable + if (direct && !floodfill->IsReachableFrom (i2p::context.GetRouterInfo ()) && + !i2p::transport::transports.IsConnected (floodfill->GetIdentHash ())) + direct = false; // floodfill can't be reached directly if (direct) transports.SendMessage (floodfill->GetIdentHash (), dest->CreateRequestMessage (floodfill->GetIdentHash ())); else @@ -1090,7 +1092,8 @@ namespace data LogPrint (eLogInfo, "NetDb: Publishing our RouterInfo to ", i2p::data::GetIdentHashAbbreviation(floodfill->GetIdentHash ()), ". reply token=", replyToken); m_PublishExcluded.insert (floodfill->GetIdentHash ()); m_PublishReplyToken = replyToken; - if (floodfill->IsCompatible (i2p::context.GetRouterInfo ())) // able to connect? + if (floodfill->IsReachableFrom (i2p::context.GetRouterInfo ()) || // are we able to connect? + i2p::transport::transports.IsConnected (floodfill->GetIdentHash ())) // already connected ? // send directly transports.SendMessage (floodfill->GetIdentHash (), CreateDatabaseStoreMsg (i2p::context.GetSharedRouterInfo (), replyToken)); else From 876973f071fd69a11d0932d2942320308cd9a0b4 Mon Sep 17 00:00:00 2001 From: orignal Date: Fri, 5 Mar 2021 09:29:28 -0500 Subject: [PATCH 12/37] remove coreVersion --- libi2pd/RouterContext.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libi2pd/RouterContext.cpp b/libi2pd/RouterContext.cpp index 52be93d3..663fc4e7 100644 --- a/libi2pd/RouterContext.cpp +++ b/libi2pd/RouterContext.cpp @@ -671,8 +671,8 @@ namespace i2p m_RouterInfo.Update (routerInfo.GetBuffer (), routerInfo.GetBufferLen ()); if (oldIdentity) m_RouterInfo.SetRouterIdentity (GetIdentity ()); // from new keys - m_RouterInfo.SetProperty ("coreVersion", I2P_VERSION); m_RouterInfo.SetProperty ("router.version", I2P_VERSION); + m_RouterInfo.DeleteProperty ("coreVersion"); // TODO: remove later } else { From fa5e4d57fd8a07b62d7733fa94a948be3818d854 Mon Sep 17 00:00:00 2001 From: orignal Date: Fri, 5 Mar 2021 19:40:37 -0500 Subject: [PATCH 13/37] correct caps for SSU address without host --- libi2pd/RouterInfo.cpp | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/libi2pd/RouterInfo.cpp b/libi2pd/RouterInfo.cpp index ae68d4ee..a0eadc24 100644 --- a/libi2pd/RouterInfo.cpp +++ b/libi2pd/RouterInfo.cpp @@ -522,7 +522,7 @@ namespace data if (address.IsNTCP2 ()) { WriteString ("NTCP2", s); - if (address.IsPublishedNTCP2 ()) + if (address.IsPublishedNTCP2 () && !address.host.is_unspecified ()) isPublished = true; else { @@ -545,13 +545,24 @@ namespace data // caps WriteString ("caps", properties); properties << '='; - std::string caps; - if (address.IsPeerTesting ()) caps += CAPS_FLAG_SSU_TESTING; - if (address.IsIntroducer ()) caps += CAPS_FLAG_SSU_INTRODUCER; - if (IsReachable ()) + std::string caps; + if (address.host.is_v4 ()) + { + if (IsReachable ()) + isPublished = true; + else + caps += CAPS_FLAG_V4; + } + else if (address.host.is_v6 ()) isPublished = true; else - caps += CAPS_FLAG_V4; + { + if (address.caps & AddressCaps::eV4) caps += CAPS_FLAG_V4; + if (address.caps & AddressCaps::eV6) caps += CAPS_FLAG_V6; + if (caps.empty ()) caps += CAPS_FLAG_V4; + } + if (address.IsPeerTesting ()) caps += CAPS_FLAG_SSU_TESTING; + if (address.IsIntroducer ()) caps += CAPS_FLAG_SSU_INTRODUCER; WriteString (caps, properties); properties << ';'; } @@ -640,7 +651,7 @@ namespace data } } - if (address.IsPublishedNTCP2 ()) + if (address.IsNTCP2 () && isPublished) { // publish i for NTCP2 WriteString ("i", properties); properties << '='; From ff0e6813c6e951407b8739101822cfbfd3ceb4d2 Mon Sep 17 00:00:00 2001 From: orignal Date: Fri, 5 Mar 2021 21:53:19 -0500 Subject: [PATCH 14/37] fixed typo --- libi2pd/RouterInfo.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libi2pd/RouterInfo.cpp b/libi2pd/RouterInfo.cpp index a0eadc24..20cc371c 100644 --- a/libi2pd/RouterInfo.cpp +++ b/libi2pd/RouterInfo.cpp @@ -312,8 +312,8 @@ namespace data { if (address->caps) { - if (address->caps | AddressCaps::eV4) supportedTransports |= eNTCP2V4; - if (address->caps | AddressCaps::eV6) supportedTransports |= eNTCP2V6; + if (address->caps & AddressCaps::eV4) supportedTransports |= eNTCP2V4; + if (address->caps & AddressCaps::eV6) supportedTransports |= eNTCP2V6; } else supportedTransports |= eNTCP2V4; // most likely, since we don't have host @@ -326,10 +326,10 @@ namespace data { if (isHost) supportedTransports |= address->host.is_v4 () ? eSSUV4 : eSSUV6; - else if (address->caps | AddressCaps::eV6) + else if (address->caps & AddressCaps::eV6) { supportedTransports |= eSSUV6; - if (address->caps | AddressCaps::eV4) supportedTransports |= eSSUV4; // in additional to v6 + 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 From 8f90b21a5d1d122c6fa7cc6983e99b0b5c7b8e37 Mon Sep 17 00:00:00 2001 From: orignal Date: Fri, 5 Mar 2021 22:40:27 -0500 Subject: [PATCH 15/37] fixed typo --- libi2pd/Transports.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libi2pd/Transports.cpp b/libi2pd/Transports.cpp index b9a53905..e3dcab05 100644 --- a/libi2pd/Transports.cpp +++ b/libi2pd/Transports.cpp @@ -195,7 +195,6 @@ namespace transport } else LogPrint(eLogError, "Transports: invalid NTCP2 proxy url ", ntcp2proxy); - return; } else m_NTCP2Server = new NTCP2Server (); From f38920c3385d65dd2fda43627e9921c6bd8cb61e Mon Sep 17 00:00:00 2001 From: orignal Date: Sat, 6 Mar 2021 08:50:47 -0500 Subject: [PATCH 16/37] Status: Proxy --- daemon/HTTPServer.cpp | 2 ++ libi2pd/RouterContext.h | 3 ++- libi2pd/Transports.cpp | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/daemon/HTTPServer.cpp b/daemon/HTTPServer.cpp index 89960e0c..459af3b9 100644 --- a/daemon/HTTPServer.cpp +++ b/daemon/HTTPServer.cpp @@ -252,6 +252,8 @@ namespace http { case eRouterStatusOK: s << "OK"; break; case eRouterStatusTesting: s << "Testing"; break; case eRouterStatusFirewalled: s << "Firewalled"; break; + case eRouterStatusUnknown: s << "Unknown"; break; + case eRouterStatusProxy: s << "Proxy"; break; case eRouterStatusError: { s << "Error"; diff --git a/libi2pd/RouterContext.h b/libi2pd/RouterContext.h index afca407a..c0b51ddf 100644 --- a/libi2pd/RouterContext.h +++ b/libi2pd/RouterContext.h @@ -37,7 +37,8 @@ namespace garlic eRouterStatusTesting = 1, eRouterStatusFirewalled = 2, eRouterStatusError = 3, - eRouterStatusUnknown = 4 + eRouterStatusUnknown = 4, + eRouterStatusProxy = 5 }; enum RouterError diff --git a/libi2pd/Transports.cpp b/libi2pd/Transports.cpp index e3dcab05..36bf1ac5 100644 --- a/libi2pd/Transports.cpp +++ b/libi2pd/Transports.cpp @@ -189,6 +189,7 @@ namespace transport proxytype = NTCP2Server::eHTTPProxy; m_NTCP2Server->UseProxy(proxytype, proxyurl.host, proxyurl.port); + i2p::context.SetStatus (eRouterStatusProxy); } else LogPrint(eLogError, "Transports: unsupported NTCP2 proxy URL ", ntcp2proxy); From ad22247c9e39f7b33c324b697ddc76011fae4401 Mon Sep 17 00:00:00 2001 From: orignal Date: Sat, 6 Mar 2021 15:35:31 -0500 Subject: [PATCH 17/37] start other acceptors if connected through a proxy --- libi2pd/NTCP2.cpp | 81 +++++++++++++++++++++++------------------------ 1 file changed, 40 insertions(+), 41 deletions(-) diff --git a/libi2pd/NTCP2.cpp b/libi2pd/NTCP2.cpp index 96a52b15..5c760215 100644 --- a/libi2pd/NTCP2.cpp +++ b/libi2pd/NTCP2.cpp @@ -1158,54 +1158,53 @@ namespace transport } } else - { LogPrint(eLogInfo, "NTCP2: Proxy is not used"); - auto& addresses = context.GetRouterInfo ().GetAddresses (); - for (const auto& address: addresses) + // start acceptors + auto& addresses = context.GetRouterInfo ().GetAddresses (); + for (const auto& address: addresses) + { + if (!address) continue; + if (address->IsPublishedNTCP2 () && address->port) { - if (!address) continue; - if (address->IsPublishedNTCP2 ()) + if (address->host.is_v4()) { - if (address->host.is_v4()) + try { - try - { - auto ep = m_Address4 ? boost::asio::ip::tcp::endpoint (m_Address4->address(), address->port): - boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), address->port); - m_NTCP2Acceptor.reset (new boost::asio::ip::tcp::acceptor (GetService (), ep)); - } - catch ( std::exception & ex ) - { - LogPrint(eLogError, "NTCP2: Failed to bind to v4 port ", address->port, ex.what()); - ThrowFatal ("Unable to start IPv4 NTCP2 transport at port ", address->port, ": ", ex.what ()); - continue; - } - - LogPrint (eLogInfo, "NTCP2: Start listening v4 TCP port ", address->port); - auto conn = std::make_shared(*this); - m_NTCP2Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCP2Server::HandleAccept, this, conn, std::placeholders::_1)); + auto ep = m_Address4 ? boost::asio::ip::tcp::endpoint (m_Address4->address(), address->port): + boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), address->port); + m_NTCP2Acceptor.reset (new boost::asio::ip::tcp::acceptor (GetService (), ep)); } - else if (address->host.is_v6() && (context.SupportsV6 () || context.SupportsMesh ())) + catch ( std::exception & ex ) { - m_NTCP2V6Acceptor.reset (new boost::asio::ip::tcp::acceptor (GetService ())); - try - { - m_NTCP2V6Acceptor->open (boost::asio::ip::tcp::v6()); - m_NTCP2V6Acceptor->set_option (boost::asio::ip::v6_only (true)); - m_NTCP2V6Acceptor->set_option (boost::asio::socket_base::reuse_address (true)); - m_NTCP2V6Acceptor->bind (boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v6(), address->port)); - m_NTCP2V6Acceptor->listen (); + LogPrint(eLogError, "NTCP2: Failed to bind to v4 port ", address->port, ex.what()); + ThrowFatal ("Unable to start IPv4 NTCP2 transport at port ", address->port, ": ", ex.what ()); + continue; + } - LogPrint (eLogInfo, "NTCP2: Start listening v6 TCP port ", address->port); - auto conn = std::make_shared (*this); - m_NTCP2V6Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCP2Server::HandleAcceptV6, this, conn, std::placeholders::_1)); - } - catch ( std::exception & ex ) - { - LogPrint(eLogError, "NTCP2: failed to bind to v6 port ", address->port, ": ", ex.what()); - ThrowFatal ("Unable to start IPv6 NTCP2 transport at port ", address->port, ": ", ex.what ()); - continue; - } + LogPrint (eLogInfo, "NTCP2: Start listening v4 TCP port ", address->port); + auto conn = std::make_shared(*this); + m_NTCP2Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCP2Server::HandleAccept, this, conn, std::placeholders::_1)); + } + else if (address->host.is_v6() && (context.SupportsV6 () || context.SupportsMesh ())) + { + m_NTCP2V6Acceptor.reset (new boost::asio::ip::tcp::acceptor (GetService ())); + try + { + m_NTCP2V6Acceptor->open (boost::asio::ip::tcp::v6()); + m_NTCP2V6Acceptor->set_option (boost::asio::ip::v6_only (true)); + m_NTCP2V6Acceptor->set_option (boost::asio::socket_base::reuse_address (true)); + m_NTCP2V6Acceptor->bind (boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v6(), address->port)); + m_NTCP2V6Acceptor->listen (); + + LogPrint (eLogInfo, "NTCP2: Start listening v6 TCP port ", address->port); + auto conn = std::make_shared (*this); + m_NTCP2V6Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCP2Server::HandleAcceptV6, this, conn, std::placeholders::_1)); + } + catch ( std::exception & ex ) + { + LogPrint(eLogError, "NTCP2: failed to bind to v6 port ", address->port, ": ", ex.what()); + ThrowFatal ("Unable to start IPv6 NTCP2 transport at port ", address->port, ": ", ex.what ()); + continue; } } } From 2d59c968ca7f33daed7d2ebdb6acb2e7fde12bbd Mon Sep 17 00:00:00 2001 From: orignal Date: Sat, 6 Mar 2021 18:43:50 -0500 Subject: [PATCH 18/37] don't publish NTCP2 address connected through proxy --- daemon/Daemon.cpp | 5 +++++ libi2pd/RouterContext.cpp | 7 +++++++ 2 files changed, 12 insertions(+) diff --git a/daemon/Daemon.cpp b/daemon/Daemon.cpp index e33f3d6d..e03513a9 100644 --- a/daemon/Daemon.cpp +++ b/daemon/Daemon.cpp @@ -186,6 +186,11 @@ namespace util { bool published; i2p::config::GetOption("ntcp2.published", published); if (published) + { + std::string ntcp2proxy; i2p::config::GetOption("ntcp2.proxy", ntcp2proxy); + if (!ntcp2proxy.empty ()) published = false; + } + if (published) { uint16_t ntcp2port; i2p::config::GetOption("ntcp2.port", ntcp2port); if (!ntcp2port) ntcp2port = port; // use standard port diff --git a/libi2pd/RouterContext.cpp b/libi2pd/RouterContext.cpp index 663fc4e7..809c73db 100644 --- a/libi2pd/RouterContext.cpp +++ b/libi2pd/RouterContext.cpp @@ -84,7 +84,14 @@ namespace i2p NewNTCP2Keys (); bool ntcp2Published = false; if (ntcp2) + { i2p::config::GetOption("ntcp2.published", ntcp2Published); + if (ntcp2Published) + { + std::string ntcp2proxy; i2p::config::GetOption("ntcp2.proxy", ntcp2proxy); + if (!ntcp2proxy.empty ()) ntcp2Published = false; + } + } uint8_t caps = 0; if (ipv4) { From 742dbdb68a45884ae26913f8aeccc2a46960e873 Mon Sep 17 00:00:00 2001 From: orignal Date: Sun, 7 Mar 2021 10:07:51 -0500 Subject: [PATCH 19/37] rekey low badwidth routers to ECIES --- libi2pd/RouterContext.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/libi2pd/RouterContext.cpp b/libi2pd/RouterContext.cpp index 809c73db..0f7afa4f 100644 --- a/libi2pd/RouterContext.cpp +++ b/libi2pd/RouterContext.cpp @@ -647,7 +647,14 @@ namespace i2p } } std::shared_ptr oldIdentity; - if (m_Keys.GetPublic ()->GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1) + bool rekey = m_Keys.GetPublic ()->GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1; + if (!rekey && m_Keys.GetPublic ()->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ELGAMAL) + { + // rekey routers with bandwidth = L (or default) this time + std::string bandwidth; i2p::config::GetOption("bandwidth", bandwidth); + if (bandwidth.empty () || bandwidth[0] == 'L') rekey = true; + } + if (rekey) { // update keys LogPrint (eLogInfo, "Router: router keys are obsolete. Creating new"); From f3676d7f18928605d9e43ca1dd78de0cca71b07c Mon Sep 17 00:00:00 2001 From: brain5lug Date: Sun, 7 Mar 2021 23:55:55 +0300 Subject: [PATCH 20/37] logging opimization --- libi2pd/Log.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libi2pd/Log.h b/libi2pd/Log.h index 972a00e1..08c401a9 100644 --- a/libi2pd/Log.h +++ b/libi2pd/Log.h @@ -148,7 +148,7 @@ namespace log { LogLevel level; /**< message level */ std::thread::id tid; /**< id of thread that generated message */ - LogMsg (LogLevel lvl, std::time_t ts, const std::string & txt): timestamp(ts), text(txt), level(lvl) {}; + LogMsg (LogLevel lvl, std::time_t ts, std::string&& txt): timestamp(ts), text(std::move(txt)), level(lvl) {} }; Log & Logger(); @@ -189,7 +189,7 @@ void LogPrint (LogLevel level, TArgs&&... args) noexcept return; // fold message to single string - std::stringstream ss(""); + std::stringstream ss; #if (__cplusplus >= 201703L) // C++ 17 or higher (LogPrint (ss, std::forward(args)), ...); @@ -197,7 +197,7 @@ void LogPrint (LogLevel level, TArgs&&... args) noexcept LogPrint (ss, std::forward(args)...); #endif - auto msg = std::make_shared(level, std::time(nullptr), ss.str()); + auto msg = std::make_shared(level, std::time(nullptr), std::move(ss).str()); msg->tid = std::this_thread::get_id(); log.Append(msg); } From 97f315d488d5a335673f7ddbcfefb9792ee9233b Mon Sep 17 00:00:00 2001 From: orignal Date: Mon, 8 Mar 2021 15:57:05 -0500 Subject: [PATCH 21/37] set correct 4 and 6 caps for unreachable addresses --- libi2pd/RouterContext.cpp | 19 ++++++++++++++----- libi2pd/RouterInfo.cpp | 34 +++++++++++++++++++++++++++++----- libi2pd/RouterInfo.h | 1 + 3 files changed, 44 insertions(+), 10 deletions(-) diff --git a/libi2pd/RouterContext.cpp b/libi2pd/RouterContext.cpp index 0f7afa4f..b2faf0f7 100644 --- a/libi2pd/RouterContext.cpp +++ b/libi2pd/RouterContext.cpp @@ -92,7 +92,7 @@ namespace i2p if (!ntcp2proxy.empty ()) ntcp2Published = false; } } - uint8_t caps = 0; + uint8_t caps = 0, addressCaps = 0; if (ipv4) { std::string host = "127.0.0.1"; @@ -109,7 +109,10 @@ namespace i2p if (ntcp2Published) routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address_v4::from_string (host), port); else // add non-published NTCP2 address + { + addressCaps = i2p::data::RouterInfo::AddressCaps::eV4; routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv); + } } if (ssu) { @@ -138,8 +141,12 @@ namespace i2p ntcp2Host = host; routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address_v6::from_string (ntcp2Host), port); } - else if (!ipv4) // no other ntcp2 addresses yet - routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv); + else + { + if (!ipv4) // no other ntcp2 addresses yet + routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv); + addressCaps |= i2p::data::RouterInfo::AddressCaps::eV6; + } } if (ssu) { @@ -153,7 +160,9 @@ namespace i2p if (!yggaddr.is_unspecified ()) routerInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, yggaddr, port); } - + + if (addressCaps) + routerInfo.SetUnreachableAddressesTransportCaps (addressCaps); routerInfo.SetCaps (caps); // caps + L routerInfo.SetProperty ("netId", std::to_string (m_NetID)); routerInfo.SetProperty ("router.version", I2P_VERSION); @@ -494,7 +503,6 @@ namespace i2p { if (supportsV6) { - m_RouterInfo.EnableV6 (); // insert v6 addresses if necessary bool foundSSU = false, foundNTCP2 = false; uint16_t port = 0; @@ -538,6 +546,7 @@ namespace i2p m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address::from_string (ntcp2Host), ntcp2Port); } } + m_RouterInfo.EnableV6 (); } else m_RouterInfo.DisableV6 (); diff --git a/libi2pd/RouterInfo.cpp b/libi2pd/RouterInfo.cpp index 20cc371c..1d715d25 100644 --- a/libi2pd/RouterInfo.cpp +++ b/libi2pd/RouterInfo.cpp @@ -546,10 +546,14 @@ namespace data WriteString ("caps", properties); properties << '='; std::string caps; + if (address.IsPeerTesting ()) caps += CAPS_FLAG_SSU_TESTING; if (address.host.is_v4 ()) { if (IsReachable ()) + { isPublished = true; + if (address.IsIntroducer ()) caps += CAPS_FLAG_SSU_INTRODUCER; + } else caps += CAPS_FLAG_V4; } @@ -561,8 +565,6 @@ namespace data if (address.caps & AddressCaps::eV6) caps += CAPS_FLAG_V6; if (caps.empty ()) caps += CAPS_FLAG_V4; } - if (address.IsPeerTesting ()) caps += CAPS_FLAG_SSU_TESTING; - if (address.IsIntroducer ()) caps += CAPS_FLAG_SSU_INTRODUCER; WriteString (caps, properties); properties << ';'; } @@ -799,9 +801,6 @@ namespace data if (*it == *addr) return; m_SupportedTransports |= addr->host.is_v6 () ? eSSUV6 : eSSUV4; m_Addresses->push_back(std::move(addr)); - - m_Caps |= eSSUTesting; - m_Caps |= eSSUIntroducer; } void RouterInfo::AddNTCP2Address (const uint8_t * staticKey, const uint8_t * iv, const boost::asio::ip::address& host, int port) @@ -927,13 +926,23 @@ namespace data void RouterInfo::EnableV6 () { if (!IsV6 ()) + { m_SupportedTransports |= eSSUV6 | eNTCP2V6; + uint8_t addressCaps = AddressCaps::eV6; + if (IsV4 ()) addressCaps |= AddressCaps::eV4; + SetUnreachableAddressesTransportCaps (addressCaps); + } } void RouterInfo::EnableV4 () { if (!IsV4 ()) + { m_SupportedTransports |= eSSUV4 | eNTCP2V4; + uint8_t addressCaps = AddressCaps::eV4; + if (IsV6 ()) addressCaps |= AddressCaps::eV6; + SetUnreachableAddressesTransportCaps (addressCaps); + } } @@ -945,6 +954,7 @@ namespace data for (auto it = m_Addresses->begin (); it != m_Addresses->end ();) { auto addr = *it; + addr->caps &= ~AddressCaps::eV6; if (addr->host.is_v6 ()) it = m_Addresses->erase (it); else @@ -961,6 +971,7 @@ namespace data for (auto it = m_Addresses->begin (); it != m_Addresses->end ();) { auto addr = *it; + addr->caps &= ~AddressCaps::eV4; if (addr->host.is_v4 ()) it = m_Addresses->erase (it); else @@ -1126,5 +1137,18 @@ namespace data return false; }); } + + void RouterInfo::SetUnreachableAddressesTransportCaps (uint8_t transports) + { + for (auto& addr: *m_Addresses) + { + // TODO: implement SSU + if (addr->transportStyle == eTransportNTCP && (!addr->IsPublishedNTCP2 () || addr->port)) + { + addr->caps &= ~(eV4 | eV6); + addr->caps |= transports; + } + } + } } } diff --git a/libi2pd/RouterInfo.h b/libi2pd/RouterInfo.h index 2dbaefe2..506fa314 100644 --- a/libi2pd/RouterInfo.h +++ b/libi2pd/RouterInfo.h @@ -182,6 +182,7 @@ namespace data void DeleteProperty (const std::string& key); // called from RouterContext only std::string GetProperty (const std::string& key) const; // called from RouterContext only void ClearProperties () { m_Properties.clear (); }; + void SetUnreachableAddressesTransportCaps (uint8_t transports); // bitmask of AddressCaps bool IsFloodfill () const { return m_Caps & Caps::eFloodfill; }; bool IsReachable () const { return m_Caps & Caps::eReachable; }; bool IsSSU (bool v4only = true) const; From 60b92f98dbcc80a309e98cb31aa2d2c21d87bd2d Mon Sep 17 00:00:00 2001 From: orignal Date: Mon, 8 Mar 2021 18:54:17 -0500 Subject: [PATCH 22/37] OBEP must be ipv4 compatible --- libi2pd/TunnelPool.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/libi2pd/TunnelPool.cpp b/libi2pd/TunnelPool.cpp index 07d9a205..417f5f15 100644 --- a/libi2pd/TunnelPool.cpp +++ b/libi2pd/TunnelPool.cpp @@ -419,7 +419,7 @@ namespace tunnel { auto r = i2p::transport::transports.GetRandomPeer (); if (r && !r->GetProfile ()->IsBad () && - (numHops > 1 || !inbound || r->IsReachable ())) // first must be reachable + (numHops > 1 || (!inbound && r->IsV4 ()) || r->IsReachable ())) // first inbound must be reachable { prevHop = r; peers.push_back (r->GetRouterIdentity ()); @@ -440,9 +440,10 @@ namespace tunnel LogPrint (eLogError, "Tunnels: Can't select next hop for ", prevHop->GetIdentHashBase64 ()); return false; } - if (inbound && (i == numHops - 1) && !hop->IsReachable ()) + if ((i == numHops - 1) && + ((inbound && !hop->IsReachable ()) || // IBGW is not reachable + (!inbound && !hop->IsV4 ()))) // OBEP is not ipv4 { - // if first is not reachable try again auto hop1 = nextHop (prevHop, true); if (hop1) hop = hop1; } From 5f93dc72fd228fb43835cf07b2ebfbf4936c7e46 Mon Sep 17 00:00:00 2001 From: orignal Date: Tue, 9 Mar 2021 15:28:07 -0500 Subject: [PATCH 23/37] convert ifname* params to address* --- daemon/Daemon.cpp | 19 +++++++++++++++++++ libi2pd/RouterContext.cpp | 25 ++++++++++++------------- 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/daemon/Daemon.cpp b/daemon/Daemon.cpp index e03513a9..212d90c4 100644 --- a/daemon/Daemon.cpp +++ b/daemon/Daemon.cpp @@ -144,6 +144,25 @@ namespace util ipv4 = false; ipv6 = true; #endif + // ifname -> address + std::string ifname; i2p::config::GetOption("ifname", ifname); + if (ipv4 && i2p::config::IsDefault ("address4")) + { + std::string ifname4; i2p::config::GetOption("ifname4", ifname4); + if (!ifname4.empty ()) + i2p::config::SetOption ("address4", i2p::util::net::GetInterfaceAddress(ifname4, false).to_string ()); // v4 + else if (!ifname.empty ()) + i2p::config::SetOption ("address4", i2p::util::net::GetInterfaceAddress(ifname, false).to_string ()); // v4 + } + if (ipv6 && i2p::config::IsDefault ("address6")) + { + std::string ifname6; i2p::config::GetOption("ifname6", ifname6); + if (!ifname6.empty ()) + i2p::config::SetOption ("address6", i2p::util::net::GetInterfaceAddress(ifname6, true).to_string ()); // v6 + else if (!ifname.empty ()) + i2p::config::SetOption ("address6", i2p::util::net::GetInterfaceAddress(ifname, true).to_string ()); // v6 + } + bool ygg; i2p::config::GetOption("meshnets.yggdrasil", ygg); boost::asio::ip::address_v6 yggaddr; if (ygg) diff --git a/libi2pd/RouterContext.cpp b/libi2pd/RouterContext.cpp index b2faf0f7..865ce6e6 100644 --- a/libi2pd/RouterContext.cpp +++ b/libi2pd/RouterContext.cpp @@ -76,9 +76,6 @@ namespace i2p bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2); bool ygg; i2p::config::GetOption("meshnets.yggdrasil", ygg); bool nat; i2p::config::GetOption("nat", nat); - std::string ifname; i2p::config::GetOption("ifname", ifname); - std::string ifname4; i2p::config::GetOption("ifname4", ifname4); - std::string ifname6; i2p::config::GetOption("ifname6", ifname6); if ((ntcp2 || ygg) && !m_NTCP2Keys) NewNTCP2Keys (); @@ -98,12 +95,13 @@ namespace i2p std::string host = "127.0.0.1"; if (!i2p::config::IsDefault("host")) i2p::config::GetOption("host", host); - else if (!nat && !ifname.empty()) - /* bind to interface, we have no NAT so set external address too */ - host = i2p::util::net::GetInterfaceAddress(ifname, false).to_string(); // v4 - if(ifname4.size()) - host = i2p::util::net::GetInterfaceAddress(ifname4, false).to_string(); - + else if (!nat) + { + // we have no NAT so set external address from local address + std::string address4; i2p::config::GetOption("address4", address4); + if (!address4.empty ()) host = address4; + } + if (ntcp2) { if (ntcp2Published) @@ -125,10 +123,11 @@ namespace i2p std::string host = "::1"; if (!i2p::config::IsDefault("host") && !ipv4) // override if v6 only i2p::config::GetOption("host", host); - else if (!ifname.empty()) - host = i2p::util::net::GetInterfaceAddress(ifname, true).to_string(); // v6 - if(ifname6.size()) - host = i2p::util::net::GetInterfaceAddress(ifname6, true).to_string(); + else + { + std::string address6; i2p::config::GetOption("address6", address6); + if (!address6.empty ()) host = address6; + } if (ntcp2) { From 9049902ceda0b3a8d1644beafe037d56f9b1d2c8 Mon Sep 17 00:00:00 2001 From: R4SAS Date: Wed, 10 Mar 2021 01:48:42 +0300 Subject: [PATCH 24/37] [webconsole] add address registration line generator Signed-off-by: R4SAS --- daemon/HTTPServer.cpp | 77 ++++++++++++++++++++++++++++++++++++------- libi2pd/Destination.h | 3 +- 2 files changed, 68 insertions(+), 12 deletions(-) diff --git a/daemon/HTTPServer.cpp b/daemon/HTTPServer.cpp index 459af3b9..fce3052d 100644 --- a/daemon/HTTPServer.cpp +++ b/daemon/HTTPServer.cpp @@ -123,6 +123,7 @@ namespace http { const char HTTP_COMMAND_LOGLEVEL[] = "set_loglevel"; const char HTTP_COMMAND_KILLSTREAM[] = "closestream"; const char HTTP_COMMAND_LIMITTRANSIT[] = "limittransit"; + const char HTTP_COMMAND_GET_REG_STRING[] = "get_reg_string"; const char HTTP_PARAM_SAM_SESSION_ID[] = "id"; const char HTTP_PARAM_ADDRESS[] = "address"; @@ -407,7 +408,7 @@ namespace http { } } - static void ShowLeaseSetDestination (std::stringstream& s, std::shared_ptr dest) + static void ShowLeaseSetDestination (std::stringstream& s, std::shared_ptr dest, uint32_t token) { s << "Base64:
\r\n
\r\n
\r\n"; @@ -419,6 +420,20 @@ namespace http { s << "\r\n\r\n"; } + if(dest->IsPublic()) + { + std::string webroot; i2p::config::GetOption("http.webroot", webroot); + auto base32 = dest->GetIdentHash ().ToBase32 (); + s << "
\r\n\r\n
\r\n" + "
\r\n" + " \r\n" + " \r\n" + " \r\n" + " Domain:\r\n\r\n" + " \r\n" + "
\r\nNote: result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.\r\n
\r\n
\r\n
\r\n"; + } + if(dest->GetNumRemoteLeaseSets()) { s << "