config.cc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  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 derived \c AuthConfigParser class for the "statistics-internal"
  151. /// configuration identifier.
  152. class StatisticsIntervalConfig : public AuthConfigParser {
  153. public:
  154. StatisticsIntervalConfig(AuthSrv& server) :
  155. server_(server), interval_(0)
  156. {}
  157. virtual void build(ConstElementPtr config_value) {
  158. const int32_t config_interval = config_value->intValue();
  159. if (config_interval < 0) {
  160. isc_throw(AuthConfigError, "Negative statistics interval value: "
  161. << config_interval);
  162. }
  163. if (config_interval > 86400) {
  164. isc_throw(AuthConfigError, "Statistics interval value "
  165. << config_interval
  166. << " must be equal to or shorter than 86400");
  167. }
  168. interval_ = config_interval;
  169. }
  170. virtual void commit() {
  171. // setStatisticsTimerInterval() is not 100% exception free. But
  172. // exceptions should happen only in a very rare situation, so we
  173. // let them be thrown and subsequently regard them as a fatal error.
  174. server_.setStatisticsTimerInterval(interval_);
  175. }
  176. private:
  177. AuthSrv& server_;
  178. uint32_t interval_;
  179. };
  180. /// A special parser for testing: it throws from commit() despite the
  181. /// suggested convention of the class interface.
  182. class ThrowerCommitConfig : public AuthConfigParser {
  183. public:
  184. virtual void build(ConstElementPtr) {} // ignore param, do nothing
  185. virtual void commit() {
  186. throw 10;
  187. }
  188. };
  189. // This is a generalized version of create function that can create
  190. // an AuthConfigParser object for "internal" use.
  191. AuthConfigParser*
  192. createAuthConfigParser(AuthSrv& server, const std::string& config_id,
  193. bool internal)
  194. {
  195. // For the initial implementation we use a naive if-else blocks for
  196. // simplicity. In future we'll probably generalize it using map-like
  197. // data structure, and may even provide external register interface so
  198. // that it can be dynamically customized.
  199. if (config_id == "datasources") {
  200. return (new DatasourcesConfig(server));
  201. } else if (config_id == "statistics-interval") {
  202. return (new StatisticsIntervalConfig(server));
  203. } else if (internal && config_id == "datasources/memory") {
  204. return (new MemoryDatasourceConfig(server));
  205. } else if (config_id == "_commit_throw") {
  206. // This is for testing purpose only and should not appear in the
  207. // actual configuration syntax. While this could crash the caller
  208. // as a result, the server implementation is expected to perform
  209. // syntax level validation and should be safe in practice. In future,
  210. // we may introduce dynamic registration of configuration parsers,
  211. // and then this test can be done in a cleaner and safer way.
  212. return (new ThrowerCommitConfig());
  213. } else {
  214. isc_throw(AuthConfigError, "Unknown configuration identifier: " <<
  215. config_id);
  216. }
  217. }
  218. } // end of unnamed namespace
  219. AuthConfigParser*
  220. createAuthConfigParser(AuthSrv& server, const std::string& config_id) {
  221. return (createAuthConfigParser(server, config_id, false));
  222. }
  223. void
  224. configureAuthServer(AuthSrv& server, ConstElementPtr config_set) {
  225. if (!config_set) {
  226. isc_throw(AuthConfigError,
  227. "Null pointer is passed to configuration parser");
  228. }
  229. typedef shared_ptr<AuthConfigParser> ParserPtr;
  230. vector<ParserPtr> parsers;
  231. typedef pair<string, ConstElementPtr> ConfigPair;
  232. try {
  233. BOOST_FOREACH(ConfigPair config_pair, config_set->mapValue()) {
  234. // We should eventually integrate the sqlite3 DB configuration to
  235. // this framework, but to minimize diff we begin with skipping that
  236. // part.
  237. if (config_pair.first == "database_file") {
  238. continue;
  239. }
  240. ParserPtr parser(createAuthConfigParser(server,
  241. config_pair.first));
  242. parser->build(config_pair.second);
  243. parsers.push_back(parser);
  244. }
  245. } catch (const AuthConfigError& ex) {
  246. throw; // simply rethrowing it
  247. } catch (const isc::Exception& ex) {
  248. isc_throw(AuthConfigError, "Server configuration failed: " <<
  249. ex.what());
  250. }
  251. try {
  252. BOOST_FOREACH(ParserPtr parser, parsers) {
  253. parser->commit();
  254. }
  255. } catch (...) {
  256. throw FatalError("Unrecoverable error: "
  257. "a configuration parser threw in commit");
  258. }
  259. }