mirror of
https://github.com/PurpleI2P/i2pd.git
synced 2025-03-10 19:38:28 +01:00
parse RouterInfo from buffer
This commit is contained in:
parent
251605e2b8
commit
ef72ba3f34
2 changed files with 117 additions and 101 deletions
|
@ -11,6 +11,7 @@
|
|||
#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
|
||||
|
@ -106,8 +107,7 @@ namespace data
|
|||
// skip identity
|
||||
size_t identityLen = m_RouterIdentity->GetFullLen ();
|
||||
// read new RI
|
||||
std::stringstream str (std::string ((char *)buf + identityLen, len - identityLen));
|
||||
ReadFromStream (str);
|
||||
ReadFromBuffer (buf + identityLen, len - identityLen);
|
||||
if (!m_IsUnreachable)
|
||||
UpdateBuffer (buf, len); // save buffer
|
||||
// don't delete buffer until saved to the file
|
||||
|
@ -195,39 +195,33 @@ namespace data
|
|||
}
|
||||
}
|
||||
// parse RI
|
||||
std::stringstream str;
|
||||
str.write ((const char *)m_Buffer->data () + identityLen, bufferLen - identityLen);
|
||||
ReadFromStream (str);
|
||||
if (!str)
|
||||
if (!ReadFromBuffer (m_Buffer->data () + identityLen, bufferLen - identityLen))
|
||||
{
|
||||
LogPrint (eLogError, "RouterInfo: Malformed message");
|
||||
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;
|
||||
s.read ((char *)&m_Timestamp, sizeof (m_Timestamp));
|
||||
m_Timestamp = be64toh (m_Timestamp);
|
||||
m_Timestamp = bufbe64toh (buf);
|
||||
size_t offset = 8; // timestamp
|
||||
// read addresses
|
||||
auto addresses = NewAddresses ();
|
||||
uint8_t numAddresses;
|
||||
s.read ((char *)&numAddresses, sizeof (numAddresses));
|
||||
uint8_t numAddresses = buf[offset]; offset++;
|
||||
for (int i = 0; i < numAddresses; i++)
|
||||
{
|
||||
uint8_t supportedTransports = 0;
|
||||
auto address = NewAddress ();
|
||||
uint8_t cost; // ignore
|
||||
s.read ((char *)&cost, sizeof (cost));
|
||||
s.read ((char *)&address->date, sizeof (address->date));
|
||||
offset++; // cost, ignore
|
||||
address->date = bufbe64toh (buf + offset); offset += 8; // date
|
||||
bool isHost = false, isStaticKey = false, isV2 = false, isIntroKey = false;
|
||||
char transportStyle[6];
|
||||
ReadString (transportStyle, 6, s);
|
||||
if (!strncmp (transportStyle, "NTCP", 4)) // NTCP or NTCP2
|
||||
auto transportStyle = ExtractString (buf + offset, len - offset); offset += transportStyle.length () + 1;
|
||||
if (!transportStyle.compare (0, 4, "NTCP")) // NTCP or NTCP2
|
||||
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->ssu.reset (new SSUExt ());
|
||||
|
@ -237,24 +231,21 @@ namespace data
|
|||
address->transportStyle = eTransportUnknown;
|
||||
address->caps = 0;
|
||||
address->port = 0;
|
||||
uint16_t size, r = 0;
|
||||
s.read ((char *)&size, sizeof (size)); if (!s) return;
|
||||
size = be16toh (size);
|
||||
uint16_t size = bufbe16toh (buf + offset); offset += 2; // size
|
||||
if (offset + size >= len) return false;
|
||||
if (address->transportStyle == eTransportUnknown)
|
||||
{
|
||||
// skip unknown address
|
||||
s.seekg (size, std::ios_base::cur);
|
||||
if (s) continue; else return;
|
||||
offset += size;
|
||||
continue;
|
||||
}
|
||||
size_t r = 0;
|
||||
while (r < size)
|
||||
{
|
||||
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"))
|
||||
auto [key, value, sz] = ExtractParam (buf + offset, len - offset);
|
||||
r += sz; offset += sz;
|
||||
if (key.empty ()) continue;
|
||||
if (key == "host")
|
||||
{
|
||||
boost::system::error_code ecode;
|
||||
address->host = boost::asio::ip::make_address (value, ecode);
|
||||
|
@ -268,7 +259,7 @@ namespace data
|
|||
address->transportStyle = eTransportUnknown;
|
||||
}
|
||||
}
|
||||
else if (!strcmp (key, "port"))
|
||||
else if (key == "port")
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -279,7 +270,7 @@ namespace data
|
|||
LogPrint (eLogWarning, "RouterInfo: 'port' exception ", ex.what ());
|
||||
}
|
||||
}
|
||||
else if (!strcmp (key, "mtu"))
|
||||
else if (key == "mtu")
|
||||
{
|
||||
if (address->ssu)
|
||||
{
|
||||
|
@ -295,36 +286,36 @@ namespace data
|
|||
else
|
||||
LogPrint (eLogWarning, "RouterInfo: Unexpected field 'mtu' for NTCP2");
|
||||
}
|
||||
else if (!strcmp (key, "caps"))
|
||||
else if (key == "caps")
|
||||
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
|
||||
isStaticKey = true;
|
||||
else
|
||||
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 (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
|
||||
else
|
||||
address->transportStyle = eTransportUnknown; // invalid address
|
||||
}
|
||||
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;
|
||||
else
|
||||
address->transportStyle = eTransportUnknown; // invalid address
|
||||
}
|
||||
}
|
||||
else if (!strcmp (key, "v"))
|
||||
else if (key == "v")
|
||||
{
|
||||
if (!strcmp (value, "2"))
|
||||
if (value == "2")
|
||||
isV2 = true;
|
||||
else
|
||||
{
|
||||
|
@ -340,13 +331,11 @@ namespace data
|
|||
LogPrint (eLogError, "RouterInfo: Introducer is presented for non-SSU address. Skipped");
|
||||
continue;
|
||||
}
|
||||
size_t l = strlen(key);
|
||||
unsigned char index = key[l-1] - '0'; // TODO:
|
||||
key[l-1] = 0;
|
||||
unsigned char index = key[key.length () - 1] - '0'; // TODO:
|
||||
if (index > 9)
|
||||
{
|
||||
LogPrint (eLogError, "RouterInfo: Unexpected introducer's index ", index, " skipped");
|
||||
if (s) continue; else return;
|
||||
continue;
|
||||
}
|
||||
if (index >= address->ssu->introducers.size ())
|
||||
{
|
||||
|
@ -355,7 +344,8 @@ namespace data
|
|||
address->ssu->introducers.resize (index + 1);
|
||||
}
|
||||
Introducer& introducer = address->ssu->introducers.at (index);
|
||||
if (!strcmp (key, "itag"))
|
||||
auto key1 = key.substr(0, key.length () - 1);
|
||||
if (key1 == "itag")
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -366,9 +356,9 @@ namespace data
|
|||
LogPrint (eLogWarning, "RouterInfo: 'itag' exception ", ex.what ());
|
||||
}
|
||||
}
|
||||
else if (!strcmp (key, "ih"))
|
||||
Base64ToByteStream (value, strlen (value), introducer.iH, 32);
|
||||
else if (!strcmp (key, "iexp"))
|
||||
else if (key1 == "ih")
|
||||
Base64ToByteStream (value.data (), value.length (), introducer.iH, 32);
|
||||
else if (key1 == "iexp")
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -380,9 +370,7 @@ namespace data
|
|||
}
|
||||
}
|
||||
}
|
||||
if (!s) return;
|
||||
}
|
||||
|
||||
if (address->transportStyle == eTransportNTCP2)
|
||||
{
|
||||
if (isStaticKey)
|
||||
|
@ -446,45 +434,40 @@ namespace data
|
|||
boost::atomic_store (&m_Addresses, addresses);
|
||||
#endif
|
||||
// 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
|
||||
uint8_t numPeers = buf[offset]; offset++; // num peers
|
||||
offset += numPeers*32; // TODO: read peers
|
||||
if (offset >= len) return false;
|
||||
// read properties
|
||||
m_Version = 0;
|
||||
bool isNetId = false;
|
||||
std::string family;
|
||||
uint16_t size, r = 0;
|
||||
s.read ((char *)&size, sizeof (size)); if (!s) return;
|
||||
size = be16toh (size);
|
||||
uint16_t size = bufbe16toh (buf + offset); offset += 2; // size
|
||||
if (offset + size > len) return false;
|
||||
size_t r = 0;
|
||||
while (r < size)
|
||||
{
|
||||
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;
|
||||
auto [key, value, sz] = ExtractParam (buf + offset, len - offset);
|
||||
r += sz; offset += sz;
|
||||
if (key.empty ()) continue;
|
||||
SetProperty (key, value);
|
||||
|
||||
// extract caps
|
||||
if (!strcmp (key, "caps"))
|
||||
if (key == "caps")
|
||||
{
|
||||
ExtractCaps (value);
|
||||
m_IsFloodfill = IsDeclaredFloodfill ();
|
||||
}
|
||||
// extract version
|
||||
else if (!strcmp (key, ROUTER_INFO_PROPERTY_VERSION))
|
||||
else if (key == ROUTER_INFO_PROPERTY_VERSION)
|
||||
{
|
||||
m_Version = 0;
|
||||
char * ch = value;
|
||||
while (*ch)
|
||||
for (auto ch: value)
|
||||
{
|
||||
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)))
|
||||
{
|
||||
|
@ -497,24 +480,26 @@ namespace data
|
|||
}
|
||||
}
|
||||
// check netId
|
||||
else if (!strcmp (key, ROUTER_INFO_PROPERTY_NETID))
|
||||
else if (key == ROUTER_INFO_PROPERTY_NETID)
|
||||
{
|
||||
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);
|
||||
m_IsUnreachable = true;
|
||||
}
|
||||
}
|
||||
// family
|
||||
else if (!strcmp (key, ROUTER_INFO_PROPERTY_FAMILY))
|
||||
else if (key == ROUTER_INFO_PROPERTY_FAMILY)
|
||||
{
|
||||
family = value;
|
||||
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);
|
||||
else
|
||||
{
|
||||
|
@ -522,12 +507,12 @@ namespace data
|
|||
SetUnreachable (true);
|
||||
}
|
||||
}
|
||||
|
||||
if (!s) return;
|
||||
}
|
||||
|
||||
if (!m_SupportedTransports || !isNetId || !m_Version)
|
||||
SetUnreachable (true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RouterInfo::IsFamily (FamilyID famid) const
|
||||
|
@ -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 caps = 0;
|
||||
|
@ -611,6 +601,11 @@ namespace data
|
|||
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)
|
||||
{
|
||||
if (!address || !address->ssu) return;
|
||||
|
@ -670,23 +665,39 @@ namespace data
|
|||
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;
|
||||
s.read ((char *)&l, 1);
|
||||
if (l < len)
|
||||
{
|
||||
s.read (str, l);
|
||||
if (!s) l = 0; // failed, return empty string
|
||||
str[l] = 0;
|
||||
}
|
||||
else
|
||||
uint8_t l = buf[0];
|
||||
if (l > len)
|
||||
{
|
||||
LogPrint (eLogWarning, "RouterInfo: String length ", (int)l, " exceeds buffer size ", len);
|
||||
s.seekg (l, std::ios::cur); // skip
|
||||
str[0] = 0;
|
||||
l = len;
|
||||
}
|
||||
return l+1;
|
||||
return { (const char *)(buf + 1), l };
|
||||
}
|
||||
|
||||
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)
|
||||
|
@ -1485,9 +1496,9 @@ namespace data
|
|||
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)
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
#include <inttypes.h>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <tuple>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
|
@ -219,7 +221,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 (const std::string& key, const std::string& value) {};
|
||||
virtual void SetProperty (std::string_view key, std::string_view 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;
|
||||
|
@ -333,11 +335,14 @@ namespace data
|
|||
|
||||
bool LoadFile (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);
|
||||
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 (std::string_view value);
|
||||
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);
|
||||
template<typename Filter>
|
||||
std::shared_ptr<const Address> GetAddress (Filter filter) const;
|
||||
|
@ -379,7 +384,7 @@ namespace data
|
|||
void UpdateCaps (uint8_t caps);
|
||||
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);
|
||||
std::string GetProperty (const std::string& key) const;
|
||||
void ClearProperties () override { m_Properties.clear (); };
|
||||
|
|
Loading…
Add table
Reference in a new issue