summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPiotrek Zadroga <piotrek@isc.org>2024-02-19 21:15:48 +0100
committerPiotrek Zadroga <piotrek@isc.org>2024-02-23 17:14:06 +0100
commit119c973761eb71f25219cda0a0f3d79fca8cdf57 (patch)
tree0c3ead707b1e924fb8fe2eed7fd313363de8306b
parent[#3141] option definition UTs fix (diff)
downloadkea-119c973761eb71f25219cda0a0f3d79fca8cdf57.tar.xz
kea-119c973761eb71f25219cda0a0f3d79fca8cdf57.zip
[#3141] DNRv4 UTs edited
-rw-r--r--src/lib/dhcp/option4_dnr.cc190
-rw-r--r--src/lib/dhcp/option4_dnr.h94
-rw-r--r--src/lib/dhcp/tests/option4_dnr_unittest.cc712
-rw-r--r--src/lib/dhcp/tests/option6_dnr_unittest.cc13
4 files changed, 581 insertions, 428 deletions
diff --git a/src/lib/dhcp/option4_dnr.cc b/src/lib/dhcp/option4_dnr.cc
index 0976152481..e2bc68424d 100644
--- a/src/lib/dhcp/option4_dnr.cc
+++ b/src/lib/dhcp/option4_dnr.cc
@@ -8,7 +8,6 @@
#include <dhcp/option4_dnr.h>
#include <dns/labelsequence.h>
-#include <util/strutil.h>
using namespace isc::asiolink;
using namespace isc::util;
@@ -216,32 +215,6 @@ DnrInstance::DnrInstance(Option::Universe universe)
initMembers();
}
-DnrInstance::DnrInstance(Option::Universe universe,
- const uint16_t service_priority,
- const std::string& adn,
- const DnrInstance::AddressContainer& ip_addresses,
- const std::string& svc_params)
- : universe_(universe), dnr_instance_data_length_(0), service_priority_(service_priority),
- adn_length_(0), addr_length_(0), ip_addresses_(ip_addresses), svc_params_length_(0),
- adn_only_mode_(true), svc_params_(svc_params), alpn_http_(false),
- dnr_instance_data_length_size_(0), adn_length_size_(0), addr_length_size_(0),
- minimal_length_(0) {
- initMembers();
- setAdn(adn);
- checkFields();
-}
-
-DnrInstance::DnrInstance(Option::Universe universe,
- const uint16_t service_priority,
- const std::string& adn)
- : universe_(universe), dnr_instance_data_length_(0), service_priority_(service_priority),
- adn_length_(0), addr_length_(0), svc_params_length_(0), adn_only_mode_(true),
- alpn_http_(false), dnr_instance_data_length_size_(0), adn_length_size_(0),
- addr_length_size_(0), minimal_length_(0) {
- initMembers();
- setAdn(adn);
-}
-
void
DnrInstance::packAdn(OutputBuffer& buf) const {
if (!adn_) {
@@ -282,38 +255,6 @@ DnrInstance::getAdnAsText() const {
}
void
-DnrInstance::setAdn(const std::string& adn) {
- std::string trimmed_adn = str::trim(adn);
- if (trimmed_adn.empty()) {
- isc_throw(InvalidOptionDnrDomainName, getLogPrefix()
- << "Mandatory Authentication Domain Name fully "
- "qualified domain-name must not be empty");
- }
-
- try {
- adn_.reset(new isc::dns::Name(trimmed_adn, true));
- } catch (const Exception& ex) {
- isc_throw(InvalidOptionDnrDomainName, getLogPrefix()
- << "Failed to parse "
- "fully qualified domain-name from string - "
- << ex.what());
- }
-
- size_t adn_len = 0;
- isc::dns::LabelSequence label_sequence(*adn_);
- label_sequence.getData(&adn_len);
- if (adn_len > std::numeric_limits<uint16_t>::max()) {
- isc_throw(InvalidOptionDnrDomainName, getLogPrefix() << "Given ADN FQDN length " << adn_len
- << " is bigger than uint_16 MAX");
- }
-
- adn_length_ = adn_len;
- if (universe_ == Option::V4) {
- setDnrInstanceDataLength();
- }
-}
-
-void
DnrInstance::unpackAdn(OptionBufferConstIter& begin, OptionBufferConstIter end) {
OpaqueDataTuple::LengthFieldType lft = OptionDataTypeUtil::getTupleLenFieldType(universe_);
OpaqueDataTuple adn_tuple(lft);
@@ -346,119 +287,6 @@ DnrInstance::unpackAdn(OptionBufferConstIter& begin, OptionBufferConstIter end)
begin += adn_length_ + getAdnLengthSize();
}
-void
-DnrInstance::checkSvcParams(bool from_wire_data) {
- std::string svc_params = str::trim(svc_params_);
- if (svc_params.empty()) {
- isc_throw(InvalidOptionDnrSvcParams, getLogPrefix()
- << "Provided Svc Params field is empty");
- }
-
- if (!from_wire_data) {
- // If Service Params field was not parsed from on-wire data,
- // but actually was provided with ctor, let's calculate svc_params_length_.
- auto svc_params_len = svc_params.length();
- if (svc_params_len > std::numeric_limits<uint16_t>::max()) {
- isc_throw(InvalidOptionDnrSvcParams, getLogPrefix()
- << "Given Svc Params length " << svc_params_len
- << " is bigger than uint_16 MAX");
- }
-
- svc_params_length_ = svc_params_len;
- // If Service Params field was not parsed from on-wire data,
- // but actually was provided with ctor, let's replace it with trimmed value.
- svc_params_ = svc_params;
- }
-
- // SvcParams are a whitespace-separated list, with each SvcParam
- // consisting of a SvcParamKey=SvcParamValue pair or a standalone SvcParamKey.
- // SvcParams in presentation format MAY appear in any order, but keys MUST NOT be repeated.
-
- // Let's put all elements of a whitespace-separated list into a vector.
- std::vector<std::string> tokens = str::tokens(svc_params, " ");
-
- // Set of keys used to check if a key is not repeated.
- std::unordered_set<std::string> keys;
- // String sanitizer is used to check keys syntax.
- str::StringSanitizerPtr sanitizer;
- // SvcParamKeys are lower-case alphanumeric strings. Key names
- // contain 1-63 characters from the ranges "a"-"z", "0"-"9", and "-".
- std::string regex = "[^a-z0-9-]";
- sanitizer.reset(new str::StringSanitizer(regex, ""));
-
- // Now let's check each SvcParamKey=SvcParamValue pair.
- for (const std::string& token : tokens) {
- std::vector<std::string> key_val = str::tokens(token, "=");
- if (key_val.size() > 2) {
- isc_throw(InvalidOptionDnrSvcParams,
- getLogPrefix() << "Wrong Svc Params syntax - more than one "
- "equals sign found in SvcParamKey=SvcParamValue pair");
- }
-
- // SvcParam Key related checks come below.
- std::string key = key_val[0];
- if (key.length() > 63) {
- isc_throw(InvalidOptionDnrSvcParams,
- getLogPrefix() << "Wrong Svc Params syntax - key had more "
- << "than 63 characters - " << key);
- }
-
- if (FORBIDDEN_SVC_PARAMS.find(key) != FORBIDDEN_SVC_PARAMS.end()) {
- isc_throw(InvalidOptionDnrSvcParams, getLogPrefix() << "Wrong Svc Params syntax - key "
- << key << " must not be used");
- }
-
- auto insert_res = keys.insert(key);
- if (!insert_res.second) {
- isc_throw(InvalidOptionDnrSvcParams, getLogPrefix() << "Wrong Svc Params syntax - key "
- << key << " was duplicated");
- }
-
- std::string sanitized_key = sanitizer->scrub(key);
- if (sanitized_key.size() < key.size()) {
- isc_throw(InvalidOptionDnrSvcParams,
- getLogPrefix()
- << "Wrong Svc Params syntax - invalid character used in key - " << key);
- }
- }
-}
-
-void
-DnrInstance::checkFields() {
- if (svc_params_.empty() && ip_addresses_.empty()) {
- // ADN only mode, nothing more to do.
- return;
- }
-
- if (!svc_params_.empty() && ip_addresses_.empty()) {
- // As per draft-ietf-add-dnr 3.1.8:
- // If additional data is supplied (i.e. not ADN only mode),
- // the option includes at least one valid IP address.
- isc_throw(OutOfRange, getLogPrefix() << "No IP address given. Since this is not ADN only "
- "mode, at least one valid IP address must "
- "be included");
- }
-
- if (!svc_params_.empty()) {
- checkSvcParams(false);
- }
-
- adn_only_mode_ = false;
- const uint8_t addr_field_len = (universe_ == Option::V4) ? V4ADDRESS_LEN : V6ADDRESS_LEN;
- const uint16_t max_addr_len = (universe_ == Option::V4) ? std::numeric_limits<uint8_t>::max() :
- std::numeric_limits<uint16_t>::max();
- auto addr_len = ip_addresses_.size() * addr_field_len;
- if (addr_len > max_addr_len) {
- isc_throw(OutOfRange, getLogPrefix() << "Given IP addresses length " << addr_len
- << " is bigger than MAX " << max_addr_len);
- }
-
- addr_length_ = addr_len;
- if (universe_ == Option::V4) {
- setDnrInstanceDataLength();
- }
-}
-
std::string
DnrInstance::svcParamValAsText(const std::pair<uint16_t, OpaqueDataTuple>& svc_param) const {
OptionBufferConstIter alpn_begin;
@@ -503,7 +331,7 @@ DnrInstance::svcParamValAsText(const std::pair<uint16_t, OpaqueDataTuple>& svc_p
break;
case 7:
// dohpath
- // convertion not needed, let's return data as string
+ // conversion not needed, let's return data as string
ret = svc_param.second.getText();
break;
}
@@ -661,7 +489,7 @@ DnrInstance::unpackSvcParams(OptionBufferConstIter& begin, OptionBufferConstIter
}
// Check if SvcParamKey usage is supported by DNR DHCP option.
- // Note that SUPPORTED_SVC_PARAMS set may expand in future.
+ // Note that SUPPORTED_SVC_PARAMS set may expand in the future.
if (SUPPORTED_SVC_PARAMS.find(num_svc_param_key) == SUPPORTED_SVC_PARAMS.end()) {
isc_throw(InvalidOptionDnrSvcParams,
getLogPrefix() << "Wrong Svc Params syntax - key " << svc_param_key
@@ -702,8 +530,6 @@ DnrInstance::unpackSvcParams(OptionBufferConstIter& begin, OptionBufferConstIter
svc_params_map_.insert(std::make_pair(num_svc_param_key, svc_param_tuple));
begin += svc_param_tuple.getTotalLength();
}
-
- begin += svc_params_length_;
}
}
@@ -819,6 +645,7 @@ DnrInstance::parseDnrInstanceConfigData(const std::string& config_txt) {
std::vector<std::string> alpn_ids_tokens;
OpaqueDataTuple svc_param_val_tuple(OpaqueDataTuple::LENGTH_2_BYTES);
OutputBuffer out_buf(2);
+ std::vector<uint8_t> utf8_encoded;
for (auto const& svc_param_pair : svc_params_pairs) {
std::vector<std::string> key_val_tokens = str::tokens(str::trim(svc_param_pair), "=");
if (key_val_tokens.size() != 2) {
@@ -848,7 +675,7 @@ DnrInstance::parseDnrInstanceConfigData(const std::string& config_txt) {
}
// Check if SvcParamKey usage is supported by DNR DHCP option.
- // Note that SUPPORTED_SVC_PARAMS set may expand in future.
+ // Note that SUPPORTED_SVC_PARAMS set may expand in the future.
uint16_t num_svc_param_key = svc_params_iterator->second;
if (SUPPORTED_SVC_PARAMS.find(num_svc_param_key) == SUPPORTED_SVC_PARAMS.end()) {
isc_throw(InvalidOptionDnrSvcParams,
@@ -947,10 +774,17 @@ DnrInstance::parseDnrInstanceConfigData(const std::string& config_txt) {
// We hope to have URI containing < 0x80 ASCII chars, however to be sure
// and to be inline with RFC9461 Section 5, let's encode the dohpath with utf8.
- auto const utf8_encoded = encode::encodeUtf8(svc_param_val);
+ utf8_encoded = encode::encodeUtf8(svc_param_val);
svc_param_val_tuple.append(utf8_encoded.begin(), utf8_encoded.size());
svc_params_map_.insert(std::make_pair(num_svc_param_key, svc_param_val_tuple));
break;
+ default:
+ // This should not happen because we check if num_svc_param_key is
+ // in SUPPORTED_SVC_PARAMS before. But in case new SvcParam appears in Supported,
+ // and is not handled here...
+ isc_throw(InvalidOptionDnrSvcParams,
+ getLogPrefix() << "Wrong Svc Params syntax - key " << num_svc_param_key
+ << " not supported yet.");
}
}
diff --git a/src/lib/dhcp/option4_dnr.h b/src/lib/dhcp/option4_dnr.h
index 682f68a710..0db59e8808 100644
--- a/src/lib/dhcp/option4_dnr.h
+++ b/src/lib/dhcp/option4_dnr.h
@@ -103,41 +103,6 @@ public:
/// @param universe either V4 or V6 Option universe
explicit DnrInstance(Option::Universe universe);
- /// @brief Constructor of the DNR Instance with all fields from params.
- ///
- /// Constructor of the DNR Instance where all fields
- /// i.e. Service priority, ADN, IP address(es) and Service params
- /// are provided as ctor parameters.
- ///
- /// @param universe either V4 or V6 Option universe
- /// @param service_priority Service priority
- /// @param adn ADN FQDN
- /// @param ip_addresses Container of IP addresses
- /// @param svc_params Service Parameters
- ///
- /// @throw InvalidOptionDnrDomainName Thrown in case of any issue with parsing ADN
- /// @throw InvalidOptionDnrSvcParams Thrown when @c checkSvcParams(from_wire_data) throws
- /// @throw OutOfRange Thrown in case of no IP addresses found or when IP addresses length
- /// is too big
- DnrInstance(Option::Universe universe,
- uint16_t service_priority,
- const std::string& adn,
- const AddressContainer& ip_addresses,
- const std::string& svc_params);
-
- /// @brief Constructor of the DNR Instance in ADN only mode.
- ///
- /// Constructor of the DNR Instance in ADN only mode
- /// i.e. only Service priority and ADN FQDN
- /// are provided as ctor parameters.
- ///
- /// @param universe either V4 or V6 Option universe
- /// @param service_priority Service priority
- /// @param adn ADN FQDN
- ///
- /// @throw InvalidOptionDnrDomainName Thrown in case of any issue with parsing ADN
- DnrInstance(Option::Universe universe, uint16_t service_priority, const std::string& adn);
-
/// @brief Default destructor.
virtual ~DnrInstance() = default;
@@ -241,19 +206,6 @@ public:
return (adn_only_mode_);
}
- /// @brief Sets Authentication domain name from given string.
- ///
- /// Sets FQDN of the encrypted DNS resolver from given string.
- /// It may throw an exception if parsing of the FQDN fails or if
- /// provided FQDN length is bigger than uint16_t Max.
- /// It also calculates and sets value of Addr length field.
- ///
- /// @param adn string representation of ADN FQDN
- ///
- /// @throw InvalidOptionDnrDomainName Thrown in case of any issue with parsing ADN
- /// from given string.
- void setAdn(const std::string& adn);
-
/// @brief Setter of the @c adn_only_mode_ field.
///
/// @param adn_only_mode enabled/disabled setting
@@ -342,34 +294,6 @@ public:
/// @param end end of the buffer from which the field will be read
void unpackSvcParams(OptionBufferConstIter& begin, OptionBufferConstIter end);
- /// @brief Checks SvcParams field if encoded correctly and throws in case of issue found.
- ///
- /// The field should be encoded following the rules in
- /// Section 2.1 of [I-D.ietf-dnsop-svcb-https]. SvcParams are
- /// a whitespace-separated list, with each SvcParam consisting of
- /// a SvcParamKey=SvcParamValue pair or a standalone SvcParamKey.
- ///
- /// @note It is user's responsibility to provide correct configuration
- /// of @c SvcParams as described in Section 2.1 of [I-D.ietf-dnsop-svcb-https].
- /// Currently, SvcParamValue is not verified. Proper syntax of SvcParamValue
- /// is described in Appendix A of [I-D.ietf-dnsop-svcb-https].
- ///
- /// @param from_wire_data used to determine whether SvcParams data comes
- /// from unpacked wire data or from ctor param
- ///
- /// @throw InvalidOptionDnrSvcParams Thrown in case of any issue found when checking
- /// @c ServiceParams field syntax
- void checkSvcParams(bool from_wire_data = true);
-
- /// @brief Checks IP address(es) field if data is correct and throws in case of issue found.
- ///
- /// Fields lengths are also calculated and saved to member variables.
- ///
- /// @throw OutOfRange Thrown in case of no IP addresses found or when IP addresses length
- /// is too big
- /// @throw InvalidOptionDnrSvcParams Thrown when @c checkSvcParams(from_wire_data) throws
- void checkFields();
-
/// @brief Adds IP address to @c ip_addresses_ container.
///
/// @param ip_address IP address to be added
@@ -392,7 +316,7 @@ public:
///
/// Note that this function parses single DnrInstance. For DNRv4 it is possible to have more
/// than one DnrInstance per one Option. In that case this function must be called for each
- /// DnrInstance.
+ /// DnrInstance config.
///
/// @param config_txt convenient notation of the option data received as string
///
@@ -445,12 +369,6 @@ protected:
/// fields are not present if the ADN-only mode is used.
bool adn_only_mode_;
- /// @brief Service Parameters (SvcParams) (variable length) as string.
- ///
- /// Specifies a set of service parameters that are encoded
- /// following the rules in Section 2.1 of [I-D.ietf-dnsop-svcb-https].
- std::string svc_params_;
-
/// @brief Service Parameters (SvcParams) (variable length) as on-wire data buffer.
///
/// Specifies a set of service parameters that are encoded
@@ -465,7 +383,7 @@ protected:
/// (Defined values are in Section 14.3.2 of RFC9460 - listed in @c SVC_PARAMS).
/// The value is an OpaqueDataTuple containing:
/// - the length of the SvcParamValue as an uint_16 integer in network byte order
- /// - data buffer the SvcParamValue in a format determined by the SvcParamKey.
+ /// - data buffer with the SvcParamValue in a format determined by the SvcParamKey.
std::map<uint16_t, OpaqueDataTuple> svc_params_map_;
/// @brief Calculates and returns length of DNR Instance data in octets.
@@ -557,14 +475,6 @@ public:
OptionBufferConstIter end,
bool convenient_notation = false);
- /// @brief Constructor of the empty %Option.
- ///
- /// No DNR instances are included in the %Option when using this ctor.
- /// They must be added afterwards.
- /// No fields data included in the %Option when using this ctor.
- /// They must be added afterwards.
- Option4Dnr() : Option(V4, DHO_V4_DNR) {}
-
/// @brief Adds given DNR instance to Option's DNR Instance container.
/// @param dnr_instance DNR instance to be added
void addDnrInstance(DnrInstance& dnr_instance);
diff --git a/src/lib/dhcp/tests/option4_dnr_unittest.cc b/src/lib/dhcp/tests/option4_dnr_unittest.cc
index 60f8cf31c2..2fb616a4ff 100644
--- a/src/lib/dhcp/tests/option4_dnr_unittest.cc
+++ b/src/lib/dhcp/tests/option4_dnr_unittest.cc
@@ -16,32 +16,20 @@ using namespace isc::asiolink;
namespace {
-// This test verifies constructor of the empty Option4Dnr class.
-TEST(Option4DnrTest, emptyCtor) {
- // Create option instance. Check that constructor doesn't throw.
- Option4DnrPtr option;
- EXPECT_NO_THROW(option.reset(new Option4Dnr()));
- ASSERT_TRUE(option);
+// This test verifies constructor from convenient string notation
+// with one ADN-only-mode DNR instance.
+TEST(Option4DnrTest, fromConfigCtorOneAdnOnlyModeInstance) {
+ // Prepare example config.
+ const std::string config = "1, myhost1.example.com.";
- // Check if member variables were correctly set by ctor.
- EXPECT_EQ(Option::V4, option->getUniverse());
- EXPECT_EQ(DHO_V4_DNR, option->getType());
-}
+ OptionBuffer in_buf;
+ in_buf.assign(config.begin(), config.end());
-// This test verifies constructor of the empty Option4Dnr class together
-// with adding ADN-only-mode DNR instance to option's DNR instances.
-TEST(Option4DnrTest, oneAdnOnlyModeInstance) {
// Create option instance. Check that constructor doesn't throw.
Option4DnrPtr option;
- EXPECT_NO_THROW(option.reset(new Option4Dnr()));
+ EXPECT_NO_THROW(option.reset(new Option4Dnr(in_buf.begin(), in_buf.end(), true)));
ASSERT_TRUE(option);
- // Prepare example DNR instance to add.
- DnrInstance dnr_1 = DnrInstance(Option::V4, 1, "myhost1.example.com.");
-
- // Add DNR instance.
- option->addDnrInstance(dnr_1);
-
// Check if member variables were correctly set inside DNR instances.
EXPECT_EQ(1, option->getDnrInstances().size());
EXPECT_EQ(1, option->getDnrInstances()[0].getServicePriority());
@@ -67,28 +55,26 @@ TEST(Option4DnrTest, oneAdnOnlyModeInstance) {
option->toText());
}
-// This test verifies constructor of the empty Option4Dnr class together
-// with adding multiple ADN-only-mode DNR instances to option's DNR instances.
-TEST(Option4DnrTest, multipleAdnOnlyModeInstances) {
+// This test verifies constructor from convenient string notation
+// with multiple ADN-only-mode DNR instances.
+TEST(Option4DnrTest, fromConfigCtorMultipleAdnOnlyModeInstances) {
+ // Prepare example config.
+ const std::string config = "1, myhost1.example.com. | "
+ "2, myhost2.example.com. | "
+ "3, myhost3.example.com.";
+
+ OptionBuffer in_buf;
+ in_buf.assign(config.begin(), config.end());
+
// Create option instance. Check that constructor doesn't throw.
Option4DnrPtr option;
- EXPECT_NO_THROW(option.reset(new Option4Dnr()));
+ EXPECT_NO_THROW(option.reset(new Option4Dnr(in_buf.begin(), in_buf.end(), true)));
ASSERT_TRUE(option);
// Check if member variables were correctly set by ctor.
EXPECT_EQ(Option::V4, option->getUniverse());
EXPECT_EQ(DHO_V4_DNR, option->getType());
- // Prepare example DNR instances to add.
- DnrInstance dnr_1 = DnrInstance(Option::V4, 1, "myhost1.example.com.");
- DnrInstance dnr_2 = DnrInstance(Option::V4, 2, "myhost2.example.com.");
- DnrInstance dnr_3 = DnrInstance(Option::V4, 3, "myhost3.example.com.");
-
- // Add DNR instances.
- option->addDnrInstance(dnr_1);
- option->addDnrInstance(dnr_2);
- option->addDnrInstance(dnr_3);
-
// Check if member variables were correctly set inside DNR instances.
EXPECT_EQ(3, option->getDnrInstances().size());
EXPECT_EQ(1, option->getDnrInstances()[0].getServicePriority());
@@ -128,29 +114,24 @@ TEST(Option4DnrTest, multipleAdnOnlyModeInstances) {
option->toText());
}
-// This test verifies constructor of the empty Option4Dnr class together
-// with adding to option's DNR instances:
+// This test verifies constructor from convenient string notation
+// with two DNR instances:
// 1. ADN-only-mode DNR instance
// 2. All fields included (IP addresses and service params also) DNR instance.
-TEST(Option4DnrTest, mixedDnrInstances) {
+TEST(Option4DnrTest, fromConfigCtorMixedDnrInstances) {
+ // Prepare example config.
+ const std::string config = "1, myhost1.example.com. | "
+ "2, myhost2.example.com., 192.168.0.1 192.168.0.2, "
+ "alpn=h3 port=1234 dohpath=/q{?dns}";
+
+ OptionBuffer in_buf;
+ in_buf.assign(config.begin(), config.end());
+
// Create option instance. Check that constructor doesn't throw.
Option4DnrPtr option;
- EXPECT_NO_THROW(option.reset(new Option4Dnr()));
+ EXPECT_NO_THROW(option.reset(new Option4Dnr(in_buf.begin(), in_buf.end(), true)));
ASSERT_TRUE(option);
- // Prepare example DNR instance to add.
- DnrInstance::AddressContainer addresses;
- addresses.push_back(IOAddress("192.168.0.1"));
- addresses.push_back(IOAddress("192.168.0.2"));
- std::string svc_params = "key123=val key234=val2 key345";
-
- DnrInstance dnr_1 = DnrInstance(Option::V4, 1, "myhost1.example.com.");
- DnrInstance dnr_2 = DnrInstance(Option::V4, 2, "myhost2.example.com.", addresses, svc_params);
-
- // Add DNR instance.
- option->addDnrInstance(dnr_1);
- option->addDnrInstance(dnr_2);
-
// Check if member variables were correctly set inside DNR instances.
EXPECT_EQ(2, option->getDnrInstances().size());
EXPECT_EQ(1, option->getDnrInstances()[0].getServicePriority());
@@ -164,46 +145,60 @@ TEST(Option4DnrTest, mixedDnrInstances) {
EXPECT_EQ(0, option->getDnrInstances()[0].getSvcParamsLength());
EXPECT_EQ(2, option->getDnrInstances()[1].getAddresses().size());
EXPECT_EQ(8, option->getDnrInstances()[1].getAddrLength());
- EXPECT_EQ(29, option->getDnrInstances()[1].getSvcParamsLength());
EXPECT_EQ("192.168.0.1", option->getDnrInstances()[1].getAddresses()[0].toText());
EXPECT_EQ("192.168.0.2", option->getDnrInstances()[1].getAddresses()[1].toText());
+
+ // Reference SvcParams on-wire data buffer.
+ const OptionBuffer svc_params = {
+ 0, 1, // SvcParamKey alpn
+ 0, 3, // SvcParamVal Len
+ 2, 'h', '3', // 2 octets long alpn-id http/3
+ 0, 3, // SvcParamKey port
+ 0, 2, // SvcParamVal Len
+ 0x4, 0xD2, // 1234 in hex
+ 0, 7, // SvcParamKey dohpath
+ 0, 8, // SvcParamVal Len
+ '/', 'q', '{', '?', 'd', 'n', 's', '}' // dohpath value
+ };
+
EXPECT_EQ(svc_params, option->getDnrInstances()[1].getSvcParams());
+ EXPECT_EQ(svc_params.size(), option->getDnrInstances()[1].getSvcParamsLength());
// BTW let's check if len() works ok. In ADN only mode, DNR Instance Data Len
// is set to ADN Len (21) + 3 = 24.
// expected len: 1x(24 (ADN+ADN Len+Service priority) + 2 (DNR Instance Data Len)) + 2 (headers)
// + 24 (ADN+ADN Len+Service priority) + 2 (DNR Instance Data Len) + 1 (Addr Len)
- // + 8 (IP addresses) + 29 (svc params)
- // = 92
- EXPECT_EQ(92, option->len());
+ // + 8 (IP addresses) + 25 (svc params)
+ // = 88
+ EXPECT_EQ(88, option->len());
// BTW let's check if toText() works ok.
// toText() len does not count in headers len.
- EXPECT_EQ("type=162(V4_DNR), len=90, "
+ EXPECT_EQ("type=162(V4_DNR), len=86, "
"DNR Instance 1(Instance len=24, service_priority=1, "
"adn_length=21, adn='myhost1.example.com.'), "
- "DNR Instance 2(Instance len=62, service_priority=2, "
+ "DNR Instance 2(Instance len=58, service_priority=2, "
"adn_length=21, adn='myhost2.example.com.', "
"addr_length=8, address(es): 192.168.0.1 192.168.0.2, "
- "svc_params='key123=val key234=val2 key345')",
+ "svc_params='alpn=h3 port=1234 dohpath=/q{?dns}')",
option->toText());
}
// This test verifies option packing into wire data.
// Provided data to pack contains 1 DNR instance:
// 1. ADN only mode
-TEST(Option4DnrTest, packOneAdnOnlyModeInstance) {
+TEST(Option4DnrTest, fromConfigCtorPackOneAdnOnlyModeInstance) {
+ // Prepare example config.
+ const std::string config = "1, myhost1.example.com.";
+
+ OptionBuffer in_buf;
+ in_buf.assign(config.begin(), config.end());
+
// Create option instance. Check that constructor doesn't throw.
Option4DnrPtr option;
- EXPECT_NO_THROW(option.reset(new Option4Dnr()));
+ EXPECT_NO_THROW(option.reset(new Option4Dnr(in_buf.begin(), in_buf.end(), true)));
ASSERT_TRUE(option);
- // Prepare example DNR instance to add.
- DnrInstance dnr_1 = DnrInstance(Option::V4, 1, "myhost1.example.com.");
-
- // Add DNR instance.
- option->addDnrInstance(dnr_1);
-
// Prepare on-wire format of the option.
isc::util::OutputBuffer buf(10);
ASSERT_NO_THROW(option->pack(buf));
@@ -233,26 +228,24 @@ TEST(Option4DnrTest, packOneAdnOnlyModeInstance) {
// 1. ADN only mode
// 2. ADN only mode
// 3. ADN only mode
-TEST(Option4DnrTest, packMultipleAdnOnlyModeInstances) {
+TEST(Option4DnrTest, fromConfigCtorPackMultipleAdnOnlyModeInstances) {
+ // Prepare example config.
+ const std::string config = "1, myhost1.example.com. | "
+ "2, myhost2.example.com. | "
+ "3, myhost3.example.com.";
+
+ OptionBuffer in_buf;
+ in_buf.assign(config.begin(), config.end());
+
// Create option instance. Check that constructor doesn't throw.
Option4DnrPtr option;
- EXPECT_NO_THROW(option.reset(new Option4Dnr()));
+ EXPECT_NO_THROW(option.reset(new Option4Dnr(in_buf.begin(), in_buf.end(), true)));
ASSERT_TRUE(option);
// Check if member variables were correctly set by ctor.
EXPECT_EQ(Option::V4, option->getUniverse());
EXPECT_EQ(DHO_V4_DNR, option->getType());
- // Prepare example DNR instances to add.
- DnrInstance dnr_1 = DnrInstance(Option::V4, 1, "myhost1.example.com.");
- DnrInstance dnr_2 = DnrInstance(Option::V4, 2, "myhost2.example.com.");
- DnrInstance dnr_3 = DnrInstance(Option::V4, 3, "myhost3.example.com.");
-
- // Add DNR instances.
- option->addDnrInstance(dnr_1);
- option->addDnrInstance(dnr_2);
- option->addDnrInstance(dnr_3);
-
// Prepare on-wire format of the option.
isc::util::OutputBuffer buf(10);
ASSERT_NO_THROW(option->pack(buf));
@@ -293,79 +286,459 @@ TEST(Option4DnrTest, packMultipleAdnOnlyModeInstances) {
// Provided data to pack contains 2 DNR instances:
// 1. ADN only mode
// 2. All fields included (IP addresses and service params also).
-TEST(Option4DnrTest, packMixedDnrInstances) {
+TEST(Option4DnrTest, fromConfigCtorPackMixedDnrInstances) {
// Create option instance. Check that constructor doesn't throw.
+ const std::string config = "1, myhost1.example.com. | "
+ "2, myhost2.example.com., 192.168.0.1 192.168.0.2, alpn=dot\\,doq";
+ OptionBuffer opt_buf;
+ opt_buf.assign(config.begin(), config.end());
Option4DnrPtr option;
- EXPECT_NO_THROW(option.reset(new Option4Dnr()));
+ EXPECT_NO_THROW(option.reset(new Option4Dnr(opt_buf.begin(), opt_buf.end(), true)));
ASSERT_TRUE(option);
- // Prepare example DNR instance to add.
- DnrInstance::AddressContainer addresses;
- addresses.push_back(IOAddress("192.168.0.1"));
- addresses.push_back(IOAddress("192.168.0.2"));
- std::string svc_params = "key123=val key234=val2 key345";
-
- DnrInstance dnr_1 = DnrInstance(Option::V4, 1, "myhost1.example.com.");
- DnrInstance dnr_2 = DnrInstance(Option::V4, 2, "myhost2.example.com.", addresses, svc_params);
-
- // Add DNR instance.
- option->addDnrInstance(dnr_1);
- option->addDnrInstance(dnr_2);
-
// Prepare on-wire format of the option.
- isc::util::OutputBuffer buf(10);
- ASSERT_NO_THROW(option->pack(buf));
+ isc::util::OutputBuffer out_buf(10);
+ ASSERT_NO_THROW(option->pack(out_buf));
// Prepare reference data.
const uint8_t ref_data[] = {
- DHO_V4_DNR, // Option code
- 90, // Option len=90 dec
- 0x00, 24, // DNR Instance Data Len
- 0x00, 0x01, // Service priority is 1 dec
- 21, // ADN Length is 21 dec
- 0x07, 0x6D, 0x79, 0x68, 0x6F, 0x73, 0x74, '1', // FQDN: myhost1.
- 0x07, 0x65, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65, // example.
- 0x03, 0x63, 0x6F, 0x6D, 0x00, // com.
- 0x00, 62, // DNR Instance Data Len
- 0x00, 0x02, // Service priority is 2 dec
- 21, // ADN Length is 21 dec
- 0x07, 0x6D, 0x79, 0x68, 0x6F, 0x73, 0x74, '2', // FQDN: myhost2.
- 0x07, 0x65, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65, // example.
- 0x03, 0x63, 0x6F, 0x6D, 0x00, // com.
- 8, // Addr Len
- 192, 168, 0, 1, // IP address 1
- 192, 168, 0, 2, // IP address 2
- 'k', 'e', 'y', '1', '2', '3', '=', 'v', // Svc Params
- 'a', 'l', ' ', 'k', 'e', 'y', '2', '3', // Svc Params
- '4', '=', 'v', 'a', 'l', '2', ' ', 'k', // Svc Params
- 'e', 'y', '3', '4', '5' // Svc Params
+ DHO_V4_DNR, // Option code
+ 73, // Option len
+ 0x00, 24, // DNR Instance Data Len
+ 0x00, 0x01, // Service priority is 1 dec
+ 21, // ADN Length is 21 dec
+ 0x07, 0x6D, 0x79, 0x68, 0x6F, 0x73, 0x74, '1', // FQDN: myhost1.
+ 0x07, 0x65, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65, // example.
+ 0x03, 0x63, 0x6F, 0x6D, 0x00, // com.
+ 0x00, 45, // DNR Instance Data Len
+ 0x00, 0x02, // Service priority is 2 dec
+ 21, // ADN Length is 21 dec
+ 0x07, 0x6D, 0x79, 0x68, 0x6F, 0x73, 0x74, '2', // FQDN: myhost2.
+ 0x07, 0x65, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65, // example.
+ 0x03, 0x63, 0x6F, 0x6D, 0x00, // com.
+ 8, // Addr Len
+ 192, 168, 0, 1, // IP address 1
+ 192, 168, 0, 2, // IP address 2
+ 0, 1, // SvcParamKey alpn
+ 0, 8, // SvcParamVal Len
+ 3, 'd', 'o', 't', // 3 octets long alpn-id dot
+ 3, 'd', 'o', 'q' // 3 octets long alpn-id doq
+
};
size_t ref_data_size = sizeof(ref_data) / sizeof(ref_data[0]);
// Check if the buffer has the same length as the reference data,
// so as they can be compared directly.
- ASSERT_EQ(ref_data_size, buf.getLength());
- EXPECT_EQ(0, memcmp(ref_data, buf.getData(), buf.getLength()));
+ ASSERT_EQ(ref_data_size, out_buf.getLength());
+ EXPECT_EQ(0, memcmp(ref_data, out_buf.getData(), out_buf.getLength()));
}
-// This test verifies option constructor from wire data.
-TEST(Option4DnrTest, onWireDataCtor) {
- // Prepare data to decode - ADN only mode 1 DNR instance.
- const uint8_t buf_data[] = {
+// This test verifies option packing into wire data.
+// Provided data to pack contains 2 DNR instances:
+// 1. ADN only mode
+// 2. Almost all fields included (IP addresses but no service params).
+TEST(Option4DnrTest, fromConfigCtorPackMixedDnrInstancesTwo) {
+ // Create option instance. Check that constructor doesn't throw.
+ const std::string config = "1, myhost1.example.com. | "
+ "2, myhost2.example.com., 192.168.0.1 192.168.0.2";
+ OptionBuffer opt_buf;
+ opt_buf.assign(config.begin(), config.end());
+ Option4DnrPtr option;
+ EXPECT_NO_THROW(option.reset(new Option4Dnr(opt_buf.begin(), opt_buf.end(), true)));
+ ASSERT_TRUE(option);
+
+ // Prepare on-wire format of the option.
+ isc::util::OutputBuffer out_buf(10);
+ ASSERT_NO_THROW(option->pack(out_buf));
+
+ // Prepare reference data.
+ const uint8_t ref_data[] = {
+ DHO_V4_DNR, // Option code
+ 61, // Option len
0x00, 24, // DNR Instance Data Len
0x00, 0x01, // Service priority is 1 dec
21, // ADN Length is 21 dec
0x07, 0x6D, 0x79, 0x68, 0x6F, 0x73, 0x74, '1', // FQDN: myhost1.
0x07, 0x65, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65, // example.
- 0x03, 0x63, 0x6F, 0x6D, 0x00 // com.
+ 0x03, 0x63, 0x6F, 0x6D, 0x00, // com.
+ 0x00, 33, // DNR Instance Data Len
+ 0x00, 0x02, // Service priority is 2 dec
+ 21, // ADN Length is 21 dec
+ 0x07, 0x6D, 0x79, 0x68, 0x6F, 0x73, 0x74, '2', // FQDN: myhost2.
+ 0x07, 0x65, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65, // example.
+ 0x03, 0x63, 0x6F, 0x6D, 0x00, // com.
+ 8, // Addr Len
+ 192, 168, 0, 1, // IP address 1
+ 192, 168, 0, 2 // IP address 2
};
- OptionBuffer buf(buf_data, buf_data + sizeof(buf_data));
+ size_t ref_data_size = sizeof(ref_data) / sizeof(ref_data[0]);
+
+ // Check if the buffer has the same length as the reference data,
+ // so as they can be compared directly.
+ ASSERT_EQ(ref_data_size, out_buf.getLength());
+ EXPECT_EQ(0, memcmp(ref_data, out_buf.getData(), out_buf.getLength()));
+}
+
+// This test verifies that option constructor throws
+// an exception when config provided via ctor is malformed
+// - DnrInstance config has only Svc Priority.
+TEST(Option4DnrTest, fromConfigCtorNotEnoughFields) {
+ // Prepare example config.
+ const std::string config = "1, myhost1.example.com. | "
+ "2, myhost2.example.com., 192.168.0.1 192.168.0.2, alpn=dot\\,h3 "
+ "dohpath=/some\\|path{?dns} | " // "pipe char" escape is tested here
+ "3"; // 3rd dnr instance config has only Svc Priority
+
+ OptionBuffer buf;
+ buf.assign(config.begin(), config.end());
+
+ // Create option instance. Check that constructor throws.
+ Option4DnrPtr option;
+ EXPECT_THROW(option.reset(new Option4Dnr(buf.begin(), buf.end(), true)),
+ BadValue);
+ ASSERT_FALSE(option);
+}
+
+// This test verifies that option constructor throws
+// an exception when config provided via ctor is malformed
+// - DnrInstance config has too many comma separated fields.
+TEST(Option4DnrTest, fromConfigCtorTooManyFields) {
+ // Prepare example config.
+ const std::string config = "1, myhost1.example.com. | "
+ "2, myhost2.example.com., 192.168.0.1 192.168.0.2, alpn=dot\\,h3 "
+ "dohpath=/some-path{?dns} | "
+ "3, test.com., 10.2.3.4, port=1234, extra"; // too many fields
+
+ OptionBuffer buf;
+ buf.assign(config.begin(), config.end());
+
+ // Create option instance. Check that constructor throws.
+ Option4DnrPtr option;
+ EXPECT_THROW(option.reset(new Option4Dnr(buf.begin(), buf.end(), true)),
+ BadValue);
+ ASSERT_FALSE(option);
+}
+
+// This test verifies that option constructor throws
+// an exception when config provided via ctor is malformed
+// - DnrInstance config has malformed Svc priority.
+TEST(Option4DnrTest, fromConfigCtorWrongSvcPriority) {
+ // Prepare example config.
+ const std::string config = "1, myhost1.example.com. | "
+ "2, myhost2.example.com., 192.168.0.1 192.168.0.2, alpn=dot\\,h3 "
+ "dohpath=/some-path{?dns} | "
+ "what?, test.com., 10.2.3.4, port=1234"; // non-parsable Svc Priority
+
+ OptionBuffer buf;
+ buf.assign(config.begin(), config.end());
+
+ // Create option instance. Check that constructor throws.
+ Option4DnrPtr option;
+ EXPECT_THROW(option.reset(new Option4Dnr(buf.begin(), buf.end(), true)),
+ BadValue);
+ ASSERT_FALSE(option);
+}
+
+// This test verifies that option constructor throws
+// an exception when config provided via ctor is malformed
+// - DnrInstance config has malformed ADN.
+TEST(Option4DnrTest, fromConfigCtorWrongAdn) {
+ // Prepare example config.
+ const std::string config = "1, myhost1.example.com. | "
+ "2, myhost2.example.com., 192.168.0.1 192.168.0.2, alpn=dot\\,h3 "
+ "dohpath=/some-path{?dns} | "
+ "100, , 10.2.3.4, port=1234"; // wrong ADN - empty
+
+ OptionBuffer buf;
+ buf.assign(config.begin(), config.end());
+
+ // Create option instance. Check that constructor throws.
+ Option4DnrPtr option;
+ EXPECT_THROW(option.reset(new Option4Dnr(buf.begin(), buf.end(), true)),
+ InvalidOptionDnrDomainName);
+ ASSERT_FALSE(option);
+}
+
+// This test verifies that option constructor throws
+// an exception when config provided via ctor is malformed
+// - DnrInstance config has malformed IP address.
+TEST(Option4DnrTest, fromConfigCtorWrongIpAddr) {
+ // Prepare example config.
+ const std::string config = "1, myhost1.example.com. | "
+ "2, myhost2.example.com., 192.168.0.1 192.168.0.2, alpn=dot\\,h3 "
+ "dohpath=/some-path{?dns} | "
+ "100, test.net. , what?, port=1234"; // wrong IP address
+
+ OptionBuffer buf;
+ buf.assign(config.begin(), config.end());
+
+ // Create option instance. Check that constructor throws.
+ Option4DnrPtr option;
+ EXPECT_THROW(option.reset(new Option4Dnr(buf.begin(), buf.end(), true)),
+ BadValue);
+ ASSERT_FALSE(option);
+}
+
+// This test verifies that option constructor throws
+// an exception when config provided via ctor is malformed
+// - DnrInstance config has no IP address.
+TEST(Option4DnrTest, fromConfigCtorNoIpAddr) {
+ // Prepare example config.
+ const std::string config = "1, myhost1.example.com. | "
+ "2, myhost2.example.com., 192.168.0.1 192.168.0.2, alpn=dot\\,h3 "
+ "dohpath=/some-path{?dns} | "
+ "100, test.net. , , port=1234"; // no IP address
+
+ OptionBuffer buf;
+ buf.assign(config.begin(), config.end());
+
+ // Create option instance. Check that constructor throws.
+ Option4DnrPtr option;
+ EXPECT_THROW(option.reset(new Option4Dnr(buf.begin(), buf.end(), true)),
+ BadValue);
+ ASSERT_FALSE(option);
+}
+
+// This test verifies that option constructor throws
+// an exception when config provided via ctor is malformed
+// - DnrInstance config has malformed SvcParams.
+TEST(Option4DnrTest, fromConfigCtorWrongSvcParams) {
+ // Prepare example config.
+ const std::string config = "1, myhost1.example.com. | "
+ "2, myhost2.example.com., 192.168.0.1 192.168.0.2, alpn=dot\\,h3 "
+ "dohpath=/some-path{?dns} | "
+ "100, test.net. , 10.2.3.4, ="; // wrong SvcParams
+
+ OptionBuffer buf;
+ buf.assign(config.begin(), config.end());
+
+ // Create option instance. Check that constructor throws.
+ Option4DnrPtr option;
+ EXPECT_THROW(option.reset(new Option4Dnr(buf.begin(), buf.end(), true)),
+ InvalidOptionDnrSvcParams);
+ ASSERT_FALSE(option);
+}
+
+// This test verifies that option constructor throws
+// an exception when config provided via ctor is malformed
+// - DnrInstance config has malformed SvcParams - forbidden SvcParam.
+TEST(Option4DnrTest, fromConfigCtorWrongSvcParamsForbiddenSvcParam) {
+ // Prepare example config.
+ const std::string config = "1, myhost1.example.com. | "
+ "2, myhost2.example.com., 192.168.0.1 192.168.0.2, alpn=dot\\,h3 "
+ "dohpath=/some-path{?dns} | "
+ "100, test.net. , 10.2.3.4, ipv4hint=1"; // forbidden SvcParam
+
+ OptionBuffer buf;
+ buf.assign(config.begin(), config.end());
+
+ // Create option instance. Check that constructor throws.
+ Option4DnrPtr option;
+ EXPECT_THROW(option.reset(new Option4Dnr(buf.begin(), buf.end(), true)),
+ InvalidOptionDnrSvcParams);
+ ASSERT_FALSE(option);
+}
+
+// This test verifies that option constructor throws
+// an exception when config provided via ctor is malformed
+// - DnrInstance config has malformed SvcParams - unknown SvcParam.
+TEST(Option4DnrTest, fromConfigCtorWrongSvcParamsUnknownSvcParam) {
+ // Prepare example config.
+ const std::string config = "1, myhost1.example.com. | "
+ "2, myhost2.example.com., 192.168.0.1 192.168.0.2, alpn=dot\\,h3 "
+ "dohpath=/some-path{?dns} | "
+ "100, test.net. , 10.2.3.4, unknown=1"; // unknown SvcParam
+
+ OptionBuffer buf;
+ buf.assign(config.begin(), config.end());
+
+ // Create option instance. Check that constructor throws.
+ Option4DnrPtr option;
+ EXPECT_THROW(option.reset(new Option4Dnr(buf.begin(), buf.end(), true)),
+ InvalidOptionDnrSvcParams);
+ ASSERT_FALSE(option);
+}
+
+// This test verifies that option constructor throws
+// an exception when config provided via ctor is malformed
+// - DnrInstance config has malformed SvcParams - not supported SvcParam.
+TEST(Option4DnrTest, fromConfigCtorWrongSvcParamsNotSupportedSvcParam) {
+ // Prepare example config.
+ const std::string config = "1, myhost1.example.com. | "
+ "2, myhost2.example.com., 192.168.0.1 192.168.0.2, alpn=dot\\,h3 "
+ "dohpath=/some-path{?dns} | "
+ "100, test.net. , 10.2.3.4, ech=1"; // ech is not supported in DNR
+
+ OptionBuffer buf;
+ buf.assign(config.begin(), config.end());
+
+ // Create option instance. Check that constructor throws.
+ Option4DnrPtr option;
+ EXPECT_THROW(option.reset(new Option4Dnr(buf.begin(), buf.end(), true)),
+ InvalidOptionDnrSvcParams);
+ ASSERT_FALSE(option);
+}
+
+// This test verifies that option constructor throws
+// an exception when config provided via ctor is malformed
+// - DnrInstance config has malformed SvcParams - duplicated SvcParam.
+TEST(Option4DnrTest, fromConfigCtorWrongSvcParamsDuplicatedSvcParam) {
+ // Prepare example config.
+ const std::string config = "1, myhost1.example.com. | "
+ "2, myhost2.example.com., 192.168.0.1 192.168.0.2, alpn=dot\\,h3 "
+ "dohpath=/some-path{?dns} | "
+ "100, test.net. , 10.2.3.4,"
+ " port=1234 port=2345"; // duplicated SvcParam
+
+ OptionBuffer buf;
+ buf.assign(config.begin(), config.end());
+
+ // Create option instance. Check that constructor throws.
+ Option4DnrPtr option;
+ EXPECT_THROW(option.reset(new Option4Dnr(buf.begin(), buf.end(), true)),
+ InvalidOptionDnrSvcParams);
+ ASSERT_FALSE(option);
+}
+
+// This test verifies that option constructor throws
+// an exception when config provided via ctor is malformed
+// - DnrInstance config has malformed SvcParams - unknown AlpnId.
+TEST(Option4DnrTest, fromConfigCtorWrongSvcParamsUnknownAlpnId) {
+ // Prepare example config.
+ const std::string config = "1, myhost1.example.com. | "
+ "2, myhost2.example.com., 192.168.0.1 192.168.0.2, alpn=dot\\,h3 "
+ "dohpath=/some-path{?dns} | "
+ "100, test.net. , 10.2.3.4, alpn=dot\\,unknown"; // unknown alpn-id
+
+ OptionBuffer buf;
+ buf.assign(config.begin(), config.end());
+
+ // Create option instance. Check that constructor throws.
+ Option4DnrPtr option;
+ EXPECT_THROW(option.reset(new Option4Dnr(buf.begin(), buf.end(), true)),
+ InvalidOptionDnrSvcParams);
+ ASSERT_FALSE(option);
+}
+
+// This test verifies that option constructor throws
+// an exception when config provided via ctor is malformed
+// - DnrInstance config has malformed SvcParams - dohpath is missing.
+TEST(Option4DnrTest, fromConfigCtorWrongSvcParamsSkippedDohpath) {
+ // Prepare example config.
+ const std::string config = "1, myhost1.example.com. | "
+ "2, myhost2.example.com., 192.168.0.1 192.168.0.2, alpn=dot\\,h3 "
+ "dohpath=/some-path{?dns} | "
+ "100, test.net. , 10.2.3.4, alpn=h3"; // dohpath must be there
+ // when any of http alpn-ids
+ // are included
+
+ OptionBuffer buf;
+ buf.assign(config.begin(), config.end());
+
+ // Create option instance. Check that constructor throws.
+ Option4DnrPtr option;
+ EXPECT_THROW(option.reset(new Option4Dnr(buf.begin(), buf.end(), true)),
+ InvalidOptionDnrSvcParams);
+ ASSERT_FALSE(option);
+}
+
+// This test verifies that option constructor throws
+// an exception when config provided via ctor is malformed
+// - DnrInstance config has malformed SvcParams - port can't be parsed.
+TEST(Option4DnrTest, fromConfigCtorWrongSvcParamsWrongPort) {
+ // Prepare example config.
+ const std::string config = "1, myhost1.example.com. | "
+ "2, myhost2.example.com., 192.168.0.1 192.168.0.2, alpn=dot\\,h3 "
+ "dohpath=/some-path{?dns} | "
+ "100, test.net. , 10.2.3.4, port=what?"; // port must be uint_16
+
+ OptionBuffer buf;
+ buf.assign(config.begin(), config.end());
+
+ // Create option instance. Check that constructor throws.
+ Option4DnrPtr option;
+ EXPECT_THROW(option.reset(new Option4Dnr(buf.begin(), buf.end(), true)),
+ InvalidOptionDnrSvcParams);
+ ASSERT_FALSE(option);
+}
+
+// This test verifies that option constructor throws
+// an exception when config provided via ctor is malformed
+// - DnrInstance config has malformed SvcParams - dns var missing in dohpath.
+TEST(Option4DnrTest, fromConfigCtorWrongSvcParamsMissingVarInDohpath) {
+ // Prepare example config.
+ const std::string config = "1, myhost1.example.com. | "
+ "2, myhost2.example.com., 192.168.0.1 192.168.0.2, alpn=dot\\,h3 "
+ "dohpath=/some-path{?dns} | "
+ "100, test.net. , 10.2.3.4, alpn=dot\\,h3 "
+ "dohpath=/some-path"; // dns var is missing in the uri of dohpath
+
+ OptionBuffer buf;
+ buf.assign(config.begin(), config.end());
+
+ // Create option instance. Check that constructor throws.
+ Option4DnrPtr option;
+ EXPECT_THROW(option.reset(new Option4Dnr(buf.begin(), buf.end(), true)),
+ InvalidOptionDnrSvcParams);
+ ASSERT_FALSE(option);
+}
+
+// This test verifies option packing into wire data.
+// Provided data to pack contains 2 DNR instances:
+// 1. ADN only mode
+// 2. All fields included and SvcParams are given in wrong order.
+TEST(Option4DnrTest, fromConfigCtorPackUnorderedSvcParams) {
// Create option instance. Check that constructor doesn't throw.
+ const std::string config = "1, myhost1.example.com. | "
+ "2, myhost2.example.com., 192.168.0.1 192.168.0.2, port=85"
+ " alpn=dot\\,doq"; // SvcParams are NOT in increasing order
+ OptionBuffer opt_buf;
+ opt_buf.assign(config.begin(), config.end());
Option4DnrPtr option;
- EXPECT_NO_THROW(option.reset(new Option4Dnr(buf.begin(), buf.end())));
+ EXPECT_NO_THROW(option.reset(new Option4Dnr(opt_buf.begin(), opt_buf.end(), true)));
ASSERT_TRUE(option);
+
+ // Prepare on-wire format of the option.
+ isc::util::OutputBuffer out_buf(10);
+ ASSERT_NO_THROW(option->pack(out_buf));
+
+ // Prepare reference data.
+ const uint8_t ref_data[] = {
+ DHO_V4_DNR, // Option code
+ 79, // Option len
+ 0x00, 24, // DNR Instance Data Len
+ 0x00, 0x01, // Service priority is 1 dec
+ 21, // ADN Length is 21 dec
+ 0x07, 0x6D, 0x79, 0x68, 0x6F, 0x73, 0x74, '1', // FQDN: myhost1.
+ 0x07, 0x65, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65, // example.
+ 0x03, 0x63, 0x6F, 0x6D, 0x00, // com.
+ 0x00, 51, // DNR Instance Data Len
+ 0x00, 0x02, // Service priority is 2 dec
+ 21, // ADN Length is 21 dec
+ 0x07, 0x6D, 0x79, 0x68, 0x6F, 0x73, 0x74, '2', // FQDN: myhost2.
+ 0x07, 0x65, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65, // example.
+ 0x03, 0x63, 0x6F, 0x6D, 0x00, // com.
+ 8, // Addr Len
+ 192, 168, 0, 1, // IP address 1
+ 192, 168, 0, 2, // IP address 2
+ 0, 1, // SvcParamKey alpn
+ 0, 8, // SvcParamVal Len
+ 3, 'd', 'o', 't', // 3 octets long alpn-id dot
+ 3, 'd', 'o', 'q', // 3 octets long alpn-id doq
+ 0, 3, // SvcParamKey port is in correct order
+ // after alpn
+ 0, 2, // SvcParamVal Len
+ 0, 85 // port nr
+ };
+
+ size_t ref_data_size = sizeof(ref_data) / sizeof(ref_data[0]);
+
+ // Check if the buffer has the same length as the reference data,
+ // so as they can be compared directly.
+ ASSERT_EQ(ref_data_size, out_buf.getLength());
+ EXPECT_EQ(0, memcmp(ref_data, out_buf.getData(), out_buf.getLength()));
}
// This test verifies option constructor from wire data in terms
@@ -425,7 +798,7 @@ TEST(Option4DnrTest, unpackOneAdnOnly) {
TEST(Option4DnrTest, unpackOneDnrInstance) {
// Prepare data to decode - 1 DNR instance.
const uint8_t buf_data[] = {
- 0x00, 62, // DNR Instance Data Len
+ 0x00, 45, // DNR Instance Data Len
0x00, 0x01, // Service priority is 1 dec
21, // ADN Length is 21 dec
0x07, 0x6D, 0x79, 0x68, 0x6F, 0x73, 0x74, '1', // FQDN: myhost1.
@@ -434,10 +807,10 @@ TEST(Option4DnrTest, unpackOneDnrInstance) {
8, // Addr Len
192, 168, 0, 1, // IP address 1
192, 168, 0, 2, // IP address 2
- 'k', 'e', 'y', '1', '2', '3', '=', 'v', // Svc Params
- 'a', 'l', ' ', 'k', 'e', 'y', '2', '3', // Svc Params
- '4', '=', 'v', 'a', 'l', '2', ' ', 'k', // Svc Params
- 'e', 'y', '3', '4', '5' // Svc Params
+ 0, 1, // SvcParamKey alpn
+ 0, 8, // SvcParamVal Len
+ 3, 'd', 'o', 't', // 3 octets long alpn-id dot
+ 3, 'd', 'o', 'q' // 3 octets long alpn-id doq
};
OptionBuffer buf(buf_data, buf_data + sizeof(buf_data));
@@ -452,17 +825,26 @@ TEST(Option4DnrTest, unpackOneDnrInstance) {
// Check if data was unpacked correctly from wire data.
const DnrInstance& dnr_i = option->getDnrInstances()[0];
- EXPECT_EQ(62, dnr_i.getDnrInstanceDataLength());
+ EXPECT_EQ(45, dnr_i.getDnrInstanceDataLength());
EXPECT_EQ(1, dnr_i.getServicePriority());
EXPECT_EQ(21, dnr_i.getAdnLength());
EXPECT_EQ("myhost1.example.com.", dnr_i.getAdnAsText());
EXPECT_EQ(8, dnr_i.getAddrLength());
- EXPECT_EQ(29, dnr_i.getSvcParamsLength());
EXPECT_EQ(2, dnr_i.getAddresses().size());
EXPECT_EQ("192.168.0.1", dnr_i.getAddresses()[0].toText());
EXPECT_EQ("192.168.0.2", dnr_i.getAddresses()[1].toText());
- EXPECT_EQ("key123=val key234=val2 key345", dnr_i.getSvcParams());
- EXPECT_EQ(66, option->len());
+
+ // Reference SvcParams on-wire data buffer.
+ const OptionBuffer svc_params = {
+ 0, 1, // SvcParamKey alpn
+ 0, 8, // SvcParamVal Len
+ 3, 'd', 'o', 't', // 3 octets long alpn-id dot
+ 3, 'd', 'o', 'q' // 3 octets long alpn-id doq
+ };
+
+ EXPECT_EQ(svc_params, dnr_i.getSvcParams());
+ EXPECT_EQ(svc_params.size(), dnr_i.getSvcParamsLength());
+ EXPECT_EQ(49, option->len());
}
// This test verifies option constructor from wire data in terms
@@ -479,7 +861,7 @@ TEST(Option4DnrTest, unpackMixedDnrInstances) {
0x07, 0x6D, 0x79, 0x68, 0x6F, 0x73, 0x74, '1', // FQDN: myhost1.
0x07, 0x65, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65, // example.
0x03, 0x63, 0x6F, 0x6D, 0x00, // com.
- 0x00, 62, // DNR Instance Data Len
+ 0x00, 58, // DNR Instance Data Len
0x00, 0x02, // Service priority is 2 dec
21, // ADN Length is 21 dec
0x07, 0x6D, 0x79, 0x68, 0x6F, 0x73, 0x74, '2', // FQDN: myhost2.
@@ -488,10 +870,15 @@ TEST(Option4DnrTest, unpackMixedDnrInstances) {
8, // Addr Len
192, 168, 0, 1, // IP address 1
192, 168, 0, 2, // IP address 2
- 'k', 'e', 'y', '1', '2', '3', '=', 'v', // Svc Params
- 'a', 'l', ' ', 'k', 'e', 'y', '2', '3', // Svc Params
- '4', '=', 'v', 'a', 'l', '2', ' ', 'k', // Svc Params
- 'e', 'y', '3', '4', '5' // Svc Params
+ 0, 1, // SvcParamKey alpn
+ 0, 3, // SvcParamVal Len
+ 2, 'h', '3', // 2 octets long alpn-id http/3
+ 0, 3, // SvcParamKey port
+ 0, 2, // SvcParamVal Len
+ 0x4, 0xD2, // 1234 in hex
+ 0, 7, // SvcParamKey dohpath
+ 0, 8, // SvcParamVal Len
+ '/', 'q', '{', '?', 'd', 'n', 's', '}' // dohpath value
};
OptionBuffer buf(buf_data, buf_data + sizeof(buf_data));
@@ -514,18 +901,31 @@ TEST(Option4DnrTest, unpackMixedDnrInstances) {
EXPECT_EQ(0, dnr_1.getSvcParamsLength());
const DnrInstance& dnr_2 = option->getDnrInstances()[1];
- EXPECT_EQ(62, dnr_2.getDnrInstanceDataLength());
+ EXPECT_EQ(58, dnr_2.getDnrInstanceDataLength());
EXPECT_EQ(2, dnr_2.getServicePriority());
EXPECT_EQ(21, dnr_2.getAdnLength());
EXPECT_EQ("myhost2.example.com.", dnr_2.getAdnAsText());
EXPECT_EQ(8, dnr_2.getAddrLength());
- EXPECT_EQ(29, dnr_2.getSvcParamsLength());
EXPECT_EQ(2, dnr_2.getAddresses().size());
EXPECT_EQ("192.168.0.1", dnr_2.getAddresses()[0].toText());
EXPECT_EQ("192.168.0.2", dnr_2.getAddresses()[1].toText());
- EXPECT_EQ("key123=val key234=val2 key345", dnr_2.getSvcParams());
- EXPECT_EQ(92, option->len());
+ // Reference SvcParams on-wire data buffer.
+ const OptionBuffer svc_params = {
+ 0, 1, // SvcParamKey alpn
+ 0, 3, // SvcParamVal Len
+ 2, 'h', '3', // 2 octets long alpn-id http/3
+ 0, 3, // SvcParamKey port
+ 0, 2, // SvcParamVal Len
+ 0x4, 0xD2, // 1234 in hex
+ 0, 7, // SvcParamKey dohpath
+ 0, 8, // SvcParamVal Len
+ '/', 'q', '{', '?', 'd', 'n', 's', '}' // dohpath value
+ };
+ EXPECT_EQ(svc_params, dnr_2.getSvcParams());
+ EXPECT_EQ(svc_params.size(), dnr_2.getSvcParamsLength());
+
+ EXPECT_EQ(88, option->len());
}
// Test checks that exception is thrown when trying to unpack malformed wire data
@@ -601,7 +1001,7 @@ TEST(Option4DnrTest, unpackTruncatedAdn) {
}
// Test checks that exception is thrown when trying to unpack malformed wire data
-// - ADN FQDN contains only whitespace - non valid FQDN.
+// - ADN FQDN contains only whitespace - non-valid FQDN.
TEST(Option4DnrTest, unpackInvalidFqdnAdn) {
// Prepare malformed data to decode.
const uint8_t buf_data[] = {
@@ -737,7 +1137,7 @@ TEST(Option4DnrTest, unpackIpAddressNon4Modulo) {
}
// Test checks that exception is thrown when trying to unpack malformed wire data
-// - SvcParams Key contains char that is not allowed.
+// - SvcParams contain unknown SvcParamKey.
TEST(Option4DnrTest, unpackvcParamsInvalidCharKey) {
// Prepare malformed data to decode.
const uint8_t buf_data[] = {
@@ -756,7 +1156,13 @@ TEST(Option4DnrTest, unpackvcParamsInvalidCharKey) {
8, // Addr Len
192, 168, 0, 1, // IP address 1
192, 168, 0, 2, // IP address 2 truncated
- 'k', 'e', 'y', '+', '2', '3' // Svc Params key has forbidden char +
+ 0, 1, // SvcParamKey alpn
+ 0, 8, // SvcParamVal Len
+ 3, 'd', 'o', 't', // 3 octets long alpn-id dot
+ 3, 'd', 'o', 'q', // 3 octets long alpn-id doq
+ 0, 99, // unknown SvcParamKey
+ 0, 2, // SvcParamVal Len
+ 1, 2 // random data
};
OptionBuffer buf(buf_data, buf_data + sizeof(buf_data));
@@ -770,17 +1176,17 @@ TEST(Option4DnrTest, unpackvcParamsInvalidCharKey) {
// This test verifies that string representation of the option returned by
// toText method is correctly formatted.
TEST(Option4DnrTest, toText) {
+ // Prepare example config.
+ const std::string config = "1, myhost1.example.com.";
+
+ OptionBuffer in_buf;
+ in_buf.assign(config.begin(), config.end());
+
// Create option instance. Check that constructor doesn't throw.
Option4DnrPtr option;
- EXPECT_NO_THROW(option.reset(new Option4Dnr()));
+ EXPECT_NO_THROW(option.reset(new Option4Dnr(in_buf.begin(), in_buf.end(), true)));
ASSERT_TRUE(option);
- // Prepare example DNR instance to add.
- DnrInstance dnr_1 = DnrInstance(Option::V4, 1, "myhost1.example.com.");
-
- // Add DNR instance.
- option->addDnrInstance(dnr_1);
-
// Let's check if toText() works ok.
// toText() len does not count in headers len.
const int indent = 4;
diff --git a/src/lib/dhcp/tests/option6_dnr_unittest.cc b/src/lib/dhcp/tests/option6_dnr_unittest.cc
index bf00fbeaa3..40854572d6 100644
--- a/src/lib/dhcp/tests/option6_dnr_unittest.cc
+++ b/src/lib/dhcp/tests/option6_dnr_unittest.cc
@@ -77,7 +77,7 @@ TEST(Option6DnrTest, onWireCtorDataTruncated) {
}
// Test checks that exception is thrown when trying to unpack malformed wire data
-// - ADN FQDN contains only whitespace - non valid FQDN.
+// - ADN FQDN contains only whitespace - non-valid FQDN.
TEST(Option6DnrTest, onWireCtorOnlyWhitespaceFqdn) {
// Prepare data to decode - ADN only mode.
const uint8_t buf_data[] = {
@@ -308,7 +308,8 @@ TEST(Option6DnrTest, onWireCtorSvcParamsIncluded) {
const Option6Dnr::AddressContainer& addresses = option->getAddresses();
EXPECT_EQ(1, addresses.size());
EXPECT_EQ("2001:db8:1::dead:beef", addresses[0].toText());
- EXPECT_EQ(12, option->getSvcParamsLength());
+
+ // Reference SvcParams on-wire data buffer.
const OptionBuffer svc_params = {
0, 1, // SvcParamKey alpn
0, 8, // SvcParamVal Len
@@ -316,6 +317,7 @@ TEST(Option6DnrTest, onWireCtorSvcParamsIncluded) {
3, 'd', 'o', 'q' // 3 octets long alpn-id doq
};
EXPECT_EQ(svc_params, option->getSvcParams());
+ EXPECT_EQ(svc_params.size(), option->getSvcParamsLength());
// BTW let's check if len() works ok.
// expected len: 20 (FQDN) + 2 (ADN Len) + 2 (Service priority) + 4 (headers) = 28
@@ -612,7 +614,8 @@ TEST(Option6DnrTest, toText) {
EXPECT_EQ(expected, option->toText(indent));
}
-// This test verifies on-wire format of the option is correctly created in ADN only mode.
+// This test verifies that the option is correctly created in ADN only mode,
+// when constructor from convenient string notation is used.
TEST(Option6DnrTest, fromConfigCtorPackAdnOnlyMode) {
// Prepare example config.
const std::string config = "9, myhost.example.com.";
@@ -648,8 +651,8 @@ TEST(Option6DnrTest, fromConfigCtorPackAdnOnlyMode) {
EXPECT_EQ(0, memcmp(ref_data, buf.getData(), buf.getLength()));
}
-// This test verifies on-wire format of the option is correctly created when
-// IP addresses and Svc Params are also included.
+// This test verifies on-wire format of the option is correctly created with pack
+// method when IP addresses and Svc Params are also included.
TEST(Option6DnrTest, pack) {
// Prepare example config.
const std::string config = "9, myhost.example.com., 2001:db8:1::dead:beef ff02::face:b00c,"