const MasterLoaderCallbacks& callbacks,
const AddRRCallback& add_callback,
MasterLoader::Options options) :
+ MAX_TTL(0x7fffffff),
lexer_(),
zone_origin_(zone_origin),
zone_class_(zone_class),
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
bool setCurrentTTL(const string& ttl_txt) {
try {
setCurrentTTL(RRTTL(ttl_txt));
+ limitTTL(*current_ttl_, false);
return (true);
} catch (const InvalidRRTTL&) {
return (false);
const uint32_t ttl_val =
dynamic_cast<const rdata::generic::SOA&>(*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,
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 '" <<
}
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_;
// 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);
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) {
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",