config.cc 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. // Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #include <set>
  15. #include <string>
  16. #include <utility>
  17. #include <vector>
  18. #include <boost/foreach.hpp>
  19. #include <boost/shared_ptr.hpp>
  20. #include <dns/name.h>
  21. #include <dns/rrclass.h>
  22. #include <cc/data.h>
  23. #include <datasrc/memory_datasrc.h>
  24. #include <datasrc/zonetable.h>
  25. #include <auth/auth_srv.h>
  26. #include <auth/config.h>
  27. #include <auth/common.h>
  28. using namespace std;
  29. using boost::shared_ptr;
  30. using namespace isc::dns;
  31. using namespace isc::data;
  32. using namespace isc::datasrc;
  33. namespace {
  34. // Forward declaration
  35. AuthConfigParser*
  36. createAuthConfigParser(AuthSrv& server, const std::string& config_id,
  37. bool internal);
  38. /// A derived \c AuthConfigParser class for the "datasources" configuration
  39. /// identifier.
  40. class DatasourcesConfig : public AuthConfigParser {
  41. public:
  42. DatasourcesConfig(AuthSrv& server) : server_(server) {}
  43. virtual void build(ConstElementPtr config_value);
  44. virtual void commit();
  45. private:
  46. AuthSrv& server_;
  47. vector<shared_ptr<AuthConfigParser> > datasources_;
  48. set<string> configured_sources_;
  49. };
  50. void
  51. DatasourcesConfig::build(ConstElementPtr config_value) {
  52. BOOST_FOREACH(ConstElementPtr datasrc_elem, config_value->listValue()) {
  53. // The caller is supposed to perform syntax-level checks, but we'll
  54. // do minimum level of validation ourselves so that we won't crash due
  55. // to a buggy application.
  56. ConstElementPtr datasrc_type = datasrc_elem->get("type");
  57. if (!datasrc_type) {
  58. isc_throw(AuthConfigError, "Missing data source type");
  59. }
  60. if (configured_sources_.find(datasrc_type->stringValue()) !=
  61. configured_sources_.end()) {
  62. isc_throw(AuthConfigError, "Data source type '" <<
  63. datasrc_type->stringValue() << "' already configured");
  64. }
  65. shared_ptr<AuthConfigParser> datasrc_config =
  66. shared_ptr<AuthConfigParser>(
  67. createAuthConfigParser(server_, string("datasources/") +
  68. datasrc_type->stringValue(),
  69. true));
  70. datasrc_config->build(datasrc_elem);
  71. datasources_.push_back(datasrc_config);
  72. configured_sources_.insert(datasrc_type->stringValue());
  73. }
  74. }
  75. void
  76. DatasourcesConfig::commit() {
  77. // XXX a short term workaround: clear all data sources and then reset
  78. // to new ones so that we can remove data sources that don't exist in
  79. // the new configuration and have been used in the server.
  80. // This could be inefficient and requires knowledge about
  81. // server implementation details, and isn't scalable wrt the number of
  82. // data source types, and should eventually be improved.
  83. // Currently memory data source for class IN is the only possibility.
  84. server_.setMemoryDataSrc(RRClass::IN(), AuthSrv::MemoryDataSrcPtr());
  85. BOOST_FOREACH(shared_ptr<AuthConfigParser> datasrc_config, datasources_) {
  86. datasrc_config->commit();
  87. }
  88. }
  89. /// A derived \c AuthConfigParser class for the memory type datasource
  90. /// configuration. It does not correspond to the configuration syntax;
  91. /// it's instantiated for internal use.
  92. class MemoryDatasourceConfig : public AuthConfigParser {
  93. public:
  94. MemoryDatasourceConfig(AuthSrv& server) :
  95. server_(server),
  96. rrclass_(0) // XXX: dummy initial value
  97. {}
  98. virtual void build(ConstElementPtr config_value);
  99. virtual void commit() {
  100. server_.setMemoryDataSrc(rrclass_, memory_datasrc_);
  101. }
  102. private:
  103. AuthSrv& server_;
  104. RRClass rrclass_;
  105. AuthSrv::MemoryDataSrcPtr memory_datasrc_;
  106. };
  107. void
  108. MemoryDatasourceConfig::build(ConstElementPtr config_value) {
  109. // XXX: apparently we cannot retrieve the default RR class from the
  110. // module spec. As a temporary workaround we hardcode the default value.
  111. ConstElementPtr rrclass_elem = config_value->get("class");
  112. rrclass_ = RRClass(rrclass_elem ? rrclass_elem->stringValue() : "IN");
  113. // We'd eventually optimize building zones (in case of reloading) by
  114. // selectively loading fresh zones. Right now we simply check the
  115. // RR class is supported by the server implementation.
  116. server_.getMemoryDataSrc(rrclass_);
  117. memory_datasrc_ = AuthSrv::MemoryDataSrcPtr(new MemoryDataSrc());
  118. ConstElementPtr zones_config = config_value->get("zones");
  119. if (!zones_config) {
  120. // XXX: Like the RR class, we cannot retrieve the default value here,
  121. // so we assume an empty zone list in this case.
  122. return;
  123. }
  124. BOOST_FOREACH(ConstElementPtr zone_config, zones_config->listValue()) {
  125. ConstElementPtr origin = zone_config->get("origin");
  126. if (!origin) {
  127. isc_throw(AuthConfigError, "Missing zone origin");
  128. }
  129. ConstElementPtr file = zone_config->get("file");
  130. if (!file) {
  131. isc_throw(AuthConfigError, "Missing zone file for zone: "
  132. << origin->str());
  133. }
  134. shared_ptr<MemoryZone> new_zone(new MemoryZone(rrclass_,
  135. Name(origin->stringValue())));
  136. const result::Result result = memory_datasrc_->addZone(new_zone);
  137. if (result == result::EXIST) {
  138. isc_throw(AuthConfigError, "zone "<< origin->str()
  139. << " already exists");
  140. }
  141. /*
  142. * TODO: Once we have better reloading of configuration (something
  143. * else than throwing everything away and loading it again), we will
  144. * need the load method to be split into some kind of build and
  145. * commit/abort parts.
  146. */
  147. new_zone->load(file->stringValue());
  148. }
  149. }
  150. /// A special parser for testing: it throws from commit() despite the
  151. /// suggested convention of the class interface.
  152. class ThrowerCommitConfig : public AuthConfigParser {
  153. public:
  154. virtual void build(ConstElementPtr) {} // ignore param, do nothing
  155. virtual void commit() {
  156. throw 10;
  157. }
  158. };
  159. // This is a generalized version of create function that can create
  160. // an AuthConfigParser object for "internal" use.
  161. AuthConfigParser*
  162. createAuthConfigParser(AuthSrv& server, const std::string& config_id,
  163. bool internal)
  164. {
  165. // For the initial implementation we use a naive if-else blocks for
  166. // simplicity. In future we'll probably generalize it using map-like
  167. // data structure, and may even provide external register interface so
  168. // that it can be dynamically customized.
  169. if (config_id == "datasources") {
  170. return (new DatasourcesConfig(server));
  171. } else if (internal && config_id == "datasources/memory") {
  172. return (new MemoryDatasourceConfig(server));
  173. } else if (config_id == "_commit_throw") {
  174. // This is for testing purpose only and should not appear in the
  175. // actual configuration syntax. While this could crash the caller
  176. // as a result, the server implementation is expected to perform
  177. // syntax level validation and should be safe in practice. In future,
  178. // we may introduce dynamic registration of configuration parsers,
  179. // and then this test can be done in a cleaner and safer way.
  180. return (new ThrowerCommitConfig());
  181. } else {
  182. isc_throw(AuthConfigError, "Unknown configuration identifier: " <<
  183. config_id);
  184. }
  185. }
  186. } // end of unnamed namespace
  187. AuthConfigParser*
  188. createAuthConfigParser(AuthSrv& server, const std::string& config_id) {
  189. return (createAuthConfigParser(server, config_id, false));
  190. }
  191. void
  192. configureAuthServer(AuthSrv& server, ConstElementPtr config_set) {
  193. if (!config_set) {
  194. isc_throw(AuthConfigError,
  195. "Null pointer is passed to configuration parser");
  196. }
  197. typedef shared_ptr<AuthConfigParser> ParserPtr;
  198. vector<ParserPtr> parsers;
  199. typedef pair<string, ConstElementPtr> ConfigPair;
  200. try {
  201. BOOST_FOREACH(ConfigPair config_pair, config_set->mapValue()) {
  202. // We should eventually integrate the sqlite3 DB configuration to
  203. // this framework, but to minimize diff we begin with skipping that
  204. // part.
  205. if (config_pair.first == "database_file") {
  206. continue;
  207. }
  208. ParserPtr parser(createAuthConfigParser(server,
  209. config_pair.first));
  210. parser->build(config_pair.second);
  211. parsers.push_back(parser);
  212. }
  213. } catch (const AuthConfigError& ex) {
  214. throw ex; // simply rethrowing it
  215. } catch (const isc::Exception& ex) {
  216. isc_throw(AuthConfigError, "Server configuration failed: " <<
  217. ex.what());
  218. }
  219. try {
  220. BOOST_FOREACH(ParserPtr parser, parsers) {
  221. parser->commit();
  222. }
  223. } catch (...) {
  224. throw FatalError("Unrecoverable error: "
  225. "a configuration parser threw in commit");
  226. }
  227. }