Compare commits

...

17 Commits

Author SHA1 Message Date
Pratik B.
6f11fcdf7e Merge 17399da399 into 72ff0b9fbb 2025-01-25 15:51:37 +01:00
orignal
72ff0b9fbb shorter ack request interval 2025-01-25 09:02:18 -05:00
orignal
b9c9988ff4 smaller request timeout if sent directly 2025-01-24 13:56:33 -05:00
orignal
1bb5ad22af use std::mt19937 for random. Peer test interval variance 2025-01-23 19:20:20 -05:00
orignal
4fa5cec0dc fixed termination deadlock if SAM session is active 2025-01-23 14:12:52 -05:00
orignal
1e7254dfaa don't delete router's buffer if an update received or connecting 2025-01-22 13:25:11 -05:00
orignal
ca0818af7e drop buffer upon peer disconnect 2025-01-22 12:00:37 -05:00
orignal
b3d09513b8 fixed race condition 2025-01-21 19:38:07 -05:00
orignal
2857a163e9 check last endpoint only if profile is in memory. postpone profile update when connected 2025-01-21 15:03:25 -05:00
orignal
cba7e5350d drop router's buffer after a while without updates 2025-01-20 18:17:41 -05:00
orignal
29a5effabb use std::mt19937 for random numbers in netdb 2025-01-20 13:27:40 -05:00
orignal
39e07ac265 don't load router profile in NTCP2 or SSU2 thread when check for duplicates 2025-01-20 11:58:33 -05:00
orignal
57986bd348 postpone updating router profile after tunnel build. Check profiles only in memory 2025-01-19 19:16:34 -05:00
orignal
5e301937f2 use pointer to whole struct instead publicKey for buffer 2025-01-19 15:22:46 -05:00
orignal
4edde333ad don't drop router buffer if connected or being updated 2025-01-19 11:47:32 -05:00
orignal
c600b834e3 postpone reading from file and updating router profile 2025-01-18 18:26:16 -05:00
imdef
17399da399 Added example docker-compose.yml 2024-09-25 16:55:29 +00:00
21 changed files with 259 additions and 107 deletions

View File

@@ -0,0 +1,13 @@
services:
i2pd:
container_name: i2pd2
image: purplei2p/i2pd
#optional
entrypoint: ["./entrypoint.sh", "--loglevel error"]
ports:
- 127.0.0.1:7656:7656
- 127.0.0.1:7070:7070
- 127.0.0.1:4444:4444
volumes:
- /path/to/i2pd/data:/home/i2pd/data # make sure data directory and it's contents are owned by 100:65533
- /path/to/i2pd/i2pd_certificates:/i2pd_certificates # make sure i2pd_certificates is owned by root:root and 755 permissions on the directory

View File

@@ -27,18 +27,15 @@ namespace data
size_t Identity::FromBuffer (const uint8_t * buf, size_t len)
{
if ( len < DEFAULT_IDENTITY_SIZE ) {
// buffer too small, don't overflow
return 0;
}
memcpy (publicKey, buf, DEFAULT_IDENTITY_SIZE);
if (len < DEFAULT_IDENTITY_SIZE) return 0; // buffer too small, don't overflow
memcpy (this, buf, DEFAULT_IDENTITY_SIZE);
return DEFAULT_IDENTITY_SIZE;
}
IdentHash Identity::Hash () const
{
IdentHash hash;
SHA256(publicKey, DEFAULT_IDENTITY_SIZE, hash);
SHA256((const uint8_t *)this, DEFAULT_IDENTITY_SIZE, hash);
return hash;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2024, The PurpleI2P Project
* Copyright (c) 2013-2025, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@@ -819,15 +819,20 @@ namespace transport
Terminate ();
return;
}
std::shared_ptr<i2p::data::RouterProfile> profile; // not null if older
bool isOlder = false;
if (ri.GetTimestamp () + i2p::data::NETDB_EXPIRATION_TIMEOUT_THRESHOLD*1000LL < ri1->GetTimestamp ())
{
// received RouterInfo is older than one in netdb
profile = i2p::data::GetRouterProfile (ri1->GetIdentHash ()); // retrieve profile
if (profile && profile->IsDuplicated ())
isOlder = true;
if (ri1->HasProfile ())
{
SendTerminationAndTerminate (eNTCP2Banned);
return;
auto profile = i2p::data::GetRouterProfile (ri1->GetIdentHash ()); // retrieve profile
if (profile && profile->IsDuplicated ())
{
SendTerminationAndTerminate (eNTCP2Banned);
return;
}
}
}
@@ -844,8 +849,12 @@ namespace transport
memcmp (m_RemoteEndpoint.address ().to_v6 ().to_bytes ().data () + 1, addr->host.to_v6 ().to_bytes ().data () + 1, 7) : // from the same yggdrasil subnet
memcmp (m_RemoteEndpoint.address ().to_v6 ().to_bytes ().data (), addr->host.to_v6 ().to_bytes ().data (), 8)))) // temporary address
{
if (profile) // older router?
profile->Duplicated (); // mark router as duplicated in profile
if (isOlder) // older router?
i2p::data::UpdateRouterProfile (ri1->GetIdentHash (),
[](std::shared_ptr<i2p::data::RouterProfile> profile)
{
if (profile) profile->Duplicated (); // mark router as duplicated in profile
});
else
LogPrint (eLogInfo, "NTCP2: Host mismatch between published address ", addr->host, " and actual endpoint ", m_RemoteEndpoint.address ());
SendTerminationAndTerminate (eNTCP2Banned);

View File

@@ -10,7 +10,6 @@
#include <fstream>
#include <vector>
#include <map>
#include <random>
#include <boost/asio.hpp>
#include <stdexcept>
@@ -40,7 +39,7 @@ namespace data
NetDb::NetDb (): m_IsRunning (false), m_Thread (nullptr), m_Reseeder (nullptr),
m_Storage("netDb", "r", "routerInfo-", "dat"), m_PersistProfiles (true),
m_LastExploratorySelectionUpdateTime (0)
m_LastExploratorySelectionUpdateTime (0), m_Rng(i2p::util::GetMonotonicMicroseconds () % 1000000LL)
{
}
@@ -119,8 +118,9 @@ namespace data
i2p::util::SetThreadName("NetDB");
uint64_t lastManage = 0;
uint64_t lastProfilesCleanup = i2p::util::GetMonotonicMilliseconds (), lastObsoleteProfilesCleanup = lastProfilesCleanup;
int16_t profilesCleanupVariance = 0, obsoleteProfilesCleanVariance = 0;
uint64_t lastProfilesCleanup = i2p::util::GetMonotonicMilliseconds (),
lastObsoleteProfilesCleanup = lastProfilesCleanup, lastApplyingProfileUpdates = lastProfilesCleanup;
int16_t profilesCleanupVariance = 0, obsoleteProfilesCleanVariance = 0, applyingProfileUpdatesVariance = 0;
std::list<std::shared_ptr<const I2NPMessage> > msgs;
while (m_IsRunning)
@@ -181,7 +181,7 @@ namespace data
LogPrint (eLogWarning, "NetDb: Can't persist profiles. Profiles are being saved to disk");
}
lastProfilesCleanup = mts;
profilesCleanupVariance = rand () % i2p::data::PEER_PROFILE_AUTOCLEAN_VARIANCE;
profilesCleanupVariance = m_Rng () % i2p::data::PEER_PROFILE_AUTOCLEAN_VARIANCE;
}
if (mts >= lastObsoleteProfilesCleanup + (uint64_t)(i2p::data::PEER_PROFILE_OBSOLETE_PROFILES_CLEAN_TIMEOUT + obsoleteProfilesCleanVariance)*1000)
@@ -197,7 +197,20 @@ namespace data
else
LogPrint (eLogWarning, "NetDb: Can't delete profiles. Profiles are being deleted from disk");
lastObsoleteProfilesCleanup = mts;
obsoleteProfilesCleanVariance = rand () % i2p::data::PEER_PROFILE_OBSOLETE_PROFILES_CLEAN_VARIANCE;
obsoleteProfilesCleanVariance = m_Rng () % i2p::data::PEER_PROFILE_OBSOLETE_PROFILES_CLEAN_VARIANCE;
}
if (mts >= lastApplyingProfileUpdates + i2p::data::PEER_PROFILE_APPLY_POSTPONED_TIMEOUT + applyingProfileUpdatesVariance)
{
bool isApplying = m_ApplyingProfileUpdates.valid ();
if (isApplying && m_ApplyingProfileUpdates.wait_for(std::chrono::seconds(0)) == std::future_status::ready) // still active?
{
m_ApplyingProfileUpdates.get ();
isApplying = false;
}
if (!isApplying)
m_ApplyingProfileUpdates = i2p::data::FlushPostponedRouterProfileUpdates ();
lastApplyingProfileUpdates = mts;
applyingProfileUpdatesVariance = m_Rng () % i2p::data::PEER_PROFILE_APPLY_POSTPONED_TIMEOUT_VARIANCE;
}
}
catch (std::exception& ex)
@@ -281,6 +294,7 @@ namespace data
}
else
{
r->CancelBufferToDelete (); // since an update received
if (CheckLogLevel (eLogDebug))
LogPrint (eLogDebug, "NetDb: RouterInfo is older: ", ident.ToBase64());
updated = false;
@@ -557,7 +571,7 @@ namespace data
while(n > 0)
{
std::lock_guard<std::mutex> lock(m_RouterInfosMutex);
uint32_t idx = rand () % m_RouterInfos.size ();
uint32_t idx = m_Rng () % m_RouterInfos.size ();
uint32_t i = 0;
for (const auto & it : m_RouterInfos) {
if(i >= idx) // are we at the random start point?
@@ -660,15 +674,20 @@ namespace data
{
std::lock_guard<std::mutex> l(m_RouterInfosMutex); // possible collision between DeleteBuffer and Update
buffer = r->CopyBuffer ();
r->ScheduleBufferToDelete ();
}
if (!i2p::transport::transports.IsConnected (ident))
r->ScheduleBufferToDelete ();
if (buffer)
saveToDisk.push_back(std::make_pair(ident.ToBase64 (), buffer));
saveToDisk.emplace_back(ident.ToBase64 (), buffer);
}
r->SetUpdated (false);
updatedCount++;
continue;
}
else if (r->GetBuffer () && ts > r->GetTimestamp () + NETDB_MIN_EXPIRATION_TIMEOUT*1000LL)
// since update was long time ago we assume that router is not connected anymore
r->ScheduleBufferToDelete ();
if (r->GetProfile ()->IsUnreachable ())
r->SetUnreachable (true);
// make router reachable back if too few routers or floodfills
@@ -696,18 +715,15 @@ namespace data
r->SetUnreachable (true);
}
}
// make router reachable back and don't delete buffer if connected now
if ((r->IsUnreachable () || r->IsBufferScheduledToDelete ()) && i2p::transport::transports.IsConnected (ident))
{
// make router reachable back if connected now
if (r->IsUnreachable () && i2p::transport::transports.IsConnected (ident))
r->SetUnreachable (false);
r->CancelBufferToDelete ();
}
if (r->IsUnreachable ())
{
if (r->IsFloodfill ()) deletedFloodfillsCount++;
// delete RI file
removeFromDisk.push_back (ident.ToBase64());
removeFromDisk.emplace_back (ident.ToBase64());
deletedCount++;
if (total - deletedCount < NETDB_MIN_ROUTERS) checkForExpiration = false;
}
@@ -1334,7 +1350,7 @@ namespace data
if (eligible.size () > NETDB_MAX_EXPLORATORY_SELECTION_SIZE)
{
std::sample (eligible.begin(), eligible.end(), std::back_inserter(m_ExploratorySelection),
NETDB_MAX_EXPLORATORY_SELECTION_SIZE, std::mt19937(ts));
NETDB_MAX_EXPLORATORY_SELECTION_SIZE, m_Rng);
}
else
std::swap (m_ExploratorySelection, eligible);

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2024, The PurpleI2P Project
* Copyright (c) 2013-2025, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@@ -16,6 +16,7 @@
#include <thread>
#include <mutex>
#include <future>
#include <random>
#include "Base.h"
#include "Gzip.h"
@@ -185,10 +186,11 @@ namespace data
std::shared_ptr<NetDbRequests> m_Requests;
bool m_PersistProfiles;
std::future<void> m_SavingProfiles, m_DeletingProfiles, m_PersistingRouters;
std::future<void> m_SavingProfiles, m_DeletingProfiles, m_ApplyingProfileUpdates, m_PersistingRouters;
std::vector<std::shared_ptr<const RouterInfo> > m_ExploratorySelection;
uint64_t m_LastExploratorySelectionUpdateTime; // in monotonic seconds
std::mt19937 m_Rng;
i2p::util::MemoryPoolMt<RouterInfo::Buffer> m_RouterInfoBuffersPool;
i2p::util::MemoryPoolMt<RouterInfo::Address> m_RouterInfoAddressesPool;

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2024, The PurpleI2P Project
* Copyright (c) 2013-2025, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@@ -20,8 +20,10 @@ namespace i2p
namespace data
{
RequestedDestination::RequestedDestination (const IdentHash& destination, bool isExploratory, bool direct):
m_Destination (destination), m_IsExploratory (isExploratory), m_IsDirect (direct), m_IsActive (true),
m_CreationTime (i2p::util::GetMillisecondsSinceEpoch ()), m_LastRequestTime (0), m_NumAttempts (0)
m_Destination (destination), m_IsExploratory (isExploratory), m_IsDirect (direct),
m_IsActive (true), m_IsSentDirectly (false),
m_CreationTime (i2p::util::GetMillisecondsSinceEpoch ()),
m_LastRequestTime (0), m_NumAttempts (0)
{
if (i2p::context.IsFloodfill ())
m_ExcludedPeers.insert (i2p::context.GetIdentHash ()); // exclude self if floodfill
@@ -46,6 +48,7 @@ namespace data
m_ExcludedPeers.insert (router->GetIdentHash ());
m_LastRequestTime = i2p::util::GetMillisecondsSinceEpoch ();
m_NumAttempts++;
m_IsSentDirectly = false;
return msg;
}
@@ -56,6 +59,7 @@ namespace data
m_ExcludedPeers.insert (floodfill);
m_NumAttempts++;
m_LastRequestTime = i2p::util::GetMillisecondsSinceEpoch ();
m_IsSentDirectly = true;
return msg;
}
@@ -222,7 +226,8 @@ namespace data
bool done = false;
if (ts < dest->GetCreationTime () + MAX_REQUEST_TIME)
{
if (ts > dest->GetLastRequestTime () + MIN_REQUEST_TIME) // try next floodfill if no response after min interval
if (ts > dest->GetLastRequestTime () + (dest->IsSentDirectly () ? MIN_DIRECT_REQUEST_TIME : MIN_REQUEST_TIME))
// try next floodfill if no response after min interval
done = !SendNextRequest (dest);
}
else // request is expired

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2024, The PurpleI2P Project
* Copyright (c) 2013-2025, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@@ -28,6 +28,7 @@ namespace data
const uint64_t MANAGE_REQUESTS_INTERVAL_VARIANCE = 300; // in milliseconds
const uint64_t MIN_REQUEST_TIME = 1200; // in milliseconds
const uint64_t MAX_REQUEST_TIME = MAX_NUM_REQUEST_ATTEMPTS * (MIN_REQUEST_TIME + MANAGE_REQUESTS_INTERVAL + MANAGE_REQUESTS_INTERVAL_VARIANCE);
const uint64_t MIN_DIRECT_REQUEST_TIME = 600; // in milliseconds
const uint64_t EXPLORATORY_REQUEST_INTERVAL = 55; // in seconds
const uint64_t EXPLORATORY_REQUEST_INTERVAL_VARIANCE = 170; // in seconds
const uint64_t DISCOVERED_REQUEST_INTERVAL = 360; // in milliseconds
@@ -52,6 +53,7 @@ namespace data
bool IsExploratory () const { return m_IsExploratory; };
bool IsDirect () const { return m_IsDirect; };
bool IsActive () const { return m_IsActive; };
bool IsSentDirectly () const { return m_IsSentDirectly; };
bool IsExcluded (const IdentHash& ident) const;
uint64_t GetCreationTime () const { return m_CreationTime; };
uint64_t GetLastRequestTime () const { return m_LastRequestTime; };
@@ -70,7 +72,7 @@ namespace data
private:
IdentHash m_Destination;
bool m_IsExploratory, m_IsDirect, m_IsActive;
bool m_IsExploratory, m_IsDirect, m_IsActive, m_IsSentDirectly;
std::unordered_set<IdentHash> m_ExcludedPeers;
uint64_t m_CreationTime, m_LastRequestTime; // in milliseconds
std::list<RequestComplete> m_RequestComplete;

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2024, The PurpleI2P Project
* Copyright (c) 2013-2025, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@@ -27,7 +27,9 @@ namespace data
static i2p::fs::HashedStorage g_ProfilesStorage("peerProfiles", "p", "profile-", "txt");
static std::unordered_map<i2p::data::IdentHash, std::shared_ptr<RouterProfile> > g_Profiles;
static std::mutex g_ProfilesMutex;
static std::list<std::pair<i2p::data::IdentHash, std::function<void (std::shared_ptr<RouterProfile>)> > > g_PostponedUpdates;
static std::mutex g_PostponedUpdatesMutex;
RouterProfile::RouterProfile ():
m_IsUpdated (false), m_LastDeclineTime (0), m_LastUnreachableTime (0),
m_LastUpdateTime (i2p::util::GetSecondsSinceEpoch ()),
@@ -259,14 +261,14 @@ namespace data
}
auto profile = netdb.NewRouterProfile ();
profile->Load (identHash); // if possible
std::unique_lock<std::mutex> l(g_ProfilesMutex);
std::lock_guard<std::mutex> l(g_ProfilesMutex);
g_Profiles.emplace (identHash, profile);
return profile;
}
bool IsRouterBanned (const IdentHash& identHash)
{
std::unique_lock<std::mutex> l(g_ProfilesMutex);
std::lock_guard<std::mutex> l(g_ProfilesMutex);
auto it = g_Profiles.find (identHash);
if (it != g_Profiles.end ())
return it->second->IsUnreachable ();
@@ -278,7 +280,7 @@ namespace data
g_ProfilesStorage.SetPlace(i2p::fs::GetDataDir());
g_ProfilesStorage.Init(i2p::data::GetBase64SubstitutionTable(), 64);
}
static void SaveProfilesToDisk (std::list<std::pair<i2p::data::IdentHash, std::shared_ptr<RouterProfile> > >&& profiles)
{
for (auto& it: profiles)
@@ -290,7 +292,7 @@ namespace data
auto ts = i2p::util::GetSecondsSinceEpoch ();
std::list<std::pair<i2p::data::IdentHash, std::shared_ptr<RouterProfile> > > tmp;
{
std::unique_lock<std::mutex> l(g_ProfilesMutex);
std::lock_guard<std::mutex> l(g_ProfilesMutex);
for (auto it = g_Profiles.begin (); it != g_Profiles.end ();)
{
if (ts - it->second->GetLastUpdateTime () > PEER_PROFILE_PERSIST_INTERVAL)
@@ -312,7 +314,7 @@ namespace data
{
std::unordered_map<i2p::data::IdentHash, std::shared_ptr<RouterProfile> > tmp;
{
std::unique_lock<std::mutex> l(g_ProfilesMutex);
std::lock_guard<std::mutex> l(g_ProfilesMutex);
std::swap (tmp, g_Profiles);
}
auto ts = i2p::util::GetSecondsSinceEpoch ();
@@ -347,7 +349,7 @@ namespace data
{
{
auto ts = i2p::util::GetSecondsSinceEpoch ();
std::unique_lock<std::mutex> l(g_ProfilesMutex);
std::lock_guard<std::mutex> l(g_ProfilesMutex);
for (auto it = g_Profiles.begin (); it != g_Profiles.end ();)
{
if (ts - it->second->GetLastUpdateTime () >= PEER_PROFILE_EXPIRATION_TIMEOUT)
@@ -359,5 +361,47 @@ namespace data
return std::async (std::launch::async, DeleteFilesFromDisk);
}
bool UpdateRouterProfile (const IdentHash& identHash, std::function<void (std::shared_ptr<RouterProfile>)> update)
{
if (!update) return true;
std::shared_ptr<RouterProfile> profile;
{
std::lock_guard<std::mutex> l(g_ProfilesMutex);
auto it = g_Profiles.find (identHash);
if (it != g_Profiles.end ())
profile = it->second;
}
if (profile)
{
update (profile);
return true;
}
// postpone
std::lock_guard<std::mutex> l(g_PostponedUpdatesMutex);
g_PostponedUpdates.emplace_back (identHash, update);
return false;
}
static void ApplyPostponedUpdates (std::list<std::pair<i2p::data::IdentHash, std::function<void (std::shared_ptr<RouterProfile>)> > >&& updates)
{
for (const auto& [ident, update] : updates)
{
auto profile = GetRouterProfile (ident);
update (profile);
}
}
std::future<void> FlushPostponedRouterProfileUpdates ()
{
if (g_PostponedUpdates.empty ()) return std::future<void>();
std::list<std::pair<i2p::data::IdentHash, std::function<void (std::shared_ptr<RouterProfile>)> > > updates;
{
std::lock_guard<std::mutex> l(g_PostponedUpdatesMutex);
g_PostponedUpdates.swap (updates);
}
return std::async (std::launch::async, ApplyPostponedUpdates, std::move (updates));
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2024, The PurpleI2P Project
* Copyright (c) 2013-2025, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@@ -11,6 +11,7 @@
#include <memory>
#include <future>
#include <functional>
#include <boost/asio.hpp>
#include "Identity.h"
@@ -44,6 +45,8 @@ namespace data
const int PEER_PROFILE_UNREACHABLE_INTERVAL = 480; // in seconds (8 minutes)
const int PEER_PROFILE_USEFUL_THRESHOLD = 3;
const int PEER_PROFILE_ALWAYS_DECLINING_NUM = 5; // num declines in row to consider always declined
const int PEER_PROFILE_APPLY_POSTPONED_TIMEOUT = 2100; // in milliseconds
const int PEER_PROFILE_APPLY_POSTPONED_TIMEOUT_VARIANCE = 500; // in milliseconds
class RouterProfile
{
@@ -108,6 +111,8 @@ namespace data
std::future<void> DeleteObsoleteProfiles ();
void SaveProfiles ();
std::future<void> PersistProfiles ();
bool UpdateRouterProfile (const IdentHash& identHash, std::function<void (std::shared_ptr<RouterProfile>)> update); // return true if updated immediately, and false if postponed
std::future<void> FlushPostponedRouterProfileUpdates ();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2024, The PurpleI2P Project
* Copyright (c) 2013-2025, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@@ -1136,12 +1136,12 @@ namespace data
void RouterInfo::UpdateBuffer (const uint8_t * buf, size_t len)
{
m_IsBufferScheduledToDelete = false;
if (!m_Buffer)
m_Buffer = NewBuffer ();
if (len > m_Buffer->size ()) len = m_Buffer->size ();
memcpy (m_Buffer->data (), buf, len);
m_Buffer->SetBufferLen (len);
m_IsBufferScheduledToDelete = false;
}
std::shared_ptr<RouterInfo::Buffer> RouterInfo::CopyBuffer () const

View File

@@ -890,7 +890,7 @@ namespace transport
}
auto session = std::make_shared<SSU2Session> (*this, router, address);
if (!isValidEndpoint && router->GetProfile ()->HasLastEndpoint (address->IsV4 ()))
if (!isValidEndpoint && router->HasProfile () && router->GetProfile ()->HasLastEndpoint (address->IsV4 ()))
{
// router doesn't publish endpoint, but we connected before and hole punch might be alive
auto ep = router->GetProfile ()->GetLastEndpoint ();

View File

@@ -1178,13 +1178,18 @@ namespace transport
LogPrint (eLogError, "SSU2: Couldn't update RouterInfo from SessionConfirmed in netdb");
return false;
}
std::shared_ptr<i2p::data::RouterProfile> profile; // not null if older
bool isOlder = false;
if (ri->GetTimestamp () + i2p::data::NETDB_EXPIRATION_TIMEOUT_THRESHOLD*1000LL < ri1->GetTimestamp ())
{
// received RouterInfo is older than one in netdb
profile = i2p::data::GetRouterProfile (ri->GetIdentHash ()); // retrieve profile
if (profile && profile->IsDuplicated ())
return false;
isOlder = true;
if (ri->HasProfile ())
{
auto profile = i2p::data::GetRouterProfile (ri->GetIdentHash ()); // retrieve profile
if (profile && profile->IsDuplicated ())
return false;
}
}
ri = ri1;
@@ -1198,15 +1203,28 @@ namespace transport
(!m_RemoteEndpoint.address ().is_v6 () ||
memcmp (m_RemoteEndpoint.address ().to_v6 ().to_bytes ().data (), m_Address->host.to_v6 ().to_bytes ().data (), 8))) // temporary address
{
if (profile) // older router?
profile->Duplicated (); // mark router as duplicated in profile
if (isOlder) // older router?
i2p::data::UpdateRouterProfile (ri->GetIdentHash (),
[](std::shared_ptr<i2p::data::RouterProfile> profile)
{
if (profile) profile->Duplicated (); // mark router as duplicated in profile
});
else
LogPrint (eLogInfo, "SSU2: Host mismatch between published address ", m_Address->host,
" and actual endpoint ", m_RemoteEndpoint.address (), " from ", i2p::data::GetIdentHashAbbreviation (ri->GetIdentHash ()));
return false;
}
if (!m_Address->published)
ri->GetProfile ()->SetLastEndpoint (m_RemoteEndpoint);
{
if (ri->HasProfile ())
ri->GetProfile ()->SetLastEndpoint (m_RemoteEndpoint);
else
i2p::data::UpdateRouterProfile (ri->GetIdentHash (),
[ep = m_RemoteEndpoint](std::shared_ptr<i2p::data::RouterProfile> profile)
{
if (profile) profile->SetLastEndpoint (ep);
});
}
SetRemoteIdentity (ri->GetRouterIdentity ());
AdjustMaxPayloadSize ();
m_Server.AddSessionByRouterHash (shared_from_this ()); // we know remote router now

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2024, The PurpleI2P Project
* Copyright (c) 2013-2025, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@@ -101,13 +101,13 @@ namespace tunnel
TunnelMessageBlock block;
block.deliveryType = eDeliveryTypeLocal;
block.data = msg;
std::unique_lock<std::mutex> l(m_SendMutex);
std::lock_guard<std::mutex> l(m_SendMutex);
m_Gateway.PutTunnelDataMsg (block);
}
void TransitTunnelGateway::FlushTunnelDataMsgs ()
{
std::unique_lock<std::mutex> l(m_SendMutex);
std::lock_guard<std::mutex> l(m_SendMutex);
m_Gateway.SendBuffer ();
}
@@ -130,14 +130,22 @@ namespace tunnel
EncryptTunnelMsg (tunnelMsg, newMsg);
LogPrint (eLogDebug, "TransitTunnel: handle msg for endpoint ", GetTunnelID ());
std::lock_guard<std::mutex> l(m_HandleMutex);
m_Endpoint.HandleDecryptedTunnelDataMsg (newMsg);
}
void TransitTunnelEndpoint::FlushTunnelDataMsgs ()
{
std::lock_guard<std::mutex> l(m_HandleMutex);
m_Endpoint.FlushI2NPMsgs ();
}
void TransitTunnelEndpoint::Cleanup ()
{
std::lock_guard<std::mutex> l(m_HandleMutex);
m_Endpoint.Cleanup ();
}
std::string TransitTunnelEndpoint::GetNextPeerName () const
{
auto hash = m_Endpoint.GetCurrentHash ();

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2024, The PurpleI2P Project
* Copyright (c) 2013-2025, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@@ -100,7 +100,7 @@ namespace tunnel
TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey),
m_Endpoint (false) {}; // transit endpoint is always outbound
void Cleanup () override { m_Endpoint.Cleanup (); }
void Cleanup () override;
void HandleTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage>&& tunnelMsg) override;
void FlushTunnelDataMsgs () override;
@@ -109,6 +109,7 @@ namespace tunnel
private:
std::mutex m_HandleMutex;
TunnelEndpoint m_Endpoint;
};

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2024, The PurpleI2P Project
* Copyright (c) 2013-2025, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@@ -160,7 +160,8 @@ namespace transport
m_TotalSentBytes (0), m_TotalReceivedBytes (0), m_TotalTransitTransmittedBytes (0),
m_InBandwidth (0), m_OutBandwidth (0), m_TransitBandwidth (0),
m_InBandwidth15s (0), m_OutBandwidth15s (0), m_TransitBandwidth15s (0),
m_InBandwidth5m (0), m_OutBandwidth5m (0), m_TransitBandwidth5m (0)
m_InBandwidth5m (0), m_OutBandwidth5m (0), m_TransitBandwidth5m (0),
m_Rng(i2p::util::GetMonotonicMicroseconds () % 1000000LL)
{
}
@@ -338,7 +339,7 @@ namespace transport
if (m_IsNAT)
{
m_PeerTestTimer->expires_from_now (boost::posix_time::minutes(PEER_TEST_INTERVAL));
m_PeerTestTimer->expires_from_now (boost::posix_time::seconds(PEER_TEST_INTERVAL + m_Rng() % PEER_TEST_INTERVAL_VARIANCE));
m_PeerTestTimer->async_wait (std::bind (&Transports::HandlePeerTestTimer, this, std::placeholders::_1));
}
}
@@ -560,7 +561,14 @@ namespace transport
bool Transports::ConnectToPeer (const i2p::data::IdentHash& ident, std::shared_ptr<Peer> peer)
{
if (!peer->router) // reconnect
peer->SetRouter (netdb.FindRouter (ident)); // try to get new one from netdb
{
auto r = netdb.FindRouter (ident); // try to get new one from netdb
if (r)
{
peer->SetRouter (r);
r->CancelBufferToDelete ();
}
}
if (peer->router) // we have RI already
{
if (peer->priority.empty ())
@@ -647,7 +655,7 @@ namespace transport
return true;
}
void Transports::SetPriority (std::shared_ptr<Peer> peer) const
void Transports::SetPriority (std::shared_ptr<Peer> peer)
{
static const std::vector<i2p::data::RouterInfo::SupportedTransports>
ntcp2Priority =
@@ -673,7 +681,7 @@ namespace transport
peer->numAttempts = 0;
peer->priority.clear ();
bool isReal = peer->router->GetProfile ()->IsReal ();
bool ssu2 = isReal ? (rand () & 1) : false; // try NTCP2 if router is not confirmed real
bool ssu2 = isReal ? (m_Rng () & 1) : false; // try NTCP2 if router is not confirmed real
const auto& priority = ssu2 ? ssu2Priority : ntcp2Priority;
if (directTransports)
{
@@ -701,7 +709,8 @@ namespace transport
// try recently connected SSU2 if any
auto supportedTransports = context.GetRouterInfo ().GetCompatibleTransports (false) &
peer->router->GetCompatibleTransports (false);
if (supportedTransports & (i2p::data::RouterInfo::eSSU2V4 | i2p::data::RouterInfo::eSSU2V6))
if ((supportedTransports & (i2p::data::RouterInfo::eSSU2V4 | i2p::data::RouterInfo::eSSU2V6)) &&
peer->router->HasProfile ())
{
auto ep = peer->router->GetProfile ()->GetLastEndpoint ();
if (!ep.address ().is_unspecified () && ep.port ())
@@ -791,7 +800,7 @@ namespace transport
}
else
{
testDelay += PEER_TEST_DELAY_INTERVAL + rand() % PEER_TEST_DELAY_INTERVAL_VARIANCE;
testDelay += PEER_TEST_DELAY_INTERVAL + m_Rng() % PEER_TEST_DELAY_INTERVAL_VARIANCE;
if (m_Service)
{
auto delayTimer = std::make_shared<boost::asio::deadline_timer>(*m_Service);
@@ -829,7 +838,7 @@ namespace transport
}
else
{
testDelay += PEER_TEST_DELAY_INTERVAL + rand() % PEER_TEST_DELAY_INTERVAL_VARIANCE;
testDelay += PEER_TEST_DELAY_INTERVAL + m_Rng() % PEER_TEST_DELAY_INTERVAL_VARIANCE;
if (m_Service)
{
auto delayTimer = std::make_shared<boost::asio::deadline_timer>(*m_Service);
@@ -886,7 +895,11 @@ namespace transport
auto transport = peer->priority[peer->numAttempts-1];
if (transport == i2p::data::RouterInfo::eNTCP2V4 ||
transport == i2p::data::RouterInfo::eNTCP2V6 || transport == i2p::data::RouterInfo::eNTCP2V6Mesh)
peer->router->GetProfile ()->Connected (); // outgoing NTCP2 connection if always real
i2p::data::UpdateRouterProfile (ident,
[](std::shared_ptr<i2p::data::RouterProfile> profile)
{
if (profile) profile->Connected (); // outgoing NTCP2 connection if always real
});
i2p::data::netdb.SetUnreachable (ident, false); // clear unreachable
}
peer->numAttempts = 0;
@@ -921,7 +934,11 @@ namespace transport
session->SendI2NPMessages (msgs); // send DatabaseStore
}
auto r = i2p::data::netdb.FindRouter (ident); // router should be in netdb after SessionConfirmed
if (r) r->GetProfile ()->Connected ();
i2p::data::UpdateRouterProfile (ident,
[](std::shared_ptr<i2p::data::RouterProfile> profile)
{
if (profile) profile->Connected ();
});
auto ts = i2p::util::GetSecondsSinceEpoch ();
auto peer = std::make_shared<Peer>(r, ts);
peer->sessions.push_back (session);
@@ -955,8 +972,13 @@ namespace transport
}
else
{
std::lock_guard<std::mutex> l(m_PeersMutex);
m_Peers.erase (it);
{
std::lock_guard<std::mutex> l(m_PeersMutex);
m_Peers.erase (it);
}
// delete buffer of just disconnected router
auto r = i2p::data::netdb.FindRouter (ident);
if (r && !r->IsUpdated ()) r->ScheduleBufferToDelete ();
}
}
}
@@ -1006,7 +1028,7 @@ namespace transport
if (session)
session->SendLocalRouterInfo (true);
it->second->nextRouterInfoUpdateTime = ts + PEER_ROUTER_INFO_UPDATE_INTERVAL +
rand () % PEER_ROUTER_INFO_UPDATE_INTERVAL_VARIANCE;
m_Rng() % PEER_ROUTER_INFO_UPDATE_INTERVAL_VARIANCE;
}
++it;
}
@@ -1030,7 +1052,7 @@ namespace transport
if (ecode != boost::asio::error::operation_aborted)
{
PeerTest ();
m_PeerTestTimer->expires_from_now (boost::posix_time::minutes(PEER_TEST_INTERVAL));
m_PeerTestTimer->expires_from_now (boost::posix_time::seconds(PEER_TEST_INTERVAL + m_Rng() % PEER_TEST_INTERVAL_VARIANCE));
m_PeerTestTimer->async_wait (std::bind (&Transports::HandlePeerTestTimer, this, std::placeholders::_1));
}
}
@@ -1177,7 +1199,7 @@ namespace transport
}
/** XXX: if routes are not restricted this dies */
std::shared_ptr<const i2p::data::RouterInfo> Transports::GetRestrictedPeer() const
std::shared_ptr<const i2p::data::RouterInfo> Transports::GetRestrictedPeer()
{
{
std::lock_guard<std::mutex> l(m_FamilyMutex);
@@ -1186,7 +1208,7 @@ namespace transport
if(sz > 1)
{
auto it = m_TrustedFamilies.begin ();
std::advance(it, rand() % sz);
std::advance(it, m_Rng() % sz);
fam = *it;
}
else if (sz == 1)
@@ -1204,7 +1226,7 @@ namespace transport
if(sz == 1)
return i2p::data::netdb.FindRouter(m_TrustedRouters[0]);
auto it = m_TrustedRouters.begin();
std::advance(it, rand() % sz);
std::advance(it, m_Rng() % sz);
return i2p::data::netdb.FindRouter(*it);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2024, The PurpleI2P Project
* Copyright (c) 2013-2025, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@@ -20,6 +20,7 @@
#include <string>
#include <memory>
#include <atomic>
#include <random>
#include <boost/asio.hpp>
#include "TransportSession.h"
#include "SSU2.h"
@@ -106,7 +107,8 @@ namespace transport
};
const uint64_t SESSION_CREATION_TIMEOUT = 15; // in seconds
const int PEER_TEST_INTERVAL = 71; // in minutes
const int PEER_TEST_INTERVAL = 68*60; // in seconds
const int PEER_TEST_INTERVAL_VARIANCE = 3*60; // in seconds
const int PEER_TEST_DELAY_INTERVAL = 20; // in milliseconds
const int PEER_TEST_DELAY_INTERVAL_VARIANCE = 30; // in milliseconds
const int MAX_NUM_DELAYED_MESSAGES = 150;
@@ -168,7 +170,7 @@ namespace transport
std::shared_ptr<const i2p::data::RouterInfo> GetRandomPeer (bool isHighBandwidth) const;
/** get a trusted first hop for restricted routes */
std::shared_ptr<const i2p::data::RouterInfo> GetRestrictedPeer() const;
std::shared_ptr<const i2p::data::RouterInfo> GetRestrictedPeer();
/** do we want to use restricted routes? */
bool RoutesRestricted() const;
/** restrict routes to use only these router families for first hops */
@@ -191,7 +193,7 @@ namespace transport
void HandleRequestComplete (std::shared_ptr<const i2p::data::RouterInfo> r, i2p::data::IdentHash ident);
std::shared_ptr<TransportSession> PostMessages (const i2p::data::IdentHash& ident, std::list<std::shared_ptr<i2p::I2NPMessage> >& msgs);
bool ConnectToPeer (const i2p::data::IdentHash& ident, std::shared_ptr<Peer> peer);
void SetPriority (std::shared_ptr<Peer> peer) const;
void SetPriority (std::shared_ptr<Peer> peer);
void HandlePeerCleanupTimer (const boost::system::error_code& ecode);
void HandlePeerTestTimer (const boost::system::error_code& ecode);
void HandleUpdateBandwidthTimer (const boost::system::error_code& ecode);
@@ -239,6 +241,7 @@ namespace transport
mutable std::mutex m_TrustedRoutersMutex;
i2p::I2NPMessagesHandler m_LoopbackHandler;
std::mt19937 m_Rng;
public:

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2024, The PurpleI2P Project
* Copyright (c) 2013-2025, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@@ -179,9 +179,12 @@ namespace tunnel
{
uint8_t ret = hop->GetRetCode (msg + 1);
LogPrint (eLogDebug, "Tunnel: Build response ret code=", (int)ret);
auto profile = i2p::data::netdb.FindRouterProfile (hop->ident->GetIdentHash ());
if (profile)
profile->TunnelBuildResponse (ret);
if (hop->ident)
i2p::data::UpdateRouterProfile (hop->ident->GetIdentHash (),
[ret](std::shared_ptr<i2p::data::RouterProfile> profile)
{
if (profile) profile->TunnelBuildResponse (ret);
});
if (ret)
// if any of participants declined the tunnel is not established
established = false;
@@ -743,11 +746,11 @@ namespace tunnel
while (hop)
{
if (hop->ident)
{
auto profile = i2p::data::netdb.FindRouterProfile (hop->ident->GetIdentHash ());
if (profile)
profile->TunnelNonReplied ();
}
i2p::data::UpdateRouterProfile (hop->ident->GetIdentHash (),
[](std::shared_ptr<i2p::data::RouterProfile> profile)
{
if (profile) profile->TunnelNonReplied ();
});
hop = hop->next;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2024, The PurpleI2P Project
* Copyright (c) 2013-2025, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@@ -560,7 +560,7 @@ namespace tunnel
i2p::data::netdb.GetRandomRouter (prevHop, reverse, endpoint, false);
if (hop)
{
if (!hop->GetProfile ()->IsBad ())
if (!hop->HasProfile () || !hop->GetProfile ()->IsBad ())
break;
}
else if (tryClient)
@@ -588,7 +588,7 @@ namespace tunnel
(inbound && i2p::transport::transports.GetNumPeers () > 25))
{
auto r = i2p::transport::transports.GetRandomPeer (m_IsHighBandwidth && !i2p::context.IsLimitedConnectivity ());
if (r && r->IsECIES () && !r->GetProfile ()->IsBad () &&
if (r && r->IsECIES () && (!r->HasProfile () || !r->GetProfile ()->IsBad ()) &&
(numHops > 1 || (r->IsV4 () && (!inbound || r->IsPublished (true))))) // first inbound must be published ipv4
{
prevHop = r;

View File

@@ -764,6 +764,7 @@ namespace client
void I2CPSession::AddRoutingSession (const i2p::data::IdentHash& signingKey, std::shared_ptr<i2p::garlic::GarlicRoutingSession> remoteSession)
{
if (!remoteSession) return;
remoteSession->SetAckRequestInterval (I2CP_SESSION_ACK_REQUEST_INTERVAL);
std::lock_guard<std::mutex> l(m_RoutingSessionsMutex);
m_RoutingSessions[signingKey] = remoteSession;
}
@@ -1110,12 +1111,12 @@ namespace client
void I2CPServer::Stop ()
{
m_Acceptor.cancel ();
{
auto sessions = m_Sessions;
for (auto& it: sessions)
it.second->Stop ();
}
m_Sessions.clear ();
decltype(m_Sessions) sessions;
m_Sessions.swap (sessions);
for (auto& it: sessions)
it.second->Stop ();
StopIOService ();
}

View File

@@ -31,6 +31,7 @@ namespace client
const size_t I2CP_MAX_MESSAGE_LENGTH = 65535;
const size_t I2CP_MAX_SEND_QUEUE_SIZE = 1024*1024; // in bytes, 1M
const int I2CP_LEASESET_CREATION_TIMEOUT = 10; // in seconds
const int I2CP_SESSION_ACK_REQUEST_INTERVAL = 12100; // in milliseconds
const size_t I2CP_HEADER_LENGTH_OFFSET = 0;
const size_t I2CP_HEADER_TYPE_OFFSET = I2CP_HEADER_LENGTH_OFFSET + 4;

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2024, The PurpleI2P Project
* Copyright (c) 2013-2025, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@@ -1350,12 +1350,14 @@ namespace client
LogPrint (eLogError, "SAM: Runtime exception: ", ex.what ());
}
decltype(m_Sessions) sessions;
{
std::unique_lock<std::mutex> l(m_SessionsMutex);
for (auto& it: m_Sessions)
it.second->Close ();
m_Sessions.clear ();
}
m_Sessions.swap (sessions);
}
for (auto& it: sessions)
it.second->Close ();
StopIOService ();
}