|
@@ -12,8 +12,16 @@
|
|
|
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
|
// PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
|
|
+#include <sstream>
|
|
|
+#include <vector>
|
|
|
+#include <map>
|
|
|
+
|
|
|
+#include <boost/bind.hpp>
|
|
|
+
|
|
|
+#include <dns/masterload.h>
|
|
|
#include <dns/message.h>
|
|
|
#include <dns/name.h>
|
|
|
+#include <dns/opcode.h>
|
|
|
#include <dns/rcode.h>
|
|
|
#include <dns/rrttl.h>
|
|
|
#include <dns/rrtype.h>
|
|
@@ -23,167 +31,167 @@
|
|
|
|
|
|
#include <auth/query.h>
|
|
|
|
|
|
+#include <testutils/dnsmessage_test.h>
|
|
|
+
|
|
|
#include <gtest/gtest.h>
|
|
|
|
|
|
+using namespace std;
|
|
|
using namespace isc::dns;
|
|
|
+using namespace isc::dns::rdata;
|
|
|
using namespace isc::datasrc;
|
|
|
using namespace isc::auth;
|
|
|
+using namespace isc::testutils;
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
-RRsetPtr a_rrset = RRsetPtr(new RRset(Name("www.example.com"),
|
|
|
- RRClass::IN(), RRType::A(),
|
|
|
- RRTTL(3600)));
|
|
|
-RRsetPtr soa_rrset = RRsetPtr(new RRset(Name("example.com"),
|
|
|
- RRClass::IN(), RRType::SOA(),
|
|
|
- RRTTL(3600)));
|
|
|
-RRsetPtr ns_rrset(RRsetPtr(new RRset(Name("ns.example.com"),
|
|
|
- RRClass::IN(), RRType::NS(),
|
|
|
- RRTTL(3600))));
|
|
|
-RRsetPtr glue_a_rrset(RRsetPtr(new RRset(Name("glue.ns.example.com"),
|
|
|
- RRClass::IN(), RRType::A(),
|
|
|
- RRTTL(3600))));
|
|
|
-RRsetPtr glue_aaaa_rrset(RRsetPtr(new RRset(Name("glue.ns.example.com"),
|
|
|
- RRClass::IN(), RRType::AAAA(),
|
|
|
- RRTTL(3600))));
|
|
|
-RRsetPtr noglue_a_rrset(RRsetPtr(new RRset(Name("noglue.example.com"),
|
|
|
- RRClass::IN(), RRType::A(),
|
|
|
- RRTTL(3600))));
|
|
|
-RRsetPtr delegated_mx_a_rrset(RRsetPtr(new RRset(
|
|
|
- Name("mx.delegation.example.com"), RRClass::IN(), RRType::A(),
|
|
|
- RRTTL(3600))));
|
|
|
+// This is the content of the mock zone (see below).
|
|
|
+// It's a sequence of textual RRs that is supposed to be parsed by
|
|
|
+// dns::masterLoad(). Some of the RRs are also used as the expected
|
|
|
+// data in specific tests, in which case they are referenced via specific
|
|
|
+// local variables (such as soa_txt).
|
|
|
+const char* const soa_txt = "example.com. 3600 IN SOA . . 0 0 0 0 0\n";
|
|
|
+const char* const zone_ns_txt =
|
|
|
+ "example.com. 3600 IN NS glue.delegation.example.com.\n"
|
|
|
+ "example.com. 3600 IN NS noglue.example.com.\n"
|
|
|
+ "example.com. 3600 IN NS example.net.\n";
|
|
|
+const char* const ns_addrs_txt =
|
|
|
+ "glue.delegation.example.com. 3600 IN A 192.0.2.153\n"
|
|
|
+ "glue.delegation.example.com. 3600 IN AAAA 2001:db8::53\n"
|
|
|
+ "noglue.example.com. 3600 IN A 192.0.2.53\n";
|
|
|
+const char* const delegation_txt =
|
|
|
+ "delegation.example.com. 3600 IN NS glue.delegation.example.com.\n"
|
|
|
+ "delegation.example.com. 3600 IN NS noglue.example.com.\n"
|
|
|
+ "delegation.example.com. 3600 IN NS cname.example.com.\n"
|
|
|
+ "delegation.example.com. 3600 IN NS example.org.\n";
|
|
|
+const char* const mx_txt =
|
|
|
+ "mx.example.com. 3600 IN MX 10 www.example.com.\n"
|
|
|
+ "mx.example.com. 3600 IN MX 20 mailer.example.org.\n"
|
|
|
+ "mx.example.com. 3600 IN MX 30 mx.delegation.example.com.\n";
|
|
|
+const char* const www_a_txt = "www.example.com. 3600 IN A 192.0.2.80\n";
|
|
|
+// The rest of data won't be referenced from the test cases.
|
|
|
+const char* const other_zone_rrs =
|
|
|
+ "cname.example.com. 3600 IN CNAME www.example.com.\n"
|
|
|
+ "cnamemailer.example.com. 3600 IN CNAME www.example.com.\n"
|
|
|
+ "cnamemx.example.com. 3600 IN MX 10 cnamemailer.example.com.\n"
|
|
|
+ "mx.delegation.example.com. 3600 IN A 192.0.2.100\n";
|
|
|
|
|
|
// This is a mock Zone class for testing.
|
|
|
-// It is a derived class of Zone, and simply hardcodes the results of find()
|
|
|
-// See the find() implementation if you want to know its content.
|
|
|
+// It is a derived class of Zone for the convenient of tests.
|
|
|
+// Its find() method emulates the common behavior of protocol compliant
|
|
|
+// zone classes, but simplifies some minor cases and also supports broken
|
|
|
+// behavior.
|
|
|
+// For simplicity, most names are assumed to be "in zone"; there's only
|
|
|
+// one zone cut at the point of name "delegation.example.com".
|
|
|
+// It doesn't handle empty non terminal nodes (if we need to test such cases
|
|
|
+// find() should have specialized code for it).
|
|
|
class MockZone : public Zone {
|
|
|
public:
|
|
|
- MockZone(bool has_SOA = true, bool has_apex_NS = true) :
|
|
|
+ MockZone() :
|
|
|
origin_(Name("example.com")),
|
|
|
- has_SOA_(has_SOA),
|
|
|
- has_apex_NS_(has_apex_NS),
|
|
|
- delegation_rrset(RRsetPtr(new RRset(Name("delegation.example.com"),
|
|
|
- RRClass::IN(), RRType::NS(),
|
|
|
- RRTTL(3600)))),
|
|
|
- cname_rrset(RRsetPtr(new RRset(Name("cname.example.com"),
|
|
|
- RRClass::IN(), RRType::CNAME(),
|
|
|
- RRTTL(3600)))),
|
|
|
- auth_ns_rrset(RRsetPtr(new RRset(Name("example.com"),
|
|
|
- RRClass::IN(), RRType::NS(),
|
|
|
- RRTTL(3600)))),
|
|
|
- mx_cname_rrset_(new RRset(Name("cnamemailer.example.com"),
|
|
|
- RRClass::IN(), RRType::CNAME(), RRTTL(3600))),
|
|
|
- mx_rrset_(new RRset(Name("mx.example.com"), RRClass::IN(),
|
|
|
- RRType::MX(), RRTTL(3600)))
|
|
|
+ delegation_name_("delegation.example.com"),
|
|
|
+ has_SOA_(true),
|
|
|
+ has_apex_NS_(true),
|
|
|
+ rrclass_(RRClass::IN())
|
|
|
{
|
|
|
- delegation_rrset->addRdata(rdata::generic::NS(
|
|
|
- Name("glue.ns.example.com")));
|
|
|
- delegation_rrset->addRdata(rdata::generic::NS(
|
|
|
- Name("noglue.example.com")));
|
|
|
- delegation_rrset->addRdata(rdata::generic::NS(
|
|
|
- Name("cname.example.com")));
|
|
|
- delegation_rrset->addRdata(rdata::generic::NS(
|
|
|
- Name("example.org")));
|
|
|
- cname_rrset->addRdata(rdata::generic::CNAME(
|
|
|
- Name("www.example.com")));
|
|
|
- auth_ns_rrset->addRdata(rdata::generic::NS(
|
|
|
- Name("glue.ns.example.com")));
|
|
|
- auth_ns_rrset->addRdata(rdata::generic::NS(
|
|
|
- Name("noglue.example.com")));
|
|
|
- auth_ns_rrset->addRdata(rdata::generic::NS(
|
|
|
- Name("example.net")));
|
|
|
- mx_rrset_->addRdata(isc::dns::rdata::generic::MX(10,
|
|
|
- Name("www.example.com")));
|
|
|
- mx_rrset_->addRdata(isc::dns::rdata::generic::MX(20,
|
|
|
- Name("mailer.example.org")));
|
|
|
- mx_rrset_->addRdata(isc::dns::rdata::generic::MX(30,
|
|
|
- Name("mx.delegation.example.com")));
|
|
|
- mx_cname_rrset_->addRdata(rdata::generic::CNAME(
|
|
|
- Name("mx.example.com")));
|
|
|
+ stringstream zone_stream;
|
|
|
+ zone_stream << soa_txt << zone_ns_txt << ns_addrs_txt <<
|
|
|
+ delegation_txt << mx_txt << www_a_txt << other_zone_rrs;
|
|
|
+
|
|
|
+ masterLoad(zone_stream, origin_, rrclass_,
|
|
|
+ boost::bind(&MockZone::loadRRset, this, _1));
|
|
|
}
|
|
|
- virtual const isc::dns::Name& getOrigin() const;
|
|
|
- virtual const isc::dns::RRClass& getClass() const;
|
|
|
+ virtual const isc::dns::Name& getOrigin() const { return (origin_); }
|
|
|
+ virtual const isc::dns::RRClass& getClass() const { return (rrclass_); }
|
|
|
+ virtual FindResult find(const isc::dns::Name& name,
|
|
|
+ const isc::dns::RRType& type,
|
|
|
+ RRsetList* target = NULL,
|
|
|
+ const FindOptions options = FIND_DEFAULT) const;
|
|
|
|
|
|
- FindResult find(const isc::dns::Name& name,
|
|
|
- const isc::dns::RRType& type,
|
|
|
- RRsetList* target = NULL,
|
|
|
- const FindOptions options = FIND_DEFAULT) const;
|
|
|
+ // If false is passed, it makes the zone broken as if it didn't have the
|
|
|
+ // SOA.
|
|
|
+ void setSOAFlag(bool on) { has_SOA_ = on; }
|
|
|
+
|
|
|
+ // If false is passed, it makes the zone broken as if it didn't have
|
|
|
+ // the apex NS.
|
|
|
+ void setApexNSFlag(bool on) { has_apex_NS_ = on; }
|
|
|
|
|
|
private:
|
|
|
- Name origin_;
|
|
|
+ typedef map<RRType, ConstRRsetPtr> RRsetStore;
|
|
|
+ typedef map<Name, RRsetStore> Domains;
|
|
|
+ Domains domains_;
|
|
|
+ void loadRRset(ConstRRsetPtr rrset) {
|
|
|
+ domains_[rrset->getName()][rrset->getType()] = rrset;
|
|
|
+ if (rrset->getName() == delegation_name_ &&
|
|
|
+ rrset->getType() == RRType::NS()) {
|
|
|
+ delegation_rrset_ = rrset;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ const Name origin_;
|
|
|
+ const Name delegation_name_;
|
|
|
bool has_SOA_;
|
|
|
bool has_apex_NS_;
|
|
|
- RRsetPtr delegation_rrset;
|
|
|
- RRsetPtr cname_rrset;
|
|
|
- RRsetPtr auth_ns_rrset;
|
|
|
- RRsetPtr mx_cname_rrset_;
|
|
|
- RRsetPtr mx_rrset_;
|
|
|
+ ConstRRsetPtr delegation_rrset_;
|
|
|
+ const RRClass rrclass_;
|
|
|
};
|
|
|
|
|
|
-const Name&
|
|
|
-MockZone::getOrigin() const {
|
|
|
- return (origin_);
|
|
|
-}
|
|
|
-
|
|
|
-const RRClass&
|
|
|
-MockZone::getClass() const {
|
|
|
- return (RRClass::IN());
|
|
|
-}
|
|
|
-
|
|
|
Zone::FindResult
|
|
|
MockZone::find(const Name& name, const RRType& type,
|
|
|
RRsetList* target, const FindOptions options) const
|
|
|
{
|
|
|
- // hardcode the find results
|
|
|
- if (name == Name("www.example.com") && type == RRType::A()) {
|
|
|
- return (FindResult(SUCCESS, a_rrset));
|
|
|
- } else if (name == Name("www.example.com")) {
|
|
|
- return (FindResult(NXRRSET, RRsetPtr()));
|
|
|
- } else if (name == Name("glue.ns.example.com") && type == RRType::A() &&
|
|
|
- (options & FIND_GLUE_OK) != 0) {
|
|
|
- return (FindResult(SUCCESS, glue_a_rrset));
|
|
|
- } else if (name == Name("noglue.example.com") && (type == RRType::A() ||
|
|
|
- type == RRType::ANY())) {
|
|
|
- return (FindResult(SUCCESS, noglue_a_rrset));
|
|
|
- } else if (name == Name("glue.ns.example.com") && type == RRType::AAAA() &&
|
|
|
- (options & FIND_GLUE_OK) != 0) {
|
|
|
- return (FindResult(SUCCESS, glue_aaaa_rrset));
|
|
|
- } else if (name == Name("example.com") && type == RRType::SOA() &&
|
|
|
- has_SOA_)
|
|
|
- {
|
|
|
- return (FindResult(SUCCESS, soa_rrset));
|
|
|
- } else if (name == Name("example.com") && type == RRType::NS() &&
|
|
|
- has_apex_NS_)
|
|
|
- {
|
|
|
- return (FindResult(SUCCESS, auth_ns_rrset));
|
|
|
- } else if (name == Name("example.com") && type == RRType::ANY()) {
|
|
|
- target->addRRset(soa_rrset);
|
|
|
- target->addRRset(auth_ns_rrset);
|
|
|
- return (FindResult(SUCCESS, RRsetPtr()));
|
|
|
- } else if (name == Name("mx.delegation.example.com") &&
|
|
|
- type == RRType::A() && (options & FIND_GLUE_OK) != 0)
|
|
|
- {
|
|
|
- return (FindResult(SUCCESS, delegated_mx_a_rrset));
|
|
|
- } else if (name == Name("delegation.example.com") ||
|
|
|
- name.compare(Name("delegation.example.com")).getRelation() ==
|
|
|
- NameComparisonResult::SUBDOMAIN)
|
|
|
- {
|
|
|
- return (FindResult(DELEGATION, delegation_rrset));
|
|
|
- } else if (name == Name("ns.example.com")) {
|
|
|
- return (FindResult(DELEGATION, ns_rrset));
|
|
|
- } else if (name == Name("nxdomain.example.com")) {
|
|
|
+ // Emulating a broken zone: mandatory apex RRs are missing if specifically
|
|
|
+ // configured so (which are rare cases).
|
|
|
+ if (name == origin_ && type == RRType::SOA() && !has_SOA_) {
|
|
|
return (FindResult(NXDOMAIN, RRsetPtr()));
|
|
|
- } else if (name == Name("nxrrset.example.com")) {
|
|
|
+ } else if (name == origin_ && type == RRType::NS() && !has_apex_NS_) {
|
|
|
+ return (FindResult(NXDOMAIN, RRsetPtr()));
|
|
|
+ }
|
|
|
+
|
|
|
+ // Special case for names on or under a zone cut
|
|
|
+ if ((options & FIND_GLUE_OK) == 0 &&
|
|
|
+ (name == delegation_name_ ||
|
|
|
+ name.compare(delegation_name_).getRelation() ==
|
|
|
+ NameComparisonResult::SUBDOMAIN)) {
|
|
|
+ return (FindResult(DELEGATION, delegation_rrset_));
|
|
|
+ }
|
|
|
+
|
|
|
+ // normal cases. names are searched for only per exact-match basis
|
|
|
+ // for simplicity.
|
|
|
+ const Domains::const_iterator found_domain = domains_.find(name);
|
|
|
+ if (found_domain != domains_.end()) {
|
|
|
+ // First, try exact match.
|
|
|
+ RRsetStore::const_iterator found_rrset =
|
|
|
+ found_domain->second.find(type);
|
|
|
+ if (found_rrset != found_domain->second.end()) {
|
|
|
+ return (FindResult(SUCCESS, found_rrset->second));
|
|
|
+ }
|
|
|
+
|
|
|
+ // If not found but the qtype is ANY, return the first RRset
|
|
|
+ if (!found_domain->second.empty() && type == RRType::ANY()) {
|
|
|
+ for (found_rrset = found_domain->second.begin();
|
|
|
+ found_rrset != found_domain->second.end(); found_rrset++)
|
|
|
+ {
|
|
|
+ // Insert RRs under the domain name into target
|
|
|
+ if (target) {
|
|
|
+ target->addRRset(
|
|
|
+ boost::const_pointer_cast<RRset>(found_rrset->second));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return (FindResult(SUCCESS, found_domain->second.begin()->second));
|
|
|
+ }
|
|
|
+
|
|
|
+ // Otherwise, if this domain name has CNAME, return it.
|
|
|
+ found_rrset = found_domain->second.find(RRType::CNAME());
|
|
|
+ if (found_rrset != found_domain->second.end()) {
|
|
|
+ return (FindResult(CNAME, found_rrset->second));
|
|
|
+ }
|
|
|
+
|
|
|
+ // Otherwise it's NXRRSET case.
|
|
|
return (FindResult(NXRRSET, RRsetPtr()));
|
|
|
- } else if ((name == Name("cname.example.com"))) {
|
|
|
- return (FindResult(CNAME, cname_rrset));
|
|
|
- } else if (name == Name("cnamemailer.example.com")) {
|
|
|
- return (FindResult(CNAME, mx_cname_rrset_));
|
|
|
- } else if (name == Name("mx.example.com")) {
|
|
|
- return (FindResult(SUCCESS, mx_rrset_));
|
|
|
- } else {
|
|
|
- return (FindResult(DNAME, RRsetPtr()));
|
|
|
}
|
|
|
+
|
|
|
+ // query name isn't found in our domains. returns NXDOMAIN.
|
|
|
+ return (FindResult(NXDOMAIN, RRsetPtr()));
|
|
|
}
|
|
|
|
|
|
class QueryTest : public ::testing::Test {
|
|
@@ -191,195 +199,145 @@ protected:
|
|
|
QueryTest() :
|
|
|
qname(Name("www.example.com")), qclass(RRClass::IN()),
|
|
|
qtype(RRType::A()), response(Message::RENDER),
|
|
|
- query(memory_datasrc, qname, qtype, response)
|
|
|
+ qid(response.getQid()), query_code(Opcode::QUERY().getCode())
|
|
|
{
|
|
|
response.setRcode(Rcode::NOERROR());
|
|
|
+ response.setOpcode(Opcode::QUERY());
|
|
|
+ // create and add a matching zone.
|
|
|
+ mock_zone = new MockZone();
|
|
|
+ memory_datasrc.addZone(ZonePtr(mock_zone));
|
|
|
}
|
|
|
+ MockZone* mock_zone;
|
|
|
MemoryDataSrc memory_datasrc;
|
|
|
const Name qname;
|
|
|
const RRClass qclass;
|
|
|
const RRType qtype;
|
|
|
Message response;
|
|
|
- Query query;
|
|
|
+ const qid_t qid;
|
|
|
+ const uint16_t query_code;
|
|
|
};
|
|
|
|
|
|
+// A wrapper to check resulting response message commonly used in
|
|
|
+// tests below.
|
|
|
+// check_origin needs to be specified only when the authority section has
|
|
|
+// an SOA RR. The interface is not generic enough but should be okay
|
|
|
+// for our test cases in practice.
|
|
|
+void
|
|
|
+responseCheck(Message& response, const isc::dns::Rcode& rcode,
|
|
|
+ unsigned int flags, const unsigned int ancount,
|
|
|
+ const unsigned int nscount, const unsigned int arcount,
|
|
|
+ const char* const expected_answer,
|
|
|
+ const char* const expected_authority,
|
|
|
+ const char* const expected_additional,
|
|
|
+ const Name& check_origin = Name::ROOT_NAME())
|
|
|
+{
|
|
|
+ // In our test cases QID, Opcode, and QDCOUNT should be constant, so
|
|
|
+ // we don't bother the test cases specifying these values.
|
|
|
+ headerCheck(response, response.getQid(), rcode, Opcode::QUERY().getCode(),
|
|
|
+ flags, 0, ancount, nscount, arcount);
|
|
|
+ if (expected_answer != NULL) {
|
|
|
+ rrsetsCheck(expected_answer,
|
|
|
+ response.beginSection(Message::SECTION_ANSWER),
|
|
|
+ response.endSection(Message::SECTION_ANSWER));
|
|
|
+ }
|
|
|
+ if (expected_authority != NULL) {
|
|
|
+ rrsetsCheck(expected_authority,
|
|
|
+ response.beginSection(Message::SECTION_AUTHORITY),
|
|
|
+ response.endSection(Message::SECTION_AUTHORITY),
|
|
|
+ check_origin);
|
|
|
+ }
|
|
|
+ if (expected_additional != NULL) {
|
|
|
+ rrsetsCheck(expected_additional,
|
|
|
+ response.beginSection(Message::SECTION_ADDITIONAL),
|
|
|
+ response.endSection(Message::SECTION_ADDITIONAL));
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
TEST_F(QueryTest, noZone) {
|
|
|
// There's no zone in the memory datasource. So the response should have
|
|
|
// REFUSED.
|
|
|
- EXPECT_NO_THROW(query.process());
|
|
|
+ MemoryDataSrc empty_memory_datasrc;
|
|
|
+ Query nozone_query(empty_memory_datasrc, qname, qtype, response);
|
|
|
+ EXPECT_NO_THROW(nozone_query.process());
|
|
|
EXPECT_EQ(Rcode::REFUSED(), response.getRcode());
|
|
|
}
|
|
|
|
|
|
TEST_F(QueryTest, exactMatch) {
|
|
|
- // add a matching zone.
|
|
|
- memory_datasrc.addZone(ZonePtr(new MockZone()));
|
|
|
+ Query query(memory_datasrc, qname, qtype, response);
|
|
|
EXPECT_NO_THROW(query.process());
|
|
|
// find match rrset
|
|
|
- EXPECT_TRUE(response.getHeaderFlag(Message::HEADERFLAG_AA));
|
|
|
- EXPECT_EQ(Rcode::NOERROR(), response.getRcode());
|
|
|
- EXPECT_TRUE(response.hasRRset(Message::SECTION_ANSWER,
|
|
|
- Name("www.example.com"), RRClass::IN(),
|
|
|
- RRType::A()));
|
|
|
- EXPECT_TRUE(response.hasRRset(Message::SECTION_AUTHORITY,
|
|
|
- Name("example.com"), RRClass::IN(),
|
|
|
- RRType::NS()));
|
|
|
- EXPECT_TRUE(response.hasRRset(Message::SECTION_ADDITIONAL,
|
|
|
- Name("glue.ns.example.com"),
|
|
|
- RRClass::IN(), RRType::A()));
|
|
|
- EXPECT_TRUE(response.hasRRset(Message::SECTION_ADDITIONAL,
|
|
|
- Name("glue.ns.example.com"),
|
|
|
- RRClass::IN(), RRType::AAAA()));
|
|
|
- EXPECT_TRUE(response.hasRRset(Message::SECTION_ADDITIONAL,
|
|
|
- Name("noglue.example.com"),
|
|
|
- RRClass::IN(), RRType::A()));
|
|
|
+ responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
|
|
|
+ www_a_txt, zone_ns_txt, ns_addrs_txt);
|
|
|
}
|
|
|
|
|
|
TEST_F(QueryTest, exactAddrMatch) {
|
|
|
// find match rrset, omit additional data which has already been provided
|
|
|
// in the answer section from the additional.
|
|
|
- memory_datasrc.addZone(ZonePtr(new MockZone()));
|
|
|
- const Name noglue_name(Name("noglue.example.com"));
|
|
|
- Query noglue_query(memory_datasrc, noglue_name, qtype, response);
|
|
|
- EXPECT_NO_THROW(noglue_query.process());
|
|
|
- EXPECT_TRUE(response.getHeaderFlag(Message::HEADERFLAG_AA));
|
|
|
- EXPECT_EQ(Rcode::NOERROR(), response.getRcode());
|
|
|
- EXPECT_TRUE(response.hasRRset(Message::SECTION_ANSWER,
|
|
|
- Name("noglue.example.com"), RRClass::IN(),
|
|
|
- RRType::A()));
|
|
|
- EXPECT_TRUE(response.hasRRset(Message::SECTION_AUTHORITY,
|
|
|
- Name("example.com"), RRClass::IN(),
|
|
|
- RRType::NS()));
|
|
|
- EXPECT_TRUE(response.hasRRset(Message::SECTION_ADDITIONAL,
|
|
|
- Name("glue.ns.example.com"),
|
|
|
- RRClass::IN(), RRType::A()));
|
|
|
- EXPECT_TRUE(response.hasRRset(Message::SECTION_ADDITIONAL,
|
|
|
- Name("glue.ns.example.com"),
|
|
|
- RRClass::IN(), RRType::AAAA()));
|
|
|
- EXPECT_FALSE(response.hasRRset(Message::SECTION_ADDITIONAL,
|
|
|
- Name("noglue.example.com"),
|
|
|
- RRClass::IN(), RRType::A()));
|
|
|
+ EXPECT_NO_THROW(Query(memory_datasrc, Name("noglue.example.com"), qtype,
|
|
|
+ response).process());
|
|
|
+
|
|
|
+ responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 2,
|
|
|
+ "noglue.example.com. 3600 IN A 192.0.2.53\n", zone_ns_txt,
|
|
|
+ "glue.delegation.example.com. 3600 IN A 192.0.2.153\n"
|
|
|
+ "glue.delegation.example.com. 3600 IN AAAA 2001:db8::53\n");
|
|
|
}
|
|
|
|
|
|
TEST_F(QueryTest, apexNSMatch) {
|
|
|
// find match rrset, omit authority data which has already been provided
|
|
|
// in the answer section from the authority section.
|
|
|
- memory_datasrc.addZone(ZonePtr(new MockZone()));
|
|
|
- const Name apex_name(Name("example.com"));
|
|
|
- Query apex_ns_query(memory_datasrc, apex_name, RRType::NS(), response);
|
|
|
- EXPECT_NO_THROW(apex_ns_query.process());
|
|
|
- EXPECT_TRUE(response.getHeaderFlag(Message::HEADERFLAG_AA));
|
|
|
- EXPECT_EQ(Rcode::NOERROR(), response.getRcode());
|
|
|
- EXPECT_TRUE(response.hasRRset(Message::SECTION_ANSWER,
|
|
|
- Name("example.com"), RRClass::IN(),
|
|
|
- RRType::NS()));
|
|
|
- EXPECT_FALSE(response.hasRRset(Message::SECTION_AUTHORITY,
|
|
|
- Name("example.com"), RRClass::IN(),
|
|
|
- RRType::NS()));
|
|
|
- EXPECT_TRUE(response.hasRRset(Message::SECTION_ADDITIONAL,
|
|
|
- Name("glue.ns.example.com"),
|
|
|
- RRClass::IN(), RRType::A()));
|
|
|
- EXPECT_TRUE(response.hasRRset(Message::SECTION_ADDITIONAL,
|
|
|
- Name("glue.ns.example.com"),
|
|
|
- RRClass::IN(), RRType::AAAA()));
|
|
|
- EXPECT_TRUE(response.hasRRset(Message::SECTION_ADDITIONAL,
|
|
|
- Name("noglue.example.com"),
|
|
|
- RRClass::IN(), RRType::A()));
|
|
|
+ EXPECT_NO_THROW(Query(memory_datasrc, Name("example.com"), RRType::NS(),
|
|
|
+ response).process());
|
|
|
+
|
|
|
+ responseCheck(response, Rcode::NOERROR(), AA_FLAG, 3, 0, 3,
|
|
|
+ zone_ns_txt, NULL, ns_addrs_txt);
|
|
|
}
|
|
|
|
|
|
TEST_F(QueryTest, exactAnyMatch) {
|
|
|
// find match rrset, omit additional data which has already been provided
|
|
|
// in the answer section from the additional.
|
|
|
- memory_datasrc.addZone(ZonePtr(new MockZone()));
|
|
|
- const Name noglue_name(Name("noglue.example.com"));
|
|
|
- Query noglue_query(memory_datasrc, noglue_name, RRType::ANY(), response);
|
|
|
- EXPECT_NO_THROW(noglue_query.process());
|
|
|
- EXPECT_TRUE(response.getHeaderFlag(Message::HEADERFLAG_AA));
|
|
|
- EXPECT_EQ(Rcode::NOERROR(), response.getRcode());
|
|
|
- EXPECT_TRUE(response.hasRRset(Message::SECTION_ANSWER,
|
|
|
- Name("noglue.example.com"), RRClass::IN(),
|
|
|
- RRType::A()));
|
|
|
- EXPECT_TRUE(response.hasRRset(Message::SECTION_AUTHORITY,
|
|
|
- Name("example.com"), RRClass::IN(),
|
|
|
- RRType::NS()));
|
|
|
- EXPECT_TRUE(response.hasRRset(Message::SECTION_ADDITIONAL,
|
|
|
- Name("glue.ns.example.com"),
|
|
|
- RRClass::IN(), RRType::A()));
|
|
|
- EXPECT_TRUE(response.hasRRset(Message::SECTION_ADDITIONAL,
|
|
|
- Name("glue.ns.example.com"),
|
|
|
- RRClass::IN(), RRType::AAAA()));
|
|
|
- EXPECT_FALSE(response.hasRRset(Message::SECTION_ADDITIONAL,
|
|
|
- Name("noglue.example.com"),
|
|
|
- RRClass::IN(), RRType::A()));
|
|
|
+ EXPECT_NO_THROW(Query(memory_datasrc, Name("noglue.example.com"),
|
|
|
+ RRType::ANY(), response).process());
|
|
|
+
|
|
|
+ responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 2,
|
|
|
+ "noglue.example.com. 3600 IN A 192.0.2.53\n",
|
|
|
+ zone_ns_txt,
|
|
|
+ "glue.delegation.example.com. 3600 IN A 192.0.2.153\n"
|
|
|
+ "glue.delegation.example.com. 3600 IN AAAA 2001:db8::53\n");
|
|
|
}
|
|
|
|
|
|
// This tests that when we need to look up Zone's apex NS records for
|
|
|
// authoritative answer, and there is no apex NS records. It should
|
|
|
// throw in that case.
|
|
|
TEST_F(QueryTest, noApexNS) {
|
|
|
- // Add a zone without apex NS records
|
|
|
- memory_datasrc.addZone(ZonePtr(new MockZone(true, false)));
|
|
|
- const Name noglue_name(Name("noglue.example.com"));
|
|
|
- Query noglue_query(memory_datasrc, noglue_name, qtype, response);
|
|
|
- EXPECT_THROW(noglue_query.process(), Query::NoApexNS);
|
|
|
- // We don't look into the response, as it throwed
|
|
|
+ // Disable apex NS record
|
|
|
+ mock_zone->setApexNSFlag(false);
|
|
|
+
|
|
|
+ EXPECT_THROW(Query(memory_datasrc, Name("noglue.example.com"), qtype,
|
|
|
+ response).process(), Query::NoApexNS);
|
|
|
+ // We don't look into the response, as it threw
|
|
|
}
|
|
|
|
|
|
TEST_F(QueryTest, delegation) {
|
|
|
- // add a matching zone.
|
|
|
- memory_datasrc.addZone(ZonePtr(new MockZone()));
|
|
|
- const Name delegation_name(Name("delegation.example.com"));
|
|
|
- Query delegation_query(memory_datasrc, delegation_name, qtype, response);
|
|
|
- EXPECT_NO_THROW(delegation_query.process());
|
|
|
- EXPECT_FALSE(response.getHeaderFlag(Message::HEADERFLAG_AA));
|
|
|
- EXPECT_EQ(Rcode::NOERROR(), response.getRcode());
|
|
|
- EXPECT_TRUE(response.hasRRset(Message::SECTION_AUTHORITY,
|
|
|
- Name("delegation.example.com"),
|
|
|
- RRClass::IN(), RRType::NS()));
|
|
|
- // glue address records
|
|
|
- EXPECT_TRUE(response.hasRRset(Message::SECTION_ADDITIONAL,
|
|
|
- Name("glue.ns.example.com"),
|
|
|
- RRClass::IN(), RRType::A()));
|
|
|
- EXPECT_TRUE(response.hasRRset(Message::SECTION_ADDITIONAL,
|
|
|
- Name("glue.ns.example.com"),
|
|
|
- RRClass::IN(), RRType::AAAA()));
|
|
|
- // noglue address records
|
|
|
- EXPECT_TRUE(response.hasRRset(Message::SECTION_ADDITIONAL,
|
|
|
- Name("noglue.example.com"),
|
|
|
- RRClass::IN(), RRType::A()));
|
|
|
- // NS name has a CNAME
|
|
|
- EXPECT_FALSE(response.hasRRset(Message::SECTION_ADDITIONAL,
|
|
|
- Name("www.example.com"),
|
|
|
- RRClass::IN(), RRType::A()));
|
|
|
- // NS name is out of zone
|
|
|
- EXPECT_FALSE(response.hasRRset(Message::SECTION_ADDITIONAL,
|
|
|
- Name("example.org"),
|
|
|
- RRClass::IN(), RRType::A()));
|
|
|
+ EXPECT_NO_THROW(Query(memory_datasrc, Name("delegation.example.com"),
|
|
|
+ qtype, response).process());
|
|
|
+
|
|
|
+ responseCheck(response, Rcode::NOERROR(), 0, 0, 4, 3,
|
|
|
+ NULL, delegation_txt, ns_addrs_txt);
|
|
|
}
|
|
|
|
|
|
TEST_F(QueryTest, nxdomain) {
|
|
|
- // add a matching zone.
|
|
|
- memory_datasrc.addZone(ZonePtr(new MockZone()));
|
|
|
- const Name nxdomain_name(Name("nxdomain.example.com"));
|
|
|
- Query nxdomain_query(memory_datasrc, nxdomain_name, qtype, response);
|
|
|
- EXPECT_NO_THROW(nxdomain_query.process());
|
|
|
- EXPECT_EQ(Rcode::NXDOMAIN(), response.getRcode());
|
|
|
- EXPECT_EQ(0, response.getRRCount(Message::SECTION_ANSWER));
|
|
|
- EXPECT_EQ(0, response.getRRCount(Message::SECTION_ADDITIONAL));
|
|
|
- EXPECT_TRUE(response.hasRRset(Message::SECTION_AUTHORITY,
|
|
|
- Name("example.com"), RRClass::IN(), RRType::SOA()));
|
|
|
+ EXPECT_NO_THROW(Query(memory_datasrc, Name("nxdomain.example.com"), qtype,
|
|
|
+ response).process());
|
|
|
+ responseCheck(response, Rcode::NXDOMAIN(), AA_FLAG, 0, 1, 0,
|
|
|
+ NULL, soa_txt, NULL, mock_zone->getOrigin());
|
|
|
}
|
|
|
|
|
|
TEST_F(QueryTest, nxrrset) {
|
|
|
- // add a matching zone.
|
|
|
- memory_datasrc.addZone(ZonePtr(new MockZone()));
|
|
|
- const Name nxrrset_name(Name("nxrrset.example.com"));
|
|
|
- Query nxrrset_query(memory_datasrc, nxrrset_name, qtype, response);
|
|
|
- EXPECT_NO_THROW(nxrrset_query.process());
|
|
|
- EXPECT_EQ(Rcode::NOERROR(), response.getRcode());
|
|
|
- EXPECT_EQ(0, response.getRRCount(Message::SECTION_ANSWER));
|
|
|
- EXPECT_EQ(0, response.getRRCount(Message::SECTION_ADDITIONAL));
|
|
|
- EXPECT_TRUE(response.hasRRset(Message::SECTION_AUTHORITY,
|
|
|
- Name("example.com"), RRClass::IN(), RRType::SOA()));
|
|
|
+ EXPECT_NO_THROW(Query(memory_datasrc, Name("www.example.com"),
|
|
|
+ RRType::TXT(), response).process());
|
|
|
+
|
|
|
+ responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 1, 0,
|
|
|
+ NULL, soa_txt, NULL, mock_zone->getOrigin());
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -387,27 +345,23 @@ TEST_F(QueryTest, nxrrset) {
|
|
|
* throw in that case.
|
|
|
*/
|
|
|
TEST_F(QueryTest, noSOA) {
|
|
|
- memory_datasrc.addZone(ZonePtr(new MockZone(false)));
|
|
|
+ // disable zone's SOA RR.
|
|
|
+ mock_zone->setSOAFlag(false);
|
|
|
|
|
|
// The NX Domain
|
|
|
- const Name nxdomain_name(Name("nxdomain.example.com"));
|
|
|
- Query nxdomain_query(memory_datasrc, nxdomain_name, qtype, response);
|
|
|
- EXPECT_THROW(nxdomain_query.process(), Query::NoSOA);
|
|
|
+ EXPECT_THROW(Query(memory_datasrc, Name("nxdomain.example.com"),
|
|
|
+ qtype, response).process(), Query::NoSOA);
|
|
|
// Of course, we don't look into the response, as it throwed
|
|
|
|
|
|
// NXRRSET
|
|
|
- const Name nxrrset_name(Name("nxrrset.example.com"));
|
|
|
- Query nxrrset_query(memory_datasrc, nxrrset_name, qtype, response);
|
|
|
- EXPECT_THROW(nxrrset_query.process(), Query::NoSOA);
|
|
|
+ EXPECT_THROW(Query(memory_datasrc, Name("nxrrset.example.com"),
|
|
|
+ qtype, response).process(), Query::NoSOA);
|
|
|
}
|
|
|
|
|
|
TEST_F(QueryTest, noMatchZone) {
|
|
|
// there's a zone in the memory datasource but it doesn't match the qname.
|
|
|
// should result in REFUSED.
|
|
|
- memory_datasrc.addZone(ZonePtr(new MockZone()));
|
|
|
- const Name nomatch_name(Name("example.org"));
|
|
|
- Query nomatch_query(memory_datasrc, nomatch_name, qtype, response);
|
|
|
- nomatch_query.process();
|
|
|
+ Query(memory_datasrc, Name("example.org"), qtype, response).process();
|
|
|
EXPECT_EQ(Rcode::REFUSED(), response.getRcode());
|
|
|
}
|
|
|
|
|
@@ -418,76 +372,27 @@ TEST_F(QueryTest, noMatchZone) {
|
|
|
* A record, other to unknown out of zone one.
|
|
|
*/
|
|
|
TEST_F(QueryTest, MX) {
|
|
|
- memory_datasrc.addZone(ZonePtr(new MockZone()));
|
|
|
- Name qname("mx.example.com");
|
|
|
- Query mx_query(memory_datasrc, qname, RRType::MX(), response);
|
|
|
- EXPECT_NO_THROW(mx_query.process());
|
|
|
- EXPECT_EQ(Rcode::NOERROR(), response.getRcode());
|
|
|
- EXPECT_TRUE(response.hasRRset(Message::SECTION_ANSWER,
|
|
|
- Name("mx.example.com"), RRClass::IN(), RRType::MX()));
|
|
|
- EXPECT_TRUE(response.hasRRset(Message::SECTION_ADDITIONAL,
|
|
|
- Name("www.example.com"), RRClass::IN(), RRType::A()));
|
|
|
- // We want to skip the additional ones related to authoritative
|
|
|
- RRsetPtr ns;
|
|
|
- for (SectionIterator<RRsetPtr> ai(response.beginSection(
|
|
|
- Message::SECTION_AUTHORITY)); ai != response.endSection(
|
|
|
- Message::SECTION_AUTHORITY); ++ai)
|
|
|
- {
|
|
|
- if ((*ai)->getName() == Name("example.com") && (*ai)->getType() ==
|
|
|
- RRType::NS())
|
|
|
- {
|
|
|
- ns = *ai;
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- /*
|
|
|
- * In fact, the MX RRset mentions three names, but we don't know anything
|
|
|
- * about one of them and one is under a zone cut, so we should have just
|
|
|
- * one RRset (A for www.example.com)
|
|
|
- */
|
|
|
- // We can't use getRRCount, as it counts RRs, not RRsets
|
|
|
- unsigned additional_count(0);
|
|
|
- for (SectionIterator<RRsetPtr> ai(response.beginSection(
|
|
|
- Message::SECTION_ADDITIONAL)); ai != response.endSection(
|
|
|
- Message::SECTION_ADDITIONAL); ++ai)
|
|
|
- {
|
|
|
- // Skip the ones for the NS record
|
|
|
- if (ns) {
|
|
|
- for (RdataIteratorPtr nsi(ns->getRdataIterator()); !nsi->isLast();
|
|
|
- nsi->next())
|
|
|
- {
|
|
|
- if ((*ai)->getName() ==
|
|
|
- dynamic_cast<const isc::dns::rdata::generic::NS&>(
|
|
|
- nsi->getCurrent()).getNSName())
|
|
|
- {
|
|
|
- goto NS_ADDITIONAL_DATA;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- // It is not related to the NS, then it must be related to the MX
|
|
|
- ++additional_count;
|
|
|
- EXPECT_EQ(Name("www.example.com"), (*ai)->getName());
|
|
|
- EXPECT_EQ(RRType::A(), (*ai)->getType());
|
|
|
- NS_ADDITIONAL_DATA:;
|
|
|
- }
|
|
|
- EXPECT_EQ(1, additional_count);
|
|
|
+ Query(memory_datasrc, Name("mx.example.com"), RRType::MX(),
|
|
|
+ response).process();
|
|
|
+
|
|
|
+ responseCheck(response, Rcode::NOERROR(), AA_FLAG, 3, 3, 4,
|
|
|
+ mx_txt, NULL,
|
|
|
+ (string(ns_addrs_txt) + string(www_a_txt)).c_str());
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
- * Test when we ask for MX and encounter an alias (CNAME in this case).
|
|
|
+ * Test when we ask for MX whose exchange is an alias (CNAME in this case).
|
|
|
*
|
|
|
- * This should not trigger the additional processing.
|
|
|
+ * This should not trigger the additional processing for the exchange.
|
|
|
*/
|
|
|
TEST_F(QueryTest, MXAlias) {
|
|
|
- memory_datasrc.addZone(ZonePtr(new MockZone()));
|
|
|
- Name qname("cnamemailer.example.com");
|
|
|
- Query mx_query(memory_datasrc, qname, RRType::MX(), response);
|
|
|
- EXPECT_NO_THROW(mx_query.process());
|
|
|
- EXPECT_EQ(Rcode::NOERROR(), response.getRcode());
|
|
|
- // We should not have the IP address in additional section
|
|
|
- // Currently, the section should be completely empty
|
|
|
- EXPECT_TRUE(response.beginSection(Message::SECTION_ADDITIONAL) ==
|
|
|
- response.endSection(Message::SECTION_ADDITIONAL));
|
|
|
-}
|
|
|
+ Query(memory_datasrc, Name("cnamemx.example.com"), RRType::MX(),
|
|
|
+ response).process();
|
|
|
|
|
|
+ // there shouldn't be no additional RRs for the exchanges (we have 3
|
|
|
+ // RRs for the NS). The normal MX case is tested separately so we don't
|
|
|
+ // bother to examine the answer (and authority) sections.
|
|
|
+ responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
|
|
|
+ NULL, NULL, ns_addrs_txt);
|
|
|
+}
|
|
|
}
|