master_loader.cc 13 KB

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