BIND 10 master, updated. 655395cdae670d648bb556467773c4014094cce5 [master] Merge branch 'trac2429'
BIND 10 source code commits
bind10-changes at lists.isc.org
Tue Dec 18 08:15:49 UTC 2012
The branch, master has been updated
via 655395cdae670d648bb556467773c4014094cce5 (commit)
via 93cbc6cc8a27e8dad8eb0ef10a66655e7fbf6101 (commit)
via d7a0286809c9759de8f33209823eaddfe3f76264 (commit)
via 8b616d0d3fbf1da3533c9836687f7fa12a7da1c6 (commit)
via f277a67977c700be9e205c6af2c29f2b0764b19c (commit)
via 862d60332f64f7bb357cf8f300fd0faf610ef638 (commit)
via b9fa6ce6e6ba2d491096bc5c632e80c70a73614e (commit)
via 2aa24772e0d3c790ab2127933159747f30bd5430 (commit)
via ed49f68cc90984e065b40a0100e154956403fb35 (commit)
via 62f943fe6baa7a99990856bb03be039e54341ad3 (commit)
via 525af02fc521561f117064cfee06288798236287 (commit)
via c8f37fef053d256ba32adc921cf677059f61117f (commit)
via be2fccd2405459f3b421d2f7a7bec11103710f4c (commit)
via 33d80f86895ac4ace68327554995503471054421 (commit)
via e2ff6e8c7eb46427bd96557566728cd64593449c (commit)
via ea84394f03c42756deddc0c575925dc4ee5b4e90 (commit)
via c4824042727cb37018bdc7fdc1986a909b6f0853 (commit)
via 334ffd13161af15de4bfb404be77e73e085de7f6 (commit)
via e5336c30e084c9adb8c3c7aed879ba6004442d71 (commit)
via f7470975b47ef0cff5fda310ae1ed17fa20df2ba (commit)
via 1f1d62190f05bc03a5d58b7cc247ce471bcf2b39 (commit)
via 84a3a650b35dcc7b69c40383d28795a678d71044 (commit)
via 9fecf9fce1733d3df9e6dce9545c1427c47265d0 (commit)
via 606ba69e360a2d841e9ec5eb7e5bd32d2c86f062 (commit)
via 14e57fb23c208b65884b5b6377836c15ed3d341c (commit)
via c5526a8ccd41201df0ec98f7b0da06113b9e6e12 (commit)
via 9ece3d02afe58dcc487e0ca0a4c464f1ba53eca7 (commit)
via 1649bf2b29e4770739fdc517c4e0d98253dfbe0d (commit)
via 3c779a4236347f9d6128de3fd7f0ef5433a304a9 (commit)
from ea8e70a330e7930e3be85fcb6fe6ac7ca44fc643 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit 655395cdae670d648bb556467773c4014094cce5
Merge: ea8e70a 93cbc6c
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Tue Dec 18 00:15:49 2012 -0800
[master] Merge branch 'trac2429'
With fixing conflicts:
src/lib/dns/master_loader.cc
src/lib/dns/tests/master_loader_unittest.cc
-----------------------------------------------------------------------
Summary of changes:
src/lib/dns/master_loader.cc | 194 ++++++++++++++++++-----
src/lib/dns/rdata/generic/soa_6.cc | 11 ++
src/lib/dns/rdata/generic/soa_6.h | 4 +
src/lib/dns/rrttl.cc | 64 ++++++--
src/lib/dns/rrttl.h | 69 ++++++++-
src/lib/dns/tests/master_loader_unittest.cc | 221 ++++++++++++++++++++++++---
src/lib/dns/tests/rdata_soa_unittest.cc | 19 ++-
src/lib/dns/tests/rrttl_unittest.cc | 17 +++
8 files changed, 526 insertions(+), 73 deletions(-)
-----------------------------------------------------------------------
diff --git a/src/lib/dns/master_loader.cc b/src/lib/dns/master_loader.cc
index 0a3db39..11da289 100644
--- a/src/lib/dns/master_loader.cc
+++ b/src/lib/dns/master_loader.cc
@@ -15,11 +15,14 @@
#include <dns/master_loader.h>
#include <dns/master_lexer.h>
#include <dns/name.h>
+#include <dns/rdataclass.h>
#include <dns/rrttl.h>
#include <dns/rrclass.h>
#include <dns/rrtype.h>
#include <dns/rdata.h>
+#include <boost/scoped_ptr.hpp>
+
#include <string>
#include <memory>
#include <boost/algorithm/string/predicate.hpp> // for iequals
@@ -64,23 +67,10 @@ public:
ok_(true),
many_errors_((options & MANY_ERRORS) != 0),
complete_(false),
- seen_error_(false)
+ seen_error_(false),
+ warn_rfc1035_ttl_(true)
{}
- void reportError(const std::string& filename, size_t line,
- const std::string& reason)
- {
- seen_error_ = true;
- callbacks_.error(filename, line, reason);
- if (!many_errors_) {
- // In case we don't have the lenient mode, every error is fatal
- // and we throw
- ok_ = false;
- complete_ = true;
- isc_throw(MasterLoaderError, reason.c_str());
- }
- }
-
void pushSource(const std::string& filename) {
std::string error;
if (!lexer_.pushSource(filename.c_str(), &error)) {
@@ -95,6 +85,28 @@ public:
initialized_ = true;
}
+ void pushStreamSource(std::istream& stream) {
+ lexer_.pushSource(stream);
+ initialized_ = true;
+ }
+
+ bool loadIncremental(size_t count_limit);
+
+private:
+ void reportError(const std::string& filename, size_t line,
+ const std::string& reason)
+ {
+ seen_error_ = true;
+ callbacks_.error(filename, line, reason);
+ if (!many_errors_) {
+ // In case we don't have the lenient mode, every error is fatal
+ // and we throw
+ ok_ = false;
+ complete_ = true;
+ isc_throw(MasterLoaderError, reason.c_str());
+ }
+ }
+
bool popSource() {
if (lexer_.getSourceCount() == 1) {
return (false);
@@ -103,19 +115,12 @@ public:
return (true);
}
- void pushStreamSource(std::istream& stream) {
- lexer_.pushSource(stream);
- initialized_ = true;
- }
-
// Get a string token. Handle it as error if it is not string.
const string getString() {
lexer_.getNextToken(MasterToken::STRING).getString(string_token_);
return (string_token_);
}
- bool loadIncremental(size_t count_limit);
-
void doInclude() {
// First, get the filename to include
const string
@@ -138,6 +143,107 @@ public:
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 2^31-1 (0x7fffffff),
+ // 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 (depending on
+ // the meaning of 'received'), but the end result would be the same (i.e.,
+ // the guarantee on transmission). 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 > RRTTL::MAX()) {
+ 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. This should be from either $TTL or SOA
+ // minimum TTL (it's the caller's responsibility; this method doesn't
+ // care about where it comes from). 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);
+ }
+
+ // Try to set/reset the current TTL from candidate TTL text. It's possible
+ // it does not actually represent a TTL (which is not immediately
+ // considered an error). Return true iff it's recognized as a valid TTL
+ // (and only in which case the current TTL is set).
+ bool setCurrentTTL(const string& ttl_txt) {
+ // We use the factory version instead of RRTTL constructor as we
+ // need to expect cases where ttl_txt does not actually represent a TTL
+ // but an RR class or type.
+ const MaybeRRTTL maybe_ttl = RRTTL::createFromText(ttl_txt);
+ if (maybe_ttl) {
+ current_ttl_ = maybe_ttl;
+ limitTTL(*current_ttl_, false);
+ return (true);
+ }
+ return (false);
+ }
+
+ // Determine the TTL of the current RR based on the given parsing context.
+ //
+ // explicit_ttl is true iff the TTL is explicitly specified for that RR
+ // (in which case current_ttl_ is set to that TTL).
+ // rrtype is the type of the current RR, and rdata is its RDATA. They
+ // only matter if the type is SOA and no available TTL is known. In this
+ // case the minimum TTL of the SOA will be used as the TTL of that SOA
+ // and the default TTL for subsequent RRs.
+ const RRTTL& getCurrentTTL(bool explicit_ttl, const RRType& rrtype,
+ const rdata::ConstRdataPtr& rdata) {
+ // We've completed parsing the full of RR, and the lexer is already
+ // positioned at the next line. If we need to call callback,
+ // we need to adjust the line number.
+ const size_t current_line = lexer_.getSourceLine() - 1;
+
+ if (!current_ttl_ && !default_ttl_) {
+ if (rrtype == RRType::SOA()) {
+ callbacks_.warning(lexer_.getSourceName(), current_line,
+ "no TTL specified; "
+ "using SOA MINTTL instead");
+ const uint32_t ttl_val =
+ dynamic_cast<const rdata::generic::SOA&>(*rdata).
+ getMinimum();
+ setDefaultTTL(RRTTL(ttl_val), true);
+ current_ttl_ = *default_ttl_;
+ } else {
+ // On catching the exception we'll try to reach EOL again,
+ // so we need to unget it now.
+ lexer_.ungetToken();
+ throw InternalException(__FILE__, __LINE__,
+ "no TTL specified; load rejected");
+ }
+ } else if (!explicit_ttl && default_ttl_) {
+ current_ttl_ = *default_ttl_;
+ } else if (!explicit_ttl && warn_rfc1035_ttl_) {
+ // Omitted (class and) TTL values are default to the last
+ // explicitly stated values (RFC 1035, Sec. 5.1).
+ callbacks_.warning(lexer_.getSourceName(), current_line,
+ "using RFC1035 TTL semantics; default to the "
+ "last explicitly stated TTL");
+ warn_rfc1035_ttl_ = false; // we only warn about this once
+ }
+ assert(current_ttl_);
+ return (*current_ttl_);
+ }
+
void handleDirective(const char* directive, size_t length) {
if (iequals(directive, "INCLUDE")) {
doInclude();
@@ -146,9 +252,8 @@ public:
isc_throw(isc::NotImplemented,
"Origin directive not implemented yet");
} else if (iequals(directive, "TTL")) {
- // TODO: Implement
- isc_throw(isc::NotImplemented,
- "TTL directive not implemented yet");
+ setDefaultTTL(RRTTL(getString()), false);
+ eatUntilEOL(true);
} else {
isc_throw(InternalException, "Unknown directive '" <<
string(directive, directive + length) << "'");
@@ -163,7 +268,7 @@ public:
case MasterToken::END_OF_FILE:
callbacks_.warning(lexer_.getSourceName(),
lexer_.getSourceLine(),
- "File does not end with newline");
+ "Unexpected end of file");
// We don't pop here. The End of file will stay there,
// and we'll handle it in the next iteration of
// loadIncremental properly.
@@ -190,6 +295,11 @@ private:
const RRClass zone_class_;
MasterLoaderCallbacks callbacks_;
AddRRCallback add_callback_;
+ boost::scoped_ptr<RRTTL> default_ttl_; // Default TTL of RRs used when
+ // unspecified. If NULL no default
+ // is known.
+ MaybeRRTTL current_ttl_; // The TTL used most recently. Initially unset.
+ // Once set always stores a valid RRTTL.
const MasterLoader::Options options_;
const std::string master_file_;
std::string string_token_;
@@ -201,6 +311,8 @@ public:
bool complete_; // All work done.
bool seen_error_; // Was there at least one error during the
// load?
+ bool warn_rfc1035_ttl_; // should warn if implicit TTL determination
+ // from the previous RR is used.
};
bool
@@ -260,8 +372,18 @@ MasterLoader::MasterLoaderImpl::loadIncremental(size_t count_limit) {
// anything yet
// The parameters
- const RRTTL ttl(getString());
- const RRClass rrclass(getString());
+ MasterToken rrparam_token = lexer_.getNextToken();
+
+ bool explicit_ttl = false;
+ if (rrparam_token.getType() == MasterToken::STRING) {
+ // Try TTL
+ if (setCurrentTTL(rrparam_token.getString())) {
+ explicit_ttl = true;
+ rrparam_token = lexer_.getNextToken();
+ }
+ }
+
+ const RRClass rrclass(rrparam_token.getString());
const RRType rrtype(getString());
// TODO: Some more validation?
@@ -273,17 +395,19 @@ MasterLoader::MasterLoaderImpl::loadIncremental(size_t count_limit) {
}
// TODO: Check if it is SOA, it should be at the origin.
- const rdata::RdataPtr data(rdata::createRdata(rrtype, rrclass,
- lexer_,
- &zone_origin_,
- options_,
- callbacks_));
+ const rdata::RdataPtr rdata(rdata::createRdata(rrtype, rrclass,
+ lexer_,
+ &zone_origin_,
+ options_,
+ callbacks_));
// In case we get NULL, it means there was error creating
// the Rdata. The errors should have been reported by
// callbacks_ already. We need to decide if we want to continue
// or not.
- if (data) {
- add_callback_(name, rrclass, rrtype, ttl, data);
+ if (rdata) {
+ add_callback_(name, rrclass, rrtype,
+ getCurrentTTL(explicit_ttl, rrtype, rdata),
+ rdata);
// Good, we loaded another one
++count;
diff --git a/src/lib/dns/rdata/generic/soa_6.cc b/src/lib/dns/rdata/generic/soa_6.cc
index f2b9627..3ff2f08 100644
--- a/src/lib/dns/rdata/generic/soa_6.cc
+++ b/src/lib/dns/rdata/generic/soa_6.cc
@@ -16,6 +16,7 @@
#include <string>
+#include <boost/static_assert.hpp>
#include <boost/lexical_cast.hpp>
#include <exceptions/exceptions.h>
@@ -112,6 +113,16 @@ SOA::getSerial() const {
return (Serial(b.readUint32()));
}
+uint32_t
+SOA::getMinimum() const {
+ // Make sure the buffer access is safe.
+ BOOST_STATIC_ASSERT(sizeof(numdata_) ==
+ sizeof(uint32_t) * 4 + sizeof(uint32_t));
+
+ InputBuffer b(&numdata_[sizeof(uint32_t) * 4], sizeof(uint32_t));
+ return (b.readUint32());
+}
+
string
SOA::toText() const {
InputBuffer b(numdata_, sizeof(numdata_));
diff --git a/src/lib/dns/rdata/generic/soa_6.h b/src/lib/dns/rdata/generic/soa_6.h
index 2c180b2..d736666 100644
--- a/src/lib/dns/rdata/generic/soa_6.h
+++ b/src/lib/dns/rdata/generic/soa_6.h
@@ -35,8 +35,12 @@ public:
SOA(const Name& mname, const Name& rname, uint32_t serial,
uint32_t refresh, uint32_t retry, uint32_t expire,
uint32_t minimum);
+
/// \brief Returns the serial stored in the SOA.
Serial getSerial() const;
+
+ /// brief Returns the minimum TTL field value of the SOA.
+ uint32_t getMinimum() const;
private:
/// Note: this is a prototype version; we may reconsider
/// this representation later.
diff --git a/src/lib/dns/rrttl.cc b/src/lib/dns/rrttl.cc
index c47ec1f..e7f8441 100644
--- a/src/lib/dns/rrttl.cc
+++ b/src/lib/dns/rrttl.cc
@@ -57,9 +57,14 @@ Unit units[] = {
namespace isc {
namespace dns {
-RRTTL::RRTTL(const std::string& ttlstr) {
+namespace {
+bool
+parseTTLString(const string& ttlstr, uint32_t& ttlval, string* error_txt) {
if (ttlstr.empty()) {
- isc_throw(InvalidRRTTL, "Empty TTL string");
+ if (error_txt != NULL) {
+ *error_txt = "Empty TTL string";
+ }
+ return (false);
}
// We use a larger data type during the computation. This is because
// some compilers don't fail when out of range, so we check the range
@@ -80,8 +85,10 @@ RRTTL::RRTTL(const std::string& ttlstr) {
if (unit == end) {
if (units_mode) {
// We had some units before. The last one is missing unit.
- isc_throw(InvalidRRTTL, "Missing the last unit: " <<
- ttlstr);
+ if (error_txt != NULL) {
+ *error_txt = "Missing the last unit: " + ttlstr;
+ }
+ return (false);
} else {
// Case without any units at all. Just convert and store
// it.
@@ -102,12 +109,18 @@ RRTTL::RRTTL(const std::string& ttlstr) {
}
}
if (!found) {
- isc_throw(InvalidRRTTL, "Unknown unit used: " << *unit <<
- " in: " << ttlstr);
+ if (error_txt != NULL) {
+ *error_txt = "Unknown unit used: " +
+ boost::lexical_cast<string>(*unit) + " in: " + ttlstr;
+ }
+ return (false);
}
// Now extract the number.
if (unit == pos) {
- isc_throw(InvalidRRTTL, "Missing number in TTL: " << ttlstr);
+ if (error_txt != NULL) {
+ *error_txt = "Missing number in TTL: " + ttlstr;
+ }
+ return (false);
}
const int64_t value = boost::lexical_cast<int64_t>(string(pos,
unit));
@@ -118,21 +131,48 @@ RRTTL::RRTTL(const std::string& ttlstr) {
// there's no need to continue).
if (value < 0 || value > 0xffffffff || val < 0 ||
val > 0xffffffff) {
- isc_throw(InvalidRRTTL, "Part of TTL out of range: " <<
- ttlstr);
+ if (error_txt != NULL) {
+ *error_txt = "Part of TTL out of range: " + ttlstr;
+ }
+ return (false);
}
// Move to after the unit.
pos = unit + 1;
}
} catch (const boost::bad_lexical_cast&) {
- isc_throw(InvalidRRTTL, "invalid TTL: " << ttlstr);
+ if (error_txt != NULL) {
+ *error_txt = "invalid TTL: " + ttlstr;
+ }
+ return (false);
}
if (val >= 0 && val <= 0xffffffff) {
- ttlval_ = val;
+ ttlval = val;
} else {
- isc_throw(InvalidRRTTL, "TTL out of range: " << ttlstr);
+ if (error_txt != NULL) {
+ *error_txt = "TTL out of range: " + ttlstr;
+ }
+ return (false);
+ }
+
+ return (true);
+}
+}
+
+RRTTL::RRTTL(const std::string& ttlstr) {
+ string error_txt;
+ if (!parseTTLString(ttlstr, ttlval_, &error_txt)) {
+ isc_throw(InvalidRRTTL, error_txt);
+ }
+}
+
+MaybeRRTTL
+RRTTL::createFromText(const string& ttlstr) {
+ uint32_t ttlval;
+ if (parseTTLString(ttlstr, ttlval, NULL)) {
+ return (MaybeRRTTL(ttlval));
}
+ return (MaybeRRTTL());
}
RRTTL::RRTTL(InputBuffer& buffer) {
diff --git a/src/lib/dns/rrttl.h b/src/lib/dns/rrttl.h
index 5acd3b1..e160a1e 100644
--- a/src/lib/dns/rrttl.h
+++ b/src/lib/dns/rrttl.h
@@ -15,10 +15,12 @@
#ifndef RRTTL_H
#define RRTTL_H 1
-#include <stdint.h>
-
#include <exceptions/exceptions.h>
+#include <boost/optional.hpp>
+
+#include <stdint.h>
+
namespace isc {
namespace util {
class InputBuffer;
@@ -30,6 +32,16 @@ namespace dns {
// forward declarations
class AbstractMessageRenderer;
+class RRTTL; // forward declaration to define MaybeRRTTL
+
+/// \brief A shortcut for a compound type to represent RRTTL-or-not.
+///
+/// A value of this type can be interpreted in a boolean context, whose
+/// value is \c true if and only if it contains a valid RRTTL object.
+/// And, if it contains a valid RRTTL object, its value is accessible
+/// using \c operator*, just like a bare pointer to \c RRTTL.
+typedef boost::optional<RRTTL> MaybeRRTTL;
+
///
/// \brief A standard DNS module exception that is thrown if an RRTTL object
/// is being constructed from an unrecognized string.
@@ -61,7 +73,7 @@ public:
class RRTTL {
public:
///
- /// \name Constructors and Destructor
+ /// \name Constructors, Factory and Destructor
///
/// Note: We use the default copy constructor and the default copy
/// assignment operator intentionally.
@@ -72,6 +84,7 @@ public:
///
/// \param ttlval An 32-bit integer of the RRTTL.
explicit RRTTL(uint32_t ttlval) : ttlval_(ttlval) {}
+
/// Constructor from a string.
///
/// It accepts either a decimal number, specifying number of seconds. Or,
@@ -87,6 +100,7 @@ public:
/// \throw InvalidRRTTL in case the string is not recognized as valid
/// TTL representation.
explicit RRTTL(const std::string& ttlstr);
+
/// Constructor from wire-format data.
///
/// The \c buffer parameter normally stores a complete DNS message
@@ -98,6 +112,39 @@ public:
///
/// \param buffer A buffer storing the wire format data.
explicit RRTTL(isc::util::InputBuffer& buffer);
+
+ /// A separate factory of RRTTL from text.
+ ///
+ /// This static method is similar to the constructor that takes a string
+ /// object, but works as a factory and reports parsing failure in the
+ /// form of the return value. Normally the constructor version should
+ /// suffice, but in some cases the caller may have to expect mixture of
+ /// valid and invalid input, and may want to minimize the overhead of
+ /// possible exception handling. This version is provided for such
+ /// purpose.
+ ///
+ /// If the given text represents a valid RRTTL, it returns a \c MaybeRRTTL
+ /// object that stores a corresponding \c RRTTL object, which is
+ /// accessible via \c operator*(). In this case the returned object will
+ /// be interpreted as \c true in a boolean context. If the given text
+ /// does not represent a valid RRTTL, it returns a \c MaybeRRTTL object
+ /// which is interpreted as \c false in a boolean context.
+ ///
+ /// One main purpose of this function is to minimize the overhead
+ /// when the given text does not represent a valid RR TTL. For this
+ /// reason this function intentionally omits the capability of delivering
+ /// details reason for the parse failure, such as in the \c want()
+ /// string when exception is thrown from the constructor (it will
+ /// internally require a creation of string object, which is relatively
+ /// expensive). If such detailed information is necessary, the constructor
+ /// version should be used to catch the resulting exception.
+ ///
+ /// This function never throws the \c InvalidRRTTL exception.
+ ///
+ /// \param ttlstr A string representation of the \c RRTTL.
+ /// \return An MaybeRRTTL object either storing an RRTTL object for
+ /// the given text or a \c false value.
+ static MaybeRRTTL createFromText(const std::string& ttlstr);
///
//@}
@@ -236,6 +283,22 @@ public:
{ return (ttlval_ > other.ttlval_); }
//@}
+ ///
+ /// \name Protocol constants
+ ///
+ //@{
+ /// \brief The TTL of the max allowable value, per RFC2181 Section 8.
+ ///
+ /// The max value is the largest unsigned 31 bit integer, 2^31-1.
+ ///
+ /// \note At the moment an RRTTL object can have a value larger than
+ /// this limit. We may revisit it in a future version.
+ static const RRTTL& MAX() {
+ static const RRTTL max_ttl(0x7fffffff);
+ return (max_ttl);
+ }
+ //@}
+
private:
uint32_t ttlval_;
};
diff --git a/src/lib/dns/tests/master_loader_unittest.cc b/src/lib/dns/tests/master_loader_unittest.cc
index bfbf9fa..147deb9 100644
--- a/src/lib/dns/tests/master_loader_unittest.cc
+++ b/src/lib/dns/tests/master_loader_unittest.cc
@@ -17,11 +17,14 @@
#include <dns/rrtype.h>
#include <dns/rrset.h>
#include <dns/rrclass.h>
+#include <dns/rrttl.h>
#include <dns/name.h>
#include <dns/rdata.h>
#include <gtest/gtest.h>
+
#include <boost/bind.hpp>
+#include <boost/lexical_cast.hpp>
#include <boost/scoped_ptr.hpp>
#include <string>
@@ -35,6 +38,7 @@ using std::string;
using std::list;
using std::stringstream;
using std::endl;
+using boost::lexical_cast;
namespace {
class MasterLoaderTest : public ::testing::Test {
@@ -102,7 +106,8 @@ public:
// Check the next RR in the ones produced by the loader
// Other than passed arguments are checked to be the default for the tests
- void checkRR(const string& name, const RRType& type, const string& data) {
+ void checkRR(const string& name, const RRType& type, const string& data,
+ const RRTTL& rrttl = RRTTL(3600)) {
ASSERT_FALSE(rrsets_.empty());
RRsetPtr current = rrsets_.front();
rrsets_.pop_front();
@@ -110,6 +115,7 @@ public:
EXPECT_EQ(Name(name), current->getName());
EXPECT_EQ(type, current->getType());
EXPECT_EQ(RRClass::IN(), current->getClass());
+ EXPECT_EQ(rrttl, current->getTTL());
ASSERT_EQ(1, current->getRdataCount());
EXPECT_EQ(0, isc::dns::rdata::createRdata(type, RRClass::IN(), data)->
compare(current->getRdataIterator()->getCurrent()));
@@ -267,31 +273,53 @@ TEST_F(MasterLoaderTest, invalidFile) {
struct ErrorCase {
const char* const line; // The broken line in master file
+ const char* const reason; // If non NULL, the reason string
const char* const problem; // Description of the problem for SCOPED_TRACE
} const error_cases[] = {
- { "www... 3600 IN A 192.0.2.1", "Invalid name" },
- { "www FORTNIGHT IN A 192.0.2.1", "Invalid TTL" },
- { "www 3600 XX A 192.0.2.1", "Invalid class" },
- { "www 3600 IN A bad_ip", "Invalid Rdata" },
- { "www 3600 IN", "Unexpected EOLN" },
- { "www 3600 CH TXT nothing", "Class mismatch" },
- { "www \"3600\" IN A 192.0.2.1", "Quoted TTL" },
- { "www 3600 \"IN\" A 192.0.2.1", "Quoted class" },
- { "www 3600 IN \"A\" 192.0.2.1", "Quoted type" },
- { "unbalanced)paren 3600 IN A 192.0.2.1", "Token error 1" },
- { "www 3600 unbalanced)paren A 192.0.2.1", "Token error 2" },
+ { "www... 3600 IN A 192.0.2.1", NULL, "Invalid name" },
+ { "www FORTNIGHT IN A 192.0.2.1", NULL, "Invalid TTL" },
+ { "www 3600 XX A 192.0.2.1", NULL, "Invalid class" },
+ { "www 3600 IN A bad_ip", NULL, "Invalid Rdata" },
+ { "www 3600 IN", NULL, "Unexpected EOLN" },
+ { "www 3600 CH TXT nothing", NULL, "Class mismatch" },
+ { "www \"3600\" IN A 192.0.2.1", NULL, "Quoted TTL" },
+ { "www 3600 \"IN\" A 192.0.2.1", NULL, "Quoted class" },
+ { "www 3600 IN \"A\" 192.0.2.1", NULL, "Quoted type" },
+ { "unbalanced)paren 3600 IN A 192.0.2.1", NULL, "Token error 1" },
+ { "www 3600 unbalanced)paren A 192.0.2.1", NULL,
+ "Token error 2" },
// Check the unknown directive. The rest looks like ordinary RR,
// so we see the $ is actually special.
- { "$UNKNOWN 3600 IN A 192.0.2.1", "Unknown $ directive" },
- { "$INCLUD " TEST_DATA_SRCDIR "/example.org", "Include too short" },
- { "$INCLUDES " TEST_DATA_SRCDIR "/example.org", "Include too long" },
- { "$INCLUDE", "Missing include path" },
- { "$INCLUDE /file/not/found", "Include file not found" },
- { "$INCLUDE /file/not/found and here goes bunch of garbage",
+ { "$UNKNOWN 3600 IN A 192.0.2.1", NULL, "Unknown $ directive" },
+ { "$INCLUD " TEST_DATA_SRCDIR "/example.org", NULL, "Include too short" },
+ { "$INCLUDES " TEST_DATA_SRCDIR "/example.org", NULL, "Include too long" },
+ { "$INCLUDE", NULL, "Missing include path" },
+ { "$INCLUDE /file/not/found", NULL, "Include file not found" },
+ { "$INCLUDE /file/not/found and here goes bunch of garbage", NULL,
"Include file not found and garbage at the end of line" },
- { NULL, NULL }
+ { "$TTL 100 extra-garbage", "Extra tokens at the end of line",
+ "$TTL with extra token" },
+ { "$TTL", "unexpected end of input", "missing TTL" },
+ { "$TTL No-ttl", "Unknown unit used: N in: No-ttl", "bad TTL" },
+ { "$TTL \"100\"", "invalid TTL: \"100\"", "bad TTL, quoted" },
+ { "$TT 100", "Unknown directive 'TT'", "bad directive, too short" },
+ { "$TTLLIKE 100", "Unknown directive 'TTLLIKE'", "bad directive, extra" },
+ { NULL, NULL, NULL }
};
+// A commonly used helper to check callback message.
+void
+checkCallbackMessage(const string& actual_msg, const string& expected_msg,
+ size_t expected_line) {
+ // The actual message should begin with the expected message.
+ EXPECT_EQ(0, actual_msg.find(expected_msg)) << "actual message: "
+ << actual_msg;
+
+ // and it should end with "...:<line_num>]"
+ const string line_desc = ":" + lexical_cast<string>(expected_line) + "]";
+ EXPECT_EQ(actual_msg.size() - line_desc.size(), actual_msg.find(line_desc));
+}
+
// Test a broken zone is handled properly. We test several problems,
// both in strict and lenient mode.
TEST_F(MasterLoaderTest, brokenZone) {
@@ -309,6 +337,9 @@ TEST_F(MasterLoaderTest, brokenZone) {
EXPECT_THROW(loader_->load(), MasterLoaderError);
EXPECT_FALSE(loader_->loadedSucessfully());
EXPECT_EQ(1, errors_.size());
+ if (ec->reason != NULL) {
+ checkCallbackMessage(errors_.at(0), ec->reason, 2);
+ }
EXPECT_TRUE(warnings_.empty());
checkRR("example.org", RRType::SOA(), "ns1.example.org. "
@@ -379,6 +410,158 @@ TEST_F(MasterLoaderTest, includeWithGarbage) {
checkRR("www.example.org", RRType::AAAA(), "2001:db8::1");
}
+// Test for "$TTL"
+TEST_F(MasterLoaderTest, ttlDirective) {
+ stringstream zone_stream;
+
+ // Set the default TTL with $TTL followed by an RR omitting the TTL
+ zone_stream << "$TTL 1800\nexample.org. IN A 192.0.2.1\n";
+ // $TTL can be quoted. Also testing the case of $TTL being changed.
+ zone_stream << "\"$TTL\" 100\na.example.org. IN A 192.0.2.2\n";
+ // 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.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);
+ loader_->load();
+ EXPECT_TRUE(loader_->loadedSucessfully());
+ 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.4", RRTTL(360));
+ checkRR("d.example.org", RRType::A(), "192.0.2.5", RRTTL(2147483647));
+}
+
+TEST_F(MasterLoaderTest, ttlFromSOA) {
+ // No $TTL, and the SOA doesn't have an explicit TTL field. Its minimum
+ // TTL field will be used as the RR's TTL, and it'll be used as the
+ // default TTL for others.
+ stringstream zone_stream("example.org. IN SOA . . 0 0 0 0 1800\n"
+ "a.example.org. IN A 192.0.2.1\n");
+ setLoader(zone_stream, Name("example.org."), RRClass::IN(),
+ MasterLoader::DEFAULT);
+ loader_->load();
+ EXPECT_TRUE(loader_->loadedSucessfully());
+ checkRR("example.org", RRType::SOA(), ". . 0 0 0 0 1800", RRTTL(1800));
+ checkRR("a.example.org", RRType::A(), "192.0.2.1", RRTTL(1800));
+
+ // The use of SOA minimum TTL should have caused a warning.
+ EXPECT_EQ(1, warnings_.size());
+ checkCallbackMessage(warnings_.at(0),
+ "no TTL specified; using SOA MINTTL instead", 1);
+}
+
+TEST_F(MasterLoaderTest, ttlFromPrevious) {
+ // No available default TTL. 2nd and 3rd RR will use the TTL of the
+ // 1st RR. This will result in a warning, but only for the first time.
+ stringstream zone_stream("a.example.org. 1800 IN A 192.0.2.1\n"
+ "b.example.org. IN A 192.0.2.2\n"
+ "c.example.org. IN A 192.0.2.3\n");
+ setLoader(zone_stream, Name("example.org."), RRClass::IN(),
+ MasterLoader::DEFAULT);
+ loader_->load();
+ EXPECT_TRUE(loader_->loadedSucessfully());
+ checkRR("a.example.org", RRType::A(), "192.0.2.1", RRTTL(1800));
+ checkRR("b.example.org", RRType::A(), "192.0.2.2", RRTTL(1800));
+ checkRR("c.example.org", RRType::A(), "192.0.2.3", RRTTL(1800));
+
+ EXPECT_EQ(1, warnings_.size());
+ checkCallbackMessage(warnings_.at(0), "using RFC1035 TTL semantics", 2);
+}
+
+TEST_F(MasterLoaderTest, ttlFromPreviousSOA) {
+ // Mixture of the previous two cases: SOA has explicit TTL, followed by
+ // an RR without an explicit TTL. In this case the minimum TTL won't be
+ // recognized as the "default TTL".
+ stringstream zone_stream("example.org. 100 IN SOA . . 0 0 0 0 1800\n"
+ "a.example.org. IN A 192.0.2.1\n");
+ setLoader(zone_stream, Name("example.org."), RRClass::IN(),
+ MasterLoader::DEFAULT);
+ loader_->load();
+ EXPECT_TRUE(loader_->loadedSucessfully());
+
+ checkRR("example.org", RRType::SOA(), ". . 0 0 0 0 1800", RRTTL(100));
+ checkRR("a.example.org", RRType::A(), "192.0.2.1", RRTTL(100));
+
+ EXPECT_EQ(1, warnings_.size());
+ checkCallbackMessage(warnings_.at(0), "using RFC1035 TTL semantics", 2);
+}
+
+TEST_F(MasterLoaderTest, ttlUnknown) {
+ // No available TTL is known for the first RR.
+ stringstream zone_stream("a.example.org. IN A 192.0.2.1\n");
+ setLoader(zone_stream, Name("example.org."), RRClass::IN(),
+ MasterLoader::DEFAULT);
+ EXPECT_THROW(loader_->load(), MasterLoaderError);
+}
+
+TEST_F(MasterLoaderTest, ttlUnknownAndContinue) {
+ stringstream zone_stream("a.example.org. IN A 192.0.2.1\n"
+ "b.example.org. 1800 IN A 192.0.2.2\n");
+ setLoader(zone_stream, Name("example.org."), RRClass::IN(),
+ MasterLoader::MANY_ERRORS);
+ loader_->load();
+ EXPECT_FALSE(loader_->loadedSucessfully());
+ checkRR("b.example.org", RRType::A(), "192.0.2.2", RRTTL(1800));
+
+ EXPECT_TRUE(warnings_.empty());
+ EXPECT_EQ(1, errors_.size());
+ checkCallbackMessage(errors_.at(0), "no TTL specified; load rejected", 1);
+}
+
+TEST_F(MasterLoaderTest, ttlUnknownAndEOF) {
+ // Similar to the previous case, but the input will be abruptly terminated
+ // after the offending RR. This will cause an additional warning.
+ stringstream zone_stream("a.example.org. IN A 192.0.2.1");
+ setLoader(zone_stream, Name("example.org."), RRClass::IN(),
+ MasterLoader::MANY_ERRORS);
+ loader_->load();
+ EXPECT_FALSE(loader_->loadedSucessfully());
+ EXPECT_TRUE(rrsets_.empty());
+
+ EXPECT_EQ(1, errors_.size());
+ checkCallbackMessage(errors_.at(0), "no TTL specified; load rejected", 1);
+
+ // RDATA implementation can complain about it, too. To be independent of
+ // its details, we focus on the very last warning.
+ EXPECT_FALSE(warnings_.empty());
+ checkCallbackMessage(*warnings_.rbegin(), "Unexpected 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",
diff --git a/src/lib/dns/tests/rdata_soa_unittest.cc b/src/lib/dns/tests/rdata_soa_unittest.cc
index 2df711c..52f0601 100644
--- a/src/lib/dns/tests/rdata_soa_unittest.cc
+++ b/src/lib/dns/tests/rdata_soa_unittest.cc
@@ -32,12 +32,14 @@ using namespace isc::dns::rdata;
namespace {
class Rdata_SOA_Test : public RdataTest {
- // there's nothing to specialize
+protected:
+ Rdata_SOA_Test() : rdata_soa(Name("ns.example.com"),
+ Name("root.example.com"),
+ 2010012601, 3600, 300, 3600000, 1200)
+ {}
+ const generic::SOA rdata_soa;
};
-const generic::SOA rdata_soa(Name("ns.example.com"), Name("root.example.com"),
- 2010012601, 3600, 300, 3600000, 1200);
-
TEST_F(Rdata_SOA_Test, createFromText) {
//TBD
}
@@ -86,4 +88,13 @@ TEST_F(Rdata_SOA_Test, getSerial) {
EXPECT_EQ(2010012601, rdata_soa.getSerial().getValue());
}
+TEST_F(Rdata_SOA_Test, getMinimum) {
+ EXPECT_EQ(1200, rdata_soa.getMinimum());
+
+ // Also check with a very large number (with the MSB being 1).
+ EXPECT_EQ(2154848336u, generic::SOA(Name("ns.example.com"),
+ Name("root.example.com"),
+ 0, 0, 0, 0, 0x80706050).getMinimum());
+}
+
}
diff --git a/src/lib/dns/tests/rrttl_unittest.cc b/src/lib/dns/tests/rrttl_unittest.cc
index fe9c55c..916f720 100644
--- a/src/lib/dns/tests/rrttl_unittest.cc
+++ b/src/lib/dns/tests/rrttl_unittest.cc
@@ -20,6 +20,8 @@
#include <dns/tests/unittest_util.h>
+#include <boost/scoped_ptr.hpp>
+
using namespace std;
using namespace isc;
using namespace isc::dns;
@@ -85,6 +87,17 @@ TEST_F(RRTTLTest, fromText) {
EXPECT_THROW(RRTTL("4294967296"), InvalidRRTTL); // must be 32-bit
}
+TEST_F(RRTTLTest, createFromText) {
+ // It returns an actual RRTT iff the given text is recognized as a
+ // valid RR TTL.
+ MaybeRRTTL maybe_ttl = RRTTL::createFromText("3600");
+ EXPECT_TRUE(maybe_ttl);
+ EXPECT_EQ(RRTTL(3600), *maybe_ttl);
+
+ maybe_ttl = RRTTL::createFromText("bad");
+ EXPECT_FALSE(maybe_ttl);
+}
+
void
checkUnit(unsigned multiply, char suffix) {
SCOPED_TRACE(string("Unit check with suffix ") + suffix);
@@ -252,6 +265,10 @@ TEST_F(RRTTLTest, gthan) {
EXPECT_FALSE(ttl_small > ttl_large);
}
+TEST_F(RRTTLTest, maxTTL) {
+ EXPECT_EQ((1u << 31) - 1, RRTTL::MAX().getValue());
+}
+
// test operator<<. We simply confirm it appends the result of toText().
TEST_F(RRTTLTest, LeftShiftOperator) {
ostringstream oss;
More information about the bind10-changes
mailing list