[svn] commit: r988 - in /trunk: ./ src/bin/auth/ src/bin/bind10/ src/lib/auth/cpp/ src/lib/auth/cpp/testdata/ src/lib/dns/cpp/ src/lib/dns/cpp/rdata/generic/ src/lib/dns/cpp/tests/ src/lib/dns/cpp/tests/testdata/

BIND 10 source code commits bind10-changes at lists.isc.org
Fri Feb 26 06:32:44 UTC 2010


Author: each
Date: Fri Feb 26 06:32:44 2010
New Revision: 988

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

Added:
    trunk/src/lib/auth/cpp/data_source_sqlite3.cc
    trunk/src/lib/auth/cpp/data_source_sqlite3.h
    trunk/src/lib/auth/cpp/datasrc_unittest.cc
    trunk/src/lib/auth/cpp/run_unittests.cc
    trunk/src/lib/auth/cpp/testdata/
    trunk/src/lib/auth/cpp/testdata/q_cname
    trunk/src/lib/auth/cpp/testdata/q_cname_ext
    trunk/src/lib/auth/cpp/testdata/q_cname_int
    trunk/src/lib/auth/cpp/testdata/q_dname
    trunk/src/lib/auth/cpp/testdata/q_example_ns
    trunk/src/lib/auth/cpp/testdata/q_example_ptr
    trunk/src/lib/auth/cpp/testdata/q_glork
    trunk/src/lib/auth/cpp/testdata/q_spork
    trunk/src/lib/auth/cpp/testdata/q_sql1
    trunk/src/lib/auth/cpp/testdata/q_subzone
    trunk/src/lib/auth/cpp/testdata/q_subzone_ds
    trunk/src/lib/auth/cpp/testdata/q_wild
    trunk/src/lib/auth/cpp/testdata/q_www
    trunk/src/lib/auth/cpp/unittest_ds.cc
    trunk/src/lib/auth/cpp/unittest_ds.h
    trunk/src/lib/auth/cpp/unittest_util.cc
    trunk/src/lib/auth/cpp/unittest_util.h
    trunk/src/lib/dns/cpp/dnstime.cc
    trunk/src/lib/dns/cpp/dnstime.h
    trunk/src/lib/dns/cpp/hex.cc
    trunk/src/lib/dns/cpp/hex.h
    trunk/src/lib/dns/cpp/rdata/generic/dname_39.cc
    trunk/src/lib/dns/cpp/rdata/generic/dname_39.h
    trunk/src/lib/dns/cpp/rdata/generic/dnskey_48.cc
    trunk/src/lib/dns/cpp/rdata/generic/dnskey_48.h
    trunk/src/lib/dns/cpp/rdata/generic/ds_43.cc
    trunk/src/lib/dns/cpp/rdata/generic/ds_43.h
    trunk/src/lib/dns/cpp/rdata/generic/nsec_47.cc
    trunk/src/lib/dns/cpp/rdata/generic/nsec_47.h
    trunk/src/lib/dns/cpp/tests/hex_unittest.cc
    trunk/src/lib/dns/cpp/tests/testdata/rdata_dname_fromWire
    trunk/src/lib/dns/cpp/tests/testdata/rdata_dnskey_fromWire
    trunk/src/lib/dns/cpp/tests/testdata/rdata_ds_fromWire
    trunk/src/lib/dns/cpp/tests/testdata/rdata_nsec_fromWire
Modified:
    trunk/configure.ac
    trunk/src/bin/auth/Makefile.am
    trunk/src/bin/auth/auth_srv.cc
    trunk/src/bin/auth/auth_srv.h
    trunk/src/bin/bind10/bind10.py.in
    trunk/src/lib/auth/cpp/Makefile.am
    trunk/src/lib/auth/cpp/TODO
    trunk/src/lib/auth/cpp/data_source.cc
    trunk/src/lib/auth/cpp/data_source.h
    trunk/src/lib/auth/cpp/data_source_static.cc
    trunk/src/lib/auth/cpp/data_source_static.h
    trunk/src/lib/auth/cpp/query.cc
    trunk/src/lib/auth/cpp/query.h
    trunk/src/lib/dns/cpp/Makefile.am
    trunk/src/lib/dns/cpp/base64.cc
    trunk/src/lib/dns/cpp/base64.h
    trunk/src/lib/dns/cpp/message.cc
    trunk/src/lib/dns/cpp/message.h
    trunk/src/lib/dns/cpp/name.cc
    trunk/src/lib/dns/cpp/name.h
    trunk/src/lib/dns/cpp/rdata/generic/mx_15.cc
    trunk/src/lib/dns/cpp/rdata/generic/mx_15.h
    trunk/src/lib/dns/cpp/rdata/generic/rrsig_46.cc
    trunk/src/lib/dns/cpp/rrclass-placeholder.h
    trunk/src/lib/dns/cpp/rrset.cc
    trunk/src/lib/dns/cpp/rrset.h
    trunk/src/lib/dns/cpp/rrtype-placeholder.h
    trunk/src/lib/dns/cpp/tests/Makefile.am
    trunk/src/lib/dns/cpp/tests/base64_unittest.cc
    trunk/src/lib/dns/cpp/tests/name_unittest.cc
    trunk/src/lib/dns/cpp/tests/rdata_rrsig_unittest.cc

Modified: trunk/configure.ac
==============================================================================
--- trunk/configure.ac (original)
+++ trunk/configure.ac Fri Feb 26 06:32:44 2010
@@ -138,6 +138,8 @@
 AC_SUBST(GTEST_INCLUDES)
 AC_SUBST(GTEST_LDFLAGS)
 AC_SUBST(GTEST_LDADD)
+
+PKG_CHECK_MODULES(SQLITE, sqlite3)
 
 # Checks for library functions.
 

Modified: trunk/src/bin/auth/Makefile.am
==============================================================================
--- trunk/src/bin/auth/Makefile.am (original)
+++ trunk/src/bin/auth/Makefile.am Fri Feb 26 06:32:44 2010
@@ -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 @@
 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}

Modified: trunk/src/bin/auth/auth_srv.cc
==============================================================================
--- trunk/src/bin/auth/auth_srv.cc (original)
+++ trunk/src/bin/auth/auth_srv.cc Fri Feb 26 06:32:44 2010
@@ -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 @@
         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 @@
 
         // do the DataSource call here
         Query q = Query(msg, false);
-        data_src.runQuery(q);
+        data_src.doQuery(q);
 
         OutputBuffer obuffer(4096);
         MessageRenderer renderer(obuffer);

Modified: trunk/src/bin/auth/auth_srv.h
==============================================================================
--- trunk/src/bin/auth/auth_srv.h (original)
+++ trunk/src/bin/auth/auth_srv.h Fri Feb 26 06:32:44 2010
@@ -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 @@
     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;
 };
 

Modified: trunk/src/bin/bind10/bind10.py.in
==============================================================================
--- trunk/src/bin/bind10/bind10.py.in (original)
+++ trunk/src/bin/bind10/bind10.py.in Fri Feb 26 06:32:44 2010
@@ -50,7 +50,7 @@
 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

Modified: trunk/src/lib/auth/cpp/Makefile.am
==============================================================================
--- trunk/src/lib/auth/cpp/Makefile.am (original)
+++ trunk/src/lib/auth/cpp/Makefile.am Fri Feb 26 06:32:44 2010
@@ -5,4 +5,21 @@
 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)

Modified: trunk/src/lib/auth/cpp/TODO
==============================================================================
--- trunk/src/lib/auth/cpp/TODO (original)
+++ trunk/src/lib/auth/cpp/TODO Fri Feb 26 06:32:44 2010
@@ -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

Modified: trunk/src/lib/auth/cpp/data_source.cc
==============================================================================
--- trunk/src/lib/auth/cpp/data_source.cc (original)
+++ trunk/src/lib/auth/cpp/data_source.cc Fri Feb 26 06:32:44 2010
@@ -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 {
-
-DSResult
-DataSrc::runQuery(Query q) {
-    DSResult result;
-    Name container(".");
+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));
+    }
+
+    // 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);
+    }
+}
+
+// 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;
+        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, sigs;
-        bool found = false;
-        QueryTaskPtr task = q.tasks().front();
+        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 {
-            result = ds->findRRset(task->qname, task->qclass, task->qtype,
-                                   data);
-        }
-
-        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 (q.status() == QUERY_FINISHING) {
-                    q.setStatus(QUERY_DONE);
-                    return (SUCCESS);
-                }
-
-                // 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);
-                }
+            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;
-
-            case CNAME:
-                m.addRRset(task->section, data[0]);
-                if (q.wantDnssec() && sigs.size() == 1) {
-                    m.addRRset(task->section, sigs[0]);
-                }
-
-                // if (data[0].getType() == RRType::CNAME()) {
-                //     // take apart the CNAME rdata and re-query HERE
-                // }
+            }
+
+            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 {
+            task.flags = NO_SUCH_ZONE;
+        }
+
+        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;
+                    }
+
+                    copyAuth(q, auth);
+                }
                 continue;
 
-            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);
-                }
-
+            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;
+
+            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 (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);
+            }
+
+            if (task.state == QueryTask::GETANSWER) {
+                if (task.flags & NAME_NOT_FOUND) {
+                    m.setRcode(Rcode::NXDOMAIN());
+                }
+
+                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;
+                }
+
+                m.addRRset(Section::AUTHORITY(), soa[RRType::SOA()],
+                           q.wantDnssec());
+            }
+
+            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 (ERROR);
-                }
-
-                m.setRcode(Rcode::NXDOMAIN());
-                m.addRRset(Section::AUTHORITY(), data[0]);
-                if (q.wantDnssec() && sigs.size() == 1) {
-                    m.addRRset(Section::AUTHORITY(), sigs[0]);
-                }
-                break;
-
-            case TYPE_NOT_FOUND:
-                m.setRcode(Rcode::NOERROR());
-                q.setStatus(QUERY_NODATA);
-                return (result);
-
-            case ZONE_NOT_FOUND:
-                m.setRcode(Rcode::REFUSED());
-                q.setStatus(QUERY_NODATA);
-                return (result);
-
-            default:
-                m.setRcode(Rcode::SERVFAIL());
-                q.setStatus(QUERY_NODATA);
-                return (result);
-        }
-    }
-
-    return (result);
-};
-
-
-}
-}
+                    return;
+                }
+
+                if (t.flags == 0) {
+                    m.addRRset(Section::AUTHORITY(), nsec[RRType::NSEC()],
+                               true);
+                }
+            }
+
+            return;
+        } else {
+            // Should never be reached!
+            m.setRcode(Rcode::SERVFAIL());
+            return;
+        }
+    }
+
+    // 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);
+            }
+        }
+    }
+}
+
+}
+}

Modified: trunk/src/lib/auth/cpp/data_source.h
==============================================================================
--- trunk/src/lib/auth/cpp/data_source.h (original)
+++ trunk/src/lib/auth/cpp/data_source.h Fri Feb 26 06:32:44 2010
@@ -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;
+    // general DataSrc class, and SHOULD NOT be overwritten by subclasses.
+    virtual void doQuery(Query query) = 0;
+
+    // XXX: High-level methods to be implemented later:
+    // virtual void doUpdate(Update update) = 0;
+    // virtual void doXfr(Query query) = 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 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 DSResult findRRset(const Name& qname,
-                               const RRClass& qclass,
-                               const RRType& qtype,
-                               RRsetList& target,
-                               RRsetList& sigs) const = 0;
-
-    virtual DSResult findRRset(const Name& qname,
-                               const RRClass& qclass,
-                               const RRType& qtype,
-                               RRsetList& target) const = 0;
-
-    virtual const DataSrc* findClosestEnclosure(const Name& qname,
-                                                Name& container,
-                                                bool& found) 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 const RRClass& getClass() const = 0;
-    //virtual const RRClass& setClass() const = 0;
+    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;
+
 };
 
 // Base class for a DNS Data Source
@@ -80,29 +124,111 @@
     DataSrc(const RRClass& c) : rrclass(c) {}
     virtual ~DataSrc() {};
 
-    DSResult runQuery(Query q);
-
-    virtual DSResult findRRset(const Name& qname,
-                               const RRClass& qclass,
-                               const RRType& qtype,
-                               RRsetList& target,
-                               RRsetList& sigs) const = 0;
-
-    virtual DSResult findRRset(const Name& qname,
-                               const RRClass& qclass,
-                               const RRType& qtype,
-                               RRsetList& target) const = 0;
-
-    virtual const DataSrc* findClosestEnclosure(const Name& qname,
-                                                Name& container,
-                                                bool& found) const = 0;
+    void doQuery(Query q);
+
+    virtual void findClosestEnclosure(NameMatch& match) const = 0;
 
     const RRClass& getClass() const { return rrclass; }
     void setClass(RRClass& c) { rrclass = c; }
-
-    DSResult init() { return NOT_IMPLEMENTED; }
-    DSResult close() { return NOT_IMPLEMENTED; }
-
+    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,
+                               RRsetList& target,
+                               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;
+        }
+
+        flags = 0;
+        r = findExactRRset(q, qname, qclass, RRType::AAAA(), target,
+                           flags, zone);
+        if (r == SUCCESS && flags == 0) {
+            aaaa = true;
+        }
+
+        if (!a && !aaaa) {
+            flags = TYPE_NOT_FOUND;
+        } else {
+            flags = 0;
+        }
+
+        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);
+        }
+
+        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 @@
         }
 
         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;
-            }
-        }
-
-        return (best);
-    };
+            ds->findClosestEnclosure(match);
+        }
+    }
+
+    // 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_;
+};
+
 }
 }
 

Modified: trunk/src/lib/auth/cpp/data_source_static.cc
==============================================================================
--- trunk/src/lib/auth/cpp/data_source_static.cc (original)
+++ trunk/src/lib/auth/cpp/data_source_static.cc Fri Feb 26 06:32:44 2010
@@ -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>
 
+using namespace std;
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+
 namespace isc {
-namespace dns {
-
-using namespace isc::dns::rdata;
+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 @@
     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();
+void
+StaticDataSrc::findClosestEnclosure(NameMatch& match) const {
+    const Name& qname = match.qname();
+    NameComparisonResult::NameRelation cmp;
 
-    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;
-        }
+    cmp = qname.compare(version_name).getRelation();
+    if (cmp == NameComparisonResult::EQUAL ||
+        cmp == NameComparisonResult::SUBDOMAIN) {
+        match.update(*this, version_name);
+        return;
     }
 
-    NameComparisonResult::NameRelation authors_cmp = 
-        qname.compare(authors_name).getRelation();
+    cmp = qname.compare(authors_name).getRelation();
+    if (cmp == NameComparisonResult::EQUAL ||
+        cmp == NameComparisonResult::SUBDOMAIN) {
+        match.update(*this, authors_name);
+        return;
+    }
+}
 
-    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;
-        }
+DataSrc::Result
+StaticDataSrc::findRRset(const Query& q,
+                         const Name& qname,
+                         const RRClass& qclass,
+                         const RRType& qtype,
+                         RRsetList& target,
+                         uint32_t& flags,
+                         Name* zone) const
+{
+    flags = 0;
+    if (qclass != getClass()) {
+        return (ERROR);
     }
 
-    return NULL;
-}
+    bool any = (qtype == RRType::ANY());
 
-DSResult
-StaticDataSrc::findRRset(const Name& qname,
-                         const RRClass& qclass,
-                         const RRType& qtype,
-                         RRsetList& target) 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;
+    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;
     }
-    // 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);
+    return (SUCCESS);
 }
 
 }

Modified: trunk/src/lib/auth/cpp/data_source_static.h
==============================================================================
--- trunk/src/lib/auth/cpp/data_source_static.h (original)
+++ trunk/src/lib/auth/cpp/data_source_static.h Fri Feb 26 06:32:44 2010
@@ -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() {}
 
-    const DataSrc* findClosestEnclosure(const Name& qname,
-                                        Name& container,
-                                        bool& found) const;
+    void findClosestEnclosure(NameMatch& match) const;
 
-    DSResult findRRset(const Name& qname,
-                       const RRClass& qclass,
-                       const RRType& qtype,
-                       RRsetList& target, RRsetList& sigs) 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) 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 init() { return SUCCESS; };
-    DSResult close() { return SUCCESS; } ;
+    Result findPreviousName(const Query& q,
+                            const Name& qname,
+                            Name& target,
+                            Name* zone) const {
+        return (NOT_IMPLEMENTED);
+    }
+
+    Result init() { return (SUCCESS); }
+    Result close() { return (SUCCESS); }
 
 private:
     const Name authors_name;

Modified: trunk/src/lib/auth/cpp/query.cc
==============================================================================
--- trunk/src/lib/auth/cpp/query.cc (original)
+++ trunk/src/lib/auth/cpp/query.cc Fri Feb 26 06:32:44 2010
@@ -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() {}
 
 }
 }

Modified: trunk/src/lib/auth/cpp/query.h
==============================================================================
--- trunk/src/lib/auth/cpp/query.h (original)
+++ trunk/src/lib/auth/cpp/query.h Fri Feb 26 06:32:44 2010
@@ -26,40 +26,152 @@
 #include <dns/rrclass.h>
 #include <dns/rrtype.h>
 
+using namespace isc::dns;
+
 namespace isc {
-namespace dns {
-
-enum QueryStatus {
-    QUERY_INCOMPLETE,
-    QUERY_FINISHING,
-    QUERY_DONE,
-    QUERY_NODATA
-};
-
-///
-/// \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 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 @@
 // 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");
-
-        // Populate the query tasks queue with the initial question
+        if (message_->getRRCount(Section::QUESTION()) != 1) {
+            dns_throw(Unexpected, "malformed message: too many questions");
+        }
+
+        // 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 @@
     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_;

Modified: trunk/src/lib/dns/cpp/Makefile.am
==============================================================================
--- trunk/src/lib/dns/cpp/Makefile.am (original)
+++ trunk/src/lib/dns/cpp/Makefile.am Fri Feb 26 06:32:44 2010
@@ -19,6 +19,8 @@
 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

Modified: trunk/src/lib/dns/cpp/base64.cc
==============================================================================
--- trunk/src/lib/dns/cpp/base64.cc (original)
+++ trunk/src/lib/dns/cpp/base64.cc Fri Feb 26 06:32:44 2010
@@ -14,6 +14,7 @@
 
 // $Id$
 
+#include <stdint.h>
 #include <cassert>
 #include <iterator>
 #include <string>
@@ -34,13 +35,12 @@
 
 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 @@
         }
         return (*this);
     }
-    const char& operator*() const {
+    const uint8_t& operator*() const {
         if (in_pad_) {
             return (BINARY_ZERO_CODE);
         } else {
@@ -65,17 +65,17 @@
         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 @@
 } // 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 @@
     // 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");

Modified: trunk/src/lib/dns/cpp/base64.h
==============================================================================
--- trunk/src/lib/dns/cpp/base64.h (original)
+++ trunk/src/lib/dns/cpp/base64.h Fri Feb 26 06:32:44 2010
@@ -17,6 +17,7 @@
 #ifndef __BASE64_H
 #define __BASE64_H 1
 
+#include <stdint.h>
 #include <string>
 #include <vector>
 
@@ -41,8 +42,8 @@
         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);
 }
 }
 

Modified: trunk/src/lib/dns/cpp/message.cc
==============================================================================
--- trunk/src/lib/dns/cpp/message.cc (original)
+++ trunk/src/lib/dns/cpp/message.cc Fri Feb 26 06:32:44 2010
@@ -216,6 +216,12 @@
     impl_->flags_ |= flag.getBit();
 }
 
+void
+Message::clearHeaderFlag(const MessageFlag& flag)
+{
+    impl_->flags_ &= ~flag.getBit();
+}
+
 qid_t
 Message::getQid() const
 {
@@ -259,11 +265,17 @@
 }
 
 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

Modified: trunk/src/lib/dns/cpp/message.h
==============================================================================
--- trunk/src/lib/dns/cpp/message.h (original)
+++ trunk/src/lib/dns/cpp/message.h Fri Feb 26 06:32:44 2010
@@ -478,6 +478,7 @@
 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 @@
     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 @@
     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;

Modified: trunk/src/lib/dns/cpp/name.cc
==============================================================================
--- trunk/src/lib/dns/cpp/name.cc (original)
+++ trunk/src/lib/dns/cpp/name.cc Fri Feb 26 06:32:44 2010
@@ -18,6 +18,8 @@
 #include <cassert>
 #include <iterator>
 #include <functional>
+#include <vector>
+#include <iostream>
 
 #include <algorithm>
 
@@ -606,6 +608,36 @@
 }
 
 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_) {

Modified: trunk/src/lib/dns/cpp/name.h
==============================================================================
--- trunk/src/lib/dns/cpp/name.h (original)
+++ trunk/src/lib/dns/cpp/name.h Fri Feb 26 06:32:44 2010
@@ -513,6 +513,14 @@
     /// 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

Modified: trunk/src/lib/dns/cpp/rdata/generic/mx_15.cc
==============================================================================
--- trunk/src/lib/dns/cpp/rdata/generic/mx_15.cc (original)
+++ trunk/src/lib/dns/cpp/rdata/generic/mx_15.cc Fri Feb 26 06:32:44 2010
@@ -38,10 +38,21 @@
     // 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 @@
     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

Modified: trunk/src/lib/dns/cpp/rdata/generic/mx_15.h
==============================================================================
--- trunk/src/lib/dns/cpp/rdata/generic/mx_15.h (original)
+++ trunk/src/lib/dns/cpp/rdata/generic/mx_15.h Fri Feb 26 06:32:44 2010
@@ -36,6 +36,12 @@
 
     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.

Modified: trunk/src/lib/dns/cpp/rdata/generic/rrsig_46.cc
==============================================================================
--- trunk/src/lib/dns/cpp/rdata/generic/rrsig_46.cc (original)
+++ trunk/src/lib/dns/cpp/rdata/generic/rrsig_46.cc Fri Feb 26 06:32:44 2010
@@ -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 @@
     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 @@
         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 @@
 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_));

Modified: trunk/src/lib/dns/cpp/rrclass-placeholder.h
==============================================================================
--- trunk/src/lib/dns/cpp/rrclass-placeholder.h (original)
+++ trunk/src/lib/dns/cpp/rrclass-placeholder.h Fri Feb 26 06:32:44 2010
@@ -238,16 +238,16 @@
     // 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_;
 };
 

Modified: trunk/src/lib/dns/cpp/rrset.cc
==============================================================================
--- trunk/src/lib/dns/cpp/rrset.cc (original)
+++ trunk/src/lib/dns/cpp/rrset.cc Fri Feb 26 06:32:44 2010
@@ -214,6 +214,15 @@
     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:

Modified: trunk/src/lib/dns/cpp/rrset.h
==============================================================================
--- trunk/src/lib/dns/cpp/rrset.h (original)
+++ trunk/src/lib/dns/cpp/rrset.h Fri Feb 26 06:32:44 2010
@@ -25,6 +25,7 @@
 #include <exceptions/exceptions.h>
 
 #include "rdata.h"
+#include "rrtype.h"
 
 namespace isc {
 namespace dns {
@@ -50,22 +51,20 @@
 class BasicRRset;
 class RdataIterator;
 class BasicRRsetImpl;
-
-/// \brief A pointer-like type pointing to an \c AbstractRRset object.
+class RRset;
+
+/// \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;
-
-/// \brief A pointer-like type pointing to an (immutable) \c AbstractRRset
+typedef boost::shared_ptr<RRset> RRsetPtr;
+
+/// \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 @@
     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

Modified: trunk/src/lib/dns/cpp/rrtype-placeholder.h
==============================================================================
--- trunk/src/lib/dns/cpp/rrtype-placeholder.h (original)
+++ trunk/src/lib/dns/cpp/rrtype-placeholder.h Fri Feb 26 06:32:44 2010
@@ -251,12 +251,45 @@
     // 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.

Modified: trunk/src/lib/dns/cpp/tests/Makefile.am
==============================================================================
--- trunk/src/lib/dns/cpp/tests/Makefile.am (original)
+++ trunk/src/lib/dns/cpp/tests/Makefile.am Fri Feb 26 06:32:44 2010
@@ -16,12 +16,17 @@
 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)

Modified: trunk/src/lib/dns/cpp/tests/base64_unittest.cc
==============================================================================
--- trunk/src/lib/dns/cpp/tests/base64_unittest.cc (original)
+++ trunk/src/lib/dns/cpp/tests/base64_unittest.cc Fri Feb 26 06:32:44 2010
@@ -43,11 +43,11 @@
         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);

Modified: trunk/src/lib/dns/cpp/tests/name_unittest.cc
==============================================================================
--- trunk/src/lib/dns/cpp/tests/name_unittest.cc (original)
+++ trunk/src/lib/dns/cpp/tests/name_unittest.cc Fri Feb 26 06:32:44 2010
@@ -468,6 +468,12 @@
     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.

Modified: trunk/src/lib/dns/cpp/tests/rdata_rrsig_unittest.cc
==============================================================================
--- trunk/src/lib/dns/cpp/tests/rdata_rrsig_unittest.cc (original)
+++ trunk/src/lib/dns/cpp/tests/rdata_rrsig_unittest.cc Fri Feb 26 06:32:44 2010
@@ -36,20 +36,21 @@
     // 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 @@
     generic::RRSIG rdata_rrsig(rrsig_txt);
     rdata_rrsig.toWire(renderer);
 }
+
 }




More information about the bind10-changes mailing list