[svn] commit: r4146 - in /branches/trac458: ./ src/bin/auth/ src/bin/auth/benchmarks/ src/bin/auth/tests/ src/bin/auth/tests/testdata/ src/bin/bind10/ src/bin/recurse/tests/ src/lib/asiolink/ src/lib/asiolink/internal/ src/lib/asiolink/internal/tests/ src/lib/asiolink/tests/ src/lib/datasrc/ src/lib/datasrc/tests/ src/lib/datasrc/tests/testdata/ src/lib/log/ src/lib/python/isc/config/ src/lib/python/isc/config/tests/ src/lib/python/isc/utils/ src/lib/testutils/ src/lib/testutils/testdata/

BIND 10 source code commits bind10-changes at lists.isc.org
Tue Jan 4 09:09:02 UTC 2011


Author: vorner
Date: Tue Jan  4 09:09:01 2011
New Revision: 4146

Log:
Sync with trunk

Added:
    branches/trac458/src/lib/asiolink/internal/Makefile.am
      - copied unchanged from r4145, trunk/src/lib/asiolink/internal/Makefile.am
    branches/trac458/src/lib/asiolink/internal/tests/
      - copied from r4145, trunk/src/lib/asiolink/internal/tests/
    branches/trac458/src/lib/datasrc/tests/testdata/duplicate_rrset.zone
      - copied unchanged from r4145, trunk/src/lib/datasrc/tests/testdata/duplicate_rrset.zone
    branches/trac458/src/lib/testutils/srv_test.cc
      - copied unchanged from r4145, trunk/src/lib/testutils/srv_test.cc
    branches/trac458/src/lib/testutils/testdata/example.com.zone
      - copied unchanged from r4145, trunk/src/lib/testutils/testdata/example.com.zone
    branches/trac458/src/lib/testutils/testdata/example.net.zone
      - copied unchanged from r4145, trunk/src/lib/testutils/testdata/example.net.zone
    branches/trac458/src/lib/testutils/testdata/example.org.zone
      - copied unchanged from r4145, trunk/src/lib/testutils/testdata/example.org.zone
    branches/trac458/src/lib/testutils/testdata/example.zone
      - copied unchanged from r4145, trunk/src/lib/testutils/testdata/example.zone
    branches/trac458/src/lib/testutils/testdata/iquery_fromWire.spec
      - copied unchanged from r4145, trunk/src/lib/testutils/testdata/iquery_fromWire.spec
    branches/trac458/src/lib/testutils/testdata/iquery_response_fromWire.spec
      - copied unchanged from r4145, trunk/src/lib/testutils/testdata/iquery_response_fromWire.spec
Removed:
    branches/trac458/src/lib/asiolink/tests/udpdns_unittest.cc
    branches/trac458/src/lib/testutils/srv_unittest.h
Modified:
    branches/trac458/   (props changed)
    branches/trac458/ChangeLog
    branches/trac458/configure.ac
    branches/trac458/src/bin/auth/auth_srv.cc
    branches/trac458/src/bin/auth/benchmarks/query_bench.cc
    branches/trac458/src/bin/auth/config.cc
    branches/trac458/src/bin/auth/query.cc
    branches/trac458/src/bin/auth/query.h
    branches/trac458/src/bin/auth/tests/Makefile.am
    branches/trac458/src/bin/auth/tests/auth_srv_unittest.cc
    branches/trac458/src/bin/auth/tests/config_unittest.cc
    branches/trac458/src/bin/auth/tests/query_unittest.cc
    branches/trac458/src/bin/auth/tests/testdata/   (props changed)
    branches/trac458/src/bin/auth/tests/testdata/queryBadEDNS_fromWire.spec
    branches/trac458/src/bin/bind10/bind10.py.in   (props changed)
    branches/trac458/src/bin/recurse/tests/   (props changed)
    branches/trac458/src/bin/recurse/tests/Makefile.am
    branches/trac458/src/bin/recurse/tests/recursor_config_unittest.cc
    branches/trac458/src/bin/recurse/tests/recursor_unittest.cc
    branches/trac458/src/lib/asiolink/   (props changed)
    branches/trac458/src/lib/asiolink/Makefile.am
    branches/trac458/src/lib/asiolink/asiolink.h
    branches/trac458/src/lib/asiolink/tests/Makefile.am
    branches/trac458/src/lib/asiolink/tests/asiolink_unittest.cc
    branches/trac458/src/lib/datasrc/cache.h
    branches/trac458/src/lib/datasrc/data_source.cc
    branches/trac458/src/lib/datasrc/memory_datasrc.cc
    branches/trac458/src/lib/datasrc/memory_datasrc.h
    branches/trac458/src/lib/datasrc/rbtree.h
    branches/trac458/src/lib/datasrc/tests/memory_datasrc_unittest.cc
    branches/trac458/src/lib/datasrc/tests/rbtree_unittest.cc
    branches/trac458/src/lib/datasrc/zone.h
    branches/trac458/src/lib/datasrc/zonetable.h
    branches/trac458/src/lib/log/   (props changed)
    branches/trac458/src/lib/python/isc/config/module_spec.py
    branches/trac458/src/lib/python/isc/config/tests/module_spec_test.py
    branches/trac458/src/lib/python/isc/utils/   (props changed)
    branches/trac458/src/lib/testutils/   (props changed)
    branches/trac458/src/lib/testutils/Makefile.am
    branches/trac458/src/lib/testutils/README
    branches/trac458/src/lib/testutils/srv_test.h
    branches/trac458/src/lib/testutils/testdata/Makefile.am

Modified: branches/trac458/ChangeLog
==============================================================================
--- branches/trac458/ChangeLog (original)
+++ branches/trac458/ChangeLog Tue Jan  4 09:09:01 2011
@@ -1,3 +1,19 @@
+  143.	[build]		jinmei
+	Fixed build problems with clang++ in unit tests due to recent
+	changes.  No behavior change. (Trac #448, svn r4133)
+
+  142.	[func]		jinmei
+	b10-auth: updated query benchmark so that it can test in memory
+	data source.  Also fixed a bug that the output buffer isn't
+	cleared after query processing, resulting in misleading results
+	or program crash.  This is a regression due to change #135.
+	(Trac #465, svn r4103)
+
+  141.	[bug]		jinmei
+	b10-auth: Fixed a bug that the authoritative server includes
+	trailing garbage data in responses.  This is a regression due to
+	change #135. (Trac #462, svn r4081)
+
   140.  [func]		y-aharen
 	src/bin/auth: Added a feature to count queries and send counter
 	values to statistics periodically. To support it, added wrapping

Modified: branches/trac458/configure.ac
==============================================================================
--- branches/trac458/configure.ac (original)
+++ branches/trac458/configure.ac Tue Jan  4 09:09:01 2011
@@ -597,6 +597,8 @@
                  src/lib/Makefile
                  src/lib/asiolink/Makefile
                  src/lib/asiolink/tests/Makefile
+                 src/lib/asiolink/internal/Makefile
+                 src/lib/asiolink/internal/tests/Makefile
                  src/lib/bench/Makefile
                  src/lib/bench/example/Makefile
                  src/lib/bench/tests/Makefile

Modified: branches/trac458/src/bin/auth/auth_srv.cc
==============================================================================
--- branches/trac458/src/bin/auth/auth_srv.cc (original)
+++ branches/trac458/src/bin/auth/auth_srv.cc Tue Jan  4 09:09:01 2011
@@ -162,33 +162,20 @@
     AuthSrv* server_;
 };
 
-// This is a derived class of \c DNSAnswer, to serve as a
-// callback in the asiolink module.  It takes a completed
-// set of answer data from the DNS lookup and assembles it
-// into a wire-format response.
+// This is a derived class of \c DNSAnswer, to serve as a callback in the
+// asiolink module.  We actually shouldn't do anything in this class because
+// we build complete response messages in the process methods; otherwise
+// the response message will contain trailing garbage.  In future, we should
+// probably even drop the reliance on DNSAnswer.  We don't need the coroutine
+// tricks provided in that framework, and its overhead would be significant
+// in terms of performance consideration for the authoritative server
+// implementation.
 class MessageAnswer : public DNSAnswer {
 public:
-    MessageAnswer(AuthSrv* srv) : server_(srv) {}
-    virtual void operator()(const IOMessage& io_message, MessagePtr message,
-                            OutputBufferPtr buffer) const
-    {
-        MessageRenderer renderer(*buffer);
-        if (io_message.getSocket().getProtocol() == IPPROTO_UDP) {
-            ConstEDNSPtr edns(message->getEDNS());
-            renderer.setLengthLimit(edns ? edns->getUDPSize() :
-                Message::DEFAULT_MAX_UDPSIZE);
-        } else {
-            renderer.setLengthLimit(65535);
-        }
-        message->toWire(renderer);
-        if (server_->getVerbose()) {
-            cerr << "[b10-auth] sending a response (" << renderer.getLength()
-                 << " bytes):\n" << message->toText() << endl;
-        }
-    }
-
-private:
-    AuthSrv* server_;
+    MessageAnswer(AuthSrv*) {}
+    virtual void operator()(const IOMessage&, MessagePtr,
+                            OutputBufferPtr) const
+    {}
 };
 
 // This is a derived class of \c SimpleCallback, to serve

Modified: branches/trac458/src/bin/auth/benchmarks/query_bench.cc
==============================================================================
--- branches/trac458/src/bin/auth/benchmarks/query_bench.cc (original)
+++ branches/trac458/src/bin/auth/benchmarks/query_bench.cc Tue Jan  4 09:09:01 2011
@@ -33,6 +33,7 @@
 #include <xfr/xfrout_client.h>
 
 #include <auth/auth_srv.h>
+#include <auth/config.h>
 #include <auth/query.h>
 
 #include <asiolink/asiolink.h>
@@ -53,24 +54,25 @@
 // Just something to pass as the server to resume
 class DummyServer : public DNSServer {
     public:
-        virtual void operator()(asio::error_code, size_t) { }
-        virtual void resume(const bool) { }
+        virtual void operator()(asio::error_code, size_t) {}
+        virtual void resume(const bool) {}
         virtual DNSServer* clone() {
-            return new DummyServer(*this);
+            return (new DummyServer(*this));
         }
 };
 
 class QueryBenchMark {
-private:
+protected:
     // Maintain dynamically generated objects via shared pointers because
     // QueryBenchMark objects will be copied.
     typedef boost::shared_ptr<AuthSrv> AuthSrvPtr;
+private:
     typedef boost::shared_ptr<const IOEndpoint> IOEndpointPtr;
-public:
-    QueryBenchMark(const int cache_slots, const char* const datasrc_file,
+protected:
+    QueryBenchMark(const bool enable_cache,
                    const BenchQueries& queries, MessagePtr query_message,
                    OutputBufferPtr buffer) :
-        server_(new AuthSrv(cache_slots >= 0 ? true : false, xfrout_client)),
+        server_(new AuthSrv(enable_cache, xfrout_client)),
         queries_(queries),
         query_message_(query_message),
         buffer_(buffer),
@@ -78,13 +80,8 @@
         dummy_endpoint(IOEndpointPtr(IOEndpoint::create(IPPROTO_UDP,
                                                         IOAddress("192.0.2.1"),
                                                         5300)))
-    {
-        if (cache_slots >= 0) {
-            server_->setCacheSlots(cache_slots);
-        }
-        server_->updateConfig(Element::fromJSON("{\"database_file\": \"" +
-                                                string(datasrc_file) + "\"}"));
-    }
+    {}
+public:
     unsigned int run() {
         BenchQueries::const_iterator query;
         const BenchQueries::const_iterator query_end = queries_.end();
@@ -93,14 +90,16 @@
             IOMessage io_message(&(*query)[0], (*query).size(), dummy_socket,
                                  *dummy_endpoint);
             query_message_->clear(Message::PARSE);
+            buffer_->clear();
             server_->processMessage(io_message, query_message_, buffer_,
-                &server);
+                                    &server);
         }
 
         return (queries_.size());
     }
+protected:
+    AuthSrvPtr server_;
 private:
-    AuthSrvPtr server_;
     const BenchQueries& queries_;
     MessagePtr query_message_;
     OutputBufferPtr buffer_;
@@ -108,26 +107,92 @@
     IOEndpointPtr dummy_endpoint;
 };
 
+class Sqlite3QueryBenchMark  : public QueryBenchMark {
+public:
+    Sqlite3QueryBenchMark(const int cache_slots,
+                          const char* const datasrc_file,
+                          const BenchQueries& queries,
+                          MessagePtr query_message,
+                          OutputBufferPtr buffer) :
+        QueryBenchMark(cache_slots >= 0 ? true : false, queries,
+                       query_message, buffer)
+    {
+        if (cache_slots >= 0) {
+            server_->setCacheSlots(cache_slots);
+        }
+        server_->updateConfig(Element::fromJSON("{\"database_file\": \"" +
+                                                string(datasrc_file) + "\"}"));
+    }
+};
+
+class MemoryQueryBenchMark  : public QueryBenchMark {
+public:
+    MemoryQueryBenchMark(const char* const zone_file,
+                         const char* const zone_origin,
+                          const BenchQueries& queries,
+                          MessagePtr query_message,
+                          OutputBufferPtr buffer) :
+        QueryBenchMark(false, queries, query_message, buffer)
+    {
+        configureAuthServer(*server_,
+                            Element::fromJSON(
+                                "{\"datasources\": "
+                                " [{\"type\": \"memory\","
+                                "   \"zones\": [{\"origin\": \"" +
+                                string(zone_origin) + "\","
+                                "    \"file\": \"" +
+                                string(zone_file) + "\"}]}]}"));
+    }
+};
+
+void
+printQPSResult(unsigned int iteration, double duration,
+            double iteration_per_second)
+{
+    cout.precision(6);
+    cout << "Processed " << iteration << " queries in "
+         << fixed << duration << "s";
+    cout.precision(2);
+    cout << " (" << fixed << iteration_per_second << "qps)" << endl;
+}
 }
 
 namespace isc {
 namespace bench {
 template<>
 void
-BenchMark<QueryBenchMark>::printResult() const {
-    cout.precision(6);
-    cout << "Processed " << getIteration() << " queries in "
-         << fixed << getDuration() << "s";
-    cout.precision(2);
-    cout << " (" << fixed << getIterationPerSecond() << "qps)" << endl;
+BenchMark<Sqlite3QueryBenchMark>::printResult() const {
+    printQPSResult(getIteration(), getDuration(), getIterationPerSecond());
+}
+
+template<>
+void
+BenchMark<MemoryQueryBenchMark>::printResult() const {
+    printQPSResult(getIteration(), getDuration(), getIterationPerSecond());
 }
 }
 }
 
 namespace {
+const int ITERATION_DEFAULT = 1;
+enum DataSrcType {
+    SQLITE3,
+    MEMORY
+};
+
 void
 usage() {
-    cerr << "Usage: query_bench [-n iterations] datasrc_file query_datafile"
+    cerr <<
+        "Usage: query_bench [-n iterations] [-t datasrc_type] [-o origin] "
+        "datasrc_file query_datafile\n"
+        "  -n Number of iterations per test case (default: "
+         << ITERATION_DEFAULT << ")\n"
+        "  -t Type of data source: sqlite3|memory (default: sqlite3)\n"
+        "  -o Origin name of datasrc_file necessary for \"memory\", "
+        "ignored for others\n"
+        "  datasrc_file: sqlite3 DB file for \"sqlite3\", "
+        "textual master file for \"memory\" datasrc\n"
+        "  query_datafile: queryperf style input data"
          << endl;
     exit (1);
 }
@@ -136,12 +201,20 @@
 int
 main(int argc, char* argv[]) {
     int ch;
-    int iteration = 1;
-    while ((ch = getopt(argc, argv, "n:")) != -1) {
+    int iteration = ITERATION_DEFAULT;
+    const char* opt_datasrc_type = "sqlite3";
+    const char* origin = NULL;
+    while ((ch = getopt(argc, argv, "n:t:o:")) != -1) {
         switch (ch) {
         case 'n':
             iteration = atoi(optarg);
             break;
+        case 't':
+            opt_datasrc_type = optarg;
+            break;
+        case 'o':
+            origin = optarg;
+            break;
         case '?':
         default:
             usage();
@@ -155,6 +228,21 @@
     const char* const datasrc_file = argv[0];
     const char* const query_data_file = argv[1];
 
+    DataSrcType datasrc_type = SQLITE3;
+    if (strcmp(opt_datasrc_type, "sqlite3") == 0) {
+        ;                       // no need to override
+    } else if (strcmp(opt_datasrc_type, "memory") == 0) {
+        datasrc_type = MEMORY;
+    } else {
+        cerr << "Unknown data source type: " << datasrc_type << endl;
+        return (1);
+    }
+
+    if (datasrc_type == MEMORY && origin == NULL) {
+        cerr << "'-o Origin' is missing for memory data source " << endl;
+        return (1);
+    }
+
     BenchQueries queries;
     loadQueryData(query_data_file, queries, RRClass::IN());
     OutputBufferPtr buffer(new OutputBuffer(4096));
@@ -162,32 +250,46 @@
 
     cout << "Parameters:" << endl;
     cout << "  Iterations: " << iteration << endl;
-    cout << "  Data Source: " << datasrc_file << endl;
+    cout << "  Data Source: type=" << opt_datasrc_type << ", file=" <<
+        datasrc_file << endl;
+    if (origin != NULL) {
+        cout << "  Origin: " << origin << endl;
+    }
     cout << "  Query data: file=" << query_data_file << " (" << queries.size()
          << " queries)" << endl << endl;
 
-    cout << "Benchmark enabling Hot Spot Cache with unlimited slots "
-         << endl;
-    BenchMark<QueryBenchMark>(iteration,
-                              QueryBenchMark(0, datasrc_file, queries, message,
-                                             buffer));
-
-    cout << "Benchmark enabling Hot Spot Cache with 10*#queries slots "
-         << endl;
-    BenchMark<QueryBenchMark>(iteration,
-                              QueryBenchMark(10 * queries.size(), datasrc_file,
+    switch (datasrc_type) {
+    case SQLITE3:
+        cout << "Benchmark enabling Hot Spot Cache with unlimited slots "
+             << endl;
+        BenchMark<Sqlite3QueryBenchMark>(
+            iteration, Sqlite3QueryBenchMark(0, datasrc_file, queries,
+                                             message, buffer));
+
+        cout << "Benchmark enabling Hot Spot Cache with 10*#queries slots "
+             << endl;
+        BenchMark<Sqlite3QueryBenchMark>(
+            iteration, Sqlite3QueryBenchMark(10 * queries.size(), datasrc_file,
                                              queries, message, buffer));
 
-    cout << "Benchmark enabling Hot Spot Cache with #queries/2 slots "
-         << endl;
-    BenchMark<QueryBenchMark>(iteration,
-                              QueryBenchMark(queries.size() / 2, datasrc_file,
+        cout << "Benchmark enabling Hot Spot Cache with #queries/2 slots "
+             << endl;
+        BenchMark<Sqlite3QueryBenchMark>(
+            iteration, Sqlite3QueryBenchMark(queries.size() / 2, datasrc_file,
                                              queries, message, buffer));
 
-    cout << "Benchmark disabling Hot Spot Cache" << endl;
-    BenchMark<QueryBenchMark>(iteration,
-                              QueryBenchMark(-1, datasrc_file, queries,
-                                             message, buffer));    
+        cout << "Benchmark disabling Hot Spot Cache" << endl;
+        BenchMark<Sqlite3QueryBenchMark>(
+            iteration, Sqlite3QueryBenchMark(-1, datasrc_file, queries,
+                                             message, buffer));
+        break;
+    case MEMORY:
+        cout << "Benchmark with In Memory Data Source" << endl;
+        BenchMark<MemoryQueryBenchMark>(
+            iteration, MemoryQueryBenchMark(datasrc_file, origin, queries,
+                                            message, buffer));
+        break;
+    }
 
     return (0);
 }

Modified: branches/trac458/src/bin/auth/config.cc
==============================================================================
--- branches/trac458/src/bin/auth/config.cc (original)
+++ branches/trac458/src/bin/auth/config.cc Tue Jan  4 09:09:01 2011
@@ -151,16 +151,21 @@
             isc_throw(AuthConfigError, "Missing zone file for zone: "
                       << origin->str());
         }
-        const result::Result result = memory_datasrc_->addZone(
-            ZonePtr(new MemoryZone(rrclass_, Name(origin->stringValue()))));
+        shared_ptr<MemoryZone> new_zone(new MemoryZone(rrclass_,
+            Name(origin->stringValue())));
+        const result::Result result = memory_datasrc_->addZone(new_zone);
         if (result == result::EXIST) {
             isc_throw(AuthConfigError, "zone "<< origin->str()
                       << " already exists");
         }
 
-        // TODO
-        // then load the zone from 'file', which is currently not implemented.
-        //
+        /*
+         * TODO: Once we have better reloading of configuration (something
+         * else than throwing everything away and loading it again), we will
+         * need the load method to be split into some kind of build and
+         * commit/abort parts.
+         */
+        new_zone->load(file->stringValue());
     }
 }
 

Modified: branches/trac458/src/bin/auth/query.cc
==============================================================================
--- branches/trac458/src/bin/auth/query.cc (original)
+++ branches/trac458/src/bin/auth/query.cc Tue Jan  4 09:09:01 2011
@@ -25,10 +25,69 @@
 
 using namespace isc::dns;
 using namespace isc::datasrc;
+using namespace isc::dns::rdata;
 using namespace std;
 
 namespace isc {
 namespace auth {
+
+void
+Query::getAdditional(const isc::datasrc::Zone& zone,
+                     const isc::dns::RRset& rrset) const
+{
+    if (rrset.getType() == RRType::NS()) {
+        // Need to perform the search in the "GLUE OK" mode.
+        RdataIteratorPtr rdata_iterator = rrset.getRdataIterator();
+        for (; !rdata_iterator->isLast(); rdata_iterator->next()) {
+             const Rdata& rdata(rdata_iterator->getCurrent());
+             const generic::NS& ns = dynamic_cast<const generic::NS&>(rdata);
+             findAddrs(zone, ns.getNSName(), Zone::FIND_GLUE_OK);
+        }
+    }
+}
+
+void
+Query::findAddrs(const isc::datasrc::Zone& zone,
+                 const isc::dns::Name& qname,
+                 const isc::datasrc::Zone::FindOptions options) const
+{
+    // Out of zone name
+    NameComparisonResult result = zone.getOrigin().compare(qname);
+    if ((result.getRelation() != NameComparisonResult::SUPERDOMAIN) &&
+        (result.getRelation() != NameComparisonResult::EQUAL))
+        return;
+
+    // Find A rrset
+    Zone::FindResult a_result = zone.find(qname, RRType::A(), options);
+    if (a_result.code == Zone::SUCCESS) {
+        response_.addRRset(Message::SECTION_ADDITIONAL,
+                     boost::const_pointer_cast<RRset>(a_result.rrset));
+    }
+    // Find AAAA rrset
+    Zone::FindResult aaaa_result = zone.find(qname, RRType::AAAA(), options);
+    if (aaaa_result.code == Zone::SUCCESS) {
+        response_.addRRset(Message::SECTION_ADDITIONAL,
+                     boost::const_pointer_cast<RRset>(aaaa_result.rrset));
+    }
+}
+
+void
+Query::putSOA(const Zone& zone) const {
+    Zone::FindResult soa_result(zone.find(zone.getOrigin(),
+        RRType::SOA()));
+    if (soa_result.code != Zone::SUCCESS) {
+        isc_throw(NoSOA, "There's no SOA record in zone " <<
+            zone.getOrigin().toText());
+    } else {
+        /*
+         * FIXME:
+         * The const-cast is wrong, but the Message interface seems
+         * to insist.
+         */
+        response_.addRRset(Message::SECTION_AUTHORITY,
+            boost::const_pointer_cast<RRset>(soa_result.rrset));
+    }
+}
 
 void
 Query::process() const {
@@ -100,15 +159,21 @@
                 // TODO : fill in authority and addtional sections.
                 break;
             case Zone::DELEGATION:
-                // TODO : add NS to authority section, fill in additional section.
+                response_.setHeaderFlag(Message::HEADERFLAG_AA, false);
+                response_.setRcode(Rcode::NOERROR());
+                response_.addRRset(Message::SECTION_AUTHORITY,
+                            boost::const_pointer_cast<RRset>(db_result.rrset));
+                getAdditional(*result.zone, *db_result.rrset);
                 break;
             case Zone::NXDOMAIN:
+                // Just empty answer with SOA in authority section
                 response_.setRcode(Rcode::NXDOMAIN());
-                // TODO : add SOA to authority section
+                putSOA(*result.zone);
                 break;
             case Zone::NXRRSET:
+                // Just empty answer with SOA in authority section
                 response_.setRcode(Rcode::NOERROR());
-                // TODO : add SOA to authority section
+                putSOA(*result.zone);
                 break;
             case Zone::CNAME:
             case Zone::DNAME:
@@ -117,5 +182,6 @@
         }
     }
 }
+
 }
 }

Modified: branches/trac458/src/bin/auth/query.h
==============================================================================
--- branches/trac458/src/bin/auth/query.h (original)
+++ branches/trac458/src/bin/auth/query.h Tue Jan  4 09:09:01 2011
@@ -14,11 +14,15 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
+#include <exceptions/exceptions.h>
+#include <datasrc/zone.h>
+
 namespace isc {
 namespace dns {
 class Message;
 class Name;
 class RRType;
+class RRset;
 }
 
 namespace datasrc {
@@ -60,6 +64,48 @@
 /// accidentally, and since it's considered a temporary development state,
 /// we keep this name at the moment.
 class Query {
+private:
+
+    /// \short Adds a SOA.
+    ///
+    /// Adds a SOA of the zone into the authority zone of response_.
+    /// Can throw NoSOA.
+    ///
+    void putSOA(const isc::datasrc::Zone& zone) const;
+
+    /// Look up additional data (i.e., address records for the names included
+    /// in NS or MX records).
+    ///
+    /// This method may throw a exception because its underlying methods may
+    /// throw exceptions.
+    ///
+    /// \param zone The Zone wherein the additional data to the query is bo be
+    /// found.
+    /// \param rrset The RRset (i.e., NS or MX rrset) which require additional
+    /// processing.
+    void getAdditional(const isc::datasrc::Zone& zone,
+                       const isc::dns::RRset& rrset) const;
+
+    /// Find address records for a specified name.
+    ///
+    /// Search the specified zone for AAAA/A RRs of each of the NS/MX RDATA
+    /// (domain name), and insert the found ones into the additional section
+    /// if address records are available. By default the search will stop
+    /// once it encounters a zone cut.
+    ///
+    /// Note: we need to perform the search in the "GLUE OK" mode for NS RDATA,
+    /// which means that we should include A/AAAA RRs under a zone cut.
+    /// The glue records must exactly match the name in the NS RDATA, without
+    /// CNAME or wildcard processing.
+    ///
+    /// \param zone The Zone wherein the address records is to be found.
+    /// \param qname The name in rrset RDATA.
+    /// \param options The search options.
+    void findAddrs(const isc::datasrc::Zone& zone,
+                   const isc::dns::Name& qname,
+                   const isc::datasrc::Zone::FindOptions options
+                   = isc::datasrc::Zone::FIND_DEFAULT) const;
+
 public:
     /// Constructor from query parameters.
     ///
@@ -100,9 +146,32 @@
     /// providing compatible behavior may have its own benefit, so this point
     /// should be revisited later.
     ///
-    /// Right now this method never throws an exception, but it may in a
-    /// future version.
+    /// This might throw BadZone or any of its specific subclasses, but that
+    /// shouldn't happen in real-life (as BadZone means wrong data, it should
+    /// have been rejected upon loading).
     void process() const;
+
+    /// \short Bad zone data encountered.
+    ///
+    /// This is thrown when process encounteres misconfigured zone in a way
+    /// it can't continue. This throws, not sets the Rcode, because such
+    /// misconfigured zone should not be present in the data source and
+    /// should have been rejected sooner.
+    struct BadZone : public isc::Exception {
+        BadZone(const char* file, size_t line, const char* what) :
+            Exception(file, line, what)
+        {}
+    };
+
+    /// \short Zone is missing its SOA record.
+    ///
+    /// We tried to add a SOA into the authoritative section, but the zone
+    /// does not contain one.
+    struct NoSOA : public BadZone {
+        NoSOA(const char* file, size_t line, const char* what) :
+            BadZone(file, line, what)
+        {}
+    };
 
 private:
     const isc::datasrc::MemoryDataSrc& memory_datasrc_;

Modified: branches/trac458/src/bin/auth/tests/Makefile.am
==============================================================================
--- branches/trac458/src/bin/auth/tests/Makefile.am (original)
+++ branches/trac458/src/bin/auth/tests/Makefile.am Tue Jan  4 09:09:01 2011
@@ -33,6 +33,7 @@
 run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
 run_unittests_LDADD = $(GTEST_LDADD)
 run_unittests_LDADD += $(SQLITE_LIBS)
+run_unittests_LDADD += $(top_builddir)/src/lib/testutils/libtestutils.la
 run_unittests_LDADD +=  $(top_builddir)/src/lib/datasrc/libdatasrc.la
 run_unittests_LDADD +=  $(top_builddir)/src/lib/dns/libdns++.la
 run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la

Modified: branches/trac458/src/bin/auth/tests/auth_srv_unittest.cc
==============================================================================
--- branches/trac458/src/bin/auth/tests/auth_srv_unittest.cc (original)
+++ branches/trac458/src/bin/auth/tests/auth_srv_unittest.cc Tue Jan  4 09:09:01 2011
@@ -15,16 +15,34 @@
 // $Id$
 
 #include <config.h>
+
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <dns/message.h>
+#include <dns/messagerenderer.h>
+#include <dns/name.h>
+#include <dns/rrclass.h>
+#include <dns/rrtype.h>
+#include <dns/rrttl.h>
+#include <dns/rdataclass.h>
+
 #include <datasrc/memory_datasrc.h>
 #include <auth/auth_srv.h>
-#include <testutils/srv_unittest.h>
 #include <auth/statistics.h>
 
+#include <dns/tests/unittest_util.h>
+#include <testutils/srv_test.h>
+
+using namespace std;
 using namespace isc::cc;
 using namespace isc::dns;
+using namespace isc::dns::rdata;
 using namespace isc::data;
 using namespace isc::xfr;
 using namespace asiolink;
+using namespace isc::testutils;
 using isc::UnitTestUtil;
 
 namespace {
@@ -41,56 +59,160 @@
         server.setXfrinSession(&notify_session);
         server.setStatisticsSession(&statistics_session);
     }
+    virtual void processMessage() {
+        server.processMessage(*io_message, parse_message, response_obuffer,
+                              &dnsserv);
+    }
     MockSession statistics_session;
     MockXfroutClient xfrout;
     AuthSrv server;
     const RRClass rrclass;
+    vector<uint8_t> response_data;
 };
+
+// A helper function that builds a response to version.bind/TXT/CH that
+// should be identical to the response from our builtin (static) data source
+// by default.  The resulting wire-format data will be stored in 'data'.
+void
+createBuiltinVersionResponse(const qid_t qid, vector<uint8_t>& data) {
+    const Name version_name("version.bind");
+    Message message(Message::RENDER);
+
+    UnitTestUtil::createRequestMessage(message, Opcode::QUERY(),
+                                       qid, version_name,
+                                       RRClass::CH(), RRType::TXT());
+    message.setHeaderFlag(Message::HEADERFLAG_QR);
+    message.setHeaderFlag(Message::HEADERFLAG_AA);
+    RRsetPtr rrset_version = RRsetPtr(new RRset(version_name, RRClass::CH(),
+                                                RRType::TXT(), RRTTL(0)));
+    rrset_version->addRdata(generic::TXT(PACKAGE_STRING));
+    message.addRRset(Message::SECTION_ANSWER, rrset_version);
+
+    RRsetPtr rrset_version_ns = RRsetPtr(new RRset(version_name, RRClass::CH(),
+                                                   RRType::NS(), RRTTL(0)));
+    rrset_version_ns->addRdata(generic::NS(version_name));
+    message.addRRset(Message::SECTION_AUTHORITY, rrset_version_ns);
+
+    OutputBuffer obuffer(0);
+    MessageRenderer renderer(obuffer);
+    message.toWire(renderer);
+
+    data.clear();
+    data.assign(static_cast<const uint8_t*>(renderer.getData()),
+                static_cast<const uint8_t*>(renderer.getData()) +
+                renderer.getLength());
+}
+
+// In the following tests we confirm the response data is rendered in
+// wire format in the expected way.
+
+// The most primitive check: checking the result of the processMessage()
+// method
+TEST_F(AuthSrvTest, builtInQuery) {
+    UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
+                                       default_qid, Name("version.bind"),
+                                       RRClass::CH(), RRType::TXT());
+    createRequestPacket(request_message, IPPROTO_UDP);
+    server.processMessage(*io_message, parse_message, response_obuffer,
+                          &dnsserv);
+    createBuiltinVersionResponse(default_qid, response_data);
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
+                        response_obuffer->getData(),
+                        response_obuffer->getLength(),
+                        &response_data[0], response_data.size());
+}
+
+// Same test emulating the UDPServer class behavior (defined in libasiolink).
+// This is not a good test in that it assumes internal implementation details
+// of UDPServer, but we've encountered a regression due to the introduction
+// of that class, so we add a test for that case to prevent such a regression
+// in future.
+// Besides, the generalization of UDPServer is probably too much for the
+// authoritative only server in terms of performance, and it's quite likely
+// we need to drop it for the authoritative server implementation.
+// At that point we can drop this test, too.
+TEST_F(AuthSrvTest, builtInQueryViaDNSServer) {
+    UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
+                                       default_qid, Name("version.bind"),
+                                       RRClass::CH(), RRType::TXT());
+    createRequestPacket(request_message, IPPROTO_UDP);
+
+    (*server.getDNSLookupProvider())(*io_message, parse_message,
+                                     response_obuffer, &dnsserv);
+    (*server.getDNSAnswerProvider())(*io_message, parse_message,
+                                     response_obuffer);
+
+    createBuiltinVersionResponse(default_qid, response_data);
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
+                        response_obuffer->getData(),
+                        response_obuffer->getLength(),
+                        &response_data[0], response_data.size());
+}
+
+// Same type of test as builtInQueryViaDNSServer but for an error response.
+TEST_F(AuthSrvTest, iqueryViaDNSServer) {
+    createDataFromFile("iquery_fromWire.wire");
+    (*server.getDNSLookupProvider())(*io_message, parse_message,
+                                     response_obuffer, &dnsserv);
+    (*server.getDNSAnswerProvider())(*io_message, parse_message,
+                                     response_obuffer);
+
+    UnitTestUtil::readWireData("iquery_response_fromWire.wire",
+                               response_data);
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
+                        response_obuffer->getData(),
+                        response_obuffer->getLength(),
+                        &response_data[0], response_data.size());
+}
 
 // Unsupported requests.  Should result in NOTIMP.
 TEST_F(AuthSrvTest, unsupportedRequest) {
-    UNSUPPORTED_REQUEST_TEST;
+    unsupportedRequest();
 }
 
 // Simple API check
 TEST_F(AuthSrvTest, verbose) {
-    VERBOSE_TEST;
+    EXPECT_FALSE(server.getVerbose());
+    server.setVerbose(true);
+    EXPECT_TRUE(server.getVerbose());
+    server.setVerbose(false);
+    EXPECT_FALSE(server.getVerbose());
 }
 
 // Multiple questions.  Should result in FORMERR.
 TEST_F(AuthSrvTest, multiQuestion) {
-    MULTI_QUESTION_TEST;
+    multiQuestion();
 }
 
 // Incoming data doesn't even contain the complete header.  Must be silently
 // dropped.
 TEST_F(AuthSrvTest, shortMessage) {
-    SHORT_MESSAGE_TEST;
+    shortMessage();
 }
 
 // Response messages.  Must be silently dropped, whether it's a valid response
 // or malformed or could otherwise cause a protocol error.
 TEST_F(AuthSrvTest, response) {
-    RESPONSE_TEST;
+    response();
 }
 
 // Query with a broken question
 TEST_F(AuthSrvTest, shortQuestion) {
-    SHORT_QUESTION_TEST;
+    shortQuestion();
 }
 
 // Query with a broken answer section
 TEST_F(AuthSrvTest, shortAnswer) {
-    SHORT_ANSWER_TEST;
+    shortAnswer();
 }
 
 // Query with unsupported version of EDNS.
 TEST_F(AuthSrvTest, ednsBadVers) {
-    EDNS_BADVERS_TEST;
+    ednsBadVers();
 }
 
 TEST_F(AuthSrvTest, AXFROverUDP) {
-    AXFR_OVER_UDP_TEST;
+    axfrOverUDP();
 }
 
 TEST_F(AuthSrvTest, AXFRSuccess) {

Modified: branches/trac458/src/bin/auth/tests/config_unittest.cc
==============================================================================
--- branches/trac458/src/bin/auth/tests/config_unittest.cc (original)
+++ branches/trac458/src/bin/auth/tests/config_unittest.cc Tue Jan  4 09:09:01 2011
@@ -17,6 +17,7 @@
 #include <exceptions/exceptions.h>
 
 #include <dns/rrclass.h>
+#include <dns/masterload.h>
 
 #include <cc/data.h>
 
@@ -143,33 +144,42 @@
 }
 
 TEST_F(MemoryDatasrcConfigTest, addOneZone) {
-    parser->build(Element::fromJSON(
-                      "[{\"type\": \"memory\","
-                      "  \"zones\": [{\"origin\": \"example.com\","
-                      "               \"file\": \"example.zone\"}]}]"));
-    parser->commit();
-    EXPECT_EQ(1, server.getMemoryDataSrc(rrclass)->getZoneCount());
+    EXPECT_NO_THROW(parser->build(Element::fromJSON(
+                      "[{\"type\": \"memory\","
+                      "  \"zones\": [{\"origin\": \"example.com\","
+                      "               \"file\": \"" TEST_DATA_DIR
+                      "/example.zone\"}]}]")));
+    EXPECT_NO_THROW(parser->commit());
+    EXPECT_EQ(1, server.getMemoryDataSrc(rrclass)->getZoneCount());
+    // Check it actually loaded something
+    EXPECT_EQ(Zone::SUCCESS, server.getMemoryDataSrc(rrclass)->findZone(
+        Name("ns.example.com.")).zone->find(Name("ns.example.com."),
+        RRType::A()).code);
 }
 
 TEST_F(MemoryDatasrcConfigTest, addMultiZones) {
-    parser->build(Element::fromJSON(
-                      "[{\"type\": \"memory\","
-                      "  \"zones\": [{\"origin\": \"example.com\","
-                      "               \"file\": \"example.zone\"},"
+    EXPECT_NO_THROW(parser->build(Element::fromJSON(
+                      "[{\"type\": \"memory\","
+                      "  \"zones\": [{\"origin\": \"example.com\","
+                      "               \"file\": \"" TEST_DATA_DIR
+                      "/example.zone\"},"
                       "              {\"origin\": \"example.org\","
-                      "               \"file\": \"example.org.zone\"},"
+                      "               \"file\": \"" TEST_DATA_DIR
+                      "/example.org.zone\"},"
                       "              {\"origin\": \"example.net\","
-                      "               \"file\": \"example.net.zone\"}]}]"));
-    parser->commit();
+                      "               \"file\": \"" TEST_DATA_DIR
+                      "/example.net.zone\"}]}]")));
+    EXPECT_NO_THROW(parser->commit());
     EXPECT_EQ(3, server.getMemoryDataSrc(rrclass)->getZoneCount());
 }
 
 TEST_F(MemoryDatasrcConfigTest, replace) {
-    parser->build(Element::fromJSON(
-                      "[{\"type\": \"memory\","
-                      "  \"zones\": [{\"origin\": \"example.com\","
-                      "               \"file\": \"example.zone\"}]}]"));
-    parser->commit();
+    EXPECT_NO_THROW(parser->build(Element::fromJSON(
+                      "[{\"type\": \"memory\","
+                      "  \"zones\": [{\"origin\": \"example.com\","
+                      "               \"file\": \"" TEST_DATA_DIR
+                      "/example.zone\"}]}]")));
+    EXPECT_NO_THROW(parser->commit());
     EXPECT_EQ(1, server.getMemoryDataSrc(rrclass)->getZoneCount());
     EXPECT_EQ(isc::datasrc::result::SUCCESS,
               server.getMemoryDataSrc(rrclass)->findZone(
@@ -179,31 +189,69 @@
     // should replace the old one.
     delete parser;
     parser = createAuthConfigParser(server, "datasources"); 
-    parser->build(Element::fromJSON(
+    EXPECT_NO_THROW(parser->build(Element::fromJSON(
                       "[{\"type\": \"memory\","
                       "  \"zones\": [{\"origin\": \"example.org\","
-                      "               \"file\": \"example.org.zone\"},"
+                      "               \"file\": \"" TEST_DATA_DIR
+                      "/example.org.zone\"},"
                       "              {\"origin\": \"example.net\","
-                      "               \"file\": \"example.net.zone\"}]}]"));
-    parser->commit();
+                      "               \"file\": \"" TEST_DATA_DIR
+                      "/example.net.zone\"}]}]")));
+    EXPECT_NO_THROW(parser->commit());
     EXPECT_EQ(2, server.getMemoryDataSrc(rrclass)->getZoneCount());
     EXPECT_EQ(isc::datasrc::result::NOTFOUND,
               server.getMemoryDataSrc(rrclass)->findZone(
                   Name("example.com")).code);
 }
 
+TEST_F(MemoryDatasrcConfigTest, exception) {
+    // Load a zone
+    EXPECT_NO_THROW(parser->build(Element::fromJSON(
+                      "[{\"type\": \"memory\","
+                      "  \"zones\": [{\"origin\": \"example.com\","
+                      "               \"file\": \"" TEST_DATA_DIR
+                      "/example.zone\"}]}]")));
+    EXPECT_NO_THROW(parser->commit());
+    EXPECT_EQ(1, server.getMemoryDataSrc(rrclass)->getZoneCount());
+    EXPECT_EQ(isc::datasrc::result::SUCCESS,
+              server.getMemoryDataSrc(rrclass)->findZone(
+                  Name("example.com")).code);
+
+    // create a new parser, and try to load something. It will throw,
+    // the given master file should not exist
+    delete parser;
+    parser = createAuthConfigParser(server, "datasources");
+    EXPECT_THROW(parser->build(Element::fromJSON(
+                      "[{\"type\": \"memory\","
+                      "  \"zones\": [{\"origin\": \"example.org\","
+                      "               \"file\": \"" TEST_DATA_DIR
+                      "/example.org.zone\"},"
+                      "              {\"origin\": \"example.net\","
+                      "               \"file\": \"" TEST_DATA_DIR
+                      "/nonexistent.zone\"}]}]")), isc::dns::MasterLoadError);
+    // As that one throwed exception, it is not expected from us to
+    // commit it
+
+    // The original should be untouched
+    EXPECT_EQ(1, server.getMemoryDataSrc(rrclass)->getZoneCount());
+    EXPECT_EQ(isc::datasrc::result::SUCCESS,
+              server.getMemoryDataSrc(rrclass)->findZone(
+                  Name("example.com")).code);
+}
+
 TEST_F(MemoryDatasrcConfigTest, remove) {
-    parser->build(Element::fromJSON(
-                      "[{\"type\": \"memory\","
-                      "  \"zones\": [{\"origin\": \"example.com\","
-                      "               \"file\": \"example.zone\"}]}]"));
-    parser->commit();
+    EXPECT_NO_THROW(parser->build(Element::fromJSON(
+                      "[{\"type\": \"memory\","
+                      "  \"zones\": [{\"origin\": \"example.com\","
+                      "               \"file\": \"" TEST_DATA_DIR
+                      "/example.zone\"}]}]")));
+    EXPECT_NO_THROW(parser->commit());
     EXPECT_EQ(1, server.getMemoryDataSrc(rrclass)->getZoneCount());
 
     delete parser;
     parser = createAuthConfigParser(server, "datasources"); 
-    parser->build(Element::fromJSON("[]"));
-    parser->commit();
+    EXPECT_NO_THROW(parser->build(Element::fromJSON("[]")));
+    EXPECT_NO_THROW(parser->commit());
     EXPECT_EQ(AuthSrv::MemoryDataSrcPtr(), server.getMemoryDataSrc(rrclass));
 }
 
@@ -212,9 +260,11 @@
                      Element::fromJSON(
                          "[{\"type\": \"memory\","
                          "  \"zones\": [{\"origin\": \"example.com\","
-                         "               \"file\": \"example.zone\"},"
+                         "               \"file\": \"" TEST_DATA_DIR
+                         "/example.zone\"},"
                          "              {\"origin\": \"example.com\","
-                         "               \"file\": \"example.com.zone\"}]}]")),
+                         "               \"file\": \"" TEST_DATA_DIR
+                         "/example.com.zone\"}]}]")),
                  AuthConfigError);
 }
 

Modified: branches/trac458/src/bin/auth/tests/query_unittest.cc
==============================================================================
--- branches/trac458/src/bin/auth/tests/query_unittest.cc (original)
+++ branches/trac458/src/bin/auth/tests/query_unittest.cc Tue Jan  4 09:09:01 2011
@@ -29,24 +29,57 @@
 using namespace isc::datasrc;
 using namespace isc::auth;
 
+namespace {
+
 RRsetPtr a_rrset = RRsetPtr(new RRset(Name("www.example.com"),
                                       RRClass::IN(), RRType::A(),
                                       RRTTL(3600)));
-namespace {
+RRsetPtr soa_rrset = RRsetPtr(new RRset(Name("example.com"),
+                                        RRClass::IN(), RRType::SOA(),
+                                        RRTTL(3600)));
+RRsetPtr ns_rrset(RRsetPtr(new RRset(Name("ns.example.com"),
+                                     RRClass::IN(), RRType::NS(),
+                                     RRTTL(3600))));
+RRsetPtr glue_a_rrset(RRsetPtr(new RRset(Name("glue.ns.example.com"),
+                                         RRClass::IN(), RRType::A(),
+                                         RRTTL(3600))));
+RRsetPtr glue_aaaa_rrset(RRsetPtr(new RRset(Name("glue.ns.example.com"),
+                                            RRClass::IN(), RRType::AAAA(),
+                                            RRTTL(3600))));
+RRsetPtr noglue_a_rrset(RRsetPtr(new RRset(Name("noglue.example.com"),
+                                         RRClass::IN(), RRType::A(),
+                                         RRTTL(3600))));
 // This is a mock Zone class for testing.
 // It is a derived class of Zone, and simply hardcode the results of find()
 // return SUCCESS for "www.example.com",
 // return NXDOMAIN for "nxdomain.example.com",
 // return NXRRSET for "nxrrset.example.com",
 // return CNAME for "cname.example.com",
-// else return DNAME
+// otherwise return DNAME
 class MockZone : public Zone {
 public:
-    MockZone() :
+    MockZone(bool has_SOA = true) :
         origin_(Name("example.com")),
+        has_SOA_(has_SOA),
+        delegation_rrset(RRsetPtr(new RRset(Name("delegation.example.com"),
+                                            RRClass::IN(), RRType::NS(),
+                                            RRTTL(3600)))),
+        cname_rrset(RRsetPtr(new RRset(Name("cname.example.com"),
+                                       RRClass::IN(), RRType::CNAME(),
+                                       RRTTL(3600)))),
         mx_rrset_(new RRset(Name("mx.example.com"), RRClass::IN(),
             RRType::MX(), RRTTL(3600)))
     {
+        delegation_rrset->addRdata(rdata::generic::NS(
+                          Name("glue.ns.example.com")));
+        delegation_rrset->addRdata(rdata::generic::NS(
+                          Name("noglue.example.com")));
+        delegation_rrset->addRdata(rdata::generic::NS(
+                          Name("cname.example.com")));
+        delegation_rrset->addRdata(rdata::generic::NS(
+                          Name("example.org")));
+        cname_rrset->addRdata(rdata::generic::CNAME(
+                          Name("www.example.com")));
         mx_rrset_->addRdata(isc::dns::rdata::generic::MX(10,
             Name("www.example.com")));
         mx_rrset_->addRdata(isc::dns::rdata::generic::MX(20,
@@ -56,10 +89,14 @@
     virtual const isc::dns::RRClass& getClass() const;
 
     FindResult find(const isc::dns::Name& name,
-            const isc::dns::RRType& type) const;
+                    const isc::dns::RRType& type,
+                    const FindOptions options = FIND_DEFAULT) const;
 
 private:
     Name origin_;
+    bool has_SOA_;
+    RRsetPtr delegation_rrset;
+    RRsetPtr cname_rrset;
     RRsetPtr mx_rrset_;
 };
 
@@ -74,22 +111,38 @@
 }
 
 Zone::FindResult
-MockZone::find(const Name& name, const RRType&) const {
+MockZone::find(const Name& name, const RRType& type,
+               const FindOptions options) const
+{
     // hardcode the find results
     if (name == Name("www.example.com")) {
-        return FindResult(SUCCESS, a_rrset);
+        return (FindResult(SUCCESS, a_rrset));
+    } else if (name == Name("glue.ns.example.com") && type == RRType::A() &&
+        options == FIND_GLUE_OK) {
+        return (FindResult(SUCCESS, glue_a_rrset));
+    } else if (name == Name("noglue.example.com") && type == RRType::A()) {
+        return (FindResult(SUCCESS, noglue_a_rrset));
+    } else if (name == Name("glue.ns.example.com") && type == RRType::AAAA() &&
+        options == FIND_GLUE_OK) {
+        return (FindResult(SUCCESS, glue_aaaa_rrset));
+    } else if (name == Name("example.com") && type == RRType::SOA() &&
+        has_SOA_)
+    {
+        return (FindResult(SUCCESS, soa_rrset));
     } else if (name == Name("delegation.example.com")) {
-        return FindResult(DELEGATION, RRsetPtr());
+        return (FindResult(DELEGATION, delegation_rrset));
+    } else if (name == Name("ns.example.com")) {
+        return (FindResult(DELEGATION, ns_rrset));
     } else if (name == Name("nxdomain.example.com")) {
-        return FindResult(NXDOMAIN, RRsetPtr());
+        return (FindResult(NXDOMAIN, RRsetPtr()));
     } else if (name == Name("nxrrset.example.com")) {
-        return FindResult(NXRRSET, RRsetPtr());
-    } else if (name == Name("cname.example.com")) {
-        return FindResult(CNAME, RRsetPtr());
+        return (FindResult(NXRRSET, RRsetPtr()));
+    } else if ((name == Name("cname.example.com"))) {
+        return (FindResult(CNAME, cname_rrset));
     } else if (name == Name("mx.example.com")) {
         return (FindResult(SUCCESS, mx_rrset_));
     } else {
-        return FindResult(DNAME, RRsetPtr());
+        return (FindResult(DNAME, RRsetPtr()));
     }
 }
 
@@ -118,25 +171,82 @@
 }
 
 TEST_F(QueryTest, matchZone) {
-    // match qname, normal query
+    // add a matching zone.
     memory_datasrc.addZone(ZonePtr(new MockZone()));
     query.process();
+    EXPECT_TRUE(response.getHeaderFlag(Message::HEADERFLAG_AA));
     EXPECT_EQ(Rcode::NOERROR(), response.getRcode());
     EXPECT_TRUE(response.hasRRset(Message::SECTION_ANSWER,
                                   Name("www.example.com"), RRClass::IN(),
                                   RRType::A()));
+
+    // Delegation
+    const Name delegation_name(Name("delegation.example.com"));
+    Query delegation_query(memory_datasrc, delegation_name, qtype, response);
+    delegation_query.process();
+    EXPECT_FALSE(response.getHeaderFlag(Message::HEADERFLAG_AA));
+    EXPECT_EQ(Rcode::NOERROR(), response.getRcode());
+    EXPECT_TRUE(response.hasRRset(Message::SECTION_AUTHORITY,
+                                  Name("delegation.example.com"),
+                                  RRClass::IN(), RRType::NS()));
+    // glue address records
+    EXPECT_TRUE(response.hasRRset(Message::SECTION_ADDITIONAL,
+                                  Name("glue.ns.example.com"),
+                                  RRClass::IN(), RRType::A()));
+    EXPECT_TRUE(response.hasRRset(Message::SECTION_ADDITIONAL,
+                                  Name("glue.ns.example.com"),
+                                  RRClass::IN(), RRType::AAAA()));
+    // noglue address records
+    EXPECT_TRUE(response.hasRRset(Message::SECTION_ADDITIONAL,
+                                  Name("noglue.example.com"),
+                                  RRClass::IN(), RRType::A()));
+    // NS name has a CNAME
+    EXPECT_FALSE(response.hasRRset(Message::SECTION_ADDITIONAL,
+                                  Name("www.example.com"),
+                                  RRClass::IN(), RRType::A()));
+    // NS name is out of zone
+    EXPECT_FALSE(response.hasRRset(Message::SECTION_ADDITIONAL,
+                                  Name("example.org"),
+                                  RRClass::IN(), RRType::A()));
 
     // NXDOMAIN
     const Name nxdomain_name(Name("nxdomain.example.com"));
     Query nxdomain_query(memory_datasrc, nxdomain_name, qtype, response);
     nxdomain_query.process();
     EXPECT_EQ(Rcode::NXDOMAIN(), response.getRcode());
+    EXPECT_EQ(0, response.getRRCount(Message::SECTION_ANSWER));
+    EXPECT_EQ(0, response.getRRCount(Message::SECTION_ADDITIONAL));
+    EXPECT_TRUE(response.hasRRset(Message::SECTION_AUTHORITY,
+        Name("example.com"), RRClass::IN(), RRType::SOA()));
 
     // NXRRSET
     const Name nxrrset_name(Name("nxrrset.example.com"));
     Query nxrrset_query(memory_datasrc, nxrrset_name, qtype, response);
     nxrrset_query.process();
     EXPECT_EQ(Rcode::NOERROR(), response.getRcode());
+    EXPECT_EQ(0, response.getRRCount(Message::SECTION_ANSWER));
+    EXPECT_EQ(0, response.getRRCount(Message::SECTION_ADDITIONAL));
+    EXPECT_TRUE(response.hasRRset(Message::SECTION_AUTHORITY,
+        Name("example.com"), RRClass::IN(), RRType::SOA()));
+}
+
+/*
+ * This tests that when there's no SOA and we need a negative answer. It should
+ * throw in that case.
+ */
+TEST_F(QueryTest, noSOA) {
+    memory_datasrc.addZone(ZonePtr(new MockZone(false)));
+
+    // The NX Domain
+    const Name nxdomain_name(Name("nxdomain.example.com"));
+    Query nxdomain_query(memory_datasrc, nxdomain_name, qtype, response);
+    EXPECT_THROW(nxdomain_query.process(), Query::NoSOA);
+    // Of course, we don't look into the response, as it throwed
+
+    // NXRRSET
+    const Name nxrrset_name(Name("nxrrset.example.com"));
+    Query nxrrset_query(memory_datasrc, nxrrset_name, qtype, response);
+    EXPECT_THROW(nxrrset_query.process(), Query::NoSOA);
 }
 
 TEST_F(QueryTest, noMatchZone) {

Modified: branches/trac458/src/bin/auth/tests/testdata/queryBadEDNS_fromWire.spec
==============================================================================
--- branches/trac458/src/bin/auth/tests/testdata/queryBadEDNS_fromWire.spec (original)
+++ branches/trac458/src/bin/auth/tests/testdata/queryBadEDNS_fromWire.spec Tue Jan  4 09:09:01 2011
@@ -1,5 +1,5 @@
 #
-# A QUERY message with unsupported version of EDNS..
+# A QUERY message with unsupported version of EDNS.
 #
 
 [header]

Modified: branches/trac458/src/bin/recurse/tests/Makefile.am
==============================================================================
--- branches/trac458/src/bin/recurse/tests/Makefile.am (original)
+++ branches/trac458/src/bin/recurse/tests/Makefile.am Tue Jan  4 09:09:01 2011
@@ -27,6 +27,7 @@
 run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
 run_unittests_LDADD = $(GTEST_LDADD)
 run_unittests_LDADD += $(SQLITE_LIBS)
+run_unittests_LDADD += $(top_builddir)/src/lib/testutils/libtestutils.la
 run_unittests_LDADD +=  $(top_builddir)/src/lib/datasrc/libdatasrc.la
 run_unittests_LDADD +=  $(top_builddir)/src/lib/dns/libdns++.la
 run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la

Modified: branches/trac458/src/bin/recurse/tests/recursor_config_unittest.cc
==============================================================================
--- branches/trac458/src/bin/recurse/tests/recursor_config_unittest.cc (original)
+++ branches/trac458/src/bin/recurse/tests/recursor_config_unittest.cc Tue Jan  4 09:09:01 2011
@@ -12,10 +12,24 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include <cc/data.h>
+
+#include <asiolink/asiolink.h>
 
 #include <recurse/recursor.h>
-#include <testutils/srv_unittest.h>
+
+#include <dns/tests/unittest_util.h>
+#include <testutils/srv_test.h>
+
+using namespace std;
+using namespace isc::data;
+using namespace isc::testutils;
+using namespace asiolink;
+using isc::UnitTestUtil;
 
 namespace {
 class RecursorConfig : public ::testing::Test {

Modified: branches/trac458/src/bin/recurse/tests/recursor_unittest.cc
==============================================================================
--- branches/trac458/src/bin/recurse/tests/recursor_unittest.cc (original)
+++ branches/trac458/src/bin/recurse/tests/recursor_unittest.cc Tue Jan  4 09:09:01 2011
@@ -12,10 +12,15 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
+#include <dns/name.h>
 
 #include <recurse/recursor.h>
-#include <testutils/srv_unittest.h>
+#include <dns/tests/unittest_util.h>
+#include <testutils/srv_test.h>
+
+using namespace isc::dns;
+using namespace isc::testutils;
+using isc::UnitTestUtil;
 
 namespace {
 const char* const TEST_PORT = "53535";
@@ -23,48 +28,52 @@
 class RecursorTest : public SrvTestBase{
 protected:
     RecursorTest() : server(){}
+    virtual void processMessage() {
+        server.processMessage(*io_message, parse_message, response_obuffer,
+                              &dnsserv);
+    }
     Recursor server;
 };
 
 // Unsupported requests.  Should result in NOTIMP.
 TEST_F(RecursorTest, unsupportedRequest) {
-    UNSUPPORTED_REQUEST_TEST;
+    unsupportedRequest();
 }
 
 // Multiple questions.  Should result in FORMERR.
 TEST_F(RecursorTest, multiQuestion) {
-    MULTI_QUESTION_TEST; 
+    multiQuestion();
 }
 
 // Incoming data doesn't even contain the complete header.  Must be silently
 // dropped.
 TEST_F(RecursorTest, shortMessage) {
-    SHORT_MESSAGE_TEST;
+    shortMessage();
 }
 
 // Response messages.  Must be silently dropped, whether it's a valid response
 // or malformed or could otherwise cause a protocol error.
 TEST_F(RecursorTest, response) {
-    RESPONSE_TEST;
+    response();
 }
 
 // Query with a broken question
 TEST_F(RecursorTest, shortQuestion) {
-    SHORT_QUESTION_TEST;
+    shortQuestion();
 }
 
 // Query with a broken answer section
 TEST_F(RecursorTest, shortAnswer) {
-    SHORT_ANSWER_TEST;
+    shortAnswer();
 }
 
 // Query with unsupported version of EDNS.
 TEST_F(RecursorTest, ednsBadVers) {
-    EDNS_BADVERS_TEST;
+    ednsBadVers();
 }
 
 TEST_F(RecursorTest, AXFROverUDP) {
-    AXFR_OVER_UDP_TEST;
+    axfrOverUDP();
 }
 
 TEST_F(RecursorTest, AXFRFail) {

Modified: branches/trac458/src/lib/asiolink/Makefile.am
==============================================================================
--- branches/trac458/src/lib/asiolink/Makefile.am (original)
+++ branches/trac458/src/lib/asiolink/Makefile.am Tue Jan  4 09:09:01 2011
@@ -1,4 +1,4 @@
-SUBDIRS = . tests
+SUBDIRS = . tests internal
 
 AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
 AM_CPPFLAGS += $(BOOST_INCLUDES)

Modified: branches/trac458/src/lib/asiolink/asiolink.h
==============================================================================
--- branches/trac458/src/lib/asiolink/asiolink.h (original)
+++ branches/trac458/src/lib/asiolink/asiolink.h Tue Jan  4 09:09:01 2011
@@ -235,7 +235,7 @@
     /// that share the same \c io_service with the authoritative server.
     /// It will eventually be removed once the wrapper interface is
     /// generalized.
-    asio::io_service& get_io_service() { return io_service_.get_io_service(); };
+    asio::io_service& get_io_service() { return io_service_.get_io_service(); }
 private:
     DNSServiceImpl* impl_;
     IOService& io_service_;

Modified: branches/trac458/src/lib/asiolink/tests/Makefile.am
==============================================================================
--- branches/trac458/src/lib/asiolink/tests/Makefile.am (original)
+++ branches/trac458/src/lib/asiolink/tests/Makefile.am Tue Jan  4 09:09:01 2011
@@ -18,7 +18,6 @@
 run_unittests_SOURCES = $(top_srcdir)/src/lib/dns/tests/unittest_util.h
 run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc
 run_unittests_SOURCES += asiolink_unittest.cc
-run_unittests_SOURCES += udpdns_unittest.cc
 run_unittests_SOURCES += run_unittests.cc
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)

Modified: branches/trac458/src/lib/asiolink/tests/asiolink_unittest.cc
==============================================================================
--- branches/trac458/src/lib/asiolink/tests/asiolink_unittest.cc (original)
+++ branches/trac458/src/lib/asiolink/tests/asiolink_unittest.cc Tue Jan  4 09:09:01 2011
@@ -17,6 +17,9 @@
 
 #include <config.h>
 
+#include <sys/socket.h>
+#include <sys/time.h>
+
 #include <string.h>
 
 #include <boost/lexical_cast.hpp>
@@ -32,19 +35,21 @@
 #include <dns/buffer.h>
 #include <dns/message.h>
 
+// IMPORTANT: We shouldn't directly use ASIO definitions in this test.
+// In particular, we must not include asio.hpp in this file.
+// The asiolink module is primarily intended to be a wrapper that hide the
+// details of the underlying implementations.  We need to test the wrapper
+// level behaviors.  In addition, some compilers reject to compile this file
+// if we include asio.hpp unless we specify a special compiler option.
+// If we need to test something at the level of underlying ASIO and need
+// their definition, that test should go to asiolink/internal/tests.
 #include <asiolink/asiolink.h>
 #include <asiolink/iosocket.h>
-#include <asiolink/internal/tcpdns.h>
-#include <asiolink/internal/udpdns.h>
-
-#include <asio.hpp>
 
 using isc::UnitTestUtil;
 using namespace std;
 using namespace asiolink;
 using namespace isc::dns;
-using namespace asio;
-using asio::ip::udp;
 
 namespace {
 const char* const TEST_SERVER_PORT = "53535";
@@ -330,10 +335,30 @@
         // ... and this one will block until the send has completed
         io_service_->run_one();
 
-        // Now we attempt to recv() whatever was sent
-        const int ret = recv(sock_, buffer, size, MSG_DONTWAIT);
+        // Now we attempt to recv() whatever was sent.
+        // XXX: there's no guarantee the receiving socket can immediately get
+        // the packet.  Normally we can perform blocking recv to wait for it,
+        // but in theory it's even possible that the packet is lost.
+        // In order to prevent the test from hanging in such a worst case
+        // we add an ad hoc timeout.
+        const struct timeval timeo = { 10, 0 };
+        int recv_options = 0;
+        if (setsockopt(sock_, SOL_SOCKET, SO_RCVTIMEO, &timeo,
+                       sizeof(timeo))) {
+            if (errno == ENOPROTOOPT) {
+                // Workaround for Solaris: it doesn't accept SO_RCVTIMEO
+                // with the error of ENOPROTOOPT.  Since this is a workaround
+                // for rare error cases anyway, we simply switch to the
+                // "don't wait" mode.  If we still find an error in recv()
+                // can happen often we'll consider a more complete solution.
+                recv_options = MSG_DONTWAIT;
+            } else {
+                isc_throw(IOError, "set RCVTIMEO failed: " << strerror(errno));
+            }
+        }
+        const int ret = recv(sock_, buffer, size, recv_options);
         if (ret < 0) {
-            isc_throw(IOError, "recvfrom failed");
+            isc_throw(IOError, "recvfrom failed: " << strerror(errno));
         }
         
         // Pass the message size back via the size parameter
@@ -411,8 +436,7 @@
     // has completed.
     class MockServer : public DNSServer {
     public:
-        explicit MockServer(asio::io_service& io_service,
-                            const asio::ip::address& addr, const uint16_t port,
+        explicit MockServer(IOService& io_service,
                             SimpleCallback* checkin = NULL,
                             DNSLookup* lookup = NULL,
                             DNSAnswer* answer = NULL) :
@@ -426,9 +450,7 @@
                         size_t length = 0)
         {}
 
-        void resume(const bool done) {
-            done_ = done;
-            io_.post(*this);
+        void resume(const bool) { // in our test this shouldn't be called
         }
 
         DNSServer* clone() {
@@ -443,7 +465,7 @@
         }
 
     protected:
-        asio::io_service& io_;
+        IOService& io_;
         bool done_;
 
     private:
@@ -462,8 +484,8 @@
     // This version of mock server just stops the io_service when it is resumed
     class MockServerStop : public MockServer {
         public:
-            explicit MockServerStop(asio::io_service& io_service, bool* done) :
-                MockServer(io_service, asio::ip::address(), 0),
+            explicit MockServerStop(IOService& io_service, bool* done) :
+                MockServer(io_service),
                 done_(done)
             {}
 
@@ -511,7 +533,6 @@
     string callback_address_;
     vector<uint8_t> callback_data_;
     int sock_;
-private:
     struct addrinfo* res_;
 };
 
@@ -640,14 +661,12 @@
 // full code coverage including error cases.
 TEST_F(ASIOLinkTest, recursiveSend) {
     setDNSService(true, false);
-    asio::io_service& io = io_service_->get_io_service();
 
     // Note: We use the test prot plus one to ensure we aren't binding
     // to the same port as the actual server
     uint16_t port = boost::lexical_cast<uint16_t>(TEST_CLIENT_PORT);
-    asio::ip::address addr = asio::ip::address::from_string(TEST_IPV4_ADDR);
-
-    MockServer server(io, addr, port, NULL, NULL, NULL);
+
+    MockServer server(*io_service_);
     RecursiveQuery rq(*dns_service_, singleAddress(TEST_IPV4_ADDR, port));
 
     Question q(Name("example.com"), RRClass::IN(), RRType::TXT());
@@ -656,7 +675,7 @@
 
     char data[4096];
     size_t size = sizeof(data);
-    EXPECT_NO_THROW(recvUDP(AF_INET, data, size));
+    ASSERT_NO_THROW(recvUDP(AF_INET, data, size));
 
     Message m(Message::PARSE);
     InputBuffer ibuf(data, size);
@@ -672,34 +691,27 @@
     EXPECT_EQ(q.getClass(), q2->getClass());
 }
 
-void
-receive_and_inc(udp::socket* socket, int* num) {
-    (*num) ++;
-    static char inbuff[512];
-    socket->async_receive(asio::buffer(inbuff, 512),
-        boost::bind(receive_and_inc, socket, num));
-}
-
 // Test it tries the correct amount of times before giving up
 TEST_F(ASIOLinkTest, recursiveTimeout) {
     // Prepare the service (we do not use the common setup, we do not answer
     setDNSService();
-    asio::io_service& service = io_service_->get_io_service();
 
     // Prepare the socket
-    uint16_t port = boost::lexical_cast<uint16_t>(TEST_CLIENT_PORT);
-    udp::socket socket(service, udp::v4());
-    socket.set_option(socket_base::reuse_address(true));
-    socket.bind(udp::endpoint(ip::address::from_string(TEST_IPV4_ADDR), port));
-    // And count the answers
-    int num = -1; // One is counted before the receipt of the first one
-    receive_and_inc(&socket, &num);
+    res_ = resolveAddress(AF_INET, IPPROTO_UDP, true);
+    sock_ = socket(res_->ai_family, res_->ai_socktype, res_->ai_protocol);
+    if (sock_ < 0) {
+        isc_throw(IOError, "failed to open test socket");
+    }
+    if (bind(sock_, res_->ai_addr, res_->ai_addrlen) < 0) {
+        isc_throw(IOError, "failed to bind test socket");
+    }
 
     // Prepare the server
     bool done(true);
-    MockServerStop server(service, &done);
+    MockServerStop server(*io_service_, &done);
 
     // Do the answer
+    const uint16_t port = boost::lexical_cast<uint16_t>(TEST_CLIENT_PORT);
     RecursiveQuery query(*dns_service_, singleAddress(TEST_IPV4_ADDR, port),
         10, 2);
     Question question(Name("example.net"), RRClass::IN(), RRType::A());
@@ -707,7 +719,27 @@
     query.sendQuery(question, buffer, &server);
 
     // Run the test
-    service.run();
+    io_service_->run();
+
+    // Read up to 3 packets.  Use some ad hoc timeout to prevent an infinite
+    // block (see also recvUDP()).
+    const struct timeval timeo = { 10, 0 };
+    int recv_options = 0;
+    if (setsockopt(sock_, SOL_SOCKET, SO_RCVTIMEO, &timeo, sizeof(timeo))) {
+        if (errno == ENOPROTOOPT) { // see ASIOLinkTest::recvUDP()
+            recv_options = MSG_DONTWAIT;
+        } else {
+            isc_throw(IOError, "set RCVTIMEO failed: " << strerror(errno));
+        }
+    }
+    int num = 0;
+    do {
+        char inbuff[512];
+        if (recv(sock_, inbuff, sizeof(inbuff), recv_options) < 0) {
+            num = -1;
+            break;
+        }
+    } while (++num < 3);
 
     // The query should fail
     EXPECT_FALSE(done);

Modified: branches/trac458/src/lib/datasrc/cache.h
==============================================================================
--- branches/trac458/src/lib/datasrc/cache.h (original)
+++ branches/trac458/src/lib/datasrc/cache.h Tue Jan  4 09:09:01 2011
@@ -81,7 +81,7 @@
 /// from the tail of the list.  This operation is not locked.  BIND 10
 /// does not currently use threads, but if it ever does (or if libdatasrc
 /// is ever used by a threaded application), this will need to be
-//revisited.
+/// revisited.
 class HotCache {
 private:
     /// \name Static definitions
@@ -164,7 +164,7 @@
     ///
     /// Retrieves a record from the cache matching the given 
     /// query-tuple.  Returns true if one is found.  If it is a
-    /// posiitve cache entry, then 'rrset' is set to the cached
+    /// positive cache entry, then 'rrset' is set to the cached
     /// RRset.  For both positive and negative cache entries, 'flags'
     /// is set to the query response flags.  The cache entry is 
     /// then promoted to the head of the LRU queue.  (NOTE: Because

Modified: branches/trac458/src/lib/datasrc/data_source.cc
==============================================================================
--- branches/trac458/src/lib/datasrc/data_source.cc (original)
+++ branches/trac458/src/lib/datasrc/data_source.cc Tue Jan  4 09:09:01 2011
@@ -103,14 +103,14 @@
                                new QueryTask(q, ns.getNSName(),
                                              Message::SECTION_ADDITIONAL,
                                              QueryTask::GLUE_QUERY,
-                                             QueryTask::GETADDITIONAL))); 
+                                             QueryTask::GETADDITIONAL)));
         } else if (rrset->getType() == RRType::MX()) {
             const generic::MX& mx = dynamic_cast<const generic::MX&>(rd);
             q.tasks().push(QueryTaskPtr(
                                new QueryTask(q, mx.getMXName(),
                                              Message::SECTION_ADDITIONAL,
                                              QueryTask::NOGLUE_QUERY,
-                                             QueryTask::GETADDITIONAL))); 
+                                             QueryTask::GETADDITIONAL)));
         }
     }
 }

Modified: branches/trac458/src/lib/datasrc/memory_datasrc.cc
==============================================================================
--- branches/trac458/src/lib/datasrc/memory_datasrc.cc (original)
+++ branches/trac458/src/lib/datasrc/memory_datasrc.cc Tue Jan  4 09:09:01 2011
@@ -15,9 +15,11 @@
 #include <map>
 #include <cassert>
 #include <boost/shared_ptr.hpp>
+#include <boost/bind.hpp>
 
 #include <dns/name.h>
 #include <dns/rrclass.h>
+#include <dns/masterload.h>
 
 #include <datasrc/memory_datasrc.h>
 #include <datasrc/rbtree.h>
@@ -65,7 +67,7 @@
      * access is without the impl_-> and it will get inlined anyway.
      */
     // Implementation of MemoryZone::add
-    result::Result add(const ConstRRsetPtr& rrset) {
+    result::Result add(const ConstRRsetPtr& rrset, DomainTree* domains) {
         // Sanitize input
         if (!rrset) {
             isc_throw(NullRRset, "The rrset provided is NULL");
@@ -80,7 +82,7 @@
         }
         // Get the node
         DomainNode* node;
-        switch (domains_.insert(name, &node)) {
+        switch (domains->insert(name, &node)) {
             // Just check it returns reasonable results
             case DomainTree::SUCCEED:
             case DomainTree::ALREADYEXIST:
@@ -104,6 +106,15 @@
         // Try inserting the rrset there
         if (domain->insert(DomainPair(rrset->getType(), rrset)).second) {
             // Ok, we just put it in
+
+            // If this RRset creates a zone cut at this node, mark the node
+            // indicating the need for callback in find().
+            // TBD: handle DNAME, too
+            if (rrset->getType() == RRType::NS() &&
+                rrset->getName() != origin_) {
+                node->enableCallback();
+            }
+
             return (result::SUCCESS);
         } else {
             // The RRSet of given type was already there
@@ -111,16 +122,84 @@
         }
     }
 
+    /*
+     * Same as above, but it checks the return value and if it already exists,
+     * it throws.
+     */
+    void addFromLoad(const ConstRRsetPtr& set, DomainTree* domains) {
+            switch (add(set, domains)) {
+                case result::EXIST:
+                    isc_throw(dns::MasterLoadError, "Duplicate rrset: " <<
+                        set->toText());
+                case result::SUCCESS:
+                    return;
+                default:
+                    assert(0);
+            }
+    }
+
+    // Maintain intermediate data specific to the search context used in
+    /// \c find().
+    ///
+    /// It will be passed to \c zonecutCallback() and record a possible
+    /// zone cut node and related RRset (normally NS or DNAME).
+    struct FindState {
+        FindState(FindOptions options) : zonecut_node_(NULL),
+                                         options_(options)
+        {}
+        const DomainNode* zonecut_node_;
+        ConstRRsetPtr rrset_;
+        const FindOptions options_;
+    };
+
+    // A callback called from possible zone cut nodes.  This will be passed
+    // from the \c find() method to \c RBTree::find().
+    static bool zonecutCallback(const DomainNode& node, FindState* state) {
+        // We perform callback check only for the highest zone cut in the
+        // rare case of nested zone cuts.
+        if (state->zonecut_node_ != NULL) {
+            return (false);
+        }
+
+        const Domain::const_iterator found(node.getData()->find(RRType::NS()));
+        if (found != node.getData()->end()) {
+            // BIND 9 checks if this node is not the origin.  But it cannot
+            // be the origin because we don't enable the callback at the
+            // origin node (see MemoryZoneImpl::add()).  Or should we do a
+            // double check for it?
+            state->zonecut_node_ = &node;
+            state->rrset_ = found->second;
+
+            // Unless glue is allowed the search stops here, so we return
+            // false; otherwise return true to continue the search.
+            return ((state->options_ & FIND_GLUE_OK) == 0);
+        }
+
+        // This case should not happen because we enable callback only
+        // when we add an RR searched for above.
+        assert(0);
+        // This is here to avoid warning (therefore compilation error)
+        // in case assert is turned off. Otherwise we could get "Control
+        // reached end of non-void function".
+        return (false);
+    }
+
     // Implementation of MemoryZone::find
-    FindResult find(const Name& name, RRType type) const {
+    FindResult find(const Name& name, RRType type,
+                    const FindOptions options) const
+    {
         // Get the node
-        DomainNode* node;
-        switch (domains_.find(name, &node)) {
+        DomainNode* node(NULL);
+        FindState state(options);
+        switch (domains_.find(name, &node, zonecutCallback, &state)) {
             case DomainTree::PARTIALMATCH:
-                // Pretend it was not found for now
-                // TODO: Implement real delegation. Currently, not having
-                // the the domain can cause a partialmatch as well, so
-                // better check.
+                if (state.zonecut_node_ != NULL) {
+                    return (FindResult(DELEGATION, state.rrset_));
+                }
+                // TODO: we should also cover empty non-terminal cases, which
+                // will require non trivial code and is deferred for later
+                // development.  For now, we regard any partial match that
+                // didn't hit a zone cut as "not found".
             case DomainTree::NOTFOUND:
                 return (FindResult(NXDOMAIN, ConstRRsetPtr()));
             case DomainTree::EXACTMATCH: // This one is OK, handle it
@@ -131,7 +210,18 @@
         assert(node);
         assert(!node->isEmpty());
 
-        Domain::const_iterator found(node->getData()->find(type));
+        Domain::const_iterator found;
+
+        // If the node callback is enabled, this may be a zone cut.  If it
+        // has a NS RR, we should return a delegation.
+        if (node->isCallbackEnabled()) {
+            found = node->getData()->find(RRType::NS());
+            if (found != node->getData()->end()) {
+                return (FindResult(DELEGATION, found->second));
+            }
+        }
+
+        found = node->getData()->find(type);
         if (found != node->getData()->end()) {
             // Good, it is here
             return (FindResult(SUCCESS, found->second));
@@ -166,13 +256,27 @@
 }
 
 Zone::FindResult
-MemoryZone::find(const Name& name, const RRType& type) const {
-    return (impl_->find(name, type));
+MemoryZone::find(const Name& name, const RRType& type,
+                 const FindOptions options) const
+{
+    return (impl_->find(name, type, options));
 }
 
 result::Result
 MemoryZone::add(const ConstRRsetPtr& rrset) {
-    return (impl_->add(rrset));
+    return (impl_->add(rrset, &impl_->domains_));
+}
+
+
+void
+MemoryZone::load(const string& filename) {
+    // Load it into a temporary tree
+    MemoryZoneImpl::DomainTree tmp;
+    masterLoad(filename.c_str(), getOrigin(), getClass(),
+        boost::bind(&MemoryZoneImpl::addFromLoad, impl_, _1, &tmp));
+    // If it went well, put it inside
+    tmp.swap(impl_->domains_);
+    // And let the old data die with tmp
 }
 
 /// Implementation details for \c MemoryDataSrc hidden from the public

Modified: branches/trac458/src/lib/datasrc/memory_datasrc.h
==============================================================================
--- branches/trac458/src/lib/datasrc/memory_datasrc.h (original)
+++ branches/trac458/src/lib/datasrc/memory_datasrc.h Tue Jan  4 09:09:01 2011
@@ -62,7 +62,8 @@
     /// It returns NULL pointer in case of NXDOMAIN and NXRRSET
     /// (the base class documentation does not seem to require that).
     virtual FindResult find(const isc::dns::Name& name,
-                            const isc::dns::RRType& type) const;
+                            const isc::dns::RRType& type,
+                            const FindOptions options = FIND_DEFAULT) const;
 
     /// \brief Inserts an rrset into the zone.
     ///
@@ -97,6 +98,30 @@
         { }
     };
 
+    /// \brief Load zone from masterfile.
+    ///
+    /// This loads data from masterfile specified by filename. It replaces
+    /// current content. The masterfile parsing ability is kind of limited,
+    /// see isc::dns::masterLoad.
+    ///
+    /// This throws isc::dns::MasterLoadError if there is problem with loading
+    /// (missing file, malformed, it contains different zone, etc - see
+    /// isc::dns::masterLoad for details).
+    ///
+    /// In case of internal problems, OutOfZone, NullRRset or AssertError could
+    /// be thrown, but they should not be expected. Exceptions caused by
+    /// allocation may be thrown as well.
+    ///
+    /// If anything is thrown, the previous content is preserved (so it can
+    /// be used to update the data, but if user makes a typo, the old one
+    /// is kept).
+    ///
+    /// \param filename The master file to load.
+    ///
+    /// \todo We may need to split it to some kind of build and commit/abort.
+    ///     This will probably be needed when a better implementation of
+    ///     configuration reloading is written.
+    void load(const std::string& filename);
 private:
     /// \name Hidden private data
     //@{

Modified: branches/trac458/src/lib/datasrc/rbtree.h
==============================================================================
--- branches/trac458/src/lib/datasrc/rbtree.h (original)
+++ branches/trac458/src/lib/datasrc/rbtree.h Tue Jan  4 09:09:01 2011
@@ -38,8 +38,11 @@
 /// Helper function to remove the base domain from super domain
 ///
 /// the precondition of this function is the super_name contains the
-/// sub_name so \code Name a("a.b.c"); Name b("b.c");
-/// Name c = a - b; \\c will be "a" \endcode
+/// sub_name so
+/// \code Name a("a.b.c");
+/// Name b("b.c");
+/// Name c = a - b;
+/// \endcode
 ///
 /// \note function in this namespace is not intended to be used outside.
 inline isc::dns::Name
@@ -51,8 +54,9 @@
 
 template <typename T>
 class RBTree;
+
 /// \brief \c RBNode use by RBTree to store any data related to one domain name
-
+///
 /// It has two roles, the first one is as one node in the \c RBTree,
 /// the second one is to store the data related to one domain name and maintain
 /// the domain name hierarchy struct in one domain name space.
@@ -74,11 +78,11 @@
     friend class RBTree<T>;
     typedef boost::shared_ptr<T> NodeDataPtr;
 
-    /// \name Deonstructor
+    /// \name Destructor
     /// \note it's seems a little strange that constructor is private
     /// but deconstructor left public, the reason is for some smart pointer
     /// like std::auto_ptr, they needs to delete RBNode in sometimes, but
-    /// \code delete *pointer_to_node \codeend shouldn't be called directly
+    /// \code delete *pointer_to_node \endcode shouldn't be called directly
     //@{
     ~RBNode();
     //@}
@@ -111,6 +115,24 @@
     void setData(const NodeDataPtr& data) { data_ = data; }
     //@}
 
+    /// \name Callback related methods
+    ///
+    /// See the description of \c RBTree<T>::find() about callbacks.
+    ///
+    /// These methods never throw an exception.
+    //@{
+    /// Return if callback is enabled at the node.
+    ///
+    /// This method never throws an exception.
+    bool isCallbackEnabled() const { return (callback_required_); }
+
+    /// Enable callback at the node.
+    void enableCallback() { callback_required_ = true; }
+
+    /// Disable callback at the node.
+    void disableCallback() { callback_required_ = false; }
+    //@}
+
 
 private:
     /// \brief Define rbnode color
@@ -147,6 +169,7 @@
 
     isc::dns::Name     name_;
     NodeDataPtr       data_;
+
     /// the down pointer points to the root node of sub domains of current
     /// domain
     /// \par Adding down pointer to \c RBNode is for two purpose:
@@ -154,6 +177,10 @@
     /// big flat tree into several hierarchy trees
     /// \li It save memory useage, so same label won't be saved several times
     RBNode<T>*  down_;
+
+    // If true, callback should be called at this node in search.
+    // (This may have to become part of more general "attribute flags")
+    bool callback_required_;
 };
 
 
@@ -167,7 +194,8 @@
     color_(BLACK),
     // dummy name, the value doesn't matter:
     name_(isc::dns::Name::ROOT_NAME()),
-    down_(this)
+    down_(this),
+    callback_required_(false)
 {
 }
 
@@ -178,7 +206,8 @@
     right_(NULL_NODE()),
     color_(RED),
     name_(name),
-    down_(NULL_NODE())
+    down_(NULL_NODE()),
+    callback_required_(false)
 {
 }
 
@@ -186,51 +215,55 @@
 template <typename T>
 RBNode<T>::~RBNode() {
 }
-/// \brief \c RBTree class represents all the domains with the same suffix,
-/// so it can be used to store the domains in one zone.
-///
-/// \c RBTree is a generic red black tree, and contains all the nodes with
-/// the same suffix, since each name may have sub domain names
-/// so \c RBTree is a recursive data structure namely tree in tree.
-/// So for one zone, several RBTrees may be involved. But from outside, the sub
-/// tree is opaque for end users.
-///
-/// \c RBTree split the domain space into hierarchy red black trees, nodes in one
-/// tree has the same base name. The benefit of this struct is that:
-/// - enhance the query performace compared with one big flat red black tree
-/// - decrase the memory footprint to save common labels only once.
-
-/*
-/// \verbatim
-/// with the following names:
-///     a       x.d.e.f     o.w.y.d.e.f
-///     b       z.d.e.f     p.w.y.d.e.f
-///     c       g.h         q.w.y.d.e.f
-///     the tree will looks like:
-///                               b
-///                             /   \
-///                            a    d.e.f
-///                                   /|\
-///                                  c | g.h
-///                                    |
-///                                   w.y
-///                                   /|\
-///                                  x | z
-///                                    |
-///                                    p
-///                                   / \
-///                                  o   q
-/// \endverbatim
-/// \note open problems:
-/// - current find funciton only return non-empty nodes, so there is no difference
-///   between find one not exist name with empty non-terminal nodes, but in DNS query
-///   logic, they are different
-/// \todo
-/// - add remove interface
-/// - add iterator to iterate the whole rbtree while may needed by axfr
-/// - since \c RBNode only has down pointer without up pointer, the node path during finding
-///   should be recorded for later use
-*/
+
+// note: the following class description is documented using C-style comments
+// because the verbatim diagram contain a backslash, which could be interpreted
+// as part of a multi-line comment with C++ style comments.
+/**
+ *  \brief \c RBTree class represents all the domains with the same suffix,
+ *  so it can be used to store the domains in one zone.
+ * 
+ *  \c RBTree is a generic red black tree, and contains all the nodes with
+ *  the same suffix, since each name may have sub domain names
+ *  so \c RBTree is a recursive data structure namely tree in tree.
+ *  So for one zone, several RBTrees may be involved. But from outside, the sub
+ *  tree is opaque for end users.
+ * 
+ *  \c RBTree split the domain space into hierarchy red black trees, nodes in one
+ *  tree has the same base name. The benefit of this struct is that:
+ *  - enhance the query performace compared with one big flat red black tree
+ *  - decrase the memory footprint to save common labels only once.
+ * 
+ *  \verbatim
+  with the following names:
+      a       x.d.e.f     o.w.y.d.e.f
+      b       z.d.e.f     p.w.y.d.e.f
+      c       g.h         q.w.y.d.e.f
+      the tree will looks like:
+                                b
+                              /   \
+                             a    d.e.f
+                                    /|\
+                                   c | g.h
+                                     |
+                                    w.y
+                                    /|\
+                                   x | z
+                                     |
+                                     p
+                                    / \
+                                   o   q
+ *  \endverbatim
+ *  \note open problems:
+ *  - current find funciton only return non-empty nodes, so there is no difference
+ *    between find one not exist name with empty non-terminal nodes, but in DNS query
+ *    logic, they are different
+ *  \todo
+ *  - add remove interface
+ *  - add iterator to iterate the whole rbtree while may needed by axfr
+ *  - since \c RBNode only has down pointer without up pointer, the node path during finding
+ *    should be recorded for later use
+ */
 template <typename T>
 class RBTree : public boost::noncopyable {
     friend class RBNode<T>;
@@ -247,7 +280,7 @@
 
     /// \name Constructor and Destructor
     //@{
-    RBTree();
+    explicit RBTree();
 
     /// \b Note: RBTree is not intended to be inherited so the destructor
     /// is not virtual
@@ -256,13 +289,98 @@
 
     /// \name Inquery methods
     //@{
-    /// \brief Find the node with the name
+    /// \brief Find the node that gives a longest match against the given name
+    ///
+    /// This method searches the \c RBTree for a node whose name is a longest
+    /// match against \c name.  The found node, if any, is returned via the
+    /// \c node pointer.
+    /// By default, nodes that don't have data will be ignored, and the result
+    /// can be \c NOTFOUND even if there is a node whose name matches the
+    /// given \c name.
+    /// We'll soon introduce a "no data OK" mode in this method.  It would
+    /// match any node of the tree regardless of whether the node has data
+    /// or not.
+    /// Since the tree is "compressed", i.e., a node can contain multiple
+    /// name labels, there are counter intuitive cases in the "no data OK"
+    /// mode.  For example, see the diagram of the class description.
+    /// Name "y.d.e.f" is logically contained in the tree as part of the
+    /// "compressed" node of "w.y".  But the search logic of this method
+    /// cannot find the logical match, and would return a \c PARTIALMATCH
+    /// result pointing to node "d.e.f".  To correctly identify the real
+    /// longest match, "y.d.e.f" with empty data, the caller needs to
+    /// perform additional steps.
+    ///
+    /// This version of \c find() method is templated to allow the caller
+    /// to specify a "hook" at nodes that give a partial match.
+    /// When the search encounters a node with data that partially matches
+    /// \c name (i.e. node's name is a superdomain of \c name) and has
+    /// enabled callback (via the \c RBNode::enableCallback() method), if
+    /// \c callback is non \c NULL then the callback function is called
+    /// with the argument of a reference to the node and the given
+    /// callback argument (\c callback_arg).  The template parameter specifies
+    /// the type of the callback argument.
+    /// The callback function returns either \c true or \c false, meaning
+    /// the search should stop or continue, respectively.
+    /// If the return value is \c true the search stops immediately at the
+    /// node even if there could be a longer matching name below it.
+    /// In reality, this convoluted callback rule is specifically intended
+    /// to be used to handle a zone cut (delegation) at a name search inside
+    /// a zone, and won't be used in any other cases.
+    /// Other applications of the tree won't need callbacks, and they should
+    /// use the non templated version of the \c find() method.
+    ///
+    /// Since the expected usage of callback is very limited, we do not
+    /// generalize the interface so that it can be an arbitrary functions or
+    /// functor objects in favor of simplicity and efficiency.
+    ///
+    /// This method involves operations on names that can throw an exception.
+    /// If that happens the exception will be propagated to the caller.
+    /// The callback function should generally not throw an exception, but
+    /// if it throws, the exception will be propagated to the caller.
+    ///
     /// \param name Target to be found
-    /// \param node Point to the node when the return vaule is \c not
-    /// NOTFOUND, if the return value is NOTFOUND, the value of node is
-    /// \c unknown
-    Result find(const isc::dns::Name& name, RBNode<T>** node) const;
-    Result find(const isc::dns::Name& name, const RBNode<T>** node) const;
+    /// \param node On success (either \c EXACTMATCH or \c PARTIALMATCH)
+    /// it will store a pointer to the matching node
+    /// \param callback If non \c NULL, a call back function to be called
+    /// at "delegation" nodes (see above).
+    /// \param callback_arg A caller supplied argument to be passed to
+    /// \c callback.
+    ///
+    /// \return \c EXACTMATCH A node that whose name is equal to \c name is
+    /// found.  \c *node will be set to point to that node.
+    /// \return \c PARTIALMATCH There is a no exact match, but a superdomain
+    /// of \c name exists.  \c node will be set to point to the node whose
+    /// name is the longest among such superdomains.
+    /// \return \c NOTFOUND There is no exact or partial match against \c name
+    /// \c *node will be intact in this case.
+    template <typename CBARG>
+    Result find(const isc::dns::Name& name, RBNode<T>** node,
+                bool (*callback)(const RBNode<T>&, CBARG),
+                CBARG callback_arg) const;
+
+    /// Same as the other version, but the returned \c node will be immutable.
+    template <typename CBARG>
+    Result find(const isc::dns::Name& name, const RBNode<T>** node,
+                bool (*callback)(const RBNode<T>&, CBARG),
+                CBARG callback_arg) const;
+
+    /// Same as the templated version, but does not use callback.
+    ///
+    /// Applications except the zone implementation should generally use the
+    /// non templated version.
+    Result find(const isc::dns::Name& name, RBNode<T>** node) const {
+        return (find<void*>(name, node, NULL, NULL));
+    }
+
+    /// Same as the templated version, but does not use callback, and the
+    /// returned \c node will be immutable.
+    ///
+    /// In general, this version should be preferred over the other non
+    /// templated version, unless the caller knows it should modify the
+    /// returned node.
+    Result find(const isc::dns::Name& name, const RBNode<T>** node) const {
+        return (find<void*>(name, node, NULL, NULL));
+    }
 
     /// \brief Get the total node count in the tree
     /// the node count including the node created common suffix node,
@@ -289,10 +407,20 @@
     /// - ALREADYEXIST means already has the node with the given name
     //
     /// \node To modify the data related with one name but not sure the name has
-    /// inserted or not, it is better to call \code insert \endcode,instead of
-    /// \code find() \endcode, in case the name isn't exist and needs to insert again
+    /// inserted or not, it is better to call \c insert,instead of
+    /// \c find(), in case the name isn't exist and needs to insert again
     Result insert(const isc::dns::Name& name, RBNode<T>** inserted_node);
     //@}
+
+    /// \brief Swaps two tree's contents.
+    ///
+    /// This acts the same as many std::*.swap functions, exchanges the
+    /// contents. This doesn't throw anything.
+    void swap(RBTree<T>& other) {
+        std::swap(root_, other.root_);
+        std::swap(NULLNODE, other.NULLNODE);
+        std::swap(node_count_, other.node_count_);
+    }
 
 private:
     /// \name RBTree balance functions
@@ -315,8 +443,11 @@
     /// and node will points to c.b.a
     /// \note parameter up now is not used by any funciton, but we are gonna
     /// need it soon to implement function like remove
+    template <typename CBARG>
     Result findHelper(const isc::dns::Name& name, const RBNode<T>** up,
-                      RBNode<T>** node) const;
+                      RBNode<T>** node,
+                      bool (*callback)(const RBNode<T>&, CBARG),
+                      CBARG callback_arg) const;
     void dumpTreeHelper(std::ostream& os, const RBNode<T>* node,
                         unsigned int depth) const;
     /// for indent purpose, add certian mount empty charachter to output stream
@@ -380,30 +511,39 @@
     --node_count_;
 }
 
-template <typename T>
+template <typename T> template <typename CBARG>
 typename RBTree<T>::Result
-RBTree<T>::find(const isc::dns::Name& name, RBNode<T>** node) const {
+RBTree<T>::find(const isc::dns::Name& name, RBNode<T>** node,
+                bool (*callback)(const RBNode<T>&, CBARG),
+                CBARG callback_arg) const
+{
     const RBNode<T>* up_node = NULLNODE;
-    return (findHelper(name, &up_node, node));
-}
-
-template <typename T>
+    return (findHelper(name, &up_node, node, callback, callback_arg));
+}
+
+template <typename T> template <typename CBARG>
 typename RBTree<T>::Result
-RBTree<T>::find(const isc::dns::Name& name, const RBNode<T>** node) const {
+RBTree<T>::find(const isc::dns::Name& name, const RBNode<T>** node,
+                bool (*callback)(const RBNode<T>&, CBARG),
+                CBARG callback_arg) const
+{
     const RBNode<T>* up_node;
     RBNode<T>* target_node;
     const typename RBTree<T>::Result ret =
-        findHelper(name, &up_node, &target_node);
+        findHelper(name, &up_node, &target_node, callback, callback_arg);
     if (ret != NOTFOUND) {
         *node = target_node;
     }
     return (ret);
 }
 
-template <typename T>
+template <typename T> template <typename CBARG>
 typename RBTree<T>::Result
-RBTree<T>::findHelper(const isc::dns::Name& target_name, const RBNode<T>** up_node,
-                      RBNode<T>** target) const
+RBTree<T>::findHelper(const isc::dns::Name& target_name,
+                      const RBNode<T>** up_node,
+                      RBNode<T>** target,
+                      bool (*callback)(const RBNode<T>&, CBARG),
+                      CBARG callback_arg) const
 {
     using namespace helper;
 
@@ -431,12 +571,17 @@
                 node = (compare_result.getOrder() < 0) ?
                     node->left_ : node->right_;
             } else if (relation == isc::dns::NameComparisonResult::SUBDOMAIN) {
-                *up_node = node;
-                name = name - node->name_;
                 if (!node->isEmpty()) {
                     ret = RBTree<T>::PARTIALMATCH;
                     *target = node;
+                    if (callback != NULL && node->callback_required_) {
+                        if ((callback)(*node, callback_arg)) {
+                            break;
+                        }
+                    }
                 }
+                *up_node = node;
+                name = name - node->name_;
                 node = node->down_;
             } else {
                 break;
@@ -527,14 +672,15 @@
 RBTree<T>::nodeFission(RBNode<T>& node, const isc::dns::Name& base_name) {
     using namespace helper;
     const isc::dns::Name sub_name = node.name_ - base_name;
-    // using auto_ptr here is to avoid memory leak in case of exceptoin raised
+    // using auto_ptr here is to avoid memory leak in case of exception raised
     // after the RBNode creation
     std::auto_ptr<RBNode<T> > down_node(new RBNode<T>(sub_name));
     std::swap(node.data_, down_node->data_);
+    std::swap(node.callback_required_, down_node->callback_required_);
     down_node->down_ = node.down_;
     node.name_ = base_name;
     node.down_ = down_node.get();
-    //root node of sub tree, the initial color is BLACK
+    // root node of sub tree, the initial color is BLACK
     down_node->color_ = RBNode<T>::BLACK;
     ++node_count_;
     down_node.release();

Modified: branches/trac458/src/lib/datasrc/tests/memory_datasrc_unittest.cc
==============================================================================
--- branches/trac458/src/lib/datasrc/tests/memory_datasrc_unittest.cc (original)
+++ branches/trac458/src/lib/datasrc/tests/memory_datasrc_unittest.cc Tue Jan  4 09:09:01 2011
@@ -17,6 +17,7 @@
 #include <dns/name.h>
 #include <dns/rrclass.h>
 #include <dns/rrttl.h>
+#include <dns/masterload.h>
 
 #include <datasrc/memory_datasrc.h>
 
@@ -26,6 +27,9 @@
 using namespace isc::datasrc;
 
 namespace {
+// Commonly used result codes (Who should write the prefix all the time)
+using result::SUCCESS;
+using result::EXIST;
 
 class MemoryDataSrcTest : public ::testing::Test {
 protected:
@@ -138,18 +142,31 @@
         class_(RRClass::IN()),
         origin_("example.org"),
         ns_name_("ns.example.org"),
+        child_ns_name_("child.example.org"),
+        child_glue_name_("ns.child.example.org"),
+        grandchild_ns_name_("grand.child.example.org"),
+        grandchild_glue_name_("ns.grand.child.example.org"),
         zone_(class_, origin_),
         rr_out_(new RRset(Name("example.com"), class_, RRType::A(),
             RRTTL(300))),
         rr_ns_(new RRset(origin_, class_, RRType::NS(), RRTTL(300))),
         rr_ns_a_(new RRset(ns_name_, class_, RRType::A(), RRTTL(300))),
         rr_ns_aaaa_(new RRset(ns_name_, class_, RRType::AAAA(), RRTTL(300))),
-        rr_a_(new RRset(origin_, class_, RRType::A(), RRTTL(300)))
+        rr_a_(new RRset(origin_, class_, RRType::A(), RRTTL(300))),
+        rr_child_ns_(new RRset(child_ns_name_, class_, RRType::NS(),
+                               RRTTL(300))),
+        rr_child_glue_(new RRset(child_glue_name_, class_, RRType::A(),
+                              RRTTL(300))),
+        rr_grandchild_ns_(new RRset(grandchild_ns_name_, class_, RRType::NS(),
+                                    RRTTL(300))),
+        rr_grandchild_glue_(new RRset(grandchild_glue_name_, class_,
+                                      RRType::AAAA(), RRTTL(300)))
     {
     }
     // Some data to test with
-    RRClass class_;
-    Name origin_, ns_name_;
+    const RRClass class_;
+    const Name origin_, ns_name_, child_ns_name_, child_glue_name_,
+        grandchild_ns_name_, grandchild_glue_name_;
     // The zone to torture by tests
     MemoryZone zone_;
 
@@ -159,7 +176,7 @@
      * inside anyway. We will check it finds them and does not change
      * the pointer.
      */
-    RRsetPtr
+    ConstRRsetPtr
         // Out of zone RRset
         rr_out_,
         // NS of example.org
@@ -170,6 +187,10 @@
         rr_ns_aaaa_,
         // A of example.org
         rr_a_;
+    ConstRRsetPtr rr_child_ns_; // NS of a child domain (for delegation)
+    ConstRRsetPtr rr_child_glue_; // glue RR of the child domain
+    ConstRRsetPtr rr_grandchild_ns_; // NS below a zone cut (unusual)
+    ConstRRsetPtr rr_grandchild_glue_; // glue RR below a deeper zone cut
 
     /**
      * \brief Test one find query to the zone.
@@ -181,19 +202,32 @@
      * \param name The name to ask for.
      * \param rrtype The RRType to ask of.
      * \param result The expected code of the result.
+     * \param check_answer Should a check against equality of the answer be
+     *     done?
      * \param answer The expected rrset, if any should be returned.
+     * \param zone Check different MemoryZone object than zone_ (if NULL,
+     *     uses zone_)
      */
     void findTest(const Name& name, const RRType& rrtype, Zone::Result result,
-        const ConstRRsetPtr& answer = ConstRRsetPtr())
+                  bool check_answer = true,
+                  const ConstRRsetPtr& answer = ConstRRsetPtr(),
+                  MemoryZone *zone = NULL,
+                  Zone::FindOptions options = Zone::FIND_DEFAULT)
     {
+        if (!zone) {
+            zone = &zone_;
+        }
         // The whole block is inside, because we need to check the result and
         // we can't assign to FindResult
         EXPECT_NO_THROW({
-            Zone::FindResult find_result(zone_.find(name, rrtype));
-            // Check it returns correct answers
-            EXPECT_EQ(result, find_result.code);
-            EXPECT_EQ(answer, find_result.rrset);
-        });
+                Zone::FindResult find_result(zone->find(name, rrtype,
+                                                        options));
+                // Check it returns correct answers
+                EXPECT_EQ(result, find_result.code);
+                if (check_answer) {
+                    EXPECT_EQ(answer, find_result.rrset);
+                }
+            });
     }
 };
 
@@ -219,7 +253,6 @@
     // Test null pointer
     EXPECT_THROW(zone_.add(ConstRRsetPtr()), MemoryZone::NullRRset);
 
-    using namespace result; // Who should write the prefix all the time
     // Now put all the data we have there. It should throw nothing
     EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_ns_)));
     EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_ns_a_)));
@@ -229,6 +262,89 @@
     // Try putting there something twice, it should be rejected
     EXPECT_NO_THROW(EXPECT_EQ(EXIST, zone_.add(rr_ns_)));
     EXPECT_NO_THROW(EXPECT_EQ(EXIST, zone_.add(rr_ns_a_)));
+}
+
+// Test adding child zones and zone cut handling
+TEST_F(MemoryZoneTest, delegationNS) {
+    // add in-zone data
+    EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_ns_)));
+
+    // install a zone cut
+    EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_child_ns_)));
+
+    // below the zone cut
+    findTest(Name("www.child.example.org"), RRType::A(), Zone::DELEGATION,
+             true, rr_child_ns_);
+
+    // at the zone cut
+    findTest(Name("child.example.org"), RRType::A(), Zone::DELEGATION,
+             true, rr_child_ns_);
+    findTest(Name("child.example.org"), RRType::NS(), Zone::DELEGATION,
+             true, rr_child_ns_);
+
+    // finding NS for the apex (origin) node.  This must not be confused
+    // with delegation due to the existence of an NS RR.
+    findTest(origin_, RRType::NS(), Zone::SUCCESS, true, rr_ns_);
+
+    // unusual case of "nested delegation": the highest cut should be used.
+    EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_grandchild_ns_)));
+    findTest(Name("www.grand.child.example.org"), RRType::A(),
+             Zone::DELEGATION, true, rr_child_ns_); // note: !rr_grandchild_ns_
+}
+
+TEST_F(MemoryZoneTest, glue) {
+    // install zone data:
+    // a zone cut
+    EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_child_ns_)));
+    // glue for this cut
+    EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_child_glue_)));
+    // a nested zone cut (unusual)
+    EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_grandchild_ns_)));
+    // glue under the deeper zone cut
+    EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_grandchild_glue_)));
+
+    // by default glue is hidden due to the zone cut
+    findTest(child_glue_name_, RRType::A(), Zone::DELEGATION, true,
+             rr_child_ns_);
+
+
+    // If we do it in the "glue OK" mode, we should find the exact match.
+    findTest(child_glue_name_, RRType::A(), Zone::SUCCESS, true,
+             rr_child_glue_, NULL, Zone::FIND_GLUE_OK);
+
+    // glue OK + NXRRSET case
+    findTest(child_glue_name_, RRType::AAAA(), Zone::NXRRSET, true,
+             ConstRRsetPtr(), NULL, Zone::FIND_GLUE_OK);
+
+    // glue OK + NXDOMAIN case
+    findTest(Name("www.child.example.org"), RRType::A(), Zone::DELEGATION,
+             true, rr_child_ns_, NULL, Zone::FIND_GLUE_OK);
+
+    // TODO:
+    // glue name would match a wildcard under a zone cut: wildcard match
+    // shouldn't happen under a cut and result must be PARTIALMATCH
+    // (This case cannot be tested yet)
+
+    // nested cut case.  The glue should be found.
+    findTest(grandchild_glue_name_, RRType::AAAA(), Zone::SUCCESS,
+             true, rr_grandchild_glue_, NULL, Zone::FIND_GLUE_OK);    
+
+    // A non-existent name in nested cut.  This should result in delegation
+    // at the highest zone cut.
+    findTest(Name("www.grand.child.example.org"), RRType::TXT(),
+             Zone::DELEGATION, true, rr_child_ns_, NULL, Zone::FIND_GLUE_OK);
+}
+
+// Test adding DNAMEs and resulting delegation handling
+// Listing ideas only for now
+TEST_F(MemoryZoneTest, delegationDNAME) {
+    // apex DNAME: allowed by spec.  No DNAME delegation at the apex;
+    // descendants are subject to delegation.
+
+    // Other cases of NS and DNAME mixture are prohibited.
+    // BIND 9 doesn't reject such cases at load time, however.
+
+    // DNAME and ordinary types (allowed by spec)
 }
 
 /**
@@ -240,7 +356,6 @@
  */
 TEST_F(MemoryZoneTest, find) {
     // Fill some data inside
-    using namespace result; // Who should write the prefix all the time
     // Now put all the data we have there. It should throw nothing
     EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_ns_)));
     EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_ns_a_)));
@@ -248,8 +363,8 @@
     EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_a_)));
 
     // These two should be successful
-    findTest(origin_, RRType::NS(), Zone::SUCCESS, rr_ns_);
-    findTest(ns_name_, RRType::A(), Zone::SUCCESS, rr_ns_a_);
+    findTest(origin_, RRType::NS(), Zone::SUCCESS, true, rr_ns_);
+    findTest(ns_name_, RRType::A(), Zone::SUCCESS, true, rr_ns_a_);
 
     // These domain exist but don't have the provided RRType
     findTest(origin_, RRType::AAAA(), Zone::NXRRSET);
@@ -260,4 +375,34 @@
     findTest(Name("example.net"), RRType::A(), Zone::NXDOMAIN);
 }
 
-}
+TEST_F(MemoryZoneTest, load) {
+    // Put some data inside the zone
+    EXPECT_NO_THROW(EXPECT_EQ(result::SUCCESS, zone_.add(rr_ns_)));
+    // Loading with different origin should fail
+    EXPECT_THROW(zone_.load(TEST_DATA_DIR "/root.zone"), MasterLoadError);
+    // See the original data is still there, survived the exception
+    findTest(origin_, RRType::NS(), Zone::SUCCESS, true, rr_ns_);
+    // Create correct zone
+    MemoryZone rootzone(class_, Name("."));
+    // Try putting something inside
+    EXPECT_NO_THROW(EXPECT_EQ(result::SUCCESS, rootzone.add(rr_ns_aaaa_)));
+    // Load the zone. It should overwrite/remove the above RRset
+    EXPECT_NO_THROW(rootzone.load(TEST_DATA_DIR "/root.zone"));
+
+    // Now see there are some rrsets (we don't look inside, though)
+    findTest(Name("."), RRType::SOA(), Zone::SUCCESS, false, ConstRRsetPtr(),
+        &rootzone);
+    findTest(Name("."), RRType::NS(), Zone::SUCCESS, false, ConstRRsetPtr(),
+        &rootzone);
+    findTest(Name("a.root-servers.net."), RRType::A(), Zone::SUCCESS, false,
+        ConstRRsetPtr(), &rootzone);
+    // But this should no longer be here
+    findTest(ns_name_, RRType::AAAA(), Zone::NXDOMAIN, true, ConstRRsetPtr(),
+        &rootzone);
+
+    // Try loading zone that is wrong in a different way
+    EXPECT_THROW(zone_.load(TEST_DATA_DIR "/duplicate_rrset.zone"),
+        MasterLoadError);
+}
+
+}

Modified: branches/trac458/src/lib/datasrc/tests/rbtree_unittest.cc
==============================================================================
--- branches/trac458/src/lib/datasrc/tests/rbtree_unittest.cc (original)
+++ branches/trac458/src/lib/datasrc/tests/rbtree_unittest.cc Tue Jan  4 09:09:01 2011
@@ -169,6 +169,61 @@
     EXPECT_EQ(Name("q"), rbtnode->getName());
 }
 
+bool
+testCallback(const RBNode<int>&, bool* callack_checker) {
+    *callack_checker = true;
+    return (false);
+}
+
+TEST_F(RBTreeTest, callback) {
+    // by default callback isn't enabled
+    EXPECT_EQ(RBTree<int>::SUCCEED, rbtree.insert(Name("callback.example"),
+                                                  &rbtnode));
+    rbtnode->setData(RBNode<int>::NodeDataPtr(new int(1)));
+    EXPECT_FALSE(rbtnode->isCallbackEnabled());
+
+    // enable/re-disable callback
+    rbtnode->enableCallback();
+    EXPECT_TRUE(rbtnode->isCallbackEnabled());
+    rbtnode->disableCallback();
+    EXPECT_FALSE(rbtnode->isCallbackEnabled());
+
+    // enable again for subsequent tests
+    rbtnode->enableCallback();
+    // add more levels below and above the callback node for partial match.
+    RBNode<int>* subrbtnode;
+    EXPECT_EQ(RBTree<int>::SUCCEED, rbtree.insert(Name("sub.callback.example"),
+                                                  &subrbtnode));
+    subrbtnode->setData(RBNode<int>::NodeDataPtr(new int(2)));
+    RBNode<int>* parentrbtnode;
+    EXPECT_EQ(RBTree<int>::ALREADYEXIST, rbtree.insert(Name("example"),
+                                                       &parentrbtnode));
+    //  the chilld/parent nodes shouldn't "inherit" the callback flag.
+    // "rbtnode" may be invalid due to the insertion, so we need to re-find
+    // it.
+    EXPECT_EQ(RBTree<int>::EXACTMATCH, rbtree.find(Name("callback.example"),
+                                                   &rbtnode));
+    EXPECT_TRUE(rbtnode->isCallbackEnabled());
+    EXPECT_FALSE(subrbtnode->isCallbackEnabled());
+    EXPECT_FALSE(parentrbtnode->isCallbackEnabled());
+
+    // check if the callback is called from find()
+    bool callback_called = false;
+    EXPECT_EQ(RBTree<int>::EXACTMATCH,
+              rbtree.find(Name("sub.callback.example"), &crbtnode,
+                          testCallback, &callback_called));
+    EXPECT_TRUE(callback_called);
+
+    // enable callback at the parent node, but it doesn't have data so
+    // the callback shouldn't be called.
+    parentrbtnode->enableCallback();
+    callback_called = false;
+    EXPECT_EQ(RBTree<int>::EXACTMATCH,
+              rbtree.find(Name("callback.example"), &crbtnode,
+                          testCallback, &callback_called));
+    EXPECT_FALSE(callback_called);
+}
+
 TEST_F(RBTreeTest, dumpTree) {
     std::ostringstream str;
     std::ostringstream str2;
@@ -176,4 +231,34 @@
     str2 << "tree has 13 node(s)\nb. (black)\n     a. (black)\n          NULL\n          NULL\n     d.e.f. (black)[invisible] \n          begin down from d.e.f.\n          w.y. (black)[invisible] \n               begin down from w.y.\n               p. (black)\n                    o. (red)\n                         NULL\n                         NULL\n                    q. (red)\n                         NULL\n                         NULL\n               end down from w.y.\n               x. (red)\n                    NULL\n                    NULL\n               z. (red)\n                    begin down from z.\n                    j. (black)\n                         NULL\n                         NULL\n                    end down from z.\n                    NULL\n                    NULL\n          end down from d.e.f.\n          c. (red)\n               NULL\n               NULL\n          g.h. (red)\n               begin down from g.h.\n               i. (black)\n  
                   NULL\n                    NULL\n               end down from g.h.\n               NULL\n               NULL\n";
     EXPECT_EQ(str.str(), str2.str());
 }
-}
+
+TEST_F(RBTreeTest, swap) {
+    // Store info about the first tree
+    std::ostringstream str1;
+    rbtree.dumpTree(str1);
+    size_t count1(rbtree.getNodeCount());
+
+    // Create second one and store state
+    RBTree<int> tree2;
+    RBNode<int>* node;
+    tree2.insert(Name("second"), &node);
+    std::ostringstream str2;
+    tree2.dumpTree(str2);
+
+    // Swap them
+    ASSERT_NO_THROW(tree2.swap(rbtree));
+
+    // Check their sizes
+    ASSERT_EQ(1, rbtree.getNodeCount());
+    ASSERT_EQ(count1, tree2.getNodeCount());
+
+    // And content
+    std::ostringstream out;
+    rbtree.dumpTree(out);
+    ASSERT_EQ(str2.str(), out.str());
+    out.str("");
+    tree2.dumpTree(out);
+    ASSERT_EQ(str1.str(), out.str());
+}
+
+}

Modified: branches/trac458/src/lib/datasrc/zone.h
==============================================================================
--- branches/trac458/src/lib/datasrc/zone.h (original)
+++ branches/trac458/src/lib/datasrc/zone.h Tue Jan  4 09:09:01 2011
@@ -98,6 +98,16 @@
         {}
         const Result code;
         const isc::dns::ConstRRsetPtr rrset;
+    };
+
+    /// Find options.
+    ///
+    /// The option values are used as a parameter for \c find().
+    /// These are values of a bitmask type.  Bitwise operations can be
+    /// performed on these values to express compound options.
+    enum FindOptions {
+        FIND_DEFAULT = 0,       ///< The default options
+        FIND_GLUE_OK = 1        ///< Allow search under a zone cut
     };
 
     ///
@@ -150,6 +160,17 @@
     /// - If the search name matches a delegation point of DNAME, it returns
     ///   the code of \c DNAME and that DNAME RR.
     ///
+    /// The \c options parameter specifies customized behavior of the search.
+    /// Their semantics is as follows:
+    /// - \c GLUE_OK Allow search under a zone cut.  By default the search
+    ///   will stop once it encounters a zone cut.  If this option is specified
+    ///   it remembers information about the highest zone cut and continues
+    ///   the search until it finds an exact match for the given name or it
+    ///   detects there is no exact match.  If an exact match is found,
+    ///   RRsets for that name are searched just like the normal case;
+    ///   otherwise, if the search has encountered a zone cut, \c DELEGATION
+    ///   with the information of the highest zone cut will be returned.
+    ///
     /// A derived version of this method may involve internal resource
     /// allocation, especially for constructing the resulting RRset, and may
     /// throw an exception if it fails.
@@ -162,9 +183,12 @@
     ///
     /// \param name The domain name to be searched for.
     /// \param type The RR type to be searched for.
+    /// \param options The search options.
     /// \return A \c FindResult object enclosing the search result (see above).
     virtual FindResult find(const isc::dns::Name& name,
-                            const isc::dns::RRType& type) const = 0;
+                            const isc::dns::RRType& type,
+                            const FindOptions options
+                            = FIND_DEFAULT) const = 0;
     //@}
 };
 
@@ -177,4 +201,8 @@
 }
 }
 
-#endif
+#endif  // __ZONE_H
+
+// Local Variables:
+// mode: c++
+// End:

Modified: branches/trac458/src/lib/datasrc/zonetable.h
==============================================================================
--- branches/trac458/src/lib/datasrc/zonetable.h (original)
+++ branches/trac458/src/lib/datasrc/zonetable.h Tue Jan  4 09:09:01 2011
@@ -25,7 +25,7 @@
 namespace dns {
 class Name;
 class RRClass;
-};
+}
 
 namespace datasrc {
 

Modified: branches/trac458/src/lib/python/isc/config/module_spec.py
==============================================================================
--- branches/trac458/src/lib/python/isc/config/module_spec.py (original)
+++ branches/trac458/src/lib/python/isc/config/module_spec.py Tue Jan  4 09:09:01 2011
@@ -339,13 +339,14 @@
 
     # check if there are items in our data that are not in the
     # specification
-    for item_name in data:
-        found = False
-        for spec_item in module_spec:
-            if spec_item["item_name"] == item_name:
-                found = True
-        if not found:
-            if errors != None:
-                errors.append("unknown item " + item_name)
-            validated = False
+    if data is not None:
+        for item_name in data:
+            found = False
+            for spec_item in module_spec:
+                if spec_item["item_name"] == item_name:
+                    found = True
+            if not found:
+                if errors != None:
+                    errors.append("unknown item " + item_name)
+                validated = False
     return validated

Modified: branches/trac458/src/lib/python/isc/config/tests/module_spec_test.py
==============================================================================
--- branches/trac458/src/lib/python/isc/config/tests/module_spec_test.py (original)
+++ branches/trac458/src/lib/python/isc/config/tests/module_spec_test.py Tue Jan  4 09:09:01 2011
@@ -109,6 +109,9 @@
         return dd.validate_command(cmd_name, params)
 
     def test_command_validation(self):
+        # tests for a command that doesn't take an argument
+        self.assertEqual(True, self.read_spec_file("spec2.spec").validate_command("shutdown", None));
+        self.assertEqual(False, self.read_spec_file("spec2.spec").validate_command("shutdown", '{"val": 1}'));
         self.assertEqual(True, self.validate_command_params("spec27.spec", "data22_1.data", 'cmd1'))
         self.assertEqual(False, self.validate_command_params("spec27.spec", "data22_2.data",'cmd1'))
         self.assertEqual(False, self.validate_command_params("spec27.spec", "data22_3.data", 'cmd1'))
@@ -321,6 +324,7 @@
                }]
 
         errors = []
+        self.assertEqual(True, isc.config.module_spec._validate_spec_list(spec, True, None, None))
         self.assertEqual(False, isc.config.module_spec._validate_spec_list(spec, True, { 'does_not_exist': 1 }, None))
         self.assertEqual(False, isc.config.module_spec._validate_spec_list(spec, True, { 'does_not_exist': 1 }, errors))
         self.assertEqual(['unknown item does_not_exist'], errors)

Modified: branches/trac458/src/lib/testutils/Makefile.am
==============================================================================
--- branches/trac458/src/lib/testutils/Makefile.am (original)
+++ branches/trac458/src/lib/testutils/Makefile.am Tue Jan  4 09:09:01 2011
@@ -1,5 +1,13 @@
-SUBDIRS = testdata
+SUBDIRS = . testdata
 
-EXTRA_DIST = srv_test.h
-EXTRA_DIST += srv_unittest.h
-EXTRA_DIST += mockups.h
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CXXFLAGS=$(B10_CXXFLAGS)
+
+if HAVE_GTEST
+lib_LTLIBRARIES = libtestutils.la
+
+libtestutils_la_SOURCES = srv_test.h srv_test.cc
+libtestutils_la_SOURCES += mockups.h
+libtestutils_la_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+endif

Modified: branches/trac458/src/lib/testutils/README
==============================================================================
--- branches/trac458/src/lib/testutils/README (original)
+++ branches/trac458/src/lib/testutils/README Tue Jan  4 09:09:01 2011
@@ -1,4 +1,2 @@
 Here is some code used by more than one test. No code is used for bind10
 itself, only for testing.
-
-As it contains headers only currently, it does not compile here.

Modified: branches/trac458/src/lib/testutils/srv_test.h
==============================================================================
--- branches/trac458/src/lib/testutils/srv_test.h (original)
+++ branches/trac458/src/lib/testutils/srv_test.h Tue Jan  4 09:09:01 2011
@@ -12,10 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id: auth_srv_unittest.cc 3310 2010-10-21 23:10:24Z each $
-
-#include <config.h>
-
 #include <gtest/gtest.h>
 
 #include <dns/buffer.h>
@@ -27,139 +23,99 @@
 #include <dns/rrclass.h>
 #include <dns/rrtype.h>
 
-#include <cc/data.h>
-#include <cc/session.h>
-
-#include <xfr/xfrout_client.h>
-
-#include <auth/auth_srv.h>
-#include <asiolink/asiolink.h>
-
-#include <dns/tests/unittest_util.h>
 #include "mockups.h"
 
-using namespace std;
-using namespace isc::cc;
-using namespace isc::dns;
-using namespace isc::data;
-using namespace isc::xfr;
-using namespace asiolink;
-using isc::UnitTestUtil;
+namespace asiolink {
+class IOSocket;
+class IOMessage;
+class IOEndpoint;
+}
 
-namespace {
-const char* const DEFAULT_REMOTE_ADDRESS = "192.0.2.1";
+namespace isc {
+namespace testutils {
+extern const char* const DEFAULT_REMOTE_ADDRESS;
+
+// These are flags to indicate whether the corresponding flag bit of the
+// DNS header is to be set in the test cases.  (The flag values
+// is irrelevant to their wire-format values)
+extern const unsigned int QR_FLAG;
+extern const unsigned int AA_FLAG;
+extern const unsigned int TC_FLAG;
+extern const unsigned int RD_FLAG;
+extern const unsigned int RA_FLAG;
+extern const unsigned int AD_FLAG;
+extern const unsigned int CD_FLAG;
+
+void
+headerCheck(const isc::dns::Message& message, const isc::dns::qid_t qid,
+            const isc::dns::Rcode& rcode,
+            const uint16_t opcodeval, const unsigned int flags,
+            const unsigned int qdcount,
+            const unsigned int ancount, const unsigned int nscount,
+            const unsigned int arcount);
 
 // The base class for Auth and Recurse test case
 class SrvTestBase : public ::testing::Test {
 protected:
-    SrvTestBase() : request_message(Message::RENDER),
-                    parse_message(new Message(Message::PARSE)),
-                    default_qid(0x1035), opcode(Opcode(Opcode::QUERY())),
-                    qname("www.example.com"), qclass(RRClass::IN()),
-                    qtype(RRType::A()), io_sock(NULL), 
-                    io_message(NULL), endpoint(NULL),
-                    request_obuffer(0), request_renderer(request_obuffer),
-                    response_obuffer(new OutputBuffer(0))
-    {}
-    ~SrvTestBase() {
-        delete io_message;
-        delete endpoint;
-    }
+    SrvTestBase();
+    virtual ~SrvTestBase();
+
+    /// Let the server process a DNS message.
+    ///
+    /// The derived class implementation is expected to pass \c io_message,
+    /// \c parse_message, \c response_obuffer, and \c dnsserv to the server
+    /// implementation it is testing.
+    virtual void processMessage() = 0;
+
+    /// The following methods implement server independent test logic using
+    /// the template method pattern.  Each test calls \c processMessage()
+    /// to delegate the server-dependent behavior to the actual implementation
+    /// classes.
+    void unsupportedRequest();
+    void multiQuestion();
+    void shortMessage();
+    void response();
+    void shortQuestion();
+    void shortAnswer();
+    void ednsBadVers();
+    void axfrOverUDP();
+
+    /// Create DNS packet data from a file.
+    ///
+    /// It constructs wire-format DNS packet data from \c datafile in the
+    /// form of \c IOMessage in \c io_message.
+    /// The existing content of \c io_message, if any, will be deleted.
+    void createDataFromFile(const char* const datafile,
+                            int protocol = IPPROTO_UDP);
+
+    ///  Create DNS packet data from a message.
+    ///
+    /// It constructs wire-format DNS packet data from \c message in the
+    /// form of \c IOMessage in \c io_message.
+    /// The existing content of \c io_message, if any, will be deleted.
+    void createRequestPacket(isc::dns::Message& message,
+                             const int protocol = IPPROTO_UDP);
+
     MockSession notify_session;
     MockServer dnsserv;
-    Message request_message;
-    MessagePtr parse_message;
-    const qid_t default_qid;
-    const Opcode opcode;
-    const Name qname;
-    const RRClass qclass;
-    const RRType qtype;
-    IOSocket* io_sock;
-    IOMessage* io_message;
-    const IOEndpoint* endpoint;
-    OutputBuffer request_obuffer;
-    MessageRenderer request_renderer;
-    OutputBufferPtr response_obuffer;
-    vector<uint8_t> data;
+    isc::dns::Message request_message;
+    isc::dns::MessagePtr parse_message;
+    const isc::dns::qid_t default_qid;
+    const isc::dns::Opcode opcode;
+    const isc::dns::Name qname;
+    const isc::dns::RRClass qclass;
+    const isc::dns::RRType qtype;
+    asiolink::IOSocket* io_sock;
+    asiolink::IOMessage* io_message;
+    const asiolink::IOEndpoint* endpoint;
+    isc::dns::OutputBuffer request_obuffer;
+    isc::dns::MessageRenderer request_renderer;
+    isc::dns::OutputBufferPtr response_obuffer;
+    std::vector<uint8_t> data;
+};
+} // end of namespace testutils
+} // end of namespace isc
 
-    void createDataFromFile(const char* const datafile, int protocol);
-    void createRequestPacket(Message& message, const int protocol);
-};
-
-void
-SrvTestBase::createDataFromFile(const char* const datafile,
-                                const int protocol = IPPROTO_UDP)
-{
-    delete io_message;
-    data.clear();
-
-    delete endpoint;
-
-    endpoint = IOEndpoint::create(protocol,
-                                  IOAddress(DEFAULT_REMOTE_ADDRESS), 5300);
-    UnitTestUtil::readWireData(datafile, data);
-    io_sock = (protocol == IPPROTO_UDP) ? &IOSocket::getDummyUDPSocket() :
-        &IOSocket::getDummyTCPSocket();
-    io_message = new IOMessage(&data[0], data.size(), *io_sock, *endpoint);
-}
-
-void
-SrvTestBase::createRequestPacket(Message& message,
-                                 const int protocol = IPPROTO_UDP)
-{
-    message.toWire(request_renderer);
-
-    delete io_message;
-
-    endpoint = IOEndpoint::create(protocol,
-                                  IOAddress(DEFAULT_REMOTE_ADDRESS), 5300);
-    io_sock = (protocol == IPPROTO_UDP) ? &IOSocket::getDummyUDPSocket() :
-        &IOSocket::getDummyTCPSocket();
-    io_message = new IOMessage(request_renderer.getData(),
-                               request_renderer.getLength(),
-                               *io_sock, *endpoint);
-}
-
-// These are flags to indicate whether the corresponding flag bit of the
-// DNS header is to be set in the test cases.  (Note that the flag values
-// is irrelevant to their wire-format values)
-const unsigned int QR_FLAG = 0x1;
-const unsigned int AA_FLAG = 0x2;
-const unsigned int TC_FLAG = 0x4;
-const unsigned int RD_FLAG = 0x8;
-const unsigned int RA_FLAG = 0x10;
-const unsigned int AD_FLAG = 0x20;
-const unsigned int CD_FLAG = 0x40;
-
-void
-headerCheck(const Message& message, const qid_t qid, const Rcode& rcode,
-            const uint16_t opcodeval, const unsigned int flags,
-            const unsigned int qdcount,
-            const unsigned int ancount, const unsigned int nscount,
-            const unsigned int arcount)
-{
-    EXPECT_EQ(qid, message.getQid());
-    EXPECT_EQ(rcode, message.getRcode());
-    EXPECT_EQ(opcodeval, message.getOpcode().getCode());
-    EXPECT_EQ((flags & QR_FLAG) != 0,
-              message.getHeaderFlag(Message::HEADERFLAG_QR));
-    EXPECT_EQ((flags & AA_FLAG) != 0,
-              message.getHeaderFlag(Message::HEADERFLAG_AA));
-    EXPECT_EQ((flags & TC_FLAG) != 0,
-              message.getHeaderFlag(Message::HEADERFLAG_TC));
-    EXPECT_EQ((flags & RA_FLAG) != 0,
-              message.getHeaderFlag(Message::HEADERFLAG_RA));
-    EXPECT_EQ((flags & RD_FLAG) != 0,
-              message.getHeaderFlag(Message::HEADERFLAG_RD));
-    EXPECT_EQ((flags & AD_FLAG) != 0,
-              message.getHeaderFlag(Message::HEADERFLAG_AD));
-    EXPECT_EQ((flags & CD_FLAG) != 0,
-              message.getHeaderFlag(Message::HEADERFLAG_CD));
-
-    EXPECT_EQ(qdcount, message.getRRCount(Message::SECTION_QUESTION));
-    EXPECT_EQ(ancount, message.getRRCount(Message::SECTION_ANSWER));
-    EXPECT_EQ(nscount, message.getRRCount(Message::SECTION_AUTHORITY));
-    EXPECT_EQ(arcount, message.getRRCount(Message::SECTION_ADDITIONAL));
-}
-
-}
+// Local Variables: 
+// mode: c++
+// End: 

Modified: branches/trac458/src/lib/testutils/testdata/Makefile.am
==============================================================================
--- branches/trac458/src/lib/testutils/testdata/Makefile.am (original)
+++ branches/trac458/src/lib/testutils/testdata/Makefile.am Tue Jan  4 09:09:01 2011
@@ -4,6 +4,7 @@
 BUILT_SOURCES += iqueryresponse_fromWire.wire multiquestion_fromWire.wire
 BUILT_SOURCES += queryBadEDNS_fromWire.wire shortanswer_fromWire.wire
 BUILT_SOURCES += simplequery_fromWire.wire simpleresponse_fromWire.wire
+BUILT_SOURCES += iquery_fromWire.wire iquery_response_fromWire.wire
 
 # NOTE: keep this in sync with real file listing
 # so is included in tarball
@@ -18,6 +19,8 @@
 EXTRA_DIST += shortresponse_fromWire
 EXTRA_DIST += simplequery_fromWire.spec
 EXTRA_DIST += simpleresponse_fromWire.spec
+EXTRA_DIST += iquery_fromWire.spec iquery_response_fromWire.spec
+EXTRA_DIST += example.com.zone example.net.zone example.org.zone example.zone
 
 EXTRA_DIST += example.com
 EXTRA_DIST += example.sqlite3




More information about the bind10-changes mailing list