Commit 938ab8a3 authored by Stephen Morris's avatar Stephen Morris
Browse files

[2472] Merge branch 'master' into trac2472

parents f57c0b95 e4fe372e
506. [doc] jelte
Added a chapter about the use of the bindctl command tool to
to the BIND 10 guide.
(Trac #2305, git c4b0294b5bf4a9d32fb18ab62ca572f492788d72)
505. [bug] jelte
Fixed a bug in b10-xfrin where a wrong call was made during the
final check of a TSIG-signed transfer, incorrectly rejecting the
......
......@@ -767,7 +767,7 @@ if test "$MYSQL_CONFIG" != "" ; then
LIBS="$MYSQL_LIBS $LIBS"
AC_LINK_IFELSE(
[AC_LANG_PROGRAM([#include <mysql/mysql.h>],
[AC_LANG_PROGRAM([#include <mysql.h>],
[MYSQL mysql_handle;
(void) mysql_init(&mysql_handle);
])],
......
This diff is collapsed.
......@@ -98,6 +98,16 @@ This debug message is issued when the separate thread for maintaining data
source clients successfully loaded the named zone of the named class as a
result of the 'loadzone' command.
% AUTH_DATASRC_CLIENTS_BUILDER_LOAD_ZONE_NOCACHE skipped loading zone %1/%2 due to no in-memory cache
This debug message is issued when the separate thread for maintaining data
source clients received a command to reload a zone but skipped it because
the specified zone is not loaded in-memory (but served from an underlying
data source). This could happen if the loadzone command is manually issued
by a user but the zone name is misspelled, but a more likely cause is
that the command is sent from another BIND 10 module (such as xfrin or DDNS).
In the latter case it can be simply ignored because there is no need
for explicit reloading.
% AUTH_DATASRC_CLIENTS_BUILDER_RECONFIGURE_CONFIG_ERROR Error in data source configuration: %1
The thread for maintaining data source clients has received a command to
reconfigure, but the parameter data (the new configuration) contains an
......
......@@ -651,9 +651,10 @@ AuthSrvImpl::processNormalQuery(const IOMessage& io_message, Message& message,
local_edns->setUDPSize(AuthSrvImpl::DEFAULT_LOCAL_UDPSIZE);
message.setEDNS(local_edns);
}
// Get access to data source client list through the holder and keep the
// holder until the processing and rendering is done to avoid inter-thread
// race.
// Get access to data source client list through the holder and keep
// the holder until the processing and rendering is done to avoid
// race with any other thread(s) such as the background loader.
auth::DataSrcClientsMgr::Holder datasrc_holder(datasrc_clients_mgr_);
try {
......@@ -688,6 +689,9 @@ AuthSrvImpl::processNormalQuery(const IOMessage& io_message, Message& message,
return (true);
// The message can contain some data from the locked resource. But outside
// this method, we touch only the RCode of it, so it should be safe.
// Lock on datasrc_clients_mgr_ acquired by datasrc_holder is
// released here upon its deletion.
}
bool
......
......@@ -581,6 +581,9 @@ DataSrcClientsBuilderBase<MutexType, CondVarType>::doLoadZone(
try {
boost::shared_ptr<datasrc::memory::ZoneWriter> zwriter =
getZoneWriter(*client_list, rrclass, origin);
if (!zwriter) {
return;
}
zwriter->load(); // this can take time but doesn't cause a race
{ // install() can cause a race and must be in a critical section
......@@ -614,8 +617,14 @@ DataSrcClientsBuilderBase<MutexType, CondVarType>::getZoneWriter(
datasrc::ConfigurableClientList& client_list,
const dns::RRClass& rrclass, const dns::Name& origin)
{
const datasrc::ConfigurableClientList::ZoneWriterPair writerpair =
client_list.getCachedZoneWriter(origin);
// getCachedZoneWriter() could get access to an underlying data source
// that can cause a race condition with the main thread using that data
// source for lookup. So we need to protect the access here.
datasrc::ConfigurableClientList::ZoneWriterPair writerpair;
{
typename MutexType::Locker locker(*map_mutex_);
writerpair = client_list.getCachedZoneWriter(origin);
}
switch (writerpair.first) {
case datasrc::ConfigurableClientList::ZONE_SUCCESS:
......@@ -626,8 +635,10 @@ DataSrcClientsBuilderBase<MutexType, CondVarType>::getZoneWriter(
<< "/" << rrclass << ": not found in any configured "
"data source.");
case datasrc::ConfigurableClientList::ZONE_NOT_CACHED:
isc_throw(InternalCommandError, "failed to load zone " << origin
<< "/" << rrclass << ": not served from memory");
LOG_DEBUG(auth_logger, DBG_AUTH_OPS,
AUTH_DATASRC_CLIENTS_BUILDER_LOAD_ZONE_NOCACHE)
.arg(origin).arg(rrclass);
break; // return NULL below
case datasrc::ConfigurableClientList::CACHE_DISABLED:
// This is an internal error. Auth server must have the cache
// enabled.
......@@ -636,8 +647,6 @@ DataSrcClientsBuilderBase<MutexType, CondVarType>::getZoneWriter(
"is somehow disabled");
}
// all cases above should either return or throw, but some compilers
// still need a return statement
return (boost::shared_ptr<datasrc::memory::ZoneWriter>());
}
} // namespace datasrc_clientmgr_internal
......
......@@ -308,8 +308,12 @@ TEST_F(DataSrcClientsBuilderTest, loadZone) {
"{\"class\": \"IN\","
" \"origin\": \"test1.example\"}"));
EXPECT_TRUE(builder.handleCommand(loadzone_cmd));
EXPECT_EQ(1, map_mutex.lock_count); // we should have acquired the lock
EXPECT_EQ(1, map_mutex.unlock_count); // and released it.
// loadZone involves two critical sections: one for getting the zone
// writer, and one for actually updating the zone data. So the lock/unlock
// count should be incremented by 2.
EXPECT_EQ(2, map_mutex.lock_count);
EXPECT_EQ(2, map_mutex.unlock_count);
newZoneChecks(clients_map, rrclass);
}
......@@ -381,7 +385,10 @@ TEST_F(DataSrcClientsBuilderTest,
find(Name("example.org")).finder_->
find(Name("example.org"), RRType::SOA())->code);
// attempt of reloading a zone but in-memory cache is disabled.
// attempt of reloading a zone but in-memory cache is disabled. In this
// case the command is simply ignored.
const size_t orig_lock_count = map_mutex.lock_count;
const size_t orig_unlock_count = map_mutex.unlock_count;
const ConstElementPtr config2(Element::fromJSON("{"
"\"IN\": [{"
" \"type\": \"sqlite3\","
......@@ -390,11 +397,13 @@ TEST_F(DataSrcClientsBuilderTest,
" \"cache-zones\": [\"example.org\"]"
"}]}"));
clients_map = configureDataSource(config2);
EXPECT_THROW(builder.handleCommand(
builder.handleCommand(
Command(LOADZONE, Element::fromJSON(
"{\"class\": \"IN\","
" \"origin\": \"example.org\"}"))),
TestDataSrcClientsBuilder::InternalCommandError);
" \"origin\": \"example.org\"}")));
// Only one mutex was needed because there was no actual reload.
EXPECT_EQ(orig_lock_count + 1, map_mutex.lock_count);
EXPECT_EQ(orig_unlock_count + 1, map_mutex.unlock_count);
// basically impossible case: in-memory cache is completely disabled.
// In this implementation of manager-builder, this should never happen,
......
......@@ -221,7 +221,8 @@ class ModuleInfo:
def module_help(self):
"""Prints the help info for this module to stdout"""
print("Module ", self, "\nAvailable commands:")
print("Module " + str(self))
print("Available commands:")
for k in self.commands.values():
n = k.get_name()
if len(n) >= CONST_BINDCTL_HELP_INDENT_WIDTH:
......
......@@ -4,9 +4,6 @@ AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
AM_CPPFLAGS += $(BOOST_INCLUDES)
AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/lib/dhcp/tests\"
AM_CPPFLAGS += -DINSTALL_PROG=\"$(abs_top_srcdir)/install-sh\"
# Temp
AM_CPPFLAGS += -I/usr/include/mysql -DBIG_JOINS=1 -fno-strict-aliasing
AM_CPPFLAGS += -DUNIV_LINUX
AM_CXXFLAGS = $(B10_CXXFLAGS)
......
......@@ -97,6 +97,7 @@ libb10_dns___la_SOURCES += master_lexer_inputsource.h master_lexer_inputsource.c
libb10_dns___la_SOURCES += labelsequence.h labelsequence.cc
libb10_dns___la_SOURCES += masterload.h masterload.cc
libb10_dns___la_SOURCES += master_lexer.h master_lexer.cc
libb10_dns___la_SOURCES += master_lexer_state.h
libb10_dns___la_SOURCES += message.h message.cc
libb10_dns___la_SOURCES += messagerenderer.h messagerenderer.cc
libb10_dns___la_SOURCES += name.h name.cc
......
......@@ -16,6 +16,7 @@
#include <dns/master_lexer.h>
#include <dns/master_lexer_inputsource.h>
#include <dns/master_lexer_state.h>
#include <boost/shared_ptr.hpp>
......@@ -32,10 +33,34 @@ typedef boost::shared_ptr<master_lexer_internal::InputSource> InputSourcePtr;
using namespace master_lexer_internal;
struct MasterLexer::MasterLexerImpl {
MasterLexerImpl() : token_(Token::NOT_STARTED) {}
MasterLexerImpl() : source_(NULL), token_(Token::NOT_STARTED),
paren_count_(0), last_was_eol_(false)
{}
// A helper method to skip possible comments toward the end of EOL or EOF.
// commonly used by state classes. It returns the corresponding "end-of"
// character in case it's a comment; otherwise it simply returns the
// current character.
int skipComment(int c) {
if (c == ';') {
while (true) {
c = source_->getChar();
if (c == '\n' || c == InputSource::END_OF_STREAM) {
return (c);
}
}
}
return (c);
}
std::vector<InputSourcePtr> sources_;
Token token_;
InputSource* source_; // current source (NULL if sources_ is empty)
Token token_; // currently recognized token (set by a state)
// These are used in states, and defined here only as a placeholder.
// The main lexer class does not need these members.
size_t paren_count_; // nest count of the parentheses
bool last_was_eol_; // whether the lexer just passed an end-of-line
};
MasterLexer::MasterLexer() : impl_(new MasterLexerImpl) {
......@@ -60,12 +85,14 @@ MasterLexer::pushSource(const char* filename, std::string* error) {
return (false);
}
impl_->source_ = impl_->sources_.back().get();
return (true);
}
void
MasterLexer::pushSource(std::istream& input) {
impl_->sources_.push_back(InputSourcePtr(new InputSource(input)));
impl_->source_ = impl_->sources_.back().get();
}
void
......@@ -75,6 +102,8 @@ MasterLexer::popSource() {
"MasterLexer::popSource on an empty source");
}
impl_->sources_.pop_back();
impl_->source_ = impl_->sources_.empty() ? NULL :
impl_->sources_.back().get();
}
std::string
......@@ -115,5 +144,144 @@ MasterLexer::Token::getErrorText() const {
return (error_text[val_.error_code_]);
}
namespace master_lexer_internal {
// Below we implement state classes for state transitions of MasterLexer.
// Note that these need to be defined here so that they can refer to
// the details of MasterLexerImpl.
typedef MasterLexer::Token Token; // convenience shortcut
bool
State::wasLastEOL(const MasterLexer& lexer) const {
return (lexer.impl_->last_was_eol_);
}
const MasterLexer::Token&
State::getToken(const MasterLexer& lexer) const {
return (lexer.impl_->token_);
}
size_t
State::getParenCount(const MasterLexer& lexer) const {
return (lexer.impl_->paren_count_);
}
namespace {
class CRLF : public State {
public:
CRLF() {}
virtual ~CRLF() {} // see the base class for the destructor
virtual const State* handle(MasterLexer& lexer) const {
// We've just seen '\r'. If this is part of a sequence of '\r\n',
// we combine them as a single END-OF-LINE. Otherwise we treat the
// single '\r' as an EOL and continue tokeniziation from the character
// immediately after '\r'. One tricky case is that there's a comment
// between '\r' and '\n'. This implementation combines these
// characters and treats them as a single EOL (the behavior derived
// from BIND 9). Technically this may not be correct, but in practice
// the caller wouldn't distinguish this case from the case it has
// two EOLs, so we simplify the process.
const int c = getLexerImpl(lexer)->skipComment(
getLexerImpl(lexer)->source_->getChar());
if (c != '\n') {
getLexerImpl(lexer)->source_->ungetChar();
}
getLexerImpl(lexer)->token_ = Token(Token::END_OF_LINE);
getLexerImpl(lexer)->last_was_eol_ = true;
return (NULL);
}
};
// Currently this is provided mostly as a place holder
class String : public State {
public:
String() {}
virtual ~String() {} // see the base class for the destructor
virtual const State* handle(MasterLexer& /*lexer*/) const {
return (NULL);
}
};
// We use a common instance of a each state in a singleton-like way to save
// construction overhead. They are not singletons in its strict sense as
// we don't prohibit direct construction of these objects. But that doesn't
// matter much anyway, because the definitions are completely hidden within
// this file.
const CRLF CRLF_STATE;
const String STRING_STATE;
}
const State&
State::getInstance(ID state_id) {
switch (state_id) {
case CRLF:
return (CRLF_STATE);
case String:
return (STRING_STATE);
}
// This is a bug of the caller, and this method is only expected to be
// used by tests, so we just forcefully make it fail by asserting the
// condition.
assert(false);
return (STRING_STATE); // a dummy return, to silence some compilers.
}
const State*
State::start(MasterLexer& lexer, MasterLexer::Options options) {
// define some shortcuts
MasterLexer::MasterLexerImpl& lexerimpl = *lexer.impl_;
size_t& paren_count = lexerimpl.paren_count_;
while (true) {
const int c = lexerimpl.skipComment(lexerimpl.source_->getChar());
if (c == InputSource::END_OF_STREAM) {
lexerimpl.last_was_eol_ = false;
if (paren_count != 0) {
lexerimpl.token_ = Token(Token::UNBALANCED_PAREN);
paren_count = 0; // reset to 0; this helps in lenient mode.
return (NULL);
}
lexerimpl.token_ = Token(Token::END_OF_FILE);
return (NULL);
} else if (c == ' ' || c == '\t') {
// If requested and we are not in (), recognize the initial space.
if (lexerimpl.last_was_eol_ && paren_count == 0 &&
(options & MasterLexer::INITIAL_WS) != 0) {
lexerimpl.last_was_eol_ = false;
lexerimpl.token_ = Token(Token::INITIAL_WS);
return (NULL);
}
} else if (c == '\n') {
lexerimpl.last_was_eol_ = true;
if (paren_count == 0) { // we don't recognize EOL if we are in ()
lexerimpl.token_ = Token(Token::END_OF_LINE);
return (NULL);
}
} else if (c == '\r') {
if (paren_count == 0) { // check if we are in () (see above)
return (&CRLF_STATE);
}
} else if (c == '(') {
lexerimpl.last_was_eol_ = false;
++paren_count;
} else if (c == ')') {
lexerimpl.last_was_eol_ = false;
if (paren_count == 0) {
lexerimpl.token_ = Token(Token::UNBALANCED_PAREN);
return (NULL);
}
--paren_count;
} else {
// Note: in #2373 we should probably ungetChar().
lexerimpl.last_was_eol_ = false;
return (&STRING_STATE);
}
// no code should be here; we just continue the loop.
}
}
} // namespace master_lexer_internal
} // end of namespace dns
} // end of namespace isc
......@@ -24,6 +24,9 @@
namespace isc {
namespace dns {
namespace master_lexer_internal {
class State;
}
/// \brief Tokenizer for parsing DNS master files.
///
......@@ -64,9 +67,22 @@ namespace dns {
/// this class does not throw for an error that would be reported as an
/// exception in other classes.
class MasterLexer {
friend class master_lexer_internal::State;
public:
class Token; // we define it separately for better readability
/// \brief Options for getNextToken.
///
/// A compound option, indicating multiple options are set, can be
/// specified using the logical OR operator (operator|()).
enum Options {
NONE = 0, ///< No option
INITIAL_WS = 1, ///< recognize begin-of-line spaces after an
///< end-of-line
QSTRING = 2, ///< recognize quoted string
NUMBER = 4 ///< recognize numeric text as integer
};
/// \brief The constructor.
///
/// \throw std::bad_alloc Internal resource allocation fails (rare case).
......@@ -167,6 +183,16 @@ private:
MasterLexerImpl* impl_;
};
/// \brief Operator to combine \c MasterLexer options
///
/// This is a trivial shortcut so that compound options can be specified
/// in an intuitive way.
inline MasterLexer::Options
operator|(MasterLexer::Options o1, MasterLexer::Options o2) {
return (static_cast<MasterLexer::Options>(
static_cast<unsigned>(o1) | static_cast<unsigned>(o2)));
}
/// \brief Tokens for \c MasterLexer
///
/// This is a simple value-class encapsulating a type of a lexer token and
......@@ -192,7 +218,8 @@ public:
enum Type {
END_OF_LINE, ///< End of line detected (if asked for detecting it)
END_OF_FILE, ///< End of file detected (if asked for detecting it)
INITIAL_WS, ///< White spaces at the beginning of a line
INITIAL_WS, ///< White spaces at the beginning of a line after an
///< end of line
NOVALUE_TYPE_MAX = INITIAL_WS, ///< Max integer corresponding to
/// no-value (type only) types.
/// Mainly for internal use.
......
// 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_LEXER_STATE_H
#define MASTER_LEXER_STATE_H 1
#include <dns/master_lexer.h>
namespace isc {
namespace dns {
namespace master_lexer_internal {
/// \brief Tokenization state for \c MasterLexer.
///
/// This is a base class of classes that represent various states of a single
/// tokenization session of \c MasterLexer, i.e., the states used for a
/// single call to \c MasterLexer::getNextToken().
///
/// It follows the convention of the state design pattern: each derived class
/// corresponds to a specific state, and the state transition takes place
/// through the virtual method named \c handle(). The \c handle() method
/// takes the main \c MasterLexer object that holds all necessary internal
/// context, and updates it as necessary; each \c State derived class is
/// completely stateless.
///
/// The initial transition takes place in a static method of the base class,
/// \c start(). This is mainly for implementation convenience; we need to
/// pass options given to \c MasterLexer::getNextToken() for the initial
/// state, so it makes more sense to separate the interface for the transition
/// from the initial state.
///
/// When an object of a specific state class completes the session, it
/// normally sets the identified token in the lexer, and returns NULL;
/// if more transition is necessary, it returns a pointer to the next state
/// object.
///
/// As is usual in the state design pattern, the \c State class is made
/// a friend class of \c MasterLexer and can refer to its internal details.
/// This is intentional; essentially its a part of \c MasterLexer and
/// is defined as a separate class only for implementation clarity and better
/// testability. It's defined in a publicly visible header, but that's only
/// for testing purposes. No normal application or even no other classes of
/// this library are expected to use this class.
class State {
public:
/// \brief Virtual destructor.
///
/// In our usage this actually doesn't matter, but some compilers complain
/// about it and we need to silence them.
virtual ~State() {}
/// \brief Begin state transitions to get the next token.
///
/// This is the first method that \c MasterLexer needs to call for a
/// tokenization session. The lexer passes a reference to itself
/// and options given in \c getNextToken().
///
/// \throw InputSource::ReadError Unexpected I/O error
/// \throw std::bad_alloc Internal resource allocation failure
///
/// \param lexer The lexer object that holds the main context.
/// \param options The options passed to getNextToken().
/// \return A pointer to the next state object or NULL if the transition
/// is completed.
static const State* start(MasterLexer& lexer,
MasterLexer::Options options);
/// \brief Handle the process of one specific state.
///
/// This method is expected to be called on the object returned by
/// start(), and keep called on the returned object until NULL is
/// returned. The call chain will form the complete state transition.
///
/// \throw InputSource::ReadError Unexpected I/O error
/// \throw std::bad_alloc Internal resource allocation failure
///
/// \param lexer The lexer object that holds the main context.
/// \return A pointer to the next state object or NULL if the transition
/// is completed.
virtual const State* handle(MasterLexer& lexer) const = 0;
/// \brief Types of states.
///
/// Specific states are basically hidden within the implementation,
/// but we'd like to allow tests to examine them, so we provide
/// a way to get an instance of a specific state.
enum ID {
CRLF, ///< Just seen a carriage-return character
String ///< Handling a string token
};
/// \brief Returns a \c State instance of the given state.
///
/// This is provided only for testing purposes so tests can check
/// the behavior of each state separately. \c MasterLexer shouldn't
/// need this method.
static const State& getInstance(ID state_id);
/// \name Read-only accessors for testing purposes.
///
/// These allow tests to inspect some selected portion of the internal
/// states of \c MasterLexer. These shouldn't be used except for testing
/// purposes.
///@{
bool wasLastEOL(const MasterLexer& lexer) const;
const MasterLexer::Token& getToken(const MasterLexer& lexer) const;
size_t getParenCount(const MasterLexer& lexer) const;
///@}
protected:
/// \brief An accessor to the internal implementation class of
/// \c MasterLexer.
///
/// This is provided for specific derived classes as they are not direct
/// friends of \c MasterLexer.
///
/// \param lexer The lexer object that holds the main context.
/// \return A pointer to the implementation class object of the given
/// lexer. This is never NULL.
MasterLexer::MasterLexerImpl* getLexerImpl(MasterLexer& lexer) const {
return (lexer.impl_);
}
};
} // namespace master_lexer_internal
} // namespace dns
} // namespace isc
#endif // MASTER_LEXER_STATE_H
// Local Variables:
// mode: c++
// End:
......@@ -27,6 +27,7 @@ run_unittests_SOURCES += labelsequence_unittest.cc
run_unittests_SOURCES += messagerenderer_unittest.cc
run_unittests_SOURCES += master_lexer_token_unittest.cc
run_unittests_SOURCES += master_lexer_unittest.cc
run_unittests_SOURCES += master_lexer_state_unittest.cc
run_unittests_SOURCES += name_unittest.cc
run_unittests_SOURCES += nsec3hash_unittest.cc
run_unittests_SOURCES += rrclass_unittest.cc rrtype_unittest.cc
......
// 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