BIND 10 master, updated. 7430591b4ae4c7052cab86ed17d0221db3b524a8 Merge #2751

BIND 10 source code commits bind10-changes at lists.isc.org
Tue Sep 17 14:15:40 UTC 2013


The branch, master has been updated
       via  7430591b4ae4c7052cab86ed17d0221db3b524a8 (commit)
       via  4bee63eec6164380cc042de3405102a6e4a74b67 (commit)
       via  37069dfbb56bd41717c439339668807a28ca9be0 (commit)
       via  04eb5cebe1ab646507b4dab3964a87e730a79646 (commit)
       via  8e64776206cf6845c938ddfb1ad76c7d6a61377e (commit)
       via  3d4ea5002959fd0510241f2c3550e90d9899fcee (commit)
       via  06091ef843f52f38d0d13eb58bd1400f637ee96b (commit)
       via  c812d223a1a93535f29ede6a082ea96fc1f928fc (commit)
       via  128be57f78800c62ca957cd14350bac9627782e0 (commit)
       via  bb33775113793e5457116033bfafa8f97dd27d18 (commit)
       via  a3f0255a3b6696ec8d2f21b78366ec44d13374d7 (commit)
       via  bd154b0950fd08ea60336c73249bc8b0de57779d (commit)
       via  d936a170390f87dca5f73751c7f61891a3f4c4c1 (commit)
       via  c33aa974e86c6f63e062e562fc0196a9de0ae23a (commit)
      from  807d78bb19ec4df52474e91831c4b670c76db043 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 7430591b4ae4c7052cab86ed17d0221db3b524a8
Merge: 807d78b 4bee63e
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date:   Tue Sep 17 15:31:17 2013 +0200

    Merge #2751
    
    Support for RdataSet::subtract.

-----------------------------------------------------------------------

Summary of changes:
 src/lib/datasrc/memory/rdataset.cc                |  166 ++++++++++++++++++---
 src/lib/datasrc/memory/rdataset.h                 |   53 +++++++
 src/lib/datasrc/tests/memory/rdataset_unittest.cc |  145 +++++++++++++++++-
 src/lib/dns/messagerenderer.h                     |    1 -
 4 files changed, 342 insertions(+), 23 deletions(-)

-----------------------------------------------------------------------
diff --git a/src/lib/datasrc/memory/rdataset.cc b/src/lib/datasrc/memory/rdataset.cc
index 7f37f51..b60316c 100644
--- a/src/lib/datasrc/memory/rdataset.cc
+++ b/src/lib/datasrc/memory/rdataset.cc
@@ -12,6 +12,9 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#include "rdataset.h"
+#include "rdata_serialization.h"
+
 #include <exceptions/exceptions.h>
 
 #include <dns/rdata.h>
@@ -19,11 +22,10 @@
 #include <dns/rrclass.h>
 #include <dns/rrtype.h>
 #include <dns/rrset.h>
-
-#include "rdataset.h"
-#include "rdata_serialization.h"
+#include <util/buffer.h>
 
 #include <boost/static_assert.hpp>
+#include <boost/bind.hpp>
 
 #include <stdint.h>
 #include <algorithm>
@@ -74,12 +76,16 @@ lowestTTL(const RdataSet* rdataset, ConstRRsetPtr& rrset,
                 sig_rrset->getTTL());
     }
 }
-}
 
-RdataSet*
-RdataSet::create(util::MemorySegment& mem_sgmt, RdataEncoder& encoder,
-                 ConstRRsetPtr rrset, ConstRRsetPtr sig_rrset,
-                 const RdataSet* old_rdataset)
+// Do some sanity checks on params of create and substract. Return the
+// target rrclass and rrtype (as they are produced as side effect of the
+// checks).
+//
+// The only reason for this function is to reuse common code of the
+// methods.
+std::pair<RRClass, RRType>
+sanityChecks(const ConstRRsetPtr& rrset, const ConstRRsetPtr &sig_rrset,
+             const RdataSet *old_rdataset)
 {
     // Check basic validity
     if (!rrset && !sig_rrset) {
@@ -91,6 +97,9 @@ RdataSet::create(util::MemorySegment& mem_sgmt, RdataEncoder& encoder,
     if (sig_rrset && sig_rrset->getRdataCount() == 0) {
         isc_throw(BadValue, "Empty SIG RRset");
     }
+    if (sig_rrset && sig_rrset->getType() != RRType::RRSIG()) {
+        isc_throw(BadValue, "SIG RRset doesn't have type RRSIG");
+    }
     if (rrset && sig_rrset && rrset->getClass() != sig_rrset->getClass()) {
         isc_throw(BadValue, "RR class doesn't match between RRset and RRSIG");
     }
@@ -98,9 +107,45 @@ RdataSet::create(util::MemorySegment& mem_sgmt, RdataEncoder& encoder,
     const RRClass rrclass = rrset ? rrset->getClass() : sig_rrset->getClass();
     const RRType rrtype = rrset ? rrset->getType() :
         getCoveredType(sig_rrset->getRdataIterator()->getCurrent());
+
     if (old_rdataset && old_rdataset->type != rrtype) {
-        isc_throw(BadValue, "RR type doesn't match for merging RdataSet");
+        isc_throw(BadValue, "RR type doesn't match between RdataSets");
     }
+
+    return (std::pair<RRClass, RRType>(rrclass, rrtype));
+}
+
+} // Anonymous namespace
+
+RdataSet*
+RdataSet::packSet(util::MemorySegment& mem_sgmt, RdataEncoder& encoder,
+                  size_t rdata_count, size_t rrsig_count, const RRType& rrtype,
+                  const RRTTL& rrttl)
+{
+    const size_t ext_rrsig_count_len =
+        rrsig_count >= MANY_RRSIG_COUNT ? sizeof(uint16_t) : 0;
+    const size_t data_len = encoder.getStorageLength();
+    void* p = mem_sgmt.allocate(sizeof(RdataSet) + ext_rrsig_count_len +
+                                data_len);
+    RdataSet* rdataset = new(p) RdataSet(rrtype, rdata_count, rrsig_count,
+                                         rrttl);
+    if (rrsig_count >= RdataSet::MANY_RRSIG_COUNT) {
+        *rdataset->getExtSIGCountBuf() = rrsig_count;
+    }
+    encoder.encode(rdataset->getDataBuf(), data_len);
+    return (rdataset);
+}
+
+RdataSet*
+RdataSet::create(util::MemorySegment& mem_sgmt, RdataEncoder& encoder,
+                 ConstRRsetPtr rrset, ConstRRsetPtr sig_rrset,
+                 const RdataSet* old_rdataset)
+{
+    const std::pair<RRClass, RRType>& rrparams =
+        sanityChecks(rrset, sig_rrset, old_rdataset);
+    const RRClass& rrclass = rrparams.first;
+    const RRType& rrtype = rrparams.second;
+
     const RRTTL rrttl = lowestTTL(old_rdataset, rrset, sig_rrset);
     if (old_rdataset) {
         encoder.start(rrclass, rrtype, old_rdataset->getDataBuf(),
@@ -148,18 +193,99 @@ RdataSet::create(util::MemorySegment& mem_sgmt, RdataEncoder& encoder,
                   << MAX_RRSIG_COUNT);
     }
 
-    const size_t ext_rrsig_count_len =
-        rrsig_count >= MANY_RRSIG_COUNT ? sizeof(uint16_t) : 0;
-    const size_t data_len = encoder.getStorageLength();
-    void* p = mem_sgmt.allocate(sizeof(RdataSet) + ext_rrsig_count_len +
-                                data_len);
-    RdataSet* rdataset = new(p) RdataSet(rrtype, rdata_count, rrsig_count,
-                                         rrttl);
-    if (rrsig_count >= MANY_RRSIG_COUNT) {
-        *rdataset->getExtSIGCountBuf() = rrsig_count;
+    return (packSet(mem_sgmt, encoder, rdata_count, rrsig_count, rrtype,
+                    rrttl));
+}
+
+namespace {
+
+void writeName(util::OutputBuffer* buffer, const LabelSequence& name,
+               RdataNameAttributes)
+{
+    size_t len;
+    const uint8_t* data = name.getData(&len);
+    buffer->writeData(data, len);
+}
+
+void writeData(util::OutputBuffer* buffer, const void* data, size_t len) {
+    buffer->writeData(data, len);
+}
+
+size_t subtractIterate(const dns::ConstRRsetPtr& subtract,
+                       const RRClass& rrclass, const RRType& rrtype,
+                       boost::function<bool ()> iterator,
+                       boost::function<void (const Rdata& rdata)> inserter,
+                       util::OutputBuffer& buffer)
+{
+    size_t count = 0;
+    while (iterator()) {
+        util::InputBuffer input(buffer.getData(), buffer.getLength());
+        const RdataPtr& rdata(createRdata(rrtype, rrclass, input,
+                                          buffer.getLength()));
+        buffer.clear();
+
+        bool insert = true;
+        if (subtract) {
+            for (RdataIteratorPtr it = subtract->getRdataIterator();
+                 !it->isLast(); it->next()) {
+                if (rdata->compare(it->getCurrent()) == 0) {
+                    insert = false;
+                    break;
+                }
+            }
+        }
+
+        if (insert) {
+            inserter(*rdata);
+            ++count;
+        }
     }
-    encoder.encode(rdataset->getDataBuf(), data_len);
-    return (rdataset);
+    return (count);
+}
+
+} // Anonymous namespace
+
+RdataSet*
+RdataSet::subtract(util::MemorySegment& mem_sgmt, RdataEncoder& encoder,
+                   const dns::ConstRRsetPtr& rrset,
+                   const dns::ConstRRsetPtr& sig_rrset,
+                   const RdataSet& old_rdataset)
+{
+    const std::pair<RRClass, RRType>& rrparams =
+        sanityChecks(rrset, sig_rrset, &old_rdataset);
+    const RRClass& rrclass = rrparams.first;
+    const RRType& rrtype = rrparams.second;
+
+    // Do the encoding
+    encoder.start(rrclass, rrtype);
+    util::OutputBuffer buffer(1024);
+    RdataReader reader(rrclass, rrtype, old_rdataset.getDataBuf(),
+                       old_rdataset.getRdataCount(),
+                       old_rdataset.getSigRdataCount(),
+                       boost::bind(writeName, &buffer, _1, _2),
+                       boost::bind(writeData, &buffer, _1, _2));
+
+    // Copy over the Rdata (except for the subtracted)
+    const size_t rdata_count =
+        subtractIterate(rrset, rrclass, rrtype,
+                        boost::bind(&RdataReader::iterateRdata, &reader),
+                        boost::bind(&RdataEncoder::addRdata, &encoder, _1),
+                        buffer);
+    // Copy over the signatures (except for the subtracted)
+    const size_t rrsig_count =
+        subtractIterate(sig_rrset, rrclass, RRType::RRSIG(),
+                        boost::bind(&RdataReader::iterateSingleSig, &reader),
+                        boost::bind(&RdataEncoder::addSIGRdata, &encoder, _1),
+                        buffer);
+
+    // Note that we don't need to check for overflow, if it fitted before, it
+    // fits after removal of something too.
+
+    if (rdata_count == 0 && rrsig_count == 0) {
+        return (NULL); // It is left empty
+    }
+    return (packSet(mem_sgmt, encoder, rdata_count, rrsig_count, rrtype,
+                    restoreTTL(old_rdataset.getTTLData())));
 }
 
 void
diff --git a/src/lib/datasrc/memory/rdataset.h b/src/lib/datasrc/memory/rdataset.h
index caef551..bd93781 100644
--- a/src/lib/datasrc/memory/rdataset.h
+++ b/src/lib/datasrc/memory/rdataset.h
@@ -207,6 +207,53 @@ public:
                             dns::ConstRRsetPtr sig_rrset,
                             const RdataSet* old_rdataset = NULL);
 
+    /// \brief Subtract some RDATAs and RRSIGs from an RdataSet
+    ///
+    /// Allocate and construct a new RdataSet that contains all the
+    /// data from the \c old_rdataset except for the ones in rrset
+    /// and sig_rrset.
+    ///
+    /// The interface is almost the same as with \c create, as well
+    /// as the restrictions and internal format. The most significant
+    /// difference in the interface is old_rdataset is mandatory here.
+    ///
+    /// This ignores RDATAs present in rrset and not in old_rdataset
+    /// (similarly for RRSIGs). If an RDATA in rrset and not in
+    /// old_rdataset is an error condition or needs other special
+    /// handling, it is up to the caller to check the old_rdataset
+    /// first.
+    ///
+    /// There'll be no memory leak on exception. However, the memory
+    /// allocated from the mem_sgmt may move when
+    /// \c util::MemorySegmentGrown is thrown. Note that it may happen
+    /// even when subtracting data from the old_rdataset, since a new
+    /// copy is being created.
+    ///
+    /// The old_rdataset is not destroyed and it is up to the caller to
+    /// manage its allocation.
+    ///
+    /// \throw util::MemorySegmentGrown The memory segment has grown, possibly
+    ///     relocating data.
+    /// \throw isc::BadValue Given RRset(s) are invalid.
+    /// \throw std::bad_alloc Memory allocation fails.
+    ///
+    /// \param mem_sgmt A \c MemorySegment from which memory for the new
+    /// \c RdataSet is allocated.
+    /// \param encoder The RDATA encoder to encode \c rrset and \c sig_rrset
+    /// with the \c RdataSet is to be created.
+    /// \param rrset A (non-RRSIG) RRset containing the RDATA that are not
+    /// to be present in the result. Can be NULL if sig_rrset is not.
+    /// \param sig_rrset An RRSIG RRset containing the RRSIGs that are not
+    /// to be present in the result. Can be NULL if rrset is not.
+    /// \param old_rdataset The data from which to subtract.
+    ///
+    /// \return A pointer to the created \c RdataSet.
+    static RdataSet* subtract(util::MemorySegment& mem_sgmt,
+                              RdataEncoder& encoder,
+                              const dns::ConstRRsetPtr& rrset,
+                              const dns::ConstRRsetPtr& sig_rrset,
+                              const RdataSet& old_rdataset);
+
     /// \brief Destruct and deallocate \c RdataSet
     ///
     /// Note that this method needs to know the expected RR class of the
@@ -311,6 +358,12 @@ private:
     // field for the real number of RRSIGs.  It's 2^3 - 1 = 7.
     static const size_t MANY_RRSIG_COUNT = (1 << 3) - 1;
 
+    // Common code for packing the result in create and subtract.
+    static RdataSet* packSet(util::MemorySegment& mem_sgmt,
+                             RdataEncoder& encoder, size_t rdata_count,
+                             size_t rrsig_count, const dns::RRType& rrtype,
+                             const dns::RRTTL& rrttl);
+
 public:
     /// \brief Return the bare pointer to the next node.
     ///
diff --git a/src/lib/datasrc/tests/memory/rdataset_unittest.cc b/src/lib/datasrc/tests/memory/rdataset_unittest.cc
index 31e5be2..e6fd5cc 100644
--- a/src/lib/datasrc/tests/memory/rdataset_unittest.cc
+++ b/src/lib/datasrc/tests/memory/rdataset_unittest.cc
@@ -103,8 +103,10 @@ checkData(const void* data, size_t size, const RRType* rrtype,
     ASSERT_TRUE(*it != it_end); // shouldn't reach the end yet
 
     isc::util::InputBuffer b(data, size);
-    EXPECT_EQ(0, createRdata(*rrtype, RRClass::IN(), b, size)->compare(
-                  *createRdata(*rrtype, RRClass::IN(), **it)));
+    const RdataPtr& actual(createRdata(*rrtype, RRClass::IN(), b, size));
+    const RdataPtr& expected(createRdata(*rrtype, RRClass::IN(), **it));
+    EXPECT_EQ(0, actual->compare(*expected)) << actual->toText() <<
+        " vs. " << expected->toText();
     ++(*it);                    // move to the next expected data
 }
 
@@ -224,6 +226,91 @@ TEST_F(RdataSetTest, mergeCreate) {
     }
 }
 
+TEST_F(RdataSetTest, subtract) {
+    // Prepare test data
+    const char* const a_rdatas[] = { "192.0.2.1", "192.0.2.2" };
+    const char* const sig_rdatas[] = {
+        "A 5 2 3600 20120814220826 20120715220826 1234 example.com. FAKE",
+        "A 5 2 3600 20120814220826 20120715220826 4321 example.com. FAKE" };
+    ConstRRsetPtr a_rrsets = textToRRset("www.example.com. 1076895760 IN A "
+                                         + string(a_rdatas[0]) + "\n"
+                                         + "www.example.com. 1076895760 IN A "
+                                         + string(a_rdatas[1]));
+    ConstRRsetPtr rrsig_rrsets =
+        textToRRset("www.example.com. 1076895760 IN RRSIG "
+                    + string(sig_rdatas[0]) + "\n"
+                    + "www.example.com. 1076895760 IN RRSIG "
+                    + string(sig_rdatas[1]));
+    ConstRRsetPtr null_rrset;   // convenience shortcut
+    // Prepare the data to subtract (they have one common and one differing
+    // element each).
+    const char* const a_rdatas_rm[] = { "192.0.2.1", "192.0.2.3" };
+    const char* const sig_rdatas_rm[] = {
+        "A 5 2 3600 20120814220826 20120715220826 1234 example.com. FAKE",
+        "A 5 2 3600 20120814220826 20120715220826 5678 example.com. FAKE" };
+    ConstRRsetPtr a_rrsets_rm =
+        textToRRset("www.example.com. 1076895760 IN A "
+                    + string(a_rdatas_rm[0]) + "\n"
+                    + "www.example.com. 1076895760 IN A "
+                    + string(a_rdatas_rm[1]));
+    ConstRRsetPtr rrsig_rrsets_rm =
+        textToRRset("www.example.com. 1076895760 IN RRSIG "
+                    + string(sig_rdatas_rm[0]) + "\n"
+                    + "www.example.com. 1076895760 IN RRSIG "
+                    + string(sig_rdatas_rm[1]));
+
+    // A similar cycle as in the mergeCreate test.
+    for (int i = 1; i < 4; ++i) {
+        for (int j = 1; j < 4; ++j) {
+            SCOPED_TRACE("creating subtract case " + lexical_cast<string>(i) +
+                         ", " + lexical_cast<string>(j));
+            // Create old rdataset
+            SegmentObjectHolder<RdataSet, RRClass> holder1(mem_sgmt_, rrclass);
+            holder1.set(RdataSet::create(mem_sgmt_, encoder_,
+                                 (i & 1) ? a_rrsets : null_rrset,
+                                 (i & 2) ? rrsig_rrsets : null_rrset));
+            // Create subtracted rdataset, from the old one and RRsets
+            SegmentObjectHolder<RdataSet, RRClass> holder2(mem_sgmt_, rrclass);
+            holder2.set(RdataSet::subtract(mem_sgmt_, encoder_,
+                                 (j & 1) ? a_rrsets_rm : null_rrset,
+                                 (j & 2) ? rrsig_rrsets_rm : null_rrset,
+                                 *holder1.get()));
+
+            // Set up the expected data for the case.
+            vector<string> expected_rdata;
+            if (i & 1) {
+                if (!(j & 1)) { // Not removed the other
+                    expected_rdata.push_back(a_rdatas[0]);
+                }
+                expected_rdata.push_back(a_rdatas[1]);
+            }
+            vector<string> expected_sigs;
+            if (i & 2) {
+                if (!(j & 2)) { // Not removed the other
+                    expected_sigs.push_back(sig_rdatas[0]);
+                }
+                expected_sigs.push_back(sig_rdatas[1]);
+            }
+
+            // Then perform the check
+            checkRdataSet(*holder2.get(), expected_rdata, expected_sigs);
+        }
+    }
+    // Reusing the data we have, test some corner cases.
+    SegmentObjectHolder<RdataSet, RRClass> holder_old(mem_sgmt_, rrclass);
+    holder_old.set(RdataSet::create(mem_sgmt_, encoder_, a_rrsets,
+                                    rrsig_rrsets));
+
+    // It throws if no Rdata passed.
+    EXPECT_THROW(RdataSet::subtract(mem_sgmt_, encoder_, null_rrset,
+                                    null_rrset, *holder_old.get()),
+                 isc::BadValue);
+
+    // If we remove everything, it returns NULL
+    EXPECT_EQ(NULL, RdataSet::subtract(mem_sgmt_, encoder_, a_rrsets,
+                                       rrsig_rrsets, *holder_old.get()));
+}
+
 TEST_F(RdataSetTest, duplicate) {
     // Create RRset and RRSIG containing duplicate RDATA.
     ConstRRsetPtr dup_rrset =
@@ -610,4 +697,58 @@ TEST_F(RdataSetTest, varyingTTL) {
     EXPECT_EQ(RRTTL(5), restoreTTL(rdataset->getTTLData()));
     RdataSet::destroy(mem_sgmt_, rdataset, rrclass);
 }
+
+// Creation of rdataset with bad params, with create and subtract
+TEST_F(RdataSetTest, badParams) {
+    const ConstRRsetPtr empty_rrset(new RRset(Name("www.example.com"),
+                                             RRClass::IN(), RRType::A(),
+                                             RRTTL(3600)));
+    const ConstRRsetPtr empty_rrsig(new RRset(Name("www.example.com"),
+                                             RRClass::IN(), RRType::RRSIG(),
+                                             RRTTL(3600)));
+    const ConstRRsetPtr a_rrset = textToRRset("www.example.com. 3600 IN A "
+                                              "192.0.2.1");
+    const ConstRRsetPtr aaaa_rrset = textToRRset("www.example.com. 3600 IN AAAA "
+                                                 "2001:db8::1");
+    const ConstRRsetPtr sig_rrset = textToRRset("www.example.com. 3600 IN RRSIG "
+                                                "A 5 2 3600 20120814220826 "
+                                                "20120715220826 1234 "
+                                                "example.com. FAKE");
+    const ConstRRsetPtr sig_rrset_ch = textToRRset("www.example.com. 3600 CH RRSIG "
+                                                   "A 5 2 3600 20120814220826 "
+                                                   "20120715220826 1234 "
+                                                   "example.com. FAKE",
+                                                   RRClass::CH());
+    SegmentObjectHolder<RdataSet, RRClass> holder(mem_sgmt_, rrclass);
+    holder.set(RdataSet::create(mem_sgmt_, encoder_, a_rrset, sig_rrset));
+    // Empty RRset as rdata
+    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, empty_rrset, sig_rrset),
+                 isc::BadValue);
+    // The same for rrsig
+    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, a_rrset, empty_rrsig),
+                 isc::BadValue);
+    // Mismatched type
+    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, empty_rrset, a_rrset),
+                 isc::BadValue);
+    // Similar for subtract
+    EXPECT_THROW(RdataSet::subtract(mem_sgmt_, encoder_, empty_rrset,
+                                    sig_rrset, *holder.get()),
+                 isc::BadValue);
+    EXPECT_THROW(RdataSet::subtract(mem_sgmt_, encoder_, a_rrset, empty_rrset,
+                                    *holder.get()),
+                 isc::BadValue);
+    // Class mismatch
+    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, a_rrset, sig_rrset_ch),
+                 isc::BadValue);
+    EXPECT_THROW(RdataSet::subtract(mem_sgmt_, encoder_, a_rrset,
+                                    sig_rrset_ch, *holder.get()),
+                 isc::BadValue);
+    // Bad rrtype
+    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, aaaa_rrset,
+                                  ConstRRsetPtr(), holder.get()),
+                 isc::BadValue);
+    EXPECT_THROW(RdataSet::subtract(mem_sgmt_, encoder_, aaaa_rrset,
+                                    ConstRRsetPtr(), *holder.get()),
+                 isc::BadValue);
+}
 }
diff --git a/src/lib/dns/messagerenderer.h b/src/lib/dns/messagerenderer.h
index 092d6de..7defe95 100644
--- a/src/lib/dns/messagerenderer.h
+++ b/src/lib/dns/messagerenderer.h
@@ -367,7 +367,6 @@ public:
     using AbstractMessageRenderer::CASE_INSENSITIVE;
     using AbstractMessageRenderer::CASE_SENSITIVE;
 
-    /// \brief Constructor from an output buffer.
     MessageRenderer();
 
     virtual ~MessageRenderer();



More information about the bind10-changes mailing list