]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
Provide a default searchRecords implementation.
authorMiod Vallat <miod.vallat@powerdns.com>
Thu, 17 Jul 2025 15:24:42 +0000 (17:24 +0200)
committerMiod Vallat <miod.vallat@powerdns.com>
Thu, 31 Jul 2025 10:29:30 +0000 (12:29 +0200)
This is the LMDB searchRecords being promoted to backend-agnostic, as it
only depends on list() and getAllDomains() to work (and get(), obviously).

A new capability, CAP_SEARCH, is added to advertize these requirements are
fulfilled; search will return failure if they are not.

Signed-off-by: Miod Vallat <miod.vallat@powerdns.com>
docs/backends/lua2.rst
docs/backends/tinydns.rst
modules/bindbackend/binddnssec.cc
modules/lmdbbackend/lmdbbackend.cc
modules/lmdbbackend/lmdbbackend.hh
modules/lua2backend/lua2api2.hh
modules/remotebackend/remotebackend.cc
modules/tinydnsbackend/tinydnsbackend.hh
pdns/backends/gsql/gsqlbackend.cc
pdns/dnsbackend.cc
pdns/dnsbackend.hh

index eec138431faadbc52cbe41c3da0be04965f90f20..99dba66bba85d696d018ead05a0dd917e7f4603d 100644 (file)
@@ -11,7 +11,7 @@ Lua2 Backend
 * DNSSEC: Yes
 * Disabled data: No
 * Comments: No
-* Search: No
+* Search: Yes\*
 * Views: No
 * API: Read-Write
 * Multiple instances: Yes
@@ -123,7 +123,8 @@ OUTPUT:
  domaininfo. See :ref:`dns_get_domaininfo() <backends_lua2_dns_get_domaininfo>`.
 
 NOTES:
- This function is **optional**, except if you need primary functionality.
+ This function is **optional**, except if you need primary functionality. It
+ is required if you want to be able to search records.
 
 ``dns_get_domain_metadata(domain, kind)``
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
index 933d58855c30775b62e9a87a5977f5324a9082e4..ba56c2c0df1fe11d277ba1a0fae20ca229c5bd28 100644 (file)
@@ -11,7 +11,7 @@ TinyDNS Backend
 * DNSSEC: No
 * Disabled data: No
 * Comments: No
-* Search: No
+* Search: since version 5.1.0
 * Views: No
 * API: Read-only
 * Multiple Instances: Yes
index 20f00691cdefa3364a753e4a1a742300ad86d761..ee06af285bff1c8b97f4ca53d9c72fec534a5e81 100644 (file)
@@ -38,7 +38,7 @@ void Bind2Backend::setupDNSSEC()
 
 unsigned int Bind2Backend::getCapabilities()
 {
-  unsigned int caps = CAP_LIST;
+  unsigned int caps = CAP_LIST | CAP_SEARCH;
   if (d_hybrid) {
     caps |= CAP_DNSSEC;
   }
@@ -203,7 +203,7 @@ void Bind2Backend::freeStatements()
 
 unsigned int Bind2Backend::getCapabilities()
 {
-  unsigned int caps = CAP_LIST;
+  unsigned int caps = CAP_LIST | CAP_SEARCH;
   if (d_dnssecdb || d_hybrid) {
     caps |= CAP_DNSSEC;
   }
index 7da2de0b0e871c4909f87c4859c626891d25254a..376433c429b0180da12c8a687e2bbaed748dcdbc 100644 (file)
@@ -846,7 +846,7 @@ void LMDBBackend::openAllTheDatabases()
 
 unsigned int LMDBBackend::getCapabilities()
 {
-  unsigned int caps = CAP_DNSSEC | CAP_DIRECT | CAP_LIST | CAP_CREATE;
+  unsigned int caps = CAP_DNSSEC | CAP_DIRECT | CAP_LIST | CAP_CREATE | CAP_SEARCH;
   if (d_views) {
     caps |= CAP_VIEWS;
   }
@@ -1426,32 +1426,6 @@ bool LMDBBackend::replaceComments([[maybe_unused]] domainid_t domain_id, [[maybe
   return comments.empty();
 }
 
-bool LMDBBackend::searchRecords(const string& pattern, size_t maxResults, vector<DNSResourceRecord>& result)
-{
-  SimpleMatch simpleMatch(pattern, true);
-  std::vector<DomainInfo> domains;
-  getAllDomains(&domains, false, true);
-  for (const auto& info : domains) {
-    if (!list(info.zone, info.id, true)) {
-      return false;
-    }
-    DNSResourceRecord rec;
-    while (get(rec)) {
-      if (maxResults == 0) {
-        continue;
-      }
-      if (simpleMatch.match(rec.qname.toStringNoDot()) || simpleMatch.match(rec.content)) {
-        result.emplace_back(rec);
-        --maxResults;
-      }
-    }
-    if (maxResults == 0) {
-      break;
-    }
-  }
-  return true;
-}
-
 // FIXME: this is not very efficient
 static DNSName keyUnconv(std::string& instr)
 {
index 3665533a0a9e99871aacf498b3f59f489bf6f28a..7d4260033e5170690989045252793884ad89b71e 100644 (file)
@@ -88,7 +88,6 @@ public:
   bool feedEnts3(domainid_t domain_id, const DNSName& domain, map<DNSName, bool>& nonterm, const NSEC3PARAMRecordContent& ns3prc, bool narrow) override;
   bool replaceRRSet(domainid_t domain_id, const DNSName& qname, const QType& qt, const vector<DNSResourceRecord>& rrset) override;
   bool replaceComments(domainid_t domain_id, const DNSName& qname, const QType& qt, const vector<Comment>& comments) override;
-  bool searchRecords(const string& pattern, size_t maxResults, vector<DNSResourceRecord>& result) override;
 
   void viewList(vector<string>& /* result */) override;
   void viewListZones(const string& /* view */, vector<ZoneName>& /* result */) override;
index 660f117b8aa1da1ed73cedb5a6e89d6f2d576c1a..7e31f0d06653d8380021d97003687521b1abf4f6 100644 (file)
@@ -135,6 +135,9 @@ public:
     if (d_dnssec) {
       caps |= CAP_DNSSEC;
     }
+    if (f_get_all_domains != nullptr) {
+      caps |= CAP_SEARCH;
+    }
     return caps;
   }
 
index 84939731101e1a4704b6661b20e6fd104fc7c2bd..6e11267a4895a1a1899e607c5a26982cb565c0e0 100644 (file)
@@ -544,7 +544,7 @@ bool RemoteBackend::unpublishDomainKey(const ZoneName& name, unsigned int keyId)
 
 unsigned int RemoteBackend::getCapabilities()
 {
-  unsigned int caps = CAP_DIRECT | CAP_LIST;
+  unsigned int caps = CAP_DIRECT | CAP_LIST | CAP_SEARCH;
   if (d_dnssec) {
     caps |= CAP_DNSSEC;
   }
index d2fe0789eabaca12c0ebeae4f67694d1aeebf183..c7b9cff4e135d03467263c85929afbf9f02e5daa 100644 (file)
@@ -68,7 +68,7 @@ public:
   // Methods for simple operation
   TinyDNSBackend(const string& suffix);
 
-  unsigned int getCapabilities() override { return CAP_LIST; }
+  unsigned int getCapabilities() override { return CAP_LIST | CAP_SEARCH; }
   void lookup(const QType& qtype, const DNSName& qdomain, domainid_t zoneId, DNSPacket* pkt_p = nullptr) override;
   bool list(const ZoneName& target, domainid_t domain_id, bool include_disabled = false) override;
   bool get(DNSResourceRecord& rr) override;
index 24cebbb6b1224e9e9fafa9b45f6977c9aaebed94..0d7160487d46bd376cd00d31672f6062c5fdb2e5 100644 (file)
@@ -874,7 +874,7 @@ bool GSQLBackend::updateEmptyNonTerminals(domainid_t domain_id, set<DNSName>& in
 
 unsigned int GSQLBackend::getCapabilities()
 {
-  unsigned int caps = CAP_COMMENTS | CAP_DIRECT | CAP_LIST | CAP_CREATE;
+  unsigned int caps = CAP_COMMENTS | CAP_DIRECT | CAP_LIST | CAP_CREATE | CAP_SEARCH;
   if (d_dnssecQueries) {
     caps |= CAP_DNSSEC;
   }
index 90901e91a8186216f80f6d3762554720b2e2f9f5..8e1d436eac91e8ba61c8607e7264a804918c2c10 100644 (file)
@@ -73,6 +73,40 @@ void DNSBackend::APILookup(const QType& qtype, const DNSName& qdomain, domainid_
   lookup(qtype, qdomain, zoneId, nullptr);
 }
 
+// Default search logic, for backends which can enumerate their records.
+bool DNSBackend::searchRecords(const string& pattern, size_t maxResults, vector<DNSResourceRecord>& result)
+{
+  // We depend upon working list(), but also getAllDomains(), which is why we
+  // are checking explicitly for CAP_SEARCH in addition to CAP_LIST - the
+  // default getAllDomains() implementation below is not safe to use here.
+  if ((getCapabilities() & (CAP_LIST | CAP_SEARCH)) != (CAP_LIST | CAP_SEARCH)) {
+    return false;
+  }
+
+  SimpleMatch simpleMatch(pattern, true);
+  std::vector<DomainInfo> domains;
+  getAllDomains(&domains, false, true);
+  for (const auto& info : domains) {
+    if (!list(info.zone, info.id, true)) {
+      return false;
+    }
+    DNSResourceRecord rec;
+    while (get(rec)) {
+      if (maxResults == 0) {
+        continue;
+      }
+      if (simpleMatch.match(rec.qname) || simpleMatch.match(rec.content)) {
+        result.emplace_back(rec);
+        --maxResults;
+      }
+    }
+    if (maxResults == 0) {
+      break;
+    }
+  }
+  return true;
+}
+
 void BackendFactory::declare(const string& suffix, const string& param, const string& explanation, const string& value)
 {
   string fullname = d_name + suffix + "-" + param;
index f87391e63cdb4ca31e4681a49a4c8559b0e8ee14..ec1532c30e0e2b8a45ad1b6b1dd082300ab6479c 100644 (file)
@@ -164,6 +164,7 @@ public:
     CAP_LIST = 1 << 3, // Backend supports record enumeration
     CAP_CREATE = 1 << 4, // Backend supports domain creation
     CAP_VIEWS = 1 << 5, // Backend supports views
+    CAP_SEARCH = 1 << 6, // Backend supports record search
   };
 
   virtual unsigned int getCapabilities() = 0;
@@ -468,10 +469,7 @@ public:
   }
 
   //! Search for records, returns true if search was done successfully.
-  virtual bool searchRecords(const string& /* pattern */, size_t /* maxResults */, vector<DNSResourceRecord>& /* result */)
-  {
-    return false;
-  }
+  virtual bool searchRecords(const string& pattern, size_t maxResults, vector<DNSResourceRecord>& result);
 
   //! Search for comments, returns true if search was done successfully.
   virtual bool searchComments(const string& /* pattern */, size_t /* maxResults */, vector<Comment>& /* result */)