// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC") // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above // copyright notice and this permission notice appear in all copies. // // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; using boost::shared_ptr; using namespace isc::dns; using namespace isc::datasrc; using namespace isc::testutils; namespace { // Commonly used test zone file. const char* const TEST_ZONE_FILE = TEST_DATA_DIR "/contexttest.zone"; // Convenient shortcut typedef shared_ptr DataSourceClientPtr; // This is the type used as the test parameter. Note that this is // intentionally a plain old type (i.e. a function pointer), not a class; // otherwise it could cause initialization fiasco at the instantiation time. typedef DataSourceClientPtr (*ClientCreator)(RRClass, const Name&); // Creator for the in-memory client to be tested DataSourceClientPtr createInMemoryClient(RRClass zclass, const Name& zname) { shared_ptr client(new InMemoryClient); shared_ptr finder( new InMemoryZoneFinder(zclass, zname)); finder->load(TEST_ZONE_FILE); client->addZone(finder); return (client); } // Creator for the SQLite3 client to be tested. addRRset() is a helper // subroutine. void addRRset(ZoneUpdaterPtr updater, ConstRRsetPtr rrset) { updater->addRRset(*rrset); } DataSourceClientPtr createSQLite3Client(RRClass zclass, const Name& zname) { // We always begin with an empty template SQLite3 DB file and install // the zone data from the zone file to ensure both cases have the // same test data. const char* const install_cmd = INSTALL_PROG " " TEST_DATA_DIR "/rwtest.sqlite3 " TEST_DATA_BUILDDIR "/contexttest.sqlite3.copied"; if (system(install_cmd) != 0) { isc_throw(isc::Unexpected, "Error setting up; command failed: " << install_cmd); } shared_ptr accessor( new SQLite3Accessor(TEST_DATA_BUILDDIR "/contexttest.sqlite3.copied", zclass.toText())); shared_ptr client(new DatabaseClient(zclass, accessor)); ZoneUpdaterPtr updater = client->getUpdater(zname, true); masterLoad(TEST_ZONE_FILE, zname, zclass, boost::bind(addRRset, updater, _1)); // Insert an out-of-zone name to test if it's incorrectly returned. // Note that neither updater nor SQLite3 accessor checks this condition, // so this should succeed. stringstream ss("ns.example.com. 3600 IN A 192.0.2.7"); masterLoad(ss, Name::ROOT_NAME(), zclass, boost::bind(addRRset, updater, _1)); updater->commit(); return (client); } // The test class. Its parameterized so we can share the test scnearios // for any concrete data source implementaitons. class ZoneFinderContextTest : public ::testing::TestWithParam { protected: ZoneFinderContextTest() : qclass_(RRClass::IN()), qzone_("example.org") { client_ = (*GetParam())(qclass_, qzone_); REQUESTED_A.push_back(RRType::A()); REQUESTED_AAAA.push_back(RRType::AAAA()); REQUESTED_BOTH.push_back(RRType::A()); REQUESTED_BOTH.push_back(RRType::AAAA()); } void SetUp() { finder_ = client_->findZone(qzone_).zone_finder; ASSERT_TRUE(finder_); } const RRClass qclass_; const Name qzone_; DataSourceClientPtr client_; ZoneFinderPtr finder_; vector requested_types_; vector REQUESTED_A; vector REQUESTED_AAAA; vector REQUESTED_BOTH; vector result_sets_; }; // We test the in-memory and SQLite3 data source implementations. INSTANTIATE_TEST_CASE_P(, ZoneFinderContextTest, ::testing::Values(createInMemoryClient, createSQLite3Client)); TEST_P(ZoneFinderContextTest, getAdditionalAuthNS) { ZoneFinderContextPtr ctx = finder_->find(qzone_, RRType::NS()); EXPECT_EQ(ZoneFinder::SUCCESS, ctx->code); // Getting both A and AAAA NS addresses ctx->getAdditional(REQUESTED_BOTH, result_sets_); rrsetsCheck("ns1.example.org. 3600 IN A 192.0.2.1\n" "ns1.example.org. 3600 IN AAAA 2001:db8::1\n" "ns2.example.org. 3600 IN A 192.0.2.2\n", result_sets_.begin(), result_sets_.end()); // Getting only A result_sets_.clear(); ctx->getAdditional(REQUESTED_A, result_sets_); rrsetsCheck("ns1.example.org. 3600 IN A 192.0.2.1\n" "ns2.example.org. 3600 IN A 192.0.2.2\n", result_sets_.begin(), result_sets_.end()); // Getting only AAAA result_sets_.clear(); ctx->getAdditional(REQUESTED_AAAA, result_sets_); rrsetsCheck("ns1.example.org. 3600 IN AAAA 2001:db8::1\n", result_sets_.begin(), result_sets_.end()); // Getting A again, without clearing the result sets. This confirms // getAdditional() doesn't change the existing vector content. ctx->getAdditional(REQUESTED_A, result_sets_); // The first element should be the existing AAAA RR, followed by the A's. EXPECT_EQ(RRType::AAAA(), result_sets_[0]->getType()); rrsetsCheck("ns1.example.org. 3600 IN AAAA 2001:db8::1\n" "ns1.example.org. 3600 IN A 192.0.2.1\n" "ns2.example.org. 3600 IN A 192.0.2.2\n", result_sets_.begin(), result_sets_.end()); // Normally expected type set contain only A and/or AAAA, but others aren't // excluded. result_sets_.clear(); requested_types_.push_back(RRType::TXT()); ctx->getAdditional(requested_types_, result_sets_); rrsetsCheck("ns2.example.org. 3600 IN TXT \"text data\"", result_sets_.begin(), result_sets_.end()); // Even empty set is okay. The result should also be empty. result_sets_.clear(); ctx->getAdditional(vector(), result_sets_); EXPECT_TRUE(result_sets_.empty()); } TEST_P(ZoneFinderContextTest, getAdditionalDelegation) { // Basically similar to the AuthNS case, but NS names are glues. // It contains an out-of-zone NS name. Its address (even if it's somehow // inserted to the zone data) shouldn't be returned. const Name qname("www.a.example.org"); ZoneFinderContextPtr ctx = finder_->find(qname, RRType::AAAA()); EXPECT_EQ(ZoneFinder::DELEGATION, ctx->code); ctx->getAdditional(REQUESTED_BOTH, result_sets_); rrsetsCheck("ns1.a.example.org. 3600 IN A 192.0.2.5\n" "ns2.a.example.org. 3600 IN A 192.0.2.6\n" "ns2.a.example.org. 3600 IN AAAA 2001:db8::6\n", result_sets_.begin(), result_sets_.end()); result_sets_.clear(); ctx->getAdditional(REQUESTED_A, result_sets_); rrsetsCheck("ns1.a.example.org. 3600 IN A 192.0.2.5\n" "ns2.a.example.org. 3600 IN A 192.0.2.6\n", result_sets_.begin(), result_sets_.end()); result_sets_.clear(); ctx->getAdditional(REQUESTED_AAAA, result_sets_); rrsetsCheck("ns2.a.example.org. 3600 IN AAAA 2001:db8::6\n", result_sets_.begin(), result_sets_.end()); } TEST_P(ZoneFinderContextTest, getAdditionalMX) { // Similar to the previous cases, but for MX addresses. The test zone // contains MX name under a zone cut. Its address shouldn't be returned. ZoneFinderContextPtr ctx = finder_->find(qzone_, RRType::MX()); EXPECT_EQ(ZoneFinder::SUCCESS, ctx->code); // Getting both A and AAAA NS addresses ctx->getAdditional(REQUESTED_BOTH, result_sets_); rrsetsCheck("mx1.example.org. 3600 IN A 192.0.2.10\n" "mx2.example.org. 3600 IN AAAA 2001:db8::10\n", result_sets_.begin(), result_sets_.end()); // Getting only A result_sets_.clear(); ctx->getAdditional(REQUESTED_A, result_sets_); rrsetsCheck("mx1.example.org. 3600 IN A 192.0.2.10\n", result_sets_.begin(), result_sets_.end()); // Getting only AAAA result_sets_.clear(); ctx->getAdditional(REQUESTED_AAAA, result_sets_); rrsetsCheck("mx2.example.org. 3600 IN AAAA 2001:db8::10\n", result_sets_.begin(), result_sets_.end()); } TEST_P(ZoneFinderContextTest, getAdditionalWithSIG) { // Similar to the AuthNS test, but the original find() requested DNSSEC // RRSIGs. Then additional records will also have RRSIGs. ZoneFinderContextPtr ctx = finder_->find(qzone_, RRType::NS(), ZoneFinder::FIND_DNSSEC); EXPECT_EQ(ZoneFinder::SUCCESS, ctx->code); ctx->getAdditional(REQUESTED_BOTH, result_sets_); rrsetsCheck("ns1.example.org. 3600 IN A 192.0.2.1\n" "ns1.example.org. 3600 IN AAAA 2001:db8::1\n" "ns2.example.org. 3600 IN A 192.0.2.2\n", result_sets_.begin(), result_sets_.end()); vector sigresult_sets; BOOST_FOREACH(ConstRRsetPtr rrset, result_sets_) { ConstRRsetPtr sig_rrset = rrset->getRRsig(); if (sig_rrset) { sigresult_sets.push_back(sig_rrset); } } rrsetsCheck("ns1.example.org. 3600 IN RRSIG A 7 3 3600 20150420235959 " "20051021000000 40430 example.org. FAKEFAKE\n" "ns1.example.org. 3600 IN RRSIG AAAA 7 3 3600 20150420235959 " "20051021000000 40430 example.org. FAKEFAKEFAKE\n", sigresult_sets.begin(), sigresult_sets.end()); } TEST_P(ZoneFinderContextTest, getAdditionalNoOP) { // getAdditional() is only meaningful after SUCCESS or DELEGATION. ZoneFinderContextPtr ctx = finder_->find(Name("nxdomain.example.org"), RRType::NS()); EXPECT_EQ(ZoneFinder::NXDOMAIN, ctx->code); ctx->getAdditional(REQUESTED_BOTH, result_sets_); EXPECT_TRUE(result_sets_.empty()); ctx = finder_->find(qzone_, RRType::TXT()); EXPECT_EQ(ZoneFinder::NXRRSET, ctx->code); ctx->getAdditional(REQUESTED_BOTH, result_sets_); EXPECT_TRUE(result_sets_.empty()); ctx = finder_->find(Name("alias.example.org."), RRType::A()); EXPECT_EQ(ZoneFinder::CNAME, ctx->code); ctx->getAdditional(REQUESTED_BOTH, result_sets_); EXPECT_TRUE(result_sets_.empty()); ctx = finder_->find(Name("www.dname.example.org."), RRType::A()); EXPECT_EQ(ZoneFinder::DNAME, ctx->code); ctx->getAdditional(REQUESTED_BOTH, result_sets_); EXPECT_TRUE(result_sets_.empty()); } TEST_P(ZoneFinderContextTest, getAdditionalForAny) { // getAdditional() after successful type ANY query should return // the additional records of all returned RRsets. vector all_rrsets; ZoneFinderContextPtr ctx = finder_->findAll(qzone_, all_rrsets); EXPECT_EQ(ZoneFinder::SUCCESS, ctx->code); ctx->getAdditional(REQUESTED_BOTH, result_sets_); rrsetsCheck("ns1.example.org. 3600 IN A 192.0.2.1\n" "ns1.example.org. 3600 IN AAAA 2001:db8::1\n" "ns2.example.org. 3600 IN A 192.0.2.2\n" "mx1.example.org. 3600 IN A 192.0.2.10\n" "mx2.example.org. 3600 IN AAAA 2001:db8::10\n", result_sets_.begin(), result_sets_.end()); // If the type ANY query results in DELEGATION, the result should be the // same as normal query. all_rrsets.clear(); result_sets_.clear(); ctx = finder_->findAll(Name("www.a.example.org"), all_rrsets); EXPECT_EQ(ZoneFinder::DELEGATION, ctx->code); ctx->getAdditional(REQUESTED_BOTH, result_sets_); rrsetsCheck("ns1.a.example.org. 3600 IN A 192.0.2.5\n" "ns2.a.example.org. 3600 IN A 192.0.2.6\n" "ns2.a.example.org. 3600 IN AAAA 2001:db8::6\n", result_sets_.begin(), result_sets_.end()); } }