2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-08-30 05:27:55 +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
69. [func]* jelte

View File

@ -568,7 +568,7 @@ WARN_LOGFILE =
# directories like "/usr/src/myproject". Separate the files or directories
# 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
# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is

View File

@ -60,7 +60,7 @@ private:
AuthSrvImpl(const AuthSrvImpl& source);
AuthSrvImpl& operator=(const AuthSrvImpl& source);
public:
AuthSrvImpl();
AuthSrvImpl(const bool use_cache);
isc::data::ElementPtr setDbFile(const isc::data::ElementPtr config);
@ -76,9 +76,13 @@ public:
/// Currently non-configurable, but will be.
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,
// 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
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() {
@ -239,7 +246,7 @@ AuthSrv::processMessage(InputBuffer& request_buffer, Message& message,
message.setUDPSize(AuthSrvImpl::DEFAULT_LOCAL_UDPSIZE);
try {
Query query(message, dnssec_ok);
Query query(message, impl_->cache_, dnssec_ok);
impl_->data_sources_.doQuery(query);
} catch (const Exception& ex) {
if (impl_->verbose_mode_) {

View File

@ -43,7 +43,7 @@ private:
AuthSrv(const AuthSrv& source);
AuthSrv& operator=(const AuthSrv& source);
public:
explicit AuthSrv();
explicit AuthSrv(const bool use_cache);
~AuthSrv();
//@}
/// \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
usage() {
cerr << "Usage: b10-auth [-p port] [-4|-6]" << endl;
cerr << "Usage: b10-auth [-p port] [-4|-6] [-nv]" << endl;
exit(1);
}
} // end of anonymous namespace
@ -95,9 +95,9 @@ int
main(int argc, char* argv[]) {
int ch;
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) {
case '4':
// 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.
use_ipv4 = false;
break;
case 'n':
cache = false;
break;
case 'p':
port = optarg;
break;
@ -142,7 +145,7 @@ main(int argc, char* argv[]) {
specfile = string(AUTH_SPECFILE_LOCATION);
}
auth_server = new AuthSrv;
auth_server = new AuthSrv(cache);
auth_server->setVerbose(verbose_mode);
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 {
protected:
AuthSrvTest() : request_message(Message::RENDER),
AuthSrvTest() : server(true), request_message(Message::RENDER),
parse_message(Message::PARSE), default_qid(0x1035),
opcode(Opcode(Opcode::QUERY())), qname("www.example.com"),
qclass(RRClass::IN()), qtype(RRType::A()), ibuffer(NULL),

View File

@ -176,8 +176,8 @@ class ProcessInfo:
class BoB:
"""Boss of BIND class."""
def __init__(self, msgq_socket_file=None, auth_port=5300, verbose=False,
setuid=None, username=None):
def __init__(self, msgq_socket_file=None, auth_port=5300, nocache=False,
verbose=False, setuid=None, username=None):
"""Initialize the Boss of BIND. This is a singleton (only one
can run).
@ -195,6 +195,7 @@ class BoB:
self.runnable = False
self.uid = setuid
self.username = username
self.nocache = nocache
def config_handler(self, new_config):
if self.verbose:
@ -302,6 +303,8 @@ class BoB:
# start b10-auth
# XXX: this must be read from the configuration manager in the future
authargs = ['b10-auth', '-p', str(self.auth_port)]
if self.nocache:
authargs += ['-n']
if self.verbose:
sys.stdout.write("[bind10] Starting b10-auth using port %d\n" %
self.auth_port)
@ -557,6 +560,8 @@ def main():
parser = OptionParser(version=__version__)
parser.add_option("-v", "--verbose", dest="verbose", action="store_true",
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",
action="callback", callback=check_port, default="5300",
help="port the b10-auth daemon will use (default 5300)")
@ -621,7 +626,7 @@ def main():
# Go bob!
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()
if 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 += sqlite3_datasrc.h sqlite3_datasrc.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/foreach.hpp>
#include <datasrc/cache.h>
#include <datasrc/data_source.h>
#include <datasrc/query.h>
#include <dns/base32.h>
#include <dns/buffer.h>
#include <dns/message.h>
@ -35,9 +39,6 @@
#include <cc/data.h>
#include "data_source.h"
#include "query.h"
#define RETERR(x) do { \
DataSrc::Result r = (x); \
if (r != DataSrc::SUCCESS) \
@ -53,7 +54,37 @@ namespace datasrc {
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
// (i.e., address records for the names included in NS or MX records)
void
@ -68,14 +99,14 @@ getAdditional(Query& q, ConstRRsetPtr rrset) {
if (rrset->getType() == RRType::NS()) {
const generic::NS& ns = dynamic_cast<const generic::NS&>(rd);
q.tasks().push(QueryTaskPtr(
new QueryTask(ns.getNSName(), q.qclass(),
new QueryTask(q, ns.getNSName(),
Section::ADDITIONAL(),
QueryTask::GLUE_QUERY,
QueryTask::GETADDITIONAL)));
} else if (rrset->getType() == RRType::MX()) {
const generic::MX& mx = dynamic_cast<const generic::MX&>(rd);
q.tasks().push(QueryTaskPtr(
new QueryTask(mx.getMXName(), q.qclass(),
new QueryTask(q, mx.getMXName(),
Section::ADDITIONAL(),
QueryTask::NOGLUE_QUERY,
QueryTask::GETADDITIONAL)));
@ -125,47 +156,313 @@ chaseCname(Query& q, QueryTaskPtr task, RRsetPtr rrset) {
return;
}
// Stop chasing CNAMES after 16 lookups, to prevent loops
if (q.tooMany()) {
return;
}
q.tasks().push(QueryTaskPtr(
new QueryTask(dynamic_cast<const generic::CNAME&>
new QueryTask(q, dynamic_cast<const generic::CNAME&>
(it->getCurrent()).getCname(),
task->qclass,
task->qtype,
Section::ANSWER(),
task->qtype, Section::ANSWER(),
QueryTask::FOLLOWCNAME)));
}
// Perform the query specified in a QueryTask object
DataSrc::Result
doQueryTask(const DataSrc* ds, const Name* zonename, QueryTask& task,
RRsetList& target)
{
switch (task.op) {
case QueryTask::AUTH_QUERY:
return (ds->findRRset(task.qname, task.qclass, task.qtype,
target, task.flags, zonename));
// Check the cache for data which can answer the current query task.
bool
checkCache(QueryTask& task, RRsetList& target) {
HotCache& cache = task.q.getCache();
RRsetList rrsets;
RRsetPtr rrset;
int count = 0;
uint32_t flags = 0, cflags = 0;
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:
return (ds->findExactRRset(task.qname, task.qclass, task.qtype,
target, task.flags, zonename));
result = ds->findExactRRset(task.qname, task.qclass, task.qtype,
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::NOGLUE_QUERY:
return (ds->findAddrs(task.qname, task.qclass, target,
task.flags, zonename));
result = ds->findAddrs(task.qname, task.qclass, target,
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:
return (ds->findReferral(task.qname, task.qclass, target,
task.flags, zonename));
result = ds->findReferral(task.qname, task.qclass, target,
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
return (DataSrc::ERROR);
}
// Add an RRset (and its associated RRSIG) to a message section,
// checking first to ensure that there isn't already an RRset with
// 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
inline bool
refQuery(const Name& name, const RRClass& qclass, const DataSrc* ds,
const Name* zonename, RRsetList& target)
refQuery(const Query& q, const Name& name, ZoneInfo& zoneinfo,
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
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;
// they'll be handled in a normal lookup in the zone.
inline bool
hasDelegation(const DataSrc* ds, const Name* zonename, Query& q,
QueryTaskPtr task)
{
hasDelegation(Query& q, QueryTaskPtr task, ZoneInfo& zoneinfo) {
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();
if (diff > 1) {
bool found = false;
RRsetList ref;
for (int i = diff - 1; i > 0; --i) {
const Name sub(task->qname.split(i));
if (refQuery(sub, q.qclass(), ds, zonename, ref)) {
if (refQuery(q, sub, zoneinfo, ref)) {
found = true;
break;
}
@ -280,12 +583,12 @@ hasDelegation(const DataSrc* ds, const Name* zonename, Query& q,
}
inline DataSrc::Result
addSOA(Query& q, const Name* zonename, const DataSrc* ds) {
addSOA(Query& q, ZoneInfo& zoneinfo) {
RRsetList soa;
QueryTask newtask(*zonename, q.qclass(), RRType::SOA(),
QueryTask::SIMPLE_QUERY);
RETERR(doQueryTask(ds, zonename, newtask, soa));
const Name* const zonename = zoneinfo.getEnclosingZone();
QueryTask newtask(q, *zonename, RRType::SOA(), QueryTask::SIMPLE_QUERY);
RETERR(doQueryTask(newtask, zoneinfo, soa));
if (newtask.flags != 0) {
return (DataSrc::ERROR);
}
@ -296,14 +599,11 @@ addSOA(Query& q, const Name* zonename, const DataSrc* ds) {
}
inline DataSrc::Result
addNSEC(Query& q, const QueryTaskPtr task, const Name& name,
const Name& zonename, const DataSrc* ds)
{
addNSEC(Query& q, const Name& name, ZoneInfo& zoneinfo) {
RRsetList nsec;
QueryTask newtask(name, task->qclass, RRType::NSEC(),
QueryTask::SIMPLE_QUERY);
RETERR(doQueryTask(ds, &zonename, newtask, nsec));
QueryTask newtask(q, name, RRType::NSEC(), QueryTask::SIMPLE_QUERY);
RETERR(doQueryTask(newtask, zoneinfo, nsec));
if (newtask.flags == 0) {
addToMessage(q, Section::AUTHORITY(),
nsec.findRRset(RRType::NSEC(), q.qclass()));
@ -313,23 +613,31 @@ addNSEC(Query& q, const QueryTaskPtr task, const Name& name,
}
inline DataSrc::Result
getNsec3(const DataSrc* ds, const Name& zonename, const RRClass& qclass,
string& hash, RRsetPtr& target)
{
getNsec3(Query& q, ZoneInfo& zoneinfo, 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;
RETERR(ds->findCoveringNSEC3(zonename, hash, rl));
target = rl.findRRset(RRType::NSEC3(), qclass);
RETERR(ds->findCoveringNSEC3(*zonename, hash, rl));
target = rl.findRRset(RRType::NSEC3(), q.qclass());
return (DataSrc::SUCCESS);
}
ConstNsec3ParamPtr
getNsec3Param(Query& q, const DataSrc* ds, const Name& zonename) {
getNsec3Param(Query& q, ZoneInfo& zoneinfo) {
DataSrc::Result result;
RRsetList nsec3param;
QueryTask newtask(zonename, q.qclass(), RRType::NSEC3PARAM(),
const Name* const zonename = zoneinfo.getEnclosingZone();
QueryTask newtask(q, *zonename, RRType::NSEC3PARAM(),
QueryTask::SIMPLE_QUERY);
result = doQueryTask(ds, &zonename, newtask, nsec3param);
result = doQueryTask(newtask, zoneinfo, nsec3param);
newtask.flags &= ~DataSrc::REFERRAL;
if (result != DataSrc::SUCCESS || newtask.flags != 0) {
return (ConstNsec3ParamPtr());
@ -356,15 +664,16 @@ getNsec3Param(Query& q, const DataSrc* ds, const Name& zonename) {
}
inline DataSrc::Result
proveNX(Query& q, QueryTaskPtr task, const DataSrc* ds,
const Name& zonename, const bool wildcard)
{
ConstNsec3ParamPtr nsec3 = getNsec3Param(q, ds, zonename);
proveNX(Query& q, QueryTaskPtr task, ZoneInfo& zoneinfo, const bool wildcard) {
Message& m = q.message();
const Name* const zonename = zoneinfo.getEnclosingZone();
ConstNsec3ParamPtr nsec3 = getNsec3Param(q, zoneinfo);
if (nsec3 != NULL) {
// Attach the NSEC3 record covering the QNAME
RRsetPtr rrset;
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);
// 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
Name enclosure(zonename);
Name enclosure(*zonename);
const int diff = task->qname.getLabelCount() -
enclosure.getLabelCount();
string hash2;
@ -388,7 +697,7 @@ proveNX(Query& q, QueryTaskPtr task, const DataSrc* ds,
// hash2 will be overwritten with the actual hash found;
// 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) {
addToMessage(q, Section::AUTHORITY(), rrset);
break;
@ -403,19 +712,24 @@ proveNX(Query& q, QueryTaskPtr task, const DataSrc* ds,
// Otherwise, there is no wildcard record, so we must add a
// covering NSEC3 to prove that it doesn't exist.
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) {
addToMessage(q, Section::AUTHORITY(), rrset);
}
} else {
Name nsecname(task->qname);
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 ||
nsecname == zonename)
nsecname == *zonename)
{
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
// 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);
@ -435,9 +749,7 @@ proveNX(Query& q, QueryTaskPtr task, const DataSrc* ds,
// Attempt a wildcard lookup
inline DataSrc::Result
tryWildcard(Query& q, QueryTaskPtr task, const DataSrc* ds,
const Name* zonename, bool& found)
{
tryWildcard(Query& q, QueryTaskPtr task, ZoneInfo& zoneinfo, bool& found) {
Message& m = q.message();
DataSrc::Result result;
found = false;
@ -448,6 +760,7 @@ tryWildcard(Query& q, QueryTaskPtr task, const DataSrc* ds,
return (DataSrc::SUCCESS);
}
const Name* const zonename = zoneinfo.getEnclosingZone();
const int diff = task->qname.getLabelCount() - zonename->getLabelCount();
if (diff < 1) {
return (DataSrc::SUCCESS);
@ -459,9 +772,9 @@ tryWildcard(Query& q, QueryTaskPtr task, const DataSrc* ds,
for (int i = 1; i <= diff; ++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);
result = doQueryTask(ds, zonename, newtask, wild);
result = doQueryTask(newtask, zoneinfo, wild);
if (result == DataSrc::SUCCESS) {
if (newtask.flags == 0) {
task->flags &= ~DataSrc::NAME_NOT_FOUND;
@ -487,7 +800,7 @@ tryWildcard(Query& q, QueryTaskPtr task, const DataSrc* ds,
if (found) {
// Prove the nonexistence of the name we were looking for
if (q.wantDnssec()) {
result = proveNX(q, task, ds, *zonename, true);
result = proveNX(q, task, zoneinfo, true);
if (result != DataSrc::SUCCESS) {
m.setRcode(Rcode::SERVFAIL());
return (DataSrc::ERROR);
@ -511,7 +824,7 @@ tryWildcard(Query& q, QueryTaskPtr task, const DataSrc* ds,
}
RRsetList auth;
if (!refQuery(*zonename, q.qclass(), ds, zonename, auth)) {
if (!refQuery(q, *zonename, zoneinfo, auth)) {
return (DataSrc::ERROR);
}
@ -521,7 +834,6 @@ tryWildcard(Query& q, QueryTaskPtr task, const DataSrc* ds,
return (DataSrc::SUCCESS);
}
} // end of anonymous namespace
//
// doQuery: Processes a query.
@ -531,6 +843,12 @@ DataSrc::doQuery(Query& q) {
Message& m = q.message();
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());
while (!q.tasks().empty()) {
QueryTaskPtr task = q.tasks().front();
@ -549,55 +867,29 @@ DataSrc::doQuery(Query& q) {
return;
}
// Find the closest enclosing zone for which we are authoritative,
// 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));
ZoneInfo zoneinfo(this, task->qname, task->qclass, task->qtype);
RRsetList data;
Result result = SUCCESS;
if (datasource) {
// For these query task types, if there is more than
// one level between the zone name and qname, we need to
// check the intermediate nodes for referrals.
if ((task->op == QueryTask::AUTH_QUERY ||
task->op == QueryTask::NOGLUE_QUERY) &&
hasDelegation(datasource, zonename, q, task)) {
continue;
}
// For these query task types, if there is more than
// one level between the zone name and qname, we need to
// check the intermediate nodes for referrals.
if ((task->op == QueryTask::AUTH_QUERY ||
task->op == QueryTask::NOGLUE_QUERY) &&
hasDelegation(q, task, zoneinfo)) {
continue;
}
result = doQueryTask(datasource, zonename, *task, data);
if (result != SUCCESS) {
m.setRcode(Rcode::SERVFAIL());
return;
}
result = doQueryTask(*task, zoneinfo, data);
if (result != SUCCESS) {
m.setRcode(Rcode::SERVFAIL());
return;
}
// 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
// specifically for, and found, a DS, NSEC, or DNAME record.
if ((task->flags & REFERRAL) != 0 &&
(zonename->getLabelCount() == task->qname.getLabelCount() ||
((task->qtype == RRType::NSEC() ||
task->qtype == RRType::DS() ||
task->qtype == RRType::DNAME()) &&
data.findRRset(task->qtype, task->qclass)))) {
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.
// 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;
@ -605,6 +897,19 @@ DataSrc::doQuery(Query& q) {
continue;
}
// 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
// specifically for, and found, a DS, NSEC, or DNAME record.
const Name* const zonename = zoneinfo.getEnclosingZone();
if ((task->flags & REFERRAL) != 0 &&
(zonename->getLabelCount() == task->qname.getLabelCount() ||
((task->qtype == RRType::NSEC() ||
task->qtype == RRType::DS() ||
task->qtype == RRType::DNAME()) &&
data.findRRset(task->qtype, task->qclass)))) {
task->flags &= ~REFERRAL;
}
if (result == SUCCESS && task->flags == 0) {
bool have_ns = false, need_auth = false;
switch (task->state) {
@ -626,13 +931,12 @@ DataSrc::doQuery(Query& q) {
// Add the NS records for the enclosing zone to
// the authority section.
RRsetList auth;
if (!refQuery(*zonename, q.qclass(), datasource, zonename,
auth) ||
!auth.findRRset(RRType::NS(),
datasource->getClass())) {
const DataSrc* ds = zoneinfo.getDataSource();
if (!refQuery(q, Name(*zonename), zoneinfo, auth) ||
!auth.findRRset(RRType::NS(), ds->getClass())) {
isc_throw(DataSourceError,
"NS RR not found in " << *zonename << "/" <<
datasource->getClass());
q.qclass());
}
copyAuth(q, auth);
@ -673,8 +977,7 @@ DataSrc::doQuery(Query& q) {
if (task->state == QueryTask::GETANSWER) {
RRsetList auth;
m.clearHeaderFlag(MessageFlag::AA());
if (!refQuery(task->qname, q.qclass(), datasource, zonename,
auth)) {
if (!refQuery(q, task->qname, zoneinfo, auth)) {
m.setRcode(Rcode::SERVFAIL());
return;
}
@ -697,7 +1000,7 @@ DataSrc::doQuery(Query& q) {
// and the name was not found, we need to find out whether
// there are any relevant wildcards.
bool wildcard_found = false;
result = tryWildcard(q, task, datasource, zonename, wildcard_found);
result = tryWildcard(q, task, zoneinfo, wildcard_found);
if (result != SUCCESS) {
m.setRcode(Rcode::SERVFAIL());
return;
@ -719,21 +1022,22 @@ DataSrc::doQuery(Query& q) {
m.setRcode(Rcode::NXDOMAIN());
}
result = addSOA(q, zonename, datasource);
result = addSOA(q, zoneinfo);
if (result != SUCCESS) {
isc_throw(DataSourceError,
"SOA RR not found in" << *zonename <<
"/" << datasource->getClass());
"/" << q.qclass());
}
}
Name nsecname(task->qname);
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()) {
result = proveNX(q, task, datasource, *zonename, false);
result = proveNX(q, task, zoneinfo, false);
if (result != DataSrc::SUCCESS) {
m.setRcode(Rcode::SERVFAIL());
return;
@ -858,25 +1162,38 @@ MetaDataSrc::removeDataSrc(ConstDataSrcPtr data_src) {
}
void
MetaDataSrc::findClosestEnclosure(NameMatch& match, const RRClass& qclass) const
{
if (getClass() != qclass &&
getClass() != RRClass::ANY() && qclass != RRClass::ANY()) {
MetaDataSrc::findClosestEnclosure(DataSrcMatch& match) const {
if (getClass() != match.getClass() &&
getClass() != RRClass::ANY() && match.getClass() != RRClass::ANY()) {
return;
}
BOOST_FOREACH (ConstDataSrcPtr data_src, data_sources) {
data_src->findClosestEnclosure(match, qclass);
data_src->findClosestEnclosure(match);
}
}
NameMatch::~NameMatch() {
DataSrcMatch::~DataSrcMatch() {
delete closest_name_;
}
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) {
const NameComparisonResult::NameRelation cmp =
getName().compare(container).getRelation();
if (cmp != NameComparisonResult::EQUAL &&
cmp != NameComparisonResult::SUBDOMAIN)
{
return;
}
closest_name_ = new Name(container);
best_source_ = &new_source;
return;
@ -884,7 +1201,7 @@ NameMatch::update(const DataSrc& new_source, const Name& container) {
if (container.compare(*closest_name_).getRelation() ==
NameComparisonResult::SUBDOMAIN) {
const Name* newname = new Name(container);
Name* newname = new Name(container);
delete closest_name_;
closest_name_ = newname;
best_source_ = &new_source;

View File

@ -40,7 +40,7 @@ class RRsetList;
namespace datasrc {
class NameMatch;
class DataSrcMatch;
class Query;
class DataSrc;
@ -89,12 +89,15 @@ public:
// NAME_NOT_FOUND: The node does not exist in the data source.
// TYPE_NOT_FOUND: The node does not contain the requested RRType
// 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 {
REFERRAL = 0x01,
CNAME_FOUND = 0x02,
NAME_NOT_FOUND = 0x04,
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
@ -107,9 +110,7 @@ public:
// 'Medium-level' methods. This will be implemented by the general
// DataSrc class but MAY be overwritten by subclasses.
virtual void findClosestEnclosure(NameMatch& match,
const isc::dns::RRClass& qclasss)
const = 0;
virtual void findClosestEnclosure(DataSrcMatch& match) const = 0;
// Optional 'low-level' methods. These will have stub implementations
// in the general DataSrc class but MAY be overwritten by subclasses
@ -179,9 +180,7 @@ public:
virtual void doQuery(Query& q);
virtual void findClosestEnclosure(NameMatch& match,
const isc::dns::RRClass& qclass)
const = 0;
virtual void findClosestEnclosure(DataSrcMatch& match) const = 0;
const isc::dns::RRClass& getClass() const { return rrclass; }
void setClass(isc::dns::RRClass& c) { rrclass = c; }
@ -250,8 +249,7 @@ public:
void removeDataSrc(ConstDataSrcPtr data_src);
size_t dataSrcCount() { return data_sources.size(); };
void findClosestEnclosure(NameMatch& match,
const isc::dns::RRClass& qclass) const;
void findClosestEnclosure(DataSrcMatch& match) const;
// Actual queries for data should not be sent to a MetaDataSrc object,
// so we return NOT_IMPLEMENTED if we receive any.
@ -298,31 +296,107 @@ private:
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.
///
/// Note: The copy constructor and the assignment operator are intentionally
/// defined as private.
/// Note: The copy constructor and the assignment operator are
/// intentionally defined as private.
//@{
private:
NameMatch(const NameMatch& source);
NameMatch& operator=(const NameMatch& source);
DataSrcMatch(const DataSrcMatch& source);
DataSrcMatch& operator=(const DataSrcMatch& source);
public:
NameMatch(const isc::dns::Name& qname) :
closest_name_(NULL), best_source_(NULL), qname_(qname) {}
~NameMatch();
/// \brief The constructor.
///
/// 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);
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:
const isc::dns::Name* closest_name_;
isc::dns::Name* closest_name_;
const DataSrc* best_source_;
const isc::dns::Name qname_;
const isc::dns::Name name_;
const isc::dns::RRClass& rrclass_;
};
class Nsec3Param {

View File

@ -28,36 +28,37 @@ using namespace isc::dns;
namespace isc {
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) :
qname(n), qclass(c), qtype(t), section(sect), op(AUTH_QUERY),
state(GETANSWER), flags(0)
q(qry), qname(n), qclass(qry.qclass()), qtype(t), section(sect),
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 Op o) :
qname(n), qclass(c), qtype(t), section(sect), op(o), state(GETANSWER),
flags(0)
q(qry), qname(n), qclass(qry.qclass()), qtype(t), section(sect), op(o),
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 State st) :
qname(n), qclass(c), qtype(t), section(sect), op(AUTH_QUERY), state(st),
flags(0)
q(qry), qname(n), qclass(qry.qclass()), qtype(t), section(sect),
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 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) :
qname(n), qclass(c), qtype(t), section(Section::ANSWER()), op(o),
state(GETANSWER), flags(0)
q(qry), qname(n), qclass(qry.qclass()), qtype(t),
section(Section::ANSWER()), op(o), state(GETANSWER), flags(0)
{
if (op != SIMPLE_QUERY) {
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.
QueryTask::QueryTask(const isc::dns::Name& n, const isc::dns::RRClass& c,
const Op o) :
qname(n), qclass(c), qtype(RRType::ANY()), section(Section::ANSWER()),
op(o), state(GETANSWER), flags(0)
QueryTask::QueryTask(const Query& qry, const isc::dns::Name& n, const Op o) :
q(qry), qname(n), qclass(qry.qclass()), qtype(RRType::ANY()),
section(Section::ANSWER()), op(o), state(GETANSWER), flags(0)
{
if (op != REF_QUERY) {
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 State st) :
qname(n), qclass(c), qtype(RRType::ANY()), section(sect), op(o),
state(st), flags(0)
q(qry), qname(n), qclass(qry.qclass()), qtype(RRType::ANY()),
section(sect), op(o), state(st), flags(0)
{
if (op != GLUE_QUERY && op != NOGLUE_QUERY) {
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() {}
Query::Query(Message& m, bool dnssec) :
Query::Query(Message& m, HotCache& c, bool dnssec) :
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
if (message_->getRRCount(Section::QUESTION()) != 1) {
@ -104,7 +104,7 @@ Query::Query(Message& m, bool dnssec) :
qtype_ = &question->getType();
restarts_ = 0;
querytasks_.push(QueryTaskPtr(new QueryTask(*qname_, *qclass_, *qtype_,
querytasks_.push(QueryTaskPtr(new QueryTask(*this, *qname_, *qtype_,
Section::ANSWER())));
}

View File

@ -19,6 +19,9 @@
#include <boost/shared_ptr.hpp>
#include <datasrc/cache.h>
#include <datasrc/data_source.h>
#include <dns/name.h>
#include <dns/message.h>
#include <dns/rrtype.h>
@ -27,9 +30,11 @@
#include <queue>
namespace isc {
namespace datasrc {
class Query;
typedef boost::shared_ptr<Query> QueryPtr;
// An individual task to be carried out by the query logic
class QueryTask {
private:
@ -41,6 +46,9 @@ public:
// XXX: Members are currently public, but should probably be
// 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.
// Note that qtype is ignored in the GLUE_QUERY/NOGLUE_QUERY case.
const isc::dns::Name qname;
@ -118,15 +126,14 @@ public:
uint32_t flags;
// 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);
QueryTask(const isc::dns::Name& n, const isc::dns::RRClass& c,
const isc::dns::RRType& t, const isc::dns::Section& sect,
Op o);
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, Op o);
QueryTask(const Query& q, const isc::dns::Name& n,
const isc::dns::RRType& t, const isc::dns::Section& sect,
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,
Op o, State st);
@ -134,12 +141,12 @@ public:
// to simplify the code.
//
// 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);
// 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.
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);
~QueryTask();
@ -148,9 +155,6 @@ public:
typedef boost::shared_ptr<QueryTask> QueryTaskPtr;
typedef std::queue<QueryTaskPtr> QueryTaskQueue;
class Query;
typedef boost::shared_ptr<Query> QueryPtr;
// Data Source query
class Query {
public:
@ -171,7 +175,7 @@ private:
Query& operator=(const Query& source);
public:
// Query constructor
Query(isc::dns::Message& m, bool dnssec);
Query(isc::dns::Message& m, HotCache& c, bool dnssec);
/// \brief The destructor.
virtual ~Query();
//@}
@ -179,17 +183,17 @@ public:
// wantAdditional() == true indicates that additional-section data
// should be looked up while processing this query. false indicates
// 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; }
// wantDnssec() == true indicates that DNSSEC data should be retrieved
// 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; }
const isc::dns::Name& qname() const { return *qname_; }
const isc::dns::RRClass& qclass() const { return *qclass_; }
const isc::dns::RRType& qtype() const { return *qtype_; }
const isc::dns::Name& qname() const { return (*qname_); }
const isc::dns::RRClass& qclass() const { return (*qclass_); }
const isc::dns::RRType& qtype() const { return (*qtype_); }
// Note: these can't be constant member functions because they expose
// 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
// but it's a different topic), but at the moment we keep them.
// We should definitely revisit the design later.
isc::dns::Message& message() { return *message_; }
QueryTaskQueue& tasks() { return querytasks_; }
isc::dns::Message& message() { return (*message_); }
QueryTaskQueue& tasks() { return (querytasks_); }
Status status() const { return status_; }
Status status() const { return (status_); }
void setStatus(Status s) { status_ = s; }
// Limit CNAME chains to 16 per query, to avoid loops
@ -211,6 +215,13 @@ public:
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:
Status status_;
@ -218,6 +229,9 @@ private:
const isc::dns::RRClass* qclass_;
const isc::dns::RRType* qtype_;
HotCache* cache_;
DataSrc* datasrc_;
isc::dns::Message* message_;
QueryTaskQueue querytasks_;

View File

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

View File

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

View File

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

View File

@ -39,8 +39,6 @@ class RRsetList;
namespace datasrc {
class Query;
class NameMatch;
struct StaticDataSrcImpl;
class StaticDataSrc : public DataSrc {
@ -58,8 +56,7 @@ public:
~StaticDataSrc();
//@}
void findClosestEnclosure(NameMatch& match,
const isc::dns::RRClass& qclass) const;
void findClosestEnclosure(DataSrcMatch& match) const;
Result findRRset(const isc::dns::Name& qname,
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 += static_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_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)

View File

@ -69,6 +69,7 @@ protected:
void createAndProcessQuery(const Name& qname, const RRClass& qclass,
const RRType& qtype);
HotCache cache;
MetaDataSrc meta_source;
OutputBuffer obuffer;
MessageRenderer renderer;
@ -76,10 +77,10 @@ protected:
};
void
performQuery(DataSrc& data_source, Message& message) {
performQuery(DataSrc& data_source, HotCache& cache, Message& message) {
message.setHeaderFlag(MessageFlag::AA());
message.setRcode(Rcode::NOERROR());
Query q(message, true);
Query q(message, cache, true);
data_source.doQuery(q);
}
@ -92,7 +93,7 @@ DataSrcTest::readAndProcessQuery(const char* datafile) {
msg.fromWire(buffer);
msg.makeResponse();
performQuery(meta_source, msg);
performQuery(meta_source, cache, msg);
}
void
@ -103,7 +104,7 @@ DataSrcTest::createAndProcessQuery(const Name& qname, const RRClass& qclass,
msg.setOpcode(Opcode::QUERY());
msg.addQuestion(Question(qname, qclass, qtype));
msg.setHeaderFlag(MessageFlag::RD());
performQuery(meta_source, msg);
performQuery(meta_source, cache, msg);
}
void
@ -213,6 +214,83 @@ TEST_F(DataSrcTest, NSQuery) {
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) {
readAndProcessQuery("q_example_ptr");
@ -858,18 +936,6 @@ TEST_F(DataSrcTest, AddRemoveDataSrc) {
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) {
EXPECT_THROW(createAndProcessQuery(Name("www.nons.example"),
RRClass::IN(), RRType::A()),
@ -888,4 +954,114 @@ TEST_F(DataSrcTest, noSOAZone) {
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 <dns/buffer.h>
#include <dns/message.h>
#include <dns/name.h>
#include <dns/rrtype.h>
#include <dns/rrclass.h>
#include <datasrc/query.h>
namespace {
#include <dns/tests/unittest_util.h>
using isc::UnitTestUtil;
using namespace isc::dns;
using namespace isc::datasrc;
namespace {
class QueryTest : public ::testing::Test {
protected:
QueryTest() :
name(Name("www.example.com")),
rrtype(RRType::A()),
rrclass(RRClass::IN())
{}
const Name name;
const RRType rrtype;
const RRClass rrclass;
void readQuery(Message& m, const char* datafile);
HotCache cache;
};
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
createTask(const Name& name, const RRClass& rrclass0, const RRType& rrtype0) {
createTask(Message& m, const Name& name, const RRType& rrtype0, HotCache& c) {
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)));
}
// Check the QueryTask created using a temporary RRType object will remain
// valid.
TEST_F(QueryTest, constructWithTemporary) {
QueryTaskPtr task_a = createTask(name, rrclass, RRType::A());
QueryTaskPtr task_aaaa = createTask(name, rrclass, RRType::AAAA());
EXPECT_EQ(rrtype, task_a->qtype);
Message m1(Message::PARSE);
readQuery(m1, "q_wild_a");
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.init(SQLITE_DBFILE_EXAMPLE2));
NameMatch name_match(www_name);
data_source.findClosestEnclosure(name_match, rrclass);
// XXX: some deviant compilers seem to fail to recognize a NULL as a
// pointer type. This explicit cast works around such compilers.
EXPECT_EQ(static_cast<void*>(NULL), name_match.closestName());
EXPECT_EQ(static_cast<void*>(NULL), name_match.bestDataSrc());
DataSrcMatch match(www_name, rrclass);
data_source.findClosestEnclosure(match);
EXPECT_EQ(NULL, match.getEnclosingZone());
EXPECT_EQ(NULL, match.getDataSource());
}
TEST_F(Sqlite3DataSourceTest, openFail) {
@ -415,52 +413,52 @@ TEST_F(Sqlite3DataSourceTest, memoryDB) {
}
TEST_F(Sqlite3DataSourceTest, findClosestEnclosure) {
NameMatch name_match(www_name);
data_source.findClosestEnclosure(name_match, rrclass);
EXPECT_EQ(zone_name, *name_match.closestName());
EXPECT_EQ(&data_source, name_match.bestDataSrc());
DataSrcMatch match(www_name, rrclass);
data_source.findClosestEnclosure(match);
EXPECT_EQ(zone_name, *match.getEnclosingZone());
EXPECT_EQ(&data_source, match.getDataSource());
}
TEST_F(Sqlite3DataSourceTest, findClosestEnclosureMatchRoot) {
EXPECT_EQ(DataSrc::SUCCESS, data_source.close());
EXPECT_EQ(DataSrc::SUCCESS, data_source.init(SQLITE_DBFILE_EXAMPLE_ROOT));
NameMatch name_match(Name("org."));
data_source.findClosestEnclosure(name_match, rrclass);
EXPECT_EQ(Name("."), *name_match.closestName());
EXPECT_EQ(&data_source, name_match.bestDataSrc());
DataSrcMatch match(Name("org."), rrclass);
data_source.findClosestEnclosure(match);
EXPECT_EQ(Name("."), *match.getEnclosingZone());
EXPECT_EQ(&data_source, match.getDataSource());
}
TEST_F(Sqlite3DataSourceTest, findClosestEnclosureAtDelegation) {
// The search name exists both in the parent and child zones, but
// child has a better match.
NameMatch name_match(child_name);
data_source.findClosestEnclosure(name_match, rrclass);
EXPECT_EQ(child_name, *name_match.closestName());
EXPECT_EQ(&data_source, name_match.bestDataSrc());
DataSrcMatch match(child_name, rrclass);
data_source.findClosestEnclosure(match);
EXPECT_EQ(child_name, *match.getEnclosingZone());
EXPECT_EQ(&data_source, match.getDataSource());
}
TEST_F(Sqlite3DataSourceTest, findClosestEnclosureNoMatch) {
NameMatch name_match(nomatch_name);
data_source.findClosestEnclosure(name_match, rrclass);
EXPECT_EQ(static_cast<void*>(NULL), name_match.closestName());
EXPECT_EQ(static_cast<void*>(NULL), name_match.bestDataSrc());
DataSrcMatch match(nomatch_name, rrclass);
data_source.findClosestEnclosure(match);
EXPECT_EQ(NULL, match.getEnclosingZone());
EXPECT_EQ(NULL, match.getDataSource());
}
TEST_F(Sqlite3DataSourceTest, findClosestClassMismatch) {
NameMatch name_match(www_name);
data_source.findClosestEnclosure(name_match, rrclass_notmatch);
EXPECT_EQ(static_cast<void*>(NULL), name_match.closestName());
EXPECT_EQ(static_cast<void*>(NULL), name_match.bestDataSrc());
DataSrcMatch match(nomatch_name, rrclass);
data_source.findClosestEnclosure(match);
EXPECT_EQ(NULL, match.getEnclosingZone());
EXPECT_EQ(NULL, match.getDataSource());
}
// If the query class is ANY, the result should be the same as the case where
// the class exactly matches.
TEST_F(Sqlite3DataSourceTest, findClosestClassAny) {
NameMatch name_match(www_name);
data_source.findClosestEnclosure(name_match, RRClass::ANY());
EXPECT_EQ(zone_name, *name_match.closestName());
EXPECT_EQ(&data_source, name_match.bestDataSrc());
DataSrcMatch match(www_name, RRClass::ANY());
data_source.findClosestEnclosure(match);
EXPECT_EQ(zone_name, *match.getEnclosingZone());
EXPECT_EQ(&data_source, match.getDataSource());
}
TEST_F(Sqlite3DataSourceTest, findRRsetNormal) {

View File

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

View File

@ -65,8 +65,9 @@ namespace {
// {"example.com", "AAAA", "2001:db8::2"},
// ...
// 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
// specified per RRset.
// the RRset to be signed. Multiple RRSIGs can follow the RRset. RRSIG
// 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.
//
@ -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", "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", "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", "A", "192.0.2.1"},
@ -335,8 +340,9 @@ buildZone(Zone& zone, const RRData* records, const bool is_glue) {
rrset->addRdata(createRdata(rrtype, zone.rrclass, records[i].rdata));
if (rrtype == RRType::RRSIG()) {
prev_rrset->addRRsig(rrset);
} else {
prev_rrset = rrset;
}
prev_rrset = rrset;
}
}
@ -361,12 +367,10 @@ TestDataSrc::init() {
}
void
TestDataSrc::findClosestEnclosure(NameMatch& match,
const RRClass& qclass) const
{
const Name& qname = match.qname();
TestDataSrc::findClosestEnclosure(DataSrcMatch& match) const {
const Name& qname = match.getName();
if (qclass != getClass() && qclass != RRClass::ANY()) {
if (match.getClass() != getClass() && match.getClass() != RRClass::ANY()) {
return;
}

View File

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

View File

@ -229,6 +229,20 @@ public:
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:
Name name_;
RRType rrtype_;

View File

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

View File

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

View File

@ -119,4 +119,35 @@ TEST_F(QuestionTest, LeftShiftOperator)
oss << test_question1;
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"),
2010012601, 3600, 300, 3600000, 1200);
const generic::CNAME rdata_cname("target.example.com");
const generic::DNAME rdata_dname("dtarget.example.com");
void
RRsetListTest::setupList(RRsetList& list) {
@ -86,6 +87,24 @@ TEST_F(RRsetListTest, addRRsets) {
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) {
RRsetList list;
setupList(list);