Commit 655395cd authored by JINMEI Tatuya's avatar JINMEI Tatuya

[master] Merge branch 'trac2429'

With fixing conflicts:
	src/lib/dns/master_loader.cc
	src/lib/dns/tests/master_loader_unittest.cc
parents ea8e70a3 93cbc6cc
...@@ -15,11 +15,14 @@ ...@@ -15,11 +15,14 @@
#include <dns/master_loader.h> #include <dns/master_loader.h>
#include <dns/master_lexer.h> #include <dns/master_lexer.h>
#include <dns/name.h> #include <dns/name.h>
#include <dns/rdataclass.h>
#include <dns/rrttl.h> #include <dns/rrttl.h>
#include <dns/rrclass.h> #include <dns/rrclass.h>
#include <dns/rrtype.h> #include <dns/rrtype.h>
#include <dns/rdata.h> #include <dns/rdata.h>
#include <boost/scoped_ptr.hpp>
#include <string> #include <string>
#include <memory> #include <memory>
#include <boost/algorithm/string/predicate.hpp> // for iequals #include <boost/algorithm/string/predicate.hpp> // for iequals
...@@ -64,23 +67,10 @@ public: ...@@ -64,23 +67,10 @@ public:
ok_(true), ok_(true),
many_errors_((options & MANY_ERRORS) != 0), many_errors_((options & MANY_ERRORS) != 0),
complete_(false), 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) { void pushSource(const std::string& filename) {
std::string error; std::string error;
if (!lexer_.pushSource(filename.c_str(), &error)) { if (!lexer_.pushSource(filename.c_str(), &error)) {
...@@ -95,6 +85,28 @@ public: ...@@ -95,6 +85,28 @@ public:
initialized_ = true; 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() { bool popSource() {
if (lexer_.getSourceCount() == 1) { if (lexer_.getSourceCount() == 1) {
return (false); return (false);
...@@ -103,19 +115,12 @@ public: ...@@ -103,19 +115,12 @@ public:
return (true); 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. // Get a string token. Handle it as error if it is not string.
const string getString() { const string getString() {
lexer_.getNextToken(MasterToken::STRING).getString(string_token_); lexer_.getNextToken(MasterToken::STRING).getString(string_token_);
return (string_token_); return (string_token_);
} }
bool loadIncremental(size_t count_limit);
void doInclude() { void doInclude() {
// First, get the filename to include // First, get the filename to include
const string const string
...@@ -138,6 +143,107 @@ public: ...@@ -138,6 +143,107 @@ public:
pushSource(filename); 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) { void handleDirective(const char* directive, size_t length) {
if (iequals(directive, "INCLUDE")) { if (iequals(directive, "INCLUDE")) {
doInclude(); doInclude();
...@@ -146,9 +252,8 @@ public: ...@@ -146,9 +252,8 @@ public:
isc_throw(isc::NotImplemented, isc_throw(isc::NotImplemented,
"Origin directive not implemented yet"); "Origin directive not implemented yet");
} else if (iequals(directive, "TTL")) { } else if (iequals(directive, "TTL")) {
// TODO: Implement setDefaultTTL(RRTTL(getString()), false);
isc_throw(isc::NotImplemented, eatUntilEOL(true);
"TTL directive not implemented yet");
} else { } else {
isc_throw(InternalException, "Unknown directive '" << isc_throw(InternalException, "Unknown directive '" <<
string(directive, directive + length) << "'"); string(directive, directive + length) << "'");
...@@ -163,7 +268,7 @@ public: ...@@ -163,7 +268,7 @@ public:
case MasterToken::END_OF_FILE: case MasterToken::END_OF_FILE:
callbacks_.warning(lexer_.getSourceName(), callbacks_.warning(lexer_.getSourceName(),
lexer_.getSourceLine(), lexer_.getSourceLine(),
"File does not end with newline"); "Unexpected end of file");
// We don't pop here. The End of file will stay there, // We don't pop here. The End of file will stay there,
// and we'll handle it in the next iteration of // and we'll handle it in the next iteration of
// loadIncremental properly. // loadIncremental properly.
...@@ -190,6 +295,11 @@ private: ...@@ -190,6 +295,11 @@ private:
const RRClass zone_class_; const RRClass zone_class_;
MasterLoaderCallbacks callbacks_; MasterLoaderCallbacks callbacks_;
AddRRCallback add_callback_; 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 MasterLoader::Options options_;
const std::string master_file_; const std::string master_file_;
std::string string_token_; std::string string_token_;
...@@ -201,6 +311,8 @@ public: ...@@ -201,6 +311,8 @@ public:
bool complete_; // All work done. bool complete_; // All work done.
bool seen_error_; // Was there at least one error during the bool seen_error_; // Was there at least one error during the
// load? // load?
bool warn_rfc1035_ttl_; // should warn if implicit TTL determination
// from the previous RR is used.
}; };
bool bool
...@@ -260,8 +372,18 @@ MasterLoader::MasterLoaderImpl::loadIncremental(size_t count_limit) { ...@@ -260,8 +372,18 @@ MasterLoader::MasterLoaderImpl::loadIncremental(size_t count_limit) {
// anything yet // anything yet
// The parameters // The parameters
const RRTTL ttl(getString()); MasterToken rrparam_token = lexer_.getNextToken();
const RRClass rrclass(getString());
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()); const RRType rrtype(getString());
// TODO: Some more validation? // TODO: Some more validation?
...@@ -273,17 +395,19 @@ MasterLoader::MasterLoaderImpl::loadIncremental(size_t count_limit) { ...@@ -273,17 +395,19 @@ MasterLoader::MasterLoaderImpl::loadIncremental(size_t count_limit) {
} }
// TODO: Check if it is SOA, it should be at the origin. // TODO: Check if it is SOA, it should be at the origin.
const rdata::RdataPtr data(rdata::createRdata(rrtype, rrclass, const rdata::RdataPtr rdata(rdata::createRdata(rrtype, rrclass,
lexer_, lexer_,
&zone_origin_, &zone_origin_,
options_, options_,
callbacks_)); callbacks_));
// In case we get NULL, it means there was error creating // In case we get NULL, it means there was error creating
// the Rdata. The errors should have been reported by // the Rdata. The errors should have been reported by
// callbacks_ already. We need to decide if we want to continue // callbacks_ already. We need to decide if we want to continue
// or not. // or not.
if (data) { if (rdata) {
add_callback_(name, rrclass, rrtype, ttl, data); add_callback_(name, rrclass, rrtype,
getCurrentTTL(explicit_ttl, rrtype, rdata),
rdata);
// Good, we loaded another one // Good, we loaded another one
++count; ++count;
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <string> #include <string>
#include <boost/static_assert.hpp>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include <exceptions/exceptions.h> #include <exceptions/exceptions.h>
...@@ -112,6 +113,16 @@ SOA::getSerial() const { ...@@ -112,6 +113,16 @@ SOA::getSerial() const {
return (Serial(b.readUint32())); 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 string
SOA::toText() const { SOA::toText() const {
InputBuffer b(numdata_, sizeof(numdata_)); InputBuffer b(numdata_, sizeof(numdata_));
......
...@@ -35,8 +35,12 @@ public: ...@@ -35,8 +35,12 @@ public:
SOA(const Name& mname, const Name& rname, uint32_t serial, SOA(const Name& mname, const Name& rname, uint32_t serial,
uint32_t refresh, uint32_t retry, uint32_t expire, uint32_t refresh, uint32_t retry, uint32_t expire,
uint32_t minimum); uint32_t minimum);
/// \brief Returns the serial stored in the SOA. /// \brief Returns the serial stored in the SOA.
Serial getSerial() const; Serial getSerial() const;
/// brief Returns the minimum TTL field value of the SOA.
uint32_t getMinimum() const;
private: private:
/// Note: this is a prototype version; we may reconsider /// Note: this is a prototype version; we may reconsider
/// this representation later. /// this representation later.
......
...@@ -57,9 +57,14 @@ Unit units[] = { ...@@ -57,9 +57,14 @@ Unit units[] = {
namespace isc { namespace isc {
namespace dns { namespace dns {
RRTTL::RRTTL(const std::string& ttlstr) { namespace {
bool
parseTTLString(const string& ttlstr, uint32_t& ttlval, string* error_txt) {
if (ttlstr.empty()) { 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 // 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 // some compilers don't fail when out of range, so we check the range
...@@ -80,8 +85,10 @@ RRTTL::RRTTL(const std::string& ttlstr) { ...@@ -80,8 +85,10 @@ RRTTL::RRTTL(const std::string& ttlstr) {
if (unit == end) { if (unit == end) {
if (units_mode) { if (units_mode) {
// We had some units before. The last one is missing unit. // We had some units before. The last one is missing unit.
isc_throw(InvalidRRTTL, "Missing the last unit: " << if (error_txt != NULL) {
ttlstr); *error_txt = "Missing the last unit: " + ttlstr;
}
return (false);
} else { } else {
// Case without any units at all. Just convert and store // Case without any units at all. Just convert and store
// it. // it.
...@@ -102,12 +109,18 @@ RRTTL::RRTTL(const std::string& ttlstr) { ...@@ -102,12 +109,18 @@ RRTTL::RRTTL(const std::string& ttlstr) {
} }
} }
if (!found) { if (!found) {
isc_throw(InvalidRRTTL, "Unknown unit used: " << *unit << if (error_txt != NULL) {
" in: " << ttlstr); *error_txt = "Unknown unit used: " +
boost::lexical_cast<string>(*unit) + " in: " + ttlstr;
}
return (false);
} }
// Now extract the number. // Now extract the number.
if (unit == pos) { 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, const int64_t value = boost::lexical_cast<int64_t>(string(pos,
unit)); unit));
...@@ -118,21 +131,48 @@ RRTTL::RRTTL(const std::string& ttlstr) { ...@@ -118,21 +131,48 @@ RRTTL::RRTTL(const std::string& ttlstr) {
// there's no need to continue). // there's no need to continue).
if (value < 0 || value > 0xffffffff || val < 0 || if (value < 0 || value > 0xffffffff || val < 0 ||
val > 0xffffffff) { val > 0xffffffff) {
isc_throw(InvalidRRTTL, "Part of TTL out of range: " << if (error_txt != NULL) {
ttlstr); *error_txt = "Part of TTL out of range: " + ttlstr;
}
return (false);
} }
// Move to after the unit. // Move to after the unit.
pos = unit + 1; pos = unit + 1;
} }
} catch (const boost::bad_lexical_cast&) { } 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) { if (val >= 0 && val <= 0xffffffff) {
ttlval_ = val; ttlval = val;
} else { } 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) { RRTTL::RRTTL(InputBuffer& buffer) {
......
...@@ -15,10 +15,12 @@ ...@@ -15,10 +15,12 @@
#ifndef RRTTL_H #ifndef RRTTL_H
#define RRTTL_H 1 #define RRTTL_H 1
#include <stdint.h>
#include <exceptions/exceptions.h> #include <exceptions/exceptions.h>
#include <boost/optional.hpp>
#include <stdint.h>
namespace isc { namespace isc {
namespace util { namespace util {
class InputBuffer; class InputBuffer;
...@@ -30,6 +32,16 @@ namespace dns { ...@@ -30,6 +32,16 @@ namespace dns {
// forward declarations // forward declarations
class AbstractMessageRenderer; 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 /// \brief A standard DNS module exception that is thrown if an RRTTL object
/// is being constructed from an unrecognized string. /// is being constructed from an unrecognized string.
...@@ -61,7 +73,7 @@ public: ...@@ -61,7 +73,7 @@ public:
class RRTTL { class RRTTL {
public: public:
/// ///
/// \name Constructors and Destructor /// \name Constructors, Factory and Destructor
/// ///
/// Note: We use the default copy constructor and the default copy /// Note: We use the default copy constructor and the default copy
/// assignment operator intentionally. /// assignment operator intentionally.
...@@ -72,6 +84,7 @@ public: ...@@ -72,6 +84,7 @@ public:
/// ///
/// \param ttlval An 32-bit integer of the RRTTL. /// \param ttlval An 32-bit integer of the RRTTL.
explicit RRTTL(uint32_t ttlval) : ttlval_(ttlval) {} explicit RRTTL(uint32_t ttlval) : ttlval_(ttlval) {}
/// Constructor from a string. /// Constructor from a string.
/// ///
/// It accepts either a decimal number, specifying number of seconds. Or, /// It accepts either a decimal number, specifying number of seconds. Or,
...@@ -87,6 +100,7 @@ public: ...@@ -87,6 +100,7 @@ public:
/// \throw InvalidRRTTL in case the string is not recognized as valid /// \throw InvalidRRTTL in case the string is not recognized as valid
/// TTL representation. /// TTL representation.
explicit RRTTL(const std::string& ttlstr); explicit RRTTL(const std::string& ttlstr);
/// Constructor from wire-format data. /// Constructor from wire-format data.
/// ///
/// The \c buffer parameter normally stores a complete DNS message /// The \c buffer parameter normally stores a complete DNS message
...@@ -98,6 +112,39 @@ public: ...@@ -98,6 +112,39 @@ public:
/// ///
/// \param buffer A buffer storing the wire format data. /// \param buffer A buffer storing the wire format data.
explicit RRTTL(isc::util::InputBuffer& buffer); 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);
///