Compare commits

..

4 Commits

Author SHA1 Message Date
r4sas
6d0dad5fd4 [build] test linking reorder
Signed-off-by: r4sas <r4sas@i2pmail.org>
2025-02-10 21:55:19 +00:00
R4SAS
b17087ba4a [build] test linking reorder on cmake 2025-02-10 18:54:52 +03:00
R4SAS
f466be3b5c [build] test linking reorder on mingw 2025-02-10 18:53:30 +03:00
R4SAS
24f167141d [build] test linking reorder on linux 2025-02-10 18:52:32 +03:00
19 changed files with 204 additions and 274 deletions

View File

@@ -1,7 +1,7 @@
# for this file format description,
# see https://github.com/olivierlacan/keep-a-changelog
## [2.56.0] - 2025-02-11
## [2.56.0] - 2025-02-10
### Added
- Config params for shared local destination
- AddressBook full addresses cache
@@ -12,8 +12,8 @@
- Set minimal version for peer test to 0.9.62
- Send ack requested flag after second SSU2 resend attempt
- Shorter ECIESx25519 ack request interval for datagram and I2CP sessions
- Don't change datagram routing path too often if unidirectional data stream
- Reduce LeaseSet and local RouterInfo publishing confirmation intervals
- Don't change datagram routing path too often if unidirectional data stream
- Reduce LeaseSet and local RouterInfo publishing confimation intervals
- Don't delete buffer of connected routers or if an update received
- Smaller RouterInfo request timeout if sent directly
- Persist local RouterInfo in separate thread
@@ -85,12 +85,12 @@
- Handle i2cp.inboundlimit and i2cp.outboundlimit params in I2CP
- Publish LeaseSet with new timestamp update if tunnel was replaced in the same second
- Increase max number of generated tags to 800 per tagset
- Routing path expiration by time instead num attempts
- Routing path expiration by time instead num attempts
- Save timestamp from epoch instead local time to profiles
- Update introducer's iTag if session to introducer was replaced to new one
- RTT, window size and number of NACKs calculation for streaming
- Don't select same peer for tunnel too often
- Use WinApi for data path UTF-8 conversion for Windows
- Use WinApi for data path UTF-8 conversion for Windows
### Fixed
- Jump link crash if address book is disabled
- Race condition if connect through an introducer

View File

@@ -1,13 +0,0 @@
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

@@ -148,7 +148,7 @@ getent passwd i2pd >/dev/null || \
%changelog
* Tue Feb 11 2025 orignal <orignal@i2pmail.org> - 2.56.0
* Sun Feb 09 2025 orignal <orignal@i2pmail.org> - 2.56.0
- update to 2.56.0
* Mon Dec 30 2024 orignal <orignal@i2pmail.org> - 2.55.0

View File

@@ -146,7 +146,7 @@ getent passwd i2pd >/dev/null || \
%changelog
* Tue Feb 11 2025 orignal <orignal@i2pmail.org> - 2.56.0
* Sun Feb 09 2025 orignal <orignal@i2pmail.org> - 2.56.0
- update to 2.56.0
* Mon Dec 30 2024 orignal <orignal@i2pmail.org> - 2.55.0

2
debian/changelog vendored
View File

@@ -2,7 +2,7 @@ i2pd (2.56.0-1) unstable; urgency=medium
* updated to version 2.56.0/0.9.65
-- orignal <orignal@i2pmail.org> Tue, 11 Feb 2025 16:00:00 +0000
-- orignal <orignal@i2pmail.org> Sun, 09 Feb 2025 16:00:00 +0000
i2pd (2.55.0-1) unstable; urgency=medium

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2025, The PurpleI2P Project
* Copyright (c) 2013-2024, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@@ -90,10 +90,10 @@ namespace data
}
bool Families::VerifyFamily (const std::string& family, const IdentHash& ident,
std::string_view signature, const char * key) const
const char * signature, const char * key) const
{
uint8_t buf[100], signatureBuf[64];
size_t len = family.length ();
size_t len = family.length (), signatureLen = strlen (signature);
if (len + 32 > 100)
{
LogPrint (eLogError, "Family: ", family, " is too long");
@@ -105,7 +105,7 @@ namespace data
memcpy (buf, family.c_str (), len);
memcpy (buf + len, (const uint8_t *)ident, 32);
len += 32;
auto signatureBufLen = Base64ToByteStream (signature.data (), signature.length (), signatureBuf, 64);
auto signatureBufLen = Base64ToByteStream (signature, signatureLen, signatureBuf, 64);
if (signatureBufLen)
{
EVP_MD_CTX * ctx = EVP_MD_CTX_create ();

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2025, The PurpleI2P Project
* Copyright (c) 2013-2024, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@@ -11,7 +11,6 @@
#include <map>
#include <string>
#include <string_view>
#include <memory>
#include <openssl/evp.h>
#include "Identity.h"
@@ -29,7 +28,7 @@ namespace data
~Families ();
void LoadCertificates ();
bool VerifyFamily (const std::string& family, const IdentHash& ident,
std::string_view signature, const char * key = nullptr) const;
const char * signature, const char * key = nullptr) const;
FamilyID GetFamilyID (const std::string& family) const;
private:

View File

@@ -11,7 +11,6 @@
#include "I2PEndian.h"
#include <fstream>
#include <memory>
#include <charconv>
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string.hpp> // for boost::to_lower
#ifndef __cpp_lib_atomic_shared_ptr
@@ -107,7 +106,8 @@ namespace data
// skip identity
size_t identityLen = m_RouterIdentity->GetFullLen ();
// read new RI
ReadFromBuffer (buf + identityLen, len - identityLen);
std::stringstream str (std::string ((char *)buf + identityLen, len - identityLen));
ReadFromStream (str);
if (!m_IsUnreachable)
UpdateBuffer (buf, len); // save buffer
// don't delete buffer until saved to the file
@@ -195,34 +195,39 @@ namespace data
}
}
// parse RI
if (!ReadFromBuffer (m_Buffer->data () + identityLen, bufferLen - identityLen))
std::stringstream str;
str.write ((const char *)m_Buffer->data () + identityLen, bufferLen - identityLen);
ReadFromStream (str);
if (!str)
{
LogPrint (eLogError, "RouterInfo: Malformed message");
m_IsUnreachable = true;
}
}
}
bool RouterInfo::ReadFromBuffer (const uint8_t * buf, size_t len)
void RouterInfo::ReadFromStream (std::istream& s)
{
if (len < 9) return false;
if (!s) return;
m_Caps = 0; m_Congestion = eLowCongestion;
m_Timestamp = bufbe64toh (buf);
size_t offset = 8; // timestamp
s.read ((char *)&m_Timestamp, sizeof (m_Timestamp));
m_Timestamp = be64toh (m_Timestamp);
// read addresses
auto addresses = NewAddresses ();
uint8_t numAddresses = buf[offset]; offset++;
uint8_t numAddresses;
s.read ((char *)&numAddresses, sizeof (numAddresses));
for (int i = 0; i < numAddresses; i++)
{
if (offset + 9 > len) return false; // 1 byte cost + 8 bytes date
uint8_t supportedTransports = 0;
auto address = NewAddress ();
offset++; // cost, ignore
address->date = bufbe64toh (buf + offset); offset += 8; // date
uint8_t cost; // ignore
s.read ((char *)&cost, sizeof (cost));
s.read ((char *)&address->date, sizeof (address->date));
bool isHost = false, isStaticKey = false, isV2 = false, isIntroKey = false;
auto transportStyle = ExtractString (buf + offset, len - offset); offset += transportStyle.length () + 1;
if (!transportStyle.compare (0, 4, "NTCP")) // NTCP or NTCP2
char transportStyle[6];
ReadString (transportStyle, 6, s);
if (!strncmp (transportStyle, "NTCP", 4)) // NTCP or NTCP2
address->transportStyle = eTransportNTCP2;
else if (!transportStyle.compare (0, 3, "SSU")) // SSU or SSU2
else if (!strncmp (transportStyle, "SSU", 3)) // SSU or SSU2
{
address->transportStyle = eTransportSSU2;
address->ssu.reset (new SSUExt ());
@@ -232,22 +237,24 @@ namespace data
address->transportStyle = eTransportUnknown;
address->caps = 0;
address->port = 0;
if (offset + 2 > len) return false;
uint16_t size = bufbe16toh (buf + offset); offset += 2; // size
if (offset + size >= len) return false;
uint16_t size, r = 0;
s.read ((char *)&size, sizeof (size)); if (!s) return;
size = be16toh (size);
if (address->transportStyle == eTransportUnknown)
{
// skip unknown address
offset += size;
continue;
s.seekg (size, std::ios_base::cur);
if (s) continue; else return;
}
size_t r = 0;
while (r < size)
{
auto [key, value, sz] = ExtractParam (buf + offset, len - offset);
r += sz; offset += sz;
if (key.empty ()) continue;
if (key == "host")
char key[255], value[255];
r += ReadString (key, 255, s);
s.seekg (1, std::ios_base::cur); r++; // =
r += ReadString (value, 255, s);
s.seekg (1, std::ios_base::cur); r++; // ;
if (!s) return;
if (!strcmp (key, "host"))
{
boost::system::error_code ecode;
address->host = boost::asio::ip::make_address (value, ecode);
@@ -261,7 +268,7 @@ namespace data
address->transportStyle = eTransportUnknown;
}
}
else if (key == "port")
else if (!strcmp (key, "port"))
{
try
{
@@ -272,7 +279,7 @@ namespace data
LogPrint (eLogWarning, "RouterInfo: 'port' exception ", ex.what ());
}
}
else if (key == "mtu")
else if (!strcmp (key, "mtu"))
{
if (address->ssu)
{
@@ -288,36 +295,36 @@ namespace data
else
LogPrint (eLogWarning, "RouterInfo: Unexpected field 'mtu' for NTCP2");
}
else if (key == "caps")
else if (!strcmp (key, "caps"))
address->caps = ExtractAddressCaps (value);
else if (key == "s") // ntcp2 or ssu2 static key
else if (!strcmp (key, "s")) // ntcp2 or ssu2 static key
{
if (Base64ToByteStream (value.data (), value.length (), address->s, 32) == 32 &&
if (Base64ToByteStream (value, strlen (value), address->s, 32) == 32 &&
!(address->s[31] & 0x80)) // check if x25519 public key
isStaticKey = true;
else
address->transportStyle = eTransportUnknown; // invalid address
}
else if (key == "i") // ntcp2 iv or ssu2 intro
else if (!strcmp (key, "i")) // ntcp2 iv or ssu2 intro
{
if (address->IsNTCP2 ())
{
if (Base64ToByteStream (value.data (), value.length (), address->i, 16) == 16)
if (Base64ToByteStream (value, strlen (value), address->i, 16) == 16)
address->published = true; // presence of "i" means "published" NTCP2
else
address->transportStyle = eTransportUnknown; // invalid address
}
else if (address->IsSSU2 ())
{
if (Base64ToByteStream (value.data (), value.length (), address->i, 32) == 32)
if (Base64ToByteStream (value, strlen (value), address->i, 32) == 32)
isIntroKey = true;
else
address->transportStyle = eTransportUnknown; // invalid address
}
}
else if (key == "v")
else if (!strcmp (key, "v"))
{
if (value == "2")
if (!strcmp (value, "2"))
isV2 = true;
else
{
@@ -333,11 +340,13 @@ namespace data
LogPrint (eLogError, "RouterInfo: Introducer is presented for non-SSU address. Skipped");
continue;
}
unsigned char index = key[key.length () - 1] - '0'; // TODO:
size_t l = strlen(key);
unsigned char index = key[l-1] - '0'; // TODO:
key[l-1] = 0;
if (index > 9)
{
LogPrint (eLogError, "RouterInfo: Unexpected introducer's index ", index, " skipped");
continue;
if (s) continue; else return;
}
if (index >= address->ssu->introducers.size ())
{
@@ -346,8 +355,7 @@ namespace data
address->ssu->introducers.resize (index + 1);
}
Introducer& introducer = address->ssu->introducers.at (index);
auto key1 = key.substr(0, key.length () - 1);
if (key1 == "itag")
if (!strcmp (key, "itag"))
{
try
{
@@ -358,9 +366,9 @@ namespace data
LogPrint (eLogWarning, "RouterInfo: 'itag' exception ", ex.what ());
}
}
else if (key1 == "ih")
Base64ToByteStream (value.data (), value.length (), introducer.iH, 32);
else if (key1 == "iexp")
else if (!strcmp (key, "ih"))
Base64ToByteStream (value, strlen (value), introducer.iH, 32);
else if (!strcmp (key, "iexp"))
{
try
{
@@ -372,7 +380,9 @@ namespace data
}
}
}
}
if (!s) return;
}
if (address->transportStyle == eTransportNTCP2)
{
if (isStaticKey)
@@ -436,41 +446,45 @@ namespace data
boost::atomic_store (&m_Addresses, addresses);
#endif
// read peers
if (offset + 1 > len) return false;
uint8_t numPeers = buf[offset]; offset++; // num peers
offset += numPeers*32; // TODO: read peers
uint8_t numPeers;
s.read ((char *)&numPeers, sizeof (numPeers)); if (!s) return;
s.seekg (numPeers*32, std::ios_base::cur); // TODO: read peers
// read properties
if (offset + 2 > len) return false;
m_Version = 0;
bool isNetId = false;
std::string family;
uint16_t size = bufbe16toh (buf + offset); offset += 2; // size
if (offset + size > len) return false;
size_t r = 0;
uint16_t size, r = 0;
s.read ((char *)&size, sizeof (size)); if (!s) return;
size = be16toh (size);
while (r < size)
{
auto [key, value, sz] = ExtractParam (buf + offset, len - offset);
r += sz; offset += sz;
if (key.empty ()) continue;
char key[255], value[255];
r += ReadString (key, 255, s);
s.seekg (1, std::ios_base::cur); r++; // =
r += ReadString (value, 255, s);
s.seekg (1, std::ios_base::cur); r++; // ;
if (!s) return;
SetProperty (key, value);
// extract caps
if (key == "caps")
if (!strcmp (key, "caps"))
{
ExtractCaps (value);
m_IsFloodfill = IsDeclaredFloodfill ();
}
// extract version
else if (key == ROUTER_INFO_PROPERTY_VERSION)
else if (!strcmp (key, ROUTER_INFO_PROPERTY_VERSION))
{
m_Version = 0;
for (auto ch: value)
char * ch = value;
while (*ch)
{
if (ch >= '0' && ch <= '9')
if (*ch >= '0' && *ch <= '9')
{
m_Version *= 10;
m_Version += (ch - '0');
m_Version += (*ch - '0');
}
ch++;
}
if (m_Version < NETDB_MIN_PEER_TEST_VERSION && (m_SupportedTransports & (eSSU2V4 | eSSU2V6)))
{
@@ -483,26 +497,24 @@ namespace data
}
}
// check netId
else if (key == ROUTER_INFO_PROPERTY_NETID)
else if (!strcmp (key, ROUTER_INFO_PROPERTY_NETID))
{
isNetId = true;
int netID;
auto res = std::from_chars(value.data(), value.data() + value.size(), netID);
if (res.ec != std::errc() || netID != i2p::context.GetNetID ())
if (atoi (value) != i2p::context.GetNetID ())
{
LogPrint (eLogError, "RouterInfo: Unexpected ", ROUTER_INFO_PROPERTY_NETID, "=", value);
m_IsUnreachable = true;
}
}
// family
else if (key == ROUTER_INFO_PROPERTY_FAMILY)
else if (!strcmp (key, ROUTER_INFO_PROPERTY_FAMILY))
{
family = value;
boost::to_lower (family);
}
else if (key == ROUTER_INFO_PROPERTY_FAMILY_SIG)
else if (!strcmp (key, ROUTER_INFO_PROPERTY_FAMILY_SIG))
{
if (netdb.GetFamilies ().VerifyFamily (family, GetIdentHash (), value)) // TODO
if (netdb.GetFamilies ().VerifyFamily (family, GetIdentHash (), value))
m_FamilyID = netdb.GetFamilies ().GetFamilyID (family);
else
{
@@ -510,24 +522,25 @@ namespace data
SetUnreachable (true);
}
}
if (!s) return;
}
if (!m_SupportedTransports || !isNetId || !m_Version)
SetUnreachable (true);
return true;
}
}
bool RouterInfo::IsFamily (FamilyID famid) const
{
return m_FamilyID == famid;
}
void RouterInfo::ExtractCaps (std::string_view value)
void RouterInfo::ExtractCaps (const char * value)
{
for (auto cap: value)
const char * cap = value;
while (*cap)
{
switch (cap)
switch (*cap)
{
case CAPS_FLAG_FLOODFILL:
m_Caps |= Caps::eFloodfill;
@@ -536,16 +549,16 @@ namespace data
case CAPS_FLAG_LOW_BANDWIDTH2:
case CAPS_FLAG_LOW_BANDWIDTH3:
case CAPS_FLAG_LOW_BANDWIDTH4:
m_BandwidthCap = cap;
m_BandwidthCap = *cap;
break;
case CAPS_FLAG_HIGH_BANDWIDTH:
m_Caps |= Caps::eHighBandwidth;
m_BandwidthCap = cap;
m_BandwidthCap = *cap;
break;
case CAPS_FLAG_EXTRA_BANDWIDTH1:
case CAPS_FLAG_EXTRA_BANDWIDTH2:
m_Caps |= Caps::eExtraBandwidth | Caps::eHighBandwidth;
m_BandwidthCap = cap;
m_BandwidthCap = *cap;
break;
case CAPS_FLAG_HIDDEN:
m_Caps |= Caps::eHidden;
@@ -567,15 +580,17 @@ namespace data
break;
default: ;
}
cap++;
}
}
uint8_t RouterInfo::ExtractAddressCaps (std::string_view value) const
}
uint8_t RouterInfo::ExtractAddressCaps (const char * value) const
{
uint8_t caps = 0;
for (auto cap: value)
const char * cap = value;
while (*cap)
{
switch (cap)
switch (*cap)
{
case CAPS_FLAG_V4:
caps |= AddressCaps::eV4;
@@ -591,10 +606,11 @@ namespace data
break;
default: ;
}
cap++;
}
return caps;
}
}
void RouterInfo::UpdateIntroducers (std::shared_ptr<Address> address, uint64_t ts)
{
if (!address || !address->ssu) return;
@@ -654,41 +670,25 @@ namespace data
return SaveToFile (fullPath, m_Buffer);
}
std::string_view RouterInfo::ExtractString (const uint8_t * buf, size_t len) const
size_t RouterInfo::ReadString (char * str, size_t len, std::istream& s) const
{
uint8_t l = buf[0];
if (l > len)
uint8_t l;
s.read ((char *)&l, 1);
if (l < len)
{
s.read (str, l);
if (!s) l = 0; // failed, return empty string
str[l] = 0;
}
else
{
LogPrint (eLogWarning, "RouterInfo: String length ", (int)l, " exceeds buffer size ", len);
l = len;
}
return { (const char *)(buf + 1), l };
s.seekg (l, std::ios::cur); // skip
str[0] = 0;
}
return l+1;
}
std::tuple<std::string_view, std::string_view, size_t> RouterInfo::ExtractParam (const uint8_t * buf, size_t len) const
{
auto key = ExtractString (buf, len);
size_t offset = key.length () + 1;
if (offset >= len) return { std::string_view(), std::string_view(), len };
if (buf[offset] != '=')
{
LogPrint (eLogWarning, "RouterInfo: Unexpected character ", buf[offset], " instead '=' after ", key);
key = std::string_view();
}
offset++;
if (offset >= len) return { key, std::string_view(), len };
auto value = ExtractString (buf + offset, len - offset);
offset += value.length () + 1;
if (offset >= len) return { key, std::string_view(), len };
if (buf[offset] != ';')
{
LogPrint (eLogWarning, "RouterInfo: Unexpected character ", buf[offset], " instead ';' after ", value);
value = std::string_view();
}
offset++;
return { key, value, offset };
}
void RouterInfo::AddNTCP2Address (const uint8_t * staticKey, const uint8_t * iv,int port, uint8_t caps)
{
auto addr = std::make_shared<Address>();
@@ -1485,11 +1485,9 @@ namespace data
s.write (properties.str ().c_str (), properties.str ().size ());
}
void LocalRouterInfo::SetProperty (std::string_view key, std::string_view value)
void LocalRouterInfo::SetProperty (const std::string& key, const std::string& value)
{
auto [it, inserted] = m_Properties.emplace (key, value);
if (!inserted)
it->second = value;
m_Properties[key] = value;
}
void LocalRouterInfo::DeleteProperty (const std::string& key)

View File

@@ -11,8 +11,6 @@
#include <inttypes.h>
#include <string>
#include <string_view>
#include <tuple>
#include <map>
#include <vector>
#include <array>
@@ -221,7 +219,7 @@ namespace data
std::string GetIdentHashBase64 () const { return GetIdentHash ().ToBase64 (); };
uint64_t GetTimestamp () const { return m_Timestamp; };
int GetVersion () const { return m_Version; };
virtual void SetProperty (std::string_view key, std::string_view value) {};
virtual void SetProperty (const std::string& key, const std::string& value) {};
virtual void ClearProperties () {};
AddressesPtr GetAddresses () const; // should be called for local RI only, otherwise must return shared_ptr
std::shared_ptr<const Address> GetNTCP2V4Address () const;
@@ -335,12 +333,11 @@ namespace data
bool LoadFile (const std::string& fullPath);
void ReadFromFile (const std::string& fullPath);
bool ReadFromBuffer (const uint8_t * buf, size_t len); // return false if malformed
void ReadFromStream (std::istream& s);
void ReadFromBuffer (bool verifySignature);
std::string_view ExtractString (const uint8_t * buf, size_t len) const;
std::tuple<std::string_view, std::string_view, size_t> ExtractParam (const uint8_t * buf, size_t len) const;
void ExtractCaps (std::string_view value);
uint8_t ExtractAddressCaps (std::string_view value) const;
size_t ReadString (char* str, size_t len, std::istream& s) const;
void ExtractCaps (const char * value);
uint8_t ExtractAddressCaps (const char * value) const;
void UpdateIntroducers (std::shared_ptr<Address> address, uint64_t ts);
template<typename Filter>
std::shared_ptr<const Address> GetAddress (Filter filter) const;
@@ -382,7 +379,7 @@ namespace data
void UpdateCaps (uint8_t caps);
bool UpdateCongestion (Congestion c); // returns true if updated
void SetProperty (std::string_view key, std::string_view value) override;
void SetProperty (const std::string& key, const std::string& value) override;
void DeleteProperty (const std::string& key);
std::string GetProperty (const std::string& key) const;
void ClearProperties () override { m_Properties.clear (); };

View File

@@ -1251,21 +1251,18 @@ namespace transport
}
uint64_t token;
RAND_bytes ((uint8_t *)&token, 8);
if (!token) token = 1; // token can't be zero
m_IncomingTokens.try_emplace (ep, token, uint32_t(ts + SSU2_TOKEN_EXPIRATION_TIMEOUT));
m_IncomingTokens.emplace (ep, std::make_pair (token, uint32_t(ts + SSU2_TOKEN_EXPIRATION_TIMEOUT)));
return token;
}
std::pair<uint64_t, uint32_t> SSU2Server::NewIncomingToken (const boost::asio::ip::udp::endpoint& ep)
{
m_IncomingTokens.erase (ep); // drop previous
uint64_t token;
RAND_bytes ((uint8_t *)&token, 8);
if (!token) token = 1; // token can't be zero
uint32_t expires = i2p::util::GetSecondsSinceEpoch () + SSU2_NEXT_TOKEN_EXPIRATION_TIMEOUT;
auto [it, inserted] = m_IncomingTokens.try_emplace (ep, token, expires);
if (!inserted)
it->second = { token, expires }; // override
return it->second;
auto ret = std::make_pair (token, uint32_t(i2p::util::GetSecondsSinceEpoch () + SSU2_NEXT_TOKEN_EXPIRATION_TIMEOUT));
m_IncomingTokens.emplace (ep, ret);
return ret;
}
std::vector<std::shared_ptr<SSU2Session> > SSU2Server::FindIntroducers (int maxNumIntroducers,

View File

@@ -191,7 +191,12 @@ namespace transport
void SSU2PeerTestSession::SendPeerTest (uint8_t msg, const uint8_t * signedData, size_t signedDataLen, bool delayed)
{
#if __cplusplus >= 202002L // C++20
m_SignedData.assign (signedData, signedData + signedDataLen);
#else
m_SignedData.resize (signedDataLen);
memcpy (m_SignedData.data (), signedData, signedDataLen);
#endif
if (!delayed)
SendPeerTest (msg);
// schedule resend for msgs 5 or 6
@@ -252,7 +257,7 @@ namespace transport
{
// we are Charlie
uint64_t destConnID = htobe64 (((uint64_t)nonce << 32) | nonce); // dest id
uint64_t sourceConnID = ~destConnID;
uint32_t sourceConnID = ~destConnID;
SetSourceConnID (sourceConnID);
SetDestConnID (destConnID);
SetState (eSSU2SessionStateHolePunch);
@@ -308,7 +313,12 @@ namespace transport
void SSU2HolePunchSession::SendHolePunch (const uint8_t * relayResponseBlock, size_t relayResponseBlockLen)
{
#if __cplusplus >= 202002L // C++20
m_RelayResponseBlock.assign (relayResponseBlock, relayResponseBlock + relayResponseBlockLen);
#else
m_RelayResponseBlock.resize (relayResponseBlockLen);
memcpy (m_RelayResponseBlock.data (), relayResponseBlock, relayResponseBlockLen);
#endif
SendHolePunch ();
ScheduleResend ();
}

View File

@@ -189,7 +189,7 @@ namespace transport
if (!asz) return false;
payload[17] = asz;
packet->payloadSize = asz + 18;
SignedData<128> s;
SignedData s;
s.Insert ((const uint8_t *)"RelayRequestData", 16); // prologue
s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash
s.Insert (session->GetRemoteIdentity ()->GetIdentHash (), 32); // chash
@@ -1965,7 +1965,6 @@ namespace transport
void SSU2Session::HandleRelayRequest (const uint8_t * buf, size_t len)
{
// we are Bob
if (len < 9) return;
auto mts = i2p::util::GetMillisecondsSinceEpoch ();
uint32_t nonce = bufbe32toh (buf + 1); // nonce
uint32_t relayTag = bufbe32toh (buf + 5); // relay tag
@@ -1999,7 +1998,7 @@ namespace transport
packet->payloadSize = r ? CreateRouterInfoBlock (packet->payload, m_MaxPayloadSize - len - 32, r) : 0;
if (!packet->payloadSize && r)
session->SendFragmentedMessage (CreateDatabaseStoreMsg (r));
packet->payloadSize += CreateRelayIntroBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize, buf + 1, len - 1);
packet->payloadSize += CreateRelayIntroBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize, buf + 1, len -1);
if (packet->payloadSize < m_MaxPayloadSize)
packet->payloadSize += CreatePaddingBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize);
uint32_t packetNum = session->SendData (packet->payload, packet->payloadSize);
@@ -2014,24 +2013,18 @@ namespace transport
void SSU2Session::HandleRelayIntro (const uint8_t * buf, size_t len, int attempts)
{
// we are Charlie
if (len < 47) return;
SSU2RelayResponseCode code = eSSU2RelayResponseCodeAccept;
boost::asio::ip::udp::endpoint ep;
std::shared_ptr<const i2p::data::RouterInfo::Address> addr;
auto r = i2p::data::netdb.FindRouter (buf + 1); // Alice
if (r)
{
SignedData<128> s;
SignedData s;
s.Insert ((const uint8_t *)"RelayRequestData", 16); // prologue
s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash
s.Insert (i2p::context.GetIdentHash (), 32); // chash
s.Insert (buf + 33, 14); // nonce, relay tag, timestamp, ver, asz
uint8_t asz = buf[46];
if (asz + 47 + r->GetIdentity ()->GetSignatureLen () > len)
{
LogPrint (eLogWarning, "SSU2: Malformed RelayIntro len=", len);
return;
}
s.Insert (buf + 47, asz); // Alice Port, Alice IP
if (s.Verify (r->GetIdentity (), buf + 47 + asz))
{
@@ -2120,7 +2113,6 @@ namespace transport
void SSU2Session::HandleRelayResponse (const uint8_t * buf, size_t len)
{
if (len < 6) return;
uint32_t nonce = bufbe32toh (buf + 2);
if (m_State == eSSU2SessionStateIntroduced)
{
@@ -2141,9 +2133,7 @@ namespace transport
auto it = m_RelaySessions.find (nonce);
if (it != m_RelaySessions.end ())
{
auto relaySession = it->second.first;
m_RelaySessions.erase (it);
if (relaySession && relaySession->IsEstablished ())
if (it->second.first && it->second.first->IsEstablished ())
{
// we are Bob, message from Charlie
auto packet = m_Server.GetSentPacketsPool ().AcquireShared ();
@@ -2153,12 +2143,12 @@ namespace transport
memcpy (payload + 3, buf, len); // forward to Alice as is
packet->payloadSize = len + 3;
packet->payloadSize += CreatePaddingBlock (payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize);
uint32_t packetNum = relaySession->SendData (packet->payload, packet->payloadSize);
uint32_t packetNum = it->second.first->SendData (packet->payload, packet->payloadSize);
if (m_RemoteVersion >= SSU2_MIN_RELAY_RESPONSE_RESEND_VERSION)
{
// sometimes Alice doesn't ack this RelayResponse in older versions
packet->sendTime = i2p::util::GetMillisecondsSinceEpoch ();
relaySession->m_SentPackets.emplace (packetNum, packet);
it->second.first->m_SentPackets.emplace (packetNum, packet);
}
}
else
@@ -2167,31 +2157,25 @@ namespace transport
if (!buf[1]) // status code accepted?
{
// verify signature
uint8_t csz = (len >= 12) ? buf[11] : 0;
if (csz + 12 + relaySession->GetRemoteIdentity ()->GetSignatureLen () > len)
{
LogPrint (eLogWarning, "SSU2: Malformed RelayResponse len=", len);
relaySession->Done ();
return;
}
SignedData<128> s;
uint8_t csz = buf[11];
SignedData s;
s.Insert ((const uint8_t *)"RelayAgreementOK", 16); // prologue
s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash
s.Insert (buf + 2, 10 + csz); // nonce, timestamp, ver, csz and Charlie's endpoint
if (s.Verify (relaySession->GetRemoteIdentity (), buf + 12 + csz))
if (s.Verify (it->second.first->GetRemoteIdentity (), buf + 12 + csz))
{
if (relaySession->m_State == eSSU2SessionStateIntroduced) // HolePunch not received yet
if (it->second.first->m_State == eSSU2SessionStateIntroduced) // HolePunch not received yet
{
// update Charlie's endpoint
if (ExtractEndpoint (buf + 12, csz, relaySession->m_RemoteEndpoint))
if (ExtractEndpoint (buf + 12, csz, it->second.first->m_RemoteEndpoint))
{
// update token
uint64_t token;
memcpy (&token, buf + len - 8, 8);
m_Server.UpdateOutgoingToken (relaySession->m_RemoteEndpoint,
m_Server.UpdateOutgoingToken (it->second.first->m_RemoteEndpoint,
token, i2p::util::GetSecondsSinceEpoch () + SSU2_TOKEN_EXPIRATION_TIMEOUT);
// connect to Charlie, HolePunch will be ignored
relaySession->ConnectAfterIntroduction ();
it->second.first->ConnectAfterIntroduction ();
}
else
LogPrint (eLogWarning, "SSU2: RelayResponse can't extract endpoint");
@@ -2200,15 +2184,16 @@ namespace transport
else
{
LogPrint (eLogWarning, "SSU2: RelayResponse signature verification failed");
relaySession->Done ();
it->second.first->Done ();
}
}
else
{
LogPrint (eLogInfo, "SSU2: RelayResponse status code=", (int)buf[1], " nonce=", bufbe32toh (buf + 2));
relaySession->Done ();
it->second.first->Done ();
}
}
m_RelaySessions.erase (it);
}
else
LogPrint (eLogDebug, "SSU2: RelayResponse unknown nonce ", bufbe32toh (buf + 2));
@@ -2280,7 +2265,7 @@ namespace transport
uint8_t asz = buf[offset + 9];
std::vector<uint8_t> newSignedData (asz + 10 + i2p::context.GetIdentity ()->GetSignatureLen ());
memcpy (newSignedData.data (), buf + offset, asz + 10);
SignedData<128> s;
SignedData s;
s.Insert ((const uint8_t *)"PeerTestValidate", 16); // prologue
s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash
s.Insert (buf + 3, 32); // ahash
@@ -2391,7 +2376,7 @@ namespace transport
if (r)
{
uint8_t asz = buf[offset + 9];
SignedData<128> s;
SignedData s;
s.Insert ((const uint8_t *)"PeerTestValidate", 16); // prologue
s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash
s.Insert (i2p::context.GetIdentity ()->GetIdentHash (), 32); // ahash
@@ -2777,7 +2762,7 @@ namespace transport
size_t SSU2Session::CreatePaddingBlock (uint8_t * buf, size_t len, size_t minSize)
{
if (len < 3 || len < minSize) return 0;
size_t paddingSize = m_Server.GetRng ()() & 0x1F; // 0 - 31
size_t paddingSize = m_Server.GetRng ()() & 0x0F; // 0 - 15
if (paddingSize + 3 > len) paddingSize = len - 3;
else if (paddingSize + 3 < minSize) paddingSize = minSize - 3;
buf[0] = eSSU2BlkPadding;
@@ -2879,7 +2864,7 @@ namespace transport
LogPrint (eLogError, "SSU2: Buffer for RelayResponse signature is too small ", len);
return 0;
}
SignedData<128> s;
SignedData s;
s.Insert ((const uint8_t *)"RelayAgreementOK", 16); // prologue
if (code == eSSU2RelayResponseCodeAccept || code >= 64) // Charlie
s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash
@@ -2941,7 +2926,7 @@ namespace transport
size_t asz = CreateEndpoint (signedData + 10, 86, boost::asio::ip::udp::endpoint (localAddress->host, localAddress->port));
signedData[9] = asz;
// signature
SignedData<128> s;
SignedData s;
s.Insert ((const uint8_t *)"PeerTestValidate", 16); // prologue
s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash
s.Insert (signedData, 10 + asz); // ver, nonce, ts, asz, Alice's endpoint

View File

@@ -1297,7 +1297,7 @@ namespace stream
m_NumPacketsToSend = 1; m_PacingTimeRem = 0;
}
m_IsSendTime = true;
if (m_WindowIncCounter && (m_WindowSize < MAX_WINDOW_SIZE || m_WindowDropTargetSize) && !m_SendBuffer.IsEmpty () && m_PacingTime > m_MinPacingTime && m_RTT <= m_SlowRTT)
if (m_WindowIncCounter && (m_WindowSize < MAX_WINDOW_SIZE || m_WindowDropTargetSize) && !m_SendBuffer.IsEmpty () && m_PacingTime > m_MinPacingTime)
{
for (int i = 0; i < m_NumPacketsToSend; i++)
{
@@ -1307,7 +1307,7 @@ namespace stream
{
if (m_LastWindowDropSize && (m_LastWindowDropSize >= m_WindowDropTargetSize))
m_WindowDropTargetSize += 1 - (1 / ((m_LastWindowDropSize + PREV_SPEED_KEEP_TIME_COEFF) / m_WindowDropTargetSize)); // some magic here
else if (m_LastWindowDropSize && (m_LastWindowDropSize < m_WindowDropTargetSize))
else if (m_LastWindowDropSize && (m_LastWindowDropSize < m_WindowSize))
m_WindowDropTargetSize += (m_WindowDropTargetSize - (m_LastWindowDropSize - PREV_SPEED_KEEP_TIME_COEFF)) / m_WindowDropTargetSize; // some magic here
else
m_WindowDropTargetSize += (m_WindowDropTargetSize - (1 - PREV_SPEED_KEEP_TIME_COEFF)) / m_WindowDropTargetSize;
@@ -1646,22 +1646,14 @@ namespace stream
void Stream::ProcessWindowDrop ()
{
if (m_WindowDropTargetSize)
m_WindowDropTargetSize = (m_WindowDropTargetSize / 2) * 0.75; // congestion window size and -25% to drain queue
else
if (m_WindowSize > m_LastWindowDropSize)
{
if (m_WindowSize < m_LastWindowDropSize)
{
m_LastWindowDropSize = m_WindowSize - (m_LastWindowDropSize - m_WindowSize);
if (m_LastWindowDropSize < MIN_WINDOW_SIZE) m_LastWindowDropSize = MIN_WINDOW_SIZE;
}
else
{
m_LastWindowDropSize = (m_LastWindowDropSize + m_WindowSize + m_WindowSizeTail) / 2;
if (m_LastWindowDropSize > MAX_WINDOW_SIZE) m_LastWindowDropSize = MAX_WINDOW_SIZE;
}
m_WindowDropTargetSize = m_LastWindowDropSize * 0.75; // -25% to drain queue
m_LastWindowDropSize = (m_LastWindowDropSize + m_WindowSize + m_WindowSizeTail) / 2;
if (m_LastWindowDropSize > MAX_WINDOW_SIZE) m_LastWindowDropSize = MAX_WINDOW_SIZE;
}
else
m_LastWindowDropSize = m_WindowSize;
m_WindowDropTargetSize = m_LastWindowDropSize - (m_LastWindowDropSize / 4); // -25%;
if (m_WindowDropTargetSize < MIN_WINDOW_SIZE)
m_WindowDropTargetSize = MIN_WINDOW_SIZE;
m_WindowIncCounter = 0; // disable window growth

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2025, The PurpleI2P Project
* Copyright (c) 2013-2024, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@@ -10,7 +10,7 @@
#define TRANSPORT_SESSION_H__
#include <inttypes.h>
#include <string.h>
#include <iostream>
#include <memory>
#include <vector>
#include <mutex>
@@ -28,51 +28,45 @@ namespace transport
const size_t IPV6_HEADER_SIZE = 40;
const size_t UDP_HEADER_SIZE = 8;
template<size_t sz>
class SignedData
{
public:
SignedData (): m_Size(0) {}
SignedData () {}
SignedData (const SignedData& other)
{
m_Size = other.m_Size;
memcpy (m_Buf, other.m_Buf, m_Size);
m_Stream << other.m_Stream.rdbuf ();
}
void Reset ()
{
m_Size = 0;
m_Stream.str("");
}
size_t Insert (const uint8_t * buf, size_t len)
void Insert (const uint8_t * buf, size_t len)
{
if (m_Size + len > sz) len = sz - m_Size;
memcpy (m_Buf + m_Size, buf, len);
m_Size += len;
return len;
m_Stream.write ((char *)buf, len);
}
template<typename T>
void Insert (T t)
{
Insert ((const uint8_t *)&t, sizeof (T));
m_Stream.write ((char *)&t, sizeof (T));
}
bool Verify (std::shared_ptr<const i2p::data::IdentityEx> ident, const uint8_t * signature) const
{
return ident->Verify (m_Buf, m_Size, signature);
return ident->Verify ((const uint8_t *)m_Stream.str ().c_str (), m_Stream.str ().size (), signature);
}
void Sign (const i2p::data::PrivateKeys& keys, uint8_t * signature) const
{
keys.Sign (m_Buf, m_Size, signature);
keys.Sign ((const uint8_t *)m_Stream.str ().c_str (), m_Stream.str ().size (), signature);
}
private:
uint8_t m_Buf[sz];
size_t m_Size;
std::stringstream m_Stream;
};
const int64_t TRANSPORT_SESSION_SLOWNESS_THRESHOLD = 500; // in milliseconds

View File

@@ -315,28 +315,22 @@ namespace tunnel
void OutboundTunnel::SendTunnelDataMsgTo (const uint8_t * gwHash, uint32_t gwTunnel, std::shared_ptr<i2p::I2NPMessage> msg)
{
TunnelMessageBlock block;
block.tunnelID = 0; // Initialize tunnelID to a default value
if (gwHash)
{
block.hash = gwHash;
if (gwTunnel)
{
block.deliveryType = eDeliveryTypeTunnel;
block.tunnelID = gwTunnel; // Set tunnelID only if gwTunnel is non-zero
block.tunnelID = gwTunnel;
}
else
{
block.deliveryType = eDeliveryTypeRouter;
}
}
else
{
block.deliveryType = eDeliveryTypeLocal;
}
block.data = msg;
SendTunnelDataMsgs({block});
SendTunnelDataMsgs ({block});
}
void OutboundTunnel::SendTunnelDataMsgs (const std::vector<TunnelMessageBlock>& msgs)

View File

@@ -351,13 +351,10 @@ namespace tunnel
{
it.second.first->SetState (eTunnelStateFailed);
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
if (m_OutboundTunnels.size () > 1) // don't fail last tunnel
if (m_OutboundTunnels.size () > 1 || m_NumOutboundTunnels <= 1) // don't fail last tunnel
m_OutboundTunnels.erase (it.second.first);
else
{
it.second.first->SetState (eTunnelStateTestFailed);
CreateOutboundTunnel (); // create new tunnel immediately because last one failed
}
}
else if (it.second.first->GetState () != eTunnelStateExpiring)
it.second.first->SetState (eTunnelStateTestFailed);
@@ -371,16 +368,13 @@ namespace tunnel
bool failed = false;
{
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
if (m_InboundTunnels.size () > 1) // don't fail last tunnel
if (m_InboundTunnels.size () > 1 || m_NumInboundTunnels <= 1) // don't fail last tunnel
{
m_InboundTunnels.erase (it.second.second);
failed = true;
}
else
{
it.second.second->SetState (eTunnelStateTestFailed);
CreateInboundTunnel (); // create new tunnel immediately because last one failed
}
}
if (failed && m_LocalDestination)
m_LocalDestination->SetLeaseSetUpdated (true);

View File

@@ -297,7 +297,7 @@ namespace client
}
else
{
LogPrint (eLogInfo, "Clients: Can't open file ", fullPath, " Creating new one with signature type ", sigType, " crypto type ", cryptoType);
LogPrint (eLogCritical, "Clients: Can't open file ", fullPath, " Creating new one with signature type ", sigType, " crypto type ", cryptoType);
keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType, cryptoType, true);
std::ofstream f (fullPath, std::ofstream::binary | std::ofstream::out);
size_t len = keys.GetFullLen ();
@@ -871,7 +871,7 @@ namespace client
}
else
LogPrint (eLogError, "Clients: Unknown section type = ", type, " of ", name, " in ", tunConf);
LogPrint (eLogWarning, "Clients: Unknown section type = ", type, " of ", name, " in ", tunConf);
}
catch (std::exception& ex)
{

View File

@@ -29,15 +29,13 @@ namespace client
const std::map<std::string, std::string>& params):
LeaseSetDestination (service, isPublic, &params),
m_Owner (owner), m_Identity (identity), m_EncryptionKeyType (m_Identity->GetCryptoKeyType ()),
m_IsCreatingLeaseSet (false), m_IsSameThread (isSameThread),
m_LeaseSetCreationTimer (service), m_ReadinessCheckTimer (service)
m_IsCreatingLeaseSet (false), m_IsSameThread (isSameThread), m_LeaseSetCreationTimer (service)
{
}
void I2CPDestination::Stop ()
{
m_LeaseSetCreationTimer.cancel ();
m_ReadinessCheckTimer.cancel ();
LeaseSetDestination::Stop ();
m_Owner = nullptr;
}
@@ -90,7 +88,7 @@ namespace client
void I2CPDestination::CreateNewLeaseSet (const std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> >& tunnels)
{
boost::asio::post (GetService (), std::bind (&I2CPDestination::PostCreateNewLeaseSet, GetSharedFromThis (), tunnels));
boost::asio::post (GetService (), std::bind (&I2CPDestination::PostCreateNewLeaseSet, this, tunnels));
}
void I2CPDestination::PostCreateNewLeaseSet (std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels)
@@ -100,20 +98,6 @@ namespace client
LogPrint (eLogInfo, "I2CP: LeaseSet is being created");
return;
}
m_ReadinessCheckTimer.cancel ();
auto pool = GetTunnelPool ();
if (!pool || pool->GetOutboundTunnels ().empty ())
{
// try again later
m_ReadinessCheckTimer.expires_from_now (boost::posix_time::seconds(I2CP_DESTINATION_READINESS_CHECK_INTERVAL));
m_ReadinessCheckTimer.async_wait(
[s=GetSharedFromThis (), tunnels=std::move(tunnels)](const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
s->PostCreateNewLeaseSet (tunnels);
});
return;
}
uint8_t priv[256] = {0};
i2p::data::LocalLeaseSet ls (m_Identity, priv, tunnels); // we don't care about encryption key, we need leases only
m_LeaseSetExpirationTime = ls.GetExpirationTime ();
@@ -571,7 +555,7 @@ namespace client
m_IsSending = false;
}
std::string_view I2CPSession::ExtractString (const uint8_t * buf, size_t len) const
std::string_view I2CPSession::ExtractString (const uint8_t * buf, size_t len)
{
uint8_t l = buf[0];
if (l > len) l = len;
@@ -588,7 +572,7 @@ namespace client
return l + 1;
}
void I2CPSession::ExtractMapping (const uint8_t * buf, size_t len, std::map<std::string, std::string>& mapping) const
void I2CPSession::ExtractMapping (const uint8_t * buf, size_t len, std::map<std::string, std::string>& mapping)
// TODO: move to Base.cpp
{
size_t offset = 0;

View File

@@ -31,7 +31,6 @@ 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_DESTINATION_READINESS_CHECK_INTERVAL = 5; // in seconds
const int I2CP_SESSION_ACK_REQUEST_INTERVAL = 12100; // in milliseconds
const size_t I2CP_HEADER_LENGTH_OFFSET = 0;
@@ -132,7 +131,7 @@ namespace client
uint8_t m_ECIESx25519PrivateKey[32];
uint64_t m_LeaseSetExpirationTime;
bool m_IsCreatingLeaseSet, m_IsSameThread;
boost::asio::deadline_timer m_LeaseSetCreationTimer, m_ReadinessCheckTimer;
boost::asio::deadline_timer m_LeaseSetCreationTimer;
i2p::util::MemoryPoolMt<I2NPMessageBuffer<I2NP_MAX_MESSAGE_SIZE> > m_I2NPMsgsPool;
};
@@ -194,9 +193,9 @@ namespace client
void HandleI2CPMessageSent (const boost::system::error_code& ecode, std::size_t bytes_transferred);
std::string_view ExtractString (const uint8_t * buf, size_t len) const;
std::string_view ExtractString (const uint8_t * buf, size_t len);
size_t PutString (uint8_t * buf, size_t len, std::string_view str);
void ExtractMapping (const uint8_t * buf, size_t len, std::map<std::string, std::string>& mapping) const;
void ExtractMapping (const uint8_t * buf, size_t len, std::map<std::string, std::string>& mapping);
void SendSessionStatusMessage (I2CPSessionStatus status);
void SendHostReplyMessage (uint32_t requestID, std::shared_ptr<const i2p::data::IdentityEx> identity);