From ef72ba3f349e0b6888e98625d553229ff0c51bde Mon Sep 17 00:00:00 2001 From: orignal Date: Tue, 18 Feb 2025 21:11:59 -0500 Subject: [PATCH] parse RouterInfo from buffer --- libi2pd/RouterInfo.cpp | 205 ++++++++++++++++++++++------------------- libi2pd/RouterInfo.h | 13 ++- 2 files changed, 117 insertions(+), 101 deletions(-) diff --git a/libi2pd/RouterInfo.cpp b/libi2pd/RouterInfo.cpp index 9cad82ab..13ce0c20 100644 --- a/libi2pd/RouterInfo.cpp +++ b/libi2pd/RouterInfo.cpp @@ -11,6 +11,7 @@ #include "I2PEndian.h" #include #include +#include #include #include // 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,14 +507,14 @@ 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; @@ -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, uint64_t ts) { if (!address || !address->ssu) return; @@ -670,25 +665,41 @@ 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; - } - return l+1; + l = len; + } + return { (const char *)(buf + 1), l }; } + std::tuple 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
(); @@ -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) diff --git a/libi2pd/RouterInfo.h b/libi2pd/RouterInfo.h index 3a4f4fc8..8134059a 100644 --- a/libi2pd/RouterInfo.h +++ b/libi2pd/RouterInfo.h @@ -11,6 +11,8 @@ #include #include +#include +#include #include #include #include @@ -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 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 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, uint64_t ts); template std::shared_ptr 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 (); };