Commit a3c44e25 authored by JINMEI Tatuya's avatar JINMEI Tatuya
Browse files

merged trac #446: config knob for in memory data source.


git-svn-id: svn://bind10.isc.org/svn/bind10/trunk@3998 e5f2f494-b856-4b98-b285-d166d9295462
parents 05b245d8 a80861a9
......@@ -39,6 +39,7 @@ pkglibexec_PROGRAMS = b10-auth
b10_auth_SOURCES = auth_srv.cc auth_srv.h
b10_auth_SOURCES += query.cc query.h
b10_auth_SOURCES += change_user.cc change_user.h
b10_auth_SOURCES += config.cc config.h
b10_auth_SOURCES += common.h
b10_auth_SOURCES += main.cc
b10_auth_LDADD = $(top_builddir)/src/lib/datasrc/libdatasrc.la
......
......@@ -7,6 +7,51 @@
"item_type": "string",
"item_optional": true,
"item_default": "@@LOCALSTATEDIR@@/@PACKAGE@/zone.sqlite3"
},
{ "item_name": "datasources",
"item_type": "list",
"item_optional": true,
"item_default": [],
"list_item_spec": {
"item_name": "list_element",
"item_type": "map",
"item_optional": false,
"item_default": {},
"map_item_spec": [
{ "item_name": "type",
"item_type": "string",
"item_optional": false,
"item_default": ""
},
{ "item_name": "class",
"item_type": "string",
"item_optional": false,
"item_default": "IN"
},
{ "item_name": "zones",
"item_type": "list",
"item_optional": false,
"item_default": [],
"list_item_spec": {
"item_name": "list_element",
"item_type": "map",
"item_optional": true,
"map_item_spec": [
{ "item_name": "origin",
"item_type": "string",
"item_optional": false,
"item_default": ""
},
{ "item_name": "file",
"item_type": "string",
"item_optional": false,
"item_default": ""
}
]
}
}
]
}
}
],
"commands": [
......
......@@ -45,12 +45,14 @@
#include <datasrc/query.h>
#include <datasrc/data_source.h>
#include <datasrc/memory_datasrc.h>
#include <datasrc/static_datasrc.h>
#include <datasrc/sqlite3_datasrc.h>
#include <xfr/xfrout_client.h>
#include <auth/common.h>
#include <auth/config.h>
#include <auth/auth_srv.h>
using namespace std;
......@@ -90,6 +92,9 @@ public:
bool verbose_mode_;
AbstractSession* xfrin_session_;
/// In-memory data source. Currently class IN only for simplicity.
AuthSrv::MemoryDataSrcPtr memory_datasrc_;
/// Hot spot cache
isc::datasrc::HotCache cache_;
private:
......@@ -290,6 +295,39 @@ AuthSrv::getConfigSession() const {
return (impl_->config_session_);
}
AuthSrv::ConstMemoryDataSrcPtr
AuthSrv::getMemoryDataSrc(const RRClass& rrclass) const {
// XXX: for simplicity, we only support the IN class right now.
if (rrclass != RRClass::IN()) {
isc_throw(InvalidParameter,
"Memory data source is not supported for RR class "
<< rrclass);
}
return (impl_->memory_datasrc_);
}
void
AuthSrv::setMemoryDataSrc(const isc::dns::RRClass& rrclass,
MemoryDataSrcPtr memory_datasrc)
{
// XXX: see above
if (rrclass != RRClass::IN()) {
isc_throw(InvalidParameter,
"Memory data source is not supported for RR class "
<< rrclass);
}
if (impl_->verbose_mode_) {
if (!impl_->memory_datasrc_ && memory_datasrc) {
cerr << "[b10-auth] Memory data source is enabled for class "
<< rrclass << endl;
} else if (impl_->memory_datasrc_ && !memory_datasrc) {
cerr << "[b10-auth] Memory data source is disabled for class "
<< rrclass << endl;
}
}
impl_->memory_datasrc_ = memory_datasrc;
}
void
AuthSrv::processMessage(const IOMessage& io_message, MessagePtr message,
OutputBufferPtr buffer, DNSServer* server)
......@@ -609,6 +647,9 @@ AuthSrv::updateConfig(ConstElementPtr new_config) {
try {
// the ModuleCCSession has already checked if we have
// the correct ElementPtr type as specified in our .spec file
if (new_config) {
configureAuthServer(*this, new_config);
}
return (impl_->setDbFile(new_config));
} catch (const isc::Exception& error) {
if (impl_->verbose_mode_) {
......
......@@ -19,12 +19,19 @@
#include <string>
// For MemoryDataSrcPtr below. This should be a temporary definition until
// we reorganize the data source framework.
#include <boost/shared_ptr.hpp>
#include <cc/data.h>
#include <config/ccsession.h>
#include <asiolink/asiolink.h>
namespace isc {
namespace datasrc {
class MemoryDataSrc;
}
namespace xfr {
class AbstractXfroutClient;
}
......@@ -224,6 +231,53 @@ public:
///
void setXfrinSession(isc::cc::AbstractSession* xfrin_session);
/// A shared pointer type for \c MemoryDataSrc.
///
/// This is defined inside the \c AuthSrv class as it's supposed to be
/// a short term interface until we integrate the in-memory and other
/// data source frameworks.
typedef boost::shared_ptr<isc::datasrc::MemoryDataSrc> MemoryDataSrcPtr;
/// An immutable shared pointer type for \c MemoryDataSrc.
typedef boost::shared_ptr<const isc::datasrc::MemoryDataSrc>
ConstMemoryDataSrcPtr;
/// Returns the in-memory data source configured for the \c AuthSrv,
/// if any.
///
/// The in-memory data source is configured per RR class. However,
/// the data source may not be available for all RR classes.
/// If it is not available for the specified RR class, an exception of
/// class \c InvalidParameter will be thrown.
/// This method never throws an exception otherwise.
///
/// Even for supported RR classes, the in-memory data source is not
/// configured by default. In that case a NULL (shared) pointer will
/// be returned.
///
/// \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;
/// Sets or replaces the in-memory data source of the specified RR class.
///
/// As noted in \c getMemoryDataSrc(), some RR classes may not be
/// supported, in which case an exception of class \c InvalidParameter
/// will be thrown.
/// This method never throws an exception otherwise.
///
/// If there is already an in memory data source configured, it will be
/// replaced with the newly specified one.
/// \c memory_datasrc can be NULL, in which case it will (re)disable the
/// in-memory data source.
///
/// \param rrclass The RR class of the in-memory data source to be set.
/// \param memory_datasrc A (shared) pointer to \c MemoryDataSrc to be set.
void setMemoryDataSrc(const isc::dns::RRClass& rrclass,
MemoryDataSrcPtr memory_datasrc);
private:
AuthSrvImpl* impl_;
asiolink::IOService* io_service_;
......
......@@ -9,6 +9,7 @@ CLEANFILES = *.gcno *.gcda
noinst_PROGRAMS = query_bench
query_bench_SOURCES = query_bench.cc
query_bench_SOURCES += ../auth_srv.h ../auth_srv.cc
query_bench_SOURCES += ../config.h ../config.cc
query_bench_LDADD = $(top_builddir)/src/lib/dns/libdns++.la
query_bench_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
......
......@@ -26,6 +26,7 @@
#include <auth/common.h>
using namespace boost;
using namespace std;
void
changeUser(const char* const username) {
......@@ -42,14 +43,14 @@ changeUser(const char* const username) {
}
}
if (runas_pw == NULL) {
isc_throw(FatalError, "Unknown user name or UID:" << username);
throw FatalError("Unknown user name or UID:" + string(username));
}
if (setgid(runas_pw->pw_gid) < 0) {
isc_throw(FatalError, "setgid() failed: " << strerror(errno));
throw FatalError("setgid() failed: " + string(strerror(errno)));
}
if (setuid(runas_pw->pw_uid) < 0) {
isc_throw(FatalError, "setuid() failed: " << strerror(errno));
throw FatalError("setuid() failed: " + string(strerror(errno)));
}
}
......@@ -17,12 +17,18 @@
#ifndef __COMMON_H
#define __COMMON_H 1
#include <exceptions/exceptions.h>
#include <stdexcept>
#include <string>
class FatalError : public isc::Exception {
/// An exception class that is thrown in an unrecoverable error condition.
///
/// This exception should not be caught except at the highest level of
/// the application only for terminating the program gracefully, and so
/// it cannot be a derived class of \c isc::Exception.
class FatalError : public std::runtime_error {
public:
FatalError(const char* file, size_t line, const char* what) :
isc::Exception(file, line, what) {}
FatalError(const std::string& what) : std::runtime_error(what)
{}
};
#endif // __COMMON_H
......
// 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 <set>
#include <string>
#include <utility>
#include <vector>
#include <boost/foreach.hpp>
#include <boost/shared_ptr.hpp>
#include <dns/name.h>
#include <dns/rrclass.h>
#include <cc/data.h>
#include <datasrc/memory_datasrc.h>
#include <datasrc/zonetable.h>
#include <auth/auth_srv.h>
#include <auth/config.h>
#include <auth/common.h>
using namespace std;
using boost::shared_ptr;
using namespace isc::dns;
using namespace isc::data;
using namespace isc::datasrc;
namespace {
// Forward declaration
AuthConfigParser*
createAuthConfigParser(AuthSrv& server, const std::string& config_id,
bool internal);
/// A derived \c AuthConfigParser class for the "datasources" configuration
/// identifier.
class DatasourcesConfig : public AuthConfigParser {
public:
DatasourcesConfig(AuthSrv& server) : server_(server) {}
virtual void build(ConstElementPtr config_value);
virtual void commit();
private:
AuthSrv& server_;
vector<shared_ptr<AuthConfigParser> > datasources_;
set<string> configured_sources_;
};
void
DatasourcesConfig::build(ConstElementPtr config_value) {
BOOST_FOREACH(ConstElementPtr datasrc_elem, config_value->listValue()) {
// The caller is supposed to perform syntax-level checks, but we'll
// do minimum level of validation ourselves so that we won't crash due
// to a buggy application.
ConstElementPtr datasrc_type = datasrc_elem->get("type");
if (!datasrc_type) {
isc_throw(AuthConfigError, "Missing data source type");
}
if (configured_sources_.find(datasrc_type->stringValue()) !=
configured_sources_.end()) {
isc_throw(AuthConfigError, "Data source type '" <<
datasrc_type->stringValue() << "' already configured");
}
shared_ptr<AuthConfigParser> datasrc_config =
shared_ptr<AuthConfigParser>(
createAuthConfigParser(server_, string("datasources/") +
datasrc_type->stringValue(),
true));
datasrc_config->build(datasrc_elem);
datasources_.push_back(datasrc_config);
configured_sources_.insert(datasrc_type->stringValue());
}
}
void
DatasourcesConfig::commit() {
// XXX a short term workaround: clear all data sources and then reset
// to new ones so that we can remove data sources that don't exist in
// the new configuration and have been used in the server.
// This could be inefficient and requires knowledge about
// server implementation details, and isn't scalable wrt the number of
// data source types, and should eventually be improved.
// Currently memory data source for class IN is the only possibility.
server_.setMemoryDataSrc(RRClass::IN(), AuthSrv::MemoryDataSrcPtr());
BOOST_FOREACH(shared_ptr<AuthConfigParser> datasrc_config, datasources_) {
datasrc_config->commit();
}
}
/// A derived \c AuthConfigParser class for the memory type datasource
/// configuration. It does not correspond to the configuration syntax;
/// it's instantiated for internal use.
class MemoryDatasourceConfig : public AuthConfigParser {
public:
MemoryDatasourceConfig(AuthSrv& server) :
server_(server),
rrclass_(0) // XXX: dummy initial value
{}
virtual void build(ConstElementPtr config_value);
virtual void commit() {
server_.setMemoryDataSrc(rrclass_, memory_datasrc_);
}
private:
AuthSrv& server_;
RRClass rrclass_;
AuthSrv::MemoryDataSrcPtr memory_datasrc_;
};
void
MemoryDatasourceConfig::build(ConstElementPtr config_value) {
// XXX: apparently we cannot retrieve the default RR class from the
// module spec. As a temporary workaround we hardcode the default value.
ConstElementPtr rrclass_elem = config_value->get("class");
rrclass_ = RRClass(rrclass_elem ? rrclass_elem->stringValue() : "IN");
// We'd eventually optimize building zones (in case of reloading) by
// selectively loading fresh zones. Right now we simply check the
// RR class is supported by the server implementation.
server_.getMemoryDataSrc(rrclass_);
memory_datasrc_ = AuthSrv::MemoryDataSrcPtr(new MemoryDataSrc());
ConstElementPtr zones_config = config_value->get("zones");
if (!zones_config) {
// XXX: Like the RR class, we cannot retrieve the default value here,
// so we assume an empty zone list in this case.
return;
}
BOOST_FOREACH(ConstElementPtr zone_config, zones_config->listValue()) {
ConstElementPtr origin = zone_config->get("origin");
if (!origin) {
isc_throw(AuthConfigError, "Missing zone origin");
}
ConstElementPtr file = zone_config->get("file");
if (!file) {
isc_throw(AuthConfigError, "Missing zone file for zone: "
<< origin->str());
}
const result::Result result = memory_datasrc_->addZone(
ZonePtr(new MemoryZone(rrclass_, Name(origin->stringValue()))));
if (result == result::EXIST) {
isc_throw(AuthConfigError, "zone "<< origin->str()
<< " already exists");
}
// TODO
// then load the zone from 'file', which is currently not implemented.
//
}
}
/// A special parser for testing: it throws from commit() despite the
/// suggested convention of the class interface.
class ThrowerCommitConfig : public AuthConfigParser {
public:
virtual void build(ConstElementPtr) {} // ignore param, do nothing
virtual void commit() {
throw 10;
}
};
// This is a generalized version of create function that can create
// an AuthConfigParser object for "internal" use.
AuthConfigParser*
createAuthConfigParser(AuthSrv& server, const std::string& config_id,
bool internal)
{
// For the initial implementation we use a naive if-else blocks for
// simplicity. In future we'll probably generalize it using map-like
// data structure, and may even provide external register interface so
// that it can be dynamically customized.
if (config_id == "datasources") {
return (new DatasourcesConfig(server));
} else if (internal && config_id == "datasources/memory") {
return (new MemoryDatasourceConfig(server));
} else if (config_id == "_commit_throw") {
// This is for testing purpose only and should not appear in the
// actual configuration syntax. While this could crash the caller
// as a result, the server implementation is expected to perform
// syntax level validation and should be safe in practice. In future,
// we may introduce dynamic registration of configuration parsers,
// and then this test can be done in a cleaner and safer way.
return (new ThrowerCommitConfig());
} else {
isc_throw(AuthConfigError, "Unknown configuration identifier: " <<
config_id);
}
}
} // end of unnamed namespace
AuthConfigParser*
createAuthConfigParser(AuthSrv& server, const std::string& config_id) {
return (createAuthConfigParser(server, config_id, false));
}
void
configureAuthServer(AuthSrv& server, ConstElementPtr config_set) {
if (!config_set) {
isc_throw(AuthConfigError,
"Null pointer is passed to configuration parser");
}
typedef shared_ptr<AuthConfigParser> ParserPtr;
vector<ParserPtr> parsers;
typedef pair<string, ConstElementPtr> ConfigPair;
try {
BOOST_FOREACH(ConfigPair config_pair, config_set->mapValue()) {
// We should eventually integrate the sqlite3 DB configuration to
// this framework, but to minimize diff we begin with skipping that
// part.
if (config_pair.first == "database_file") {
continue;
}
ParserPtr parser(createAuthConfigParser(server,
config_pair.first));
parser->build(config_pair.second);
parsers.push_back(parser);
}
} catch (const AuthConfigError& ex) {
throw ex; // simply rethrowing it
} catch (const isc::Exception& ex) {
isc_throw(AuthConfigError, "Server configuration failed: " <<
ex.what());
}
try {
BOOST_FOREACH(ParserPtr parser, parsers) {
parser->commit();
}
} catch (...) {
throw FatalError("Unrecoverable error: "
"a configuration parser threw in commit");
}
}
// 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 <exceptions/exceptions.h>
#include <cc/data.h>
#ifndef __CONFIG_H
#define __CONFIG_H 1
class AuthSrv;
/// An exception that is thrown if an error occurs while configuring an
/// \c AuthSrv object.
class AuthConfigError : public isc::Exception {
public:
AuthConfigError(const char* file, size_t line, const char* what) :
isc::Exception(file, line, what) {}
};
/// The abstract base class that represents a single configuration identifier
/// for an \c AuthSrv object.
///
/// In general, each top level configuration identifier for \c AuthSrv is
/// expected to have its own derived class of this base class.
/// For example, for the following configuration:
/// \code { "param1": 10, "param2": { "subparam1": "foo", "subparam2": [] } }
/// \endcode
/// "param1" and "param2" are top level identifiers, and would correspond to
/// derived \c AuthConfigParser classes.
/// "subparam1" and/or "subparam2" may also have dedicated derived classes.
///
/// These derived classes are hidden inside the implementation; applications
/// are not expected to (and in fact cannot) instantiate them directly.
///
/// Each derived class is generally expected to be constructed with an
/// \c AuthSrv object to be configured and hold a reference to the server
/// throughout the configuration process.
/// For each derived class, the \c build() method parses the configuration
/// value for the corresponding identifier and prepares new configuration
/// value(s) to be applied to the server. This method may throw an exception
/// when it encounters an error.
/// The \c commit() method actually applies the new configuration value
/// to the server. It's basically not expected to throw an exception;
/// any configuration operations that can fail (such as ones involving
/// resource allocation) should be done in \c build().
///
/// When the destructor is called before \c commit(), the destructor is
/// supposed to make sure the state of the \c AuthSrv object is the same
/// as that before it starts building the configuration value.
/// If \c build() doesn't change the server state (which is recommended)
/// the destructor doesn't have to do anything special in this regard.
/// This is a key to ensure the strong exception guarantee (see also
/// the description of \c configureAuthServer()).
class AuthConfigParser {
///
/// \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:
AuthConfigParser(const AuthConfigParser& source);
AuthConfigParser& operator=(const AuthConfigParser& 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).
AuthConfigParser() {}
public:
/// The destructor.
virtual ~AuthConfigParser() {}
//@}
/// Prepare configuration value.
///
/// This method parses the "value part" of the configuration identifier
/// that corresponds to this derived class and prepares a new value to
/// apply to the server.
/// In the above example, the derived class for the identifier "param1"
/// would be passed an data \c Element storing an integer whose value
/// is 10, and would record that value internally;
/// the derived class for the identifier "param2" would be passed a
/// map element and (after parsing) convert it into some internal
/// data structure.
///
/// This method must validate the given value both in terms of syntax
/// and semantics of the configuration, so that the server will be
/// validly configured at the time of \c commit(). Note: the given
/// configuration value is normally syntactically validated, but the
/// \c build() implementation must also expect invalid input. If it
/// detects an error it may throw an exception of a derived class
/// of \c isc::Exception.