123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309 |
- // 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 <dns/name.h>
- #include <dns/rrclass.h>
- #include <cc/data.h>
- #include <datasrc/memory_datasrc.h>
- #include <datasrc/zonetable.h>
- #include <datasrc/factory.h>
- #include <auth/auth_srv.h>
- #include <auth/auth_config.h>
- #include <auth/common.h>
- #include <server_common/portconfig.h>
- #include <boost/foreach.hpp>
- #include <boost/shared_ptr.hpp>
- #include <boost/scoped_ptr.hpp>
- #include <set>
- #include <string>
- #include <utility>
- #include <vector>
- using namespace std;
- using namespace isc::dns;
- using namespace isc::data;
- using namespace isc::datasrc;
- 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)
- class VersionConfig : public AuthConfigParser {
- public:
- VersionConfig() {}
- virtual void build(ConstElementPtr) {};
- 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 {
- public:
- StatisticsIntervalConfig(AuthSrv& server) :
- server_(server), interval_(0)
- {}
- virtual void build(ConstElementPtr config_value) {
- const int32_t config_interval = config_value->intValue();
- if (config_interval < 0) {
- isc_throw(AuthConfigError, "Negative statistics interval value: "
- << config_interval);
- }
- if (config_interval > 86400) {
- isc_throw(AuthConfigError, "Statistics interval value "
- << config_interval
- << " must be equal to or shorter than 86400");
- }
- interval_ = config_interval;
- }
- virtual void commit() {
- // setStatisticsTimerInterval() is not 100% exception free. But
- // exceptions should happen only in a very rare situation, so we
- // let them be thrown and subsequently regard them as a fatal error.
- server_.setStatisticsTimerInterval(interval_);
- }
- private:
- AuthSrv& server_;
- uint32_t interval_;
- };
- /// 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;
- }
- };
- /**
- * \brief Configuration parser for listen_on.
- *
- * It parses and sets the listening addresses of the server.
- *
- * It acts in unusual way. Since actually binding (changing) the sockets
- * is an operation that is expected to throw often, it shouldn't happen
- * in commit. Thefere we do it in build. But if the config is not committed
- * then, we would have it wrong. So we store the old addresses and if
- * commit is not called before destruction of the object, we return the
- * old addresses (which is the same kind of dangerous operation, but it is
- * expected that if we just managed to bind some and had the old ones binded
- * before, it should work).
- *
- * We might do something better in future (like open only the ports that are
- * extra, put them in in commit and close the old ones), but that's left out
- * for now.
- */
- class ListenAddressConfig : public AuthConfigParser {
- public:
- ListenAddressConfig(AuthSrv& server) :
- server_(server)
- { }
- ~ ListenAddressConfig() {
- if (rollbackAddresses_.get() != NULL) {
- server_.setListenAddresses(*rollbackAddresses_);
- }
- }
- private:
- typedef auto_ptr<AddressList> AddrListPtr;
- public:
- virtual void build(ConstElementPtr config) {
- AddressList newAddresses = parseAddresses(config, "listen_on");
- AddrListPtr old(new AddressList(server_.getListenAddresses()));
- server_.setListenAddresses(newAddresses);
- /*
- * Set the rollback addresses only after successful setting of the
- * new addresses, so we don't try to rollback if the setup is
- * unsuccessful (the above can easily throw).
- */
- rollbackAddresses_ = old;
- }
- virtual void commit() {
- rollbackAddresses_.release();
- }
- private:
- AuthSrv& server_;
- /**
- * This is the old address list, if we expect to roll back. When we commit,
- * this is set to NULL.
- */
- AddrListPtr rollbackAddresses_;
- };
- } // end of unnamed namespace
- AuthConfigParser*
- createAuthConfigParser(AuthSrv& server, const std::string& config_id) {
- // 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 (config_id == "statistics-interval") {
- return (new StatisticsIntervalConfig(server));
- } else if (config_id == "listen_on") {
- return (new ListenAddressConfig(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 if (config_id == "version") {
- // Currently, the version identifier is ignored, but it should
- // later be used to mark backwards incompatible changes in the
- // config data
- return (new VersionConfig());
- } else {
- isc_throw(AuthConfigError, "Unknown configuration identifier: " <<
- config_id);
- }
- }
- void
- configureAuthServer(AuthSrv& server, ConstElementPtr config_set) {
- if (!config_set) {
- isc_throw(AuthConfigError,
- "Null pointer is passed to configuration parser");
- }
- typedef boost::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; // 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");
- }
- }
|