Browse Source

Committed each-ds branch to trunk (still needing formal review; see
ticket #50 for full details)


git-svn-id: svn://bind10.isc.org/svn/bind10/trunk@988 e5f2f494-b856-4b98-b285-d166d9295462

Evan Hunt 15 years ago
parent
commit
404aa0602f
69 changed files with 5148 additions and 363 deletions
  1. 2 0
      configure.ac
  2. 4 3
      src/bin/auth/Makefile.am
  3. 10 1
      src/bin/auth/auth_srv.cc
  4. 2 3
      src/bin/auth/auth_srv.h
  5. 1 1
      src/bin/bind10/bind10.py.in
  6. 17 0
      src/lib/auth/cpp/Makefile.am
  7. 3 5
      src/lib/auth/cpp/TODO
  8. 517 75
      src/lib/auth/cpp/data_source.cc
  9. 258 64
      src/lib/auth/cpp/data_source.h
  10. 626 0
      src/lib/auth/cpp/data_source_sqlite3.cc
  11. 118 0
      src/lib/auth/cpp/data_source_sqlite3.h
  12. 71 76
      src/lib/auth/cpp/data_source_static.cc
  13. 31 15
      src/lib/auth/cpp/data_source_static.h
  14. 596 0
      src/lib/auth/cpp/datasrc_unittest.cc
  15. 3 13
      src/lib/auth/cpp/query.cc
  16. 152 35
      src/lib/auth/cpp/query.h
  17. 24 0
      src/lib/auth/cpp/run_unittests.cc
  18. 4 0
      src/lib/auth/cpp/testdata/q_cname
  19. 4 0
      src/lib/auth/cpp/testdata/q_cname_ext
  20. 4 0
      src/lib/auth/cpp/testdata/q_cname_int
  21. 4 0
      src/lib/auth/cpp/testdata/q_dname
  22. 3 0
      src/lib/auth/cpp/testdata/q_example_ns
  23. 3 0
      src/lib/auth/cpp/testdata/q_example_ptr
  24. 4 0
      src/lib/auth/cpp/testdata/q_glork
  25. 3 0
      src/lib/auth/cpp/testdata/q_spork
  26. 4 0
      src/lib/auth/cpp/testdata/q_sql1
  27. 4 0
      src/lib/auth/cpp/testdata/q_subzone
  28. 4 0
      src/lib/auth/cpp/testdata/q_subzone_ds
  29. 4 0
      src/lib/auth/cpp/testdata/q_wild
  30. 4 0
      src/lib/auth/cpp/testdata/q_www
  31. 716 0
      src/lib/auth/cpp/unittest_ds.cc
  32. 137 0
      src/lib/auth/cpp/unittest_ds.h
  33. 127 0
      src/lib/auth/cpp/unittest_util.cc
  34. 87 0
      src/lib/auth/cpp/unittest_util.h
  35. 2 0
      src/lib/dns/cpp/Makefile.am
  36. 13 13
      src/lib/dns/cpp/base64.cc
  37. 3 2
      src/lib/dns/cpp/base64.h
  38. 110 0
      src/lib/dns/cpp/dnstime.cc
  39. 57 0
      src/lib/dns/cpp/dnstime.h
  40. 81 0
      src/lib/dns/cpp/hex.cc
  41. 53 0
      src/lib/dns/cpp/hex.h
  42. 13 1
      src/lib/dns/cpp/message.cc
  43. 7 1
      src/lib/dns/cpp/message.h
  44. 32 0
      src/lib/dns/cpp/name.cc
  45. 8 0
      src/lib/dns/cpp/name.h
  46. 82 0
      src/lib/dns/cpp/rdata/generic/dname_39.cc
  47. 49 0
      src/lib/dns/cpp/rdata/generic/dname_39.h
  48. 207 0
      src/lib/dns/cpp/rdata/generic/dnskey_48.cc
  49. 61 0
      src/lib/dns/cpp/rdata/generic/dnskey_48.h
  50. 177 0
      src/lib/dns/cpp/rdata/generic/ds_43.cc
  51. 58 0
      src/lib/dns/cpp/rdata/generic/ds_43.h
  52. 25 2
      src/lib/dns/cpp/rdata/generic/mx_15.cc
  53. 6 0
      src/lib/dns/cpp/rdata/generic/mx_15.h
  54. 197 0
      src/lib/dns/cpp/rdata/generic/nsec_47.cc
  55. 53 0
      src/lib/dns/cpp/rdata/generic/nsec_47.h
  56. 20 36
      src/lib/dns/cpp/rdata/generic/rrsig_46.cc
  57. 4 4
      src/lib/dns/cpp/rrclass-placeholder.h
  58. 9 0
      src/lib/dns/cpp/rrset.cc
  59. 69 7
      src/lib/dns/cpp/rrset.h
  60. 33 0
      src/lib/dns/cpp/rrtype-placeholder.h
  61. 5 0
      src/lib/dns/cpp/tests/Makefile.am
  62. 2 2
      src/lib/dns/cpp/tests/base64_unittest.cc
  63. 69 0
      src/lib/dns/cpp/tests/hex_unittest.cc
  64. 6 0
      src/lib/dns/cpp/tests/name_unittest.cc
  65. 6 4
      src/lib/dns/cpp/tests/rdata_rrsig_unittest.cc
  66. 44 0
      src/lib/dns/cpp/tests/testdata/rdata_dname_fromWire
  67. 24 0
      src/lib/dns/cpp/tests/testdata/rdata_dnskey_fromWire
  68. 6 0
      src/lib/dns/cpp/tests/testdata/rdata_ds_fromWire
  69. 6 0
      src/lib/dns/cpp/tests/testdata/rdata_nsec_fromWire

+ 2 - 0
configure.ac

@@ -139,6 +139,8 @@ AC_SUBST(GTEST_INCLUDES)
 AC_SUBST(GTEST_LDFLAGS)
 AC_SUBST(GTEST_LDADD)
 
+PKG_CHECK_MODULES(SQLITE, sqlite3)
+
 # Checks for library functions.
 
 AC_CONFIG_FILES([Makefile

+ 4 - 3
src/bin/auth/Makefile.am

@@ -1,4 +1,4 @@
-AM_CPPFLAGS = -I$(top_builddir)/include -I$(top_builddir)/src/lib -I$(top_builddir)/src/lib/dns/cpp -I$(top_builddir)/include/dns/cpp -I$(top_builddir)/include -I$(top_srcdir)/ext
+AM_CPPFLAGS = -I$(top_builddir)/include -I$(top_builddir)/src/lib -I$(top_builddir)/src/lib/dns/cpp -I$(top_builddir)/include/dns/cpp -I$(top_builddir)/include -I$(top_srcdir)/ext $(SQLITE_CFLAGS)
 
 pkglibexecdir = $(libexecdir)/@PACKAGE@
 
@@ -8,11 +8,12 @@ pkglibexec_PROGRAMS = b10-auth
 b10_auth_SOURCES = auth_srv.cc auth_srv.h
 b10_auth_SOURCES += common.cc common.h
 b10_auth_SOURCES += main.cc
-b10_auth_LDADD =  $(top_builddir)/src/lib/dns/cpp/.libs/libdns.a
-b10_auth_LDADD +=  $(top_builddir)/src/lib/auth/cpp/.libs/libauth.a
+b10_auth_LDADD =  $(top_builddir)/src/lib/auth/cpp/.libs/libauth.a
+b10_auth_LDADD +=  $(top_builddir)/src/lib/dns/cpp/.libs/libdns.a
 b10_auth_LDADD += $(top_builddir)/src/lib/config/cpp/libcfgclient.a
 b10_auth_LDADD += $(top_builddir)/src/lib/cc/cpp/libcc.a
 b10_auth_LDADD += $(top_builddir)/src/lib/exceptions/cpp/.libs/libexceptions.a
+b10_auth_LDADD += $(SQLITE_LIBS)
 
 # TODO: config.h.in is wrong because doesn't honor pkgdatadir
 # and can't use @datadir@ because doesn't expand default ${prefix}

+ 10 - 1
src/bin/auth/auth_srv.cc

@@ -44,6 +44,7 @@
 
 using namespace std;
 
+using namespace isc::auth;
 using namespace isc::dns;
 using namespace isc::dns::rdata;
 using namespace isc::data;
@@ -68,6 +69,14 @@ AuthSrv::AuthSrv(int port) {
         throw FatalError("could not bind socket");
 
     sock = s;
+
+    // add static data source
+    data_src.addDataSrc(new StaticDataSrc);
+
+    // add SQL data source
+    Sqlite3DataSrc* sd = new Sqlite3DataSrc;
+    sd->init();
+    data_src.addDataSrc(sd);
 }
 
 void
@@ -104,7 +113,7 @@ AuthSrv::processMessage() {
 
         // do the DataSource call here
         Query q = Query(msg, false);
-        data_src.runQuery(q);
+        data_src.doQuery(q);
 
         OutputBuffer obuffer(4096);
         MessageRenderer renderer(obuffer);

+ 2 - 3
src/bin/auth/auth_srv.h

@@ -19,11 +19,11 @@
 
 #include <cc/data.h>
 #include <auth/data_source_static.h>
+#include <auth/data_source_sqlite3.h>
 
 class AuthSrv {
 public:
     explicit AuthSrv(int port);
-    //~AuthSrv() {}
     int getSocket() { return (sock); }
     void processMessage();
     void serve(std::string zone_name);
@@ -31,8 +31,7 @@ public:
     isc::data::ElementPtr updateConfig(isc::data::ElementPtr config);
 private:
 
-    // TODO: make this a MetaDataSrc, but that one is abstract...
-    isc::dns::StaticDataSrc data_src;
+    isc::auth::MetaDataSrc data_src;
     int sock;
 };
 

+ 1 - 1
src/bin/bind10/bind10.py.in

@@ -50,7 +50,7 @@ import isc.cc
 import isc
 
 # This is the version that gets displayed to the user.
-__version__ = "v20091030 (Paving the DNS Parking Lot)"
+__version__ = "v20100225"
 
 # Nothing at all to do with the 1990-12-10 article here:
 # http://www.subgenius.com/subg-digest/v2/0056.html

+ 17 - 0
src/lib/auth/cpp/Makefile.am

@@ -5,4 +5,21 @@ CLEANFILES = *.gcno *.gcda
 lib_LTLIBRARIES = libauth.la
 libauth_la_SOURCES = data_source.h data_source.cc
 libauth_la_SOURCES += data_source_static.h data_source_static.cc
+libauth_la_SOURCES += data_source_sqlite3.h data_source_sqlite3.cc
 libauth_la_SOURCES += query.h query.cc
+
+TESTS =
+if HAVE_GTEST
+TESTS += run_unittests
+run_unittests_SOURCES = run_unittests.cc
+run_unittests_SOURCES += unittest_util.h unittest_util.cc
+run_unittests_SOURCES += unittest_ds.h unittest_ds.cc
+run_unittests_SOURCES += datasrc_unittest.cc
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+run_unittests_LDADD = $(GTEST_LDADD)
+run_unittests_LDADD += .libs/libauth.a
+run_unittests_LDADD += $(top_builddir)/src/lib/dns/cpp/.libs/libdns.a 
+run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/cpp/.libs/libexceptions.a
+endif
+
+noinst_PROGRAMS = $(TESTS)

+ 3 - 5
src/lib/auth/cpp/TODO

@@ -1,5 +1,3 @@
-- NXDOMAIN case must add NSEC/NSEC3 data when wantDnssec()
-- DataSrc should implement a method findAddrRRsets() which queries
-  for A and AAAA records.  at the high level this would be implemented
-  as two queries in serial; low level subclasses could override it with
-  a single query.
+- change filenames so we don't have everything starting with "data_source_"?
+- clean up SQL data source code
+- store rdata in the database as binary blobs instead of text

+ 517 - 75
src/lib/auth/cpp/data_source.cc

@@ -1,119 +1,561 @@
+#include <iostream>
+#include <vector>
+
 #include <dns/buffer.h>
+#include <dns/message.h>
 #include <dns/name.h>
+#include <dns/rdataclass.h>
 #include <dns/rrset.h>
-#include <dns/message.h>
+#include <dns/rrsetlist.h>
 
 #include <cc/data.h>
 
 #include "data_source.h"
 
+using namespace std;
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+
 namespace isc {
-namespace dns {
+namespace auth {
+
+// 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)
+static void
+getAdditional(Query& q, RRsetPtr rrset) {
+    if (!q.wantAdditional()) {
+        return;
+    }
+
+    RdataIteratorPtr it = rrset->getRdataIterator();
+    for (it->first(); !it->isLast(); it->next()) {
+        const Rdata& rd(it->getCurrent());
+        QueryTask* t = NULL;
+        if (rrset->getType() == RRType::NS()) {
+            const generic::NS& ns = dynamic_cast<const generic::NS&>(rd);
+
+            t = new QueryTask(ns.getNSName(), q.qclass(),
+                              Section::ADDITIONAL(),
+                              QueryTask::GLUE_QUERY,
+                              QueryTask::GETADDITIONAL); 
+        } else if (rrset->getType() == RRType::MX()) {
+            const generic::MX& mx = dynamic_cast<const generic::MX&>(rd);
+            t = new QueryTask(mx.getMXName(), q.qclass(),
+                              Section::ADDITIONAL(),
+                              QueryTask::NOGLUE_QUERY,
+                              QueryTask::GETADDITIONAL); 
+        }
+        if (t != NULL) {
+            q.tasks().push(*t);
+        }
+    }
+}
+
+// Synthesize a CNAME answer, for the benefit of clients that don't
+// understand DNAME
+static void
+synthesizeCname(Query& q, QueryTask& task, RRsetPtr rrset, RRsetList& target) {
+    RdataIteratorPtr it;
+    it = rrset->getRdataIterator();
+
+    // More than one DNAME RR in the RRset is illegal, so we only have
+    // to process the first one.
+    it->first();
+    if (it->isLast()) {
+        return;
+    }
+
+    const Rdata& rd(it->getCurrent());
+    const generic::DNAME& dname = dynamic_cast<const generic::DNAME&>(rd);
+    const Name& dname_target(dname.getDname());
+
+    try {
+        int qnlen = task.qname.getLabelCount();
+        int dnlen = rrset->getName().getLabelCount();
+        const Name& prefix(task.qname.split(0, qnlen - dnlen));
+        const Name& newname(prefix.concatenate(dname_target));
+        RRsetPtr cname(new RRset(task.qname, task.qclass, RRType::CNAME(),
+                                 rrset->getTTL()));
+        cname->addRdata(generic::CNAME(newname));
+        cname->setTTL(rrset->getTTL());
+        target.addRRset(cname);
+    } catch (...) {}
+}
+
+// Add a task to the query task queue to look up the data pointed
+// to by a CNAME record
+static void
+chaseCname(Query& q, QueryTask& task, RRsetPtr rrset) {
+    RdataIteratorPtr it;
+    it = rrset->getRdataIterator();
+
+    // More than one CNAME RR in the RRset is illegal, so we only have
+    // to process the first one.
+    it->first();
+    if (it->isLast()) {
+        return;
+    }
+
+    const Rdata& rd(it->getCurrent());
+    const generic::CNAME& cname = dynamic_cast<const generic::CNAME&>(rd);
+    const Name& target(cname.getCname());
+
+    QueryTask* t = new QueryTask(target, task.qclass, task.qtype,
+                                 Section::ANSWER(), QueryTask::FOLLOWCNAME);
+    q.tasks().push(*t);
+}
+
+// Perform the query specified in a QueryTask object
+DataSrc::Result
+doQueryTask(const DataSrc* ds, Query& q, QueryTask& task, RRsetList& target) {
+    switch (task.op) {
+    case QueryTask::AUTH_QUERY:
+        return (ds->findRRset(q, task.qname, task.qclass, task.qtype,
+                              target, task.flags, task.zone));
+
+    case QueryTask::SIMPLE_QUERY:
+        return (ds->findExactRRset(q, task.qname, task.qclass, task.qtype,
+                                   target, task.flags, task.zone));
+
+    case QueryTask::GLUE_QUERY:
+    case QueryTask::NOGLUE_QUERY:
+        return (ds->findAddrs(q, task.qname, task.qclass, target,
+                              task.flags, task.zone));
+
+    case QueryTask::REF_QUERY:
+        return (ds->findReferral(q, task.qname, task.qclass, target,
+                                 task.flags, task.zone));
+    }
 
-DSResult
-DataSrc::runQuery(Query q) {
-    DSResult result;
-    Name container(".");
+    // Not reached
+    return (DataSrc::ERROR);
+}
+
+// Copy referral information into the authority section of a message
+static inline void
+copyAuth(Query& q, const RRsetList& auth) {
     Message& m = q.message();
+    BOOST_FOREACH(RRsetPtr rrset, auth) {
+        if (rrset->getType() == RRType::DNAME()) {
+            continue;
+        }
+        m.addRRset(Section::AUTHORITY(), rrset, q.wantDnssec());
+        getAdditional(q, rrset);
+    }
+}
 
-    while (!q.tasks().empty()) {
-        RRsetList data, sigs;
+// Query for referrals (i.e., NS/DS or DNAME) at a given name
+static inline bool
+refQuery(const Name& name, Query& q, QueryTask& task,
+         const DataSrc* ds, RRsetList& target) {
+    QueryTask t(name, q.qclass(), QueryTask::REF_QUERY);
+    t.zone = task.zone;
+
+    DataSrc::Result result = doQueryTask(ds, q, t, target);
+
+    // Lookup failed
+    if (result != DataSrc::SUCCESS) {
+        return (false);
+    }
+    
+    // Referral bit is expected, so clear it when checking flags
+    if ((t.flags & ~DataSrc::REFERRAL) != 0) {
+        return (false);
+    }
+
+    return (true);
+}
+
+// Match downward, from the zone apex to the query name, looking for
+// referrals.
+static inline bool
+hasDelegation(const DataSrc* ds, Query& q, QueryTask& task) {
+    Message& m = q.message();
+    int nlen = task.qname.getLabelCount();
+    int diff = nlen - task.zone->getLabelCount();
+    if (diff > 1) {
         bool found = false;
-        QueryTaskPtr task = q.tasks().front();
+        RRsetList ref;
+        for(int i = diff; i > 1; i--) {
+            Name sub(task.qname.split(i - 1, nlen - i));
+            if (refQuery(sub, q, task, ds, ref)) {
+                found = true;
+                break;
+            }
+        }
+
+        // Found a referral while getting additional data
+        // for something other than NS; we skip it.
+        if (found && task.op == QueryTask::NOGLUE_QUERY) {
+            return (true);
+        }
+
+        // Found a referral while getting answer data;
+        // send a delegation.
+        if (found) {
+            if (RRsetPtr r = ref[RRType::DNAME()]) {
+                RRsetList syn;
+                m.addRRset(Section::ANSWER(), r, q.wantDnssec());
+                m.setHeaderFlag(MessageFlag::AA());
+                synthesizeCname(q, task, r, syn);
+                if (syn.size() == 1) {
+                    m.addRRset(Section::ANSWER(),
+                               syn[RRType::CNAME()],
+                               q.wantDnssec());
+                    chaseCname(q, task, syn[RRType::CNAME()]);
+                    return (true);
+                }
+            }
+
+            copyAuth(q, ref);
+            return (true);
+        }
+    }
+
+    // We appear to have authoritative data; set the header
+    // flag.  (We may clear it later if we find a referral
+    // at the actual qname node.)
+    if (task.op == QueryTask::AUTH_QUERY &&
+        task.state == QueryTask::GETANSWER) {
+        m.setHeaderFlag(MessageFlag::AA());
+    }
+
+    return (false);
+}
+
+// Attempt a wildcard lookup
+static inline DataSrc::Result
+tryWildcard(Query& q, QueryTask& task, const DataSrc* ds, bool& found) {
+    Message& m = q.message();
+    DataSrc::Result result;
+    found = false;
+
+    if ((task.flags & DataSrc::NAME_NOT_FOUND) == 0 || 
+        (task.state != QueryTask::GETANSWER &&
+         task.state != QueryTask::FOLLOWCNAME)) {
+        return (DataSrc::SUCCESS);
+    }
+
+    int nlen = task.qname.getLabelCount();
+    int diff = nlen - task.zone->getLabelCount();
+    if (diff < 1) {
+        return (DataSrc::SUCCESS);
+    }
+
+    RRsetList wild;
+    Name star("*");
+    uint32_t rflags = 0;
+
+    for(int i = 1; i <= diff; i++) {
+        const Name& wname(star.concatenate(task.qname.split(i, nlen - i)));
+        QueryTask t(wname, task.qclass, task.qtype,
+                    QueryTask::SIMPLE_QUERY); 
+        t.zone = task.zone;
+        result = doQueryTask(ds, q, t, wild);
+        if (result == DataSrc::SUCCESS &&
+            (t.flags == 0 || (t.flags & DataSrc::CNAME_FOUND))) {
+            rflags = t.flags;
+            found = true;
+            break;
+        }
+    }
+
+    // A wildcard was found.  Add the data to the answer
+    // section (but with the name changed to match the
+    // qname), and then continue as if this were a normal
+    // answer: if a CNAME, chase the target, otherwise
+    // add authority.
+    if (found) {
+        if (rflags & DataSrc::CNAME_FOUND) {
+            if (RRsetPtr rrset = wild[RRType::CNAME()]) {
+                rrset->setName(task.qname);
+                m.addRRset(Section::ANSWER(), rrset, q.wantDnssec());
+                chaseCname(q, task, rrset);
+            }
+        } else {
+            BOOST_FOREACH (RRsetPtr rrset, wild) {
+                rrset->setName(task.qname);
+                m.addRRset(Section::ANSWER(), rrset, q.wantDnssec());
+            }
+
+            RRsetList auth;
+            if (! refQuery(Name(*task.zone), q, task, ds, auth)) {
+                return (DataSrc::ERROR);
+            }
+
+            copyAuth(q, auth);
+        }
+    } else if (q.wantDnssec()) {
+        // No wildcard found; add an NSEC to prove it
+        RRsetList nsec;
+        QueryTask t = QueryTask(*task.zone, task.qclass, RRType::NSEC(),
+                                QueryTask::SIMPLE_QUERY); 
+        t.zone = task.zone;
+        result = doQueryTask(ds, q, t, nsec);
+        if (result != DataSrc::SUCCESS) {
+            return (DataSrc::ERROR);
+        }
+
+        if (t.flags == 0) {
+            m.addRRset(Section::AUTHORITY(), nsec[RRType::NSEC()], true);
+        }
+    }
+
+    return (DataSrc::SUCCESS);
+}
+
+//
+// doQuery: Processes a query.
+// 
+void
+DataSrc::doQuery(Query q) {
+    Result result;
+    Message& m = q.message();
+    vector<RRsetPtr> additional;
+
+    // XXX: this is for testing purposes; it should be done when 
+    // parsing the message for EDNS0 options
+    q.setWantDnssec(true);
+
+    m.clearHeaderFlag(MessageFlag::AA());
+    while (!q.tasks().empty()) {
+        RRsetList data;
+
+        QueryTask task = q.tasks().front();
         q.tasks().pop();
 
-        const DataSrc* ds = findClosestEnclosure(task->qname, container, found);
-
-        if (ds == NULL) {
-            result = ZONE_NOT_FOUND;
-        } else if (q.wantDnssec()) {
-            result = ds->findRRset(task->qname, task->qclass, task->qtype,
-                                   data, sigs);
-            // XXX validity check:
-            // for now, there must only be exactly one RRset in data
-            // and no more than one RRset in sigs.  the rrtype of data
-            // must match the sigtype of sigs, if any
+        // These task types should never be on the task queue.
+        if (task.op == QueryTask::SIMPLE_QUERY ||
+            task.op == QueryTask::REF_QUERY) {
+            m.setRcode(Rcode::SERVFAIL());
+            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.)
+        Name search(".");
+        if (task.qtype == RRType::DS()) {
+            search = task.qname.split(1, task.qname.getLabelCount() - 1);
+        } else {
+            search = task.qname;
+        }
+
+        NameMatch match(search);
+        findClosestEnclosure(match);
+        const DataSrc* ds = match.bestDataSrc();
+        const Name* zone = match.closestName();
+
+        if (ds) {
+            task.zone = new Name(*zone);
+
+            // 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(ds, q, task)) {
+                continue;
+            }
+
+            result = doQueryTask(ds, q, 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 the NS, DS or DNAME record.
+            if ((task.flags & REFERRAL) &&
+                (zone->getLabelCount() == task.qname.getLabelCount() ||
+                 task.qtype == RRType::NS() ||
+                 task.qtype == RRType::DS() ||
+                 task.qtype == RRType::DNAME())) {
+                task.flags &= ~REFERRAL;
+            }
         } else {
-            result = ds->findRRset(task->qname, task->qclass, task->qtype,
-                                   data);
+            task.flags = NO_SUCH_ZONE;
         }
 
-        switch (result) {
-            case SUCCESS:
-                // XXX: what if 'data' contains more than one RRset?
-                m.addRRset(task->section, data[0]);
-                if (q.wantDnssec() && sigs.size() == 1) {
-                    m.addRRset(task->section, sigs[0]);
+        if (result == SUCCESS && task.flags == 0) {
+            bool have_ns = false, need_auth = false;
+            switch (task.state) {
+            case QueryTask::GETANSWER:
+            case QueryTask::FOLLOWCNAME:
+                BOOST_FOREACH(RRsetPtr rrset, data) {
+                    m.addRRset(task.section, rrset, q.wantDnssec());
+                    if (q.tasks().empty()) {
+                        need_auth = true;
+                    }
+                    getAdditional(q, rrset);
+                    if (rrset->getType() == RRType::NS()) {
+                        have_ns = true;
+                    }
                 }
+                q.setStatus(Query::ANSWERED);
+                if (need_auth && !have_ns) {
+                    // Data found, no additional processing needed.
+                    // Add the NS records for the enclosing zone to
+                    // the authority section.
+                    RRsetList auth;
+                    if (! refQuery(Name(*zone), q, task, ds, auth)) {
+                        m.setRcode(Rcode::SERVFAIL());
+                        return;
+                    }
 
-                if (q.status() == QUERY_FINISHING) {
-                    q.setStatus(QUERY_DONE);
-                    return (SUCCESS);
+                    copyAuth(q, auth);
                 }
+                continue;
 
-                // if there are no more work items, add the authority section
-                if (q.tasks().empty() && q.status() == QUERY_INCOMPLETE) {
-                    QueryTask *qt = new QueryTask(container, task->qclass,
-                                                  RRType::NS(),
-                                                  Section::AUTHORITY());
-                    q.tasks().push(QueryTaskPtr(qt));
-                    q.setStatus(QUERY_FINISHING);
+            case QueryTask::GETADDITIONAL:
+                // Got additional data.  Do not add it to the message
+                // yet; instead store it and copy it in at the end
+                // (this allow RRSIGs to be omitted if necessary).
+                BOOST_FOREACH(RRsetPtr rrset, data) {
+                    if (q.status() == Query::ANSWERED &&
+                        rrset->getName() == q.qname() &&
+                        rrset->getType() == q.qtype()) {
+                        continue;
+                    }
+                    additional.push_back(rrset);
                 }
                 continue;
 
-            case CNAME:
-                m.addRRset(task->section, data[0]);
-                if (q.wantDnssec() && sigs.size() == 1) {
-                    m.addRRset(task->section, sigs[0]);
+            default:
+                dns_throw (Unexpected, "unexpected query state");
+            }
+        } else if (result == ERROR || result == NOT_IMPLEMENTED) {
+            m.setRcode(Rcode::SERVFAIL());
+            return;
+        } else if (task.flags & CNAME_FOUND) {
+            // The qname node contains a CNAME.  Add a new task to the
+            // queue to look up its target.
+            if (RRsetPtr rrset = data[RRType::CNAME()]) {
+                m.addRRset(task.section, rrset, q.wantDnssec());
+                chaseCname(q, task, rrset);
+            }
+            continue;
+        } else if (task.flags & REFERRAL) {
+            // The qname node contains an out-of-zone referral.
+            if (task.state == QueryTask::GETANSWER) {
+                RRsetList auth;
+                m.clearHeaderFlag(MessageFlag::AA());
+                if (! refQuery(task.qname, q, task, ds, auth)) {
+                    m.setRcode(Rcode::SERVFAIL());
+                    return;
                 }
+                BOOST_FOREACH (RRsetPtr rrset, auth) {
+                    if (rrset->getType() == RRType::DNAME()) {
+                        continue;
+                    }
+                    if (rrset->getType() == RRType::DS() &&
+                        task.qtype == RRType::DS()) {
+                        m.addRRset(Section::ANSWER(), rrset, q.wantDnssec());
+                    } else {
+                        m.addRRset(Section::AUTHORITY(), rrset, q.wantDnssec());
+                    }
+                    getAdditional(q, rrset);
+                }
+            } 
+            continue;
+        } else if (task.flags & NO_SUCH_ZONE) {
+            // No such zone.  If we're chasing cnames or adding additional
+            // data, that's okay, but if doing an original query, return
+            // REFUSED.
+            if (task.state == QueryTask::GETANSWER) {
+                m.setRcode(Rcode::REFUSED());
+                return;
+            }
+            continue;
+        } else if (task.flags & (NAME_NOT_FOUND|TYPE_NOT_FOUND)) {
+            // No data found at this qname/qtype.
+            // If we were looking for answer data, not additional,
+            // 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, ds, wildcard_found);
+            if (result != SUCCESS) {
+                m.setRcode(Rcode::SERVFAIL());
+                return;
+            }
 
-                // if (data[0].getType() == RRType::CNAME()) {
-                //     // take apart the CNAME rdata and re-query HERE
-                // }
+            if (wildcard_found) {
                 continue;
+            }
+
+            // If we've reached this point, there is definitely no answer.
+            // If we were chasing cnames or adding additional data, that's
+            // okay, but if we were doing an original query, reply with the
+            // SOA in the authority section.  For NAME_NOT_FOUND, set
+            // NXDOMAIN, and also add the previous NSEC to the authority
+            // section.  For TYPE_NOT_FOUND, do not set an error rcode,
+            // and send the current NSEC in the authority section.
+            Name nsecname(task.qname);
+            if (task.flags & NAME_NOT_FOUND) {
+                ds->findPreviousName(q, task.qname, nsecname, task.zone);
+            }
 
-            case NAME_NOT_FOUND:
-                q.setStatus(QUERY_NODATA);
-                if (q.wantDnssec()) {
-                    result = ds->findRRset(container, task->qclass,
-                                           RRType::SOA(), data, sigs);
-                } else {
-                    result = ds->findRRset(container, task->qclass, 
-                                           RRType::SOA(), data);
+            if (task.state == QueryTask::GETANSWER) {
+                if (task.flags & NAME_NOT_FOUND) {
+                    m.setRcode(Rcode::NXDOMAIN());
                 }
 
-                if (result != SUCCESS) {
+                RRsetList soa;
+                QueryTask t(Name(*zone), task.qclass, RRType::SOA(), 
+                            QueryTask::SIMPLE_QUERY); 
+                t.zone = task.zone;
+                result = doQueryTask(ds, q, t, soa);
+                if (result != SUCCESS || t.flags != 0) {
                     m.setRcode(Rcode::SERVFAIL());
-                    return (ERROR);
+                    return;
                 }
 
-                m.setRcode(Rcode::NXDOMAIN());
-                m.addRRset(Section::AUTHORITY(), data[0]);
-                if (q.wantDnssec() && sigs.size() == 1) {
-                    m.addRRset(Section::AUTHORITY(), sigs[0]);
-                }
-                break;
+                m.addRRset(Section::AUTHORITY(), soa[RRType::SOA()],
+                           q.wantDnssec());
+            }
 
-            case TYPE_NOT_FOUND:
-                m.setRcode(Rcode::NOERROR());
-                q.setStatus(QUERY_NODATA);
-                return (result);
+            if (q.wantDnssec()) {
+                RRsetList nsec;
+                QueryTask t = QueryTask(nsecname, task.qclass, RRType::NSEC(), 
+                                        QueryTask::SIMPLE_QUERY); 
+                t.zone = task.zone;
+                result = doQueryTask(ds, q, t, nsec);
+                if (result != SUCCESS) {
+                    m.setRcode(Rcode::SERVFAIL());
+                    return;
+                }
 
-            case ZONE_NOT_FOUND:
-                m.setRcode(Rcode::REFUSED());
-                q.setStatus(QUERY_NODATA);
-                return (result);
+                if (t.flags == 0) {
+                    m.addRRset(Section::AUTHORITY(), nsec[RRType::NSEC()],
+                               true);
+                }
+            }
 
-            default:
-                m.setRcode(Rcode::SERVFAIL());
-                q.setStatus(QUERY_NODATA);
-                return (result);
+            return;
+        } else {
+            // Should never be reached!
+            m.setRcode(Rcode::SERVFAIL());
+            return;
         }
     }
 
-    return (result);
-};
+    // We're done, so now copy in the additional data:
+    // data first, then signatures.  (If we run out of
+    // space, signatures in additional section are
+    // optional.)
+    BOOST_FOREACH(RRsetPtr rrset, additional) {
+        m.addRRset(Section::ADDITIONAL(), rrset, false);
+    }
 
+    if (q.wantDnssec()) {
+        BOOST_FOREACH(RRsetPtr rrset, additional) {
+            if (rrset->getRRsig()) {
+                m.addRRset(Section::ADDITIONAL(), rrset->getRRsig(), false);
+            }
+        }
+    }
+}
 
 }
 }

+ 258 - 64
src/lib/auth/cpp/data_source.h

@@ -20,57 +20,101 @@
 #include <boost/foreach.hpp>
 #include <dns/name.h>
 #include <dns/rrset.h>
+#include <dns/rrsetlist.h>
 #include <auth/query.h>
+#include <iostream>
+
+using namespace isc::dns;
 
 namespace isc {
-namespace dns {
-
-enum DSResult {
-    SUCCESS,
-    NOT_IMPLEMENTED,
-    ERROR,
-    CNAME,
-    ZONE_NOT_FOUND,
-    NAME_NOT_FOUND,
-    TYPE_NOT_FOUND
-};
+namespace auth {
 
 class DataSrc;
-
-typedef std::vector<RRsetPtr> RRsetList;
+class NameMatch;
 
 class AbstractDataSrc {
 public:
+    enum Result {
+        SUCCESS,
+        ERROR,
+        NOT_IMPLEMENTED
+    };
+
+    // These flags indicate conditions encountered while processing a query.
+    //
+    // REFERRAL:       The node contains an NS record
+    // CNAME_FOUND:    The node contains a CNAME record
+    // 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.
+    enum QueryResponseFlags {
+        REFERRAL = 0x01,
+        CNAME_FOUND = 0x02,
+        NAME_NOT_FOUND = 0x04,
+        TYPE_NOT_FOUND = 0x08,
+        NO_SUCH_ZONE = 0x10
+    };
+
     virtual ~AbstractDataSrc() {};
 
     // 'High-level' methods.  These will be implemented by the
-    // general DataSrc class, but MAY be overwritten by subclasses.
-    virtual DSResult runQuery(Query query) = 0;
-
-    // Mandatory 'low-level' methods: These will NOT be implemented by
-    // the general DataSrc class; subclasses MUST implement them.
-    virtual DSResult findRRset(const Name& qname,
-                               const RRClass& qclass,
-                               const RRType& qtype,
-                               RRsetList& target,
-                               RRsetList& sigs) const = 0;
+    // general DataSrc class, and SHOULD NOT be overwritten by subclasses.
+    virtual void doQuery(Query query) = 0;
 
-    virtual DSResult findRRset(const Name& qname,
-                               const RRClass& qclass,
-                               const RRType& qtype,
-                               RRsetList& target) const = 0;
+    // XXX: High-level methods to be implemented later:
+    // virtual void doUpdate(Update update) = 0;
+    // virtual void doXfr(Query query) = 0;
 
-    virtual const DataSrc* findClosestEnclosure(const Name& qname,
-                                                Name& container,
-                                                bool& found) const = 0;
+    // 'Medium-level' methods.  This will be implemented by the general
+    // DataSrc class but MAY be overwritten by subclasses.
+    virtual void findClosestEnclosure(NameMatch& match) const = 0;
 
     // Optional 'low-level' methods.  These will have stub implementations
     // in the general DataSrc class but MAY be overwritten by subclasses
-    virtual DSResult init() = 0;
-    virtual DSResult close() = 0;
+    virtual Result init() = 0;
+    virtual Result close() = 0;
+
+    // Mandatory 'low-level' methods: These will NOT be implemented by
+    // the general DataSrc class; subclasses MUST implement them.
+    virtual Result findRRset(const Query& q,
+                             const Name& qname,
+                             const RRClass& qclass,
+                             const RRType& qtype,
+                             RRsetList& target,
+                             uint32_t& flags,
+                             Name* zone = NULL) const = 0;
+
+    virtual Result findExactRRset(const Query& q,
+                                  const Name& qname,
+                                  const RRClass& qclass,
+                                  const RRType& qtype,
+                                  RRsetList& target,
+                                  uint32_t& flags,
+                                  Name* zone = NULL) const = 0;
+
+    // These will have dumb implementations in the general DataSrc
+    // class, and SHOULD be overwritten by subclasses.
+    virtual Result findAddrs(const Query& q,
+                             const Name& qname,
+                             const RRClass& qclass,
+                             RRsetList& target,
+                             uint32_t& flags,
+                             Name* zone = NULL) const = 0;
+
+     virtual Result findReferral(const Query& q,
+                                const Name& qname,
+                                const RRClass& qclass,
+                                RRsetList& target,
+                                uint32_t& flags,
+                                Name* zone = NULL) const = 0;
+
+    // This MUST be implemented by concrete data sources which support
+    // DNSSEC, but is optional for others (e.g., the static data source).
+    virtual Result findPreviousName(const Query& q,
+                                    const Name& qname,
+                                    Name& target,
+                                    Name* zone) const = 0;
 
-    //virtual const RRClass& getClass() const = 0;
-    //virtual const RRClass& setClass() const = 0;
 };
 
 // Base class for a DNS Data Source
@@ -80,29 +124,111 @@ public:
     DataSrc(const RRClass& c) : rrclass(c) {}
     virtual ~DataSrc() {};
 
-    DSResult runQuery(Query q);
+    void doQuery(Query q);
+
+    virtual void findClosestEnclosure(NameMatch& match) const = 0;
 
-    virtual DSResult findRRset(const Name& qname,
+    const RRClass& getClass() const { return rrclass; }
+    void setClass(RRClass& c) { rrclass = c; }
+    void setClass(const RRClass& c) { rrclass = c; }
+
+    Result init() { return NOT_IMPLEMENTED; }
+    Result close() { return NOT_IMPLEMENTED; }
+
+    virtual Result findRRset(const Query& q,
+                             const Name& qname,
+                             const RRClass& qclass,
+                             const RRType& qtype,
+                             RRsetList& target,
+                             uint32_t& flags,
+                             Name* zone = NULL) const = 0;
+
+    virtual Result findExactRRset(const Query& q,
+                                  const Name& qname,
+                                  const RRClass& qclass,
+                                  const RRType& qtype,
+                                  RRsetList& target,
+                                  uint32_t& flags,
+                                  Name* zone = NULL) const = 0;
+
+    virtual Result findAddrs(const Query& q,
+                               const Name& qname,
                                const RRClass& qclass,
-                               const RRType& qtype,
                                RRsetList& target,
-                               RRsetList& sigs) const = 0;
+                               uint32_t& flags,
+                               Name* zone = NULL) const {
+        Result r;
+        bool a = false, aaaa = false;
+
+        flags = 0;
+        r = findExactRRset(q, qname, qclass, RRType::A(), target, flags, zone);
+        if (r == SUCCESS && flags == 0) {
+            a = true;
+        }
 
-    virtual DSResult findRRset(const Name& qname,
-                               const RRClass& qclass,
-                               const RRType& qtype,
-                               RRsetList& target) const = 0;
+        flags = 0;
+        r = findExactRRset(q, qname, qclass, RRType::AAAA(), target,
+                           flags, zone);
+        if (r == SUCCESS && flags == 0) {
+            aaaa = true;
+        }
 
-    virtual const DataSrc* findClosestEnclosure(const Name& qname,
-                                                Name& container,
-                                                bool& found) const = 0;
+        if (!a && !aaaa) {
+            flags = TYPE_NOT_FOUND;
+        } else {
+            flags = 0;
+        }
 
-    const RRClass& getClass() const { return rrclass; }
-    void setClass(RRClass& c) { rrclass = c; }
+        return (SUCCESS);
+    }
+
+    virtual Result findReferral(const Query& q,
+                                const Name& qname,
+                                const RRClass& qclass,
+                                RRsetList& target,
+                                uint32_t& flags,
+                                Name* zone = NULL) const {
+        Result r;
+        bool ns = false, ds = false, dname = false;
+
+        flags = 0;
+        r = findExactRRset(q, qname, qclass, RRType::NS(), target, flags, zone);
+        if (r == SUCCESS && flags == 0) {
+            ns = true;
+        } else if ((flags & (NO_SUCH_ZONE|NAME_NOT_FOUND))) {
+            return (SUCCESS);
+        }
+
+        flags = 0;
+        r = findExactRRset(q, qname, qclass, RRType::DS(), target, flags, zone);
+        if (r == SUCCESS && flags == 0) {
+            ds = true;
+        } else if ((flags & (NO_SUCH_ZONE|NAME_NOT_FOUND))) {
+            return (SUCCESS);
+        }
+
+        flags = 0;
+        r = findExactRRset(q, qname, qclass, RRType::DNAME(), target,
+                           flags, zone);
+        if (r == SUCCESS && flags == 0) {
+            dname = true;
+        } else if ((flags & (NO_SUCH_ZONE|NAME_NOT_FOUND))) {
+            return (SUCCESS);
+        }
 
-    DSResult init() { return NOT_IMPLEMENTED; }
-    DSResult close() { return NOT_IMPLEMENTED; }
+        if (!ns && !dname && !ds) {
+            flags = TYPE_NOT_FOUND;
+        } else {
+            flags = 0;
+        }
+
+        return (SUCCESS);
+    }
 
+    virtual Result findPreviousName(const Query& q,
+                                    const Name& qname,
+                                    Name& target,
+                                    Name* zone) const = 0;
 private:
     RRClass rrclass;
 };
@@ -119,33 +245,101 @@ public:
         }
 
         data_sources.push_back(ds);
-    };
+    }
 
-    const DataSrc* findClosestEnclosure(const Name& qname,
-                                        Name& container,
-                                        bool& found) const
-    {
-        const DataSrc* best = NULL;
+    void findClosestEnclosure(NameMatch& match) const {
         BOOST_FOREACH (DataSrc* ds, data_sources) {
-            const DataSrc* source;
-
             if (getClass() != RRClass::ANY() && ds->getClass() != getClass()) {
                 continue;
             }
 
-            source = ds->findClosestEnclosure(qname, container, found);
-            if (source != NULL) {
-                best = source;
-            }
+            ds->findClosestEnclosure(match);
         }
-
-        return (best);
-    };
+    }
+
+    // Actual queries for data should not be sent to a MetaDataSrc object,
+    // so we return NOT_IMPLEMENTED if we receive any.
+    //
+    // The proper way to use the MetaDataSrc is to run findClosestEnclosure()
+    // to get a pointer to the best concrete data source for the specified
+    // zone, then send all queries directly to that data source.
+
+    Result findRRset(const Query& q, const Name& qname,
+                     const RRClass& qclass, const RRType& qtype,
+                     RRsetList& target, uint32_t& flags,
+                     Name* zone = NULL) const {
+        return (NOT_IMPLEMENTED);
+    }
+
+    Result findExactRRset(const Query& q, const Name& qname,
+                          const RRClass& qclass, const RRType& qtype,
+                          RRsetList& target, uint32_t& flags,
+                          Name* zone = NULL) const {
+        return (NOT_IMPLEMENTED);
+    }
+
+    Result findAddrs(const Query& q,
+                     const Name& qname, const RRClass& qclass,
+                     RRsetList& target, uint32_t& flags,
+                     Name* zone = NULL) const {
+        return (NOT_IMPLEMENTED);
+    }
+
+    Result findReferral(const Query& q,
+                        const Name& qname, const RRClass& qclass,
+                        RRsetList& target, uint32_t& flags,
+                        Name* zone = NULL) const {
+        return (NOT_IMPLEMENTED);
+    }
+
+    virtual Result findPreviousName(const Query& q,
+                                    const Name& qname,
+                                    Name& target,
+                                    Name* zone) const {
+        return (NOT_IMPLEMENTED);
+    }
 
 private:
     std::vector<DataSrc*> data_sources;
 };
 
+class NameMatch {
+public:
+    NameMatch(const Name& qname) :
+        closest_name_(NULL), best_source_(NULL), qname_(qname) {}
+
+    ~NameMatch() {
+        delete closest_name_;
+    }
+
+    void update(const DataSrc& new_source, const Name& container) {
+        if (closest_name_ == NULL) {
+            closest_name_ = new Name(container);
+            best_source_ = &new_source;
+            return;
+        }
+
+        NameComparisonResult::NameRelation cmp = 
+            container.compare(*closest_name_).getRelation();
+
+        if (cmp == NameComparisonResult::SUBDOMAIN) {
+            Name* newname = new Name(container);
+            delete closest_name_;
+            closest_name_ = newname;
+            best_source_ = &new_source;
+        }
+    }
+
+    const Name& qname() { return (qname_); }
+    const Name* closestName() { return (closest_name_); }
+    const DataSrc* bestDataSrc() { return (best_source_); }
+
+private:
+    const Name* closest_name_;
+    const DataSrc* best_source_;
+    const Name& qname_;
+};
+
 }
 }
 

+ 626 - 0
src/lib/auth/cpp/data_source_sqlite3.cc

@@ -0,0 +1,626 @@
+
+#include "data_source_sqlite3.h"
+#include <dns/rrttl.h>
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+#include <dns/rrset.h>
+#include <dns/rrsetlist.h>
+
+#include <iostream>
+
+using namespace std;
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+
+namespace isc {
+namespace auth {
+
+//
+//  Prepare a statement.  Can call release() or sqlite3_finalize()
+//  directly.
+//
+sqlite3_stmt* Sqlite3DataSrc::prepare(const char *statement) {
+    int rc;
+    sqlite3_stmt *prepared = NULL;
+
+    rc = sqlite3_prepare_v2(db, statement, -1, &prepared, NULL);
+    if (rc != SQLITE_OK) {
+        throw("could not prepare");
+    }
+    return (prepared);
+}
+
+//
+//  Release memory associated with a prepared query.
+//
+void Sqlite3DataSrc::release(sqlite3_stmt* prepared) {
+    sqlite3_finalize(prepared);
+}
+
+//
+//  Get the database schema version.
+//
+int Sqlite3DataSrc::getVersion(void) {
+    if (database_version == -1) {
+        loadVersion();
+    }
+    return (database_version);
+}
+
+//
+//  Find the exact zone match.  Return -1 if not found, or the zone's
+//  ID if found.  This will always be >= 0 if found.
+//
+int Sqlite3DataSrc::hasExactZone(const char *name) const {
+    int rc, i;
+    sqlite3_reset(q_zone);
+    rc = sqlite3_bind_text(q_zone, 1, name, -1, SQLITE_STATIC);
+    if (rc != SQLITE_OK) {
+        throw("Could not bind");
+    }
+    rc = sqlite3_step(q_zone);
+    if (rc == SQLITE_ROW) {
+        i = sqlite3_column_int(q_zone, 0);
+    } else {
+        i = -1;
+    }
+    sqlite3_reset(q_zone);
+    return (i);
+}
+
+int
+Sqlite3DataSrc::
+findRecords(const Name& name, const RRType& rdtype, RRsetList& target,
+            Name* zone, const Mode mode, uint32_t& flags) const
+{
+    int rc;
+    const string s_name = name.toText();
+    const char *c_name = s_name.c_str();
+    const string s_rdtype = rdtype.toText();
+    const char *c_rdtype = s_rdtype.c_str();
+    sqlite3_stmt *query;
+
+    switch (mode) {
+    case ADDRESS:
+        query = q_addrs;
+        break;
+    case DELEGATION:
+        query = q_referral;
+        break;
+    default:
+        if (rdtype == RRType::ANY()) {
+            query = q_any;
+        } else {
+            query = q_record;
+        }
+        break;
+    }
+
+    flags = 0;
+
+    int zone_id;
+    if (zone == NULL) {
+        zone_id = findClosest(c_name, NULL);
+    } else {
+        const string s_zone = zone->toText();
+        const char *c_zone = s_zone.c_str();
+        zone_id = findClosest(c_zone, NULL);
+    }
+
+    if (zone_id < 0) {
+        flags = NO_SUCH_ZONE;
+        return (0);
+    }
+    
+    sqlite3_reset(query);
+    sqlite3_clear_bindings(query);
+
+    rc = sqlite3_bind_int(query, 1, zone_id);
+    if (rc != SQLITE_OK) {
+        throw("Could not bind 1 (record)");
+    }
+    rc = sqlite3_bind_text(query, 2, c_name, -1, SQLITE_STATIC);
+    if (rc != SQLITE_OK) {
+        throw("Could not bind 2 (record)");
+    }
+
+    if (query == q_record) {
+        rc = sqlite3_bind_text(query, 3, c_rdtype, -1, SQLITE_STATIC);
+        if (rc != SQLITE_OK) {
+            throw("Could not bind 3 (record)");
+        }
+    }
+
+    // loop
+    int target_ttl = -1;
+    int sig_ttl = -1;
+    int rows = 0;
+    RRsetPtr rrset;
+    bool any = (rdtype == RRType::ANY());
+
+    rc = sqlite3_step(query);
+    while (rc == SQLITE_ROW) {
+        const char *type = (const char *)sqlite3_column_text(query, 0);
+        int ttl = sqlite3_column_int(query, 1);
+        const char *sigtype = (const char *)sqlite3_column_text(query, 2);
+        const char *rdata = (const char *)sqlite3_column_text(query, 3);
+
+        RRType rt(sigtype ? sigtype : type);
+
+        // looking for something else but found NS; we need to inform
+        // the caller that this is a referral, but we do not return the
+        // NS RRset to the caller.
+        if (rdtype != RRType::NS() && !any && rt == RRType::NS()) {
+            flags |= REFERRAL;
+            rc = sqlite3_step(query);
+            continue;
+        }
+
+        rows++;
+
+        // Looking for something else but found CNAME
+        if (rt == RRType::CNAME() && rdtype != RRType::CNAME()) {
+            if (rdtype == RRType::NSEC()) {
+                // NSEC query, just skip the CNAME
+                rc = sqlite3_step(query);
+                continue;
+            } else if (!any) {
+                // include the CNAME, but don't flag it for chasing if
+                // this is an ANY query
+                flags |= CNAME_FOUND;
+            }
+        }
+
+        if (!target[rt]) {
+            rrset = RRsetPtr(new RRset(name, RRClass::IN(), rt, RRTTL(3600)));
+            target.addRRset(rrset);
+        }
+
+        if (!sigtype && RRType(type) == rrset->getType()) {
+            RdataPtr item = createRdata(RRType(type), RRClass("IN"), rdata);
+            rrset->addRdata(item);
+
+            if (target_ttl == -1 || target_ttl > ttl) {
+                target_ttl = ttl;
+            }
+            rrset->setTTL(RRTTL(target_ttl));
+        } else if (sigtype && RRType(sigtype) == rrset->getType()) {
+            RdataPtr rrsig = createRdata(RRType::RRSIG(), RRClass::IN(), rdata);
+            if (rrset->getRRsig()) {
+                rrset->getRRsig()->addRdata(rrsig);
+            } else {
+                RRsetPtr sigs = RRsetPtr(new RRset(name, RRClass::IN(),
+                                                   RRType::RRSIG(),
+                                                   RRTTL(3600)));
+                sigs->addRdata(rrsig);
+                rrset->addRRsig(sigs);
+            }
+
+            if (sig_ttl == -1 || sig_ttl > ttl) {
+                sig_ttl = ttl;
+            }
+            rrset->getRRsig()->setTTL(RRTTL(sig_ttl));
+        }
+        
+        rc = sqlite3_step(query);
+    }
+
+    sqlite3_reset(query);
+    if (rows > 0) {
+        return (rows);
+    }
+
+    //
+    // No rows were found.  We need to find out whether there are
+    // any RRs with that name to determine whether this is NXDOMAIN or
+    // NXRRSET
+    //
+    sqlite3_reset(q_count);
+    sqlite3_clear_bindings(q_count);
+
+    rc = sqlite3_bind_int(q_count, 1, zone_id);
+    if (rc != SQLITE_OK) {
+        throw("Could not bind 1 (count)");
+    }
+
+    rc = sqlite3_bind_text(q_count, 2, c_name, -1, SQLITE_STATIC);
+    if (rc != SQLITE_OK) {
+        throw("Could not bind 2 (count)");
+    }
+
+    rc = sqlite3_step(q_count);
+    if(rc == SQLITE_ROW) {
+        int count = sqlite3_column_int(q_count, 0);
+        if (count != 0) {
+            flags |= TYPE_NOT_FOUND;
+            sqlite3_reset(q_count);
+            return (0);
+        }
+    }
+
+    flags |= NAME_NOT_FOUND;
+    sqlite3_reset(q_count);
+    return (0);
+}
+
+//
+//  Search for the closest enclosing zone.  Will return -1 if not found,
+//  >= 0 if found.  If position is not NULL, it will be filled in with the
+//  longest match found.
+//
+int Sqlite3DataSrc::findClosest(const char *name, const char **position) const {
+    int rc;
+    const char *current = name;
+    
+    while (*current != 0) {
+        rc = hasExactZone(current);
+        if (rc >= 0) {
+            if (position != NULL) {
+                *position = current;
+            }
+            return (rc);
+        }
+        while (*current != '.' && *current != 0) {
+            current++;
+        }
+        if (*current == '.') {
+            current++;
+        }
+    }
+
+    return (-1);
+}
+
+void Sqlite3DataSrc::loadVersion(void) {
+    int rc;
+
+    const char *q = "SELECT version FROM schema_version";
+    sqlite3_stmt *prepared = prepare(q);
+    rc = sqlite3_step(prepared);
+    if (rc != SQLITE_ROW) {
+        throw("failed to find a row in schema_version table");
+    }
+    database_version = sqlite3_column_int(prepared, 0);
+    release(prepared);
+}
+
+void Sqlite3DataSrc::setupPreparedStatements(void) {
+
+    const char *q_zone_str = "SELECT id FROM zones WHERE name=?1";
+    try {
+        q_zone = prepare(q_zone_str);
+    } catch (const char *e) {
+        cout << e << endl << q_zone_str << endl;
+        cout << sqlite3_errmsg(db) << endl;
+        throw(e);
+    }
+
+    const char *q_record_str = "SELECT rdtype, ttl, sigtype, rdata "
+                               "FROM records WHERE zone_id=?1 AND name=?2 AND "
+                               "((rdtype=?3 OR sigtype=?3) OR "
+                               "(rdtype='CNAME' OR sigtype='CNAME') OR "
+                               "(rdtype='NS' OR sigtype='NS'))";
+    try {
+        q_record = prepare(q_record_str);
+    } catch (const char *e) {
+        cout << e << endl << q_record_str << endl;
+        cout << sqlite3_errmsg(db) << endl;
+        throw(e);
+    }
+
+    const char *q_addrs_str = "SELECT rdtype, ttl, sigtype, rdata "
+                               "FROM records WHERE zone_id=?1 AND name=?2 AND "
+                               "(rdtype='A' OR sigtype='A' OR "
+                               "rdtype='AAAA' OR sigtype='AAAA')";
+    try {
+        q_addrs = prepare(q_addrs_str);
+    } catch (const char *e) {
+        cout << e << endl << q_addrs_str << endl;
+        cout << sqlite3_errmsg(db) << endl;
+        throw(e);
+    }
+    const char *q_referral_str = "SELECT rdtype, ttl, sigtype, rdata FROM "
+                                 "records WHERE zone_id=?1 AND name=?2 AND"
+                                 "(rdtype='NS' OR sigtype='NS' OR "
+                                 "rdtype='DS' OR sigtype='DS' OR "
+                                 "rdtype='DNAME' OR sigtype='DNAME')";
+    try {
+        q_referral = prepare(q_referral_str);
+    } catch (const char *e) {
+        cout << e << endl << q_referral_str << endl;
+        cout << sqlite3_errmsg(db) << endl;
+        throw(e);
+    }
+    const char *q_any_str = "SELECT rdtype, ttl, sigtype, rdata "
+                             "FROM records WHERE zone_id=?1 AND name=?2";
+    try {
+        q_any = prepare(q_any_str);
+    } catch (const char *e) {
+        cout << e << endl << q_any_str << endl;
+        cout << sqlite3_errmsg(db) << endl;
+        throw(e);
+    }
+
+    const char *q_count_str = "SELECT COUNT(*) FROM records "
+                              "WHERE zone_id=?1 AND (name=?2 OR "
+                              "name LIKE '%.' || ?2);";
+    try {
+        q_count = prepare(q_count_str);
+    } catch (const char *e) {
+        cout << e << endl << q_count_str << endl;
+        cout << sqlite3_errmsg(db) << endl;
+        throw(e);
+    }
+
+    const char *q_previous_str = "SELECT name FROM records "
+                                 "WHERE zone_id=?1 AND rdtype = 'NSEC' AND "
+                                 "rname < $2 ORDER BY rname DESC LIMIT 1";
+    try {
+        q_previous = prepare(q_previous_str);
+    } catch (const char *e) {
+        cout << e << endl << q_previous_str << endl;
+        cout << sqlite3_errmsg(db) << endl;
+        throw(e);
+    }
+
+}
+
+void Sqlite3DataSrc::execSetupQuery(const char *query) {
+    int rc;
+
+    rc = sqlite3_exec(db, query, NULL, NULL, NULL);
+    if (rc != SQLITE_OK) {
+        throw(query);
+    }
+}
+
+void Sqlite3DataSrc::checkAndSetupSchema(void) {
+    try {
+        loadVersion();
+        setupPreparedStatements();
+        cout << "Loaded existing schema" << endl;
+    } catch(...) {
+        execSetupQuery("CREATE TABLE schema_version ("
+                          "version INTEGER NOT NULL)");
+        execSetupQuery("INSERT INTO schema_version VALUES (1)");
+        execSetupQuery("CREATE TABLE zones ("
+                          "id INTEGER PRIMARY KEY, "
+                          "name STRING NOT NULL, "
+                          "rdclass STRING NOT NULL DEFAULT 'IN', "
+                          "dnssec BOOLEAN NOT NULL DEFAULT 0)");
+        execSetupQuery("CREATE TABLE records ("
+                          "id INTEGER PRIMARY KEY, "
+                          "zone_id INTEGER NOT NULL, "
+                          "name STRING NOT NULL, "
+                          "rname STRING NOT NULL, "
+                          "ttl INTEGER NOT NULL, "
+                          "rdtype STRING NOT NULL, "
+                          "sigtype STRING, "
+                          "rdata STRING NOT NULL)");
+        execSetupQuery("CREATE INDEX records_byname ON records (name)");
+        execSetupQuery("CREATE INDEX records_byrname ON records (rname)");
+        execSetupQuery("CREATE INDEX zones_byname ON zones (name)");
+
+        setupPreparedStatements();
+        cout << "Created new file and schema" << endl;
+    }
+}
+
+Sqlite3DataSrc::Sqlite3DataSrc()
+{
+    db = NULL;
+    database_version = -1;
+    q_zone = NULL;
+    q_record = NULL;
+    q_addrs = NULL;
+    q_referral = NULL;
+    q_any = NULL;
+    q_count = NULL;
+    q_previous = NULL;
+}
+
+Sqlite3DataSrc::~Sqlite3DataSrc()
+{
+    close();
+}
+
+DataSrc::Result
+Sqlite3DataSrc::init()
+{
+    try {
+        open("/tmp/zone.sqlite3");
+    
+        cout << "Schema version: " << getVersion() << endl;
+    } catch (const char *e) {
+        cout << e << endl;
+    }
+
+    return (SUCCESS);
+}
+
+void
+Sqlite3DataSrc::findClosestEnclosure(NameMatch& match) const {
+    const Name& qname = match.qname();
+    const string target_string = qname.toText();
+    const char *position = NULL;
+    
+    int ret = findClosest(target_string.c_str(), &position);
+    if (ret == -1) {
+        return;
+    }
+
+    match.update(*this, Name(position));
+}
+
+DataSrc::Result
+Sqlite3DataSrc::findPreviousName(const Query& q,
+                                 const Name& qname,
+                                 Name& target,
+                                 Name* zone) const
+{
+    const char *c_rname = qname.reverse().toText().c_str();
+
+    int zone_id;
+    if (zone == NULL) {
+        const char *c_name = qname.toText().c_str();
+        zone_id = findClosest(c_name, NULL);
+    } else {
+        const char *c_zone = zone->toText().c_str();
+        zone_id = findClosest(c_zone, NULL);
+    }
+
+    if (zone_id < 0) {
+        return (ERROR);
+    }
+    
+    sqlite3_reset(q_previous);
+    sqlite3_clear_bindings(q_previous);
+
+    int rc = sqlite3_bind_int(q_previous, 1, zone_id);
+    if (rc != SQLITE_OK) {
+        throw ("Could not bind 1 (record)");
+    }
+    rc = sqlite3_bind_text(q_previous, 2, c_rname, -1, SQLITE_STATIC);
+    if (rc != SQLITE_OK) {
+        throw ("Could not bind 2 (record)");
+    }
+
+    rc = sqlite3_step(q_previous);
+    if (rc != SQLITE_ROW) {
+        sqlite3_reset(q_previous);
+        return (ERROR);
+    }
+
+    const char *prev = (const char *) sqlite3_column_text(q_previous, 0);
+    target = Name(prev);
+    sqlite3_reset(q_previous);
+    return (SUCCESS);
+}
+
+DataSrc::Result
+Sqlite3DataSrc::findRRset(const Query& q,
+                          const Name& qname,
+                          const RRClass& qclass,
+                          const RRType& qtype,
+                          RRsetList& target,
+                          uint32_t& flags,
+                          Name* zone) const
+{
+    findRecords(qname, qtype, target, zone, NORMAL, flags);
+    return (SUCCESS);
+}
+
+DataSrc::Result
+Sqlite3DataSrc::findExactRRset(const Query& q,
+                               const Name& qname,
+                               const RRClass& qclass,
+                               const RRType& qtype,
+                               RRsetList& target,
+                               uint32_t& flags,
+                               Name* zone) const
+{
+    findRecords(qname, qtype, target, zone, NORMAL, flags);
+
+    // Ignore referrals in this case
+    flags &= ~REFERRAL;
+
+    // CNAMEs don't count in this case
+    if (flags & CNAME_FOUND) {
+        flags &= ~CNAME_FOUND;
+        flags |= TYPE_NOT_FOUND;
+    }
+
+    return (SUCCESS);
+}
+
+DataSrc::Result
+Sqlite3DataSrc::findAddrs(const Query& q,
+                          const Name& qname,
+                          const RRClass& qclass,
+                          RRsetList& target,
+                          uint32_t& flags,
+                          Name* zone) const {
+    findRecords(qname, RRType::ANY(), target, zone, ADDRESS, flags);
+    return (SUCCESS);
+}
+
+DataSrc::Result
+Sqlite3DataSrc::findReferral(const Query& q,
+                            const Name& qname,
+                            const RRClass& qclass,
+                            RRsetList& target,
+                            uint32_t& flags,
+                            Name* zone) const {
+    findRecords(qname, RRType::ANY(), target, zone, DELEGATION, flags);
+    return (SUCCESS);
+}
+//
+//  Open the database.
+//
+void
+Sqlite3DataSrc::open(const string& name)
+{
+    int rc;
+
+    database_name = name;
+    
+    rc = sqlite3_open(database_name.c_str(), &db);
+    if (rc) {
+        cerr << "open database: " << sqlite3_errmsg(db) << "\n";
+        sqlite3_close(db);
+        throw("Cannot open database");
+    }
+    
+    checkAndSetupSchema();
+}
+
+DataSrc::Result
+Sqlite3DataSrc::close(void)
+{
+    if (db == NULL) {
+        return (SUCCESS);
+    }
+
+    if (q_zone != NULL) {
+        release(q_zone);
+        q_zone = NULL;
+    }
+
+    if (q_record) {
+        release(q_record);
+        q_record = NULL;
+    }
+
+    if (q_addrs) {
+        release(q_addrs);
+        q_addrs = NULL;
+    }
+
+    if (q_referral) {
+        release(q_referral);
+        q_referral = NULL;
+    }
+
+    if (q_any) {
+        release(q_any);
+        q_any = NULL;
+    }
+
+    if (q_count) {
+        release(q_count);
+        q_count = NULL;
+    }
+
+    if (q_previous) {
+        release(q_previous);
+        q_previous = NULL;
+    }
+
+    sqlite3_close(db);
+
+    db = NULL;
+    return (SUCCESS);
+}
+
+}
+}

+ 118 - 0
src/lib/auth/cpp/data_source_sqlite3.h

@@ -0,0 +1,118 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#ifndef __SQLITE3_DATA_SOURCE_H
+#define __SQLITE3_DATA_SOURCE_H
+
+#include <sqlite3.h>
+
+#include "data_source.h"
+
+using namespace isc::dns;
+
+namespace isc {
+namespace auth {
+
+class Sqlite3DataSrc : public DataSrc {
+public:
+    Sqlite3DataSrc();
+    virtual ~Sqlite3DataSrc();
+
+    virtual void findClosestEnclosure(NameMatch& match) const;
+
+    virtual Result findRRset(const Query& q,
+                             const Name& qname,
+                             const RRClass& qclass,
+                             const RRType& qtype,
+                             RRsetList& target,
+                             uint32_t& flags,
+                             Name* zone = NULL) const;
+
+    virtual Result findExactRRset(const Query& q,
+                                  const Name& qname,
+                                  const RRClass& qclass,
+                                  const RRType& qtype,
+                                  RRsetList& target,
+                                  uint32_t& flags,
+                                  Name* zone = NULL) const;
+
+    virtual Result findAddrs(const Query& q,
+                               const Name& qname,
+                               const RRClass& qclass,
+                               RRsetList& target,
+                               uint32_t& flags,
+                               Name* zone = NULL) const;
+
+    virtual Result findReferral(const Query& q,
+                                const Name& qname,
+                                const RRClass& qclass,
+                                RRsetList& target,
+                                uint32_t& flags,
+                                Name* zone = NULL) const;
+
+    virtual DataSrc::Result findPreviousName(const Query& q,
+                                             const Name& qname,
+                                             Name& target,
+                                             Name* zone) const;
+
+    virtual Result init();
+    virtual Result close();
+
+private:
+    enum Mode {
+        NORMAL,
+        ADDRESS,
+        DELEGATION
+    };
+
+    void open(const std::string& name);
+    sqlite3_stmt* prepare(const char *statement);
+    void release(sqlite3_stmt* prepared);
+    int getVersion(void);
+    int hasExactZone(const char *name) const;
+    int findRecords(const Name& name, const RRType& rdtype,
+                    RRsetList& target, Name* zone,
+                    const Mode mode, uint32_t& flags) const;
+    int findClosest(const char *name, const char **position) const;
+    void loadVersion(void);
+    void setupPreparedStatements(void);
+    void execSetupQuery(const char *query);
+    void checkAndSetupSchema(void);
+
+    sqlite3 *db;
+    std::string database_name;
+    int database_version;
+    
+    //
+    // Prepared statements
+    //
+    sqlite3_stmt *q_zone;
+    sqlite3_stmt *q_record;
+    sqlite3_stmt *q_addrs;
+    sqlite3_stmt *q_referral;
+    sqlite3_stmt *q_any;
+    sqlite3_stmt *q_count;
+    sqlite3_stmt *q_previous;
+};
+
+}
+}
+
+#endif // __SQLITE3_DATA_SOURCE_H
+
+// Local Variables: 
+// mode: c++
+// End: 

+ 71 - 76
src/lib/auth/cpp/data_source_static.cc

@@ -1,4 +1,18 @@
-
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
 
 #include "data_source_static.h"
 
@@ -12,14 +26,18 @@
 
 #include <iostream>
 
-namespace isc {
-namespace dns {
-
+using namespace std;
+using namespace isc::dns;
 using namespace isc::dns::rdata;
 
+namespace isc {
+namespace auth {
+
 StaticDataSrc::StaticDataSrc() : authors_name("authors.bind"),
                                  version_name("version.bind")
 {
+    setClass(RRClass::CH());
+
     authors = RRsetPtr(new RRset(authors_name, RRClass::CH(),
                                           RRType::TXT(), RRTTL(0)));
     authors->addRdata(generic::TXT("Evan Hunt"));
@@ -47,88 +65,65 @@ StaticDataSrc::StaticDataSrc() : authors_name("authors.bind"),
     version_ns->addRdata(generic::NS(version_name));
 }
 
-const DataSrc*
-StaticDataSrc::findClosestEnclosure(const Name& qname, Name& container, bool& found) const {
-    NameComparisonResult::NameRelation version_cmp = 
-        qname.compare(version_name).getRelation();
-
-    if (version_cmp == NameComparisonResult::EQUAL ||
-        version_cmp == NameComparisonResult::SUBDOMAIN) {
-        NameComparisonResult::NameRelation sub_cmp = 
-           version_name.compare(container).getRelation();
-
-        if (sub_cmp == NameComparisonResult::SUBDOMAIN) {
-            container = authors_name;
-            found = true;
-            return this;
-        } else if (!found && sub_cmp == NameComparisonResult::EQUAL) {
-            found = true;
-            return this;
-        } else {
-            return NULL;
-        }
-    }
+void
+StaticDataSrc::findClosestEnclosure(NameMatch& match) const {
+    const Name& qname = match.qname();
+    NameComparisonResult::NameRelation cmp;
 
-    NameComparisonResult::NameRelation authors_cmp = 
-        qname.compare(authors_name).getRelation();
-
-    if (authors_cmp == NameComparisonResult::EQUAL ||
-        authors_cmp == NameComparisonResult::SUBDOMAIN) {
-        NameComparisonResult::NameRelation sub_cmp = 
-            authors_name.compare(container).getRelation();
-
-        if (sub_cmp == NameComparisonResult::SUBDOMAIN) {
-            container = authors_name;
-            found = true;
-            return this;
-        } else if (!found && sub_cmp == NameComparisonResult::EQUAL) {
-            found = true;
-            return this;
-        } else {
-            return NULL;
-        }
+    cmp = qname.compare(version_name).getRelation();
+    if (cmp == NameComparisonResult::EQUAL ||
+        cmp == NameComparisonResult::SUBDOMAIN) {
+        match.update(*this, version_name);
+        return;
     }
 
-    return NULL;
+    cmp = qname.compare(authors_name).getRelation();
+    if (cmp == NameComparisonResult::EQUAL ||
+        cmp == NameComparisonResult::SUBDOMAIN) {
+        match.update(*this, authors_name);
+        return;
+    }
 }
 
-DSResult
-StaticDataSrc::findRRset(const Name& qname,
+DataSrc::Result
+StaticDataSrc::findRRset(const Query& q,
+                         const Name& qname,
                          const RRClass& qclass,
                          const RRType& qtype,
-                         RRsetList& target) const
+                         RRsetList& target,
+                         uint32_t& flags,
+                         Name* zone) const
 {
-    if (qname == version_name &&
-        qclass == version->getClass() && qtype == version->getType()) {
-        target.push_back(version);
-        return SUCCESS;
-    } else if (qname == version_name &&
-               qclass == version_ns->getClass() &&
-               qtype == version_ns->getType()) {
-        target.push_back(version_ns);
-        return SUCCESS;
-    } else if (qname == authors_name &&
-               qclass == authors->getClass() && qtype == authors->getType()) {
-        target.push_back(authors);
-        return SUCCESS;
-    } else if (qname == authors_name &&
-               qclass == authors_ns->getClass() &&
-               qtype == authors_ns->getType()) {
-        target.push_back(authors_ns);
-        return SUCCESS;
+    flags = 0;
+    if (qclass != getClass()) {
+        return (ERROR);
     }
-    // XXX: this is not 100% correct.
-    // We should also support the nodata/noerror case.
-    return NAME_NOT_FOUND;
-}
 
-DSResult
-StaticDataSrc::findRRset(const Name& qname,
-                         const RRClass& qclass,
-                         const RRType& qtype,
-                         RRsetList& target, RRsetList& sigs) const
-{
-    return findRRset(qname, qclass, qtype, target);
+    bool any = (qtype == RRType::ANY());
+
+    if (qname == version_name) {
+        if (qtype == RRType::TXT() || any) {
+            target.addRRset(version);
+        } else if (qtype == RRType::NS()) {
+            target.addRRset(version_ns);
+        } else {
+            flags = TYPE_NOT_FOUND;
+        }
+    } else if (qname == authors_name) {
+        if (qtype == RRType::TXT() || any) {
+            target.addRRset(authors);
+            return (SUCCESS);
+        } else if (qtype == RRType::NS()) {
+            target.addRRset(authors_ns);
+            return (SUCCESS);
+        } else {
+            flags = TYPE_NOT_FOUND;
+        }
+    } else {
+        flags = NAME_NOT_FOUND;
+    }
+
+    return (SUCCESS);
 }
 
 }

+ 31 - 15
src/lib/auth/cpp/data_source_static.h

@@ -27,30 +27,46 @@
 
 #include "data_source.h"
 
+using namespace isc::dns;
+
 namespace isc {
-namespace dns {
+namespace auth {
 
 class StaticDataSrc : public DataSrc {
 public:
     StaticDataSrc();
-    ~StaticDataSrc() {};
+    ~StaticDataSrc() {}
+
+    void findClosestEnclosure(NameMatch& match) const;
 
-    const DataSrc* findClosestEnclosure(const Name& qname,
-                                        Name& container,
-                                        bool& found) const;
+    Result findRRset(const Query& q,
+                     const Name& qname,
+                     const RRClass& qclass,
+                     const RRType& qtype,
+                     RRsetList& target,
+                     uint32_t& flags,
+                     Name* zone = NULL) const;
 
-    DSResult findRRset(const Name& qname,
-                       const RRClass& qclass,
-                       const RRType& qtype,
-                       RRsetList& target, RRsetList& sigs) const;
+    Result findExactRRset(const Query& q,
+                         const Name& qname,
+                         const RRClass& qclass,
+                         const RRType& qtype,
+                         RRsetList& target,
+                         uint32_t& flags,
+                         Name* zone = NULL) const
+    {
+        return (findRRset(q, qname, qclass, qtype, target, flags, zone));
+    }
 
-    DSResult findRRset(const Name& qname,
-                       const RRClass& qclass,
-                       const RRType& qtype,
-                       RRsetList& target) const;
+    Result findPreviousName(const Query& q,
+                            const Name& qname,
+                            Name& target,
+                            Name* zone) const {
+        return (NOT_IMPLEMENTED);
+    }
 
-    DSResult init() { return SUCCESS; };
-    DSResult close() { return SUCCESS; } ;
+    Result init() { return (SUCCESS); }
+    Result close() { return (SUCCESS); }
 
 private:
     const Name authors_name;

+ 596 - 0
src/lib/auth/cpp/datasrc_unittest.cc

@@ -0,0 +1,596 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#include <stdint.h>
+
+#include <iostream>
+#include <vector>
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include <dns/buffer.h>
+#include <dns/message.h>
+#include <dns/messagerenderer.h>
+#include <dns/question.h>
+#include <dns/rdataclass.h>
+#include <dns/rrclass.h>
+#include <dns/rrttl.h>
+#include <dns/rrtype.h>
+
+#include "unittest_util.h"
+#include "unittest_ds.h"
+
+using isc::UnitTestUtil;
+using namespace std;
+using namespace isc::dns;
+using namespace isc::auth;
+
+namespace {
+class DataSrcTest : public ::testing::Test {
+protected:
+    DataSrcTest() : obuffer(0), renderer(obuffer) {}
+    TestDataSrc ds;
+    OutputBuffer obuffer;
+    MessageRenderer renderer;
+    Message msg;
+    static void msgFromFile(Message& message, const char* datafile);
+};
+
+void
+DataSrcTest::msgFromFile(Message& message, const char* datafile)
+{
+    std::vector<unsigned char> data;
+    UnitTestUtil::readWireData(datafile, data);
+
+    InputBuffer buffer(&data[0], data.size());
+    message.fromWire(buffer);
+}
+
+TEST_F(DataSrcTest, Query) {
+    msgFromFile(msg, "testdata/q_www");
+    msg.makeResponse();
+    msg.setHeaderFlag(MessageFlag::AA());
+    msg.setRcode(Rcode::NOERROR());
+    Query q = Query(msg, false);
+    ds.doQuery(q);
+    Message* m = &(q.message());
+
+    EXPECT_EQ(Rcode::NOERROR(), m->getRcode());
+    EXPECT_TRUE(m->getHeaderFlag(MessageFlag::QR()));
+    EXPECT_TRUE(m->getHeaderFlag(MessageFlag::AA()));
+    EXPECT_TRUE(m->getHeaderFlag(MessageFlag::RD()));
+
+    EXPECT_EQ(2, m->getRRCount(Section::ANSWER()));
+    EXPECT_EQ(4, m->getRRCount(Section::AUTHORITY()));
+    EXPECT_EQ(6, m->getRRCount(Section::ADDITIONAL()));
+
+    RRsetIterator rit = m->beginSection(Section::ANSWER());
+    RRsetPtr rrset = *rit;
+    EXPECT_EQ(Name("www.example.com"), rrset->getName());
+    EXPECT_EQ(RRType::A(), rrset->getType());
+    EXPECT_EQ(RRClass::IN(), rrset->getClass());
+
+    RdataIteratorPtr it = rrset->getRdataIterator();
+    it->first();
+    EXPECT_EQ("192.168.1.1", it->getCurrent().toText());
+    it->next();
+    EXPECT_TRUE(it->isLast());
+
+    // XXX: also check ANSWER RRSIG
+
+    rit = m->beginSection(Section::AUTHORITY());
+    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());
+
+    rit = m->beginSection(Section::ADDITIONAL());
+    rrset = *rit;
+    EXPECT_EQ(Name("dns01.example.com"), rrset->getName());
+    EXPECT_EQ(RRType::A(), rrset->getType());
+    EXPECT_EQ(RRClass::IN(), rrset->getClass());
+
+    it = rrset->getRdataIterator();
+    it->first();
+    EXPECT_EQ("192.168.2.1", it->getCurrent().toText());
+    it->next();
+    EXPECT_TRUE(it->isLast());
+}
+
+TEST_F(DataSrcTest, NSQuery) {
+    msgFromFile(msg, "testdata/q_example_ns");
+    msg.makeResponse();
+    msg.setHeaderFlag(MessageFlag::AA());
+    msg.setRcode(Rcode::NOERROR());
+    Query q = Query(msg, false);
+    ds.doQuery(q);
+    Message* m = &(q.message());
+
+    EXPECT_EQ(Rcode::NOERROR(), m->getRcode());
+    EXPECT_TRUE(m->getHeaderFlag(MessageFlag::QR()));
+    EXPECT_TRUE(m->getHeaderFlag(MessageFlag::AA()));
+    EXPECT_TRUE(m->getHeaderFlag(MessageFlag::RD()));
+
+    EXPECT_EQ(4, m->getRRCount(Section::ANSWER()));
+    EXPECT_EQ(0, m->getRRCount(Section::AUTHORITY()));
+    EXPECT_EQ(6, m->getRRCount(Section::ADDITIONAL()));
+
+    RRsetIterator rit = m->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());
+}
+
+TEST_F(DataSrcTest, NxRRset) {
+    msgFromFile(msg, "testdata/q_example_ptr");
+    msg.makeResponse();
+    msg.setHeaderFlag(MessageFlag::AA());
+    msg.setRcode(Rcode::NOERROR());
+    Query q = Query(msg, false);
+    ds.doQuery(q);
+    Message* m = &(q.message());
+
+    EXPECT_EQ(Rcode::NOERROR(), m->getRcode());
+    EXPECT_TRUE(m->getHeaderFlag(MessageFlag::QR()));
+    EXPECT_TRUE(m->getHeaderFlag(MessageFlag::AA()));
+    EXPECT_TRUE(m->getHeaderFlag(MessageFlag::RD()));
+
+    EXPECT_EQ(0, m->getRRCount(Section::ANSWER()));
+    EXPECT_EQ(4, m->getRRCount(Section::AUTHORITY()));
+    EXPECT_EQ(0, m->getRRCount(Section::ADDITIONAL()));
+
+    RRsetIterator rit = m->beginSection(Section::AUTHORITY());
+    RRsetPtr rrset = *rit;
+    EXPECT_EQ(Name("example.com"), rrset->getName());
+    EXPECT_EQ(RRType::SOA(), rrset->getType());
+}
+
+TEST_F(DataSrcTest, Nxdomain) {
+    msgFromFile(msg, "testdata/q_glork");
+    msg.makeResponse();
+    msg.setHeaderFlag(MessageFlag::AA());
+    msg.setRcode(Rcode::NOERROR());
+    Query q = Query(msg, false);
+    ds.doQuery(q);
+    Message* m = &(q.message());
+
+    EXPECT_EQ(Rcode::NXDOMAIN(), m->getRcode());
+    EXPECT_TRUE(m->getHeaderFlag(MessageFlag::QR()));
+    EXPECT_TRUE(m->getHeaderFlag(MessageFlag::AA()));
+    EXPECT_TRUE(m->getHeaderFlag(MessageFlag::RD()));
+
+    EXPECT_EQ(0, m->getRRCount(Section::ANSWER()));
+    EXPECT_EQ(6, m->getRRCount(Section::AUTHORITY()));
+    EXPECT_EQ(0, m->getRRCount(Section::ADDITIONAL()));
+
+    RRsetIterator rit = m->beginSection(Section::AUTHORITY());
+    RRsetPtr rrset = *rit;
+    EXPECT_EQ(Name("example.com"), rrset->getName());
+    EXPECT_EQ(RRType::NSEC(), rrset->getType());
+    EXPECT_EQ(RRClass::IN(), rrset->getClass());
+    // XXX: check for other authority section answers
+}
+
+TEST_F(DataSrcTest, NxZone) {
+    msgFromFile(msg, "testdata/q_spork");
+    msg.makeResponse();
+    msg.setHeaderFlag(MessageFlag::AA());
+    msg.setRcode(Rcode::NOERROR());
+    Query q = Query(msg, false);
+    ds.doQuery(q);
+    Message* m = &(q.message());
+
+    EXPECT_EQ(Rcode::REFUSED(), m->getRcode());
+    EXPECT_TRUE(m->getHeaderFlag(MessageFlag::QR()));
+    EXPECT_FALSE(m->getHeaderFlag(MessageFlag::AA()));
+    EXPECT_TRUE(m->getHeaderFlag(MessageFlag::RD()));
+
+}
+
+TEST_F(DataSrcTest, Wildcard) {
+    msgFromFile(msg, "testdata/q_wild");
+    msg.makeResponse();
+    msg.setHeaderFlag(MessageFlag::AA());
+    msg.setRcode(Rcode::NOERROR());
+    Query q = Query(msg, false);
+    ds.doQuery(q);
+    Message* m = &(q.message());
+
+    EXPECT_EQ(Rcode::NOERROR(), m->getRcode());
+    EXPECT_TRUE(m->getHeaderFlag(MessageFlag::QR()));
+    EXPECT_TRUE(m->getHeaderFlag(MessageFlag::AA()));
+    EXPECT_TRUE(m->getHeaderFlag(MessageFlag::RD()));
+
+    EXPECT_EQ(2, m->getRRCount(Section::ANSWER()));
+    EXPECT_EQ(4, m->getRRCount(Section::AUTHORITY()));
+    EXPECT_EQ(6, m->getRRCount(Section::ADDITIONAL()));
+
+    RRsetIterator rit = m->beginSection(Section::ANSWER());
+    RRsetPtr rrset = *rit;
+    EXPECT_EQ(Name("www.wild.example.com"), rrset->getName());
+    EXPECT_EQ(RRType::A(), rrset->getType());
+    EXPECT_EQ(RRClass::IN(), rrset->getClass());
+
+    RdataIteratorPtr it = rrset->getRdataIterator();
+    it->first();
+    EXPECT_EQ("192.168.3.2", it->getCurrent().toText());
+    it->next();
+    EXPECT_TRUE(it->isLast());
+
+    rit = m->beginSection(Section::AUTHORITY());
+    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());
+
+    rit = m->beginSection(Section::ADDITIONAL());
+    rrset = *rit;
+    EXPECT_EQ(Name("dns01.example.com"), rrset->getName());
+    EXPECT_EQ(RRType::A(), rrset->getType());
+    EXPECT_EQ(RRClass::IN(), rrset->getClass());
+
+    it = rrset->getRdataIterator();
+    it->first();
+    EXPECT_EQ("192.168.2.1", it->getCurrent().toText());
+    it->next();
+    EXPECT_TRUE(it->isLast());
+}
+
+TEST_F(DataSrcTest, AuthDelegation) {
+    msgFromFile(msg, "testdata/q_sql1");
+    msg.makeResponse();
+    msg.setHeaderFlag(MessageFlag::AA());
+    msg.setRcode(Rcode::NOERROR());
+    Query q = Query(msg, false);
+    ds.doQuery(q);
+    Message* m = &(q.message());
+
+    EXPECT_EQ(Rcode::NOERROR(), m->getRcode());
+    EXPECT_TRUE(m->getHeaderFlag(MessageFlag::QR()));
+    EXPECT_TRUE(m->getHeaderFlag(MessageFlag::AA()));
+    EXPECT_TRUE(m->getHeaderFlag(MessageFlag::RD()));
+
+    EXPECT_EQ(2, m->getRRCount(Section::ANSWER()));
+    EXPECT_EQ(4, m->getRRCount(Section::AUTHORITY()));
+    EXPECT_EQ(6, m->getRRCount(Section::ADDITIONAL()));
+
+    RRsetIterator rit = m->beginSection(Section::ANSWER());
+    RRsetPtr rrset = *rit;
+    EXPECT_EQ(Name("www.sql1.example.com"), rrset->getName());
+    EXPECT_EQ(RRType::A(), rrset->getType());
+    EXPECT_EQ(RRClass::IN(), rrset->getClass());
+
+    RdataIteratorPtr it = rrset->getRdataIterator();
+    it->first();
+    EXPECT_EQ("192.168.2.2", it->getCurrent().toText());
+    it->next();
+    EXPECT_TRUE(it->isLast());
+
+    rit = m->beginSection(Section::AUTHORITY());
+    rrset = *rit;
+    EXPECT_EQ(Name("sql1.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());
+
+    rit = m->beginSection(Section::ADDITIONAL());
+    rrset = *rit;
+    EXPECT_EQ(Name("dns01.example.com"), rrset->getName());
+    EXPECT_EQ(RRType::A(), rrset->getType());
+    EXPECT_EQ(RRClass::IN(), rrset->getClass());
+
+    it = rrset->getRdataIterator();
+    it->first();
+    EXPECT_EQ("192.168.2.1", it->getCurrent().toText());
+    it->next();
+    EXPECT_TRUE(it->isLast());
+}
+
+TEST_F(DataSrcTest, Dname) {
+    msgFromFile(msg, "testdata/q_dname");
+    msg.makeResponse();
+    msg.setHeaderFlag(MessageFlag::AA());
+    msg.setRcode(Rcode::NOERROR());
+    Query q = Query(msg, false);
+    ds.doQuery(q);
+    Message* m = &(q.message());
+
+    EXPECT_EQ(Rcode::NOERROR(), m->getRcode());
+    EXPECT_TRUE(m->getHeaderFlag(MessageFlag::QR()));
+    EXPECT_TRUE(m->getHeaderFlag(MessageFlag::AA()));
+    EXPECT_TRUE(m->getHeaderFlag(MessageFlag::RD()));
+
+    EXPECT_EQ(5, m->getRRCount(Section::ANSWER()));
+    EXPECT_EQ(4, m->getRRCount(Section::AUTHORITY()));
+    EXPECT_EQ(6, m->getRRCount(Section::ADDITIONAL()));
+
+    RRsetIterator rit = m->beginSection(Section::ANSWER());
+    RRsetPtr rrset = *rit;
+    EXPECT_EQ(Name("dname.example.com"), rrset->getName());
+    EXPECT_EQ(RRType::DNAME(), rrset->getType());
+    EXPECT_EQ(RRClass::IN(), rrset->getClass());
+
+    RdataIteratorPtr it = rrset->getRdataIterator();
+    it->first();
+    EXPECT_EQ("sql1.example.com.", it->getCurrent().toText());
+    it->next();
+    EXPECT_TRUE(it->isLast());
+
+    // XXX: check CNAME and A record too
+
+    rit = m->beginSection(Section::AUTHORITY());
+    rrset = *rit;
+    EXPECT_EQ(Name("sql1.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());
+
+    rit = m->beginSection(Section::ADDITIONAL());
+    rrset = *rit;
+    EXPECT_EQ(Name("dns01.example.com"), rrset->getName());
+    EXPECT_EQ(RRType::A(), rrset->getType());
+    EXPECT_EQ(RRClass::IN(), rrset->getClass());
+
+    it = rrset->getRdataIterator();
+    it->first();
+    EXPECT_EQ("192.168.2.1", it->getCurrent().toText());
+    it->next();
+    EXPECT_TRUE(it->isLast());
+}
+
+TEST_F(DataSrcTest, Cname) {
+    msgFromFile(msg, "testdata/q_cname");
+    msg.makeResponse();
+    msg.setHeaderFlag(MessageFlag::AA());
+    msg.setRcode(Rcode::NOERROR());
+    Query q = Query(msg, false);
+    ds.doQuery(q);
+    Message* m = &(q.message());
+
+    EXPECT_EQ(Rcode::NOERROR(), m->getRcode());
+    EXPECT_TRUE(m->getHeaderFlag(MessageFlag::QR()));
+    EXPECT_TRUE(m->getHeaderFlag(MessageFlag::AA()));
+    EXPECT_TRUE(m->getHeaderFlag(MessageFlag::RD()));
+
+    EXPECT_EQ(2, m->getRRCount(Section::ANSWER()));
+    EXPECT_EQ(0, m->getRRCount(Section::AUTHORITY()));
+    EXPECT_EQ(0, m->getRRCount(Section::ADDITIONAL()));
+
+    RRsetIterator rit = m->beginSection(Section::ANSWER());
+    RRsetPtr rrset = *rit;
+    EXPECT_EQ(Name("foo.example.com"), rrset->getName());
+    EXPECT_EQ(RRType::CNAME(), rrset->getType());
+    EXPECT_EQ(RRClass::IN(), rrset->getClass());
+
+    RdataIteratorPtr it = rrset->getRdataIterator();
+    it->first();
+    EXPECT_EQ("cnametest.flame.org.", it->getCurrent().toText());
+    it->next();
+    EXPECT_TRUE(it->isLast());
+}
+
+TEST_F(DataSrcTest, CnameInt) {
+    msgFromFile(msg, "testdata/q_cname_int");
+    msg.makeResponse();
+    msg.setHeaderFlag(MessageFlag::AA());
+    msg.setRcode(Rcode::NOERROR());
+    Query q = Query(msg, false);
+    ds.doQuery(q);
+    Message* m = &(q.message());
+
+    EXPECT_EQ(Rcode::NOERROR(), m->getRcode());
+    EXPECT_TRUE(m->getHeaderFlag(MessageFlag::QR()));
+    EXPECT_TRUE(m->getHeaderFlag(MessageFlag::AA()));
+    EXPECT_TRUE(m->getHeaderFlag(MessageFlag::RD()));
+
+    EXPECT_EQ(4, m->getRRCount(Section::ANSWER()));
+    EXPECT_EQ(4, m->getRRCount(Section::AUTHORITY()));
+    EXPECT_EQ(6, m->getRRCount(Section::ADDITIONAL()));
+
+    RRsetIterator rit = m->beginSection(Section::ANSWER());
+    RRsetPtr rrset = *rit;
+    EXPECT_EQ(Name("cname-int.example.com"), rrset->getName());
+    EXPECT_EQ(RRType::CNAME(), rrset->getType());
+    EXPECT_EQ(RRClass::IN(), rrset->getClass());
+
+    RdataIteratorPtr it = rrset->getRdataIterator();
+    it->first();
+    EXPECT_EQ("www.example.com.", it->getCurrent().toText());
+    it->next();
+    EXPECT_TRUE(it->isLast());
+
+    // XXX: check a record as well
+
+    rit = m->beginSection(Section::AUTHORITY());
+    rrset = *rit;
+    EXPECT_EQ(Name("example.com"), rrset->getName());
+    EXPECT_EQ(RRType::NS(), rrset->getType());
+    EXPECT_EQ(RRClass::IN(), rrset->getClass());
+}
+
+TEST_F(DataSrcTest, CnameExt) {
+    msgFromFile(msg, "testdata/q_cname_ext");
+    msg.makeResponse();
+    msg.setHeaderFlag(MessageFlag::AA());
+    msg.setRcode(Rcode::NOERROR());
+    Query q = Query(msg, false);
+    ds.doQuery(q);
+    Message* m = &(q.message());
+
+    EXPECT_EQ(Rcode::NOERROR(), m->getRcode());
+    EXPECT_TRUE(m->getHeaderFlag(MessageFlag::QR()));
+    EXPECT_TRUE(m->getHeaderFlag(MessageFlag::AA()));
+    EXPECT_TRUE(m->getHeaderFlag(MessageFlag::RD()));
+
+    EXPECT_EQ(4, m->getRRCount(Section::ANSWER()));
+    EXPECT_EQ(4, m->getRRCount(Section::AUTHORITY()));
+    EXPECT_EQ(6, m->getRRCount(Section::ADDITIONAL()));
+
+    RRsetIterator rit = m->beginSection(Section::ANSWER());
+    RRsetPtr rrset = *rit;
+    EXPECT_EQ(Name("cname-ext.example.com"), rrset->getName());
+    EXPECT_EQ(RRType::CNAME(), rrset->getType());
+    EXPECT_EQ(RRClass::IN(), rrset->getClass());
+
+    RdataIteratorPtr it = rrset->getRdataIterator();
+    it->first();
+    EXPECT_EQ("www.sql1.example.com.", it->getCurrent().toText());
+    it->next();
+    EXPECT_TRUE(it->isLast());
+
+    rit = m->beginSection(Section::AUTHORITY());
+    rrset = *rit;
+    EXPECT_EQ(Name("sql1.example.com"), rrset->getName());
+    EXPECT_EQ(RRType::NS(), rrset->getType());
+    EXPECT_EQ(RRClass::IN(), rrset->getClass());
+}
+
+TEST_F(DataSrcTest, Delegation) {
+    msgFromFile(msg, "testdata/q_subzone");
+    msg.makeResponse();
+    msg.setHeaderFlag(MessageFlag::AA());
+    msg.setRcode(Rcode::NOERROR());
+    Query q = Query(msg, false);
+    ds.doQuery(q);
+    Message* m = &(q.message());
+
+    EXPECT_EQ(Rcode::NOERROR(), m->getRcode());
+    EXPECT_TRUE(m->getHeaderFlag(MessageFlag::QR()));
+    EXPECT_FALSE(m->getHeaderFlag(MessageFlag::AA()));
+    EXPECT_TRUE(m->getHeaderFlag(MessageFlag::RD()));
+
+    EXPECT_EQ(0, m->getRRCount(Section::ANSWER()));
+    EXPECT_EQ(5, m->getRRCount(Section::AUTHORITY()));
+    EXPECT_EQ(2, m->getRRCount(Section::ADDITIONAL()));
+
+    RRsetIterator rit = m->beginSection(Section::AUTHORITY());
+    RRsetPtr rrset = *rit;
+    EXPECT_EQ(Name("subzone.example.com."), rrset->getName());
+    EXPECT_EQ(RRType::NS(), rrset->getType());
+    EXPECT_EQ(RRClass::IN(), rrset->getClass());
+
+    RdataIteratorPtr it = rrset->getRdataIterator();
+    it->first();
+    EXPECT_EQ("ns1.subzone.example.com.", it->getCurrent().toText());
+    it->next();
+    EXPECT_FALSE(it->isLast());
+
+    rit = m->beginSection(Section::ADDITIONAL());
+    rrset = *rit;
+    EXPECT_EQ(Name("ns1.subzone.example.com"), rrset->getName());
+    EXPECT_EQ(RRType::A(), rrset->getType());
+    EXPECT_EQ(RRClass::IN(), rrset->getClass());
+
+    it = rrset->getRdataIterator();
+    it->first();
+    EXPECT_EQ("192.168.3.1", it->getCurrent().toText());
+    it->next();
+    EXPECT_TRUE(it->isLast());
+}
+
+TEST_F(DataSrcTest, DS) {
+    msgFromFile(msg, "testdata/q_subzone_ds");
+    msg.makeResponse();
+    msg.setHeaderFlag(MessageFlag::AA());
+    msg.setRcode(Rcode::NOERROR());
+    Query q = Query(msg, false);
+    ds.doQuery(q);
+    Message* m = &(q.message());
+
+    EXPECT_EQ(Rcode::NOERROR(), m->getRcode());
+    EXPECT_TRUE(m->getHeaderFlag(MessageFlag::QR()));
+    EXPECT_TRUE(m->getHeaderFlag(MessageFlag::AA()));
+    EXPECT_TRUE(m->getHeaderFlag(MessageFlag::RD()));
+
+    EXPECT_EQ(3, m->getRRCount(Section::ANSWER()));
+    EXPECT_EQ(4, m->getRRCount(Section::AUTHORITY()));
+    EXPECT_EQ(6, m->getRRCount(Section::ADDITIONAL()));
+
+    RRsetIterator rit = m->beginSection(Section::ANSWER());
+    RRsetPtr rrset = *rit;
+    EXPECT_EQ(Name("subzone.example.com."), rrset->getName());
+    EXPECT_EQ(RRType::DS(), rrset->getType());
+    EXPECT_EQ(RRClass::IN(), rrset->getClass());
+
+    rit = m->beginSection(Section::AUTHORITY());
+    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());
+}
+
+}
+

+ 3 - 13
src/lib/auth/cpp/query.cc

@@ -24,20 +24,10 @@
 #include "query.h"
 
 namespace isc {
-namespace dns {
+namespace auth {
 
-QueryTask::QueryTask(const Name& n, const RRClass& c,
-                     const RRType& t, const Section& s) :
-    qname(n), qclass(c), qtype(t), section(s)
-{
-        // Empty constructor.  It is defined outside the class statement
-        // because otherwise the linker will be confused.
-}
-
-QueryTask::~QueryTask() {
-        // Empty destructor.  It is defined outside the class statement
-        // because otherwise the linker will be confused.
-}
+// Destructor defined here to avoid confusing the linker
+QueryTask::~QueryTask() {}
 
 }
 }

+ 152 - 35
src/lib/auth/cpp/query.h

@@ -26,40 +26,152 @@
 #include <dns/rrclass.h>
 #include <dns/rrtype.h>
 
-namespace isc {
-namespace dns {
-
-enum QueryStatus {
-    QUERY_INCOMPLETE,
-    QUERY_FINISHING,
-    QUERY_DONE,
-    QUERY_NODATA
-};
+using namespace isc::dns;
 
-///
-/// \brief exception to throw if a DNS Message is malformed
-/// 
-class MalformedMessage : public Exception {
-public:
-    MalformedMessage(const char* file, size_t line, const char* what) :
-        isc::Exception(file, line, what) {}
-};
+namespace isc {
+namespace auth {
 
 // An individual task to be carried out by the query logic
 class QueryTask {
 public:
-    QueryTask(const Name& n, const RRClass& c,
-              const RRType& t, const Section& s);
-    virtual ~QueryTask();
+    // XXX: Members are currently public, but should probably be
+    // moved to private and wrapped in get() functions later.
 
+    // The standard query tuple: qname/qclass/qtype.
+    // Note that qtype is ignored in the GLUE_QUERY/NOGLUE_QUERY case.
     const Name& qname;
     const RRClass& qclass;
     const RRType& qtype;
+
+    // Optional: name for the containing zone, if known.
+    // This is particularly needed when looking up data in a
+    // zone other than the closest enclosure (such as getting
+    // DS queries from a parent zone on a server which serves
+    // both parent and child).
+    Name* zone;
+
+    // The section of the reply into which the data should be
+    // written after it has been fetched from the data source.
     const Section& section;
+
+    // The op field indicates the operation to be carried out by
+    // this query task:
+    //
+    // - SIMPLE_QUERY: look for a match for qname/qclass/qtype
+    //   in local data (regardless of whether it is above or below
+    //   a zone cut).
+    //
+    // - AUTH_QUERY: look for a match for qname/qclass/qtype, or
+    //   for qname/qclass/CNAME, or for a referral.
+    //
+    // - GLUE_QUERY: look for matches with qname/qclass/A
+    //   OR qname/class/AAAA in local data, regardless of
+    //   authority, for use in glue.  (This can be implemented
+    //   as two successive SIMPLE_QUERY tasks, but might be
+    //   optimized by the concrete data source implementation
+    //   by turning it into a single database lookup.)
+    //
+    // - NOGLUE_QUERY: same as GLUE_QUERY except that answers
+    //   are rejected if they are below a zone cut.
+    //
+    // - REF_QUERY: look for matches for qname/qclass/NS,
+    //   qname/qclass/DS, and qname/qclass/DNAME.  Used
+    //   to search for a zone cut.
+
+    const enum Op {
+        SIMPLE_QUERY,
+        AUTH_QUERY,
+        GLUE_QUERY,
+        NOGLUE_QUERY,
+        REF_QUERY,
+    } op;
+
+    // The state field indicates the state of the query; it controls
+    // the next step after processing each query task.
+    //
+    // - GETANSWER: We are looking for the answer to a primary query.
+    //   (The qname of the task should exactly match the qname of the
+    //   query.)  If we have no match, the query has failed.
+    //
+    // - GETADDITIONAL: We are filling in additional data, either
+    //   as a result of finding NS or MX records via a GETANSWER
+    //   query task, or as a result of finding NS records when
+    //   getting authority-section data.
+    //
+    // - FOLLOWCNAME: We are looking for the target of a CNAME RR that
+    //   was found via a previous GETANSWER query task.  If we have no
+    //   match, the query is still successful.
+    //
+    // (NOTE: It is only necessary to set a task state when pushing
+    // tasks onto the query task queue, which in turn is only necessary
+    // when it's uncertain which data source will be authoritative for the
+    // data.  That's why there is no GETAUTHORITY task state; when
+    // processing an answer, either positive or negative, the authoritative
+    // data source will already have been discovered, and can be queried
+    // directly.)
+
+    enum State {
+        GETANSWER,
+        GETADDITIONAL,
+        FOLLOWCNAME
+    } state;
+
+    // Response flags to indicate conditions encountered while
+    // processing this task.
+    uint32_t flags;
+
+    // Constructors
+    QueryTask(const Name& n, const RRClass& c,
+              const RRType& t, const Section& sect) :
+        qname(n), qclass(c), qtype(t), zone(NULL),
+        section(sect), op(AUTH_QUERY), state(GETANSWER), flags(0) {}
+    QueryTask(const Name& n, const RRClass& c,
+              const RRType& t, const Section& sect, const Op o) :
+        qname(n), qclass(c), qtype(t), zone(NULL),
+        section(sect), op(o), state(GETANSWER), flags(0) {}
+    QueryTask(const Name& n, const RRClass& c,
+              const RRType& t, const Section& sect, const State st) :
+        qname(n), qclass(c), qtype(t), zone(NULL),
+        section(sect), op(AUTH_QUERY), state(st), flags(0) {}
+    QueryTask(const Name& n, const RRClass& c,
+              const RRType& t, const Section& sect,
+              const Op o, const State st) :
+        qname(n), qclass(c), qtype(t), zone(NULL),
+        section(sect), op(o), state(st), flags(0) {}
+
+    // These are special constructors for particular query task types,
+    // to simplify the code.
+    //
+    // A simple query doesn't need to specify section or state.
+    QueryTask(const Name& n, const RRClass& c, const RRType& t, const Op o) :
+        qname(n), qclass(c), qtype(t), zone(NULL),
+        section(Section::ANSWER()), op(o), state(GETANSWER), flags(0) {
+        if (op != SIMPLE_QUERY) {
+            throw "invalid constructor for this task operation";
+        }
+    }
+    // A referral query doesn't need to specify section, state, or type.
+    QueryTask(const Name& n, const RRClass& c, const Op o) :
+        qname(n), qclass(c), qtype(RRType::ANY()), zone(NULL),
+        section(Section::ANSWER()), op(o), state(GETANSWER), flags(0) {
+        if (op != REF_QUERY) {
+            throw "invalid constructor for this task operation";
+        }
+    }
+    // A glue (or noglue) query doesn't need to specify type.
+    QueryTask(const Name& n, const RRClass& c,
+              const Section& sect, const Op o, const State st) :
+        qname(n), qclass(c), qtype(RRType::ANY()), zone(NULL),
+        section(sect), op(o), state(st), flags(0) {
+        if (op != GLUE_QUERY && op != NOGLUE_QUERY) {
+            throw "invalid constructor for this task operation";
+        }
+    }
+
+    virtual ~QueryTask();
 };
 
-typedef boost::shared_ptr<QueryTask> QueryTaskPtr;
-typedef std::queue<QueryTaskPtr> QueryTaskQueue;
+typedef std::queue<QueryTask> QueryTaskQueue;
 
 class Query;
 typedef boost::shared_ptr<Query> QueryPtr;
@@ -67,25 +179,31 @@ typedef boost::shared_ptr<Query> QueryPtr;
 // Data Source query
 class Query {
 public:
+    // The state of a query: pending or answered.
+    enum Status {
+        PENDING,
+        ANSWERED
+    };
+
+    // Query constructor
     Query(Message& m, bool dnssec) {
         message_ = &m;
         want_additional = true;
         want_dnssec = dnssec;
-        status_ = QUERY_INCOMPLETE;
+        status_ = PENDING;
 
         // Check message formatting
-        QuestionIterator qid = message_->beginQuestion();
-        qid++;
-        if (qid != message_->endQuestion())
-                dns_throw(MalformedMessage, "too many questions");
+        if (message_->getRRCount(Section::QUESTION()) != 1) {
+            dns_throw(Unexpected, "malformed message: too many questions");
+        }
 
-        // Populate the query tasks queue with the initial question
+        // Populate the query task queue with the initial question
         QuestionPtr query = *message_->beginQuestion();
         qname_ = &query->getName();
         qclass_ = &query->getClass();
         qtype_ = &query->getType();
-        querytasks.push(QueryTaskPtr(new QueryTask(*qname_, *qclass_,
-                                     *qtype_, Section::ANSWER())));
+        querytasks.push(QueryTask(*qname_, *qclass_, *qtype_,
+                                  Section::ANSWER()));
     };
 
     virtual ~Query() {}
@@ -108,13 +226,12 @@ public:
     Message& message() { return *message_; }
     QueryTaskQueue& tasks() { return querytasks; }
 
-    QueryStatus status() { return status_; }
-    void setStatus(QueryStatus s) { status_ = s; }
-
-protected:
-    QueryStatus status_;
+    Status status() { return status_; }
+    void setStatus(Status s) { status_ = s; }
 
 private:
+    Status status_;
+
     const Name* qname_;
     const RRClass* qclass_;
     const RRType* qtype_;

+ 24 - 0
src/lib/auth/cpp/run_unittests.cc

@@ -0,0 +1,24 @@
+// Copyright (C) 2009  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id: run_unittests.cc 754 2010-02-08 22:28:56Z each $
+
+#include <gtest/gtest.h>
+
+int
+main(int argc, char* argv[])
+{
+    ::testing::InitGoogleTest(&argc, argv);
+    return (RUN_ALL_TESTS());
+}

+ 4 - 0
src/lib/auth/cpp/testdata/q_cname

@@ -0,0 +1,4 @@
+# foo.example.com/A (cname to outside zone)
+  9d 57 01 00 00 01 00 00 00 00 00 00 03 66 6f 6f
+  07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 00 01 00
+  01

+ 4 - 0
src/lib/auth/cpp/testdata/q_cname_ext

@@ -0,0 +1,4 @@
+# cname-ext.example.com/A (cname to local authoritative zone)
+  ed 42 01 00 00 01 00 00 00 00 00 00 09 63 6e 61
+  6d 65 2d 65 78 74 07 65 78 61 6d 70 6c 65 03 63
+  6f 6d 00 00 01 00 01

+ 4 - 0
src/lib/auth/cpp/testdata/q_cname_int

@@ -0,0 +1,4 @@
+# cname-int.example.com/A (cname to same zone)
+  51 b2 01 00 00 01 00 00 00 00 00 00 09 63 6e 61
+  6d 65 2d 69 6e 74 07 65 78 61 6d 70 6c 65 03 63
+  6f 6d 00 00 01 00 01

+ 4 - 0
src/lib/auth/cpp/testdata/q_dname

@@ -0,0 +1,4 @@
+# www.dname.example.com/A (dname)
+  3c 71 01 00 00 01 00 00 00 00 00 00 03 77 77 77
+  05 64 6e 61 6d 65 07 65 78 61 6d 70 6c 65 03 63
+  6f 6d 00 00 01 00 01

+ 3 - 0
src/lib/auth/cpp/testdata/q_example_ns

@@ -0,0 +1,3 @@
+# example.com/NS (normal lookup, positive answer)
+  dd 6b 01 00 00 01 00 00 00 00 00 00 07 65 78 61
+  6d 70 6c 65 03 63 6f 6d 00 00 02 00 01

+ 3 - 0
src/lib/auth/cpp/testdata/q_example_ptr

@@ -0,0 +1,3 @@
+# example.com/PTR (NOERROR/NODATA)
+  4d 2a 01 00 00 01 00 00 00 00 00 00 07 65 78 61
+  6d 70 6c 65 03 63 6f 6d 00 00 0c 00 01

+ 4 - 0
src/lib/auth/cpp/testdata/q_glork

@@ -0,0 +1,4 @@
+# glork.example.com/A (NXDOMAIN)
+  c1 11 01 00 00 01 00 00 00 00 00 00 05 67 6c 6f
+  72 6b 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 00
+  01 00 01

+ 3 - 0
src/lib/auth/cpp/testdata/q_spork

@@ -0,0 +1,3 @@
+# spork.com/a (REFUSED)
+  a8 8a 01 00 00 01 00 00 00 00 00 00 05 73 70 6f 72 6b 03 63
+  6f 6d 00 00 01 00 01

+ 4 - 0
src/lib/auth/cpp/testdata/q_sql1

@@ -0,0 +1,4 @@
+# www.sql1.example.com/A (delegation to local authoritative zone)
+  75 b3 01 00 00 01 00 00 00 00 00 00 03 77 77 77
+  04 73 71 6c 31 07 65 78 61 6d 70 6c 65 03 63 6f
+  6d 00 00 01 00 01

+ 4 - 0
src/lib/auth/cpp/testdata/q_subzone

@@ -0,0 +1,4 @@
+# www.subzone.example.com/A (delegation to non-local zone)
+  00 7c 01 00 00 01 00 00 00 00 00 00 03 77 77 77
+  07 73 75 62 7a 6f 6e 65 07 65 78 61 6d 70 6c 65
+  03 63 6f 6d 00 00 01 00 01

+ 4 - 0
src/lib/auth/cpp/testdata/q_subzone_ds

@@ -0,0 +1,4 @@
+# subzone.example.com/DS (delegation signer)
+  14 f0 01 00 00 01 00 00 00 00 00 00 07 73 75 62
+  7a 6f 6e 65 07 65 78 61 6d 70 6c 65 03 63 6f 6d
+  00 00 2b 00 01

+ 4 - 0
src/lib/auth/cpp/testdata/q_wild

@@ -0,0 +1,4 @@
+# www.wild.example.com/A (wildcard)
+  d8 ef 01 00 00 01 00 00 00 00 00 00 03 77 77 77
+  04 77 69 6c 64 07 65 78 61 6d 70 6c 65 03 63 6f
+  6d 00 00 01 00 01

+ 4 - 0
src/lib/auth/cpp/testdata/q_www

@@ -0,0 +1,4 @@
+# www.example.com/A (normal lookup, positive answer)
+  29 e8 01 00 00 01 00 00 00 00 00 00 03 77 77 77
+  07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 00 01 00
+  01

+ 716 - 0
src/lib/auth/cpp/unittest_ds.cc

@@ -0,0 +1,716 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#include "unittest_util.h"
+#include "unittest_ds.h"
+#include "data_source.h"
+
+#include <dns/buffer.h>
+#include <dns/messagerenderer.h>
+#include <dns/name.h>
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+#include <dns/rrclass.h>
+#include <dns/rrset.h>
+#include <dns/rrtype.h>
+#include <dns/rrttl.h>
+
+#include <iostream>
+
+using isc::UnitTestUtil;
+using namespace std;
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+
+namespace isc {
+namespace auth {
+
+TestDataSrc::TestDataSrc() : example("example.com"),
+                             sql1("sql1.example.com"),
+                             www_sql1("www.sql1.example.com"),
+                             www("www.example.com"),
+                             foo("foo.example.com"),
+                             dns01("dns01.example.com"),
+                             dns02("dns02.example.com"),
+                             dns03("dns03.example.com"),
+                             cnameint("cname-int.example.com"),
+                             cnameext("cname-ext.example.com"),
+                             dname("dname.example.com"),
+                             wild("*.wild.example.com"),
+                             subzone("subzone.example.com")
+{
+    RRset* rp;
+    RRsetPtr rrsig;
+
+    // example.com
+    example_ns = RRsetPtr(new RRset(example, RRClass::IN(),
+                                    RRType::NS(), RRTTL(3600)));
+    example_ns->addRdata(generic::NS(dns01));
+    example_ns->addRdata(generic::NS(dns02));
+    example_ns->addRdata(generic::NS(dns03));
+
+    rp = new RRset(example, RRClass::IN(), RRType::RRSIG(), RRTTL(3600));
+    rrsig = RRsetPtr(rp);
+    rrsig->addRdata(generic::RRSIG("NS 5 2 3600 20100322084538 20100220084538 33495 example.com. ClcrfjkQZUY5L6ZlCkU3cJHzcrEGrofKSVeeoeZ+w6yeEowFNVXs2YBo3tom53DiCrdD9rs3feVSLGW5rjsz/O6lDuomgQG+EVSnWa7GTIPBXj1BmDXXp3XxeldYmhf4UzaN5BA+RUA5E8NChNKuNNof76j2S9tilfN/kvpy4fw="));
+    example_ns->addRRsig(rrsig);
+
+    example_soa = RRsetPtr(new RRset(example, RRClass::IN(),
+                                    RRType::SOA(), RRTTL(3600)));
+    example_soa->addRdata(generic::SOA("master.example.com. admin.example.com. 1234 3600 1800 2419200 7200"));
+
+    rp = new RRset(example, RRClass::IN(), RRType::RRSIG(), RRTTL(3600));
+    rrsig = RRsetPtr(rp);
+    rrsig->addRdata(generic::RRSIG("SOA 5 2 3600 20100322084538 20100220084538 33495 example.com.  KUun66Qaw36osk2BJS6U1fAy3PPDkNo2QK4meGNbDBY8q8b+f2o+IXJ14YCvssGl1ORW0CcLnDRxssnk8V/Svmj5iFhO+8HC2hnVBdi2zewvdVtwRb+lWwKN7pkXXwuy6g1t9WCd/j5FCc/wgxqtZUTPb6XgZcnHrORDMOTqLs4="));
+    example_soa->addRRsig(rrsig);
+
+    example_nsec = RRsetPtr(new RRset(example, RRClass::IN(),
+                                      RRType::NSEC(), RRTTL(3600)));
+    example_nsec->addRdata(generic::NSEC("cname-ext.example.com. NS SOA MX RRSIG NSEC DNSKEY"));
+    rp = new RRset(example, RRClass::IN(), RRType::RRSIG(), RRTTL(3600));
+    rrsig = RRsetPtr(rp);
+
+    rrsig->addRdata(generic::RRSIG("NSEC 5 2 7200 20100322084538 20100220084538 33495 example.com. KxuVaPPKNPJzr/q+cJPiNlkHVTQK0LVsgTbSqruXQc25lAd0wn5oKUtxL1bEAchHkfA8eLzcYCj2ZqqAv9OJubw53mfskTad7UHs4Uj2RTrIsNGMCiZGgOpvNb9JcWpQtoyXVT1uNse+Qsbeir0eyeYIufUynFU041jtNrlJMio="));
+    example_nsec->addRRsig(rrsig);
+
+    // sql1.example.com
+    sql1_ns = RRsetPtr(new RRset(sql1, RRClass::IN(),
+                                 RRType::NS(), RRTTL(3600)));
+    sql1_ns->addRdata(generic::NS(dns01));
+    sql1_ns->addRdata(generic::NS(dns02));
+    sql1_ns->addRdata(generic::NS(dns03));
+
+    rp = new RRset(sql1, RRClass::IN(), RRType::RRSIG(), RRTTL(3600));
+    rrsig = RRsetPtr(rp);
+    rrsig->addRdata(generic::RRSIG("NS 5 3 3600 20100322084536 20100220084536 12447 sql1.example.com. 0CL8noy0NSgoWwuKd+Dc6vyIIw2BrAEBx0IJzcSB6GlB25x/zjEd6AJG0be13HN6jOaTX8iWTuCVrEYuXg76V+M4EvTZHjEScj0az74TrDv4Vdo459paGKCX9B8NLJW1mW4fzZrrXQ8jmBEZeS91Q5rJrO+UKJEuUz3LYdTPvao="));
+    sql1_ns->addRRsig(rrsig);
+
+    sql1_soa = RRsetPtr(new RRset(sql1, RRClass::IN(),
+                                 RRType::SOA(), RRTTL(3600)));
+    sql1_soa->addRdata(generic::SOA("master.example.com. admin.example.com. 678 3600 1800 2419200 7200"));
+
+    rp = new RRset(sql1, RRClass::IN(), RRType::RRSIG(), RRTTL(3600));
+    rrsig = RRsetPtr(rp);
+    rrsig->addRdata(generic::RRSIG("SOA 5 3 3600 20100322084536 20100220084536 12447 sql1.example.com. oakulfyljL/RAKgCKXEZ3KsG8BJj5WG4JK4moWFB6c9OKem6jIk8hKP2XlUVXFuOYJlRdIM4KicmR2GAK+5jJp6z5ShssstYTXo3QosVm6oCKumuFeLFHzcjfqP1D+F9NsvHldJIBnS/4ebPkmR5OENyCZXQF5HmN2awIj4CLjE="));
+    sql1_soa->addRRsig(rrsig);
+
+    sql1_nsec = RRsetPtr(new RRset(sql1, RRClass::IN(),
+                                      RRType::NSEC(), RRTTL(3600)));
+    sql1_nsec->addRdata(generic::NSEC("www.sql1.example.com. NS SOA RRSIG NSEC DNSKEY"));
+    rp = new RRset(sql1, RRClass::IN(), RRType::RRSIG(), RRTTL(3600));
+    rrsig = RRsetPtr(rp);
+
+    rrsig->addRdata(generic::RRSIG("NSEC 5 3 7200 20100322084536 20100220084536 12447 sql1.example.com. v71CgdTYccCiTqfRcn6HsvISQa8ruvUfCKtpwym0RW/G27xlZn8otj2IMtWwkLxti8Rqqu+PTViLaOIbeVfHBcqzAd7U59cAOYoq3ODZx6auiE3C23HAKqUavKcP7Esaajm1cbcWy6Kyie4CAZc8M7EeKxgkXMKJGqBQzF+/FOo="));
+    sql1_nsec->addRRsig(rrsig);
+    sql1_ds = RRsetPtr(new RRset(sql1, RRClass::IN(),
+                                 RRType::DS(), RRTTL(3600)));
+    sql1_ds->addRdata(generic::DS("33313 5 1 0FDD7A2C11AA7F55D50FBF9B7EDDA2322C541A8D"));
+    sql1_ds->addRdata(generic::DS("33313 5 2 00B99B7006F496D135B01AB17EDB469B4BE9E1973884DEA757BC4E3015A8C3AB"));
+
+    rp = new RRset(sql1, RRClass::IN(), RRType::RRSIG(), RRTTL(3600));
+    rrsig = RRsetPtr(rp);
+    rrsig->addRdata(generic::RRSIG("DS 5 3 3600 20100322084538 20100220084538 33495 example.com. dIqZKvpkJN1l92SOiWgJh3KbjErIN+EfojMsm4pEdV5xQdZwj6DNNEu6Kw4rRwdvrZIu0TyqPr3jSJb7o6R7vZgZzmLfVV/ojQah7rwuYHCFcfyZ4JyK2311fMhRR1QAvMsdcjdyA1XC140Cm6AnL3cH5rh/KUks/0ec3Ca7GNQ="));
+    sql1_ds->addRRsig(rrsig);
+
+
+    sql1_ds_nsec = RRsetPtr(new RRset(sql1, RRClass::IN(),
+                                   RRType::NSEC(), RRTTL(3600)));
+    sql1_ds_nsec->addRdata(generic::NSEC("subzone.example.com. NS DS RRSIG NSEC"));
+    rp = new RRset(sql1, RRClass::IN(), RRType::RRSIG(), RRTTL(3600));
+    rrsig = RRsetPtr(rp);
+    rrsig->addRdata(generic::RRSIG("NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. k9FRdFyk/cPdkmmaoZbGZPpzIzfbFWQ3QCHd2qhJa0xAXaEOT/GBL6aFqx9SlunDu2wgES+To5fWPZGi4NzWpp6c5t27rnATN/oCEQ/UYIJKmWbqrXdst0Ps5boznk7suK2Y+km31KxaIf3fDd/T3kZCVsR0aWKRRRatPb7GfLw="));
+    sql1_ds_nsec->addRRsig(rrsig);
+
+    // www.sql1.example.com
+    www_sql1_a = RRsetPtr(new RRset(www_sql1,
+                                    RRClass::IN(), RRType::A(),
+                                    RRTTL(3600)));
+    www_sql1_a->addRdata(in::A("192.168.2.2"));
+
+    rp = new RRset(www_sql1, RRClass::IN(), RRType::RRSIG(), RRTTL(3600));
+    rrsig = RRsetPtr(rp);
+    rrsig->addRdata(generic::RRSIG("A 5 4 3600 20100322084536 20100220084536 12447 sql1.example.com. DNdVKxB3oBsB14NPoV9WG14Y/g4zMcIXLYnFjj9vRZRZJpAvbTEipiXlayuhOxnqU827OipETQyeULZmLsqIQ1wK4Fgf+9b5aJ8D85/o4wBka00X4hZ3MwDPRb4mjuogwBTBg5NRpNSzUfbkPGiav08BFwgg+Efm9veSB05arS0="));
+    www_sql1_a->addRRsig(rrsig);
+
+    www_sql1_nsec = RRsetPtr(new RRset(www_sql1,
+                                       RRClass::IN(), RRType::NSEC(),
+                                       RRTTL(3600)));
+    www_sql1_nsec->addRdata(generic::NSEC("sql1.example.com. A RRSIG NSEC"));
+    rp = new RRset(www_sql1, RRClass::IN(), RRType::RRSIG(), RRTTL(3600));
+    rrsig = RRsetPtr(rp);
+
+    rrsig->addRdata(generic::RRSIG("NSEC 5 4 7200 20100322084536 20100220084536 12447 sql1.example.com. cJMJhDx/ND7/9j3zhyXe+6eaSsU7ByYpXhJzbe+OhjFgH0VasQXq7o1QB3I293UZ+yhkjgXap+9QtPlraaNaYyTyOMQ42OoxSefJpYz9CME/FI2tsUfyrCnLFxYRNet7sMS0q+hLqxRayuEHDFDp72hHPGLJQ8a7jq4SrIonT50="));
+    www_sql1_nsec->addRRsig(rrsig);
+
+    // dns01.example.com
+    dns01_a = RRsetPtr(new RRset(dns01,
+                                         RRClass::IN(), RRType::A(),
+                                         RRTTL(3600)));
+    dns01_a->addRdata(in::A("192.168.2.1"));
+
+    rp = new RRset(dns01, RRClass::IN(), RRType::RRSIG(), RRTTL(3600));
+    rrsig = RRsetPtr(rp);
+    rrsig->addRdata(generic::RRSIG("A 5 3 3600 20100322084538 20100220084538 33495 example.com. NIawlZLk8WZAjNux7oQM2mslfW52OZFFkWt++7FHu2SU98XqEeKfCMnpgtWe5T8Nr9cS8df901iEOJoWQzGTEaHYUBtEhsSjBVn7mKp3fz6473a2xxy75SUKZ0rxjNXSZ8Q5rnFmkX0HTH2Sg51mtjH6aC2pfheQnA2t193BnSg="));
+    dns01_a->addRRsig(rrsig);
+
+    dns01_nsec = RRsetPtr(new RRset(dns01, RRClass::IN(), RRType::NSEC(), RRTTL(3600)));
+    dns01_nsec->addRdata(generic::NSEC("dns02.example.com. A RRSIG NSEC"));
+
+    rp = new RRset(dns01, RRClass::IN(), RRType::RRSIG(), RRTTL(3600));
+    rrsig = RRsetPtr(rp);
+    rrsig->addRdata(generic::RRSIG("NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. EkyeshmMNP9xiAz6mDFDIwksTdmkF9zsFzLuVKAgK6eUk7St6tp5PSvjA8nWol0vdvvz4LK85a4ffTFEiNRyvWeYP2vOhEkyDcrwuCd8Vc3jh/8Sm1Js+nX7hJStrZGFvp2TWPpt9nKH5p3MxXvTb/YVurnue0xSeFAE17O3+I0="));
+    dns01_nsec->addRRsig(rrsig);
+
+    // dns02.example.com
+    dns02_a = RRsetPtr(new RRset(dns02, RRClass::IN(), RRType::A(), RRTTL(3600)));
+    dns02_a->addRdata(in::A("192.168.2.2"));
+
+    rp = new RRset(dns02, RRClass::IN(), RRType::RRSIG(), RRTTL(3600));
+    rrsig = RRsetPtr(rp);
+    rrsig->addRdata(generic::RRSIG("A 5 3 3600 20100322084538 20100220084538 33495 example.com. XJtVMbUIRE0mk6Hn/Nx6k36jaxaBDPK2/IYB6vCQjJETz6gW4T6q/H/eY9/Lsw5iYPFhoBRDxT4XFj575t98kELXnJe1WhuMbRPlOhyOjxkLECaUne/sbFPOtbGFx9ohuojI0RgxxZiCFaO8wJuv6nfPuzmlLajWS6z9NZeOMIk="));
+    dns02_a->addRRsig(rrsig);
+
+    dns02_nsec = RRsetPtr(new RRset(dns02, RRClass::IN(),
+                                      RRType::NSEC(), RRTTL(3600)));
+    dns02_nsec->addRdata(generic::NSEC("dns03.example.com. A RRSIG NSEC"));
+    rp = new RRset(dns02, RRClass::IN(), RRType::RRSIG(), RRTTL(3600));
+    rrsig = RRsetPtr(rp);
+
+    rrsig->addRdata(generic::RRSIG("NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. imBNTMB3sPU4kblcaAH6V7lCVt5xgtAybi3DA/SbLEulLaV2NE6vcoEn/AieaM4mOJicQnUDj/H+1hSEhzxU2tRM8zfVlvztxQWn6eh7ZR4mKfNDSvRUGU9ykhpwMyC7wjOt1j5bcSA/OTnLRAilslnJyOM4bSaxVEFo8YPjncY="));
+    dns02_nsec->addRRsig(rrsig);
+
+    // dns03.example.com
+    dns03_a = RRsetPtr(new RRset(dns03,
+                                         RRClass::IN(), RRType::A(),
+                                         RRTTL(3600)));
+    dns03_a->addRdata(in::A("192.168.2.3"));
+
+    rp = new RRset(dns03, RRClass::IN(), RRType::RRSIG(), RRTTL(3600));
+    rrsig = RRsetPtr(rp);
+    rrsig->addRdata(generic::RRSIG("A 5 3 3600 20100322084538 20100220084538 33495 example.com. Ubrcm1H+F6m8khle7P9zU8eO+Jtuj+1Vx1MM5KAkmZPJwQe9uTcoCpQa6DXOGG9kajDTnNN1Be1gkZuJDTZJG4SmJLXLbNY3RDnxpGmWta3qs/VgDq78/YM8ropt1/s7YKyrCfGE2ff+FUB0mLObiG01ZV2gu5HJzgE7SEWLEiI="));
+    dns03_a->addRRsig(rrsig);
+
+    dns03_nsec = RRsetPtr(new RRset(dns03, RRClass::IN(),
+                                      RRType::NSEC(), RRTTL(3600)));
+    dns03_nsec->addRdata(generic::NSEC("foo.example.com. A RRSIG NSEC"));
+    rp = new RRset(dns03, RRClass::IN(), RRType::RRSIG(), RRTTL(3600));
+    rrsig = RRsetPtr(rp);
+
+    rrsig->addRdata(generic::RRSIG("NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com.  nn829Xw5CJFnPHwI9WHeT5epQv+odtCkHnjlPFGoPTLOyiks+041UmMqtq3uiSp4d2meMSe9UuDvoROT0L6NTtQQvVqiDhTn0irTFw1uw7fO8ZTG7eyu6Ypfz0+HvfbNvd4kMoD2OTgADRXPVsCTwK+PBOIIG9YTEQfl8pCqW5g="));
+    dns03_nsec->addRRsig(rrsig);
+
+    // www.example.com
+    www_a = RRsetPtr(new RRset(www, RRClass::IN(), RRType::A(),
+                                       RRTTL(3600)));
+    www_a->addRdata(in::A("192.168.1.1"));
+
+    rp = new RRset(www, RRClass::IN(), RRType::RRSIG(), RRTTL(3600));
+    rrsig = RRsetPtr(rp);
+    rrsig->addRdata(generic::RRSIG("A 5 3 3600 20100322084538 20100220084538 33495 example.com. qyFyyV/mE8x4pdhudr5iycwhDsva31MzwO1kBR+bDKvzJg8mN8KxlPZrOlNNUhd3YRXQVwieMyxOTWRPXoxrNEDkNwimXkfe3rrHY7ibV9eNS4OIBUjb44VjCNr9CmQSzfuQ2yxO2r+YIuPYHRCjieD4xh6t9ay4IaCN/tDAJ+Q="));
+    www_a->addRRsig(rrsig);
+
+    www_nsec = RRsetPtr(new RRset(www, RRClass::IN(),
+                                  RRType::NSEC(), RRTTL(3600)));
+    www_nsec->addRdata(generic::NSEC("example.com. A RRSIG NSEC"));
+    rp = new RRset(www, RRClass::IN(), RRType::RRSIG(), RRTTL(3600));
+    rrsig = RRsetPtr(rp);
+
+    rrsig->addRdata(generic::RRSIG("NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. ZLZlSVBa2oe4U+7SZASnypP2VkI5gg1/1cVGqYUvfYNIUkcVMWDgn7DZCfpmo+2vdlV/4VhAc+sjDd+X+e57XGnW8+lqZHvG6NMMhmSGmeATD3D+8lEJJGo0dxoN4rHJQyp/eT2S4nChz+D/ze+YRagYxGF7pXm9zcrw3kKZGTs="));
+    www_nsec->addRRsig(rrsig);
+
+    // *.wild.example.com
+    wild_a = RRsetPtr(new RRset(wild, RRClass::IN(), RRType::A(),
+                                        RRTTL(3600)));
+    wild_a->addRdata(in::A("192.168.3.2"));
+
+    rp = new RRset(wild, RRClass::IN(), RRType::RRSIG(), RRTTL(3600));
+    rrsig = RRsetPtr(rp);
+    rrsig->addRdata(generic::RRSIG("A 5 3 3600 20100322084538 20100220084538 33495 example.com. FdO+UWONgtLKFxUzzygGunw67F9y8SzsP7yOLEYVJclRR8X3Ii62L0gtQHq2y0TcKsXttRsD6XY+tM5P/pgXlTNi7Bk4Fgb0PIDPjOsfT4DrS80kWn0YbinM/4/FA1j5ru5sTTboOY5UGhvDnoA9ogNuQQYb2/3wkoH0PrA2Q/0="));
+    wild_a->addRRsig(rrsig);
+
+    wild_nsec = RRsetPtr(new RRset(wild, RRClass::IN(),
+                                   RRType::NSEC(), RRTTL(3600)));
+    wild_nsec->addRdata(generic::NSEC("www.example.com. A RRSIG NSEC"));
+    rp = new RRset(wild, RRClass::IN(), RRType::RRSIG(), RRTTL(3600));
+    rrsig = RRsetPtr(rp);
+
+    rrsig->addRdata(generic::RRSIG("NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. OoGYslRj4xjZnBuzgOqsrvkDAHWycmQzbUxCRmgWnCbXiobJK7/ynONH3jm8G3vGlU0lwpHkhNs6cUK+6Nu8W49X3MT0Xksl/brroLcXYLi3vfxnYUNMMpXdeFl6WNNfoJRo90F/f/TWXAClRrDS29qiG3G1PEJZikIxZsZ0tyM="));
+    wild_nsec->addRRsig(rrsig);
+
+    // foo.example.com
+    foo_cname = RRsetPtr(new RRset(foo, RRClass::IN(), RRType::CNAME(),
+                                           RRTTL(3600)));
+    foo_cname->addRdata(generic::CNAME("cnametest.flame.org"));
+
+    rp = new RRset(foo, RRClass::IN(), RRType::RRSIG(), RRTTL(3600));
+    rrsig = RRsetPtr(rp);
+    rrsig->addRdata(generic::RRSIG("CNAME 5 3 3600 20100322084538 20100220084538 33495 example.com. DSqkLnsh0gCeCPVW/Q8viy9GNP+KHmFGfWqyVG1S6koBtGN/VQQ16M4PHZ9Zssmf/JcDVJNIhAChHPE2WJiaPCNGTprsaUshf1Q2vMPVnkrJKgDY8SVRYMptmT8eaT0gGri4KhqRoFpMT5OYfesybwDgfhFSQQAh6ps3bIUsy4o="));
+    foo_cname->addRRsig(rrsig);
+
+    foo_nsec = RRsetPtr(new RRset(foo, RRClass::IN(),
+                                  RRType::NSEC(), RRTTL(3600)));
+    foo_nsec->addRdata(generic::NSEC("mail.example.com. CNAME RRSIG NSEC"));
+    rp = new RRset(foo, RRClass::IN(), RRType::RRSIG(), RRTTL(3600));
+    rrsig = RRsetPtr(rp);
+
+    rrsig->addRdata(generic::RRSIG("NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. RTQwlSqui6StUYye1KCSOEr1d3irndWFqHBpwP7g7n+w8EDXJ8I7lYgwzHvlQt6BLAxe5fUDi7ct8M5hXvsm7FoWPZ5wXH+2/eJUCYxIw4vezKMkMwBP6M/YkJ2CMqY8DppYf60QaLDONQAr7AcK/naSyioeI5h6eaoVitUDMso="));
+    foo_nsec->addRRsig(rrsig);
+
+    // cname-int.example.com
+    cnameint_cname = RRsetPtr(new RRset(cnameint, RRClass::IN(),
+                                        RRType::CNAME(), RRTTL(3600)));
+    cnameint_cname->addRdata(generic::CNAME("www.example.com"));
+
+    rp = new RRset(cnameint, RRClass::IN(), RRType::RRSIG(), RRTTL(3600));
+    rrsig = RRsetPtr(rp);
+    rrsig->addRdata(generic::RRSIG("CNAME 5 3 3600 20100322084538 20100220084538 33495 example.com. U1wjt0XY9xjTwvUmWSUcfLGMhCjfX2ylWfHrycy50x2oxcK9z94E1ejen9wDTIEBSGYgi6wpZ8RK0+02N1DWTGpDqNXd7aFRfDrWQJ/q/XJHDx0vlcmhkWhrT82LBfKxkrptOzchuSo/c0mpK+mpiIMc1VOwY+yuQ2ALfcD6EHw="));
+    cnameint_cname->addRRsig(rrsig);
+
+    cnameint_nsec = RRsetPtr(new RRset(cnameint, RRClass::IN(),
+                                      RRType::NSEC(), RRTTL(3600)));
+    cnameint_nsec->addRdata(generic::NSEC("dname.example.com. CNAME RRSIG NSEC"));
+    rp = new RRset(cnameint, RRClass::IN(), RRType::RRSIG(), RRTTL(3600));
+    rrsig = RRsetPtr(rp);
+
+    rrsig->addRdata(generic::RRSIG("NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. rbV+gaxfrsoha59NOLF4EFyWQ+GuFCVK/8D77x1atan3HNlXBlZ1smgudKTaJ3CtlobIDt0MEdPxY1yn2Tskw/5mlP1PWf8oaP3BwGSQdn4gLI8+sMpNOPFEdXpxqxngm2F6/7fqniL1QuSAQBEdO+5UiCAgnncPmAsSJg3u1zg="));
+    cnameint_nsec->addRRsig(rrsig);
+
+    // cname-ext.example.com
+    cnameext_cname = RRsetPtr(new RRset(cnameext, RRClass::IN(),
+                                        RRType::CNAME(), RRTTL(3600)));
+    cnameext_cname->addRdata(generic::CNAME("www.sql1.example.com"));
+
+    rp = new RRset(cnameext, RRClass::IN(), RRType::RRSIG(), RRTTL(3600));
+    rrsig = RRsetPtr(rp);
+    rrsig->addRdata(generic::RRSIG("CNAME 5 3 3600 20100322084538 20100220084538 33495 example.com. bGPIuZilyygvTThK4BrdECuaBcnZUgW/0d09iN2CrNjckchQl3dtbnMNirFsVs9hShDSldRNlQpiAVMpnPgXHhReNum7jmX6yqIH6s8GKIo91zr3VL/ramlezie5w4MilDHrxXLK2pb8IHmP+ZHivQ2EtdYQZgETWBWxr5FDfwk="));
+    cnameext_cname->addRRsig(rrsig);
+
+    cnameext_nsec = RRsetPtr(new RRset(cnameext, RRClass::IN(),
+                                      RRType::NSEC(), RRTTL(3600)));
+    cnameext_nsec->addRdata(generic::NSEC("cname-int.example.com. CNAME RRSIG NSEC"));
+    rp = new RRset(cnameext, RRClass::IN(), RRType::RRSIG(), RRTTL(3600));
+    rrsig = RRsetPtr(rp);
+
+    rrsig->addRdata(generic::RRSIG("NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. inWsFwSDWG7TakjwbUTzTRpXz0WifelA5Kn3ABk6BVirIPmd+yQoNj2QZBDFAQwhnLPlNws2Oo4vgMsBMyx1Fv5eHgMUuCN3DUDaLlzlPtUb42CjOUa+jZBeTV/Hd7WZrirluASE1QFDprLdSSqoPPfAKvN3pORtW7y580dMOIM="));
+    cnameext_nsec->addRRsig(rrsig);
+
+    // dname.example.com
+    dname_dname = RRsetPtr(new RRset(dname, RRClass::IN(), RRType::DNAME(),
+                               RRTTL(3600)));
+    dname_dname->addRdata(generic::DNAME("sql1.example.com."));
+
+    rp = new RRset(dname, RRClass::IN(), RRType::RRSIG(), RRTTL(3600));
+    rrsig = RRsetPtr(rp);
+    rrsig->addRdata(generic::RRSIG("DNAME 5 3 3600 20100322084538 20100220084538 33495 example.com. ae8U47oaiwWdurkSyzcsCAF6DxBqjukizwF7K7U6lQVMtfoUE14oiAqfj1fjH8YLDOO/Hd1twrd/u0vgjnI1Gg32YTi7cYOzwE912SV1u2B/y0awaQKWPBwOW0aI7vxelt1vMUF81xosiQD04gOIdDBTqbHKcDxum87iWbhk4Ug="));
+    dname_dname->addRRsig(rrsig);
+
+    dname_nsec = RRsetPtr(new RRset(dname, RRClass::IN(),
+                                      RRType::NSEC(), RRTTL(3600)));
+    dname_nsec->addRdata(generic::NSEC("dns01.example.com. DNAME RRSIG NSEC"));
+    rp = new RRset(dname, RRClass::IN(), RRType::RRSIG(), RRTTL(3600));
+    rrsig = RRsetPtr(rp);
+
+    rrsig->addRdata(generic::RRSIG("NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. c21Fff2D8vBrLzohBnUeflkaRdUAnUxAFGp+UQ0miACDCMOFBlCS9v9g/2+orOnKfd3l4vyz55C310t8JXgXb119ofaZWj2zkdUe+X8Bax+sMS0Y5K/sUhSNvbJbozr9UYPdvjSVBiWgh3s9fsb+etKq9uFukAzGU/FuGYpO0r0="));
+    dname_nsec->addRRsig(rrsig);
+
+    // subzone.example.com
+    subzone_ns = RRsetPtr(new RRset(subzone, RRClass::IN(), RRType::NS(),
+                                    RRTTL(3600)));
+    subzone_ns->addRdata(generic::NS(Name("ns1.subzone.example.com")));
+    subzone_ns->addRdata(generic::NS(Name("ns2.subzone.example.com")));
+
+    subzone_ds = RRsetPtr(new RRset(subzone, RRClass::IN(), RRType::DS(),
+                                    RRTTL(3600)));
+
+    subzone_glue1 = RRsetPtr(new RRset(Name("ns1.subzone.example.com"),
+                                    RRClass::IN(), RRType::A(),
+                                    RRTTL(3600)));
+    subzone_glue1->addRdata(in::A("192.168.3.1"));
+    subzone_glue2 = RRsetPtr(new RRset(Name("ns2.subzone.example.com"),
+                                    RRClass::IN(), RRType::A(),
+                                    RRTTL(3600)));
+    subzone_glue2->addRdata(in::A("192.168.3.2"));
+
+    subzone_ds = RRsetPtr(new RRset(subzone, RRClass::IN(), RRType::DS(),
+                                    RRTTL(3600)));
+
+    subzone_ds->addRdata(generic::DS("33313 5 1 0FDD7A2C11AA7F55D50FBF9B7EDDA2322C541A8D"));
+    subzone_ds->addRdata(generic::DS("33313 5 2 00B99B7006F496D135B01AB17EDB469B4BE9E1973884DEA757BC4E3015A8C3AB"));
+
+    rp = new RRset(subzone, RRClass::IN(), RRType::RRSIG(), RRTTL(3600));
+    rrsig = RRsetPtr(rp);
+    rrsig->addRdata(generic::RRSIG("DS 5 3 3600 20100322084538 20100220084538 33495 example.com. dIqZKvpkJN1l92SOiWgJh3KbjErIN+EfojMsm4pEdV5xQdZwj6DNNEu6Kw4rRwdvrZIu0TyqPr3jSJb7o6R7vZgZzmLfVV/ojQah7rwuYHCFcfyZ4JyK2311fMhRR1QAvMsdcjdyA1XC140Cm6AnL3cH5rh/KUks/0ec3Ca7GNQ="));
+    subzone_ds->addRRsig(rrsig);
+
+    subzone_nsec = RRsetPtr(new RRset(subzone, RRClass::IN(),
+                                      RRType::NSEC(), RRTTL(3600)));
+    subzone_nsec->addRdata(generic::NSEC("*.wild.example.com. NS DS RRSIG NSEC"));
+    rp = new RRset(subzone, RRClass::IN(), RRType::RRSIG(), RRTTL(3600));
+    rrsig = RRsetPtr(rp);
+
+    rrsig->addRdata(generic::RRSIG("NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. Oe2kgIhsLtPJ4+lDZDxznV8/vEVoXKOBFN9lwWyebaKa19BaSXlQ+YVejmulmKDDjEucMvEfuItfn6w7bnU+DzOLk5D1lJCjwDlKz8u3xOAx16TiuQn4bgQAOiFtBQygmGGqO3BVpX+jxsmw7eH3emofy8uUqr/C4aopnwuf28g="));
+    subzone_nsec->addRRsig(rrsig);
+}
+
+void
+TestDataSrc::findClosestEnclosure(NameMatch& match) const {
+    const Name& qname = match.qname();
+    NameComparisonResult::NameRelation cmp;
+
+    cmp = qname.compare(sql1).getRelation();
+    if (cmp == NameComparisonResult::EQUAL ||
+          cmp == NameComparisonResult::SUBDOMAIN) {
+        match.update(*this, sql1);
+        return;
+    }
+
+    cmp = qname.compare(example).getRelation();
+    if (cmp == NameComparisonResult::EQUAL ||
+          cmp == NameComparisonResult::SUBDOMAIN) {
+        match.update(*this, example);
+        return;
+    }
+
+}
+
+void
+TestDataSrc::findRecords(const Name& name, const RRType& rdtype,
+                         RRsetList& target, Name* zone, const Mode mode,
+                         uint32_t& flags) const
+{
+    bool any = (rdtype == RRType::ANY());
+    flags = 0;
+
+
+    if (*zone == sql1) {
+        if (name == sql1 && mode == DELEGATION) {
+            target.addRRset(sql1_ns);
+            flags |= REFERRAL;
+        } else if (name == sql1) {
+            if (any) {
+                target.addRRset(sql1_ns);
+                target.addRRset(sql1_nsec);
+                flags |= REFERRAL;
+            } else if (rdtype == RRType::NS()) {
+                target.addRRset(sql1_ns);
+                flags |= REFERRAL;
+            } else if (rdtype == RRType::SOA()) {
+                target.addRRset(sql1_soa);
+                flags |= REFERRAL;
+            } else if (rdtype == RRType::NSEC()) {
+                target.addRRset(sql1_nsec);
+                flags |= REFERRAL;
+            } else {
+                flags |= TYPE_NOT_FOUND;
+            }
+        } else if (name == www_sql1) {
+            if (any) {
+                target.addRRset(www_sql1_a);
+                target.addRRset(www_sql1_nsec);
+            } else if (rdtype == RRType::A()) {
+                target.addRRset(www_sql1_a);
+            } else if (rdtype == RRType::NSEC()) {
+                target.addRRset(www_sql1_nsec);
+                flags |= REFERRAL;
+            } else {
+                flags |= TYPE_NOT_FOUND;
+            }
+        } else {
+            flags |= NAME_NOT_FOUND;
+        }
+    } else {
+        if (name == example && mode == DELEGATION) {
+            target.addRRset(example_ns);
+            flags |= REFERRAL;
+        } else if (name == example) {
+            if (any) {
+                target.addRRset(example_ns);
+                target.addRRset(example_soa);
+                target.addRRset(example_nsec);
+                flags |= REFERRAL;
+            } else if (rdtype == RRType::NS()) {
+                target.addRRset(example_ns);
+                flags |= REFERRAL;
+            } else if (rdtype == RRType::SOA()) {
+                target.addRRset(example_soa);
+                flags |= REFERRAL;
+            } else if (rdtype == RRType::NSEC()) {
+                target.addRRset(example_nsec);
+                flags |= REFERRAL;
+            } else {
+                flags |= TYPE_NOT_FOUND;
+            }
+        } else if (name == sql1 && mode == DELEGATION) {
+            target.addRRset(sql1_ns);
+            target.addRRset(sql1_ds);
+            target.addRRset(sql1_ds_nsec);
+            flags |= REFERRAL;
+        } else if (name == sql1) {
+            if (any) {
+                target.addRRset(sql1_ns);
+                target.addRRset(sql1_ds);
+                target.addRRset(sql1_ds_nsec);
+                flags |= REFERRAL;
+            } else if (rdtype == RRType::DS()) {
+                target.addRRset(sql1_ds);
+                flags |= REFERRAL;
+            } else if (rdtype == RRType::NS()) {
+                target.addRRset(sql1_ns);
+                flags |= REFERRAL;
+            } else if (rdtype == RRType::NSEC()) {
+                target.addRRset(sql1_ds_nsec);
+                flags |= REFERRAL;
+            } else {
+                flags |= TYPE_NOT_FOUND;
+            }
+        } else if (name == subzone && mode == DELEGATION) {
+            target.addRRset(subzone_ns);
+            target.addRRset(subzone_ds);
+            flags |= REFERRAL;
+        } else if (name == subzone) {
+            if (any) {
+                target.addRRset(subzone_ns);
+                target.addRRset(subzone_nsec);
+                flags |= REFERRAL;
+            } else if (rdtype == RRType::NS()) {
+                target.addRRset(subzone_ns);
+                flags |= REFERRAL;
+            } else if (rdtype == RRType::DS()) {
+                target.addRRset(subzone_ds);
+                flags |= REFERRAL;
+            } else if (rdtype == RRType::NSEC()) {
+                target.addRRset(subzone_nsec);
+                flags |= REFERRAL;
+            } else {
+                flags |= TYPE_NOT_FOUND;
+            }
+        } else if (name == dns01 && mode == ADDRESS) {
+            target.addRRset(dns01_a);
+        } else if (name == dns01) {
+            if (any) {
+                target.addRRset(dns01_a);
+                target.addRRset(dns01_nsec);
+            } else if (rdtype == RRType::A()) {
+                target.addRRset(dns01_a);
+            } else if (rdtype == RRType::NSEC()) {
+                target.addRRset(dns01_nsec);
+            } else {
+                flags |= TYPE_NOT_FOUND;
+            }
+        } else if (name == dns02 && mode == ADDRESS) {
+            target.addRRset(dns02_a);
+        } else if (name == dns02) {
+            if (any) {
+                target.addRRset(dns02_a);
+                target.addRRset(dns02_nsec);
+            } else if (rdtype == RRType::A()) {
+                target.addRRset(dns02_a);
+            } else if (rdtype == RRType::NSEC()) {
+                target.addRRset(dns02_nsec);
+            } else {
+                flags |= TYPE_NOT_FOUND;
+            }
+        } else if (name == dns03 && mode == ADDRESS) {
+            target.addRRset(dns03_a);
+        } else if (name == dns03) {
+            if (any) {
+                target.addRRset(dns03_a);
+                target.addRRset(dns03_nsec);
+            } else if (rdtype == RRType::A()) {
+                target.addRRset(dns03_a);
+            } else if (rdtype == RRType::NSEC()) {
+                target.addRRset(dns03_nsec);
+            } else {
+                flags |= TYPE_NOT_FOUND;
+            }
+        } else if (name == wild) {
+            if (any) {
+                target.addRRset(wild_a);
+                target.addRRset(wild_nsec);
+            } else if (rdtype == RRType::A()) {
+                target.addRRset(wild_a);
+            } else if (rdtype == RRType::NSEC()) {
+                target.addRRset(wild_nsec);
+                flags |= REFERRAL;
+            } else {
+                flags |= TYPE_NOT_FOUND;
+            }
+        } else if (name == www) {
+            if (any) {
+                target.addRRset(www_a);
+                target.addRRset(www_nsec);
+            } else if (rdtype == RRType::A()) {
+                target.addRRset(www_a);
+            } else if (rdtype == RRType::NSEC()) {
+                target.addRRset(www_nsec);
+                flags |= REFERRAL;
+            } else {
+                flags |= TYPE_NOT_FOUND;
+            }
+        } else if (name == foo) {
+            if (rdtype == RRType::NSEC()) {
+                target.addRRset(foo_nsec);
+            } else {
+                target.addRRset(foo_cname);
+                if (rdtype != RRType::CNAME()) {
+                    flags |= CNAME_FOUND;
+                }
+            }
+        } else if (name == cnameint) {
+            if (rdtype == RRType::NSEC()) {
+                target.addRRset(cnameint_nsec);
+            } else {
+                target.addRRset(cnameint_cname);
+                if (rdtype != RRType::CNAME()) {
+                    flags |= CNAME_FOUND;
+                }
+            }
+        } else if (name == cnameext) {
+            if (rdtype == RRType::NSEC()) {
+                target.addRRset(cnameext_nsec);
+            } else {
+                target.addRRset(cnameext_cname);
+                if (rdtype != RRType::CNAME()) {
+                    flags |= CNAME_FOUND;
+                }
+            }
+        } else if (name == dname) {
+            if (any) {
+                target.addRRset(dname_dname);
+                target.addRRset(dname_nsec);
+                flags |= REFERRAL;
+            } else if (rdtype == RRType::DNAME()) {
+                target.addRRset(dname_dname);
+                flags |= REFERRAL;
+            } else if (rdtype == RRType::NSEC()) {
+                target.addRRset(dns01_nsec);
+                flags |= REFERRAL;
+            } else {
+                flags |= TYPE_NOT_FOUND;
+            }
+        } else if (name == Name("ns1.subzone.example.com") && mode == ADDRESS) {
+            target.addRRset(subzone_glue1);
+        } else if (name == Name("ns2.subzone.example.com") && mode == ADDRESS) {
+            target.addRRset(subzone_glue2);
+        } else {
+            flags |= NAME_NOT_FOUND;
+        }
+    }
+    return;
+}
+
+DataSrc::Result
+TestDataSrc::findRRset(const Query& q,
+                       const Name& qname,
+                       const RRClass& qclass,
+                       const RRType& qtype,
+                       RRsetList& target,
+                       uint32_t& flags,
+                       Name* zone) const
+{
+    findRecords(qname, qtype, target, zone, NORMAL, flags);
+    return (SUCCESS);
+}
+
+DataSrc::Result
+TestDataSrc::findExactRRset(const Query& q,
+                            const Name& qname,
+                            const RRClass& qclass,
+                            const RRType& qtype,
+                            RRsetList& target,
+                            uint32_t& flags,
+                            Name* zone) const
+{
+    findRecords(qname, qtype, target, zone, NORMAL, flags);
+    // Ignore referrals in this case
+    flags &= ~REFERRAL;
+
+    // CNAMEs don't count in this case
+    if (flags & CNAME_FOUND) {
+        flags &= ~CNAME_FOUND;
+        flags |= TYPE_NOT_FOUND;
+    }
+
+    return (SUCCESS);
+}
+
+DataSrc::Result
+TestDataSrc::findAddrs(const Query& q,
+                        const Name& qname,
+                        const RRClass& qclass,
+                        RRsetList& target,
+                        uint32_t& flags,
+                        Name* zone) const
+{
+    findRecords(qname, RRType::ANY(), target, zone, ADDRESS, flags);
+    return (SUCCESS);
+}
+
+DataSrc::Result
+TestDataSrc::findReferral(const Query& q,
+                          const Name& qname,
+                          const RRClass& qclass,
+                          RRsetList& target,
+                          uint32_t& flags,
+                          Name* zone) const
+{
+    findRecords(qname, RRType::ANY(), target, zone, DELEGATION, flags);
+    return (SUCCESS);
+}
+
+DataSrc::Result
+TestDataSrc::findPreviousName(const Query& q,
+                              const Name& qname,
+                              Name& target,
+                              Name* zone) const
+{
+    if (*zone == example) {
+        if (qname >= example || qname < cnameext) {
+            target = example;
+        } else if (qname < cnameint) {
+            target = cnameext;
+        } else if (qname < dname) {
+            target = cnameint;
+        } else if (qname < dns01) {
+            target = dname;
+        } else if (qname < dns02) {
+            target = dns01;
+        } else if (qname < dns03) {
+            target = dns02;
+        } else if (qname < foo) {
+            target = dns03;
+        } else if (qname < sql1) {
+            target = foo;
+        } else if (qname < subzone) {
+            target = sql1;
+        } else if (qname < www) {
+            target = subzone;
+        } else if (qname < wild) {
+            target = www;
+        } else {
+            target = wild;
+        }
+    } else {
+        if (qname >= sql1 || qname < www_sql1) {
+            target = sql1;
+        } else {
+            target = www_sql1;
+        }
+    }
+    return (SUCCESS);
+}
+
+}
+}

+ 137 - 0
src/lib/auth/cpp/unittest_ds.h

@@ -0,0 +1,137 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#ifndef __TEST_DATA_SOURCE_H
+#define __TEST_DATA_SOURCE_H
+
+#include <gtest/gtest.h>
+#include "unittest_util.h"
+
+using isc::UnitTestUtil;
+using namespace isc::dns;
+
+#include "data_source.h"
+
+namespace isc {
+namespace auth {
+class TestDataSrc : public DataSrc {
+public:
+    TestDataSrc();
+    ~TestDataSrc() {}
+
+    void findClosestEnclosure(NameMatch& match) const;
+
+    Result findRRset(const Query& q,
+                     const Name& qname,
+                     const RRClass& qclass,
+                     const RRType& qtype,
+                     RRsetList& target,
+                     uint32_t& flags,
+                     Name* zone = NULL) const;
+
+    Result findExactRRset(const Query& q,
+                          const Name& qname,
+                          const RRClass& qclass,
+                          const RRType& qtype,
+                          RRsetList& target,
+                          uint32_t& flags,
+                          Name* zone = NULL) const;
+
+    Result findAddrs(const Query& q,
+                     const Name& qname,
+                     const RRClass& qclass,
+                     RRsetList& target,
+                     uint32_t& flags,
+                     Name* zone = NULL) const;
+
+    Result findReferral(const Query& q,
+                        const Name& qname,
+                        const RRClass& qclass,
+                        RRsetList& target,
+                        uint32_t& flags,
+                        Name* zone = NULL) const;
+
+    Result findPreviousName(const Query& q,
+                            const Name& qname,
+                            Name& target,
+                            Name* zone) const;
+
+    Result init() { return (SUCCESS); }
+    Result close() { return (SUCCESS); }
+
+private:
+    enum Mode {
+        NORMAL,
+        ADDRESS,
+        DELEGATION
+    };
+
+    void findRecords(const Name& name, const RRType& rdtype,
+                     RRsetList& target, Name* zone, const Mode mode,
+                     uint32_t& flags) const;
+
+    const Name example;
+    const Name sql1;
+    const Name www_sql1;
+    const Name www;
+    const Name foo;
+    const Name dns01;
+    const Name dns02;
+    const Name dns03;
+    const Name cnameint;
+    const Name cnameext;
+    const Name dname;
+    const Name wild;
+    const Name subzone;
+    RRsetPtr example_ns;
+    RRsetPtr example_soa;
+    RRsetPtr example_nsec;
+    RRsetPtr www_a;
+    RRsetPtr www_nsec;
+    RRsetPtr foo_cname;
+    RRsetPtr foo_nsec;
+    RRsetPtr cnameint_cname;
+    RRsetPtr cnameint_nsec;
+    RRsetPtr cnameext_cname;
+    RRsetPtr cnameext_nsec;
+    RRsetPtr dns01_a;
+    RRsetPtr dns01_nsec;
+    RRsetPtr dns02_a;
+    RRsetPtr dns02_nsec;
+    RRsetPtr dns03_a;
+    RRsetPtr dns03_nsec;
+    RRsetPtr wild_a;
+    RRsetPtr wild_nsec;
+    RRsetPtr dname_dname;
+    RRsetPtr dname_nsec;
+    RRsetPtr sql1_ns;
+    RRsetPtr sql1_soa;
+    RRsetPtr sql1_nsec;
+    RRsetPtr sql1_ds;
+    RRsetPtr sql1_ds_nsec;
+    RRsetPtr www_sql1_a;
+    RRsetPtr www_sql1_nsec;
+    RRsetPtr subzone_ns;
+    RRsetPtr subzone_nsec;
+    RRsetPtr subzone_glue1;
+    RRsetPtr subzone_glue2;
+    RRsetPtr subzone_ds;
+};
+
+}
+}
+
+#endif

+ 127 - 0
src/lib/auth/cpp/unittest_util.cc

@@ -0,0 +1,127 @@
+// Copyright (C) 2009  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id: unittest_util.cc 754 2010-02-08 22:28:56Z each $
+
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <stdexcept>
+#include <vector>
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include <dns/name.h>
+#include "unittest_util.h"
+
+using isc::UnitTestUtil;
+using isc::dns::NameComparisonResult;
+
+namespace isc {
+
+void
+UnitTestUtil::readWireData(const char* datafile,
+                           std::vector<unsigned char>& data)
+{
+    std::ifstream ifs;
+ 
+    ifs.open(datafile, std::ios_base::in);
+    if ((ifs.rdstate() & std::istream::failbit) != 0) {
+        throw std::runtime_error("failed to open data file: " +
+                                 std::string(datafile));
+    }
+
+    data.clear();
+
+    std::string s;
+    while (getline(ifs, s), !ifs.eof()) {
+        if (ifs.bad() || ifs.fail()) {
+            throw std::runtime_error("unexpected data line");
+        }
+        if (s.empty() || s[0] == '#') {
+            continue;
+        }
+
+        readWireData(s, data);
+    }
+}
+
+void
+UnitTestUtil::readWireData(const std::string& datastr,
+                           std::vector<unsigned char>& data)
+{
+    std::istringstream iss(datastr);
+
+    do {
+        std::string bytes;
+        iss >> bytes;
+        if (iss.bad() || iss.fail() || (bytes.size() % 2) != 0) {
+            throw std::runtime_error("unexpected input or I/O error");
+        }
+
+        for (int pos = 0; pos < bytes.size(); pos += 2) {
+            unsigned int ch;
+            std::istringstream(bytes.substr(pos, 2)) >> std::hex >> ch;
+            data.push_back(static_cast<unsigned char>(ch));
+        }
+    } while (!iss.eof());
+}
+
+::testing::AssertionResult
+UnitTestUtil::matchWireData(const char* dataexp1, const char* lenexp1,
+                            const char* dataexp2, const char* lenexp2,
+                            const void* data1, size_t len1,
+                            const void* data2, size_t len2)
+{
+    ::testing::Message msg;
+    size_t cmplen = std::min(len1, len2);
+
+    for (int i = 0; i < cmplen; i++) {
+        int ch1 = static_cast<const uint8_t*>(data1)[i];
+        int ch2 = static_cast<const uint8_t*>(data2)[i];
+        if (ch1 != ch2) {
+            msg << "Wire data mismatch at " << i << "th byte\n"
+                << "  Actual: " << ch1 << "\n"
+                << "Expected: " << ch2 << "\n";
+            return (::testing::AssertionFailure(msg));
+        }
+    }
+    if (len1 != len2) {
+        msg << "Wire data mismatch in length:\n"
+            << "  Actual: " << len1 << "\n"
+            << "Expected: " << len2 << "\n";
+        return (::testing::AssertionFailure(msg));
+    }
+    return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult
+UnitTestUtil::matchName(const char* nameexp1, const char* nameexp2,
+                        const isc::dns::Name& name1,
+                        const isc::dns::Name& name2)
+{
+    ::testing::Message msg;
+
+    NameComparisonResult cmpresult = name1.compare(name2);
+    if (cmpresult.getOrder() != 0 ||
+        cmpresult.getRelation() != NameComparisonResult::EQUAL) {
+        msg << "Two names are expected to be equal but not:\n"
+            << "  One: " << name1 << "\n"
+            << "Other: " << name2 << "\n";
+        return (::testing::AssertionFailure(msg));
+    }
+    return ::testing::AssertionSuccess();
+}
+}

+ 87 - 0
src/lib/auth/cpp/unittest_util.h

@@ -0,0 +1,87 @@
+// Copyright (C) 2009  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#ifndef __UNITTEST_UTIL_H
+#define __UNITTEST_UTIL_H 1
+
+#include <vector>
+#include <string>
+
+#include <dns/name.h>
+
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace isc::dns;
+
+namespace isc {
+
+class UnitTestUtil {
+public:
+    ///
+    /// read text format wire data from a file and put it to the given vector.
+    ///
+    static void readWireData(const char*datafile,
+                             std::vector<unsigned char>& data);
+
+    ///
+    /// convert a sequence of hex strings into the corresponding list of
+    /// 8-bit integers, and append them to the vector.
+    ///
+    static void readWireData(const std::string& datastr,
+                             std::vector<unsigned char>& data);
+
+    ///
+    /// Compare len1 bytes of data1 with len2 bytes of data2 as binary data.
+    ///
+    /// If they don't match report the point of mismatch in the google test
+    /// format.  This method is expected to be used from the EXPECT_PRED_FORMAT4
+    /// macro of google test as follows:
+    /// \code EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
+    ///                           actual_data, actual_data_len,
+    ///                           expected_data, expected_data_len); \endcode
+    /// Parameters from dataexp1 to lenexp2 are passed via the macro but will
+    /// be ignored by this method.
+    /// Note: newer versions of google test supports the direct use of
+    /// AssertionResult with the EXPECT_TRUE macro, which would be more
+    /// intuitive, but to be as compatible as possible we use the more primitive
+    /// macro, i.e., EXPECT_PRED_FORMAT4.
+    ///
+    static ::testing::AssertionResult
+    matchWireData(const char* dataexp1, const char* lenexp1,
+                  const char* dataexp2, const char* lenexp2,
+                  const void* data1, size_t len1,
+                  const void* data2, size_t len2);
+
+    ///
+    /// Compare two names.
+    ///
+    /// This check method uses \c Name::compare() for comparison, which performs
+    /// deeper checks including the equality of offsets, and should be better
+    /// than EXPECT_EQ, which uses operater==.  Like the \c matchWireData()
+    /// method, the usage is a bit awkward; the caller should use
+    /// \c EXPECT_PRED_FORMAT2.
+    ///
+    static ::testing::AssertionResult
+    matchName(const char* nameexp1, const char* nameexp2,
+              const Name& name1, const Name& name2);
+};
+}
+#endif // __UNITTEST_UTIL_H
+
+// Local Variables: 
+// mode: c++
+// End: 

+ 2 - 0
src/lib/dns/cpp/Makefile.am

@@ -19,6 +19,8 @@ libdns_la_SOURCES += rrsetlist.h rrsetlist.cc
 libdns_la_SOURCES += question.h question.cc
 libdns_la_SOURCES += message.h message.cc
 libdns_la_SOURCES += base64.h base64.cc
+libdns_la_SOURCES += dnstime.h dnstime.cc
+libdns_la_SOURCES += hex.h hex.cc
 
 rrclass.h: rrclass-placeholder.h
 rrtype.h: rrtype-placeholder.h

+ 13 - 13
src/lib/dns/cpp/base64.cc

@@ -14,6 +14,7 @@
 
 // $Id$
 
+#include <stdint.h>
 #include <cassert>
 #include <iterator>
 #include <string>
@@ -34,13 +35,12 @@ namespace dns {
 
 namespace {
 const char BASE64_PADDING_CHAR = '=';
-const char BINARY_ZERO_CODE = 0;
-
-typedef
-class BinaryNormalizer : public iterator<input_iterator_tag, char> {
+const uint8_t BINARY_ZERO_CODE = 0;
+  
+class BinaryNormalizer : public iterator<input_iterator_tag, uint8_t> {
 public:
-    BinaryNormalizer(const vector<char>::const_iterator& base,
-                     const vector<char>::const_iterator& base_end) :
+    BinaryNormalizer(const vector<uint8_t>::const_iterator& base,
+                     const vector<uint8_t>::const_iterator& base_end) :
         base_(base), base_end_(base_end), in_pad_(false)
     {}
     BinaryNormalizer& operator++()
@@ -53,7 +53,7 @@ public:
         }
         return (*this);
     }
-    const char& operator*() const {
+    const uint8_t& operator*() const {
         if (in_pad_) {
             return (BINARY_ZERO_CODE);
         } else {
@@ -65,17 +65,17 @@ public:
         return (base_ == other.base_);
     }
 private:
-    vector<char>::const_iterator base_;
-    const vector<char>::const_iterator base_end_;
+    vector<uint8_t>::const_iterator base_;
+    const vector<uint8_t>::const_iterator base_end_;
     bool in_pad_;
 };
 
-typedef 
+typedef
 base64_from_binary<transform_width<BinaryNormalizer, 6, 8> > base64_encoder;
 } // end of anonymous namespace
 
 string
-encodeBase64(const vector<char>& binary)
+encodeBase64(const vector<uint8_t>& binary)
 {
     // calculate the resulting length.  it's the smallest multiple of 4
     // equal to or larger than 4/3 * original data length.
@@ -138,7 +138,7 @@ base64_decoder;
 } // end of anonymous namespace
 
 void
-decodeBase64(const string& base64, vector<char>& result)
+decodeBase64(const string& base64, vector<uint8_t>& result)
 {
     // enumerate the number of trailing padding characters (=), ignoring
     // white spaces.  since base64_from_binary doesn't accept padding,
@@ -173,7 +173,7 @@ decodeBase64(const string& base64, vector<char>& result)
     // Confirm the original base64 text is the canonical encoding of the
     // data.
     assert(result.size() >= padlen);
-    vector<char>::const_reverse_iterator rit = result.rbegin();
+    vector<uint8_t>::const_reverse_iterator rit = result.rbegin();
     for (int i = 0; i < padlen; ++i, ++rit) {
         if (*rit != 0) {
             dns_throw(BadBase64String, "Non 0 bits included in padding");

+ 3 - 2
src/lib/dns/cpp/base64.h

@@ -17,6 +17,7 @@
 #ifndef __BASE64_H
 #define __BASE64_H 1
 
+#include <stdint.h>
 #include <string>
 #include <vector>
 
@@ -41,8 +42,8 @@ public:
         isc::Exception(file, line, what) {}
 };
 
-std::string encodeBase64(const std::vector<char>& binary);
-void decodeBase64(const std::string& base64, std::vector<char>& result);
+std::string encodeBase64(const std::vector<uint8_t>& binary);
+void decodeBase64(const std::string& base64, std::vector<uint8_t>& result);
 }
 }
 

+ 110 - 0
src/lib/dns/cpp/dnstime.cc

@@ -0,0 +1,110 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#include <string>
+#include <iomanip>
+#include <iostream>
+#include <sstream>
+#include <vector>
+
+#include "base64.h"
+#include "buffer.h"
+#include "messagerenderer.h"
+#include "name.h"
+#include "rrtype.h"
+#include "rrttl.h"
+#include "rdata.h"
+#include "rdataclass.h"
+#include <boost/lexical_cast.hpp>
+
+#include <stdio.h>
+#include <time.h>
+
+#include "dnstime.h"
+
+using namespace std;
+
+namespace isc {
+namespace dns {
+
+void
+DNSSECTimeToText(const time_t timeval, string& s)
+{
+    struct tm *t = gmtime(&timeval);
+
+    s.reserve(14);              // YYYYMMDDHHmmSS
+    ostringstream oss(s);
+    oss << setfill('0') << setw(4) << t->tm_year + 1900
+        << setw(2) << t->tm_mon + 1 << t->tm_mday 
+        << t->tm_hour << t->tm_min << t->tm_sec;
+    s = oss.str();
+}
+
+static inline void
+checkRange(int min, int max, int value, const string& valname) {
+    if ((value >= min) && (value <= max)) {
+        return;
+    }
+    ostringstream oss;
+    oss << "Invalid " << valname << " value: " << value;
+    dns_throw(InvalidTime, oss.str().c_str());
+}
+
+static int days[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+
+static inline bool
+isLeap(int y) {
+    return ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0);
+}
+
+time_t
+DNSSECTimeFromText(const string& time_txt)
+{
+    time_t timeval;
+
+    // first try reading YYYYMMDDHHmmSS format
+    int year, month, day, hour, minute, second;
+    if (sscanf(time_txt.c_str(), "%4d%2d%2d%2d%2d%2d",
+               &year, &month, &day, &hour, &minute, &second) == 6) {
+
+        checkRange(1970, 9999, year, "year");
+        checkRange(1, 12, month, "month");
+        checkRange(1, days[month - 1] + ((month == 2 && isLeap(year)) ? 1 : 0),
+                day, "day");
+        checkRange(0, 23, hour, "hour");
+        checkRange(0, 59, minute, "minute");
+        checkRange(0, 60, second, "second");
+
+        timeval = second + (60 * minute) + (3600 * hour) + ((day - 1) * 86400);
+        for (int m = 0; m < (month - 1); m++)
+                timeval += days[m] * 86400;
+        if (isLeap(year) && month > 2)
+                timeval += 86400;
+        for (int y = 1970; y < year; y++) {
+            timeval += ((isLeap(y) ? 366 : 365 ) * 86400);
+        }
+
+        return (timeval);
+    }
+
+    ostringstream oss;
+    oss << "Couldn't convert time value: " << time_txt;
+    dns_throw(InvalidTime, oss.str().c_str());
+}
+
+
+}
+}

+ 57 - 0
src/lib/dns/cpp/dnstime.h

@@ -0,0 +1,57 @@
+// Copyright (C) 2009  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#ifndef __DNSTIME_H
+#define __DNSTIME_H 1
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <time.h>
+
+#include <exceptions/exceptions.h>
+
+//
+// Note: this helper module isn't specific to the DNS protocol per se.
+// We should probably move this to somewhere else, possibly in some common
+// utility area.
+//
+
+namespace isc {
+namespace dns {
+
+///
+/// \brief A standard DNS (or ISC) module exception that is thrown if 
+/// a time conversion function encounters bad input
+///
+class InvalidTime : public Exception {
+public:
+    InvalidTime(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
+time_t
+DNSSECTimeFromText(const std::string& time_txt);
+
+void
+DNSSECTimeToText(const time_t timeval, std::string& s);
+}
+}
+
+#endif  // __DNSTIME_H
+
+// Local Variables: 
+// mode: c++
+// End: 

+ 81 - 0
src/lib/dns/cpp/hex.cc

@@ -0,0 +1,81 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#include <cassert>
+#include <iterator>
+#include <iomanip>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <vector>
+
+
+#include <exceptions/exceptions.h>
+#include <boost/foreach.hpp>
+#include <ctype.h>
+#include <stdint.h>
+
+#include "hex.h"
+
+using namespace std;
+
+namespace isc {
+namespace dns {
+
+static const char hexdigits[] = "0123456789ABCDEF";
+
+std::string
+encodeHex(const std::vector<uint8_t>& binary)
+{
+    // calculate the resulting length.  it should be twice the
+    // original data length
+    size_t len = (binary.size() * 2);
+    std::ostringstream hex;
+
+    BOOST_FOREACH(uint8_t octet, binary) {
+        hex << hexdigits[octet >> 4] << hexdigits[octet & 0xf];
+    }
+    assert(len >= hex.str().length());
+    return (hex.str());
+}
+
+void
+decodeHex(const std::string& hex, std::vector<uint8_t>& result)
+{
+    result.clear();
+    std::istringstream iss(hex);
+    char c1, c2;
+    uint8_t n;
+
+    iss.width(1);
+    if ((hex.size() % 2) == 1) {
+        iss >> c2;
+        n = strchr(hexdigits, c2) - hexdigits;
+        result.push_back(n);
+    }
+    while (!iss.eof()) {
+        iss >> c1 >> c2;;
+        n = (strchr(hexdigits, c1) - hexdigits) << 4;
+        n |= (strchr(hexdigits, c2) - hexdigits);
+
+        if (!iss.bad() && !iss.fail()) {
+            result.push_back(n);
+        }
+    }
+}
+
+}
+}

+ 53 - 0
src/lib/dns/cpp/hex.h

@@ -0,0 +1,53 @@
+// Copyright (C) 2009  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#ifndef __HEX_H
+#define __HEX_H 1
+
+#include <string>
+#include <vector>
+
+#include <exceptions/exceptions.h>
+
+//
+// Note: this helper module isn't specific to the DNS protocol per se.
+// We should probably move this to somewhere else, possibly in some common
+// utility area.
+//
+
+namespace isc {
+namespace dns {
+
+///
+/// \brief A standard DNS (or ISC) module exception that is thrown if a hex
+/// decoder encounters an invalid input.
+///
+class BadHexString : public Exception {
+public:
+    BadHexString(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
+std::string encodeHex(const std::vector<uint8_t>& binary);
+void decodeHex(const std::string& hex, std::vector<uint8_t>& result);
+}
+}
+
+#endif  // __HEX_H
+
+// Local Variables: 
+// mode: c++
+// End: 

+ 13 - 1
src/lib/dns/cpp/message.cc

@@ -216,6 +216,12 @@ Message::setHeaderFlag(const MessageFlag& flag)
     impl_->flags_ |= flag.getBit();
 }
 
+void
+Message::clearHeaderFlag(const MessageFlag& flag)
+{
+    impl_->flags_ &= ~flag.getBit();
+}
+
 qid_t
 Message::getQid() const
 {
@@ -259,11 +265,17 @@ Message::getRRCount(const Section& section) const
 }
 
 void
-Message::addRRset(const Section& section, RRsetPtr rrset)
+Message::addRRset(const Section& section, RRsetPtr rrset, bool sign)
 {
     // Note: should check duplicate (TBD)
     impl_->rrsets_[sectionCodeToId(section)].push_back(rrset);
     impl_->counts_[section.getCode()] += rrset->getRdataCount();
+
+    RRsetPtr sp = rrset->getRRsig();
+    if (sign && sp) {
+        impl_->rrsets_[sectionCodeToId(section)].push_back(sp);
+        impl_->counts_[section.getCode()] += sp->getRdataCount();
+    }
 }
 
 void

+ 7 - 1
src/lib/dns/cpp/message.h

@@ -478,6 +478,7 @@ private:
 public:
     bool getHeaderFlag(const MessageFlag& flag) const;
     void setHeaderFlag(const MessageFlag& flag);
+    void clearHeaderFlag(const MessageFlag& flag);
     qid_t getQid() const;
     void setQid(qid_t qid);
     const Rcode& getRcode() const;
@@ -502,7 +503,7 @@ public:
     void addQuestion(QuestionPtr question);
     void addQuestion(const Question& question);
     void removeQuestion(QuestionPtr question);
-    void addRRset(const Section& section, RRsetPtr rrset);
+    void addRRset(const Section& section, RRsetPtr rrset, bool sign = false);
     void removeRRset(const Section& section, RRsetPtr rrset);
     // notyet:
     //void addRR(const Section& section, const RR& rr);
@@ -528,6 +529,11 @@ public:
     static const rcode_t RCODE_NXDOMAIN = 3;
     static const rcode_t RCODE_NOTIMP = 4;
     static const rcode_t RCODE_REFUSED = 5;
+    static const rcode_t RCODE_YXDOMAIN = 6;
+    static const rcode_t RCODE_YXRRSET = 7;
+    static const rcode_t RCODE_NXRRSET = 8;
+    static const rcode_t RCODE_NOTAUTH = 9;
+    static const rcode_t RCODE_NOTZONE = 10;
     // ...more to follow
 
     static const opcode_t OPCODE_QUERY = 0;

+ 32 - 0
src/lib/dns/cpp/name.cc

@@ -18,6 +18,8 @@
 #include <cassert>
 #include <iterator>
 #include <functional>
+#include <vector>
+#include <iostream>
 
 #include <algorithm>
 
@@ -606,6 +608,36 @@ Name::concatenate(const Name& suffix) const
 }
 
 Name
+Name::reverse() const
+{
+    Name retname;
+    //
+    // Set up offsets: The size of the string and number of labels will
+    // be the same in as in the original.
+    //
+    retname.offsets_.reserve(labelcount_);
+    retname.ndata_.reserve(length_);
+
+    // Copy the original name, label by label, from tail to head.
+    vector<unsigned char>::const_reverse_iterator rit0 = offsets_.rbegin();
+    vector<unsigned char>::const_reverse_iterator rit1 = rit0 + 1;
+    string::const_iterator n0 = ndata_.begin();
+    retname.offsets_.push_back(0);
+    while (rit1 != offsets_.rend()) {
+        retname.ndata_.append(n0 + *rit1, n0 + *rit0);
+        retname.offsets_.push_back(retname.ndata_.size());
+        ++rit0;
+        ++rit1;
+    }
+    retname.ndata_.push_back(0);
+
+    retname.labelcount_ = labelcount_;
+    retname.length_ = length_;
+
+    return (retname);
+}
+
+Name
 Name::split(unsigned int first, unsigned int n) const
 {
     if (n == 0 || first + n > labelcount_) {

+ 8 - 0
src/lib/dns/cpp/name.h

@@ -513,6 +513,14 @@ public:
     /// labels including and following the <code>first</code> label.  
     Name split(unsigned int first, unsigned int n) const;
 
+    /// \brief Reverse the labels of a name
+    ///
+    /// This method reverses the labels of a name.  For example, if
+    /// \c this is "www.example.com.", this method will return 
+    /// "com.example.www."  (This is useful because DNSSEC sort order
+    /// is equivalent to a lexical sort of label-reversed names.)
+    Name reverse() const;
+
     /// \brief Concatenate two names.
     ///
     /// This method appends \c suffix to \c this Name.  The trailing dot of

+ 82 - 0
src/lib/dns/cpp/rdata/generic/dname_39.cc

@@ -0,0 +1,82 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#include <string>
+
+#include "buffer.h"
+#include "name.h"
+#include "messagerenderer.h"
+#include "rdata.h"
+#include "rdataclass.h"
+
+using namespace std;
+
+// BEGIN_ISC_NAMESPACE
+// BEGIN_RDATA_NAMESPACE
+
+DNAME::DNAME(const std::string& namestr) :
+    dname_(namestr)
+{}
+
+DNAME::DNAME(InputBuffer& buffer, size_t rdata_len) :
+    dname_(buffer)
+{
+    // we don't need rdata_len for parsing.  if necessary, the caller will
+    // check consistency.
+}
+
+DNAME::DNAME(const DNAME& other) :
+    dname_(other.dname_)
+{}
+
+DNAME::DNAME(const Name& dname) :
+    dname_(dname)
+{}
+
+void
+DNAME::toWire(OutputBuffer& buffer) const
+{
+    dname_.toWire(buffer);
+}
+
+void
+DNAME::toWire(MessageRenderer& renderer) const
+{
+    renderer.writeName(dname_);
+}
+
+string
+DNAME::toText() const
+{
+    return (dname_.toText());
+}
+
+int
+DNAME::compare(const Rdata& other) const
+{
+    const DNAME& other_dname = dynamic_cast<const DNAME&>(other);
+
+    return (compareNames(dname_, other_dname.dname_));
+}
+
+const Name&
+DNAME::getDname() const
+{
+    return (dname_);
+}
+
+// END_RDATA_NAMESPACE
+// END_ISC_NAMESPACE

+ 49 - 0
src/lib/dns/cpp/rdata/generic/dname_39.h

@@ -0,0 +1,49 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+// BEGIN_HEADER_GUARD
+
+#include <string>
+
+#include "name.h"
+#include "rdata.h"
+
+// BEGIN_ISC_NAMESPACE
+
+// BEGIN_COMMON_DECLARATIONS
+// END_COMMON_DECLARATIONS
+
+// BEGIN_RDATA_NAMESPACE
+
+class DNAME : public Rdata {
+public:
+    // BEGIN_COMMON_MEMBERS
+    // END_COMMON_MEMBERS
+
+    // DNAME specific methods
+    DNAME(const Name& dname);
+    const Name& getDname() const;
+private:
+    Name dname_;
+};
+
+// END_RDATA_NAMESPACE
+// END_ISC_NAMESPACE
+// END_HEADER_GUARD
+
+// Local Variables: 
+// mode: c++
+// End: 

+ 207 - 0
src/lib/dns/cpp/rdata/generic/dnskey_48.cc

@@ -0,0 +1,207 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#include <iostream>
+#include <string>
+#include <sstream>
+#include <vector>
+
+#include "base64.h"
+#include "buffer.h"
+#include "messagerenderer.h"
+#include "name.h"
+#include "rdata.h"
+#include "rdataclass.h"
+#include <boost/lexical_cast.hpp>
+#include <boost/foreach.hpp>
+
+#include <stdio.h>
+#include <time.h>
+
+using namespace std;
+
+// BEGIN_ISC_NAMESPACE
+// BEGIN_RDATA_NAMESPACE
+
+struct DNSKEYImpl {
+    // straightforward representation of DNSKEY RDATA fields
+    DNSKEYImpl(uint16_t flags, uint8_t protocol, uint8_t algorithm,
+               const vector<uint8_t>& keydata) :
+        flags_(flags), protocol_(protocol), algorithm_(algorithm),
+        keydata_(keydata)
+    {}
+
+    uint16_t flags_;
+    uint8_t protocol_;
+    uint8_t algorithm_;
+    const vector<uint8_t> keydata_;
+};
+
+DNSKEY::DNSKEY(const string& dnskey_str) :
+    impl_(NULL)
+{
+    istringstream iss(dnskey_str);
+    unsigned int flags, protocol, algorithm;
+    stringbuf keydatabuf;
+
+    iss >> flags >> protocol >> algorithm >> &keydatabuf;
+    if (iss.bad() || iss.fail() || !iss.eof()) {
+        dns_throw(InvalidRdataText, "Invalid DNSKEY text");
+    }
+    if (flags > 0xffff) {
+        dns_throw(InvalidRdataText, "DNSKEY flags out of range");
+    }
+    if (protocol > 0xff) {
+        dns_throw(InvalidRdataText, "DNSKEY protocol out of range");
+    }
+    if (algorithm > 0xff) {
+        dns_throw(InvalidRdataText, "DNSKEY algorithm out of range");
+    }
+
+    vector<uint8_t> keydata;
+    decodeBase64(keydatabuf.str(), keydata);
+
+    impl_ = new DNSKEYImpl(flags, protocol, algorithm, keydata);
+}
+
+DNSKEY::DNSKEY(InputBuffer& buffer, size_t rdata_len)
+{
+    uint16_t flags = buffer.readUint16();
+    uint16_t protocol = buffer.readUint8();
+    uint16_t algorithm = buffer.readUint8();
+    vector<uint8_t> keydata;
+
+    rdata_len -= 4;
+    for (int i = 0; i < rdata_len; i++) {
+        keydata.push_back(buffer.readUint8());
+    }
+
+    impl_ = new DNSKEYImpl(flags, protocol, algorithm, keydata);
+}
+
+DNSKEY::DNSKEY(const DNSKEY& source) :
+    impl_(new DNSKEYImpl(*source.impl_))
+{}
+
+DNSKEY&
+DNSKEY::operator=(const DNSKEY& source)
+{
+    if (impl_ == source.impl_) {
+        return (*this);
+    }
+
+    DNSKEYImpl* newimpl = new DNSKEYImpl(*source.impl_);
+    delete impl_;
+    impl_ = newimpl;
+
+    return (*this);
+}
+
+DNSKEY::~DNSKEY()
+{
+    delete impl_;
+}
+
+string
+DNSKEY::toText() const
+{
+    return (boost::lexical_cast<string>(static_cast<int>(impl_->flags_)) +
+        " " + boost::lexical_cast<string>(static_cast<int>(impl_->protocol_)) +
+        " " + boost::lexical_cast<string>(static_cast<int>(impl_->algorithm_)) +
+        " " + encodeBase64(impl_->keydata_));
+}
+
+void
+DNSKEY::toWire(OutputBuffer& buffer) const
+{
+    buffer.writeUint16(impl_->flags_);
+    buffer.writeUint8(impl_->protocol_);
+    buffer.writeUint8(impl_->algorithm_);
+    buffer.writeData(&impl_->keydata_[0], impl_->keydata_.size());
+}
+
+void
+DNSKEY::toWire(MessageRenderer& renderer) const
+{
+    renderer.writeUint16(impl_->flags_);
+    renderer.writeUint8(impl_->protocol_);
+    renderer.writeUint8(impl_->algorithm_);
+    renderer.writeData(&impl_->keydata_[0], impl_->keydata_.size());
+}
+
+int
+DNSKEY::compare(const Rdata& other) const
+{
+    const DNSKEY& other_dnskey = dynamic_cast<const DNSKEY&>(other);
+
+    if (impl_->flags_ != other_dnskey.impl_->flags_) {
+        return (impl_->flags_ < other_dnskey.impl_->flags_ ? -1 : 1);
+    }
+    if (impl_->protocol_ != other_dnskey.impl_->protocol_) {
+        return (impl_->protocol_ < other_dnskey.impl_->protocol_ ? -1 : 1);
+    }
+    if (impl_->algorithm_ != other_dnskey.impl_->algorithm_) {
+        return (impl_->algorithm_ < other_dnskey.impl_->algorithm_ ? -1 : 1);
+    }
+
+    size_t this_len = impl_->keydata_.size();
+    size_t other_len = other_dnskey.impl_->keydata_.size();
+    size_t cmplen = min(this_len, other_len);
+    int cmp = memcmp(&impl_->keydata_[0], &other_dnskey.impl_->keydata_[0],
+                     cmplen);
+    if (cmp != 0) {
+        return (cmp);
+    } else {
+        return ((this_len == other_len) ? 0 : (this_len < other_len) ? -1 : 1);
+    }
+}
+
+const uint16_t
+DNSKEY::getTag() const
+{
+    if (impl_->algorithm_ == 1) {
+        int len = impl_->keydata_.size();
+        if (len >= 3) {
+            return ((impl_->keydata_[len - 3] << 8) + impl_->keydata_[len - 2]);
+        } else {
+            dns_throw(InvalidRdataText, "Bad keydata");
+        }
+    }
+
+    uint32_t ac = impl_->flags_;
+    ac += (impl_->protocol_ << 8);
+    ac += impl_->algorithm_;
+    
+    size_t size = impl_->keydata_.size();
+    for (int i = 0; i < size; i ++) {
+        ac += (i & 1) ? impl_->keydata_[i] : (impl_->keydata_[i] << 8);
+    }
+    ac += (ac >> 16) & 0xffff;
+    return (ac & 0xffff);
+}
+
+const uint16_t
+DNSKEY::getFlags() const {
+    return (impl_->flags_);
+}
+
+const uint8_t
+DNSKEY::getAlg() const {
+    return (impl_->algorithm_);
+}
+
+// END_RDATA_NAMESPACE
+// END_ISC_NAMESPACE

+ 61 - 0
src/lib/dns/cpp/rdata/generic/dnskey_48.h

@@ -0,0 +1,61 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#include <stdint.h>
+
+#include <string>
+
+#include "name.h"
+#include "rrtype.h"
+#include "rrttl.h"
+#include "rdata.h"
+
+// BEGIN_HEADER_GUARD
+
+// BEGIN_ISC_NAMESPACE
+
+// BEGIN_COMMON_DECLARATIONS
+// END_COMMON_DECLARATIONS
+
+// BEGIN_RDATA_NAMESPACE
+
+struct DNSKEYImpl;
+
+class DNSKEY : public Rdata {
+public:
+    // BEGIN_COMMON_MEMBERS
+    // END_COMMON_MEMBERS
+    DNSKEY& operator=(const DNSKEY& source);
+    ~DNSKEY();
+
+    ///
+    /// Specialized methods
+    ///
+    const uint16_t getTag() const;
+    const uint16_t getFlags() const;
+    const uint8_t getAlg() const;
+
+private:
+    DNSKEYImpl* impl_;
+};
+
+// END_RDATA_NAMESPACE
+// END_ISC_NAMESPACE
+// END_HEADER_GUARD
+
+// Local Variables: 
+// mode: c++
+// End: 

+ 177 - 0
src/lib/dns/cpp/rdata/generic/ds_43.cc

@@ -0,0 +1,177 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#include <iostream>
+#include <string>
+#include <sstream>
+#include <vector>
+
+#include "buffer.h"
+#include "hex.h"
+#include "messagerenderer.h"
+#include "name.h"
+#include "rdata.h"
+#include "rdataclass.h"
+#include <boost/lexical_cast.hpp>
+
+#include <stdio.h>
+#include <time.h>
+
+using namespace std;
+
+// BEGIN_ISC_NAMESPACE
+// BEGIN_RDATA_NAMESPACE
+
+struct DSImpl {
+    // straightforward representation of DS RDATA fields
+    DSImpl(uint16_t tag, uint8_t algorithm, uint8_t digest_type,
+           const vector<uint8_t>& digest) :
+        tag_(tag), algorithm_(algorithm), digest_type_(digest_type),
+        digest_(digest)
+    {}
+
+    uint16_t tag_;
+    uint8_t algorithm_;
+    uint8_t digest_type_;
+    const vector<uint8_t> digest_;
+};
+
+DS::DS(const string& ds_str) :
+    impl_(NULL)
+{
+    istringstream iss(ds_str);
+    unsigned int tag, algorithm, digest_type;
+    stringbuf digestbuf;
+
+    iss >> tag >> algorithm >> digest_type >> &digestbuf;
+    if (iss.bad() || iss.fail() || !iss.eof()) {
+        dns_throw(InvalidRdataText, "Invalid DS text");
+    }
+    if (tag > 0xffff) {
+        dns_throw(InvalidRdataText, "DS tag out of range");
+    }
+    if (algorithm > 0xff) {
+        dns_throw(InvalidRdataText, "DS algorithm out of range");
+    }
+    if (digest_type > 0xff) {
+        dns_throw(InvalidRdataText, "DS digest type out of range");
+    }
+
+    vector<uint8_t> digest;
+    decodeHex(digestbuf.str(), digest);
+
+    impl_ = new DSImpl(tag, algorithm, digest_type, digest);
+}
+
+DS::DS(InputBuffer& buffer, size_t rdata_len)
+{
+    uint16_t tag = buffer.readUint16();
+    uint16_t algorithm = buffer.readUint8();
+    uint16_t digest_type = buffer.readUint8();
+    vector<uint8_t> digest;
+
+    rdata_len -= 4;
+    for (int i = 0; i < rdata_len; i++) {
+        digest.push_back(buffer.readUint8());
+    }
+
+    impl_ = new DSImpl(tag, algorithm, digest_type, digest);
+}
+
+DS::DS(const DS& source) :
+    impl_(new DSImpl(*source.impl_))
+{}
+
+DS&
+DS::operator=(const DS& source)
+{
+    if (impl_ == source.impl_) {
+        return (*this);
+    }
+
+    DSImpl* newimpl = new DSImpl(*source.impl_);
+    delete impl_;
+    impl_ = newimpl;
+
+    return (*this);
+}
+
+DS::~DS()
+{
+    delete impl_;
+}
+
+string
+DS::toText() const
+{
+    using namespace boost;
+    return (lexical_cast<string>(static_cast<int>(impl_->tag_)) +
+        " " + lexical_cast<string>(static_cast<int>(impl_->algorithm_)) +
+        " " + lexical_cast<string>(static_cast<int>(impl_->digest_type_)) +
+        " " + encodeHex(impl_->digest_));
+}
+
+void
+DS::toWire(OutputBuffer& buffer) const
+{
+    buffer.writeUint16(impl_->tag_);
+    buffer.writeUint8(impl_->algorithm_);
+    buffer.writeUint8(impl_->digest_type_);
+    buffer.writeData(&impl_->digest_[0], impl_->digest_.size());
+}
+
+void
+DS::toWire(MessageRenderer& renderer) const
+{
+    renderer.writeUint16(impl_->tag_);
+    renderer.writeUint8(impl_->algorithm_);
+    renderer.writeUint8(impl_->digest_type_);
+    renderer.writeData(&impl_->digest_[0], impl_->digest_.size());
+}
+
+int
+DS::compare(const Rdata& other) const
+{
+    const DS& other_ds = dynamic_cast<const DS&>(other);
+
+    if (impl_->tag_ != other_ds.impl_->tag_) {
+        return (impl_->tag_ < other_ds.impl_->tag_ ? -1 : 1);
+    }
+    if (impl_->algorithm_ != other_ds.impl_->algorithm_) {
+        return (impl_->algorithm_ < other_ds.impl_->algorithm_ ? -1 : 1);
+    }
+    if (impl_->digest_type_ != other_ds.impl_->digest_type_) {
+        return (impl_->digest_type_ < other_ds.impl_->digest_type_ ? -1 : 1);
+    }
+
+    size_t this_len = impl_->digest_.size();
+    size_t other_len = other_ds.impl_->digest_.size();
+    size_t cmplen = min(this_len, other_len);
+    int cmp = memcmp(&impl_->digest_[0], &other_ds.impl_->digest_[0], cmplen);
+    if (cmp != 0) {
+        return (cmp);
+    } else {
+        return ((this_len == other_len) ? 0 : (this_len < other_len) ? -1 : 1);
+    }
+}
+
+const uint16_t
+DS::getTag() const {
+    return impl_->tag_;
+}
+
+// END_RDATA_NAMESPACE
+// END_ISC_NAMESPACE

+ 58 - 0
src/lib/dns/cpp/rdata/generic/ds_43.h

@@ -0,0 +1,58 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#include <stdint.h>
+
+#include <string>
+
+#include "name.h"
+#include "rrtype.h"
+#include "rrttl.h"
+#include "rdata.h"
+
+// BEGIN_HEADER_GUARD
+
+// BEGIN_ISC_NAMESPACE
+
+// BEGIN_COMMON_DECLARATIONS
+// END_COMMON_DECLARATIONS
+
+// BEGIN_RDATA_NAMESPACE
+
+struct DSImpl;
+
+class DS : public Rdata {
+public:
+    // BEGIN_COMMON_MEMBERS
+    // END_COMMON_MEMBERS
+    DS& operator=(const DS& source);
+    ~DS();
+
+    ///
+    /// Specialized methods
+    ///
+    const uint16_t getTag() const;
+private:
+    DSImpl* impl_;
+};
+
+// END_RDATA_NAMESPACE
+// END_ISC_NAMESPACE
+// END_HEADER_GUARD
+
+// Local Variables: 
+// mode: c++
+// End: 

+ 25 - 2
src/lib/dns/cpp/rdata/generic/mx_15.cc

@@ -38,10 +38,21 @@ MX::MX(InputBuffer& buffer, size_t rdata_len) :
     // check consistency.
 }
 
-MX::MX(const std::string& mxstr) :
+MX::MX(const std::string& mx_str) :
     preference_(0), mxname_(".")
 {
-    dns_throw(InvalidRdataText, "Not implemented yet");
+    istringstream iss(mx_str);
+    uint16_t pref;
+    string mxname;
+
+    iss >> pref >> mxname;
+
+    if (iss.bad() || iss.fail() || !iss.eof()) {
+        dns_throw(InvalidRdataText, "Invalid MX text format");
+    }
+
+    preference_ = pref;
+    mxname_ = Name(mxname);
 }
 
 MX::MX(uint16_t preference, const Name& mxname) :
@@ -86,5 +97,17 @@ MX::compare(const Rdata& other) const
     return (compareNames(mxname_, other_mx.mxname_));
 }
 
+const Name&
+MX::getMXName() const
+{
+    return (mxname_);
+}
+
+const uint16_t
+MX::getMXPref() const
+{
+    return (preference_);
+}
+
 // END_RDATA_NAMESPACE
 // END_ISC_NAMESPACE

+ 6 - 0
src/lib/dns/cpp/rdata/generic/mx_15.h

@@ -36,6 +36,12 @@ public:
 
     explicit MX(uint16_t preference, const Name& mxname);
 
+    ///
+    /// Specialized methods
+    ///
+    const Name& getMXName() const;
+    const uint16_t getMXPref() const;
+
 private:
     /// Note: this is a prototype version; we may reconsider
     /// this representation later.

+ 197 - 0
src/lib/dns/cpp/rdata/generic/nsec_47.cc

@@ -0,0 +1,197 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#include <iostream>
+#include <string>
+#include <sstream>
+#include <vector>
+
+#include "base64.h"
+#include "buffer.h"
+#include "messagerenderer.h"
+#include "name.h"
+#include "rrtype.h"
+#include "rrttl.h"
+#include "rdata.h"
+#include "rdataclass.h"
+
+#include <stdio.h>
+#include <time.h>
+
+using namespace std;
+
+// BEGIN_ISC_NAMESPACE
+// BEGIN_RDATA_NAMESPACE
+
+struct NSECImpl {
+    // straightforward representation of NSEC RDATA fields
+    NSECImpl(const Name& next, vector<uint8_t> typebits) :
+        nextname_(next), typebits_(typebits)
+    {}
+
+    Name nextname_;
+    vector<uint8_t> typebits_;
+};
+
+NSEC::NSEC(const string& nsec_str) :
+    impl_(NULL)
+{
+    istringstream iss(nsec_str);
+    string nextname;
+    uint8_t bitmap[8 * 1024];       // 64k bits
+    vector<uint8_t> typebits;
+
+    iss >> nextname;
+    if (iss.bad() || iss.fail()) {
+        dns_throw(InvalidRdataText, "Invalid NSEC name");
+    }
+
+    memset(bitmap, 0, sizeof(bitmap));
+    do { 
+        string type;
+        int code;
+        iss >> type;
+        code = RRType(type).getCode();
+        bitmap[code / 8] |= (0x80 >> (code % 8));
+    } while(!iss.eof());
+
+    for(int window = 0; window < 256; window++) {
+        int octet;
+        for (octet = 31; octet >= 0; octet--) {
+            if (bitmap[window * 32 + octet] != 0) {
+                break;
+            }
+        }
+        if (octet < 0)
+            continue;
+        typebits.push_back(window);
+        typebits.push_back(octet + 1);
+        for (int i = 0; i <= octet; i++) {
+            typebits.push_back(bitmap[window * 32 + i]);
+        }
+    }
+
+    impl_ = new NSECImpl(Name(nextname), typebits);
+}
+
+NSEC::NSEC(InputBuffer& buffer, size_t rdata_len)
+{
+    size_t pos = buffer.getPosition();
+    Name nextname(buffer);
+    rdata_len -= (buffer.getPosition() - pos);
+
+    vector<uint8_t> typebits;
+    for (int i = 0; i < rdata_len; i++) {
+        typebits.push_back(buffer.readUint8());
+    }
+
+    impl_ = new NSECImpl(nextname, typebits);
+}
+
+NSEC::NSEC(const NSEC& source) :
+    impl_(new NSECImpl(*source.impl_))
+{}
+
+NSEC&
+NSEC::operator=(const NSEC& source)
+{
+    if (impl_ == source.impl_) {
+        return (*this);
+    }
+
+    NSECImpl* newimpl = new NSECImpl(*source.impl_);
+    delete impl_;
+    impl_ = newimpl;
+
+    return (*this);
+}
+
+NSEC::~NSEC()
+{
+    delete impl_;
+}
+
+string
+NSEC::toText() const
+{
+    ostringstream s;
+    int len = 0;
+    s << impl_->nextname_;
+    for (int i = 0; i < impl_->typebits_.size(); i += len) {
+        if (i + 2 > impl_->typebits_.size()) {
+            dns_throw(InvalidRdataText, "Invalid NSEC Rdata");
+        }
+        int window = impl_->typebits_[i];
+        len = impl_->typebits_[i + 1];
+        if (len < 0 || len >= 32) {
+            dns_throw(InvalidRdataText, "Invalid NSEC Rdata");
+        }
+        i += 2;
+        for (int j = 0; j < len; j++) {
+            if (impl_->typebits_[i + j] == 0) {
+                continue;
+            }
+            for (int k = 0; k < 8; k++) {
+                if ((impl_->typebits_[i + j] & (0x80 >> k)) == 0) {
+                    continue;
+                }
+                int t = window * 256 + j * 8 + k;
+                s << " " << RRType(t).toText();
+            }
+        }
+    }
+
+    return (s.str());
+}
+
+void
+NSEC::toWire(OutputBuffer& buffer) const
+{
+    impl_->nextname_.toWire(buffer);
+    buffer.writeData(&impl_->typebits_[0], impl_->typebits_.size());
+}
+
+void
+NSEC::toWire(MessageRenderer& renderer) const
+{
+    impl_->nextname_.toWire(renderer);
+    renderer.writeData(&impl_->typebits_[0], impl_->typebits_.size());
+}
+
+int
+NSEC::compare(const Rdata& other) const
+{
+    const NSEC& other_nsec = dynamic_cast<const NSEC&>(other);
+
+    int cmp = compareNames(impl_->nextname_, other_nsec.impl_->nextname_);
+    if (cmp != 0) {
+        return (cmp);
+    }
+
+    size_t this_len = impl_->typebits_.size();
+    size_t other_len = other_nsec.impl_->typebits_.size();
+    size_t cmplen = min(this_len, other_len);
+    cmp = memcmp(&impl_->typebits_[0], &other_nsec.impl_->typebits_[0],
+                 cmplen);
+    if (cmp != 0) {
+        return (cmp);
+    } else {
+        return ((this_len == other_len) ? 0 : (this_len < other_len) ? -1 : 1);
+    }
+}
+
+// END_RDATA_NAMESPACE
+// END_ISC_NAMESPACE

+ 53 - 0
src/lib/dns/cpp/rdata/generic/nsec_47.h

@@ -0,0 +1,53 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#include <stdint.h>
+
+#include <string>
+
+#include "name.h"
+#include "rrtype.h"
+#include "rrttl.h"
+#include "rdata.h"
+
+// BEGIN_HEADER_GUARD
+
+// BEGIN_ISC_NAMESPACE
+
+// BEGIN_COMMON_DECLARATIONS
+// END_COMMON_DECLARATIONS
+
+// BEGIN_RDATA_NAMESPACE
+
+struct NSECImpl;
+
+class NSEC : public Rdata {
+public:
+    // BEGIN_COMMON_MEMBERS
+    // END_COMMON_MEMBERS
+    NSEC& operator=(const NSEC& source);
+    ~NSEC();
+private:
+    NSECImpl* impl_;
+};
+
+// END_RDATA_NAMESPACE
+// END_ISC_NAMESPACE
+// END_HEADER_GUARD
+
+// Local Variables: 
+// mode: c++
+// End: 

+ 20 - 36
src/lib/dns/cpp/rdata/generic/rrsig_46.cc

@@ -15,11 +15,14 @@
 // $Id$
 
 #include <string>
+#include <iomanip>
+#include <iostream>
 #include <sstream>
 #include <vector>
 
 #include "base64.h"
 #include "buffer.h"
+#include "dnstime.h"
 #include "messagerenderer.h"
 #include "name.h"
 #include "rrtype.h"
@@ -28,51 +31,25 @@
 #include "rdataclass.h"
 #include <boost/lexical_cast.hpp>
 
+#include <stdio.h>
+#include <time.h>
+
 using namespace std;
 
 // BEGIN_ISC_NAMESPACE
 // BEGIN_RDATA_NAMESPACE
 
-namespace {
-inline uint32_t
-convertDNSSECTime(const string time_txt)
-{
-    istringstream iss(time_txt);
-
-    uint32_t timeval;
-    iss >> timeval;
-
-    // right now, we don't support the YYYYMMDDHHmmSS format for
-    // expire/inception
-    if (iss.bad() || iss.fail() || !iss.eof()) {
-        dns_throw(InvalidRdataText, "Invalid RRSIG Time format");
-    }
-
-    return (timeval);
-}
-}
-
 struct RRSIGImpl {
     // straightforward representation of RRSIG RDATA fields
     RRSIGImpl(const RRType& covered, uint8_t algorithm, uint8_t labels,
               uint32_t originalttl, uint32_t timeexpire, uint32_t timeinception,
               uint16_t keyid, const Name& signer,
-              const vector<char>& signature) :
+              const vector<uint8_t>& signature) :
         covered_(covered), algorithm_(algorithm), labels_(labels),
         originalttl_(originalttl), timeexpire_(timeexpire),
         timeinception_(timeinception), keyid_(keyid), signer_(signer),
         signature_(signature)
     {}
-    RRSIGImpl(const RRType& covered, uint8_t algorithm, uint8_t labels,
-              uint32_t originalttl, uint32_t timeexpire, uint32_t timeinception,
-              uint16_t keyid, const Name& signer,
-              const string& signature_txt) :
-        covered_(covered), algorithm_(algorithm), labels_(labels),
-        originalttl_(originalttl), timeexpire_(timeexpire),
-        timeinception_(timeinception), keyid_(keyid), signer_(signer)
-    {
-        decodeBase64(signature_txt, signature_);
-    }
 
     const RRType covered_;
     uint8_t algorithm_;
@@ -82,7 +59,7 @@ struct RRSIGImpl {
     uint32_t timeinception_;
     uint16_t keyid_;
     const Name signer_;
-    vector<char> signature_;
+    const vector<uint8_t> signature_;
 };
 
 RRSIG::RRSIG(const string& rrsig_str) :
@@ -108,12 +85,15 @@ RRSIG::RRSIG(const string& rrsig_str) :
         dns_throw(InvalidRdataText, "RRSIG labels out of range");
     }
 
-    uint32_t timeexpire = convertDNSSECTime(expire_txt);
-    uint32_t timeinception = convertDNSSECTime(inception_txt);
+    uint32_t timeexpire = DNSSECTimeFromText(expire_txt);
+    uint32_t timeinception = DNSSECTimeFromText(inception_txt);
+
+    vector<uint8_t> signature;
+    decodeBase64(signaturebuf.str(), signature);
 
     impl_ = new RRSIGImpl(RRType(covered_txt), algorithm, labels,
                           originalttl, timeexpire, timeinception, keyid,
-                          Name(signer_txt), signaturebuf.str());
+                          Name(signer_txt), signature);
 }
 
 RRSIG::RRSIG(InputBuffer& buffer, size_t rdata_len)
@@ -146,12 +126,16 @@ RRSIG::~RRSIG()
 string
 RRSIG::toText() const
 {
+    string expire, inception;
+    DNSSECTimeToText(impl_->timeexpire_, expire);
+    DNSSECTimeToText(impl_->timeinception_, inception);
+
     return (impl_->covered_.toText() +
             " " + boost::lexical_cast<string>(static_cast<int>(impl_->algorithm_))
             + " " + boost::lexical_cast<string>(static_cast<int>(impl_->labels_))
             + " " + boost::lexical_cast<string>(impl_->originalttl_)
-            + " " + boost::lexical_cast<string>(impl_->timeexpire_)
-            + " " + boost::lexical_cast<string>(impl_->timeinception_)
+            + " " + expire
+            + " " + inception
             + " " + boost::lexical_cast<string>(impl_->keyid_)
             + " " + impl_->signer_.toText()
             + " " + encodeBase64(impl_->signature_));

+ 4 - 4
src/lib/dns/cpp/rrclass-placeholder.h

@@ -238,16 +238,16 @@ public:
     // BEGIN_WELL_KNOWN_CLASS_DECLARATIONS
     // END_WELL_KNOWN_CLASS_DECLARATIONS
     
+    static const RRClass& NONE();
+    static const RRClass& ANY();
+
+private:
     // \brief Meta-classes
     enum {
         RRCLASS_RESERVED0 = 0,
         RRCLASS_NONE = 254,
         RRCLASS_ANY = 255
     };
-    static const RRClass& NONE();
-    static const RRClass& ANY();
-
-private:
     uint16_t classcode_;
 };
 

+ 9 - 0
src/lib/dns/cpp/rrset.cc

@@ -214,6 +214,15 @@ BasicRRset::toWire(MessageRenderer& renderer) const
     return (AbstractRRset::toWire(renderer));
 }
 
+RRset::RRset(const Name& name, const RRClass& rrclass,
+            const RRType& rrtype, const RRTTL& ttl) :
+    BasicRRset(name, rrclass, rrtype, ttl)
+{
+    rrsig_ = RRsetPtr();
+}
+
+RRset::~RRset() {}
+
 namespace {
 class BasicRdataIterator : public RdataIterator {
 private:

+ 69 - 7
src/lib/dns/cpp/rrset.h

@@ -25,6 +25,7 @@
 #include <exceptions/exceptions.h>
 
 #include "rdata.h"
+#include "rrtype.h"
 
 namespace isc {
 namespace dns {
@@ -50,22 +51,20 @@ class AbstractRRset;
 class BasicRRset;
 class RdataIterator;
 class BasicRRsetImpl;
+class RRset;
 
-/// \brief A pointer-like type pointing to an \c AbstractRRset object.
+/// \brief A pointer-like type pointing to an \c RRset object.
 ///
 /// This type is commonly used as an argument of various functions defined
 /// in this library in order to handle RRsets in a polymorphic manner.
-typedef boost::shared_ptr<AbstractRRset> RRsetPtr;
+typedef boost::shared_ptr<RRset> RRsetPtr;
 
-/// \brief A pointer-like type pointing to an (immutable) \c AbstractRRset
+/// \brief A pointer-like type pointing to an (immutable) \c RRset
 /// object.
 ///
 /// This type is commonly used as an argument of various functions defined
 /// in this library in order to handle RRsets in a polymorphic manner.
-typedef boost::shared_ptr<const AbstractRRset> ConstRRsetPtr;
-
-/// \brief A convenient abbreviation for the most generic derived RRset class.
-typedef BasicRRset RRset;
+typedef boost::shared_ptr<const RRset> ConstRRsetPtr;
 
 /// \brief A pointer-like type point to an \c RdataIterator object.
 typedef boost::shared_ptr<RdataIterator> RdataIteratorPtr;
@@ -662,6 +661,69 @@ private:
     BasicRRsetImpl* impl_;
 };
 
+/// \brief The \c RRset class is a concrete derived class of
+/// \c BasicRRset which contains a pointer to an additional RRset
+/// containing associated RRSIG records.  This allows DNSSEC aware
+/// applications to treat data associated with a particular
+/// QNAME/QTYPE/QCLASS as a single object.
+class RRset : public BasicRRset {
+public:
+    explicit RRset(const Name& name, const RRClass& rrclass,
+          const RRType& rrtype, const RRTTL& ttl);
+
+    virtual ~RRset();
+
+    /// \brief Updates the owner name of the \c RRset, including RRSIGs if any
+    virtual void setName(const Name& n) {
+        BasicRRset::setName(n);
+        if (rrsig_) {
+            rrsig_->setName(n);
+        }
+    }
+
+    /// \brief Updates the owner name of the \c RRset, including RRSIGs if any
+    virtual void setTTL(const RRTTL& ttl) {
+        BasicRRset::setTTL(ttl);
+        if (rrsig_) {
+            rrsig_->setTTL(ttl);
+        }
+    }
+
+    /// \brief Adds an RRSIG RR to this RRset's signatures
+    virtual void addRRsig(const rdata::RdataPtr rdata) {
+        if (!rrsig_) {
+            rrsig_ = RRsetPtr(new RRset(this->getName(), this->getClass(),
+                                        RRType::RRSIG(), this->getTTL()));
+        }
+        rrsig_->addRdata(rdata);
+    }
+
+    /// \brief Adds an RRSIG RRset to this RRset
+    void addRRsig(AbstractRRset& sigs) {
+        RdataIteratorPtr it = sigs.getRdataIterator();
+
+        if (!rrsig_) {
+            rrsig_ = RRsetPtr(new RRset(this->getName(), this->getClass(),
+                                        RRType::RRSIG(), this->getTTL()));
+        }
+
+        for (it->first(); !it->isLast(); it->next()) {
+            rrsig_->addRdata(it->getCurrent());
+        }
+    }
+
+    void addRRsig(RRsetPtr sigs) { addRRsig(*sigs); }
+
+    /// \brief Clear the RRSIGs for this RRset
+    void removeRRsig() { rrsig_ = RRsetPtr(); }
+
+    /// \brief Return a pointer to this RRset's RRSIG RRset
+    RRsetPtr getRRsig() { return (rrsig_); }
+private:
+    RRsetPtr rrsig_;
+};
+
+
 /// \brief Insert the \c RRset as a string into stream.
 ///
 /// This method convert the \c rrset into a string and inserts it into the

+ 33 - 0
src/lib/dns/cpp/rrtype-placeholder.h

@@ -251,13 +251,46 @@ public:
     // BEGIN_WELL_KNOWN_TYPE_DECLARATIONS
     // END_WELL_KNOWN_TYPE_DECLARATIONS
 
+    static const RRType& IXFR();
+    static const RRType& AXFR();
+    static const RRType& ANY();
+
 private:
+    // \brief Meta-classes
+    // XXX: these should be implemented using rrparamregistry
+    enum {
+        RRTYPE_IXFR = 251,
+        RRTYPE_AXFR = 252,
+        RRTYPE_ANY = 255
+    };
+
     uint16_t typecode_;
 };
 
 // BEGIN_WELL_KNOWN_TYPE_DEFINITIONS
 // END_WELL_KNOWN_TYPE_DEFINITIONS
 
+inline const RRType&
+RRType::IXFR()
+{
+    static RRType rrtype(RRTYPE_IXFR);
+    return (rrtype);
+}
+
+inline const RRType&
+RRType::AXFR()
+{
+    static RRType rrtype(RRTYPE_AXFR);
+    return (rrtype);
+}
+
+inline const RRType&
+RRType::ANY()
+{
+    static RRType rrtype(RRTYPE_ANY);
+    return (rrtype);
+}
+
 ///
 /// \brief Insert the \c RRType as a string into stream.
 ///

+ 5 - 0
src/lib/dns/cpp/tests/Makefile.am

@@ -16,12 +16,17 @@ run_unittests_SOURCES += rdata_in_a_unittest.cc rdata_in_aaaa_unittest.cc
 run_unittests_SOURCES += rdata_ns_unittest.cc rdata_soa_unittest.cc
 run_unittests_SOURCES += rdata_txt_unittest.cc rdata_mx_unittest.cc
 run_unittests_SOURCES += rdata_cname_unittest.cc
+run_unittests_SOURCES += rdata_dname_unittest.cc
+run_unittests_SOURCES += rdata_dnskey_unittest.cc
+run_unittests_SOURCES += rdata_ds_unittest.cc
+run_unittests_SOURCES += rdata_nsec_unittest.cc
 run_unittests_SOURCES += rdata_rrsig_unittest.cc
 run_unittests_SOURCES += rrset_unittest.cc rrsetlist_unittest.cc
 run_unittests_SOURCES += question_unittest.cc
 run_unittests_SOURCES += rrparamregistry_unittest.cc
 run_unittests_SOURCES += message_unittest.cc
 run_unittests_SOURCES += base64_unittest.cc
+run_unittests_SOURCES += hex_unittest.cc
 run_unittests_SOURCES += run_unittests.cc
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)

+ 2 - 2
src/lib/dns/cpp/tests/base64_unittest.cc

@@ -43,11 +43,11 @@ protected:
         test_sequence.push_back(StringPair("foobar", "Zm9vYmFy"));
     }
     vector<StringPair> test_sequence;
-    vector<char> decoded_data;
+    vector<uint8_t> decoded_data;
 };
 
 void
-decodeCheck(const string& input_string, vector<char>& output,
+decodeCheck(const string& input_string, vector<uint8_t>& output,
             const string& expected)
 {
     decodeBase64(input_string, output);

+ 69 - 0
src/lib/dns/cpp/tests/hex_unittest.cc

@@ -0,0 +1,69 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id: rrtype_unittest.cc 476 2010-01-19 00:29:28Z jinmei $
+
+#include <stdint.h>
+
+#include <vector>
+#include <string>
+
+#include <dns/hex.h>
+
+#include <gtest/gtest.h>
+
+#include "unittest_util.h"
+
+using isc::UnitTestUtil;
+using namespace std;
+using namespace isc::dns;
+
+namespace {
+class HexTest : public ::testing::Test {
+protected:
+    HexTest() {}
+    static const std::string hex_txt;
+};
+
+const std::string HexTest::hex_txt("DEADBEEFDECADE");
+
+TEST_F(HexTest, encodeHex) {
+    std::vector<uint8_t>data;
+    std::string s;
+    data.push_back(0xde);
+    data.push_back(0xad);
+    data.push_back(0xbe);
+    data.push_back(0xef);
+    data.push_back(0xde);
+    data.push_back(0xca);
+    data.push_back(0xde);
+    EXPECT_EQ(hex_txt, encodeHex(data));
+}
+
+TEST_F(HexTest, decodeHex) {
+    std::vector<uint8_t>result;
+    result.clear();
+    decodeHex(hex_txt, result);
+    EXPECT_EQ(0xde, result[0]);
+    EXPECT_EQ(0xad, result[1]);
+    EXPECT_EQ(0xbe, result[2]);
+    EXPECT_EQ(0xef, result[3]);
+    EXPECT_EQ(0xde, result[4]);
+    EXPECT_EQ(0xca, result[5]);
+    EXPECT_EQ(0xde, result[6]);
+
+}
+
+}
+

+ 6 - 0
src/lib/dns/cpp/tests/name_unittest.cc

@@ -468,6 +468,12 @@ TEST_F(NameTest, concatenate)
     EXPECT_THROW(n1.concatenate(n2), TooLongName);
 }
 
+TEST_F(NameTest, reverse)
+{
+    EXPECT_PRED_FORMAT2(UnitTestUtil::matchName, example_name.reverse(),
+                        Name("com.example.www."));
+}
+
 TEST_F(NameTest, split)
 {
     // normal cases with or without explicitly specifying the trailing dot.

+ 6 - 4
src/lib/dns/cpp/tests/rdata_rrsig_unittest.cc

@@ -36,20 +36,21 @@ class Rdata_RRSIG_Test : public RdataTest {
     // there's nothing to specialize
 };
 
-TEST_F(Rdata_RRSIG_Test, fromText)
+TEST_F(Rdata_RRSIG_Test, fromText_RRSIG)
 {
-    string rrsig_txt("A 5 4 43200 1264801134 191145710 8496 isc.org. "
+    string rrsig_txt("A 5 4 43200 20100223214617 20100222214617 8496 isc.org. "
                      "evxhlGx13mpKLVkKsjpGzycS5twtIoxOmlN14w9t5AgzGBmz"
                      "diGdLIrFabqr72af2rUq+UDBKMWXujwZTZUTws32sVldDPk/"
                      "NbuacJM25fQXfv5mO3Af7TOoow3AjMaVG9icjCW0V55WcWQU"
                      "f49t+sXKPzbipN9g+s1ZPiIyofc=");
     generic::RRSIG rdata_rrsig(rrsig_txt);
     EXPECT_EQ(rrsig_txt, rdata_rrsig.toText());
+
 }
 
-TEST_F(Rdata_RRSIG_Test, toWireRenderer)
+TEST_F(Rdata_RRSIG_Test, toWireRenderer_RRSIG)
 {
-    string rrsig_txt("A 5 4 43200 1264801134 191145710 8496 isc.org. "
+    string rrsig_txt("A 5 4 43200 20100223214617 20100222214617 8496 isc.org. "
                      "evxhlGx13mpKLVkKsjpGzycS5twtIoxOmlN14w9t5AgzGBmz"
                      "diGdLIrFabqr72af2rUq+UDBKMWXujwZTZUTws32sVldDPk/"
                      "NbuacJM25fQXfv5mO3Af7TOoow3AjMaVG9icjCW0V55WcWQU"
@@ -57,4 +58,5 @@ TEST_F(Rdata_RRSIG_Test, toWireRenderer)
     generic::RRSIG rdata_rrsig(rrsig_txt);
     rdata_rrsig.toWire(renderer);
 }
+
 }

+ 44 - 0
src/lib/dns/cpp/tests/testdata/rdata_dname_fromWire

@@ -0,0 +1,44 @@
+#
+# various kinds of DNAME RDATA stored in an input buffer
+#
+# Valid non-compressed RDATA for dn.example.com.
+# RDLENGHT=16 bytes
+# 0  1
+ 00 10
+# 2  3  4  5  6  7  8  9 10  1  2  3  4  5  6  7(bytes)
+#(2) d  n (7) e  x  a  m  p  l  e (3) c  o  m  .
+ 02 64 6e 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00
+#
+# short length
+# 8  9
+ 00 0f
+#20  1  2  3  4  5  6  7  8  9 30  1  2  3  4  5
+ 02 64 6e 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00
+#
+# length too long
+# 6  7
+ 00 11
+#
+# 8  9 40  1  2  3  4  5  6  7  8  9 50  1  2  3  4
+ 02 64 6e 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 00
+#
+# Valid compressed DNAME name: 'dn2' + pointer
+# 5  6
+ 00 06
+# 7  8  9 60  1  2
+#(3) d  n  2 ptr=5
+ 03 64 6e 32 c0 05
+#
+# Valid compressed DNAME name but RDLENGTH is incorrect: it must be the length
+# of the sequence from the head to the pointer, not the decompressed name
+# length.
+# 3  4
+ 00 11
+# 5  6  7  8  9 70
+ 03 64 6e 32 c0 05
+# incomplete name (no trailing dot).  this can be tested only at the end of
+# the buffer.
+# 1  2
+ 00 0f
+# 3  4  5  6  7  8  9 80  1  2  3  4  5  6  7
+ 02 64 6e 07 65 78 61 6d 70 6c 65 03 63 6f 6d

+ 24 - 0
src/lib/dns/cpp/tests/testdata/rdata_dnskey_fromWire

@@ -0,0 +1,24 @@
+# RDLENGTH = 265 bytes
+ 01 09
+# DNSKEY, flags 257
+ 01 01
+# protocol 3, algorithm 5
+ 03 05
+# keydata:
+ 04 40 00 00 03 a1 1d 00 c1 ae 14 1b b6 98 60 ab
+ 6c 10 52 91 10 e6 de 03 b5 41 f1 a0 c5 45 bb 68
+ 56 2c 33 2f a0 e3 11 5e 31 ab 86 10 9e 16 f0 19
+ 8a 1e f2 24 77 fc 64 67 d6 ea 17 77 f2 15 c6 ff
+ 1c a5 60 23 ba 2a ba 5b 76 88 f0 c7 c6 0c 5c b0
+ 39 fe 40 3e bb 9d 16 20 bf 19 47 54 7a 29 36 ec
+ 61 53 1f fd 0c 79 46 23 5b 3c 29 70 fa f4 fe 53
+ c7 97 10 99 8e db 48 c8 4b 55 0b 82 ac b7 e3 b7
+ 01 07 5c cc 9e 7c ff e0 b2 69 03 47 5a f4 26 ca
+ 8f 70 36 e7 84 f9 d7 9b 0d 20 c7 30 b0 1f 3f db
+ ed 84 eb 7f f3 66 b4 33 06 48 f4 06 b3 7f f4 17
+ b1 8e 98 a4 b3 78 d1 85 96 ad 12 c5 e7 dd d4 f2
+ e3 b4 74 f5 48 b1 e5 67 09 b7 ec 73 a9 9e fe ca
+ cc 8b 28 e3 9e 75 2d fd 67 b4 83 9a c9 f6 78 0d
+ 05 2a d4 29 c0 0e 8b 5d e1 b6 c3 e8 f1 9b 0d e8
+ 03 c9 55 52 01 1f fe bc de 0b f6 c1 c8 13 6c 3b
+ bd 1a 10 54 dd

+ 6 - 0
src/lib/dns/cpp/tests/testdata/rdata_ds_fromWire

@@ -0,0 +1,6 @@
+# RDLENGTH 36 bytes
+00 24
+# DS record, keyid 12892
+32 5c 05 02 f1 e1 84 c0 e1 d6 15 d2 0e b3 c2 23
+ac ed 3b 03 c7 73 dd 95 2d 5f 0e b5 c7 77 58 6d
+e1 8d a6 b5

+ 6 - 0
src/lib/dns/cpp/tests/testdata/rdata_nsec_fromWire

@@ -0,0 +1,6 @@
+# RDLENGTH, 22 bytes
+00 16
+# NSEC record
+# www2.isc.org. CNAME RRSIG NSEC
+04 77 77 77 32 03 69 73 63 03 6f 72 67 00 00 06
+04 00 00 00 00 03