2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-09-05 00:15:17 +00:00

[3036] Return instance of Option6ClientFqdn for option 39.

Also folded long lines in OptionDefinition implementation.
This commit is contained in:
Marcin Siodelski
2013-07-18 17:01:11 +02:00
parent 8b1aa00503
commit d803eeaf3f
4 changed files with 102 additions and 40 deletions

View File

@@ -17,6 +17,7 @@
#include <dhcp/option6_addrlst.h>
#include <dhcp/option6_ia.h>
#include <dhcp/option6_iaaddr.h>
#include <dhcp/option6_client_fqdn.h>
#include <dhcp/option_custom.h>
#include <dhcp/option_definition.h>
#include <dhcp/option_int.h>
@@ -101,7 +102,8 @@ OptionDefinition::addRecordField(const OptionDataType data_type) {
if (data_type >= OPT_RECORD_TYPE ||
data_type == OPT_ANY_ADDRESS_TYPE ||
data_type == OPT_EMPTY_TYPE) {
isc_throw(isc::BadValue, "attempted to add invalid data type to the record.");
isc_throw(isc::BadValue,
"attempted to add invalid data type to the record.");
}
record_fields_.push_back(data_type);
}
@@ -127,19 +129,23 @@ OptionDefinition::optionFactory(Option::Universe u, uint16_t type,
factoryInteger<int8_t>(u, type, begin, end));
case OPT_UINT16_TYPE:
return (array_type_ ? factoryIntegerArray<uint16_t>(u, type, begin, end) :
return (array_type_ ?
factoryIntegerArray<uint16_t>(u, type, begin, end) :
factoryInteger<uint16_t>(u, type, begin, end));
case OPT_INT16_TYPE:
return (array_type_ ? factoryIntegerArray<uint16_t>(u, type, begin, end) :
return (array_type_ ?
factoryIntegerArray<uint16_t>(u, type, begin, end) :
factoryInteger<int16_t>(u, type, begin, end));
case OPT_UINT32_TYPE:
return (array_type_ ? factoryIntegerArray<uint32_t>(u, type, begin, end) :
return (array_type_ ?
factoryIntegerArray<uint32_t>(u, type, begin, end) :
factoryInteger<uint32_t>(u, type, begin, end));
case OPT_INT32_TYPE:
return (array_type_ ? factoryIntegerArray<uint32_t>(u, type, begin, end) :
return (array_type_ ?
factoryIntegerArray<uint32_t>(u, type, begin, end) :
factoryInteger<int32_t>(u, type, begin, end));
case OPT_IPV4_ADDRESS_TYPE:
@@ -183,6 +189,10 @@ OptionDefinition::optionFactory(Option::Universe u, uint16_t type,
// option only for the same reasons as described in
// for IA_NA and IA_PD above.
return (factoryIAAddr6(type, begin, end));
} else if (code_ == D6O_CLIENT_FQDN && haveClientFqdnFormat()) {
// FQDN option requires special processing. Thus, there is
// a specialized class to handle it.
return (OptionPtr(new Option6ClientFqdn(begin, end)));
}
}
}
@@ -264,10 +274,12 @@ OptionDefinition::validate() const {
// it no way to tell when other data fields begin.
err_str << "array of strings is not a valid option definition.";
} else if (type_ == OPT_BINARY_TYPE) {
err_str << "array of binary values is not a valid option definition.";
err_str << "array of binary values is not"
<< " a valid option definition.";
} else if (type_ == OPT_EMPTY_TYPE) {
err_str << "array of empty value is not a valid option definition.";
err_str << "array of empty value is not"
<< " a valid option definition.";
}
@@ -275,33 +287,34 @@ OptionDefinition::validate() const {
// At least two data fields should be added to the record. Otherwise
// non-record option definition could be used.
if (getRecordFields().size() < 2) {
err_str << "invalid number of data fields: " << getRecordFields().size()
err_str << "invalid number of data fields: "
<< getRecordFields().size()
<< " specified for the option of type 'record'. Expected at"
<< " least 2 fields.";
} else {
// If the number of fields is valid we have to check if their order
// is valid too. We check that string or binary data fields are not
// laid before other fields. But we allow that they are laid at the end of
// an option.
// laid before other fields. But we allow that they are laid at the
// end of an option.
const RecordFieldsCollection& fields = getRecordFields();
for (RecordFieldsConstIter it = fields.begin();
it != fields.end(); ++it) {
if (*it == OPT_STRING_TYPE &&
it < fields.end() - 1) {
err_str << "string data field can't be laid before data fields"
<< " of other types.";
err_str << "string data field can't be laid before data"
<< " fields of other types.";
break;
}
if (*it == OPT_BINARY_TYPE &&
it < fields.end() - 1) {
err_str << "binary data field can't be laid before data fields"
<< " of other types.";
err_str << "binary data field can't be laid before data"
<< " fields of other types.";
}
/// Empty type is not allowed within a record.
if (*it == OPT_EMPTY_TYPE) {
err_str << "empty data type can't be stored as a field in an"
<< " option record.";
err_str << "empty data type can't be stored as a field in"
<< " an option record.";
break;
}
}
@@ -341,13 +354,24 @@ OptionDefinition::haveIAAddr6Format() const {
return (haveIAx6Format(OPT_IPV6_ADDRESS_TYPE));
}
bool
OptionDefinition::haveClientFqdnFormat() const {
return (haveType(OPT_RECORD_TYPE) &&
(record_fields_.size() == 2) &&
(record_fields_[0] == OPT_UINT8_TYPE) &&
(record_fields_[1] == OPT_FQDN_TYPE));
}
template<typename T>
T OptionDefinition::lexicalCastWithRangeCheck(const std::string& value_str) const {
T
OptionDefinition::lexicalCastWithRangeCheck(const std::string& value_str)
const {
// Lexical cast in case of our data types make sense only
// for uintX_t, intX_t and bool type.
if (!OptionDataTypeTraits<T>::integer_type &&
OptionDataTypeTraits<T>::type != OPT_BOOLEAN_TYPE) {
isc_throw(BadDataTypeCast, "unable to do lexical cast to non-integer and"
isc_throw(BadDataTypeCast,
"unable to do lexical cast to non-integer and"
<< " non-boolean data type");
}
// We use the 64-bit value here because it has wider range than
@@ -364,16 +388,18 @@ T OptionDefinition::lexicalCastWithRangeCheck(const std::string& value_str) cons
if (OptionDataTypeTraits<T>::integer_type) {
data_type_str = "integer";
}
isc_throw(BadDataTypeCast, "unable to do lexical cast to " << data_type_str
<< " data type for value " << value_str << ": " << ex.what());
isc_throw(BadDataTypeCast, "unable to do lexical cast to "
<< data_type_str << " data type for value "
<< value_str << ": " << ex.what());
}
// Perform range checks for integer values only (exclude bool values).
if (OptionDataTypeTraits<T>::integer_type) {
if (result > numeric_limits<T>::max() ||
result < numeric_limits<T>::min()) {
isc_throw(BadDataTypeCast, "unable to do lexical cast for value "
<< value_str << ". This value is expected to be in the range of "
<< numeric_limits<T>::min() << ".." << numeric_limits<T>::max());
<< value_str << ". This value is expected to be"
<< " in the range of " << numeric_limits<T>::min()
<< ".." << numeric_limits<T>::max());
}
}
return (static_cast<T>(result));
@@ -395,30 +421,37 @@ OptionDefinition::writeToBuffer(const std::string& value,
// That way we actually waste 7 bits but it seems to be the
// simpler way to encode boolean.
// @todo Consider if any other encode methods can be used.
OptionDataTypeUtil::writeBool(lexicalCastWithRangeCheck<bool>(value), buf);
OptionDataTypeUtil::writeBool(lexicalCastWithRangeCheck<bool>(value),
buf);
return;
case OPT_INT8_TYPE:
OptionDataTypeUtil::writeInt<uint8_t>(lexicalCastWithRangeCheck<int8_t>(value),
OptionDataTypeUtil::writeInt<uint8_t>
(lexicalCastWithRangeCheck<int8_t>(value),
buf);
return;
case OPT_INT16_TYPE:
OptionDataTypeUtil::writeInt<uint16_t>(lexicalCastWithRangeCheck<int16_t>(value),
OptionDataTypeUtil::writeInt<uint16_t>
(lexicalCastWithRangeCheck<int16_t>(value),
buf);
return;
case OPT_INT32_TYPE:
OptionDataTypeUtil::writeInt<uint32_t>(lexicalCastWithRangeCheck<int32_t>(value),
OptionDataTypeUtil::writeInt<uint32_t>
(lexicalCastWithRangeCheck<int32_t>(value),
buf);
return;
case OPT_UINT8_TYPE:
OptionDataTypeUtil::writeInt<uint8_t>(lexicalCastWithRangeCheck<uint8_t>(value),
OptionDataTypeUtil::writeInt<uint8_t>
(lexicalCastWithRangeCheck<uint8_t>(value),
buf);
return;
case OPT_UINT16_TYPE:
OptionDataTypeUtil::writeInt<uint16_t>(lexicalCastWithRangeCheck<uint16_t>(value),
OptionDataTypeUtil::writeInt<uint16_t>
(lexicalCastWithRangeCheck<uint16_t>(value),
buf);
return;
case OPT_UINT32_TYPE:
OptionDataTypeUtil::writeInt<uint32_t>(lexicalCastWithRangeCheck<uint32_t>(value),
OptionDataTypeUtil::writeInt<uint32_t>
(lexicalCastWithRangeCheck<uint32_t>(value),
buf);
return;
case OPT_IPV4_ADDRESS_TYPE:
@@ -426,7 +459,8 @@ OptionDefinition::writeToBuffer(const std::string& value,
{
asiolink::IOAddress address(value);
if (!address.isV4() && !address.isV6()) {
isc_throw(BadDataTypeCast, "provided address " << address.toText()
isc_throw(BadDataTypeCast, "provided address "
<< address.toText()
<< " is not a valid IPv4 or IPv6 address.");
}
OptionDataTypeUtil::writeAddress(address, buf);
@@ -454,7 +488,8 @@ OptionPtr
OptionDefinition::factoryAddrList4(uint16_t type,
OptionBufferConstIter begin,
OptionBufferConstIter end) {
boost::shared_ptr<Option4AddrLst> option(new Option4AddrLst(type, begin, end));
boost::shared_ptr<Option4AddrLst> option(new Option4AddrLst(type, begin,
end));
return (option);
}
@@ -462,7 +497,8 @@ OptionPtr
OptionDefinition::factoryAddrList6(uint16_t type,
OptionBufferConstIter begin,
OptionBufferConstIter end) {
boost::shared_ptr<Option6AddrLst> option(new Option6AddrLst(type, begin, end));
boost::shared_ptr<Option6AddrLst> option(new Option6AddrLst(type, begin,
end));
return (option);
}
@@ -486,8 +522,9 @@ OptionDefinition::factoryIA6(uint16_t type,
OptionBufferConstIter begin,
OptionBufferConstIter end) {
if (std::distance(begin, end) < Option6IA::OPTION6_IA_LEN) {
isc_throw(isc::OutOfRange, "input option buffer has invalid size, expected "
"at least " << Option6IA::OPTION6_IA_LEN << " bytes");
isc_throw(isc::OutOfRange, "input option buffer has invalid size,"
<< " expected at least " << Option6IA::OPTION6_IA_LEN
<< " bytes");
}
boost::shared_ptr<Option6IA> option(new Option6IA(type, begin, end));
return (option);
@@ -498,10 +535,12 @@ OptionDefinition::factoryIAAddr6(uint16_t type,
OptionBufferConstIter begin,
OptionBufferConstIter end) {
if (std::distance(begin, end) < Option6IAAddr::OPTION6_IAADDR_LEN) {
isc_throw(isc::OutOfRange, "input option buffer has invalid size, expected "
" at least " << Option6IAAddr::OPTION6_IAADDR_LEN << " bytes");
isc_throw(isc::OutOfRange,
"input option buffer has invalid size, expected at least "
<< Option6IAAddr::OPTION6_IAADDR_LEN << " bytes");
}
boost::shared_ptr<Option6IAAddr> option(new Option6IAAddr(type, begin, end));
boost::shared_ptr<Option6IAAddr> option(new Option6IAAddr(type, begin,
end));
return (option);
}

View File

@@ -275,6 +275,12 @@ public:
/// @return true if specified format is IAADDR option format.
bool haveIAAddr6Format() const;
/// @brief Check if specified format is OPTION_CLIENT_FQDN option format.
///
/// @return true of specified format is OPTION_CLIENT_FQDN option format,
/// false otherwise.
bool haveClientFqdnFormat() const;
/// @brief Option factory.
///
/// This function creates an instance of DHCP option using

View File

@@ -19,6 +19,7 @@
#include <dhcp/libdhcp++.h>
#include <dhcp/option4_addrlst.h>
#include <dhcp/option6_addrlst.h>
#include <dhcp/option6_client_fqdn.h>
#include <dhcp/option6_ia.h>
#include <dhcp/option6_iaaddr.h>
#include <dhcp/option_custom.h>
@@ -905,7 +906,8 @@ TEST_F(LibDhcpTest, stdOptionDefs6) {
typeid(Option));
LibDhcpTest::testStdOptionDefs6(D6O_CLIENT_FQDN, client_fqdn_buf.begin(),
client_fqdn_buf.end(), typeid(OptionCustom));
client_fqdn_buf.end(),
typeid(Option6ClientFqdn));
LibDhcpTest::testStdOptionDefs6(D6O_PANA_AGENT, begin, end,
typeid(Option6AddrLst));

View File

@@ -928,7 +928,7 @@ TEST_F(OptionDefinitionTest, utf8StringTokenized) {
// Let's create some dummy option.
const uint16_t opt_code = 80;
OptionDefinition opt_def("OPTION_WITH_STRING", opt_code, "string");
std::vector<std::string> values;
values.push_back("Hello World");
values.push_back("this string should not be included in the option");
@@ -960,7 +960,7 @@ TEST_F(OptionDefinitionTest, integerInvalidType) {
// The purpose of this test is to verify that helper methods
// haveIA6Format and haveIAAddr6Format can be used to determine
// IA_NA and IAADDR option formats.
TEST_F(OptionDefinitionTest, recognizeFormat) {
TEST_F(OptionDefinitionTest, haveIAFormat) {
// IA_NA option format.
OptionDefinition opt_def1("OPTION_IA_NA", D6O_IA_NA, "record");
for (int i = 0; i < 3; ++i) {
@@ -984,4 +984,19 @@ TEST_F(OptionDefinitionTest, recognizeFormat) {
EXPECT_FALSE(opt_def4.haveIAAddr6Format());
}
// This test verifies that haveClientFqdnFormat function recognizes that option
// definition describes the format of DHCPv6 Client Fqdn Option Format.
TEST_F(OptionDefinitionTest, haveClientFqdnFormat) {
OptionDefinition opt_def("OPTION_CLIENT_FQDN", D6O_CLIENT_FQDN, "record");
opt_def.addRecordField("uint8");
opt_def.addRecordField("fqdn");
EXPECT_TRUE(opt_def.haveClientFqdnFormat());
// Create option format which is not matching the Client FQDN option format
// to verify that tested function does dont always return true.
OptionDefinition opt_def_invalid("OPTION_CLIENT_FQDN", D6O_CLIENT_FQDN,
"uint8");
EXPECT_FALSE(opt_def_invalid.haveClientFqdnFormat());
}
} // anonymous namespace