Browse Source

[master] Merge branch 'master' of ssh://git.bind10.isc.org/var/bind10/git/bind10

Naoki Kambe 12 years ago
parent
commit
307a588d6e

+ 8 - 12
src/bin/auth/query.cc

@@ -57,7 +57,7 @@ namespace auth {
 void
 Query::ResponseCreator::addRRset(isc::dns::Message& message,
                                  const isc::dns::Message::Section section,
-                                 const ConstRRsetPtr& rrset, const bool dnssec)
+                                 const ConstRRsetPtr& rrset)
 {
     /// Is this RRset already in the list of RRsets added to the message?
     const std::vector<const AbstractRRset*>::const_iterator i =
@@ -68,8 +68,7 @@ Query::ResponseCreator::addRRset(isc::dns::Message& message,
         // No - add it to both the message and the list of RRsets processed.
         // The const-cast is wrong, but the message interface seems to insist.
         message.addRRset(section,
-                         boost::const_pointer_cast<AbstractRRset>(rrset),
-                         dnssec);
+                         boost::const_pointer_cast<AbstractRRset>(rrset));
         added_.push_back(rrset.get());
     }
 }
@@ -78,8 +77,7 @@ void
 Query::ResponseCreator::create(Message& response,
                                const vector<ConstRRsetPtr>& answers,
                                const vector<ConstRRsetPtr>& authorities,
-                               const vector<ConstRRsetPtr>& additionals,
-                               const bool dnssec)
+                               const vector<ConstRRsetPtr>& additionals)
 {
     // Inserter should be reset each time the query is reset, so should be
     // empty at this point.
@@ -91,13 +89,13 @@ Query::ResponseCreator::create(Message& response,
     // guarantee that if there are duplicates, the single RRset added will
     // appear in the most important section.
     BOOST_FOREACH(const ConstRRsetPtr& rrset, answers) {
-        addRRset(response, Message::SECTION_ANSWER, rrset, dnssec);
+        addRRset(response, Message::SECTION_ANSWER, rrset);
     }
     BOOST_FOREACH(const ConstRRsetPtr& rrset, authorities) {
-        addRRset(response, Message::SECTION_AUTHORITY, rrset, dnssec);
+        addRRset(response, Message::SECTION_AUTHORITY, rrset);
     }
     BOOST_FOREACH(const ConstRRsetPtr& rrset, additionals) {
-        addRRset(response, Message::SECTION_ADDITIONAL, rrset, dnssec);
+        addRRset(response, Message::SECTION_ADDITIONAL, rrset);
     }
 }
 
@@ -533,8 +531,7 @@ Query::process(datasrc::ClientList& client_list,
             break;
     }
 
-    response_creator_.create(*response_, answers_, authorities_, additionals_,
-                             dnssec_);
+    response_creator_.create(*response_, answers_, authorities_, additionals_);
 }
 
 void
@@ -592,8 +589,7 @@ Query::processDSAtChild() {
         }
     }
 
-    response_creator_.create(*response_, answers_, authorities_, additionals_,
-                             dnssec_);
+    response_creator_.create(*response_, answers_, authorities_, additionals_);
     return (true);
 }
 

+ 4 - 8
src/bin/auth/query.h

@@ -441,16 +441,13 @@ public:
         /// authority, and additional sections, and add them to their
         /// corresponding sections in the given message.  The RRsets are
         /// filtered such that a particular RRset appears only once in the
-        /// message.
-        ///
-        /// If \c dnssec is true, it tells the message to include any RRSIGs
-        /// attached to the RRsets.
+        /// message. Any RRSIGs attached to the RRsets will be included
+        /// when they are rendered.
         void create(
             isc::dns::Message& message,
             const std::vector<isc::dns::ConstRRsetPtr>& answers_,
             const std::vector<isc::dns::ConstRRsetPtr>& authorities_,
-            const std::vector<isc::dns::ConstRRsetPtr>& additionals_,
-            const bool dnssec);
+            const std::vector<isc::dns::ConstRRsetPtr>& additionals_);
 
     private:
         // \brief RRset comparison functor.
@@ -469,10 +466,9 @@ public:
         /// \param message Message to which the RRset is to be added
         /// \param section Section of the message in which the RRset is put
         /// \param rrset Pointer to RRset to be added to the message
-        /// \param dnssec Whether RRSIG records should be added as well
         void addRRset(isc::dns::Message& message,
                       const isc::dns::Message::Section section,
-                      const isc::dns::ConstRRsetPtr& rrset, const bool dnssec);
+                      const isc::dns::ConstRRsetPtr& rrset);
 
 
     private:

+ 28 - 32
src/bin/auth/tests/query_unittest.cc

@@ -211,12 +211,18 @@ const char* const nonsec_a_txt =
     "nonsec.example.com. 3600 IN A 192.0.2.0\n";
 
 // NSEC3 RRs.  You may also need to add mapping to MockZoneFinder::hash_map_.
-const char* const nsec3_apex_txt =
+const string nsec3_apex_txt =
     "0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example.com. 3600 IN NSEC3 1 1 12 "
     "aabbccdd 2t7b4g4vsa5smi47k61mv5bv1a22bojr NS SOA NSEC3PARAM RRSIG\n";
-const char* const nsec3_www_txt =
+const string nsec3_apex_rrsig_txt =
+    "0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example.com. 3600 IN RRSIG NSEC3 5 3 "
+    "3600 20000101000000 20000201000000 12345 example.com. FAKEFAKEFAKE";
+const string nsec3_www_txt =
     "q04jkcevqvmu85r014c7dkba38o0ji5r.example.com. 3600 IN NSEC3 1 1 12 "
     "aabbccdd r53bq7cc2uvmubfu5ocmm6pers9tk9en A RRSIG\n";
+const string nsec3_www_rrsig_txt =
+    "q04jkcevqvmu85r014c7dkba38o0ji5r.example.com. 3600 IN RRSIG NSEC3 5 3 "
+    "3600 20000101000000 20000201000000 12345 example.com. FAKEFAKEFAKE";
 
 // NSEC3 for wild.example.com (used in wildcard tests, will be added on
 // demand not to confuse other tests)
@@ -479,9 +485,10 @@ protected:
                                        isc::dns::ConstRRsetPtr rrset,
                                        FindResultFlags flags = RESULT_DEFAULT)
     {
+        ConstRRsetPtr rp = stripRRsigs(rrset, options);
         return (ZoneFinderContextPtr(
                     new Context(*this, options,
-                                ResultContext(code, rrset, flags))));
+                                ResultContext(code, rp, flags))));
     }
 
 private:
@@ -594,7 +601,7 @@ MockZoneFinder::findAll(const Name& name, std::vector<ConstRRsetPtr>& target,
                  found_domain->second.begin();
                  found_rrset != found_domain->second.end(); ++found_rrset) {
                 // Insert RRs under the domain name into target
-                target.push_back(found_rrset->second);
+                target.push_back(stripRRsigs(found_rrset->second, options));
             }
             return (ZoneFinderContextPtr(
                         new Context(*this, options,
@@ -701,25 +708,8 @@ MockZoneFinder::find(const Name& name, const RRType& type,
         RRsetStore::const_iterator found_rrset =
             found_domain->second.find(type);
         if (found_rrset != found_domain->second.end()) {
-            ConstRRsetPtr rrset;
-            // Strip whatever signature there is in case DNSSEC is not required
-            // Just to make sure the Query asks for it when it is needed
-            if ((options & ZoneFinder::FIND_DNSSEC) != 0 ||
-                include_rrsig_anyway_ ||
-                !found_rrset->second->getRRsig()) {
-                rrset = found_rrset->second;
-            } else {
-                RRsetPtr noconst(new RRset(found_rrset->second->getName(),
-                                           found_rrset->second->getClass(),
-                                           found_rrset->second->getType(),
-                                           found_rrset->second->getTTL()));
-                for (RdataIteratorPtr
-                     i(found_rrset->second->getRdataIterator());
-                     !i->isLast(); i->next()) {
-                    noconst->addRdata(i->getCurrent());
-                }
-                rrset = noconst;
-            }
+            ConstRRsetPtr rrset = ZoneFinder::stripRRsigs(found_rrset->second,
+                                                          options);
             return (createContext(options, SUCCESS, rrset));
         }
 
@@ -1985,14 +1975,16 @@ TEST_F(QueryTest, findNSEC3) {
     // Apex name.  It should have a matching NSEC3
     {
         SCOPED_TRACE("apex, non recursive");
-        nsec3Check(true, expected_closest_labels, nsec3_apex_txt,
+        nsec3Check(true, expected_closest_labels,
+                   nsec3_apex_txt + "\n" + nsec3_apex_rrsig_txt,
                    mock_finder->findNSEC3(Name("example.com"), false));
     }
 
     // Recursive mode doesn't change the result in this case.
     {
         SCOPED_TRACE("apex, recursive");
-        nsec3Check(true, expected_closest_labels, nsec3_apex_txt,
+        nsec3Check(true, expected_closest_labels,
+                   nsec3_apex_txt + "\n" + nsec3_apex_rrsig_txt,
                    mock_finder->findNSEC3(Name("example.com"), true));
     }
 
@@ -2000,7 +1992,8 @@ TEST_F(QueryTest, findNSEC3) {
     // returned.
     {
         SCOPED_TRACE("nxdomain, non recursive");
-        nsec3Check(false, 4, nsec3_www_txt,
+        nsec3Check(false, 4,
+                   nsec3_www_txt + "\n" + nsec3_www_rrsig_txt,
                    mock_finder->findNSEC3(Name("nxdomain.example.com"),
                                           false));
     }
@@ -2010,7 +2003,8 @@ TEST_F(QueryTest, findNSEC3) {
     {
         SCOPED_TRACE("nxdomain, recursive");
         nsec3Check(true, expected_closest_labels,
-                   string(nsec3_apex_txt) + string(nsec3_www_txt),
+                   nsec3_apex_txt + "\n" + nsec3_apex_rrsig_txt + "\n" +
+                   nsec3_www_txt + "\n" + nsec3_www_rrsig_txt,
                    mock_finder->findNSEC3(Name("nxdomain.example.com"), true));
     }
 
@@ -2019,7 +2013,8 @@ TEST_F(QueryTest, findNSEC3) {
     {
         SCOPED_TRACE("nxdomain, next closer != qname");
         nsec3Check(true, expected_closest_labels,
-                   string(nsec3_apex_txt) + string(nsec3_www_txt),
+                   nsec3_apex_txt + "\n" + nsec3_apex_rrsig_txt + "\n" +
+                   nsec3_www_txt + "\n" + nsec3_www_rrsig_txt,
                    mock_finder->findNSEC3(Name("nx.domain.example.com"),
                                           true));
     }
@@ -2027,13 +2022,15 @@ TEST_F(QueryTest, findNSEC3) {
     // In the rest of test we check hash comparison for wrap around cases.
     {
         SCOPED_TRACE("largest");
-        nsec3Check(false, 4, nsec3_apex_txt,
+        nsec3Check(false, 4,
+                   nsec3_apex_txt + "\n" + nsec3_apex_rrsig_txt,
                    mock_finder->findNSEC3(Name("nxdomain2.example.com"),
                                           false));
     }
     {
         SCOPED_TRACE("smallest");
-        nsec3Check(false, 4, nsec3_www_txt,
+        nsec3Check(false, 4,
+                   nsec3_www_txt + "\n" + nsec3_www_rrsig_txt,
                    mock_finder->findNSEC3(Name("nxdomain3.example.com"),
                                           false));
     }
@@ -2516,8 +2513,7 @@ TEST_F(QueryTest, DuplicateNameRemoval) {
     EXPECT_EQ(0, message.getRRCount(Message::SECTION_ADDITIONAL));
 
     // ... and fill it.
-    Query::ResponseCreator().create(message, answer, authority, additional,
-                                    false);
+    Query::ResponseCreator().create(message, answer, authority, additional);
 
     // Check counts in each section.  Note that these are RR counts,
     // not RRset counts.

+ 5 - 8
src/lib/cache/message_entry.cc

@@ -110,8 +110,7 @@ MessageEntry::getRRsetEntries(vector<RRsetEntryPtr>& rrset_entry_vec,
 void
 MessageEntry::addRRset(isc::dns::Message& message,
                        const vector<RRsetEntryPtr>& rrset_entry_vec,
-                       const isc::dns::Message::Section& section,
-                       bool dnssec_need)
+                       const isc::dns::Message::Section& section)
 {
     uint16_t start_index = 0;
     uint16_t end_index = answer_count_;
@@ -126,8 +125,7 @@ MessageEntry::addRRset(isc::dns::Message& message,
     }
 
     for (uint16_t index = start_index; index < end_index; ++index) {
-        message.addRRset(section, rrset_entry_vec[index]->getRRset(),
-                         dnssec_need);
+        message.addRRset(section, rrset_entry_vec[index]->getRRset());
     }
 }
 
@@ -156,10 +154,9 @@ MessageEntry::genMessage(const time_t& time_now,
         msg.setHeaderFlag(Message::HEADERFLAG_AA, false);
         msg.setHeaderFlag(Message::HEADERFLAG_TC, headerflag_tc_);
 
-        bool dnssec_need = msg.getEDNS().get();
-        addRRset(msg, rrset_entry_vec, Message::SECTION_ANSWER, dnssec_need);
-        addRRset(msg, rrset_entry_vec, Message::SECTION_AUTHORITY, dnssec_need);
-        addRRset(msg, rrset_entry_vec, Message::SECTION_ADDITIONAL, dnssec_need);
+        addRRset(msg, rrset_entry_vec, Message::SECTION_ANSWER);
+        addRRset(msg, rrset_entry_vec, Message::SECTION_AUTHORITY);
+        addRRset(msg, rrset_entry_vec, Message::SECTION_ADDITIONAL);
 
         return (true);
     }

+ 1 - 3
src/lib/cache/message_entry.h

@@ -154,11 +154,9 @@ protected:
     /// \param rrset_entry_vec vector for rrset entries in
     ///        different sections.
     /// \param section The section to add to
-    /// \param dnssec_need need dnssec records or not.
     void addRRset(isc::dns::Message& message,
                   const std::vector<RRsetEntryPtr>& rrset_entry_vec,
-                  const isc::dns::Message::Section& section,
-                  bool dnssec_need);
+                  const isc::dns::Message::Section& section);
 
     /// \brief Get the all the rrset entries for the message entry.
     ///

+ 1 - 1
src/lib/datasrc/Makefile.am

@@ -29,7 +29,7 @@ libb10_datasrc_la_SOURCES += cache.h cache.cc
 libb10_datasrc_la_SOURCES += rbnode_rrset.h
 libb10_datasrc_la_SOURCES += rbtree.h
 libb10_datasrc_la_SOURCES += zonetable.h zonetable.cc
-libb10_datasrc_la_SOURCES += zone.h zone_finder_context.cc
+libb10_datasrc_la_SOURCES += zone.h zone_finder.cc zone_finder_context.cc
 libb10_datasrc_la_SOURCES += result.h
 libb10_datasrc_la_SOURCES += logger.h logger.cc
 libb10_datasrc_la_SOURCES += client.h iterator.h

+ 2 - 2
src/lib/datasrc/data_source.cc

@@ -564,12 +564,12 @@ addToMessage(Query& q, const Message::Section sect, RRsetPtr rrset,
         if (rrset->getType() == RRType::RRSIG() ||
             !m.hasRRset(sect, rrset->getName(), rrset->getClass(),
                         rrset->getType())) {
-            m.addRRset(sect, rrset, false);
+            m.addRRset(sect, rrset);
         }
     } else {
         if (!m.hasRRset(sect, rrset->getName(), rrset->getClass(),
                         rrset->getType())) {
-            m.addRRset(sect, rrset, q.wantDnssec());
+            m.addRRset(sect, rrset);
         }
     }
 }

+ 32 - 16
src/lib/datasrc/database.cc

@@ -157,7 +157,7 @@ public:
         const isc::dns::RRType& type_covered =
             static_cast<isc::dns::rdata::generic::RRSIG*>(
                 sig_rdata.get())->typeCovered();
-        sigs[type_covered].push_back(sig_rdata);
+        sigs_[type_covered].push_back(sig_rdata);
     }
 
     // If the store contains signatures for the type of the given
@@ -165,21 +165,26 @@ public:
     void appendSignatures(isc::dns::RRsetPtr& rrset) const {
         std::map<isc::dns::RRType,
                  std::vector<isc::dns::rdata::RdataPtr> >::const_iterator
-            found = sigs.find(rrset->getType());
-        if (found != sigs.end()) {
+            found = sigs_.find(rrset->getType());
+        if (found != sigs_.end()) {
             BOOST_FOREACH(isc::dns::rdata::RdataPtr sig, found->second) {
                 rrset->addRRsig(sig);
             }
         }
     }
 
+    bool empty() const {
+        return (sigs_.empty());
+    }
+
 private:
-    std::map<isc::dns::RRType, std::vector<isc::dns::rdata::RdataPtr> > sigs;
+    std::map<isc::dns::RRType, std::vector<isc::dns::rdata::RdataPtr> > sigs_;
 };
 }
 
 DatabaseClient::Finder::FoundRRsets
 DatabaseClient::Finder::getRRsets(const string& name, const WantedTypes& types,
+                                  bool sigs,
                                   const string* construct_name, bool any,
                                   DatabaseAccessor::IteratorContextPtr context)
 {
@@ -213,7 +218,7 @@ DatabaseClient::Finder::getRRsets(const string& name, const WantedTypes& types,
         try {
             const RRType cur_type(columns[DatabaseAccessor::TYPE_COLUMN]);
 
-            if (cur_type == RRType::RRSIG()) {
+            if (sigs && (cur_type == RRType::RRSIG())) {
                 // If we get signatures before we get the actual data, we
                 // can't know which ones to keep and which to drop...
                 // So we keep a separate store of any signature that may be
@@ -228,7 +233,7 @@ DatabaseClient::Finder::getRRsets(const string& name, const WantedTypes& types,
             if (types.find(cur_type) != types.end() || any) {
                 // This type is requested, so put it into result
                 const RRTTL cur_ttl(columns[DatabaseAccessor::TTL_COLUMN]);
-                // Ths sigtype column was an optimization for finding the
+                // The sigtype column was an optimization for finding the
                 // relevant RRSIG RRs for a lookup. Currently this column is
                 // not used in this revised datasource implementation. We
                 // should either start using it again, or remove it from use
@@ -276,10 +281,12 @@ DatabaseClient::Finder::getRRsets(const string& name, const WantedTypes& types,
         isc_throw(DataSourceError, "CNAME shares domain " << name <<
                   " with something else");
     }
-    // Add signatures to all found RRsets
-    for (std::map<RRType, RRsetPtr>::iterator i(result.begin());
-         i != result.end(); ++ i) {
-        sig_store.appendSignatures(i->second);
+    if (!sig_store.empty()) {
+        // Add signatures to all found RRsets
+        for (std::map<RRType, RRsetPtr>::iterator i(result.begin());
+             i != result.end(); ++ i) {
+            sig_store.appendSignatures(i->second);
+        }
     }
     if (records_found && any) {
         result[RRType::ANY()] = RRsetPtr();
@@ -448,7 +455,9 @@ DatabaseClient::Finder::findDelegationPoint(const isc::dns::Name& name,
         // Look if there's NS or DNAME at this point of the tree, but ignore
         // the NS RRs at the apex of the zone.
         const FoundRRsets found = getRRsets(superdomain.toText(),
-                                            DELEGATION_TYPES());
+                                            DELEGATION_TYPES(),
+                                            ((options & FIND_DNSSEC) ==
+                                             FIND_DNSSEC));
         if (found.first) {
             // This node contains either NS or DNAME RRs so it does exist.
             const FoundIterator nsi(found.second.find(RRType::NS()));
@@ -581,6 +590,8 @@ DatabaseClient::Finder::findWildcardMatch(
         // RFC 4592 section 4.4).
         // Search for a match.  The types are the same as with original query.
         const FoundRRsets found = getRRsets(wildcard, final_types,
+                                            ((options & FIND_DNSSEC) ==
+                                             FIND_DNSSEC),
                                             &construct_name,
                                             type == RRType::ANY());
         if (found.first) {
@@ -685,7 +696,7 @@ DatabaseClient::Finder::FindDNSSECContext::probe() {
             // such cases).
             const string origin = finder_.getOrigin().toText();
             const FoundRRsets nsec3_found =
-                finder_.getRRsets(origin, NSEC3PARAM_TYPES());
+                finder_.getRRsets(origin, NSEC3PARAM_TYPES(), true);
             const FoundIterator nfi=
                 nsec3_found.second.find(RRType::NSEC3PARAM());
             is_nsec3_ = (nfi != nsec3_found.second.end());
@@ -696,7 +707,7 @@ DatabaseClient::Finder::FindDNSSECContext::probe() {
             // described in Section 10.4 of RFC 5155.
             if (!is_nsec3_) {
                 const FoundRRsets nsec_found =
-                    finder_.getRRsets(origin, NSEC_TYPES());
+                    finder_.getRRsets(origin, NSEC_TYPES(), true);
                 const FoundIterator nfi =
                     nsec_found.second.find(RRType::NSEC());
                 is_nsec_ = (nfi != nsec_found.second.end());
@@ -749,7 +760,7 @@ DatabaseClient::Finder::FindDNSSECContext::getDNSSECRRset(const Name &name,
         const Name& nsec_name =
             covering ? finder_.findPreviousName(name) : name;
         const FoundRRsets found = finder_.getRRsets(nsec_name.toText(),
-                                                    NSEC_TYPES());
+                                                    NSEC_TYPES(), true);
         const FoundIterator nci = found.second.find(RRType::NSEC());
         if (nci != found.second.end()) {
             return (nci->second);
@@ -976,6 +987,8 @@ DatabaseClient::Finder::findInternal(const Name& name, const RRType& type,
     WantedTypes final_types(FINAL_TYPES());
     final_types.insert(type);
     const FoundRRsets found = getRRsets(name.toText(), final_types,
+                                        ((options & FIND_DNSSEC) ==
+                                         FIND_DNSSEC),
                                         NULL, type == RRType::ANY());
     FindDNSSECContext dnssec_ctx(*this, options);
     if (found.first) {
@@ -1009,7 +1022,8 @@ DatabaseClient::Finder::findNSEC3(const Name& name, bool recursive) {
     // Now, we need to get the NSEC3 params from the apex and create the hash
     // creator for it.
     const FoundRRsets nsec3param(getRRsets(getOrigin().toText(),
-                                           NSEC3PARAM_TYPES()));
+                                           NSEC3PARAM_TYPES(),
+                                           true));
     const FoundIterator param(nsec3param.second.find(RRType::NSEC3PARAM()));
     if (!nsec3param.first || param == nsec3param.second.end()) {
         // No NSEC3 params? :-(
@@ -1049,6 +1063,7 @@ DatabaseClient::Finder::findNSEC3(const Name& name, bool recursive) {
         }
 
         const FoundRRsets nsec3(getRRsets(hash + "." + otext, NSEC3_TYPES(),
+                                          true,
                                           NULL, false, context));
 
         if (nsec3.first) {
@@ -1074,7 +1089,8 @@ DatabaseClient::Finder::findNSEC3(const Name& name, bool recursive) {
                 arg(labels).arg(prevHash);
             context = accessor_->getNSEC3Records(prevHash, zone_id_);
             const FoundRRsets prev_nsec3(getRRsets(prevHash + "." + otext,
-                                                   NSEC3_TYPES(), NULL, false,
+                                                   NSEC3_TYPES(), true,
+                                                   NULL, false,
                                                    context));
 
             if (!prev_nsec3.first) {

+ 3 - 0
src/lib/datasrc/database.h

@@ -963,6 +963,8 @@ public:
         ///
         /// \param name Which domain name should be scanned.
         /// \param types List of types the caller is interested in.
+        /// \param sigs Return RRSIGs if true is passed. Otherwise, no
+        ///     associated RRSIGs are set on the returned RRsets.
         /// \param construct_name If this is NULL, the resulting RRsets have
         ///     their name set to name. If it is not NULL, it overrides the
         ///     name and uses this one (this can be used for wildcard
@@ -984,6 +986,7 @@ public:
         ///     database or the database contains bad data.
         FoundRRsets getRRsets(const std::string& name,
                               const WantedTypes& types,
+                              bool sigs,
                               const std::string* construct_name = NULL,
                               bool any = false,
                               DatabaseAccessor::IteratorContextPtr srcContext =

+ 26 - 6
src/lib/datasrc/memory_datasrc.cc

@@ -738,7 +738,24 @@ prepareRRset(const Name& name, const ConstRBNodeRRsetPtr& rrset, bool rename,
         rrset->copyAdditionalNodes(*result);
         return (result);
     } else {
-        return (rrset);
+        ConstRRsetPtr sig_rrset = rrset->getRRsig();
+        if (sig_rrset &&
+            ((options & ZoneFinder::FIND_DNSSEC) == 0)) {
+            RRsetPtr result_base(new RRset(name, rrset->getClass(),
+                                           rrset->getType(),
+                                           rrset->getTTL()));
+            for (RdataIteratorPtr i(rrset->getRdataIterator());
+                 !i->isLast();
+                 i->next()) {
+                result_base->addRdata(i->getCurrent());
+            }
+
+            RBNodeRRsetPtr result(new RBNodeRRset(result_base));
+            rrset->copyAdditionalNodes(*result);
+            return (result);
+        } else {
+            return (rrset);
+        }
     }
 }
 
@@ -795,10 +812,10 @@ protected:
             }
             BOOST_FOREACH(const DomainPair& dom_it, *found_node_->getData()) {
                 getAdditionalForRRset(*dom_it.second, requested_types,
-                                      result);
+                                      result, options_);
             }
         } else {
-            getAdditionalForRRset(*rrset_, requested_types, result);
+            getAdditionalForRRset(*rrset_, requested_types, result, options_);
         }
     }
 
@@ -809,7 +826,8 @@ private:
     // type for each node.
     static void getAdditionalForRRset(const RBNodeRRset& rrset,
                                       const vector<RRType>& requested_types,
-                                      vector<ConstRRsetPtr>& result)
+                                      vector<ConstRRsetPtr>& result,
+                                      ZoneFinder::FindOptions options)
     {
         const vector<AdditionalNodeInfo>* additionals_ =
             rrset.getAdditionalNodes();
@@ -836,11 +854,13 @@ private:
                     // in case the caller has the same RRset but as a result
                     // of normal find() and needs to know they are of the same
                     // kind; otherwise we simply use the stored RBNodeRRset.
+                    ConstRRsetPtr rp;
                     if (wild_expanded) {
-                        result.push_back(found->second->getUnderlyingRRset());
+                        rp = found->second->getUnderlyingRRset();
                     } else {
-                        result.push_back(found->second);
+                        rp = found->second;
                     }
+                    result.push_back(ZoneFinder::stripRRsigs(rp, options));
                 }
             }
         }

+ 18 - 6
src/lib/datasrc/tests/Makefile.am

@@ -46,17 +46,21 @@ common_ldadd += $(GTEST_LDADD) $(SQLITE_LIBS)
 
 # The general tests
 run_unittests_SOURCES = $(common_sources)
-run_unittests_SOURCES += datasrc_unittest.cc
-run_unittests_SOURCES += static_unittest.cc
-run_unittests_SOURCES += query_unittest.cc
-run_unittests_SOURCES += cache_unittest.cc
+
+# Commented out by ticket #2165. If you re-enable these, please modify
+# EXTRA_DIST at the bottom of this file.
+#run_unittests_SOURCES += datasrc_unittest.cc
+#run_unittests_SOURCES += static_unittest.cc
+#run_unittests_SOURCES += query_unittest.cc
+#run_unittests_SOURCES += cache_unittest.cc
+#run_unittests_SOURCES += sqlite3_unittest.cc
+#run_unittests_SOURCES += test_datasrc.h test_datasrc.cc
+
 run_unittests_SOURCES += test_client.h test_client.cc
-run_unittests_SOURCES += test_datasrc.h test_datasrc.cc
 run_unittests_SOURCES += rbtree_unittest.cc
 run_unittests_SOURCES += logger_unittest.cc
 run_unittests_SOURCES += client_unittest.cc
 run_unittests_SOURCES += database_unittest.cc
-run_unittests_SOURCES += sqlite3_unittest.cc
 run_unittests_SOURCES += sqlite3_accessor_unittest.cc
 run_unittests_SOURCES += memory_datasrc_unittest.cc
 run_unittests_SOURCES += rbnode_rrset_unittest.cc
@@ -118,3 +122,11 @@ EXTRA_DIST += testdata/new_minor_schema.sqlite3
 EXTRA_DIST += testdata/newschema.sqlite3
 EXTRA_DIST += testdata/oldschema.sqlite3
 EXTRA_DIST += testdata/static.zone
+
+# Added by ticket #2165
+EXTRA_DIST += datasrc_unittest.cc
+EXTRA_DIST += static_unittest.cc
+EXTRA_DIST += query_unittest.cc
+EXTRA_DIST += cache_unittest.cc
+EXTRA_DIST += sqlite3_unittest.cc
+EXTRA_DIST += test_datasrc.h test_datasrc.cc

+ 48 - 12
src/lib/datasrc/tests/database_unittest.cc

@@ -1711,17 +1711,22 @@ doFindTest(ZoneFinder& finder,
         checkRRset(result->rrset, expected_name != Name(".") ? expected_name :
                    name, finder.getClass(), expected_type, expected_ttl,
                    expected_rdatas);
-
-        if (!expected_sig_rdatas.empty() && result->rrset->getRRsig()) {
-            checkRRset(result->rrset->getRRsig(), expected_name != Name(".") ?
-                       expected_name : name, finder.getClass(),
-                       isc::dns::RRType::RRSIG(), expected_ttl,
-                       expected_sig_rdatas);
-        } else if (expected_sig_rdatas.empty()) {
+        if ((options & ZoneFinder::FIND_DNSSEC) == ZoneFinder::FIND_DNSSEC) {
+            if (!expected_sig_rdatas.empty() && result->rrset->getRRsig()) {
+                checkRRset(result->rrset->getRRsig(),
+                           expected_name != Name(".") ? expected_name : name,
+                           finder.getClass(),
+                           isc::dns::RRType::RRSIG(), expected_ttl,
+                           expected_sig_rdatas);
+            } else if (expected_sig_rdatas.empty()) {
+                EXPECT_EQ(isc::dns::RRsetPtr(), result->rrset->getRRsig()) <<
+                    "Unexpected RRSIG: " << result->rrset->getRRsig()->toText();
+            } else {
+                ADD_FAILURE() << "Missing RRSIG";
+            }
+        } else if (result->rrset->getRRsig()) {
             EXPECT_EQ(isc::dns::RRsetPtr(), result->rrset->getRRsig()) <<
                 "Unexpected RRSIG: " << result->rrset->getRRsig()->toText();
-        } else {
-            ADD_FAILURE() << "Missing RRSIG";
         }
     } else if (expected_rdatas.empty()) {
         EXPECT_EQ(isc::dns::RRsetPtr(), result->rrset) <<
@@ -2842,6 +2847,35 @@ TYPED_TEST(DatabaseClientTest, anyFromFind) {
                                          RRType::ANY()), isc::Unexpected);
 }
 
+TYPED_TEST(DatabaseClientTest, findRRSIGsWithoutDNSSEC) {
+    // Trying to find RRSIG records directly should work even if
+    // FIND_DNSSEC flag is not specified.
+
+    boost::shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
+    ConstZoneFinderContextPtr result =
+        finder->find(isc::dns::Name("signed1.example.org."), RRType::RRSIG());
+
+    EXPECT_EQ(ZoneFinder::SUCCESS, result->code);
+
+    std::vector<std::string> expected_rdata;
+    expected_rdata.push_back(TEST_RECORDS[10][4]);
+    expected_rdata.push_back(TEST_RECORDS[11][4]);
+    expected_rdata.push_back(TEST_RECORDS[14][4]);
+
+    RdataIteratorPtr it(result->rrset->getRdataIterator());
+    std::vector<std::string> rdata;
+    while (!it->isLast()) {
+        rdata.push_back(it->getCurrent().toText());
+        it->next();
+    }
+    std::sort(rdata.begin(), rdata.end());
+    std::sort(expected_rdata.begin(), expected_rdata.end());
+    ASSERT_EQ(expected_rdata.size(), rdata.size());
+    for (size_t i(0); i < expected_rdata.size(); ++ i) {
+        EXPECT_EQ(expected_rdata[i], rdata[i]);
+    }
+}
+
 // Test the findAll method.
 TYPED_TEST(DatabaseClientTest, getAll) {
     // The domain doesn't exist, so we must get the right NSEC
@@ -3636,9 +3670,11 @@ TYPED_TEST(DatabaseClientTest, compoundUpdate) {
 TYPED_TEST(DatabaseClientTest, invalidRdata) {
     boost::shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
 
-    EXPECT_THROW(finder->find(Name("invalidrdata.example.org."), RRType::A()),
+    EXPECT_THROW(finder->find(Name("invalidrdata.example.org."),
+                              RRType::A()),
                  DataSourceError);
-    EXPECT_THROW(finder->find(Name("invalidrdata2.example.org."), RRType::A()),
+    EXPECT_THROW(finder->find(Name("invalidrdata2.example.org."),
+                              RRType::A(), ZoneFinder::FIND_DNSSEC),
                  DataSourceError);
 }
 
@@ -4053,7 +4089,7 @@ TYPED_TEST(DatabaseClientTest, findNSEC3) {
     this->current_accessor_->enableNSEC3();
 
     // The rest is in the function, it is shared with in-memory tests
-    performNSEC3Test(*finder);
+    performNSEC3Test(*finder, true);
 }
 
 }

+ 22 - 13
src/lib/datasrc/tests/faked_nsec3.cc

@@ -34,6 +34,11 @@ const char* const nsec3_common = " 300 IN NSEC3 1 1 12 aabbccdd "
     "2T7B4G4VSA5SMI47K61MV5BV1A22BOJR A RRSIG";
 const char* const nsec3_rrsig_common = " 300 IN RRSIG NSEC3 5 3 3600 "
     "20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE";
+const char* const nsec3_rrsig_common2 = " 300 IN RRSIG NSEC3 5 4 7200 "
+    "20100410172647 20100311172647 63192 example.org. gNIVj4T8t51fEU6k"
+    "OPpvK7HOGBFZGbalN5ZKmInyrww6UWZsUNdw07ge6/U6HfG+/s61RZ/Lis2M6yUWH"
+    "yXbNbj/QqwqgadG5dhxTArfuR02xP600x0fWX8LXzW4yLMdKVxGbzYT+vvGz71o8g"
+    "HSY5vYTtothcZQa4BMKhmGQEk=";
 const char* const apex_hash = "0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM";
 const char* const apex_hash_lower = "0p9mhaveqvm6t7vbl5lop2u3t2rp3tom";
 const char* const ns1_hash = "2T7B4G4VSA5SMI47K61MV5BV1A22BOJR";
@@ -93,8 +98,7 @@ void
 findNSEC3Check(bool expected_matched, uint8_t expected_labels,
                const string& expected_closest,
                const string& expected_next,
-               const ZoneFinder::FindNSEC3Result& result,
-               bool expected_sig)
+               const ZoneFinder::FindNSEC3Result& result)
 {
     EXPECT_EQ(expected_matched, result.matched);
     // Convert to int so the error messages would be more readable:
@@ -104,9 +108,6 @@ findNSEC3Check(bool expected_matched, uint8_t expected_labels,
     vector<ConstRRsetPtr> actual_rrsets;
     ASSERT_TRUE(result.closest_proof);
     actual_rrsets.push_back(result.closest_proof);
-    if (expected_sig) {
-        actual_rrsets.push_back(result.closest_proof->getRRsig());
-    }
     rrsetsCheck(expected_closest, actual_rrsets.begin(),
                 actual_rrsets.end());
 
@@ -116,30 +117,38 @@ findNSEC3Check(bool expected_matched, uint8_t expected_labels,
     } else {
         ASSERT_TRUE(result.next_proof);
         actual_rrsets.push_back(result.next_proof);
-        if (expected_sig) {
-            actual_rrsets.push_back(result.next_proof->getRRsig());
-        }
         rrsetsCheck(expected_next, actual_rrsets.begin(),
                     actual_rrsets.end());
     }
 }
 
 void
-performNSEC3Test(ZoneFinder &finder) {
+performNSEC3Test(ZoneFinder &finder, bool rrsigs_exist) {
     // Parameter validation: the query name must be in or below the zone
     EXPECT_THROW(finder.findNSEC3(Name("example.com"), false), OutOfZone);
     EXPECT_THROW(finder.findNSEC3(Name("org"), true), OutOfZone);
 
     const Name origin("example.org");
-    const string apex_nsec3_text = string(apex_hash) + ".example.org." +
+    string apex_nsec3_text = string(apex_hash) + ".example.org." +
         string(nsec3_common);
-    const string ns1_nsec3_text = string(ns1_hash) + ".example.org." +
+    string ns1_nsec3_text = string(ns1_hash) + ".example.org." +
         string(nsec3_common);
-    const string w_nsec3_text = string(w_hash) + ".example.org." +
+    string w_nsec3_text = string(w_hash) + ".example.org." +
         string(nsec3_common);
-    const string zzz_nsec3_text = string(zzz_hash) + ".example.org." +
+    string zzz_nsec3_text = string(zzz_hash) + ".example.org." +
         string(nsec3_common);
 
+    if (rrsigs_exist) {
+        apex_nsec3_text += "\n" + string(apex_hash) + ".example.org." +
+            string(nsec3_rrsig_common2);
+        ns1_nsec3_text += "\n" + string(ns1_hash) + ".example.org." +
+            string(nsec3_rrsig_common2);
+        w_nsec3_text += "\n" + string(w_hash) + ".example.org." +
+            string(nsec3_rrsig_common2);
+        zzz_nsec3_text += "\n" + string(zzz_hash) + ".example.org." +
+            string(nsec3_rrsig_common2);
+    }
+
     // Apex name.  It should have a matching NSEC3.
     {
         SCOPED_TRACE("apex, non recursive mode");

+ 2 - 3
src/lib/datasrc/tests/faked_nsec3.h

@@ -69,13 +69,12 @@ void
 findNSEC3Check(bool expected_matched, uint8_t expected_labels,
                const std::string& expected_closest,
                const std::string& expected_next,
-               const isc::datasrc::ZoneFinder::FindNSEC3Result& result,
-               bool expected_sig = false);
+               const isc::datasrc::ZoneFinder::FindNSEC3Result& result);
 
 // Perform the shared part of NSEC3 test (shared between in-memory and database
 // tests).
 void
-performNSEC3Test(ZoneFinder &finder);
+performNSEC3Test(ZoneFinder &finder, bool rrsigs_exist = false);
 
 }
 }

+ 5 - 3
src/lib/datasrc/tests/memory_datasrc_unittest.cc

@@ -1263,12 +1263,14 @@ TEST_F(InMemoryZoneFinderTest, loadFromIterator) {
     RRsetPtr expected_answer = textToRRset(soa_txt, RRClass::IN(), origin_);
     expected_answer->addRRsig(textToRRset(soa_sig_txt));
     findTest(origin_, RRType::SOA(), ZoneFinder::SUCCESS, true,
-             expected_answer);
+             expected_answer, ZoneFinder::RESULT_DEFAULT, NULL,
+             ZoneFinder::FIND_DNSSEC);
 
     expected_answer = textToRRset(a_txt);
     expected_answer->addRRsig(textToRRset(a_sig_txt));
     findTest(Name("ns1.example.org"), RRType::A(), ZoneFinder::SUCCESS, true,
-             expected_answer);
+             expected_answer, ZoneFinder::RESULT_DEFAULT, NULL,
+             ZoneFinder::FIND_DNSSEC);
 
     // File name should be (re)set to empty.
     EXPECT_TRUE(zone_finder_.getFileName().empty());
@@ -2065,7 +2067,7 @@ TEST_F(InMemoryZoneFinderTest, addNSEC3WithRRSIG) {
     // Then look for it.  The NSEC3 should have the RRSIG that was just added.
     findNSEC3Check(true, origin_.getLabelCount(),
                    nsec3_text + "\n" + nsec3_rrsig_text, "",
-                   zone_finder_.findNSEC3(Name("example.org"), false), true);
+                   zone_finder_.findNSEC3(Name("example.org"), false));
 
     // Duplicate add of RRSIG for the same NSEC3 is prohibited.
     EXPECT_THROW(zone_finder_.add(textToRRset(nsec3_rrsig_text)),

+ 4 - 0
src/lib/datasrc/tests/zone_finder_context_unittest.cc

@@ -326,7 +326,11 @@ TEST_P(ZoneFinderContextTest, getAdditionalWithSIG) {
 
     ctx->getAdditional(REQUESTED_BOTH, result_sets_);
     rrsetsCheck("ns1.example.org. 3600 IN A 192.0.2.1\n"
+                "ns1.example.org. 3600 IN RRSIG	A 7 3 3600 20150420235959 "
+                "20051021000000 40430 example.org. FAKEFAKE\n"
                 "ns1.example.org. 3600 IN AAAA 2001:db8::1\n"
+                "ns1.example.org. 3600 IN RRSIG	AAAA 7 3 3600 20150420235959 "
+                "20051021000000 40430 example.org. FAKEFAKEFAKE\n"
                 "ns2.example.org. 3600 IN A 192.0.2.2\n",
                 result_sets_.begin(), result_sets_.end());
 

+ 7 - 0
src/lib/datasrc/zone.h

@@ -134,6 +134,11 @@ protected:
     };
 
 public:
+    /// \brief A helper function to strip RRSIGs when FIND_DNSSEC is not
+    /// requested.
+    static isc::dns::ConstRRsetPtr
+    stripRRsigs(isc::dns::ConstRRsetPtr rp, const FindOptions options);
+
     /// \brief Context of the result of a find() call.
     ///
     /// This class encapsulates results and (possibly) associated context
@@ -299,7 +304,9 @@ public:
     private:
         ZoneFinder& finder_;
         const FindResultFlags flags_;
+    protected:
         const FindOptions options_;
+    private:
         std::vector<isc::dns::ConstRRsetPtr> all_set_;
     };
 

+ 55 - 0
src/lib/datasrc/zone_finder.cc

@@ -0,0 +1,55 @@
+// Copyright (C) 2012  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 <dns/rdata.h>
+#include <dns/rrset.h>
+#include <dns/rrtype.h>
+#include <dns/rdataclass.h>
+
+#include <datasrc/zone.h>
+
+using namespace std;
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+
+namespace isc {
+namespace datasrc {
+
+isc::dns::ConstRRsetPtr
+ZoneFinder::stripRRsigs(isc::dns::ConstRRsetPtr rp,
+                        const FindOptions options) {
+    if (rp) {
+        isc::dns::ConstRRsetPtr sig_rrset = rp->getRRsig();
+        if (sig_rrset &&
+            ((options & ZoneFinder::FIND_DNSSEC) == 0)) {
+            isc::dns::RRsetPtr result_base
+                (new isc::dns::RRset(rp->getName(),
+                                     rp->getClass(),
+                                     rp->getType(),
+                                     rp->getTTL()));
+            for (isc::dns::RdataIteratorPtr i(rp->getRdataIterator());
+                 !i->isLast();
+                 i->next()) {
+                result_base->addRdata(i->getCurrent());
+            }
+
+            return (result_base);
+        }
+    }
+
+    return (rp);
+}
+
+} // namespace datasrc
+} // datasrc isc

+ 3 - 7
src/lib/dns/message.cc

@@ -493,7 +493,7 @@ Message::getRRCount(const Section section) const {
 }
 
 void
-Message::addRRset(const Section section, RRsetPtr rrset, const bool sign) {
+Message::addRRset(const Section section, RRsetPtr rrset) {
     if (!rrset) {
         isc_throw(InvalidParameter,
                   "NULL RRset is given to Message::addRRset");
@@ -508,12 +508,7 @@ Message::addRRset(const Section section, RRsetPtr rrset, const bool sign) {
 
     impl_->rrsets_[section].push_back(rrset);
     impl_->counts_[section] += rrset->getRdataCount();
-
-    RRsetPtr sp = rrset->getRRsig();
-    if (sign && sp != NULL) {
-        impl_->rrsets_[section].push_back(sp);
-        impl_->counts_[section] += sp->getRdataCount();
-    }
+    impl_->counts_[section] += rrset->getRRsigDataCount();
 }
 
 bool
@@ -555,6 +550,7 @@ Message::removeRRset(const Section section, RRsetIterator& iterator) {
 
             // Found the matching RRset so remove it & ignore rest
             impl_->counts_[section] -= (*iterator)->getRdataCount();
+            impl_->counts_[section] -= (*iterator)->getRRsigDataCount();
             impl_->rrsets_[section].erase(i);
             removed = true;
             break;

+ 1 - 6
src/lib/dns/message.h

@@ -459,9 +459,6 @@ public:
     /// \brief Add a (pointer like object of) RRset to the given section
     /// of the message.
     ///
-    /// This interface takes into account the RRSIG possibly attached to
-    /// \c rrset.  This interface design needs to be revisited later.
-    ///
     /// Note that \c addRRset() does not currently check for duplicate
     /// data before inserting RRsets.  The caller is responsible for
     /// checking for these (see \c hasRRset() below).
@@ -473,9 +470,7 @@ public:
     ///
     /// \param section The message section to which the rrset is to be added
     /// \param rrset The rrset to be added.  Must not be NULL.
-    /// \param sign If true, and if \c rrset has associated RRSIGs, the
-    /// RRSIGs will also be added to the same section of the message.
-    void addRRset(const Section section, RRsetPtr rrset, bool sign = false);
+    void addRRset(const Section section, RRsetPtr rrset);
 
     /// \brief Determine whether the given section already has an RRset
     /// matching the given name, RR class and RR type.

+ 3 - 7
src/lib/dns/python/message_python.cc

@@ -143,9 +143,7 @@ PyMethodDef Message_methods[] = {
     { "add_rrset", reinterpret_cast<PyCFunction>(Message_addRRset), METH_VARARGS,
       "Add an RRset to the given section of the message.\n"
       "The first argument is of type Section\n"
-      "The second is of type RRset\n"
-      "The third argument is an optional Boolean specifying whether "
-      "the RRset is signed"},
+      "The second is of type RRset"},
     { "clear", reinterpret_cast<PyCFunction>(Message_clear), METH_VARARGS,
       "Clears the message content (if any) and reinitialize the "
       "message in the given mode\n"
@@ -571,17 +569,15 @@ Message_addQuestion(s_Message* self, PyObject* args) {
 
 PyObject*
 Message_addRRset(s_Message* self, PyObject* args) {
-    PyObject *sign = Py_False;
     int section;
     PyObject* rrset;
-    if (!PyArg_ParseTuple(args, "iO!|O!", &section, &rrset_type, &rrset,
-                          &PyBool_Type, &sign)) {
+    if (!PyArg_ParseTuple(args, "iO!", &section, &rrset_type, &rrset)) {
         return (NULL);
     }
 
     try {
         self->cppobj->addRRset(static_cast<Message::Section>(section),
-                               PyRRset_ToRRsetPtr(rrset), sign == Py_True);
+                               PyRRset_ToRRsetPtr(rrset));
         Py_RETURN_NONE;
     } catch (const InvalidMessageOperation& imo) {
         PyErr_SetString(po_InvalidMessageOperation, imo.what());

+ 41 - 0
src/lib/dns/rrset.cc

@@ -258,6 +258,47 @@ RRset::getRRsigDataCount() const {
     }
 }
 
+unsigned int
+RRset::toWire(OutputBuffer& buffer) const {
+    unsigned int rrs_written;
+
+    rrs_written = rrsetToWire<OutputBuffer>(*this, buffer, 0);
+    if (getRdataCount() > rrs_written) {
+        return (rrs_written);
+    }
+
+    if (rrsig_) {
+        rrs_written += rrsetToWire<OutputBuffer>(*(rrsig_.get()), buffer, 0);
+    }
+
+    return (rrs_written);
+}
+
+unsigned int
+RRset::toWire(AbstractMessageRenderer& renderer) const {
+    unsigned int rrs_written;
+
+    rrs_written =
+        rrsetToWire<AbstractMessageRenderer>(*this, renderer,
+                                             renderer.getLengthLimit());
+    if (getRdataCount() > rrs_written) {
+        renderer.setTruncated();
+        return (rrs_written);
+    }
+
+    if (rrsig_) {
+        rrs_written +=
+            rrsetToWire<AbstractMessageRenderer>(*(rrsig_.get()), renderer,
+                                                 renderer.getLengthLimit());
+    }
+
+    if (getRdataCount() + getRRsigDataCount() > rrs_written) {
+        renderer.setTruncated();
+    }
+
+    return (rrs_written);
+}
+
 namespace {
 class BasicRdataIterator : public RdataIterator {
 private:

+ 11 - 0
src/lib/dns/rrset.h

@@ -828,6 +828,17 @@ public:
 
     virtual ~RRset();
 
+    /// \brief Render the RRset in the wire format with name compression and
+    /// truncation handling.
+    ///
+    /// See \c AbstractRRset::toWire(MessageRenderer&)const.
+    virtual unsigned int toWire(AbstractMessageRenderer& renderer) const;
+
+    /// \brief Render the RRset in the wire format without any compression.
+    ///
+    /// See \c AbstractRRset::toWire(OutputBuffer&)const.
+    virtual unsigned int toWire(isc::util::OutputBuffer& buffer) const;
+
     /// \brief Updates the owner name of the \c RRset, including RRSIGs if any
     virtual void setName(const Name& n) {
         BasicRRset::setName(n);

+ 58 - 24
src/lib/dns/tests/message_unittest.cc

@@ -289,32 +289,22 @@ TEST_F(MessageTest, getRRCount) {
 }
 
 TEST_F(MessageTest, addRRset) {
-    // default case
+    // initially, we have 0
+    EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_ANSWER));
+
+    // add two A RRs (unsigned)
     message_render.addRRset(Message::SECTION_ANSWER, rrset_a);
     EXPECT_EQ(rrset_a,
               *message_render.beginSection(Message::SECTION_ANSWER));
     EXPECT_EQ(2, message_render.getRRCount(Message::SECTION_ANSWER));
 
-    // signed RRset, default case
     message_render.clear(Message::RENDER);
-    message_render.addRRset(Message::SECTION_ANSWER, rrset_aaaa);
-    EXPECT_EQ(rrset_aaaa,
-              *message_render.beginSection(Message::SECTION_ANSWER));
-    EXPECT_EQ(1, message_render.getRRCount(Message::SECTION_ANSWER));
 
-    // signed RRset, add with the RRSIG.  getRRCount() should return 2
-    message_render.clear(Message::RENDER);
-    message_render.addRRset(Message::SECTION_ANSWER, rrset_aaaa, true);
+    // add one AAAA RR (signed)
+    message_render.addRRset(Message::SECTION_ANSWER, rrset_aaaa);
     EXPECT_EQ(rrset_aaaa,
               *message_render.beginSection(Message::SECTION_ANSWER));
     EXPECT_EQ(2, message_render.getRRCount(Message::SECTION_ANSWER));
-
-    // signed RRset, add explicitly without RRSIG.
-    message_render.clear(Message::RENDER);
-    message_render.addRRset(Message::SECTION_ANSWER, rrset_aaaa, false);
-    EXPECT_EQ(rrset_aaaa,
-              *message_render.beginSection(Message::SECTION_ANSWER));
-    EXPECT_EQ(1, message_render.getRRCount(Message::SECTION_ANSWER));
 }
 
 TEST_F(MessageTest, badAddRRset) {
@@ -381,9 +371,9 @@ TEST_F(MessageTest, removeRRset) {
         RRClass::IN(), RRType::A()));
     EXPECT_TRUE(message_render.hasRRset(Message::SECTION_ANSWER, test_name,
         RRClass::IN(), RRType::AAAA()));
-    EXPECT_EQ(3, message_render.getRRCount(Message::SECTION_ANSWER));
+    EXPECT_EQ(4, message_render.getRRCount(Message::SECTION_ANSWER));
 
-    // Locate the AAAA RRset and remove it; this has one RR in it.
+    // Locate the AAAA RRset and remove it and any associated RRSIGs
     RRsetIterator i = message_render.beginSection(Message::SECTION_ANSWER);
     if ((*i)->getType() == RRType::A()) {
         ++i;
@@ -420,7 +410,7 @@ TEST_F(MessageTest, clearAnswerSection) {
         RRClass::IN(), RRType::A()));
     ASSERT_TRUE(message_render.hasRRset(Message::SECTION_ANSWER, test_name,
         RRClass::IN(), RRType::AAAA()));
-    ASSERT_EQ(3, message_render.getRRCount(Message::SECTION_ANSWER));
+    ASSERT_EQ(4, message_render.getRRCount(Message::SECTION_ANSWER));
 
     message_render.clearSection(Message::SECTION_ANSWER);
     EXPECT_FALSE(message_render.hasRRset(Message::SECTION_ANSWER, test_name,
@@ -439,7 +429,7 @@ TEST_F(MessageTest, clearAuthoritySection) {
         RRClass::IN(), RRType::A()));
     ASSERT_TRUE(message_render.hasRRset(Message::SECTION_AUTHORITY, test_name,
         RRClass::IN(), RRType::AAAA()));
-    ASSERT_EQ(3, message_render.getRRCount(Message::SECTION_AUTHORITY));
+    ASSERT_EQ(4, message_render.getRRCount(Message::SECTION_AUTHORITY));
 
     message_render.clearSection(Message::SECTION_AUTHORITY);
     EXPECT_FALSE(message_render.hasRRset(Message::SECTION_AUTHORITY, test_name,
@@ -458,7 +448,7 @@ TEST_F(MessageTest, clearAdditionalSection) {
         RRClass::IN(), RRType::A()));
     ASSERT_TRUE(message_render.hasRRset(Message::SECTION_ADDITIONAL, test_name,
         RRClass::IN(), RRType::AAAA()));
-    ASSERT_EQ(3, message_render.getRRCount(Message::SECTION_ADDITIONAL));
+    ASSERT_EQ(4, message_render.getRRCount(Message::SECTION_ADDITIONAL));
 
     message_render.clearSection(Message::SECTION_ADDITIONAL);
     EXPECT_FALSE(message_render.hasRRset(Message::SECTION_ADDITIONAL, test_name,
@@ -529,7 +519,7 @@ TEST_F(MessageTest, appendSection) {
         RRClass::IN(), RRType::A()));
 
     target.appendSection(Message::SECTION_ADDITIONAL, message_render);
-    EXPECT_EQ(3, target.getRRCount(Message::SECTION_ADDITIONAL));
+    EXPECT_EQ(4, target.getRRCount(Message::SECTION_ADDITIONAL));
     EXPECT_TRUE(target.hasRRset(Message::SECTION_ADDITIONAL, test_name,
         RRClass::IN(), RRType::A()));
     EXPECT_TRUE(target.hasRRset(Message::SECTION_ADDITIONAL, test_name,
@@ -539,7 +529,7 @@ TEST_F(MessageTest, appendSection) {
     Message source2(Message::RENDER);
     source2.addRRset(Message::SECTION_ANSWER, rrset_aaaa);
     target.appendSection(Message::SECTION_ANSWER, source2);
-    EXPECT_EQ(3, target.getRRCount(Message::SECTION_ANSWER));
+    EXPECT_EQ(4, target.getRRCount(Message::SECTION_ANSWER));
     EXPECT_TRUE(target.hasRRset(Message::SECTION_ANSWER, test_name,
         RRClass::IN(), RRType::A()));
     EXPECT_TRUE(target.hasRRset(Message::SECTION_ANSWER, test_name,
@@ -766,7 +756,7 @@ TEST_F(MessageTest, toWireSigned) {
     rrset_a->addRRsig(rrset_rrsig);
     EXPECT_EQ(2, rrset_a->getRRsigDataCount());
 
-    message_render.addRRset(Message::SECTION_ANSWER, rrset_a, true);
+    message_render.addRRset(Message::SECTION_ANSWER, rrset_a);
 
     EXPECT_EQ(1, message_render.getRRCount(Message::SECTION_QUESTION));
     EXPECT_EQ(4, message_render.getRRCount(Message::SECTION_ANSWER));
@@ -780,6 +770,50 @@ TEST_F(MessageTest, toWireSigned) {
                         renderer.getLength(), &data[0], data.size());
 }
 
+TEST_F(MessageTest, toWireSignedAndTruncated) {
+    message_render.setQid(0x75c1);
+    message_render.setOpcode(Opcode::QUERY());
+    message_render.setRcode(Rcode::NOERROR());
+    message_render.setHeaderFlag(Message::HEADERFLAG_QR, true);
+    message_render.setHeaderFlag(Message::HEADERFLAG_RD, true);
+    message_render.setHeaderFlag(Message::HEADERFLAG_AA, true);
+    message_render.addQuestion(Question(Name("test.example.com"), RRClass::IN(),
+                                        RRType::TXT()));
+
+    RRsetPtr rrset_txt = RRsetPtr(new RRset(test_name, RRClass::IN(),
+                                            RRType::TXT(), RRTTL(3600)));
+    rrset_txt->addRdata(generic::TXT(string(255, 'a')));
+    rrset_txt->addRdata(generic::TXT(string(255, 'b')));
+    rrset_txt->addRdata(generic::TXT(string(255, 'c')));
+    rrset_txt->addRdata(generic::TXT(string(255, 'd')));
+    rrset_txt->addRdata(generic::TXT(string(255, 'e')));
+    rrset_txt->addRdata(generic::TXT(string(255, 'f')));
+    rrset_txt->addRdata(generic::TXT(string(255, 'g')));
+    rrset_txt->addRdata(generic::TXT(string(255, 'h')));
+
+    rrset_rrsig = RRsetPtr(new RRset(test_name, RRClass::IN(),
+                                     RRType::RRSIG(), RRTTL(3600)));
+    // one signature algorithm (5 = RSA/SHA-1)
+    rrset_rrsig->addRdata(generic::RRSIG("TXT 5 3 3600 "
+                                         "20000101000000 20000201000000 "
+                                         "12345 example.com. FAKEFAKEFAKE"));
+    rrset_txt->addRRsig(rrset_rrsig);
+    EXPECT_EQ(1, rrset_txt->getRRsigDataCount());
+
+    message_render.addRRset(Message::SECTION_ANSWER, rrset_txt);
+
+    EXPECT_EQ(1, message_render.getRRCount(Message::SECTION_QUESTION));
+    EXPECT_EQ(9, message_render.getRRCount(Message::SECTION_ANSWER));
+    EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_AUTHORITY));
+    EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_ADDITIONAL));
+
+    message_render.toWire(renderer);
+    vector<unsigned char> data;
+    UnitTestUtil::readWireData("message_toWire7", data);
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
+                        renderer.getLength(), &data[0], data.size());
+}
+
 TEST_F(MessageTest, toWireInParseMode) {
     // toWire() isn't allowed in the parse mode.
     EXPECT_THROW(message_parse.toWire(renderer), InvalidMessageOperation);

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

@@ -87,7 +87,7 @@ EXTRA_DIST += message_fromWire19.spec message_fromWire20.spec
 EXTRA_DIST += message_fromWire21.spec message_fromWire22.spec
 EXTRA_DIST += message_toWire1 message_toWire2.spec message_toWire3.spec
 EXTRA_DIST += message_toWire4.spec message_toWire5.spec
-EXTRA_DIST += message_toWire6
+EXTRA_DIST += message_toWire6 message_toWire7
 EXTRA_DIST += message_toText1.txt message_toText1.spec
 EXTRA_DIST += message_toText2.txt message_toText2.spec
 EXTRA_DIST += message_toText3.txt message_toText3.spec

+ 1 - 1
src/lib/dns/tests/testdata/message_toWire6

@@ -2,7 +2,7 @@
 # A simple DNS query message (with a signed response)
 # ID = 0x75c1
 # QR=1 (response), Opcode=0, AA=1, RD=1 (other fields are 0)
-# QDCOUNT=1, ANCOUNT=2, other COUNTS=0
+# QDCOUNT=1, ANCOUNT=4, other COUNTS=0
 # Question: test.example.com. IN A
 # Answer:
 #  test.example.com. 3600 IN A 192.0.2.1

+ 35 - 0
src/lib/dns/tests/testdata/message_toWire7

@@ -0,0 +1,35 @@
+#
+# A simple DNS query message (with a signed response)
+# ID = 0x75c1
+# QR=1 (response), Opcode=0, AA=1, RD=1 (other fields are 0)
+# QDCOUNT=1, ANCOUNT=1, ADCOUNT=0
+# Question: test.example.com. IN TXT
+# Answer:
+#  test.example.com. 3600 IN TXT aaaaa...
+#
+75c1 8700
+0001 0001 0000 0000
+#(4) t  e  s  t (7) e  x  a  m  p  l  e (3) c  o  m  .
+ 04 74 65 73 74 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00
+0010 0001
+# same name, fully compressed
+c0 0c
+# TTL=3600, TXT, IN, RDLENGTH=256, RDATA
+0010 0001 00000e10 0100 ff
+# 'a' repeated 255 times
+61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61
+61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61
+61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61
+61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61
+61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61
+61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61
+61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61
+61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61
+61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61
+61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61
+61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61
+61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61
+61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61
+61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61
+61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61
+61 61 61 61 61 61 61 61 61 61 61 61 61 61 61

+ 2 - 14
src/lib/python/isc/datasrc/tests/datasrc_test.py

@@ -482,15 +482,9 @@ class DataSrcUpdater(unittest.TestCase):
                          rrset.to_text())
 
         rrset_to_delete = rrset;
-
-        # can't delete rrset with associated sig. Abuse that to force an
-        # exception first, then remove the sig, then delete the record
-        updater = dsc.get_updater(isc.dns.Name("example.com"), True)
-        self.assertRaises(isc.datasrc.Error, updater.delete_rrset,
-                          rrset_to_delete)
-
         rrset_to_delete.remove_rrsig()
 
+        updater = dsc.get_updater(isc.dns.Name("example.com"), True)
         updater.delete_rrset(rrset_to_delete)
 
         # The record should be gone in the updater, but not in the original
@@ -582,15 +576,9 @@ class DataSrcUpdater(unittest.TestCase):
                          rrset.to_text())
 
         rrset_to_delete = rrset;
-
-        # can't delete rrset with associated sig. Abuse that to force an
-        # exception first, then remove the sig, then delete the record
-        updater = dsc.get_updater(isc.dns.Name("example.com"), True)
-        self.assertRaises(isc.datasrc.Error, updater.delete_rrset,
-                          rrset_to_delete)
-
         rrset_to_delete.remove_rrsig()
 
+        updater = dsc.get_updater(isc.dns.Name("example.com"), True)
         updater.delete_rrset(rrset_to_delete)
 
         # The record should be gone in the updater, but not in the original

+ 1 - 1
src/lib/resolve/resolve.cc

@@ -26,7 +26,7 @@ namespace {
             message_(message), section_(sect)
         {}
         void operator()(const RRsetPtr rrset) {
-            message_->addRRset(section_, rrset, true);
+            message_->addRRset(section_, rrset);
         }
         MessagePtr message_;
         const Message::Section section_;

+ 16 - 0
src/lib/testutils/dnsmessage_test.h

@@ -168,6 +168,10 @@ public:
     {}
     void operator()(isc::dns::ConstRRsetPtr rrset) {
         output_ += "  " + rrset->toText();
+
+        if (rrset->getRRsig()) {
+            output_ += "  " + rrset->getRRsig()->toText();
+        }
     }
 private:
     std::string& output_;
@@ -256,6 +260,7 @@ rrsetsCheck(EXPECTED_ITERATOR expected_begin, EXPECTED_ITERATOR expected_end,
         if (found_rrset_it != expected_end) {
             rrsetCheck(*found_rrset_it, *it);
             ++rrset_matched;
+            rrset_matched += (*it)->getRRsigDataCount();
         }
     }
 
@@ -265,9 +270,20 @@ rrsetsCheck(EXPECTED_ITERATOR expected_begin, EXPECTED_ITERATOR expected_end,
                      "Expected:\n" + expected_text);
         // make sure all expected RRsets are in actual sets
         EXPECT_EQ(std::distance(expected_begin, expected_end), rrset_matched);
+
+#if (0)
+        // TODO: see bug #2223. The following code was
+        // disabled by #2165. The RRSIG RRsets are no longer directly
+        // stored in the Message's rrsets, so the iterator will not find
+        // them. The expected text used in many tests are flattened,
+        // where the RRSIGs are inline. In other words, RRSIGs may occur
+        // between (expected_begin, expected_end) but not between
+        // (actual_begin, actual_end).
+
         // make sure rrsets only contains expected RRsets
         EXPECT_EQ(std::distance(expected_begin, expected_end),
                   std::distance(actual_begin, actual_end));
+#endif
     }
 }