BIND 10 master, updated. 56cfd6612fcaeae9acec4a94e1e5f1a88142c44d [master] Merge branch 'trac2490'
BIND 10 source code commits
bind10-changes at lists.isc.org
Mon Nov 26 18:50:19 UTC 2012
The branch, master has been updated
via 56cfd6612fcaeae9acec4a94e1e5f1a88142c44d (commit)
via 139f66086e6cd02bfbf3b9fd0f55761ae4df3aac (commit)
via c84d8a4bcf3058d49c88962744102cb3efa7aa2c (commit)
via 9f48afa5e955391ea42332f2b6cc0d8f4757b438 (commit)
via b4dd93598f4e795c6cac151bc26caa56975fa835 (commit)
via 1eba6a3eb2b7277635af75688c0d56874d0a5f4f (commit)
via 9e4d4dc41860e8c370c10695ffaa5dc7f30f059c (commit)
via 35c3fb162500afbfaab41533d160629a3f48017f (commit)
via ce7728b0d2d217cb0299b1a5b54470ed8c13148d (commit)
via 648f693c9e524c49b0ad1e53096664fb5d162f4a (commit)
via b24fd3ded0a466be9eac27617952728e44149695 (commit)
via e77b710633d5cf1d34dea5153a0f77b7d1f111b5 (commit)
via b4b3814079cf33af52d9ab257464ef0685894b82 (commit)
from ca25d3c2eba98609d20cd646da422d6297e4f818 (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 56cfd6612fcaeae9acec4a94e1e5f1a88142c44d
Merge: ca25d3c 139f660
Author: Marcin Siodelski <marcin at isc.org>
Date: Mon Nov 26 18:47:46 2012 +0100
[master] Merge branch 'trac2490'
-----------------------------------------------------------------------
Summary of changes:
src/bin/dhcp6/config_parser.cc | 5 +-
src/bin/dhcp6/dhcp6_srv.cc | 11 -
src/bin/dhcp6/dhcp6_srv.h | 11 -
src/bin/dhcp6/tests/config_parser_unittest.cc | 5 -
src/lib/dhcp/libdhcp++.cc | 116 ++--
src/lib/dhcp/libdhcp++.h | 26 +-
src/lib/dhcp/option6_int.h | 12 +-
src/lib/dhcp/option6_int_array.h | 14 +-
src/lib/dhcp/option_data_types.h | 105 +++-
src/lib/dhcp/option_definition.cc | 480 ++++++++++++----
src/lib/dhcp/option_definition.h | 243 +++++---
src/lib/dhcp/tests/libdhcp++_unittest.cc | 155 +++---
src/lib/dhcp/tests/option6_ia_unittest.cc | 2 +-
src/lib/dhcp/tests/option_definition_unittest.cc | 643 ++++++++++++++++------
14 files changed, 1290 insertions(+), 538 deletions(-)
-----------------------------------------------------------------------
diff --git a/src/bin/dhcp6/config_parser.cc b/src/bin/dhcp6/config_parser.cc
index d6f3afe..1c55649 100644
--- a/src/bin/dhcp6/config_parser.cc
+++ b/src/bin/dhcp6/config_parser.cc
@@ -698,11 +698,8 @@ private:
// We have exactly one option definition for the particular option code
// use it to create the option instance.
const OptionDefinitionPtr& def = *(range.first);
- // getFactory should never return NULL pointer.
- Option::Factory* factory = def->getFactory();
- assert(factory != NULL);
try {
- OptionPtr option = factory(Option::V6, option_code, binary);
+ OptionPtr option = def->optionFactory(Option::V6, option_code, binary);
Subnet::OptionDescriptor desc(option, false);
option_descriptor_.option = option;
option_descriptor_.persistent = false;
diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc
index 2a5798d..02c0dd0 100644
--- a/src/bin/dhcp6/dhcp6_srv.cc
+++ b/src/bin/dhcp6/dhcp6_srv.cc
@@ -56,12 +56,6 @@ Dhcpv6Srv::Dhcpv6Srv(uint16_t port, const char* dbconfig)
// Initialize objects required for DHCP server operation.
try {
- // Initialize standard DHCPv6 option definitions. This function
- // may throw bad_alloc if system goes out of memory during the
- // creation if option definitions. It may also throw isc::Unexpected
- // if definitions are wrong. This would mean error in implementation.
- initStdOptionDefs();
-
// Port 0 is used for testing purposes. It means that the server should
// not open any sockets at all. Some tests, e.g. configuration parser,
// require Dhcpv6Srv object, but they don't really need it to do
@@ -622,10 +616,5 @@ Dhcpv6Srv::serverReceivedPacketName(uint8_t type) {
return (UNKNOWN);
}
-void
-Dhcpv6Srv::initStdOptionDefs() {
- LibDHCP::initStdOptionDefs(Option::V6);
-}
-
};
};
diff --git a/src/bin/dhcp6/dhcp6_srv.h b/src/bin/dhcp6/dhcp6_srv.h
index b4ce8b1..de9ee36 100644
--- a/src/bin/dhcp6/dhcp6_srv.h
+++ b/src/bin/dhcp6/dhcp6_srv.h
@@ -241,17 +241,6 @@ protected:
/// interfaces for new DUID generation are detected.
void setServerID();
- /// @brief Initializes option definitions for standard options.
- ///
- /// Each standard option's format is described by the
- /// dhcp::OptionDefinition object. This function creates such objects
- /// for each standard DHCPv6 option.
- ///
- /// @todo list thrown exceptions.
- /// @todo extend this function to cover all standard options. Currently
- /// it is limited to critical options only.
- void initStdOptionDefs();
-
private:
/// @brief Allocation Engine.
/// Pointer to the allocation engine that we are currently using
diff --git a/src/bin/dhcp6/tests/config_parser_unittest.cc b/src/bin/dhcp6/tests/config_parser_unittest.cc
index 2064e72..4fd6baa 100644
--- a/src/bin/dhcp6/tests/config_parser_unittest.cc
+++ b/src/bin/dhcp6/tests/config_parser_unittest.cc
@@ -46,11 +46,6 @@ public:
// srv_(0) means to not open any sockets. We don't want to
// deal with sockets here, just check if configuration handling
// is sane.
-
- // Create instances of option definitions and put them into storage.
- // This is normally initialized by the server when calling run()
- // run() function.
- LibDHCP::initStdOptionDefs(Option::V6);
}
~Dhcp6ParserTest() {
diff --git a/src/lib/dhcp/libdhcp++.cc b/src/lib/dhcp/libdhcp++.cc
index 7fb88bf..a997d31 100644
--- a/src/lib/dhcp/libdhcp++.cc
+++ b/src/lib/dhcp/libdhcp++.cc
@@ -48,8 +48,12 @@ const OptionDefContainer&
LibDHCP::getOptionDefs(Option::Universe u) {
switch (u) {
case Option::V4:
+ initStdOptionDefs4();
return (v4option_defs_);
case Option::V6:
+ if (v6option_defs_.size() == 0) {
+ initStdOptionDefs6();
+ }
return (v6option_defs_);
default:
isc_throw(isc::BadValue, "invalid universe " << u << " specified");
@@ -96,30 +100,43 @@ size_t LibDHCP::unpackOptions6(const OptionBuffer& buf,
// @todo: consider throwing exception here.
return (offset);
}
+
+ // Get the list of stdandard option definitions.
+ OptionDefContainer option_defs = LibDHCP::getOptionDefs(Option::V6);
+ // Get the search index #1. It allows to search for option definitions
+ // using option code.
+ const OptionDefContainerTypeIndex& idx = option_defs.get<1>();
+ // Get all options with the particular option code. Note that option code
+ // is non-unique within this container however at this point we expect
+ // to get one option definition with the particular code. If more are
+ // returned we report an error.
+ const OptionDefContainerTypeRange& range = idx.equal_range(opt_type);
+ // Get the number of returned option definitions for the option code.
+ size_t num_defs = distance(range.first, range.second);
OptionPtr opt;
- switch (opt_type) {
- case D6O_IA_NA:
- case D6O_IA_PD:
- opt = OptionPtr(new Option6IA(opt_type,
- buf.begin() + offset,
- buf.begin() + offset + opt_len));
- break;
- case D6O_IAADDR:
- opt = OptionPtr(new Option6IAAddr(opt_type,
- buf.begin() + offset,
- buf.begin() + offset + opt_len));
- break;
- case D6O_ORO:
- opt = OptionPtr(new Option6IntArray<uint16_t>(opt_type,
- buf.begin() + offset,
- buf.begin() + offset + opt_len));
- break;
- default:
- opt = OptionPtr(new Option(Option::V6,
- opt_type,
+ if (num_defs > 1) {
+ // Multiple options of the same code are not supported right now!
+ isc_throw(isc::Unexpected, "Internal error: multiple option definitions"
+ " for option type " << opt_type << " returned. Currently it is not"
+ " supported to initialize multiple option definitions"
+ " for the same option code. This will be supported once"
+ " support for option spaces is implemented");
+ } else if (num_defs == 0) {
+ // @todo Don't crash if definition does not exist because only a few
+ // option definitions are initialized right now. In the future
+ // we will initialize definitions for all options and we will
+ // remove this elseif. For now, return generic option.
+ opt = OptionPtr(new Option(Option::V6, opt_type,
buf.begin() + offset,
buf.begin() + offset + opt_len));
- break;
+ } else {
+ // The option definition has been found. Use it to create
+ // the option instance from the provided buffer chunk.
+ const OptionDefinitionPtr& def = *(range.first);
+ assert(def);
+ opt = def->optionFactory(Option::V6, opt_type,
+ buf.begin() + offset,
+ buf.begin() + offset + opt_len);
}
// add option to options
options.insert(std::make_pair(opt_type, opt));
@@ -229,20 +246,6 @@ void LibDHCP::OptionFactoryRegister(Option::Universe u,
}
void
-LibDHCP::initStdOptionDefs(Option::Universe u) {
- switch (u) {
- case Option::V4:
- initStdOptionDefs4();
- break;
- case Option::V6:
- initStdOptionDefs6();
- break;
- default:
- isc_throw(isc::BadValue, "invalid universe " << u << " specified");
- }
-}
-
-void
LibDHCP::initStdOptionDefs4() {
isc_throw(isc::NotImplemented, "initStdOptionDefs4 is not implemented");
}
@@ -254,19 +257,20 @@ LibDHCP::initStdOptionDefs6() {
struct OptionParams {
std::string name;
uint16_t code;
- OptionDefinition::DataType type;
+ OptionDataType type;
bool array;
};
OptionParams params[] = {
- { "CLIENTID", D6O_CLIENTID, OptionDefinition::BINARY_TYPE, false },
- { "SERVERID", D6O_SERVERID, OptionDefinition::BINARY_TYPE, false },
- { "IA_NA", D6O_IA_NA, OptionDefinition::RECORD_TYPE, false },
- { "IAADDR", D6O_IAADDR, OptionDefinition::RECORD_TYPE, false },
- { "ORO", D6O_ORO, OptionDefinition::UINT16_TYPE, true },
- { "ELAPSED_TIME", D6O_ELAPSED_TIME, OptionDefinition::UINT16_TYPE, false },
- { "STATUS_CODE", D6O_STATUS_CODE, OptionDefinition::RECORD_TYPE, false },
- { "RAPID_COMMIT", D6O_RAPID_COMMIT, OptionDefinition::EMPTY_TYPE, false },
- { "DNS_SERVERS", D6O_NAME_SERVERS, OptionDefinition::IPV6_ADDRESS_TYPE, true }
+ { "CLIENTID", D6O_CLIENTID, OPT_BINARY_TYPE, false },
+ { "SERVERID", D6O_SERVERID, OPT_BINARY_TYPE, false },
+ { "IA_NA", D6O_IA_NA, OPT_RECORD_TYPE, false },
+ { "IAADDR", D6O_IAADDR, OPT_RECORD_TYPE, false },
+ { "ORO", D6O_ORO, OPT_UINT16_TYPE, true },
+ { "ELAPSED_TIME", D6O_ELAPSED_TIME, OPT_UINT16_TYPE, false },
+ { "STATUS_CODE", D6O_STATUS_CODE, OPT_RECORD_TYPE, false },
+ { "RAPID_COMMIT", D6O_RAPID_COMMIT, OPT_EMPTY_TYPE, false },
+ { "DNS_SERVERS", D6O_NAME_SERVERS, OPT_IPV6_ADDRESS_TYPE, true },
+ { "IA_PD", D6O_IA_PD, OPT_RECORD_TYPE, false }
};
const int params_size = sizeof(params) / sizeof(params[0]);
@@ -277,18 +281,19 @@ LibDHCP::initStdOptionDefs6() {
params[i].array));
switch(params[i].code) {
case D6O_IA_NA:
+ case D6O_IA_PD:
for (int j = 0; j < 3; ++j) {
- definition->addRecordField(OptionDefinition::UINT32_TYPE);
+ definition->addRecordField(OPT_UINT32_TYPE);
}
break;
case D6O_IAADDR:
- definition->addRecordField(OptionDefinition::IPV6_ADDRESS_TYPE);
- definition->addRecordField(OptionDefinition::UINT32_TYPE);
- definition->addRecordField(OptionDefinition::UINT32_TYPE);
+ definition->addRecordField(OPT_IPV6_ADDRESS_TYPE);
+ definition->addRecordField(OPT_UINT32_TYPE);
+ definition->addRecordField(OPT_UINT32_TYPE);
break;
case D6O_STATUS_CODE:
- definition->addRecordField(OptionDefinition::UINT16_TYPE);
- definition->addRecordField(OptionDefinition::STRING_TYPE);
+ definition->addRecordField(OPT_UINT16_TYPE);
+ definition->addRecordField(OPT_STRING_TYPE);
break;
default:
// The default case is intentionally left empty
@@ -298,9 +303,12 @@ LibDHCP::initStdOptionDefs6() {
try {
definition->validate();
} catch (const Exception& ex) {
- isc_throw(isc::Unexpected, "internal server error: invalid definition of standard"
- << " DHCPv6 option (with code " << params[i].code << "): "
- << ex.what());
+ // This is unlikely event that validation fails and may
+ // be only caused by programming error. To guarantee the
+ // data consistency we clear all option definitions that
+ // have been added so far and pass the exception forward.
+ v6option_defs_.clear();
+ throw;
}
v6option_defs_.push_back(definition);
}
diff --git a/src/lib/dhcp/libdhcp++.h b/src/lib/dhcp/libdhcp++.h
index c773fd7..cab7928 100644
--- a/src/lib/dhcp/libdhcp++.h
+++ b/src/lib/dhcp/libdhcp++.h
@@ -33,7 +33,14 @@ public:
/// @brief Return collection of option definitions.
///
+ /// Method returns the collection of DHCP standard DHCP
+ /// option definitions.
+ /// @todo DHCPv4 option definitions are not implemented. For now
+ /// this function will throw isc::NotImplemented in case of attempt
+ /// to get option definitions for V4 universe.
+ ///
/// @param u universe of the options (V4 or V6).
+ ///
/// @return collection of option definitions.
static const OptionDefContainer& getOptionDefs(Option::Universe u);
@@ -113,21 +120,6 @@ public:
uint16_t type,
Option::Factory * factory);
- /// Initialize standard DHCP options (V4 or V6).
- ///
- /// The method creates option definitions for all options
- /// (DHCPv4 or DHCPv6 depending on universe specified).
- /// Currently DHCPv4 option definitions initialization is not
- /// implemented thus this function will throw isc::NotImplemented
- /// if V4 universe is specified.
- ///
- /// @param u universe
- /// @throw isc::Unexpected if internal error occured during option
- /// definitions creation.
- /// @throw std::bad_alloc if system went out of memory.
- /// @throw isc::NotImplemented when V4 universe specified.
- static void initStdOptionDefs(Option::Universe u);
-
private:
/// Initialize standard DHCPv4 option definitions.
@@ -144,9 +136,9 @@ private:
///
/// The method creates option definitions for all DHCPv6 options.
///
- /// @throw isc::Unexpected if internal error occured during option
- /// definitions creation.
/// @throw std::bad_alloc if system went out of memory.
+ /// @throw MalformedOptionDefinition if any of the definitions
+ /// is incorect. This is a programming error.
static void initStdOptionDefs6();
/// pointers to factories that produce DHCPv6 options
diff --git a/src/lib/dhcp/option6_int.h b/src/lib/dhcp/option6_int.h
index 2c51389..9b116e0 100644
--- a/src/lib/dhcp/option6_int.h
+++ b/src/lib/dhcp/option6_int.h
@@ -48,7 +48,7 @@ public:
/// as template parameter is not a supported integer type.
Option6Int(uint16_t type, T value)
: Option(Option::V6, type), value_(value) {
- if (!OptionDataTypes<T>::valid) {
+ if (!OptionDataTypeTraits<T>::integer_type) {
isc_throw(dhcp::InvalidDataType, "non-integer type");
}
}
@@ -69,7 +69,7 @@ public:
Option6Int(uint16_t type, OptionBufferConstIter begin,
OptionBufferConstIter end)
: Option(Option::V6, type) {
- if (!OptionDataTypes<T>::valid) {
+ if (!OptionDataTypeTraits<T>::integer_type) {
isc_throw(dhcp::InvalidDataType, "non-integer type");
}
unpack(begin, end);
@@ -91,7 +91,7 @@ public:
// order to the provided buffer. The same functions can be safely used
// for either unsiged or signed integers so there is not need to create
// special cases for intX_t types.
- switch (OptionDataTypes<T>::len) {
+ switch (OptionDataTypeTraits<T>::len) {
case 1:
buf.writeUint8(value_);
break;
@@ -130,7 +130,7 @@ public:
// order from the provided buffer. The same functions can be safely used
// for either unsiged or signed integers so there is not need to create
// special cases for intX_t types.
- int data_size_len = OptionDataTypes<T>::len;
+ int data_size_len = OptionDataTypeTraits<T>::len;
switch (data_size_len) {
case 1:
value_ = *begin;
@@ -145,9 +145,9 @@ public:
isc_throw(dhcp::InvalidDataType, "non-integer type");
}
// Use local variable to set a new value for this iterator.
- // When using OptionDataTypes<T>::len directly some versions
+ // When using OptionDataTypeTraits<T>::len directly some versions
// of clang complain about unresolved reference to
- // OptionDataTypes structure during linking.
+ // OptionDataTypeTraits structure during linking.
begin += data_size_len;
LibDHCP::unpackOptions6(OptionBuffer(begin, end), options_);
}
diff --git a/src/lib/dhcp/option6_int_array.h b/src/lib/dhcp/option6_int_array.h
index aba05a1..c37c107 100644
--- a/src/lib/dhcp/option6_int_array.h
+++ b/src/lib/dhcp/option6_int_array.h
@@ -58,7 +58,7 @@ public:
Option6IntArray(uint16_t type)
: Option(Option::V6, type),
values_(0) {
- if (!OptionDataTypes<T>::valid) {
+ if (!OptionDataTypeTraits<T>::integer_type) {
isc_throw(dhcp::InvalidDataType, "non-integer type");
}
}
@@ -74,7 +74,7 @@ public:
/// as template parameter is not a supported integer type.
Option6IntArray(uint16_t type, const OptionBuffer& buf)
: Option(Option::V6, type) {
- if (!OptionDataTypes<T>::valid) {
+ if (!OptionDataTypeTraits<T>::integer_type) {
isc_throw(dhcp::InvalidDataType, "non-integer type");
}
unpack(buf.begin(), buf.end());
@@ -97,7 +97,7 @@ public:
Option6IntArray(uint16_t type, OptionBufferConstIter begin,
OptionBufferConstIter end)
: Option(Option::V6, type) {
- if (!OptionDataTypes<T>::valid) {
+ if (!OptionDataTypeTraits<T>::integer_type) {
isc_throw(dhcp::InvalidDataType, "non-integer type");
}
unpack(begin, end);
@@ -120,7 +120,7 @@ public:
// order to the provided buffer. The same functions can be safely used
// for either unsiged or signed integers so there is not need to create
// special cases for intX_t types.
- switch (OptionDataTypes<T>::len) {
+ switch (OptionDataTypeTraits<T>::len) {
case 1:
buf.writeUint8(values_[i]);
break;
@@ -166,7 +166,7 @@ public:
// order from the provided buffer. The same functions can be safely used
// for either unsiged or signed integers so there is not need to create
// special cases for intX_t types.
- int data_size_len = OptionDataTypes<T>::len;
+ int data_size_len = OptionDataTypeTraits<T>::len;
switch (data_size_len) {
case 1:
values_.push_back(*begin);
@@ -181,9 +181,9 @@ public:
isc_throw(dhcp::InvalidDataType, "non-integer type");
}
// Use local variable to set a new value for this iterator.
- // When using OptionDataTypes<T>::len directly some versions
+ // When using OptionDataTypeTraits<T>::len directly some versions
// of clang complain about unresolved reference to
- // OptionDataTypes structure during linking.
+ // OptionDataTypeTraits structure during linking.
begin += data_size_len;
}
// We do not unpack sub-options here because we have array-type option.
diff --git a/src/lib/dhcp/option_data_types.h b/src/lib/dhcp/option_data_types.h
index 0ad14d2..99b4220 100644
--- a/src/lib/dhcp/option_data_types.h
+++ b/src/lib/dhcp/option_data_types.h
@@ -15,6 +15,7 @@
#ifndef OPTION_DATA_TYPES_H
#define OPTION_DATA_TYPES_H
+#include <asiolink/io_address.h>
#include <exceptions/exceptions.h>
#include <stdint.h>
@@ -29,59 +30,147 @@ public:
isc::Exception(file, line, what) { };
};
-/// @brief Trait class for integer data types supported in DHCP option definitions.
+/// @brief Exception to be thrown when cast to the data type was unsuccessful.
+class BadDataTypeCast : public Exception {
+public:
+ BadDataTypeCast(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) { };
+};
+
+
+/// @brief Data types of DHCP option fields.
+enum OptionDataType {
+ OPT_EMPTY_TYPE,
+ OPT_BINARY_TYPE,
+ OPT_BOOLEAN_TYPE,
+ OPT_INT8_TYPE,
+ OPT_INT16_TYPE,
+ OPT_INT32_TYPE,
+ OPT_UINT8_TYPE,
+ OPT_UINT16_TYPE,
+ OPT_UINT32_TYPE,
+ OPT_ANY_ADDRESS_TYPE,
+ OPT_IPV4_ADDRESS_TYPE,
+ OPT_IPV6_ADDRESS_TYPE,
+ OPT_STRING_TYPE,
+ OPT_FQDN_TYPE,
+ OPT_RECORD_TYPE,
+ OPT_UNKNOWN_TYPE
+};
+
+/// @brief Trait class for data types supported in DHCP option definitions.
///
/// This is useful to check whether the type specified as template parameter
/// is supported by classes like Option6Int, Option6IntArray and some template
/// factory functions in OptionDefinition class.
template<typename T>
-struct OptionDataTypes {
+struct OptionDataTypeTraits {
static const bool valid = false;
static const int len = 0;
+ static const bool integer_type = false;
+ static const OptionDataType type = OPT_UNKNOWN_TYPE;
+};
+
+/// binary type is supported
+template<>
+struct OptionDataTypeTraits<OptionBuffer> {
+ static const bool valid = true;
+ static const int len = sizeof(OptionBuffer);
+ static const bool integer_type = false;
+ static const OptionDataType type = OPT_BINARY_TYPE;
+};
+
+/// bool type is supported
+template<>
+struct OptionDataTypeTraits<bool> {
+ static const bool valid = true;
+ static const int len = sizeof(bool);
+ static const bool integer_type = false;
+ static const OptionDataType type = OPT_BOOLEAN_TYPE;
};
/// int8_t type is supported.
template<>
-struct OptionDataTypes<int8_t> {
+struct OptionDataTypeTraits<int8_t> {
static const bool valid = true;
static const int len = 1;
+ static const bool integer_type = true;
+ static const OptionDataType type = OPT_INT8_TYPE;
};
/// int16_t type is supported.
template<>
-struct OptionDataTypes<int16_t> {
+struct OptionDataTypeTraits<int16_t> {
static const bool valid = true;
static const int len = 2;
+ static const bool integer_type = true;
+ static const OptionDataType type = OPT_INT16_TYPE;
};
/// int32_t type is supported.
template<>
-struct OptionDataTypes<int32_t> {
+struct OptionDataTypeTraits<int32_t> {
static const bool valid = true;
static const int len = 4;
+ static const bool integer_type = true;
+ static const OptionDataType type = OPT_INT32_TYPE;
};
/// uint8_t type is supported.
template<>
-struct OptionDataTypes<uint8_t> {
+struct OptionDataTypeTraits<uint8_t> {
static const bool valid = true;
static const int len = 1;
+ static const bool integer_type = true;
+ static const OptionDataType type = OPT_UINT8_TYPE;
};
/// uint16_t type is supported.
template<>
-struct OptionDataTypes<uint16_t> {
+struct OptionDataTypeTraits<uint16_t> {
static const bool valid = true;
static const int len = 2;
+ static const bool integer_type = true;
+ static const OptionDataType type = OPT_UINT16_TYPE;
};
/// uint32_t type is supported.
template<>
-struct OptionDataTypes<uint32_t> {
+struct OptionDataTypeTraits<uint32_t> {
static const bool valid = true;
static const int len = 4;
+ static const bool integer_type = true;
+ static const OptionDataType type = OPT_UINT32_TYPE;
+};
+
+/// IPv4 and IPv6 address type is supported
+template<>
+struct OptionDataTypeTraits<asiolink::IOAddress> {
+ static const bool valid = true;
+ // The len value is used to determine the size of the data
+ // to be written to an option buffer. IOAddress object may
+ // either represent an IPv4 or IPv6 addresses which have
+ // different lengths. Thus we can't put fixed value here.
+ // The length of a data to be written into an option buffer
+ // have to be determined in the runtime for a particular
+ // IOAddress object. Thus setting len to zero.
+ static const int len = 0;
+ static const bool integer_type = false;
+ static const OptionDataType type = OPT_ANY_ADDRESS_TYPE;
};
+/// string type is supported
+template<>
+struct OptionDataTypeTraits<std::string> {
+ static const bool valid = true;
+ // The len value is used to determine the size of the data
+ // to be written to an option buffer. For strings this
+ // size is unknown until we actually deal with the particular
+ // string to be written. Thus setting it to zero.
+ static const int len = 0;
+ static const bool integer_type = false;
+ static const OptionDataType type = OPT_STRING_TYPE;
+};
} // isc::dhcp namespace
} // isc namespace
diff --git a/src/lib/dhcp/option_definition.cc b/src/lib/dhcp/option_definition.cc
index ea3ced6..62a81c2 100644
--- a/src/lib/dhcp/option_definition.cc
+++ b/src/lib/dhcp/option_definition.cc
@@ -20,6 +20,7 @@
#include <dhcp/option6_int.h>
#include <dhcp/option6_int_array.h>
#include <dhcp/option_definition.h>
+#include <util/encode/hex.h>
using namespace std;
using namespace isc::util;
@@ -28,30 +29,212 @@ namespace isc {
namespace dhcp {
OptionDefinition::DataTypeUtil::DataTypeUtil() {
- data_types_["empty"] = EMPTY_TYPE;
- data_types_["binary"] = BINARY_TYPE;
- data_types_["boolean"] = BOOLEAN_TYPE;
- data_types_["int8"] = INT8_TYPE;
- data_types_["int16"] = INT16_TYPE;
- data_types_["int32"] = INT32_TYPE;
- data_types_["uint8"] = UINT8_TYPE;
- data_types_["uint16"] = UINT16_TYPE;
- data_types_["uint32"] = UINT32_TYPE;
- data_types_["ipv4-address"] = IPV4_ADDRESS_TYPE;
- data_types_["ipv6-address"] = IPV6_ADDRESS_TYPE;
- data_types_["string"] = STRING_TYPE;
- data_types_["fqdn"] = FQDN_TYPE;
- data_types_["record"] = RECORD_TYPE;
+ data_types_["empty"] = OPT_EMPTY_TYPE;
+ data_types_["binary"] = OPT_BINARY_TYPE;
+ data_types_["boolean"] = OPT_BOOLEAN_TYPE;
+ data_types_["int8"] = OPT_INT8_TYPE;
+ data_types_["int16"] = OPT_INT16_TYPE;
+ data_types_["int32"] = OPT_INT32_TYPE;
+ data_types_["uint8"] = OPT_UINT8_TYPE;
+ data_types_["uint16"] = OPT_UINT16_TYPE;
+ data_types_["uint32"] = OPT_UINT32_TYPE;
+ data_types_["ipv4-address"] = OPT_IPV4_ADDRESS_TYPE;
+ data_types_["ipv6-address"] = OPT_IPV6_ADDRESS_TYPE;
+ data_types_["string"] = OPT_STRING_TYPE;
+ data_types_["fqdn"] = OPT_FQDN_TYPE;
+ data_types_["record"] = OPT_RECORD_TYPE;
}
-OptionDefinition::DataType
-OptionDefinition::DataTypeUtil::getDataType(const std::string& data_type) {
- std::map<std::string, DataType>::const_iterator data_type_it =
+OptionDataType
+OptionDefinition::DataTypeUtil::getOptionDataType(const std::string& data_type) {
+ std::map<std::string, OptionDataType>::const_iterator data_type_it =
data_types_.find(data_type);
if (data_type_it != data_types_.end()) {
return (data_type_it->second);
}
- return UNKNOWN_TYPE;
+ return (OPT_UNKNOWN_TYPE);
+}
+
+template<typename T>
+T OptionDefinition::DataTypeUtil::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"
+ << " non-boolean data type");
+ }
+ // We use the 64-bit value here because it has wider range than
+ // any other type we use here and it allows to detect out of
+ // bounds conditions e.g. negative value specified for uintX_t
+ // data type. Obviously if the value exceeds the limits of int64
+ // this function will not handle that properly.
+ int64_t result = 0;
+ try {
+ result = boost::lexical_cast<int64_t>(value_str);
+ } catch (const boost::bad_lexical_cast& ex) {
+ // Prepare error message here.
+ std::string data_type_str = "boolean";
+ 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());
+ }
+ // 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());
+ }
+ }
+ return (static_cast<T>(result));
+}
+
+void
+OptionDefinition::DataTypeUtil::writeToBuffer(const std::string& value,
+ const OptionDataType type,
+ OptionBuffer& buf) {
+ // We are going to write value given by value argument to the buffer.
+ // The actual type of the value is given by second argument. Check
+ // this argument to determine how to write this value to the buffer.
+ switch (type) {
+ case OPT_BINARY_TYPE:
+ {
+ // Binary value means that the value is encoded as a string
+ // of hexadecimal deigits. We need to decode this string
+ // to the binary format here.
+ OptionBuffer binary;
+ try {
+ util::encode::decodeHex(value, binary);
+ } catch (const Exception& ex) {
+ isc_throw(BadDataTypeCast, "unable to cast " << value
+ << " to binary data type: " << ex.what());
+ }
+ // Decode was successful so append decoded binary value
+ // to the buffer.
+ buf.insert(buf.end(), binary.begin(), binary.end());
+ return;
+ }
+ case OPT_BOOLEAN_TYPE:
+ {
+ // We encode the true value as 1 and false as 0 on 8 bits.
+ // That way we actually waist 7 bits but it seems to be the
+ // simpler way to encode boolean.
+ // @todo Consider if any other encode methods can be used.
+ bool bool_value = lexicalCastWithRangeCheck<bool>(value);
+ if (bool_value) {
+ buf.push_back(static_cast<uint8_t>(1));
+ } else {
+ buf.push_back(static_cast<uint8_t>(0));
+ }
+ return;
+ }
+ case OPT_INT8_TYPE:
+ {
+ // Buffer holds the uin8_t values so we need to cast the signed
+ // value to unsigned but the bits values remain untouched.
+ buf.push_back(static_cast<uint8_t>(lexicalCastWithRangeCheck<int8_t>(value)));
+ return;
+ }
+ case OPT_INT16_TYPE:
+ {
+ // Write the int16 value as uint16 value is ok because the bit values
+ // remain untouched.
+ int16_t int_value = lexicalCastWithRangeCheck<int16_t>(value);
+ buf.resize(buf.size() + 2);
+ writeUint16(static_cast<uint16_t>(int_value), &buf[buf.size() - 2]);
+ return;
+ }
+ case OPT_INT32_TYPE:
+ {
+ int32_t int_value = lexicalCastWithRangeCheck<int32_t>(value);
+ buf.resize(buf.size() + 4);
+ writeUint32(static_cast<uint32_t>(int_value), &buf[buf.size() - 4]);
+ return;
+ }
+ case OPT_UINT8_TYPE:
+ {
+ buf.push_back(lexicalCastWithRangeCheck<uint8_t>(value));
+ return;
+ }
+ case OPT_UINT16_TYPE:
+ {
+ uint16_t uint_value = lexicalCastWithRangeCheck<uint16_t>(value);
+ buf.resize(buf.size() + 2);
+ writeUint16(uint_value, &buf[buf.size() - 2]);
+ return;
+ }
+ case OPT_UINT32_TYPE:
+ {
+ uint32_t uint_value = lexicalCastWithRangeCheck<uint32_t>(value);
+ buf.resize(buf.size() + 4);
+ writeUint32(uint_value, &buf[buf.size() - 4]);
+ return;
+ }
+ case OPT_IPV4_ADDRESS_TYPE:
+ {
+ // The easiest way to get the binary form of IPv4 address is
+ // to create IOAddress object from string and use its accessors
+ // to retrieve the binary form.
+ asiolink::IOAddress address(value);
+ if (!address.getAddress().is_v4()) {
+ isc_throw(BadDataTypeCast, "provided address " << address.toText()
+ << " is not a valid IPV4 address");
+ }
+ asio::ip::address_v4::bytes_type addr_bytes =
+ address.getAddress().to_v4().to_bytes();
+ // Increase the buffer size by the size of IPv4 address.
+ buf.resize(buf.size() + addr_bytes.size());
+ std::copy_backward(addr_bytes.begin(), addr_bytes.end(),
+ buf.end());
+ return;
+ }
+ case OPT_IPV6_ADDRESS_TYPE:
+ {
+ asiolink::IOAddress address(value);
+ if (!address.getAddress().is_v6()) {
+ isc_throw(BadDataTypeCast, "provided address " << address.toText()
+ << " is not a valid IPV6 address");
+ }
+ asio::ip::address_v6::bytes_type addr_bytes =
+ address.getAddress().to_v6().to_bytes();
+ // Incresase the buffer size by the size of IPv6 address.
+ buf.resize(buf.size() + addr_bytes.size());
+ std::copy_backward(addr_bytes.begin(), addr_bytes.end(),
+ buf.end());
+ return;
+ }
+ case OPT_STRING_TYPE:
+ if (value.size() > 0) {
+ // Increase the size of the storage by the size of the string.
+ buf.resize(buf.size() + value.size());
+ // Assuming that the string is already UTF8 encoded.
+ std::copy_backward(value.c_str(), value.c_str() + value.size(),
+ buf.end());
+ return;
+ }
+ case OPT_FQDN_TYPE:
+ {
+ // FQDN implementation is not terribly complicated but will require
+ // creation of some additional logic (maybe object) that will parse
+ // the fqdn into labels.
+ isc_throw(isc::NotImplemented, "write of FQDN record into option buffer"
+ " is not supported yet");
+ return;
+ }
+ default:
+ // We hit this point because invalid option data type has been specified
+ // This may be the case because 'empty' or 'record' data type has been
+ // specified. We don't throw exception here because it will be thrown
+ // at the exit point from this function.
+ ;
+ }
+ isc_throw(isc::BadValue, "attempt to write invalid option data field type"
+ " into the option buffer: " << type);
+
}
OptionDefinition::OptionDefinition(const std::string& name,
@@ -60,17 +243,17 @@ OptionDefinition::OptionDefinition(const std::string& name,
const bool array_type /* = false */)
: name_(name),
code_(code),
- type_(UNKNOWN_TYPE),
+ type_(OPT_UNKNOWN_TYPE),
array_type_(array_type) {
// Data type is held as enum value by this class.
// Use the provided option type string to get the
// corresponding enum value.
- type_ = DataTypeUtil::instance().getDataType(type);
+ type_ = DataTypeUtil::instance().getOptionDataType(type);
}
OptionDefinition::OptionDefinition(const std::string& name,
const uint16_t code,
- const DataType type,
+ const OptionDataType type,
const bool array_type /* = false */)
: name_(name),
code_(code),
@@ -80,67 +263,113 @@ OptionDefinition::OptionDefinition(const std::string& name,
void
OptionDefinition::addRecordField(const std::string& data_type_name) {
- DataType data_type = DataTypeUtil::instance().getDataType(data_type_name);
+ OptionDataType data_type = DataTypeUtil::instance().getOptionDataType(data_type_name);
addRecordField(data_type);
}
void
-OptionDefinition::addRecordField(const DataType data_type) {
- if (type_ != RECORD_TYPE) {
+OptionDefinition::addRecordField(const OptionDataType data_type) {
+ if (type_ != OPT_RECORD_TYPE) {
isc_throw(isc::InvalidOperation, "'record' option type must be used"
" to add data fields to the record");
}
- if (data_type >= UNKNOWN_TYPE) {
+ if (data_type >= OPT_UNKNOWN_TYPE) {
isc_throw(isc::BadValue, "attempted to add invalid data type to the record");
}
record_fields_.push_back(data_type);
}
-Option::Factory*
-OptionDefinition::getFactory() const {
+OptionPtr
+OptionDefinition::optionFactory(Option::Universe u, uint16_t type,
+ OptionBufferConstIter begin,
+ OptionBufferConstIter end) const {
validate();
+
+ try {
+ if (type_ == OPT_BINARY_TYPE) {
+ return (factoryGeneric(u, type, begin, end));
+
+ } else if (type_ == OPT_IPV6_ADDRESS_TYPE && array_type_) {
+ return (factoryAddrList6(type, begin, end));
+
+ } else if (type_ == OPT_IPV4_ADDRESS_TYPE && array_type_) {
+ return (factoryAddrList4(type, begin, end));
+
+ } else if (type_ == OPT_EMPTY_TYPE) {
+ return (factoryEmpty(u, type));
+
+ } else if (u == Option::V6 &&
+ code_ == D6O_IA_NA &&
+ haveIA6Format()) {
+ return (factoryIA6(type, begin, end));
+
+ } else if (u == Option::V6 &&
+ code_ == D6O_IAADDR &&
+ haveIAAddr6Format()) {
+ return (factoryIAAddr6(type, begin, end));
+
+ } else if (type_ == OPT_UINT8_TYPE) {
+ if (array_type_) {
+ return (factoryGeneric(u, type, begin, end));
+ } else {
+ return (factoryInteger<uint8_t>(u, type, begin, end));
+ }
+
+ } else if (type_ == OPT_UINT16_TYPE) {
+ if (array_type_) {
+ return (factoryIntegerArray<uint16_t>(type, begin, end));
+ } else {
+ return (factoryInteger<uint16_t>(u, type, begin, end));
+ }
+
+ } else if (type_ == OPT_UINT32_TYPE) {
+ if (array_type_) {
+ return (factoryIntegerArray<uint32_t>(type, begin, end));
+ } else {
+ return (factoryInteger<uint32_t>(u, type, begin, end));
+ }
- // @todo This function must be extended to return more factory
- // functions that create instances of more specialized options.
- // This requires us to first implement those more specialized
- // options that will be derived from Option class.
- if (type_ == BINARY_TYPE) {
- return (factoryGeneric);
- } else if (type_ == IPV6_ADDRESS_TYPE && array_type_) {
- return (factoryAddrList6);
- } else if (type_ == IPV4_ADDRESS_TYPE && array_type_) {
- return (factoryAddrList4);
- } else if (type_ == EMPTY_TYPE) {
- return (factoryEmpty);
- } else if (code_ == D6O_IA_NA && haveIA6Format()) {
- return (factoryIA6);
- } else if (code_ == D6O_IAADDR && haveIAAddr6Format()) {
- return (factoryIAAddr6);
- } else if (type_ == UINT8_TYPE) {
- if (array_type_) {
- return (factoryGeneric);
- } else {
- return (factoryInteger<uint8_t>);
}
- } else if (type_ == UINT16_TYPE) {
- if (array_type_) {
- return (factoryIntegerArray<uint16_t>);
- } else {
- return (factoryInteger<uint16_t>);
+ return (factoryGeneric(u, type, begin, end));
+
+ } catch (const Exception& ex) {
+ isc_throw(InvalidOptionValue, ex.what());
+ }
+}
+
+OptionPtr
+OptionDefinition::optionFactory(Option::Universe u, uint16_t type,
+ const OptionBuffer& buf) const {
+ return (optionFactory(u, type, buf.begin(), buf.end()));
+}
+
+OptionPtr
+OptionDefinition::optionFactory(Option::Universe u, uint16_t type,
+ const std::vector<std::string>& values) const {
+ validate();
+
+ OptionBuffer buf;
+ if (!array_type_ && type_ != OPT_RECORD_TYPE) {
+ if (values.size() == 0) {
+ isc_throw(InvalidOptionValue, "no option value specified");
}
- } else if (type_ == UINT32_TYPE) {
- if (array_type_) {
- return (factoryIntegerArray<uint32_t>);
- } else {
- return (factoryInteger<uint32_t>);
+ DataTypeUtil::instance().writeToBuffer(values[0], type_, buf);
+ } else if (array_type_ && type_ != OPT_RECORD_TYPE) {
+ for (size_t i = 0; i < values.size(); ++i) {
+ DataTypeUtil::instance().writeToBuffer(values[i], type_, buf);
+ }
+ } else if (type_ == OPT_RECORD_TYPE) {
+ const RecordFieldsCollection& records = getRecordFields();
+ if (records.size() > values.size()) {
+ isc_throw(InvalidOptionValue, "number of data fields for the option"
+ << " type " << type_ << " is greater than number of values"
+ << " provided.");
+ }
+ for (size_t i = 0; i < records.size(); ++i) {
+ DataTypeUtil::instance().writeToBuffer(values[i], records[i], buf);
}
}
- // Factory generic returns instance of Option class. However, once we
- // implement CustomOption class we may want to return factory function
- // that will create instance of CustomOption rather than Option.
- // CustomOption will allow to access particular data fields within the
- // option rather than raw data buffer.
- return (factoryGeneric);
+ return (optionFactory(u, type, buf.begin(), buf.end()));
}
void
@@ -153,28 +382,61 @@ OptionDefinition::sanityCheckUniverse(const Option::Universe expected_universe,
void
OptionDefinition::validate() const {
- // Option name must not be empty.
+ std::ostringstream err_str;
if (name_.empty()) {
- isc_throw(isc::BadValue, "option name must not be empty");
- }
- // Option name must not contain spaces.
- if (name_.find(" ") != string::npos) {
- isc_throw(isc::BadValue, "option name must not contain spaces");
+ // Option name must not be empty.
+ err_str << "option name must not be empty";
+ } else if (name_.find(" ") != string::npos) {
+ // Option name must not contain spaces.
+ err_str << "option name must not contain spaces";
+ } else if (type_ >= OPT_UNKNOWN_TYPE) {
+ // Option definition must be of a known type.
+ err_str << "option type value " << type_ << " is out of range";
+ } else if (type_ == OPT_STRING_TYPE && array_type_) {
+ // Array of strings is not allowed because there is no way
+ // to determine the size of a particular string and thus there
+ // 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_RECORD_TYPE) {
+ // 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()
+ << " 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 data fields are not 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.";
+ break;
+ }
+ }
+ }
+
}
- // Unsupported option types are not allowed.
- if (type_ >= UNKNOWN_TYPE) {
- isc_throw(isc::OutOfRange, "option type value " << type_
- << " is out of range");
+
+ // Non-empty error string means that we have hit the error. We throw
+ // exception and include error string.
+ if (!err_str.str().empty()) {
+ isc_throw(MalformedOptionDefinition, err_str.str());
}
}
bool
-OptionDefinition::haveIAx6Format(OptionDefinition::DataType first_type) const {
- return (haveType(RECORD_TYPE) &&
+OptionDefinition::haveIAx6Format(OptionDataType first_type) const {
+ return (haveType(OPT_RECORD_TYPE) &&
record_fields_.size() == 3 &&
record_fields_[0] == first_type &&
- record_fields_[1] == UINT32_TYPE &&
- record_fields_[2] == UINT32_TYPE);
+ record_fields_[1] == OPT_UINT32_TYPE &&
+ record_fields_[2] == OPT_UINT32_TYPE);
}
bool
@@ -185,70 +447,66 @@ OptionDefinition::haveIA6Format() const {
// arrays do not impose limitations on number of elements in
// the array while this limitation is needed for IA_NA - need
// exactly 3 elements.
- return (haveIAx6Format(UINT32_TYPE));
+ return (haveIAx6Format(OPT_UINT32_TYPE));
}
bool
OptionDefinition::haveIAAddr6Format() const {
- return (haveIAx6Format(IPV6_ADDRESS_TYPE));
+ return (haveIAx6Format(OPT_IPV6_ADDRESS_TYPE));
}
OptionPtr
-OptionDefinition::factoryAddrList4(Option::Universe u, uint16_t type,
- const OptionBuffer& buf) {
- sanityCheckUniverse(u, Option::V4);
- boost::shared_ptr<Option4AddrLst> option(new Option4AddrLst(type, buf.begin(),
- buf.begin() + buf.size()));
+OptionDefinition::factoryAddrList4(uint16_t type,
+ OptionBufferConstIter begin,
+ OptionBufferConstIter end) {
+ boost::shared_ptr<Option4AddrLst> option(new Option4AddrLst(type, begin, end));
return (option);
}
OptionPtr
-OptionDefinition::factoryAddrList6(Option::Universe u, uint16_t type,
- const OptionBuffer& buf) {
- sanityCheckUniverse(u, Option::V6);
- boost::shared_ptr<Option6AddrLst> option(new Option6AddrLst(type, buf.begin(),
- buf.begin() + buf.size()));
+OptionDefinition::factoryAddrList6(uint16_t type,
+ OptionBufferConstIter begin,
+ OptionBufferConstIter end) {
+ boost::shared_ptr<Option6AddrLst> option(new Option6AddrLst(type, begin, end));
return (option);
}
OptionPtr
-OptionDefinition::factoryEmpty(Option::Universe u, uint16_t type, const OptionBuffer& buf) {
- if (buf.size() > 0) {
- isc_throw(isc::BadValue, "input option buffer must be empty"
- " when creating empty option instance");
- }
+OptionDefinition::factoryEmpty(Option::Universe u, uint16_t type) {
OptionPtr option(new Option(u, type));
return (option);
}
OptionPtr
-OptionDefinition::factoryGeneric(Option::Universe u, uint16_t type, const OptionBuffer& buf) {
- OptionPtr option(new Option(u, type, buf));
+OptionDefinition::factoryGeneric(Option::Universe u, uint16_t type,
+ OptionBufferConstIter begin,
+ OptionBufferConstIter end) {
+ OptionPtr option(new Option(u, type, begin, end));
return (option);
}
OptionPtr
-OptionDefinition::factoryIA6(Option::Universe u, uint16_t type, const OptionBuffer& buf) {
- sanityCheckUniverse(u, Option::V6);
- if (buf.size() != Option6IA::OPTION6_IA_LEN) {
- isc_throw(isc::OutOfRange, "input option buffer has invalid size, expeted "
- << Option6IA::OPTION6_IA_LEN << " bytes");
+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");
}
- boost::shared_ptr<Option6IA> option(new Option6IA(type, buf.begin(),
- buf.begin() + buf.size()));
+ boost::shared_ptr<Option6IA> option(new Option6IA(type, begin, end));
return (option);
}
OptionPtr
-OptionDefinition::factoryIAAddr6(Option::Universe u, uint16_t type, const OptionBuffer& buf) {
- sanityCheckUniverse(u, Option::V6);
- if (buf.size() != Option6IAAddr::OPTION6_IAADDR_LEN) {
- isc_throw(isc::OutOfRange, "input option buffer has invalid size, expeted "
- << Option6IAAddr::OPTION6_IAADDR_LEN << " bytes");
+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");
}
- boost::shared_ptr<Option6IAAddr> option(new Option6IAAddr(type, buf.begin(),
- buf.begin() + buf.size()));
+ boost::shared_ptr<Option6IAAddr> option(new Option6IAAddr(type, begin, end));
return (option);
}
diff --git a/src/lib/dhcp/option_definition.h b/src/lib/dhcp/option_definition.h
index d9b7f98..18f79d2 100644
--- a/src/lib/dhcp/option_definition.h
+++ b/src/lib/dhcp/option_definition.h
@@ -27,6 +27,21 @@
namespace isc {
namespace dhcp {
+/// @brief Exception to be thrown when invalid option value has been
+/// specified for a particular option definition.
+class InvalidOptionValue : public Exception {
+public:
+ InvalidOptionValue(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) { };
+};
+
+/// @brief Exception to be thrown when option definition is invalid.
+class MalformedOptionDefinition : public Exception {
+public:
+ MalformedOptionDefinition(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) { };
+};
+
/// @brief Forward declaration to OptionDefinition.
class OptionDefinition;
@@ -82,7 +97,7 @@ class Option6IntArray;
///
/// Should the option comprise data fields of different types, the "record"
/// option type is used. In such cases the data field types within the record
-/// are specified using \ref OptioDefinition::addRecordField.
+/// are specified using \ref OptionDefinition::addRecordField.
///
/// When the OptionDefinition object has been sucessfully created, it can be
/// queried to return the appropriate option factory function for the specified
@@ -111,33 +126,14 @@ class Option6IntArray;
class OptionDefinition {
public:
- /// Data types of DHCP option fields.
- enum DataType {
- EMPTY_TYPE,
- BINARY_TYPE,
- BOOLEAN_TYPE,
- INT8_TYPE,
- INT16_TYPE,
- INT32_TYPE,
- UINT8_TYPE,
- UINT16_TYPE,
- UINT32_TYPE,
- IPV4_ADDRESS_TYPE,
- IPV6_ADDRESS_TYPE,
- STRING_TYPE,
- FQDN_TYPE,
- RECORD_TYPE,
- UNKNOWN_TYPE
- };
-
/// List of fields within the record.
- typedef std::vector<DataType> RecordFieldsCollection;
+ typedef std::vector<OptionDataType> RecordFieldsCollection;
/// Const iterator for record data fields.
- typedef std::vector<DataType>::const_iterator RecordFieldsConstIter;
+ typedef std::vector<OptionDataType>::const_iterator RecordFieldsConstIter;
private:
- /// @brief Utility class for operations on DataTypes.
+ /// @brief Utility class for operations on OptionDataTypes.
///
/// This class is implemented as the singleton because the list of
/// supported data types need only be loaded only once into memory as it
@@ -161,7 +157,41 @@ private:
/// @param data_type_name data type string.
///
/// @return option data type.
- DataType getDataType(const std::string& data_type_name);
+ OptionDataType getOptionDataType(const std::string& data_type_name);
+
+ /// @brief Perform lexical cast of the value and validate its range.
+ ///
+ /// This function performs lexical cast of a string value to integer
+ /// or boolean value and checks if the resulting value is within a
+ /// range of a target type. Note that range checks are not performed
+ /// on boolean values. The target type should be one of the supported
+ /// integer types or bool.
+ ///
+ /// @param value_str input value given as string.
+ /// @tparam T target type for lexical cast.
+ ///
+ /// @return cast value.
+ /// @throw BadDataTypeCast if cast was not successful.
+ template<typename T>
+ T lexicalCastWithRangeCheck(const std::string& value_str) const;
+
+ /// @brief Write the string value into the provided buffer.
+ ///
+ /// This method writes the given value to the specified buffer.
+ /// The provided string value may represent data of different types.
+ /// The actual data type is specified with the second argument.
+ /// Based on a value of this argument, this function will first
+ /// try to cast the string value to the particular data type and
+ /// if it is successful it will store the data in the buffer
+ /// in a binary format.
+ ///
+ /// @param value string representation of the value to be written.
+ /// @param type the actual data type to be stored.
+ /// @param [in, out] buf buffer where the value is to be stored.
+ ///
+ /// @throw BadDataTypeCast if data write was unsuccessful.
+ void writeToBuffer(const std::string& value, const OptionDataType type,
+ OptionBuffer& buf);
private:
/// @brief Private constructor.
@@ -174,7 +204,7 @@ private:
DataTypeUtil();
/// Map of data types, maps name of the type to enum value.
- std::map<std::string, DataType> data_types_;
+ std::map<std::string, OptionDataType> data_types_;
};
public:
@@ -199,7 +229,7 @@ public:
/// option fields are the array.
OptionDefinition(const std::string& name,
const uint16_t code,
- const DataType type,
+ const OptionDataType type,
const bool array_type = false);
/// @brief Adds data field to the record.
@@ -216,7 +246,7 @@ public:
///
/// @throw isc::InvalidOperation if option type is not set to RECORD_TYPE.
/// @throw isc::BadValue if specified invalid data type.
- void addRecordField(const DataType data_type);
+ void addRecordField(const OptionDataType data_type);
/// @brief Return array type indicator.
///
@@ -231,13 +261,6 @@ public:
/// @return option code.
uint16_t getCode() const { return (code_); }
- /// @brief Return factory function for the given definition.
- ///
- /// @throw isc::OutOfRange if \ref validate returns it.
- /// @throw isc::BadValue if \ref validate returns it.
- /// @return pointer to a factory function.
- Option::Factory* getFactory() const;
-
/// @brief Return option name.
///
/// @return option name.
@@ -251,13 +274,11 @@ public:
/// @brief Return option data type.
///
/// @return option data type.
- DataType getType() const { return (type_); };
+ OptionDataType getType() const { return (type_); };
/// @brief Check if the option definition is valid.
///
- /// @throw isc::OutOfRange if invalid option type was specified.
- /// @throw isc::BadValue if invalid option name was specified,
- /// e.g. empty or containing spaces.
+ /// @throw MalformedOptionDefinition option definition is invalid.
void validate() const;
/// @brief Check if specified format is IA_NA option format.
@@ -270,101 +291,163 @@ public:
/// @return true if specified format is IAADDR option format.
bool haveIAAddr6Format() const;
+ /// @brief Option factory.
+ ///
+ /// This function creates an instance of DHCP option using
+ /// provided chunk of buffer. This function may be used to
+ /// create option which is to be sent in the outgoing packet.
+ ///
+ /// @param u option universe (V4 or V6).
+ /// @param type option type.
+ /// @param begin beginning of the option buffer.
+ /// @param end end of the option buffer.
+ ///
+ /// @return instance of the DHCP option.
+ /// @throw MalformedOptionDefinition if option definition is invalid.
+ /// @throw InvalidOptionValue if data for the option is invalid.
+ OptionPtr optionFactory(Option::Universe u, uint16_t type,
+ OptionBufferConstIter begin,
+ OptionBufferConstIter end) const;
+
+ /// @brief Option factory.
+ ///
+ /// This function creates an instance of DHCP option using
+ /// whole provided buffer. This function may be used to
+ /// create option which is to be sent in the outgoing packet.
+ ///
+ /// @param u option universe (V4 or V6).
+ /// @param type option type.
+ /// @param buf option buffer.
+ ///
+ /// @return instance of the DHCP option.
+ /// @throw MalformedOptionDefinition if option definition is invalid.
+ /// @throw InvalidOptionValue if data for the option is invalid.
+ OptionPtr optionFactory(Option::Universe u, uint16_t type,
+ const OptionBuffer& buf) const;
+
+ /// @brief Option factory.
+ ///
+ /// This function creates an instance of DHCP option using the vector
+ /// of strings which carry data values for option data fields.
+ /// The order of values in the vector corresponds to the order of data
+ /// fields in the option. The supplied string values are cast to
+ /// their actual data types which are determined based on the
+ /// option definition. If cast fails due to type mismatch, an exception
+ /// is thrown. This factory function can be used to create option
+ /// instance when user specified option value in the <b>comma separated
+ /// values</b> format in the configuration database. Provided string
+ /// must be tokenized into the vector of string values and this vector
+ /// can be supplied to this function.
+ ///
+ /// @param u option universe (V4 or V6).
+ /// @param type option type.
+ /// @param values a vector of values to be used to set data for an option.
+ ///
+ /// @return instance of the DHCP option.
+ /// @throw MalformedOptionDefinition if option definition is invalid.
+ /// @throw InvalidOptionValue if data for the option is invalid.
+ OptionPtr optionFactory(Option::Universe u, uint16_t type,
+ const std::vector<std::string>& values) const;
+
/// @brief Factory to create option with address list.
///
- /// @param u universe (must be V4).
/// @param type option type.
- /// @param buf option buffer with a list of IPv4 addresses.
+ /// @param begin iterator pointing to the beginning of the buffer
+ /// with a list of IPv4 addresses.
+ /// @param end iterator pointing to the end of the buffer with
+ /// a list of IPv4 addresses.
///
/// @throw isc::OutOfRange if length of the provided option buffer
/// is not multiple of IPV4 address length.
- static OptionPtr factoryAddrList4(Option::Universe u, uint16_t type,
- const OptionBuffer& buf);
+ static OptionPtr factoryAddrList4(uint16_t type,
+ OptionBufferConstIter begin,
+ OptionBufferConstIter end);
/// @brief Factory to create option with address list.
///
- /// @param u universe (must be V6).
/// @param type option type.
- /// @param buf option buffer with a list of IPv6 addresses.
+ /// @param begin iterator pointing to the beginning of the buffer
+ /// with a list of IPv6 addresses.
+ /// @param end iterator pointing to the end of the buffer with
+ /// a list of IPv6 addresses.
///
/// @throw isc::OutOfaRange if length of provided option buffer
/// is not multiple of IPV6 address length.
- static OptionPtr factoryAddrList6(Option::Universe u, uint16_t type,
- const OptionBuffer& buf);
+ static OptionPtr factoryAddrList6(uint16_t type,
+ OptionBufferConstIter begin,
+ OptionBufferConstIter end);
/// @brief Empty option factory.
///
/// @param u universe (V6 or V4).
/// @param type option type.
- /// @param buf option buffer (must be empty).
- static OptionPtr factoryEmpty(Option::Universe u, uint16_t type,
- const OptionBuffer& buf);
+ static OptionPtr factoryEmpty(Option::Universe u, uint16_t type);
/// @brief Factory to create generic option.
///
/// @param u universe (V6 or V4).
/// @param type option type.
- /// @param buf option buffer.
+ /// @param begin iterator pointing to the beginning of the buffer.
+ /// @param end iterator pointing to the end of the buffer.
static OptionPtr factoryGeneric(Option::Universe u, uint16_t type,
- const OptionBuffer& buf);
+ OptionBufferConstIter begin,
+ OptionBufferConstIter end);
/// @brief Factory for IA-type of option.
///
- /// @param u universe (must be V6).
/// @param type option type.
- /// @param buf option buffer.
+ /// @param begin iterator pointing to the beginning of the buffer.
+ /// @param end iterator pointing to the end of the buffer.
///
/// @throw isc::OutOfRange if provided option buffer is too short or
/// too long. Expected size is 12 bytes.
/// @throw isc::BadValue if specified universe value is not V6.
- static OptionPtr factoryIA6(Option::Universe u, uint16_t type,
- const OptionBuffer& buf);
+ static OptionPtr factoryIA6(uint16_t type,
+ OptionBufferConstIter begin,
+ OptionBufferConstIter end);
/// @brief Factory for IAADDR-type of option.
///
- /// @param u universe (must be V6).
/// @param type option type.
- /// @param buf option buffer.
+ /// @param begin iterator pointing to the beginning of the buffer.
+ /// @param end iterator pointing to the end of the buffer.
///
/// @throw isc::OutOfRange if provided option buffer is too short or
/// too long. Expected size is 24 bytes.
/// @throw isc::BadValue if specified universe value is not V6.
- static OptionPtr factoryIAAddr6(Option::Universe u, uint16_t type,
- const OptionBuffer& buf);
+ static OptionPtr factoryIAAddr6(uint16_t type,
+ OptionBufferConstIter begin,
+ OptionBufferConstIter end);
/// @brief Factory function to create option with integer value.
///
/// @param type option type.
- /// @param buf option buffer.
+ /// @param begin iterator pointing to the beginning of the buffer.
+ /// @param end iterator pointing to the end of the buffer.
/// @tparam T type of the data field (must be one of the uintX_t or intX_t).
///
/// @throw isc::OutOfRange if provided option buffer length is invalid.
template<typename T>
- static OptionPtr factoryInteger(Option::Universe, uint16_t type, const OptionBuffer& buf) {
- if (buf.size() > sizeof(T)) {
- isc_throw(isc::OutOfRange, "provided option buffer is too large, expected: "
- << sizeof(T) << " bytes");
- }
- OptionPtr option(new Option6Int<T>(type, buf.begin(), buf.end()));
+ static OptionPtr factoryInteger(Option::Universe, uint16_t type,
+ OptionBufferConstIter begin,
+ OptionBufferConstIter end) {
+ OptionPtr option(new Option6Int<T>(type, begin, end));
return (option);
}
/// @brief Factory function to create option with array of integer values.
///
/// @param type option type.
- /// @param buf option buffer.
+ /// @param begin iterator pointing to the beginning of the buffer.
+ /// @param end iterator pointing to the end of the buffer.
/// @tparam T type of the data field (must be one of the uintX_t or intX_t).
///
/// @throw isc::OutOfRange if provided option buffer length is invalid.
template<typename T>
- static OptionPtr factoryIntegerArray(Option::Universe, uint16_t type, const OptionBuffer& buf) {
- if (buf.size() == 0) {
- isc_throw(isc::OutOfRange, "option buffer length must be greater than zero");
- } else if (buf.size() % OptionDataTypes<T>::len != 0) {
- isc_throw(isc::OutOfRange, "option buffer length must be multiple of "
- << OptionDataTypes<T>::len << " bytes");
- }
- OptionPtr option(new Option6IntArray<T>(type, buf.begin(), buf.end()));
+ static OptionPtr factoryIntegerArray(uint16_t type,
+ OptionBufferConstIter begin,
+ OptionBufferConstIter end) {
+ OptionPtr option(new Option6IntArray<T>(type, begin, end));
return (option);
}
@@ -379,12 +462,12 @@ private:
/// @param first_type type of the first data field.
///
/// @return true if actual option format matches expected format.
- bool haveIAx6Format(const OptionDefinition::DataType first_type) const;
+ bool haveIAx6Format(const OptionDataType first_type) const;
/// @brief Check if specified type matches option definition type.
///
/// @return true if specified type matches option definition type.
- inline bool haveType(const DataType type) const {
+ inline bool haveType(const OptionDataType type) const {
return (type == type_);
}
@@ -395,14 +478,14 @@ private:
///
/// @throw isc::BadValue if expected universe and actual universe don't match.
static inline void sanityCheckUniverse(const Option::Universe expected_universe,
- const Option::Universe actual_universe);
+ const Option::Universe actual_universe);
/// Option name.
std::string name_;
/// Option code.
uint16_t code_;
/// Option data type.
- DataType type_;
+ OptionDataType type_;
/// Indicates wheter option is a single value or array.
bool array_type_;
/// Collection of data fields within the record.
@@ -421,7 +504,7 @@ private:
/// Note that this container can hold multiple options with the
/// same code. For this reason, the latter index can be used to
/// obtain a range of options for a particular option code.
-///
+///
/// @todo: need an index to search options using option space name
/// once option spaces are implemented.
typedef boost::multi_index_container<
diff --git a/src/lib/dhcp/tests/libdhcp++_unittest.cc b/src/lib/dhcp/tests/libdhcp++_unittest.cc
index c8a3271..af3d6ca 100644
--- a/src/lib/dhcp/tests/libdhcp++_unittest.cc
+++ b/src/lib/dhcp/tests/libdhcp++_unittest.cc
@@ -39,8 +39,7 @@ using namespace isc::util;
namespace {
class LibDhcpTest : public ::testing::Test {
public:
- LibDhcpTest() {
- }
+ LibDhcpTest() { }
/// @brief Generic factory function to create any option.
///
@@ -64,14 +63,13 @@ public:
/// @param bug buffer to be used to create option instance.
/// @param expected_type type of the option created by the
/// factory function returned by the option definition.
- static void testInitOptionDefs6(const uint16_t code,
+ static void testStdOptionDefs6(const uint16_t code,
const OptionBuffer& buf,
const std::type_info& expected_type) {
- // Initialize stdandard options definitions. They are held
- // in the static container throughout the program.
- LibDHCP::initStdOptionDefs(Option::V6);
// Get all option definitions, we will use them to extract
// the definition for a particular option code.
+ // We don't have to initialize option deinitions here because they
+ // are initialized in the class'es constructor.
OptionDefContainer options = LibDHCP::getOptionDefs(Option::V6);
// Get the container index #1. This one allows for searching
// option definitions using option code.
@@ -90,14 +88,9 @@ public:
ASSERT_TRUE(def);
// Check that option definition is valid.
ASSERT_NO_THROW(def->validate());
- // Get the factory function for the particular option
- // definition. We will use this factory function to
- // create option instance.
- Option::Factory* factory = NULL;
- ASSERT_NO_THROW(factory = def->getFactory());
OptionPtr option;
// Create the option.
- ASSERT_NO_THROW(option = factory(Option::V6, code, buf));
+ ASSERT_NO_THROW(option = def->optionFactory(Option::V6, code, buf));
// Make sure it is not NULL.
ASSERT_TRUE(option);
// And the actual object type is the one that we expect.
@@ -108,14 +101,14 @@ public:
};
static const uint8_t packed[] = {
- 0, 12, 0, 5, 100, 101, 102, 103, 104, // opt1 (9 bytes)
- 0, 13, 0, 3, 105, 106, 107, // opt2 (7 bytes)
- 0, 14, 0, 2, 108, 109, // opt3 (6 bytes)
- 1, 0, 0, 4, 110, 111, 112, 113, // opt4 (8 bytes)
- 1, 1, 0, 1, 114 // opt5 (5 bytes)
+ 0, 1, 0, 5, 100, 101, 102, 103, 104, // CLIENT_ID (9 bytes)
+ 0, 2, 0, 3, 105, 106, 107, // SERVER_ID (7 bytes)
+ 0, 14, 0, 0, // RAPID_COMMIT (0 bytes)
+ 0, 6, 0, 4, 108, 109, 110, 111, // ORO (8 bytes)
+ 0, 8, 0, 2, 112, 113 // ELAPSED_TIME (6 bytes)
};
-TEST(LibDhcpTest, optionFactory) {
+TEST_F(LibDhcpTest, optionFactory) {
OptionBuffer buf;
// Factory functions for specific options must be registered before
// they can be used to create options instances. Otherwise exception
@@ -187,7 +180,7 @@ TEST(LibDhcpTest, optionFactory) {
opt_clientid->getData().begin()));
}
-TEST(LibDhcpTest, packOptions6) {
+TEST_F(LibDhcpTest, packOptions6) {
OptionBuffer buf(512);
isc::dhcp::Option::OptionCollection opts; // list of options
@@ -196,11 +189,11 @@ TEST(LibDhcpTest, packOptions6) {
buf[i]=i+100;
}
- OptionPtr opt1(new Option(Option::V6, 12, buf.begin() + 0, buf.begin() + 5));
- OptionPtr opt2(new Option(Option::V6, 13, buf.begin() + 5, buf.begin() + 8));
- OptionPtr opt3(new Option(Option::V6, 14, buf.begin() + 8, buf.begin() + 10));
- OptionPtr opt4(new Option(Option::V6,256, buf.begin() + 10,buf.begin() + 14));
- OptionPtr opt5(new Option(Option::V6,257, buf.begin() + 14,buf.begin() + 15));
+ OptionPtr opt1(new Option(Option::V6, 1, buf.begin() + 0, buf.begin() + 5));
+ OptionPtr opt2(new Option(Option::V6, 2, buf.begin() + 5, buf.begin() + 8));
+ OptionPtr opt3(new Option(Option::V6, 14, buf.begin() + 8, buf.begin() + 8));
+ OptionPtr opt4(new Option(Option::V6, 6, buf.begin() + 8, buf.begin() + 12));
+ OptionPtr opt5(new Option(Option::V6, 8, buf.begin() + 12, buf.begin() + 14));
opts.insert(pair<int, OptionPtr >(opt1->getType(), opt1));
opts.insert(pair<int, OptionPtr >(opt1->getType(), opt2));
@@ -211,11 +204,11 @@ TEST(LibDhcpTest, packOptions6) {
OutputBuffer assembled(512);
EXPECT_NO_THROW(LibDHCP::packOptions6(assembled, opts));
- EXPECT_EQ(35, assembled.getLength()); // options should take 35 bytes
- EXPECT_EQ(0, memcmp(assembled.getData(), packed, 35) );
+ EXPECT_EQ(sizeof(packed), assembled.getLength());
+ EXPECT_EQ(0, memcmp(assembled.getData(), packed, sizeof(packed)));
}
-TEST(LibDhcpTest, unpackOptions6) {
+TEST_F(LibDhcpTest, unpackOptions6) {
// just couple of random options
// Option is used as a simple option implementation
@@ -224,55 +217,85 @@ TEST(LibDhcpTest, unpackOptions6) {
isc::dhcp::Option::OptionCollection options; // list of options
OptionBuffer buf(512);
- memcpy(&buf[0], packed, 35);
+ memcpy(&buf[0], packed, sizeof(packed));
EXPECT_NO_THROW ({
- LibDHCP::unpackOptions6(OptionBuffer(buf.begin(), buf.begin()+35), options);
+ LibDHCP::unpackOptions6(OptionBuffer(buf.begin(), buf.begin() + sizeof(packed)),
+ options);
});
EXPECT_EQ(options.size(), 5); // there should be 5 options
- isc::dhcp::Option::OptionCollection::const_iterator x = options.find(12);
+ isc::dhcp::Option::OptionCollection::const_iterator x = options.find(1);
ASSERT_FALSE(x == options.end()); // option 1 should exist
- EXPECT_EQ(12, x->second->getType()); // this should be option 12
+ EXPECT_EQ(1, x->second->getType()); // this should be option 1
ASSERT_EQ(9, x->second->len()); // it should be of length 9
- EXPECT_EQ(0, memcmp(&x->second->getData()[0], packed+4, 5)); // data len=5
+ ASSERT_EQ(5, x->second->getData().size());
+ EXPECT_EQ(0, memcmp(&x->second->getData()[0], packed + 4, 5)); // data len=5
- x = options.find(13);
- ASSERT_FALSE(x == options.end()); // option 13 should exist
- EXPECT_EQ(13, x->second->getType()); // this should be option 13
+ x = options.find(2);
+ ASSERT_FALSE(x == options.end()); // option 2 should exist
+ EXPECT_EQ(2, x->second->getType()); // this should be option 2
ASSERT_EQ(7, x->second->len()); // it should be of length 7
- EXPECT_EQ(0, memcmp(&x->second->getData()[0], packed+13, 3)); // data len=3
+ ASSERT_EQ(3, x->second->getData().size());
+ EXPECT_EQ(0, memcmp(&x->second->getData()[0], packed + 13, 3)); // data len=3
x = options.find(14);
- ASSERT_FALSE(x == options.end()); // option 3 should exist
+ ASSERT_FALSE(x == options.end()); // option 14 should exist
EXPECT_EQ(14, x->second->getType()); // this should be option 14
- ASSERT_EQ(6, x->second->len()); // it should be of length 6
- EXPECT_EQ(0, memcmp(&x->second->getData()[0], packed+20, 2)); // data len=2
-
- x = options.find(256);
- ASSERT_FALSE(x == options.end()); // option 256 should exist
- EXPECT_EQ(256, x->second->getType()); // this should be option 256
- ASSERT_EQ(8, x->second->len()); // it should be of length 7
- EXPECT_EQ(0, memcmp(&x->second->getData()[0], packed+26, 4)); // data len=4
-
- x = options.find(257);
- ASSERT_FALSE(x == options.end()); // option 257 should exist
- EXPECT_EQ(257, x->second->getType()); // this should be option 257
- ASSERT_EQ(5, x->second->len()); // it should be of length 5
- EXPECT_EQ(0, memcmp(&x->second->getData()[0], packed+34, 1)); // data len=1
+ ASSERT_EQ(4, x->second->len()); // it should be of length 4
+ EXPECT_EQ(0, x->second->getData().size()); // data len = 0
+
+ x = options.find(6);
+ ASSERT_FALSE(x == options.end()); // option 6 should exist
+ EXPECT_EQ(6, x->second->getType()); // this should be option 6
+ ASSERT_EQ(8, x->second->len()); // it should be of length 8
+ // Option with code 6 is the OPTION_ORO. This option is
+ // represented by the Option6IntArray<uint16_t> class which
+ // comprises the set of uint16_t values. We need to cast the
+ // returned pointer to this type to get values stored in it.
+ boost::shared_ptr<Option6IntArray<uint16_t> > opt_oro =
+ boost::dynamic_pointer_cast<Option6IntArray<uint16_t> >(x->second);
+ // This value will be NULL if cast was unsuccessful. This is the case
+ // when returned option has different type than expected.
+ ASSERT_TRUE(opt_oro);
+ // Get set of uint16_t values.
+ std::vector<uint16_t> opts = opt_oro->getValues();
+ // Prepare the refrence data.
+ std::vector<uint16_t> expected_opts;
+ expected_opts.push_back(0x6C6D); // equivalent to: 108, 109
+ expected_opts.push_back(0x6E6F); // equivalent to 110, 111
+ ASSERT_EQ(expected_opts.size(), opts.size());
+ // Validated if option has been unpacked correctly.
+ EXPECT_TRUE(std::equal(expected_opts.begin(), expected_opts.end(),
+ opts.begin()));
+
+ x = options.find(8);
+ ASSERT_FALSE(x == options.end()); // option 8 should exist
+ EXPECT_EQ(8, x->second->getType()); // this should be option 8
+ ASSERT_EQ(6, x->second->len()); // it should be of length 9
+ // Option with code 8 is OPTION_ELAPSED_TIME. This option is
+ // represented by Option6Int<uint16_t> value that holds single
+ // uint16_t value.
+ boost::shared_ptr<Option6Int<uint16_t> > opt_elapsed_time =
+ boost::dynamic_pointer_cast<Option6Int<uint16_t> >(x->second);
+ // This value will be NULL if cast was unsuccessful. This is the case
+ // when returned option has different type than expected.
+ ASSERT_TRUE(opt_elapsed_time);
+ // Returned value should be equivalent to two byte values: 112, 113
+ EXPECT_EQ(0x7071, opt_elapsed_time->getValue());
x = options.find(0);
EXPECT_TRUE(x == options.end()); // option 0 not found
- x = options.find(1); // 1 is htons(256) on little endians. Worth checking
+ x = options.find(256); // 256 is htons(1) on little endians. Worth checking
EXPECT_TRUE(x == options.end()); // option 1 not found
- x = options.find(2);
+ x = options.find(7);
EXPECT_TRUE(x == options.end()); // option 2 not found
x = options.find(32000);
- EXPECT_TRUE(x == options.end()); // option 32000 not found
+ EXPECT_TRUE(x == options.end()); // option 32000 not found */
}
@@ -284,7 +307,7 @@ static uint8_t v4Opts[] = {
128, 3, 40, 41, 42
};
-TEST(LibDhcpTest, packOptions4) {
+TEST_F(LibDhcpTest, packOptions4) {
vector<uint8_t> payload[5];
for (int i = 0; i < 5; i++) {
@@ -316,7 +339,7 @@ TEST(LibDhcpTest, packOptions4) {
}
-TEST(LibDhcpTest, unpackOptions4) {
+TEST_F(LibDhcpTest, unpackOptions4) {
vector<uint8_t> packed(v4Opts, v4Opts + sizeof(v4Opts));
isc::dhcp::Option::OptionCollection options; // list of options
@@ -375,24 +398,24 @@ TEST(LibDhcpTest, unpackOptions4) {
// @todo Only limited number of option definitions are now created
// This test have to be extended once all option definitions are
// created.
-TEST(LibDhcpTest, initStdOptionDefs) {
- LibDhcpTest::testInitOptionDefs6(D6O_CLIENTID, OptionBuffer(14, 1),
+TEST_F(LibDhcpTest, stdOptionDefs6) {
+ LibDhcpTest::testStdOptionDefs6(D6O_CLIENTID, OptionBuffer(14, 1),
typeid(Option));
- LibDhcpTest::testInitOptionDefs6(D6O_SERVERID, OptionBuffer(14, 1),
+ LibDhcpTest::testStdOptionDefs6(D6O_SERVERID, OptionBuffer(14, 1),
typeid(Option));
- LibDhcpTest::testInitOptionDefs6(D6O_IA_NA, OptionBuffer(12, 1),
+ LibDhcpTest::testStdOptionDefs6(D6O_IA_NA, OptionBuffer(12, 1),
typeid(Option6IA));
- LibDhcpTest::testInitOptionDefs6(D6O_IAADDR, OptionBuffer(24, 1),
+ LibDhcpTest::testStdOptionDefs6(D6O_IAADDR, OptionBuffer(24, 1),
typeid(Option6IAAddr));
- LibDhcpTest::testInitOptionDefs6(D6O_ORO, OptionBuffer(10, 1),
+ LibDhcpTest::testStdOptionDefs6(D6O_ORO, OptionBuffer(10, 1),
typeid(Option6IntArray<uint16_t>));
- LibDhcpTest::testInitOptionDefs6(D6O_ELAPSED_TIME, OptionBuffer(2, 1),
+ LibDhcpTest::testStdOptionDefs6(D6O_ELAPSED_TIME, OptionBuffer(2, 1),
typeid(Option6Int<uint16_t>));
- LibDhcpTest::testInitOptionDefs6(D6O_STATUS_CODE, OptionBuffer(10, 1),
+ LibDhcpTest::testStdOptionDefs6(D6O_STATUS_CODE, OptionBuffer(10, 1),
typeid(Option));
- LibDhcpTest::testInitOptionDefs6(D6O_RAPID_COMMIT, OptionBuffer(),
+ LibDhcpTest::testStdOptionDefs6(D6O_RAPID_COMMIT, OptionBuffer(),
typeid(Option));
- LibDhcpTest::testInitOptionDefs6(D6O_NAME_SERVERS, OptionBuffer(32, 1),
+ LibDhcpTest::testStdOptionDefs6(D6O_NAME_SERVERS, OptionBuffer(32, 1),
typeid(Option6AddrLst));
}
diff --git a/src/lib/dhcp/tests/option6_ia_unittest.cc b/src/lib/dhcp/tests/option6_ia_unittest.cc
index bc72242..1ee618a 100644
--- a/src/lib/dhcp/tests/option6_ia_unittest.cc
+++ b/src/lib/dhcp/tests/option6_ia_unittest.cc
@@ -206,7 +206,7 @@ TEST_F(Option6IATest, suboptions_unpack) {
Option6IA* ia = 0;
EXPECT_NO_THROW({
- ia = new Option6IA(D6O_IA_NA, buf_.begin() + 4, buf_.begin() + sizeof(expected));
+ ia = new Option6IA(D6O_IA_NA, buf_.begin() + 4, buf_.begin() + sizeof(expected));
});
ASSERT_TRUE(ia);
diff --git a/src/lib/dhcp/tests/option_definition_unittest.cc b/src/lib/dhcp/tests/option_definition_unittest.cc
index bdf3edf..9fa39c4 100644
--- a/src/lib/dhcp/tests/option_definition_unittest.cc
+++ b/src/lib/dhcp/tests/option_definition_unittest.cc
@@ -47,32 +47,35 @@ public:
OptionDefinitionTest() { }
};
+// The purpose of this test is to verify that OptionDefinition
+// constructor initializes its members correctly.
TEST_F(OptionDefinitionTest, constructor) {
// Specify the option data type as string. This should get converted
// to enum value returned by getType().
OptionDefinition opt_def1("OPTION_CLIENTID", 1, "string");
EXPECT_EQ("OPTION_CLIENTID", opt_def1.getName());
+
EXPECT_EQ(1, opt_def1.getCode());
- EXPECT_EQ(OptionDefinition::STRING_TYPE, opt_def1.getType());
+ EXPECT_EQ(OPT_STRING_TYPE, opt_def1.getType());
EXPECT_FALSE(opt_def1.getArrayType());
EXPECT_NO_THROW(opt_def1.validate());
// Specify the option data type as an enum value.
OptionDefinition opt_def2("OPTION_RAPID_COMMIT", 14,
- OptionDefinition::EMPTY_TYPE);
+ OPT_EMPTY_TYPE);
EXPECT_EQ("OPTION_RAPID_COMMIT", opt_def2.getName());
EXPECT_EQ(14, opt_def2.getCode());
- EXPECT_EQ(OptionDefinition::EMPTY_TYPE, opt_def2.getType());
+ EXPECT_EQ(OPT_EMPTY_TYPE, opt_def2.getType());
EXPECT_FALSE(opt_def2.getArrayType());
EXPECT_NO_THROW(opt_def1.validate());
// Check if it is possible to set that option is an array.
OptionDefinition opt_def3("OPTION_NIS_SERVERS", 27,
- OptionDefinition::IPV6_ADDRESS_TYPE,
+ OPT_IPV6_ADDRESS_TYPE,
true);
EXPECT_EQ("OPTION_NIS_SERVERS", opt_def3.getName());
EXPECT_EQ(27, opt_def3.getCode());
- EXPECT_EQ(OptionDefinition::IPV6_ADDRESS_TYPE, opt_def3.getType());
+ EXPECT_EQ(OPT_IPV6_ADDRESS_TYPE, opt_def3.getType());
EXPECT_TRUE(opt_def3.getArrayType());
EXPECT_NO_THROW(opt_def3.validate());
@@ -81,23 +84,27 @@ TEST_F(OptionDefinitionTest, constructor) {
// it has been created.
EXPECT_NO_THROW(
OptionDefinition opt_def4("OPTION_SERVERID",
- OptionDefinition::UNKNOWN_TYPE + 10,
- OptionDefinition::STRING_TYPE);
+ OPT_UNKNOWN_TYPE + 10,
+ OPT_STRING_TYPE);
);
}
+// The purpose of this test is to verify that various data fields
+// can be specified for an option definition when this definition
+// is marked as 'record' and that fields can't be added if option
+// definition is not marked as 'record'.
TEST_F(OptionDefinitionTest, addRecordField) {
// We can only add fields to record if the option type has been
// specified as 'record'. We try all other types but 'record'
// here and expect exception to be thrown.
- for (int i = 0; i < OptionDefinition::UNKNOWN_TYPE; ++i) {
+ for (int i = 0; i < OPT_UNKNOWN_TYPE; ++i) {
// Do not try for 'record' type because this is the only
// type for which adding record will succeed.
- if (i == OptionDefinition::RECORD_TYPE) {
+ if (i == OPT_RECORD_TYPE) {
continue;
}
OptionDefinition opt_def("OPTION_IAADDR", 5,
- static_cast<OptionDefinition::DataType>(i));
+ static_cast<OptionDataType>(i));
EXPECT_THROW(opt_def.addRecordField("uint8"), isc::InvalidOperation);
}
@@ -106,54 +113,88 @@ TEST_F(OptionDefinitionTest, addRecordField) {
EXPECT_NO_THROW(opt_def.addRecordField("ipv6-address"));
EXPECT_NO_THROW(opt_def.addRecordField("uint32"));
// It should not matter if we specify field type by its name or using enum.
- EXPECT_NO_THROW(opt_def.addRecordField(OptionDefinition::UINT32_TYPE));
+ EXPECT_NO_THROW(opt_def.addRecordField(OPT_UINT32_TYPE));
// Check what we have actually added.
OptionDefinition::RecordFieldsCollection fields = opt_def.getRecordFields();
ASSERT_EQ(3, fields.size());
- EXPECT_EQ(OptionDefinition::IPV6_ADDRESS_TYPE, fields[0]);
- EXPECT_EQ(OptionDefinition::UINT32_TYPE, fields[1]);
- EXPECT_EQ(OptionDefinition::UINT32_TYPE, fields[2]);
+ EXPECT_EQ(OPT_IPV6_ADDRESS_TYPE, fields[0]);
+ EXPECT_EQ(OPT_UINT32_TYPE, fields[1]);
+ EXPECT_EQ(OPT_UINT32_TYPE, fields[2]);
// Let's try some more negative scenarios: use invalid data types.
EXPECT_THROW(opt_def.addRecordField("unknown_type_xyz"), isc::BadValue);
- OptionDefinition::DataType invalid_type =
- static_cast<OptionDefinition::DataType>(OptionDefinition::UNKNOWN_TYPE + 10);
+ OptionDataType invalid_type =
+ static_cast<OptionDataType>(OPT_UNKNOWN_TYPE + 10);
EXPECT_THROW(opt_def.addRecordField(invalid_type), isc::BadValue);
+
+ // It is bad if we use 'record' option type but don't specify
+ // at least two fields.
+ OptionDefinition opt_def2("OPTION_EMPTY_RECORD", 100, "record");
+ EXPECT_THROW(opt_def2.validate(), MalformedOptionDefinition);
+ opt_def2.addRecordField("uint8");
+ EXPECT_THROW(opt_def2.validate(), MalformedOptionDefinition);
+ opt_def2.addRecordField("uint32");
+ EXPECT_NO_THROW(opt_def2.validate());
}
+// The purpose of this test is to check that validate() function
+// reports errors for invalid option definitions.
TEST_F(OptionDefinitionTest, validate) {
// Not supported option type string is not allowed.
OptionDefinition opt_def1("OPTION_CLIENTID", D6O_CLIENTID, "non-existent-type");
- EXPECT_THROW(opt_def1.validate(), isc::OutOfRange);
+ EXPECT_THROW(opt_def1.validate(), MalformedOptionDefinition);
// Not supported option type enum value is not allowed.
- OptionDefinition opt_def2("OPTION_CLIENTID", D6O_CLIENTID, OptionDefinition::UNKNOWN_TYPE);
- EXPECT_THROW(opt_def2.validate(), isc::OutOfRange);
+ OptionDefinition opt_def2("OPTION_CLIENTID", D6O_CLIENTID, OPT_UNKNOWN_TYPE);
+ EXPECT_THROW(opt_def2.validate(), MalformedOptionDefinition);
OptionDefinition opt_def3("OPTION_CLIENTID", D6O_CLIENTID,
- static_cast<OptionDefinition::DataType>(OptionDefinition::UNKNOWN_TYPE
+ static_cast<OptionDataType>(OPT_UNKNOWN_TYPE
+ 2));
- EXPECT_THROW(opt_def3.validate(), isc::OutOfRange);
-
+ EXPECT_THROW(opt_def3.validate(), MalformedOptionDefinition);
+
// Empty option name is not allowed.
OptionDefinition opt_def4("", D6O_CLIENTID, "string");
- EXPECT_THROW(opt_def4.validate(), isc::BadValue);
+ EXPECT_THROW(opt_def4.validate(), MalformedOptionDefinition);
// Option name must not contain spaces.
OptionDefinition opt_def5(" OPTION_CLIENTID", D6O_CLIENTID, "string");
- EXPECT_THROW(opt_def5.validate(), isc::BadValue);
+ EXPECT_THROW(opt_def5.validate(), MalformedOptionDefinition);
- OptionDefinition opt_def6("OPTION CLIENTID", D6O_CLIENTID, "string");
- EXPECT_THROW(opt_def6.validate(), isc::BadValue);
+ // Option name must not contain spaces.
+ OptionDefinition opt_def6("OPTION CLIENTID", D6O_CLIENTID, "string", true);
+ EXPECT_THROW(opt_def6.validate(), MalformedOptionDefinition);
+
+ // Having array of strings does not make sense because there is no way
+ // to determine string's length.
+ OptionDefinition opt_def7("OPTION_CLIENTID", D6O_CLIENTID, "string", true);
+ EXPECT_THROW(opt_def7.validate(), MalformedOptionDefinition);
+
+ // It does not make sense to have string field within the record before
+ // other fields because there is no way to determine the length of this
+ // string and thus there is no way to determine where the other field
+ // begins.
+ OptionDefinition opt_def8("OPTION_STATUS_CODE", D6O_STATUS_CODE,
+ "record");
+ opt_def8.addRecordField("string");
+ opt_def8.addRecordField("uint16");
+ EXPECT_THROW(opt_def8.validate(), MalformedOptionDefinition);
+
+ // ... but it is ok if the string value is the last one.
+ OptionDefinition opt_def9("OPTION_STATUS_CODE", D6O_STATUS_CODE,
+ "record");
+ opt_def9.addRecordField("uint8");
+ opt_def9.addRecordField("string");
}
-TEST_F(OptionDefinitionTest, factoryAddrList6) {
+
+// The purpose of this test is to verify that option definition
+// that comprises array of IPv6 addresses will return an instance
+// of option with a list of IPv6 addresses.
+TEST_F(OptionDefinitionTest, ipv6AddressArray) {
OptionDefinition opt_def("OPTION_NIS_SERVERS", D6O_NIS_SERVERS,
"ipv6-address", true);
- Option::Factory* factory(NULL);
- EXPECT_NO_THROW(factory = opt_def.getFactory());
- ASSERT_TRUE(factory != NULL);
// Create a list of some V6 addresses.
std::vector<asiolink::IOAddress> addrs;
@@ -176,7 +217,7 @@ TEST_F(OptionDefinitionTest, factoryAddrList6) {
// the provided buffer.
OptionPtr option_v6;
ASSERT_NO_THROW(
- option_v6 = factory(Option::V6, D6O_NIS_SERVERS, buf);
+ option_v6 = opt_def.optionFactory(Option::V6, D6O_NIS_SERVERS, buf);
);
ASSERT_TRUE(typeid(*option_v6) == typeid(Option6AddrLst));
boost::shared_ptr<Option6AddrLst> option_cast_v6 =
@@ -195,17 +236,64 @@ TEST_F(OptionDefinitionTest, factoryAddrList6) {
buf.insert(buf.end(), 1, 1);
// It should throw exception then.
EXPECT_THROW(
- factory(Option::V6, D6O_NIS_SERVERS, buf),
- isc::OutOfRange
+ opt_def.optionFactory(Option::V6, D6O_NIS_SERVERS, buf),
+ InvalidOptionValue
+ );
+}
+
+// The purpose of this test is to verify that option definition
+// that comprises array of IPv6 addresses will return an instance
+// of option with a list of IPv6 addresses. Array of IPv6 addresses
+// is specified as a vector of strings (each string represents single
+// IPv6 address).
+TEST_F(OptionDefinitionTest, ipv6AddressArrayTokenized) {
+ OptionDefinition opt_def("OPTION_NIS_SERVERS", D6O_NIS_SERVERS,
+ "ipv6-address", true);
+
+ // Create a vector of some V6 addresses.
+ std::vector<asiolink::IOAddress> addrs;
+ addrs.push_back(asiolink::IOAddress("2001:0db8::ff00:0042:8329"));
+ addrs.push_back(asiolink::IOAddress("2001:0db8::ff00:0042:2319"));
+ addrs.push_back(asiolink::IOAddress("::1"));
+ addrs.push_back(asiolink::IOAddress("::2"));
+
+ // Create a vector of strings representing addresses given above.
+ std::vector<std::string> addrs_str;
+ for (std::vector<asiolink::IOAddress>::const_iterator it = addrs.begin();
+ it != addrs.end(); ++it) {
+ addrs_str.push_back(it->toText());
+ }
+
+ // Create DHCPv6 option using the list of IPv6 addresses given in the
+ // string form.
+ OptionPtr option_v6;
+ ASSERT_NO_THROW(
+ option_v6 = opt_def.optionFactory(Option::V6, D6O_NIS_SERVERS,
+ addrs_str);
);
+ // Non-null pointer option is supposed to be returned and it
+ // should have Option6AddrLst type.
+ ASSERT_TRUE(option_v6);
+ ASSERT_TRUE(typeid(*option_v6) == typeid(Option6AddrLst));
+ // Cast to the actual option type to get IPv6 addresses from it.
+ boost::shared_ptr<Option6AddrLst> option_cast_v6 =
+ boost::static_pointer_cast<Option6AddrLst>(option_v6);
+ // Check that cast was successful.
+ ASSERT_TRUE(option_cast_v6);
+ // Get the list of parsed addresses from the option object.
+ std::vector<asiolink::IOAddress> addrs_returned =
+ option_cast_v6->getAddresses();
+ // Returned addresses must match the addresses that have been used to create
+ // the option instance.
+ EXPECT_TRUE(std::equal(addrs.begin(), addrs.end(), addrs_returned.begin()));
}
-TEST_F(OptionDefinitionTest, factoryAddrList4) {
+// The purpose of this test is to verify that option definition
+// that comprises array of IPv4 addresses will return an instance
+// of option with a list of IPv4 addresses.
+TEST_F(OptionDefinitionTest, ipv4AddressArray) {
OptionDefinition opt_def("OPTION_NAME_SERVERS", D6O_NIS_SERVERS,
"ipv4-address", true);
- Option::Factory* factory(NULL);
- EXPECT_NO_THROW(factory = opt_def.getFactory());
- ASSERT_TRUE(factory != NULL);
// Create a list of some V6 addresses.
std::vector<asiolink::IOAddress> addrs;
@@ -228,7 +316,7 @@ TEST_F(OptionDefinitionTest, factoryAddrList4) {
// the provided buffer.
OptionPtr option_v4;
ASSERT_NO_THROW(
- option_v4 = factory(Option::V4, DHO_NAME_SERVERS, buf)
+ option_v4 = opt_def.optionFactory(Option::V4, DHO_NAME_SERVERS, buf)
);
ASSERT_TRUE(typeid(*option_v4) == typeid(Option4AddrLst));
// Get the list of parsed addresses from the option object.
@@ -245,19 +333,66 @@ TEST_F(OptionDefinitionTest, factoryAddrList4) {
// fulfilled anymore.
buf.insert(buf.end(), 1, 1);
// It should throw exception then.
- EXPECT_THROW(factory(Option::V4, DHO_NIS_SERVERS, buf), isc::OutOfRange);
+ EXPECT_THROW(opt_def.optionFactory(Option::V4, DHO_NIS_SERVERS, buf),
+ InvalidOptionValue);
+}
+
+// The purpose of this test is to verify that option definition
+// that comprises array of IPv4 addresses will return an instance
+// of option with a list of IPv4 addresses. The array of IPv4 addresses
+// is specified as a vector of strings (each string represents single
+// IPv4 address).
+TEST_F(OptionDefinitionTest, ipv4AddressArrayTokenized) {
+ OptionDefinition opt_def("OPTION_NIS_SERVERS", DHO_NIS_SERVERS,
+ "ipv4-address", true);
+
+ // Create a vector of some V6 addresses.
+ std::vector<asiolink::IOAddress> addrs;
+ addrs.push_back(asiolink::IOAddress("192.168.0.1"));
+ addrs.push_back(asiolink::IOAddress("172.16.1.1"));
+ addrs.push_back(asiolink::IOAddress("127.0.0.1"));
+ addrs.push_back(asiolink::IOAddress("213.41.23.12"));
+
+ // Create a vector of strings representing addresses given above.
+ std::vector<std::string> addrs_str;
+ for (std::vector<asiolink::IOAddress>::const_iterator it = addrs.begin();
+ it != addrs.end(); ++it) {
+ addrs_str.push_back(it->toText());
+ }
+
+ // Create DHCPv4 option using the list of IPv4 addresses given in the
+ // string form.
+ OptionPtr option_v4;
+ ASSERT_NO_THROW(
+ option_v4 = opt_def.optionFactory(Option::V4, DHO_NIS_SERVERS,
+ addrs_str);
+ );
+ // Non-null pointer option is supposed to be returned and it
+ // should have Option6AddrLst type.
+ ASSERT_TRUE(option_v4);
+ ASSERT_TRUE(typeid(*option_v4) == typeid(Option4AddrLst));
+ // Cast to the actual option type to get IPv4 addresses from it.
+ boost::shared_ptr<Option4AddrLst> option_cast_v4 =
+ boost::static_pointer_cast<Option4AddrLst>(option_v4);
+ // Check that cast was successful.
+ ASSERT_TRUE(option_cast_v4);
+ // Get the list of parsed addresses from the option object.
+ std::vector<asiolink::IOAddress> addrs_returned =
+ option_cast_v4->getAddresses();
+ // Returned addresses must match the addresses that have been used to create
+ // the option instance.
+ EXPECT_TRUE(std::equal(addrs.begin(), addrs.end(), addrs_returned.begin()));
}
-TEST_F(OptionDefinitionTest, factoryEmpty) {
+// The purpose of thie test is to verify that option definition for
+// 'empty' option can be created and that it returns 'empty' option.
+TEST_F(OptionDefinitionTest, empty) {
OptionDefinition opt_def("OPTION_RAPID_COMMIT", D6O_RAPID_COMMIT, "empty");
- Option::Factory* factory(NULL);
- EXPECT_NO_THROW(factory = opt_def.getFactory());
- ASSERT_TRUE(factory != NULL);
// Create option instance and provide empty buffer as expected.
OptionPtr option_v6;
ASSERT_NO_THROW(
- option_v6 = factory(Option::V6, D6O_RAPID_COMMIT, OptionBuffer())
+ option_v6 = opt_def.optionFactory(Option::V6, D6O_RAPID_COMMIT, OptionBuffer())
);
ASSERT_TRUE(typeid(*option_v6) == typeid(Option));
// Expect 'empty' DHCPv6 option.
@@ -266,31 +401,23 @@ TEST_F(OptionDefinitionTest, factoryEmpty) {
EXPECT_EQ(0, option_v6->getData().size());
// Repeat the same test scenario for DHCPv4 option.
- EXPECT_THROW(factory(Option::V4, 214, OptionBuffer(2)),isc::BadValue);
-
OptionPtr option_v4;
- ASSERT_NO_THROW(option_v4 = factory(Option::V4, 214, OptionBuffer()));
+ ASSERT_NO_THROW(option_v4 = opt_def.optionFactory(Option::V4, 214, OptionBuffer()));
// Expect 'empty' DHCPv4 option.
EXPECT_EQ(Option::V4, option_v4->getUniverse());
EXPECT_EQ(2, option_v4->getHeaderLen());
EXPECT_EQ(0, option_v4->getData().size());
-
- // This factory produces empty option (consisting of option type
- // and length). Attempt to provide some data in the buffer should
- // result in exception.
- EXPECT_THROW(factory(Option::V6, D6O_RAPID_COMMIT,OptionBuffer(2)),isc::BadValue);
}
-TEST_F(OptionDefinitionTest, factoryBinary) {
+// The purpose of this test is to verify that definition can be
+// creates for the option that holds binary data.
+TEST_F(OptionDefinitionTest, binary) {
// Binary option is the one that is represented by the generic
// Option class. In fact all options can be represented by this
// class but for some of them it is just natural. The SERVERID
// option consists of the option code, length and binary data so
// this one was picked for this test.
OptionDefinition opt_def("OPTION_SERVERID", D6O_SERVERID, "binary");
- Option::Factory* factory(NULL);
- EXPECT_NO_THROW(factory = opt_def.getFactory());
- ASSERT_TRUE(factory != NULL);
// Prepare some dummy data (serverid): 0, 1, 2 etc.
OptionBuffer buf(14);
@@ -302,7 +429,7 @@ TEST_F(OptionDefinitionTest, factoryBinary) {
// object of the type Option should be returned.
OptionPtr option_v6;
ASSERT_NO_THROW(
- option_v6 = factory(Option::V6, D6O_SERVERID, buf);
+ option_v6 = opt_def.optionFactory(Option::V6, D6O_SERVERID, buf);
);
// Expect base option type returned.
ASSERT_TRUE(typeid(*option_v6) == typeid(Option));
@@ -320,7 +447,7 @@ TEST_F(OptionDefinitionTest, factoryBinary) {
// Repeat the same test scenario for DHCPv4 option.
OptionPtr option_v4;
- ASSERT_NO_THROW(option_v4 = factory(Option::V4, 214, buf));
+ ASSERT_NO_THROW(option_v4 = opt_def.optionFactory(Option::V4, 214, buf));
// Expect 'empty' DHCPv4 option.
EXPECT_EQ(Option::V4, option_v4->getUniverse());
EXPECT_EQ(2, option_v4->getHeaderLen());
@@ -331,19 +458,69 @@ TEST_F(OptionDefinitionTest, factoryBinary) {
buf.begin()));
}
-TEST_F(OptionDefinitionTest, factoryIA6) {
+// The purpose of this test is to verify that definition can be
+// creates for the option that holds binary data and that the
+// binary data can be specified in 'comma separated values'
+// format.
+TEST_F(OptionDefinitionTest, binaryTokenized) {
+ OptionDefinition opt_def("OPTION_FOO", 1000, "binary", true);
+
+ // Prepare some dummy data (serverid): 0, 1, 2 etc.
+ OptionBuffer buf(16);
+ for (int i = 0; i < buf.size(); ++i) {
+ buf[i] = i;
+ }
+ std::vector<std::string> hex_data;
+ hex_data.push_back("00010203");
+ hex_data.push_back("04050607");
+ hex_data.push_back("08090A0B0C0D0E0F");
+
+ // Create option instance with the factory function.
+ // If the OptionDefinition code works properly than
+ // object of the type Option should be returned.
+ OptionPtr option_v6;
+ ASSERT_NO_THROW(
+ option_v6 = opt_def.optionFactory(Option::V6, 1000, hex_data);
+ );
+ // Expect base option type returned.
+ ASSERT_TRUE(typeid(*option_v6) == typeid(Option));
+ // Sanity check on universe, length and size. These are
+ // the basic parameters identifying any option.
+ EXPECT_EQ(Option::V6, option_v6->getUniverse());
+ EXPECT_EQ(4, option_v6->getHeaderLen());
+ ASSERT_EQ(buf.size(), option_v6->getData().size());
+
+ // Get data from the option and compare against reference buffer.
+ // They are expected to match.
+ EXPECT_TRUE(std::equal(option_v6->getData().begin(),
+ option_v6->getData().end(),
+ buf.begin()));
+
+ // Repeat the same test scenario for DHCPv4 option.
+ OptionPtr option_v4;
+ ASSERT_NO_THROW(option_v4 = opt_def.optionFactory(Option::V4, 214, hex_data));
+ EXPECT_EQ(Option::V4, option_v4->getUniverse());
+ EXPECT_EQ(2, option_v4->getHeaderLen());
+ ASSERT_EQ(buf.size(), option_v4->getData().size());
+
+ EXPECT_TRUE(std::equal(option_v6->getData().begin(),
+ option_v6->getData().end(),
+ buf.begin()));
+}
+
+// The purpose of this test is to verify that definition can be created
+// for option that comprises record of data. In this particular test
+// the IA_NA option is used. This option comprises three uint32 fields.
+TEST_F(OptionDefinitionTest, recordIA6) {
// This option consists of IAID, T1 and T2 fields (each 4 bytes long).
const int option6_ia_len = 12;
// Get the factory function pointer.
- OptionDefinition opt_def("OPTION_IA_NA", D6O_IA_NA, "record", true);
+ OptionDefinition opt_def("OPTION_IA_NA", D6O_IA_NA, "record", false);
// Each data field is uint32.
for (int i = 0; i < 3; ++i) {
EXPECT_NO_THROW(opt_def.addRecordField("uint32"));
}
- Option::Factory* factory(NULL);
- EXPECT_NO_THROW(factory = opt_def.getFactory());
- ASSERT_TRUE(factory != NULL);
// Check the positive scenario.
OptionBuffer buf(12);
@@ -351,7 +528,7 @@ TEST_F(OptionDefinitionTest, factoryIA6) {
buf[i] = i;
}
OptionPtr option_v6;
- ASSERT_NO_THROW(option_v6 = factory(Option::V6, D6O_IA_NA, buf));
+ ASSERT_NO_THROW(option_v6 = opt_def.optionFactory(Option::V6, D6O_IA_NA, buf));
ASSERT_TRUE(typeid(*option_v6) == typeid(Option6IA));
boost::shared_ptr<Option6IA> option_cast_v6 =
boost::static_pointer_cast<Option6IA>(option_v6);
@@ -359,25 +536,18 @@ TEST_F(OptionDefinitionTest, factoryIA6) {
EXPECT_EQ(0x04050607, option_cast_v6->getT1());
EXPECT_EQ(0x08090A0B, option_cast_v6->getT2());
- // This should work for DHCPv6 only, try passing invalid universe value.
- EXPECT_THROW(
- factory(Option::V4, D6O_IA_NA, OptionBuffer(option6_ia_len)),
- isc::BadValue
- );
- // The length of the buffer must be 12 bytes.
+ // The length of the buffer must be at least 12 bytes.
// Check too short buffer.
EXPECT_THROW(
- factory(Option::V6, D6O_IA_NA, OptionBuffer(option6_ia_len - 1)),
- isc::OutOfRange
+ opt_def.optionFactory(Option::V6, D6O_IA_NA, OptionBuffer(option6_ia_len - 1)),
+ InvalidOptionValue
);
- // Check too long buffer.
- EXPECT_THROW(
- factory(Option::V6, D6O_IA_NA, OptionBuffer(option6_ia_len + 1)),
- isc::OutOfRange
- );
}
-TEST_F(OptionDefinitionTest, factoryIAAddr6) {
+// The purpose of this test is to verify that definition can be created
+// for option that comprises record of data. In this particular test
+// the IAADDR option is used.
+TEST_F(OptionDefinitionTest, recordIAAddr6) {
// This option consists of IPV6 Address (16 bytes) and preferred-lifetime and
// valid-lifetime fields (each 4 bytes long).
const int option6_iaaddr_len = 24;
@@ -386,9 +556,6 @@ TEST_F(OptionDefinitionTest, factoryIAAddr6) {
ASSERT_NO_THROW(opt_def.addRecordField("ipv6-address"));
ASSERT_NO_THROW(opt_def.addRecordField("uint32"));
ASSERT_NO_THROW(opt_def.addRecordField("uint32"));
- Option::Factory* factory(NULL);
- EXPECT_NO_THROW(factory = opt_def.getFactory());
- ASSERT_TRUE(factory != NULL);
// Check the positive scenario.
OptionPtr option_v6;
@@ -403,7 +570,7 @@ TEST_F(OptionDefinitionTest, factoryIAAddr6) {
for (int i = 0; i < option6_iaaddr_len - asiolink::V6ADDRESS_LEN; ++i) {
buf.push_back(i);
}
- ASSERT_NO_THROW(option_v6 = factory(Option::V6, D6O_IAADDR, buf));
+ ASSERT_NO_THROW(option_v6 = opt_def.optionFactory(Option::V6, D6O_IAADDR, buf));
ASSERT_TRUE(typeid(*option_v6) == typeid(Option6IAAddr));
boost::shared_ptr<Option6IAAddr> option_cast_v6 =
boost::static_pointer_cast<Option6IAAddr>(option_v6);
@@ -411,44 +578,54 @@ TEST_F(OptionDefinitionTest, factoryIAAddr6) {
EXPECT_EQ(0x00010203, option_cast_v6->getPreferred());
EXPECT_EQ(0x04050607, option_cast_v6->getValid());
- // This should work for DHCPv6 only, try passing invalid universe value.
- EXPECT_THROW(
- factory(Option::V4, D6O_IAADDR, OptionBuffer(option6_iaaddr_len)),
- isc::BadValue
- );
- // The length of the buffer must be 12 bytes.
+ // The length of the buffer must be at least 12 bytes.
// Check too short buffer.
EXPECT_THROW(
- factory(Option::V6, D6O_IAADDR, OptionBuffer(option6_iaaddr_len - 1)),
- isc::OutOfRange
+ opt_def.optionFactory(Option::V6, D6O_IAADDR, OptionBuffer(option6_iaaddr_len - 1)),
+ InvalidOptionValue
);
- // Check too long buffer.
- EXPECT_THROW(
- factory(Option::V6, D6O_IAADDR, OptionBuffer(option6_iaaddr_len + 1)),
- isc::OutOfRange
- );
}
-TEST_F(OptionDefinitionTest, factoryIntegerInvalidType) {
- // The template function factoryInteger<> accepts integer values only
- // as template typename. Here we try passing different type and
- // see if it rejects it.
- EXPECT_THROW(
- OptionDefinition::factoryInteger<bool>(Option::V6, D6O_PREFERENCE, OptionBuffer(1)),
- isc::dhcp::InvalidDataType
- );
+// The purpose of this test is to verify that definition can be created
+// for option that comprises record of data. In this particular test
+// the IAADDR option is used. The data for the option is speicifed as
+// a vector of strings. Each string carries the data for the corresponding
+// data field.
+TEST_F(OptionDefinitionTest, recordIAAddr6Tokenized) {
+ // This option consists of IPV6 Address (16 bytes) and preferred-lifetime and
+ // valid-lifetime fields (each 4 bytes long).
+ OptionDefinition opt_def("OPTION_IAADDR", D6O_IAADDR, "record");
+ ASSERT_NO_THROW(opt_def.addRecordField("ipv6-address"));
+ ASSERT_NO_THROW(opt_def.addRecordField("uint32"));
+ ASSERT_NO_THROW(opt_def.addRecordField("uint32"));
+
+ // Check the positive scenario.
+ std::vector<std::string> data_field_values;
+ data_field_values.push_back("2001:0db8::ff00:0042:8329");
+ data_field_values.push_back("1234");
+ data_field_values.push_back("5678");
+
+ OptionPtr option_v6;
+ ASSERT_NO_THROW(option_v6 = opt_def.optionFactory(Option::V6, D6O_IAADDR,
+ data_field_values));
+ ASSERT_TRUE(typeid(*option_v6) == typeid(Option6IAAddr));
+ boost::shared_ptr<Option6IAAddr> option_cast_v6 =
+ boost::static_pointer_cast<Option6IAAddr>(option_v6);
+ EXPECT_EQ("2001:db8::ff00:42:8329", option_cast_v6->getAddress().toText());
+ EXPECT_EQ(1234, option_cast_v6->getPreferred());
+ EXPECT_EQ(5678, option_cast_v6->getValid());
}
-TEST_F(OptionDefinitionTest, factoryUint8) {
+// The purpose of this test is to verify that definition for option that
+// comprises single uint8 value can be created and that this definition
+// can be used to create an option with single uint8 value.
+TEST_F(OptionDefinitionTest, uint8) {
OptionDefinition opt_def("OPTION_PREFERENCE", D6O_PREFERENCE, "uint8");
- Option::Factory* factory(NULL);
- EXPECT_NO_THROW(factory = opt_def.getFactory());
- ASSERT_TRUE(factory != NULL);
OptionPtr option_v6;
// Try to use correct buffer length = 1 byte.
ASSERT_NO_THROW(
- option_v6 = factory(Option::V6, D6O_PREFERENCE, OptionBuffer(1, 1));
+ option_v6 = opt_def.optionFactory(Option::V6, D6O_PREFERENCE, OptionBuffer(1, 1));
);
ASSERT_TRUE(typeid(*option_v6) == typeid(Option6Int<uint8_t>));
// Validate the value.
@@ -456,26 +633,47 @@ TEST_F(OptionDefinitionTest, factoryUint8) {
boost::static_pointer_cast<Option6Int<uint8_t> >(option_v6);
EXPECT_EQ(1, option_cast_v6->getValue());
- // Try to provide too large buffer. Expect exception.
+ // Try to provide zero-length buffer. Expect exception.
EXPECT_THROW(
- option_v6 = factory(Option::V6, D6O_PREFERENCE, OptionBuffer(3)),
- isc::OutOfRange
+ option_v6 = opt_def.optionFactory(Option::V6, D6O_PREFERENCE, OptionBuffer()),
+ InvalidOptionValue
);
- // Try to provide zero-length buffer. Expect exception.
- EXPECT_THROW(
- option_v6 = factory(Option::V6, D6O_PREFERENCE, OptionBuffer()),
- isc::OutOfRange
+ // @todo Add more cases for DHCPv4
+}
+
+// The purpose of this test is to verify that definition for option that
+// comprises single uint8 value can be created and that this definition
+// can be used to create an option with single uint8 value.
+TEST_F(OptionDefinitionTest, uint8Tokenized) {
+ OptionDefinition opt_def("OPTION_PREFERENCE", D6O_PREFERENCE, "uint8");
+
+ OptionPtr option_v6;
+ std::vector<std::string> values;
+ values.push_back("123");
+ values.push_back("456");
+ try {
+ option_v6 = opt_def.optionFactory(Option::V6, D6O_PREFERENCE, values);
+ } catch (std::exception& ex) {
+ std::cout << ex.what() << std::endl;
+ }
+ ASSERT_NO_THROW(
+ option_v6 = opt_def.optionFactory(Option::V6, D6O_PREFERENCE, values);
);
+ ASSERT_TRUE(typeid(*option_v6) == typeid(Option6Int<uint8_t>));
+ // Validate the value.
+ boost::shared_ptr<Option6Int<uint8_t> > option_cast_v6 =
+ boost::static_pointer_cast<Option6Int<uint8_t> >(option_v6);
+ EXPECT_EQ(123, option_cast_v6->getValue());
// @todo Add more cases for DHCPv4
}
-TEST_F(OptionDefinitionTest, factoryUint16) {
+// The purpose of this test is to verify that definition for option that
+// comprises single uint16 value can be created and that this definition
+// can be used to create an option with single uint16 value.
+TEST_F(OptionDefinitionTest, uint16) {
OptionDefinition opt_def("OPTION_ELAPSED_TIME", D6O_ELAPSED_TIME, "uint16");
- Option::Factory* factory(NULL);
- EXPECT_NO_THROW(factory = opt_def.getFactory());
- ASSERT_TRUE(factory != NULL);
OptionPtr option_v6;
// Try to use correct buffer length = 2 bytes.
@@ -483,7 +681,7 @@ TEST_F(OptionDefinitionTest, factoryUint16) {
buf.push_back(1);
buf.push_back(2);
ASSERT_NO_THROW(
- option_v6 = factory(Option::V6, D6O_ELAPSED_TIME, buf);
+ option_v6 = opt_def.optionFactory(Option::V6, D6O_ELAPSED_TIME, buf);
);
ASSERT_TRUE(typeid(*option_v6) == typeid(Option6Int<uint16_t>));
// Validate the value.
@@ -491,25 +689,44 @@ TEST_F(OptionDefinitionTest, factoryUint16) {
boost::static_pointer_cast<Option6Int<uint16_t> >(option_v6);
EXPECT_EQ(0x0102, option_cast_v6->getValue());
- // Try to provide too large buffer. Expect exception.
- EXPECT_THROW(
- option_v6 = factory(Option::V6, D6O_ELAPSED_TIME, OptionBuffer(3)),
- isc::OutOfRange
- );
// Try to provide zero-length buffer. Expect exception.
EXPECT_THROW(
- option_v6 = factory(Option::V6, D6O_ELAPSED_TIME, OptionBuffer(1)),
- isc::OutOfRange
+ option_v6 = opt_def.optionFactory(Option::V6, D6O_ELAPSED_TIME, OptionBuffer(1)),
+ InvalidOptionValue
+ );
+
+ // @todo Add more cases for DHCPv4
+}
+
+// The purpose of this test is to verify that definition for option that
+// comprises single uint16 value can be created and that this definition
+// can be used to create an option with single uint16 value.
+TEST_F(OptionDefinitionTest, uint16Tokenized) {
+ OptionDefinition opt_def("OPTION_ELAPSED_TIME", D6O_ELAPSED_TIME, "uint16");
+
+ OptionPtr option_v6;
+
+ std::vector<std::string> values;
+ values.push_back("1234");
+ values.push_back("5678");
+ ASSERT_NO_THROW(
+ option_v6 = opt_def.optionFactory(Option::V6, D6O_ELAPSED_TIME, values);
);
+ ASSERT_TRUE(typeid(*option_v6) == typeid(Option6Int<uint16_t>));
+ // Validate the value.
+ boost::shared_ptr<Option6Int<uint16_t> > option_cast_v6 =
+ boost::static_pointer_cast<Option6Int<uint16_t> >(option_v6);
+ EXPECT_EQ(1234, option_cast_v6->getValue());
// @todo Add more cases for DHCPv4
+
}
-TEST_F(OptionDefinitionTest, factoryUint32) {
+// The purpose of this test is to verify that definition for option that
+// comprises single uint32 value can be created and that this definition
+// can be used to create an option with single uint32 value.
+TEST_F(OptionDefinitionTest, uint32) {
OptionDefinition opt_def("OPTION_CLT_TIME", D6O_CLT_TIME, "uint32");
- Option::Factory* factory(NULL);
- EXPECT_NO_THROW(factory = opt_def.getFactory());
- ASSERT_TRUE(factory != NULL);
OptionPtr option_v6;
OptionBuffer buf;
@@ -518,7 +735,7 @@ TEST_F(OptionDefinitionTest, factoryUint32) {
buf.push_back(3);
buf.push_back(4);
ASSERT_NO_THROW(
- option_v6 = factory(Option::V6, D6O_CLT_TIME, buf);
+ option_v6 = opt_def.optionFactory(Option::V6, D6O_CLT_TIME, buf);
);
ASSERT_TRUE(typeid(*option_v6) == typeid(Option6Int<uint32_t>));
// Validate the value.
@@ -526,27 +743,44 @@ TEST_F(OptionDefinitionTest, factoryUint32) {
boost::static_pointer_cast<Option6Int<uint32_t> >(option_v6);
EXPECT_EQ(0x01020304, option_cast_v6->getValue());
- // Try to provide too large buffer. Expect exception.
+ // Try to provide too short buffer. Expect exception.
EXPECT_THROW(
- option_v6 = factory(Option::V6, D6O_CLT_TIME, OptionBuffer(5)),
- isc::OutOfRange
+ option_v6 = opt_def.optionFactory(Option::V6, D6O_CLT_TIME, OptionBuffer(2)),
+ InvalidOptionValue
);
- // Try to provide zero-length buffer. Expect exception.
- EXPECT_THROW(
- option_v6 = factory(Option::V6, D6O_CLT_TIME, OptionBuffer(2)),
- isc::OutOfRange
+
+ // @todo Add more cases for DHCPv4
+}
+
+// The purpose of this test is to verify that definition for option that
+// comprises single uint32 value can be created and that this definition
+// can be used to create an option with single uint32 value.
+TEST_F(OptionDefinitionTest, uint32Tokenized) {
+ OptionDefinition opt_def("OPTION_CLT_TIME", D6O_CLT_TIME, "uint32");
+
+ OptionPtr option_v6;
+ std::vector<std::string> values;
+ values.push_back("123456");
+ values.push_back("789");
+ ASSERT_NO_THROW(
+ option_v6 = opt_def.optionFactory(Option::V6, D6O_CLT_TIME, values);
);
+ ASSERT_TRUE(typeid(*option_v6) == typeid(Option6Int<uint32_t>));
+ // Validate the value.
+ boost::shared_ptr<Option6Int<uint32_t> > option_cast_v6 =
+ boost::static_pointer_cast<Option6Int<uint32_t> >(option_v6);
+ EXPECT_EQ(123456, option_cast_v6->getValue());
// @todo Add more cases for DHCPv4
}
-TEST_F(OptionDefinitionTest, factoryUint16Array) {
+// The purpose of this test is to verify that definition for option that
+// comprises array of uint16 values can be created and that this definition
+// can be used to create option with an array of uint16 values.
+TEST_F(OptionDefinitionTest, uint16Array) {
// Let's define some dummy option.
const uint16_t opt_code = 79;
OptionDefinition opt_def("OPTION_UINT16_ARRAY", opt_code, "uint16", true);
- Option::Factory* factory(NULL);
- EXPECT_NO_THROW(factory = opt_def.getFactory());
- ASSERT_TRUE(factory != NULL);
OptionPtr option_v6;
// Positive scenario, initiate the buffer with length being
@@ -558,7 +792,7 @@ TEST_F(OptionDefinitionTest, factoryUint16Array) {
}
// Constructor should succeed because buffer has correct size.
EXPECT_NO_THROW(
- option_v6 = factory(Option::V6, opt_code, buf);
+ option_v6 = opt_def.optionFactory(Option::V6, opt_code, buf);
);
ASSERT_TRUE(typeid(*option_v6) == typeid(Option6IntArray<uint16_t>));
boost::shared_ptr<Option6IntArray<uint16_t> > option_cast_v6 =
@@ -576,24 +810,50 @@ TEST_F(OptionDefinitionTest, factoryUint16Array) {
// Provided buffer size must be greater than zero. Check if we
// get exception if we provide zero-length buffer.
EXPECT_THROW(
- option_v6 = factory(Option::V6, opt_code, OptionBuffer()),
- isc::OutOfRange
+ option_v6 = opt_def.optionFactory(Option::V6, opt_code, OptionBuffer()),
+ InvalidOptionValue
);
// Buffer length must be multiple of data type size.
EXPECT_THROW(
- option_v6 = factory(Option::V6, opt_code, OptionBuffer(5)),
- isc::OutOfRange
+ option_v6 = opt_def.optionFactory(Option::V6, opt_code, OptionBuffer(5)),
+ InvalidOptionValue
+ );
+}
+
+// The purpose of this test is to verify that definition for option that
+// comprises array of uint16 values can be created and that this definition
+// can be used to create option with an array of uint16 values.
+TEST_F(OptionDefinitionTest, uint16ArrayTokenized) {
+ // Let's define some dummy option.
+ const uint16_t opt_code = 79;
+ OptionDefinition opt_def("OPTION_UINT16_ARRAY", opt_code, "uint16", true);
+
+ OptionPtr option_v6;
+ std::vector<std::string> str_values;
+ str_values.push_back("12345");
+ str_values.push_back("5679");
+ str_values.push_back("12");
+ EXPECT_NO_THROW(
+ option_v6 = opt_def.optionFactory(Option::V6, opt_code, str_values);
);
+ ASSERT_TRUE(typeid(*option_v6) == typeid(Option6IntArray<uint16_t>));
+ boost::shared_ptr<Option6IntArray<uint16_t> > option_cast_v6 =
+ boost::static_pointer_cast<Option6IntArray<uint16_t> >(option_v6);
+ // Get the values from the initiated options and validate.
+ std::vector<uint16_t> values = option_cast_v6->getValues();
+ EXPECT_EQ(12345, values[0]);
+ EXPECT_EQ(5679, values[1]);
+ EXPECT_EQ(12, values[2]);
}
-TEST_F(OptionDefinitionTest, factoryUint32Array) {
+// The purpose of this test is to verify that definition for option that
+// comprises array of uint32 values can be created and that this definition
+// can be used to create option with an array of uint32 values.
+TEST_F(OptionDefinitionTest, uint32Array) {
// Let's define some dummy option.
const uint16_t opt_code = 80;
OptionDefinition opt_def("OPTION_UINT32_ARRAY", opt_code, "uint32", true);
- Option::Factory* factory(NULL);
- EXPECT_NO_THROW(factory = opt_def.getFactory());
- ASSERT_TRUE(factory != NULL);
OptionPtr option_v6;
// Positive scenario, initiate the buffer with length being
@@ -605,7 +865,7 @@ TEST_F(OptionDefinitionTest, factoryUint32Array) {
}
// Constructor should succeed because buffer has correct size.
EXPECT_NO_THROW(
- option_v6 = factory(Option::V6, opt_code, buf);
+ option_v6 = opt_def.optionFactory(Option::V6, opt_code, buf);
);
ASSERT_TRUE(typeid(*option_v6) == typeid(Option6IntArray<uint32_t>));
boost::shared_ptr<Option6IntArray<uint32_t> > option_cast_v6 =
@@ -623,16 +883,85 @@ TEST_F(OptionDefinitionTest, factoryUint32Array) {
// Provided buffer size must be greater than zero. Check if we
// get exception if we provide zero-length buffer.
EXPECT_THROW(
- option_v6 = factory(Option::V6, opt_code, OptionBuffer()),
- isc::OutOfRange
+ option_v6 = opt_def.optionFactory(Option::V6, opt_code, OptionBuffer()),
+ InvalidOptionValue
);
// Buffer length must be multiple of data type size.
EXPECT_THROW(
- option_v6 = factory(Option::V6, opt_code, OptionBuffer(5)),
- isc::OutOfRange
+ option_v6 = opt_def.optionFactory(Option::V6, opt_code, OptionBuffer(5)),
+ InvalidOptionValue
+ );
+}
+
+// The purpose of this test is to verify that definition for option that
+// comprises array of uint32 values can be created and that this definition
+// can be used to create option with an array of uint32 values.
+TEST_F(OptionDefinitionTest, uint32ArrayTokenized) {
+ // Let's define some dummy option.
+ const uint16_t opt_code = 80;
+
+ OptionDefinition opt_def("OPTION_UINT32_ARRAY", opt_code, "uint32", true);
+
+ OptionPtr option_v6;
+ std::vector<std::string> str_values;
+ str_values.push_back("123456");
+ str_values.push_back("7");
+ str_values.push_back("256");
+ str_values.push_back("1111");
+
+ EXPECT_NO_THROW(
+ option_v6 = opt_def.optionFactory(Option::V6, opt_code, str_values);
+ );
+ ASSERT_TRUE(typeid(*option_v6) == typeid(Option6IntArray<uint32_t>));
+ boost::shared_ptr<Option6IntArray<uint32_t> > option_cast_v6 =
+ boost::static_pointer_cast<Option6IntArray<uint32_t> >(option_v6);
+ // Get the values from the initiated options and validate.
+ std::vector<uint32_t> values = option_cast_v6->getValues();
+ EXPECT_EQ(123456, values[0]);
+ EXPECT_EQ(7, values[1]);
+ EXPECT_EQ(256, values[2]);
+ EXPECT_EQ(1111, values[3]);
+}
+
+// The purpose of this test is to verify that the definition can be created
+// for the option that comprises string value in the UTF8 format.
+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");
+ OptionPtr option_v6;
+ EXPECT_NO_THROW(
+ option_v6 = opt_def.optionFactory(Option::V6, opt_code, values);
+ );
+ ASSERT_TRUE(option_v6);
+ ASSERT_TRUE(typeid(*option_v6) == typeid(Option));
+ std::vector<uint8_t> data = option_v6->getData();
+ std::vector<uint8_t> ref_data(values[0].c_str(), values[0].c_str()
+ + values[0].length());
+ EXPECT_TRUE(std::equal(ref_data.begin(), ref_data.end(), data.begin()));
+}
+
+// The purpose of this test is to check that non-integer data type can't
+// be used for factoryInteger function.
+TEST_F(OptionDefinitionTest, integerInvalidType) {
+ // The template function factoryInteger<> accepts integer values only
+ // as template typename. Here we try passing different type and
+ // see if it rejects it.
+ OptionBuffer buf(1);
+ EXPECT_THROW(
+ OptionDefinition::factoryInteger<bool>(Option::V6, D6O_PREFERENCE,
+ buf.begin(), buf.end()),
+ isc::dhcp::InvalidDataType
);
}
+// 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) {
// IA_NA option format.
OptionDefinition opt_def1("OPTION_IA_NA", D6O_IA_NA, "record");
More information about the bind10-changes
mailing list