BIND 10 trac3082, updated. b2a59752080f1aa7dbcfd677c5bfb8f65239c4c1 [3082] Add support for ASCII domain name encoding and decoding.
BIND 10 source code commits
bind10-changes at lists.isc.org
Fri Aug 9 17:29:58 UTC 2013
The branch, trac3082 has been updated
via b2a59752080f1aa7dbcfd677c5bfb8f65239c4c1 (commit)
from 4fbb85cb991a8f3f412c2c386dcbe154fa5f04f2 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit b2a59752080f1aa7dbcfd677c5bfb8f65239c4c1
Author: Marcin Siodelski <marcin at isc.org>
Date: Fri Aug 9 19:29:39 2013 +0200
[3082] Add support for ASCII domain name encoding and decoding.
-----------------------------------------------------------------------
Summary of changes:
src/lib/dhcp/option4_client_fqdn.cc | 61 ++++++--
src/lib/dhcp/option4_client_fqdn.h | 16 ++-
src/lib/dhcp/tests/option4_client_fqdn_unittest.cc | 149 +++++++++++++++++++-
3 files changed, 211 insertions(+), 15 deletions(-)
-----------------------------------------------------------------------
diff --git a/src/lib/dhcp/option4_client_fqdn.cc b/src/lib/dhcp/option4_client_fqdn.cc
index d705dc6..3e797d5 100644
--- a/src/lib/dhcp/option4_client_fqdn.cc
+++ b/src/lib/dhcp/option4_client_fqdn.cc
@@ -51,6 +51,13 @@ public:
void parseWireData(OptionBufferConstIter first,
OptionBufferConstIter last);
+ void parseCanonicalDomainName(OptionBufferConstIter first,
+ OptionBufferConstIter last);
+
+ void
+ parseASCIIDomainName(OptionBufferConstIter first,
+ OptionBufferConstIter last);
+
};
Option4ClientFqdnImpl::
@@ -176,6 +183,24 @@ Option4ClientFqdnImpl::parseWireData(OptionBufferConstIter first,
rcode1_ = Option4ClientFqdn::Rcode(*(first++));
rcode2_ = Option4ClientFqdn::Rcode(*(first++));
+ try {
+ if ((flags_ & Option4ClientFqdn::FLAG_E) != 0) {
+ parseCanonicalDomainName(first, last);
+
+ } else {
+ parseASCIIDomainName(first, last);
+
+ }
+ } catch (const Exception& ex) {
+ isc_throw(InvalidOption4ClientFqdnDomainName,
+ "failed to parse the domain-name in DHCPv4 Client FQDN"
+ << " Option: " << ex.what());
+ }
+}
+
+void
+Option4ClientFqdnImpl::parseCanonicalDomainName(OptionBufferConstIter first,
+ OptionBufferConstIter last) {
// Parse domain-name if any.
if (std::distance(first, last) > 0) {
// The FQDN may comprise a partial domain-name. In this case it lacks
@@ -204,7 +229,18 @@ Option4ClientFqdnImpl::parseWireData(OptionBufferConstIter first,
}
}
- Option4ClientFqdn::Option4ClientFqdn(const uint8_t flag, const Rcode& rcode)
+void
+Option4ClientFqdnImpl::parseASCIIDomainName(OptionBufferConstIter first,
+ OptionBufferConstIter last) {
+ if (std::distance(first, last) > 0) {
+ std::string domain_name(first, last);
+ domain_name_.reset(new isc::dns::Name(domain_name));
+ domain_name_type_ = domain_name[domain_name.length() - 1] == '.' ?
+ Option4ClientFqdn::FULL : Option4ClientFqdn::PARTIAL;
+ }
+}
+
+Option4ClientFqdn::Option4ClientFqdn(const uint8_t flag, const Rcode& rcode)
: Option(Option::V4, DHO_FQDN),
impl_(new Option4ClientFqdnImpl(flag, rcode, "", PARTIAL)) {
}
@@ -307,15 +343,22 @@ Option4ClientFqdn::getDomainName() const {
void
Option4ClientFqdn::packDomainName(isc::util::OutputBuffer& buf) const {
- // Domain name, encoded as a set of labels.
- isc::dns::LabelSequence labels(*impl_->domain_name_);
- if (labels.getDataLength() > 0) {
- size_t read_len = 0;
- const uint8_t* data = labels.getData(&read_len);
- if (impl_->domain_name_type_ == PARTIAL) {
- --read_len;
+ if (getFlag(FLAG_E)) {
+ // Domain name, encoded as a set of labels.
+ isc::dns::LabelSequence labels(*impl_->domain_name_);
+ if (labels.getDataLength() > 0) {
+ size_t read_len = 0;
+ const uint8_t* data = labels.getData(&read_len);
+ if (impl_->domain_name_type_ == PARTIAL) {
+ --read_len;
+ }
+ buf.writeData(data, read_len);
}
- buf.writeData(data, read_len);
+
+ } else {
+ std::string domain_name = impl_->domain_name_->toText();
+ buf.writeData(&domain_name[0], domain_name.size());
+
}
}
diff --git a/src/lib/dhcp/option4_client_fqdn.h b/src/lib/dhcp/option4_client_fqdn.h
index b02736d..980706a 100644
--- a/src/lib/dhcp/option4_client_fqdn.h
+++ b/src/lib/dhcp/option4_client_fqdn.h
@@ -79,10 +79,18 @@ class Option4ClientFqdnImpl;
/// Domain names being carried by DHCPv4 Client Fqdn %Option can be fully
/// qualified or partial. Partial domain names are encoded similar to the
/// fully qualified domain names, except that they lack terminating zero
-/// at the end of their wire representation. It is also accepted to create an
-/// instance of this option which has empty domain-name. Clients use empty
-/// domain-names to indicate that server should generate complete fully
-/// qualified domain-name.
+/// at the end of their wire representation (or dot in case of ASCII encoding).
+/// It is also accepted to create an/ instance of this option which has empty
+/// domain-name. Clients use empty domain-names to indicate that server should
+/// generate complete fully qualified domain-name.
+///
+/// @warning: The RFC4702 section 2.3.1 states that the clients and servers
+/// should use character sets specified in RFC952, section 2.1. This class doesn't
+/// detect the character set violation for ASCII encoded domain-name. This could
+/// be implemented in the future but it is not important for two reasons:
+/// - ASCII encoding is deprecated
+/// - clients SHOULD obey restrictions but if they don't server may still
+/// process the option
///
/// RFC 4702 mandates that the DHCP client sets RCODE1 and RCODE2 to 0 and that
/// server sets them to 255. This class allows to set the value for these
diff --git a/src/lib/dhcp/tests/option4_client_fqdn_unittest.cc b/src/lib/dhcp/tests/option4_client_fqdn_unittest.cc
index 002cc0e..b1eb174 100644
--- a/src/lib/dhcp/tests/option4_client_fqdn_unittest.cc
+++ b/src/lib/dhcp/tests/option4_client_fqdn_unittest.cc
@@ -143,8 +143,10 @@ TEST(Option4ClientFqdnTest, copyConstruct) {
EXPECT_EQ(Option4ClientFqdn::PARTIAL, option_copy->getDomainNameType());
}
-// This test verifies that the option in the on-wire format is parsed correctly.
+// This test verifies that the option in the on-wire format with the domain-name
+// encoded in the canonical format is parsed correctly.
TEST(Option4ClientFqdnTest, constructFromWire) {
+ // The E flag sets the domain-name format to canonical.
const uint8_t in_data[] = {
FLAG_S | FLAG_E, // flags
0, // RCODE1
@@ -171,6 +173,38 @@ TEST(Option4ClientFqdnTest, constructFromWire) {
EXPECT_EQ(Option4ClientFqdn::FULL, option->getDomainNameType());
}
+// This test verifies that the option in the on-wire format with the domain-name
+// encoded in the ASCII format is parsed correctly.
+TEST(Option4ClientFqdnTest, constructFromWireASCII) {
+ // The E flag is set to zero which indicates that the domain name
+ // is encoded in the ASCII format. The "dot" character at the end
+ // indicates that the domain-name is fully qualified.
+ const uint8_t in_data[] = {
+ FLAG_S, // flags
+ 0, // RCODE1
+ 0, // RCODE2
+ 109, 121, 104, 111, 115, 116, 46, // myhost.
+ 101, 120, 97, 109, 112, 108, 101, 46, // example.
+ 99, 111, 109, 46 // com.
+ };
+ size_t in_data_size = sizeof(in_data) / sizeof(in_data[0]);
+ OptionBuffer in_buf(in_data, in_data + in_data_size);
+
+ // Create option instance. Check that constructor doesn't throw.
+ boost::scoped_ptr<Option4ClientFqdn> option;
+ ASSERT_NO_THROW(
+ option.reset(new Option4ClientFqdn(in_buf.begin(), in_buf.end()))
+ );
+ ASSERT_TRUE(option);
+
+ EXPECT_TRUE(option->getFlag(Option4ClientFqdn::FLAG_S));
+ EXPECT_FALSE(option->getFlag(Option4ClientFqdn::FLAG_E));
+ EXPECT_FALSE(option->getFlag(Option4ClientFqdn::FLAG_O));
+ EXPECT_FALSE(option->getFlag(Option4ClientFqdn::FLAG_N));
+ EXPECT_EQ("myhost.example.com.", option->getDomainName());
+ EXPECT_EQ(Option4ClientFqdn::FULL, option->getDomainNameType());
+}
+
// This test verifies that truncated option is rejected.
TEST(Option4ClientFqdnTest, constructFromWireTruncated) {
// Empty buffer is invalid. It should be at least one octet long.
@@ -186,8 +220,48 @@ TEST(Option4ClientFqdnTest, constructFromWireTruncated) {
EXPECT_NO_THROW(Option4ClientFqdn(in_buf.begin(), in_buf.end()));
}
+// This test verifies that exception is thrown when invalid domain-name
+// in canonical format is carried in the option.
+TEST(Option4ClientFqdnTest, constructFromWireInvalidName) {
+ const uint8_t in_data[] = {
+ FLAG_S | FLAG_E, // flags
+ 0, // RCODE1
+ 0, // RCODE2
+ 6, 109, 121, 104, 111, 115, 116, // myhost.
+ 7, 101, 120, 97, 109, 112, 108, 101, // example.
+ 5, 99, 111, 109, 0 // com. (invalid label length 5)
+ };
+ size_t in_data_size = sizeof(in_data) / sizeof(in_data[0]);
+ OptionBuffer in_buf(in_data, in_data + in_data_size);
+
+ EXPECT_THROW(
+ Option4ClientFqdn(in_buf.begin(), in_buf.end()),
+ InvalidOption4ClientFqdnDomainName
+ );
+}
+
+// This test verifies that exception is thrown when invalid domain-name
+// in ASCII format is carried in the option.
+TEST(Option4ClientFqdnTest, constructFromWireInvalidASCIIName) {
+ const uint8_t in_data[] = {
+ FLAG_S, // flags
+ 0, // RCODE1
+ 0, // RCODE2
+ 109, 121, 104, 111, 115, 116, 46, 46, // myhost.. (double dot!)
+ 101, 120, 97, 109, 112, 108, 101, 46, // example.
+ 99, 111, 109, 46 // com.
+ };
+ size_t in_data_size = sizeof(in_data) / sizeof(in_data[0]);
+ OptionBuffer in_buf(in_data, in_data + in_data_size);
+
+ EXPECT_THROW(
+ Option4ClientFqdn(in_buf.begin(), in_buf.end()),
+ InvalidOption4ClientFqdnDomainName
+ );
+}
+
// This test verifies that the option in the on-wire format with partial
-// domain-name is parsed correctly.
+// domain-name encoded in canonical format is parsed correctly.
TEST(Option4ClientFqdnTest, constructFromWirePartial) {
const uint8_t in_data[] = {
FLAG_N | FLAG_E, // flags
@@ -213,6 +287,35 @@ TEST(Option4ClientFqdnTest, constructFromWirePartial) {
EXPECT_EQ(Option4ClientFqdn::PARTIAL, option->getDomainNameType());
}
+// This test verifies that the option in the on-wire format with partial
+// domain-name encoded in ASCII format is parsed correctly.
+TEST(Option4ClientFqdnTest, constructFromWirePartialASCII) {
+ // There is no "dot" character at the end, so the domain-name is partial.
+ const uint8_t in_data[] = {
+ FLAG_N, // flags
+ 255, // RCODE1
+ 255, // RCODE2
+ 109, 121, 104, 111, 115, 116, 46, // myhost.
+ 101, 120, 97, 109, 112, 108, 101 // example
+ };
+ size_t in_data_size = sizeof(in_data) / sizeof(in_data[0]);
+ OptionBuffer in_buf(in_data, in_data + in_data_size);
+
+ // Create option instance. Check that constructor doesn't throw.
+ boost::scoped_ptr<Option4ClientFqdn> option;
+ ASSERT_NO_THROW(
+ option.reset(new Option4ClientFqdn(in_buf.begin(), in_buf.end()))
+ );
+ ASSERT_TRUE(option);
+
+ EXPECT_FALSE(option->getFlag(Option4ClientFqdn::FLAG_S));
+ EXPECT_FALSE(option->getFlag(Option4ClientFqdn::FLAG_E));
+ EXPECT_FALSE(option->getFlag(Option4ClientFqdn::FLAG_O));
+ EXPECT_TRUE(option->getFlag(Option4ClientFqdn::FLAG_N));
+ EXPECT_EQ("myhost.example", option->getDomainName());
+ EXPECT_EQ(Option4ClientFqdn::PARTIAL, option->getDomainNameType());
+}
+
// This test verifies that the option in the on-wire format with empty
// domain-name is parsed correctly.
TEST(Option4ClientFqdnTest, constructFromWireEmpty) {
@@ -356,6 +459,14 @@ TEST(Option4ClientFqdnTest, constructInvalidName) {
EXPECT_THROW(Option4ClientFqdn(FLAG_E, Option4ClientFqdn::RCODE_CLIENT(),
"my...host.example.com"),
InvalidOption4ClientFqdnDomainName);
+
+ // Do the same test for the domain-name in ASCII format.
+ ASSERT_NO_THROW(Option4ClientFqdn(0, Option4ClientFqdn::RCODE_CLIENT(),
+ "myhost.example.com"));
+
+ EXPECT_THROW(Option4ClientFqdn(0, Option4ClientFqdn::RCODE_CLIENT(),
+ "my...host.example.com"),
+ InvalidOption4ClientFqdnDomainName);
}
// This test verifies that getFlag throws an exception if flag value other
@@ -560,6 +671,40 @@ TEST(Option4ClientFqdnTest, pack) {
EXPECT_EQ(0, memcmp(ref_data, buf.getData(), buf.getLength()));
}
+TEST(Option4ClientFqdnTest, packASCII) {
+ // Create option instance. Check that constructor doesn't throw.
+ const uint8_t flags = FLAG_S;
+ boost::scoped_ptr<Option4ClientFqdn> option;
+ ASSERT_NO_THROW(
+ option.reset(new Option4ClientFqdn(flags,
+ Option4ClientFqdn::RCODE_CLIENT(),
+ "myhost.example.com"))
+ );
+ ASSERT_TRUE(option);
+
+ // Prepare on-wire format of the option.
+ isc::util::OutputBuffer buf(10);
+ ASSERT_NO_THROW(option->pack(buf));
+
+ // Prepare reference data.
+ const uint8_t ref_data[] = {
+ 81, 23, // header
+ FLAG_S, // flags
+ 0, // RCODE1
+ 0, // RCODE2
+ 109, 121, 104, 111, 115, 116, 46, // myhost.
+ 101, 120, 97, 109, 112, 108, 101, 46, // example.
+ 99, 111, 109, 46 // com.
+ };
+ size_t ref_data_size = sizeof(ref_data) / sizeof(ref_data[0]);
+
+ // Check if the buffer has the same length as the reference data,
+ // so as they can be compared directly.
+ ASSERT_EQ(ref_data_size, buf.getLength());
+ EXPECT_EQ(0, memcmp(ref_data, buf.getData(), buf.getLength()));
+
+}
+
// This test verifies on-wire format of the option with partial domain name
// is correctly created.
TEST(Option4ClientFqdnTest, packPartial) {
More information about the bind10-changes
mailing list