Browse Source

[2429] played with max allowable TTL (2^31-1).

JINMEI Tatuya 12 years ago
parent
commit
e2ff6e8c7e
2 changed files with 70 additions and 5 deletions
  1. 35 3
      src/lib/dns/master_loader.cc
  2. 35 2
      src/lib/dns/tests/master_loader_unittest.cc

+ 35 - 3
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<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,
@@ -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_;

+ 35 - 2
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",