Commit 681cb466 authored by chenzhengzhang's avatar chenzhengzhang
Browse files

Merge branch 'master' into trac466

parents a48a734b 2e4b0a5c
147. [bug] jinmei
python/isc/config: Fixed a bug that importing custom configuration
(in b10-config.db) of a remote module didn't work.
(Trac #478, git ea4a481)
146. [func] jelte
Command arguments were not validated internally against their
specifications. This change fixes that (on the C++ side, Python
side depends on an as yet planned addition). Note: this is only
an added internal check, the cli already checks format.
(Trac #473, git 5474eba181cb2fdd80e2b2200e072cd0a13a4e52)
145. [func]* jinmei
b10-auth: added a new command 'loadzone' for (re)loading a
specific zone. The command syntax is generic but it is currently
only feasible for class IN in memory data source. To reload a
zone "example.com" via bindctl, execute the command as follows:
> Auth loadzone origin = example.com
(Trac #467)
144. [build] jinmei
Introduced a workaround for clang++ build on FreeBSD (and probably
some other OSes). If building BIND 10 fails with clang++ due to
a link error about "__dso_handle", try again from the configure
script with CXX_LIBTOOL_LDFLAGS=-L/usr/lib (the path actually
doesn't matter; the important part is the -L flag). This
workaround is not automatically enabled as it's difficult to
detect the need for it dynamically, and must be enabled via the
variable by hand. (Trac #474, git cfde436)
143. [build] jinmei
Fixed build problems with clang++ in unit tests due to recent
changes. No behavior change. (Trac #448, svn r4133)
......@@ -781,8 +811,12 @@ bind10-devel-20100421 released on April 21, 2010
bind10-devel-20100319 released on March 19, 2010
For complete code revision history, see http://bind10.isc.org/browser
Specific subversion changesets can be accessed at:
http://bind10.isc.org/changeset/rrrr
Specific git changesets can be accessed at:
http://bind10.isc.org/changeset/?reponame=&old=rrrr^&new=rrrr
or after cloning the original git repository by executing:
% git diff rrrr^ rrrr
Subversion changesets are not accessible any more. The subversion
revision numbers will be replaced with corresponding git revisions.
Trac tickets can be accessed at: https://bind10.isc.org/ticket/nnn
LEGEND
......
......@@ -9,7 +9,23 @@ AC_CONFIG_HEADERS([config.h])
# Checks for programs.
AC_PROG_CXX
# Libtool configuration
#
# On FreeBSD (and probably some others), clang++ does not meet an autoconf
# assumption in identifying libtool configuration regarding shared library:
# the configure script will execute "$CC -shared $CFLAGS -v -o" and expect
# the output contains -Lxxx or -Ryyy. This is the case for g++, but not for
# clang++, and, as a result, it will cause various errors in linking programs
# or running them with a shared object (such as some of our python scripts).
# To work around this problem we define a temporary variable
# "CXX_LIBTOOL_LDFLAGS". It's expected to be defined as, e.g, "-L/usr/lib"
# to temporarily fake the output so that it will be compatible with that of
# g++.
CFLAGS_SAVED=$CFLAGS
CFLAGS="$CFLAGS $CXX_LIBTOOL_LDFLAGS"
AC_PROG_LIBTOOL
CFLAGS=$CFLAGS_SAVED
# Use C++ language
AC_LANG([C++])
......
......@@ -40,6 +40,7 @@ b10_auth_SOURCES = query.cc query.h
b10_auth_SOURCES += auth_srv.cc auth_srv.h
b10_auth_SOURCES += change_user.cc change_user.h
b10_auth_SOURCES += config.cc config.h
b10_auth_SOURCES += command.cc command.h
b10_auth_SOURCES += common.h
b10_auth_SOURCES += statistics.cc statistics.h
b10_auth_SOURCES += main.cc
......
......@@ -64,8 +64,25 @@
"command_name": "sendstats",
"command_description": "Send data to a statistics module at once",
"command_args": []
},
{
"command_name": "loadzone",
"command_description": "(Re)load a specified zone",
"command_args": [
{
"item_name": "class", "item_type": "string",
"item_optional": true, "item_default": "IN"
},
{
"item_name": "origin", "item_type": "string",
"item_optional": false, "item_default": ""
},
{
"item_name": "datasrc", "item_type": "string",
"item_optional": true, "item_default": "memory"
}
]
}
]
}
}
......@@ -195,11 +195,20 @@ private:
AuthSrv::AuthSrv(const bool use_cache, AbstractXfroutClient& xfrout_client) :
impl_(new AuthSrvImpl(use_cache, xfrout_client)),
io_service_(NULL),
checkin_(new ConfigChecker(this)),
dns_lookup_(new MessageLookup(this)),
dns_answer_(new MessageAnswer(this))
{}
void
AuthSrv::stop() {
if (io_service_ == NULL) {
throw FatalError("Assumption failure; server is stopped before start");
}
io_service_->stop();
}
AuthSrv::~AuthSrv() {
delete impl_;
delete checkin_;
......@@ -299,8 +308,8 @@ AuthSrv::getConfigSession() const {
return (impl_->config_session_);
}
AuthSrv::ConstMemoryDataSrcPtr
AuthSrv::getMemoryDataSrc(const RRClass& rrclass) const {
AuthSrv::MemoryDataSrcPtr
AuthSrv::getMemoryDataSrc(const RRClass& rrclass) {
// XXX: for simplicity, we only support the IN class right now.
if (rrclass != impl_->memory_datasrc_class_) {
isc_throw(InvalidParameter,
......
......@@ -87,6 +87,15 @@ public:
~AuthSrv();
//@}
/// Stop the server.
///
/// It stops the internal event loop of the server and subsequently
/// returns the control to the top level context.
/// The server must have been associated with an \c IOService object;
/// otherwise an exception of \c FatalError will be thrown.
/// This method never throws an exception otherwise.
void stop();
/// \brief Process an incoming DNS message, then signal 'server' to resume
///
/// A DNS query (or other message) has been received by a \c DNSServer
......@@ -265,8 +274,7 @@ public:
/// \param rrclass The RR class of the requested in-memory data source.
/// \return A pointer to the in-memory data source, if configured;
/// otherwise NULL.
ConstMemoryDataSrcPtr
getMemoryDataSrc(const isc::dns::RRClass& rrclass) const;
MemoryDataSrcPtr getMemoryDataSrc(const isc::dns::RRClass& rrclass);
/// Sets or replaces the in-memory data source of the specified RR class.
///
......
// Copyright (C) 2010 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.
#include <string>
#include <boost/scoped_ptr.hpp>
#include <boost/shared_ptr.hpp>
#include <exceptions/exceptions.h>
#include <dns/rrclass.h>
#include <cc/data.h>
#include <datasrc/memory_datasrc.h>
#include <config/ccsession.h>
#include <auth/auth_srv.h>
#include <auth/command.h>
using namespace std;
using boost::shared_ptr;
using boost::scoped_ptr;
using namespace isc::dns;
using namespace isc::data;
using namespace isc::datasrc;
using namespace isc::config;
namespace {
/// An exception that is thrown if an error occurs while handling a command
/// on an \c AuthSrv object.
///
/// Currently it's only used internally, since \c execAuthServerCommand()
/// (which is the only interface to this module) catches all \c isc::
/// exceptions and converts them.
class AuthCommandError : public isc::Exception {
public:
AuthCommandError(const char* file, size_t line, const char* what) :
isc::Exception(file, line, what) {}
};
/// An abstract base class that represents a single command identifier
/// for an \c AuthSrv object.
///
/// Each of derived classes of \c AuthCommand, which are hidden inside the
/// implementation, corresponds to a command executed on \c AuthSrv, such as
/// "shutdown". The derived class is responsible to execute the corresponding
/// command with the given command arguments (if any) in its \c exec()
/// method.
///
/// In the initial implementation the existence of the command classes is
/// hidden inside the implementation since the only public interface is
/// \c execAuthServerCommand(), which does not expose this class.
/// In future, we may want to make this framework more dynamic, i.e.,
/// registering specific derived classes run time outside of this
/// implementation. If and when that happens the definition of the abstract
/// class will be published.
class AuthCommand {
///
/// \name Constructors and Destructor
///
/// Note: The copy constructor and the assignment operator are
/// intentionally defined as private to make it explicit that this is a
/// pure base class.
//@{
private:
AuthCommand(const AuthCommand& source);
AuthCommand& operator=(const AuthCommand& source);
protected:
/// \brief The default constructor.
///
/// This is intentionally defined as \c protected as this base class should
/// never be instantiated (except as part of a derived class).
AuthCommand() {}
public:
/// The destructor.
virtual ~AuthCommand() {}
//@}
/// Execute a single control command.
///
/// Specific derived methods can throw exceptions. When called via
/// \c execAuthServerCommand(), all BIND 10 exceptions are caught
/// and converted into an error code.
/// The derived method may also throw an exception of class
/// \c AuthCommandError when it encounters an internal error, such as
/// semantics error on the command arguments.
///
/// \param server The \c AuthSrv object on which the command is executed.
/// \param args Command specific argument.
virtual void exec(AuthSrv& server, isc::data::ConstElementPtr args) = 0;
};
// Handle the "shutdown" command. No argument is assumed.
class ShutdownCommand : public AuthCommand {
public:
virtual void exec(AuthSrv& server, isc::data::ConstElementPtr) {
server.stop();
}
};
// Handle the "sendstats" command. No argument is assumed.
class SendStatsCommand : public AuthCommand {
public:
virtual void exec(AuthSrv& server, isc::data::ConstElementPtr) {
if (server.getVerbose()) {
cerr << "[b10-auth] command 'sendstats' received" << endl;
}
server.submitStatistics();
}
};
// Handle the "loadzone" command.
class LoadZoneCommand : public AuthCommand {
public:
virtual void exec(AuthSrv& server, isc::data::ConstElementPtr args) {
// parse and validate the args.
if (!validate(server, args)) {
return;
}
// Load a new zone and replace the current zone with the new one.
// TODO: eventually this should be incremental or done in some way
// that doesn't block other server operations.
// TODO: we may (should?) want to check the "last load time" and
// the timestamp of the file and skip loading if the file isn't newer.
shared_ptr<MemoryZone> newzone(new MemoryZone(oldzone->getClass(),
oldzone->getOrigin()));
newzone->load(oldzone->getFileName());
oldzone->swap(*newzone);
if (server.getVerbose()) {
cerr << "[b10-auth] Loaded zone '" << newzone->getOrigin()
<< "'/" << newzone->getClass() << endl;
}
}
private:
shared_ptr<MemoryZone> oldzone; // zone to be updated with the new file.
// A helper private method to parse and validate command parameters.
// On success, it sets 'oldzone' to the zone to be updated.
// It returns true if everything is okay; and false if the command is
// valid but there's no need for further process.
bool validate(AuthSrv& server, isc::data::ConstElementPtr args) {
if (args == NULL) {
isc_throw(AuthCommandError, "Null argument");
}
// In this initial implementation, we assume memory data source
// for class IN by default.
ConstElementPtr datasrc_elem = args->get("datasrc");
if (datasrc_elem) {
if (datasrc_elem->stringValue() == "sqlite3") {
if (server.getVerbose()) {
cerr << "[b10-auth] Nothing to do for loading sqlite3"
<< endl;
}
return (false);
} else if (datasrc_elem->stringValue() != "memory") {
// (note: at this point it's guaranteed that datasrc_elem
// is of string type)
isc_throw(AuthCommandError,
"Data source type " << datasrc_elem->stringValue()
<< " is not supported");
}
}
ConstElementPtr class_elem = args->get("class");
const RRClass zone_class = class_elem ?
RRClass(class_elem->stringValue()) : RRClass::IN();
AuthSrv::MemoryDataSrcPtr datasrc(server.getMemoryDataSrc(zone_class));
if (datasrc == NULL) {
isc_throw(AuthCommandError, "Memory data source is disabled");
}
ConstElementPtr origin_elem = args->get("origin");
if (!origin_elem) {
isc_throw(AuthCommandError, "Zone origin is missing");
}
const Name origin(origin_elem->stringValue());
// Get the current zone
const MemoryDataSrc::FindResult result = datasrc->findZone(origin);
if (result.code != result::SUCCESS) {
isc_throw(AuthCommandError, "Zone " << origin <<
" is not found in data source");
}
oldzone = boost::dynamic_pointer_cast<MemoryZone>(result.zone);
return (true);
}
};
// The factory of command objects.
AuthCommand*
createAuthCommand(const string& command_id) {
// For the initial implementation we use a naive if-else blocks
// (see also createAuthConfigParser())
if (command_id == "shutdown") {
return (new ShutdownCommand());
} else if (command_id == "sendstats") {
return (new SendStatsCommand());
} else if (command_id == "loadzone") {
return (new LoadZoneCommand());
} else if (false && command_id == "_throw_exception") {
// This is for testing purpose only and should not appear in the
// actual configuration syntax.
// XXX: ModuleCCSession doesn't seem to validate commands (unlike
// config), so we should disable this case for now.
throw runtime_error("throwing for test");
}
isc_throw(AuthCommandError, "Unknown command identifier: " << command_id);
}
} // end of unnamed namespace
ConstElementPtr
execAuthServerCommand(AuthSrv& server, const string& command_id,
ConstElementPtr args)
{
if (server.getVerbose()) {
cerr << "[b10-auth] Received '" << command_id << "' command" << endl;
}
try {
scoped_ptr<AuthCommand>(createAuthCommand(command_id))->exec(server,
args);
} catch (const isc::Exception& ex) {
if (server.getVerbose()) {
cerr << "[b10-auth] Command '" << command_id
<< "' execution failed: " << ex.what() << endl;
}
return (createAnswer(1, ex.what()));
}
return (createAnswer());
}
// Copyright (C) 2010 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.
#include <string>
#include <cc/data.h>
#ifndef __COMMAND_H
#define __COMMAND_H 1
class AuthSrv;
/// Execute a control command on \c AuthSrv
///
/// It executes the operation identified by \c command_id with arguments
/// \c args on \c server, and returns the result in the form of the standard
/// command/config answer message (see \c isc::config::createAnswer()).
///
/// This method internally performs minimal validation on \c command_id and
/// \c args to not cause a surprising result such as a crash, but it is
/// generally expected that the caller has performed syntax level validation
/// based on the command specification for the authoritative server.
/// For example, the caller is responsible \c command_id is a valid command
/// name for the authoritative server.
///
/// The execution of the command may internally trigger an exception; for
/// instance, if a string that is expected to be a valid domain name is bogus,
/// the underlying DNS library will throw an exception. These internal
/// exceptions will be caught inside this function, and will be converted
/// to a return value with a non 0 error code.
/// However, exceptions thrown outside of BIND 10 modules (including standard
/// exceptions) are expected to be handled at a higher layer, and will be
/// propagated to the caller. In particular if memory allocation fails and
/// \c std::bad_alloc is thrown it will be propagated.
///
/// \param server The \c AuthSrv object on which the command is executed.
/// \param command_id The identifier of the command (such as "shutdown")
/// \param args Command specific argument in a map type of
/// \c isc::data::Element, or a \c NULL \c ElementPtr if the argument isn't
/// specified.
/// \return The result of the command operation.
isc::data::ConstElementPtr
execAuthServerCommand(AuthSrv& server, const std::string& command_id,
isc::data::ConstElementPtr args);
#endif // __COMMAND_H
// Local Variables:
// mode: c++
// End:
......@@ -42,6 +42,7 @@
#include <auth/spec_config.h>
#include <auth/common.h>
#include <auth/config.h>
#include <auth/command.h>
#include <auth/change_user.h>
#include <auth/auth_srv.h>
#include <asiolink/asiolink.h>
......@@ -80,23 +81,8 @@ my_config_handler(ConstElementPtr new_config) {
ConstElementPtr
my_command_handler(const string& command, ConstElementPtr args) {
ConstElementPtr answer = createAnswer();
if (command == "print_message") {
cout << args << endl;
/* let's add that message to our answer as well */
answer = createAnswer(0, args);
} else if (command == "shutdown") {
io_service.stop();
} else if (command == "sendstats") {
if (verbose_mode) {
cerr << "[b10-auth] command 'sendstats' received" << endl;
}
assert(auth_server != NULL);
auth_server->submitStatistics();
}
return (answer);
assert(auth_server != NULL);
return (execAuthServerCommand(*auth_server, command, args));
}
void
......
......@@ -18,6 +18,7 @@
#define __STATISTICS_H 1
#include <cc/session.h>
#include <stdint.h>
class AuthCountersImpl;
......
......@@ -2,8 +2,9 @@ AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CPPFLAGS += -I$(top_builddir)/src/lib/dns -I$(top_srcdir)/src/bin
AM_CPPFLAGS += -I$(top_builddir)/src/lib/cc
AM_CPPFLAGS += $(BOOST_INCLUDES)
AM_CPPFLAGS += -DTEST_DATA_DIR=\"$(top_srcdir)/src/lib/testutils/testdata\"
AM_CPPFLAGS += -DTEST_DATA_DIR=\"$(abs_top_srcdir)/src/lib/testutils/testdata\"
AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/lib/testutils/testdata\"
AM_CPPFLAGS += -DINSTALL_PROG=\"$(abs_top_srcdir)/install-sh\"
AM_CXXFLAGS = $(B10_CXXFLAGS)
......@@ -22,9 +23,11 @@ run_unittests_SOURCES += ../auth_srv.h ../auth_srv.cc
run_unittests_SOURCES += ../query.h ../query.cc
run_unittests_SOURCES += ../change_user.h ../change_user.cc
run_unittests_SOURCES += ../config.h ../config.cc
run_unittests_SOURCES += ../command.h ../command.cc
run_unittests_SOURCES += ../statistics.h ../statistics.cc
run_unittests_SOURCES += auth_srv_unittest.cc
run_unittests_SOURCES += config_unittest.cc
run_unittests_SOURCES += command_unittest.cc
run_unittests_SOURCES += query_unittest.cc
run_unittests_SOURCES += change_user_unittest.cc
run_unittests_SOURCES += statistics_unittest.cc
......
......@@ -30,6 +30,7 @@
#include <datasrc/memory_datasrc.h>
#include <auth/auth_srv.h>
#include <auth/common.h>
#include <auth/statistics.h>
#include <dns/tests/unittest_util.h>
......@@ -640,4 +641,13 @@ TEST_F(AuthSrvTest, queryCounterUnexpected) {
response_obuffer, &dnsserv),
isc::Unexpected);
}
TEST_F(AuthSrvTest, stop) {
// normal case is covered in command_unittest.cc. we should primarily
// test it here, but the current design of the stop test takes time,
// so we consolidate the cases in the command tests.
// stop before start is prohibited.
EXPECT_THROW(server.stop(), FatalError);
}
}
// Copyright (C) 2010 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.
#include <cassert>
#include <cstdlib>
#include <string>
#include <stdexcept>
#include <boost/bind.hpp>
#include <gtest/gtest.h>
#include <dns/name.h>
#include <dns/rrclass.h>
#include <dns/rrtype.h>
#include <cc/data.h>
#include <config/ccsession.h>
#include <datasrc/memory_datasrc.h>
#include <auth/auth_srv.h>
#include <auth/config.h>
#include <auth/command.h>
#include <asiolink/asiolink.h>
#include <testutils/mockups.h>
using namespace std;
using namespace isc::dns;
using namespace isc::data;
using namespace isc::datasrc;
using namespace isc::config;
namespace {
class AuthConmmandTest : public ::testing::Test {
protected:
AuthConmmandTest() : server(false, xfrout), rcode(-1) {
server.setStatisticsSession(&statistics_session);
}
void checkAnswer(const int expected_code) {
parseAnswer(rcode, result);
EXPECT_EQ(expected_code, rcode);