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:
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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));
|
||||
|
@@ -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
|
||||
|
Reference in New Issue
Block a user