From: JINMEI Tatuya Date: Thu, 13 Dec 2012 07:42:42 +0000 (-0800) Subject: [2429] played with max allowable TTL (2^31-1). X-Git-Tag: bind10-1.0.0-beta-release~16^2~12 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e2ff6e8c7eb46427bd96557566728cd64593449c;p=thirdparty%2Fkea.git [2429] played with max allowable TTL (2^31-1). --- diff --git a/src/lib/dns/master_loader.cc b/src/lib/dns/master_loader.cc index c35744bc90..998e8747ad 100644 --- a/src/lib/dns/master_loader.cc +++ b/src/lib/dns/master_loader.cc @@ -52,6 +52,7 @@ public: const MasterLoaderCallbacks& callbacks, const AddRRCallback& add_callback, MasterLoader::Options options) : + MAX_TTL(0x7fffffff), lexer_(), zone_origin_(zone_origin), zone_class_(zone_class), @@ -142,13 +143,38 @@ private: pushSource(filename); } + // Upper limit check when recognizing a specific TTL value from the + // zone file ($TTL, the RR's TTL field, or the SOA minimum). RFC2181 + // Section 8 limits the range of TTL values to unsigned 32-bit integers, + // and prohibits transmitting a TTL field exceeding this range. We + // guarantee that by limiting the value at the time of zone + // parsing/loading, following what BIND 9 does. Resetting it to 0 + // at this point may not be exactly what the RFC states, but the end + // result would be the same. Again, we follow the BIND 9's behavior here. + // + // post_parsing is true iff this method is called after parsing the entire + // RR and the lexer is positioned at the next line. It's just for + // calculating the accurate source line when callback is necessary. + void limitTTL(RRTTL& ttl, bool post_parsing) { + if (ttl > MAX_TTL) { + const size_t src_line = lexer_.getSourceLine() - + (post_parsing ? 1 : 0); + callbacks_.warning(lexer_.getSourceName(), src_line, + "TTL " + ttl.toText() + " > MAXTTL, " + "setting to 0 per RFC2181"); + ttl = RRTTL(0); + } + } + // Set/reset the default TTL. Either from $TTL or SOA minimum TTL. - void setDefaultTTL(const RRTTL& ttl) { + // see LimitTTL() for parameter post_parsing. + void setDefaultTTL(const RRTTL& ttl, bool post_parsing) { if (!default_ttl_) { default_ttl_.reset(new RRTTL(ttl)); } else { *default_ttl_ = ttl; } + limitTTL(*default_ttl_, post_parsing); } // Set/reset the TTL currently being used. This can be used the last @@ -168,6 +194,7 @@ private: bool setCurrentTTL(const string& ttl_txt) { try { setCurrentTTL(RRTTL(ttl_txt)); + limitTTL(*current_ttl_, false); return (true); } catch (const InvalidRRTTL&) { return (false); @@ -197,7 +224,7 @@ private: const uint32_t ttl_val = dynamic_cast(*rdata). getMinimum(); - setDefaultTTL(RRTTL(ttl_val)); + setDefaultTTL(RRTTL(ttl_val), true); setCurrentTTL(*default_ttl_); } else { // On catching the exception we'll try to reach EOL again, @@ -227,7 +254,7 @@ private: isc_throw(isc::NotImplemented, "Origin directive not implemented yet"); } else if (iequals(directive, "TTL")) { - setDefaultTTL(RRTTL(getString())); + setDefaultTTL(RRTTL(getString()), false); eatUntilEOL(true); } else { isc_throw(InternalException, "Unknown directive '" << @@ -265,6 +292,11 @@ private: } private: + // RFC2181 Section 8 specifies TTLs are unsigned 32-bit integer, + // effectively limiting the maximum value to 2^32-1. This constant + // represent a TTL of the max value. + const RRTTL MAX_TTL; + MasterLexer lexer_; const Name zone_origin_; const RRClass zone_class_; diff --git a/src/lib/dns/tests/master_loader_unittest.cc b/src/lib/dns/tests/master_loader_unittest.cc index 9f6c40ca01..912a0b4534 100644 --- a/src/lib/dns/tests/master_loader_unittest.cc +++ b/src/lib/dns/tests/master_loader_unittest.cc @@ -419,7 +419,9 @@ TEST_F(MasterLoaderTest, ttlDirective) { // Extended TTL form is accepted. zone_stream << "$TTL 1H\nb.example.org. IN A 192.0.2.3\n"; // Matching is case insensitive. - zone_stream << "$tTl 360\nc.example.org. IN A 192.0.2.3\n"; + zone_stream << "$tTl 360\nc.example.org. IN A 192.0.2.4\n"; + // Maximum allowable TTL + zone_stream << "$TTL 2147483647\nd.example.org. IN A 192.0.2.5\n"; setLoader(zone_stream, Name("example.org."), RRClass::IN(), MasterLoader::DEFAULT); @@ -428,7 +430,8 @@ TEST_F(MasterLoaderTest, ttlDirective) { checkRR("example.org", RRType::A(), "192.0.2.1", RRTTL(1800)); checkRR("a.example.org", RRType::A(), "192.0.2.2", RRTTL(100)); checkRR("b.example.org", RRType::A(), "192.0.2.3", RRTTL(3600)); - checkRR("c.example.org", RRType::A(), "192.0.2.3", RRTTL(360)); + checkRR("c.example.org", RRType::A(), "192.0.2.4", RRTTL(360)); + checkRR("d.example.org", RRType::A(), "192.0.2.5", RRTTL(2147483647)); } TEST_F(MasterLoaderTest, ttlFromSOA) { @@ -527,6 +530,36 @@ TEST_F(MasterLoaderTest, ttlUnknownAndEOF) { checkCallbackMessage(*warnings_.rbegin(), "Unexpected end end of file", 1); } +TEST_F(MasterLoaderTest, ttlOverflow) { + stringstream zone_stream; + zone_stream << "example.org. IN SOA . . 0 0 0 0 2147483648\n"; + zone_stream << "$TTL 3600\n"; // reset to an in-range value + zone_stream << "$TTL 2147483649\n" << "a.example.org. IN A 192.0.2.1\n"; + zone_stream << "$TTL 3600\n"; // reset to an in-range value + zone_stream << "b.example.org. 2147483650 IN A 192.0.2.2\n"; + setLoader(zone_stream, Name("example.org."), RRClass::IN(), + MasterLoader::DEFAULT); + + loader_->load(); + EXPECT_TRUE(loader_->loadedSucessfully()); + EXPECT_EQ(3, rrsets_.size()); + + checkRR("example.org", RRType::SOA(), ". . 0 0 0 0 2147483648", RRTTL(0)); + checkRR("a.example.org", RRType::A(), "192.0.2.1", RRTTL(0)); + checkRR("b.example.org", RRType::A(), "192.0.2.2", RRTTL(0)); + + EXPECT_EQ(4, warnings_.size()); + checkCallbackMessage(warnings_.at(1), + "TTL 2147483648 > MAXTTL, setting to 0 per RFC2181", + 1); + checkCallbackMessage(warnings_.at(2), + "TTL 2147483649 > MAXTTL, setting to 0 per RFC2181", + 3); + checkCallbackMessage(warnings_.at(3), + "TTL 2147483650 > MAXTTL, setting to 0 per RFC2181", + 6); +} + // Test the constructor rejects empty add callback. TEST_F(MasterLoaderTest, emptyCallback) { EXPECT_THROW(MasterLoader(TEST_DATA_SRCDIR "/example.org",