BIND 10 trac2491, updated. 10e87104e0b7977804c8c55275a403602dcbb2e5 [2491] Added function to add new integer data field to an option.
BIND 10 source code commits
bind10-changes at lists.isc.org
Mon Dec 3 13:05:21 UTC 2012
The branch, trac2491 has been updated
via 10e87104e0b7977804c8c55275a403602dcbb2e5 (commit)
via 6b59d1bc4fe7976ea6c1cf7820649824b4135e07 (commit)
via 5c50504446f431198d329d31b62e076d65e3eacb (commit)
via 594916b524ff40cd66ce7891ce743c70a9d0aa68 (commit)
via 59903362374b1fca9612fe5a872579dd4a4d9873 (commit)
from 1dc0f0871d7045e8fb57bb0767fc31a2f7e3fe80 (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 10e87104e0b7977804c8c55275a403602dcbb2e5
Author: Marcin Siodelski <marcin at isc.org>
Date: Mon Dec 3 14:05:10 2012 +0100
[2491] Added function to add new integer data field to an option.
commit 6b59d1bc4fe7976ea6c1cf7820649824b4135e07
Author: Marcin Siodelski <marcin at isc.org>
Date: Mon Dec 3 13:26:14 2012 +0100
[2491] Added a function to add boolean data field to an array.
commit 5c50504446f431198d329d31b62e076d65e3eacb
Author: Marcin Siodelski <marcin at isc.org>
Date: Mon Dec 3 13:10:22 2012 +0100
[2491] Add IP address into an option being array of IP addresses.
commit 594916b524ff40cd66ce7891ce743c70a9d0aa68
Author: Marcin Siodelski <marcin at isc.org>
Date: Mon Dec 3 11:01:15 2012 +0100
[2491] Do not override FQDN until it is validated.
commit 59903362374b1fca9612fe5a872579dd4a4d9873
Author: Marcin Siodelski <marcin at isc.org>
Date: Mon Dec 3 10:45:32 2012 +0100
[2491] Added a function to write fqdn into a buffer.
-----------------------------------------------------------------------
Summary of changes:
src/lib/dhcp/option_custom.cc | 73 ++++++++--
src/lib/dhcp/option_custom.h | 61 ++++++++-
src/lib/dhcp/option_data_types.cc | 2 +-
src/lib/dhcp/tests/option_custom_unittest.cc | 184 +++++++++++++++++++++++++-
4 files changed, 300 insertions(+), 20 deletions(-)
-----------------------------------------------------------------------
diff --git a/src/lib/dhcp/option_custom.cc b/src/lib/dhcp/option_custom.cc
index 11e124c..3aece6c 100644
--- a/src/lib/dhcp/option_custom.cc
+++ b/src/lib/dhcp/option_custom.cc
@@ -45,10 +45,38 @@ OptionCustom::OptionCustom(const OptionDefinition& def,
}
void
+OptionCustom::addArrayDataField(const asiolink::IOAddress& address) {
+ checkArrayType();
+
+ if ((address.getFamily() == AF_INET &&
+ definition_.getType() != OPT_IPV4_ADDRESS_TYPE) ||
+ (address.getFamily() == AF_INET6 &&
+ definition_.getType() != OPT_IPV6_ADDRESS_TYPE)) {
+ isc_throw(BadDataTypeCast, "invalid address specified "
+ << address.toText() << ". Expected a valid IPv"
+ << (definition_.getType() == OPT_IPV4_ADDRESS_TYPE ? "4" : "6")
+ << " address.");
+ }
+
+ OptionBuffer buf;
+ OptionDataTypeUtil::writeAddress(address, buf);
+ buffers_.push_back(buf);
+}
+
+void
+OptionCustom::addArrayDataField(const bool value) {
+ checkArrayType();
+
+ OptionBuffer buf;
+ OptionDataTypeUtil::writeBool(value, buf);
+ buffers_.push_back(buf);
+}
+
+void
OptionCustom::checkIndex(const uint32_t index) const {
if (index >= buffers_.size()) {
isc_throw(isc::OutOfRange, "specified data field index " << index
- << " is out of rangex.");
+ << " is out of range.");
}
}
@@ -77,7 +105,7 @@ OptionCustom::checkDataType(const uint32_t index) const {
if (OptionDataTypeTraits<T>::type != data_type) {
isc_throw(isc::dhcp::InvalidDataType,
"specified data type " << data_type << " does not"
- "match data type in an option definition for field"
+ " match the data type in an option definition for field"
" index " << index);
}
}
@@ -101,7 +129,7 @@ OptionCustom::createBuffers() {
if (data_size == 0 &&
*field == OPT_FQDN_TYPE) {
- OptionDataTypeUtil::writeFqdn(".", buf);
+ OptionDataTypeUtil::writeFqdn(".", buf);
} else {
buf.resize(data_size);
}
@@ -114,8 +142,9 @@ OptionCustom::createBuffers() {
if (data_size == 0 &&
data_type == OPT_FQDN_TYPE) {
OptionDataTypeUtil::writeFqdn(".", buf);
+ } else {
+ buf.resize(data_size);
}
- buf.resize(data_size);
buffers.push_back(buf);
}
std::swap(buffers, buffers_);
@@ -358,7 +387,8 @@ OptionCustom::readAddress(const uint32_t index) const {
return (OptionDataTypeUtil::readAddress(buffers_[index], AF_INET6));
} else {
isc_throw(BadDataTypeCast, "unable to read data from the buffer as"
- << " IP address. Invalid buffer length " << buffers_[index].size());
+ << " IP address. Invalid buffer length "
+ << buffers_[index].size() << ".");
}
}
@@ -414,12 +444,25 @@ OptionCustom::writeBoolean(const bool value, const uint32_t index) {
std::string
OptionCustom::readFqdn(const uint32_t index) const {
checkIndex(index);
- try {
- size_t len = 0;
- return (OptionDataTypeUtil::readFqdn(buffers_[index], len));
- } catch (const Exception& ex) {
- isc_throw(BadDataTypeCast, ex.what());
- }
+ size_t len = 0;
+ return (OptionDataTypeUtil::readFqdn(buffers_[index], len));
+}
+
+void
+OptionCustom::writeFqdn(const std::string& fqdn, const uint32_t index) {
+ checkIndex(index);
+
+ // Create a temporay buffer where the FQDN will be written.
+ OptionBuffer buf;
+ // Try to write to the temporary buffer rather than to the
+ // buffers_ member directly guarantees that we don't modify
+ // (clear) buffers_ until we are sure that the provided FQDN
+ // is valid.
+ OptionDataTypeUtil::writeFqdn(fqdn, buf);
+ // If we got to this point it means that the FQDN is valid.
+ // We can move the contents of the teporary buffer to the
+ // target buffer.
+ std::swap(buffers_[index], buf);
}
std::string
@@ -432,13 +475,17 @@ void
OptionCustom::writeString(const std::string& text, const uint32_t index) {
checkIndex(index);
+ // Let's clear a buffer as we want to replace the value of the
+ // whole buffer. If we fail to clear the buffer the data will
+ // be appended.
+ buffers_[index].clear();
+ // If the text value is empty we can leave because the buffer
+ // is already empty.
if (!text.empty()) {
- buffers_[index].clear();
OptionDataTypeUtil::writeString(text, buffers_[index]);
}
}
-
void
OptionCustom::unpack(OptionBufferConstIter begin,
OptionBufferConstIter end) {
diff --git a/src/lib/dhcp/option_custom.h b/src/lib/dhcp/option_custom.h
index 7be9ab5..a3d16ef 100644
--- a/src/lib/dhcp/option_custom.h
+++ b/src/lib/dhcp/option_custom.h
@@ -87,6 +87,37 @@ public:
OptionCustom(const OptionDefinition& def, Universe u,
OptionBufferConstIter first, OptionBufferConstIter last);
+ /// @brief Create new buffer and set its value as an IP address.
+ ///
+ /// @param address IPv4 or IPv6 address to be written to
+ /// a buffer being created.
+ void addArrayDataField(const asiolink::IOAddress& address);
+
+ /// @brief Create new buffer and store boolean value in it.
+ ///
+ /// @param value value to be stored in the created buffer.
+ void addArrayDataField(const bool value);
+
+ /// @brief Create new buffer and store integer value in it.
+ ///
+ /// @param value value to be stored in the created buffer.
+ /// @tparam T integer type of the value being stored.
+ template<typename T>
+ void addArrayDataField(const T value) {
+ checkArrayType();
+
+ OptionDataType data_type = definition_.getType();
+ if (OptionDataTypeTraits<T>::type != data_type) {
+ isc_throw(isc::dhcp::InvalidDataType,
+ "specified data type " << data_type << " does not"
+ " match the data type in an option definition");
+ }
+
+ OptionBuffer buf;
+ OptionDataTypeUtil::writeInt<T>(value, buf);
+ buffers_.push_back(buf);
+ }
+
/// @brief Return a number of the data fields.
///
/// @return number of data fields held by the option.
@@ -151,6 +182,14 @@ public:
/// @return string representation if FQDN.
std::string readFqdn(const uint32_t index = 0) const;
+ /// @brief Write an FQDN into a buffer.
+ ///
+ /// @param fqdn text representation of FQDN.
+ /// @param index buffer index.
+ ///
+ /// @throw isc::OutOfRange if index is out of range.
+ void writeFqdn(const std::string& fqdn, const uint32_t index = 0);
+
/// @brief Read a buffer as integer value.
///
/// @param index buffer index.
@@ -254,12 +293,19 @@ protected:
private:
- /// @brief Check if data field index is valid.
+ /// @brief Verify that the option comprises an array of values.
///
- /// @param index Data field index to check.
+ /// This helper function is used by createArrayEntry functions
+ /// and throws an exception if the particular option is not
+ /// an array.
///
- /// @throw isc::OutOfRange if index is out of range.
- void checkIndex(const uint32_t index) const;
+ /// @throw isc::InvalidOperation if option is not an array.
+ inline void checkArrayType() const {
+ if (!definition_.getArrayType()) {
+ isc_throw(InvalidOperation, "failed to add new array entry to an"
+ << " option. The option is not an array.");
+ }
+ }
/// @brief Verify that the integer type is consistent with option
/// field type.
@@ -274,6 +320,13 @@ private:
template<typename T>
void checkDataType(const uint32_t index) const;
+ /// @brief Check if data field index is valid.
+ ///
+ /// @param index Data field index to check.
+ ///
+ /// @throw isc::OutOfRange if index is out of range.
+ void checkIndex(const uint32_t index) const;
+
/// @brief Create a collection of non initialized buffers.
void createBuffers();
diff --git a/src/lib/dhcp/option_data_types.cc b/src/lib/dhcp/option_data_types.cc
index ad1491d..459dea7 100644
--- a/src/lib/dhcp/option_data_types.cc
+++ b/src/lib/dhcp/option_data_types.cc
@@ -216,7 +216,7 @@ 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");
+ << " 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
diff --git a/src/lib/dhcp/tests/option_custom_unittest.cc b/src/lib/dhcp/tests/option_custom_unittest.cc
index 16148ee..5b8cf79 100644
--- a/src/lib/dhcp/tests/option_custom_unittest.cc
+++ b/src/lib/dhcp/tests/option_custom_unittest.cc
@@ -517,7 +517,6 @@ TEST_F(OptionCustomTest, uint32DataArray) {
buf.begin() + 3)),
isc::OutOfRange
);
-
}
// The purpose of this test is to verify that the option definition comprising
@@ -944,12 +943,193 @@ TEST_F(OptionCustomTest, setStringData) {
// By default the string data field is empty.
EXPECT_TRUE(value.empty());
// Write some text to this field.
- EXPECT_NO_THROW(option->writeString("hello world"));
+ ASSERT_NO_THROW(option->writeString("hello world"));
// Check that it has been actually written.
EXPECT_NO_THROW(value = option->readString());
EXPECT_EQ("hello world", value);
}
+/// The purpose of this test is to verify that an option comprising
+/// a default FQDN value can be created and that this value can be
+/// overriden after the option has been created.
+TEST_F(OptionCustomTest, setFqdnData) {
+ OptionDefinition opt_def("OPTION_FOO", 1000, "fqdn");
+
+ // Create an option and let the data field be initialized
+ // to default value (do not provide any data buffer).
+ boost::scoped_ptr<OptionCustom> option;
+ ASSERT_NO_THROW(
+ option.reset(new OptionCustom(opt_def, Option::V6));
+ );
+ ASSERT_TRUE(option);
+ // Read a default FQDN value from the option.
+ std::string fqdn;
+ ASSERT_NO_THROW(fqdn = option->readFqdn());
+ EXPECT_EQ(".", fqdn);
+ // Try override the default FQDN value.
+ ASSERT_NO_THROW(option->writeFqdn("example.com"));
+ // Check that the value has been actually overriden.
+ ASSERT_NO_THROW(fqdn = option->readFqdn());
+ EXPECT_EQ("example.com.", fqdn);
+}
+
+// The purpose of this test is to verify that an option carrying
+// an array of boolean values can be created with no values
+// initially and that values can be later added to it.
+TEST_F(OptionCustomTest, setBooleanDataArray) {
+ OptionDefinition opt_def("OPTION_FOO", 1000, "boolean", true);
+
+ // Create an option and let the data field be initialized
+ // to default value (do not provide any data buffer).
+ boost::scoped_ptr<OptionCustom> option;
+ ASSERT_NO_THROW(
+ option.reset(new OptionCustom(opt_def, Option::V6));
+ );
+ ASSERT_TRUE(option);
+
+ // Initially, the array should contain no values.
+ ASSERT_EQ(0, option->getDataFieldsNum());
+
+ // Add some boolean values to it.
+ ASSERT_NO_THROW(option->addArrayDataField(true));
+ ASSERT_NO_THROW(option->addArrayDataField(false));
+ ASSERT_NO_THROW(option->addArrayDataField(true));
+
+ // Verify that the new data fields can be added.
+ bool value0 = false;
+ ASSERT_NO_THROW(value0 = option->readBoolean(0));
+ EXPECT_TRUE(value0);
+ bool value1 = true;
+ ASSERT_NO_THROW(value1 = option->readBoolean(1));
+ EXPECT_FALSE(value1);
+ bool value2 = false;
+ ASSERT_NO_THROW(value2 = option->readBoolean(2));
+ EXPECT_TRUE(value2);
+}
+
+// The purpose of this test is to verify that am option carying
+// an array of 16-bit signed integer values can be created with
+// no values initially and that the values can be later added to it.
+TEST_F(OptionCustomTest, setUint16DataArray) {
+ OptionDefinition opt_def("OPTION_FOO", 1000, "uint16", true);
+
+ // Create an option and let the data field be initialized
+ // to default value (do not provide any data buffer).
+ boost::scoped_ptr<OptionCustom> option;
+ ASSERT_NO_THROW(
+ option.reset(new OptionCustom(opt_def, Option::V6));
+ );
+ ASSERT_TRUE(option);
+
+ // Initially, the array should contain no values.
+ ASSERT_EQ(0, option->getDataFieldsNum());
+
+ // Add 3 new data fields holding integer values.
+ ASSERT_NO_THROW(option->addArrayDataField<uint16_t>(67));
+ ASSERT_NO_THROW(option->addArrayDataField<uint16_t>(876));
+ ASSERT_NO_THROW(option->addArrayDataField<uint16_t>(32222));
+
+ // We should now have 3 data fields.
+ ASSERT_EQ(3, option->getDataFieldsNum());
+
+ // Check that the values have been correctly set.
+ uint16_t value0;
+ ASSERT_NO_THROW(value0 = option->readInteger<uint16_t>(0));
+ EXPECT_EQ(67, value0);
+ uint16_t value1;
+ ASSERT_NO_THROW(value1 = option->readInteger<uint16_t>(1));
+ EXPECT_EQ(876, value1);
+ uint16_t value2;
+ ASSERT_NO_THROW(value2 = option->readInteger<uint16_t>(2));
+ EXPECT_EQ(32222, value2);
+}
+
+/// The purpose of this test is to verify that an option comprising
+/// array of IPv4 address can be created with no addresses and that
+/// multiple IPv4 addresses can be added to it after creation.
+TEST_F(OptionCustomTest, setIpv4AddressDataArray) {
+ OptionDefinition opt_def("OPTION_FOO", 232, "ipv4-address", true);
+
+ // Create an option and let the data field be initialized
+ // to default value (do not provide any data buffer).
+ boost::scoped_ptr<OptionCustom> option;
+ ASSERT_NO_THROW(
+ option.reset(new OptionCustom(opt_def, Option::V4));
+ );
+ ASSERT_TRUE(option);
+
+ // Expect that the array does not contain any data fields yet.
+ ASSERT_EQ(0, option->getDataFieldsNum());
+
+ // Add 3 IPv4 addresses.
+ ASSERT_NO_THROW(option->addArrayDataField(IOAddress("192.168.0.1")));
+ ASSERT_NO_THROW(option->addArrayDataField(IOAddress("192.168.0.2")));
+ ASSERT_NO_THROW(option->addArrayDataField(IOAddress("192.168.0.3")));
+
+ ASSERT_EQ(3, option->getDataFieldsNum());
+
+ // Check that all IP addresses have been set correctly.
+ IOAddress address0("127.0.0.1");
+ ASSERT_NO_THROW(address0 = option->readAddress(0));
+ EXPECT_EQ("192.168.0.1", address0.toText());
+ IOAddress address1("127.0.0.1");
+ ASSERT_NO_THROW(address1 = option->readAddress(1));
+ EXPECT_EQ("192.168.0.2", address1.toText());
+ IOAddress address2("127.0.0.1");
+ ASSERT_NO_THROW(address2 = option->readAddress(2));
+ EXPECT_EQ("192.168.0.3", address2.toText());
+
+ // Add invalid address (IPv6 instead of IPv4).
+ EXPECT_THROW(
+ option->addArrayDataField(IOAddress("2001:db8:1::1")),
+ isc::dhcp::BadDataTypeCast
+ );
+}
+
+/// The purpose of this test is to verify that an option comprising
+/// array of IPv6 address can be created with no addresses and that
+/// multiple IPv6 addresses can be added to it after creation.
+TEST_F(OptionCustomTest, setIpv6AddressDataArray) {
+ OptionDefinition opt_def("OPTION_FOO", 1000, "ipv6-address", true);
+
+ // Create an option and let the data field be initialized
+ // to default value (do not provide any data buffer).
+ boost::scoped_ptr<OptionCustom> option;
+ ASSERT_NO_THROW(
+ option.reset(new OptionCustom(opt_def, Option::V6));
+ );
+ ASSERT_TRUE(option);
+
+ // Initially, the array does not contain any data fields.
+ ASSERT_EQ(0, option->getDataFieldsNum());
+
+ // Add 3 new IPv6 addresses into the array.
+ ASSERT_NO_THROW(option->addArrayDataField(IOAddress("2001:db8:1::1")));
+ ASSERT_NO_THROW(option->addArrayDataField(IOAddress("2001:db8:1::2")));
+ ASSERT_NO_THROW(option->addArrayDataField(IOAddress("2001:db8:1::3")));
+
+ // We should have now 3 addresses added.
+ ASSERT_EQ(3, option->getDataFieldsNum());
+
+ // Check that they have correct values set.
+ IOAddress address0("::1");
+ ASSERT_NO_THROW(address0 = option->readAddress(0));
+ EXPECT_EQ("2001:db8:1::1", address0.toText());
+ IOAddress address1("::1");
+ ASSERT_NO_THROW(address1 = option->readAddress(1));
+ EXPECT_EQ("2001:db8:1::2", address1.toText());
+ IOAddress address2("::1");
+ ASSERT_NO_THROW(address2 = option->readAddress(2));
+ EXPECT_EQ("2001:db8:1::3", address2.toText());
+
+ // Add invalid address (IPv4 instead of IPv6).
+ EXPECT_THROW(
+ option->addArrayDataField(IOAddress("192.168.0.1")),
+ isc::dhcp::BadDataTypeCast
+ );
+}
+
+
// The purpose of this test is to verify that pack function for
// DHCPv4 custom option works correctly.
TEST_F(OptionCustomTest, pack4) {
More information about the bind10-changes
mailing list