Browse Source

[1611] updated the database zonefinder implementation to use new FindResult.
specifically, it now uses the separate flags field for wildcard and NSEC/NSEC3
signed information. WILDCARD(_xxx) result codes were deprecated and removed.
Adjusted tests and python code accordingly. Finally removed the temporary
workaround in auth::Query.

JINMEI Tatuya 13 years ago
parent
commit
2e0fd5445e

+ 15 - 52
src/bin/auth/query.cc

@@ -30,42 +30,6 @@ using namespace isc::dns;
 using namespace isc::datasrc;
 using namespace isc::dns::rdata;
 
-namespace {
-// This is a temporary wrapper to convert old format of FindResult to new
-// one, until we update all supported data source implementations.
-ZoneFinder::FindResult
-findWrapper(const ZoneFinder::FindResult& orig_result) {
-    // Retrieve the original flags
-    ZoneFinder::FindResultFlags flags = ZoneFinder::RESULT_DEFAULT;
-    if (orig_result.isWildcard()) {
-        flags = flags | ZoneFinder::RESULT_WILDCARD;
-    }
-    if (orig_result.isNSECSigned()) {
-        flags = flags | ZoneFinder::RESULT_NSEC_SIGNED;
-    }
-    if (orig_result.isNSEC3Signed()) {
-        flags = flags | ZoneFinder::RESULT_NSEC3_SIGNED;
-    }
-
-    // Convert older code to new one, adjusting flags if necessary
-    ZoneFinder::Result code = orig_result.code;
-    if (code == ZoneFinder::WILDCARD) {
-        code = ZoneFinder::SUCCESS;
-        flags = flags | ZoneFinder::RESULT_WILDCARD;
-    } else if (code == ZoneFinder::WILDCARD_CNAME) {
-        code = ZoneFinder::CNAME;
-        flags = flags | ZoneFinder::RESULT_WILDCARD;
-    } else if (code == ZoneFinder::WILDCARD_NXRRSET) {
-        code = ZoneFinder::NXRRSET;
-        flags = flags | ZoneFinder::RESULT_WILDCARD;
-    }
-    if (orig_result.rrset && orig_result.rrset->getType() == RRType::NSEC()) {
-        flags = flags | ZoneFinder::RESULT_NSEC_SIGNED;
-    }
-    return (ZoneFinder::FindResult(code, orig_result.rrset, flags));
-}
-}
-
 namespace isc {
 namespace auth {
 
@@ -105,8 +69,8 @@ Query::addAdditionalAddrs(ZoneFinder& zone, const Name& qname,
 
     // Find A rrset
     if (qname_ != qname || qtype_ != RRType::A()) {
-        ZoneFinder::FindResult a_result =
-            findWrapper(zone.find(qname, RRType::A(), options | dnssec_opt_));
+        ZoneFinder::FindResult a_result = zone.find(qname, RRType::A(),
+                                                    options | dnssec_opt_);
         if (a_result.code == ZoneFinder::SUCCESS) {
             response_.addRRset(Message::SECTION_ADDITIONAL,
                     boost::const_pointer_cast<RRset>(a_result.rrset), dnssec_);
@@ -115,9 +79,8 @@ Query::addAdditionalAddrs(ZoneFinder& zone, const Name& qname,
 
     // Find AAAA rrset
     if (qname_ != qname || qtype_ != RRType::AAAA()) {
-        ZoneFinder::FindResult aaaa_result =
-            findWrapper(zone.find(qname, RRType::AAAA(),
-                                  options | dnssec_opt_));
+        ZoneFinder::FindResult aaaa_result = zone.find(qname, RRType::AAAA(),
+                                                       options | dnssec_opt_);
         if (aaaa_result.code == ZoneFinder::SUCCESS) {
             response_.addRRset(Message::SECTION_ADDITIONAL,
                     boost::const_pointer_cast<RRset>(aaaa_result.rrset),
@@ -128,9 +91,9 @@ Query::addAdditionalAddrs(ZoneFinder& zone, const Name& qname,
 
 void
 Query::addSOA(ZoneFinder& finder) {
-    ZoneFinder::FindResult soa_result =
-        findWrapper(finder.find(finder.getOrigin(),
-                                RRType::SOA(), dnssec_opt_));
+    ZoneFinder::FindResult soa_result = finder.find(finder.getOrigin(),
+                                                    RRType::SOA(),
+                                                    dnssec_opt_);
     if (soa_result.code != ZoneFinder::SUCCESS) {
         isc_throw(NoSOA, "There's no SOA record in zone " <<
             finder.getOrigin().toText());
@@ -186,7 +149,7 @@ Query::addNXDOMAINProof(ZoneFinder& finder, ConstRRsetPtr nsec) {
     // otherwise we shouldn't have got NXDOMAIN for the original query in
     // the first place).
     const ZoneFinder::FindResult fresult =
-        findWrapper(finder.find(wildname, RRType::NSEC(), dnssec_opt_));
+        finder.find(wildname, RRType::NSEC(), dnssec_opt_);
     if (fresult.code != ZoneFinder::NXDOMAIN || !fresult.rrset ||
         fresult.rrset->getRdataCount() == 0) {
         isc_throw(BadNSEC, "Unexpected result for wildcard NXDOMAIN proof");
@@ -210,8 +173,8 @@ Query::addWildcardProof(ZoneFinder& finder) {
     // substitution.  Confirm that by specifying NO_WILDCARD.  It should result
     // in NXDOMAIN and an NSEC RR that proves it should be returned.
     const ZoneFinder::FindResult fresult =
-        findWrapper(finder.find(qname_, RRType::NSEC(),
-                                dnssec_opt_ | ZoneFinder::NO_WILDCARD));
+        finder.find(qname_, RRType::NSEC(),
+                    dnssec_opt_ | ZoneFinder::NO_WILDCARD);
     if (fresult.code != ZoneFinder::NXDOMAIN || !fresult.rrset ||
         fresult.rrset->getRdataCount() == 0) {
         isc_throw(BadNSEC, "Unexpected result for wildcard proof");
@@ -230,8 +193,8 @@ Query::addWildcardNXRRSETProof(ZoneFinder& finder, ConstRRsetPtr nsec) {
     }
     
     const ZoneFinder::FindResult fresult =
-        findWrapper(finder.find(qname_, RRType::NSEC(),
-                                dnssec_opt_ | ZoneFinder::NO_WILDCARD));
+        finder.find(qname_, RRType::NSEC(),
+                    dnssec_opt_ | ZoneFinder::NO_WILDCARD);
     if (fresult.code != ZoneFinder::NXDOMAIN || !fresult.rrset ||
         fresult.rrset->getRdataCount() == 0) {
         isc_throw(BadNSEC, "Unexpected result for no match QNAME proof");
@@ -249,8 +212,8 @@ void
 Query::addAuthAdditional(ZoneFinder& finder) {
     // Fill in authority and addtional sections.
     ZoneFinder::FindResult ns_result =
-        findWrapper(finder.find(finder.getOrigin(), RRType::NS(),
-                                dnssec_opt_));
+        finder.find(finder.getOrigin(), RRType::NS(), dnssec_opt_);
+
     // zone origin name should have NS records
     if (ns_result.code != ZoneFinder::SUCCESS) {
         isc_throw(NoApexNS, "There's no apex NS records in zone " <<
@@ -295,7 +258,7 @@ Query::process() {
         find = boost::bind(&ZoneFinder::find, &zfinder, qname_, qtype_,
                            dnssec_opt_);
     }
-    ZoneFinder::FindResult db_result(findWrapper(find()));
+    ZoneFinder::FindResult db_result(find());
     switch (db_result.code) {
         case ZoneFinder::DNAME: {
             // First, put the dname into the answer

+ 5 - 5
src/bin/xfrin/tests/xfrin_test.py

@@ -167,14 +167,14 @@ class MockDataSourceClient():
 
         '''
         if name == TEST_ZONE_NAME and rrtype == RRType.SOA():
-            return (ZoneFinder.SUCCESS, begin_soa_rrset)
+            return (ZoneFinder.SUCCESS, begin_soa_rrset, 0)
         if name == Name('no-soa.example'):
-            return (ZoneFinder.NXDOMAIN, None)
+            return (ZoneFinder.NXDOMAIN, None, 0)
         if name == Name('dup-soa.example'):
             dup_soa_rrset = RRset(name, TEST_RRCLASS, RRType.SOA(), RRTTL(0))
             dup_soa_rrset.add_rdata(begin_soa_rdata)
             dup_soa_rrset.add_rdata(soa_rdata)
-            return (ZoneFinder.SUCCESS, dup_soa_rrset)
+            return (ZoneFinder.SUCCESS, dup_soa_rrset, 0)
         raise ValueError('Unexpected input to mock finder: bug in test case?')
 
     def get_updater(self, zone_name, replace, journaling=False):
@@ -1751,7 +1751,7 @@ class TestXFRSessionWithSQLite3(TestXfrinConnection):
     def get_zone_serial(self):
         result, finder = self.conn._datasrc_client.find_zone(TEST_ZONE_NAME)
         self.assertEqual(DataSourceClient.SUCCESS, result)
-        result, soa = finder.find(TEST_ZONE_NAME, RRType.SOA())
+        result, soa, _ = finder.find(TEST_ZONE_NAME, RRType.SOA())
         self.assertEqual(ZoneFinder.SUCCESS, result)
         self.assertEqual(1, soa.get_rdata_count())
         return get_soa_serial(soa.get_rdata()[0])
@@ -1759,7 +1759,7 @@ class TestXFRSessionWithSQLite3(TestXfrinConnection):
     def record_exist(self, name, type):
         result, finder = self.conn._datasrc_client.find_zone(TEST_ZONE_NAME)
         self.assertEqual(DataSourceClient.SUCCESS, result)
-        result, soa = finder.find(name, type)
+        result, soa, _ = finder.find(name, type)
         return result == ZoneFinder.SUCCESS
 
     def test_do_ixfrin_sqlite3(self):

+ 1 - 1
src/bin/xfrin/xfrin.py.in

@@ -583,7 +583,7 @@ class XfrinConnection(asyncore.dispatcher):
             result, finder = self._datasrc_client.find_zone(self._zone_name)
         if result != DataSourceClient.SUCCESS:
             return None
-        result, soa_rrset = finder.find(self._zone_name, RRType.SOA())
+        result, soa_rrset, _ = finder.find(self._zone_name, RRType.SOA())
         if result != ZoneFinder.SUCCESS:
             logger.info(XFRIN_ZONE_NO_SOA, self.zone_str())
             return None

+ 1 - 1
src/bin/xfrout/xfrout.py.in

@@ -335,7 +335,7 @@ class XfroutSession():
         result, finder = self._datasrc_client.find_zone(zone_name)
         if result != DataSourceClient.SUCCESS:
             return (Rcode.NOTAUTH(), None)
-        result, soa_rrset = finder.find(zone_name, RRType.SOA())
+        result, soa_rrset, _ = finder.find(zone_name, RRType.SOA())
         if result != ZoneFinder.SUCCESS:
             return (Rcode.SERVFAIL(), None)
         # Especially for database-based zones, a working zone may be in

+ 41 - 31
src/lib/datasrc/database.cc

@@ -616,7 +616,7 @@ DatabaseClient::Finder::findWildcardMatch(
                           DATASRC_DATABASE_WILDCARD_CANCEL_NS).
                     arg(accessor_->getDBName()).arg(wildcard).
                     arg(dresult.first_ns->getName());
-                return (ZoneFinder::FindResult(DELEGATION, dresult.first_ns));
+                return (FindResult(DELEGATION, dresult.first_ns));
 
             } else if (!hasSubdomains(name.split(i - 1).toText())) {
                 // The wildcard match is the best one, find the final result
@@ -630,7 +630,7 @@ DatabaseClient::Finder::findWildcardMatch(
                           DATASRC_DATABASE_WILDCARD_CANCEL_SUB).
                     arg(accessor_->getDBName()).arg(wildcard).
                     arg(name).arg(superdomain);
-                return (ZoneFinder::FindResult(NXDOMAIN, ConstRRsetPtr()));
+                return (FindResult(NXDOMAIN, ConstRRsetPtr()));
             }
 
         } else if (hasSubdomains(wildcard)) {
@@ -641,22 +641,23 @@ DatabaseClient::Finder::findWildcardMatch(
             if ((options & FIND_DNSSEC) != 0) {
                 ConstRRsetPtr nsec = findNSECCover(Name(wildcard));
                 if (nsec) {
-                    return (ZoneFinder::FindResult(WILDCARD_NXRRSET, nsec));
+                    return (FindResult(NXRRSET, nsec,
+                                       RESULT_WILDCARD | RESULT_NSEC_SIGNED));
                 }
             }
-            return (ZoneFinder::FindResult(NXRRSET, ConstRRsetPtr()));
+            return (FindResult(NXRRSET, ConstRRsetPtr(), RESULT_WILDCARD));
         }
     }
 
     // Nothing found at any level.
-    return (ZoneFinder::FindResult(NXDOMAIN, ConstRRsetPtr()));
+    return (FindResult(NXDOMAIN, ConstRRsetPtr()));
 }
 
 ZoneFinder::FindResult
 DatabaseClient::Finder::logAndCreateResult(
     const Name& name, const string* wildname, const RRType& type,
     ZoneFinder::Result code, ConstRRsetPtr rrset,
-    const isc::log::MessageID& log_id) const
+    const isc::log::MessageID& log_id, FindResultFlags flags) const
 {
     if (rrset) {
         if (wildname == NULL) {
@@ -679,7 +680,7 @@ DatabaseClient::Finder::logAndCreateResult(
                 arg(getClass()).arg(*wildname);
         }
     }
-    return (ZoneFinder::FindResult(code, rrset));
+    return (ZoneFinder::FindResult(code, rrset, flags));
 }
 
 ZoneFinder::FindResult
@@ -693,12 +694,20 @@ DatabaseClient::Finder::findOnNameResult(const Name& name,
                                          target)
 {
     const bool wild = (wildname != NULL);
+    FindResultFlags flags = wild ? RESULT_WILDCARD : RESULT_DEFAULT;
 
     // Get iterators for the different types of records we are interested in -
     // CNAME, NS and Wanted types.
     const FoundIterator nsi(found.second.find(RRType::NS()));
     const FoundIterator cni(found.second.find(RRType::CNAME()));
     const FoundIterator wti(found.second.find(type));
+    // For wildcard case with DNSSEC required, the caller would need to know
+    // whether it's NSEC or NSEC3 signed.  So we need to do an additional
+    // search here, even though the NSEC RR may not be returned.
+    if (wild && (options & FIND_DNSSEC) != 0 &&
+        found.second.find(RRType::NSEC()) != found.second.end()) {
+        flags = flags | RESULT_NSEC_SIGNED;
+    }
 
     if (!is_origin && ((options & FIND_GLUE_OK) == 0) &&
         nsi != found.second.end()) {
@@ -709,7 +718,8 @@ DatabaseClient::Finder::findOnNameResult(const Name& name,
         return (logAndCreateResult(name, wildname, type, DELEGATION,
                                    nsi->second,
                                    wild ? DATASRC_DATABASE_WILDCARD_NS :
-                                   DATASRC_DATABASE_FOUND_DELEGATION_EXACT));
+                                   DATASRC_DATABASE_FOUND_DELEGATION_EXACT,
+                                   flags));
 
     } else if (type != RRType::CNAME() && cni != found.second.end()) {
         // We are not searching for a CNAME but nevertheless we have found one
@@ -722,10 +732,10 @@ DatabaseClient::Finder::findOnNameResult(const Name& name,
                       cni->second->getRdataCount() << " rdata at " << name <<
                       ", expected 1");
         }
-        return (logAndCreateResult(name, wildname, type,
-                                   wild ? WILDCARD_CNAME : CNAME, cni->second,
+        return (logAndCreateResult(name, wildname, type, CNAME, cni->second,
                                    wild ? DATASRC_DATABASE_WILDCARD_CNAME :
-                                   DATASRC_DATABASE_FOUND_CNAME));
+                                   DATASRC_DATABASE_FOUND_CNAME,
+                                   flags));
 
     } else if (wti != found.second.end()) {
         bool any(type == RRType::ANY());
@@ -748,9 +758,8 @@ DatabaseClient::Finder::findOnNameResult(const Name& name,
         // includes the case where we were explicitly querying for a CNAME and
         // found it.  It also includes the case where we were querying for an
         // NS RRset and found it at the apex of the zone.)
-        return (logAndCreateResult(name, wildname, type,
-                                   wild ? WILDCARD : SUCCESS, wti->second,
-                                   lid));
+        return (logAndCreateResult(name, wildname, type, SUCCESS,
+                                   wti->second, lid, flags));
     }
 
     // If we get here, we have found something at the requested name but not
@@ -778,15 +787,13 @@ DatabaseClient::Finder::findOnNameResult(const Name& name,
     if (nsec_rrset) {
         // This log message covers both normal and wildcard cases, so we pass
         // NULL for 'wildname'.
-        return (logAndCreateResult(name, NULL, type,
-                                   wild ? WILDCARD_NXRRSET : NXRRSET,
-                                   nsec_rrset,
-                                   DATASRC_DATABASE_FOUND_NXRRSET_NSEC));
+        return (logAndCreateResult(name, NULL, type, NXRRSET, nsec_rrset,
+                                   DATASRC_DATABASE_FOUND_NXRRSET_NSEC,
+                                   flags | RESULT_NSEC_SIGNED));
     }
-    return (logAndCreateResult(name, wildname, type,
-                               wild ? WILDCARD_NXRRSET : NXRRSET, nsec_rrset,
+    return (logAndCreateResult(name, wildname, type, NXRRSET, nsec_rrset,
                                wild ? DATASRC_DATABASE_WILDCARD_NXRRSET :
-                               DATASRC_DATABASE_FOUND_NXRRSET));
+                               DATASRC_DATABASE_FOUND_NXRRSET, flags));
 }
 
 ZoneFinder::FindResult
@@ -809,9 +816,10 @@ DatabaseClient::Finder::findNoNameResult(const Name& name, const RRType& type,
         LOG_DEBUG(logger, DBG_TRACE_DETAILED,
                   DATASRC_DATABASE_FOUND_EMPTY_NONTERMINAL).
             arg(accessor_->getDBName()).arg(name);
-        return (FindResult(NXRRSET, dnssec_data ? findNSECCover(name) :
-                           ConstRRsetPtr()));
-
+        const ConstRRsetPtr nsec = dnssec_data ? findNSECCover(name) :
+            ConstRRsetPtr();
+        return (FindResult(NXRRSET, nsec,
+                           nsec ? RESULT_NSEC_SIGNED : RESULT_DEFAULT));
     } else if ((options & NO_WILDCARD) == 0) {
         // It's not an empty non-terminal and wildcard matching is not
         // disabled, so check for wildcards. If there is a wildcard match
@@ -820,7 +828,7 @@ DatabaseClient::Finder::findNoNameResult(const Name& name, const RRType& type,
         const ZoneFinder::FindResult wresult =
             findWildcardMatch(name, type, options, dresult, target);
         if (wresult.code != NXDOMAIN) {
-            return (FindResult(wresult.code, wresult.rrset));
+            return (wresult);
         }
     }
 
@@ -828,15 +836,16 @@ DatabaseClient::Finder::findNoNameResult(const Name& name, const RRType& type,
     // NSEC records if requested).
     LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_DATABASE_NO_MATCH).
               arg(accessor_->getDBName()).arg(name).arg(type).arg(getClass());
-    return (FindResult(NXDOMAIN, dnssec_data ? findNSECCover(name) :
-                           ConstRRsetPtr()));
+    const ConstRRsetPtr nsec = dnssec_data ? findNSECCover(name) :
+        ConstRRsetPtr();
+    return (FindResult(NXDOMAIN, nsec,
+                       nsec ? RESULT_NSEC_SIGNED : RESULT_DEFAULT));
 }
 
 ZoneFinder::FindResult
-DatabaseClient::Finder::findInternal(const isc::dns::Name& name,
-                             const isc::dns::RRType& type,
-                             std::vector<isc::dns::ConstRRsetPtr>* target,
-                             const FindOptions options)
+DatabaseClient::Finder::findInternal(const Name& name, const RRType& type,
+                                     std::vector<ConstRRsetPtr>* target,
+                                     const FindOptions options)
 {
     LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_DATABASE_FIND_RECORDS)
               .arg(accessor_->getDBName()).arg(name).arg(type).arg(getClass());
@@ -864,6 +873,7 @@ DatabaseClient::Finder::findInternal(const isc::dns::Name& name,
     // presence of the delegation.)
     const DelegationSearchResult dresult = findDelegationPoint(name, options);
     if (dresult.rrset) {
+        // In this case no special flags are needed.
         return (FindResult(dresult.code, dresult.rrset));
     }
 

+ 2 - 1
src/lib/datasrc/database.h

@@ -1056,7 +1056,8 @@ public:
                                       const isc::dns::RRType& type,
                                       ZoneFinder::Result code,
                                       isc::dns::ConstRRsetPtr rrset,
-                                      const isc::log::MessageID& log_id) const;
+                                      const isc::log::MessageID& log_id,
+                                      FindResultFlags flags) const;
 
         /// \brief Checks if something lives below this domain.
         ///

+ 87 - 52
src/lib/datasrc/tests/database_unittest.cc

@@ -1416,12 +1416,20 @@ doFindTest(ZoneFinder& finder,
            ZoneFinder::Result expected_result,
            const std::vector<std::string>& expected_rdatas,
            const std::vector<std::string>& expected_sig_rdatas,
+           ZoneFinder::FindResultFlags expected_flags =
+           ZoneFinder::RESULT_DEFAULT,
            const isc::dns::Name& expected_name = isc::dns::Name::ROOT_NAME(),
            const ZoneFinder::FindOptions options = ZoneFinder::FIND_DEFAULT)
 {
     SCOPED_TRACE("doFindTest " + name.toText() + " " + type.toText());
     const ZoneFinder::FindResult result = finder.find(name, type, options);
     ASSERT_EQ(expected_result, result.code) << name << " " << type;
+    EXPECT_EQ((expected_flags & ZoneFinder::RESULT_WILDCARD) != 0,
+              result.isWildcard());
+    EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0,
+              result.isNSECSigned());
+    EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0,
+              result.isNSEC3Signed());
     if (!expected_rdatas.empty() && result.rrset) {
         checkRRset(result.rrset, expected_name != Name(".") ? expected_name :
                    name, finder.getClass(), expected_type, expected_ttl,
@@ -1838,17 +1846,17 @@ TYPED_TEST(DatabaseClientTest, findDelegation) {
     doFindTest(*finder, isc::dns::Name("ns.delegation.example.org."),
                this->qtype_, isc::dns::RRType::NS(),
                this->rrttl_, ZoneFinder::DELEGATION, this->expected_rdatas_,
-               this->expected_sig_rdatas_,
+               this->expected_sig_rdatas_, ZoneFinder::RESULT_DEFAULT,
                isc::dns::Name("delegation.example.org."));
     doFindTest(*finder, isc::dns::Name("ns.delegation.example.org."),
                isc::dns::RRType::AAAA(), isc::dns::RRType::NS(),
                this->rrttl_, ZoneFinder::DELEGATION, this->expected_rdatas_,
-               this->expected_sig_rdatas_,
+               this->expected_sig_rdatas_, ZoneFinder::RESULT_DEFAULT,
                isc::dns::Name("delegation.example.org."));
     doFindTest(*finder, isc::dns::Name("deep.below.delegation.example.org."),
                isc::dns::RRType::AAAA(), isc::dns::RRType::NS(),
                this->rrttl_, ZoneFinder::DELEGATION, this->expected_rdatas_,
-               this->expected_sig_rdatas_,
+               this->expected_sig_rdatas_, ZoneFinder::RESULT_DEFAULT,
                isc::dns::Name("delegation.example.org."));
 
     // Even when we check directly at the delegation point, we should get
@@ -1876,15 +1884,18 @@ TYPED_TEST(DatabaseClientTest, findDelegation) {
     doFindTest(*finder, isc::dns::Name("below.dname.example.org."),
                this->qtype_, isc::dns::RRType::DNAME(),
                this->rrttl_, ZoneFinder::DNAME, this->expected_rdatas_,
-               this->expected_sig_rdatas_, isc::dns::Name("dname.example.org."));
+               this->expected_sig_rdatas_, ZoneFinder::RESULT_DEFAULT,
+               isc::dns::Name("dname.example.org."));
     doFindTest(*finder, isc::dns::Name("below.dname.example.org."),
                isc::dns::RRType::AAAA(), isc::dns::RRType::DNAME(),
                this->rrttl_, ZoneFinder::DNAME, this->expected_rdatas_,
-               this->expected_sig_rdatas_, isc::dns::Name("dname.example.org."));
+               this->expected_sig_rdatas_, ZoneFinder::RESULT_DEFAULT,
+               isc::dns::Name("dname.example.org."));
     doFindTest(*finder, isc::dns::Name("really.deep.below.dname.example.org."),
                isc::dns::RRType::AAAA(), isc::dns::RRType::DNAME(),
                this->rrttl_, ZoneFinder::DNAME, this->expected_rdatas_,
-               this->expected_sig_rdatas_, isc::dns::Name("dname.example.org."));
+               this->expected_sig_rdatas_, ZoneFinder::RESULT_DEFAULT,
+               isc::dns::Name("dname.example.org."));
 
     // Asking direcly for DNAME should give SUCCESS
     doFindTest(*finder, isc::dns::Name("dname.example.org."),
@@ -1944,12 +1955,14 @@ TYPED_TEST(DatabaseClientTest, glueOK) {
                isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(),
                this->rrttl_, ZoneFinder::NXRRSET,
                this->expected_rdatas_, this->expected_sig_rdatas_,
+               ZoneFinder::RESULT_DEFAULT,
                isc::dns::Name("ns.delegation.example.org."),
                ZoneFinder::FIND_GLUE_OK);
     doFindTest(*finder, isc::dns::Name("nothere.delegation.example.org."),
                isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(),
                this->rrttl_, ZoneFinder::NXDOMAIN,
                this->expected_rdatas_, this->expected_sig_rdatas_,
+               ZoneFinder::RESULT_DEFAULT,
                isc::dns::Name("nothere.delegation.example.org."),
                ZoneFinder::FIND_GLUE_OK);
     this->expected_rdatas_.push_back("192.0.2.1");
@@ -1957,6 +1970,7 @@ TYPED_TEST(DatabaseClientTest, glueOK) {
                this->qtype_, this->qtype_,
                this->rrttl_, ZoneFinder::SUCCESS,
                this->expected_rdatas_, this->expected_sig_rdatas_,
+               ZoneFinder::RESULT_DEFAULT,
                isc::dns::Name("ns.delegation.example.org."),
                ZoneFinder::FIND_GLUE_OK);
     this->expected_rdatas_.clear();
@@ -1972,6 +1986,7 @@ TYPED_TEST(DatabaseClientTest, glueOK) {
                isc::dns::RRType::NS(), isc::dns::RRType::NS(),
                this->rrttl_, ZoneFinder::SUCCESS,
                this->expected_rdatas_, this->expected_sig_rdatas_,
+               ZoneFinder::RESULT_DEFAULT,
                isc::dns::Name("delegation.example.org."),
                ZoneFinder::FIND_GLUE_OK);
     this->expected_rdatas_.clear();
@@ -1983,12 +1998,12 @@ TYPED_TEST(DatabaseClientTest, glueOK) {
     doFindTest(*finder, isc::dns::Name("below.dname.example.org."),
                this->qtype_, isc::dns::RRType::DNAME(),
                this->rrttl_, ZoneFinder::DNAME, this->expected_rdatas_,
-               this->expected_sig_rdatas_,
+               this->expected_sig_rdatas_, ZoneFinder::RESULT_DEFAULT,
                isc::dns::Name("dname.example.org."), ZoneFinder::FIND_GLUE_OK);
     doFindTest(*finder, isc::dns::Name("below.dname.example.org."),
                isc::dns::RRType::AAAA(), isc::dns::RRType::DNAME(),
                this->rrttl_, ZoneFinder::DNAME, this->expected_rdatas_,
-               this->expected_sig_rdatas_,
+               this->expected_sig_rdatas_, ZoneFinder::RESULT_DEFAULT,
                isc::dns::Name("dname.example.org."), ZoneFinder::FIND_GLUE_OK);
 }
 
@@ -2003,21 +2018,24 @@ TYPED_TEST(DatabaseClientTest, wildcard) {
                                          "FAKEFAKEFAKE");
     doFindTest(*finder, isc::dns::Name("a.wild.example.org"),
                this->qtype_, this->qtype_, this->rrttl_,
-               ZoneFinder::WILDCARD, this->expected_rdatas_,
-               this->expected_sig_rdatas_);
+               ZoneFinder::SUCCESS, this->expected_rdatas_,
+               this->expected_sig_rdatas_, ZoneFinder::RESULT_WILDCARD);
     doFindTest(*finder, isc::dns::Name("b.a.wild.example.org"),
-               this->qtype_, this->qtype_, this->rrttl_, ZoneFinder::WILDCARD,
-               this->expected_rdatas_, this->expected_sig_rdatas_);
+               this->qtype_, this->qtype_, this->rrttl_, ZoneFinder::SUCCESS,
+               this->expected_rdatas_, this->expected_sig_rdatas_,
+               ZoneFinder::RESULT_WILDCARD);
     this->expected_rdatas_.clear();
     this->expected_sig_rdatas_.clear();
     doFindTest(*finder, isc::dns::Name("a.wild.example.org"),
                isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(),
-               this->rrttl_, ZoneFinder::WILDCARD_NXRRSET,
-               this->expected_rdatas_, this->expected_sig_rdatas_);
+               this->rrttl_, ZoneFinder::NXRRSET,
+               this->expected_rdatas_, this->expected_sig_rdatas_,
+               ZoneFinder::RESULT_WILDCARD);
     doFindTest(*finder, isc::dns::Name("b.a.wild.example.org"),
                isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(),
-               this->rrttl_, ZoneFinder::WILDCARD_NXRRSET,
-               this->expected_rdatas_, this->expected_sig_rdatas_);
+               this->rrttl_, ZoneFinder::NXRRSET,
+               this->expected_rdatas_, this->expected_sig_rdatas_,
+               ZoneFinder::RESULT_WILDCARD);
 
     // Direct request for this wildcard
     this->expected_rdatas_.push_back("192.0.2.5");
@@ -2067,14 +2085,14 @@ TYPED_TEST(DatabaseClientTest, wildcard) {
     doFindTest(*finder, isc::dns::Name("below.delegatedwild.example.org"),
                this->qtype_, isc::dns::RRType::NS(), this->rrttl_,
                ZoneFinder::DELEGATION, this->expected_rdatas_,
-               this->expected_sig_rdatas_,
+               this->expected_sig_rdatas_, ZoneFinder::RESULT_DEFAULT,
                isc::dns::Name("delegatedwild.example.org"));
     // FIXME: This doesn't look logically OK, GLUE_OK should make it transparent,
     // so the match should either work or be canceled, but return NXDOMAIN
     doFindTest(*finder, isc::dns::Name("below.delegatedwild.example.org"),
                this->qtype_, isc::dns::RRType::NS(), this->rrttl_,
                ZoneFinder::DELEGATION, this->expected_rdatas_,
-               this->expected_sig_rdatas_,
+               this->expected_sig_rdatas_, ZoneFinder::RESULT_DEFAULT,
                isc::dns::Name("delegatedwild.example.org"),
                ZoneFinder::FIND_GLUE_OK);
 
@@ -2097,16 +2115,16 @@ TYPED_TEST(DatabaseClientTest, wildcard) {
     this->expected_rdatas_.clear();
     const char* negative_names[] = {
         "a.foo.example.org.",
-        "*.foo.example.org.",
-        "foo.example.org.",
+        //"*.foo.example.org.", => should be an exact (empty) match
+        //"foo.example.org.", => WILDCARD should be canceled
         "wild.bar.foo.example.org.",
         "baz.foo.*.bar.example.org",
         "baz.foo.baz.bar.example.org",
         "*.foo.baz.bar.example.org",
-        "*.foo.*.bar.example.org",
-        "foo.*.bar.example.org",
-        "*.bar.example.org",
-        "bar.example.org",
+        //"*.foo.*.bar.example.org", => exact (empty) match
+        //"foo.*.bar.example.org", => exact (empty) match
+        //"*.bar.example.org", => exact (empty) match
+        //"bar.example.org", => exact (empty) match
         NULL
     };
     // Unless FIND_DNSSEC is specified, this is no different from other
@@ -2114,10 +2132,11 @@ TYPED_TEST(DatabaseClientTest, wildcard) {
     for (const char** name = negative_names; *name != NULL; ++ name) {
         doFindTest(*finder, isc::dns::Name(*name), this->qtype_,
                    this->qtype_, this->rrttl_, ZoneFinder::NXRRSET,
-                   this->expected_rdatas_, this->expected_sig_rdatas_);
+                   this->expected_rdatas_, this->expected_sig_rdatas_,
+                   ZoneFinder::RESULT_WILDCARD);
     }
 
-    // With FIND_DNSSEC, it should result in WILDCARD_NXRRSET.
+    // With FIND_DNSSEC, it should have NSEC_SIGNED flag.
     const char* negative_dnssec_names[] = {
         "a.bar.example.org.",
         "foo.baz.bar.example.org.",
@@ -2129,8 +2148,9 @@ TYPED_TEST(DatabaseClientTest, wildcard) {
     this->expected_sig_rdatas_.clear();
     for (const char** name = negative_dnssec_names; *name != NULL; ++ name) {
         doFindTest(*finder, isc::dns::Name(*name), this->qtype_,
-                   RRType::NSEC(), this->rrttl_, ZoneFinder::WILDCARD_NXRRSET,
+                   RRType::NSEC(), this->rrttl_, ZoneFinder::NXRRSET,
                    this->expected_rdatas_, this->expected_sig_rdatas_,
+                   ZoneFinder::RESULT_WILDCARD | ZoneFinder::RESULT_NSEC_SIGNED,
                    Name("bao.example.org."), ZoneFinder::FIND_DNSSEC);
     }
 
@@ -2140,19 +2160,18 @@ TYPED_TEST(DatabaseClientTest, wildcard) {
     this->expected_sig_rdatas_.clear();
     doFindTest(*finder, isc::dns::Name("a.cnamewild.example.org."),
                isc::dns::RRType::TXT(), isc::dns::RRType::CNAME(),
-               this->rrttl_, ZoneFinder::WILDCARD_CNAME,
-               this->expected_rdatas_, this->expected_sig_rdatas_);
+               this->rrttl_, ZoneFinder::CNAME,
+               this->expected_rdatas_, this->expected_sig_rdatas_,
+               ZoneFinder::RESULT_WILDCARD);
 
     // DNAME on a wildcard.  In our implementation we ignore DNAMEs on a
     // wildcard, but at a higher level we say the behavior is "unspecified".
     // rfc2672bis strongly discourages the mixture of DNAME and wildcard
     // (with SHOULD NOT).
-    this->expected_rdatas_.clear();
-    this->expected_sig_rdatas_.clear();
     doFindTest(*finder, Name("a.dnamewild.example.org."),
                this->qtype_, this->qtype_, this->rrttl_,
-               ZoneFinder::WILDCARD_NXRRSET, this->expected_rdatas_,
-               this->expected_sig_rdatas_);
+               ZoneFinder::NXRRSET, this->empty_rdatas_,
+               this->empty_rdatas_, ZoneFinder::RESULT_WILDCARD);
 
     // Some strange things in the wild node
     this->expected_rdatas_.clear();
@@ -2160,7 +2179,8 @@ TYPED_TEST(DatabaseClientTest, wildcard) {
     doFindTest(*finder, isc::dns::Name("a.nswild.example.org."),
                isc::dns::RRType::TXT(), isc::dns::RRType::NS(),
                this->rrttl_, ZoneFinder::DELEGATION,
-               this->expected_rdatas_, this->expected_sig_rdatas_);
+               this->expected_rdatas_, this->empty_rdatas_,
+               ZoneFinder::RESULT_WILDCARD);
 }
 
 TYPED_TEST(DatabaseClientTest, noWildcard) {
@@ -2178,7 +2198,8 @@ TYPED_TEST(DatabaseClientTest, noWildcard) {
     doFindTest(*finder, isc::dns::Name("a.wild.example.org"),
                RRType::NSEC(), RRType::NSEC(), this->rrttl_,
                ZoneFinder::NXDOMAIN, this->expected_rdatas_,
-               this->expected_sig_rdatas_, Name("*.wild.example.org."),
+               this->expected_sig_rdatas_, ZoneFinder::RESULT_NSEC_SIGNED,
+               Name("*.wild.example.org."),
                ZoneFinder::FIND_DNSSEC | ZoneFinder::NO_WILDCARD);
 
     // Should be the same without FIND_DNSSEC (but in this case no RRsets
@@ -2186,7 +2207,8 @@ TYPED_TEST(DatabaseClientTest, noWildcard) {
     doFindTest(*finder, isc::dns::Name("a.wild.example.org"),
                RRType::NSEC(), RRType::NSEC(), this->rrttl_,
                ZoneFinder::NXDOMAIN, this->empty_rdatas_,
-               this->empty_rdatas_, Name::ROOT_NAME(), // name is dummy
+               this->empty_rdatas_, ZoneFinder::RESULT_DEFAULT,
+               Name::ROOT_NAME(), // name is dummy
                ZoneFinder::NO_WILDCARD);
 
     // Same for wildcard empty non terminal.
@@ -2195,7 +2217,8 @@ TYPED_TEST(DatabaseClientTest, noWildcard) {
     doFindTest(*finder, isc::dns::Name("a.bar.example.org"),
                RRType::NSEC(), RRType::NSEC(), this->rrttl_,
                ZoneFinder::NXDOMAIN, this->expected_rdatas_,
-               this->empty_rdatas_, Name("wild.*.foo.*.bar.example.org"),
+               this->empty_rdatas_, ZoneFinder::RESULT_NSEC_SIGNED,
+               Name("wild.*.foo.*.bar.example.org"),
                ZoneFinder::FIND_DNSSEC | ZoneFinder::NO_WILDCARD);
 
     // Search for a wildcard name with NO_WILDCARD.  There should be no
@@ -2206,7 +2229,8 @@ TYPED_TEST(DatabaseClientTest, noWildcard) {
     doFindTest(*finder, isc::dns::Name("*.nonterminal.example.org"),
                RRType::NSEC(), RRType::NSEC(), this->rrttl_,
                ZoneFinder::NXDOMAIN, this->expected_rdatas_,
-               this->empty_rdatas_, Name("l.example.org"),
+               this->empty_rdatas_, ZoneFinder::RESULT_NSEC_SIGNED,
+               Name("l.example.org"),
                ZoneFinder::FIND_DNSSEC | ZoneFinder::NO_WILDCARD);
 
     // On the other hand, if there's exact match for the wildcard name
@@ -2220,8 +2244,8 @@ TYPED_TEST(DatabaseClientTest, noWildcard) {
     doFindTest(*finder, isc::dns::Name("*.wild.example.org"),
                this->qtype_, this->qtype_, this->rrttl_,
                ZoneFinder::SUCCESS, this->expected_rdatas_,
-               this->expected_sig_rdatas_, Name("*.wild.example.org"),
-               ZoneFinder::NO_WILDCARD);
+               this->expected_sig_rdatas_, ZoneFinder::RESULT_DEFAULT,
+               Name("*.wild.example.org"), ZoneFinder::NO_WILDCARD);
 }
 
 TYPED_TEST(DatabaseClientTest, NXRRSET_NSEC) {
@@ -2237,7 +2261,8 @@ TYPED_TEST(DatabaseClientTest, NXRRSET_NSEC) {
                isc::dns::RRType::TXT(), isc::dns::RRType::NSEC(),
                this->rrttl_, ZoneFinder::NXRRSET,
                this->expected_rdatas_, this->expected_sig_rdatas_,
-               Name::ROOT_NAME(), ZoneFinder::FIND_DNSSEC);
+               ZoneFinder::RESULT_NSEC_SIGNED, Name::ROOT_NAME(),
+               ZoneFinder::FIND_DNSSEC);
 }
 
 TYPED_TEST(DatabaseClientTest, wildcardNXRRSET_NSEC) {
@@ -2256,8 +2281,9 @@ TYPED_TEST(DatabaseClientTest, wildcardNXRRSET_NSEC) {
     // Note that the NSEC name should NOT be synthesized.
     doFindTest(*finder, isc::dns::Name("a.wild.example.org."),
                isc::dns::RRType::TXT(), isc::dns::RRType::NSEC(),
-               this->rrttl_, ZoneFinder::WILDCARD_NXRRSET,
+               this->rrttl_, ZoneFinder::NXRRSET,
                this->expected_rdatas_, this->expected_sig_rdatas_,
+               ZoneFinder::RESULT_WILDCARD | ZoneFinder::RESULT_NSEC_SIGNED,
                Name("*.wild.example.org"), ZoneFinder::FIND_DNSSEC);
 }
 
@@ -2273,7 +2299,8 @@ TYPED_TEST(DatabaseClientTest, NXDOMAIN_NSEC) {
                isc::dns::RRType::TXT(), isc::dns::RRType::NSEC(),
                this->rrttl_, ZoneFinder::NXDOMAIN,
                this->expected_rdatas_, this->expected_sig_rdatas_,
-               Name("www.example.org."), ZoneFinder::FIND_DNSSEC);
+               ZoneFinder::RESULT_NSEC_SIGNED, Name("www.example.org."),
+               ZoneFinder::FIND_DNSSEC);
     this->expected_rdatas_.clear();
     this->expected_rdatas_.push_back("acnamesig1.example.org. NS A NSEC RRSIG");
     // This tests it works correctly in apex (there was a bug, where a check
@@ -2282,7 +2309,8 @@ TYPED_TEST(DatabaseClientTest, NXDOMAIN_NSEC) {
                isc::dns::RRType::TXT(), isc::dns::RRType::NSEC(),
                this->rrttl_, ZoneFinder::NXDOMAIN,
                this->expected_rdatas_, this->expected_sig_rdatas_,
-               Name("example.org."), ZoneFinder::FIND_DNSSEC);
+               ZoneFinder::RESULT_NSEC_SIGNED, Name("example.org."),
+               ZoneFinder::FIND_DNSSEC);
 
     // Check that if the DB doesn't support it, the exception from there
     // is not propagated and it only does not include the NSEC
@@ -2294,8 +2322,9 @@ TYPED_TEST(DatabaseClientTest, NXDOMAIN_NSEC) {
                                isc::dns::RRType::TXT(),
                                isc::dns::RRType::NSEC(), this->rrttl_,
                                ZoneFinder::NXDOMAIN, this->empty_rdatas_,
-                               this->empty_rdatas_, Name::ROOT_NAME(),
-                               ZoneFinder::FIND_DNSSEC));
+                               this->empty_rdatas_,
+                               ZoneFinder::RESULT_DEFAULT,
+                               Name::ROOT_NAME(), ZoneFinder::FIND_DNSSEC));
 }
 
 TYPED_TEST(DatabaseClientTest, emptyNonterminalNSEC) {
@@ -2305,9 +2334,10 @@ TYPED_TEST(DatabaseClientTest, emptyNonterminalNSEC) {
     this->expected_rdatas_.push_back("empty.nonterminal.example.org. NSEC");
     doFindTest(*finder, isc::dns::Name("nonterminal.example.org."),
                isc::dns::RRType::TXT(), isc::dns::RRType::NSEC(), this->rrttl_,
-               ZoneFinder::NXRRSET,
-               this->expected_rdatas_, this->expected_sig_rdatas_,
-               Name("l.example.org."), ZoneFinder::FIND_DNSSEC);
+               ZoneFinder::NXRRSET, this->expected_rdatas_,
+               this->expected_sig_rdatas_,
+               ZoneFinder::RESULT_NSEC_SIGNED, Name("l.example.org."),
+               ZoneFinder::FIND_DNSSEC);
 
     // Check that if the DB doesn't support it, the exception from there
     // is not propagated and it only does not include the NSEC
@@ -2320,6 +2350,7 @@ TYPED_TEST(DatabaseClientTest, emptyNonterminalNSEC) {
                                isc::dns::RRType::NSEC(),
                                this->rrttl_, ZoneFinder::NXRRSET,
                                this->empty_rdatas_, this->empty_rdatas_,
+                               ZoneFinder::RESULT_DEFAULT,
                                Name::ROOT_NAME(), ZoneFinder::FIND_DNSSEC));
 }
 
@@ -2387,9 +2418,13 @@ TYPED_TEST(DatabaseClientTest, getAll) {
 
     // And on wildcard. Check the signatures as well.
     target.clear();
-    EXPECT_EQ(ZoneFinder::WILDCARD,
-              finder->findAll(isc::dns::Name("a.wild.example.org"),
-                              target, ZoneFinder::FIND_DNSSEC).code);
+    const ZoneFinder::FindResult result =
+        finder->findAll(Name("a.wild.example.org"), target,
+                        ZoneFinder::FIND_DNSSEC);
+    EXPECT_EQ(ZoneFinder::SUCCESS, result.code);
+    EXPECT_TRUE(result.isWildcard());
+    EXPECT_TRUE(result.isNSECSigned());
+    EXPECT_FALSE(result.isNSEC3Signed());
     ASSERT_EQ(2, target.size());
     a_idx = target[1]->getType() == RRType::A();
     EXPECT_EQ(RRType::A(), target[a_idx]->getType());

+ 1 - 4
src/lib/datasrc/zone.h

@@ -136,10 +136,7 @@ public:
         NXDOMAIN, ///< There is no domain name that matches the search name
         NXRRSET,  ///< There is a matching name but no RRset of the search type
         CNAME,    ///< The search encounters and returns a CNAME RR
-        DNAME,    ///< The search encounters and returns a DNAME RR
-        WILDCARD, ///< Succes by wildcard match, for DNSSEC
-        WILDCARD_CNAME, ///< CNAME on wildcard, search returns CNAME, for DNSSEC
-        WILDCARD_NXRRSET ///< NXRRSET on wildcard, for DNSSEC
+        DNAME    ///< The search encounters and returns a DNAME RR
     };
 
     /// Special attribute flags on the result of the \c find() method

+ 9 - 6
src/lib/python/isc/datasrc/datasrc.cc

@@ -129,12 +129,6 @@ initModulePart_ZoneFinder(PyObject* mod) {
                              Py_BuildValue("I", ZoneFinder::CNAME));
         installClassVariable(zonefinder_type, "DNAME",
                              Py_BuildValue("I", ZoneFinder::DNAME));
-        installClassVariable(zonefinder_type, "WILDCARD",
-                             Py_BuildValue("I", ZoneFinder::WILDCARD));
-        installClassVariable(zonefinder_type, "WILDCARD_NXRRSET",
-                             Py_BuildValue("I", ZoneFinder::WILDCARD_NXRRSET));
-        installClassVariable(zonefinder_type, "WILDCARD_CNAME",
-                             Py_BuildValue("I", ZoneFinder::WILDCARD_CNAME));
 
         installClassVariable(zonefinder_type, "FIND_DEFAULT",
                              Py_BuildValue("I", ZoneFinder::FIND_DEFAULT));
@@ -144,6 +138,15 @@ initModulePart_ZoneFinder(PyObject* mod) {
                              Py_BuildValue("I", ZoneFinder::FIND_DNSSEC));
         installClassVariable(zonefinder_type, "NO_WILDCARD",
                              Py_BuildValue("I", ZoneFinder::NO_WILDCARD));
+
+        installClassVariable(zonefinder_type, "RESULT_WILDCARD",
+                             Py_BuildValue("I", ZoneFinder::RESULT_WILDCARD));
+        installClassVariable(zonefinder_type, "RESULT_NSEC_SIGNED",
+                             Py_BuildValue("I",
+                                           ZoneFinder::RESULT_NSEC_SIGNED));
+        installClassVariable(zonefinder_type, "RESULT_NSEC3_SIGNED",
+                             Py_BuildValue("I",
+                                           ZoneFinder::RESULT_NSEC3_SIGNED));
     } catch (const std::exception& ex) {
         const std::string ex_what =
             "Unexpected failure in ZoneFinder initialization: " +

+ 28 - 5
src/lib/python/isc/datasrc/finder_python.cc

@@ -45,6 +45,23 @@ using namespace isc::dns::python;
 using namespace isc::datasrc;
 using namespace isc::datasrc::python;
 
+namespace  {
+ZoneFinder::FindResultFlags
+getFindResultFlags(const ZoneFinder::FindResult& result) {
+    ZoneFinder::FindResultFlags result_flags = ZoneFinder::RESULT_DEFAULT;
+    if (result.isWildcard()) {
+        result_flags = result_flags | ZoneFinder::RESULT_WILDCARD;
+    }
+    if (result.isNSECSigned()) {
+        result_flags = result_flags | ZoneFinder::RESULT_NSEC_SIGNED;
+    }
+    if (result.isNSEC3Signed()) {
+        result_flags = result_flags | ZoneFinder::RESULT_NSEC3_SIGNED;
+    }
+    return (result_flags);
+}
+}
+
 namespace isc_datasrc_internal {
 // This is the shared code for the find() call in the finder and the updater
 // Is is intentionally not available through any header, nor at our standard
@@ -71,11 +88,14 @@ PyObject* ZoneFinder_helper(ZoneFinder* finder, PyObject* args) {
                              options));
             const ZoneFinder::Result r = find_result.code;
             isc::dns::ConstRRsetPtr rrsp = find_result.rrset;
+            ZoneFinder::FindResultFlags result_flags =
+                getFindResultFlags(find_result);
             if (rrsp) {
                 // Use N instead of O so the refcount isn't increased twice
-                return (Py_BuildValue("IN", r, createRRsetObject(*rrsp)));
+                return (Py_BuildValue("INI", r, createRRsetObject(*rrsp),
+                                      result_flags));
             } else {
-                return (Py_BuildValue("IO", r, Py_None));
+                return (Py_BuildValue("IOI", r, Py_None, result_flags));
             }
         } catch (const DataSourceError& dse) {
             PyErr_SetString(getDataSourceException("Error"), dse.what());
@@ -111,7 +131,9 @@ PyObject* ZoneFinder_helper_all(ZoneFinder* finder, PyObject* args) {
                 finder->findAll(PyName_ToName(name), target, options));
             const ZoneFinder::Result r = find_result.code;
             isc::dns::ConstRRsetPtr rrsp = find_result.rrset;
-            if (r == ZoneFinder::SUCCESS || r == ZoneFinder::WILDCARD) {
+            ZoneFinder::FindResultFlags result_flags =
+                getFindResultFlags(find_result);
+            if (r == ZoneFinder::SUCCESS) {
                 // Copy all the RRsets to the result list
                 PyObjectContainer list_container(PyList_New(target.size()));
                 for (size_t i(0); i < target.size(); ++i) {
@@ -126,9 +148,10 @@ PyObject* ZoneFinder_helper_all(ZoneFinder* finder, PyObject* args) {
             } else {
                 if (rrsp) {
                     // Use N instead of O so the refcount isn't increased twice
-                    return (Py_BuildValue("IN", r, createRRsetObject(*rrsp)));
+                    return (Py_BuildValue("INI", r, createRRsetObject(*rrsp),
+                                          result_flags));
                 } else {
-                    return (Py_BuildValue("IO", r, Py_None));
+                    return (Py_BuildValue("IOI", r, Py_None, result_flags));
                 }
             }
         } catch (const DataSourceError& dse) {

+ 72 - 68
src/lib/python/isc/datasrc/tests/datasrc_test.py

@@ -63,8 +63,8 @@ def test_findall_common(self, tested):
     object.
     """
     # Some "failure" responses
-    result, rrset = tested.find_all(isc.dns.Name("www.sql1.example.com"),
-                                    ZoneFinder.FIND_DEFAULT)
+    result, rrset, _ = tested.find_all(isc.dns.Name("www.sql1.example.com"),
+                                       ZoneFinder.FIND_DEFAULT)
     self.assertEqual(ZoneFinder.DELEGATION, result)
     expected = RRset(Name('sql1.example.com.'), RRClass.IN(), RRType.NS(),
                      RRTTL(3600))
@@ -76,8 +76,8 @@ def test_findall_common(self, tested):
                              'dns03.example.com.'))
     self.assertTrue(rrsets_equal(expected, rrset))
 
-    result, rrset = tested.find_all(isc.dns.Name("nxdomain.example.com"),
-                                     ZoneFinder.FIND_DEFAULT)
+    result, rrset, _ = tested.find_all(isc.dns.Name("nxdomain.example.com"),
+                                       ZoneFinder.FIND_DEFAULT)
     self.assertEqual(ZoneFinder.NXDOMAIN, result)
     self.assertIsNone(None, rrset)
 
@@ -319,10 +319,12 @@ class DataSrcClient(unittest.TestCase):
         self.assertNotEqual(ZoneFinder.NXDOMAIN, ZoneFinder.NXRRSET)
         self.assertNotEqual(ZoneFinder.NXRRSET, ZoneFinder.CNAME)
         self.assertNotEqual(ZoneFinder.CNAME, ZoneFinder.DNAME)
-        self.assertNotEqual(ZoneFinder.DNAME, ZoneFinder.WILDCARD)
-        self.assertNotEqual(ZoneFinder.WILDCARD, ZoneFinder.WILDCARD_CNAME)
-        self.assertNotEqual(ZoneFinder.WILDCARD_CNAME,
-                            ZoneFinder.WILDCARD_NXRRSET)
+
+    def test_findresultflags(self):
+        self.assertNotEqual(ZoneFinder.RESULT_WILDCARD,
+                            ZoneFinder.RESULT_NSEC_SIGNED)
+        self.assertNotEqual(ZoneFinder.RESULT_NSEC_SIGNED,
+                            ZoneFinder.RESULT_NSEC3_SIGNED)
 
     def test_findall(self):
         """
@@ -345,66 +347,68 @@ class DataSrcClient(unittest.TestCase):
         self.assertEqual(isc.dns.RRClass.IN(), finder.get_class())
         self.assertEqual("example.com.", finder.get_origin().to_text())
 
-        result, rrset = finder.find(isc.dns.Name("www.example.com"),
-                                    isc.dns.RRType.A(),
-                                    finder.FIND_DEFAULT)
+        result, rrset, _ = finder.find(isc.dns.Name("www.example.com"),
+                                       isc.dns.RRType.A(),
+                                       finder.FIND_DEFAULT)
         self.assertEqual(finder.SUCCESS, result)
         self.assertEqual("www.example.com. 3600 IN A 192.0.2.1\n",
                          rrset.to_text())
 
         # Check the optional parameters are optional
-        result, rrset = finder.find(isc.dns.Name("www.example.com"),
-                                    isc.dns.RRType.A())
+        result, rrset, _ = finder.find(isc.dns.Name("www.example.com"),
+                                       isc.dns.RRType.A())
         self.assertEqual(finder.SUCCESS, result)
         self.assertEqual("www.example.com. 3600 IN A 192.0.2.1\n",
                          rrset.to_text())
 
-        result, rrset = finder.find(isc.dns.Name("www.sql1.example.com"),
-                                    isc.dns.RRType.A(),
-                                    finder.FIND_DEFAULT)
+        result, rrset, _ = finder.find(isc.dns.Name("www.sql1.example.com"),
+                                       isc.dns.RRType.A(),
+                                       finder.FIND_DEFAULT)
         self.assertEqual(finder.DELEGATION, result)
         self.assertEqual("sql1.example.com. 3600 IN NS dns01.example.com.\n" +
                          "sql1.example.com. 3600 IN NS dns02.example.com.\n" +
                          "sql1.example.com. 3600 IN NS dns03.example.com.\n",
                          rrset.to_text())
 
-        result, rrset = finder.find(isc.dns.Name("doesnotexist.example.com"),
-                                    isc.dns.RRType.A(),
-                                    finder.FIND_DEFAULT)
+        result, rrset, _ = finder.find(isc.dns.Name("doesnotexist.example.com"),
+                                       isc.dns.RRType.A(),
+                                       finder.FIND_DEFAULT)
         self.assertEqual(finder.NXDOMAIN, result)
         self.assertEqual(None, rrset)
 
-        result, rrset = finder.find(isc.dns.Name("www.some.other.domain"),
-                                    isc.dns.RRType.A(),
-                                    finder.FIND_DEFAULT)
+        result, rrset, _ = finder.find(isc.dns.Name("www.some.other.domain"),
+                                       isc.dns.RRType.A(),
+                                       finder.FIND_DEFAULT)
         self.assertEqual(finder.NXDOMAIN, result)
         self.assertEqual(None, rrset)
 
-        result, rrset = finder.find(isc.dns.Name("www.example.com"),
-                                    isc.dns.RRType.TXT(),
-                                    finder.FIND_DEFAULT)
+        result, rrset, _ = finder.find(isc.dns.Name("www.example.com"),
+                                       isc.dns.RRType.TXT(),
+                                       finder.FIND_DEFAULT)
         self.assertEqual(finder.NXRRSET, result)
         self.assertEqual(None, rrset)
 
-        result, rrset = finder.find(isc.dns.Name("cname-ext.example.com"),
-                                    isc.dns.RRType.A(),
-                                    finder.FIND_DEFAULT)
+        result, rrset, _ = finder.find(isc.dns.Name("cname-ext.example.com"),
+                                       isc.dns.RRType.A(),
+                                       finder.FIND_DEFAULT)
         self.assertEqual(finder.CNAME, result)
         self.assertEqual(
             "cname-ext.example.com. 3600 IN CNAME www.sql1.example.com.\n",
             rrset.to_text())
 
-        result, rrset = finder.find(isc.dns.Name("foo.wild.example.com"),
-                                    isc.dns.RRType.A(),
-                                    finder.FIND_DEFAULT)
-        self.assertEqual(finder.WILDCARD, result)
+        result, rrset, flags = \
+            finder.find(isc.dns.Name("foo.wild.example.com"),
+                        isc.dns.RRType.A(), finder.FIND_DEFAULT)
+        self.assertEqual(finder.SUCCESS, result)
+        self.assertTrue((flags & finder.RESULT_WILDCARD) != 0)
         self.assertEqual("foo.wild.example.com. 3600 IN A 192.0.2.255\n",
                          rrset.to_text())
 
-        result, rrset = finder.find(isc.dns.Name("foo.wild.example.com"),
-                                    isc.dns.RRType.TXT(),
-                                    finder.FIND_DEFAULT)
-        self.assertEqual(finder.WILDCARD_NXRRSET, result)
+        result, rrset, _ = finder.find(isc.dns.Name("foo.wild.example.com"),
+                                       isc.dns.RRType.TXT(),
+                                       finder.FIND_DEFAULT)
+        self.assertEqual(finder.NXRRSET, result)
+        self.assertTrue((flags & finder.RESULT_WILDCARD) != 0)
         self.assertEqual(None, rrset)
 
         self.assertRaises(TypeError, finder.find,
@@ -463,16 +467,16 @@ class DataSrcUpdater(unittest.TestCase):
         # Check basic behavior of updater's finder
         dsc = isc.datasrc.DataSourceClient("sqlite3", WRITE_ZONE_DB_CONFIG)
         updater = dsc.get_updater(isc.dns.Name("example.com"), False)
-        result, rrset = updater.find(isc.dns.Name("www.example.com"),
-                                     isc.dns.RRType.A(),
-                                     ZoneFinder.FIND_DEFAULT)
+        result, rrset, _ = updater.find(isc.dns.Name("www.example.com"),
+                                        isc.dns.RRType.A(),
+                                        ZoneFinder.FIND_DEFAULT)
         self.assertEqual(ZoneFinder.SUCCESS, result)
         self.assertEqual("www.example.com. 3600 IN A 192.0.2.1\n",
                          rrset.to_text())
 
         # Omit optional parameters
-        result, rrset = updater.find(isc.dns.Name("www.example.com"),
-                                     isc.dns.RRType.A())
+        result, rrset, _ = updater.find(isc.dns.Name("www.example.com"),
+                                        isc.dns.RRType.A())
         self.assertEqual(ZoneFinder.SUCCESS, result)
         self.assertEqual("www.example.com. 3600 IN A 192.0.2.1\n",
                          rrset.to_text())
@@ -487,9 +491,9 @@ class DataSrcUpdater(unittest.TestCase):
         self.assertEqual(isc.dns.RRClass.IN(), finder.get_class())
         self.assertEqual("example.com.", finder.get_origin().to_text())
 
-        result, rrset = finder.find(isc.dns.Name("www.example.com"),
-                                    isc.dns.RRType.A(),
-                                    finder.FIND_DEFAULT)
+        result, rrset, _ = finder.find(isc.dns.Name("www.example.com"),
+                                       isc.dns.RRType.A(),
+                                       finder.FIND_DEFAULT)
         self.assertEqual(finder.SUCCESS, result)
         self.assertEqual("www.example.com. 3600 IN A 192.0.2.1\n",
                          rrset.to_text())
@@ -508,15 +512,15 @@ class DataSrcUpdater(unittest.TestCase):
 
         # The record should be gone in the updater, but not in the original
         # finder (since we have not committed)
-        result, rrset = updater.find(isc.dns.Name("www.example.com"),
-                                     isc.dns.RRType.A(),
-                                     finder.FIND_DEFAULT)
+        result, rrset, _ = updater.find(isc.dns.Name("www.example.com"),
+                                        isc.dns.RRType.A(),
+                                        finder.FIND_DEFAULT)
         self.assertEqual(finder.NXDOMAIN, result)
         self.assertEqual(None, rrset)
 
-        result, rrset = finder.find(isc.dns.Name("www.example.com"),
-                                    isc.dns.RRType.A(),
-                                    finder.FIND_DEFAULT)
+        result, rrset, _ = finder.find(isc.dns.Name("www.example.com"),
+                                       isc.dns.RRType.A(),
+                                       finder.FIND_DEFAULT)
         self.assertEqual(finder.SUCCESS, result)
         self.assertEqual("www.example.com. 3600 IN A 192.0.2.1\n",
                          rrset.to_text())
@@ -526,9 +530,9 @@ class DataSrcUpdater(unittest.TestCase):
         self.assertRaises(isc.datasrc.Error, updater.commit)
 
         # the record should be gone now in the 'real' finder as well
-        result, rrset = finder.find(isc.dns.Name("www.example.com"),
-                                    isc.dns.RRType.A(),
-                                    finder.FIND_DEFAULT)
+        result, rrset, _ = finder.find(isc.dns.Name("www.example.com"),
+                                       isc.dns.RRType.A(),
+                                       finder.FIND_DEFAULT)
         self.assertEqual(finder.NXDOMAIN, result)
         self.assertEqual(None, rrset)
 
@@ -540,9 +544,9 @@ class DataSrcUpdater(unittest.TestCase):
         # second commit should throw
         self.assertRaises(isc.datasrc.Error, updater.commit)
 
-        result, rrset = finder.find(isc.dns.Name("www.example.com"),
-                                    isc.dns.RRType.A(),
-                                    finder.FIND_DEFAULT)
+        result, rrset, _ = finder.find(isc.dns.Name("www.example.com"),
+                                       isc.dns.RRType.A(),
+                                       finder.FIND_DEFAULT)
         self.assertEqual(finder.SUCCESS, result)
         self.assertEqual("www.example.com. 3600 IN A 192.0.2.1\n",
                          rrset.to_text())
@@ -564,9 +568,9 @@ class DataSrcUpdater(unittest.TestCase):
         self.assertEqual(finder.SUCCESS, result)
         self.assertEqual(isc.dns.RRClass.IN(), finder.get_class())
         self.assertEqual("example.com.", finder.get_origin().to_text())
-        result, rrset = finder.find(isc.dns.Name("www.example.com"),
-                                    isc.dns.RRType.A(),
-                                    finder.FIND_DEFAULT)
+        result, rrset, _ = finder.find(isc.dns.Name("www.example.com"),
+                                       isc.dns.RRType.A(),
+                                       finder.FIND_DEFAULT)
         self.assertEqual(finder.SUCCESS, result)
         self.assertEqual("www.example.com. 3600 IN A 192.0.2.1\n",
                          rrset.to_text())
@@ -587,9 +591,9 @@ class DataSrcUpdater(unittest.TestCase):
         self.assertEqual(isc.dns.RRClass.IN(), finder.get_class())
         self.assertEqual("example.com.", finder.get_origin().to_text())
 
-        result, rrset = finder.find(isc.dns.Name("www.example.com"),
-                                    isc.dns.RRType.A(),
-                                    finder.FIND_DEFAULT)
+        result, rrset, _ = finder.find(isc.dns.Name("www.example.com"),
+                                       isc.dns.RRType.A(),
+                                       finder.FIND_DEFAULT)
         self.assertEqual(finder.SUCCESS, result)
         self.assertEqual("www.example.com. 3600 IN A 192.0.2.1\n",
                          rrset.to_text())
@@ -608,9 +612,9 @@ class DataSrcUpdater(unittest.TestCase):
 
         # The record should be gone in the updater, but not in the original
         # finder (since we have not committed)
-        result, rrset = updater.find(isc.dns.Name("www.example.com"),
-                                     isc.dns.RRType.A(),
-                                     finder.FIND_DEFAULT)
+        result, rrset, _ = updater.find(isc.dns.Name("www.example.com"),
+                                        isc.dns.RRType.A(),
+                                        finder.FIND_DEFAULT)
         self.assertEqual(finder.NXDOMAIN, result)
         self.assertEqual(None, rrset)
 
@@ -618,9 +622,9 @@ class DataSrcUpdater(unittest.TestCase):
         updater = None
 
         # the record should still be available in the 'real' finder as well
-        result, rrset = finder.find(isc.dns.Name("www.example.com"),
-                                    isc.dns.RRType.A(),
-                                    finder.FIND_DEFAULT)
+        result, rrset, _ = finder.find(isc.dns.Name("www.example.com"),
+                                       isc.dns.RRType.A(),
+                                       finder.FIND_DEFAULT)
         self.assertEqual(finder.SUCCESS, result)
         self.assertEqual("www.example.com. 3600 IN A 192.0.2.1\n",
                          rrset.to_text())

+ 5 - 5
src/lib/python/isc/notify/notify_out.py

@@ -284,12 +284,12 @@ class NotifyOut:
                          format_zone_str(zone_name, zone_class))
             return []
 
-        result, ns_rrset = finder.find(zone_name, RRType.NS())
+        result, ns_rrset, _ = finder.find(zone_name, RRType.NS())
         if result is not finder.SUCCESS or ns_rrset is None:
             logger.warn(NOTIFY_OUT_ZONE_NO_NS,
                         format_zone_str(zone_name, zone_class))
             return []
-        result, soa_rrset = finder.find(zone_name, RRType.SOA())
+        result, soa_rrset, _ = finder.find(zone_name, RRType.SOA())
         if result is not finder.SUCCESS or soa_rrset is None or \
                 soa_rrset.get_rdata_count() != 1:
             logger.warn(NOTIFY_OUT_ZONE_BAD_SOA,
@@ -302,11 +302,11 @@ class NotifyOut:
             ns_name = Name(ns_rdata.to_text())
             if soa_mname == ns_name:
                 continue
-            result, rrset = finder.find(ns_name, RRType.A())
+            result, rrset, _ = finder.find(ns_name, RRType.A())
             if result is finder.SUCCESS and rrset is not None:
                 addrs.extend([a.to_text() for a in rrset.get_rdata()])
 
-            result, rrset = finder.find(ns_name, RRType.AAAA())
+            result, rrset, _ = finder.find(ns_name, RRType.AAAA())
             if result is finder.SUCCESS and rrset is not None:
                 addrs.extend([aaaa.to_text() for aaaa in rrset.get_rdata()])
 
@@ -500,7 +500,7 @@ class NotifyOut:
                                            zone_name.to_text() + '/' +
                                            zone_class.to_text() + ' not found')
 
-        result, soa_rrset = finder.find(zone_name, RRType.SOA())
+        result, soa_rrset, _ = finder.find(zone_name, RRType.SOA())
         if result is not finder.SUCCESS or soa_rrset is None or \
                 soa_rrset.get_rdata_count() != 1:
             raise NotifyOutDataSourceError('_get_zone_soa: Zone ' +