Skip to content

Commit ada7e88

Browse files
committed
[#4443] Extended lenient parsing of client fqdn options
1 parent 310c66a commit ada7e88

7 files changed

Lines changed: 73 additions & 9 deletions

File tree

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
[func] fdupont
2+
Extended lenient parsing of v4 "fqdn" and v6 "client-fqdn"
3+
options to skip options with bad flags.
4+
(Gitlab #4443)

doc/sphinx/arm/dhcp4-srv.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8871,7 +8871,8 @@ or in terms of the log message above, the tuple length ``y`` becomes ``x``.
88718871
}
88728872
88738873
Starting with Kea version 2.5.8, this parsing is extended to silently ignore
8874-
FQDN (81) options with some invalid domain names.
8874+
FQDN (81) options with some invalid domain names, and starting with Kea
8875+
version 3.1.9 with invalid flags (i.e. 'S" and 'N' flags set to 1).
88758876

88768877
Ignore DHCP Server Identifier
88778878
-----------------------------

doc/sphinx/arm/dhcp6-srv.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8739,7 +8739,8 @@ MiNID.
87398739
}
87408740
87418741
Starting with Kea version 2.5.8, this parsing is extended to silently ignore
8742-
client-fqdn (39) options with some invalid domain names.
8742+
client-fqdn (39) options with some invalid domain names, and starting with Kea
8743+
version 3.1.9 with invalid flags (i.e. 'S" and 'N' flags set to 1).
87438744

87448745
.. _dhcp6_allocation_strategies:
87458746

src/lib/dhcp/option4_client_fqdn.cc

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,14 @@
1616
namespace isc {
1717
namespace dhcp {
1818

19-
/// @brief Implements the logic for the Option6ClientFqdn class.
19+
/// @brief Implements the logic for the Option4ClientFqdn class.
2020
///
2121
/// The purpose of the class is to separate the implementation details
2222
/// of the Option4ClientFqdn class from the interface. This implementation
2323
/// uses libdns classes to process FQDNs. At some point it may be
2424
/// desired to split libdhcp++ from libdns. In such case the
2525
/// implementation of this class may be changed. The declaration of the
26-
/// Option6ClientFqdn class holds the pointer to implementation, so
26+
/// Option4ClientFqdn class holds the pointer to implementation, so
2727
/// the transition to a different implementation would not affect the
2828
/// header file.
2929
class Option4ClientFqdnImpl {
@@ -88,7 +88,7 @@ class Option4ClientFqdnImpl {
8888
/// check if the MBZ bits are set (if true). This parameter should be set
8989
/// to false when validating flags in the received message. This is because
9090
/// server should ignore MBZ bits in received messages.
91-
/// @throw InvalidOption6FqdnFlags if flags are invalid.
91+
/// @throw InvalidOption4FqdnFlags if flags are invalid.
9292
static void checkFlags(const uint8_t flags, const bool check_mbz);
9393

9494
/// @brief Parse the Option provided in the wire format.
@@ -149,7 +149,15 @@ Option4ClientFqdnImpl::Option4ClientFqdnImpl(OptionBufferConstIter first,
149149
// Verify that flags value was correct. This constructor is used to parse
150150
// incoming packet, so don't check MBZ bits. They are ignored because we
151151
// don't want to discard the whole option because MBZ bits are set.
152-
checkFlags(flags_, false);
152+
try {
153+
checkFlags(flags_, false);
154+
} catch (const InvalidOption4FqdnFlags& ex) {
155+
if (Option::lenient_parsing_) {
156+
isc_throw(SkipThisOptionError, ex.what());
157+
} else {
158+
throw;
159+
}
160+
}
153161
}
154162

155163
Option4ClientFqdnImpl::
@@ -509,7 +517,15 @@ Option4ClientFqdn::unpack(OptionBufferConstIter first,
509517
// Check that the flags in the received option are valid. Ignore MBZ bits,
510518
// because we don't want to discard the whole option because of MBZ bits
511519
// being set.
512-
impl_->checkFlags(impl_->flags_, false);
520+
try {
521+
impl_->checkFlags(impl_->flags_, false);
522+
} catch (const InvalidOption4FqdnFlags& ex) {
523+
if (Option::lenient_parsing_) {
524+
isc_throw(SkipThisOptionError, ex.what());
525+
} else {
526+
throw;
527+
}
528+
}
513529
}
514530

515531
std::string

src/lib/dhcp/option6_client_fqdn.cc

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,15 @@ Option6ClientFqdnImpl::Option6ClientFqdnImpl(OptionBufferConstIter first,
122122
parseWireData(first, last);
123123
// Verify that flags value was correct. Do not check if MBZ bits are
124124
// set because we should ignore those bits in received message.
125-
checkFlags(flags_, false);
125+
try {
126+
checkFlags(flags_, false);
127+
} catch (const InvalidOption6FqdnFlags& ex) {
128+
if (Option::lenient_parsing_) {
129+
isc_throw(SkipThisOptionError, ex.what());
130+
} else {
131+
throw;
132+
}
133+
}
126134
}
127135

128136
Option6ClientFqdnImpl::
@@ -429,7 +437,15 @@ Option6ClientFqdn::unpack(OptionBufferConstIter first,
429437
// Check that the flags in the received option are valid. Ignore MBZ bits
430438
// because we don't want to discard the whole option because of MBZ bits
431439
// being set.
432-
impl_->checkFlags(impl_->flags_, false);
440+
try {
441+
impl_->checkFlags(impl_->flags_, false);
442+
} catch (const InvalidOption6FqdnFlags& ex) {
443+
if (Option::lenient_parsing_) {
444+
isc_throw(SkipThisOptionError, ex.what());
445+
} else {
446+
throw;
447+
}
448+
}
433449
}
434450

435451
std::string

src/lib/dhcp/tests/option4_client_fqdn_unittest.cc

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,8 +459,12 @@ TEST(Option4ClientFqdnTest, constructFromWireInvalidFlags) {
459459
// Replace the flags with invalid value and verify that constructor throws
460460
// appropriate exception.
461461
in_buf[0] = Option4ClientFqdn::FLAG_N | Option4ClientFqdn::FLAG_S;
462+
LenientOptionParsing lop(false);
462463
EXPECT_THROW(Option4ClientFqdn(in_buf.begin(), in_buf.end()),
463464
InvalidOption4FqdnFlags);
465+
Option::lenient_parsing_ = true;
466+
EXPECT_THROW(Option4ClientFqdn(in_buf.begin(), in_buf.end()),
467+
SkipThisOptionError);
464468
}
465469

466470
// This test verifies that if invalid domain name is used the constructor
@@ -834,6 +838,15 @@ TEST(Option4ClientFqdnTest, unpack) {
834838
EXPECT_TRUE(option->getFlag(Option4ClientFqdn::FLAG_E));
835839
EXPECT_EQ("myhost.example.com.", option->getDomainName());
836840
EXPECT_EQ(Option4ClientFqdn::FULL, option->getDomainNameType());
841+
842+
// Check that flags are checked.
843+
in_buf[0] |= Option4ClientFqdn::FLAG_N;
844+
LenientOptionParsing lop(false);
845+
EXPECT_THROW(option->unpack(in_buf.begin(), in_buf.end()),
846+
InvalidOption4FqdnFlags);
847+
Option::lenient_parsing_ = true;
848+
EXPECT_THROW(option->unpack(in_buf.begin(), in_buf.end()),
849+
SkipThisOptionError);
837850
}
838851

839852
// This test verifies that on-wire option data holding partial domain name

src/lib/dhcp/tests/option6_client_fqdn_unittest.cc

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,8 +410,12 @@ TEST(Option6ClientFqdnTest, constructFromWireInvalidFlags) {
410410
// Replace the flags with invalid value and verify that constructor throws
411411
// appropriate exception.
412412
in_buf[0] = Option6ClientFqdn::FLAG_N | Option6ClientFqdn::FLAG_S;
413+
LenientOptionParsing lop(false);
413414
EXPECT_THROW(Option6ClientFqdn(in_buf.begin(), in_buf.end()),
414415
InvalidOption6FqdnFlags);
416+
Option::lenient_parsing_ = true;
417+
EXPECT_THROW(Option6ClientFqdn(in_buf.begin(), in_buf.end()),
418+
SkipThisOptionError);
415419
}
416420

417421
// This test verifies that if invalid domain name is used the constructor
@@ -702,6 +706,15 @@ TEST(Option6ClientFqdnTest, unpack) {
702706
EXPECT_FALSE(option->getFlag(Option6ClientFqdn::FLAG_O));
703707
EXPECT_EQ("myhost.example.com.", option->getDomainName());
704708
EXPECT_EQ(Option6ClientFqdn::FULL, option->getDomainNameType());
709+
710+
// Check that flags are checked.
711+
in_buf[0] |= Option6ClientFqdn::FLAG_N;
712+
LenientOptionParsing lop(false);
713+
EXPECT_THROW(option->unpack(in_buf.begin(), in_buf.end()),
714+
InvalidOption6FqdnFlags);
715+
Option::lenient_parsing_ = true;
716+
EXPECT_THROW(option->unpack(in_buf.begin(), in_buf.end()),
717+
SkipThisOptionError);
705718
}
706719

707720
// This test verifies that on-wire option data holding partial domain name

0 commit comments

Comments
 (0)