Browse Source

Merge #2751

Support for RdataSet::subtract.
Michal 'vorner' Vaner 11 years ago
parent
commit
7430591b4a

+ 146 - 20
src/lib/datasrc/memory/rdataset.cc

@@ -12,6 +12,9 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 // PERFORMANCE OF THIS SOFTWARE.
 
 
+#include "rdataset.h"
+#include "rdata_serialization.h"
+
 #include <exceptions/exceptions.h>
 #include <exceptions/exceptions.h>
 
 
 #include <dns/rdata.h>
 #include <dns/rdata.h>
@@ -19,11 +22,10 @@
 #include <dns/rrclass.h>
 #include <dns/rrclass.h>
 #include <dns/rrtype.h>
 #include <dns/rrtype.h>
 #include <dns/rrset.h>
 #include <dns/rrset.h>
-
-#include "rdataset.h"
-#include "rdata_serialization.h"
+#include <util/buffer.h>
 
 
 #include <boost/static_assert.hpp>
 #include <boost/static_assert.hpp>
+#include <boost/bind.hpp>
 
 
 #include <stdint.h>
 #include <stdint.h>
 #include <algorithm>
 #include <algorithm>
@@ -74,12 +76,16 @@ lowestTTL(const RdataSet* rdataset, ConstRRsetPtr& rrset,
                 sig_rrset->getTTL());
                 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
     // Check basic validity
     if (!rrset && !sig_rrset) {
     if (!rrset && !sig_rrset) {
@@ -91,6 +97,9 @@ RdataSet::create(util::MemorySegment& mem_sgmt, RdataEncoder& encoder,
     if (sig_rrset && sig_rrset->getRdataCount() == 0) {
     if (sig_rrset && sig_rrset->getRdataCount() == 0) {
         isc_throw(BadValue, "Empty SIG RRset");
         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()) {
     if (rrset && sig_rrset && rrset->getClass() != sig_rrset->getClass()) {
         isc_throw(BadValue, "RR class doesn't match between RRset and RRSIG");
         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 RRClass rrclass = rrset ? rrset->getClass() : sig_rrset->getClass();
     const RRType rrtype = rrset ? rrset->getType() :
     const RRType rrtype = rrset ? rrset->getType() :
         getCoveredType(sig_rrset->getRdataIterator()->getCurrent());
         getCoveredType(sig_rrset->getRdataIterator()->getCurrent());
+
     if (old_rdataset && old_rdataset->type != rrtype) {
     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);
     const RRTTL rrttl = lowestTTL(old_rdataset, rrset, sig_rrset);
     if (old_rdataset) {
     if (old_rdataset) {
         encoder.start(rrclass, rrtype, old_rdataset->getDataBuf(),
         encoder.start(rrclass, rrtype, old_rdataset->getDataBuf(),
@@ -148,18 +193,99 @@ RdataSet::create(util::MemorySegment& mem_sgmt, RdataEncoder& encoder,
                   << MAX_RRSIG_COUNT);
                   << 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
 void

+ 53 - 0
src/lib/datasrc/memory/rdataset.h

@@ -207,6 +207,53 @@ public:
                             dns::ConstRRsetPtr sig_rrset,
                             dns::ConstRRsetPtr sig_rrset,
                             const RdataSet* old_rdataset = NULL);
                             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
     /// \brief Destruct and deallocate \c RdataSet
     ///
     ///
     /// Note that this method needs to know the expected RR class of the
     /// 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.
     // field for the real number of RRSIGs.  It's 2^3 - 1 = 7.
     static const size_t MANY_RRSIG_COUNT = (1 << 3) - 1;
     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:
 public:
     /// \brief Return the bare pointer to the next node.
     /// \brief Return the bare pointer to the next node.
     ///
     ///

+ 143 - 2
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
     ASSERT_TRUE(*it != it_end); // shouldn't reach the end yet
 
 
     isc::util::InputBuffer b(data, size);
     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
     ++(*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) {
 TEST_F(RdataSetTest, duplicate) {
     // Create RRset and RRSIG containing duplicate RDATA.
     // Create RRset and RRSIG containing duplicate RDATA.
     ConstRRsetPtr dup_rrset =
     ConstRRsetPtr dup_rrset =
@@ -610,4 +697,58 @@ TEST_F(RdataSetTest, varyingTTL) {
     EXPECT_EQ(RRTTL(5), restoreTTL(rdataset->getTTLData()));
     EXPECT_EQ(RRTTL(5), restoreTTL(rdataset->getTTLData()));
     RdataSet::destroy(mem_sgmt_, rdataset, rrclass);
     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);
+}
 }
 }

+ 0 - 1
src/lib/dns/messagerenderer.h

@@ -367,7 +367,6 @@ public:
     using AbstractMessageRenderer::CASE_INSENSITIVE;
     using AbstractMessageRenderer::CASE_INSENSITIVE;
     using AbstractMessageRenderer::CASE_SENSITIVE;
     using AbstractMessageRenderer::CASE_SENSITIVE;
 
 
-    /// \brief Constructor from an output buffer.
     MessageRenderer();
     MessageRenderer();
 
 
     virtual ~MessageRenderer();
     virtual ~MessageRenderer();