master_loader.cc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. // Copyright (C) 2012 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 <dns/master_loader.h>
  15. #include <dns/master_lexer.h>
  16. #include <dns/name.h>
  17. #include <dns/rrttl.h>
  18. #include <dns/rrclass.h>
  19. #include <dns/rrtype.h>
  20. #include <dns/rdata.h>
  21. #include <string>
  22. #include <memory>
  23. #include <strings.h>
  24. using std::string;
  25. using std::auto_ptr;
  26. namespace isc {
  27. namespace dns {
  28. // An internal exception, used to control the code flow in case of errors.
  29. // It is thrown during the loading and caught later, not to be propagated
  30. // outside of the file.
  31. class InternalException : public isc::Exception {
  32. public:
  33. InternalException(const char* filename, size_t line, const char* what) :
  34. Exception(filename, line, what)
  35. {}
  36. };
  37. class MasterLoader::MasterLoaderImpl {
  38. public:
  39. MasterLoaderImpl(const char* master_file,
  40. const Name& zone_origin,
  41. const RRClass& zone_class,
  42. const MasterLoaderCallbacks& callbacks,
  43. const AddRRCallback& add_callback,
  44. MasterLoader::Options options) :
  45. lexer_(),
  46. zone_origin_(zone_origin),
  47. zone_class_(zone_class),
  48. callbacks_(callbacks),
  49. add_callback_(add_callback),
  50. options_(options),
  51. master_file_(master_file),
  52. initialized_(false),
  53. ok_(true),
  54. many_errors_((options & MANY_ERRORS) != 0),
  55. source_count_(0),
  56. complete_(false),
  57. seen_error_(false)
  58. {}
  59. void reportError(const std::string& filename, size_t line,
  60. const std::string& reason)
  61. {
  62. seen_error_ = true;
  63. callbacks_.error(filename, line, reason);
  64. if (!many_errors_) {
  65. // In case we don't have the lenient mode, every error is fatal
  66. // and we throw
  67. ok_ = false;
  68. complete_ = true;
  69. isc_throw(MasterLoaderError, reason.c_str());
  70. }
  71. }
  72. void pushSource(const std::string& filename) {
  73. std::string error;
  74. if (!lexer_.pushSource(filename.c_str(), &error)) {
  75. if (initialized_) {
  76. // $INCLUDE file
  77. reportError(lexer_.getSourceName(), lexer_.getSourceLine(),
  78. error);
  79. } else {
  80. // Top-level file
  81. reportError("", 0, error);
  82. ok_ = false;
  83. }
  84. }
  85. initialized_ = true;
  86. ++source_count_;
  87. }
  88. bool popSource() {
  89. lexer_.popSource();
  90. return (--source_count_ != 0);
  91. }
  92. void pushStreamSource(std::istream& stream) {
  93. lexer_.pushSource(stream);
  94. initialized_ = true;
  95. ++source_count_;
  96. }
  97. // Get a string token. Handle it as error if it is not string.
  98. const string getString() {
  99. lexer_.getNextToken(MasterToken::STRING).getString(string_token_);
  100. return (string_token_);
  101. }
  102. bool loadIncremental(size_t count_limit);
  103. void doInclude() {
  104. // First, get the filename to include
  105. const MasterToken::StringRegion
  106. filename(lexer_.getNextToken(MasterLexer::QSTRING).
  107. getStringRegion());
  108. // TODO: Handle the case where there's Name after the
  109. // filename, meaning origin. Once $ORIGIN handling is
  110. // done, it should be interconnected somehow.
  111. // Push the filename. We abuse the fact that filename
  112. // may not contain '\0' anywhere in it, so we can
  113. // freely use the filename.beg directly.
  114. pushSource(filename.beg);
  115. // TODO: Eat any extra tokens at the end of line (they
  116. // should not be here, of course).
  117. }
  118. void handleDirective(const char* directive, size_t length) {
  119. // We use strncasecmp, because there seems to be no reasonable
  120. // way to compare strings case-insensitive in C++
  121. // Warning: The order of compared strings does matter. The length
  122. // parameter applies to the first one only.
  123. if (strncasecmp(directive, "INCLUDE", length) == 0) {
  124. doInclude();
  125. } else if (strncasecmp(directive, "ORIGIN", length) == 0) {
  126. // TODO: Implement
  127. isc_throw(isc::NotImplemented,
  128. "Origin directive not implemented yet");
  129. } else if (strncasecmp(directive, "TTL", length) == 0) {
  130. // TODO: Implement
  131. isc_throw(isc::NotImplemented,
  132. "TTL directive not implemented yet");
  133. } else {
  134. isc_throw(InternalException, "Unknown directive '" <<
  135. string(directive, directive + length) << "'");
  136. }
  137. }
  138. private:
  139. MasterLexer lexer_;
  140. const Name zone_origin_;
  141. const RRClass zone_class_;
  142. MasterLoaderCallbacks callbacks_;
  143. AddRRCallback add_callback_;
  144. const MasterLoader::Options options_;
  145. const std::string master_file_;
  146. std::string string_token_;
  147. bool initialized_;
  148. bool ok_; // Is it OK to continue loading?
  149. const bool many_errors_; // Are many errors allowed (or should we abort
  150. // on the first)
  151. size_t source_count_; // How many sources are currently pushed.
  152. public:
  153. bool complete_; // All work done.
  154. bool seen_error_; // Was there at least one error during the
  155. // load?
  156. };
  157. bool
  158. MasterLoader::MasterLoaderImpl::loadIncremental(size_t count_limit) {
  159. if (count_limit == 0) {
  160. isc_throw(isc::InvalidParameter, "Count limit set to 0");
  161. }
  162. if (complete_) {
  163. isc_throw(isc::InvalidOperation,
  164. "Trying to load when already loaded");
  165. }
  166. if (!initialized_) {
  167. pushSource(master_file_);
  168. }
  169. size_t count = 0;
  170. while (ok_ && count < count_limit) {
  171. try {
  172. // Skip all EOLNs (empty lines) and finish on EOF
  173. bool empty = true;
  174. do {
  175. const MasterToken& empty_token(lexer_.getNextToken());
  176. if (empty_token.getType() == MasterToken::END_OF_FILE) {
  177. if (!popSource()) {
  178. return (true);
  179. } else {
  180. // We try to read a token from the popped source
  181. // So retry the loop
  182. continue;
  183. }
  184. }
  185. empty = empty_token.getType() == MasterToken::END_OF_LINE;
  186. } while (empty);
  187. // Return the last token, as it was not empty
  188. lexer_.ungetToken();
  189. const MasterToken::StringRegion&
  190. name_string(lexer_.getNextToken(MasterToken::QSTRING).
  191. getStringRegion());
  192. if (name_string.len > 0 && name_string.beg[0] == '$') {
  193. // This should have either thrown (and the error handler
  194. // will read up until the end of line) or read until the
  195. // end of line.
  196. // Exclude the $ from the string on this point.
  197. handleDirective(name_string.beg + 1, name_string.len - 1);
  198. // So, get to the next line, there's nothing more interesting
  199. // in this one.
  200. continue;
  201. }
  202. const Name name(name_string.beg, name_string.len,
  203. &zone_origin_);
  204. // TODO: Some more flexibility. We don't allow omitting
  205. // anything yet
  206. // The parameters
  207. const RRTTL ttl(getString());
  208. const RRClass rrclass(getString());
  209. const RRType rrtype(getString());
  210. // TODO: Some more validation?
  211. if (rrclass != zone_class_) {
  212. // It doesn't really matter much what type of exception
  213. // we throw, we catch it just below.
  214. isc_throw(isc::BadValue, "Class mismatch: " << rrclass <<
  215. "vs. " << zone_class_);
  216. }
  217. const rdata::RdataPtr data(rdata::createRdata(rrtype, rrclass,
  218. lexer_,
  219. &zone_origin_,
  220. options_,
  221. callbacks_));
  222. // In case we get NULL, it means there was error creating
  223. // the Rdata. The errors should have been reported by
  224. // callbacks_ already. We need to decide if we want to continue
  225. // or not.
  226. if (data) {
  227. add_callback_(name, rrclass, rrtype, ttl, data);
  228. // Good, we loaded another one
  229. ++count;
  230. } else {
  231. seen_error_ = true;
  232. if (!many_errors_) {
  233. ok_ = false;
  234. complete_ = true;
  235. // We don't have the exact error here, but it was reported
  236. // by the error callback.
  237. isc_throw(MasterLoaderError, "Invalid RR data");
  238. }
  239. }
  240. } catch (const MasterLoaderError&) {
  241. // This is a hack. We exclude the MasterLoaderError from the
  242. // below case. Once we restrict the below to some smaller
  243. // exception, we should remove this.
  244. throw;
  245. } catch (const isc::Exception& e) {
  246. // TODO: Once we do #2518, catch only the DNSTextError here,
  247. // not isc::Exception. The rest should be just simply
  248. // propagated.
  249. reportError(lexer_.getSourceName(), lexer_.getSourceLine(),
  250. e.what());
  251. // We want to continue. Try to read until the end of line
  252. bool end = false;
  253. do {
  254. const MasterToken& token(lexer_.getNextToken());
  255. switch (token.getType()) {
  256. case MasterToken::END_OF_FILE:
  257. callbacks_.warning(lexer_.getSourceName(),
  258. lexer_.getSourceLine(),
  259. "Unexpected end ond of file");
  260. // TODO: Try pop in case this is not the only
  261. // source
  262. return (true);
  263. case MasterToken::END_OF_LINE:
  264. end = true;
  265. break;
  266. default:
  267. // Do nothing. This is just to make compiler
  268. // happy
  269. break;
  270. }
  271. } while (!end);
  272. }
  273. }
  274. // When there was a fatal error and ok is false, we say we are done.
  275. return (!ok_);
  276. }
  277. MasterLoader::MasterLoader(const char* master_file,
  278. const Name& zone_origin,
  279. const RRClass& zone_class,
  280. const MasterLoaderCallbacks& callbacks,
  281. const AddRRCallback& add_callback,
  282. Options options)
  283. {
  284. if (add_callback.empty()) {
  285. isc_throw(isc::InvalidParameter, "Empty add RR callback");
  286. }
  287. impl_ = new MasterLoaderImpl(master_file, zone_origin,
  288. zone_class, callbacks, add_callback, options);
  289. }
  290. MasterLoader::MasterLoader(std::istream& stream,
  291. const Name& zone_origin,
  292. const RRClass& zone_class,
  293. const MasterLoaderCallbacks& callbacks,
  294. const AddRRCallback& add_callback,
  295. Options options)
  296. {
  297. if (add_callback.empty()) {
  298. isc_throw(isc::InvalidParameter, "Empty add RR callback");
  299. }
  300. auto_ptr<MasterLoaderImpl> impl(new MasterLoaderImpl("", zone_origin,
  301. zone_class, callbacks,
  302. add_callback,
  303. options));
  304. impl->pushStreamSource(stream);
  305. impl_ = impl.release();
  306. }
  307. MasterLoader::~MasterLoader() {
  308. delete impl_;
  309. }
  310. bool
  311. MasterLoader::loadIncremental(size_t count_limit) {
  312. const bool result = impl_->loadIncremental(count_limit);
  313. impl_->complete_ = result;
  314. return (result);
  315. }
  316. bool
  317. MasterLoader::loadedSucessfully() const {
  318. return (impl_->complete_ && !impl_->seen_error_);
  319. }
  320. } // end namespace dns
  321. } // end namespace isc