parse RouterInfo from buffer

This commit is contained in:
orignal 2025-02-18 21:11:59 -05:00
parent 251605e2b8
commit ef72ba3f34
2 changed files with 117 additions and 101 deletions

View file

@ -11,6 +11,7 @@
#include "I2PEndian.h" #include "I2PEndian.h"
#include <fstream> #include <fstream>
#include <memory> #include <memory>
#include <charconv>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include <boost/algorithm/string.hpp> // for boost::to_lower #include <boost/algorithm/string.hpp> // for boost::to_lower
#ifndef __cpp_lib_atomic_shared_ptr #ifndef __cpp_lib_atomic_shared_ptr
@ -106,8 +107,7 @@ namespace data
// skip identity // skip identity
size_t identityLen = m_RouterIdentity->GetFullLen (); size_t identityLen = m_RouterIdentity->GetFullLen ();
// read new RI // read new RI
std::stringstream str (std::string ((char *)buf + identityLen, len - identityLen)); ReadFromBuffer (buf + identityLen, len - identityLen);
ReadFromStream (str);
if (!m_IsUnreachable) if (!m_IsUnreachable)
UpdateBuffer (buf, len); // save buffer UpdateBuffer (buf, len); // save buffer
// don't delete buffer until saved to the file // don't delete buffer until saved to the file
@ -195,39 +195,33 @@ namespace data
} }
} }
// parse RI // parse RI
std::stringstream str; if (!ReadFromBuffer (m_Buffer->data () + identityLen, bufferLen - identityLen))
str.write ((const char *)m_Buffer->data () + identityLen, bufferLen - identityLen);
ReadFromStream (str);
if (!str)
{ {
LogPrint (eLogError, "RouterInfo: Malformed message"); LogPrint (eLogError, "RouterInfo: Malformed message");
m_IsUnreachable = true; m_IsUnreachable = true;
} }
} }
void RouterInfo::ReadFromStream (std::istream& s) bool RouterInfo::ReadFromBuffer (const uint8_t * buf, size_t len)
{ {
if (!s) return; if (len < 9) return false;
m_Caps = 0; m_Congestion = eLowCongestion; m_Caps = 0; m_Congestion = eLowCongestion;
s.read ((char *)&m_Timestamp, sizeof (m_Timestamp)); m_Timestamp = bufbe64toh (buf);
m_Timestamp = be64toh (m_Timestamp); size_t offset = 8; // timestamp
// read addresses // read addresses
auto addresses = NewAddresses (); auto addresses = NewAddresses ();
uint8_t numAddresses; uint8_t numAddresses = buf[offset]; offset++;
s.read ((char *)&numAddresses, sizeof (numAddresses));
for (int i = 0; i < numAddresses; i++) for (int i = 0; i < numAddresses; i++)
{ {
uint8_t supportedTransports = 0; uint8_t supportedTransports = 0;
auto address = NewAddress (); auto address = NewAddress ();
uint8_t cost; // ignore offset++; // cost, ignore
s.read ((char *)&cost, sizeof (cost)); address->date = bufbe64toh (buf + offset); offset += 8; // date
s.read ((char *)&address->date, sizeof (address->date));
bool isHost = false, isStaticKey = false, isV2 = false, isIntroKey = false; bool isHost = false, isStaticKey = false, isV2 = false, isIntroKey = false;
char transportStyle[6]; auto transportStyle = ExtractString (buf + offset, len - offset); offset += transportStyle.length () + 1;
ReadString (transportStyle, 6, s); if (!transportStyle.compare (0, 4, "NTCP")) // NTCP or NTCP2
if (!strncmp (transportStyle, "NTCP", 4)) // NTCP or NTCP2
address->transportStyle = eTransportNTCP2; address->transportStyle = eTransportNTCP2;
else if (!strncmp (transportStyle, "SSU", 3)) // SSU or SSU2 else if (!transportStyle.compare (0, 3, "SSU")) // SSU or SSU2
{ {
address->transportStyle = eTransportSSU2; address->transportStyle = eTransportSSU2;
address->ssu.reset (new SSUExt ()); address->ssu.reset (new SSUExt ());
@ -237,24 +231,21 @@ namespace data
address->transportStyle = eTransportUnknown; address->transportStyle = eTransportUnknown;
address->caps = 0; address->caps = 0;
address->port = 0; address->port = 0;
uint16_t size, r = 0; uint16_t size = bufbe16toh (buf + offset); offset += 2; // size
s.read ((char *)&size, sizeof (size)); if (!s) return; if (offset + size >= len) return false;
size = be16toh (size);
if (address->transportStyle == eTransportUnknown) if (address->transportStyle == eTransportUnknown)
{ {
// skip unknown address // skip unknown address
s.seekg (size, std::ios_base::cur); offset += size;
if (s) continue; else return; continue;
} }
size_t r = 0;
while (r < size) while (r < size)
{ {
char key[255], value[255]; auto [key, value, sz] = ExtractParam (buf + offset, len - offset);
r += ReadString (key, 255, s); r += sz; offset += sz;
s.seekg (1, std::ios_base::cur); r++; // = if (key.empty ()) continue;
r += ReadString (value, 255, s); if (key == "host")
s.seekg (1, std::ios_base::cur); r++; // ;
if (!s) return;
if (!strcmp (key, "host"))
{ {
boost::system::error_code ecode; boost::system::error_code ecode;
address->host = boost::asio::ip::make_address (value, ecode); address->host = boost::asio::ip::make_address (value, ecode);
@ -268,7 +259,7 @@ namespace data
address->transportStyle = eTransportUnknown; address->transportStyle = eTransportUnknown;
} }
} }
else if (!strcmp (key, "port")) else if (key == "port")
{ {
try try
{ {
@ -279,7 +270,7 @@ namespace data
LogPrint (eLogWarning, "RouterInfo: 'port' exception ", ex.what ()); LogPrint (eLogWarning, "RouterInfo: 'port' exception ", ex.what ());
} }
} }
else if (!strcmp (key, "mtu")) else if (key == "mtu")
{ {
if (address->ssu) if (address->ssu)
{ {
@ -295,36 +286,36 @@ namespace data
else else
LogPrint (eLogWarning, "RouterInfo: Unexpected field 'mtu' for NTCP2"); LogPrint (eLogWarning, "RouterInfo: Unexpected field 'mtu' for NTCP2");
} }
else if (!strcmp (key, "caps")) else if (key == "caps")
address->caps = ExtractAddressCaps (value); address->caps = ExtractAddressCaps (value);
else if (!strcmp (key, "s")) // ntcp2 or ssu2 static key else if (key == "s") // ntcp2 or ssu2 static key
{ {
if (Base64ToByteStream (value, strlen (value), address->s, 32) == 32 && if (Base64ToByteStream (value.data (), value.length (), address->s, 32) == 32 &&
!(address->s[31] & 0x80)) // check if x25519 public key !(address->s[31] & 0x80)) // check if x25519 public key
isStaticKey = true; isStaticKey = true;
else else
address->transportStyle = eTransportUnknown; // invalid address address->transportStyle = eTransportUnknown; // invalid address
} }
else if (!strcmp (key, "i")) // ntcp2 iv or ssu2 intro else if (key == "i") // ntcp2 iv or ssu2 intro
{ {
if (address->IsNTCP2 ()) if (address->IsNTCP2 ())
{ {
if (Base64ToByteStream (value, strlen (value), address->i, 16) == 16) if (Base64ToByteStream (value.data (), value.length (), address->i, 16) == 16)
address->published = true; // presence of "i" means "published" NTCP2 address->published = true; // presence of "i" means "published" NTCP2
else else
address->transportStyle = eTransportUnknown; // invalid address address->transportStyle = eTransportUnknown; // invalid address
} }
else if (address->IsSSU2 ()) else if (address->IsSSU2 ())
{ {
if (Base64ToByteStream (value, strlen (value), address->i, 32) == 32) if (Base64ToByteStream (value.data (), value.length (), address->i, 32) == 32)
isIntroKey = true; isIntroKey = true;
else else
address->transportStyle = eTransportUnknown; // invalid address address->transportStyle = eTransportUnknown; // invalid address
} }
} }
else if (!strcmp (key, "v")) else if (key == "v")
{ {
if (!strcmp (value, "2")) if (value == "2")
isV2 = true; isV2 = true;
else else
{ {
@ -340,13 +331,11 @@ namespace data
LogPrint (eLogError, "RouterInfo: Introducer is presented for non-SSU address. Skipped"); LogPrint (eLogError, "RouterInfo: Introducer is presented for non-SSU address. Skipped");
continue; continue;
} }
size_t l = strlen(key); unsigned char index = key[key.length () - 1] - '0'; // TODO:
unsigned char index = key[l-1] - '0'; // TODO:
key[l-1] = 0;
if (index > 9) if (index > 9)
{ {
LogPrint (eLogError, "RouterInfo: Unexpected introducer's index ", index, " skipped"); LogPrint (eLogError, "RouterInfo: Unexpected introducer's index ", index, " skipped");
if (s) continue; else return; continue;
} }
if (index >= address->ssu->introducers.size ()) if (index >= address->ssu->introducers.size ())
{ {
@ -355,7 +344,8 @@ namespace data
address->ssu->introducers.resize (index + 1); address->ssu->introducers.resize (index + 1);
} }
Introducer& introducer = address->ssu->introducers.at (index); Introducer& introducer = address->ssu->introducers.at (index);
if (!strcmp (key, "itag")) auto key1 = key.substr(0, key.length () - 1);
if (key1 == "itag")
{ {
try try
{ {
@ -366,9 +356,9 @@ namespace data
LogPrint (eLogWarning, "RouterInfo: 'itag' exception ", ex.what ()); LogPrint (eLogWarning, "RouterInfo: 'itag' exception ", ex.what ());
} }
} }
else if (!strcmp (key, "ih")) else if (key1 == "ih")
Base64ToByteStream (value, strlen (value), introducer.iH, 32); Base64ToByteStream (value.data (), value.length (), introducer.iH, 32);
else if (!strcmp (key, "iexp")) else if (key1 == "iexp")
{ {
try try
{ {
@ -380,9 +370,7 @@ namespace data
} }
} }
} }
if (!s) return; }
}
if (address->transportStyle == eTransportNTCP2) if (address->transportStyle == eTransportNTCP2)
{ {
if (isStaticKey) if (isStaticKey)
@ -446,45 +434,40 @@ namespace data
boost::atomic_store (&m_Addresses, addresses); boost::atomic_store (&m_Addresses, addresses);
#endif #endif
// read peers // read peers
uint8_t numPeers; uint8_t numPeers = buf[offset]; offset++; // num peers
s.read ((char *)&numPeers, sizeof (numPeers)); if (!s) return; offset += numPeers*32; // TODO: read peers
s.seekg (numPeers*32, std::ios_base::cur); // TODO: read peers if (offset >= len) return false;
// read properties // read properties
m_Version = 0; m_Version = 0;
bool isNetId = false; bool isNetId = false;
std::string family; std::string family;
uint16_t size, r = 0; uint16_t size = bufbe16toh (buf + offset); offset += 2; // size
s.read ((char *)&size, sizeof (size)); if (!s) return; if (offset + size > len) return false;
size = be16toh (size); size_t r = 0;
while (r < size) while (r < size)
{ {
char key[255], value[255]; auto [key, value, sz] = ExtractParam (buf + offset, len - offset);
r += ReadString (key, 255, s); r += sz; offset += sz;
s.seekg (1, std::ios_base::cur); r++; // = if (key.empty ()) continue;
r += ReadString (value, 255, s);
s.seekg (1, std::ios_base::cur); r++; // ;
if (!s) return;
SetProperty (key, value); SetProperty (key, value);
// extract caps // extract caps
if (!strcmp (key, "caps")) if (key == "caps")
{ {
ExtractCaps (value); ExtractCaps (value);
m_IsFloodfill = IsDeclaredFloodfill (); m_IsFloodfill = IsDeclaredFloodfill ();
} }
// extract version // extract version
else if (!strcmp (key, ROUTER_INFO_PROPERTY_VERSION)) else if (key == ROUTER_INFO_PROPERTY_VERSION)
{ {
m_Version = 0; m_Version = 0;
char * ch = value; for (auto ch: value)
while (*ch)
{ {
if (*ch >= '0' && *ch <= '9') if (ch >= '0' && ch <= '9')
{ {
m_Version *= 10; m_Version *= 10;
m_Version += (*ch - '0'); m_Version += (ch - '0');
} }
ch++;
} }
if (m_Version < NETDB_MIN_PEER_TEST_VERSION && (m_SupportedTransports & (eSSU2V4 | eSSU2V6))) if (m_Version < NETDB_MIN_PEER_TEST_VERSION && (m_SupportedTransports & (eSSU2V4 | eSSU2V6)))
{ {
@ -497,24 +480,26 @@ namespace data
} }
} }
// check netId // check netId
else if (!strcmp (key, ROUTER_INFO_PROPERTY_NETID)) else if (key == ROUTER_INFO_PROPERTY_NETID)
{ {
isNetId = true; isNetId = true;
if (atoi (value) != i2p::context.GetNetID ()) int netID;
auto res = std::from_chars(value.data(), value.data() + value.size(), netID);
if (res.ec != std::errc() || netID != i2p::context.GetNetID ())
{ {
LogPrint (eLogError, "RouterInfo: Unexpected ", ROUTER_INFO_PROPERTY_NETID, "=", value); LogPrint (eLogError, "RouterInfo: Unexpected ", ROUTER_INFO_PROPERTY_NETID, "=", value);
m_IsUnreachable = true; m_IsUnreachable = true;
} }
} }
// family // family
else if (!strcmp (key, ROUTER_INFO_PROPERTY_FAMILY)) else if (key == ROUTER_INFO_PROPERTY_FAMILY)
{ {
family = value; family = value;
boost::to_lower (family); boost::to_lower (family);
} }
else if (!strcmp (key, ROUTER_INFO_PROPERTY_FAMILY_SIG)) else if (key == ROUTER_INFO_PROPERTY_FAMILY_SIG)
{ {
if (netdb.GetFamilies ().VerifyFamily (family, GetIdentHash (), value)) if (netdb.GetFamilies ().VerifyFamily (family, GetIdentHash (), std::string(value).c_str ())) // TODO
m_FamilyID = netdb.GetFamilies ().GetFamilyID (family); m_FamilyID = netdb.GetFamilies ().GetFamilyID (family);
else else
{ {
@ -522,14 +507,14 @@ namespace data
SetUnreachable (true); SetUnreachable (true);
} }
} }
if (!s) return;
} }
if (!m_SupportedTransports || !isNetId || !m_Version) if (!m_SupportedTransports || !isNetId || !m_Version)
SetUnreachable (true); SetUnreachable (true);
}
return true;
}
bool RouterInfo::IsFamily (FamilyID famid) const bool RouterInfo::IsFamily (FamilyID famid) const
{ {
return m_FamilyID == famid; return m_FamilyID == famid;
@ -584,6 +569,11 @@ namespace data
} }
} }
void RouterInfo::ExtractCaps (std::string_view value)
{
ExtractCaps (std::string (value).c_str ()); // TODO
}
uint8_t RouterInfo::ExtractAddressCaps (const char * value) const uint8_t RouterInfo::ExtractAddressCaps (const char * value) const
{ {
uint8_t caps = 0; uint8_t caps = 0;
@ -611,6 +601,11 @@ namespace data
return caps; return caps;
} }
uint8_t RouterInfo::ExtractAddressCaps (std::string_view value) const
{
return ExtractAddressCaps (std::string (value).c_str ()); // TODO:
}
void RouterInfo::UpdateIntroducers (std::shared_ptr<Address> address, uint64_t ts) void RouterInfo::UpdateIntroducers (std::shared_ptr<Address> address, uint64_t ts)
{ {
if (!address || !address->ssu) return; if (!address || !address->ssu) return;
@ -670,25 +665,41 @@ namespace data
return SaveToFile (fullPath, m_Buffer); return SaveToFile (fullPath, m_Buffer);
} }
size_t RouterInfo::ReadString (char * str, size_t len, std::istream& s) const std::string_view RouterInfo::ExtractString (const uint8_t * buf, size_t len) const
{ {
uint8_t l; uint8_t l = buf[0];
s.read ((char *)&l, 1); if (l > len)
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); LogPrint (eLogWarning, "RouterInfo: String length ", (int)l, " exceeds buffer size ", len);
s.seekg (l, std::ios::cur); // skip l = len;
str[0] = 0; }
} return { (const char *)(buf + 1), l };
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) void RouterInfo::AddNTCP2Address (const uint8_t * staticKey, const uint8_t * iv,int port, uint8_t caps)
{ {
auto addr = std::make_shared<Address>(); auto addr = std::make_shared<Address>();
@ -1485,9 +1496,9 @@ namespace data
s.write (properties.str ().c_str (), properties.str ().size ()); s.write (properties.str ().c_str (), properties.str ().size ());
} }
void LocalRouterInfo::SetProperty (const std::string& key, const std::string& value) void LocalRouterInfo::SetProperty (std::string_view key, std::string_view value)
{ {
m_Properties[key] = value; m_Properties.emplace (key, value);
} }
void LocalRouterInfo::DeleteProperty (const std::string& key) void LocalRouterInfo::DeleteProperty (const std::string& key)

View file

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