[svn] commit: r2383 - in /trunk: ./ doc/ src/bin/auth/ src/bin/auth/tests/ src/bin/bind10/ src/lib/datasrc/ src/lib/datasrc/tests/ src/lib/dns/ src/lib/dns/tests/
BIND 10 source code commits
bind10-changes at lists.isc.org
Wed Jun 30 23:47:29 UTC 2010
Author: each
Date: Wed Jun 30 23:47:29 2010
New Revision: 2383
Log:
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)
Modified:
trunk/ChangeLog
trunk/doc/Doxyfile
trunk/src/bin/auth/auth_srv.cc
trunk/src/bin/auth/auth_srv.h
trunk/src/bin/auth/main.cc
trunk/src/bin/auth/tests/auth_srv_unittest.cc
trunk/src/bin/bind10/bind10.py.in
trunk/src/lib/datasrc/Makefile.am
trunk/src/lib/datasrc/data_source.cc
trunk/src/lib/datasrc/data_source.h
trunk/src/lib/datasrc/query.cc
trunk/src/lib/datasrc/query.h
trunk/src/lib/datasrc/sqlite3_datasrc.cc
trunk/src/lib/datasrc/sqlite3_datasrc.h
trunk/src/lib/datasrc/static_datasrc.cc
trunk/src/lib/datasrc/static_datasrc.h
trunk/src/lib/datasrc/tests/Makefile.am
trunk/src/lib/datasrc/tests/datasrc_unittest.cc
trunk/src/lib/datasrc/tests/query_unittest.cc
trunk/src/lib/datasrc/tests/sqlite3_unittest.cc
trunk/src/lib/datasrc/tests/static_unittest.cc
trunk/src/lib/datasrc/tests/test_datasrc.cc
trunk/src/lib/datasrc/tests/test_datasrc.h
trunk/src/lib/dns/question.h
trunk/src/lib/dns/rrsetlist.cc
trunk/src/lib/dns/rrsetlist.h
trunk/src/lib/dns/tests/question_unittest.cc
trunk/src/lib/dns/tests/rrsetlist_unittest.cc
Modified: trunk/ChangeLog
==============================================================================
--- trunk/ChangeLog (original)
+++ trunk/ChangeLog Wed Jun 30 23:47:29 2010
@@ -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
Modified: trunk/doc/Doxyfile
==============================================================================
--- trunk/doc/Doxyfile (original)
+++ trunk/doc/Doxyfile Wed Jun 30 23:47:29 2010
@@ -568,7 +568,7 @@
# 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
Modified: trunk/src/bin/auth/auth_srv.cc
==============================================================================
--- trunk/src/bin/auth/auth_srv.cc (original)
+++ trunk/src/bin/auth/auth_srv.cc Wed Jun 30 23:47:29 2010
@@ -60,7 +60,7 @@
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 @@
/// 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 @@
// add static data source
data_sources_.addDataSrc(ConstDataSrcPtr(new StaticDataSrc));
-}
-
-AuthSrv::AuthSrv() : impl_(new AuthSrvImpl) {
+
+ // enable or disable the cache
+ cache_.setEnabled(use_cache);
+}
+
+AuthSrv::AuthSrv(const bool use_cache) : impl_(new AuthSrvImpl(use_cache)) {
}
AuthSrv::~AuthSrv() {
@@ -239,7 +246,7 @@
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_) {
Modified: trunk/src/bin/auth/auth_srv.h
==============================================================================
--- trunk/src/bin/auth/auth_srv.h (original)
+++ trunk/src/bin/auth/auth_srv.h Wed Jun 30 23:47:29 2010
@@ -43,7 +43,7 @@
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;
Modified: trunk/src/bin/auth/main.cc
==============================================================================
--- trunk/src/bin/auth/main.cc (original)
+++ trunk/src/bin/auth/main.cc Wed Jun 30 23:47:29 2010
@@ -86,7 +86,7 @@
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 @@
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,
@@ -109,6 +109,9 @@
case '6':
// The same note as -4 applies.
use_ipv4 = false;
+ break;
+ case 'n':
+ cache = false;
break;
case 'p':
port = optarg;
@@ -142,7 +145,7 @@
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,
Modified: trunk/src/bin/auth/tests/auth_srv_unittest.cc
==============================================================================
--- trunk/src/bin/auth/tests/auth_srv_unittest.cc (original)
+++ trunk/src/bin/auth/tests/auth_srv_unittest.cc Wed Jun 30 23:47:29 2010
@@ -44,7 +44,7 @@
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),
Modified: trunk/src/bin/bind10/bind10.py.in
==============================================================================
--- trunk/src/bin/bind10/bind10.py.in (original)
+++ trunk/src/bin/bind10/bind10.py.in Wed Jun 30 23:47:29 2010
@@ -176,8 +176,8 @@
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 @@
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 @@
# 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 @@
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 @@
# 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)
Modified: trunk/src/lib/datasrc/Makefile.am
==============================================================================
--- trunk/src/lib/datasrc/Makefile.am (original)
+++ trunk/src/lib/datasrc/Makefile.am Wed Jun 30 23:47:29 2010
@@ -13,3 +13,4 @@
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
Modified: trunk/src/lib/datasrc/data_source.cc
==============================================================================
--- trunk/src/lib/datasrc/data_source.cc (original)
+++ trunk/src/lib/datasrc/data_source.cc Wed Jun 30 23:47:29 2010
@@ -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 @@
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 @@
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,46 +156,312 @@
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
+// 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(const DataSrc* ds, const Name* zonename, QueryTask& task,
- RRsetList& target)
-{
+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:
+ 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:
- return (ds->findRRset(task.qname, task.qclass, task.qtype,
- target, task.flags, zonename));
-
- case QueryTask::SIMPLE_QUERY:
- return (ds->findExactRRset(task.qname, task.qclass, task.qtype,
- target, task.flags, zonename));
+ 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
@@ -202,12 +499,12 @@
// 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);
-
- if (doQueryTask(ds, zonename, newtask, target) != DataSrc::SUCCESS) {
+ QueryTask newtask(q, name, QueryTask::REF_QUERY);
+
+ if (doQueryTask(newtask, zoneinfo, target) != DataSrc::SUCCESS) {
// Lookup failed
return (false);
}
@@ -224,16 +521,22 @@
// 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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
// 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 @@
// 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 @@
// 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);
- }
-
- RETERR(addNSEC(q, task, nsecname, zonename, ds));
+ const DataSrc* ds = zoneinfo.getDataSource();
+ if (ds == NULL) {
+ m.setRcode(Rcode::SERVFAIL());
+ return (DataSrc::ERROR);
+ }
+ ds->findPreviousName(task->qname, nsecname, zonename);
+ }
+
+ RETERR(addNSEC(q, nsecname, zoneinfo));
if ((task->flags & DataSrc::TYPE_NOT_FOUND) != 0 ||
- nsecname == zonename)
+ nsecname == *zonename)
{
return (DataSrc::SUCCESS);
}
@@ -427,7 +741,7 @@
// 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 @@
// 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 @@
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 @@
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 @@
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 @@
}
RRsetList auth;
- if (!refQuery(*zonename, q.qclass(), ds, zonename, auth)) {
+ if (!refQuery(q, *zonename, zoneinfo, auth)) {
return (DataSrc::ERROR);
}
@@ -521,7 +834,6 @@
return (DataSrc::SUCCESS);
}
-} // end of anonymous namespace
//
// doQuery: Processes a query.
@@ -531,6 +843,12 @@
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,60 +867,47 @@
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;
- }
-
- result = doQueryTask(datasource, zonename, *task, 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.
+ // 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(*task, zoneinfo, data);
+ if (result != SUCCESS) {
+ m.setRcode(Rcode::SERVFAIL());
+ return;
+ }
+
+ // No such zone. If we're chasing cnames or adding additional
+ // data, that's okay, but if doing an original query, return
+ // REFUSED.
+ if (task->flags == NO_SUCH_ZONE) {
if (task->state == QueryTask::GETANSWER) {
m.setRcode(Rcode::REFUSED());
return;
}
continue;
+ }
+
+ // Query found a referral; let's find out if that was expected--
+ // 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) {
@@ -626,13 +931,12 @@
// 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 @@
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 @@
// 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 @@
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 @@
}
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);
- }
-}
-
-NameMatch::~NameMatch() {
+ data_src->findClosestEnclosure(match);
+ }
+}
+
+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 @@
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;
Modified: trunk/src/lib/datasrc/data_source.h
==============================================================================
--- trunk/src/lib/datasrc/data_source.h (original)
+++ trunk/src/lib/datasrc/data_source.h Wed Jun 30 23:47:29 2010
@@ -40,7 +40,7 @@
namespace datasrc {
-class NameMatch;
+class DataSrcMatch;
class Query;
class DataSrc;
@@ -89,12 +89,15 @@
// 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 @@
// '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 @@
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 @@
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 @@
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.
-private:
- NameMatch(const NameMatch& source);
- NameMatch& operator=(const NameMatch& source);
-public:
- NameMatch(const isc::dns::Name& qname) :
- closest_name_(NULL), best_source_(NULL), qname_(qname) {}
- ~NameMatch();
+ /// Note: The copy constructor and the assignment operator are
+ /// intentionally defined as private.
+ //@{
+private:
+ DataSrcMatch(const DataSrcMatch& source);
+ DataSrcMatch& operator=(const DataSrcMatch& source);
+public:
+ /// \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_;
+private:
+ 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 {
Modified: trunk/src/lib/datasrc/query.cc
==============================================================================
--- trunk/src/lib/datasrc/query.cc (original)
+++ trunk/src/lib/datasrc/query.cc Wed Jun 30 23:47:29 2010
@@ -28,36 +28,37 @@
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),
+ q(qry), qname(n), qclass(qry.qclass()), qtype(t), section(sect),
+ op(AUTH_QUERY), state(GETANSWER), flags(0)
+{}
+
+QueryTask::QueryTask(const Query& qry, const isc::dns::Name& n,
+ const isc::dns::RRType& t, const isc::dns::Section& sect,
+ const Op o) :
+ 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 Op o) :
- qname(n), qclass(c), qtype(t), section(sect), op(o), state(GETANSWER),
- flags(0)
+ const State st) :
+ 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 State st) :
- qname(n), qclass(c), qtype(t), section(sect), op(AUTH_QUERY), state(st),
- flags(0)
+ const Op o, const State st) :
+ 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,
- 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)
-{}
-
-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 @@
}
// 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() {}
-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 @@
qtype_ = &question->getType();
restarts_ = 0;
- querytasks_.push(QueryTaskPtr(new QueryTask(*qname_, *qclass_, *qtype_,
+ querytasks_.push(QueryTaskPtr(new QueryTask(*this, *qname_, *qtype_,
Section::ANSWER())));
}
Modified: trunk/src/lib/datasrc/query.h
==============================================================================
--- trunk/src/lib/datasrc/query.h (original)
+++ trunk/src/lib/datasrc/query.h Wed Jun 30 23:47:29 2010
@@ -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,8 +30,10 @@
#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 {
@@ -41,6 +46,9 @@
// 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 @@
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 @@
// 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();
@@ -147,9 +154,6 @@
typedef boost::shared_ptr<QueryTask> QueryTaskPtr;
typedef std::queue<QueryTaskPtr> QueryTaskQueue;
-
-class Query;
-typedef boost::shared_ptr<Query> QueryPtr;
// Data Source query
class Query {
@@ -171,7 +175,7 @@
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 @@
// 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 @@
// 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_; }
-
- Status status() const { return status_; }
+ isc::dns::Message& message() { return (*message_); }
+ QueryTaskQueue& tasks() { return (querytasks_); }
+
+ 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 @@
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 @@
const isc::dns::RRClass* qclass_;
const isc::dns::RRType* qtype_;
+ HotCache* cache_;
+ DataSrc* datasrc_;
+
isc::dns::Message* message_;
QueryTaskQueue querytasks_;
Modified: trunk/src/lib/datasrc/sqlite3_datasrc.cc
==============================================================================
--- trunk/src/lib/datasrc/sqlite3_datasrc.cc (original)
+++ trunk/src/lib/datasrc/sqlite3_datasrc.cc Wed Jun 30 23:47:29 2010
@@ -344,19 +344,17 @@
}
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
Modified: trunk/src/lib/datasrc/sqlite3_datasrc.h
==============================================================================
--- trunk/src/lib/datasrc/sqlite3_datasrc.h (original)
+++ trunk/src/lib/datasrc/sqlite3_datasrc.h Wed Jun 30 23:47:29 2010
@@ -58,8 +58,7 @@
~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,
Modified: trunk/src/lib/datasrc/static_datasrc.cc
==============================================================================
--- trunk/src/lib/datasrc/static_datasrc.cc (original)
+++ trunk/src/lib/datasrc/static_datasrc.cc Wed Jun 30 23:47:29 2010
@@ -129,11 +129,10 @@
}
void
-StaticDataSrc::findClosestEnclosure(NameMatch& match,
- const RRClass& qclass) const {
- const Name& qname = match.qname();
-
- if (qclass != getClass() && qclass != RRClass::ANY()) {
+StaticDataSrc::findClosestEnclosure(DataSrcMatch& match) const {
+ const Name& qname = match.getName();
+
+ if (match.getClass() != getClass() && match.getClass() != RRClass::ANY()) {
return;
}
Modified: trunk/src/lib/datasrc/static_datasrc.h
==============================================================================
--- trunk/src/lib/datasrc/static_datasrc.h (original)
+++ trunk/src/lib/datasrc/static_datasrc.h Wed Jun 30 23:47:29 2010
@@ -39,8 +39,6 @@
namespace datasrc {
-class Query;
-class NameMatch;
struct StaticDataSrcImpl;
class StaticDataSrc : public DataSrc {
@@ -58,8 +56,7 @@
~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,
Modified: trunk/src/lib/datasrc/tests/Makefile.am
==============================================================================
--- trunk/src/lib/datasrc/tests/Makefile.am (original)
+++ trunk/src/lib/datasrc/tests/Makefile.am Wed Jun 30 23:47:29 2010
@@ -16,6 +16,7 @@
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)
Modified: trunk/src/lib/datasrc/tests/datasrc_unittest.cc
==============================================================================
--- trunk/src/lib/datasrc/tests/datasrc_unittest.cc (original)
+++ trunk/src/lib/datasrc/tests/datasrc_unittest.cc Wed Jun 30 23:47:29 2010
@@ -69,6 +69,7 @@
void createAndProcessQuery(const Name& qname, const RRClass& qclass,
const RRType& qtype);
+ HotCache cache;
MetaDataSrc meta_source;
OutputBuffer obuffer;
MessageRenderer renderer;
@@ -76,10 +77,10 @@
};
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 @@
msg.fromWire(buffer);
msg.makeResponse();
- performQuery(meta_source, msg);
+ performQuery(meta_source, cache, msg);
}
void
@@ -103,7 +104,7 @@
msg.setOpcode(Opcode::QUERY());
msg.addQuestion(Question(qname, qclass, qtype));
msg.setHeaderFlag(MessageFlag::RD());
- performQuery(meta_source, msg);
+ performQuery(meta_source, cache, msg);
}
void
@@ -211,6 +212,83 @@
EXPECT_EQ("dns03.example.com.", it->getCurrent().toText());
it->next();
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) {
@@ -858,6 +936,24 @@
EXPECT_EQ(0, ds.dataSrcCount());
}
+TEST_F(DataSrcTest, noNSZone) {
+ EXPECT_THROW(createAndProcessQuery(Name("www.nons.example"),
+ RRClass::IN(), RRType::A()),
+ DataSourceError);
+}
+
+TEST_F(DataSrcTest, noNSButDnameZone) {
+ EXPECT_THROW(createAndProcessQuery(Name("www.nons-dname.example"),
+ RRClass::IN(), RRType::A()),
+ DataSourceError);
+}
+
+TEST_F(DataSrcTest, noSOAZone) {
+ EXPECT_THROW(createAndProcessQuery(Name("notexist.nosoa.example"),
+ RRClass::IN(), RRType::A()),
+ DataSourceError);
+}
+
// currently fails
TEST_F(DataSrcTest, DISABLED_synthesizedCnameTooLong) {
// qname has the possible max length (255 octets). it matches a DNAME,
@@ -870,22 +966,102 @@
RRClass::IN(), RRType::A());
}
-TEST_F(DataSrcTest, noNSZone) {
- EXPECT_THROW(createAndProcessQuery(Name("www.nons.example"),
- RRClass::IN(), RRType::A()),
- DataSourceError);
-}
-
-TEST_F(DataSrcTest, noNSButDnameZone) {
- EXPECT_THROW(createAndProcessQuery(Name("www.nons-dname.example"),
- RRClass::IN(), RRType::A()),
- DataSourceError);
-}
-
-TEST_F(DataSrcTest, noSOAZone) {
- EXPECT_THROW(createAndProcessQuery(Name("notexist.nosoa.example"),
- RRClass::IN(), RRType::A()),
- DataSourceError);
-}
-
-}
+// 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());
+}
+
+}
Modified: trunk/src/lib/datasrc/tests/query_unittest.cc
==============================================================================
--- trunk/src/lib/datasrc/tests/query_unittest.cc (original)
+++ trunk/src/lib/datasrc/tests/query_unittest.cc Wed Jun 30 23:47:29 2010
@@ -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);
+
}
}
Modified: trunk/src/lib/datasrc/tests/sqlite3_unittest.cc
==============================================================================
--- trunk/src/lib/datasrc/tests/sqlite3_unittest.cc (original)
+++ trunk/src/lib/datasrc/tests/sqlite3_unittest.cc Wed Jun 30 23:47:29 2010
@@ -374,12 +374,10 @@
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, 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) {
Modified: trunk/src/lib/datasrc/tests/static_unittest.cc
==============================================================================
--- trunk/src/lib/datasrc/tests/static_unittest.cc (original)
+++ trunk/src/lib/datasrc/tests/static_unittest.cc Wed Jun 30 23:47:29 2010
@@ -196,55 +196,54 @@
}
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) {
Modified: trunk/src/lib/datasrc/tests/test_datasrc.cc
==============================================================================
--- trunk/src/lib/datasrc/tests/test_datasrc.cc (original)
+++ trunk/src/lib/datasrc/tests/test_datasrc.cc Wed Jun 30 23:47:29 2010
@@ -65,8 +65,9 @@
// {"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 @@
{"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 @@
rrset->addRdata(createRdata(rrtype, zone.rrclass, records[i].rdata));
if (rrtype == RRType::RRSIG()) {
prev_rrset->addRRsig(rrset);
- }
- prev_rrset = rrset;
+ } else {
+ prev_rrset = rrset;
+ }
}
}
@@ -361,12 +367,10 @@
}
void
-TestDataSrc::findClosestEnclosure(NameMatch& match,
- const RRClass& qclass) const
-{
- const Name& qname = match.qname();
-
- if (qclass != getClass() && qclass != RRClass::ANY()) {
+TestDataSrc::findClosestEnclosure(DataSrcMatch& match) const {
+ const Name& qname = match.getName();
+
+ if (match.getClass() != getClass() && match.getClass() != RRClass::ANY()) {
return;
}
Modified: trunk/src/lib/datasrc/tests/test_datasrc.h
==============================================================================
--- trunk/src/lib/datasrc/tests/test_datasrc.h (original)
+++ trunk/src/lib/datasrc/tests/test_datasrc.h Wed Jun 30 23:47:29 2010
@@ -48,8 +48,7 @@
~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,
Modified: trunk/src/lib/dns/question.h
==============================================================================
--- trunk/src/lib/dns/question.h (original)
+++ trunk/src/lib/dns/question.h Wed Jun 30 23:47:29 2010
@@ -229,6 +229,20 @@
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_;
Modified: trunk/src/lib/dns/rrsetlist.cc
==============================================================================
--- trunk/src/lib/dns/rrsetlist.cc (original)
+++ trunk/src/lib/dns/rrsetlist.cc Wed Jun 30 23:47:29 2010
@@ -40,6 +40,14 @@
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_) {
Modified: trunk/src/lib/dns/rrsetlist.h
==============================================================================
--- trunk/src/lib/dns/rrsetlist.h (original)
+++ trunk/src/lib/dns/rrsetlist.h Wed Jun 30 23:47:29 2010
@@ -82,6 +82,7 @@
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,
Modified: trunk/src/lib/dns/tests/question_unittest.cc
==============================================================================
--- trunk/src/lib/dns/tests/question_unittest.cc (original)
+++ trunk/src/lib/dns/tests/question_unittest.cc Wed Jun 30 23:47:29 2010
@@ -119,4 +119,35 @@
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));
}
+
+}
Modified: trunk/src/lib/dns/tests/rrsetlist_unittest.cc
==============================================================================
--- trunk/src/lib/dns/tests/rrsetlist_unittest.cc (original)
+++ trunk/src/lib/dns/tests/rrsetlist_unittest.cc Wed Jun 30 23:47:29 2010
@@ -48,6 +48,7 @@
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) {
@@ -84,6 +85,24 @@
RRsetList list;
setupList(list);
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) {
More information about the bind10-changes
mailing list