|
|
|
|
@@ -9,7 +9,7 @@
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <inttypes.h>
|
|
|
|
|
#include <string>
|
|
|
|
|
#include <map>
|
|
|
|
|
#include <unordered_map>
|
|
|
|
|
#include <fstream>
|
|
|
|
|
#include <chrono>
|
|
|
|
|
#include <condition_variable>
|
|
|
|
|
@@ -17,6 +17,7 @@
|
|
|
|
|
#include <boost/algorithm/string.hpp>
|
|
|
|
|
#include "Base.h"
|
|
|
|
|
#include "util.h"
|
|
|
|
|
#include "Timestamp.h"
|
|
|
|
|
#include "Identity.h"
|
|
|
|
|
#include "FS.h"
|
|
|
|
|
#include "Log.h"
|
|
|
|
|
@@ -49,9 +50,10 @@ namespace client
|
|
|
|
|
if (m_IsPersist)
|
|
|
|
|
i2p::config::GetOption("addressbook.hostsfile", m_HostsFile);
|
|
|
|
|
}
|
|
|
|
|
std::shared_ptr<const i2p::data::IdentityEx> GetAddress (const i2p::data::IdentHash& ident) const override;
|
|
|
|
|
std::shared_ptr<const i2p::data::IdentityEx> GetAddress (const i2p::data::IdentHash& ident) override;
|
|
|
|
|
void AddAddress (std::shared_ptr<const i2p::data::IdentityEx> address) override;
|
|
|
|
|
void RemoveAddress (const i2p::data::IdentHash& ident) override;
|
|
|
|
|
void CleanUpCache () override;
|
|
|
|
|
|
|
|
|
|
bool Init () override;
|
|
|
|
|
int Load (Addresses& addresses) override;
|
|
|
|
|
@@ -61,7 +63,7 @@ namespace client
|
|
|
|
|
void SaveEtag (const i2p::data::IdentHash& subsciption, const std::string& etag, const std::string& lastModified) override;
|
|
|
|
|
bool GetEtag (const i2p::data::IdentHash& subscription, std::string& etag, std::string& lastModified) override;
|
|
|
|
|
void ResetEtags () override;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
|
|
|
|
int LoadFromFile (const std::string& filename, Addresses& addresses); // returns -1 if can't open file, otherwise number of records
|
|
|
|
|
@@ -72,6 +74,8 @@ namespace client
|
|
|
|
|
std::string etagsPath, indexPath, localPath;
|
|
|
|
|
bool m_IsPersist;
|
|
|
|
|
std::string m_HostsFile; // file to dump hosts.txt, empty if not used
|
|
|
|
|
std::unordered_map<i2p::data::IdentHash, std::pair<std::vector<uint8_t>, uint64_t> > m_FullAddressCache; // ident hash -> (full ident buffer, last access timestamp)
|
|
|
|
|
std::mutex m_FullAddressCacheMutex;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
bool AddressBookFilesystemStorage::Init()
|
|
|
|
|
@@ -92,8 +96,19 @@ namespace client
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::shared_ptr<const i2p::data::IdentityEx> AddressBookFilesystemStorage::GetAddress (const i2p::data::IdentHash& ident) const
|
|
|
|
|
std::shared_ptr<const i2p::data::IdentityEx> AddressBookFilesystemStorage::GetAddress (const i2p::data::IdentHash& ident)
|
|
|
|
|
{
|
|
|
|
|
auto ts = i2p::util::GetMonotonicSeconds ();
|
|
|
|
|
{
|
|
|
|
|
std::lock_guard<std::mutex> l(m_FullAddressCacheMutex);
|
|
|
|
|
auto it = m_FullAddressCache.find (ident);
|
|
|
|
|
if (it != m_FullAddressCache.end ())
|
|
|
|
|
{
|
|
|
|
|
it->second.second = ts;
|
|
|
|
|
return std::make_shared<i2p::data::IdentityEx>(it->second.first.data (), it->second.first.size ());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!m_IsPersist)
|
|
|
|
|
{
|
|
|
|
|
LogPrint(eLogDebug, "Addressbook: Persistence is disabled");
|
|
|
|
|
@@ -101,43 +116,67 @@ namespace client
|
|
|
|
|
}
|
|
|
|
|
std::string filename = storage.Path(ident.ToBase32());
|
|
|
|
|
std::ifstream f(filename, std::ifstream::binary);
|
|
|
|
|
if (!f.is_open ()) {
|
|
|
|
|
if (!f.is_open ())
|
|
|
|
|
{
|
|
|
|
|
LogPrint(eLogDebug, "Addressbook: Requested, but not found: ", filename);
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
f.seekg (0,std::ios::end);
|
|
|
|
|
size_t len = f.tellg ();
|
|
|
|
|
if (len < i2p::data::DEFAULT_IDENTITY_SIZE) {
|
|
|
|
|
if (len < i2p::data::DEFAULT_IDENTITY_SIZE)
|
|
|
|
|
{
|
|
|
|
|
LogPrint (eLogError, "Addressbook: File ", filename, " is too short: ", len);
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
f.seekg(0, std::ios::beg);
|
|
|
|
|
uint8_t * buf = new uint8_t[len];
|
|
|
|
|
f.read((char *)buf, len);
|
|
|
|
|
auto address = std::make_shared<i2p::data::IdentityEx>(buf, len);
|
|
|
|
|
delete[] buf;
|
|
|
|
|
return address;
|
|
|
|
|
std::vector<uint8_t> buf(len);
|
|
|
|
|
f.read((char *)buf.data (), len);
|
|
|
|
|
if (!f)
|
|
|
|
|
{
|
|
|
|
|
LogPrint (eLogError, "Addressbook: Couldn't read ", filename);
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
std::lock_guard<std::mutex> l(m_FullAddressCacheMutex);
|
|
|
|
|
m_FullAddressCache.try_emplace (ident, buf, ts);
|
|
|
|
|
}
|
|
|
|
|
return std::make_shared<i2p::data::IdentityEx>(buf.data (), len);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AddressBookFilesystemStorage::AddAddress (std::shared_ptr<const i2p::data::IdentityEx> address)
|
|
|
|
|
{
|
|
|
|
|
if (!m_IsPersist) return;
|
|
|
|
|
std::string path = storage.Path( address->GetIdentHash().ToBase32() );
|
|
|
|
|
std::ofstream f (path, std::ofstream::binary | std::ofstream::out);
|
|
|
|
|
if (!f.is_open ()) {
|
|
|
|
|
LogPrint (eLogError, "Addressbook: Can't open file ", path);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (!address) return;
|
|
|
|
|
size_t len = address->GetFullLen ();
|
|
|
|
|
uint8_t * buf = new uint8_t[len];
|
|
|
|
|
address->ToBuffer (buf, len);
|
|
|
|
|
f.write ((char *)buf, len);
|
|
|
|
|
delete[] buf;
|
|
|
|
|
std::vector<uint8_t> buf;
|
|
|
|
|
if (!len) return; // invalid address
|
|
|
|
|
{
|
|
|
|
|
std::lock_guard<std::mutex> l(m_FullAddressCacheMutex);
|
|
|
|
|
auto [it, inserted] = m_FullAddressCache.try_emplace (address->GetIdentHash(), len, i2p::util::GetMonotonicSeconds ());
|
|
|
|
|
if (inserted)
|
|
|
|
|
address->ToBuffer (it->second.first.data (), len);
|
|
|
|
|
if (m_IsPersist)
|
|
|
|
|
buf = it->second.first;
|
|
|
|
|
}
|
|
|
|
|
if (m_IsPersist && !buf.empty ())
|
|
|
|
|
{
|
|
|
|
|
std::string path = storage.Path(address->GetIdentHash().ToBase32());
|
|
|
|
|
std::ofstream f (path, std::ofstream::binary | std::ofstream::out);
|
|
|
|
|
if (!f.is_open ())
|
|
|
|
|
{
|
|
|
|
|
LogPrint (eLogError, "Addressbook: Can't open file ", path);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
f.write ((const char *)buf.data (), len);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AddressBookFilesystemStorage::RemoveAddress (const i2p::data::IdentHash& ident)
|
|
|
|
|
{
|
|
|
|
|
{
|
|
|
|
|
std::lock_guard<std::mutex> l(m_FullAddressCacheMutex);
|
|
|
|
|
m_FullAddressCache.erase (ident);
|
|
|
|
|
}
|
|
|
|
|
if (!m_IsPersist) return;
|
|
|
|
|
storage.Remove( ident.ToBase32() );
|
|
|
|
|
}
|
|
|
|
|
@@ -281,6 +320,19 @@ namespace client
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AddressBookFilesystemStorage::CleanUpCache ()
|
|
|
|
|
{
|
|
|
|
|
auto ts = i2p::util::GetMonotonicSeconds ();
|
|
|
|
|
std::lock_guard<std::mutex> l(m_FullAddressCacheMutex);
|
|
|
|
|
for (auto it = m_FullAddressCache.begin (); it != m_FullAddressCache.end ();)
|
|
|
|
|
{
|
|
|
|
|
if (ts > it->second.second + ADDRESS_CACHE_EXPIRATION_TIMEOUT)
|
|
|
|
|
it = m_FullAddressCache.erase (it);
|
|
|
|
|
else
|
|
|
|
|
it++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
Address::Address (std::string_view b32):
|
|
|
|
|
@@ -327,6 +379,7 @@ namespace client
|
|
|
|
|
LoadHosts (); /* try storage, then hosts.txt, then download */
|
|
|
|
|
StartSubscriptions ();
|
|
|
|
|
StartLookups ();
|
|
|
|
|
ScheduleCacheUpdate ();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -341,9 +394,14 @@ namespace client
|
|
|
|
|
StopSubscriptions ();
|
|
|
|
|
if (m_SubscriptionsUpdateTimer)
|
|
|
|
|
{
|
|
|
|
|
delete m_SubscriptionsUpdateTimer;
|
|
|
|
|
m_SubscriptionsUpdateTimer->cancel ();
|
|
|
|
|
m_SubscriptionsUpdateTimer = nullptr;
|
|
|
|
|
}
|
|
|
|
|
if (m_AddressCacheUpdateTimer)
|
|
|
|
|
{
|
|
|
|
|
m_AddressCacheUpdateTimer->cancel ();
|
|
|
|
|
m_AddressCacheUpdateTimer = nullptr;
|
|
|
|
|
}
|
|
|
|
|
bool isDownloading = m_Downloading.valid ();
|
|
|
|
|
if (isDownloading)
|
|
|
|
|
{
|
|
|
|
|
@@ -682,7 +740,7 @@ namespace client
|
|
|
|
|
auto dest = i2p::client::context.GetSharedLocalDestination ();
|
|
|
|
|
if (dest)
|
|
|
|
|
{
|
|
|
|
|
m_SubscriptionsUpdateTimer = new boost::asio::deadline_timer (dest->GetService ());
|
|
|
|
|
m_SubscriptionsUpdateTimer = std::make_unique<boost::asio::deadline_timer>(dest->GetService ());
|
|
|
|
|
m_SubscriptionsUpdateTimer->expires_from_now (boost::posix_time::minutes(INITIAL_SUBSCRIPTION_UPDATE_TIMEOUT));
|
|
|
|
|
m_SubscriptionsUpdateTimer->async_wait (std::bind (&AddressBook::HandleSubscriptionsUpdateTimer,
|
|
|
|
|
this, std::placeholders::_1));
|
|
|
|
|
@@ -833,6 +891,29 @@ namespace client
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AddressBook::ScheduleCacheUpdate ()
|
|
|
|
|
{
|
|
|
|
|
if (!m_AddressCacheUpdateTimer)
|
|
|
|
|
{
|
|
|
|
|
auto dest = i2p::client::context.GetSharedLocalDestination ();
|
|
|
|
|
if(dest)
|
|
|
|
|
m_AddressCacheUpdateTimer = std::make_unique<boost::asio::deadline_timer>(dest->GetService ());
|
|
|
|
|
}
|
|
|
|
|
if (m_AddressCacheUpdateTimer)
|
|
|
|
|
{
|
|
|
|
|
m_AddressCacheUpdateTimer->expires_from_now (boost::posix_time::seconds(ADDRESS_CACHE_UPDATE_INTERVAL ));
|
|
|
|
|
m_SubscriptionsUpdateTimer->async_wait (
|
|
|
|
|
[this](const boost::system::error_code& ecode)
|
|
|
|
|
{
|
|
|
|
|
if (ecode != boost::asio::error::operation_aborted)
|
|
|
|
|
{
|
|
|
|
|
if (m_Storage) m_Storage->CleanUpCache ();
|
|
|
|
|
ScheduleCacheUpdate ();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AddressBookSubscription::AddressBookSubscription (AddressBook& book, std::string_view link):
|
|
|
|
|
m_Book (book), m_Link (link)
|
|
|
|
|
{
|
|
|
|
|
|