using namespace isc::server_common::portconfig;
namespace {
-/// 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<boost::shared_ptr<AuthConfigParser> > datasources_;
- set<string> configured_sources_;
- vector<pair<RRClass, DataSourceClientContainerPtr> > clients_;
-};
/// A derived \c AuthConfigParser for the version value
/// (which is not used at this moment)
virtual void commit() {};
};
-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");
- }
-
- // Apart from that it's not really easy to get at the default
- // class value for the class here, it should probably really
- // be a property of the instantiated data source. For now
- // use hardcoded default IN.
- const RRClass rrclass =
- datasrc_elem->contains("class") ?
- RRClass(datasrc_elem->get("class")->stringValue()) : RRClass::IN();
-
- // Right now, we only support the in-memory data source for the
- // RR class of IN. We reject other cases explicitly by hardcoded
- // checks. This will soon be generalized, at which point these
- // checks will also have to be cleaned up.
- if (rrclass != RRClass::IN()) {
- isc_throw(isc::InvalidParameter, "Unsupported data source class: "
- << rrclass);
- }
- if (datasrc_type->stringValue() != "memory") {
- isc_throw(AuthConfigError, "Unsupported data source type: "
- << datasrc_type->stringValue());
- }
-
- // Create a new client for the specified data source and store it
- // in the local vector. For now, we always build a new client
- // from the scratch, and replace any existing ones with the new ones.
- // We might eventually want to optimize building zones (in case of
- // reloading) by selectively loading fresh zones for data source
- // where zone loading is expensive (such as in-memory).
- clients_.push_back(
- pair<RRClass, DataSourceClientContainerPtr>(
- rrclass,
- DataSourceClientContainerPtr(new DataSourceClientContainer(
- datasrc_type->stringValue(),
- datasrc_elem))));
-
- configured_sources_.insert(datasrc_type->stringValue());
- }
-}
-
-void
-DatasourcesConfig::commit() {
- // As noted in build(), the current implementation only supports the
- // in-memory data source for class IN, and build() should have ensured
- // it. So, depending on the vector is empty or not, we either clear
- // or install an in-memory data source for the server.
- //
- // When we generalize it, we'll somehow install all data source clients
- // built in the vector, clearing deleted ones from the server.
- if (clients_.empty()) {
- server_.setInMemoryClient(RRClass::IN(),
- DataSourceClientContainerPtr());
- } else {
- server_.setInMemoryClient(clients_.front().first,
- clients_.front().second);
- }
-}
-
/// A derived \c AuthConfigParser class for the "statistics-internal"
/// configuration identifier.
class StatisticsIntervalConfig : public AuthConfigParser {
// 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 (config_id == "statistics-interval") {
+ if (config_id == "statistics-interval") {
return (new StatisticsIntervalConfig(server));
} else if (config_id == "listen_on") {
return (new ListenAddressConfig(server));
// later be used to mark backwards incompatible changes in the
// config data
return (new VersionConfig());
+ } else if (config_id == "datasources") {
+ // TODO: Ignored for now, since the value is probably used by
+ // other modules. Once they have been removed from there, remove
+ // it from here and the spec file.
+
+ // We need to return something. The VersionConfig is empty now,
+ // so we may abuse that one, as it is a short-term solution only.
+ return (new VersionConfig());
} else {
isc_throw(AuthConfigError, "Unknown configuration identifier: " <<
config_id);
isc::dns::Message& message,
bool done);
private:
- std::string db_file_;
-
- MetaDataSrc data_sources_;
- /// We keep a pointer to the currently running sqlite datasource
- /// so that we can specifically remove that one should the database
- /// file change
- ConstDataSrcPtr cur_datasrc_;
-
bool xfrout_connected_;
AbstractXfroutClient& xfrout_client_;
xfrout_client_(xfrout_client),
ddns_forwarder_("update", ddns_forwarder)
{
- // cur_datasrc_ is automatically initialized by the default constructor,
- // effectively being an empty (sqlite) data source. once ccsession is up
- // the datasource will be set by the configuration setting
-
- // add static data source
- data_sources_.addDataSrc(ConstDataSrcPtr(new StaticDataSrc));
-
// enable or disable the cache
cache_.setEnabled(use_cache);
}
data, true));
}
-ConstElementPtr
-AuthSrvImpl::setDbFile(ConstElementPtr config) {
- ConstElementPtr answer = isc::config::createAnswer();
-
- if (config && config->contains("database_file")) {
- db_file_ = config->get("database_file")->stringValue();
- } else if (config_session_ != NULL) {
- bool is_default;
- string item("database_file");
- ConstElementPtr value = config_session_->getValue(is_default, item);
- ElementPtr final = Element::createMap();
-
- // If the value is the default, and we are running from
- // a specific directory ('from build'), we need to use
- // a different value than the default (which may not exist)
- // (btw, this should not be done here in the end, i think
- // the from-source script should have a check for this,
- // but for that we need offline access to config, so for
- // now this is a decent solution)
- if (is_default && getenv("B10_FROM_BUILD")) {
- value = Element::create(string(getenv("B10_FROM_BUILD")) +
- "/bind10_zones.sqlite3");
- }
- final->set(item, value);
- config = final;
-
- db_file_ = value->stringValue();
- } else {
- return (answer);
- }
- LOG_DEBUG(auth_logger, DBG_AUTH_OPS, AUTH_DATA_SOURCE).arg(db_file_);
-
- // create SQL data source
- // Note: the following step is tricky to be exception-safe and to ensure
- // exception guarantee: We first need to perform all operations that can
- // fail, while acquiring resources in the RAII manner. We then perform
- // delete and swap operations which should not fail.
- DataSrcPtr datasrc_ptr(DataSrcPtr(new Sqlite3DataSrc));
- datasrc_ptr->init(config);
- data_sources_.addDataSrc(datasrc_ptr);
-
- // The following code should be exception free.
- if (cur_datasrc_ != NULL) {
- data_sources_.removeDataSrc(cur_datasrc_);
- }
- cur_datasrc_ = datasrc_ptr;
-
- return (answer);
-}
-
void
AuthSrvImpl::resumeServer(DNSServer* server, Message& message, bool done) {
if (done) {
if (new_config) {
configureAuthServer(*this, new_config);
}
- return (impl_->setDbFile(new_config));
+ return (isc::config::createAnswer());
} catch (const isc::Exception& error) {
LOG_ERROR(auth_logger, AUTH_CONFIG_UPDATE_FAIL).arg(error.what());
return (isc::config::createAnswer(1, error.what()));
isc::testutils::TestSocketRequestor sock_requestor_;
};
-TEST_F(AuthConfigTest,
-#ifdef USE_STATIC_LINK
- DISABLED_datasourceConfig
-#else
- datasourceConfig
-#endif
- )
-{
- // By default, we don't have any in-memory data source.
- EXPECT_FALSE(server.hasInMemoryClient());
- configureAuthServer(server, Element::fromJSON(
- "{\"datasources\": [{\"type\": \"memory\"}]}"));
- // after successful configuration, we should have one (with empty zoneset).
- EXPECT_TRUE(server.hasInMemoryClient());
- EXPECT_EQ(0, server.getInMemoryClient(rrclass)->getZoneCount());
-}
-
-TEST_F(AuthConfigTest, databaseConfig) {
- // right now, "database_file" is handled separately, so the parser
- // doesn't recognize it, but it shouldn't throw an exception due to that.
- EXPECT_NO_THROW(configureAuthServer(
- server,
- Element::fromJSON(
- "{\"database_file\": \"should_be_ignored\"}")));
-}
-
TEST_F(AuthConfigTest, versionConfig) {
// make sure it does not throw on 'version'
EXPECT_NO_THROW(configureAuthServer(
EXPECT_THROW(configureAuthServer(
server,
Element::fromJSON(
- "{\"datasources\": [{\"type\": \"memory\"}], "
- " \"no_such_config_var\": 1}")),
+ "{ \"no_such_config_var\": 1}")),
AuthConfigError);
// The server state shouldn't change
EXPECT_FALSE(server.hasInMemoryClient());
}
-TEST_F(AuthConfigTest, exceptionConversion) {
- // This configuration contains a bogus RR class, which will trigger an
- // exception from libdns++. configureAuthServer() should convert this
- // to AuthConfigError and rethrow the converted one.
- EXPECT_THROW(configureAuthServer(
- server,
- Element::fromJSON(
- "{\"datasources\": "
- " [{\"type\": \"memory\","
- " \"class\": \"BADCLASS\","
- " \"zones\": [{\"origin\": \"example.com\","
- " \"file\": \"example.zone\"}]}]}")),
- AuthConfigError);
-}
-
TEST_F(AuthConfigTest, badConfig) {
// These should normally not happen, but should be handled to avoid
// an unexpected crash due to a bug of the caller.
EXPECT_EQ(DNSService::SERVER_SYNC_OK, dnss_.getUDPFdParams().at(1).options);
}
-class MemoryDatasrcConfigTest : public AuthConfigTest {
-protected:
- MemoryDatasrcConfigTest() :
- parser(createAuthConfigParser(server, "datasources"))
- {}
- ~MemoryDatasrcConfigTest() {
- delete parser;
- }
- AuthConfigParser* parser;
-};
-
-TEST_F(MemoryDatasrcConfigTest, addZeroDataSrc) {
- parser->build(Element::fromJSON("[]"));
- parser->commit();
- EXPECT_FALSE(server.hasInMemoryClient());
-}
-
-TEST_F(MemoryDatasrcConfigTest,
-#ifdef USE_STATIC_LINK
- DISABLED_addEmpty
-#else
- addEmpty
-#endif
- )
-{
- // By default, we don't have any in-memory data source.
- EXPECT_FALSE(server.hasInMemoryClient());
- parser->build(Element::fromJSON("[{\"type\": \"memory\"}]"));
- parser->commit();
- EXPECT_EQ(0, server.getInMemoryClient(rrclass)->getZoneCount());
-}
-
-TEST_F(MemoryDatasrcConfigTest,
-#ifdef USE_STATIC_LINK
- DISABLED_addZeroZone
-#else
- addZeroZone
-#endif
- )
-{
- parser->build(Element::fromJSON("[{\"type\": \"memory\","
- " \"zones\": []}]"));
- parser->commit();
- EXPECT_EQ(0, server.getInMemoryClient(rrclass)->getZoneCount());
-}
-
-TEST_F(MemoryDatasrcConfigTest,
-#ifdef USE_STATIC_LINK
- DISABLED_addOneZone
-#else
- addOneZone
-#endif
- )
-{
- EXPECT_NO_THROW(parser->build(Element::fromJSON(
- "[{\"type\": \"memory\","
- " \"zones\": [{\"origin\": \"example.com\","
- " \"file\": \"" TEST_DATA_DIR
- "/example.zone\"}]}]")));
- EXPECT_NO_THROW(parser->commit());
- EXPECT_EQ(1, server.getInMemoryClient(rrclass)->getZoneCount());
- // Check it actually loaded something
- EXPECT_EQ(ZoneFinder::SUCCESS, server.getInMemoryClient(rrclass)->findZone(
- Name("ns.example.com.")).zone_finder->find(Name("ns.example.com."),
- RRType::A())->code);
-}
-
-// This test uses dynamic load of a data source module, and won't work when
-// statically linked.
-TEST_F(MemoryDatasrcConfigTest,
-#ifdef USE_STATIC_LINK
- DISABLED_addOneWithFiletypeSQLite3
-#else
- addOneWithFiletypeSQLite3
-#endif
- )
-{
- const string test_db = TEST_DATA_BUILDDIR "/auth_test.sqlite3.copied";
- stringstream ss("example.org. 3600 IN SOA . . 0 0 0 0 0\n");
- createSQLite3DB(rrclass, Name("example.org"), test_db.c_str(), ss);
-
- // In-memory with an SQLite3 data source as the backend.
- parser->build(Element::fromJSON(
- "[{\"type\": \"memory\","
- " \"zones\": [{\"origin\": \"example.org\","
- " \"file\": \""
- + test_db + "\","
- " \"filetype\": \"sqlite3\"}]}]"));
- parser->commit();
- EXPECT_EQ(1, server.getInMemoryClient(rrclass)->getZoneCount());
-
- // Failure case: the specified zone doesn't exist in the DB file.
- delete parser;
- parser = createAuthConfigParser(server, "datasources");
- EXPECT_THROW(parser->build(
- Element::fromJSON(
- "[{\"type\": \"memory\","
- " \"zones\": [{\"origin\": \"example.com\","
- " \"file\": \""
- + test_db + "\","
- " \"filetype\": \"sqlite3\"}]}]")),
- DataSourceError);
-}
-
-TEST_F(MemoryDatasrcConfigTest,
-#ifdef USE_STATIC_LINK
- DISABLED_addOneWithFiletypeText
-#else
- addOneWithFiletypeText
-#endif
- )
-{
- // Explicitly specifying "text" is okay.
- parser->build(Element::fromJSON(
- "[{\"type\": \"memory\","
- " \"zones\": [{\"origin\": \"example.com\","
- " \"file\": \""
- TEST_DATA_DIR "/example.zone\","
- " \"filetype\": \"text\"}]}]"));
- parser->commit();
- EXPECT_EQ(1, server.getInMemoryClient(rrclass)->getZoneCount());
-}
-
-TEST_F(MemoryDatasrcConfigTest,
-#ifdef USE_STATIC_LINK
- DISABLED_addMultiZones
-#else
- addMultiZones
-#endif
- )
-{
- EXPECT_NO_THROW(parser->build(Element::fromJSON(
- "[{\"type\": \"memory\","
- " \"zones\": [{\"origin\": \"example.com\","
- " \"file\": \"" TEST_DATA_DIR
- "/example.zone\"},"
- " {\"origin\": \"example.org\","
- " \"file\": \"" TEST_DATA_DIR
- "/example.org.zone\"},"
- " {\"origin\": \"example.net\","
- " \"file\": \"" TEST_DATA_DIR
- "/example.net.zone\"}]}]")));
- EXPECT_NO_THROW(parser->commit());
- EXPECT_EQ(3, server.getInMemoryClient(rrclass)->getZoneCount());
-}
-
-TEST_F(MemoryDatasrcConfigTest,
-#ifdef USE_STATIC_LINK
- DISABLED_replace
-#else
- replace
-#endif
- )
-{
- EXPECT_NO_THROW(parser->build(Element::fromJSON(
- "[{\"type\": \"memory\","
- " \"zones\": [{\"origin\": \"example.com\","
- " \"file\": \"" TEST_DATA_DIR
- "/example.zone\"}]}]")));
- EXPECT_NO_THROW(parser->commit());
- EXPECT_EQ(1, server.getInMemoryClient(rrclass)->getZoneCount());
- EXPECT_EQ(isc::datasrc::result::SUCCESS,
- server.getInMemoryClient(rrclass)->findZone(
- Name("example.com")).code);
-
- // create a new parser, and install a new set of configuration. It
- // should replace the old one.
- delete parser;
- parser = createAuthConfigParser(server, "datasources");
- EXPECT_NO_THROW(parser->build(Element::fromJSON(
- "[{\"type\": \"memory\","
- " \"zones\": [{\"origin\": \"example.org\","
- " \"file\": \"" TEST_DATA_DIR
- "/example.org.zone\"},"
- " {\"origin\": \"example.net\","
- " \"file\": \"" TEST_DATA_DIR
- "/example.net.zone\"}]}]")));
- EXPECT_NO_THROW(parser->commit());
- EXPECT_EQ(2, server.getInMemoryClient(rrclass)->getZoneCount());
- EXPECT_EQ(isc::datasrc::result::NOTFOUND,
- server.getInMemoryClient(rrclass)->findZone(
- Name("example.com")).code);
-}
-
-TEST_F(MemoryDatasrcConfigTest,
-#ifdef USE_STATIC_LINK
- DISABLED_exception
-#else
- exception
-#endif
- )
-{
- // Load a zone
- EXPECT_NO_THROW(parser->build(Element::fromJSON(
- "[{\"type\": \"memory\","
- " \"zones\": [{\"origin\": \"example.com\","
- " \"file\": \"" TEST_DATA_DIR
- "/example.zone\"}]}]")));
- EXPECT_NO_THROW(parser->commit());
- EXPECT_EQ(1, server.getInMemoryClient(rrclass)->getZoneCount());
- EXPECT_EQ(isc::datasrc::result::SUCCESS,
- server.getInMemoryClient(rrclass)->findZone(
- Name("example.com")).code);
-
- // create a new parser, and try to load something. It will throw,
- // the given master file should not exist
- delete parser;
- parser = createAuthConfigParser(server, "datasources");
- EXPECT_THROW(parser->build(Element::fromJSON(
- "[{\"type\": \"memory\","
- " \"zones\": [{\"origin\": \"example.org\","
- " \"file\": \"" TEST_DATA_DIR
- "/example.org.zone\"},"
- " {\"origin\": \"example.net\","
- " \"file\": \"" TEST_DATA_DIR
- "/nonexistent.zone\"}]}]")),
- isc::datasrc::DataSourceError);
- // As that one throwed exception, it is not expected from us to
- // commit it
-
- // The original should be untouched
- EXPECT_EQ(1, server.getInMemoryClient(rrclass)->getZoneCount());
- EXPECT_EQ(isc::datasrc::result::SUCCESS,
- server.getInMemoryClient(rrclass)->findZone(
- Name("example.com")).code);
-}
-
-TEST_F(MemoryDatasrcConfigTest,
-#ifdef USE_STATIC_LINK
- DISABLED_remove
-#else
- remove
-#endif
- )
-{
- EXPECT_NO_THROW(parser->build(Element::fromJSON(
- "[{\"type\": \"memory\","
- " \"zones\": [{\"origin\": \"example.com\","
- " \"file\": \"" TEST_DATA_DIR
- "/example.zone\"}]}]")));
- EXPECT_NO_THROW(parser->commit());
- EXPECT_EQ(1, server.getInMemoryClient(rrclass)->getZoneCount());
-
- delete parser;
- parser = createAuthConfigParser(server, "datasources");
- EXPECT_NO_THROW(parser->build(Element::fromJSON("[]")));
- EXPECT_NO_THROW(parser->commit());
- EXPECT_FALSE(server.hasInMemoryClient());
-}
-
-TEST_F(MemoryDatasrcConfigTest, addDuplicateZones) {
- EXPECT_THROW(parser->build(
- Element::fromJSON(
- "[{\"type\": \"memory\","
- " \"zones\": [{\"origin\": \"example.com\","
- " \"file\": \"" TEST_DATA_DIR
- "/example.zone\"},"
- " {\"origin\": \"example.com\","
- " \"file\": \"" TEST_DATA_DIR
- "/example.com.zone\"}]}]")),
- DataSourceError);
-}
-
-TEST_F(MemoryDatasrcConfigTest, addBadZone) {
- // origin and file are missing
- EXPECT_THROW(parser->build(
- Element::fromJSON(
- "[{\"type\": \"memory\","
- " \"zones\": [{}]}]")),
- DataSourceError);
-
- // origin is missing
- EXPECT_THROW(parser->build(
- Element::fromJSON(
- "[{\"type\": \"memory\","
- " \"zones\": [{\"file\": \"example.zone\"}]}]")),
- DataSourceError);
-
- // file is missing
- EXPECT_THROW(parser->build(
- Element::fromJSON(
- "[{\"type\": \"memory\","
- " \"zones\": [{\"origin\": \"example.com\"}]}]")),
- DataSourceError);
-
- // missing zone file
- EXPECT_THROW(parser->build(
- Element::fromJSON(
- "[{\"type\": \"memory\","
- " \"zones\": [{\"origin\": \"example.com\"}]}]")),
- DataSourceError);
-
- // bogus origin name
- EXPECT_THROW(parser->build(Element::fromJSON(
- "[{\"type\": \"memory\","
- " \"zones\": [{\"origin\": \"example..com\","
- " \"file\": \"example.zone\"}]}]")),
- DataSourceError);
-
- // bogus RR class name
- EXPECT_THROW(parser->build(
- Element::fromJSON(
- "[{\"type\": \"memory\","
- " \"class\": \"BADCLASS\","
- " \"zones\": [{\"origin\": \"example.com\","
- " \"file\": \"example.zone\"}]}]")),
- InvalidRRClass);
-
- // valid RR class, but not currently supported
- EXPECT_THROW(parser->build(
- Element::fromJSON(
- "[{\"type\": \"memory\","
- " \"class\": \"CH\","
- " \"zones\": [{\"origin\": \"example.com\","
- " \"file\": \"example.zone\"}]}]")),
- isc::InvalidParameter);
-}
-
-TEST_F(MemoryDatasrcConfigTest,
-#ifdef USE_STATIC_LINK
- DISABLED_badDatasrcType
-#else
- badDatasrcType
-#endif
- )
-{
- EXPECT_THROW(parser->build(Element::fromJSON("[{\"type\": \"badsrc\"}]")),
- AuthConfigError);
- EXPECT_THROW(parser->build(Element::fromJSON("[{\"notype\": \"memory\"}]")),
- AuthConfigError);
- EXPECT_THROW(parser->build(Element::fromJSON("[{\"type\": 1}]")),
- isc::data::TypeError);
- EXPECT_THROW(parser->build(Element::fromJSON("[{\"type\": \"memory\"},"
- " {\"type\": \"memory\"}]")),
- AuthConfigError);
-}
-
class StatisticsIntervalConfigTest : public AuthConfigTest {
protected:
StatisticsIntervalConfigTest() :