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:
parent
222a6e821c
commit
34159d6962
@ -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
|
||||
|
@ -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
|
||||
|
@ -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_) {
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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),
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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())));
|
||||
}
|
||||
|
||||
|
@ -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_;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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_;
|
||||
|
@ -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_) {
|
||||
|
@ -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,
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user