-// Copyright (C) 2012-2017 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2018 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
#include <dhcpsrv/lease.h>
#include <util/pointer_util.h>
+#include <boost/scoped_ptr.hpp>
#include <sstream>
#include <iostream>
+
using namespace isc::util;
using namespace isc::data;
using namespace std;
}
}
+Lease::Type
+Lease::textToType(const std::string& text) {
+ if (text == "V4") {
+ return (TYPE_V4);
+
+ } else if (text == "IA_NA") {
+ return (TYPE_NA);
+
+ } else if (text == "IA_TA") {
+ return (TYPE_TA);
+
+ } else if (text == "IA_PD") {
+ return (TYPE_PD);
+ }
+
+ isc_throw(BadValue, "unsupported lease type " << text);
+}
+
std::string
Lease::basicStatesToText(const uint32_t state) {
switch (state) {
fqdn_rev_ == other.fqdn_rev_);
}
+void
+Lease::fromElementCommon(const LeasePtr& lease, const data::ConstElementPtr& element) {
+ if (!element) {
+ isc_throw(BadValue, "parsed lease data is null");
+ }
+
+ if (element->getType() != Element::map) {
+ isc_throw(BadValue, "parsed lease data is not a JSON map");
+ }
+
+
+ if (!lease) {
+ isc_throw(Unexpected, "pointer to parsed lease is null");
+ }
+
+ bool is_v6 = static_cast<bool>(boost::dynamic_pointer_cast<Lease6>(lease));
+
+ // IP address.
+ ConstElementPtr ip_address = element->get("ip-address");
+ if (!ip_address || (ip_address->getType() != Element::string)) {
+ isc_throw(BadValue, "ip-address not present in the parsed lease"
+ " or it is not a string");
+ }
+
+ boost::scoped_ptr<asiolink::IOAddress> io_address;
+ try {
+ io_address.reset(new asiolink::IOAddress(ip_address->stringValue()));
+
+ } catch (const std::exception& ex) {
+ isc_throw(BadValue, "invalid IP address " << ip_address->stringValue()
+ << " in the parsed lease");
+ }
+
+ if (!is_v6 && !io_address->isV4()) {
+ isc_throw(BadValue, "address " << *io_address << " it not an IPv4 address");
+
+ } else if (is_v6 && !io_address->isV6()) {
+ isc_throw(BadValue, "address " << *io_address << " it not an IPv6 address");
+ }
+
+ lease->addr_ = *io_address;
+
+ // Subnet identifier.
+ ConstElementPtr subnet_id = element->get("subnet-id");
+ if (!subnet_id || (subnet_id->getType() != Element::integer)) {
+ isc_throw(BadValue, "subnet-id not present in the parsed lease"
+ " or it is not a number");
+ }
+
+ if (subnet_id->intValue() <= 0) {
+ isc_throw(BadValue, "subnet-id " << subnet_id->intValue() << " is not"
+ << " a positive integer");
+ }
+
+ lease->subnet_id_ = SubnetID(subnet_id->intValue());
+
+ // Hardware address.
+ ConstElementPtr hw_address = element->get("hw-address");
+ if (hw_address) {
+ if (hw_address->getType() != Element::string) {
+ isc_throw(BadValue, "hw-address is not a string in the parsed lease");
+
+ }
+
+ try {
+ HWAddr parsed_hw_address = HWAddr::fromText(hw_address->stringValue());
+ lease->hwaddr_.reset(new HWAddr(parsed_hw_address.hwaddr_, HTYPE_ETHER));
+
+ } catch (const std::exception& ex) {
+ isc_throw(BadValue, "invalid hardware address "
+ << hw_address->stringValue() << " in the parsed lease");
+ }
+
+ } else if (!is_v6) {
+ isc_throw(BadValue, "hw-address not present in the parsed lease");
+ }
+
+ // cltt
+ ConstElementPtr cltt = element->get("cltt");
+ if (!cltt || (cltt->getType() != Element::integer)) {
+ isc_throw(BadValue, "cltt is not present in the parsed lease"
+ " or it is not a number");
+ }
+
+ if (cltt->intValue() <= 0) {
+ isc_throw(BadValue, "cltt " << cltt->intValue() << " is not a"
+ " positive integer in the parsed lease");
+ }
+
+ lease->cltt_ = static_cast<time_t>(cltt->intValue());
+
+ // valid lifetime
+ ConstElementPtr valid_lifetime = element->get("valid-lft");
+ if (!valid_lifetime || (valid_lifetime->getType() != Element::integer)) {
+ isc_throw(BadValue, "valid-lft is not present in the parsed lease"
+ " or it is not a number");
+ }
+
+ if (valid_lifetime->intValue() < 0) {
+ isc_throw(BadValue, "valid-lft " << valid_lifetime->intValue()
+ << " is negative in the parsed lease");
+ }
+
+ lease->valid_lft_ = valid_lifetime->intValue();
+
+ // fqdn-fwd
+ ConstElementPtr fqdn_fwd = element->get("fqdn-fwd");
+ if (!fqdn_fwd || fqdn_fwd->getType() != Element::boolean) {
+ isc_throw(BadValue, "fqdn-fwd is not present in the parsed lease"
+ " or it is not a boolean value");
+ }
+
+ lease->fqdn_fwd_ = fqdn_fwd->boolValue();
+
+ // fqdn-fwd
+ ConstElementPtr fqdn_rev = element->get("fqdn-rev");
+ if (!fqdn_rev || (fqdn_rev->getType() != Element::boolean)) {
+ isc_throw(BadValue, "fqdn-rev is not present in the parsed lease"
+ " or it is not a boolean value");
+ }
+
+ lease->fqdn_rev_ = fqdn_rev->boolValue();
+
+ // hostname
+ ConstElementPtr hostname = element->get("hostname");
+ if (!hostname || (hostname->getType() != Element::string)) {
+ isc_throw(BadValue, "hostname is not present in the parsed lease"
+ " or it is not a string value");
+ }
+
+ lease->hostname_ = hostname->stringValue();
+
+ // state
+ ConstElementPtr state = element->get("state");
+ if (!state || (state->getType() != Element::integer)) {
+ isc_throw(BadValue, "state is not present in the parsed lease"
+ " or it is not a number");
+ }
+
+ if ((state->intValue() < 0) || (state->intValue() > Lease::STATE_EXPIRED_RECLAIMED)) {
+ isc_throw(BadValue, "state " << state->intValue()
+ << " must be in range [0.."
+ << Lease::STATE_EXPIRED_RECLAIMED << "]");
+ }
+
+ lease->state_ = state->intValue();
+}
+
Lease4::Lease4(const Lease4& other)
: Lease(other.addr_, other.t1_, other.t2_, other.valid_lft_,
other.subnet_id_, other.cltt_, other.fqdn_fwd_,
return (map);
}
+Lease4Ptr
+Lease4::fromElement(const ConstElementPtr& element) {
+ Lease4Ptr lease(new Lease4());
+
+ // Extract common lease properties into the lease.
+ fromElementCommon(boost::dynamic_pointer_cast<Lease>(lease), element);
+
+ // Client identifier is IPv4 specific.
+ ConstElementPtr client_id = element->get("client-id");
+ if (client_id) {
+ if (client_id->getType() != Element::string) {
+ isc_throw(BadValue, "client identifier is not a string in the"
+ " parsed lease");
+ }
+
+ try {
+ lease->client_id_ = ClientId::fromText(client_id->stringValue());
+
+ } catch (const std::exception& ex) {
+ isc_throw(BadValue, "invalid client identifier "
+ << client_id->stringValue() << " in the parsed lease");
+ }
+ }
+
+ return (lease);
+}
+
Lease6::Lease6(Lease::Type type, const isc::asiolink::IOAddress& addr,
DuidPtr duid, uint32_t iaid, uint32_t preferred, uint32_t valid,
uint32_t t1, uint32_t t2, SubnetID subnet_id,
return (map);
}
+Lease6Ptr
+Lease6::fromElement(const data::ConstElementPtr& element) {
+ Lease6Ptr lease(new Lease6());
+
+ // Extract common lease properties into the lease.
+ fromElementCommon(boost::dynamic_pointer_cast<Lease>(lease), element);
+
+ // lease type
+ ConstElementPtr lease_type = element->get("type");
+ if (!lease_type || (lease_type->getType() != Element::string)) {
+ isc_throw(BadValue, "type is not present in the parsed lease"
+ " or it is not a string value");
+ }
+
+ lease->type_ = textToType(lease_type->stringValue());
+
+ // prefix length
+ ConstElementPtr prefix_len = element->get("prefix-len");
+ if (lease->type_ == Lease::TYPE_PD) {
+ if (!prefix_len || (prefix_len->getType() != Element::integer)) {
+ isc_throw(BadValue, "prefix-len is not present in the parsed lease"
+ " or it is not a number");
+ }
+
+ if ((prefix_len->intValue() < 1) || (prefix_len->intValue() > 128)) {
+ isc_throw(BadValue, "prefix-len " << prefix_len->intValue()
+ << " must be in range of [1..128]");
+ }
+
+ lease->prefixlen_ = static_cast<uint8_t>(prefix_len->intValue());
+ }
+
+ // IAID
+ ConstElementPtr iaid = element->get("iaid");
+ if (!iaid || (iaid->getType() != Element::integer)) {
+ isc_throw(BadValue, "iaid is not present in the parsed lease"
+ " or it is not a number");
+ }
+
+ if (iaid->intValue() < 0) {
+ isc_throw(BadValue, "iaid " << iaid->intValue() << " must not be negative");
+ }
+
+ lease->iaid_ = static_cast<uint32_t>(iaid->intValue());
+
+ // DUID
+ ConstElementPtr duid = element->get("duid");
+ if (!duid || (duid->getType() != Element::string)) {
+ isc_throw(BadValue, "duid not present in the parsed lease"
+ " or it is not a string");
+ }
+
+ try {
+ DUID parsed_duid = DUID::fromText(duid->stringValue());
+ lease->duid_.reset(new DUID(parsed_duid.getDuid()));
+
+ } catch (const std::exception& ex) {
+ isc_throw(BadValue, "invalid DUID "
+ << duid->stringValue() << " in the parsed lease");
+ }
+
+ // preferred lifetime
+ ConstElementPtr preferred_lft = element->get("preferred-lft");
+ if (!preferred_lft || (preferred_lft->getType() != Element::integer)) {
+ isc_throw(BadValue, "preferred-lft is not present in the parsed lease"
+ " or is not a number");
+ }
+
+ if (preferred_lft->intValue() < 0) {
+ isc_throw(BadValue, "preferred-lft " << preferred_lft->intValue()
+ << " must not be negative");
+ }
+
+ lease->preferred_lft_ = static_cast<uint32_t>(preferred_lft->intValue());
+
+ return (lease);
+}
std::ostream&
operator<<(std::ostream& os, const Lease& lease) {
/// because subnet.h needs Lease::Type, so it includes lease.h
typedef uint32_t SubnetID;
+struct Lease;
+
+/// @brief Pointer to the lease object.
+typedef boost::shared_ptr<Lease> LeasePtr;
+
/// @brief a common structure for IPv4 and IPv6 leases
///
/// This structure holds all information that is common between IPv4 and IPv6
/// @return text description
static std::string typeToText(Type type);
+ /// @brief Converts type name to the actual type.
+ ///
+ /// @param text lease type as text.
+ /// @return converted type.
+ /// @throw BadValue if the text contains unsupported value.
+ static Type textToType(const std::string& text);
+
/// @name Common lease states constants.
//@{
///
///
/// @param probation_period lease lifetime will be set to this value
virtual void decline(uint32_t probation_period) = 0;
+
+protected:
+
+ /// @brief Sets common (for v4 and v6) properties of the lease object.
+ ///
+ /// This method is called by the @c fromElement methods of the @c Lease
+ /// class derivations.
+ ///
+ /// @param [out] lease pointer to the lease object for which common
+ /// properties should be set.
+ /// @param element pointer to the data element object to be parsed.
+ static void fromElementCommon(const LeasePtr& lease,
+ const data::ConstElementPtr& element);
+
};
+struct Lease4;
+
+/// @brief Pointer to a Lease4 structure.
+typedef boost::shared_ptr<Lease4> Lease4Ptr;
+
/// @brief Structure that holds a lease for IPv4 address
///
/// For performance reasons it is a simple structure, not a class. If we chose
/// @brief Return the JSON representation of a lease
virtual isc::data::ElementPtr toElement() const;
+ /// @brief Returns pointer to the IPv4 lease created from JSON
+ /// representation.
+ ///
+ /// @param element pointer to the data element object to be parsed.
+ /// @return Pointer to the created lease.
+ static Lease4Ptr fromElement(const data::ConstElementPtr& element);
+
/// @todo: Add DHCPv4 failover related fields here
};
-/// @brief Pointer to a Lease4 structure.
-typedef boost::shared_ptr<Lease4> Lease4Ptr;
-
/// @brief A collection of IPv4 leases.
typedef std::vector<Lease4Ptr> Lease4Collection;
/// @brief A shared pointer to the collection of IPv4 leases.
typedef boost::shared_ptr<Lease4Collection> Lease4CollectionPtr;
+struct Lease6;
+
+/// @brief Pointer to a Lease6 structure.
+typedef boost::shared_ptr<Lease6> Lease6Ptr;
+
/// @brief Structure that holds a lease for IPv6 address and/or prefix
///
/// For performance reasons it is a simple structure, not a class. If we chose
/// @brief Return the JSON representation of a lease
virtual isc::data::ElementPtr toElement() const;
-};
-/// @brief Pointer to a Lease6 structure.
-typedef boost::shared_ptr<Lease6> Lease6Ptr;
+ /// @brief Returns pointer to the IPv6 lease created from JSON
+ /// representation.
+ ///
+ /// @param element pointer to the data element object to be parsed.
+ /// @return Pointer to the created lease.
+ static Lease6Ptr fromElement(const data::ConstElementPtr& element);
+};
/// @brief Pointer to a const Lease6 structure.
typedef boost::shared_ptr<const Lease6> ConstLease6Ptr;
-// Copyright (C) 2013-2017 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2018 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
return (lease);
}
+/// @brief Tests that an exception is thrown when one of the lease
+/// parameters is missing or invalid during lease parsing.
+///
+/// If the tested parameter is mandatory the test first removes the
+/// specified parameter from the JSON structure and tries to re-create
+/// the lease, expecting an error.
+/// Next, the test sets invalid value for this parameter and also
+/// expected an error.
+///
+/// @tparam LeaseType @c Lease4 or @c Lease6.
+/// @tparam ValueType type of the invalid value to be used for the
+/// specified parameter.
+/// @param lease_as_json a string contains lease in a JSON format.
+/// @param parameter_name name of the lease parameter to be tested.
+/// @param invalid_value invalid parameter value.
+/// @param mandatory boolean value indicating if the parameter is
+/// mandatory.
+template<typename LeaseType, typename ValueType>
+void testInvalidElement(const std::string& lease_as_json,
+ const std::string& parameter_name,
+ ValueType invalid_value,
+ const bool mandatory = true) {
+ ElementPtr lease;
+
+ // If the parameter is mandatory, check that the exception is
+ // thrown if it is missing.
+ if (mandatory) {
+ ASSERT_NO_THROW(lease = Element::fromJSON(lease_as_json));
+ lease->remove(parameter_name);
+ EXPECT_THROW(LeaseType::fromElement(lease), BadValue)
+ << "test failed for " << parameter_name;
+ }
+
+ // Set invalid value and expect an error.
+ ASSERT_NO_THROW(lease = Element::fromJSON(lease_as_json));
+ lease->set(parameter_name, Element::create(invalid_value));
+ EXPECT_THROW(LeaseType::fromElement(lease), BadValue)
+ << "test failed for " << parameter_name
+ << " and invalid value " << invalid_value;
+}
+
/// @brief Fixture class used in Lease4 testing.
class Lease4Test : public ::testing::Test {
public:
runToElementTest<Lease4>(expected, lease);
}
+// Verify that the Lease4 can be created from JSON.
+TEST_F(Lease4Test, fromElement) {
+ std::string json = "{"
+ "\"client-id\": \"17:34:e2:ff:09:92:54\","
+ "\"cltt\": 12345678,"
+ "\"fqdn-fwd\": true,"
+ "\"fqdn-rev\": true,"
+ "\"hostname\": \"urania.example.org\","
+ "\"hw-address\": \"08:00:2b:02:3f:4e\","
+ "\"ip-address\": \"192.0.2.3\","
+ "\"state\": 0,"
+ "\"subnet-id\": 789,"
+ "\"valid-lft\": 3600 "
+ "}";
+
+ Lease4Ptr lease;
+ ASSERT_NO_THROW(lease = Lease4::fromElement(Element::fromJSON(json)));
+
+ ASSERT_TRUE(lease);
+
+ EXPECT_EQ("192.0.2.3", lease->addr_.toText());
+ EXPECT_EQ(789, static_cast<uint32_t>(lease->subnet_id_));
+ ASSERT_TRUE(lease->hwaddr_);
+ EXPECT_EQ("hwtype=1 08:00:2b:02:3f:4e", lease->hwaddr_->toText());
+ ASSERT_TRUE(lease->client_id_);
+ EXPECT_EQ("17:34:e2:ff:09:92:54", lease->client_id_->toText());
+ EXPECT_EQ(12345678, lease->cltt_);
+ EXPECT_EQ(3600, lease->valid_lft_);
+ EXPECT_TRUE(lease->fqdn_fwd_);
+ EXPECT_TRUE(lease->fqdn_rev_);
+ EXPECT_EQ("urania.example.org", lease->hostname_);
+ EXPECT_EQ(Lease::STATE_DEFAULT, lease->state_);
+}
+
+// Test that specifying invalid values for a lease or not specifying
+// mandatory lease parameters causes an error while parsing the lease.
+TEST_F(Lease4Test, fromElementInvalidValues) {
+ // Create valid configuration. We use it as a base from which we will
+ // be removing some of the parameters and some values will be selectively
+ // modified.
+ std::string json = "{"
+ "\"client-id\": \"17:34:e2:ff:09:92:54\","
+ "\"cltt\": 12345678,"
+ "\"fqdn-fwd\": true,"
+ "\"fqdn-rev\": true,"
+ "\"hostname\": \"urania.example.org\","
+ "\"hw-address\": \"08:00:2b:02:3f:4e\","
+ "\"ip-address\": \"192.0.2.3\","
+ "\"state\": 0,"
+ "\"subnet-id\": 789,"
+ "\"valid-lft\": 3600 "
+ "}";
+
+ // Test invalid parameter values and missing parameters.
+ testInvalidElement<Lease4>(json, "client-id", std::string("rock"), false);
+ testInvalidElement<Lease4>(json, "cltt", std::string("xyz"));
+ testInvalidElement<Lease4>(json, "cltt", -1, false);
+ testInvalidElement<Lease4>(json, "fqdn-fwd", 123);
+ testInvalidElement<Lease4>(json, "fqdn-rev", std::string("foo"));
+ testInvalidElement<Lease4, bool>(json, "hostname", true);
+ testInvalidElement<Lease4>(json, "hw-address", "01::00::");
+ testInvalidElement<Lease4>(json, "hw-address", 1234, false);
+ testInvalidElement<Lease4, long int>(json, "ip-address", 0xFF000201);
+ testInvalidElement<Lease4>(json, "ip-address", "2001:db8:1::1", false);
+ testInvalidElement<Lease4>(json, "state", std::string("xyz"));
+ testInvalidElement<Lease4>(json, "state", 1234, false);
+ testInvalidElement<Lease4>(json, "subnet-id", std::string("xyz"));
+ testInvalidElement<Lease4>(json, "subnet-id", -5, false);
+ testInvalidElement<Lease4>(json, "valid-lft", std::string("xyz"));
+ testInvalidElement<Lease4>(json, "valid-lft", -3, false);
+}
+
// Verify that decline() method properly clears up specific fields.
TEST_F(Lease4Test, decline) {
EXPECT_FALSE(l->contains("hw-address"));
}
+// Verify that the IA_NA can be created from JSON.
+TEST(Lease6Test, fromElementNA) {
+ std::string json = "{"
+ "\"cltt\": 12345678,"
+ "\"duid\": \"00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f\","
+ "\"fqdn-fwd\": false,"
+ "\"fqdn-rev\": false,"
+ "\"hostname\": \"urania.example.org\","
+ "\"hw-address\": \"08:00:2b:02:3f:4e\","
+ "\"iaid\": 123456,"
+ "\"ip-address\": \"2001:db8::1\","
+ "\"preferred-lft\": 400,"
+ "\"state\": 1,"
+ "\"subnet-id\": 5678,"
+ "\"type\": \"IA_NA\","
+ "\"valid-lft\": 800"
+ "}";
+
+ Lease6Ptr lease;
+ ASSERT_NO_THROW(lease = Lease6::fromElement(Element::fromJSON(json)));
+
+ ASSERT_TRUE(lease);
+
+ EXPECT_EQ("2001:db8::1", lease->addr_.toText());
+ EXPECT_EQ(5678, static_cast<uint32_t>(lease->subnet_id_));
+ ASSERT_TRUE(lease->hwaddr_);
+ EXPECT_EQ("hwtype=1 08:00:2b:02:3f:4e", lease->hwaddr_->toText());
+ EXPECT_EQ(12345678, lease->cltt_);
+ EXPECT_EQ(800, lease->valid_lft_);
+ EXPECT_FALSE(lease->fqdn_fwd_);
+ EXPECT_FALSE(lease->fqdn_rev_);
+ EXPECT_EQ("urania.example.org", lease->hostname_);
+ EXPECT_EQ(Lease::STATE_DECLINED, lease->state_);
+
+ // IPv6 specific properties.
+ EXPECT_EQ(Lease::TYPE_NA, lease->type_);
+ EXPECT_EQ(0, lease->prefixlen_);
+ EXPECT_EQ(123456, lease->iaid_);
+ ASSERT_TRUE(lease->duid_);
+ EXPECT_EQ("00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f", lease->duid_->toText());
+ EXPECT_EQ(400, lease->preferred_lft_);
+}
+
+// Verify that the IA_NA can be created from JSON.
+TEST(Lease6Test, fromElementPD) {
+ std::string json = "{"
+ "\"cltt\": 12345678,"
+ "\"duid\": \"00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f\","
+ "\"fqdn-fwd\": false,"
+ "\"fqdn-rev\": false,"
+ "\"hostname\": \"urania.example.org\","
+ "\"hw-address\": \"08:00:2b:02:3f:4e\","
+ "\"iaid\": 123456,"
+ "\"ip-address\": \"3000::\","
+ "\"preferred-lft\": 400,"
+ "\"prefix-len\": 32,"
+ "\"state\": 0,"
+ "\"subnet-id\": 1234,"
+ "\"type\": \"IA_PD\","
+ "\"valid-lft\": 600"
+ "}";
+
+ Lease6Ptr lease;
+ ASSERT_NO_THROW(lease = Lease6::fromElement(Element::fromJSON(json)));
+
+ ASSERT_TRUE(lease);
+
+ EXPECT_EQ("3000::", lease->addr_.toText());
+ EXPECT_EQ(1234, static_cast<uint32_t>(lease->subnet_id_));
+ ASSERT_TRUE(lease->hwaddr_);
+ EXPECT_EQ("hwtype=1 08:00:2b:02:3f:4e", lease->hwaddr_->toText());
+ EXPECT_EQ(12345678, lease->cltt_);
+ EXPECT_EQ(600, lease->valid_lft_);
+ EXPECT_FALSE(lease->fqdn_fwd_);
+ EXPECT_FALSE(lease->fqdn_rev_);
+ EXPECT_EQ("urania.example.org", lease->hostname_);
+ EXPECT_EQ(Lease::STATE_DEFAULT , lease->state_);
+
+ // IPv6 specific properties.
+ EXPECT_EQ(Lease::TYPE_PD, lease->type_);
+ EXPECT_EQ(32, static_cast<int>(lease->prefixlen_));
+ EXPECT_EQ(123456, lease->iaid_);
+ ASSERT_TRUE(lease->duid_);
+ EXPECT_EQ("00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f", lease->duid_->toText());
+ EXPECT_EQ(400, lease->preferred_lft_);
+}
+
+// Test that specifying invalid values for a lease or not specifying
+// mandatory lease parameters causes an error while parsing the lease.
+TEST(Lease6Test, fromElementInvalidValues) {
+ // Create valid configuration. We use it as a base from which we will
+ // be removing some of the parameters and some values will be selectively
+ // modified.
+ std::string json = "{"
+ "\"cltt\": 12345678,"
+ "\"duid\": \"00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f\","
+ "\"fqdn-fwd\": false,"
+ "\"fqdn-rev\": false,"
+ "\"hostname\": \"urania.example.org\","
+ "\"hw-address\": \"08:00:2b:02:3f:4e\","
+ "\"iaid\": 123456,"
+ "\"ip-address\": \"3000::\","
+ "\"preferred-lft\": 400,"
+ "\"prefix-len\": 32,"
+ "\"state\": 0,"
+ "\"subnet-id\": 1234,"
+ "\"type\": \"IA_PD\","
+ "\"valid-lft\": 600"
+ "}";
+
+ // Test invalid parameter values and missing parameters.
+ testInvalidElement<Lease6>(json, "cltt", std::string("xyz"));
+ testInvalidElement<Lease6>(json, "cltt", -1, false);
+ testInvalidElement<Lease6>(json, "duid", "01::00::");
+ testInvalidElement<Lease6>(json, "duid", 1234, false);
+ testInvalidElement<Lease6>(json, "fqdn-fwd", 123);
+ testInvalidElement<Lease6>(json, "fqdn-rev", std::string("foo"));
+ testInvalidElement<Lease6, bool>(json, "hostname", true);
+ testInvalidElement<Lease6>(json, "hw-address", "01::00::", false);
+ testInvalidElement<Lease6>(json, "hw-address", 1234, false);
+ testInvalidElement<Lease6>(json, "iaid", std::string("1234"));
+ testInvalidElement<Lease6>(json, "iaid", -1);
+ testInvalidElement<Lease6, long int>(json, "ip-address", 0xFF000201);
+ testInvalidElement<Lease6>(json, "ip-address", "192.0.2.0", false);
+ testInvalidElement<Lease6>(json, "preferred-lft", std::string("1234"));
+ testInvalidElement<Lease6>(json, "preferred-lft", -1, false);
+ testInvalidElement<Lease6>(json, "prefix-len", std::string("1234"));
+ testInvalidElement<Lease6>(json, "prefix-len", 130);
+ testInvalidElement<Lease6>(json, "state", std::string("xyz"));
+ testInvalidElement<Lease6>(json, "state", 1234, false);
+ testInvalidElement<Lease6>(json, "subnet-id", std::string("xyz"));
+ testInvalidElement<Lease6>(json, "subnet-id", -5, false);
+ testInvalidElement<Lease6>(json, "type", std::string("IA_XY"));
+ testInvalidElement<Lease6>(json, "type", -3, false);
+ testInvalidElement<Lease6>(json, "valid-lft", std::string("xyz"));
+ testInvalidElement<Lease6>(json, "valid-lft", -3, false);
+}
+
// Verify that the lease states are correctly returned in the textual format.
TEST(Lease6Test, stateToText) {
EXPECT_EQ("default", Lease6::statesToText(Lease::STATE_DEFAULT));