Commit 3a81d2d9 authored by Michal 'vorner' Vaner's avatar Michal 'vorner' Vaner
Browse files

Merge remote-tracking branch 'origin/trac2382' into base/loader

parents 425e18f7 5d239b6a
......@@ -120,6 +120,7 @@ libb10_dns___la_SOURCES += tsigkey.h tsigkey.cc
libb10_dns___la_SOURCES += tsigrecord.h tsigrecord.cc
libb10_dns___la_SOURCES += character_string.h character_string.cc
libb10_dns___la_SOURCES += master_loader_callbacks.h
libb10_dns___la_SOURCES += master_loader.h
libb10_dns___la_SOURCES += rdata/generic/detail/nsec_bitmap.h
libb10_dns___la_SOURCES += rdata/generic/detail/nsec_bitmap.cc
libb10_dns___la_SOURCES += rdata/generic/detail/nsec3param_common.cc
......
......@@ -28,6 +28,7 @@ re_typecode = re.compile('([\da-z]+)_(\d+)')
classcode2txt = {}
typecode2txt = {}
typeandclass = []
new_rdatafactory_users = ['aaaa']
generic_code = 65536 # something larger than any code value
rdata_declarations = ''
class_definitions = ''
......@@ -116,6 +117,9 @@ class AbstractMessageRenderer;\n\n'''
explicit ''' + type_utxt + '''(const std::string& type_str);
''' + type_utxt + '''(isc::util::InputBuffer& buffer, size_t rdata_len);
''' + type_utxt + '''(const ''' + type_utxt + '''& other);
''' + type_utxt + '''(
MasterLexer& lexer, const Name* name,
MasterLoader::Options options, MasterLoaderCallbacks& callbacks);
virtual std::string toText() const;
virtual void toWire(isc::util::OutputBuffer& buffer) const;
virtual void toWire(AbstractMessageRenderer& renderer) const;
......@@ -203,17 +207,33 @@ def generate_rdatadef(file, basemtime):
rdata_deffile.write(class_definitions)
rdata_deffile.close()
def generate_rdatahdr(file, declarations, basemtime):
def generate_rdatahdr(file, heading, declarations, basemtime):
if not need_generate(file, basemtime):
print('skip generating ' + file);
return
heading += '''
#ifndef DNS_RDATACLASS_H
#define DNS_RDATACLASS_H 1
#include <dns/master_loader.h>
namespace isc {
namespace dns {
class Name;
class MasterLexer;
class MasterLoaderCallbacks;
}
}
'''
declarations += '''
#endif // DNS_RDATACLASS_H
// Local Variables:
// mode: c++
// End:
'''
rdata_header = open(file, 'w')
rdata_header.write(heading_txt)
rdata_header.write(heading)
rdata_header.write(declarations)
rdata_header.close()
......@@ -271,15 +291,24 @@ def generate_rrparam(fileprefix, basemtime):
class_utxt = class_tuple[1].upper()
indent = ' ' * 8
typeandclassparams += indent
# By default, we use OldRdataFactory (see bug #2497). If you
# want to pick RdataFactory for a particular type, add it to
# new_rdatafactory_users.
if type_txt in new_rdatafactory_users:
rdf_class = 'RdataFactory'
else:
rdf_class = 'OldRdataFactory'
if class_tuple[1] != 'generic':
typeandclassparams += 'add("' + type_utxt + '", '
typeandclassparams += str(type_code) + ', "' + class_utxt
typeandclassparams += '", ' + str(class_code)
typeandclassparams += ', RdataFactoryPtr(new RdataFactory<'
typeandclassparams += ', RdataFactoryPtr(new ' + rdf_class + '<'
typeandclassparams += class_txt + '::' + type_utxt + '>()));\n'
else:
typeandclassparams += 'add("' + type_utxt + '", ' + str(type_code)
typeandclassparams += ', RdataFactoryPtr(new RdataFactory<'
typeandclassparams += ', RdataFactoryPtr(new ' + rdf_class + '<'
typeandclassparams += class_txt + '::' + type_utxt + '>()));\n'
rrparam_temp = open(placeholder, 'r')
......@@ -296,8 +325,8 @@ if __name__ == "__main__":
try:
import_definitions(classcode2txt, typecode2txt, typeandclass)
generate_rdatadef('@builddir@/rdataclass.cc', rdatadef_mtime)
generate_rdatahdr('@builddir@/rdataclass.h', rdata_declarations,
rdatahdr_mtime)
generate_rdatahdr('@builddir@/rdataclass.h', heading_txt,
rdata_declarations, rdatahdr_mtime)
generate_typeclasscode('rrtype', rdatahdr_mtime, typecode2txt, 'Type')
generate_typeclasscode('rrclass', classdir_mtime,
classcode2txt, 'Class')
......
......@@ -458,8 +458,11 @@ String::handle(MasterLexer& lexer) const {
if (getLexerImpl(lexer)->isTokenEnd(c, escaped)) {
getLexerImpl(lexer)->source_->ungetChar();
// make sure it nul-terminated as a c-str (excluded from token
// data).
data.push_back('\0');
getLexerImpl(lexer)->token_ =
MasterToken(&data.at(0), data.size());
MasterToken(&data.at(0), data.size() - 1);
return;
}
escaped = (c == '\\' && !escaped);
......@@ -486,7 +489,10 @@ QString::handle(MasterLexer& lexer) const {
escaped = false;
data.back() = '"';
} else {
token = MasterToken(&data.at(0), data.size(), true);
// make sure it nul-terminated as a c-str (excluded from token
// data). This also simplifies the case of an empty string.
data.push_back('\0');
token = MasterToken(&data.at(0), data.size() - 1, true);
return;
}
} else if (c == '\n' && !escaped) {
......@@ -529,7 +535,8 @@ Number::handle(MasterLexer& lexer) const {
token = MasterToken(MasterToken::NUMBER_OUT_OF_RANGE);
}
} else {
token = MasterToken(&data.at(0), data.size());
data.push_back('\0'); // see String::handle()
token = MasterToken(&data.at(0), data.size() - 1);
}
return;
}
......
......@@ -90,6 +90,13 @@ public:
/// the region. On the other hand, it is not ensured that the string
/// is nul-terminated. So the usual string manipulation API may not work
/// as expected.
///
/// The `MasterLexer` implementation ensures that there are at least
/// len + 1 bytes of valid memory region starting from beg, and that
/// beg[len] is \0. This means the application can use the bytes as a
/// validly nul-terminated C string if there is no intermediate nul
/// character. Note also that due to this property beg is always non
/// NULL; for an empty string len will be set to 0 and beg[0] is \0.
struct StringRegion {
const char* beg; ///< The start address of the string
size_t len; ///< The length of the string in bytes
......
// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#ifndef MASTER_LOADER_H
#define MASTER_LOADER_H
namespace isc {
namespace dns {
// Placeholder introduced by #2497. The real class should be updated in
// #2377.
class MasterLoader {
public:
enum Options {
MANY_ERRORS, // lenient mode
// also eventually some check policies like "check NS name"
};
};
}
}
#endif // MASTER_LOADER_H
......@@ -119,4 +119,4 @@ private:
}
}
#endif // LOADER_CALLBACKS_H
#endif // MASTER_LOADER_CALLBACKS_H
......@@ -30,6 +30,7 @@
#include <util/buffer.h>
#include <dns/name.h>
#include <dns/messagerenderer.h>
#include <dns/master_lexer.h>
#include <dns/rdata.h>
#include <dns/rrparamregistry.h>
#include <dns/rrtype.h>
......@@ -65,7 +66,7 @@ createRdata(const RRType& rrtype, const RRClass& rrclass,
RdataPtr rdata =
RRParamRegistry::getRegistry().createRdata(rrtype, rrclass, buffer,
len);
if (buffer.getPosition() - old_pos != len) {
isc_throw(InvalidRdataLength, "RDLENGTH mismatch: " <<
buffer.getPosition() - old_pos << " != " << len);
......@@ -81,6 +82,90 @@ createRdata(const RRType& rrtype, const RRClass& rrclass, const Rdata& source)
source));
}
namespace {
void
fromtextError(bool& error_issued, const MasterLexer& lexer,
MasterLoaderCallbacks& callbacks,
const MasterToken* token, const char* reason)
{
// Don't be too noisy if there are many issues for single RDATA
if (error_issued) {
return;
}
error_issued = true;
if (token == NULL) {
callbacks.error(lexer.getSourceName(), lexer.getSourceLine(),
"createRdata from text failed: " + string(reason));
return;
}
switch (token->getType()) {
case MasterToken::STRING:
case MasterToken::QSTRING:
callbacks.error(lexer.getSourceName(), lexer.getSourceLine(),
"createRdata from text failed near '" +
token->getString() + "': " + string(reason));
break;
case MasterToken::ERROR:
callbacks.error(lexer.getSourceName(), lexer.getSourceLine(),
"createRdata from text failed: " +
token->getErrorText());
break;
default:
assert(false);
}
}
}
RdataPtr
createRdata(const RRType& rrtype, const RRClass& rrclass,
MasterLexer& lexer, const Name* origin,
MasterLoader::Options options,
MasterLoaderCallbacks& callbacks)
{
RdataPtr rdata;
bool error_issued = false;
try {
rdata = RRParamRegistry::getRegistry().createRdata(
rrtype, rrclass, lexer, origin, options, callbacks);
} catch (const MasterLexer::LexerError& error) {
fromtextError(error_issued, lexer, callbacks, &error.token_, "");
} catch (const Exception& ex) {
// Catching all isc::Exception is too broad, but right now we don't
// have better granularity. When we complete #2518 we can make this
// finer.
fromtextError(error_issued, lexer, callbacks, NULL, ex.what());
}
// Other exceptions mean a serious implementation bug or fatal system
// error; it doesn't make sense to catch and try to recover from them
// here. Just propagate.
// Consume to end of line / file.
// Call callback via fromtextError once if there was an error.
do {
const MasterToken& token = lexer.getNextToken();
switch (token.getType()) {
case MasterToken::END_OF_LINE:
return (rdata);
case MasterToken::END_OF_FILE:
callbacks.warning(lexer.getSourceName(), lexer.getSourceLine(),
"file does not end with newline");
return (rdata);
default:
rdata.reset(); // we'll return NULL
fromtextError(error_issued, lexer, callbacks, &token,
"extra input text");
// Continue until we see EOL or EOF
}
} while (true);
// We shouldn't reach here
assert(false);
return (RdataPtr()); // add explicit return to silence some compilers
}
int
compareNames(const Name& n1, const Name& n2) {
size_t len1 = n1.getLength();
......@@ -119,7 +204,8 @@ Generic::Generic(isc::util::InputBuffer& buffer, size_t rdata_len) {
impl_ = new GenericImpl(data);
}
Generic::Generic(const std::string& rdata_string) {
void
Generic::constructHelper(const std::string& rdata_string) {
istringstream iss(rdata_string);
string unknown_mark;
iss >> unknown_mark;
......@@ -180,6 +266,34 @@ Generic::Generic(const std::string& rdata_string) {
impl_ = new GenericImpl(data);
}
Generic::Generic(const std::string& rdata_string) {
constructHelper(rdata_string);
}
Generic::Generic(MasterLexer& lexer, const Name*,
MasterLoader::Options,
MasterLoaderCallbacks&)
{
std::string s;
while (true) {
const MasterToken& token = lexer.getNextToken();
if ((token.getType() == MasterToken::END_OF_FILE) ||
(token.getType() == MasterToken::END_OF_LINE)) {
lexer.ungetToken(); // let the upper layer handle the end-of token
break;
}
if (!s.empty()) {
s += " ";
}
s += token.getString();
}
constructHelper(s);
}
Generic::~Generic() {
delete impl_;
}
......
......@@ -15,11 +15,15 @@
#ifndef RDATA_H
#define RDATA_H 1
#include <stdint.h>
#include <dns/master_lexer.h>
#include <dns/master_loader.h>
#include <dns/master_loader_callbacks.h>
#include <exceptions/exceptions.h>
#include <boost/shared_ptr.hpp>
#include <exceptions/exceptions.h>
#include <stdint.h>
namespace isc {
namespace util {
......@@ -279,6 +283,11 @@ public:
/// \param rdata_len The length in buffer of the \c Rdata. In bytes.
Generic(isc::util::InputBuffer& buffer, size_t rdata_len);
/// \brief Constructor from master lexer.
///
Generic(MasterLexer& lexer, const Name* name,
MasterLoader::Options options, MasterLoaderCallbacks& callbacks);
///
/// \brief The destructor.
virtual ~Generic();
......@@ -367,7 +376,10 @@ public:
/// \return > 0 if \c this would be sorted after \c other.
virtual int compare(const Rdata& other) const;
//@}
private:
void constructHelper(const std::string& rdata_string);
GenericImpl* impl_;
};
......@@ -472,6 +484,53 @@ RdataPtr createRdata(const RRType& rrtype, const RRClass& rrclass,
/// \c Rdata object.
RdataPtr createRdata(const RRType& rrtype, const RRClass& rrclass,
const Rdata& source);
/// \brief Create RDATA of a given pair of RR type and class using the
/// master lexer.
///
/// This is a more generic form of factory from textual RDATA, and is mainly
/// intended to be used internally by the master file parser (\c MasterLoader)
/// of this library.
///
/// The \c lexer is expected to be at the beginning of textual RDATA of the
/// specified type and class. This function (and its underlying Rdata
/// implementations) extracts necessary tokens from the lexer and constructs
/// the RDATA from them.
///
/// Due to the intended usage of this version, this function handles error
/// cases quite differently from other versions. It internally catches
/// most of syntax and semantics errors of the input (reported as exceptions),
/// calls the corresponding callback specified by the \c callbacks parameters,
/// and returns a NULL smart pointer. If the caller rather wants to get
/// an exception in these cases, it can use pass a callback that internally
/// throws on error. Some critical exceptions such as \c std::bad_alloc are
/// still propagated to the upper layer as it doesn't make sense to try
/// recovery from such a situation within this function.
///
/// Whether or not the creation succeeds, this function updates the lexer
/// until it reaches either the end of line or file, starting from the end of
/// the RDATA text (or the point of failure if the parsing fails in the
/// middle of it). The caller can therefore assume it's ready for reading
/// the next data (which is normally a subsequent RR in the zone file) on
/// return, whether or not this function succeeds.
///
/// \param rrtype An \c RRType object specifying the type/class pair.
/// \param rrclass An \c RRClass object specifying the type/class pair.
/// \param lexer A \c MasterLexer object parsing a master file for the
/// RDATA to be created
/// \param origin If non NULL, specifies the origin of any domain name fields
/// of the RDATA that are non absolute.
/// \param options Master loader options controlling how to deal with errors
/// or non critical issues in the parsed RDATA.
/// \param callbacks Callback to be called when an error or non critical issue
/// is found.
/// \return An \c RdataPtr object pointing to the created
/// \c Rdata object. Will be NULL if parsing fails.
RdataPtr createRdata(const RRType& rrtype, const RRClass& rrclass,
MasterLexer& lexer, const Name* origin,
MasterLoader::Options options,
MasterLoaderCallbacks& callbacks);
//@}
///
......@@ -511,6 +570,6 @@ int compareNames(const Name& n1, const Name& n2);
}
#endif // RDATA_H
// Local Variables:
// Local Variables:
// mode: c++
// End:
// End:
......@@ -12,6 +12,15 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <exceptions/exceptions.h>
#include <util/buffer.h>
#include <dns/exceptions.h>
#include <dns/messagerenderer.h>
#include <dns/rdata.h>
#include <dns/rdataclass.h>
#include <dns/master_lexer.h>
#include <dns/master_loader.h>
#include <stdint.h>
#include <string.h>
......@@ -20,14 +29,6 @@
#include <arpa/inet.h> // XXX: for inet_pton/ntop(), not exist in C++ standards
#include <sys/socket.h> // for AF_INET/AF_INET6
#include <exceptions/exceptions.h>
#include <util/buffer.h>
#include <dns/exceptions.h>
#include <dns/messagerenderer.h>
#include <dns/rdata.h>
#include <dns/rdataclass.h>
using namespace std;
using namespace isc::util;
......@@ -42,6 +43,16 @@ AAAA::AAAA(const std::string& addrstr) {
}
}
AAAA::AAAA(MasterLexer& lexer, const Name*,
MasterLoader::Options, MasterLoaderCallbacks&)
{
const MasterToken& token = lexer.getNextToken(MasterToken::STRING);
if (inet_pton(AF_INET6, token.getStringRegion().beg, &addr_) != 1) {
isc_throw(InvalidRdataText, "Failed to convert '"
<< token.getString() << "' to IN/AAAA RDATA");
}
}
AAAA::AAAA(InputBuffer& buffer, size_t rdata_len) {
if (rdata_len != sizeof(addr_)) {
isc_throw(DNSMessageFORMERR,
......
......@@ -34,6 +34,11 @@ using namespace isc::util;
// If you added member functions specific to this derived class, you'll need
// to implement them here, of course.
MyType::MyType(MasterLexer& lexer, const Name* origin,
MasterLoader::Options options, MasterLoaderCallbacks& callbacks)
{
}
MyType::MyType(const string& type_str) {
}
......
......@@ -37,10 +37,40 @@ using namespace std;
using namespace boost;
using namespace isc::util;
using namespace isc::dns::rdata;
using namespace isc::dns::rdata;
namespace isc {
namespace dns {
namespace rdata {
RdataPtr
AbstractRdataFactory::create(MasterLexer& lexer, const Name*,
MasterLoader::Options,
MasterLoaderCallbacks&) const
{
std::string s;
while (true) {
const MasterToken& token = lexer.getNextToken();
if ((token.getType() == MasterToken::END_OF_FILE) ||
(token.getType() == MasterToken::END_OF_LINE)) {
lexer.ungetToken(); // let the upper layer handle the end-of token
break;
}
if (!s.empty()) {
s += " ";
}
s += token.getString();
}
return (create(s));
}
} // end of namespace isc::dns::rdata
namespace {
///
/// The following function and class are a helper to define case-insensitive
......@@ -161,8 +191,10 @@ typedef map<RRTypeClass, RdataFactoryPtr> RdataFactoryMap;
typedef map<RRType, RdataFactoryPtr> GenericRdataFactoryMap;
template <typename T>
class RdataFactory : public AbstractRdataFactory {
class OldRdataFactory : public AbstractRdataFactory {
public:
using AbstractRdataFactory::create;
virtual RdataPtr create(const string& rdata_str) const
{
return (RdataPtr(new T(rdata_str)));
......@@ -179,6 +211,18 @@ public:
}
};
template <typename T>
class RdataFactory : public OldRdataFactory<T> {
public:
using OldRdataFactory<T>::create;
virtual RdataPtr create(MasterLexer& lexer, const Name* origin,
MasterLoader::Options options,
MasterLoaderCallbacks& callbacks) const {
return (RdataPtr(new T(lexer, origin, options, callbacks)));
}
};
///
/// \brief The \c RRParamRegistryImpl class is the actual implementation of
/// \c RRParamRegistry.
......@@ -305,7 +349,7 @@ namespace {
/// This could be simplified using strncasecmp(), but unfortunately it's not
/// included in <cstring>. To be as much as portable within the C++ standard
/// we take the "in house" approach here.
///
///
bool CICharEqual(char c1, char c2) {
return (tolower(static_cast<unsigned char>(c1)) ==
tolower(static_cast<unsigned char>(c2)));
......@@ -528,5 +572,26 @@ RRParamRegistry::createRdata(const RRType& rrtype, const RRClass& rrclass,
return (RdataPtr(new rdata::generic::Generic(
dynamic_cast<const generic::Generic&>(source))));
}
RdataPtr
RRParamRegistry::createRdata(const RRType& rrtype, const RRClass& rrclass,
MasterLexer& lexer, const Name* name,
MasterLoader::Options options,
MasterLoaderCallbacks& callbacks)
{
RdataFactoryMap::const_iterator found =
impl_->rdata_factories.find(RRTypeClass(rrtype, rrclass));
if (found != impl_->rdata_factories.end()) {
return (found->second->create(lexer, name, options, callbacks));
}
GenericRdataFactoryMap::const_iterator genfound =
impl_->genericrdata_factories.find(rrtype);
if (genfound != impl_->genericrdata_factories.end()) {
return (genfound->second->create(lexer, name, options, callbacks));
}
return (RdataPtr(new generic::Generic(lexer, name, options, callbacks)));
}
}
}
......@@ -82,7 +82,7 @@ public:
/// \name Factory methods for polymorphic creation.
///
//@{
///
/// \brief Create RDATA from a string.
///
/// This method creates from a string an \c Rdata object of specific class
......@@ -91,7 +91,7 @@ public:
/// \param rdata_str A string of textual representation of the \c Rdata.
/// \return An \c RdataPtr object pointing to the created \c Rdata object.