Commit 22a33dfd authored by Marcin Siodelski's avatar Marcin Siodelski

[2491] Added a function to write/read FQDN to/from buffer.

parent c806c0c3
......@@ -37,6 +37,7 @@ libb10_dhcp___la_SOURCES += pkt4.cc pkt4.h
libb10_dhcp___la_CXXFLAGS = $(AM_CXXFLAGS)
libb10_dhcp___la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
libb10_dhcp___la_LIBADD = $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
libb10_dhcp___la_LIBADD += $(top_builddir)/src/lib/dns/libb10-dns++.la
libb10_dhcp___la_LIBADD += $(top_builddir)/src/lib/util/libb10-util.la
libb10_dhcp___la_LDFLAGS = -no-undefined -version-info 2:0:0
......
......@@ -13,6 +13,8 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <dhcp/option_data_types.h>
#include <dns/labelsequence.h>
#include <dns/name.h>
#include <util/encode/hex.h>
namespace isc {
......@@ -206,6 +208,48 @@ OptionDataTypeUtil::writeBool(const bool value,
buf.push_back(static_cast<uint8_t>(value ? 1 : 0));
}
std::string
OptionDataTypeUtil::readFqdn(const std::vector<uint8_t>& buf) {
// If buffer is empty emit an error.
if (buf.empty()) {
isc_throw(BadDataTypeCast, "unable to read FQDN from a buffer."
<< " The buffer is empty");
}
// Copy the data from a buffer to InputBuffer so as we can use
// isc::dns::Name object to get the FQDN. This is not the most
// efficient way to do it but currently there is no construtor
// in Name that would use std::vector directly.
isc::util::InputBuffer in_buf(static_cast<const void*>(&buf[0]), buf.size());
try {
// Try to create an object from the buffer. If exception is thrown
// it means that the buffer doesn't hold a valid domain name (invalid
// syntax).
isc::dns::Name name(in_buf);
return (name.toText());
} catch (const isc::Exception& ex) {
// Unable to convert the data in the buffer into FQDN.
isc_throw(BadDataTypeCast, ex.what());
}
}
void
OptionDataTypeUtil::writeFqdn(const std::string& fqdn,
std::vector<uint8_t>& buf) {
try {
isc::dns::Name name(fqdn);
isc::dns::LabelSequence labels(name);
if (labels.getDataLength() > 0) {
buf.resize(buf.size() + labels.getDataLength());
size_t read_len = 0;
const uint8_t* data = labels.getData(&read_len);
memcpy(static_cast<void*>(&buf[buf.size() - labels.getDataLength()]),
data, read_len);
}
} catch (const isc::Exception& ex) {
isc_throw(BadDataTypeCast, ex.what());
}
}
std::string
OptionDataTypeUtil::readString(const std::vector<uint8_t>& buf) {
std::string value;
......
......@@ -332,6 +332,33 @@ public:
}
}
/// @brief Read FQDN from a buffer as a string value.
///
/// The format of an FQDN within a buffer complies with RFC1035,
/// section 3.1.
///
/// @param buf input buffer holding a FQDN.
///
/// @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<uint8_t>& buf);
/// @brief Append FQDN into a buffer.
///
/// This method appends the Fully Qualified Domain Name (FQDN)
/// represented as string value into a buffer. The format of
/// the FQDN being stored into a buffer complies with RFC1035,
/// section 3.1.
///
/// @param fqdn fully qualified domain name to be written.
/// @param [out] buf output buffer.
///
/// @throw isc::dhcp::BadDataTypeCast if provided FQDN
/// is invalid.
static void writeFqdn(const std::string& fqdn,
std::vector<uint8_t>& buf);
/// @brief Read string value from a buffer.
///
/// @param buf input buffer.
......
......@@ -35,6 +35,7 @@ libdhcp___unittests_SOURCES += option6_ia_unittest.cc
libdhcp___unittests_SOURCES += option6_iaaddr_unittest.cc
libdhcp___unittests_SOURCES += option6_int_array_unittest.cc
libdhcp___unittests_SOURCES += option6_int_unittest.cc
libdhcp___unittests_SOURCES += option_data_types_unittest.cc
libdhcp___unittests_SOURCES += option_definition_unittest.cc
libdhcp___unittests_SOURCES += option_custom_unittest.cc
libdhcp___unittests_SOURCES += option_unittest.cc
......
// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <config.h>
#include <dhcp/option_data_types.h>
#include <gtest/gtest.h>
using namespace isc;
using namespace isc::dhcp;
namespace {
/// @brief Test class for option data type utilities.
class OptionDataTypesTest : public ::testing::Test {
public:
/// @brief Constructor.
OptionDataTypesTest() { }
};
// The purpose of this test is to verify that FQDN is read from
// a buffer and returned as a text. The representation of the FQDN
// in the buffer complies with RFC1035, section 3.1.
// This test also checks that if invalid (truncated) FQDN is stored
// in a buffer the appropriate exception is returned when trying to
// read it as a string.
TEST_F(OptionDataTypesTest, readFqdn) {
// The binary representation of the "mydomain.example.com".
// Values: 8, 7, 3 and 0 specify the lengths of subsequent
// labels within the FQDN.
const char data[] = {
8, 109, 121, 100, 111, 109, 97, 105, 110, // "mydomain"
7, 101, 120, 97, 109, 112, 108, 101, // "example"
3, 99, 111, 109, // "com"
0
};
// Make a vector out of the data.
std::vector<uint8_t> 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("mydomain.example.com.", fqdn);
// By resizing the buffer we simulate truncation. The first
// length field (8) indicate that the first label's size is
// 8 but the actual buffer size is 5. Expect that conversion
// fails.
buf.resize(5);
EXPECT_THROW(
OptionDataTypeUtil::readFqdn(buf),
isc::dhcp::BadDataTypeCast
);
// Another special case: provide an empty buffer.
buf.clear();
EXPECT_THROW(
OptionDataTypeUtil::readFqdn(buf),
isc::dhcp::BadDataTypeCast
);
}
// 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.
TEST_F(OptionDataTypesTest, writeFqdn) {
// Create empty buffer. The FQDN will be written to it.
OptionBuffer buf;
// Write a domain name into the buffer in the format described
// in RFC1035 section 3.1. This function should not throw
// exception because domain name is well formed.
EXPECT_NO_THROW(
OptionDataTypeUtil::writeFqdn("mydomain.example.com", buf)
);
// The length of the data is 22 (8 bytes for "mydomain" label,
// 7 bytes for "example" label, 3 bytes for "com" label and
// finally 4 bytes positions between labels where length
// information is stored.
ASSERT_EQ(22, buf.size());
// Verify that length fields between labels hold valid values.
EXPECT_EQ(8, buf[0]); // length of "mydomain"
EXPECT_EQ(7, buf[9]); // length of "example"
EXPECT_EQ(3, buf[17]); // length of "com"
EXPECT_EQ(0, buf[21]); // zero byte at the end.
// Verify that labels are valid.
std::string label0(buf.begin() + 1, buf.begin() + 9);
EXPECT_EQ("mydomain", label0);
std::string label1(buf.begin() + 10, buf.begin() + 17);
EXPECT_EQ("example", label1);
std::string label2(buf.begin() + 18, buf.begin() + 21);
EXPECT_EQ("com", label2);
// The tested function is supposed to append data to a buffer
// so let's check that it is a case by appending another domain.
OptionDataTypeUtil::writeFqdn("hello.net", buf);
// The buffer length should be now longer.
ASSERT_EQ(33, buf.size());
// Check the length fields for new labels being appended.
EXPECT_EQ(5, buf[22]);
EXPECT_EQ(3, buf[28]);
// And check that labels are ok.
std::string label3(buf.begin() + 23, buf.begin() + 28);
EXPECT_EQ("hello", label3);
std::string label4(buf.begin() + 29, buf.begin() + 32);
EXPECT_EQ("net", label4);
// Check that invalid (empty) FQDN is rejected and expected
// exception type is thrown.
buf.clear();
EXPECT_THROW(
OptionDataTypeUtil::writeFqdn("", buf),
isc::dhcp::BadDataTypeCast
);
// Check another invalid domain name (with repeated dot).
buf.clear();
EXPECT_THROW(
OptionDataTypeUtil::writeFqdn("example..com", buf),
isc::dhcp::BadDataTypeCast
);
}
} // anonymous namespace
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment