diff --git a/data/include/join/pack.hpp b/data/include/join/pack.hpp index 466111dd..d5dab45f 100644 --- a/data/include/join/pack.hpp +++ b/data/include/join/pack.hpp @@ -52,17 +52,27 @@ namespace join static_assert (std::numeric_limits::is_iec559 && std::numeric_limits::is_iec559, "MessagePack requires IEEE 754 floating point numbers"); + /** + * @brief MessagePack serialization mode. + */ + enum class PackWriteMode + { + None, /**< no write mode set. */ + ValidateEncoding, /**< validate UTF-8 encoding of strings. */ + }; + /** * @brief message pack writer class. */ - class PackWriter + template + class BasicPackWriter { public: /** * @brief create instance. * @param document MessagePack document to create. */ - explicit PackWriter (std::ostream& document) + explicit BasicPackWriter (std::ostream& document) : _out (document.rdbuf ()) { } @@ -71,32 +81,32 @@ namespace join * @brief copy constructor. * @param other object to copy. */ - PackWriter (const PackWriter& other) = delete; + BasicPackWriter (const BasicPackWriter& other) = delete; /** * @brief copy assignment. * @param other object to copy. * @return a reference of the current object. */ - PackWriter& operator= (const PackWriter& other) = delete; + BasicPackWriter& operator= (const BasicPackWriter& other) = delete; /** * @brief move constructor. * @param other object to move. */ - PackWriter (PackWriter&& other) = delete; + BasicPackWriter (BasicPackWriter&& other) = delete; /** * @brief move assignment. * @param other object to move. * @return a reference of the current object. */ - PackWriter& operator= (PackWriter&& other) = delete; + BasicPackWriter& operator= (BasicPackWriter&& other) = delete; /** * @brief destroy instance. */ - ~PackWriter () = default; + ~BasicPackWriter () = default; /** * @brief set null value. @@ -322,6 +332,23 @@ namespace join */ int setString (std::string_view value) { + if (JOIN_UNLIKELY (value.size () > std::numeric_limits::max ())) + { + join::lastError = make_error_code (SaxErrc::InvalidValue); + reset (); + return -1; + } + + if constexpr (WriteMode == PackWriteMode::ValidateEncoding) + { + if (JOIN_UNLIKELY (utf8::validate (value.data (), value.data () + value.size ()) == -1)) + { + join::lastError = make_error_code (SaxErrc::InvalidEncoding); + reset (); + return -1; + } + } + if (value.size () < 32) { append (static_cast (0xa0 | value.size ())); @@ -354,6 +381,13 @@ namespace join */ int setBinary (std::span value, std::optional subtype = std::nullopt) { + if (JOIN_UNLIKELY (value.size () > std::numeric_limits::max ())) + { + join::lastError = make_error_code (SaxErrc::InvalidValue); + reset (); + return -1; + } + if (subtype) { switch (value.size ()) @@ -697,6 +731,9 @@ namespace join size_t _depth = 0; }; + /// MessagePack writer. + using PackWriter = BasicPackWriter; + /** * @brief MessagePack deserialization mode. */ diff --git a/data/tests/pack_writer_test.cpp b/data/tests/pack_writer_test.cpp index a4f66194..4cf14e7c 100644 --- a/data/tests/pack_writer_test.cpp +++ b/data/tests/pack_writer_test.cpp @@ -429,6 +429,27 @@ TEST (PackWriter, setString) stream.str (""); EXPECT_EQ (packWriter.setString (std::string (65536, 'x')), 0); EXPECT_EQ (stream.str ().compare (0, 6, std::string ({'\xDB', '\x00', '\x01', '\x00', '\x00', '\x78'})), 0); + + char dummy = 0; + stream.str (""); + EXPECT_EQ (packWriter.setString (std::string_view (&dummy, 1ULL << 32)), -1); + EXPECT_EQ (join::lastError, SaxErrc::InvalidValue); + EXPECT_TRUE (stream.str ().empty ()); + + stream.str (""); + EXPECT_EQ (packWriter.setString ("\xff"), 0); + EXPECT_EQ (stream.str (), std::string ({'\xA1', '\xFF'})); + + join::BasicPackWriter validator (stream); + + stream.str (""); + EXPECT_EQ (validator.setString ("\xc3\xa9"), 0); + EXPECT_EQ (stream.str (), std::string ({'\xA2', '\xC3', '\xA9'})); + + stream.str (""); + EXPECT_EQ (validator.setString ("\xff"), -1); + EXPECT_EQ (join::lastError, SaxErrc::InvalidEncoding); + EXPECT_TRUE (stream.str ().empty ()); } /** @@ -474,6 +495,12 @@ TEST (PackWriter, setBinary) EXPECT_EQ (ext (256), std::string ({'\xC8', '\x01', '\x00', '\xFF'})); EXPECT_EQ (ext (65535), std::string ({'\xC8', '\xFF', '\xFF', '\xFF'})); EXPECT_EQ (ext (65536), std::string ({'\xC9', '\x00', '\x01', '\x00', '\x00', '\xFF'})); + + uint8_t dummy = 0; + stream.str (""); + EXPECT_EQ (packWriter.setBinary (std::span (&dummy, 1ULL << 32)), -1); + EXPECT_EQ (join::lastError, SaxErrc::InvalidValue); + EXPECT_TRUE (stream.str ().empty ()); } /** @@ -673,6 +700,13 @@ TEST (PackWriter, setKey) EXPECT_EQ (key (std::span (bytes, sizeof (bytes)), -1), 0); EXPECT_EQ (stream.str ().substr (1), std::string ({'\xD5', '\xFF', '\x00', '\xFF'})); + + join::BasicPackWriter validator (stream); + stream.str (""); + validator.startObject (1); + EXPECT_EQ (validator.setKey (std::string_view ("\xff")), -1); + EXPECT_EQ (join::lastError, SaxErrc::InvalidEncoding); + EXPECT_TRUE (stream.str ().empty ()); } /** diff --git a/fabric/tests/dns_test.cpp b/fabric/tests/dns_test.cpp index c6a74781..29d94211 100644 --- a/fabric/tests/dns_test.cpp +++ b/fabric/tests/dns_test.cpp @@ -329,7 +329,7 @@ TEST_F (DnsTest, resolveAllName) aliases = Dns::Resolver::lookupAllName ("::"); EXPECT_EQ (aliases.size (), 0); - aliases = Dns::Resolver::lookupAllName ("192.168.24.32"); + aliases = Dns::Resolver::lookupAllName ("192.0.2.32"); EXPECT_EQ (aliases.size (), 0); aliases = _resolver->resolveAllName ("127.0.0.1"); @@ -380,7 +380,7 @@ TEST_F (DnsTest, resolveName) alias = Dns::Resolver::lookupName ("::"); EXPECT_TRUE (alias.empty ()); - alias = Dns::Resolver::lookupName ("192.168.24.32"); + alias = Dns::Resolver::lookupName ("192.0.2.32"); EXPECT_TRUE (alias.empty ()); alias = _resolver->resolveName ("127.0.0.1");