I2P: End-to-End encrypted and anonymous Internet https://i2pd.website/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

221 lines
6.7 KiB

#include <boost/filesystem.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/ini_parser.hpp>
#include "base64.h"
#include "util.h"
#include "Profiling.h"
namespace i2p
{
namespace data
{
RouterProfile::RouterProfile (const IdentHash& identHash):
m_IdentHash (identHash), m_LastUpdateTime (boost::posix_time::second_clock::local_time()),
m_LastDeclinedTime (boost::posix_time::min_date_time),
m_NumTunnelsAgreed (0), m_NumTunnelsDeclined (0), m_NumTunnelsNonReplied (0),
m_NumTimesTaken (0), m_NumTimesRejected (0)
{
}
boost::posix_time::ptime RouterProfile::GetTime () const
{
return boost::posix_time::second_clock::local_time();
}
void RouterProfile::UpdateTime ()
{
m_LastUpdateTime = GetTime ();
}
void RouterProfile::Save ()
{
// fill sections
boost::property_tree::ptree participation;
participation.put (PEER_PROFILE_PARTICIPATION_AGREED, m_NumTunnelsAgreed);
participation.put (PEER_PROFILE_PARTICIPATION_DECLINED, m_NumTunnelsDeclined);
participation.put (PEER_PROFILE_PARTICIPATION_NON_REPLIED, m_NumTunnelsNonReplied);
boost::property_tree::ptree usage;
usage.put (PEER_PROFILE_USAGE_TAKEN, m_NumTimesTaken);
usage.put (PEER_PROFILE_USAGE_REJECTED, m_NumTimesRejected);
// fill property tree
boost::property_tree::ptree pt;
pt.put (PEER_PROFILE_LAST_UPDATE_TIME, boost::posix_time::to_simple_string (m_LastUpdateTime));
pt.put_child (PEER_PROFILE_SECTION_PARTICIPATION, participation);
pt.put_child (PEER_PROFILE_SECTION_USAGE, usage);
// save to file
auto path = i2p::util::filesystem::GetDefaultDataDir() / PEER_PROFILES_DIRECTORY;
if (!boost::filesystem::exists (path))
{
// Create directory is necessary
if (!boost::filesystem::create_directory (path))
{
LogPrint (eLogError, "Failed to create directory ", path);
return;
}
const char * chars = GetBase64SubstitutionTable (); // 64 bytes
for (int i = 0; i < 64; i++)
{
auto path1 = path / (std::string ("p") + chars[i]);
if (!boost::filesystem::create_directory (path1))
{
LogPrint (eLogError, "Failed to create directory ", path1);
return;
}
}
}
std::string base64 = m_IdentHash.ToBase64 ();
path = path / (std::string ("p") + base64[0]);
auto filename = path / (std::string (PEER_PROFILE_PREFIX) + base64 + ".txt");
try
{
boost::property_tree::write_ini (filename.string (), pt);
}
catch (std::exception& ex)
{
LogPrint (eLogError, "Can't write ", filename, ": ", ex.what ());
}
}
void RouterProfile::Load ()
{
std::string base64 = m_IdentHash.ToBase64 ();
auto path = i2p::util::filesystem::GetDefaultDataDir() / PEER_PROFILES_DIRECTORY;
path /= std::string ("p") + base64[0];
auto filename = path / (std::string (PEER_PROFILE_PREFIX) + base64 + ".txt");
if (boost::filesystem::exists (filename))
{
boost::property_tree::ptree pt;
try
{
boost::property_tree::read_ini (filename.string (), pt);
}
catch (std::exception& ex)
{
LogPrint (eLogError, "Can't read ", filename, ": ", ex.what ());
return;
}
try
{
auto t = pt.get (PEER_PROFILE_LAST_UPDATE_TIME, "");
if (t.length () > 0)
m_LastUpdateTime = boost::posix_time::time_from_string (t);
if ((GetTime () - m_LastUpdateTime).hours () < PEER_PROFILE_EXPIRATION_TIMEOUT)
{
try
{
// read participations
auto participations = pt.get_child (PEER_PROFILE_SECTION_PARTICIPATION);
m_NumTunnelsAgreed = participations.get (PEER_PROFILE_PARTICIPATION_AGREED, 0);
m_NumTunnelsDeclined = participations.get (PEER_PROFILE_PARTICIPATION_DECLINED, 0);
m_NumTunnelsNonReplied = participations.get (PEER_PROFILE_PARTICIPATION_NON_REPLIED, 0);
}
catch (boost::property_tree::ptree_bad_path& ex)
{
LogPrint (eLogWarning, "Missing section ", PEER_PROFILE_SECTION_PARTICIPATION);
}
try
{
// read usage
auto usage = pt.get_child (PEER_PROFILE_SECTION_USAGE);
m_NumTimesTaken = usage.get (PEER_PROFILE_USAGE_TAKEN, 0);
m_NumTimesRejected = usage.get (PEER_PROFILE_USAGE_REJECTED, 0);
}
catch (boost::property_tree::ptree_bad_path& ex)
{
LogPrint (eLogWarning, "Missing section ", PEER_PROFILE_SECTION_USAGE);
}
}
else
*this = RouterProfile (m_IdentHash);
}
catch (std::exception& ex)
{
LogPrint (eLogError, "Can't read profile ", base64, " :", ex.what ());
}
}
}
void RouterProfile::TunnelBuildResponse (uint8_t ret)
{
UpdateTime ();
if (ret > 0)
{
m_LastDeclinedTime = m_LastUpdateTime;
m_NumTunnelsDeclined++;
}
else
m_NumTunnelsAgreed++;
}
void RouterProfile::TunnelNonReplied ()
{
m_NumTunnelsNonReplied++;
UpdateTime ();
}
bool RouterProfile::IsLowPartcipationRate () const
{
return 4*m_NumTunnelsAgreed < m_NumTunnelsDeclined; // < 20% rate
}
bool RouterProfile::IsLowReplyRate () const
{
auto total = m_NumTunnelsAgreed + m_NumTunnelsDeclined;
return m_NumTunnelsNonReplied > 10*(total + 1);
}
bool RouterProfile::IsBad ()
{
auto t = GetTime ();
auto elapsedTime = (t - m_LastUpdateTime).total_seconds ();
if (elapsedTime > 1800)
{
m_NumTunnelsNonReplied = 0; // drop non-replied after 30 minutes of inactivity
if (elapsedTime > 14400) // drop agreed and declined after 4 hours of inactivity
{
m_NumTunnelsAgreed = 0;
m_NumTunnelsDeclined = 0;
}
}
auto isBad = IsAlwaysDeclining () || IsLowPartcipationRate () || IsLowReplyRate () ||
((t - m_LastDeclinedTime).total_seconds () < 300); // declined in last 5 minutes
if (isBad) m_NumTimesRejected++; else m_NumTimesTaken++;
return isBad;
}
std::shared_ptr<RouterProfile> GetRouterProfile (const IdentHash& identHash)
{
auto profile = std::make_shared<RouterProfile> (identHash);
profile->Load (); // if possible
return profile;
}
void DeleteObsoleteProfiles ()
{
int num = 0;
auto ts = boost::posix_time::second_clock::local_time();
boost::filesystem::path p (i2p::util::filesystem::GetDataDir()/PEER_PROFILES_DIRECTORY);
if (boost::filesystem::exists (p))
{
boost::filesystem::directory_iterator end;
for (boost::filesystem::directory_iterator it (p); it != end; ++it)
{
if (boost::filesystem::is_directory (it->status()))
{
for (boost::filesystem::directory_iterator it1 (it->path ()); it1 != end; ++it1)
{
auto lastModified = boost::posix_time::from_time_t (boost::filesystem::last_write_time (it1->path ()));
if ((ts - lastModified).hours () >= PEER_PROFILE_EXPIRATION_TIMEOUT)
{
boost::filesystem::remove (it1->path ());
num++;
}
}
}
}
}
LogPrint (eLogInfo, num, " obsolete profiles deleted");
}
}
}