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