persist incoming tags

pull/797/merge
orignal 8 years ago
parent abf0f5ac87
commit c2f62ba52a

@ -110,6 +110,7 @@ namespace client
{ {
if (!m_IsRunning) if (!m_IsRunning)
{ {
LoadTags ();
m_IsRunning = true; m_IsRunning = true;
m_Pool->SetLocalDestination (shared_from_this ()); m_Pool->SetLocalDestination (shared_from_this ());
m_Pool->SetActive (true); m_Pool->SetActive (true);
@ -145,6 +146,7 @@ namespace client
delete m_Thread; delete m_Thread;
m_Thread = 0; m_Thread = 0;
} }
SaveTags ();
CleanUp (); // GarlicDestination CleanUp (); // GarlicDestination
return true; return true;
} }

@ -16,6 +16,7 @@
#include "Base.h" #include "Base.h"
#include "FS.h" #include "FS.h"
#include "Log.h" #include "Log.h"
#include "Garlic.h"
namespace i2p { namespace i2p {
namespace fs { namespace fs {
@ -93,6 +94,11 @@ namespace fs {
std::string destinations = DataDirPath("destinations"); std::string destinations = DataDirPath("destinations");
if (!boost::filesystem::exists(destinations)) if (!boost::filesystem::exists(destinations))
boost::filesystem::create_directory(destinations); boost::filesystem::create_directory(destinations);
std::string tags = DataDirPath("tags");
if (!boost::filesystem::exists(tags))
boost::filesystem::create_directory(tags);
else
i2p::garlic::CleanUpTagsFiles ();
return true; return true;
} }
@ -116,6 +122,14 @@ namespace fs {
return boost::filesystem::exists(path); return boost::filesystem::exists(path);
} }
uint32_t GetLastUpdateTime (const std::string & path)
{
if (!boost::filesystem::exists(path)) return 0;
boost::system::error_code ec;
auto t = boost::filesystem::last_write_time (path, ec);
return ec ? 0 : t;
}
bool Remove(const std::string & path) { bool Remove(const std::string & path) {
if (!boost::filesystem::exists(path)) if (!boost::filesystem::exists(path))
return false; return false;

@ -97,7 +97,7 @@ namespace fs {
* @param files Vector to store found files * @param files Vector to store found files
* @return true on success and false if directory not exists * @return true on success and false if directory not exists
*/ */
bool ReadDir(const std::string & path, std::vector<std::string> & files); bool ReadDir(const std::string & path, std::vector<std::string> & files);
/** /**
* @brief Remove file with given path * @brief Remove file with given path
@ -112,6 +112,8 @@ namespace fs {
* @return true if file exists, false otherwise * @return true if file exists, false otherwise
*/ */
bool Exists(const std::string & path); bool Exists(const std::string & path);
uint32_t GetLastUpdateTime (const std::string & path); // seconds since epoch
bool CreateDirectory (const std::string& path); bool CreateDirectory (const std::string& path);

@ -10,6 +10,7 @@
#include "Transports.h" #include "Transports.h"
#include "Timestamp.h" #include "Timestamp.h"
#include "Log.h" #include "Log.h"
#include "FS.h"
#include "Garlic.h" #include "Garlic.h"
namespace i2p namespace i2p
@ -412,9 +413,7 @@ namespace garlic
if (key) if (key)
{ {
uint32_t ts = i2p::util::GetSecondsSinceEpoch (); uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
auto decryption = std::make_shared<i2p::crypto::CBCDecryption>(); m_Tags[SessionTag(tag, ts)] = std::make_shared<AESDecryption>(key);
decryption->SetKey (key);
m_Tags[SessionTag(tag, ts)] = decryption;
} }
} }
@ -456,8 +455,7 @@ namespace garlic
ElGamalBlock elGamal; ElGamalBlock elGamal;
if (length >= 514 && i2p::crypto::ElGamalDecrypt (GetEncryptionPrivateKey (), buf, (uint8_t *)&elGamal, m_Ctx, true)) if (length >= 514 && i2p::crypto::ElGamalDecrypt (GetEncryptionPrivateKey (), buf, (uint8_t *)&elGamal, m_Ctx, true))
{ {
auto decryption = std::make_shared<i2p::crypto::CBCDecryption>(); auto decryption = std::make_shared<AESDecryption>(elGamal.sessionKey);
decryption->SetKey (elGamal.sessionKey);
uint8_t iv[32]; // IV is first 16 bytes uint8_t iv[32]; // IV is first 16 bytes
SHA256(elGamal.preIV, 32, iv); SHA256(elGamal.preIV, 32, iv);
decryption->SetIV (iv); decryption->SetIV (iv);
@ -469,7 +467,7 @@ namespace garlic
} }
} }
void GarlicDestination::HandleAESBlock (uint8_t * buf, size_t len, std::shared_ptr<i2p::crypto::CBCDecryption> decryption, void GarlicDestination::HandleAESBlock (uint8_t * buf, size_t len, std::shared_ptr<AESDecryption> decryption,
std::shared_ptr<i2p::tunnel::InboundTunnel> from) std::shared_ptr<i2p::tunnel::InboundTunnel> from)
{ {
uint16_t tagCount = bufbe16toh (buf); uint16_t tagCount = bufbe16toh (buf);
@ -714,5 +712,75 @@ namespace garlic
HandleDeliveryStatusMessage (msg); HandleDeliveryStatusMessage (msg);
} }
void GarlicDestination::SaveTags ()
{
if (m_Tags.empty ()) return;
std::string ident = GetIdentHash().ToBase32();
std::string path = i2p::fs::DataDirPath("tags", (ident + ".tags"));
std::ofstream f (path, std::ofstream::binary | std::ofstream::out | std::ofstream::trunc);
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
// 4 bytes timestamp, 32 bytes tag, 32 bytes key
for (auto it: m_Tags)
{
if (ts < it.first.creationTime + INCOMING_TAGS_EXPIRATION_TIMEOUT)
{
f.write ((char *)&it.first.creationTime, 4);
f.write ((char *)it.first.data (), 32);
f.write ((char *)it.second->GetKey ().data (), 32);
}
}
}
void GarlicDestination::LoadTags ()
{
std::string ident = GetIdentHash().ToBase32();
std::string path = i2p::fs::DataDirPath("tags", (ident + ".tags"));
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
if (ts < i2p::fs::GetLastUpdateTime (path) + INCOMING_TAGS_EXPIRATION_TIMEOUT)
{
// might contain non-expired tags
std::ifstream f (path, std::ifstream::binary);
if (f)
{
std::map<i2p::crypto::AESKey, std::shared_ptr<AESDecryption> > keys;
// 4 bytes timestamp, 32 bytes tag, 32 bytes key
while (!f.eof ())
{
uint32_t t;
uint8_t tag[32], key[32];
f.read ((char *)&t, 4); if (f.eof ()) break;
if (ts < t + INCOMING_TAGS_EXPIRATION_TIMEOUT)
{
f.read ((char *)tag, 32);
f.read ((char *)key, 32);
}
else
f.seekg (64, std::ios::cur); // skip
if (f.eof ()) break;
std::shared_ptr<AESDecryption> decryption;
auto it = keys.find (key);
if (it != keys.end ())
decryption = it->second;
else
decryption = std::make_shared<AESDecryption>(key);
m_Tags.insert (std::make_pair (SessionTag (tag, ts), decryption));
}
if (!m_Tags.empty ())
LogPrint (eLogInfo, m_Tags.size (), " loaded for ", ident);
}
}
i2p::fs::Remove (path);
}
void CleanUpTagsFiles ()
{
std::vector<std::string> files;
i2p::fs::ReadDir (i2p::fs::DataDirPath("tags"), files);
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
for (auto it: files)
if (ts >= i2p::fs::GetLastUpdateTime (it) + INCOMING_TAGS_EXPIRATION_TIMEOUT)
i2p::fs::Remove (it);
}
} }
} }

@ -59,6 +59,22 @@ namespace garlic
uint32_t creationTime; // seconds since epoch uint32_t creationTime; // seconds since epoch
}; };
// AESDecryption is associated with session tags and store key
class AESDecryption: public i2p::crypto::CBCDecryption
{
public:
AESDecryption (const uint8_t * key): m_Key (key)
{
SetKey (key);
}
const i2p::crypto::AESKey& GetKey () const { return m_Key; };
private:
i2p::crypto::AESKey m_Key;
};
struct GarlicRoutingPath struct GarlicRoutingPath
{ {
std::shared_ptr<i2p::tunnel::OutboundTunnel> outboundTunnel; std::shared_ptr<i2p::tunnel::OutboundTunnel> outboundTunnel;
@ -67,7 +83,7 @@ namespace garlic
uint32_t updateTime; // seconds since epoch uint32_t updateTime; // seconds since epoch
int numTimesUsed; int numTimesUsed;
}; };
class GarlicDestination; class GarlicDestination;
class GarlicRoutingSession: public std::enable_shared_from_this<GarlicRoutingSession> class GarlicRoutingSession: public std::enable_shared_from_this<GarlicRoutingSession>
{ {
@ -180,10 +196,13 @@ namespace garlic
void HandleGarlicMessage (std::shared_ptr<I2NPMessage> msg); void HandleGarlicMessage (std::shared_ptr<I2NPMessage> msg);
void HandleDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg); void HandleDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
void SaveTags ();
void LoadTags ();
private: private:
void HandleAESBlock (uint8_t * buf, size_t len, std::shared_ptr<i2p::crypto::CBCDecryption> decryption, void HandleAESBlock (uint8_t * buf, size_t len, std::shared_ptr<AESDecryption> decryption,
std::shared_ptr<i2p::tunnel::InboundTunnel> from); std::shared_ptr<i2p::tunnel::InboundTunnel> from);
void HandleGarlicPayload (uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from); void HandleGarlicPayload (uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from);
@ -195,7 +214,7 @@ namespace garlic
std::mutex m_SessionsMutex; std::mutex m_SessionsMutex;
std::map<i2p::data::IdentHash, GarlicRoutingSessionPtr> m_Sessions; std::map<i2p::data::IdentHash, GarlicRoutingSessionPtr> m_Sessions;
// incoming // incoming
std::map<SessionTag, std::shared_ptr<i2p::crypto::CBCDecryption>> m_Tags; std::map<SessionTag, std::shared_ptr<AESDecryption> > m_Tags;
// DeliveryStatus // DeliveryStatus
std::mutex m_DeliveryStatusSessionsMutex; std::mutex m_DeliveryStatusSessionsMutex;
std::map<uint32_t, GarlicRoutingSessionPtr> m_DeliveryStatusSessions; // msgID -> session std::map<uint32_t, GarlicRoutingSessionPtr> m_DeliveryStatusSessions; // msgID -> session
@ -205,7 +224,10 @@ namespace garlic
// for HTTP only // for HTTP only
size_t GetNumIncomingTags () const { return m_Tags.size (); } size_t GetNumIncomingTags () const { return m_Tags.size (); }
const decltype(m_Sessions)& GetSessions () const { return m_Sessions; }; const decltype(m_Sessions)& GetSessions () const { return m_Sessions; };
}; };
void CleanUpTagsFiles ();
} }
} }

Loading…
Cancel
Save