Kea  2.1.7-git
master_loader.cc
Go to the documentation of this file.
1 // Copyright (C) 2012-2020 Internet Systems Consortium, Inc. ("ISC")
2 //
3 // This Source Code Form is subject to the terms of the Mozilla Public
4 // License, v. 2.0. If a copy of the MPL was not distributed with this
5 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 
7 #include <config.h>
8 
9 #include <dns/master_loader.h>
10 #include <dns/master_lexer.h>
11 #include <dns/name.h>
12 #include <dns/rdataclass.h>
13 #include <dns/rrttl.h>
14 #include <dns/rrclass.h>
15 #include <dns/rrtype.h>
16 #include <dns/rdata.h>
17 
18 #include <boost/format.hpp>
19 #include <boost/algorithm/string/predicate.hpp> // for iequals
20 #include <boost/scoped_ptr.hpp>
21 #include <boost/shared_ptr.hpp>
22 
23 #include <string>
24 #include <memory>
25 #include <vector>
26 
27 #include <cstdio> // for sscanf()
28 
29 using std::string;
30 using std::unique_ptr;
31 using std::vector;
32 using std::pair;
33 using boost::algorithm::iequals;
34 using boost::shared_ptr;
35 
36 namespace isc {
37 namespace dns {
38 
39 namespace {
40 
41 // An internal exception, used to control the code flow in case of errors.
42 // It is thrown during the loading and caught later, not to be propagated
43 // outside of the file.
44 class InternalException : public isc::Exception {
45 public:
46  InternalException(const char* filename, size_t line, const char* what) :
47  Exception(filename, line, what)
48  {}
49 };
50 
51 } // end unnamed namespace
52 
58 // cppcheck-suppress noConstructor
60 public:
81  MasterLoaderImpl(const char* master_file,
82  const Name& zone_origin,
83  const RRClass& zone_class,
84  const MasterLoaderCallbacks& callbacks,
85  const AddRRCallback& add_callback,
86  MasterLoader::Options options) :
87  lexer_(),
88  zone_origin_(zone_origin),
89  active_origin_(zone_origin),
90  zone_class_(zone_class),
91  callbacks_(callbacks),
92  add_callback_(add_callback),
93  options_(options),
94  master_file_(master_file),
95  initialized_(false),
96  ok_(true),
97  many_errors_((options & MANY_ERRORS) != 0),
98  previous_name_(false),
99  complete_(false),
100  seen_error_(false),
101  warn_rfc1035_ttl_(true),
102  rr_count_(0)
103  {}
104 
115  void pushSource(const std::string& filename, const Name& current_origin) {
116  std::string error;
117  if (!lexer_.pushSource(filename.c_str(), &error)) {
118  if (initialized_) {
119  isc_throw(InternalException, error.c_str());
120  } else {
121  // Top-level file
122  reportError("", 0, error);
123  ok_ = false;
124  }
125  }
126  // Store the current status, so we can recover it upon popSource
127  include_info_.push_back(IncludeInfo(current_origin, last_name_));
128  initialized_ = true;
129  previous_name_ = false;
130  }
131 
138  void pushStreamSource(std::istream& stream) {
139  lexer_.pushSource(stream);
140  initialized_ = true;
141  }
142 
146  bool loadIncremental(size_t count_limit);
147 
150  size_t getSize() const { return (lexer_.getTotalSourceSize()); }
151 
154  size_t getPosition() const { return (lexer_.getPosition()); }
155 
156 private:
161  void reportError(const std::string& filename, size_t line,
162  const std::string& reason)
163  {
164  seen_error_ = true;
165  callbacks_.error(filename, line, reason);
166  if (!many_errors_) {
167  // In case we don't have the lenient mode, every error is fatal
168  // and we throw
169  ok_ = false;
170  complete_ = true;
171  isc_throw(MasterLoaderError, reason.c_str());
172  }
173  }
174 
181  bool popSource() {
182  if (lexer_.getSourceCount() == 1) {
183  return (false);
184  }
185  lexer_.popSource();
186  // Restore original origin and last seen name
187 
188  // We move in tandem, there's an extra item included during the
189  // initialization, so we can never run out of them
190  assert(!include_info_.empty());
191  const IncludeInfo& info(include_info_.back());
192  active_origin_ = info.first;
193  last_name_ = info.second;
194  include_info_.pop_back();
195  previous_name_ = false;
196  return (true);
197  }
198 
200  const string getString() {
201  lexer_.getNextToken(MasterToken::STRING).getString(string_token_);
202  return (string_token_);
203  }
204 
219  MasterToken handleInitialToken();
220 
228  std::string generateForIter(const std::string& str, const int it);
229 
233  void doGenerate();
234 
236  void doOrigin(bool is_optional) {
237  // Parse and create the new origin. It is relative to the previous
238  // one.
239  const MasterToken&
240  name_tok(lexer_.getNextToken(MasterToken::QSTRING, is_optional));
241 
242  if (name_tok.getType() == MasterToken::QSTRING ||
243  name_tok.getType() == MasterToken::STRING) {
244 
246  name_string(name_tok.getStringRegion());
247  active_origin_ = Name(name_string.beg, name_string.len,
248  &active_origin_);
249  if (name_string.len > 0 &&
250  name_string.beg[name_string.len - 1] != '.') {
251  callbacks_.warning(lexer_.getSourceName(),
252  lexer_.getSourceLine(),
253  "The new origin is relative, did you really"
254  " mean " + active_origin_.toText() + "?");
255  }
256  } else {
257  // If it is not optional, we must not get anything but
258  // a string token.
259  assert(is_optional);
260 
261  // We return the newline there. This is because we want to
262  // behave the same if there is or isn't the name, leaving the
263  // newline there.
264  lexer_.ungetToken();
265  }
266  }
267 
269  void doInclude() {
270  // First, get the filename to include
271  const string
272  filename(lexer_.getNextToken(MasterToken::QSTRING).getString());
273 
274  // There optionally can be an origin, that applies before the include.
275  // We need to save the currently active origin before calling
276  // doOrigin(), because it would update active_origin_ while we need
277  // to pass the active origin before recognizing the new origin to
278  // pushSource. Note: RFC 1035 is not really clear on this: it reads
279  // "regardless of changes... within the included file", but the new
280  // origin is not really specified "within the included file".
281  // Nevertheless, this behavior is probably more likely to be the
282  // intent of the RFC, and it's compatible with BIND 9.
283  const Name current_origin = active_origin_;
284  doOrigin(true);
285 
286  pushSource(filename, current_origin);
287  }
288 
299  RRType parseRRParams(bool& explicit_ttl, MasterToken rrparam_token) {
300  // Find TTL, class and type. Both TTL and class are
301  // optional and may occur in any order if they exist. TTL
302  // and class come before type which must exist.
303  //
304  // [<TTL>] [<class>] <type> <RDATA>
305  // [<class>] [<TTL>] <type> <RDATA>
306 
307  // named-signzone outputs TTL first, so try parsing it in order
308  // first.
309  if (setCurrentTTL(rrparam_token.getString())) {
310  explicit_ttl = true;
311  rrparam_token = lexer_.getNextToken(MasterToken::STRING);
312  } else {
313  // If it's not a TTL here, continue and try again
314  // after the RR class below.
315  }
316 
317  boost::scoped_ptr<RRClass> rrclass
318  (RRClass::createFromText(rrparam_token.getString()));
319  if (rrclass) {
320  if (*rrclass != zone_class_) {
321  isc_throw(InternalException, "Class mismatch: " << *rrclass <<
322  " vs. " << zone_class_);
323  }
324  rrparam_token = lexer_.getNextToken(MasterToken::STRING);
325  }
326 
327  // If we couldn't parse TTL earlier in the stream (above), try
328  // again at current location.
329  if (!explicit_ttl && setCurrentTTL(rrparam_token.getString())) {
330  explicit_ttl = true;
331  rrparam_token = lexer_.getNextToken(MasterToken::STRING);
332  }
333 
334  // Return the current string token's value as the RRType.
335  return (RRType(rrparam_token.getString()));
336  }
337 
357  void limitTTL(RRTTL& ttl, bool post_parsing) {
358  if (ttl > RRTTL::MAX_TTL()) {
359  const size_t src_line = lexer_.getSourceLine() -
360  (post_parsing ? 1 : 0);
361  callbacks_.warning(lexer_.getSourceName(), src_line,
362  "TTL " + ttl.toText() + " > MAXTTL, "
363  "setting to 0 per RFC2181");
364  ttl = RRTTL(0);
365  }
366  }
367 
373  void setDefaultTTL(const RRTTL& ttl, bool post_parsing) {
374  assignTTL(default_ttl_, ttl);
375  limitTTL(*default_ttl_, post_parsing);
376  }
377 
387  bool setCurrentTTL(const string& ttl_txt) {
388  // We use the factory version instead of RRTTL constructor as we
389  // need to expect cases where ttl_txt does not actually represent a TTL
390  // but an RR class or type.
391  RRTTL* rrttl = RRTTL::createFromText(ttl_txt);
392  if (rrttl) {
393  current_ttl_.reset(rrttl);
394  limitTTL(*current_ttl_, false);
395  return (true);
396  }
397  return (false);
398  }
399 
409  const RRTTL& getCurrentTTL(bool explicit_ttl, const RRType& rrtype,
410  const rdata::ConstRdataPtr& rdata) {
411  // We've completed parsing the full of RR, and the lexer is already
412  // positioned at the next line. If we need to call callback,
413  // we need to adjust the line number.
414  const size_t current_line = lexer_.getSourceLine() - 1;
415 
416  if (!current_ttl_ && !default_ttl_) {
417  if (rrtype == RRType::SOA()) {
418  callbacks_.warning(lexer_.getSourceName(), current_line,
419  "no TTL specified; "
420  "using SOA MINTTL instead");
421  const uint32_t ttl_val =
422  dynamic_cast<const rdata::generic::SOA&>(*rdata).
423  getMinimum();
424  setDefaultTTL(RRTTL(ttl_val), true);
425  assignTTL(current_ttl_, *default_ttl_);
426  } else {
427  // On catching the exception we'll try to reach EOL again,
428  // so we need to unget it now.
429  lexer_.ungetToken();
430  throw InternalException(__FILE__, __LINE__,
431  "no TTL specified; load rejected");
432  }
433  } else if (!explicit_ttl && default_ttl_) {
434  assignTTL(current_ttl_, *default_ttl_);
435  } else if (!explicit_ttl && warn_rfc1035_ttl_) {
436  // Omitted (class and) TTL values are default to the last
437  // explicitly stated values (RFC 1035, Sec. 5.1).
438  callbacks_.warning(lexer_.getSourceName(), current_line,
439  "using RFC1035 TTL semantics; default to the "
440  "last explicitly stated TTL");
441  warn_rfc1035_ttl_ = false; // we only warn about this once
442  }
443  assert(current_ttl_);
444  return (*current_ttl_);
445  }
446 
451  void handleDirective(const char* directive, size_t length) {
452  if (iequals(directive, "INCLUDE")) {
453  doInclude();
454  } else if (iequals(directive, "ORIGIN")) {
455  doOrigin(false);
456  eatUntilEOL(true);
457  } else if (iequals(directive, "GENERATE")) {
458  doGenerate();
459  eatUntilEOL(true);
460  } else if (iequals(directive, "TTL")) {
461  setDefaultTTL(RRTTL(getString()), false);
462  eatUntilEOL(true);
463  } else {
464  isc_throw(InternalException, "Unknown directive '" <<
465  string(directive, directive + length) << "'");
466  }
467  }
468 
470  void eatUntilEOL(bool reportExtra) {
471  // We want to continue. Try to read until the end of line
472  for (;;) {
473  const MasterToken& token(lexer_.getNextToken());
474  switch (token.getType()) {
476  callbacks_.warning(lexer_.getSourceName(),
477  lexer_.getSourceLine(),
478  "File does not end with newline");
479  // We don't pop here. The End of file will stay there,
480  // and we'll handle it in the next iteration of
481  // loadIncremental properly.
482  return;
484  // Found the end of the line. Good.
485  return;
486  default:
487  // Some other type of token.
488  if (reportExtra) {
489  reportExtra = false;
490  reportError(lexer_.getSourceName(),
491  lexer_.getSourceLine(),
492  "Extra tokens at the end of line");
493  }
494  break;
495  }
496  }
497  }
498 
502  static void assignTTL(boost::scoped_ptr<RRTTL>& left, const RRTTL& right) {
503  if (!left) {
504  left.reset(new RRTTL(right));
505  } else {
506  *left = right;
507  }
508  }
509 
510 private:
511  MasterLexer lexer_;
512  const Name zone_origin_;
513  Name active_origin_; // The origin used during parsing
514  // (modifiable by $ORIGIN)
515  shared_ptr<Name> last_name_; // Last seen name (for INITIAL_WS handling)
516  const RRClass zone_class_;
517  MasterLoaderCallbacks callbacks_;
518  const AddRRCallback add_callback_;
519  boost::scoped_ptr<RRTTL> default_ttl_; // Default TTL of RRs used when
520  // unspecified. If NULL no default
521  // is known.
522  boost::scoped_ptr<RRTTL> current_ttl_; // The TTL used most recently.
523  // Initially unset. Once set
524  // always stores a valid
525  // RRTTL.
526  const MasterLoader::Options options_;
527  const std::string master_file_;
528  std::string string_token_;
529  bool initialized_;
530  bool ok_; // Is it OK to continue loading?
531  const bool many_errors_; // Are many errors allowed (or should we abort
532  // on the first)
533  // Some info about the outer files from which we include.
534  // The first one is current origin, the second is the last seen name
535  // in that file.
536  typedef pair<Name, shared_ptr<Name> > IncludeInfo;
537  vector<IncludeInfo> include_info_;
538  bool previous_name_; // True if there was a previous name in this file
539  // (false at the beginning or after an $INCLUDE line)
540 
541 public:
542  bool complete_; // All work done.
543  bool seen_error_; // Was there at least one error during the
544  // load?
545  bool warn_rfc1035_ttl_; // should warn if implicit TTL determination
546  // from the previous RR is used.
547  size_t rr_count_; // number of RRs successfully loaded
548 };
549 
550 namespace { // begin unnamed namespace
551 
604 std::string
605 genNibbles(int num, unsigned int width, bool uppercase) {
606  static const char *hex = "0123456789abcdef0123456789ABCDEF";
607  std::string rstr;
608 
609  do {
610  char ch = hex[(num & 0x0f) + (uppercase ? 16 : 0)];
611  num >>= 4;
612  rstr.push_back(ch);
613 
614  if (width > 0) {
615  --width;
616  }
617 
618  // If width is non zero then we need to add a label separator.
619  // If value is non zero then we need to add another label and
620  // that requires a label separator.
621  if (width > 0 || num != 0) {
622  rstr.push_back('.');
623 
624  if (width > 0) {
625  --width;
626  }
627  }
628  } while ((num != 0) || (width > 0));
629 
630  return (rstr);
631 }
632 
633 } // end unnamed namespace
634 
635 std::string
636 MasterLoader::MasterLoaderImpl::generateForIter(const std::string& str,
637  const int num)
638 {
639  std::string rstr;
640 
641  for (std::string::const_iterator it = str.begin(); it != str.end();) {
642  switch (*it) {
643  case '$':
644  // This is the case when the '$' character is encountered in
645  // the LHS or RHS. A computed value is added in its place in
646  // the generated string.
647  ++it;
648  if ((it != str.end()) && (*it == '$')) {
649  rstr.push_back('$');
650  ++it;
651  continue;
652  }
653 
654  // The str.end() check is required.
655  if ((it == str.end()) || (*it != '{')) {
656  // There is no modifier (between {}), so just copy the
657  // passed number into the generated string.
658  rstr += boost::str(boost::format("%d") % num);
659  } else {
660  // There is a modifier (between {}). Parse it and handle
661  // the various cases below.
662  const char* scan_str =
663  str.c_str() + std::distance(str.begin(), it);
664  int offset = 0;
665  unsigned int width;
666  char base[2] = {'d', 0}; // char plus null byte
667  // cppcheck-suppress invalidscanf_libc
668  const int n = sscanf(scan_str, "{%d,%u,%1[doxXnN]}",
669  &offset, &width, base);
670  switch (n) {
671  case 1:
672  // Only 1 item was matched (the offset). Copy (num +
673  // offset) into the generated string.
674  rstr += boost::str(boost::format("%d") % (num + offset));
675  break;
676 
677  case 2: {
678  // 2 items were matched (the offset and width). Copy
679  // (num + offset) and format it according to the width
680  // into the generated string.
681  const std::string fmt =
682  boost::str(boost::format("%%0%ud") % width);
683  rstr += boost::str(boost::format(fmt) % (num + offset));
684  break;
685  }
686 
687  case 3:
688  // 3 items were matched (offset, width and base).
689  if ((base[0] == 'n') || (base[0] == 'N')) {
690  // The base is requesting nibbles. Format it
691  // specially (see genNibbles() documentation).
692  rstr += genNibbles(num + offset, width, (base[0] == 'N'));
693  } else {
694  // The base is not requesting nibbles. Copy (num +
695  // offset) and format it according to the width
696  // and base into the generated string.
697  const std::string fmt =
698  boost::str(boost::format("%%0%u%c") % width % base[0]);
699  rstr += boost::str(boost::format(fmt) % (num + offset));
700  }
701  break;
702 
703  default:
704  // Any other case in the modifiers is an error.
705  reportError(lexer_.getSourceName(), lexer_.getSourceLine(),
706  "Invalid $GENERATE format modifiers");
707  return ("");
708  }
709 
710  // Find the closing brace. Careful that 'it' can be equal
711  // to str.end() here.
712  while ((it != str.end()) && (*it != '}')) {
713  ++it;
714  }
715  // Skip past the closing brace (if there is one).
716  if (it != str.end()) {
717  ++it;
718  }
719  }
720  break;
721 
722  case '\\':
723  // This is the case when the '\' character is encountered in
724  // the LHS or RHS. The '\' and the following character are
725  // copied as-is into the generated string. This is usually
726  // used for escaping the $ character.
727  rstr.push_back(*it);
728  ++it;
729  if (it == str.end()) {
730  continue;
731  }
732  rstr.push_back(*it);
733  ++it;
734  break;
735 
736  default:
737  // This is the default case that handles all other
738  // characters. They are copied as-is into the generated
739  // string.
740  rstr.push_back(*it);
741  ++it;
742  break;
743  }
744  }
745 
746  return (rstr);
747 }
748 
749 void
750 MasterLoader::MasterLoaderImpl::doGenerate() {
751  // Parse the range token
752  const MasterToken& range_token = lexer_.getNextToken(MasterToken::STRING);
753  if (range_token.getType() != MasterToken::STRING) {
754  reportError(lexer_.getSourceName(), lexer_.getSourceLine(),
755  "Invalid $GENERATE syntax");
756  return;
757  }
758  const std::string range = range_token.getString();
759 
760  // Parse the LHS token
761  const MasterToken& lhs_token = lexer_.getNextToken(MasterToken::STRING);
762  if (lhs_token.getType() != MasterToken::STRING) {
763  reportError(lexer_.getSourceName(), lexer_.getSourceLine(),
764  "Invalid $GENERATE syntax");
765  return;
766  }
767  const std::string lhs = lhs_token.getString();
768 
769  // Parse the TTL, RR class and RR type tokens. Note that TTL and RR
770  // class may come in any order, or may be missing (either or
771  // both). If TTL is missing, we expect that it was either specified
772  // explicitly using $TTL, or is implicitly known from a previous RR,
773  // or that this is the SOA RR from which the MINIMUM field is
774  // used. It's unlikely that $GENERATE will be used with an SOA RR,
775  // but it's possible. The parsing happens within the parseRRParams()
776  // helper method which is called below.
777  const MasterToken& param_token = lexer_.getNextToken(MasterToken::STRING);
778  if (param_token.getType() != MasterToken::STRING) {
779  reportError(lexer_.getSourceName(), lexer_.getSourceLine(),
780  "Invalid $GENERATE syntax");
781  return;
782  }
783 
784  bool explicit_ttl = false;
785  const RRType rrtype = parseRRParams(explicit_ttl, param_token);
786 
787  // Parse the RHS token. It can be a quoted string.
788  const MasterToken& rhs_token = lexer_.getNextToken(MasterToken::QSTRING);
789  if ((rhs_token.getType() != MasterToken::QSTRING) &&
790  (rhs_token.getType() != MasterToken::STRING))
791  {
792  reportError(lexer_.getSourceName(), lexer_.getSourceLine(),
793  "Invalid $GENERATE syntax");
794  return;
795  }
796  const std::string rhs = rhs_token.getString();
797 
798  // Range can be one of two forms: start-stop or start-stop/step. If
799  // the first form is used, then step is set to 1. All of start, stop
800  // and step must be positive.
801  unsigned int start;
802  unsigned int stop;
803  unsigned int step;
804  // cppcheck-suppress invalidscanf_libc
805  const int n = sscanf(range.c_str(), "%u-%u/%u", &start, &stop, &step);
806  if ((n < 2) || (stop < start)) {
807  reportError(lexer_.getSourceName(), lexer_.getSourceLine(),
808  "$GENERATE: invalid range: " + range);
809  return;
810  }
811 
812  if (n == 2) {
813  step = 1;
814  }
815 
816  // Generate and add the records.
817  for (unsigned int i = start; i <= stop; i += step) {
818  // Get generated strings for LHS and RHS. LHS goes to form the
819  // name, RHS goes to form the RDATA of the RR.
820  const std::string generated_name = generateForIter(lhs, i);
821  const std::string generated_rdata = generateForIter(rhs, i);
822  if (generated_name.empty() || generated_rdata.empty()) {
823  // The error should have been sent to the callbacks already
824  // by generateForIter().
825  reportError(lexer_.getSourceName(), lexer_.getSourceLine(),
826  "$GENERATE error");
827  return;
828  }
829 
830  // generateForIter() can return a string with a trailing '.' in
831  // case of a nibble representation. So we cannot use the
832  // relative Name constructor. We use concatenate() which is
833  // expensive, but keeps the generated LHS-based Name within the
834  // active origin.
835  last_name_.reset
836  (new Name(Name(generated_name).concatenate(active_origin_)));
837  previous_name_ = true;
838 
839  const rdata::RdataPtr rdata =
840  rdata::createRdata(rrtype, zone_class_, generated_rdata);
841  // In case we get NULL, it means there was error creating the
842  // Rdata. The errors should have been reported by callbacks_
843  // already. We need to decide if we want to continue or not.
844  if (rdata) {
845  add_callback_(*last_name_, zone_class_, rrtype,
846  getCurrentTTL(explicit_ttl, rrtype, rdata),
847  rdata);
848  // Good, we added another one
849  ++rr_count_;
850  } else {
851  seen_error_ = true;
852  if (!many_errors_) {
853  ok_ = false;
854  complete_ = true;
855  // We don't have the exact error here, but it was
856  // reported by the error callback.
857  isc_throw(MasterLoaderError, "Invalid RR data");
858  }
859  }
860  }
861 }
862 
864 MasterLoader::MasterLoaderImpl::handleInitialToken() {
865  const MasterToken& initial_token =
866  lexer_.getNextToken(MasterLexer::QSTRING | MasterLexer::INITIAL_WS);
867 
868  // The most likely case is INITIAL_WS, and then string/qstring. We
869  // handle them first.
870  if (initial_token.getType() == MasterToken::INITIAL_WS) {
871  const MasterToken& next_token = lexer_.getNextToken();
872  if (next_token.getType() == MasterToken::END_OF_LINE) {
873  return (next_token); // blank line
874  } else if (next_token.getType() == MasterToken::END_OF_FILE) {
875  lexer_.ungetToken(); // handle it in the next iteration.
876  eatUntilEOL(true); // effectively warn about the unexpected EOF.
878  }
879 
880  // This means the same name as previous.
881  if (last_name_.get() == NULL) {
882  isc_throw(InternalException, "No previous name to use in "
883  "place of initial whitespace");
884  } else if (!previous_name_) {
885  callbacks_.warning(lexer_.getSourceName(), lexer_.getSourceLine(),
886  "Owner name omitted around $INCLUDE, the result "
887  "might not be as expected");
888  }
889  return (next_token);
890  } else if (initial_token.getType() == MasterToken::STRING ||
891  initial_token.getType() == MasterToken::QSTRING) {
892  // If it is name (or directive), handle it.
894  name_string(initial_token.getStringRegion());
895 
896  if (name_string.len > 0 && name_string.beg[0] == '$') {
897  // This should have either thrown (and the error handler
898  // will read up until the end of line) or read until the
899  // end of line.
900 
901  // Exclude the $ from the string on this point.
902  handleDirective(name_string.beg + 1, name_string.len - 1);
903  // So, get to the next line, there's nothing more interesting
904  // in this one.
906  }
907 
908  // This should be an RR, starting with an owner name. Construct the
909  // name, and some string token should follow.
910  last_name_.reset(new Name(name_string.beg, name_string.len,
911  &active_origin_));
912  previous_name_ = true;
913  return (lexer_.getNextToken(MasterToken::STRING));
914  }
915 
916  switch (initial_token.getType()) { // handle less common cases
918  if (!popSource()) {
919  return (initial_token);
920  } else {
921  // We try to read a token from the popped source
922  // So continue to the next line of that source, but first, make
923  // sure the source is at EOL
924  eatUntilEOL(true);
926  }
928  return (initial_token); // empty line
929  case MasterToken::ERROR:
930  // Error token here.
931  isc_throw(InternalException, initial_token.getErrorText());
932  default:
933  // Some other token (what could that be?)
934  isc_throw(InternalException, "Parser got confused (unexpected "
935  "token " << initial_token.getType() << ")");
936  }
937 }
938 
939 bool
941  if (count_limit == 0) {
942  isc_throw(isc::InvalidParameter, "Count limit set to 0");
943  }
944  if (complete_) {
946  "Trying to load when already loaded");
947  }
948  if (!initialized_) {
949  pushSource(master_file_, active_origin_);
950  }
951  size_t count = 0;
952  while (ok_ && count < count_limit) {
953  try {
954  const MasterToken next_token = handleInitialToken();
955  if (next_token.getType() == MasterToken::END_OF_FILE) {
956  return (true); // we are done
957  } else if (next_token.getType() == MasterToken::END_OF_LINE) {
958  continue; // nothing more to do in this line
959  }
960  // We are going to parse an RR, have known the owner name,
961  // and are now seeing the next string token in the rest of the RR.
962  assert(next_token.getType() == MasterToken::STRING);
963 
964  bool explicit_ttl = false;
965  const RRType rrtype = parseRRParams(explicit_ttl, next_token);
966  // TODO: Check if it is SOA, it should be at the origin.
967 
968  const rdata::RdataPtr rdata =
969  rdata::createRdata(rrtype, zone_class_, lexer_,
970  &active_origin_, options_, callbacks_);
971 
972  // In case we get NULL, it means there was error creating
973  // the Rdata. The errors should have been reported by
974  // callbacks_ already. We need to decide if we want to continue
975  // or not.
976  if (rdata) {
977  add_callback_(*last_name_, zone_class_, rrtype,
978  getCurrentTTL(explicit_ttl, rrtype, rdata),
979  rdata);
980  // Good, we loaded another one
981  ++count;
982  ++rr_count_;
983  } else {
984  seen_error_ = true;
985  if (!many_errors_) {
986  ok_ = false;
987  complete_ = true;
988  // We don't have the exact error here, but it was reported
989  // by the error callback.
990  isc_throw(MasterLoaderError, "Invalid RR data");
991  }
992  }
993  } catch (const isc::dns::DNSTextError& e) {
994  reportError(lexer_.getSourceName(), lexer_.getSourceLine(),
995  e.what());
996  eatUntilEOL(false);
997  } catch (const MasterLexer::ReadError& e) {
998  reportError(lexer_.getSourceName(), lexer_.getSourceLine(),
999  e.what());
1000  eatUntilEOL(false);
1001  } catch (const MasterLexer::LexerError& e) {
1002  reportError(lexer_.getSourceName(), lexer_.getSourceLine(),
1003  e.what());
1004  eatUntilEOL(false);
1005  } catch (const InternalException& e) {
1006  reportError(lexer_.getSourceName(), lexer_.getSourceLine(),
1007  e.what());
1008  eatUntilEOL(false);
1009  }
1010  }
1011  // When there was a fatal error and ok is false, we say we are done.
1012  return (!ok_);
1013 }
1014 
1015 MasterLoader::MasterLoader(const char* master_file,
1016  const Name& zone_origin,
1017  const RRClass& zone_class,
1018  const MasterLoaderCallbacks& callbacks,
1019  const AddRRCallback& add_callback,
1020  Options options)
1021 {
1022  if (!add_callback) {
1023  isc_throw(isc::InvalidParameter, "Empty add RR callback");
1024  }
1025  impl_ = new MasterLoaderImpl(master_file, zone_origin,
1026  zone_class, callbacks, add_callback, options);
1027 }
1028 
1029 MasterLoader::MasterLoader(std::istream& stream,
1030  const Name& zone_origin,
1031  const RRClass& zone_class,
1032  const MasterLoaderCallbacks& callbacks,
1033  const AddRRCallback& add_callback,
1034  Options options)
1035 {
1036  if (!add_callback) {
1037  isc_throw(isc::InvalidParameter, "Empty add RR callback");
1038  }
1039  unique_ptr<MasterLoaderImpl>
1040  impl(new MasterLoaderImpl("", zone_origin, zone_class,
1041  callbacks, add_callback, options));
1042  impl->pushStreamSource(stream);
1043  impl_ = impl.release();
1044 }
1045 
1047  delete impl_;
1048 }
1049 
1050 bool
1051 MasterLoader::loadIncremental(size_t count_limit) {
1052  const bool result = impl_->loadIncremental(count_limit);
1053  impl_->complete_ = result;
1054  return (result);
1055 }
1056 
1057 bool
1059  return (impl_->complete_ && !impl_->seen_error_);
1060 }
1061 
1062 size_t
1064  return (impl_->getSize());
1065 }
1066 
1067 size_t
1069  return (impl_->getPosition());
1070 }
1071 
1072 } // end namespace dns
1073 } // end namespace isc
The Name class encapsulates DNS names.
Definition: name.h:223
MasterLoaderImpl(const char *master_file, const Name &zone_origin, const RRClass &zone_class, const MasterLoaderCallbacks &callbacks, const AddRRCallback &add_callback, MasterLoader::Options options)
Constructor.
bool loadedSuccessfully() const
Was the loading successful?
static const RRTTL & MAX_TTL()
The TTL of the max allowable value, per RFC2181 Section 8.
Definition: rrttl.h:274
Base class for all sorts of text parse errors.
size_t getSize() const
Return the total size of the input sources pushed so far.
A generic exception that is thrown if a parameter given to a method or function is considered invalid...
static const RRType & SOA()
Definition: rrtype.h:461
std::string getString() const
Return the value of a string-variant token as a string object.
Definition: master_lexer.h:183
boost::shared_ptr< const Rdata > ConstRdataPtr
Definition: rdata.h:72
Error detected in getting a token.
Definition: master_lexer.h:59
Exception thrown from a wrapper version of MasterLexer::getNextToken() for non fatal errors...
Definition: master_lexer.h:320
Private implementation class for the MasterLoader.
RdataPtr createRdata(const RRType &rrtype, const RRClass &rrclass, const std::string &rdata_string)
Create RDATA of a given pair of RR type and class from a string.
Definition: rdata.cc:56
static RRClass * createFromText(const std::string &class_str)
A separate factory of RRClass from text.
Definition: rrclass.cc:59
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
End of line detected.
Definition: master_lexer.h:48
The RRClass class encapsulates DNS resource record classes.
Definition: rrclass.h:98
Options
Options how the parsing should work.
Definition: master_loader.h:39
Type getType() const
Return the token type.
Definition: master_lexer.h:157
End of file detected.
Definition: master_lexer.h:49
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
White spaces at the beginning of a line after an end of line or at the beginning of file (if asked...
Definition: master_lexer.h:50
void uppercase(std::string &text)
Uppercase String.
Definition: strutil.h:127
bool loadIncremental(size_t count_limit)
Implementation of MasterLoader::loadIncremental()
recognize begin-of-line spaces after an end-of-line
Definition: master_lexer.h:345
A single string quoted by double-quotes (").
Definition: master_lexer.h:57
Exception thrown when we fail to read from the input stream or file.
Definition: master_lexer.h:306
The RRTTL class encapsulates TTLs used in DNS resource records.
Definition: rrttl.h:55
size_t getSize() const
Return the total size of the zone files and streams.
This is a base class for exceptions thrown from the DNS library module.
Defines the logger used by the top-level component of kea-lfc.
static RRTTL * createFromText(const std::string &ttlstr)
A separate factory of RRTTL from text.
Definition: rrttl.cc:176
A simple representation of a range of a string.
Definition: master_lexer.h:95
Error while loading by MasterLoader without specifying the MANY_ERRORS option.
Definition: master_loader.h:22
size_t getPosition() const
Return the line number being parsed in the pushed input sources.
FlexOptionImplPtr impl
const std::string toText() const
Convert the RRTTL to a string.
Definition: rrttl.cc:192
The RRType class encapsulates DNS resource record types.
Definition: rrtype.h:106
A generic exception that is thrown if a function is called in a prohibited way.
recognize quoted string
Definition: master_lexer.h:347
Tokens for MasterLexer.
Definition: master_lexer.h:39
const StringRegion & getStringRegion() const
Return the value of a string-variant token.
Definition: master_lexer.h:164
bool loadIncremental(size_t count_limit)
Load some RRs.
MasterLoader(const char *master_file, const Name &zone_origin, const RRClass &zone_class, const MasterLoaderCallbacks &callbacks, const AddRRCallback &add_callback, Options options=DEFAULT)
Constructor.
size_t getPosition() const
Return the position of the loader in zone.
void pushSource(const std::string &filename, const Name &current_origin)
Wrapper around MasterLexer::pushSource() (file version)
Tokenizer for parsing DNS master files.
Definition: master_lexer.h:301
std::function< void(const Name &name, const RRClass &rrclass, const RRType &rrtype, const RRTTL &rrttl, const rdata::RdataPtr &rdata)> AddRRCallback
Type of callback to add a RR.
Set of issue callbacks for a loader.
std::string getErrorText() const
Return a textual description of the error of a error type token.
boost::shared_ptr< Rdata > RdataPtr
The RdataPtr type is a pointer-like type, pointing to an object of some concrete derived class of Rda...
void pushStreamSource(std::istream &stream)
Wrapper around MasterLexer::pushSource() (stream version)
std::string format(const std::string &format, const std::vector< std::string > &args)
Apply Formatting.
Definition: strutil.cc:157
~MasterLoader()
Destructor.