Kea 2.5.5
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
29using std::string;
30using std::unique_ptr;
31using std::vector;
32using std::pair;
33using boost::algorithm::iequals;
34using boost::shared_ptr;
35
36namespace isc {
37namespace dns {
38
39namespace {
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.
44class InternalException : public isc::Exception {
45public:
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
60public:
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
156private:
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
245 const MasterToken::StringRegion&
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
510private:
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
541public:
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
550namespace { // begin unnamed namespace
551
604std::string
605genNibbles(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
635std::string
636MasterLoader::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
749void
750MasterLoader::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
863MasterToken
864MasterLoader::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.
877 return (MasterToken(MasterToken::END_OF_LINE));
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.
893 const MasterToken::StringRegion&
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.
905 return (MasterToken(MasterToken::END_OF_LINE));
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);
925 return (MasterToken(MasterToken::END_OF_LINE));
926 }
928 return (initial_token); // empty line
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
939bool
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
1015MasterLoader::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
1029MasterLoader::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
1050bool
1052 const bool result = impl_->loadIncremental(count_limit);
1053 impl_->complete_ = result;
1054 return (result);
1055}
1056
1057bool
1059 return (impl_->complete_ && !impl_->seen_error_);
1060}
1061
1062size_t
1064 return (impl_->getSize());
1065}
1066
1067size_t
1069 return (impl_->getPosition());
1070}
1071
1072} // end namespace dns
1073} // end namespace isc
This is a base class for exceptions thrown from the DNS library module.
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
A generic exception that is thrown if a function is called in a prohibited way.
A generic exception that is thrown if a parameter given to a method or function is considered invalid...
Base class for all sorts of text parse errors.
Exception thrown from a wrapper version of MasterLexer::getNextToken() for non fatal errors.
Definition: master_lexer.h:320
Exception thrown when we fail to read from the input stream or file.
Definition: master_lexer.h:306
@ QSTRING
recognize quoted string
Definition: master_lexer.h:347
@ INITIAL_WS
recognize begin-of-line spaces after an end-of-line
Definition: master_lexer.h:345
void ungetToken()
Return the last token back to the lexer.
size_t getSourceCount() const
Get number of sources inside the lexer.
size_t getTotalSourceSize() const
Return the total size of pushed sources.
bool pushSource(const char *filename, std::string *error=NULL)
Open a file and make it the current input source of MasterLexer.
std::string getSourceName() const
Return the name of the current input source name.
const MasterToken & getNextToken(Options options=NONE)
Parse and return another token from the input.
size_t getPosition() const
Return the position of lexer in the pushed sources so far.
void popSource()
Stop using the most recently opened input source (file or stream).
size_t getSourceLine() const
Return the input source line number.
Set of issue callbacks for a loader.
void error(const std::string &source_name, size_t source_line, const std::string &reason) const
Call callback for serious errors.
void warning(const std::string &source_name, size_t source_line, const std::string &reason) const
Call callback for potential problems.
Error while loading by MasterLoader without specifying the MANY_ERRORS option.
Definition: master_loader.h:22
Private implementation class for the MasterLoader.
bool loadIncremental(size_t count_limit)
Implementation of MasterLoader::loadIncremental()
MasterLoaderImpl(const char *master_file, const Name &zone_origin, const RRClass &zone_class, const MasterLoaderCallbacks &callbacks, const AddRRCallback &add_callback, MasterLoader::Options options)
Constructor.
size_t getPosition() const
Return the line number being parsed in the pushed input sources.
size_t getSize() const
Return the total size of the input sources pushed so far.
void pushSource(const std::string &filename, const Name &current_origin)
Wrapper around MasterLexer::pushSource() (file version)
void pushStreamSource(std::istream &stream)
Wrapper around MasterLexer::pushSource() (stream version)
size_t getPosition() const
Return the position of the loader in zone.
MasterLoader(const char *master_file, const Name &zone_origin, const RRClass &zone_class, const MasterLoaderCallbacks &callbacks, const AddRRCallback &add_callback, Options options=DEFAULT)
Constructor.
bool loadedSuccessfully() const
Was the loading successful?
Options
Options how the parsing should work.
Definition: master_loader.h:39
@ MANY_ERRORS
Lenient mode (see documentation of MasterLoader constructor).
Definition: master_loader.h:41
size_t getSize() const
Return the total size of the zone files and streams.
bool loadIncremental(size_t count_limit)
Load some RRs.
Tokens for MasterLexer.
Definition: master_lexer.h:39
std::string getString() const
Return the value of a string-variant token as a string object.
Definition: master_lexer.h:183
@ INITIAL_WS
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
@ ERROR
Error detected in getting a token.
Definition: master_lexer.h:59
@ END_OF_LINE
End of line detected.
Definition: master_lexer.h:48
@ STRING
A single string.
Definition: master_lexer.h:56
@ QSTRING
A single string quoted by double-quotes (").
Definition: master_lexer.h:57
@ END_OF_FILE
End of file detected.
Definition: master_lexer.h:49
Type getType() const
Return the token type.
Definition: master_lexer.h:157
The Name class encapsulates DNS names.
Definition: name.h:223
std::string toText(bool omit_final_dot=false) const
Convert the Name to a string.
Definition: name.cc:507
The RRClass class encapsulates DNS resource record classes.
Definition: rrclass.h:98
static RRClass * createFromText(const std::string &class_str)
A separate factory of RRClass from text.
Definition: rrclass.cc:59
static RRTTL * createFromText(const std::string &ttlstr)
A separate factory of RRTTL from text.
Definition: rrttl.cc:176
static const RRTTL & MAX_TTL()
The TTL of the max allowable value, per RFC2181 Section 8.
Definition: rrttl.h:274
The RRType class encapsulates DNS resource record types.
Definition: rrtype.h:106
static const RRType & SOA()
Definition: rrtype.h:461
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
@ info
Definition: db_log.h:118
@ error
Definition: db_log.h:116
boost::shared_ptr< const Rdata > ConstRdataPtr
Definition: rdata.h:72
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
boost::shared_ptr< Rdata > RdataPtr
The RdataPtr type is a pointer-like type, pointing to an object of some concrete derived class of Rda...
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.
FlexOptionImplPtr impl
std::string format(const std::string &format, const std::vector< std::string > &args)
Apply Formatting.
Definition: strutil.cc:157
void uppercase(std::string &text)
Uppercase String.
Definition: strutil.h:128
Defines the logger used by the top-level component of kea-lfc.