Parcourir la source

Merge branch 'master' into trac1286

Jeremy C. Reed il y a 13 ans
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
 	Do not install the unittest libraries. At this time, they
 	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/rdata_unittest.cc:228
 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)) {
         addError(errors,
                  "Config for memory backend does not contain a '"
-                 "type"
+                 +name+
                  "' value");
         return false;
     } 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 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 {
 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) {
         if (rdata_len > MAX_RDLENGTH) {
             isc_throw(InvalidRdataLength, "RDLENGTH too large: " << rdata_len);
@@ -52,6 +68,14 @@ public:
         } 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) {
         // TBD: this is a simple, incomplete implementation that only supports
         // a single character-string.
@@ -86,10 +110,17 @@ public:
         string_list_.push_back(data);
     }
 
+    /// \brief The copy constructor.
+    ///
+    /// Trivial for now, we could've used the default one.
     TXTLikeImpl(const TXTLikeImpl& other) :
         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
     toWire(OutputBuffer& buffer) const {
         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
     toWire(AbstractMessageRenderer& renderer) const {
         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
     toText() const {
         string s;
@@ -134,20 +173,33 @@ public:
         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
     compare(const TXTLikeImpl& other) const {
         // This implementation is not efficient.  Revisit this (TBD).
         OutputBuffer this_buffer(0);
         toWire(this_buffer);
+        uint8_t const* const this_data = (uint8_t const*)this_buffer.getData();
         size_t this_len = this_buffer.getLength();
 
         OutputBuffer other_buffer(0);
         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 cmplen = min(this_len, other_len);
-        const int cmp = memcmp(this_buffer.getData(), other_buffer.getData(),
-                               cmplen);
+        const int cmp = memcmp(this_data, other_data, cmplen);
+
         if (cmp != 0) {
             return (cmp);
         } else {

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

@@ -30,8 +30,17 @@ using namespace isc::util;
 // BEGIN_ISC_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>
 
+/// \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::operator=(const SPF& source) {
     if (impl_ == source.impl_) {
@@ -45,37 +54,72 @@ SPF::operator=(const SPF& source) {
     return (*this);
 }
 
+/// \brief The destructor
 SPF::~SPF() {
     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) :
     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) :
     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) :
     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
 SPF::toWire(OutputBuffer& buffer) const {
     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
 SPF::toWire(AbstractMessageRenderer& renderer) const {
     impl_->toWire(renderer);
 }
 
+/// \brief Convert the \c SPF to a string.
+///
+/// \return is the return of the corresponding implementation method.
 string
 SPF::toText() const {
     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
 SPF::compare(const Rdata& other) const {
     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;
 
+/// \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 {
 public:
     // BEGIN_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);
+
+    /// \brief The destructor.
     ~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:
     typedef TXTLikeImpl<SPF, 99> SPFImpl;
     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
 // purpose with or without fee is hereby granted, provided that the above
@@ -20,7 +20,7 @@
 #include <exceptions/exceptions.h>
 
 #include <util/buffer.h>
-#include <util/encode/hex.h>
+#include <util/encode/base64.h>
 #include <dns/exceptions.h>
 #include <dns/messagerenderer.h>
 #include <dns/rdata.h>
@@ -52,7 +52,7 @@ DHCID::DHCID(const string& dhcid_str) {
     stringbuf 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
     // 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.
 string
 DHCID::toText() const {
-    return (isc::util::encode::encodeHex(digest_));
+    return (isc::util::encode::encodeBase64(digest_));
 }
 
 /// \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
 // 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 += rdata_in_a_unittest.cc rdata_in_aaaa_unittest.cc
 run_unittests_SOURCES += rdata_ns_unittest.cc rdata_soa_unittest.cc
-run_unittests_SOURCES += rdata_txt_unittest.cc rdata_mx_unittest.cc
+run_unittests_SOURCES += rdata_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_dname_unittest.cc
 run_unittests_SOURCES += rdata_afsdb_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_ds_like_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 += rdatafields4.spec rdatafields5.spec rdatafields6.spec
 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_mx_fromWire rdata_mx_toWire1 rdata_mx_toWire2
 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 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
 # having both 'normal' python modules and a wrapper module
 # Once all programs use the new interface, we should remove the
@@ -16,6 +26,10 @@ if intree:
     from datasrc import *
 else:
     from isc.datasrc.datasrc import *
+
+# revert to the default dlopen flags
+sys.setdlopenflags(flags)
+
 from isc.datasrc.sqlite3_ds 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 os
 import shutil
+import json
 
 TESTDATA_PATH = os.environ['TESTDATA_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",
                          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):
+        # we don't do enything with this one, just making sure loading two
+        # datasources
         dsc = isc.datasrc.DataSourceClient("sqlite3", WRITE_ZONE_DB_CONFIG)
 
         # first make sure, through a separate finder, that some record exists