From e602856372cf74ec0ec9ee90ee8890ff6bd9b6fd Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Fri, 8 May 2015 21:37:23 +0200 Subject: [PATCH] [3747] Added method which checks if lease belongs to the client. --- src/lib/dhcpsrv/lease.cc | 45 +++++- src/lib/dhcpsrv/lease.h | 88 ++++++++++++ src/lib/dhcpsrv/tests/lease_unittest.cc | 176 +++++++++++++++++------- src/lib/util/Makefile.am | 1 + src/lib/util/pointer_util.h | 39 ++++++ 5 files changed, 294 insertions(+), 55 deletions(-) create mode 100644 src/lib/util/pointer_util.h diff --git a/src/lib/dhcpsrv/lease.cc b/src/lib/dhcpsrv/lease.cc index ad1a6f46b2..67fb5f1915 100644 --- a/src/lib/dhcpsrv/lease.cc +++ b/src/lib/dhcpsrv/lease.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2012-2014 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC") // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above @@ -13,8 +13,10 @@ // PERFORMANCE OF THIS SOFTWARE. #include +#include #include +using namespace isc::util; using namespace std; namespace isc { @@ -29,6 +31,7 @@ Lease::Lease(const isc::asiolink::IOAddress& addr, uint32_t t1, uint32_t t2, fqdn_fwd_(fqdn_fwd), fqdn_rev_(fqdn_rev), hwaddr_(hwaddr) { } + std::string Lease::typeToText(Lease::Type type) { switch (type) { @@ -88,6 +91,25 @@ Lease4::Lease4(const Lease4& other) } } +Lease4::Lease4(const isc::asiolink::IOAddress& address, + const HWAddrPtr& hw_address, + const ClientIdPtr& client_id, + const uint32_t valid_lifetime, + const uint32_t t1, + const uint32_t t2, + const time_t cltt, + const SubnetID subnet_id, + const bool fqdn_fwd, + const bool fqdn_rev, + const std::string& hostname) + + : Lease(address, t1, t2, valid_lifetime, subnet_id, cltt, fqdn_fwd, + fqdn_rev, hostname, hw_address), + ext_(0), client_id_(client_id) { +} + + + const std::vector& Lease4::getClientIdVector() const { if(!client_id_) { @@ -139,6 +161,23 @@ Lease4::matches(const Lease4& other) const { *hwaddr_ == *other.hwaddr_) ); } +bool +Lease4::belongsToClient(const HWAddrPtr& hw_address, + const ClientIdPtr& client_id) const { + // If client id matches, lease matches. + if (equalValues(client_id, client_id_)) { + return (true); + + } else if (!client_id || !client_id_) { + // If client id is unspecified, use HW address. + if (equalValues(hw_address, hwaddr_)) { + return (true); + } + } + + return (false); +} + Lease4& Lease4::operator=(const Lease4& other) { if (this != &other) { @@ -241,13 +280,13 @@ std::string Lease4::toText() const { ostringstream stream; - /// @todo: print out client-id (if present) stream << "Address: " << addr_ << "\n" << "Valid life: " << valid_lft_ << "\n" << "T1: " << t1_ << "\n" << "T2: " << t2_ << "\n" << "Cltt: " << cltt_ << "\n" - << "Hardware addr: " << (hwaddr_?hwaddr_->toText(false):"(none)") << "\n" + << "Hardware addr: " << (hwaddr_ ? hwaddr_->toText(false) : "(none)") << "\n" + << "Client id: " << (client_id_ ? client_id_->toText() : "(none)") << "\n" << "Subnet ID: " << subnet_id_ << "\n"; return (stream.str()); diff --git a/src/lib/dhcpsrv/lease.h b/src/lib/dhcpsrv/lease.h index 21cf9a3653..2a8e3d6298 100644 --- a/src/lib/dhcpsrv/lease.h +++ b/src/lib/dhcpsrv/lease.h @@ -217,6 +217,32 @@ struct Lease4 : public Lease { } } + /// @brief Constructor. + /// + /// @param address IPv4 address. + /// @param hw_address Pointer to client's HW addresss. + /// @param client_id pointer to the client id structure. + /// @param valid_lifetime Valid lifetime value. + /// @param t1 Renew timer. + /// @param t2 Rebind timer. + /// @param cltt Timestamp when the lease is acquired, renewed. + /// @param subnet_id Subnet identifier. + /// @param fqdn_fwd Forward DNS update performed. + /// @param fqdn_rev Reverse DNS update performed. + /// @param hostname Client's name for the DNS update.. + Lease4(const isc::asiolink::IOAddress& address, + const HWAddrPtr& hw_address, + const ClientIdPtr& client_id, + const uint32_t valid_lifetime, + const uint32_t t1, + const uint32_t t2, + const time_t cltt, + const SubnetID subnet_id, + const bool fqdn_fwd = false, + const bool fqdn_rev = false, + const std::string& hostname = ""); + + /// @brief Default constructor /// /// Initialize fields that don't have a default constructor. @@ -253,6 +279,68 @@ struct Lease4 : public Lease { /// @return true if the selected parameters of the two leases match. bool matches(const Lease4& other) const; + /// @brief Check if the lease belongs to the client with the given + /// identifiers. + /// + /// This method checks if the lease belongs to the client using the + /// specified HW address and/or client identifier. Note that any of the + /// pointers passed to this method may be set to null, in which case + /// they are treated as unspecified and are not used for matching the + /// client with the lease. + /// + /// According to the DHCPv4 specifications, the client identifier takes + /// precedence over the HW address when identifying the lease for the + /// client on the server side. In particular, the RFC4361 introduces the + /// use of DUID for DHCPv4 which should be a stable identifier for the + /// client. The use of stable identifier allows for the correlation of the + /// DHCPv4 and DHCPv6 clients in the dual stack networks. It also allows + /// for allocating the same lease to the client which hardware (and thus + /// MAC address) has changed. + /// + /// By default, Kea respects the precedence of the client identifier over + /// MAC address and when this method finds the match of the client + /// identifier with the client identifier stored in the lease, it will + /// treat the lease as the lease of this client, even when the HW + /// address doesn't match. + /// + /// The HW address is used for matching the client with the lease only + /// when the lease is not associated with any client identifier (client + /// identifier for the lease is null) or when the client identifier + /// parameter passed to this method is null. This facilitates the following + /// cases: + /// - client didn't generate client identifier and is only using the chaddr + /// field to identify itself. + /// - server's administrator configured the server to ignore client identifier, + /// the client obtained the new lease, and the administrator reconfigured + /// the server to NOT ignore the client identifier. The client is trying + /// to renew its lease and both the client identifier and HW address is + /// used for matching the lease which doesn't have the record of the + /// client identifier. + /// - client obtained the lease using the HW address and client identifier, + /// the server's administrator configured the server to ignore the client + /// identifier, and the client returns to renew the lease. This time, the + /// lease has a record of both client identifier and the HW address but + /// only the HW address is used for matching the client to the lease. + /// + /// Note that the typical case when the server's administrator may want to + /// force ignoring client identifier passed in the client's message is when + /// the client is performing multi-stage boot. In such case, the client + /// identifiers may change on various stages of the boot, but the HW address + /// will remain stable. The server's administrator prefers to use the + /// HW address for client identification in this case. + /// + /// It may also be useful to ignore client identifiers to mitigate the + /// problem of broken client implementations which generate new client + /// identifiers every time they connect to the network. + /// + /// @param hw_address Pointer to the HW address of the client. + /// @param client_id Pointer to the client identifier structure. + /// + /// @return true if the lease belongs to the client using the specified + /// hardware address and/or client identifier. + bool belongsToClient(const HWAddrPtr& hw_address, + const ClientIdPtr& client_id) const; + /// @brief Assignment operator. /// /// @param other the @c Lease4 object to be assigned. diff --git a/src/lib/dhcpsrv/tests/lease_unittest.cc b/src/lib/dhcpsrv/tests/lease_unittest.cc index 4cfaffb39b..784b3f899f 100644 --- a/src/lib/dhcpsrv/tests/lease_unittest.cc +++ b/src/lib/dhcpsrv/tests/lease_unittest.cc @@ -57,27 +57,24 @@ Lease4 createLease4(const std::string& hostname, const bool fqdn_fwd, class Lease4Test : public ::testing::Test { public: - /// Default constructor + /// @brief Default constructor /// /// Currently it only initializes hardware address. Lease4Test() { hwaddr_.reset(new HWAddr(HWADDR, sizeof(HWADDR), HTYPE_ETHER)); + clientid_.reset(new ClientId(CLIENTID, sizeof(CLIENTID))); } /// Hardware address, used by tests. HWAddrPtr hwaddr_; + + /// Pointer to the client identifier used by tests. + ClientIdPtr clientid_; }; -/// Lease4 is also defined in lease_mgr.h, so is tested in this file as well. -// This test checks if the Lease4 structure can be instantiated correctly +// This test checks if the Lease4 structure can be instantiated correctly. TEST_F(Lease4Test, constructor) { - - // Random values for the tests - const uint8_t CLIENTID[] = {0x17, 0x34, 0xe2, 0xff, 0x09, 0x92, 0x54}; - std::vector clientid_vec(CLIENTID, CLIENTID + sizeof(CLIENTID)); - ClientId clientid(clientid_vec); - - // ...and a time + // Get current time for the use in Lease. const time_t current_time = time(NULL); // Other random constants. @@ -93,15 +90,14 @@ TEST_F(Lease4Test, constructor) { for (int i = 0; i < sizeof(ADDRESS) / sizeof(ADDRESS[0]); ++i) { // Create the lease - Lease4 lease(ADDRESS[i], hwaddr_, - CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, 0, 0, + Lease4 lease(ADDRESS[i], hwaddr_, clientid_, VALID_LIFETIME, 0, 0, current_time, SUBNET_ID, true, true, "hostname.example.com."); EXPECT_EQ(ADDRESS[i], static_cast(lease.addr_)); EXPECT_EQ(0, lease.ext_); EXPECT_TRUE(hwaddr_ == lease.hwaddr_); - EXPECT_TRUE(clientid == *lease.client_id_); + EXPECT_TRUE(*clientid_ == *lease.client_id_); EXPECT_EQ(0, lease.t1_); EXPECT_EQ(0, lease.t2_); EXPECT_EQ(VALID_LIFETIME, lease.valid_lft_); @@ -118,11 +114,7 @@ TEST_F(Lease4Test, constructor) { // This test verfies that copy constructor copies Lease4 fields correctly. TEST_F(Lease4Test, copyConstructor) { - const uint8_t CLIENTID[] = {0x17, 0x34, 0xe2, 0xff, 0x09, 0x92, 0x54}; - std::vector clientid_vec(CLIENTID, CLIENTID + sizeof(CLIENTID)); - ClientId clientid(clientid_vec); - - // ...and a time + // Get current time for the use in Lease4. const time_t current_time = time(NULL); // Other random constants. @@ -130,8 +122,7 @@ TEST_F(Lease4Test, copyConstructor) { const uint32_t VALID_LIFETIME = 500; // Create the lease - Lease4 lease(0xffffffff, hwaddr_, - CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, 0, 0, current_time, + Lease4 lease(0xffffffff, hwaddr_, clientid_, VALID_LIFETIME, 0, 0, current_time, SUBNET_ID); // Use copy constructor to copy the lease. @@ -160,12 +151,7 @@ TEST_F(Lease4Test, copyConstructor) { // correctly. TEST_F(Lease4Test, operatorAssign) { - // Random values for the tests - const uint8_t CLIENTID[] = {0x17, 0x34, 0xe2, 0xff, 0x09, 0x92, 0x54}; - std::vector clientid_vec(CLIENTID, CLIENTID + sizeof(CLIENTID)); - ClientId clientid(clientid_vec); - - // ...and a time + // Get the current time for the use in Lease4. const time_t current_time = time(NULL); // Other random constants. @@ -173,8 +159,7 @@ TEST_F(Lease4Test, operatorAssign) { const uint32_t VALID_LIFETIME = 500; // Create the lease - Lease4 lease(0xffffffff, hwaddr_, - CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, 0, 0, current_time, + Lease4 lease(0xffffffff, hwaddr_, clientid_, VALID_LIFETIME, 0, 0, current_time, SUBNET_ID); // Use assignment operator to assign the lease. @@ -205,9 +190,8 @@ TEST_F(Lease4Test, matches) { // Create two leases which share the same address, HW address, client id // and ext_ value. const time_t current_time = time(NULL); - Lease4 lease1(IOAddress("192.0.2.3"), hwaddr_, CLIENTID, - sizeof(CLIENTID), VALID_LIFETIME, current_time, 0, 0, - SUBNET_ID); + Lease4 lease1(IOAddress("192.0.2.3"), hwaddr_, clientid_, VALID_LIFETIME, + current_time, 0, 0, SUBNET_ID); lease1.hostname_ = "lease1.example.com."; lease1.fqdn_fwd_ = true; lease1.fqdn_rev_ = true; @@ -251,6 +235,96 @@ TEST_F(Lease4Test, matches) { lease1.ext_ = lease2.ext_; } +// This test verifies that it is correctly determined when the lease +// belongs to the particular client identified by the client identifier +// and hw address. +TEST_F(Lease4Test, leaseBelongsToClient) { + // Create the lease with MAC address and Client Identifier. + Lease4 lease(IOAddress("192.0.2.1"), + HWAddrPtr(new HWAddr(HWAddr::fromText("00:01:02:03:04:05", HTYPE_ETHER))), + ClientId::fromText("01:02:03:04"), + 60, time(NULL), 0, 0, 1); + + // Create HW address and Client Id objects which match those held by the + // lease. This is a full match, so the lease belongs to the client. + HWAddrPtr hwaddr(new HWAddr(HWAddr::fromText("00:01:02:03:04:05", HTYPE_ETHER))); + ClientIdPtr clientid = ClientId::fromText("01:02:03:04"); + EXPECT_TRUE(lease.belongsToClient(hwaddr, clientid)); + + // Modify the HW address. The lease should still belong to the client + // because the client identifier matches. + hwaddr.reset(new HWAddr(HWAddr::fromText("00:01:02:03:04:06", HTYPE_ETHER))); + EXPECT_TRUE(lease.belongsToClient(hwaddr, clientid)); + + // Use the correct HW address, but modify the client identifier. The client + // identifier must match if present. If it doesn't match, the lease doesn't + // belong to the client. + hwaddr.reset(new HWAddr(HWAddr::fromText("00:01:02:03:04:05", HTYPE_ETHER))); + clientid = ClientId::fromText("01:02:03:05"); + EXPECT_FALSE(lease.belongsToClient(hwaddr, clientid)); + + // Set client id to null and leave only HW address. It coveres the case when + // the client id is ignored by the server (e.g. multi-stage boot case), but + // the client has a lease already. + clientid.reset(); + EXPECT_TRUE(lease.belongsToClient(hwaddr, clientid)); + + // Now use the correct client id and the null HW address. The client id + // should be sufficient to match the lease. + clientid = ClientId::fromText("01:02:03:04"); + hwaddr.reset(); + EXPECT_TRUE(lease.belongsToClient(hwaddr, clientid)); + + // Use both HW address and client id set to null. There must be no match. + hwaddr.reset(); + clientid.reset(); + EXPECT_FALSE(lease.belongsToClient(hwaddr, clientid)); + + // This time both HW address and client identifier are different than + // those held by the lease. There should be no match. + hwaddr.reset(new HWAddr(HWAddr::fromText("00:01:02:03:04:06", HTYPE_ETHER))); + clientid = ClientId::fromText("01:02:03:05"); + EXPECT_FALSE(lease.belongsToClient(hwaddr, clientid)); + + // Use the correct HW address and client id to match the lease, but set the + // client id for the lease to null. This covers the case when the lease + // has been acquired without client identifier but then the client is + // renewing using some client identifier (or server is configured to + // not ignore the client identifier). The client should obtain the lease. + hwaddr.reset(new HWAddr(HWAddr::fromText("00:01:02:03:04:05", HTYPE_ETHER))); + lease.client_id_.reset(); + EXPECT_TRUE(lease.belongsToClient(hwaddr, clientid)); + + // Change HW address. This time, the HW address doesn't match and client id + // can't be compared so it is considered as no match. + hwaddr.reset(new HWAddr(HWAddr::fromText("00:01:02:03:04:06", HTYPE_ETHER))); + EXPECT_FALSE(lease.belongsToClient(hwaddr, clientid)); + + // The lease now holds the client id by the HW address is null for the lease. + // Also, we're using correct client id to match the lease and some HW address. + // There should be a match because client id is ok. + lease.client_id_ = ClientId::fromText("01:02:03:04"); + lease.hwaddr_.reset(); + clientid = ClientId::fromText("01:02:03:04"); + hwaddr.reset(new HWAddr(HWAddr::fromText("00:01:02:03:04:05", HTYPE_ETHER))); + EXPECT_TRUE(lease.belongsToClient(hwaddr, clientid)); + + // If client id is wrong, there should be no match. + clientid = ClientId::fromText("01:02:03:05"); + EXPECT_FALSE(lease.belongsToClient(hwaddr, clientid)); + + // Setting HW address to null shouldn't matter and we should still have a + // match if the client id is correct. + clientid = ClientId::fromText("01:02:03:04"); + hwaddr.reset(); + EXPECT_TRUE(lease.belongsToClient(hwaddr, clientid)); + + // But with the null HW address and non-matching client id there should be + // no match whatsoever. + clientid = ClientId::fromText("01:02:03:06"); + EXPECT_FALSE(lease.belongsToClient(hwaddr, clientid)); +} + /// @brief Lease4 Equality Test /// /// Checks that the operator==() correctly compares two leases for equality. @@ -261,27 +335,22 @@ TEST_F(Lease4Test, operatorEquals) { // Random values for the tests const uint32_t ADDRESS = 0x01020304; const uint8_t HWADDR[] = {0x08, 0x00, 0x2b, 0x02, 0x3f, 0x4e}; - std::vector hwaddr(HWADDR, HWADDR + sizeof(HWADDR)); - const uint8_t CLIENTID[] = {0x17, 0x34, 0xe2, 0xff, 0x09, 0x92, 0x54}; - std::vector clientid_vec(CLIENTID, CLIENTID + sizeof(CLIENTID)); - ClientId clientid(clientid_vec); const time_t current_time = time(NULL); const uint32_t SUBNET_ID = 42; const uint32_t VALID_LIFETIME = 500; // Check when the leases are equal. - Lease4 lease1(ADDRESS, hwaddr_, - CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, current_time, 0, + Lease4 lease1(ADDRESS, hwaddr_, clientid_, VALID_LIFETIME, current_time, 0, 0, SUBNET_ID); // We need to make an explicit copy. Otherwise the second lease will just - // store a pointer and we'll have two leases pointing to a single HWAddr. - // That would make modifications to only one impossible. + // store a pointer and we'll have two leases pointing to a single HWAddr + // or client. That would make modifications to only one impossible. HWAddrPtr hwcopy(new HWAddr(*hwaddr_)); + ClientIdPtr clientid_copy(new ClientId(*clientid_)); - Lease4 lease2(ADDRESS, hwcopy, - CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, current_time, 0, 0, - SUBNET_ID); + Lease4 lease2(ADDRESS, hwcopy, clientid_copy, VALID_LIFETIME, current_time, + 0, 0, SUBNET_ID); EXPECT_TRUE(lease1 == lease2); EXPECT_FALSE(lease1 != lease2); @@ -308,6 +377,7 @@ TEST_F(Lease4Test, operatorEquals) { EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the EXPECT_FALSE(lease1 != lease2); // ... leases equal + std::vector clientid_vec = clientid_->getClientId(); ++clientid_vec[0]; lease1.client_id_.reset(new ClientId(clientid_vec)); EXPECT_FALSE(lease1 == lease2); @@ -390,7 +460,7 @@ TEST_F(Lease4Test, operatorEquals) { // Verify that the client id can be returned as a vector object and if client // id is NULL the empty vector is returned. -TEST(Lease4, getClientIdVector) { +TEST_F(Lease4Test, getClientIdVector) { // Create a lease. Lease4 lease; // By default, the lease should have client id set to NULL. If it doesn't, @@ -398,18 +468,17 @@ TEST(Lease4, getClientIdVector) { ASSERT_FALSE(lease.client_id_); // When client id is NULL the vector returned should be empty. EXPECT_TRUE(lease.getClientIdVector().empty()); - // Now, let's set the non NULL client id. Fill it with the 8 bytes, each - // holding a value of 0x42. - std::vector client_id_vec(8, 0x42); - lease.client_id_ = ClientIdPtr(new ClientId(client_id_vec)); + + // Initialize client identifier to non-null value. + lease.client_id_ = clientid_; // Check that the returned vector, encapsulating client id is equal to // the one that has been used to set the client id for the lease. std::vector returned_vec = lease.getClientIdVector(); - EXPECT_TRUE(returned_vec == client_id_vec); + EXPECT_TRUE(returned_vec == clientid_->getClientId()); } // Verify the behavior of the function which checks FQDN data for equality. -TEST(Lease4, hasIdenticalFqdn) { +TEST_F(Lease4Test, hasIdenticalFqdn) { Lease4 lease = createLease4("myhost.example.com.", true, true); EXPECT_TRUE(lease.hasIdenticalFqdn(createLease4("myhost.example.com.", true, true))); @@ -429,8 +498,8 @@ TEST(Lease4, hasIdenticalFqdn) { TEST_F(Lease4Test, toText) { const time_t current_time = 12345678; - Lease4 lease(IOAddress("192.0.2.3"), hwaddr_, CLIENTID, sizeof(CLIENTID), - 3600, 123, 456, current_time, 789); + Lease4 lease(IOAddress("192.0.2.3"), hwaddr_, clientid_, 3600, 123, + 456, current_time, 789); std::stringstream expected; expected << "Address: 192.0.2.3\n" @@ -439,12 +508,14 @@ TEST_F(Lease4Test, toText) { << "T2: 456\n" << "Cltt: 12345678\n" << "Hardware addr: " << hwaddr_->toText(false) << "\n" + << "Client id: " << clientid_->toText() << "\n" << "Subnet ID: 789\n"; EXPECT_EQ(expected.str(), lease.toText()); - // Now let's try with a lease without hardware address. + // Now let's try with a lease without hardware address and client identifier. lease.hwaddr_.reset(); + lease.client_id_.reset(); expected.str(""); expected << "Address: 192.0.2.3\n" << "Valid life: 3600\n" @@ -452,6 +523,7 @@ TEST_F(Lease4Test, toText) { << "T2: 456\n" << "Cltt: 12345678\n" << "Hardware addr: (none)\n" + << "Client id: (none)\n" << "Subnet ID: 789\n"; EXPECT_EQ(expected.str(), lease.toText()); } diff --git a/src/lib/util/Makefile.am b/src/lib/util/Makefile.am index 9e722f0478..e71781f5a0 100644 --- a/src/lib/util/Makefile.am +++ b/src/lib/util/Makefile.am @@ -19,6 +19,7 @@ libkea_util_la_SOURCES += memory_segment.h libkea_util_la_SOURCES += memory_segment_local.h memory_segment_local.cc libkea_util_la_SOURCES += optional_value.h libkea_util_la_SOURCES += pid_file.h pid_file.cc +libkea_util_la_SOURCES += pointer_util.h libkea_util_la_SOURCES += process_spawn.h process_spawn.cc libkea_util_la_SOURCES += range_utilities.h libkea_util_la_SOURCES += signal_set.cc signal_set.h diff --git a/src/lib/util/pointer_util.h b/src/lib/util/pointer_util.h new file mode 100644 index 0000000000..06166cc243 --- /dev/null +++ b/src/lib/util/pointer_util.h @@ -0,0 +1,39 @@ +// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#ifndef POINTER_UTIL_H +#define POINTER_UTIL_H + +namespace isc { +namespace util { + +/// @brief This function checks if two pointers are non-null and values +/// are equal. +/// +/// This function performs the typical comparison of values encapsulated by +/// the smart pointers, with checking if the pointers are non-null. +/// +/// @tparam T Pointer type, e.g. boost::shared_ptr, or boost::scoped_ptr. +/// +/// @return true only if both pointers are non-null and the values which they +/// point to are equal. +template +bool equalValues(const T& ptr1, const T& ptr2) { + return (ptr1 && ptr2 && (*ptr1 == *ptr2)); +} + +} // end of namespace isc::util +} // end of namespace isc + +#endif -- 2.47.3