Browse Source

Merge branch 'master' into trac1286

Jeremy C. Reed 13 years ago
parent
commit
c38fc5257e

+ 4 - 0
ChangeLog

@@ -1,3 +1,7 @@
+297.	[func]		dvv
+	Implement the SPF rrtype according to RFC4408.
+	(Trac #1140, git 146934075349f94ee27f23bf9ff01711b94e369e)
+
 296.	[build]		jreed
 296.	[build]		jreed
 	Do not install the unittest libraries. At this time, they
 	Do not install the unittest libraries. At this time, they
 	are not useful without source tree (and they may or may
 	are not useful without source tree (and they may or may

+ 1 - 0
src/cppcheck-suppress.lst

@@ -8,3 +8,4 @@ unreadVariable:src/lib/dns/rdata/template.cc:61
 selfAssignment:src/lib/dns/tests/name_unittest.cc:293
 selfAssignment:src/lib/dns/tests/name_unittest.cc:293
 selfAssignment:src/lib/dns/tests/rdata_unittest.cc:228
 selfAssignment:src/lib/dns/tests/rdata_unittest.cc:228
 selfAssignment:src/lib/dns/tests/tsigkey_unittest.cc:137
 selfAssignment:src/lib/dns/tests/tsigkey_unittest.cc:137
+selfAssignment:src/lib/dns/tests/rdata_txt_like_unittest.cc:222

+ 1 - 1
src/lib/datasrc/memory_datasrc.cc

@@ -830,7 +830,7 @@ checkConfigElementString(ConstElementPtr config, const std::string& name,
     if (!config->contains(name)) {
     if (!config->contains(name)) {
         addError(errors,
         addError(errors,
                  "Config for memory backend does not contain a '"
                  "Config for memory backend does not contain a '"
-                 "type"
+                 +name+
                  "' value");
                  "' value");
         return false;
         return false;
     } else if (!config->get(name) ||
     } else if (!config->get(name) ||

+ 54 - 2
src/lib/dns/rdata/generic/detail/txt_like.h

@@ -23,8 +23,24 @@
 using namespace std;
 using namespace std;
 using namespace isc::util;
 using namespace isc::util;
 
 
+/// \brief \c rdata::TXTLikeImpl class represents the TXT-like RDATA for TXT
+/// and SPF types.
+///
+/// This class implements the basic interfaces inherited by the TXT and SPF
+/// classes from the abstract \c rdata::Rdata class, and provides trivial
+/// accessors to TXT-like RDATA.
 template<class Type, uint16_t typeCode>class TXTLikeImpl {
 template<class Type, uint16_t typeCode>class TXTLikeImpl {
 public:
 public:
+    /// \brief Constructor from wire-format data.
+    ///
+    /// \param buffer A buffer storing the wire format data.
+    /// \param rdata_len The length of the RDATA in bytes, normally expected
+    /// to be the value of the RDLENGTH field of the corresponding RR.
+    ///
+    /// <b>Exceptions</b>
+    ///
+    /// \c InvalidRdataLength is thrown if rdata_len exceeds the maximum.
+    /// \c DNSMessageFORMERR is thrown if the RR is misformed.
     TXTLikeImpl(InputBuffer& buffer, size_t rdata_len) {
     TXTLikeImpl(InputBuffer& buffer, size_t rdata_len) {
         if (rdata_len > MAX_RDLENGTH) {
         if (rdata_len > MAX_RDLENGTH) {
             isc_throw(InvalidRdataLength, "RDLENGTH too large: " << rdata_len);
             isc_throw(InvalidRdataLength, "RDLENGTH too large: " << rdata_len);
@@ -52,6 +68,14 @@ public:
         } while (rdata_len > 0);
         } while (rdata_len > 0);
     }
     }
 
 
+    /// \brief Constructor from string.
+    ///
+    /// <b>Exceptions</b>
+    ///
+    /// \c CharStringTooLong is thrown if the parameter string length exceeds
+    /// maximum.
+    /// \c InvalidRdataText is thrown if the method cannot process the
+    /// parameter data.
     explicit TXTLikeImpl(const std::string& txtstr) {
     explicit TXTLikeImpl(const std::string& txtstr) {
         // TBD: this is a simple, incomplete implementation that only supports
         // TBD: this is a simple, incomplete implementation that only supports
         // a single character-string.
         // a single character-string.
@@ -86,10 +110,17 @@ public:
         string_list_.push_back(data);
         string_list_.push_back(data);
     }
     }
 
 
+    /// \brief The copy constructor.
+    ///
+    /// Trivial for now, we could've used the default one.
     TXTLikeImpl(const TXTLikeImpl& other) :
     TXTLikeImpl(const TXTLikeImpl& other) :
         string_list_(other.string_list_)
         string_list_(other.string_list_)
     {}
     {}
 
 
+    /// \brief Render the TXT-like data in the wire format to an OutputBuffer
+    /// object.
+    ///
+    /// \param buffer An output buffer to store the wire data.
     void
     void
     toWire(OutputBuffer& buffer) const {
     toWire(OutputBuffer& buffer) const {
         for (vector<vector<uint8_t> >::const_iterator it =
         for (vector<vector<uint8_t> >::const_iterator it =
@@ -101,6 +132,11 @@ public:
         }
         }
     }
     }
 
 
+    /// \brief Render the TXT-like data in the wire format to an
+    /// AbstractMessageRenderer object.
+    ///
+    /// \param buffer An output AbstractMessageRenderer to send the wire data
+    /// to.
     void
     void
     toWire(AbstractMessageRenderer& renderer) const {
     toWire(AbstractMessageRenderer& renderer) const {
         for (vector<vector<uint8_t> >::const_iterator it =
         for (vector<vector<uint8_t> >::const_iterator it =
@@ -112,6 +148,9 @@ public:
         }
         }
     }
     }
 
 
+    /// \brief Convert the TXT-like data to a string.
+    ///
+    /// \return A \c string object that represents the TXT-like data.
     string
     string
     toText() const {
     toText() const {
         string s;
         string s;
@@ -134,20 +173,33 @@ public:
         return (s);
         return (s);
     }
     }
 
 
+    /// \brief Compare two instances of TXT-like RDATA.
+    ///
+    /// It is up to the caller to make sure that \c other is an object of the
+    /// same \c TXTLikeImpl class.
+    ///
+    /// \param other the right-hand operand to compare against.
+    /// \return < 0 if \c this would be sorted before \c other.
+    /// \return 0 if \c this is identical to \c other in terms of sorting
+    /// order.
+    /// \return > 0 if \c this would be sorted after \c other.
     int
     int
     compare(const TXTLikeImpl& other) const {
     compare(const TXTLikeImpl& other) const {
         // This implementation is not efficient.  Revisit this (TBD).
         // This implementation is not efficient.  Revisit this (TBD).
         OutputBuffer this_buffer(0);
         OutputBuffer this_buffer(0);
         toWire(this_buffer);
         toWire(this_buffer);
+        uint8_t const* const this_data = (uint8_t const*)this_buffer.getData();
         size_t this_len = this_buffer.getLength();
         size_t this_len = this_buffer.getLength();
 
 
         OutputBuffer other_buffer(0);
         OutputBuffer other_buffer(0);
         other.toWire(other_buffer);
         other.toWire(other_buffer);
+        uint8_t const* const other_data
+                                      = (uint8_t const*)other_buffer.getData();
         const size_t other_len = other_buffer.getLength();
         const size_t other_len = other_buffer.getLength();
 
 
         const size_t cmplen = min(this_len, other_len);
         const size_t cmplen = min(this_len, other_len);
-        const int cmp = memcmp(this_buffer.getData(), other_buffer.getData(),
+        const int cmp = memcmp(this_data, other_data, cmplen);
-                               cmplen);
+
         if (cmp != 0) {
         if (cmp != 0) {
             return (cmp);
             return (cmp);
         } else {
         } else {

+ 44 - 0
src/lib/dns/rdata/generic/spf_99.cc

@@ -30,8 +30,17 @@ using namespace isc::util;
 // BEGIN_ISC_NAMESPACE
 // BEGIN_ISC_NAMESPACE
 // BEGIN_RDATA_NAMESPACE
 // BEGIN_RDATA_NAMESPACE
 
 
+/// This class implements the basic interfaces inherited from the abstract
+/// \c rdata::Rdata class. The semantics of the class is provided by
+/// a copy of instantiated TXTLikeImpl class common to both TXT and SPF.
+
 #include <dns/rdata/generic/detail/txt_like.h>
 #include <dns/rdata/generic/detail/txt_like.h>
 
 
+/// \brief The assignment operator
+///
+/// It internally allocates a resource, and if it fails a corresponding
+/// standard exception will be thrown.
+/// This method never throws an exception otherwise.
 SPF&
 SPF&
 SPF::operator=(const SPF& source) {
 SPF::operator=(const SPF& source) {
     if (impl_ == source.impl_) {
     if (impl_ == source.impl_) {
@@ -45,37 +54,72 @@ SPF::operator=(const SPF& source) {
     return (*this);
     return (*this);
 }
 }
 
 
+/// \brief The destructor
 SPF::~SPF() {
 SPF::~SPF() {
     delete impl_;
     delete impl_;
 }
 }
 
 
+/// \brief Constructor from wire-format data.
+///
+/// It internally allocates a resource, and if it fails a corresponding
+/// standard exception will be thrown.
 SPF::SPF(InputBuffer& buffer, size_t rdata_len) :
 SPF::SPF(InputBuffer& buffer, size_t rdata_len) :
     impl_(new SPFImpl(buffer, rdata_len))
     impl_(new SPFImpl(buffer, rdata_len))
 {}
 {}
 
 
+/// \brief Constructor from string.
+///
+/// It internally allocates a resource, and if it fails a corresponding
+/// standard exception will be thrown.
 SPF::SPF(const std::string& txtstr) :
 SPF::SPF(const std::string& txtstr) :
     impl_(new SPFImpl(txtstr))
     impl_(new SPFImpl(txtstr))
 {}
 {}
 
 
+/// \brief Copy constructor
+///
+/// It internally allocates a resource, and if it fails a corresponding
+/// standard exception will be thrown.
 SPF::SPF(const SPF& other) :
 SPF::SPF(const SPF& other) :
     Rdata(), impl_(new SPFImpl(*other.impl_))
     Rdata(), impl_(new SPFImpl(*other.impl_))
 {}
 {}
 
 
+/// \brief Render the \c SPF in the wire format to a OutputBuffer object
+///
+/// \return is the return of the corresponding implementation method.
 void
 void
 SPF::toWire(OutputBuffer& buffer) const {
 SPF::toWire(OutputBuffer& buffer) const {
     impl_->toWire(buffer);
     impl_->toWire(buffer);
 }
 }
 
 
+/// \brief Render the \c SPF in the wire format to an AbstractMessageRenderer
+/// object
+///
+/// \return is the return of the corresponding implementation method.
 void
 void
 SPF::toWire(AbstractMessageRenderer& renderer) const {
 SPF::toWire(AbstractMessageRenderer& renderer) const {
     impl_->toWire(renderer);
     impl_->toWire(renderer);
 }
 }
 
 
+/// \brief Convert the \c SPF to a string.
+///
+/// \return is the return of the corresponding implementation method.
 string
 string
 SPF::toText() const {
 SPF::toText() const {
     return (impl_->toText());
     return (impl_->toText());
 }
 }
 
 
+/// \brief Compare two instances of \c SPF RDATA.
+///
+/// This method compares \c this and the \c other \c SPF objects.
+///
+/// This method is expected to be used in a polymorphic way, and the
+/// parameter to compare against is therefore of the abstract \c Rdata class.
+/// However, comparing two \c Rdata objects of different RR types
+/// is meaningless, and \c other must point to a \c SPF object;
+/// otherwise, the standard \c bad_cast exception will be thrown.
+///
+/// \param other the right-hand operand to compare against.
+/// \return is the return of the corresponding implementation method.
 int
 int
 SPF::compare(const Rdata& other) const {
 SPF::compare(const Rdata& other) const {
     const SPF& other_txt = dynamic_cast<const SPF&>(other);
     const SPF& other_txt = dynamic_cast<const SPF&>(other);

+ 26 - 0
src/lib/dns/rdata/generic/spf_99.h

@@ -30,14 +30,40 @@
 
 
 template<class Type, uint16_t typeCode> class TXTLikeImpl;
 template<class Type, uint16_t typeCode> class TXTLikeImpl;
 
 
+/// \brief \c rdata::SPF class represents the SPF RDATA as defined %in
+/// RFC4408.
+///
+/// This class implements the basic interfaces inherited from the abstract
+/// \c rdata::Rdata class. The semantics of the class is provided by
+/// a copy of instantiated TXTLikeImpl class common to both TXT and SPF.
 class SPF : public Rdata {
 class SPF : public Rdata {
 public:
 public:
     // BEGIN_COMMON_MEMBERS
     // BEGIN_COMMON_MEMBERS
     // END_COMMON_MEMBERS
     // END_COMMON_MEMBERS
 
 
+    /// \brief Assignment operator.
+    ///
+    /// It internally allocates a resource, and if it fails a corresponding
+    /// standard exception will be thrown.
+    /// This operator never throws an exception otherwise.
+    ///
+    /// This operator provides the strong exception guarantee: When an
+    /// exception is thrown the content of the assignment target will be
+    /// intact.
     SPF& operator=(const SPF& source);
     SPF& operator=(const SPF& source);
+
+    /// \brief The destructor.
     ~SPF();
     ~SPF();
 
 
+    ///
+    /// Specialized methods
+    ///
+
+    /// \brief Return a reference to the data strings
+    ///
+    /// This method never throws an exception.
+    const std::vector<std::vector<uint8_t> >& getString() const;
+
 private:
 private:
     typedef TXTLikeImpl<SPF, 99> SPFImpl;
     typedef TXTLikeImpl<SPF, 99> SPFImpl;
     SPFImpl* impl_;
     SPFImpl* impl_;

+ 4 - 4
src/lib/dns/rdata/in_1/dhcid_49.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
 // purpose with or without fee is hereby granted, provided that the above
@@ -20,7 +20,7 @@
 #include <exceptions/exceptions.h>
 #include <exceptions/exceptions.h>
 
 
 #include <util/buffer.h>
 #include <util/buffer.h>
-#include <util/encode/hex.h>
+#include <util/encode/base64.h>
 #include <dns/exceptions.h>
 #include <dns/exceptions.h>
 #include <dns/messagerenderer.h>
 #include <dns/messagerenderer.h>
 #include <dns/rdata.h>
 #include <dns/rdata.h>
@@ -52,7 +52,7 @@ DHCID::DHCID(const string& dhcid_str) {
     stringbuf digestbuf;
     stringbuf digestbuf;
 
 
     iss >> &digestbuf;
     iss >> &digestbuf;
-    isc::util::encode::decodeHex(digestbuf.str(), digest_);
+    isc::util::encode::decodeBase64(digestbuf.str(), digest_);
 
 
     // RFC4701 states DNS software should consider the RDATA section to
     // RFC4701 states DNS software should consider the RDATA section to
     // be opaque, but there must be at least three bytes in the data:
     // be opaque, but there must be at least three bytes in the data:
@@ -112,7 +112,7 @@ DHCID::toWire(AbstractMessageRenderer& renderer) const {
 /// \return A string representation of \c DHCID.
 /// \return A string representation of \c DHCID.
 string
 string
 DHCID::toText() const {
 DHCID::toText() const {
-    return (isc::util::encode::encodeHex(digest_));
+    return (isc::util::encode::encodeBase64(digest_));
 }
 }
 
 
 /// \brief Compare two instances of \c DHCID RDATA.
 /// \brief Compare two instances of \c DHCID RDATA.

+ 1 - 1
src/lib/dns/rdata/in_1/dhcid_49.h

@@ -1,4 +1,4 @@
-// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
 // purpose with or without fee is hereby granted, provided that the above

+ 3 - 1
src/lib/dns/tests/Makefile.am

@@ -29,11 +29,13 @@ run_unittests_SOURCES += rdata_unittest.h rdata_unittest.cc
 run_unittests_SOURCES += rdatafields_unittest.cc
 run_unittests_SOURCES += rdatafields_unittest.cc
 run_unittests_SOURCES += rdata_in_a_unittest.cc rdata_in_aaaa_unittest.cc
 run_unittests_SOURCES += rdata_in_a_unittest.cc rdata_in_aaaa_unittest.cc
 run_unittests_SOURCES += rdata_ns_unittest.cc rdata_soa_unittest.cc
 run_unittests_SOURCES += rdata_ns_unittest.cc rdata_soa_unittest.cc
-run_unittests_SOURCES += rdata_txt_unittest.cc rdata_mx_unittest.cc
+run_unittests_SOURCES += rdata_txt_like_unittest.cc
+run_unittests_SOURCES += rdata_mx_unittest.cc
 run_unittests_SOURCES += rdata_ptr_unittest.cc rdata_cname_unittest.cc
 run_unittests_SOURCES += rdata_ptr_unittest.cc rdata_cname_unittest.cc
 run_unittests_SOURCES += rdata_dname_unittest.cc
 run_unittests_SOURCES += rdata_dname_unittest.cc
 run_unittests_SOURCES += rdata_afsdb_unittest.cc
 run_unittests_SOURCES += rdata_afsdb_unittest.cc
 run_unittests_SOURCES += rdata_opt_unittest.cc
 run_unittests_SOURCES += rdata_opt_unittest.cc
+run_unittests_SOURCES += rdata_dhcid_unittest.cc
 run_unittests_SOURCES += rdata_dnskey_unittest.cc
 run_unittests_SOURCES += rdata_dnskey_unittest.cc
 run_unittests_SOURCES += rdata_ds_like_unittest.cc
 run_unittests_SOURCES += rdata_ds_like_unittest.cc
 run_unittests_SOURCES += rdata_nsec_unittest.cc
 run_unittests_SOURCES += rdata_nsec_unittest.cc

+ 111 - 0
src/lib/dns/tests/rdata_dhcid_unittest.cc

@@ -0,0 +1,111 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <util/buffer.h>
+#include <dns/rdataclass.h>
+#include <util/encode/base64.h>
+
+#include <gtest/gtest.h>
+
+#include <dns/tests/unittest_util.h>
+#include <dns/tests/rdata_unittest.h>
+
+using isc::UnitTestUtil;
+using namespace std;
+using namespace isc::dns;
+using namespace isc::util;
+using namespace isc::util::encode;
+using namespace isc::dns::rdata;
+
+namespace {
+
+const string string_dhcid(
+                   "0LIg0LvQtdGB0YMg0YDQvtC00LjQu9Cw0YHRjCDRkdC70L7Rh9C60LA=");
+
+const in::DHCID rdata_dhcid(string_dhcid);
+
+class Rdata_DHCID_Test : public RdataTest {
+};
+
+TEST_F(Rdata_DHCID_Test, createFromString) {
+    const in::DHCID rdata_dhcid2(string_dhcid);
+    EXPECT_EQ(0, rdata_dhcid2.compare(rdata_dhcid));
+}
+
+TEST_F(Rdata_DHCID_Test, badBase64) {
+    EXPECT_THROW(const in::DHCID rdata_dhcid_bad("00"), isc::BadValue);
+}
+
+TEST_F(Rdata_DHCID_Test, badLength) {
+    EXPECT_THROW(const in::DHCID rdata_dhcid_bad("MDA="), InvalidRdataLength);
+}
+
+TEST_F(Rdata_DHCID_Test, copy) {
+    const in::DHCID rdata_dhcid2(rdata_dhcid);
+    EXPECT_EQ(0, rdata_dhcid.compare(rdata_dhcid2));
+}
+
+TEST_F(Rdata_DHCID_Test, createFromWire) {
+    EXPECT_EQ(0, rdata_dhcid.compare(
+                  *rdataFactoryFromFile(RRType("DHCID"), RRClass("IN"),
+                                        "rdata_dhcid_fromWire")));
+    // TBD: more tests
+}
+
+TEST_F(Rdata_DHCID_Test, toWireRenderer) {
+    rdata_dhcid.toWire(renderer);
+
+    vector<unsigned char> data;
+    UnitTestUtil::readWireData("rdata_dhcid_toWire", data);
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
+                        renderer.getLength(), &data[0], data.size());
+}
+
+TEST_F(Rdata_DHCID_Test, toWireBuffer) {
+    rdata_dhcid.toWire(obuffer);
+
+    vector<unsigned char> data;
+    UnitTestUtil::readWireData("rdata_dhcid_toWire", data);
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, obuffer.getData(),
+                        obuffer.getLength(), &data[0], data.size());
+}
+
+TEST_F(Rdata_DHCID_Test, toText) {
+    EXPECT_EQ(string_dhcid, rdata_dhcid.toText());
+}
+
+TEST_F(Rdata_DHCID_Test, getDHCIDDigest) {
+    const string string_dhcid1(encodeBase64(rdata_dhcid.getDigest()));
+
+    EXPECT_EQ(string_dhcid, string_dhcid1);
+}
+
+TEST_F(Rdata_DHCID_Test, compare) {
+    // trivial case: self equivalence
+    EXPECT_EQ(0, rdata_dhcid.compare(rdata_dhcid));
+
+    in::DHCID rdata_dhcid1("0YLQvtC/0L7Qu9GPINC00LLQsCDRgNGD0LHQu9GP");
+    in::DHCID rdata_dhcid2("0YLQvtC/0L7Qu9GPINGC0YDQuCDRgNGD0LHQu9GP");
+    in::DHCID rdata_dhcid3("0YLQvtC/0L7Qu9GPINGH0LXRgtGL0YDQtSDRgNGD0LHQu9GP");
+
+    EXPECT_LT(rdata_dhcid1.compare(rdata_dhcid2), 0);
+    EXPECT_GT(rdata_dhcid2.compare(rdata_dhcid1), 0);
+
+    EXPECT_LT(rdata_dhcid2.compare(rdata_dhcid3), 0);
+    EXPECT_GT(rdata_dhcid3.compare(rdata_dhcid2), 0);
+
+    // comparison attempt between incompatible RR types should be rejected
+    EXPECT_THROW(rdata_dhcid.compare(*rdata_nomatch), bad_cast); 
+}
+}

+ 261 - 0
src/lib/dns/tests/rdata_txt_like_unittest.cc

@@ -0,0 +1,261 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// This is the common code for TXT and SPF tests.
+
+#include <util/buffer.h>
+#include <dns/exceptions.h>
+#include <dns/rdataclass.h>
+#include <gtest/gtest.h>
+
+#include <dns/tests/unittest_util.h>
+#include <dns/tests/rdata_unittest.h>
+
+using isc::UnitTestUtil;
+using namespace std;
+using namespace isc::dns;
+using namespace isc::util;
+using namespace isc::dns::rdata;
+
+
+template<class T>
+class RRTYPE : public RRType {
+public:
+    RRTYPE();
+};
+
+template<> RRTYPE<generic::TXT>::RRTYPE() : RRType(RRType::TXT()) {}
+template<> RRTYPE<generic::SPF>::RRTYPE() : RRType(RRType::SPF()) {}
+
+namespace {
+const uint8_t wiredata_txt_like[] = {
+    sizeof("Test String") - 1,
+    'T', 'e', 's', 't', ' ', 'S', 't', 'r', 'i', 'n', 'g'
+};
+
+const uint8_t wiredata_nulltxt[] = { 0 };
+vector<uint8_t> wiredata_longesttxt(256, 'a');
+
+template<class TXT_LIKE>
+class Rdata_TXT_LIKE_Test : public RdataTest {
+protected:
+    Rdata_TXT_LIKE_Test() {
+        wiredata_longesttxt[0] = 255; // adjust length
+    }
+
+    static const TXT_LIKE rdata_txt_like;
+    static const TXT_LIKE rdata_txt_like_empty;
+    static const TXT_LIKE rdata_txt_like_quoted;
+};
+
+template<class TXT_LIKE>
+const TXT_LIKE Rdata_TXT_LIKE_Test<TXT_LIKE>::rdata_txt_like("Test String");
+
+template<class TXT_LIKE>
+const TXT_LIKE Rdata_TXT_LIKE_Test<TXT_LIKE>::rdata_txt_like_empty("");
+
+template<class TXT_LIKE>
+const TXT_LIKE Rdata_TXT_LIKE_Test<TXT_LIKE>::rdata_txt_like_quoted
+                                                          ("\"Test String\"");
+
+// The list of types we want to test.
+typedef testing::Types<generic::TXT, generic::SPF> Implementations;
+
+TYPED_TEST_CASE(Rdata_TXT_LIKE_Test, Implementations);
+
+TYPED_TEST(Rdata_TXT_LIKE_Test, createFromText) {
+    // normal case is covered in toWireBuffer.
+
+    // surrounding double-quotes shouldn't change the result.
+    EXPECT_EQ(0, this->rdata_txt_like.compare(this->rdata_txt_like_quoted));
+
+    // Null character-string.
+    this->obuffer.clear();
+    TypeParam(string("")).toWire(this->obuffer);
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
+                        this->obuffer.getData(),
+                        this->obuffer.getLength(),
+                        wiredata_nulltxt, sizeof(wiredata_nulltxt));
+
+    // Longest possible character-string.
+    this->obuffer.clear();
+    TypeParam(string(255, 'a')).toWire(this->obuffer);
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
+                        this->obuffer.getData(),
+                        this->obuffer.getLength(),
+                        &wiredata_longesttxt[0], wiredata_longesttxt.size());
+
+    // Too long text for a valid character-string.
+    EXPECT_THROW(TypeParam(string(256, 'a')), CharStringTooLong);
+
+    // The escape character makes the double quote a part of character-string,
+    // so this is invalid input and should be rejected.
+    EXPECT_THROW(TypeParam("\"Test String\\\""), InvalidRdataText);
+
+    // Terminating double-quote is provided, so this is valid, but in this
+    // version of implementation we reject escaped characters.
+    EXPECT_THROW(TypeParam("\"Test String\\\"\""), InvalidRdataText);
+}
+
+void
+makeLargest(vector<uint8_t>& data) {
+    uint8_t ch = 0;
+
+    // create 255 sets of character-strings, each of which has the longest
+    // length (255bytes string + 1-byte length field)
+    for (int i = 0; i < 255; ++i, ++ch) {
+        data.push_back(255);
+        data.insert(data.end(), 255, ch);
+    }
+    // the last character-string should be 255 bytes (including the one-byte
+    // length field) in length so that the total length should be in the range
+    // of 16-bit integers.
+    data.push_back(254);
+    data.insert(data.end(), 254, ch);
+
+    assert(data.size() == 65535);
+}
+
+TYPED_TEST(Rdata_TXT_LIKE_Test, createFromWire) {
+    EXPECT_EQ(0, this->rdata_txt_like.compare(
+                  *this->rdataFactoryFromFile(RRTYPE<TypeParam>(), RRClass("IN"),
+                                        "rdata_txt_fromWire1")));
+
+    // Empty character string
+    EXPECT_EQ(0, this->rdata_txt_like_empty.compare(
+                  *this->rdataFactoryFromFile(RRTYPE<TypeParam>(), RRClass("IN"),
+                                        "rdata_txt_fromWire2.wire")));
+
+    // Multiple character strings
+    this->obuffer.clear();
+    this->rdataFactoryFromFile(RRTYPE<TypeParam>(), RRClass("IN"),
+                         "rdata_txt_fromWire3.wire")->toWire(this->obuffer);
+    // the result should be 'wiredata_txt' repeated twice
+    vector<uint8_t> expected_data(wiredata_txt_like, wiredata_txt_like +
+                                  sizeof(wiredata_txt_like));
+    expected_data.insert(expected_data.end(), wiredata_txt_like,
+                         wiredata_txt_like + sizeof(wiredata_txt_like));
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
+                        this->obuffer.getData(),
+                        this->obuffer.getLength(),
+                        &expected_data[0], expected_data.size());
+
+    // Largest length of data.  There's nothing special, but should be
+    // constructed safely, and the content should be identical to the original
+    // data.
+    vector<uint8_t> largest_txt_like_data;
+    makeLargest(largest_txt_like_data);
+    InputBuffer ibuffer(&largest_txt_like_data[0],
+                        largest_txt_like_data.size());
+    TypeParam largest_txt_like(ibuffer, largest_txt_like_data.size());
+    this->obuffer.clear();
+    largest_txt_like.toWire(this->obuffer);
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
+                        this->obuffer.getData(),
+                        this->obuffer.getLength(),
+                        &largest_txt_like_data[0],
+                        largest_txt_like_data.size());
+
+    // rdlen parameter is out of range.  This is a rare event because we'd
+    // normally call the constructor via a polymorphic wrapper, where the
+    // length is validated.  But this should be checked explicitly.
+    InputBuffer ibuffer2(&largest_txt_like_data[0],
+                         largest_txt_like_data.size());
+    EXPECT_THROW(TypeParam(ibuffer2, 65536), InvalidRdataLength);
+
+    // RDATA is empty, which is invalid for TXT_LIKE.
+    EXPECT_THROW(this->rdataFactoryFromFile(RRTYPE<TypeParam>(), RRClass("IN"),
+                                      "rdata_txt_fromWire4.wire"),
+                 DNSMessageFORMERR);
+
+    // character-string length is too large, which could cause overrun.
+    EXPECT_THROW(this->rdataFactoryFromFile(RRTYPE<TypeParam>(), RRClass("IN"),
+                                      "rdata_txt_fromWire5.wire"),
+                 DNSMessageFORMERR);
+}
+
+TYPED_TEST(Rdata_TXT_LIKE_Test, toWireBuffer) {
+    this->rdata_txt_like.toWire(this->obuffer);
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
+                        this->obuffer.getData(),
+                        this->obuffer.getLength(),
+                        wiredata_txt_like, sizeof(wiredata_txt_like));
+}
+
+TYPED_TEST(Rdata_TXT_LIKE_Test, toWireRenderer) {
+    this->rdata_txt_like.toWire(this->renderer);
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
+                        this->renderer.getData(),
+                        this->renderer.getLength(),
+                        wiredata_txt_like, sizeof(wiredata_txt_like));
+}
+
+TYPED_TEST(Rdata_TXT_LIKE_Test, toText) {
+    EXPECT_EQ("\"Test String\"", this->rdata_txt_like.toText());
+}
+
+TYPED_TEST(Rdata_TXT_LIKE_Test, assignment) {
+    TypeParam rdata1("assignment1");
+    TypeParam rdata2("assignment2");
+    rdata1 = rdata2;
+    EXPECT_EQ(0, rdata2.compare(rdata1));
+
+    // Check if the copied data is valid even after the original is deleted
+    TypeParam* rdata3 = new TypeParam(rdata1);
+    TypeParam rdata4("assignment3");
+    rdata4 = *rdata3;
+    delete rdata3;
+    EXPECT_EQ(0, rdata4.compare(rdata1));
+
+    // Self assignment
+    rdata2 = rdata2;
+    EXPECT_EQ(0, rdata2.compare(rdata1));
+}
+
+TYPED_TEST(Rdata_TXT_LIKE_Test, compare) {
+    string const txt1("aaaaaaaa");
+    string const txt2("aaaaaaaaaa");
+    string const txt3("bbbbbbbb");
+    string const txt4(129, 'a');
+    string const txt5(128, 'b');
+
+    EXPECT_EQ(TypeParam(txt1).compare(TypeParam(txt1)), 0);
+
+    EXPECT_LT(TypeParam("").compare(TypeParam(txt1)), 0);
+    EXPECT_GT(TypeParam(txt1).compare(TypeParam("")), 0);
+
+    EXPECT_LT(TypeParam(txt1).compare(TypeParam(txt2)), 0);
+    EXPECT_GT(TypeParam(txt2).compare(TypeParam(txt1)), 0);
+
+    EXPECT_LT(TypeParam(txt1).compare(TypeParam(txt3)), 0);
+    EXPECT_GT(TypeParam(txt3).compare(TypeParam(txt1)), 0);
+
+    // we're comparing the data raw, starting at the length octet, so a shorter
+    // string sorts before a longer one no matter the lexicopraphical order
+    EXPECT_LT(TypeParam(txt3).compare(TypeParam(txt2)), 0);
+    EXPECT_GT(TypeParam(txt2).compare(TypeParam(txt3)), 0);
+
+    // to make sure the length octet compares unsigned
+    EXPECT_LT(TypeParam(txt1).compare(TypeParam(txt4)), 0);
+    EXPECT_GT(TypeParam(txt4).compare(TypeParam(txt1)), 0);
+
+    EXPECT_LT(TypeParam(txt5).compare(TypeParam(txt4)), 0);
+    EXPECT_GT(TypeParam(txt4).compare(TypeParam(txt5)), 0);
+
+    // comparison attempt between incompatible RR types should be rejected
+    EXPECT_THROW(TypeParam(txt1).compare(*this->rdata_nomatch),
+                 bad_cast);
+}
+
+}

+ 0 - 166
src/lib/dns/tests/rdata_txt_unittest.cc

@@ -1,166 +0,0 @@
-// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <util/buffer.h>
-#include <dns/exceptions.h>
-#include <dns/messagerenderer.h>
-#include <dns/rdata.h>
-#include <dns/rdataclass.h>
-#include <dns/rrclass.h>
-#include <dns/rrtype.h>
-
-#include <gtest/gtest.h>
-
-#include <dns/tests/unittest_util.h>
-#include <dns/tests/rdata_unittest.h>
-
-using isc::UnitTestUtil;
-using namespace std;
-using namespace isc::dns;
-using namespace isc::util;
-using namespace isc::dns::rdata;
-
-namespace {
-const generic::TXT rdata_txt("Test String");
-const generic::TXT rdata_txt_empty("");
-const generic::TXT rdata_txt_quoated("\"Test String\"");
-const uint8_t wiredata_txt[] = {
-    sizeof("Test String") - 1,
-    'T', 'e', 's', 't', ' ', 'S', 't', 'r', 'i', 'n', 'g'
-};
-const uint8_t wiredata_nulltxt[] = { 0 };
-vector<uint8_t> wiredata_longesttxt(256, 'a');
-
-class Rdata_TXT_Test : public RdataTest {
-protected:
-    Rdata_TXT_Test() {
-        wiredata_longesttxt[0] = 255; // adjust length
-    }
-};
-
-TEST_F(Rdata_TXT_Test, createFromText) {
-    // normal case is covered in toWireBuffer.
-
-    // surrounding double-quotes shouldn't change the result.
-    EXPECT_EQ(0, rdata_txt.compare(rdata_txt_quoated));
-
-    // Null character-string.
-    obuffer.clear();
-    generic::TXT(string("")).toWire(obuffer);
-    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
-                        obuffer.getData(), obuffer.getLength(),
-                        wiredata_nulltxt, sizeof(wiredata_nulltxt));
-
-    // Longest possible character-string.
-    obuffer.clear();
-    generic::TXT(string(255, 'a')).toWire(obuffer);
-    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
-                        obuffer.getData(), obuffer.getLength(),
-                        &wiredata_longesttxt[0], wiredata_longesttxt.size());
-
-    // Too long text for a valid character-string.
-    EXPECT_THROW(generic::TXT(string(256, 'a')), CharStringTooLong);
-
-    // The escape character makes the double quote a part of character-string,
-    // so this is invalid input and should be rejected.
-    EXPECT_THROW(generic::TXT("\"Test String\\\""), InvalidRdataText);
-
-    // Terminating double-quote is provided, so this is valid, but in this
-    // version of implementation we reject escaped characters.
-    EXPECT_THROW(generic::TXT("\"Test String\\\"\""), InvalidRdataText);
-}
-
-void
-makeLargest(vector<uint8_t>& data) {
-    uint8_t ch = 0;
-
-    // create 255 sets of character-strings, each of which has the longest
-    // length (255bytes string + 1-byte length field)
-    for (int i = 0; i < 255; ++i, ++ch) {
-        data.push_back(255);
-        data.insert(data.end(), 255, ch);
-    }
-    // the last character-string should be 255 bytes (including the one-byte
-    // length field) in length so that the total length should be in the range
-    // of 16-bit integers.
-    data.push_back(254);
-    data.insert(data.end(), 254, ch);
-
-    assert(data.size() == 65535);
-}
-
-TEST_F(Rdata_TXT_Test, createFromWire) {
-    EXPECT_EQ(0, rdata_txt.compare(
-                  *rdataFactoryFromFile(RRType("TXT"), RRClass("IN"),
-                                        "rdata_txt_fromWire1")));
-
-    // Empty character string
-    EXPECT_EQ(0, rdata_txt_empty.compare(
-                  *rdataFactoryFromFile(RRType("TXT"), RRClass("IN"),
-                                        "rdata_txt_fromWire2.wire")));
-
-    // Multiple character strings
-    obuffer.clear();
-    rdataFactoryFromFile(RRType("TXT"), RRClass("IN"),
-                         "rdata_txt_fromWire3.wire")->toWire(obuffer);
-    // the result should be 'wiredata_txt' repeated twice
-    vector<uint8_t> expected_data(wiredata_txt, wiredata_txt +
-                                  sizeof(wiredata_txt));
-    expected_data.insert(expected_data.end(), wiredata_txt,
-                         wiredata_txt + sizeof(wiredata_txt));
-    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
-                        obuffer.getData(), obuffer.getLength(),
-                        &expected_data[0], expected_data.size());
-
-    // Largest length of data.  There's nothing special, but should be
-    // constructed safely, and the content should be identical to the original
-    // data.
-    vector<uint8_t> largest_txt_data;
-    makeLargest(largest_txt_data);
-    InputBuffer ibuffer(&largest_txt_data[0], largest_txt_data.size());
-    generic::TXT largest_txt(ibuffer, largest_txt_data.size());
-    obuffer.clear();
-    largest_txt.toWire(obuffer);
-    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
-                        obuffer.getData(), obuffer.getLength(),
-                        &largest_txt_data[0], largest_txt_data.size());
-
-    // rdlen parameter is out of range.  This is a rare event because we'd
-    // normally call the constructor via a polymorphic wrapper, where the
-    // length is validated.  But this should be checked explicitly.
-    InputBuffer ibuffer2(&largest_txt_data[0], largest_txt_data.size());
-    EXPECT_THROW(generic::TXT(ibuffer2, 65536), InvalidRdataLength);
-
-    // RDATA is empty, which is invalid for TXT.
-    EXPECT_THROW(rdataFactoryFromFile(RRType("TXT"), RRClass("IN"),
-                                      "rdata_txt_fromWire4.wire"),
-                 DNSMessageFORMERR);
-
-    // character-string length is too large, which could cause overrun.
-    EXPECT_THROW(rdataFactoryFromFile(RRType("TXT"), RRClass("IN"),
-                                      "rdata_txt_fromWire5.wire"),
-                 DNSMessageFORMERR);
-}
-
-TEST_F(Rdata_TXT_Test, toWireBuffer) {
-    rdata_txt.toWire(obuffer);
-    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
-                        obuffer.getData(), obuffer.getLength(),
-                        wiredata_txt, sizeof(wiredata_txt));
-}
-
-TEST_F(Rdata_TXT_Test, toText) {
-    EXPECT_EQ("\"Test String\"", rdata_txt.toText());
-}
-}

+ 1 - 0
src/lib/dns/tests/testdata/Makefile.am

@@ -90,6 +90,7 @@ EXTRA_DIST += question_fromWire question_toWire1 question_toWire2
 EXTRA_DIST += rdatafields1.spec rdatafields2.spec rdatafields3.spec
 EXTRA_DIST += rdatafields1.spec rdatafields2.spec rdatafields3.spec
 EXTRA_DIST += rdatafields4.spec rdatafields5.spec rdatafields6.spec
 EXTRA_DIST += rdatafields4.spec rdatafields5.spec rdatafields6.spec
 EXTRA_DIST += rdata_cname_fromWire rdata_dname_fromWire rdata_dnskey_fromWire
 EXTRA_DIST += rdata_cname_fromWire rdata_dname_fromWire rdata_dnskey_fromWire
+EXTRA_DIST += rdata_dhcid_fromWire rdata_dhcid_toWire
 EXTRA_DIST += rdata_ds_fromWire rdata_in_a_fromWire rdata_in_aaaa_fromWire
 EXTRA_DIST += rdata_ds_fromWire rdata_in_a_fromWire rdata_in_aaaa_fromWire
 EXTRA_DIST += rdata_mx_fromWire rdata_mx_toWire1 rdata_mx_toWire2
 EXTRA_DIST += rdata_mx_fromWire rdata_mx_toWire1 rdata_mx_toWire2
 EXTRA_DIST += rdata_ns_fromWire
 EXTRA_DIST += rdata_ns_fromWire

+ 12 - 0
src/lib/dns/tests/testdata/rdata_dhcid_fromWire

@@ -0,0 +1,12 @@
+#
+# DHCID RDATA stored in an input buffer
+#
+# Valid RDATA for 0LIg0LvQtdGB0YMg0YDQvtC00LjQu9Cw0YHRjCDRkdC70L7Rh9C60LA=
+#
+# RDLENGHT=41 bytes
+# 0  1
+ 00 29
+# 0LIg0LvQtdGB0YMg0YDQvtC00LjQu9Cw0YHRjCDRkdC70L7Rh9C60LA=
+d0 b2 20 d0 bb d0 b5 d1 81 d1 83 20 d1 80 d0 be
+d0 b4 d0 b8 d0 bb d0 b0 d1 81 d1 8c 20 d1 91 d0
+bb d0 be d1 87 d0 ba d0 b0

+ 7 - 0
src/lib/dns/tests/testdata/rdata_dhcid_toWire

@@ -0,0 +1,7 @@
+#
+# DHCID RDATA stored in an output buffer
+#
+# 0LIg0LvQtdGB0YMg0YDQvtC00LjQu9Cw0YHRjCDRkdC70L7Rh9C60LA=
+d0 b2 20 d0 bb d0 b5 d1 81 d1 83 20 d1 80 d0 be
+d0 b4 d0 b8 d0 bb d0 b0 d1 81 d1 8c 20 d1 91 d0
+bb d0 be d1 87 d0 ba d0 b0

+ 14 - 0
src/lib/python/isc/datasrc/__init__.py

@@ -1,6 +1,16 @@
 import sys
 import sys
 import os
 import os
 
 
+# The datasource factory loader uses dlopen, as does python
+# for its modules. Some dynamic linkers do not play nice if 
+# modules are not loaded with RTLD_GLOBAL, a symptom of which
+# is that exceptions are not recognized by type. So to make
+# sure this doesn't happen, we temporarily set RTLD_GLOBAL
+# during the loading of the datasource wrappers.
+import ctypes
+flags = sys.getdlopenflags()
+sys.setdlopenflags(flags | ctypes.RTLD_GLOBAL)
+
 # this setup is a temporary workaround to deal with the problem of
 # this setup is a temporary workaround to deal with the problem of
 # having both 'normal' python modules and a wrapper module
 # having both 'normal' python modules and a wrapper module
 # Once all programs use the new interface, we should remove the
 # Once all programs use the new interface, we should remove the
@@ -16,6 +26,10 @@ if intree:
     from datasrc import *
     from datasrc import *
 else:
 else:
     from isc.datasrc.datasrc import *
     from isc.datasrc.datasrc import *
+
+# revert to the default dlopen flags
+sys.setdlopenflags(flags)
+
 from isc.datasrc.sqlite3_ds import *
 from isc.datasrc.sqlite3_ds import *
 from isc.datasrc.master import *
 from isc.datasrc.master import *
 
 

+ 33 - 0
src/lib/python/isc/datasrc/tests/datasrc_test.py

@@ -19,6 +19,7 @@ import isc.dns
 import unittest
 import unittest
 import os
 import os
 import shutil
 import shutil
+import json
 
 
 TESTDATA_PATH = os.environ['TESTDATA_PATH'] + os.sep
 TESTDATA_PATH = os.environ['TESTDATA_PATH'] + os.sep
 TESTDATA_WRITE_PATH = os.environ['TESTDATA_WRITE_PATH'] + os.sep
 TESTDATA_WRITE_PATH = os.environ['TESTDATA_WRITE_PATH'] + os.sep
@@ -381,7 +382,39 @@ class DataSrcUpdater(unittest.TestCase):
         self.assertEqual("www.example.com. 3600 IN A 192.0.2.1\n",
         self.assertEqual("www.example.com. 3600 IN A 192.0.2.1\n",
                          rrset.to_text())
                          rrset.to_text())
 
 
+    def test_two_modules(self):
+        # load two modules, and check if they don't interfere
+        mem_cfg = { "type": "memory", "class": "IN", "zones": [] };
+        dsc_mem = isc.datasrc.DataSourceClient("memory", json.dumps(mem_cfg))
+        dsc_sql = isc.datasrc.DataSourceClient("sqlite3", READ_ZONE_DB_CONFIG)
+
+        # check if exceptions are working
+        self.assertRaises(isc.datasrc.Error, isc.datasrc.DataSourceClient,
+                          "memory", "{}")
+        self.assertRaises(isc.datasrc.Error, isc.datasrc.DataSourceClient,
+                          "sqlite3", "{}")
+
+        # see if a lookup succeeds in sqlite3 ds
+        result, finder = dsc_sql.find_zone(isc.dns.Name("example.com"))
+        self.assertEqual(finder.SUCCESS, result)
+        self.assertEqual(isc.dns.RRClass.IN(), finder.get_class())
+        self.assertEqual("example.com.", finder.get_origin().to_text())
+        result, rrset = finder.find(isc.dns.Name("www.example.com"),
+                                    isc.dns.RRType.A(),
+                                    None,
+                                    finder.FIND_DEFAULT)
+        self.assertEqual(finder.SUCCESS, result)
+        self.assertEqual("www.example.com. 3600 IN A 192.0.2.1\n",
+                         rrset.to_text())
+
+        # see if a lookup fails in mem ds
+        result, finder = dsc_mem.find_zone(isc.dns.Name("example.com"))
+        self.assertEqual(finder.NXDOMAIN, result)
+
+
     def test_update_delete_abort(self):
     def test_update_delete_abort(self):
+        # we don't do enything with this one, just making sure loading two
+        # datasources
         dsc = isc.datasrc.DataSourceClient("sqlite3", WRITE_ZONE_DB_CONFIG)
         dsc = isc.datasrc.DataSourceClient("sqlite3", WRITE_ZONE_DB_CONFIG)
 
 
         # first make sure, through a separate finder, that some record exists
         # first make sure, through a separate finder, that some record exists