From: Marcin Siodelski Date: Tue, 13 Feb 2018 18:55:43 +0000 (+0100) Subject: [5466] Implemented lease4 and lease6 parsing from JSON. X-Git-Tag: ha_checkpoints12~1^2~1 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=2901009ee4bc911f27bbf847d1eb30d39bf0d9b1;p=thirdparty%2Fkea.git [5466] Implemented lease4 and lease6 parsing from JSON. --- diff --git a/src/lib/dhcpsrv/lease.cc b/src/lib/dhcpsrv/lease.cc index 1aa6e28b70..bb5a81ad6e 100644 --- a/src/lib/dhcpsrv/lease.cc +++ b/src/lib/dhcpsrv/lease.cc @@ -1,4 +1,4 @@ -// 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 @@ -6,9 +6,11 @@ #include #include +#include #include #include + using namespace isc::util; using namespace isc::data; using namespace std; @@ -50,6 +52,24 @@ Lease::typeToText(Lease::Type type) { } } +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) { @@ -95,6 +115,154 @@ Lease::hasIdenticalFqdn(const Lease& other) const { 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(boost::dynamic_pointer_cast(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 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(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_, @@ -245,6 +413,33 @@ Lease4::toElement() const { 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), 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, @@ -417,6 +612,83 @@ Lease6::toElement() const { 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), 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(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(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(preferred_lft->intValue()); + + return (lease); +} std::ostream& operator<<(std::ostream& os, const Lease& lease) { diff --git a/src/lib/dhcpsrv/lease.h b/src/lib/dhcpsrv/lease.h index 9527f352b7..d8869b5af8 100644 --- a/src/lib/dhcpsrv/lease.h +++ b/src/lib/dhcpsrv/lease.h @@ -22,6 +22,11 @@ namespace dhcp { /// 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 LeasePtr; + /// @brief a common structure for IPv4 and IPv6 leases /// /// This structure holds all information that is common between IPv4 and IPv6 @@ -41,6 +46,13 @@ struct Lease : public isc::data::CfgToElement { /// @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. //@{ /// @@ -208,8 +220,27 @@ struct Lease : public isc::data::CfgToElement { /// /// @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 Lease4Ptr; + /// @brief Structure that holds a lease for IPv4 address /// /// For performance reasons it is a simple structure, not a class. If we chose @@ -404,18 +435,27 @@ struct Lease4 : public Lease { /// @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 Lease4Ptr; - /// @brief A collection of IPv4 leases. typedef std::vector Lease4Collection; /// @brief A shared pointer to the collection of IPv4 leases. typedef boost::shared_ptr Lease4CollectionPtr; +struct Lease6; + +/// @brief Pointer to a Lease6 structure. +typedef boost::shared_ptr 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 @@ -542,10 +582,14 @@ struct Lease6 : public Lease { /// @brief Return the JSON representation of a lease virtual isc::data::ElementPtr toElement() const; -}; -/// @brief Pointer to a Lease6 structure. -typedef boost::shared_ptr 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 ConstLease6Ptr; diff --git a/src/lib/dhcpsrv/tests/lease_unittest.cc b/src/lib/dhcpsrv/tests/lease_unittest.cc index 827ccd98db..a81719e503 100644 --- a/src/lib/dhcpsrv/tests/lease_unittest.cc +++ b/src/lib/dhcpsrv/tests/lease_unittest.cc @@ -1,4 +1,4 @@ -// 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 @@ -50,6 +50,47 @@ Lease4 createLease4(const std::string& hostname, const bool fqdn_fwd, 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 +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: @@ -467,6 +508,78 @@ TEST_F(Lease4Test, toElement) { runToElementTest(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(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(json, "client-id", std::string("rock"), false); + testInvalidElement(json, "cltt", std::string("xyz")); + testInvalidElement(json, "cltt", -1, false); + testInvalidElement(json, "fqdn-fwd", 123); + testInvalidElement(json, "fqdn-rev", std::string("foo")); + testInvalidElement(json, "hostname", true); + testInvalidElement(json, "hw-address", "01::00::"); + testInvalidElement(json, "hw-address", 1234, false); + testInvalidElement(json, "ip-address", 0xFF000201); + testInvalidElement(json, "ip-address", "2001:db8:1::1", false); + testInvalidElement(json, "state", std::string("xyz")); + testInvalidElement(json, "state", 1234, false); + testInvalidElement(json, "subnet-id", std::string("xyz")); + testInvalidElement(json, "subnet-id", -5, false); + testInvalidElement(json, "valid-lft", std::string("xyz")); + testInvalidElement(json, "valid-lft", -3, false); +} + // Verify that decline() method properly clears up specific fields. TEST_F(Lease4Test, decline) { @@ -1013,6 +1126,144 @@ TEST(Lease6Test, toElementPrefix) { 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(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(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(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(json, "cltt", std::string("xyz")); + testInvalidElement(json, "cltt", -1, false); + testInvalidElement(json, "duid", "01::00::"); + testInvalidElement(json, "duid", 1234, false); + testInvalidElement(json, "fqdn-fwd", 123); + testInvalidElement(json, "fqdn-rev", std::string("foo")); + testInvalidElement(json, "hostname", true); + testInvalidElement(json, "hw-address", "01::00::", false); + testInvalidElement(json, "hw-address", 1234, false); + testInvalidElement(json, "iaid", std::string("1234")); + testInvalidElement(json, "iaid", -1); + testInvalidElement(json, "ip-address", 0xFF000201); + testInvalidElement(json, "ip-address", "192.0.2.0", false); + testInvalidElement(json, "preferred-lft", std::string("1234")); + testInvalidElement(json, "preferred-lft", -1, false); + testInvalidElement(json, "prefix-len", std::string("1234")); + testInvalidElement(json, "prefix-len", 130); + testInvalidElement(json, "state", std::string("xyz")); + testInvalidElement(json, "state", 1234, false); + testInvalidElement(json, "subnet-id", std::string("xyz")); + testInvalidElement(json, "subnet-id", -5, false); + testInvalidElement(json, "type", std::string("IA_XY")); + testInvalidElement(json, "type", -3, false); + testInvalidElement(json, "valid-lft", std::string("xyz")); + testInvalidElement(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));