diff --git a/src/bin/dhcp4/dhcp4_srv.cc b/src/bin/dhcp4/dhcp4_srv.cc index 7aa26f2faf953109fed8649d28d767db479e5d33..d619f7c95c4a99cc3234dbdf75a26c68899bfdd6 100644 --- a/src/bin/dhcp4/dhcp4_srv.cc +++ b/src/bin/dhcp4/dhcp4_srv.cc @@ -62,7 +62,7 @@ #endif #include -#include +#include #include #include #include @@ -1721,9 +1721,12 @@ Dhcpv4Srv::processHostnameOption(Dhcpv4Exchange& ex) { // send back a hostname option, send this option with a reserved // name for this client. if (should_send_hostname) { - const std::string& hostname = - d2_mgr.qualifyName(ctx->currentHost()->getHostname(), - false); + std::string hostname = + d2_mgr.qualifyName(ctx->currentHost()->getHostname(), false); + + // Convert hostname to lower case. + boost::algorithm::to_lower(hostname); + LOG_DEBUG(ddns4_logger, DBG_DHCP4_DETAIL_DATA, DHCP4_RESERVED_HOSTNAME_ASSIGNED) .arg(ex.getQuery()->getLabel()) @@ -1813,6 +1816,9 @@ Dhcpv4Srv::processHostnameOption(Dhcpv4Exchange& ex) { hostname = sanitizer->scrub(hostname); } + // Convert hostname to lower case. + boost::algorithm::to_lower(hostname); + if (label_count == 2) { // If there are two labels, it means that the client has specified // the unqualified name. We have to concatenate the unqualified name diff --git a/src/bin/dhcp4/tests/fqdn_unittest.cc b/src/bin/dhcp4/tests/fqdn_unittest.cc index 8246987db228b46f401c1372866ccd3448d4392f..575020ea2118a5c9b27268cac38258bc3bd19125 100644 --- a/src/bin/dhcp4/tests/fqdn_unittest.cc +++ b/src/bin/dhcp4/tests/fqdn_unittest.cc @@ -883,8 +883,11 @@ TEST_F(NameDhcpv4SrvTest, createNameChangeRequestsNewLease) { TEST_F(NameDhcpv4SrvTest, createNameChangeRequestsRenewNoChange) { Lease4Ptr lease = createLease(IOAddress("192.0.2.3"), "myhost.example.com.", true, true); + // Comparison should be case insensitive, so turning some of the + // characters of the old lease hostname to upper case should not + // trigger NCRs. Lease4Ptr old_lease = createLease(IOAddress("192.0.2.3"), - "myhost.example.com.", true, true); + "Myhost.Example.Com.", true, true); old_lease->valid_lft_ += 100; ASSERT_NO_THROW(srv_->createNameChangeRequests(lease, old_lease)); @@ -1067,7 +1070,8 @@ TEST_F(NameDhcpv4SrvTest, processTwoRequestsHostname) { IfaceMgrTestConfig test_config(true); IfaceMgr::instance().openSockets4(); - Pkt4Ptr req1 = generatePktWithHostname(DHCPREQUEST, "myhost.example.com."); + // Case in a hostname should be ignored. + Pkt4Ptr req1 = generatePktWithHostname(DHCPREQUEST, "Myhost.Example.Com."); // Set interface for the incoming packet. The server requires it to // generate client id. @@ -1142,11 +1146,11 @@ TEST_F(NameDhcpv4SrvTest, processRequestRenewFqdn) { "965B68B6D438D98E680BF10B09F3BCF", time(NULL), subnet_->getValid(), true); - // Create another Request message with the same FQDN. Server - // should generate no NameChangeRequests. + // Create another Request message with the same FQDN. Case changes in the + // hostname should be ignored. Server should generate no NameChangeRequests. Pkt4Ptr req2 = generatePktWithFqdn(DHCPREQUEST, Option4ClientFqdn::FLAG_S | Option4ClientFqdn::FLAG_E, - "myhost.example.com.", + "Myhost.Example.Com.", Option4ClientFqdn::FULL, true); ASSERT_NO_THROW(reply = srv_->processRequest(req2)); @@ -1183,9 +1187,9 @@ TEST_F(NameDhcpv4SrvTest, processRequestRenewHostname) { "965B68B6D438D98E680BF10B09F3BCF", time(NULL), subnet_->getValid(), true); - // Create another Request message with the same Hostname. Server - // should generate no NameChangeRequests. - Pkt4Ptr req2 = generatePktWithHostname(DHCPREQUEST, "myhost.example.com."); + // Create another Request message with the same Hostname. Case changes in the + // hostname should be ignored. Server should generate no NameChangeRequests. + Pkt4Ptr req2 = generatePktWithHostname(DHCPREQUEST, "Myhost.Example.Com."); // Set interface for the incoming packet. The server requires it to // generate client id. diff --git a/src/lib/dhcp/option4_client_fqdn.cc b/src/lib/dhcp/option4_client_fqdn.cc index f797982d2a7ac0f7f1e9d9419a0b5f3b8ce9621e..00608e0aa6150a15336fcf20d8ca9ac94fd07951 100644 --- a/src/lib/dhcp/option4_client_fqdn.cc +++ b/src/lib/dhcp/option4_client_fqdn.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 @@ -210,7 +210,9 @@ setDomainName(const std::string& domain_name, } else { try { - domain_name_.reset(new isc::dns::Name(name)); + // The second argument indicates that the name should be converted + // to lower case. + domain_name_.reset(new isc::dns::Name(name, true)); } catch (const Exception&) { isc_throw(InvalidOption4FqdnDomainName, @@ -291,7 +293,9 @@ Option4ClientFqdnImpl::parseCanonicalDomainName(OptionBufferConstIter first, buf.push_back(0); // Reset domain name. isc::util::InputBuffer name_buf(&buf[0], buf.size()); - domain_name_.reset(new isc::dns::Name(name_buf)); + // The second argument indicates that the name should be converted + // to lower case. + domain_name_.reset(new isc::dns::Name(name_buf, true)); // Terminating zero was missing, so set the domain-name type // to partial. domain_name_type_ = Option4ClientFqdn::PARTIAL; @@ -301,7 +305,9 @@ Option4ClientFqdnImpl::parseCanonicalDomainName(OptionBufferConstIter first, // Name object constructor. isc::util::InputBuffer name_buf(&(*first), std::distance(first, last)); - domain_name_.reset(new isc::dns::Name(name_buf)); + // The second argument indicates that the name should be converted + // to lower case. + domain_name_.reset(new isc::dns::Name(name_buf, true)); // Set the domain-type to fully qualified domain name. domain_name_type_ = Option4ClientFqdn::FULL; } @@ -313,7 +319,9 @@ Option4ClientFqdnImpl::parseASCIIDomainName(OptionBufferConstIter first, OptionBufferConstIter last) { if (std::distance(first, last) > 0) { std::string domain_name(first, last); - domain_name_.reset(new isc::dns::Name(domain_name)); + // The second argument indicates that the name should be converted + // to lower case. + domain_name_.reset(new isc::dns::Name(domain_name, true)); domain_name_type_ = domain_name[domain_name.length() - 1] == '.' ? Option4ClientFqdn::FULL : Option4ClientFqdn::PARTIAL; } diff --git a/src/lib/dhcp/option4_client_fqdn.h b/src/lib/dhcp/option4_client_fqdn.h index 43767a64bc00fdc828a8018ef6e3fd38ba12ca48..a67e07e44f0e50786e16d232738f4bb6fbbdd76e 100644 --- a/src/lib/dhcp/option4_client_fqdn.h +++ b/src/lib/dhcp/option4_client_fqdn.h @@ -1,4 +1,4 @@ -// Copyright (C) 2013-2016 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 @@ -78,6 +78,10 @@ class Option4ClientFqdnImpl; /// to indicate that server should generate complete fully qualified /// domain-name. /// +/// Since domain names are case insensitive (see RFC 4343), this class +/// converts them to lower case format regardless if they are received over +/// the wire or created from strings. +/// /// @warning: The RFC4702 section 2.3.1 states that the clients and servers /// should use character sets specified in RFC952, section 2.1 for ASCII-encoded /// domain-names. This class doesn't detect the character set violation for diff --git a/src/lib/dhcp/option6_client_fqdn.h b/src/lib/dhcp/option6_client_fqdn.h index 58adfbd52e8db41aa25b4457ddc472bd0a041895..2ddcfb731ca6a85cde597615b1601ac2f668332f 100644 --- a/src/lib/dhcp/option6_client_fqdn.h +++ b/src/lib/dhcp/option6_client_fqdn.h @@ -1,4 +1,4 @@ -// Copyright (C) 2013-2016 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 @@ -70,6 +70,10 @@ class Option6ClientFqdnImpl; /// domain-names to indicate that server should generate complete fully /// qualified domain-name. /// +/// Since domain names are case insensitive (see RFC 4343), this class +/// converts them to lower case format regardless if they are received over +/// the wire or created from strings. +/// /// Design choice: This class uses pimpl idiom to separate the interface /// from implementation specifics. Implementations may use different approaches /// to handle domain names (mostly validation of the domain-names). The existing diff --git a/src/lib/dhcp/tests/option4_client_fqdn_unittest.cc b/src/lib/dhcp/tests/option4_client_fqdn_unittest.cc index 389dce8d9c04a28591174c8e550e1bbc802bab28..27987fc4791b609090f7045a1d456877d33e0d85 100644 --- a/src/lib/dhcp/tests/option4_client_fqdn_unittest.cc +++ b/src/lib/dhcp/tests/option4_client_fqdn_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2013-2015 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 @@ -139,9 +139,9 @@ TEST(Option4ClientFqdnTest, constructFromWire) { Option4ClientFqdn::FLAG_S | Option4ClientFqdn::FLAG_E, // flags 0, // RCODE1 0, // RCODE2 - 6, 109, 121, 104, 111, 115, 116, // myhost. - 7, 101, 120, 97, 109, 112, 108, 101, // example. - 3, 99, 111, 109, 0 // com. + 6, 77, 121, 104, 111, 115, 116, // Myhost. + 7, 69, 120, 97, 109, 112, 108, 101, // Example. + 3, 67, 111, 109, 0 // Com. }; size_t in_data_size = sizeof(in_data) / sizeof(in_data[0]); OptionBuffer in_buf(in_data, in_data + in_data_size); @@ -171,9 +171,9 @@ TEST(Option4ClientFqdnTest, constructFromWireASCII) { Option4ClientFqdn::FLAG_S, // flags 0, // RCODE1 0, // RCODE2 - 109, 121, 104, 111, 115, 116, 46, // myhost. - 101, 120, 97, 109, 112, 108, 101, 46, // example. - 99, 111, 109, 46 // com. + 77, 121, 104, 111, 115, 116, 46, // Myhost. + 69, 120, 97, 109, 112, 108, 101, 46, // Example. + 67, 111, 109, 46 // Com. }; size_t in_data_size = sizeof(in_data) / sizeof(in_data[0]); OptionBuffer in_buf(in_data, in_data + in_data_size); @@ -251,7 +251,7 @@ TEST(Option4ClientFqdnTest, constructFromWirePartial) { Option4ClientFqdn::FLAG_N | Option4ClientFqdn:: FLAG_E, // flags 255, // RCODE1 255, // RCODE2 - 6, 109, 121, 104, 111, 115, 116 // myhost + 6, 77, 121, 104, 111, 115, 116 // Myhost }; size_t in_data_size = sizeof(in_data) / sizeof(in_data[0]); OptionBuffer in_buf(in_data, in_data + in_data_size); @@ -353,7 +353,7 @@ TEST(Option4ClientFqdnTest, assignment) { Option4ClientFqdn option(Option4ClientFqdn::FLAG_S | Option4ClientFqdn::FLAG_E, Option4ClientFqdn::RCODE_SERVER(), - "myhost.example.com", + "myhost.Example.com", Option4ClientFqdn::FULL); // Verify that the values have been set correctly. @@ -368,10 +368,10 @@ TEST(Option4ClientFqdnTest, assignment) { Option4ClientFqdn option2(Option4ClientFqdn::FLAG_N | Option4ClientFqdn::FLAG_E, Option4ClientFqdn::RCODE_SERVER(), - "myhost", + "Myhost", Option4ClientFqdn::PARTIAL); - // Verify tha the values have been set correctly. + // Verify that the values have been set correctly. ASSERT_FALSE(option2.getFlag(Option4ClientFqdn::FLAG_S)); ASSERT_TRUE(option2.getFlag(Option4ClientFqdn::FLAG_E)); ASSERT_FALSE(option2.getFlag(Option4ClientFqdn::FLAG_O)); @@ -585,7 +585,7 @@ TEST(Option4ClientFqdnTest, setDomainName) { option.reset(new Option4ClientFqdn(Option4ClientFqdn::FLAG_S | Option4ClientFqdn::FLAG_E, Option4ClientFqdn::RCODE_SERVER(), - "myhost.example.com", + "myhost.Example.com", Option4ClientFqdn::FULL)) ); ASSERT_TRUE(option); @@ -593,13 +593,13 @@ TEST(Option4ClientFqdnTest, setDomainName) { ASSERT_EQ(Option4ClientFqdn::FULL, option->getDomainNameType()); // Partial domain-name. - ASSERT_NO_THROW(option->setDomainName("myhost", + ASSERT_NO_THROW(option->setDomainName("myHost", Option4ClientFqdn::PARTIAL)); EXPECT_EQ("myhost", option->getDomainName()); EXPECT_EQ(Option4ClientFqdn::PARTIAL, option->getDomainNameType()); // Fully qualified domain-name. - ASSERT_NO_THROW(option->setDomainName("example.com", + ASSERT_NO_THROW(option->setDomainName("example.Com", Option4ClientFqdn::FULL)); EXPECT_EQ("example.com.", option->getDomainName()); EXPECT_EQ(Option4ClientFqdn::FULL, option->getDomainNameType()); @@ -623,7 +623,7 @@ TEST(Option4ClientFqdnTest, resetDomainName) { option.reset(new Option4ClientFqdn(Option4ClientFqdn::FLAG_S | Option4ClientFqdn::FLAG_E, Option4ClientFqdn::RCODE_CLIENT(), - "myhost.example.com", + "Myhost.Example.com", Option4ClientFqdn::FULL)) ); ASSERT_TRUE(option); @@ -643,7 +643,7 @@ TEST(Option4ClientFqdnTest, pack) { ASSERT_NO_THROW( option.reset(new Option4ClientFqdn(flags, Option4ClientFqdn::RCODE_CLIENT(), - "myhost.example.com")) + "Myhost.Example.Com")) ); ASSERT_TRUE(option); @@ -669,6 +669,8 @@ TEST(Option4ClientFqdnTest, pack) { EXPECT_EQ(0, memcmp(ref_data, buf.getData(), buf.getLength())); } +// This test verifies on-wire format of the Client FQDN option +// output in deprecated ASCII format. TEST(Option4ClientFqdnTest, packASCII) { // Create option instance. Check that constructor doesn't throw. const uint8_t flags = Option4ClientFqdn::FLAG_S; @@ -676,7 +678,7 @@ TEST(Option4ClientFqdnTest, packASCII) { ASSERT_NO_THROW( option.reset(new Option4ClientFqdn(flags, Option4ClientFqdn::RCODE_CLIENT(), - "myhost.example.com")) + "Myhost.Example.Com")) ); ASSERT_TRUE(option); diff --git a/src/lib/dhcp/tests/option6_client_fqdn_unittest.cc b/src/lib/dhcp/tests/option6_client_fqdn_unittest.cc index 2d1bad878b0f12cd0e9605a9cf43a166e48a7921..c2934899ccb379ed6498a0342673b45d6dee3709 100644 --- a/src/lib/dhcp/tests/option6_client_fqdn_unittest.cc +++ b/src/lib/dhcp/tests/option6_client_fqdn_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2013-2015 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 @@ -146,9 +146,9 @@ TEST(Option6ClientFqdnTest, copyConstructEmptyDomainName) { TEST(Option6ClientFqdnTest, constructFromWire) { const uint8_t in_data[] = { Option6ClientFqdn::FLAG_S, // flags - 6, 109, 121, 104, 111, 115, 116, // myhost. - 7, 101, 120, 97, 109, 112, 108, 101, // example. - 3, 99, 111, 109, 0 // com. + 6, 77, 121, 104, 111, 115, 116, // Myhost. + 7, 69, 120, 97, 109, 112, 108, 101, // Example. + 3, 67, 111, 109, 0 // Com. }; size_t in_data_size = sizeof(in_data) / sizeof(in_data[0]); OptionBuffer in_buf(in_data, in_data + in_data_size); @@ -210,7 +210,7 @@ TEST(Option6ClientFqdnTest, constructFromWireTruncated) { TEST(Option6ClientFqdnTest, constructFromWirePartial) { const uint8_t in_data[] = { Option6ClientFqdn::FLAG_N, // flags - 6, 109, 121, 104, 111, 115, 116 // myhost + 6, 77, 121, 104, 111, 115, 116 // Myhost }; size_t in_data_size = sizeof(in_data) / sizeof(in_data[0]); OptionBuffer in_buf(in_data, in_data + in_data_size); @@ -274,7 +274,7 @@ TEST(Option6ClientFqdnTest, assignment) { // Create first option. Option6ClientFqdn option(Option6ClientFqdn::FLAG_S, - "myhost.example.com", + "Myhost.Example.Com", Option6ClientFqdn::FULL); // Verify that the values have been set correctly. @@ -514,13 +514,13 @@ TEST(Option6ClientFqdnTest, setDomainName) { ASSERT_EQ(Option6ClientFqdn::FULL, option->getDomainNameType()); // Partial domain-name. - ASSERT_NO_THROW(option->setDomainName("myhost", + ASSERT_NO_THROW(option->setDomainName("Myhost", Option6ClientFqdn::PARTIAL)); EXPECT_EQ("myhost", option->getDomainName()); EXPECT_EQ(Option6ClientFqdn::PARTIAL, option->getDomainNameType()); // Fully qualified domain-name. - ASSERT_NO_THROW(option->setDomainName("example.com", + ASSERT_NO_THROW(option->setDomainName("Example.com", Option6ClientFqdn::FULL)); EXPECT_EQ("example.com.", option->getDomainName()); EXPECT_EQ(Option6ClientFqdn::FULL, option->getDomainNameType()); @@ -664,8 +664,8 @@ TEST(Option6ClientFqdnTest, unpack) { const uint8_t in_data[] = { Option6ClientFqdn::FLAG_S, // flags - 6, 109, 121, 104, 111, 115, 116, // myhost. - 7, 101, 120, 97, 109, 112, 108, 101, // example. + 6, 77, 121, 104, 111, 115, 116, // Myhost. + 7, 69, 120, 97, 109, 112, 108, 101, // Example. 3, 99, 111, 109, 0 // com. }; size_t in_data_size = sizeof(in_data) / sizeof(in_data[0]); @@ -703,7 +703,7 @@ TEST(Option6ClientFqdnTest, unpackPartial) { const uint8_t in_data[] = { Option6ClientFqdn::FLAG_S, // flags - 6, 109, 121, 104, 111, 115, 116 // myhost + 6, 77, 121, 104, 111, 115, 116 // Myhost }; size_t in_data_size = sizeof(in_data) / sizeof(in_data[0]); OptionBuffer in_buf(in_data, in_data + in_data_size); diff --git a/src/lib/dhcpsrv/lease.cc b/src/lib/dhcpsrv/lease.cc index 6b50a3858f8a94daa13675f81462616bbc271a5a..b6a05b23300d45631be9d090bcb59afcb69df213 100644 --- a/src/lib/dhcpsrv/lease.cc +++ b/src/lib/dhcpsrv/lease.cc @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -112,7 +113,7 @@ Lease::getExpirationTime() const { bool Lease::hasIdenticalFqdn(const Lease& other) const { - return (hostname_ == other.hostname_ && + return (boost::algorithm::iequals(hostname_, other.hostname_) && fqdn_fwd_ == other.fqdn_fwd_ && fqdn_rev_ == other.fqdn_rev_); } diff --git a/src/lib/dhcpsrv/lease.h b/src/lib/dhcpsrv/lease.h index c42cfc9a9e0fd06d7ef510b61276fac5b5287ec9..fbac2cf35c5e7cd17e7951a66c09208d7b9dbdc3 100644 --- a/src/lib/dhcpsrv/lease.h +++ b/src/lib/dhcpsrv/lease.h @@ -188,6 +188,8 @@ struct Lease : public isc::data::UserContext, public isc::data::CfgToElement { /// @brief Returns true if the other lease has equal FQDN data. /// + /// The comparison of the hostname is case insensitive. + /// /// @param other Lease which FQDN data is to be compared with our lease. /// /// @return Boolean value which indicates whether FQDN data of the other diff --git a/src/lib/dhcpsrv/tests/d2_client_unittest.cc b/src/lib/dhcpsrv/tests/d2_client_unittest.cc index 3f5860b5af87ec0c16b4e6d1ee59250e045c4d59..bedd42b0c489921ffe0151960889ce78d28b2a9b 100644 --- a/src/lib/dhcpsrv/tests/d2_client_unittest.cc +++ b/src/lib/dhcpsrv/tests/d2_client_unittest.cc @@ -1143,37 +1143,37 @@ TEST(D2ClientMgr, sanitizeFqdnV4) { "full FQDN, name unchanged", "One.123.example.com.", Option4ClientFqdn::FULL, - "One.123.example.com." + "one.123.example.com." }, { "partial FQDN, name unchanged, but qualified", "One.123", Option4ClientFqdn::PARTIAL, - "One.123.suffix.com." + "one.123.suffix.com." }, { "full FQDN, scrubbed", "O#n^e.123.ex&a*mple.com.", Option4ClientFqdn::FULL, - "Oxnxe.123.exxaxmple.com." + "oxnxe.123.exxaxmple.com." }, { "partial FQDN, scrubbed and qualified", "One.1+2|3", Option4ClientFqdn::PARTIAL, - "One.1x2x3.suffix.com." + "one.1x2x3.suffix.com." }, { "full FQDN with characters that get escaped", "O n e.123.exa(m)ple.com.", Option4ClientFqdn::FULL, - "Oxnxe.123.exaxmxple.com." + "oxnxe.123.exaxmxple.com." }, { "full FQDN with escape sequences", "O\032n\032e.123.example.com.", Option4ClientFqdn::FULL, - "Oxnxe.123.example.com." + "oxnxe.123.example.com." } }; diff --git a/src/lib/dhcpsrv/tests/lease_unittest.cc b/src/lib/dhcpsrv/tests/lease_unittest.cc index 8b3bac04f263a8f63bb8586e8df86051f5abfa55..39858e23e2fcc75a77ce999c0c55b4aaba35285c 100644 --- a/src/lib/dhcpsrv/tests/lease_unittest.cc +++ b/src/lib/dhcpsrv/tests/lease_unittest.cc @@ -453,6 +453,9 @@ TEST_F(Lease4Test, hasIdenticalFqdn) { Lease4 lease = createLease4("myhost.example.com.", true, true); EXPECT_TRUE(lease.hasIdenticalFqdn(createLease4("myhost.example.com.", true, true))); + // Case insensitive comparison. + EXPECT_TRUE(lease.hasIdenticalFqdn(createLease4("myHOst.ExamplE.coM.", + true, true))); EXPECT_FALSE(lease.hasIdenticalFqdn(createLease4("other.example.com.", true, true))); EXPECT_FALSE(lease.hasIdenticalFqdn(createLease4("myhost.example.com.", @@ -1032,6 +1035,9 @@ TEST(Lease6Test, hasIdenticalFqdn) { Lease6 lease = createLease6("myhost.example.com.", true, true); EXPECT_TRUE(lease.hasIdenticalFqdn(createLease6("myhost.example.com.", true, true))); + // Case insensitive comparison. + EXPECT_TRUE(lease.hasIdenticalFqdn(createLease6("myHOst.ExamplE.coM.", + true, true))); EXPECT_FALSE(lease.hasIdenticalFqdn(createLease6("other.example.com.", true, true))); EXPECT_FALSE(lease.hasIdenticalFqdn(createLease6("myhost.example.com.",