master_loader.cc 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514
  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/rdataclass.h>
  18. #include <dns/rrttl.h>
  19. #include <dns/rrclass.h>
  20. #include <dns/rrtype.h>
  21. #include <dns/rdata.h>
  22. #include <boost/scoped_ptr.hpp>
  23. #include <string>
  24. #include <memory>
  25. #include <boost/algorithm/string/predicate.hpp> // for iequals
  26. using std::string;
  27. using std::auto_ptr;
  28. using boost::algorithm::iequals;
  29. namespace isc {
  30. namespace dns {
  31. // An internal exception, used to control the code flow in case of errors.
  32. // It is thrown during the loading and caught later, not to be propagated
  33. // outside of the file.
  34. class InternalException : public isc::Exception {
  35. public:
  36. InternalException(const char* filename, size_t line, const char* what) :
  37. Exception(filename, line, what)
  38. {}
  39. };
  40. class MasterLoader::MasterLoaderImpl {
  41. public:
  42. MasterLoaderImpl(const char* master_file,
  43. const Name& zone_origin,
  44. const RRClass& zone_class,
  45. const MasterLoaderCallbacks& callbacks,
  46. const AddRRCallback& add_callback,
  47. MasterLoader::Options options) :
  48. MAX_TTL(0x7fffffff),
  49. lexer_(),
  50. zone_origin_(zone_origin),
  51. zone_class_(zone_class),
  52. callbacks_(callbacks),
  53. add_callback_(add_callback),
  54. options_(options),
  55. master_file_(master_file),
  56. initialized_(false),
  57. ok_(true),
  58. many_errors_((options & MANY_ERRORS) != 0),
  59. source_count_(0),
  60. complete_(false),
  61. seen_error_(false),
  62. warn_rfc1035_ttl_(true)
  63. {}
  64. void pushSource(const std::string& filename) {
  65. std::string error;
  66. if (!lexer_.pushSource(filename.c_str(), &error)) {
  67. if (initialized_) {
  68. isc_throw(InternalException, error.c_str());
  69. } else {
  70. // Top-level file
  71. reportError("", 0, error);
  72. ok_ = false;
  73. }
  74. }
  75. initialized_ = true;
  76. ++source_count_;
  77. }
  78. void pushStreamSource(std::istream& stream) {
  79. lexer_.pushSource(stream);
  80. initialized_ = true;
  81. ++source_count_;
  82. }
  83. bool loadIncremental(size_t count_limit);
  84. private:
  85. void reportError(const std::string& filename, size_t line,
  86. const std::string& reason)
  87. {
  88. seen_error_ = true;
  89. callbacks_.error(filename, line, reason);
  90. if (!many_errors_) {
  91. // In case we don't have the lenient mode, every error is fatal
  92. // and we throw
  93. ok_ = false;
  94. complete_ = true;
  95. isc_throw(MasterLoaderError, reason.c_str());
  96. }
  97. }
  98. bool popSource() {
  99. if (--source_count_ == 0) {
  100. return (false);
  101. }
  102. lexer_.popSource();
  103. return (true);
  104. }
  105. // Get a string token. Handle it as error if it is not string.
  106. const string getString() {
  107. lexer_.getNextToken(MasterToken::STRING).getString(string_token_);
  108. return (string_token_);
  109. }
  110. void doInclude() {
  111. // First, get the filename to include
  112. const string
  113. filename(lexer_.getNextToken(MasterToken::QSTRING).getString());
  114. // There could be an origin (or maybe not). So try looking
  115. const MasterToken name_tok(lexer_.getNextToken(MasterToken::QSTRING,
  116. true));
  117. if (name_tok.getType() == MasterToken::QSTRING ||
  118. name_tok.getType() == MasterToken::STRING) {
  119. // TODO: Handle the origin. Once we complete #2427.
  120. } else {
  121. // We return the newline there. This is because after we pop
  122. // the source, we want to call eatUntilEOL and this would
  123. // eat to the next one.
  124. lexer_.ungetToken();
  125. }
  126. pushSource(filename);
  127. }
  128. // Upper limit check when recognizing a specific TTL value from the
  129. // zone file ($TTL, the RR's TTL field, or the SOA minimum). RFC2181
  130. // Section 8 limits the range of TTL values to 2^31-1 (0x7fffffff),
  131. // and prohibits transmitting a TTL field exceeding this range. We
  132. // guarantee that by limiting the value at the time of zone
  133. // parsing/loading, following what BIND 9 does. Resetting it to 0
  134. // at this point may not be exactly what the RFC states (depending on
  135. // the meaning of 'received'), but the end result would be the same (i.e.,
  136. // the guarantee on transmission). Again, we follow the BIND 9's behavior
  137. // here.
  138. //
  139. // post_parsing is true iff this method is called after parsing the entire
  140. // RR and the lexer is positioned at the next line. It's just for
  141. // calculating the accurate source line when callback is necessary.
  142. void limitTTL(RRTTL& ttl, bool post_parsing) {
  143. if (ttl > MAX_TTL) {
  144. const size_t src_line = lexer_.getSourceLine() -
  145. (post_parsing ? 1 : 0);
  146. callbacks_.warning(lexer_.getSourceName(), src_line,
  147. "TTL " + ttl.toText() + " > MAXTTL, "
  148. "setting to 0 per RFC2181");
  149. ttl = RRTTL(0);
  150. }
  151. }
  152. // Set/reset the default TTL. This should be from either $TTL or SOA
  153. // minimum TTL (it's the caller's responsibility; this method doesn't
  154. // care about where it comes from). see LimitTTL() for parameter
  155. // post_parsing.
  156. void setDefaultTTL(const RRTTL& ttl, bool post_parsing) {
  157. if (!default_ttl_) {
  158. default_ttl_.reset(new RRTTL(ttl));
  159. } else {
  160. *default_ttl_ = ttl;
  161. }
  162. limitTTL(*default_ttl_, post_parsing);
  163. }
  164. // Set/reset the TTL currently being used to the default TTL.
  165. // This can be used as the last resort TTL when no other TTL is known for
  166. // an RR. The caller should guarantee that the default TTL has been
  167. // defined by the time of this method call. Note that this method doesn't
  168. // have to call limitTTL(); it was already applied to the stored default
  169. // TTL.
  170. void setCurrentTTLToDefault() {
  171. if (!current_ttl_) {
  172. current_ttl_.reset(new RRTTL(*default_ttl_));
  173. } else {
  174. *current_ttl_ = *default_ttl_;
  175. }
  176. }
  177. // Try to set/reset the current TTL from candidate TTL text. It's possible
  178. // it does not actually represent a TTL (which is not immediately
  179. // considered an error). Return true iff it's recognized as a valid TTL
  180. // (and only in which case the current TTL is set).
  181. bool setCurrentTTL(const string& ttl_txt) {
  182. // We use the factory version instead of RRTTL constructor as we
  183. // need to expect cases where ttl_txt does not actually represent a TTL
  184. // but an RR class or type.
  185. RRTTL* ttl = RRTTL::createFromText(ttl_txt, current_ttl_.get());
  186. if (ttl != NULL) {
  187. if (!current_ttl_) {
  188. current_ttl_.reset(ttl);
  189. }
  190. limitTTL(*current_ttl_, false);
  191. return (true);
  192. }
  193. return (false);
  194. }
  195. // Determine the TTL of the current RR based on the given parsing context.
  196. //
  197. // explicit_ttl is true iff the TTL is explicitly specified for that RR
  198. // (in which case current_ttl_ is set to that TTL).
  199. // rrtype is the type of the current RR, and rdata is its RDATA. They
  200. // only matter if the type is SOA and no available TTL is known. In this
  201. // case the minimum TTL of the SOA will be used as the TTL of that SOA
  202. // and the default TTL for subsequent RRs.
  203. const RRTTL& getCurrentTTL(bool explicit_ttl, const RRType& rrtype,
  204. const rdata::ConstRdataPtr& rdata) {
  205. // We've completed parsing the full of RR, and the lexer is already
  206. // positioned at the next line. If we need to call callback,
  207. // we need to adjust the line number.
  208. const size_t current_line = lexer_.getSourceLine() - 1;
  209. if (!current_ttl_ && !default_ttl_) {
  210. if (rrtype == RRType::SOA()) {
  211. callbacks_.warning(lexer_.getSourceName(), current_line,
  212. "no TTL specified; "
  213. "using SOA MINTTL instead");
  214. const uint32_t ttl_val =
  215. dynamic_cast<const rdata::generic::SOA&>(*rdata).
  216. getMinimum();
  217. setDefaultTTL(RRTTL(ttl_val), true);
  218. setCurrentTTLToDefault();
  219. } else {
  220. // On catching the exception we'll try to reach EOL again,
  221. // so we need to unget it now.
  222. lexer_.ungetToken();
  223. throw InternalException(__FILE__, __LINE__,
  224. "no TTL specified; load rejected");
  225. }
  226. } else if (!explicit_ttl && default_ttl_) {
  227. setCurrentTTLToDefault();
  228. } else if (!explicit_ttl && warn_rfc1035_ttl_) {
  229. // Omitted (class and) TTL values are default to the last
  230. // explicitly stated values (RFC 1035, Sec. 5.1).
  231. callbacks_.warning(lexer_.getSourceName(), current_line,
  232. "using RFC1035 TTL semantics; default to the "
  233. "last explicitly stated TTL");
  234. warn_rfc1035_ttl_ = false; // we only warn about this once
  235. }
  236. assert(current_ttl_);
  237. return (*current_ttl_);
  238. }
  239. void handleDirective(const char* directive, size_t length) {
  240. if (iequals(directive, "INCLUDE")) {
  241. doInclude();
  242. } else if (iequals(directive, "ORIGIN")) {
  243. // TODO: Implement
  244. isc_throw(isc::NotImplemented,
  245. "Origin directive not implemented yet");
  246. } else if (iequals(directive, "TTL")) {
  247. setDefaultTTL(RRTTL(getString()), false);
  248. eatUntilEOL(true);
  249. } else {
  250. isc_throw(InternalException, "Unknown directive '" <<
  251. string(directive, directive + length) << "'");
  252. }
  253. }
  254. void eatUntilEOL(bool reportExtra) {
  255. // We want to continue. Try to read until the end of line
  256. for (;;) {
  257. const MasterToken& token(lexer_.getNextToken());
  258. switch (token.getType()) {
  259. case MasterToken::END_OF_FILE:
  260. callbacks_.warning(lexer_.getSourceName(),
  261. lexer_.getSourceLine(),
  262. "Unexpected end end of file");
  263. // We don't pop here. The End of file will stay there,
  264. // and we'll handle it in the next iteration of
  265. // loadIncremental properly.
  266. return;
  267. case MasterToken::END_OF_LINE:
  268. // Found the end of the line. Good.
  269. return;
  270. default:
  271. // Some other type of token.
  272. if (reportExtra) {
  273. reportExtra = false;
  274. reportError(lexer_.getSourceName(),
  275. lexer_.getSourceLine(),
  276. "Extra tokens at the end of line");
  277. }
  278. break;
  279. }
  280. }
  281. }
  282. private:
  283. // RFC2181 Section 8 specifies TTLs are unsigned 32-bit integer,
  284. // effectively limiting the maximum value to 2^32-1. This constant
  285. // represent a TTL of the max value.
  286. const RRTTL MAX_TTL;
  287. MasterLexer lexer_;
  288. const Name zone_origin_;
  289. const RRClass zone_class_;
  290. MasterLoaderCallbacks callbacks_;
  291. AddRRCallback add_callback_;
  292. boost::scoped_ptr<RRTTL> default_ttl_; // Default TTL of RRs used when
  293. // unspecified. If NULL no default
  294. // is known.
  295. boost::scoped_ptr<RRTTL> current_ttl_; // The TTL used most recently.
  296. // Initially set to NULL. Once set
  297. // always non NULL.
  298. const MasterLoader::Options options_;
  299. const std::string master_file_;
  300. std::string string_token_;
  301. bool initialized_;
  302. bool ok_; // Is it OK to continue loading?
  303. const bool many_errors_; // Are many errors allowed (or should we abort
  304. // on the first)
  305. size_t source_count_; // How many sources are currently pushed.
  306. public:
  307. bool complete_; // All work done.
  308. bool seen_error_; // Was there at least one error during the
  309. // load?
  310. bool warn_rfc1035_ttl_; // should warn if implicit TTL determination
  311. // from the previous RR is used.
  312. };
  313. bool
  314. MasterLoader::MasterLoaderImpl::loadIncremental(size_t count_limit) {
  315. if (count_limit == 0) {
  316. isc_throw(isc::InvalidParameter, "Count limit set to 0");
  317. }
  318. if (complete_) {
  319. isc_throw(isc::InvalidOperation,
  320. "Trying to load when already loaded");
  321. }
  322. if (!initialized_) {
  323. pushSource(master_file_);
  324. }
  325. size_t count = 0;
  326. while (ok_ && count < count_limit) {
  327. try {
  328. // Skip all EOLNs (empty lines) and finish on EOF
  329. bool empty = true;
  330. do {
  331. const MasterToken& empty_token(lexer_.getNextToken());
  332. if (empty_token.getType() == MasterToken::END_OF_FILE) {
  333. if (!popSource()) {
  334. return (true);
  335. } else {
  336. // We try to read a token from the popped source
  337. // So retry the loop, but first, make sure the source
  338. // is at EOL
  339. eatUntilEOL(true);
  340. continue;
  341. }
  342. }
  343. empty = empty_token.getType() == MasterToken::END_OF_LINE;
  344. } while (empty);
  345. // Return the last token, as it was not empty
  346. lexer_.ungetToken();
  347. const MasterToken::StringRegion&
  348. name_string(lexer_.getNextToken(MasterToken::QSTRING).
  349. getStringRegion());
  350. if (name_string.len > 0 && name_string.beg[0] == '$') {
  351. // This should have either thrown (and the error handler
  352. // will read up until the end of line) or read until the
  353. // end of line.
  354. // Exclude the $ from the string on this point.
  355. handleDirective(name_string.beg + 1, name_string.len - 1);
  356. // So, get to the next line, there's nothing more interesting
  357. // in this one.
  358. continue;
  359. }
  360. const Name name(name_string.beg, name_string.len,
  361. &zone_origin_);
  362. // TODO: Some more flexibility. We don't allow omitting
  363. // anything yet
  364. // The parameters
  365. MasterToken rrparam_token = lexer_.getNextToken();
  366. bool explicit_ttl = false;
  367. if (rrparam_token.getType() == MasterToken::STRING) {
  368. // Try TTL
  369. if (setCurrentTTL(rrparam_token.getString())) {
  370. explicit_ttl = true;
  371. rrparam_token = lexer_.getNextToken();
  372. }
  373. }
  374. const RRClass rrclass(rrparam_token.getString());
  375. const RRType rrtype(getString());
  376. // TODO: Some more validation?
  377. if (rrclass != zone_class_) {
  378. // It doesn't really matter much what type of exception
  379. // we throw, we catch it just below.
  380. isc_throw(isc::BadValue, "Class mismatch: " << rrclass <<
  381. "vs. " << zone_class_);
  382. }
  383. // TODO: Check if it is SOA, it should be at the origin.
  384. const rdata::RdataPtr rdata(rdata::createRdata(rrtype, rrclass,
  385. lexer_,
  386. &zone_origin_,
  387. options_,
  388. callbacks_));
  389. // In case we get NULL, it means there was error creating
  390. // the Rdata. The errors should have been reported by
  391. // callbacks_ already. We need to decide if we want to continue
  392. // or not.
  393. if (rdata) {
  394. add_callback_(name, rrclass, rrtype,
  395. getCurrentTTL(explicit_ttl, rrtype, rdata),
  396. rdata);
  397. // Good, we loaded another one
  398. ++count;
  399. } else {
  400. seen_error_ = true;
  401. if (!many_errors_) {
  402. ok_ = false;
  403. complete_ = true;
  404. // We don't have the exact error here, but it was reported
  405. // by the error callback.
  406. isc_throw(MasterLoaderError, "Invalid RR data");
  407. }
  408. }
  409. } catch (const MasterLoaderError&) {
  410. // This is a hack. We exclude the MasterLoaderError from the
  411. // below case. Once we restrict the below to some smaller
  412. // exception, we should remove this.
  413. throw;
  414. } catch (const isc::Exception& e) {
  415. // TODO: Once we do #2518, catch only the DNSTextError here,
  416. // not isc::Exception. The rest should be just simply
  417. // propagated.
  418. reportError(lexer_.getSourceName(), lexer_.getSourceLine(),
  419. e.what());
  420. eatUntilEOL(false);
  421. }
  422. }
  423. // When there was a fatal error and ok is false, we say we are done.
  424. return (!ok_);
  425. }
  426. MasterLoader::MasterLoader(const char* master_file,
  427. const Name& zone_origin,
  428. const RRClass& zone_class,
  429. const MasterLoaderCallbacks& callbacks,
  430. const AddRRCallback& add_callback,
  431. Options options)
  432. {
  433. if (add_callback.empty()) {
  434. isc_throw(isc::InvalidParameter, "Empty add RR callback");
  435. }
  436. impl_ = new MasterLoaderImpl(master_file, zone_origin,
  437. zone_class, callbacks, add_callback, options);
  438. }
  439. MasterLoader::MasterLoader(std::istream& stream,
  440. const Name& zone_origin,
  441. const RRClass& zone_class,
  442. const MasterLoaderCallbacks& callbacks,
  443. const AddRRCallback& add_callback,
  444. Options options)
  445. {
  446. if (add_callback.empty()) {
  447. isc_throw(isc::InvalidParameter, "Empty add RR callback");
  448. }
  449. auto_ptr<MasterLoaderImpl> impl(new MasterLoaderImpl("", zone_origin,
  450. zone_class, callbacks,
  451. add_callback,
  452. options));
  453. impl->pushStreamSource(stream);
  454. impl_ = impl.release();
  455. }
  456. MasterLoader::~MasterLoader() {
  457. delete impl_;
  458. }
  459. bool
  460. MasterLoader::loadIncremental(size_t count_limit) {
  461. const bool result = impl_->loadIncremental(count_limit);
  462. impl_->complete_ = result;
  463. return (result);
  464. }
  465. bool
  466. MasterLoader::loadedSucessfully() const {
  467. return (impl_->complete_ && !impl_->seen_error_);
  468. }
  469. } // end namespace dns
  470. } // end namespace isc