2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-09-01 06:25:34 +00:00

70. [func] each

Added a hot-spot cache to libdatasrc to speed up access to
	repeatedly-queried data and reduce the number of queries to
	the underlying database; this should substantially improve
	performance.  Also added a "-n" ("no cache") option to
	bind10 and b10-auth to disable the cache if needed.
	(Trac #192, svn r2383)


git-svn-id: svn://bind10.isc.org/svn/bind10/trunk@2383 e5f2f494-b856-4b98-b285-d166d9295462
This commit is contained in:
Evan Hunt
2010-06-30 23:47:29 +00:00
parent 222a6e821c
commit 34159d6962
28 changed files with 1024 additions and 334 deletions

View File

@@ -1,3 +1,11 @@
70. [func] each
Added a hot-spot cache to libdatasrc to speed up access to
repeatedly-queried data and reduce the number of queries to
the underlying database; this should substantially improve
performance. Also added a "-n" ("no cache") option to
bind10 and b10-auth to disable the cache if needed.
(Trac #192, svn r2383)
bind10-devel-20100701 released on July 1, 2010 bind10-devel-20100701 released on July 1, 2010
69. [func]* jelte 69. [func]* jelte

View File

@@ -568,7 +568,7 @@ WARN_LOGFILE =
# directories like "/usr/src/myproject". Separate the files or directories # directories like "/usr/src/myproject". Separate the files or directories
# with spaces. # with spaces.
INPUT = ../src/lib/cc ../src/lib/config ../src/lib/dns ../src/lib/exceptions INPUT = ../src/lib/cc ../src/lib/config ../src/lib/dns ../src/lib/exceptions ../src/lib/datasrc
# This tag can be used to specify the character encoding of the source files # This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is

View File

@@ -60,7 +60,7 @@ private:
AuthSrvImpl(const AuthSrvImpl& source); AuthSrvImpl(const AuthSrvImpl& source);
AuthSrvImpl& operator=(const AuthSrvImpl& source); AuthSrvImpl& operator=(const AuthSrvImpl& source);
public: public:
AuthSrvImpl(); AuthSrvImpl(const bool use_cache);
isc::data::ElementPtr setDbFile(const isc::data::ElementPtr config); isc::data::ElementPtr setDbFile(const isc::data::ElementPtr config);
@@ -76,9 +76,13 @@ public:
/// Currently non-configurable, but will be. /// Currently non-configurable, but will be.
static const uint16_t DEFAULT_LOCAL_UDPSIZE = 4096; static const uint16_t DEFAULT_LOCAL_UDPSIZE = 4096;
/// Hot spot cache
isc::datasrc::HotCache cache_;
}; };
AuthSrvImpl::AuthSrvImpl() : cs_(NULL), verbose_mode_(false) AuthSrvImpl::AuthSrvImpl(const bool use_cache) :
cs_(NULL), verbose_mode_(false)
{ {
// cur_datasrc_ is automatically initialized by the default constructor, // cur_datasrc_ is automatically initialized by the default constructor,
// effectively being an empty (sqlite) data source. once ccsession is up // effectively being an empty (sqlite) data source. once ccsession is up
@@ -86,9 +90,12 @@ AuthSrvImpl::AuthSrvImpl() : cs_(NULL), verbose_mode_(false)
// add static data source // add static data source
data_sources_.addDataSrc(ConstDataSrcPtr(new StaticDataSrc)); data_sources_.addDataSrc(ConstDataSrcPtr(new StaticDataSrc));
// enable or disable the cache
cache_.setEnabled(use_cache);
} }
AuthSrv::AuthSrv() : impl_(new AuthSrvImpl) { AuthSrv::AuthSrv(const bool use_cache) : impl_(new AuthSrvImpl(use_cache)) {
} }
AuthSrv::~AuthSrv() { AuthSrv::~AuthSrv() {
@@ -239,7 +246,7 @@ AuthSrv::processMessage(InputBuffer& request_buffer, Message& message,
message.setUDPSize(AuthSrvImpl::DEFAULT_LOCAL_UDPSIZE); message.setUDPSize(AuthSrvImpl::DEFAULT_LOCAL_UDPSIZE);
try { try {
Query query(message, dnssec_ok); Query query(message, impl_->cache_, dnssec_ok);
impl_->data_sources_.doQuery(query); impl_->data_sources_.doQuery(query);
} catch (const Exception& ex) { } catch (const Exception& ex) {
if (impl_->verbose_mode_) { if (impl_->verbose_mode_) {

View File

@@ -43,7 +43,7 @@ private:
AuthSrv(const AuthSrv& source); AuthSrv(const AuthSrv& source);
AuthSrv& operator=(const AuthSrv& source); AuthSrv& operator=(const AuthSrv& source);
public: public:
explicit AuthSrv(); explicit AuthSrv(const bool use_cache);
~AuthSrv(); ~AuthSrv();
//@} //@}
/// \return \c true if the \message contains a response to be returned; /// \return \c true if the \message contains a response to be returned;

View File

@@ -86,7 +86,7 @@ my_command_handler(const string& command, const ElementPtr args) {
void void
usage() { usage() {
cerr << "Usage: b10-auth [-p port] [-4|-6]" << endl; cerr << "Usage: b10-auth [-p port] [-4|-6] [-nv]" << endl;
exit(1); exit(1);
} }
} // end of anonymous namespace } // end of anonymous namespace
@@ -95,9 +95,9 @@ int
main(int argc, char* argv[]) { main(int argc, char* argv[]) {
int ch; int ch;
const char* port = DNSPORT; const char* port = DNSPORT;
bool use_ipv4 = true, use_ipv6 = true; bool use_ipv4 = true, use_ipv6 = true, cache = true;
while ((ch = getopt(argc, argv, "46p:v")) != -1) { while ((ch = getopt(argc, argv, "46np:v")) != -1) {
switch (ch) { switch (ch) {
case '4': case '4':
// Note that -4 means "ipv4 only", we need to set "use_ipv6" here, // Note that -4 means "ipv4 only", we need to set "use_ipv6" here,
@@ -110,6 +110,9 @@ main(int argc, char* argv[]) {
// The same note as -4 applies. // The same note as -4 applies.
use_ipv4 = false; use_ipv4 = false;
break; break;
case 'n':
cache = false;
break;
case 'p': case 'p':
port = optarg; port = optarg;
break; break;
@@ -142,7 +145,7 @@ main(int argc, char* argv[]) {
specfile = string(AUTH_SPECFILE_LOCATION); specfile = string(AUTH_SPECFILE_LOCATION);
} }
auth_server = new AuthSrv; auth_server = new AuthSrv(cache);
auth_server->setVerbose(verbose_mode); auth_server->setVerbose(verbose_mode);
io_service = new asio_link::IOService(auth_server, port, use_ipv4, io_service = new asio_link::IOService(auth_server, port, use_ipv4,

View File

@@ -44,7 +44,7 @@ const char* BADCONFIG_TESTDB =
class AuthSrvTest : public ::testing::Test { class AuthSrvTest : public ::testing::Test {
protected: protected:
AuthSrvTest() : request_message(Message::RENDER), AuthSrvTest() : server(true), request_message(Message::RENDER),
parse_message(Message::PARSE), default_qid(0x1035), parse_message(Message::PARSE), default_qid(0x1035),
opcode(Opcode(Opcode::QUERY())), qname("www.example.com"), opcode(Opcode(Opcode::QUERY())), qname("www.example.com"),
qclass(RRClass::IN()), qtype(RRType::A()), ibuffer(NULL), qclass(RRClass::IN()), qtype(RRType::A()), ibuffer(NULL),

View File

@@ -176,8 +176,8 @@ class ProcessInfo:
class BoB: class BoB:
"""Boss of BIND class.""" """Boss of BIND class."""
def __init__(self, msgq_socket_file=None, auth_port=5300, verbose=False, def __init__(self, msgq_socket_file=None, auth_port=5300, nocache=False,
setuid=None, username=None): verbose=False, setuid=None, username=None):
"""Initialize the Boss of BIND. This is a singleton (only one """Initialize the Boss of BIND. This is a singleton (only one
can run). can run).
@@ -195,6 +195,7 @@ class BoB:
self.runnable = False self.runnable = False
self.uid = setuid self.uid = setuid
self.username = username self.username = username
self.nocache = nocache
def config_handler(self, new_config): def config_handler(self, new_config):
if self.verbose: if self.verbose:
@@ -302,6 +303,8 @@ class BoB:
# start b10-auth # start b10-auth
# XXX: this must be read from the configuration manager in the future # XXX: this must be read from the configuration manager in the future
authargs = ['b10-auth', '-p', str(self.auth_port)] authargs = ['b10-auth', '-p', str(self.auth_port)]
if self.nocache:
authargs += ['-n']
if self.verbose: if self.verbose:
sys.stdout.write("[bind10] Starting b10-auth using port %d\n" % sys.stdout.write("[bind10] Starting b10-auth using port %d\n" %
self.auth_port) self.auth_port)
@@ -557,6 +560,8 @@ def main():
parser = OptionParser(version=__version__) parser = OptionParser(version=__version__)
parser.add_option("-v", "--verbose", dest="verbose", action="store_true", parser.add_option("-v", "--verbose", dest="verbose", action="store_true",
help="display more about what is going on") help="display more about what is going on")
parser.add_option("-n", "--no-cache", action="store_true", dest="nocache",
default=False, help="disable hot-spot cache in b10-auth")
parser.add_option("-p", "--port", dest="auth_port", type="string", parser.add_option("-p", "--port", dest="auth_port", type="string",
action="callback", callback=check_port, default="5300", action="callback", callback=check_port, default="5300",
help="port the b10-auth daemon will use (default 5300)") help="port the b10-auth daemon will use (default 5300)")
@@ -621,7 +626,7 @@ def main():
# Go bob! # Go bob!
boss_of_bind = BoB(options.msgq_socket_file, int(options.auth_port), boss_of_bind = BoB(options.msgq_socket_file, int(options.auth_port),
options.verbose, setuid, username) options.nocache, options.verbose, setuid, username)
startup_result = boss_of_bind.startup() startup_result = boss_of_bind.startup()
if startup_result: if startup_result:
sys.stderr.write("[bind10] Error on startup: %s\n" % startup_result) sys.stderr.write("[bind10] Error on startup: %s\n" % startup_result)

View File

@@ -13,3 +13,4 @@ libdatasrc_la_SOURCES = data_source.h data_source.cc
libdatasrc_la_SOURCES += static_datasrc.h static_datasrc.cc libdatasrc_la_SOURCES += static_datasrc.h static_datasrc.cc
libdatasrc_la_SOURCES += sqlite3_datasrc.h sqlite3_datasrc.cc libdatasrc_la_SOURCES += sqlite3_datasrc.h sqlite3_datasrc.cc
libdatasrc_la_SOURCES += query.h query.cc libdatasrc_la_SOURCES += query.h query.cc
libdatasrc_la_SOURCES += cache.h cache.cc

View File

@@ -24,6 +24,10 @@
#include <boost/shared_ptr.hpp> #include <boost/shared_ptr.hpp>
#include <boost/foreach.hpp> #include <boost/foreach.hpp>
#include <datasrc/cache.h>
#include <datasrc/data_source.h>
#include <datasrc/query.h>
#include <dns/base32.h> #include <dns/base32.h>
#include <dns/buffer.h> #include <dns/buffer.h>
#include <dns/message.h> #include <dns/message.h>
@@ -35,9 +39,6 @@
#include <cc/data.h> #include <cc/data.h>
#include "data_source.h"
#include "query.h"
#define RETERR(x) do { \ #define RETERR(x) do { \
DataSrc::Result r = (x); \ DataSrc::Result r = (x); \
if (r != DataSrc::SUCCESS) \ if (r != DataSrc::SUCCESS) \
@@ -53,7 +54,37 @@ namespace datasrc {
typedef boost::shared_ptr<const Nsec3Param> ConstNsec3ParamPtr; typedef boost::shared_ptr<const Nsec3Param> ConstNsec3ParamPtr;
namespace { class ZoneInfo {
public:
ZoneInfo(DataSrc* ts,
const isc::dns::Name& n,
const isc::dns::RRClass& c,
const isc::dns::RRType& t = isc::dns::RRType::ANY()) :
top_source_(ts),
dsm_(((t == RRType::DS() && n.getLabelCount() != 1)
? n.split(1, n.getLabelCount() - 1) : n),
c)
{}
const Name* getEnclosingZone() {
if (dsm_.getEnclosingZone() == NULL) {
top_source_->findClosestEnclosure(dsm_);
}
return (dsm_.getEnclosingZone());
}
const DataSrc* getDataSource() {
if (dsm_.getDataSource() == NULL) {
top_source_->findClosestEnclosure(dsm_);
}
return (dsm_.getDataSource());
}
private:
const DataSrc* top_source_;
DataSrcMatch dsm_;
};
// Add a task to the query task queue to look up additional data // Add a task to the query task queue to look up additional data
// (i.e., address records for the names included in NS or MX records) // (i.e., address records for the names included in NS or MX records)
void void
@@ -68,14 +99,14 @@ getAdditional(Query& q, ConstRRsetPtr rrset) {
if (rrset->getType() == RRType::NS()) { if (rrset->getType() == RRType::NS()) {
const generic::NS& ns = dynamic_cast<const generic::NS&>(rd); const generic::NS& ns = dynamic_cast<const generic::NS&>(rd);
q.tasks().push(QueryTaskPtr( q.tasks().push(QueryTaskPtr(
new QueryTask(ns.getNSName(), q.qclass(), new QueryTask(q, ns.getNSName(),
Section::ADDITIONAL(), Section::ADDITIONAL(),
QueryTask::GLUE_QUERY, QueryTask::GLUE_QUERY,
QueryTask::GETADDITIONAL))); QueryTask::GETADDITIONAL)));
} else if (rrset->getType() == RRType::MX()) { } else if (rrset->getType() == RRType::MX()) {
const generic::MX& mx = dynamic_cast<const generic::MX&>(rd); const generic::MX& mx = dynamic_cast<const generic::MX&>(rd);
q.tasks().push(QueryTaskPtr( q.tasks().push(QueryTaskPtr(
new QueryTask(mx.getMXName(), q.qclass(), new QueryTask(q, mx.getMXName(),
Section::ADDITIONAL(), Section::ADDITIONAL(),
QueryTask::NOGLUE_QUERY, QueryTask::NOGLUE_QUERY,
QueryTask::GETADDITIONAL))); QueryTask::GETADDITIONAL)));
@@ -125,47 +156,313 @@ chaseCname(Query& q, QueryTaskPtr task, RRsetPtr rrset) {
return; return;
} }
// Stop chasing CNAMES after 16 lookups, to prevent loops
if (q.tooMany()) { if (q.tooMany()) {
return; return;
} }
q.tasks().push(QueryTaskPtr( q.tasks().push(QueryTaskPtr(
new QueryTask(dynamic_cast<const generic::CNAME&> new QueryTask(q, dynamic_cast<const generic::CNAME&>
(it->getCurrent()).getCname(), (it->getCurrent()).getCname(),
task->qclass, task->qtype, Section::ANSWER(),
task->qtype,
Section::ANSWER(),
QueryTask::FOLLOWCNAME))); QueryTask::FOLLOWCNAME)));
} }
// Perform the query specified in a QueryTask object // Check the cache for data which can answer the current query task.
DataSrc::Result bool
doQueryTask(const DataSrc* ds, const Name* zonename, QueryTask& task, checkCache(QueryTask& task, RRsetList& target) {
RRsetList& target) HotCache& cache = task.q.getCache();
{ RRsetList rrsets;
switch (task.op) { RRsetPtr rrset;
case QueryTask::AUTH_QUERY: int count = 0;
return (ds->findRRset(task.qname, task.qclass, task.qtype, uint32_t flags = 0, cflags = 0;
target, task.flags, zonename)); bool hit = false, found = false;
switch (task.op) {
case QueryTask::SIMPLE_QUERY: // Find exact RRset
// ANY queries must be handled by the low-level data source,
// or the results won't be guaranteed to be complete
if (task.qtype == RRType::ANY() || task.qclass == RRClass::ANY()) {
break;
}
hit = cache.retrieve(task.qname, task.qclass, task.qtype, rrset, flags);
if (hit) {
if (rrset) {
rrsets.addRRset(rrset);
target.append(rrsets);
}
task.flags = flags;
return (true);
}
break;
case QueryTask::AUTH_QUERY: // Find exact RRset or CNAME
if (task.qtype == RRType::ANY() || task.qclass == RRClass::ANY()) {
break;
}
hit = cache.retrieve(task.qname, task.qclass, task.qtype, rrset, flags);
if (!hit || !rrset || (flags & DataSrc::CNAME_FOUND) != 0) {
hit = cache.retrieve(task.qname, task.qclass, RRType::CNAME(),
rrset, flags);
}
if (hit) {
if (rrset) {
rrsets.addRRset(rrset);
target.append(rrsets);
}
task.flags = flags;
return (true);
}
break;
case QueryTask::GLUE_QUERY: // Find addresses
case QueryTask::NOGLUE_QUERY:
// (XXX: need to figure out how to deal with noglue case)
flags = 0;
hit = cache.retrieve(task.qname, task.qclass, RRType::A(),
rrset, cflags);
if (hit) {
flags |= cflags;
++count;
if (rrset) {
rrsets.addRRset(rrset);
found = true;
}
}
hit = cache.retrieve(task.qname, task.qclass, RRType::AAAA(),
rrset, flags);
if (hit) {
flags |= cflags;
++count;
if (rrset) {
rrsets.addRRset(rrset);
found = true;
}
}
if (count == 2) {
if (found) {
flags &= ~DataSrc::TYPE_NOT_FOUND;
target.append(rrsets);
}
task.flags = flags;
return (true);
}
break;
case QueryTask::REF_QUERY: // Find NS, DS and/or DNAME
flags = count = 0;
hit = cache.retrieve(task.qname, task.qclass, RRType::NS(),
rrset, cflags);
if (hit) {
flags |= cflags;
++count;
if (rrset) {
rrsets.addRRset(rrset);
found = true;
}
}
hit = cache.retrieve(task.qname, task.qclass, RRType::DS(),
rrset, flags);
if (hit) {
flags |= cflags;
++count;
if (rrset) {
rrsets.addRRset(rrset);
found = true;
}
}
hit = cache.retrieve(task.qname, task.qclass, RRType::DNAME(),
rrset, flags);
if (hit) {
flags |= cflags;
++count;
if (rrset) {
rrsets.addRRset(rrset);
found = true;
}
}
if (count == 3) {
if (found) {
flags &= ~DataSrc::TYPE_NOT_FOUND;
flags &= DataSrc::REFERRAL;
target.append(rrsets);
}
task.flags = flags;
return (true);
}
break;
}
return (false);
}
// Carry out the query specified in a QueryTask object
DataSrc::Result
doQueryTask(QueryTask& task, ZoneInfo& zoneinfo, RRsetList& target) {
HotCache& cache = task.q.getCache();
RRsetPtr rrset;
// First, check the cache for matching data
if (checkCache(task, target)) {
return (DataSrc::SUCCESS);
}
// Requested data weren't in the cache (or were, but had expired),
// so now we proceed with the low-level data source lookup, and cache
// whatever we find.
const DataSrc* ds = zoneinfo.getDataSource();
const Name* const zonename = zoneinfo.getEnclosingZone();
if (ds == NULL) {
task.flags |= DataSrc::NO_SUCH_ZONE;
return (DataSrc::SUCCESS);
}
DataSrc::Result result;
switch (task.op) {
case QueryTask::SIMPLE_QUERY: case QueryTask::SIMPLE_QUERY:
return (ds->findExactRRset(task.qname, task.qclass, task.qtype, result = ds->findExactRRset(task.qname, task.qclass, task.qtype,
target, task.flags, zonename)); target, task.flags, zonename);
if (result != DataSrc::SUCCESS) {
return (result);
}
if (task.qclass == RRClass::ANY()) {
// XXX: Currently, RRsetList::findRRset() doesn't handle
// ANY queries, and without that we can't cache the results,
// so we just return in that case.
return (result);
}
if (task.flags == 0) {
rrset = target.findRRset(task.qtype, task.qclass);
assert(rrset);
cache.addPositive(rrset, task.flags);
} else {
cache.addNegative(task.qname, task.qclass, task.qtype, task.flags);
}
return (result);
case QueryTask::AUTH_QUERY:
result = ds->findRRset(task.qname, task.qclass, task.qtype,
target, task.flags, zonename);
if (result != DataSrc::SUCCESS) {
return (result);
}
if (task.qclass == RRClass::ANY()) {
return (result);
}
if (task.qtype == RRType::ANY()) {
BOOST_FOREACH(RRsetPtr rr, target) {
cache.addPositive(rr, task.flags);
}
} else if ((task.flags & DataSrc::CNAME_FOUND) != 0) {
cache.addNegative(task.qname, task.qclass, task.qtype, task.flags);
rrset = target.findRRset(RRType::CNAME(), task.qclass);
assert(rrset);
cache.addPositive(rrset, task.flags);
} else if ((task.flags & DataSrc::DATA_NOT_FOUND) == 0) {
if (task.qtype != RRType::CNAME()) {
cache.addNegative(task.qname, task.qclass, RRType::CNAME(),
task.flags);
}
rrset = target.findRRset(task.qtype, task.qclass);
assert(rrset);
cache.addPositive(rrset, task.flags);
} else {
cache.addNegative(task.qname, task.qclass, task.qtype, task.flags);
}
return (result);
case QueryTask::GLUE_QUERY: case QueryTask::GLUE_QUERY:
case QueryTask::NOGLUE_QUERY: case QueryTask::NOGLUE_QUERY:
return (ds->findAddrs(task.qname, task.qclass, target, result = ds->findAddrs(task.qname, task.qclass, target,
task.flags, zonename)); task.flags, zonename);
if (result != DataSrc::SUCCESS) {
return (result);
}
if (task.qclass == RRClass::ANY()) {
return (result);
}
rrset = target.findRRset(RRType::A(), task.qclass);
if (rrset) {
cache.addPositive(rrset, task.flags);
} else {
cache.addNegative(task.qname, task.qclass, RRType::A(), task.flags);
}
rrset = target.findRRset(RRType::AAAA(), task.qclass);
if (rrset) {
cache.addPositive(rrset, task.flags);
} else {
cache.addNegative(task.qname, task.qclass, RRType::AAAA(),
task.flags);
}
return (result);
case QueryTask::REF_QUERY: case QueryTask::REF_QUERY:
return (ds->findReferral(task.qname, task.qclass, target, result = ds->findReferral(task.qname, task.qclass, target,
task.flags, zonename)); task.flags, zonename);
if (result != DataSrc::SUCCESS) {
return (result);
}
if (task.qclass == RRClass::ANY()) {
return (result);
}
rrset = target.findRRset(RRType::NS(), task.qclass);
if (rrset) {
cache.addPositive(rrset, task.flags);
} else {
cache.addNegative(task.qname, task.qclass, RRType::NS(),
task.flags);
}
rrset = target.findRRset(RRType::DS(), task.qclass);
if (rrset) {
cache.addPositive(rrset, task.flags);
} else {
cache.addNegative(task.qname, task.qclass, RRType::DS(),
task.flags);
}
rrset = target.findRRset(RRType::DNAME(), task.qclass);
if (rrset) {
cache.addPositive(rrset, task.flags);
} else {
cache.addNegative(task.qname, task.qclass, RRType::DNAME(),
task.flags);
}
return (result);
} }
// Not reached // Not reached
return (DataSrc::ERROR); return (DataSrc::ERROR);
} }
// Add an RRset (and its associated RRSIG) to a message section, // Add an RRset (and its associated RRSIG) to a message section,
// checking first to ensure that there isn't already an RRset with // checking first to ensure that there isn't already an RRset with
// the same name and type. // the same name and type.
@@ -202,12 +499,12 @@ copyAuth(Query& q, RRsetList& auth) {
// Query for referrals (i.e., NS/DS or DNAME) at a given name // Query for referrals (i.e., NS/DS or DNAME) at a given name
inline bool inline bool
refQuery(const Name& name, const RRClass& qclass, const DataSrc* ds, refQuery(const Query& q, const Name& name, ZoneInfo& zoneinfo,
const Name* zonename, RRsetList& target) RRsetList& target)
{ {
QueryTask newtask(name, qclass, QueryTask::REF_QUERY); QueryTask newtask(q, name, QueryTask::REF_QUERY);
if (doQueryTask(ds, zonename, newtask, target) != DataSrc::SUCCESS) { if (doQueryTask(newtask, zoneinfo, target) != DataSrc::SUCCESS) {
// Lookup failed // Lookup failed
return (false); return (false);
} }
@@ -224,16 +521,22 @@ refQuery(const Name& name, const RRClass& qclass, const DataSrc* ds,
// referrals. Note that we exclude the apex name and query name themselves; // referrals. Note that we exclude the apex name and query name themselves;
// they'll be handled in a normal lookup in the zone. // they'll be handled in a normal lookup in the zone.
inline bool inline bool
hasDelegation(const DataSrc* ds, const Name* zonename, Query& q, hasDelegation(Query& q, QueryTaskPtr task, ZoneInfo& zoneinfo) {
QueryTaskPtr task) const Name* const zonename = zoneinfo.getEnclosingZone();
{ if (zonename == NULL) {
if (task->state == QueryTask::GETANSWER) {
q.message().setRcode(Rcode::REFUSED());
}
return (false);
}
const int diff = task->qname.getLabelCount() - zonename->getLabelCount(); const int diff = task->qname.getLabelCount() - zonename->getLabelCount();
if (diff > 1) { if (diff > 1) {
bool found = false; bool found = false;
RRsetList ref; RRsetList ref;
for (int i = diff - 1; i > 0; --i) { for (int i = diff - 1; i > 0; --i) {
const Name sub(task->qname.split(i)); const Name sub(task->qname.split(i));
if (refQuery(sub, q.qclass(), ds, zonename, ref)) { if (refQuery(q, sub, zoneinfo, ref)) {
found = true; found = true;
break; break;
} }
@@ -280,12 +583,12 @@ hasDelegation(const DataSrc* ds, const Name* zonename, Query& q,
} }
inline DataSrc::Result inline DataSrc::Result
addSOA(Query& q, const Name* zonename, const DataSrc* ds) { addSOA(Query& q, ZoneInfo& zoneinfo) {
RRsetList soa; RRsetList soa;
QueryTask newtask(*zonename, q.qclass(), RRType::SOA(), const Name* const zonename = zoneinfo.getEnclosingZone();
QueryTask::SIMPLE_QUERY); QueryTask newtask(q, *zonename, RRType::SOA(), QueryTask::SIMPLE_QUERY);
RETERR(doQueryTask(ds, zonename, newtask, soa)); RETERR(doQueryTask(newtask, zoneinfo, soa));
if (newtask.flags != 0) { if (newtask.flags != 0) {
return (DataSrc::ERROR); return (DataSrc::ERROR);
} }
@@ -296,14 +599,11 @@ addSOA(Query& q, const Name* zonename, const DataSrc* ds) {
} }
inline DataSrc::Result inline DataSrc::Result
addNSEC(Query& q, const QueryTaskPtr task, const Name& name, addNSEC(Query& q, const Name& name, ZoneInfo& zoneinfo) {
const Name& zonename, const DataSrc* ds)
{
RRsetList nsec; RRsetList nsec;
QueryTask newtask(name, task->qclass, RRType::NSEC(), QueryTask newtask(q, name, RRType::NSEC(), QueryTask::SIMPLE_QUERY);
QueryTask::SIMPLE_QUERY); RETERR(doQueryTask(newtask, zoneinfo, nsec));
RETERR(doQueryTask(ds, &zonename, newtask, nsec));
if (newtask.flags == 0) { if (newtask.flags == 0) {
addToMessage(q, Section::AUTHORITY(), addToMessage(q, Section::AUTHORITY(),
nsec.findRRset(RRType::NSEC(), q.qclass())); nsec.findRRset(RRType::NSEC(), q.qclass()));
@@ -313,23 +613,31 @@ addNSEC(Query& q, const QueryTaskPtr task, const Name& name,
} }
inline DataSrc::Result inline DataSrc::Result
getNsec3(const DataSrc* ds, const Name& zonename, const RRClass& qclass, getNsec3(Query& q, ZoneInfo& zoneinfo, string& hash, RRsetPtr& target) {
string& hash, RRsetPtr& target) const DataSrc* ds = zoneinfo.getDataSource();
{ const Name* const zonename = zoneinfo.getEnclosingZone();
if (ds == NULL) {
q.message().setRcode(Rcode::SERVFAIL());
return (DataSrc::ERROR);
}
RRsetList rl; RRsetList rl;
RETERR(ds->findCoveringNSEC3(zonename, hash, rl)); RETERR(ds->findCoveringNSEC3(*zonename, hash, rl));
target = rl.findRRset(RRType::NSEC3(), qclass); target = rl.findRRset(RRType::NSEC3(), q.qclass());
return (DataSrc::SUCCESS); return (DataSrc::SUCCESS);
} }
ConstNsec3ParamPtr ConstNsec3ParamPtr
getNsec3Param(Query& q, const DataSrc* ds, const Name& zonename) { getNsec3Param(Query& q, ZoneInfo& zoneinfo) {
DataSrc::Result result; DataSrc::Result result;
RRsetList nsec3param; RRsetList nsec3param;
QueryTask newtask(zonename, q.qclass(), RRType::NSEC3PARAM(), const Name* const zonename = zoneinfo.getEnclosingZone();
QueryTask newtask(q, *zonename, RRType::NSEC3PARAM(),
QueryTask::SIMPLE_QUERY); QueryTask::SIMPLE_QUERY);
result = doQueryTask(ds, &zonename, newtask, nsec3param); result = doQueryTask(newtask, zoneinfo, nsec3param);
newtask.flags &= ~DataSrc::REFERRAL; newtask.flags &= ~DataSrc::REFERRAL;
if (result != DataSrc::SUCCESS || newtask.flags != 0) { if (result != DataSrc::SUCCESS || newtask.flags != 0) {
return (ConstNsec3ParamPtr()); return (ConstNsec3ParamPtr());
@@ -356,15 +664,16 @@ getNsec3Param(Query& q, const DataSrc* ds, const Name& zonename) {
} }
inline DataSrc::Result inline DataSrc::Result
proveNX(Query& q, QueryTaskPtr task, const DataSrc* ds, proveNX(Query& q, QueryTaskPtr task, ZoneInfo& zoneinfo, const bool wildcard) {
const Name& zonename, const bool wildcard) Message& m = q.message();
{ const Name* const zonename = zoneinfo.getEnclosingZone();
ConstNsec3ParamPtr nsec3 = getNsec3Param(q, ds, zonename); ConstNsec3ParamPtr nsec3 = getNsec3Param(q, zoneinfo);
if (nsec3 != NULL) { if (nsec3 != NULL) {
// Attach the NSEC3 record covering the QNAME // Attach the NSEC3 record covering the QNAME
RRsetPtr rrset; RRsetPtr rrset;
string hash1(nsec3->getHash(task->qname)); string hash1(nsec3->getHash(task->qname));
RETERR(getNsec3(ds, zonename, q.qclass(), hash1, rrset)); RETERR(getNsec3(q, zoneinfo, hash1, rrset));
addToMessage(q, Section::AUTHORITY(), rrset); addToMessage(q, Section::AUTHORITY(), rrset);
// If this is an NXRRSET or NOERROR/NODATA, we're done // If this is an NXRRSET or NOERROR/NODATA, we're done
@@ -373,7 +682,7 @@ proveNX(Query& q, QueryTaskPtr task, const DataSrc* ds,
} }
// Find the closest provable enclosing name for QNAME // Find the closest provable enclosing name for QNAME
Name enclosure(zonename); Name enclosure(*zonename);
const int diff = task->qname.getLabelCount() - const int diff = task->qname.getLabelCount() -
enclosure.getLabelCount(); enclosure.getLabelCount();
string hash2; string hash2;
@@ -388,7 +697,7 @@ proveNX(Query& q, QueryTaskPtr task, const DataSrc* ds,
// hash2 will be overwritten with the actual hash found; // hash2 will be overwritten with the actual hash found;
// we don't want to use one until we find an exact match // we don't want to use one until we find an exact match
RETERR(getNsec3(ds, zonename, q.qclass(), hash2, rrset)); RETERR(getNsec3(q, zoneinfo, hash2, rrset));
if (hash2 == nodehash) { if (hash2 == nodehash) {
addToMessage(q, Section::AUTHORITY(), rrset); addToMessage(q, Section::AUTHORITY(), rrset);
break; break;
@@ -403,19 +712,24 @@ proveNX(Query& q, QueryTaskPtr task, const DataSrc* ds,
// Otherwise, there is no wildcard record, so we must add a // Otherwise, there is no wildcard record, so we must add a
// covering NSEC3 to prove that it doesn't exist. // covering NSEC3 to prove that it doesn't exist.
string hash3(nsec3->getHash(Name("*").concatenate(enclosure))); string hash3(nsec3->getHash(Name("*").concatenate(enclosure)));
RETERR(getNsec3(ds, zonename, q.qclass(), hash3, rrset)); RETERR(getNsec3(q, zoneinfo, hash3, rrset));
if (hash3 != hash1 && hash3 != hash2) { if (hash3 != hash1 && hash3 != hash2) {
addToMessage(q, Section::AUTHORITY(), rrset); addToMessage(q, Section::AUTHORITY(), rrset);
} }
} else { } else {
Name nsecname(task->qname); Name nsecname(task->qname);
if ((task->flags & DataSrc::NAME_NOT_FOUND) != 0 || wildcard) { if ((task->flags & DataSrc::NAME_NOT_FOUND) != 0 || wildcard) {
ds->findPreviousName(task->qname, nsecname, &zonename); const DataSrc* ds = zoneinfo.getDataSource();
if (ds == NULL) {
m.setRcode(Rcode::SERVFAIL());
return (DataSrc::ERROR);
}
ds->findPreviousName(task->qname, nsecname, zonename);
} }
RETERR(addNSEC(q, task, nsecname, zonename, ds)); RETERR(addNSEC(q, nsecname, zoneinfo));
if ((task->flags & DataSrc::TYPE_NOT_FOUND) != 0 || if ((task->flags & DataSrc::TYPE_NOT_FOUND) != 0 ||
nsecname == zonename) nsecname == *zonename)
{ {
return (DataSrc::SUCCESS); return (DataSrc::SUCCESS);
} }
@@ -427,7 +741,7 @@ proveNX(Query& q, QueryTaskPtr task, const DataSrc* ds,
// Otherwise, there is no wildcard record, so we must add an // Otherwise, there is no wildcard record, so we must add an
// NSEC for the zone to prove the wildcard doesn't exist. // NSEC for the zone to prove the wildcard doesn't exist.
RETERR(addNSEC(q, task, zonename, zonename, ds)); RETERR(addNSEC(q, *zonename, zoneinfo));
} }
return (DataSrc::SUCCESS); return (DataSrc::SUCCESS);
@@ -435,9 +749,7 @@ proveNX(Query& q, QueryTaskPtr task, const DataSrc* ds,
// Attempt a wildcard lookup // Attempt a wildcard lookup
inline DataSrc::Result inline DataSrc::Result
tryWildcard(Query& q, QueryTaskPtr task, const DataSrc* ds, tryWildcard(Query& q, QueryTaskPtr task, ZoneInfo& zoneinfo, bool& found) {
const Name* zonename, bool& found)
{
Message& m = q.message(); Message& m = q.message();
DataSrc::Result result; DataSrc::Result result;
found = false; found = false;
@@ -448,6 +760,7 @@ tryWildcard(Query& q, QueryTaskPtr task, const DataSrc* ds,
return (DataSrc::SUCCESS); return (DataSrc::SUCCESS);
} }
const Name* const zonename = zoneinfo.getEnclosingZone();
const int diff = task->qname.getLabelCount() - zonename->getLabelCount(); const int diff = task->qname.getLabelCount() - zonename->getLabelCount();
if (diff < 1) { if (diff < 1) {
return (DataSrc::SUCCESS); return (DataSrc::SUCCESS);
@@ -459,9 +772,9 @@ tryWildcard(Query& q, QueryTaskPtr task, const DataSrc* ds,
for (int i = 1; i <= diff; ++i) { for (int i = 1; i <= diff; ++i) {
const Name& wname(star.concatenate(task->qname.split(i))); const Name& wname(star.concatenate(task->qname.split(i)));
QueryTask newtask(wname, task->qclass, task->qtype, Section::ANSWER(), QueryTask newtask(q, wname, task->qtype, Section::ANSWER(),
QueryTask::AUTH_QUERY); QueryTask::AUTH_QUERY);
result = doQueryTask(ds, zonename, newtask, wild); result = doQueryTask(newtask, zoneinfo, wild);
if (result == DataSrc::SUCCESS) { if (result == DataSrc::SUCCESS) {
if (newtask.flags == 0) { if (newtask.flags == 0) {
task->flags &= ~DataSrc::NAME_NOT_FOUND; task->flags &= ~DataSrc::NAME_NOT_FOUND;
@@ -487,7 +800,7 @@ tryWildcard(Query& q, QueryTaskPtr task, const DataSrc* ds,
if (found) { if (found) {
// Prove the nonexistence of the name we were looking for // Prove the nonexistence of the name we were looking for
if (q.wantDnssec()) { if (q.wantDnssec()) {
result = proveNX(q, task, ds, *zonename, true); result = proveNX(q, task, zoneinfo, true);
if (result != DataSrc::SUCCESS) { if (result != DataSrc::SUCCESS) {
m.setRcode(Rcode::SERVFAIL()); m.setRcode(Rcode::SERVFAIL());
return (DataSrc::ERROR); return (DataSrc::ERROR);
@@ -511,7 +824,7 @@ tryWildcard(Query& q, QueryTaskPtr task, const DataSrc* ds,
} }
RRsetList auth; RRsetList auth;
if (!refQuery(*zonename, q.qclass(), ds, zonename, auth)) { if (!refQuery(q, *zonename, zoneinfo, auth)) {
return (DataSrc::ERROR); return (DataSrc::ERROR);
} }
@@ -521,7 +834,6 @@ tryWildcard(Query& q, QueryTaskPtr task, const DataSrc* ds,
return (DataSrc::SUCCESS); return (DataSrc::SUCCESS);
} }
} // end of anonymous namespace
// //
// doQuery: Processes a query. // doQuery: Processes a query.
@@ -531,6 +843,12 @@ DataSrc::doQuery(Query& q) {
Message& m = q.message(); Message& m = q.message();
vector<RRsetPtr> additional; vector<RRsetPtr> additional;
// Record the fact that the query is being processed by the
// current data source.
q.setDatasrc(this);
// Process the query task queue. (The queue is initialized
// and the first task placed on it by the Query constructor.)
m.clearHeaderFlag(MessageFlag::AA()); m.clearHeaderFlag(MessageFlag::AA());
while (!q.tasks().empty()) { while (!q.tasks().empty()) {
QueryTaskPtr task = q.tasks().front(); QueryTaskPtr task = q.tasks().front();
@@ -549,41 +867,40 @@ DataSrc::doQuery(Query& q) {
return; return;
} }
// Find the closest enclosing zone for which we are authoritative, ZoneInfo zoneinfo(this, task->qname, task->qclass, task->qtype);
// and the concrete data source which is authoritative for it.
// (Note that RRtype DS queries need to go to the parent.)
const int nlabels = task->qname.getLabelCount() - 1;
NameMatch match(nlabels != 0 && task->qtype == RRType::DS() ?
task->qname.split(1) : task->qname);
findClosestEnclosure(match, task->qclass);
const DataSrc* datasource = match.bestDataSrc();
const Name* zonename = match.closestName();
assert((datasource == NULL && zonename == NULL) ||
(datasource != NULL && zonename != NULL));
RRsetList data; RRsetList data;
Result result = SUCCESS; Result result = SUCCESS;
if (datasource) {
// For these query task types, if there is more than // For these query task types, if there is more than
// one level between the zone name and qname, we need to // one level between the zone name and qname, we need to
// check the intermediate nodes for referrals. // check the intermediate nodes for referrals.
if ((task->op == QueryTask::AUTH_QUERY || if ((task->op == QueryTask::AUTH_QUERY ||
task->op == QueryTask::NOGLUE_QUERY) && task->op == QueryTask::NOGLUE_QUERY) &&
hasDelegation(datasource, zonename, q, task)) { hasDelegation(q, task, zoneinfo)) {
continue; continue;
} }
result = doQueryTask(datasource, zonename, *task, data); result = doQueryTask(*task, zoneinfo, data);
if (result != SUCCESS) { if (result != SUCCESS) {
m.setRcode(Rcode::SERVFAIL()); m.setRcode(Rcode::SERVFAIL());
return; return;
} }
// No such zone. If we're chasing cnames or adding additional
// data, that's okay, but if doing an original query, return
// REFUSED.
if (task->flags == NO_SUCH_ZONE) {
if (task->state == QueryTask::GETANSWER) {
m.setRcode(Rcode::REFUSED());
return;
}
continue;
}
// Query found a referral; let's find out if that was expected-- // Query found a referral; let's find out if that was expected--
// i.e., if an NS was at the zone apex, or if we were querying // i.e., if an NS was at the zone apex, or if we were querying
// specifically for, and found, a DS, NSEC, or DNAME record. // specifically for, and found, a DS, NSEC, or DNAME record.
const Name* const zonename = zoneinfo.getEnclosingZone();
if ((task->flags & REFERRAL) != 0 && if ((task->flags & REFERRAL) != 0 &&
(zonename->getLabelCount() == task->qname.getLabelCount() || (zonename->getLabelCount() == task->qname.getLabelCount() ||
((task->qtype == RRType::NSEC() || ((task->qtype == RRType::NSEC() ||
@@ -592,18 +909,6 @@ DataSrc::doQuery(Query& q) {
data.findRRset(task->qtype, task->qclass)))) { data.findRRset(task->qtype, task->qclass)))) {
task->flags &= ~REFERRAL; task->flags &= ~REFERRAL;
} }
} else {
task->flags = NO_SUCH_ZONE;
// No such zone. If we're chasing cnames or adding additional
// data, that's okay, but if doing an original query, return
// REFUSED.
if (task->state == QueryTask::GETANSWER) {
m.setRcode(Rcode::REFUSED());
return;
}
continue;
}
if (result == SUCCESS && task->flags == 0) { if (result == SUCCESS && task->flags == 0) {
bool have_ns = false, need_auth = false; bool have_ns = false, need_auth = false;
@@ -626,13 +931,12 @@ DataSrc::doQuery(Query& q) {
// Add the NS records for the enclosing zone to // Add the NS records for the enclosing zone to
// the authority section. // the authority section.
RRsetList auth; RRsetList auth;
if (!refQuery(*zonename, q.qclass(), datasource, zonename, const DataSrc* ds = zoneinfo.getDataSource();
auth) || if (!refQuery(q, Name(*zonename), zoneinfo, auth) ||
!auth.findRRset(RRType::NS(), !auth.findRRset(RRType::NS(), ds->getClass())) {
datasource->getClass())) {
isc_throw(DataSourceError, isc_throw(DataSourceError,
"NS RR not found in " << *zonename << "/" << "NS RR not found in " << *zonename << "/" <<
datasource->getClass()); q.qclass());
} }
copyAuth(q, auth); copyAuth(q, auth);
@@ -673,8 +977,7 @@ DataSrc::doQuery(Query& q) {
if (task->state == QueryTask::GETANSWER) { if (task->state == QueryTask::GETANSWER) {
RRsetList auth; RRsetList auth;
m.clearHeaderFlag(MessageFlag::AA()); m.clearHeaderFlag(MessageFlag::AA());
if (!refQuery(task->qname, q.qclass(), datasource, zonename, if (!refQuery(q, task->qname, zoneinfo, auth)) {
auth)) {
m.setRcode(Rcode::SERVFAIL()); m.setRcode(Rcode::SERVFAIL());
return; return;
} }
@@ -697,7 +1000,7 @@ DataSrc::doQuery(Query& q) {
// and the name was not found, we need to find out whether // and the name was not found, we need to find out whether
// there are any relevant wildcards. // there are any relevant wildcards.
bool wildcard_found = false; bool wildcard_found = false;
result = tryWildcard(q, task, datasource, zonename, wildcard_found); result = tryWildcard(q, task, zoneinfo, wildcard_found);
if (result != SUCCESS) { if (result != SUCCESS) {
m.setRcode(Rcode::SERVFAIL()); m.setRcode(Rcode::SERVFAIL());
return; return;
@@ -719,21 +1022,22 @@ DataSrc::doQuery(Query& q) {
m.setRcode(Rcode::NXDOMAIN()); m.setRcode(Rcode::NXDOMAIN());
} }
result = addSOA(q, zonename, datasource); result = addSOA(q, zoneinfo);
if (result != SUCCESS) { if (result != SUCCESS) {
isc_throw(DataSourceError, isc_throw(DataSourceError,
"SOA RR not found in" << *zonename << "SOA RR not found in" << *zonename <<
"/" << datasource->getClass()); "/" << q.qclass());
} }
} }
Name nsecname(task->qname); Name nsecname(task->qname);
if ((task->flags & NAME_NOT_FOUND) != 0) { if ((task->flags & NAME_NOT_FOUND) != 0) {
datasource->findPreviousName(task->qname, nsecname, zonename); const DataSrc* ds = zoneinfo.getDataSource();
ds->findPreviousName(task->qname, nsecname, zonename);
} }
if (q.wantDnssec()) { if (q.wantDnssec()) {
result = proveNX(q, task, datasource, *zonename, false); result = proveNX(q, task, zoneinfo, false);
if (result != DataSrc::SUCCESS) { if (result != DataSrc::SUCCESS) {
m.setRcode(Rcode::SERVFAIL()); m.setRcode(Rcode::SERVFAIL());
return; return;
@@ -858,25 +1162,38 @@ MetaDataSrc::removeDataSrc(ConstDataSrcPtr data_src) {
} }
void void
MetaDataSrc::findClosestEnclosure(NameMatch& match, const RRClass& qclass) const MetaDataSrc::findClosestEnclosure(DataSrcMatch& match) const {
{ if (getClass() != match.getClass() &&
if (getClass() != qclass && getClass() != RRClass::ANY() && match.getClass() != RRClass::ANY()) {
getClass() != RRClass::ANY() && qclass != RRClass::ANY()) {
return; return;
} }
BOOST_FOREACH (ConstDataSrcPtr data_src, data_sources) { BOOST_FOREACH (ConstDataSrcPtr data_src, data_sources) {
data_src->findClosestEnclosure(match, qclass); data_src->findClosestEnclosure(match);
} }
} }
NameMatch::~NameMatch() { DataSrcMatch::~DataSrcMatch() {
delete closest_name_; delete closest_name_;
} }
void void
NameMatch::update(const DataSrc& new_source, const Name& container) { DataSrcMatch::update(const DataSrc& new_source, const Name& container) {
if (getClass() != new_source.getClass() && getClass() != RRClass::ANY() &&
new_source.getClass() != RRClass::ANY())
{
return;
}
if (closest_name_ == NULL) { if (closest_name_ == NULL) {
const NameComparisonResult::NameRelation cmp =
getName().compare(container).getRelation();
if (cmp != NameComparisonResult::EQUAL &&
cmp != NameComparisonResult::SUBDOMAIN)
{
return;
}
closest_name_ = new Name(container); closest_name_ = new Name(container);
best_source_ = &new_source; best_source_ = &new_source;
return; return;
@@ -884,7 +1201,7 @@ NameMatch::update(const DataSrc& new_source, const Name& container) {
if (container.compare(*closest_name_).getRelation() == if (container.compare(*closest_name_).getRelation() ==
NameComparisonResult::SUBDOMAIN) { NameComparisonResult::SUBDOMAIN) {
const Name* newname = new Name(container); Name* newname = new Name(container);
delete closest_name_; delete closest_name_;
closest_name_ = newname; closest_name_ = newname;
best_source_ = &new_source; best_source_ = &new_source;

View File

@@ -40,7 +40,7 @@ class RRsetList;
namespace datasrc { namespace datasrc {
class NameMatch; class DataSrcMatch;
class Query; class Query;
class DataSrc; class DataSrc;
@@ -89,12 +89,15 @@ public:
// NAME_NOT_FOUND: The node does not exist in the data source. // NAME_NOT_FOUND: The node does not exist in the data source.
// TYPE_NOT_FOUND: The node does not contain the requested RRType // TYPE_NOT_FOUND: The node does not contain the requested RRType
// NO_SUCH_ZONE: The zone does not exist in this data source. // NO_SUCH_ZONE: The zone does not exist in this data source.
//
// DATA_NOT_FOUND: A combination of the last three, for coding convenience
enum QueryResponseFlags { enum QueryResponseFlags {
REFERRAL = 0x01, REFERRAL = 0x01,
CNAME_FOUND = 0x02, CNAME_FOUND = 0x02,
NAME_NOT_FOUND = 0x04, NAME_NOT_FOUND = 0x04,
TYPE_NOT_FOUND = 0x08, TYPE_NOT_FOUND = 0x08,
NO_SUCH_ZONE = 0x10 NO_SUCH_ZONE = 0x10,
DATA_NOT_FOUND = (NAME_NOT_FOUND|TYPE_NOT_FOUND|NO_SUCH_ZONE)
}; };
// 'High-level' methods. These will be implemented by the // 'High-level' methods. These will be implemented by the
@@ -107,9 +110,7 @@ public:
// 'Medium-level' methods. This will be implemented by the general // 'Medium-level' methods. This will be implemented by the general
// DataSrc class but MAY be overwritten by subclasses. // DataSrc class but MAY be overwritten by subclasses.
virtual void findClosestEnclosure(NameMatch& match, virtual void findClosestEnclosure(DataSrcMatch& match) const = 0;
const isc::dns::RRClass& qclasss)
const = 0;
// Optional 'low-level' methods. These will have stub implementations // Optional 'low-level' methods. These will have stub implementations
// in the general DataSrc class but MAY be overwritten by subclasses // in the general DataSrc class but MAY be overwritten by subclasses
@@ -179,9 +180,7 @@ public:
virtual void doQuery(Query& q); virtual void doQuery(Query& q);
virtual void findClosestEnclosure(NameMatch& match, virtual void findClosestEnclosure(DataSrcMatch& match) const = 0;
const isc::dns::RRClass& qclass)
const = 0;
const isc::dns::RRClass& getClass() const { return rrclass; } const isc::dns::RRClass& getClass() const { return rrclass; }
void setClass(isc::dns::RRClass& c) { rrclass = c; } void setClass(isc::dns::RRClass& c) { rrclass = c; }
@@ -250,8 +249,7 @@ public:
void removeDataSrc(ConstDataSrcPtr data_src); void removeDataSrc(ConstDataSrcPtr data_src);
size_t dataSrcCount() { return data_sources.size(); }; size_t dataSrcCount() { return data_sources.size(); };
void findClosestEnclosure(NameMatch& match, void findClosestEnclosure(DataSrcMatch& match) const;
const isc::dns::RRClass& qclass) const;
// Actual queries for data should not be sent to a MetaDataSrc object, // Actual queries for data should not be sent to a MetaDataSrc object,
// so we return NOT_IMPLEMENTED if we receive any. // so we return NOT_IMPLEMENTED if we receive any.
@@ -298,31 +296,107 @@ private:
std::vector<ConstDataSrcPtr> data_sources; std::vector<ConstDataSrcPtr> data_sources;
}; };
class NameMatch { /// \brief Information about the zone along with the %data source that best
/// matches a give name and RR class.
///
/// A \c DataSrcMatch object is created with a domain name and RR class to
/// hold the search state of looking for the zone and the %data source that
/// stores the zone that best match the given name and RR class.
/// The application of this class passes an object of \c DataSrcMatch to
/// one or more ^data sources via their \c findClosestEnclosure() method.
/// The %data source searches its content for the given key, and update
/// the state if it finds a better zone than the currently recorded one.
///
/// The state of a \c DataSrcMatch object should be updated if and only if:
/// - The specified RR class and the RR class of the %data source are the
// same, or the specified RR class is ANY; and
/// - There is no matching %data source and name found (which is probably
/// wrong, see below), or the given enclosing name gives a longer match
/// than the currently stored enclosing name against the specified name.
class DataSrcMatch {
/// ///
/// \name Constructors, Assignment Operator and Destructor. /// \name Constructors, Assignment Operator and Destructor.
/// ///
/// Note: The copy constructor and the assignment operator are intentionally /// Note: The copy constructor and the assignment operator are
/// defined as private. /// intentionally defined as private.
//@{
private: private:
NameMatch(const NameMatch& source); DataSrcMatch(const DataSrcMatch& source);
NameMatch& operator=(const NameMatch& source); DataSrcMatch& operator=(const DataSrcMatch& source);
public: public:
NameMatch(const isc::dns::Name& qname) : /// \brief The constructor.
closest_name_(NULL), best_source_(NULL), qname_(qname) {} ///
~NameMatch(); /// This constructor normally doesn't throw an exception. However,
/// it creates a copy of the given name object, which may require memory
/// allocation, and if it fails the corresponding standard exception will
/// be thrown.
///
/// \param name The domain name to be matched.
/// \param rrclass The RR class to be matched
DataSrcMatch(const isc::dns::Name& name,
const isc::dns::RRClass& rrclass) :
closest_name_(NULL), best_source_(NULL),
name_(name), rrclass_(rrclass)
{}
~DataSrcMatch();
//@} //@}
/// \name Getter and Setter Methods
//@{
/// \brief Returns the name to be matched.
const isc::dns::Name& getName() const { return (name_); }
/// \brief Returns the RR class to be matched.
///
/// This method never throws an exception.
const isc::dns::RRClass& getClass() const { return (rrclass_); }
/// \brief Returns the best enclosing zone name found for the given
// name and RR class so far.
///
/// \return A pointer to the zone apex \c Name, NULL if none found yet.
///
/// This method never throws an exception.
const isc::dns::Name* getEnclosingZone() const { return (closest_name_); }
/// \brief Returns the best %data source found for the given name and
/// RR class so far.
///
/// This method never throws an exception.
///
/// \return A pointer to a concrete %data source, NULL if none found yet.
const DataSrc* getDataSource() const { return (best_source_); }
//@}
/// \brief Update the object state with better information if possible.
///
/// This method is intended to be called by a concrete %data source's
/// \c findClosestEnclosure() method to store the best match for
/// the given name and class that has been found so far.
///
/// It compares the best name (if found) and \c container, and if the
/// latter gives a longer match, it will install the given %data source
/// and the enclosing name as the best match;
/// if there is no known pair of %data source and enclosing name,
/// this method will install the given pair unconditionally.
/// (which is probably BAD);
/// otherwise this method does nothing.
///
/// In any case, if a new pair of %data source and enclosing name are
/// installed, a new name object will be internally allocated.
/// And, if memory allocation fails the corresponding standard exception
/// will be thrown.
///
/// \param new_source A candidate %data source that gives a better match.
/// \param container The enclosing name of the matching zone in
/// \c new_source.
void update(const DataSrc& new_source, const isc::dns::Name& container); void update(const DataSrc& new_source, const isc::dns::Name& container);
const isc::dns::Name& qname() const { return (qname_); }
const isc::dns::Name* closestName() const { return (closest_name_); }
const DataSrc* bestDataSrc() const { return (best_source_); }
private: private:
const isc::dns::Name* closest_name_; isc::dns::Name* closest_name_;
const DataSrc* best_source_; const DataSrc* best_source_;
const isc::dns::Name qname_; const isc::dns::Name name_;
const isc::dns::RRClass& rrclass_;
}; };
class Nsec3Param { class Nsec3Param {

View File

@@ -28,36 +28,37 @@ using namespace isc::dns;
namespace isc { namespace isc {
namespace datasrc { namespace datasrc {
QueryTask::QueryTask(const isc::dns::Name& n, const isc::dns::RRClass& c, QueryTask::QueryTask(const Query& qry, const isc::dns::Name& n,
const isc::dns::RRType& t, const isc::dns::Section& sect) : const isc::dns::RRType& t, const isc::dns::Section& sect) :
qname(n), qclass(c), qtype(t), section(sect), op(AUTH_QUERY), q(qry), qname(n), qclass(qry.qclass()), qtype(t), section(sect),
state(GETANSWER), flags(0) op(AUTH_QUERY), state(GETANSWER), flags(0)
{} {}
QueryTask::QueryTask(const isc::dns::Name& n, const isc::dns::RRClass& c, QueryTask::QueryTask(const Query& qry, const isc::dns::Name& n,
const isc::dns::RRType& t, const isc::dns::Section& sect, const isc::dns::RRType& t, const isc::dns::Section& sect,
const Op o) : const Op o) :
qname(n), qclass(c), qtype(t), section(sect), op(o), state(GETANSWER), q(qry), qname(n), qclass(qry.qclass()), qtype(t), section(sect), op(o),
flags(0) state(GETANSWER), flags(0)
{} {}
QueryTask::QueryTask(const isc::dns::Name& n, const isc::dns::RRClass& c, QueryTask::QueryTask(const Query& qry, const isc::dns::Name& n,
const isc::dns::RRType& t, const isc::dns::Section& sect, const isc::dns::RRType& t, const isc::dns::Section& sect,
const State st) : const State st) :
qname(n), qclass(c), qtype(t), section(sect), op(AUTH_QUERY), state(st), q(qry), qname(n), qclass(qry.qclass()), qtype(t), section(sect),
flags(0) op(AUTH_QUERY), state(st), flags(0)
{} {}
QueryTask::QueryTask(const isc::dns::Name& n, const isc::dns::RRClass& c, QueryTask::QueryTask(const Query& qry, const isc::dns::Name& n,
const isc::dns::RRType& t, const isc::dns::Section& sect, const isc::dns::RRType& t, const isc::dns::Section& sect,
const Op o, const State st) : const Op o, const State st) :
qname(n), qclass(c), qtype(t), section(sect), op(o), state(st), flags(0) q(qry), qname(n), qclass(qry.qclass()), qtype(t), section(sect), op(o),
state(st), flags(0)
{} {}
QueryTask::QueryTask(const isc::dns::Name& n, const isc::dns::RRClass& c, QueryTask::QueryTask(const Query& qry, const isc::dns::Name& n,
const isc::dns::RRType& t, const Op o) : const isc::dns::RRType& t, const Op o) :
qname(n), qclass(c), qtype(t), section(Section::ANSWER()), op(o), q(qry), qname(n), qclass(qry.qclass()), qtype(t),
state(GETANSWER), flags(0) section(Section::ANSWER()), op(o), state(GETANSWER), flags(0)
{ {
if (op != SIMPLE_QUERY) { if (op != SIMPLE_QUERY) {
isc_throw(Unexpected, "invalid constructor for this task operation"); isc_throw(Unexpected, "invalid constructor for this task operation");
@@ -65,21 +66,20 @@ QueryTask::QueryTask(const isc::dns::Name& n, const isc::dns::RRClass& c,
} }
// A referral query doesn't need to specify section, state, or type. // A referral query doesn't need to specify section, state, or type.
QueryTask::QueryTask(const isc::dns::Name& n, const isc::dns::RRClass& c, QueryTask::QueryTask(const Query& qry, const isc::dns::Name& n, const Op o) :
const Op o) : q(qry), qname(n), qclass(qry.qclass()), qtype(RRType::ANY()),
qname(n), qclass(c), qtype(RRType::ANY()), section(Section::ANSWER()), section(Section::ANSWER()), op(o), state(GETANSWER), flags(0)
op(o), state(GETANSWER), flags(0)
{ {
if (op != REF_QUERY) { if (op != REF_QUERY) {
isc_throw(Unexpected, "invalid constructor for this task operation"); isc_throw(Unexpected, "invalid constructor for this task operation");
} }
} }
QueryTask::QueryTask(const isc::dns::Name& n, const isc::dns::RRClass& c, QueryTask::QueryTask(const Query& qry, const isc::dns::Name& n,
const isc::dns::Section& sect, const Op o, const isc::dns::Section& sect, const Op o,
const State st) : const State st) :
qname(n), qclass(c), qtype(RRType::ANY()), section(sect), op(o), q(qry), qname(n), qclass(qry.qclass()), qtype(RRType::ANY()),
state(st), flags(0) section(sect), op(o), state(st), flags(0)
{ {
if (op != GLUE_QUERY && op != NOGLUE_QUERY) { if (op != GLUE_QUERY && op != NOGLUE_QUERY) {
isc_throw(Unexpected, "invalid constructor for this task operation"); isc_throw(Unexpected, "invalid constructor for this task operation");
@@ -88,9 +88,9 @@ QueryTask::QueryTask(const isc::dns::Name& n, const isc::dns::RRClass& c,
QueryTask::~QueryTask() {} QueryTask::~QueryTask() {}
Query::Query(Message& m, bool dnssec) : Query::Query(Message& m, HotCache& c, bool dnssec) :
status_(PENDING), qname_(NULL), qclass_(NULL), qtype_(NULL), status_(PENDING), qname_(NULL), qclass_(NULL), qtype_(NULL),
message_(&m), want_additional_(true), want_dnssec_(dnssec) cache_(&c), message_(&m), want_additional_(true), want_dnssec_(dnssec)
{ {
// Check message formatting // Check message formatting
if (message_->getRRCount(Section::QUESTION()) != 1) { if (message_->getRRCount(Section::QUESTION()) != 1) {
@@ -104,7 +104,7 @@ Query::Query(Message& m, bool dnssec) :
qtype_ = &question->getType(); qtype_ = &question->getType();
restarts_ = 0; restarts_ = 0;
querytasks_.push(QueryTaskPtr(new QueryTask(*qname_, *qclass_, *qtype_, querytasks_.push(QueryTaskPtr(new QueryTask(*this, *qname_, *qtype_,
Section::ANSWER()))); Section::ANSWER())));
} }

View File

@@ -19,6 +19,9 @@
#include <boost/shared_ptr.hpp> #include <boost/shared_ptr.hpp>
#include <datasrc/cache.h>
#include <datasrc/data_source.h>
#include <dns/name.h> #include <dns/name.h>
#include <dns/message.h> #include <dns/message.h>
#include <dns/rrtype.h> #include <dns/rrtype.h>
@@ -27,9 +30,11 @@
#include <queue> #include <queue>
namespace isc { namespace isc {
namespace datasrc { namespace datasrc {
class Query;
typedef boost::shared_ptr<Query> QueryPtr;
// An individual task to be carried out by the query logic // An individual task to be carried out by the query logic
class QueryTask { class QueryTask {
private: private:
@@ -41,6 +46,9 @@ public:
// XXX: Members are currently public, but should probably be // XXX: Members are currently public, but should probably be
// moved to private and wrapped in get() functions later. // moved to private and wrapped in get() functions later.
// The \c Query that this \c QueryTask was created to service.
const Query& q;
// The standard query tuple: qname/qclass/qtype. // The standard query tuple: qname/qclass/qtype.
// Note that qtype is ignored in the GLUE_QUERY/NOGLUE_QUERY case. // Note that qtype is ignored in the GLUE_QUERY/NOGLUE_QUERY case.
const isc::dns::Name qname; const isc::dns::Name qname;
@@ -118,15 +126,14 @@ public:
uint32_t flags; uint32_t flags;
// Constructors // Constructors
QueryTask(const isc::dns::Name& n, const isc::dns::RRClass& c, QueryTask(const Query& q, const isc::dns::Name& n,
const isc::dns::RRType& t, const isc::dns::Section& sect); const isc::dns::RRType& t, const isc::dns::Section& sect);
QueryTask(const isc::dns::Name& n, const isc::dns::RRClass& c, QueryTask(const Query& q, const isc::dns::Name& n,
const isc::dns::RRType& t, const isc::dns::Section& sect, const isc::dns::RRType& t, const isc::dns::Section& sect, Op o);
Op o); QueryTask(const Query& q, const isc::dns::Name& n,
QueryTask(const isc::dns::Name& n, const isc::dns::RRClass& c,
const isc::dns::RRType& t, const isc::dns::Section& sect, const isc::dns::RRType& t, const isc::dns::Section& sect,
const State st); const State st);
QueryTask(const isc::dns::Name& n, const isc::dns::RRClass& c, QueryTask(const Query& q, const isc::dns::Name& n,
const isc::dns::RRType& t, const isc::dns::Section& sect, const isc::dns::RRType& t, const isc::dns::Section& sect,
Op o, State st); Op o, State st);
@@ -134,12 +141,12 @@ public:
// to simplify the code. // to simplify the code.
// //
// A simple query doesn't need to specify section or state. // A simple query doesn't need to specify section or state.
QueryTask(const isc::dns::Name& n, const isc::dns::RRClass& c, QueryTask(const Query& q, const isc::dns::Name& n,
const isc::dns::RRType& t, Op o); const isc::dns::RRType& t, Op o);
// A referral query doesn't need to specify section, state, or type. // A referral query doesn't need to specify section, state, or type.
QueryTask(const isc::dns::Name& n, const isc::dns::RRClass& c, Op o); QueryTask(const Query& q, const isc::dns::Name& n, Op o);
// A glue (or noglue) query doesn't need to specify type. // A glue (or noglue) query doesn't need to specify type.
QueryTask(const isc::dns::Name& n, const isc::dns::RRClass& c, QueryTask(const Query& q, const isc::dns::Name& n,
const isc::dns::Section& sect, Op o, State st); const isc::dns::Section& sect, Op o, State st);
~QueryTask(); ~QueryTask();
@@ -148,9 +155,6 @@ public:
typedef boost::shared_ptr<QueryTask> QueryTaskPtr; typedef boost::shared_ptr<QueryTask> QueryTaskPtr;
typedef std::queue<QueryTaskPtr> QueryTaskQueue; typedef std::queue<QueryTaskPtr> QueryTaskQueue;
class Query;
typedef boost::shared_ptr<Query> QueryPtr;
// Data Source query // Data Source query
class Query { class Query {
public: public:
@@ -171,7 +175,7 @@ private:
Query& operator=(const Query& source); Query& operator=(const Query& source);
public: public:
// Query constructor // Query constructor
Query(isc::dns::Message& m, bool dnssec); Query(isc::dns::Message& m, HotCache& c, bool dnssec);
/// \brief The destructor. /// \brief The destructor.
virtual ~Query(); virtual ~Query();
//@} //@}
@@ -179,17 +183,17 @@ public:
// wantAdditional() == true indicates that additional-section data // wantAdditional() == true indicates that additional-section data
// should be looked up while processing this query. false indicates // should be looked up while processing this query. false indicates
// that we're only interested in answer-section data // that we're only interested in answer-section data
bool wantAdditional() { return want_additional_; } bool wantAdditional() { return (want_additional_); }
void setWantAdditional(bool d) { want_additional_ = d; } void setWantAdditional(bool d) { want_additional_ = d; }
// wantDnssec() == true indicates that DNSSEC data should be retrieved // wantDnssec() == true indicates that DNSSEC data should be retrieved
// from the data source when this query is being processed // from the data source when this query is being processed
bool wantDnssec() const { return want_dnssec_; } bool wantDnssec() const { return (want_dnssec_); }
void setWantDnssec(bool d) { want_dnssec_ = d; } void setWantDnssec(bool d) { want_dnssec_ = d; }
const isc::dns::Name& qname() const { return *qname_; } const isc::dns::Name& qname() const { return (*qname_); }
const isc::dns::RRClass& qclass() const { return *qclass_; } const isc::dns::RRClass& qclass() const { return (*qclass_); }
const isc::dns::RRType& qtype() const { return *qtype_; } const isc::dns::RRType& qtype() const { return (*qtype_); }
// Note: these can't be constant member functions because they expose // Note: these can't be constant member functions because they expose
// writable 'handles' of internal member variables. It's questionable // writable 'handles' of internal member variables. It's questionable
@@ -197,10 +201,10 @@ public:
// corresponding members are public (which itself is not a good practice // corresponding members are public (which itself is not a good practice
// but it's a different topic), but at the moment we keep them. // but it's a different topic), but at the moment we keep them.
// We should definitely revisit the design later. // We should definitely revisit the design later.
isc::dns::Message& message() { return *message_; } isc::dns::Message& message() { return (*message_); }
QueryTaskQueue& tasks() { return querytasks_; } QueryTaskQueue& tasks() { return (querytasks_); }
Status status() const { return status_; } Status status() const { return (status_); }
void setStatus(Status s) { status_ = s; } void setStatus(Status s) { status_ = s; }
// Limit CNAME chains to 16 per query, to avoid loops // Limit CNAME chains to 16 per query, to avoid loops
@@ -211,6 +215,13 @@ public:
return (false); return (false);
} }
void setDatasrc(DataSrc* ds) { datasrc_ = ds; }
DataSrc* datasrc() const { return (datasrc_); }
// \brief The query cache. This is a static member of class \c Query;
// the same cache will be used by all instances.
HotCache& getCache() const { return (*cache_); }
private: private:
Status status_; Status status_;
@@ -218,6 +229,9 @@ private:
const isc::dns::RRClass* qclass_; const isc::dns::RRClass* qclass_;
const isc::dns::RRType* qtype_; const isc::dns::RRType* qtype_;
HotCache* cache_;
DataSrc* datasrc_;
isc::dns::Message* message_; isc::dns::Message* message_;
QueryTaskQueue querytasks_; QueryTaskQueue querytasks_;

View File

@@ -344,19 +344,17 @@ Sqlite3DataSrc::findClosest(const Name& name, unsigned int* position) const {
} }
void void
Sqlite3DataSrc::findClosestEnclosure(NameMatch& match, Sqlite3DataSrc::findClosestEnclosure(DataSrcMatch& match) const {
const RRClass& qclass) const if (match.getClass() != getClass() && match.getClass() != RRClass::ANY()) {
{
if (qclass != getClass() && qclass != RRClass::ANY()) {
return; return;
} }
unsigned int position; unsigned int position;
if (findClosest(match.qname(), &position) == -1) { if (findClosest(match.getName(), &position) == -1) {
return; return;
} }
match.update(*this, match.qname().split(position)); match.update(*this, match.getName().split(position));
} }
DataSrc::Result DataSrc::Result

View File

@@ -58,8 +58,7 @@ public:
~Sqlite3DataSrc(); ~Sqlite3DataSrc();
//@} //@}
void findClosestEnclosure(NameMatch& match, void findClosestEnclosure(DataSrcMatch& match) const;
const isc::dns::RRClass& qclass) const;
Result findRRset(const isc::dns::Name& qname, Result findRRset(const isc::dns::Name& qname,
const isc::dns::RRClass& qclass, const isc::dns::RRClass& qclass,

View File

@@ -129,11 +129,10 @@ isSubdomain(const Name& qname, const Name& zone) {
} }
void void
StaticDataSrc::findClosestEnclosure(NameMatch& match, StaticDataSrc::findClosestEnclosure(DataSrcMatch& match) const {
const RRClass& qclass) const { const Name& qname = match.getName();
const Name& qname = match.qname();
if (qclass != getClass() && qclass != RRClass::ANY()) { if (match.getClass() != getClass() && match.getClass() != RRClass::ANY()) {
return; return;
} }

View File

@@ -39,8 +39,6 @@ class RRsetList;
namespace datasrc { namespace datasrc {
class Query;
class NameMatch;
struct StaticDataSrcImpl; struct StaticDataSrcImpl;
class StaticDataSrc : public DataSrc { class StaticDataSrc : public DataSrc {
@@ -58,8 +56,7 @@ public:
~StaticDataSrc(); ~StaticDataSrc();
//@} //@}
void findClosestEnclosure(NameMatch& match, void findClosestEnclosure(DataSrcMatch& match) const;
const isc::dns::RRClass& qclass) const;
Result findRRset(const isc::dns::Name& qname, Result findRRset(const isc::dns::Name& qname,
const isc::dns::RRClass& qclass, const isc::dns::RRClass& qclass,

View File

@@ -16,6 +16,7 @@ run_unittests_SOURCES += datasrc_unittest.cc
run_unittests_SOURCES += sqlite3_unittest.cc run_unittests_SOURCES += sqlite3_unittest.cc
run_unittests_SOURCES += static_unittest.cc run_unittests_SOURCES += static_unittest.cc
run_unittests_SOURCES += query_unittest.cc run_unittests_SOURCES += query_unittest.cc
run_unittests_SOURCES += cache_unittest.cc
run_unittests_SOURCES += test_datasrc.h test_datasrc.cc run_unittests_SOURCES += test_datasrc.h test_datasrc.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)

View File

@@ -69,6 +69,7 @@ protected:
void createAndProcessQuery(const Name& qname, const RRClass& qclass, void createAndProcessQuery(const Name& qname, const RRClass& qclass,
const RRType& qtype); const RRType& qtype);
HotCache cache;
MetaDataSrc meta_source; MetaDataSrc meta_source;
OutputBuffer obuffer; OutputBuffer obuffer;
MessageRenderer renderer; MessageRenderer renderer;
@@ -76,10 +77,10 @@ protected:
}; };
void void
performQuery(DataSrc& data_source, Message& message) { performQuery(DataSrc& data_source, HotCache& cache, Message& message) {
message.setHeaderFlag(MessageFlag::AA()); message.setHeaderFlag(MessageFlag::AA());
message.setRcode(Rcode::NOERROR()); message.setRcode(Rcode::NOERROR());
Query q(message, true); Query q(message, cache, true);
data_source.doQuery(q); data_source.doQuery(q);
} }
@@ -92,7 +93,7 @@ DataSrcTest::readAndProcessQuery(const char* datafile) {
msg.fromWire(buffer); msg.fromWire(buffer);
msg.makeResponse(); msg.makeResponse();
performQuery(meta_source, msg); performQuery(meta_source, cache, msg);
} }
void void
@@ -103,7 +104,7 @@ DataSrcTest::createAndProcessQuery(const Name& qname, const RRClass& qclass,
msg.setOpcode(Opcode::QUERY()); msg.setOpcode(Opcode::QUERY());
msg.addQuestion(Question(qname, qclass, qtype)); msg.addQuestion(Question(qname, qclass, qtype));
msg.setHeaderFlag(MessageFlag::RD()); msg.setHeaderFlag(MessageFlag::RD());
performQuery(meta_source, msg); performQuery(meta_source, cache, msg);
} }
void void
@@ -213,6 +214,83 @@ TEST_F(DataSrcTest, NSQuery) {
EXPECT_TRUE(it->isLast()); EXPECT_TRUE(it->isLast());
} }
// Make sure two successive queries have the same result
TEST_F(DataSrcTest, DuplicateQuery) {
readAndProcessQuery("q_example_ns");
headerCheck(msg, Rcode::NOERROR(), true, true, true, 4, 0, 6);
RRsetIterator rit = msg.beginSection(Section::ANSWER());
RRsetPtr rrset = *rit;
EXPECT_EQ(Name("example.com"), rrset->getName());
EXPECT_EQ(RRType::NS(), rrset->getType());
EXPECT_EQ(RRClass::IN(), rrset->getClass());
RdataIteratorPtr it = rrset->getRdataIterator();
it->first();
EXPECT_EQ("dns01.example.com.", it->getCurrent().toText());
it->next();
EXPECT_EQ("dns02.example.com.", it->getCurrent().toText());
it->next();
EXPECT_EQ("dns03.example.com.", it->getCurrent().toText());
it->next();
EXPECT_TRUE(it->isLast());
msg.clear(Message::PARSE);
readAndProcessQuery("q_example_ns");
headerCheck(msg, Rcode::NOERROR(), true, true, true, 4, 0, 6);
rit = msg.beginSection(Section::ANSWER());
rrset = *rit;
EXPECT_EQ(Name("example.com"), rrset->getName());
EXPECT_EQ(RRType::NS(), rrset->getType());
EXPECT_EQ(RRClass::IN(), rrset->getClass());
it = rrset->getRdataIterator();
it->first();
EXPECT_EQ("dns01.example.com.", it->getCurrent().toText());
it->next();
EXPECT_EQ("dns02.example.com.", it->getCurrent().toText());
it->next();
EXPECT_EQ("dns03.example.com.", it->getCurrent().toText());
it->next();
EXPECT_TRUE(it->isLast());
}
TEST_F(DataSrcTest, DNSKEYQuery) {
readAndProcessQuery("q_example_dnskey");
headerCheck(msg, Rcode::NOERROR(), true, true, true, 4, 4, 6);
RRsetIterator rit = msg.beginSection(Section::ANSWER());
RRsetPtr rrset = *rit;
EXPECT_EQ(Name("example.com"), rrset->getName());
EXPECT_EQ(RRType::DNSKEY(), rrset->getType());
EXPECT_EQ(RRClass::IN(), rrset->getClass());
}
// Repeat the previous query to check that cache is working correctly.
// We query for a record at a zone cut to ensure the REFERRAL flag doesn't
// cause incorrect behavior.
TEST_F(DataSrcTest, DNSKEYDuplicateQuery) {
readAndProcessQuery("q_example_dnskey");
headerCheck(msg, Rcode::NOERROR(), true, true, true, 4, 4, 6);
RRsetIterator rit = msg.beginSection(Section::ANSWER());
RRsetPtr rrset = *rit;
EXPECT_EQ(Name("example.com"), rrset->getName());
EXPECT_EQ(RRType::DNSKEY(), rrset->getType());
EXPECT_EQ(RRClass::IN(), rrset->getClass());
msg.clear(Message::PARSE);
readAndProcessQuery("q_example_dnskey");
headerCheck(msg, Rcode::NOERROR(), true, true, true, 4, 4, 6);
rit = msg.beginSection(Section::ANSWER());
rrset = *rit;
EXPECT_EQ(Name("example.com"), rrset->getName());
EXPECT_EQ(RRType::DNSKEY(), rrset->getType());
EXPECT_EQ(RRClass::IN(), rrset->getClass());
}
TEST_F(DataSrcTest, NxRRset) { TEST_F(DataSrcTest, NxRRset) {
readAndProcessQuery("q_example_ptr"); readAndProcessQuery("q_example_ptr");
@@ -858,18 +936,6 @@ TEST_F(DataSrcTest, AddRemoveDataSrc) {
EXPECT_EQ(0, ds.dataSrcCount()); EXPECT_EQ(0, ds.dataSrcCount());
} }
// currently fails
TEST_F(DataSrcTest, DISABLED_synthesizedCnameTooLong) {
// qname has the possible max length (255 octets). it matches a DNAME,
// and the synthesized CNAME would exceed the valid length.
createAndProcessQuery(
Name("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcde."
"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcde."
"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcde."
"0123456789abcdef0123456789abcdef0123456789a.dname.example.org."),
RRClass::IN(), RRType::A());
}
TEST_F(DataSrcTest, noNSZone) { TEST_F(DataSrcTest, noNSZone) {
EXPECT_THROW(createAndProcessQuery(Name("www.nons.example"), EXPECT_THROW(createAndProcessQuery(Name("www.nons.example"),
RRClass::IN(), RRType::A()), RRClass::IN(), RRType::A()),
@@ -888,4 +954,114 @@ TEST_F(DataSrcTest, noSOAZone) {
DataSourceError); DataSourceError);
} }
// currently fails
TEST_F(DataSrcTest, DISABLED_synthesizedCnameTooLong) {
// qname has the possible max length (255 octets). it matches a DNAME,
// and the synthesized CNAME would exceed the valid length.
createAndProcessQuery(
Name("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcde."
"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcde."
"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcde."
"0123456789abcdef0123456789abcdef0123456789a.dname.example.org."),
RRClass::IN(), RRType::A());
}
// Tests of the DataSrcMatch class start here
class DataSrcMatchTest : public ::testing::Test {
protected:
DataSrcMatchTest() {
datasrc1.init();
}
// test data source serves example.com/IN.
TestDataSrc datasrc1;
// this data source is dummy. Its content doesn't matter in the tests.
TestDataSrc datasrc2;
};
TEST_F(DataSrcMatchTest, match) {
DataSrcMatch match(Name("very.very.long.example.com"), RRClass::IN());
datasrc1.findClosestEnclosure(match);
EXPECT_EQ(Name("example.com"), *match.getEnclosingZone());
EXPECT_EQ(&datasrc1, match.getDataSource());
}
TEST_F(DataSrcMatchTest, matchWithWrongClass) {
DataSrcMatch match(Name("very.very.long.example.com"), RRClass::CH());
datasrc1.findClosestEnclosure(match);
EXPECT_EQ(NULL, match.getEnclosingZone());
EXPECT_EQ(NULL, match.getDataSource());
}
TEST_F(DataSrcMatchTest, matchWithAnyClass) {
DataSrcMatch match(Name("very.very.long.example.com"), RRClass::ANY());
datasrc1.findClosestEnclosure(match);
EXPECT_EQ(Name("example.com"), *match.getEnclosingZone());
EXPECT_EQ(&datasrc1, match.getDataSource());
}
TEST_F(DataSrcMatchTest, updateWithWrongClass) {
DataSrcMatch match(Name("www.example.com"), RRClass::CH());
EXPECT_EQ(RRClass::IN(), datasrc2.getClass());
match.update(datasrc2, Name("com"));
EXPECT_EQ(NULL, match.getEnclosingZone());
EXPECT_EQ(NULL, match.getDataSource());
EXPECT_EQ(RRClass::IN(), datasrc1.getClass());
match.update(datasrc1, Name("example.com"));
EXPECT_EQ(NULL, match.getEnclosingZone());
EXPECT_EQ(NULL, match.getDataSource());
}
TEST_F(DataSrcMatchTest, updateAgainstAnyClass) {
DataSrcMatch match(Name("www.example.com"), RRClass::ANY());
match.update(datasrc2, Name("com"));
EXPECT_EQ(Name("com"), *match.getEnclosingZone());
EXPECT_EQ(&datasrc2, match.getDataSource());
// the given class for search is ANY, so update should be okay.
EXPECT_EQ(RRClass::IN(), datasrc1.getClass());
match.update(datasrc1, Name("example.com"));
EXPECT_EQ(Name("example.com"), *match.getEnclosingZone());
EXPECT_EQ(&datasrc1, match.getDataSource());
}
TEST_F(DataSrcMatchTest, updateWithNoMatch) {
DataSrcMatch match(Name("www.example.com"), RRClass::IN());
match.update(datasrc1, Name("com"));
EXPECT_EQ(Name("com"), *match.getEnclosingZone());
EXPECT_EQ(&datasrc1, match.getDataSource());
// An attempt of update with a name that doesn't match. This attempt
// should be ignored.
match.update(datasrc2, Name("example.org"));
EXPECT_EQ(Name("com"), *match.getEnclosingZone());
EXPECT_EQ(&datasrc1, match.getDataSource());
}
// This test currently fails.
TEST_F(DataSrcMatchTest, initialUpdateWithNoMatch) {
DataSrcMatch match(Name("www.example.com"), RRClass::IN());
// An initial attempt of update with a name that doesn't match.
// Should be ignored.
match.update(datasrc1, Name("example.org"));
EXPECT_EQ(NULL, match.getEnclosingZone());
EXPECT_EQ(NULL, match.getDataSource());
}
TEST_F(DataSrcMatchTest, updateWithShorterMatch) {
DataSrcMatch match(Name("www.example.com"), RRClass::IN());
match.update(datasrc1, Name("example.com"));
EXPECT_EQ(Name("example.com"), *match.getEnclosingZone());
EXPECT_EQ(&datasrc1, match.getDataSource());
// An attempt of update with a name that gives a shorter match.
// This attempt should be ignored.
match.update(datasrc2, Name("com"));
EXPECT_EQ(Name("example.com"), *match.getEnclosingZone());
EXPECT_EQ(&datasrc1, match.getDataSource());
}
} }

View File

@@ -16,43 +16,61 @@
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <dns/buffer.h>
#include <dns/message.h>
#include <dns/name.h> #include <dns/name.h>
#include <dns/rrtype.h> #include <dns/rrtype.h>
#include <dns/rrclass.h> #include <dns/rrclass.h>
#include <datasrc/query.h> #include <datasrc/query.h>
namespace { #include <dns/tests/unittest_util.h>
using isc::UnitTestUtil;
using namespace isc::dns; using namespace isc::dns;
using namespace isc::datasrc; using namespace isc::datasrc;
namespace {
class QueryTest : public ::testing::Test { class QueryTest : public ::testing::Test {
protected: protected:
QueryTest() : void readQuery(Message& m, const char* datafile);
name(Name("www.example.com")),
rrtype(RRType::A()), HotCache cache;
rrclass(RRClass::IN())
{}
const Name name;
const RRType rrtype;
const RRClass rrclass;
}; };
void
QueryTest::readQuery(Message& m, const char* datafile) {
std::vector<unsigned char> data;
UnitTestUtil::readWireData(datafile, data);
InputBuffer buffer(&data[0], data.size());
m.fromWire(buffer);
}
QueryTaskPtr QueryTaskPtr
createTask(const Name& name, const RRClass& rrclass0, const RRType& rrtype0) { createTask(Message& m, const Name& name, const RRType& rrtype0, HotCache& c) {
RRType rrtype(rrtype0); RRType rrtype(rrtype0);
return (QueryTaskPtr(new QueryTask(name, rrclass0, rrtype, Query q(m, c, true);
return (QueryTaskPtr(new QueryTask(q, name, rrtype,
QueryTask::SIMPLE_QUERY))); QueryTask::SIMPLE_QUERY)));
} }
// Check the QueryTask created using a temporary RRType object will remain // Check the QueryTask created using a temporary RRType object will remain
// valid. // valid.
TEST_F(QueryTest, constructWithTemporary) { TEST_F(QueryTest, constructWithTemporary) {
QueryTaskPtr task_a = createTask(name, rrclass, RRType::A()); Message m1(Message::PARSE);
QueryTaskPtr task_aaaa = createTask(name, rrclass, RRType::AAAA()); readQuery(m1, "q_wild_a");
EXPECT_EQ(rrtype, task_a->qtype); QueryTaskPtr task_a = createTask(m1, Name("www.wild.example.com"),
RRType::A(), cache);
EXPECT_EQ(RRType::A(), task_a->qtype);
Message m2(Message::PARSE);
readQuery(m2, "q_wild_aaaa");
QueryTaskPtr task_aaaa = createTask(m2, Name("www.wild.example.com"),
RRType::AAAA(), cache);
EXPECT_EQ(RRType::AAAA(), task_aaaa->qtype);
} }
} }

View File

@@ -374,12 +374,10 @@ TEST_F(Sqlite3DataSourceTest, reOpen) {
EXPECT_EQ(DataSrc::SUCCESS, data_source.close()); EXPECT_EQ(DataSrc::SUCCESS, data_source.close());
EXPECT_EQ(DataSrc::SUCCESS, data_source.init(SQLITE_DBFILE_EXAMPLE2)); EXPECT_EQ(DataSrc::SUCCESS, data_source.init(SQLITE_DBFILE_EXAMPLE2));
NameMatch name_match(www_name); DataSrcMatch match(www_name, rrclass);
data_source.findClosestEnclosure(name_match, rrclass); data_source.findClosestEnclosure(match);
// XXX: some deviant compilers seem to fail to recognize a NULL as a EXPECT_EQ(NULL, match.getEnclosingZone());
// pointer type. This explicit cast works around such compilers. EXPECT_EQ(NULL, match.getDataSource());
EXPECT_EQ(static_cast<void*>(NULL), name_match.closestName());
EXPECT_EQ(static_cast<void*>(NULL), name_match.bestDataSrc());
} }
TEST_F(Sqlite3DataSourceTest, openFail) { TEST_F(Sqlite3DataSourceTest, openFail) {
@@ -415,52 +413,52 @@ TEST_F(Sqlite3DataSourceTest, memoryDB) {
} }
TEST_F(Sqlite3DataSourceTest, findClosestEnclosure) { TEST_F(Sqlite3DataSourceTest, findClosestEnclosure) {
NameMatch name_match(www_name); DataSrcMatch match(www_name, rrclass);
data_source.findClosestEnclosure(name_match, rrclass); data_source.findClosestEnclosure(match);
EXPECT_EQ(zone_name, *name_match.closestName()); EXPECT_EQ(zone_name, *match.getEnclosingZone());
EXPECT_EQ(&data_source, name_match.bestDataSrc()); EXPECT_EQ(&data_source, match.getDataSource());
} }
TEST_F(Sqlite3DataSourceTest, findClosestEnclosureMatchRoot) { TEST_F(Sqlite3DataSourceTest, findClosestEnclosureMatchRoot) {
EXPECT_EQ(DataSrc::SUCCESS, data_source.close()); EXPECT_EQ(DataSrc::SUCCESS, data_source.close());
EXPECT_EQ(DataSrc::SUCCESS, data_source.init(SQLITE_DBFILE_EXAMPLE_ROOT)); EXPECT_EQ(DataSrc::SUCCESS, data_source.init(SQLITE_DBFILE_EXAMPLE_ROOT));
NameMatch name_match(Name("org.")); DataSrcMatch match(Name("org."), rrclass);
data_source.findClosestEnclosure(name_match, rrclass); data_source.findClosestEnclosure(match);
EXPECT_EQ(Name("."), *name_match.closestName()); EXPECT_EQ(Name("."), *match.getEnclosingZone());
EXPECT_EQ(&data_source, name_match.bestDataSrc()); EXPECT_EQ(&data_source, match.getDataSource());
} }
TEST_F(Sqlite3DataSourceTest, findClosestEnclosureAtDelegation) { TEST_F(Sqlite3DataSourceTest, findClosestEnclosureAtDelegation) {
// The search name exists both in the parent and child zones, but // The search name exists both in the parent and child zones, but
// child has a better match. // child has a better match.
NameMatch name_match(child_name); DataSrcMatch match(child_name, rrclass);
data_source.findClosestEnclosure(name_match, rrclass); data_source.findClosestEnclosure(match);
EXPECT_EQ(child_name, *name_match.closestName()); EXPECT_EQ(child_name, *match.getEnclosingZone());
EXPECT_EQ(&data_source, name_match.bestDataSrc()); EXPECT_EQ(&data_source, match.getDataSource());
} }
TEST_F(Sqlite3DataSourceTest, findClosestEnclosureNoMatch) { TEST_F(Sqlite3DataSourceTest, findClosestEnclosureNoMatch) {
NameMatch name_match(nomatch_name); DataSrcMatch match(nomatch_name, rrclass);
data_source.findClosestEnclosure(name_match, rrclass); data_source.findClosestEnclosure(match);
EXPECT_EQ(static_cast<void*>(NULL), name_match.closestName()); EXPECT_EQ(NULL, match.getEnclosingZone());
EXPECT_EQ(static_cast<void*>(NULL), name_match.bestDataSrc()); EXPECT_EQ(NULL, match.getDataSource());
} }
TEST_F(Sqlite3DataSourceTest, findClosestClassMismatch) { TEST_F(Sqlite3DataSourceTest, findClosestClassMismatch) {
NameMatch name_match(www_name); DataSrcMatch match(nomatch_name, rrclass);
data_source.findClosestEnclosure(name_match, rrclass_notmatch); data_source.findClosestEnclosure(match);
EXPECT_EQ(static_cast<void*>(NULL), name_match.closestName()); EXPECT_EQ(NULL, match.getEnclosingZone());
EXPECT_EQ(static_cast<void*>(NULL), name_match.bestDataSrc()); EXPECT_EQ(NULL, match.getDataSource());
} }
// If the query class is ANY, the result should be the same as the case where // If the query class is ANY, the result should be the same as the case where
// the class exactly matches. // the class exactly matches.
TEST_F(Sqlite3DataSourceTest, findClosestClassAny) { TEST_F(Sqlite3DataSourceTest, findClosestClassAny) {
NameMatch name_match(www_name); DataSrcMatch match(www_name, RRClass::ANY());
data_source.findClosestEnclosure(name_match, RRClass::ANY()); data_source.findClosestEnclosure(match);
EXPECT_EQ(zone_name, *name_match.closestName()); EXPECT_EQ(zone_name, *match.getEnclosingZone());
EXPECT_EQ(&data_source, name_match.bestDataSrc()); EXPECT_EQ(&data_source, match.getDataSource());
} }
TEST_F(Sqlite3DataSourceTest, findRRsetNormal) { TEST_F(Sqlite3DataSourceTest, findRRsetNormal) {

View File

@@ -196,55 +196,54 @@ TEST_F(StaticDataSourceTest, close) {
} }
TEST_F(StaticDataSourceTest, findClosestEnclosureForVersion) { TEST_F(StaticDataSourceTest, findClosestEnclosureForVersion) {
NameMatch name_match(version_name); DataSrcMatch match(version_name, rrclass);
data_source.findClosestEnclosure(name_match, rrclass); data_source.findClosestEnclosure(match);
EXPECT_EQ(version_name, *name_match.closestName()); EXPECT_EQ(version_name, *match.getEnclosingZone());
EXPECT_EQ(&data_source, name_match.bestDataSrc()); EXPECT_EQ(&data_source, match.getDataSource());
} }
// Class Any query should result in the same answer. // Class Any query should result in the same answer.
TEST_F(StaticDataSourceTest, findClosestEnclosureForVersionClassAny) { TEST_F(StaticDataSourceTest, findClosestEnclosureForVersionClassAny) {
NameMatch name_match(version_name); DataSrcMatch match(version_name, RRClass::ANY());
data_source.findClosestEnclosure(name_match, RRClass::ANY()); data_source.findClosestEnclosure(match);
EXPECT_EQ(version_name, *name_match.closestName()); EXPECT_EQ(version_name, *match.getEnclosingZone());
EXPECT_EQ(&data_source, name_match.bestDataSrc()); EXPECT_EQ(&data_source, match.getDataSource());
} }
// If class doesn't match the lookup should fail. // If class doesn't match the lookup should fail.
TEST_F(StaticDataSourceTest, findClosestEnclosureForVersionClassMismatch) { TEST_F(StaticDataSourceTest, findClosestEnclosureForVersionClassMismatch) {
NameMatch name_match(version_name); DataSrcMatch match(version_name, RRClass::IN());
data_source.findClosestEnclosure(name_match, RRClass::IN()); data_source.findClosestEnclosure(match);
// XXX: see sqlite3_unittest.cc about the cast. EXPECT_EQ(NULL, match.getEnclosingZone());
EXPECT_EQ(static_cast<void*>(NULL), name_match.closestName()); EXPECT_EQ(NULL, match.getDataSource());
EXPECT_EQ(static_cast<void*>(NULL), name_match.bestDataSrc());
} }
TEST_F(StaticDataSourceTest, findClosestEnclosureForVersionPartial) { TEST_F(StaticDataSourceTest, findClosestEnclosureForVersionPartial) {
NameMatch name_match(Name("foo").concatenate(version_name)); DataSrcMatch match(Name("foo").concatenate(version_name), rrclass);
data_source.findClosestEnclosure(name_match, rrclass); data_source.findClosestEnclosure(match);
EXPECT_EQ(version_name, *name_match.closestName()); EXPECT_EQ(version_name, *match.getEnclosingZone());
EXPECT_EQ(&data_source, name_match.bestDataSrc()); EXPECT_EQ(&data_source, match.getDataSource());
} }
TEST_F(StaticDataSourceTest, findClosestEnclosureForAuthors) { TEST_F(StaticDataSourceTest, findClosestEnclosureForAuthors) {
NameMatch name_match(authors_name); DataSrcMatch match(authors_name, rrclass);
data_source.findClosestEnclosure(name_match, rrclass); data_source.findClosestEnclosure(match);
EXPECT_EQ(authors_name, *name_match.closestName()); EXPECT_EQ(authors_name, *match.getEnclosingZone());
EXPECT_EQ(&data_source, name_match.bestDataSrc()); EXPECT_EQ(&data_source, match.getDataSource());
} }
TEST_F(StaticDataSourceTest, findClosestEnclosureForAuthorsPartial) { TEST_F(StaticDataSourceTest, findClosestEnclosureForAuthorsPartial) {
NameMatch name_match(Name("foo").concatenate(authors_name)); DataSrcMatch match(Name("foo").concatenate(authors_name), rrclass);
data_source.findClosestEnclosure(name_match, rrclass); data_source.findClosestEnclosure(match);
EXPECT_EQ(authors_name, *name_match.closestName()); EXPECT_EQ(authors_name, *match.getEnclosingZone());
EXPECT_EQ(&data_source, name_match.bestDataSrc()); EXPECT_EQ(&data_source, match.getDataSource());
} }
TEST_F(StaticDataSourceTest, findClosestEnclosureNoMatch) { TEST_F(StaticDataSourceTest, findClosestEnclosureNoMatch) {
NameMatch name_match(nomatch_name); DataSrcMatch match(nomatch_name, rrclass);
data_source.findClosestEnclosure(name_match, rrclass); data_source.findClosestEnclosure(match);
EXPECT_EQ(static_cast<void*>(NULL), name_match.closestName()); EXPECT_EQ(NULL, match.getEnclosingZone());
EXPECT_EQ(static_cast<void*>(NULL), name_match.bestDataSrc()); EXPECT_EQ(NULL, match.getDataSource());
} }
TEST_F(StaticDataSourceTest, findRRsetVersionTXT) { TEST_F(StaticDataSourceTest, findRRsetVersionTXT) {

View File

@@ -65,8 +65,9 @@ namespace {
// {"example.com", "AAAA", "2001:db8::2"}, // {"example.com", "AAAA", "2001:db8::2"},
// ... // ...
// If an RRset is associated with an RRSIG, the RRSIG must immediately follow // If an RRset is associated with an RRSIG, the RRSIG must immediately follow
// the RRset to be signed. Currently, only one (or zero) RRSIG can be // the RRset to be signed. Multiple RRSIGs can follow the RRset. RRSIG
// specified per RRset. // records will always be attached to the most recent non-RRSIG RRset;
// consequently, the first RR listed must not be an RRSIG record.
// //
// Names are sorted internally, and don't have to be sorted in the data. // Names are sorted internally, and don't have to be sorted in the data.
// //
@@ -107,6 +108,10 @@ const struct RRData example_com_records[] = {
{"example.com", "RRSIG", "SOA 5 2 3600 20100322084538 20100220084538 33495 example.com. KUun66Qaw36osk2BJS6U1fAy3PPDkNo2QK4meGNbDBY8q8b+f2o+IXJ14YCvssGl1ORW0CcLnDRxssnk8V/Svmj5iFhO+8HC2hnVBdi2zewvdVtwRb+lWwKN7pkXXwuy6g1t9WCd/j5FCc/wgxqtZUTPb6XgZcnHrORDMOTqLs4="}, {"example.com", "RRSIG", "SOA 5 2 3600 20100322084538 20100220084538 33495 example.com. KUun66Qaw36osk2BJS6U1fAy3PPDkNo2QK4meGNbDBY8q8b+f2o+IXJ14YCvssGl1ORW0CcLnDRxssnk8V/Svmj5iFhO+8HC2hnVBdi2zewvdVtwRb+lWwKN7pkXXwuy6g1t9WCd/j5FCc/wgxqtZUTPb6XgZcnHrORDMOTqLs4="},
{"example.com", "NSEC", "cname-ext.example.com. NS SOA MX RRSIG NSEC DNSKEY"}, {"example.com", "NSEC", "cname-ext.example.com. NS SOA MX RRSIG NSEC DNSKEY"},
{"example.com", "RRSIG", "NSEC 5 2 7200 20100322084538 20100220084538 33495 example.com. KxuVaPPKNPJzr/q+cJPiNlkHVTQK0LVsgTbSqruXQc25lAd0wn5oKUtxL1bEAchHkfA8eLzcYCj2ZqqAv9OJubw53mfskTad7UHs4Uj2RTrIsNGMCiZGgOpvNb9JcWpQtoyXVT1uNse+Qsbeir0eyeYIufUynFU041jtNrlJMio="}, {"example.com", "RRSIG", "NSEC 5 2 7200 20100322084538 20100220084538 33495 example.com. KxuVaPPKNPJzr/q+cJPiNlkHVTQK0LVsgTbSqruXQc25lAd0wn5oKUtxL1bEAchHkfA8eLzcYCj2ZqqAv9OJubw53mfskTad7UHs4Uj2RTrIsNGMCiZGgOpvNb9JcWpQtoyXVT1uNse+Qsbeir0eyeYIufUynFU041jtNrlJMio="},
{"example.com", "DNSKEY", "257 3 5 AwEAAe5WFbxdCPq2jZrZhlMj7oJdff3W7syJtbvzg62tRx0gkoCDoBI9DPjlOQG0UAbj+xUV4HQZJStJaZ+fHU5AwVNT+bBZdtV+NujSikhdTHb4FYLg2b3Cx9NyJvAVukHp/91HnWuG4T36CzAFrfPwsHIrBz9BsaIQ21VRkcmj7DswfI/iDGd8j6bqiODyNZYQ+ZrLmF0KIJ2yPN3iO6Zq23TaOrVTjB7d1a/h31ODfiHAxFHrkY3t3D5JR9Nsl/7fdRmSznwtcSDgLXBoFEYmw6p86AcvRyoYNcL1SXjaKVLG5jyU3UR+LcGZT5t/0xGfoIK/aKwENrsjcKZZj660b1M="},
{"example.com", "DNSKEY", "256 3 5 AwEAAcOUBllYc1hf7ND9uDy+Yz1BF3sI0m4qNGV7WcTD0WEiuV7IjXgHE36fCmS9QsUxSSOVo1I/FMxI2PJVqTYHkXFBS7AzLGsQYMU7UjBZSotBJ6Imt5pXMu+lEDNy8TOUzG3xm7g0qcbWYF6qCEfvZoBtAqi5Rk7Mlrqs8agxYyMx"},
{"example.com", "RRSIG", "DNSKEY 5 2 3600 20100416210049 20100317210049 4456 example.com. 37FC0rcwOZVarTMjft0BMbvv8hbJU7OHNsvO7R1q6OgsLTj7QGMX3sC42JGbwUrYI/OwnZblNcv1eim0g0jX5k+sVr2OJsEubngRjVqLo54qV8rBC14tLk9PGKxxjQG0IBJU866uHxzXYBO2a1r2g93/qyTtrT7iPLu/2Ce1WRKMBPK0yf4nW2usFU/PXesXFWpZ7HLGZL73/NWv8wcezBDuU0B2PlHLjSu7k6poq6JWDC02o5SYnEBwsJ5Chi+3/NZmzKTiNP7g0H4t6QhunkEXxL3z0617mwwQt00ypXsNunnPy4Ub5Kllk1SKJl8ZkEDKkJtSvuXJhcAZsLyMQw=="},
{"example.com", "RRSIG", "DNSKEY 5 2 3600 20100416210049 20100317210049 33495 example.com. h3OM5r3roBsgnEQk9fcjTg5L7p3yDptDpVzDN/lgjqpaWxtlz5LsulBH3YzwYyXzT7pG7L0/qT6dcuRECc/rniECviWvmJMJZzEAMry0Of/pk/8ekuGTxABpqwAoCwM5as30sc0cfMJTS7umpJVDA4lRB2zoKGefWnJ3+pREDiY="},
// dns01.example.com // dns01.example.com
{"dns01.example.com", "A", "192.0.2.1"}, {"dns01.example.com", "A", "192.0.2.1"},
@@ -335,10 +340,11 @@ buildZone(Zone& zone, const RRData* records, const bool is_glue) {
rrset->addRdata(createRdata(rrtype, zone.rrclass, records[i].rdata)); rrset->addRdata(createRdata(rrtype, zone.rrclass, records[i].rdata));
if (rrtype == RRType::RRSIG()) { if (rrtype == RRType::RRSIG()) {
prev_rrset->addRRsig(rrset); prev_rrset->addRRsig(rrset);
} } else {
prev_rrset = rrset; prev_rrset = rrset;
} }
} }
}
DataSrc::Result DataSrc::Result
TestDataSrc::init() { TestDataSrc::init() {
@@ -361,12 +367,10 @@ TestDataSrc::init() {
} }
void void
TestDataSrc::findClosestEnclosure(NameMatch& match, TestDataSrc::findClosestEnclosure(DataSrcMatch& match) const {
const RRClass& qclass) const const Name& qname = match.getName();
{
const Name& qname = match.qname();
if (qclass != getClass() && qclass != RRClass::ANY()) { if (match.getClass() != getClass() && match.getClass() != RRClass::ANY()) {
return; return;
} }

View File

@@ -48,8 +48,7 @@ public:
~TestDataSrc() {} ~TestDataSrc() {}
//@} //@}
void findClosestEnclosure(NameMatch& match, void findClosestEnclosure(DataSrcMatch& match) const;
const isc::dns::RRClass& qclass) const;
Result findRRset(const isc::dns::Name& qname, Result findRRset(const isc::dns::Name& qname,
const isc::dns::RRClass& qclass, const isc::dns::RRClass& qclass,

View File

@@ -229,6 +229,20 @@ public:
unsigned int toWire(OutputBuffer& buffer) const; unsigned int toWire(OutputBuffer& buffer) const;
//@} //@}
///
/// \name Comparison Operator
///
//@{
/// A comparison operator is needed for this class so it can
/// function as an index to std::map.
bool operator <(const Question& rhs) const {
return (rrclass_ < rhs.rrclass_ ||
(rrclass_ == rhs.rrclass_ &&
(rrtype_ < rhs.rrtype_ ||
(rrtype_ == rhs.rrtype_ && (name_ < rhs.name_)))));
}
//@}
private: private:
Name name_; Name name_;
RRType rrtype_; RRType rrtype_;

View File

@@ -40,6 +40,14 @@ RRsetList::addRRset(RRsetPtr rrsetptr) {
rrsets_.push_back(rrsetptr); rrsets_.push_back(rrsetptr);
} }
void
RRsetList::append(RRsetList& source)
{
BOOST_FOREACH(RRsetPtr rrset, source) {
addRRset(rrset);
}
}
RRsetPtr RRsetPtr
RRsetList::findRRset(const RRType& rrtype, const RRClass& rrclass) { RRsetList::findRRset(const RRType& rrtype, const RRClass& rrclass) {
BOOST_FOREACH(RRsetPtr rrsetptr, rrsets_) { BOOST_FOREACH(RRsetPtr rrsetptr, rrsets_) {

View File

@@ -82,6 +82,7 @@ private:
public: public:
RRsetList() {} RRsetList() {}
void addRRset(RRsetPtr new_rrsetptr); void addRRset(RRsetPtr new_rrsetptr);
void append(RRsetList& source);
RRsetPtr findRRset(const RRType& rrtype, const RRClass& rrclass); RRsetPtr findRRset(const RRType& rrtype, const RRClass& rrclass);
typedef RRsetListIterator<std::vector<RRsetPtr>::iterator, typedef RRsetListIterator<std::vector<RRsetPtr>::iterator,

View File

@@ -119,4 +119,35 @@ TEST_F(QuestionTest, LeftShiftOperator)
oss << test_question1; oss << test_question1;
EXPECT_EQ(test_question1.toText(), oss.str()); EXPECT_EQ(test_question1.toText(), oss.str());
} }
TEST_F(QuestionTest, comparison)
{
const Name a("a"), b("b");
const RRClass in(RRClass::IN()), ch(RRClass::CH());
const RRType ns(RRType::NS()), aaaa(RRType::AAAA());
EXPECT_TRUE(Question(a, in, ns) < Question(a, in, aaaa));
EXPECT_FALSE(Question(a, in, aaaa) < Question(a, in, ns));
EXPECT_TRUE(Question(a, in, ns) < Question(a, ch, ns));
EXPECT_FALSE(Question(a, ch, ns) < Question(a, in, ns));
EXPECT_TRUE(Question(a, in, ns) < Question(a, ch, aaaa));
EXPECT_FALSE(Question(a, ch, aaaa) < Question(a, in, ns));
EXPECT_TRUE(Question(a, in, ns) < Question(b, in, ns));
EXPECT_FALSE(Question(a, in, ns) < Question(a, in, ns));
EXPECT_TRUE(Question(a, in, ns) < Question(b, ch, ns));
EXPECT_FALSE(Question(b, ch, ns) < Question(a, in, ns));
EXPECT_TRUE(Question(a, in, ns) < Question(b, ch, aaaa));
EXPECT_FALSE(Question(b, ch, aaaa) < Question(a, in, ns));
EXPECT_FALSE(Question(a, in, ns) < Question(a, in, ns));
EXPECT_FALSE(Question(a, ch, ns) < Question(a, ch, ns));
EXPECT_FALSE(Question(b, in, ns) < Question(b, in, ns));
EXPECT_FALSE(Question(b, in, aaaa) < Question(b, in, aaaa));
}
} }

View File

@@ -48,6 +48,7 @@ const generic::NS rdata_ns("ns.example.com");
const generic::SOA rdata_soa(Name("ns.example.com"), Name("root.example.com"), const generic::SOA rdata_soa(Name("ns.example.com"), Name("root.example.com"),
2010012601, 3600, 300, 3600000, 1200); 2010012601, 3600, 300, 3600000, 1200);
const generic::CNAME rdata_cname("target.example.com"); const generic::CNAME rdata_cname("target.example.com");
const generic::DNAME rdata_dname("dtarget.example.com");
void void
RRsetListTest::setupList(RRsetList& list) { RRsetListTest::setupList(RRsetList& list) {
@@ -86,6 +87,24 @@ TEST_F(RRsetListTest, addRRsets) {
EXPECT_EQ(list.size(), 5); EXPECT_EQ(list.size(), 5);
} }
TEST_F(RRsetListTest, append) {
RRsetList list1;
setupList(list1);
RRsetList list2;
RRsetPtr dname(new RRset(Name("example.com"), RRClass::IN(),
RRType::DNAME(), example_ttl));
dname->addRdata(rdata_dname);
list2.addRRset(dname);
list1.append(list2);
EXPECT_EQ(list2.size(), 1);
EXPECT_EQ(list1.size(), 6);
RRsetPtr rrset = list1.findRRset(RRType::DNAME(), RRClass::IN());
EXPECT_EQ(RRType::DNAME(), rrset->getType());
EXPECT_THROW(list1.append(list2), DuplicateRRset);
}
TEST_F(RRsetListTest, extraRRset) { TEST_F(RRsetListTest, extraRRset) {
RRsetList list; RRsetList list;
setupList(list); setupList(list);