1 // Copyright (C) 2012-2021 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_callbacks.h>
10 #include <dns/master_loader.h>
11 #include <dns/rrtype.h>
12 #include <dns/rrset.h>
13 #include <dns/rrclass.h>
14 #include <dns/rrttl.h>
15 #include <dns/name.h>
16 #include <dns/rdata.h>
17 
18 #include <gtest/gtest.h>
19 
20 #include <boost/lexical_cast.hpp>
21 #include <boost/scoped_ptr.hpp>
22 
23 #include <functional>
24 #include <string>
25 #include <vector>
26 #include <list>
27 #include <sstream>
28 
29 using namespace isc::dns;
30 using std::vector;
31 using std::string;
32 using std::list;
33 using std::stringstream;
34 using std::endl;
35 using boost::lexical_cast;
36 namespace ph = std::placeholders;
37 
38 namespace {
39 class MasterLoaderTest : public ::testing::Test {
40 public:
MasterLoaderTest()41     MasterLoaderTest() :
42         callbacks_(std::bind(&MasterLoaderTest::callback, this,
43                              &errors_, ph::_1, ph::_2, ph::_3),
44                    std::bind(&MasterLoaderTest::callback, this,
45                              &warnings_, ph::_1, ph::_2, ph::_3))
46     {}
47 
TearDown()48     void TearDown() {
49         // Check there are no more RRs we didn't expect
50         EXPECT_TRUE(rrsets_.empty());
51     }
52 
53     /// Concatenate file, line, and reason, and add it to either errors
54     /// or warnings
callback(vector<string> * target,const std::string & file,size_t line,const std::string & reason)55     void callback(vector<string>* target, const std::string& file, size_t line,
56                   const std::string& reason)
57     {
58         std::stringstream ss;
59         ss << reason << " [" << file << ":" << line << "]";
60         target->push_back(ss.str());
61     }
62 
addRRset(const Name & name,const RRClass & rrclass,const RRType & rrtype,const RRTTL & rrttl,const rdata::RdataPtr & data)63     void addRRset(const Name& name, const RRClass& rrclass,
64                   const RRType& rrtype, const RRTTL& rrttl,
65                   const rdata::RdataPtr& data) {
66         const RRsetPtr rrset(new BasicRRset(name, rrclass, rrtype, rrttl));
67         rrset->addRdata(data);
68         rrsets_.push_back(rrset);
69     }
70 
setLoader(const char * file,const Name & origin,const RRClass & rrclass,const MasterLoader::Options options)71     void setLoader(const char* file, const Name& origin,
72                    const RRClass& rrclass, const MasterLoader::Options options)
73     {
74         loader_.reset(new MasterLoader(file, origin, rrclass, callbacks_,
75                                        std::bind(&MasterLoaderTest::addRRset,
76                                                  this, ph::_1, ph::_2, ph::_3,
77                                                  ph::_4, ph::_5),
78                                        options));
79     }
80 
setLoader(std::istream & stream,const Name & origin,const RRClass & rrclass,const MasterLoader::Options options)81     void setLoader(std::istream& stream, const Name& origin,
82                    const RRClass& rrclass, const MasterLoader::Options options)
83     {
84         loader_.reset(new MasterLoader(stream, origin, rrclass, callbacks_,
85                                        std::bind(&MasterLoaderTest::addRRset,
86                                                  this, ph::_1, ph::_2, ph::_3,
87                                                  ph::_4, ph::_5),
88                                        options));
89     }
90 
prepareZone(const string & line,bool include_last)91     static string prepareZone(const string& line, bool include_last) {
92         string result;
93         result += "example.org. 3600 IN SOA ns1.example.org. "
94             "admin.example.org. 1234 3600 1800 2419200 7200\n";
95         result += line;
96         if (include_last) {
97             result += "\n";
98             result += "correct 3600    IN  A 192.0.2.2\n";
99         }
100         return (result);
101     }
102 
clear()103     void clear() {
104         warnings_.clear();
105         errors_.clear();
106         rrsets_.clear();
107     }
108 
109     // Check the next RR in the ones produced by the loader
110     // Other than passed arguments are checked to be the default for the tests
checkRR(const string & name,const RRType & type,const string & data,const RRTTL & rrttl=RRTTL (3600))111     void checkRR(const string& name, const RRType& type, const string& data,
112                  const RRTTL& rrttl = RRTTL(3600)) {
113         ASSERT_FALSE(rrsets_.empty());
114         RRsetPtr current = rrsets_.front();
115         rrsets_.pop_front();
116 
117         EXPECT_EQ(Name(name), current->getName());
118         EXPECT_EQ(type, current->getType());
119         EXPECT_EQ(RRClass::IN(), current->getClass());
120         EXPECT_EQ(rrttl, current->getTTL());
121         ASSERT_EQ(1, current->getRdataCount());
122         EXPECT_EQ(0, isc::dns::rdata::createRdata(type, RRClass::IN(), data)->
123                   compare(current->getRdataIterator()->getCurrent()))
124             << data << " vs. "
125             << current->getRdataIterator()->getCurrent().toText();
126     }
127 
checkBasicRRs()128     void checkBasicRRs() {
129         checkRR("example.org", RRType::SOA(),
130                 "ns1.example.org. admin.example.org. "
131                 "1234 3600 1800 2419200 7200");
132         checkRR("example.org", RRType::NS(), "ns1.example.org.");
133         checkRR("www.example.org", RRType::A(), "192.0.2.1");
134         checkRR("www.example.org", RRType::AAAA(), "2001:db8::1");
135     }
136 
checkARR(const string & name)137     void checkARR(const string& name) {
138         checkRR(name, RRType::A(), "192.0.2.1");
139     }
140 
141     MasterLoaderCallbacks callbacks_;
142     boost::scoped_ptr<MasterLoader> loader_;
143     vector<string> errors_;
144     vector<string> warnings_;
145     list<RRsetPtr> rrsets_;
146 };
147 
148 // Test simple loading. The zone file contains no tricky things, and nothing is
149 // omitted. No RRset contains more than one RR Also no errors or warnings.
TEST_F(MasterLoaderTest,basicLoad)150 TEST_F(MasterLoaderTest, basicLoad) {
151     setLoader(TEST_DATA_SRCDIR "/example.org", Name("example.org."),
152               RRClass::IN(), MasterLoader::MANY_ERRORS);
153 
154     EXPECT_FALSE(loader_->loadedSuccessfully());
155 
156     // The following three should be set to 0 initially in case the loader
157     // is constructed from a file name.
158     EXPECT_EQ(0, loader_->getSize());
159     EXPECT_EQ(0, loader_->getPosition());
160 
161     loader_->load();
162     EXPECT_TRUE(loader_->loadedSuccessfully());
163 
164     EXPECT_TRUE(errors_.empty());
165     EXPECT_TRUE(warnings_.empty());
166 
167     // Hardcode expected values taken from the test data file, assuming it
168     // won't change too often.
169     EXPECT_EQ(550, loader_->getSize());
170     EXPECT_EQ(550, loader_->getPosition());
171 
172     checkBasicRRs();
173 }
174 
175 // Test the $INCLUDE directive
TEST_F(MasterLoaderTest,include)176 TEST_F(MasterLoaderTest, include) {
177     // Test various cases of include
178     const char* includes[] = {
179         "$include",
180         "$INCLUDE",
181         "$Include",
182         "$InCluDe",
183         "\"$INCLUDE\"",
184         NULL
185     };
186     for (const char** include = includes; *include != NULL; ++include) {
187         SCOPED_TRACE(*include);
188 
189         clear();
190         // Prepare input source that has the include and some more data
191         // below (to see it returns back to the original source).
192         const string include_str = string(*include) + " " +
193             TEST_DATA_SRCDIR + "/example.org\nwww 3600 IN AAAA 2001:db8::1\n";
194         stringstream ss(include_str);
195         setLoader(ss, Name("example.org."), RRClass::IN(),
196                   MasterLoader::MANY_ERRORS);
197 
198         loader_->load();
199         EXPECT_TRUE(loader_->loadedSuccessfully());
200         EXPECT_TRUE(errors_.empty());
201         EXPECT_TRUE(warnings_.empty());
202 
203         checkBasicRRs();
204         checkRR("www.example.org", RRType::AAAA(), "2001:db8::1");
205     }
206 }
207 
TEST_F(MasterLoaderTest,includeAndIncremental)208 TEST_F(MasterLoaderTest, includeAndIncremental) {
209     // Check getSize() and getPosition() are adjusted before and after
210     // $INCLUDE.
211     const string first_rr = "before.example.org. 0 A 192.0.2.1\n";
212     const string include_str = "$INCLUDE " TEST_DATA_SRCDIR "/example.org";
213     const string zone_data = first_rr + include_str + "\n" +
214         "www 3600 IN AAAA 2001:db8::1\n";
215     stringstream ss(zone_data);
216     setLoader(ss, Name("example.org."), RRClass::IN(), MasterLoader::DEFAULT);
217 
218     // On construction, getSize() returns the size of the data (exclude the
219     // the file to be included); position is set to 0.
220     EXPECT_EQ(zone_data.size(), loader_->getSize());
221     EXPECT_EQ(0, loader_->getPosition());
222 
223     // Read the first RR.  getSize() doesn't change; position should be
224     // at the end of the first line.
225     loader_->loadIncremental(1);
226     EXPECT_EQ(zone_data.size(), loader_->getSize());
227     EXPECT_EQ(first_rr.size(), loader_->getPosition());
228 
229     // Read next 4.  It includes $INCLUDE processing.  Magic number of 550
230     // is the size of the test zone file (see above); 507 is the position in
231     // the file at the end of 4th RR (due to extra comments it's smaller than
232     // the file size).
233     loader_->loadIncremental(4);
234     EXPECT_EQ(zone_data.size() + 550, loader_->getSize());
235     EXPECT_EQ(first_rr.size() + include_str.size() + 507,
236               loader_->getPosition());
237 
238     // Read the last one.  At this point getSize and getPosition return
239     // the same value, indicating progress of 100%.
240     loader_->loadIncremental(1);
241     EXPECT_EQ(zone_data.size() + 550, loader_->getSize());
242     EXPECT_EQ(zone_data.size() + 550, loader_->getPosition());
243 
244     // we were not interested in checking RRs in this test.  clear them to
245     // not confuse TearDown().
246     rrsets_.clear();
247 }
248 
249 // A commonly used helper to check callback message.
250 void
checkCallbackMessage(const string & actual_msg,const string & expected_msg,size_t expected_line)251 checkCallbackMessage(const string& actual_msg, const string& expected_msg,
252                      size_t expected_line) {
253     // The actual message should begin with the expected message.
254     EXPECT_EQ(0, actual_msg.find(expected_msg)) << "actual message: " <<
255                                                 actual_msg << " expected: " <<
256                                                 expected_msg;
257 
258     // and it should end with "...:<line_num>]"
259     const string line_desc = ":" + lexical_cast<string>(expected_line) + "]";
260     EXPECT_EQ(actual_msg.size() - line_desc.size(),
261               actual_msg.find(line_desc)) << "Expected on line " <<
262         expected_line;
263 }
264 
TEST_F(MasterLoaderTest,origin)265 TEST_F(MasterLoaderTest, origin) {
266     // Various forms of the directive
267     const char* origins[] = {
268         "$origin",
269         "$ORIGIN",
270         "$Origin",
271         "$OrigiN",
272         "\"$ORIGIN\"",
273         NULL
274     };
275     for (const char** origin = origins; *origin != NULL; ++origin) {
276         SCOPED_TRACE(*origin);
277 
278         clear();
279         const string directive = *origin;
280         const string input =
281             "@  1H  IN  A   192.0.2.1\n" +
282             directive + " sub.example.org.\n"
283             "\"www\"    1H  IN  A   192.0.2.1\n" +
284             // Relative name in the origin
285             directive + " relative\n"
286             "@  1H  IN  A   192.0.2.1\n"
287             // Origin is _not_ used here (absolute name)
288             "noorigin.example.org.  60M IN  A   192.0.2.1\n";
289         stringstream ss(input);
290         setLoader(ss, Name("example.org."), RRClass::IN(),
291                   MasterLoader::MANY_ERRORS);
292 
293         loader_->load();
294         EXPECT_TRUE(loader_->loadedSuccessfully());
295         EXPECT_TRUE(errors_.empty());
296         // There's a relative origin in it, we warn about that.
297         EXPECT_EQ(1, warnings_.size());
298         checkCallbackMessage(warnings_.at(0),
299                              "The new origin is relative, did you really mean "
300                              "relative.sub.example.org.?", 4);
301 
302         checkARR("example.org");
303         checkARR("www.sub.example.org");
304         checkARR("relative.sub.example.org");
305         checkARR("noorigin.example.org");
306     }
307 }
308 
TEST_F(MasterLoaderTest,generate)309 TEST_F(MasterLoaderTest, generate) {
310     // Various forms of the directive
311     const char* generates[] = {
312         "$generate",
313         "$GENERATE",
314         "$Generate",
315         "$GeneratE",
316         "\"$GENERATE\"",
317         NULL
318     };
319     for (const char** generate = generates; *generate != NULL; ++generate) {
320         SCOPED_TRACE(*generate);
321 
322         clear();
323         const string directive = *generate;
324         const string input =
325           "$ORIGIN example.org.\n"
326           "before.example.org. 3600 IN A 192.0.2.0\n" +
327           directive + " 3-5 host$ A 192.0.2.$\n" +
328           "after.example.org. 3600 IN A 192.0.2.255\n";
329         stringstream ss(input);
330         setLoader(ss, Name("example.org."), RRClass::IN(),
331                   MasterLoader::MANY_ERRORS);
332 
333         loader_->load();
334         EXPECT_TRUE(loader_->loadedSuccessfully());
335         EXPECT_TRUE(errors_.empty());
336 
337         // The "before" and "after" scaffolding below checks that no
338         // extra records are added by $GENERATE outside the requested
339         // range.
340         checkRR("before.example.org", RRType::A(), "192.0.2.0");
341         checkRR("host3.example.org", RRType::A(), "192.0.2.3");
342         checkRR("host4.example.org", RRType::A(), "192.0.2.4");
343         checkRR("host5.example.org", RRType::A(), "192.0.2.5");
344         checkRR("after.example.org", RRType::A(), "192.0.2.255");
345     }
346 }
347 
TEST_F(MasterLoaderTest,generateRelativeLHS)348 TEST_F(MasterLoaderTest, generateRelativeLHS) {
349     const string input =
350         "$ORIGIN example.org.\n"
351         "$GENERATE 1-2 @ 3600 NS ns$.example.org.\n";
352     stringstream ss(input);
353     setLoader(ss, Name("example.org."), RRClass::IN(),
354               MasterLoader::MANY_ERRORS);
355 
356     loader_->load();
357     EXPECT_TRUE(loader_->loadedSuccessfully());
358     EXPECT_TRUE(errors_.empty());
359 
360     checkRR("example.org", RRType::NS(), "ns1.example.org.");
361     checkRR("example.org", RRType::NS(), "ns2.example.org.");
362 }
363 
TEST_F(MasterLoaderTest,generateInFront)364 TEST_F(MasterLoaderTest, generateInFront) {
365     // $ is in the front
366     const string input =
367         "$ORIGIN example.org.\n"
368         "$GENERATE 9-10 $host 3600 TXT \"$ pomegranate\"\n";
369     stringstream ss(input);
370     setLoader(ss, Name("example.org."), RRClass::IN(),
371               MasterLoader::MANY_ERRORS);
372 
373     loader_->load();
374     EXPECT_TRUE(loader_->loadedSuccessfully());
375     EXPECT_TRUE(errors_.empty());
376 
377     checkRR("9host.example.org", RRType::TXT(), "9 pomegranate");
378     checkRR("10host.example.org", RRType::TXT(), "10 pomegranate");
379 }
380 
TEST_F(MasterLoaderTest,generateInMiddle)381 TEST_F(MasterLoaderTest, generateInMiddle) {
382     // $ is in the middle
383     const string input =
384         "$ORIGIN example.org.\n"
385         "$GENERATE 9-10 num$-host 3600 TXT \"This is $ pomegranate\"\n";
386     stringstream ss(input);
387     setLoader(ss, Name("example.org."), RRClass::IN(),
388               MasterLoader::MANY_ERRORS);
389 
390     loader_->load();
391     EXPECT_TRUE(loader_->loadedSuccessfully());
392     EXPECT_TRUE(errors_.empty());
393 
394     checkRR("num9-host.example.org", RRType::TXT(), "This is 9 pomegranate");
395     checkRR("num10-host.example.org", RRType::TXT(), "This is 10 pomegranate");
396 }
397 
TEST_F(MasterLoaderTest,generateAtEnd)398 TEST_F(MasterLoaderTest, generateAtEnd) {
399     // $ is at the end
400     const string input =
401         "$ORIGIN example.org.\n"
402         "$GENERATE 9-10 num$-host 3600 TXT Pomegranate$\n";
403     stringstream ss(input);
404     setLoader(ss, Name("example.org."), RRClass::IN(),
405               MasterLoader::MANY_ERRORS);
406 
407     loader_->load();
408     EXPECT_TRUE(loader_->loadedSuccessfully());
409     EXPECT_TRUE(errors_.empty());
410 
411     checkRR("num9-host.example.org", RRType::TXT(), "Pomegranate9");
412     checkRR("num10-host.example.org", RRType::TXT(), "Pomegranate10");
413 }
414 
TEST_F(MasterLoaderTest,generateStripsQuotes)415 TEST_F(MasterLoaderTest, generateStripsQuotes) {
416     const string input =
417         "$ORIGIN example.org.\n"
418         "$GENERATE 1-2 @ 3600 MX \"$ mx$.example.org.\"\n";
419     stringstream ss(input);
420     setLoader(ss, Name("example.org."), RRClass::IN(),
421               MasterLoader::MANY_ERRORS);
422 
423     loader_->load();
424     EXPECT_TRUE(loader_->loadedSuccessfully());
425     EXPECT_TRUE(errors_.empty());
426 
427     checkRR("example.org", RRType::MX(), "1 mx1.example.org.");
428     checkRR("example.org", RRType::MX(), "2 mx2.example.org.");
429 }
430 
TEST_F(MasterLoaderTest,generateWithDoublePlaceholder)431 TEST_F(MasterLoaderTest, generateWithDoublePlaceholder) {
432     const string input =
433         "$ORIGIN example.org.\n"
434         "$GENERATE 9-10 host$ 3600 TXT \"This is $$ pomegranate\"\n";
435     stringstream ss(input);
436     setLoader(ss, Name("example.org."), RRClass::IN(),
437               MasterLoader::MANY_ERRORS);
438 
439     loader_->load();
440     EXPECT_TRUE(loader_->loadedSuccessfully());
441     EXPECT_TRUE(errors_.empty());
442 
443     checkRR("host9.example.org", RRType::TXT(), "This is $ pomegranate");
444     checkRR("host10.example.org", RRType::TXT(), "This is $ pomegranate");
445 }
446 
TEST_F(MasterLoaderTest,generateWithEscape)447 TEST_F(MasterLoaderTest, generateWithEscape) {
448     const string input =
449         "$ORIGIN example.org.\n"
450         "$GENERATE 9-10 host$ 3600 TXT \"This is \\$\\pomegranate\"\n";
451     stringstream ss(input);
452     setLoader(ss, Name("example.org."), RRClass::IN(),
453               MasterLoader::MANY_ERRORS);
454 
455     loader_->load();
456     EXPECT_TRUE(loader_->loadedSuccessfully());
457     EXPECT_TRUE(errors_.empty());
458 
459     checkRR("host9.example.org", RRType::TXT(), "This is \\$\\pomegranate");
460     checkRR("host10.example.org", RRType::TXT(), "This is \\$\\pomegranate");
461 }
462 
TEST_F(MasterLoaderTest,generateWithParams)463 TEST_F(MasterLoaderTest, generateWithParams) {
464     const string input =
465         "$ORIGIN example.org.\n"
466         "$TTL 3600\n"
467         "$GENERATE 2-3 host$ A 192.0.2.$\n"
468         "$GENERATE 5-6 host$ 3600 A 192.0.2.$\n"
469         "$GENERATE 8-9 host$ IN A 192.0.2.$\n"
470         "$GENERATE 11-12 host$ IN 3600 A 192.0.2.$\n"
471         "$GENERATE 14-15 host$ 3600 IN A 192.0.2.$\n";
472     stringstream ss(input);
473     setLoader(ss, Name("example.org."), RRClass::IN(),
474               MasterLoader::MANY_ERRORS);
475 
476     loader_->load();
477     EXPECT_TRUE(loader_->loadedSuccessfully());
478     EXPECT_TRUE(errors_.empty());
479 
480     checkRR("host2.example.org", RRType::A(), "192.0.2.2");
481     checkRR("host3.example.org", RRType::A(), "192.0.2.3");
482 
483     checkRR("host5.example.org", RRType::A(), "192.0.2.5");
484     checkRR("host6.example.org", RRType::A(), "192.0.2.6");
485 
486     checkRR("host8.example.org", RRType::A(), "192.0.2.8");
487     checkRR("host9.example.org", RRType::A(), "192.0.2.9");
488 
489     checkRR("host11.example.org", RRType::A(), "192.0.2.11");
490     checkRR("host12.example.org", RRType::A(), "192.0.2.12");
491 
492     checkRR("host14.example.org", RRType::A(), "192.0.2.14");
493     checkRR("host15.example.org", RRType::A(), "192.0.2.15");
494 }
495 
TEST_F(MasterLoaderTest,generateWithStep)496 TEST_F(MasterLoaderTest, generateWithStep) {
497     const string input =
498         "$ORIGIN example.org.\n"
499         "$GENERATE 2-9/2 host$ 3600 A 192.0.2.$\n"
500         "$GENERATE 12-21/3 host$ 3600 A 192.0.2.$\n"
501         "$GENERATE 30-31/1 host$ 3600 A 192.0.2.$\n";
502     stringstream ss(input);
503     setLoader(ss, Name("example.org."), RRClass::IN(),
504               MasterLoader::MANY_ERRORS);
505 
506     loader_->load();
507     EXPECT_TRUE(loader_->loadedSuccessfully());
508     EXPECT_TRUE(errors_.empty());
509 
510     checkRR("host2.example.org", RRType::A(), "192.0.2.2");
511     checkRR("host4.example.org", RRType::A(), "192.0.2.4");
512     checkRR("host6.example.org", RRType::A(), "192.0.2.6");
513     checkRR("host8.example.org", RRType::A(), "192.0.2.8");
514 
515     checkRR("host12.example.org", RRType::A(), "192.0.2.12");
516     checkRR("host15.example.org", RRType::A(), "192.0.2.15");
517     checkRR("host18.example.org", RRType::A(), "192.0.2.18");
518     checkRR("host21.example.org", RRType::A(), "192.0.2.21");
519 
520     checkRR("host30.example.org", RRType::A(), "192.0.2.30");
521     checkRR("host31.example.org", RRType::A(), "192.0.2.31");
522 }
523 
TEST_F(MasterLoaderTest,generateWithModifiers)524 TEST_F(MasterLoaderTest, generateWithModifiers) {
525     const string input =
526         "$ORIGIN example.org.\n"
527         "$TTL 3600\n"
528 
529         // Use a positive delta of 1 in the LHS and a negative delta of
530         // -1 in the RHS
531         "$GENERATE 2-9/2 host${1} A 192.0.2.${-1}\n"
532 
533         "$GENERATE 10-12 host${0,4} A 192.0.2.$\n"
534         "$GENERATE 14-15 host${0,4,d} A 192.0.2.$\n"
535 
536         // Names are case-insensitive, so we use TXT's RDATA to check
537         // case with hex representation.
538         "$GENERATE 30-31 host$ TXT \"Value ${0,4,x}\"\n"
539         "$GENERATE 42-43 host$ TXT \"Value ${0,4,X}\"\n"
540 
541         // Octal does not use any alphabets
542         "$GENERATE 45-46 host${0,4,o} A 192.0.2.$\n"
543 
544         // Here, the LHS has a trailing dot (which would result in an
545         // out-of-zone name), but that should be handled as a relative
546         // name.
547         "$GENERATE 90-92 ${0,8,n} A 192.0.2.$\n"
548 
549         // Here, the LHS has no trailing dot, and results in the same
550         // number of labels as width=8 above.
551         "$GENERATE 94-96 ${0,7,n} A 192.0.2.$\n"
552 
553         // Names are case-insensitive, so we use TXT's RDATA to check
554         // case with nibble representation.
555         "$GENERATE 106-107 host$ TXT \"Value ${0,9,n}\"\n"
556         "$GENERATE 109-110 host$ TXT \"Value ${0,9,N}\"\n"
557 
558         // Junk type will not parse and 'd' is assumed. No error is
559         // generated (this is to match BIND 9 behavior).
560         "$GENERATE 200-201 host${0,4,j} A 192.0.2.$\n";
561     stringstream ss(input);
562     setLoader(ss, Name("example.org."), RRClass::IN(),
563               MasterLoader::MANY_ERRORS);
564 
565     loader_->load();
566     EXPECT_TRUE(loader_->loadedSuccessfully());
567     EXPECT_TRUE(errors_.empty());
568 
569     checkRR("host3.example.org", RRType::A(), "192.0.2.1");
570     checkRR("host5.example.org", RRType::A(), "192.0.2.3");
571     checkRR("host7.example.org", RRType::A(), "192.0.2.5");
572     checkRR("host9.example.org", RRType::A(), "192.0.2.7");
573 
574     checkRR("host0010.example.org", RRType::A(), "192.0.2.10");
575     checkRR("host0011.example.org", RRType::A(), "192.0.2.11");
576     checkRR("host0012.example.org", RRType::A(), "192.0.2.12");
577 
578     checkRR("host0014.example.org", RRType::A(), "192.0.2.14");
579     checkRR("host0015.example.org", RRType::A(), "192.0.2.15");
580 
581     checkRR("host30.example.org", RRType::TXT(), "Value 001e");
582     checkRR("host31.example.org", RRType::TXT(), "Value 001f");
583 
584     checkRR("host42.example.org", RRType::TXT(), "Value 002A");
585     checkRR("host43.example.org", RRType::TXT(), "Value 002B");
586 
587     checkRR("host0055.example.org", RRType::A(), "192.0.2.45");
588     checkRR("host0056.example.org", RRType::A(), "192.0.2.46");
589 
590     checkRR("a.5.0.0.example.org", RRType::A(), "192.0.2.90");
591     checkRR("b.5.0.0.example.org", RRType::A(), "192.0.2.91");
592     checkRR("c.5.0.0.example.org", RRType::A(), "192.0.2.92");
593 
594     checkRR("e.5.0.0.example.org", RRType::A(), "192.0.2.94");
595     checkRR("f.5.0.0.example.org", RRType::A(), "192.0.2.95");
596     checkRR("0.6.0.0.example.org", RRType::A(), "192.0.2.96");
597 
598     checkRR("host106.example.org", RRType::TXT(), "Value a.6.0.0.0");
599     checkRR("host107.example.org", RRType::TXT(), "Value b.6.0.0.0");
600     checkRR("host109.example.org", RRType::TXT(), "Value D.6.0.0.0");
601     checkRR("host110.example.org", RRType::TXT(), "Value E.6.0.0.0");
602 
603     checkRR("host0200.example.org", RRType::A(), "192.0.2.200");
604     checkRR("host0201.example.org", RRType::A(), "192.0.2.201");
605 }
606 
TEST_F(MasterLoaderTest,generateWithNoModifiers)607 TEST_F(MasterLoaderTest, generateWithNoModifiers) {
608     const string input =
609         "$ORIGIN example.org.\n"
610         "$TTL 3600\n"
611         "$GENERATE 10-12 host${} A 192.0.2.$\n";
612     stringstream ss(input);
613     setLoader(ss, Name("example.org."), RRClass::IN(),
614               MasterLoader::MANY_ERRORS);
615 
616     loader_->load();
617     EXPECT_FALSE(loader_->loadedSuccessfully());
618     ASSERT_EQ(2, errors_.size()); // For the broken GENERATE
619     EXPECT_TRUE(warnings_.empty());
620 
621     checkCallbackMessage(errors_.at(0),
622                          "Invalid $GENERATE format modifiers", 3);
623     checkCallbackMessage(errors_.at(1),
624                          "$GENERATE error", 3);
625 }
626 
TEST_F(MasterLoaderTest,generateWithBadModifiers)627 TEST_F(MasterLoaderTest, generateWithBadModifiers) {
628     const string input =
629         "$ORIGIN example.org.\n"
630         "$TTL 3600\n"
631         "$GENERATE 10-12 host${GARBAGE} A 192.0.2.$\n";
632     stringstream ss(input);
633     setLoader(ss, Name("example.org."), RRClass::IN(),
634               MasterLoader::MANY_ERRORS);
635 
636     loader_->load();
637     EXPECT_FALSE(loader_->loadedSuccessfully());
638     ASSERT_EQ(2, errors_.size()); // For the broken GENERATE
639     EXPECT_TRUE(warnings_.empty());
640 
641     checkCallbackMessage(errors_.at(0),
642                          "Invalid $GENERATE format modifiers", 3);
643     checkCallbackMessage(errors_.at(1),
644                          "$GENERATE error", 3);
645 }
646 
TEST_F(MasterLoaderTest,generateMissingRange)647 TEST_F(MasterLoaderTest, generateMissingRange) {
648     const string input =
649         "$ORIGIN example.org.\n"
650         "$GENERATE\n";
651     stringstream ss(input);
652     setLoader(ss, Name("example.org."), RRClass::IN(),
653               MasterLoader::MANY_ERRORS);
654 
655     loader_->load();
656     EXPECT_FALSE(loader_->loadedSuccessfully());
657     EXPECT_EQ(1, errors_.size()); // For the broken GENERATE
658     EXPECT_TRUE(warnings_.empty());
659 
660     checkCallbackMessage(errors_.at(0),
661                          "unexpected end of input", 2);
662 }
663 
TEST_F(MasterLoaderTest,generateMissingLHS)664 TEST_F(MasterLoaderTest, generateMissingLHS) {
665     const string input =
666         "$ORIGIN example.org.\n"
667         "$GENERATE 2-4\n";
668     stringstream ss(input);
669     setLoader(ss, Name("example.org."), RRClass::IN(),
670               MasterLoader::MANY_ERRORS);
671 
672     loader_->load();
673     EXPECT_FALSE(loader_->loadedSuccessfully());
674     EXPECT_EQ(1, errors_.size()); // For the broken GENERATE
675     EXPECT_TRUE(warnings_.empty());
676 
677     checkCallbackMessage(errors_.at(0),
678                          "unexpected end of input", 2);
679 }
680 
TEST_F(MasterLoaderTest,generateMissingType)681 TEST_F(MasterLoaderTest, generateMissingType) {
682     const string input =
683         "$ORIGIN example.org.\n"
684         "$GENERATE 2-4 host$\n";
685     stringstream ss(input);
686     setLoader(ss, Name("example.org."), RRClass::IN(),
687               MasterLoader::MANY_ERRORS);
688 
689     loader_->load();
690     EXPECT_FALSE(loader_->loadedSuccessfully());
691     EXPECT_EQ(1, errors_.size()); // For the broken GENERATE
692     EXPECT_TRUE(warnings_.empty());
693 
694     checkCallbackMessage(errors_.at(0),
695                          "unexpected end of input", 2);
696 }
697 
TEST_F(MasterLoaderTest,generateMissingRHS)698 TEST_F(MasterLoaderTest, generateMissingRHS) {
699     const string input =
700         "$ORIGIN example.org.\n"
701         "$GENERATE 2-4 host$ A\n";
702     stringstream ss(input);
703     setLoader(ss, Name("example.org."), RRClass::IN(),
704               MasterLoader::MANY_ERRORS);
705 
706     loader_->load();
707     EXPECT_FALSE(loader_->loadedSuccessfully());
708     EXPECT_EQ(1, errors_.size()); // For the broken GENERATE
709     EXPECT_TRUE(warnings_.empty());
710 
711     checkCallbackMessage(errors_.at(0),
712                          "unexpected end of input", 2);
713 }
714 
TEST_F(MasterLoaderTest,generateWithBadRangeSyntax)715 TEST_F(MasterLoaderTest, generateWithBadRangeSyntax) {
716     const string input =
717         "$ORIGIN example.org.\n"
718         "$GENERATE ABCD host$ 3600 A 192.0.2.$\n";
719     stringstream ss(input);
720     setLoader(ss, Name("example.org."), RRClass::IN(),
721               MasterLoader::MANY_ERRORS);
722 
723     loader_->load();
724     EXPECT_FALSE(loader_->loadedSuccessfully());
725     EXPECT_EQ(1, errors_.size()); // For the broken GENERATE
726     EXPECT_TRUE(warnings_.empty());
727 
728     checkCallbackMessage(errors_.at(0),
729                          "$GENERATE: invalid range: ABCD", 2);
730 }
731 
TEST_F(MasterLoaderTest,generateWithInvalidRange)732 TEST_F(MasterLoaderTest, generateWithInvalidRange) {
733     // start > stop
734     const string input =
735         "$ORIGIN example.org.\n"
736         "$GENERATE 2-1 host$ 3600 A 192.0.2.$\n";
737     stringstream ss(input);
738     setLoader(ss, Name("example.org."), RRClass::IN(),
739               MasterLoader::MANY_ERRORS);
740 
741     loader_->load();
742     EXPECT_FALSE(loader_->loadedSuccessfully());
743     EXPECT_EQ(1, errors_.size()); // For the broken GENERATE
744     EXPECT_TRUE(warnings_.empty());
745 
746     checkCallbackMessage(errors_.at(0),
747                          "$GENERATE: invalid range: 2-1", 2);
748 }
749 
TEST_F(MasterLoaderTest,generateWithInvalidClass)750 TEST_F(MasterLoaderTest, generateWithInvalidClass) {
751     const string input =
752         "$ORIGIN example.org.\n"
753         "$GENERATE 1-2 host$ 3600 CH A 192.0.2.$\n";
754     stringstream ss(input);
755     setLoader(ss, Name("example.org."), RRClass::IN(),
756               MasterLoader::MANY_ERRORS);
757 
758     loader_->load();
759     EXPECT_FALSE(loader_->loadedSuccessfully());
760     EXPECT_EQ(1, errors_.size()); // For the broken GENERATE
761     EXPECT_TRUE(warnings_.empty());
762 
763     checkCallbackMessage(errors_.at(0),
764                          "Class mismatch: CH vs. IN", 2);
765 }
766 
TEST_F(MasterLoaderTest,generateWithNoAvailableTTL)767 TEST_F(MasterLoaderTest, generateWithNoAvailableTTL) {
768     const string input =
769         "$ORIGIN example.org.\n"
770         "$GENERATE 1-2 host$ A 192.0.2.$\n";
771     stringstream ss(input);
772     setLoader(ss, Name("example.org."), RRClass::IN(),
773               MasterLoader::MANY_ERRORS);
774 
775     loader_->load();
776     EXPECT_FALSE(loader_->loadedSuccessfully());
777     EXPECT_EQ(1, errors_.size()); // For the broken GENERATE
778     EXPECT_TRUE(warnings_.empty());
779 
780     checkCallbackMessage(errors_.at(0),
781                          "no TTL specified; load rejected", 2);
782 }
783 
784 // Test the source is correctly popped even after error
TEST_F(MasterLoaderTest,popAfterError)785 TEST_F(MasterLoaderTest, popAfterError) {
786     const string include_str = "$include " TEST_DATA_SRCDIR
787         "/broken.zone\nwww 3600 IN AAAA 2001:db8::1\n";
788     stringstream ss(include_str);
789     // We perform the test with MANY_ERRORS, we want to see what happens
790     // after the error.
791     setLoader(ss, Name("example.org."), RRClass::IN(),
792               MasterLoader::MANY_ERRORS);
793 
794     loader_->load();
795     EXPECT_FALSE(loader_->loadedSuccessfully());
796     EXPECT_EQ(1, errors_.size()); // For the broken RR
797     EXPECT_EQ(1, warnings_.size()); // For missing EOLN
798 
799     // The included file doesn't contain anything usable, but the
800     // line after the include should be there.
801     checkRR("www.example.org", RRType::AAAA(), "2001:db8::1");
802 }
803 
804 // Check it works the same when created based on a stream, not filename
TEST_F(MasterLoaderTest,streamConstructor)805 TEST_F(MasterLoaderTest, streamConstructor) {
806     const string zone_data(prepareZone("", true));
807     stringstream zone_stream(zone_data);
808     setLoader(zone_stream, Name("example.org."), RRClass::IN(),
809               MasterLoader::MANY_ERRORS);
810 
811     EXPECT_FALSE(loader_->loadedSuccessfully());
812 
813     // Unlike the basicLoad test, if we construct the loader from a stream
814     // getSize() returns the data size in the stream immediately after the
815     // construction.
816     EXPECT_EQ(zone_data.size(), loader_->getSize());
817     EXPECT_EQ(0, loader_->getPosition());
818 
819     loader_->load();
820     EXPECT_TRUE(loader_->loadedSuccessfully());
821 
822     EXPECT_TRUE(errors_.empty());
823     EXPECT_TRUE(warnings_.empty());
824     checkRR("example.org", RRType::SOA(), "ns1.example.org. "
825             "admin.example.org. 1234 3600 1800 2419200 7200");
826     checkRR("correct.example.org", RRType::A(), "192.0.2.2");
827 
828     // On completion of the load, both getSize() and getPosition() return the
829     // size of the data.
830     EXPECT_EQ(zone_data.size(), loader_->getSize());
831     EXPECT_EQ(zone_data.size(), loader_->getPosition());
832 }
833 
834 // Try loading data incrementally.
TEST_F(MasterLoaderTest,incrementalLoad)835 TEST_F(MasterLoaderTest, incrementalLoad) {
836     setLoader(TEST_DATA_SRCDIR "/example.org", Name("example.org."),
837               RRClass::IN(), MasterLoader::MANY_ERRORS);
838 
839     EXPECT_FALSE(loader_->loadedSuccessfully());
840     EXPECT_FALSE(loader_->loadIncremental(2));
841     EXPECT_FALSE(loader_->loadedSuccessfully());
842 
843     EXPECT_TRUE(errors_.empty());
844     EXPECT_TRUE(warnings_.empty());
845 
846     checkRR("example.org", RRType::SOA(),
847             "ns1.example.org. admin.example.org. "
848             "1234 3600 1800 2419200 7200");
849     checkRR("example.org", RRType::NS(), "ns1.example.org.");
850 
851     // The third one is not loaded yet
852     EXPECT_TRUE(rrsets_.empty());
853 
854     // Load the rest.
855     EXPECT_TRUE(loader_->loadIncremental(20));
856     EXPECT_TRUE(loader_->loadedSuccessfully());
857 
858     EXPECT_TRUE(errors_.empty());
859     EXPECT_TRUE(warnings_.empty());
860 
861     checkRR("www.example.org", RRType::A(), "192.0.2.1");
862     checkRR("www.example.org", RRType::AAAA(), "2001:db8::1");
863 }
864 
865 // Try loading from file that doesn't exist. There should be single error
866 // saying so.
TEST_F(MasterLoaderTest,invalidFile)867 TEST_F(MasterLoaderTest, invalidFile) {
868     setLoader("This file doesn't exist at all",
869               Name("example.org."), RRClass::IN(), MasterLoader::MANY_ERRORS);
870 
871     // Nothing yet. The loader is dormant until invoked.
872     // Is it really what we want?
873     EXPECT_TRUE(errors_.empty());
874 
875     loader_->load();
876 
877     EXPECT_TRUE(warnings_.empty());
878     EXPECT_TRUE(rrsets_.empty());
879     ASSERT_EQ(1, errors_.size());
880     EXPECT_EQ(0, errors_[0].find("Error opening the input source file: ")) <<
881         "Different error: " << errors_[0];
882 }
883 
884 struct ErrorCase {
885     const char* const line;    // The broken line in master file
886     const char* const reason;  // If non NULL, the reason string
887     const char* const problem; // Description of the problem for SCOPED_TRACE
888 } const error_cases[] = {
889     { "www...   3600    IN  A   192.0.2.1", NULL, "Invalid name" },
890     { "www      FORTNIGHT   IN  A   192.0.2.1", NULL, "Invalid TTL" },
891     { "www      3600    XX  A   192.0.2.1", NULL, "Invalid class" },
892     { "www      3600    IN  A   bad_ip", NULL, "Invalid Rdata" },
893 
894     // Parameter ordering errors
895     { "www      IN      A   3600 192.168.2.7",
896       "createRdata from text failed: Bad IN/A RDATA text: '3600'",
897       "Incorrect order of class, TTL and type" },
898     { "www      A       IN  3600 192.168.2.8",
899       "createRdata from text failed: Bad IN/A RDATA text: 'IN'",
900       "Incorrect order of class, TTL and type" },
901     { "www      3600    A   IN   192.168.2.7",
902       "createRdata from text failed: Bad IN/A RDATA text: 'IN'",
903       "Incorrect order of class, TTL and type" },
904     { "www      A       3600 IN  192.168.2.8",
905       "createRdata from text failed: Bad IN/A RDATA text: '3600'",
906       "Incorrect order of class, TTL and type" },
907 
908     // Missing type and Rdata
909     { "www", "unexpected end of input", "Missing type and Rdata" },
910     { "www 3600", "unexpected end of input", "Missing type and Rdata" },
911     { "www IN", "unexpected end of input", "Missing type and Rdata" },
912     { "www 3600 IN", "unexpected end of input", "Missing type and Rdata" },
913     { "www IN 3600", "unexpected end of input", "Missing type and Rdata" },
914 
915     // Missing Rdata
916     { "www A",
917       "createRdata from text failed: unexpected end of input",
918       "Missing Rdata" },
919     { "www 3600 A",
920       "createRdata from text failed: unexpected end of input",
921       "Missing Rdata" },
922     { "www IN A",
923       "createRdata from text failed: unexpected end of input",
924       "Missing Rdata" },
925     { "www 3600 IN A",
926       "createRdata from text failed: unexpected end of input",
927       "Missing Rdata" },
928     { "www IN 3600 A",
929       "createRdata from text failed: unexpected end of input",
930       "Missing Rdata" },
931 
932     { "www      3600    IN", NULL, "Unexpected EOLN" },
933     { "www      3600    CH  TXT nothing", "Class mismatch: CH vs. IN",
934       "Class mismatch" },
935     { "www      \"3600\"  IN  A   192.0.2.1", NULL, "Quoted TTL" },
936     { "www      3600    \"IN\"  A   192.0.2.1", NULL, "Quoted class" },
937     { "www      3600    IN  \"A\"   192.0.2.1", NULL, "Quoted type" },
938     { "unbalanced)paren 3600    IN  A   192.0.2.1", NULL, "Token error 1" },
939     { "www  3600    unbalanced)paren    A   192.0.2.1", NULL,
940       "Token error 2" },
941     // Check the unknown directive. The rest looks like ordinary RR,
942     // so we see the $ is actually special.
943     { "$UNKNOWN 3600    IN  A   192.0.2.1", NULL, "Unknown $ directive" },
944     { "$INCLUD " TEST_DATA_SRCDIR "/example.org", "Unknown directive 'INCLUD'",
945         "Include too short" },
946     { "$INCLUDES " TEST_DATA_SRCDIR "/example.org",
947         "Unknown directive 'INCLUDES'", "Include too long" },
948     { "$INCLUDE", "unexpected end of input", "Missing include path" },
949     // The following two error messages are system dependent, omitting
950     { "$INCLUDE /file/not/found", NULL, "Include file not found" },
951     { "$INCLUDE /file/not/found example.org. and here goes bunch of garbage",
952         NULL, "Include file not found and garbage at the end of line" },
953     { "$ORIGIN", "unexpected end of input", "Missing origin name" },
954     { "$ORIGIN invalid...name", "duplicate period in invalid...name",
955         "Invalid name for origin" },
956     { "$ORIGIN )brokentoken", "unbalanced parentheses",
957         "Broken token in origin" },
958     { "$ORIGIN example.org. garbage", "Extra tokens at the end of line",
959         "Garbage after origin" },
960     { "$ORIGI name.", "Unknown directive 'ORIGI'", "$ORIGIN too short" },
961     { "$ORIGINAL name.", "Unknown directive 'ORIGINAL'", "$ORIGIN too long" },
962     { "$TTL 100 extra-garbage", "Extra tokens at the end of line",
963       "$TTL with extra token" },
964     { "$TTL", "unexpected end of input", "missing TTL" },
965     { "$TTL No-ttl", "Unknown unit used: N in: No-ttl", "bad TTL" },
966     { "$TTL \"100\"", "unexpected quotes", "bad TTL, quoted" },
967     { "$TT 100", "Unknown directive 'TT'", "bad directive, too short" },
968     { "$TTLLIKE 100", "Unknown directive 'TTLLIKE'", "bad directive, extra" },
969     { NULL, NULL, NULL }
970 };
971 
972 // Test a broken zone is handled properly. We test several problems,
973 // both in strict and lenient mode.
TEST_F(MasterLoaderTest,brokenZone)974 TEST_F(MasterLoaderTest, brokenZone) {
975     for (const ErrorCase* ec = error_cases; ec->line != NULL; ++ec) {
976         SCOPED_TRACE(ec->problem);
977         const string zone(prepareZone(ec->line, true));
978 
979         {
980             SCOPED_TRACE("Strict mode");
981             clear();
982             stringstream zone_stream(zone);
983             setLoader(zone_stream, Name("example.org."), RRClass::IN(),
984                       MasterLoader::DEFAULT);
985             EXPECT_FALSE(loader_->loadedSuccessfully());
986             EXPECT_THROW(loader_->load(), MasterLoaderError);
987             EXPECT_FALSE(loader_->loadedSuccessfully());
988             EXPECT_EQ(1, errors_.size());
989             if (ec->reason != NULL) {
990                 checkCallbackMessage(errors_.at(0), ec->reason, 2);
991             }
992             EXPECT_TRUE(warnings_.empty());
993 
994             checkRR("example.org", RRType::SOA(), "ns1.example.org. "
995                     "admin.example.org. 1234 3600 1800 2419200 7200");
996             // In the strict mode, it is aborted. The last RR is not
997             // even attempted.
998             EXPECT_TRUE(rrsets_.empty());
999         }
1000 
1001         {
1002             SCOPED_TRACE("Lenient mode");
1003             clear();
1004             stringstream zone_stream(zone);
1005             setLoader(zone_stream, Name("example.org."), RRClass::IN(),
1006                       MasterLoader::MANY_ERRORS);
1007             EXPECT_FALSE(loader_->loadedSuccessfully());
1008             EXPECT_NO_THROW(loader_->load());
1009             EXPECT_FALSE(loader_->loadedSuccessfully());
1010             EXPECT_EQ(1, errors_.size());
1011             EXPECT_TRUE(warnings_.empty());
1012             checkRR("example.org", RRType::SOA(), "ns1.example.org. "
1013                     "admin.example.org. 1234 3600 1800 2419200 7200");
1014             // This one is below the error one.
1015             checkRR("correct.example.org", RRType::A(), "192.0.2.2");
1016             EXPECT_TRUE(rrsets_.empty());
1017         }
1018 
1019         {
1020             SCOPED_TRACE("Error at EOF");
1021             // This case is interesting only in the lenient mode.
1022             clear();
1023             const string zoneEOF(prepareZone(ec->line, false));
1024             stringstream zone_stream(zoneEOF);
1025             setLoader(zone_stream, Name("example.org."), RRClass::IN(),
1026                       MasterLoader::MANY_ERRORS);
1027             EXPECT_FALSE(loader_->loadedSuccessfully());
1028             EXPECT_NO_THROW(loader_->load());
1029             EXPECT_FALSE(loader_->loadedSuccessfully());
1030             EXPECT_EQ(1, errors_.size()) << errors_[0] << "\n" << errors_[1];
1031             // The unexpected EOF warning
1032             EXPECT_EQ(1, warnings_.size());
1033             checkRR("example.org", RRType::SOA(), "ns1.example.org. "
1034                     "admin.example.org. 1234 3600 1800 2419200 7200");
1035             EXPECT_TRUE(rrsets_.empty());
1036         }
1037     }
1038 }
1039 
1040 // Check that a garbage after the include generates an error, but not fatal
1041 // one (in lenient mode) and we can recover.
TEST_F(MasterLoaderTest,includeWithGarbage)1042 TEST_F(MasterLoaderTest, includeWithGarbage) {
1043     // Include an origin (example.org) because we expect it to be handled
1044     // soon and we don't want it to break here.
1045     const string include_str("$INCLUDE " TEST_DATA_SRCDIR
1046                              "/example.org example.org. bunch of other stuff\n"
1047                              "www 3600 IN AAAA 2001:db8::1\n");
1048     stringstream zone_stream(include_str);
1049     setLoader(zone_stream, Name("example.org."), RRClass::IN(),
1050               MasterLoader::MANY_ERRORS);
1051 
1052     EXPECT_NO_THROW(loader_->load());
1053     EXPECT_FALSE(loader_->loadedSuccessfully());
1054     ASSERT_EQ(1, errors_.size());
1055     checkCallbackMessage(errors_.at(0), "Extra tokens at the end of line", 1);
1056     // It says something about extra tokens at the end
1057     EXPECT_NE(string::npos, errors_[0].find("Extra"));
1058     EXPECT_TRUE(warnings_.empty());
1059     checkBasicRRs();
1060     checkRR("www.example.org", RRType::AAAA(), "2001:db8::1");
1061 }
1062 
1063 // Check we error about garbage at the end of $ORIGIN line (but the line
1064 // works).
TEST_F(MasterLoaderTest,originWithGarbage)1065 TEST_F(MasterLoaderTest, originWithGarbage) {
1066     const string origin_str = "$ORIGIN www.example.org. More garbage here\n"
1067         "@  1H  IN  A   192.0.2.1\n";
1068     stringstream ss(origin_str);
1069     setLoader(ss, Name("example.org."), RRClass::IN(),
1070               MasterLoader::MANY_ERRORS);
1071     EXPECT_NO_THROW(loader_->load());
1072     EXPECT_FALSE(loader_->loadedSuccessfully());
1073     ASSERT_EQ(1, errors_.size());
1074     checkCallbackMessage(errors_.at(0), "Extra tokens at the end of line", 1);
1075     EXPECT_TRUE(warnings_.empty());
1076     checkARR("www.example.org");
1077 }
1078 
1079 // Test we can pass both file to include and the origin to switch
TEST_F(MasterLoaderTest,includeAndOrigin)1080 TEST_F(MasterLoaderTest, includeAndOrigin) {
1081     // First, switch origin to something else, so we can check it is
1082     // switched back.
1083     const string include_string = "$ORIGIN www.example.org.\n"
1084         "@  1H  IN  A   192.0.2.1\n"
1085         // Then include the file with data and switch origin back
1086         "$INCLUDE " TEST_DATA_SRCDIR "/example.org example.org.\n"
1087         // Another RR to see we fall back to the previous origin.
1088         "www    1H  IN  A   192.0.2.1\n";
1089     stringstream ss(include_string);
1090     setLoader(ss, Name("example.org"), RRClass::IN(),
1091               MasterLoader::MANY_ERRORS);
1092     // Successfully load the data
1093     loader_->load();
1094     EXPECT_TRUE(loader_->loadedSuccessfully());
1095     EXPECT_TRUE(errors_.empty());
1096     EXPECT_TRUE(warnings_.empty());
1097     // And check it's the correct data
1098     checkARR("www.example.org");
1099     checkBasicRRs();
1100     checkARR("www.www.example.org");
1101 }
1102 
1103 // Like above, but the origin after include is bogus. The whole line should
1104 // be rejected.
TEST_F(MasterLoaderTest,includeAndBadOrigin)1105 TEST_F(MasterLoaderTest, includeAndBadOrigin) {
1106     const string include_string =
1107         "$INCLUDE " TEST_DATA_SRCDIR "/example.org example..org.\n"
1108         // Another RR to see the switch survives after we exit include
1109         "www    1H  IN  A   192.0.2.1\n";
1110     stringstream ss(include_string);
1111     setLoader(ss, Name("example.org"), RRClass::IN(),
1112               MasterLoader::MANY_ERRORS);
1113     loader_->load();
1114     EXPECT_FALSE(loader_->loadedSuccessfully());
1115     EXPECT_EQ(1, errors_.size());
1116     checkCallbackMessage(errors_.at(0), "duplicate period in example..org.",
1117                          1);
1118     EXPECT_TRUE(warnings_.empty());
1119     // And check it's the correct data
1120     checkARR("www.example.org");
1121 }
1122 
1123 // Check the origin doesn't get outside of the included file.
TEST_F(MasterLoaderTest,includeOriginRestore)1124 TEST_F(MasterLoaderTest, includeOriginRestore) {
1125     const string include_string =
1126         "$INCLUDE " TEST_DATA_SRCDIR "/origincheck.txt\n"
1127         "@  1H  IN  A   192.0.2.1\n";
1128     stringstream ss(include_string);
1129     setLoader(ss, Name("example.org"), RRClass::IN(),
1130               MasterLoader::MANY_ERRORS);
1131     // Successfully load the data
1132     loader_->load();
1133     EXPECT_TRUE(loader_->loadedSuccessfully());
1134     EXPECT_TRUE(errors_.empty());
1135     EXPECT_TRUE(warnings_.empty());
1136     // And check it's the correct data
1137     checkARR("www.example.org");
1138     checkARR("example.org");
1139 }
1140 
1141 // Check we restore the last name for initial whitespace when returning from
1142 // include. But we do produce a warning if there's one just ofter the include.
TEST_F(MasterLoaderTest,includeAndInitialWS)1143 TEST_F(MasterLoaderTest, includeAndInitialWS) {
1144     const string include_string = "xyz  1H  IN  A   192.0.2.1\n"
1145         "$INCLUDE " TEST_DATA_SRCDIR "/example.org\n"
1146         "   1H  IN  A   192.0.2.1\n";
1147     stringstream ss(include_string);
1148     setLoader(ss, Name("example.org"), RRClass::IN(),
1149               MasterLoader::MANY_ERRORS);
1150     // Successfully load the data
1151     loader_->load();
1152     EXPECT_TRUE(loader_->loadedSuccessfully());
1153     EXPECT_TRUE(errors_.empty());
1154     EXPECT_EQ(1, warnings_.size());
1155     checkCallbackMessage(warnings_.at(0),
1156                          "Owner name omitted around $INCLUDE, the result might "
1157                          "not be as expected", 3);
1158     checkARR("xyz.example.org");
1159     checkBasicRRs();
1160     checkARR("xyz.example.org");
1161 }
1162 
1163 // Test for "$TTL"
TEST_F(MasterLoaderTest,ttlDirective)1164 TEST_F(MasterLoaderTest, ttlDirective) {
1165     stringstream zone_stream;
1166 
1167     // Set the default TTL with $TTL followed by an RR omitting the TTL
1168     zone_stream << "$TTL 1800\nexample.org. IN A 192.0.2.1\n";
1169     // $TTL can be quoted.  Also testing the case of $TTL being changed.
1170     zone_stream << "\"$TTL\" 100\na.example.org. IN A 192.0.2.2\n";
1171     // Extended TTL form is accepted.
1172     zone_stream << "$TTL 1H\nb.example.org. IN A 192.0.2.3\n";
1173     // Matching is case insensitive.
1174     zone_stream << "$tTl 360\nc.example.org. IN A 192.0.2.4\n";
1175     // Maximum allowable TTL
1176     zone_stream << "$TTL 2147483647\nd.example.org. IN A 192.0.2.5\n";
1177 
1178     setLoader(zone_stream, Name("example.org."), RRClass::IN(),
1179               MasterLoader::DEFAULT);
1180     loader_->load();
1181     EXPECT_TRUE(loader_->loadedSuccessfully());
1182     checkRR("example.org", RRType::A(), "192.0.2.1", RRTTL(1800));
1183     checkRR("a.example.org", RRType::A(), "192.0.2.2", RRTTL(100));
1184     checkRR("b.example.org", RRType::A(), "192.0.2.3", RRTTL(3600));
1185     checkRR("c.example.org", RRType::A(), "192.0.2.4", RRTTL(360));
1186     checkRR("d.example.org", RRType::A(), "192.0.2.5", RRTTL(2147483647));
1187 }
1188 
TEST_F(MasterLoaderTest,ttlFromSOA)1189 TEST_F(MasterLoaderTest, ttlFromSOA) {
1190     // No $TTL, and the SOA doesn't have an explicit TTL field.  Its minimum
1191     // TTL field will be used as the RR's TTL, and it'll be used as the
1192     // default TTL for others.
1193     stringstream zone_stream("example.org. IN SOA . . 0 0 0 0 1800\n"
1194                              "a.example.org. IN A 192.0.2.1\n");
1195     setLoader(zone_stream, Name("example.org."), RRClass::IN(),
1196               MasterLoader::DEFAULT);
1197     loader_->load();
1198     EXPECT_TRUE(loader_->loadedSuccessfully());
1199     checkRR("example.org", RRType::SOA(), ". . 0 0 0 0 1800", RRTTL(1800));
1200     checkRR("a.example.org", RRType::A(), "192.0.2.1", RRTTL(1800));
1201 
1202     // The use of SOA minimum TTL should have caused a warning.
1203     EXPECT_EQ(1, warnings_.size());
1204     checkCallbackMessage(warnings_.at(0),
1205                          "no TTL specified; using SOA MINTTL instead", 1);
1206 }
1207 
TEST_F(MasterLoaderTest,ttlFromPrevious)1208 TEST_F(MasterLoaderTest, ttlFromPrevious) {
1209     // No available default TTL.  2nd and 3rd RR will use the TTL of the
1210     // 1st RR.  This will result in a warning, but only for the first time.
1211     stringstream zone_stream("a.example.org. 1800 IN A 192.0.2.1\n"
1212                              "b.example.org. IN A 192.0.2.2\n"
1213                              "c.example.org. IN A 192.0.2.3\n");
1214     setLoader(zone_stream, Name("example.org."), RRClass::IN(),
1215               MasterLoader::DEFAULT);
1216     loader_->load();
1217     EXPECT_TRUE(loader_->loadedSuccessfully());
1218     checkRR("a.example.org", RRType::A(), "192.0.2.1", RRTTL(1800));
1219     checkRR("b.example.org", RRType::A(), "192.0.2.2", RRTTL(1800));
1220     checkRR("c.example.org", RRType::A(), "192.0.2.3", RRTTL(1800));
1221 
1222     EXPECT_EQ(1, warnings_.size());
1223     checkCallbackMessage(warnings_.at(0), "using RFC1035 TTL semantics", 2);
1224 }
1225 
TEST_F(MasterLoaderTest,RRParamsOrdering)1226 TEST_F(MasterLoaderTest, RRParamsOrdering) {
1227     // We test the order and existence of TTL, class and type. See
1228     // MasterLoader::MasterLoaderImpl::parseRRParams() for ordering.
1229 
1230     stringstream zone_stream;
1231     // <TTL> <class> <type> <RDATA>
1232     zone_stream << "a.example.org. 1800 IN A 192.0.2.1\n";
1233     // <type> <RDATA>
1234     zone_stream << "b.example.org. A 192.0.2.2\n";
1235     // <class> <TTL> <type> <RDATA>
1236     zone_stream << "c.example.org. IN 3600 A 192.0.2.3\n";
1237     // <TTL> <type> <RDATA>
1238     zone_stream << "d.example.org. 7200 A 192.0.2.4\n";
1239     // <class> <type> <RDATA>
1240     zone_stream << "e.example.org. IN A 192.0.2.5\n";
1241 
1242     setLoader(zone_stream, Name("example.org."), RRClass::IN(),
1243               MasterLoader::DEFAULT);
1244     loader_->load();
1245     EXPECT_TRUE(loader_->loadedSuccessfully());
1246     checkRR("a.example.org", RRType::A(), "192.0.2.1", RRTTL(1800));
1247     checkRR("b.example.org", RRType::A(), "192.0.2.2", RRTTL(1800));
1248     checkRR("c.example.org", RRType::A(), "192.0.2.3", RRTTL(3600));
1249     checkRR("d.example.org", RRType::A(), "192.0.2.4", RRTTL(7200));
1250     checkRR("e.example.org", RRType::A(), "192.0.2.5", RRTTL(7200));
1251 
1252     EXPECT_EQ(1, warnings_.size());
1253     checkCallbackMessage(warnings_.at(0), "using RFC1035 TTL semantics", 2);
1254 }
1255 
TEST_F(MasterLoaderTest,ttlFromPreviousSOA)1256 TEST_F(MasterLoaderTest, ttlFromPreviousSOA) {
1257     // Mixture of the previous two cases: SOA has explicit TTL, followed by
1258     // an RR without an explicit TTL.  In this case the minimum TTL won't be
1259     // recognized as the "default TTL".
1260     stringstream zone_stream("example.org. 100 IN SOA . . 0 0 0 0 1800\n"
1261                              "a.example.org. IN A 192.0.2.1\n");
1262     setLoader(zone_stream, Name("example.org."), RRClass::IN(),
1263               MasterLoader::DEFAULT);
1264     loader_->load();
1265     EXPECT_TRUE(loader_->loadedSuccessfully());
1266 
1267     checkRR("example.org", RRType::SOA(), ". . 0 0 0 0 1800", RRTTL(100));
1268     checkRR("a.example.org", RRType::A(), "192.0.2.1", RRTTL(100));
1269 
1270     EXPECT_EQ(1, warnings_.size());
1271     checkCallbackMessage(warnings_.at(0), "using RFC1035 TTL semantics", 2);
1272 }
1273 
TEST_F(MasterLoaderTest,ttlUnknown)1274 TEST_F(MasterLoaderTest, ttlUnknown) {
1275     // No available TTL is known for the first RR.
1276     stringstream zone_stream("a.example.org. IN A 192.0.2.1\n");
1277     setLoader(zone_stream, Name("example.org."), RRClass::IN(),
1278               MasterLoader::DEFAULT);
1279     EXPECT_THROW(loader_->load(), MasterLoaderError);
1280 }
1281 
TEST_F(MasterLoaderTest,ttlUnknownAndContinue)1282 TEST_F(MasterLoaderTest, ttlUnknownAndContinue) {
1283     stringstream zone_stream("a.example.org. IN A 192.0.2.1\n"
1284                              "b.example.org. 1800 IN A 192.0.2.2\n");
1285     setLoader(zone_stream, Name("example.org."), RRClass::IN(),
1286               MasterLoader::MANY_ERRORS);
1287     loader_->load();
1288     EXPECT_FALSE(loader_->loadedSuccessfully());
1289     checkRR("b.example.org", RRType::A(), "192.0.2.2", RRTTL(1800));
1290 
1291     EXPECT_TRUE(warnings_.empty());
1292     EXPECT_EQ(1, errors_.size());
1293     checkCallbackMessage(errors_.at(0), "no TTL specified; load rejected", 1);
1294 }
1295 
TEST_F(MasterLoaderTest,ttlUnknownAndEOF)1296 TEST_F(MasterLoaderTest, ttlUnknownAndEOF) {
1297     // Similar to the previous case, but the input will be abruptly terminated
1298     // after the offending RR.  This will cause an additional warning.
1299     stringstream zone_stream("a.example.org. IN A 192.0.2.1");
1300     setLoader(zone_stream, Name("example.org."), RRClass::IN(),
1301               MasterLoader::MANY_ERRORS);
1302     loader_->load();
1303     EXPECT_FALSE(loader_->loadedSuccessfully());
1304     EXPECT_TRUE(rrsets_.empty());
1305 
1306     EXPECT_EQ(1, errors_.size());
1307     checkCallbackMessage(errors_.at(0), "no TTL specified; load rejected", 1);
1308 
1309     // RDATA implementation can complain about it, too.  To be independent of
1310     // its details, we focus on the very last warning.
1311     EXPECT_FALSE(warnings_.empty());
1312     checkCallbackMessage(*warnings_.rbegin(), "File does not end with newline",
1313                          1);
1314 }
1315 
TEST_F(MasterLoaderTest,ttlOverflow)1316 TEST_F(MasterLoaderTest, ttlOverflow) {
1317     stringstream zone_stream;
1318     zone_stream << "example.org. IN SOA . . 0 0 0 0 2147483648\n";
1319     zone_stream << "$TTL 3600\n"; // reset to an in-range value
1320     zone_stream << "$TTL 2147483649\n";
1321     zone_stream << "a.example.org. IN A 192.0.2.1\n";
1322     zone_stream << "$TTL 3600\n"; // reset to an in-range value
1323     zone_stream << "b.example.org. 2147483650 IN A 192.0.2.2\n";
1324     setLoader(zone_stream, Name("example.org."), RRClass::IN(),
1325               MasterLoader::DEFAULT);
1326 
1327     loader_->load();
1328     EXPECT_TRUE(loader_->loadedSuccessfully());
1329     EXPECT_EQ(3, rrsets_.size());
1330 
1331     checkRR("example.org", RRType::SOA(), ". . 0 0 0 0 2147483648", RRTTL(0));
1332     checkRR("a.example.org", RRType::A(), "192.0.2.1", RRTTL(0));
1333     checkRR("b.example.org", RRType::A(), "192.0.2.2", RRTTL(0));
1334 
1335     EXPECT_EQ(4, warnings_.size());
1336     checkCallbackMessage(warnings_.at(1),
1337                          "TTL 2147483648 > MAXTTL, setting to 0 per RFC2181",
1338                          1);
1339     checkCallbackMessage(warnings_.at(2),
1340                          "TTL 2147483649 > MAXTTL, setting to 0 per RFC2181",
1341                          3);
1342     checkCallbackMessage(warnings_.at(3),
1343                          "TTL 2147483650 > MAXTTL, setting to 0 per RFC2181",
1344                          6);
1345 }
1346 
1347 // Test the constructor rejects empty add callback.
TEST_F(MasterLoaderTest,emptyCallback)1348 TEST_F(MasterLoaderTest, emptyCallback) {
1349     EXPECT_THROW(MasterLoader(TEST_DATA_SRCDIR "/example.org",
1350                               Name("example.org"), RRClass::IN(), callbacks_,
1351                               AddRRCallback()), isc::InvalidParameter);
1352     // And the same with the second constructor
1353     stringstream ss("");
1354     EXPECT_THROW(MasterLoader(ss, Name("example.org"), RRClass::IN(),
1355                               callbacks_, AddRRCallback()),
1356                  isc::InvalidParameter);
1357 }
1358 
1359 // Check it throws when we try to load after loading was complete.
TEST_F(MasterLoaderTest,loadTwice)1360 TEST_F(MasterLoaderTest, loadTwice) {
1361     setLoader(TEST_DATA_SRCDIR "/example.org", Name("example.org."),
1362               RRClass::IN(), MasterLoader::MANY_ERRORS);
1363 
1364     loader_->load();
1365     EXPECT_THROW(loader_->load(), isc::InvalidOperation);
1366     // Don't check them, they are not interesting, so suppress the error
1367     // at TearDown
1368     rrsets_.clear();
1369 }
1370 
1371 // Load 0 items should be rejected
TEST_F(MasterLoaderTest,loadZero)1372 TEST_F(MasterLoaderTest, loadZero) {
1373     setLoader(TEST_DATA_SRCDIR "/example.org", Name("example.org."),
1374               RRClass::IN(), MasterLoader::MANY_ERRORS);
1375     EXPECT_THROW(loader_->loadIncremental(0), isc::InvalidParameter);
1376 }
1377 
1378 // Test there's a warning when the file terminates without end of
1379 // line.
TEST_F(MasterLoaderTest,noEOLN)1380 TEST_F(MasterLoaderTest, noEOLN) {
1381     // No \n at the end
1382     const string input("example.org. 3600 IN SOA ns1.example.org. "
1383                        "admin.example.org. 1234 3600 1800 2419200 7200");
1384     stringstream ss(input);
1385     setLoader(ss, Name("example.org."), RRClass::IN(),
1386               MasterLoader::MANY_ERRORS);
1387 
1388     loader_->load();
1389     EXPECT_TRUE(loader_->loadedSuccessfully());
1390     EXPECT_TRUE(errors_.empty());
1391     // There should be one warning about the EOLN
1392     EXPECT_EQ(1, warnings_.size());
1393     checkRR("example.org", RRType::SOA(), "ns1.example.org. "
1394             "admin.example.org. 1234 3600 1800 2419200 7200");
1395 }
1396 
1397 // Test it rejects when we don't have the previous name to use in place of
1398 // initial whitespace
TEST_F(MasterLoaderTest,noPreviousName)1399 TEST_F(MasterLoaderTest, noPreviousName) {
1400     const string input("    1H  IN  A   192.0.2.1\n");
1401     stringstream ss(input);
1402     setLoader(ss, Name("example.org."), RRClass::IN(),
1403               MasterLoader::MANY_ERRORS);
1404     loader_->load();
1405     EXPECT_FALSE(loader_->loadedSuccessfully());
1406     EXPECT_EQ(1, errors_.size());
1407     checkCallbackMessage(errors_.at(0), "No previous name to use in place of "
1408                          "initial whitespace", 1);
1409     EXPECT_TRUE(warnings_.empty());
1410 }
1411 
1412 // Check we warn if the first RR in an included file has omitted name
TEST_F(MasterLoaderTest,previousInInclude)1413 TEST_F(MasterLoaderTest, previousInInclude) {
1414     const string input("www 1H  IN  A   192.0.2.1\n"
1415                        "$INCLUDE " TEST_DATA_SRCDIR "/omitcheck.txt\n");
1416     stringstream ss(input);
1417     setLoader(ss, Name("example.org"), RRClass::IN(),
1418               MasterLoader::MANY_ERRORS);
1419     loader_->load();
1420     EXPECT_TRUE(loader_->loadedSuccessfully());
1421     EXPECT_TRUE(errors_.empty());
1422     // There should be one warning about the EOLN
1423     EXPECT_EQ(1, warnings_.size());
1424     checkCallbackMessage(warnings_.at(0), "Owner name omitted around "
1425                          "$INCLUDE, the result might not be as expected", 1);
1426     checkARR("www.example.org");
1427     checkARR("www.example.org");
1428 }
1429 
TEST_F(MasterLoaderTest,numericOwnerName)1430 TEST_F(MasterLoaderTest, numericOwnerName) {
1431     const string input("$ORIGIN example.org.\n"
1432                        "1 3600 IN A 192.0.2.1\n");
1433     stringstream ss(input);
1434     setLoader(ss, Name("example.org."), RRClass::IN(),
1435               MasterLoader::MANY_ERRORS);
1436 
1437     loader_->load();
1438     EXPECT_TRUE(loader_->loadedSuccessfully());
1439     EXPECT_TRUE(errors_.empty());
1440     EXPECT_TRUE(warnings_.empty());
1441 
1442     checkRR("1.example.org", RRType::A(), "192.0.2.1");
1443 }
1444 
1445 }
1446