From: Francis Dupont Date: Thu, 9 Jul 2026 16:36:32 +0000 (+0200) Subject: [#4567] Fixed readFqdn with raw input X-Git-Tag: Kea-3.3.0~48 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=70aa0cc2e6a774bf4c49edabc3bd5b7dbfe9fdae;p=thirdparty%2Fkea.git [#4567] Fixed readFqdn with raw input --- diff --git a/changelog_unreleased/4567-bad-FQDN-length b/changelog_unreleased/4567-bad-FQDN-length new file mode 100644 index 0000000000..4de2d2d1ae --- /dev/null +++ b/changelog_unreleased/4567-bad-FQDN-length @@ -0,0 +1,5 @@ +[bug] fdupont + Fixed the length of FQDN data fields when parsing + custom options. Thank you to Qifan Zhang from + Palo Alto Networks for reporting the issue. + (Gitlab #4567) diff --git a/src/lib/dhcp/option_custom.cc b/src/lib/dhcp/option_custom.cc index fc7e02b254..88e2b9767f 100644 --- a/src/lib/dhcp/option_custom.cc +++ b/src/lib/dhcp/option_custom.cc @@ -228,8 +228,10 @@ OptionCustom::bufferLength(const OptionDataType data_type, bool in_array, // utility function will return the size of the buffer on success. if (data_type == OPT_FQDN_TYPE) { try { + // Set the raw optional falg as we do not want escapes. std::string fqdn = - OptionDataTypeUtil::readFqdn(OptionBuffer(begin, end)); + OptionDataTypeUtil::readFqdn( + OptionBuffer(begin, end), true); // The size of the buffer holding an FQDN is always // 1 byte larger than the size of the string // representation of this FQDN. diff --git a/src/lib/dhcp/option_data_types.cc b/src/lib/dhcp/option_data_types.cc index ea49933b9b..59633ee952 100644 --- a/src/lib/dhcp/option_data_types.cc +++ b/src/lib/dhcp/option_data_types.cc @@ -335,7 +335,8 @@ OptionDataTypeUtil::writeBool(const bool value, } std::string -OptionDataTypeUtil::readFqdn(const std::vector& buf) { +OptionDataTypeUtil::readFqdn(const std::vector& buf, + bool raw /* = false */) { // If buffer is empty emit an error. if (buf.empty()) { isc_throw(BadDataTypeCast, "unable to read FQDN from a buffer." @@ -348,7 +349,11 @@ OptionDataTypeUtil::readFqdn(const std::vector& buf) { // it means that the buffer doesn't hold a valid domain name (invalid // syntax). isc::dns::Name name(in_buf); - return (name.toText()); + if (!raw) { + return (name.toText()); + } else { + return (name.toRawText()); + } } catch (const isc::Exception& ex) { // Unable to convert the data in the buffer into FQDN. isc_throw(BadDataTypeCast, ex.what()); diff --git a/src/lib/dhcp/option_data_types.h b/src/lib/dhcp/option_data_types.h index fc4e193a41..5b4b29bd74 100644 --- a/src/lib/dhcp/option_data_types.h +++ b/src/lib/dhcp/option_data_types.h @@ -538,11 +538,13 @@ public: /// section 3.1. /// /// @param buf input buffer holding a FQDN. + /// @param raw raw (i.e. not escape) output. /// /// @throw BadDataTypeCast if a FQDN stored within a buffer is /// invalid (e.g. empty, contains invalid characters, truncated). /// @return fully qualified domain name in a text form. - static std::string readFqdn(const std::vector& buf); + static std::string readFqdn(const std::vector& buf, + bool raw = false); /// @brief Append FQDN into a buffer. /// diff --git a/src/lib/dhcp/tests/option_custom_unittest.cc b/src/lib/dhcp/tests/option_custom_unittest.cc index 780065650f..9f5a2af804 100644 --- a/src/lib/dhcp/tests/option_custom_unittest.cc +++ b/src/lib/dhcp/tests/option_custom_unittest.cc @@ -458,6 +458,25 @@ TEST_F(OptionCustomTest, fqdnData) { Option::lenient_parsing_ = false; } +// Same as the previous test but with characters which could be escaped. +TEST_F(OptionCustomTest, fqdnRawData) { + OptionDefinition opt_def("option-foo", 1000, "my-space", "fqdn", + "option-foo-space"); + + const char data[] = { 1, 9, 0 }; // "." + + std::vector buf(data, data + sizeof(data)); + + boost::scoped_ptr option; + ASSERT_NO_THROW(option.reset(new OptionCustom(opt_def, Option::V6, + buf.begin(), buf.end()))); + ASSERT_TRUE(option); + ASSERT_EQ(1U, option->getDataFieldsNum()); + + std::string domain0 = option->readFqdn(0); + EXPECT_EQ("\\009.", domain0); +} + // The purpose of this test is to verify that the option definition comprising // 16-bit signed integer value can be used to create an instance of custom option. TEST_F(OptionCustomTest, int16Data) { diff --git a/src/lib/dhcp/tests/option_data_types_unittest.cc b/src/lib/dhcp/tests/option_data_types_unittest.cc index f4bfd663bd..f9b85b115d 100644 --- a/src/lib/dhcp/tests/option_data_types_unittest.cc +++ b/src/lib/dhcp/tests/option_data_types_unittest.cc @@ -524,6 +524,25 @@ TEST_F(OptionDataTypesTest, readFqdn) { ); } +// Same as the previous test but with characters which could be escaped. +TEST_F(OptionDataTypesTest, readFqdnRaw) { + // The binary representation of the ".". + const char data[] = { 1, 9, 0 }; + + // Make a vector out of the data. + std::vector buf(data, data + sizeof(data)); + + // Read the buffer as FQDN and verify its correctness. + std::string fqdn; + EXPECT_NO_THROW(fqdn = OptionDataTypeUtil::readFqdn(buf)); + EXPECT_EQ("\\009.", fqdn); + std::string raw; + EXPECT_NO_THROW(raw = OptionDataTypeUtil::readFqdn(buf, true)); + EXPECT_EQ(2U, raw.size()); + EXPECT_EQ(9, raw[0]); + EXPECT_EQ('.', raw[1]); +} + // The purpose of this test is to verify that FQDN's syntax is validated // and that FQDN is correctly written to a buffer in a format described // in RFC1035 section 3.1.