From: Miod Vallat Date: Thu, 20 Mar 2025 13:35:00 +0000 (+0100) Subject: Add an API-specific lookup method to DNSBackend. X-Git-Tag: dnsdist-2.0.0-alpha2~66^2~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d1f8b2e7f990175560e578f8e70d323c0b266b72;p=thirdparty%2Fpdns.git Add an API-specific lookup method to DNSBackend. This method, APILookup(), behaves similarly to lookup() but allows disabled records to be returned to the caller. Backends with no support for disabled records (bind, geoip, ldap, lua2, pipe, tinydns) implement it as a by-default wrapper over lookup(). Other backends override with their own processing. SQL-style backends use distinct queries, api-id-query and api-any-id-query, so as not to penalize non-API workloads. --- diff --git a/docs/backends/remote.rst b/docs/backends/remote.rst index 2be4f4ebe8..59748bca4e 100644 --- a/docs/backends/remote.rst +++ b/docs/backends/remote.rst @@ -156,6 +156,7 @@ Methods required for different features :Secondary operation: ``getUnfreshSlaveInfos``, ``startTransaction``, ``commitTransaction``, ``abortTransaction``, ``feedRecord``, ``setFresh`` :DNSSEC operation (live-signing): ``getDomainKeys``, ``getBeforeAndAfterNamesAbsolute`` :Filling the Zone Cache: ``getAllDomains`` +:HTTP API specific: ``APILookup`` ``initialize`` ~~~~~~~~~~~~~~ @@ -163,7 +164,7 @@ Methods required for different features Called to initialize the backend. This is not called for HTTP connector. You should do your initializations here. -- Mandatory: Yes (except HTTP connector) +- Mandatory: yes (except HTTP connector) - Parameters: all parameters in connection string - Reply: true on success / false on failure @@ -190,7 +191,7 @@ Response: This method is used to do the basic query. You can omit auth, but if you are using DNSSEC this can lead into trouble. -- Mandatory: Yes +- Mandatory: yes - Parameters: qtype, qname, zone_id - Optional parameters: remote, local, real-remote - Reply: array of ``qtype,qname,content,ttl,domain_id,scopeMask,auth`` @@ -236,6 +237,20 @@ Response: {"result":[{"qtype":"A", "qname":"www.example.com", "content":"203.0.113.2", "ttl": 60}]} +``APILookup`` +~~~~~~~~~~~~~ + +This method is similar to :ref:`remote-lookup`, but also returns disabled +records. It allows for an extra optional parameter, ``include_disabled`` which, +if present and set to false, will only return non-disabled records (in which +case, the behaviour is equivalent to the ``lookup`` method.) + +- Mandatory: no (required if the HTTP API is to be used) +- Parameters: qtype, qname, zone_id +- Optional parameters: remote, local, real-remote, include_disabled +- Reply: array of ``qtype,qname,content,ttl,domain_id,scopeMask,auth,disabled`` +- Optional values: scopeMask and auth + ``list`` ~~~~~~~~ @@ -243,7 +258,7 @@ Lists all records for the zonename. If you are running DNSSEC, you should take care of setting auth to appropriate value, otherwise things can go wrong. -- Mandatory: No (Gives AXFR support) +- Mandatory: no (gives AXFR support) - Parameters: zonename, domain_id - Optional parameters: domain_id - Reply: array of ``qtype,qname,content,ttl,domain_id,scopeMask,auth`` @@ -385,7 +400,7 @@ one of NSEC3PARAM, PRESIGNED, SOA-EDIT. Can be others, too. You **must** always return something, if there are no values, you shall return an empty array. -- Mandatory: No +- Mandatory: no - Parameters: name, kind - Reply: array of strings @@ -429,7 +444,7 @@ Replaces the value(s) on domain name for variable kind to string(s) on array value. The old value is discarded. Value can be an empty array, which can be interpreted as deletion request. -- Mandatory: No +- Mandatory: no - Parameters: name, kind, value - Reply: true on success, false on failure @@ -543,7 +558,7 @@ Response: Adds key into local storage. See :ref:`remote-getdomainkeys` for more information. -- Mandatory: No +- Mandatory: no - Parameters: name, key=\ ````, id - Reply: true for success, false for failure @@ -607,7 +622,7 @@ Response: Removes key id from domain name. -- Mandatory: No +- Mandatory: no - Parameters: name, id - Reply: true for success, false for failure @@ -649,7 +664,7 @@ Response: Activates key id for domain name. -- Mandatory: No +- Mandatory: no - Parameters: name, id - Reply: true for success, false for failure @@ -691,7 +706,7 @@ Response: Deactivates key id for domain name. -- Mandatory: No +- Mandatory: no - Parameters: name, id - Reply: true for success, false for failure @@ -733,7 +748,7 @@ Response: Publish key id for domain name. -- Mandatory: No +- Mandatory: no - Parameters: name, id - Reply: true for success, false for failure @@ -776,7 +791,7 @@ Response: Unpublish key id for domain name. -- Mandatory: No +- Mandatory: no - Parameters: name, id - Reply: true for success, false for failure @@ -819,7 +834,7 @@ Response: Retrieves the key needed to sign AXFR. -- Mandatory: No +- Mandatory: no - Parameters: name - Reply: algorithm, content @@ -865,7 +880,7 @@ Everything else will default to something. Default values: serial:0, kind:NATIVE, id:-1, notified_serial:-1, last_check:0, masters: []. Masters, if present, must be array of strings. -- Mandatory: No +- Mandatory: no - Parameters: name - Reply: zone - Optional values: serial, kind, id, notified_serial, last_check, @@ -909,7 +924,7 @@ Response: Updates last notified serial for the domain id. Any errors are ignored. -- Mandatory: No +- Mandatory: no - Parameters: id, serial - Reply: true for success, false for failure @@ -955,7 +970,7 @@ Response: Determines whether given IP is primary for given domain name. -- Mandatory: No +- Mandatory: no - Parameters: name,ip - Reply: true for success, false for failure. @@ -999,7 +1014,7 @@ Creates new domain with given record(s) as primary servers. IP address is the address where notify is received from. nsset is array of NS resource records. -- Mandatory: No +- Mandatory: no - Parameters: ip,domain,nsset,account - Reply: true for success, false for failure. can also return account=>name of account< and nameserver. @@ -1061,7 +1076,7 @@ Alternative response Creates new domain. This method is called when NOTIFY is received and you are superslaving. - - Mandatory: No + - Mandatory: no - Parameters: ip, domain - Optional parameters: nameserver, account - Reply: true for success, false for failure @@ -1107,7 +1122,7 @@ Response: This method replaces a given resource record with new set. The new qtype can be different from the old. -- Mandatory: No +- Mandatory: no - Parameters: domain_id, qname, qtype, rrset - Reply: true for success, false for failure @@ -1154,7 +1169,7 @@ Response: Asks to feed new record into system. If startTransaction was called, trxId identifies a transaction. It is not always called by PowerDNS. -- Mandatory: No +- Mandatory: no - Parameters: rr, trxid - Reply: true for success, false for failure @@ -1206,7 +1221,7 @@ _sip._upd.example.com, but no _udp.example.com. PowerDNS requires that there exists a non-terminal in between, and this instructs you to add one. If startTransaction is called, trxid identifies a transaction. -- Mandatory: No +- Mandatory: no - Parameters: nonterm, trxid - Reply: true for success, false for failure @@ -1253,7 +1268,7 @@ Response: Same as :ref:`remote-feedents`, but provides NSEC3 hashing parameters. Note that salt is BYTE value, and can be non-readable text. -- Mandatory: No +- Mandatory: no - Parameters: trxid, domain_id, domain, times, salt, narrow, nonterm - Reply: true for success, false for failure @@ -1300,7 +1315,7 @@ Response: Starts a new transaction. Transaction ID is chosen for you. Used to identify f.ex. AXFR transfer. -- Mandatory: No +- Mandatory: no - Parameters: domain_id, domain, trxid - Reply: true for success, false for failure @@ -1347,7 +1362,7 @@ Response: Signals successful transfer and asks to commit data into permanent storage. -- Mandatory: No +- Mandatory: no - Parameters: trxid - Reply: true for success, false for failure @@ -1391,7 +1406,7 @@ Response: Signals failed transaction, and that you should rollback any changes. -- Mandatory: No +- Mandatory: no - Parameters: trxid - Reply: true for success, false for failure @@ -1436,7 +1451,7 @@ Response: Asks you to calculate a new serial based on the given data and update the serial. -- Mandatory: No +- Mandatory: no - Parameters: domain,sd - Reply: true for success, false for failure @@ -1700,7 +1715,7 @@ Response: Called when a primary freshness check succeeded. This does not indicate the zone was updated on the primary. -- Mandatory: No +- Mandatory: no - Parameters: id - Reply: true for success, false for failure diff --git a/modules/gmysqlbackend/gmysqlbackend.cc b/modules/gmysqlbackend/gmysqlbackend.cc index 77c2fcd1b8..1b1f161eaf 100644 --- a/modules/gmysqlbackend/gmysqlbackend.cc +++ b/modules/gmysqlbackend/gmysqlbackend.cc @@ -94,6 +94,9 @@ public: declare(suffix, "any-query", "Any query", record_query + " disabled=0 and name=?"); declare(suffix, "any-id-query", "Any with ID query", record_query + " disabled=0 and name=? and domain_id=?"); + declare(suffix, "api-id-query", "API basic with ID query", record_query + " (disabled=0 or ?) and type=? and name=? and domain_id=?"); + declare(suffix, "api-any-id-query", "API any with ID query", record_query + " (disabled=0 or ?) and name=? and domain_id=?"); + declare(suffix, "list-query", "AXFR query", "SELECT content,ttl,prio,type,domain_id,disabled,name,auth,ordername FROM records WHERE (disabled=0 OR ?) and domain_id=? order by name, type"); declare(suffix, "list-subzone-query", "Subzone listing", record_query + " disabled=0 and (name=? OR name like ?) and domain_id=?"); diff --git a/modules/godbcbackend/godbcbackend.cc b/modules/godbcbackend/godbcbackend.cc index 00d30bc4bb..f2c4b0fcaa 100644 --- a/modules/godbcbackend/godbcbackend.cc +++ b/modules/godbcbackend/godbcbackend.cc @@ -75,6 +75,9 @@ public: declare(suffix, "any-query", "Any query", record_query + " disabled=0 and name=?"); declare(suffix, "any-id-query", "Any with ID query", record_query + " disabled=0 and name=? and domain_id=?"); + declare(suffix, "api-id-query", "API basic with ID query", record_query + " (disabled=0 or disabled=?) and type=? and name=? and domain_id=?"); + declare(suffix, "api-any-id-query", "API any with ID query", record_query + " (disabled=0 or disabled=?) and name=? and domain_id=?"); + declare(suffix, "list-query", "AXFR query", "SELECT content,ttl,prio,type,domain_id,disabled,name,auth,CONVERT(varchar(255), ordername, 0) FROM records WHERE (disabled=0 OR disabled=?) and domain_id=? order by name, type"); declare(suffix, "list-subzone-query", "Subzone listing", record_query + " disabled=0 and (name=? OR name like ?) and domain_id=?"); diff --git a/modules/gpgsqlbackend/gpgsqlbackend.cc b/modules/gpgsqlbackend/gpgsqlbackend.cc index 10f4b9ad9f..895c865511 100644 --- a/modules/gpgsqlbackend/gpgsqlbackend.cc +++ b/modules/gpgsqlbackend/gpgsqlbackend.cc @@ -102,6 +102,9 @@ public: declare(suffix, "any-query", "Any query", record_query + " disabled=false and name=$1"); declare(suffix, "any-id-query", "Any with ID query", record_query + " disabled=false and name=$1 and domain_id=$2"); + declare(suffix, "api-id-query", "API basic with ID query", record_query + " (disabled=false or $1) and type=$2 and name=$3 and domain_id=$4"); + declare(suffix, "api-any-id-query", "API any with ID query", record_query + " (disabled=false or $1) and name=$2 and domain_id=$3"); + declare(suffix, "list-query", "AXFR query", "SELECT content,ttl,prio,type,domain_id,disabled::int,name,auth::int,ordername FROM records WHERE (disabled=false OR $1) and domain_id=$2 order by name, type"); declare(suffix, "list-subzone-query", "Subzone listing", record_query + " disabled=false and (name=$1 OR name like $2) and domain_id=$3"); diff --git a/modules/gsqlite3backend/gsqlite3backend.cc b/modules/gsqlite3backend/gsqlite3backend.cc index 4b45c5c6ee..ae0cd6d698 100644 --- a/modules/gsqlite3backend/gsqlite3backend.cc +++ b/modules/gsqlite3backend/gsqlite3backend.cc @@ -88,6 +88,9 @@ public: declare(suffix, "any-query", "Any query", record_query + " disabled=0 and name=:qname"); declare(suffix, "any-id-query", "Any with ID query", record_query + " disabled=0 and name=:qname and domain_id=:domain_id"); + declare(suffix, "api-id-query", "API basic with ID query", record_query + " (disabled=0 or :include_disabled) and type=:qtype and name=:qname and domain_id=:domain_id"); + declare(suffix, "api-any-id-query", "API any with ID query", record_query + " (disabled=0 or :include_disabled) and name=:qname and domain_id=:domain_id"); + declare(suffix, "list-query", "AXFR query", "SELECT content,ttl,prio,type,domain_id,disabled,name,auth,ordername FROM records WHERE (disabled=0 OR :include_disabled) and domain_id=:domain_id order by name, type"); declare(suffix, "list-subzone-query", "Subzone listing", record_query + " disabled=0 and (name=:zone OR name like :wildzone) and domain_id=:domain_id"); diff --git a/modules/lmdbbackend/lmdbbackend.cc b/modules/lmdbbackend/lmdbbackend.cc index 267816bbcd..737e2c6bc5 100644 --- a/modules/lmdbbackend/lmdbbackend.cc +++ b/modules/lmdbbackend/lmdbbackend.cc @@ -1481,14 +1481,14 @@ bool LMDBBackend::list(const ZoneName& target, int /* id */, bool include_disabl return true; } -void LMDBBackend::lookup(const QType& type, const DNSName& qdomain, int zoneId, DNSPacket* /* p */) +void LMDBBackend::lookupInternal(const QType& type, const DNSName& qdomain, int zoneId, DNSPacket* /* p */, bool include_disabled) { if (d_dolog) { g_log << Logger::Warning << "Got lookup for " << qdomain << "|" << type.toString() << " in zone " << zoneId << endl; d_dtime.set(); } - d_includedisabled = false; + d_includedisabled = include_disabled; DNSName hunt(qdomain); DomainInfo di; diff --git a/modules/lmdbbackend/lmdbbackend.hh b/modules/lmdbbackend/lmdbbackend.hh index 65fa274644..bf0f816ee5 100644 --- a/modules/lmdbbackend/lmdbbackend.hh +++ b/modules/lmdbbackend/lmdbbackend.hh @@ -78,7 +78,8 @@ public: bool replaceComments(uint32_t domain_id, const DNSName& qname, const QType& qt, const vector& comments) override; void getAllDomains(vector* domains, bool doSerial, bool include_disabled) override; - void lookup(const QType& type, const DNSName& qdomain, int zoneId, DNSPacket* p = nullptr) override; + void lookup(const QType& type, const DNSName& qdomain, int zoneId, DNSPacket* p = nullptr) override { lookupInternal(type, qdomain, zoneId, p, false); } + void APILookup(const QType& type, const DNSName& qdomain, int zoneId, DNSPacket* p = nullptr, bool include_disabled = false) override { lookupInternal(type, qdomain, zoneId, p, include_disabled); } bool get(DNSResourceRecord& rr) override; bool get(DNSZoneRecord& dzr) override; @@ -311,6 +312,7 @@ private: void getAllDomainsFiltered(vector* domains, const std::function& allow); + void lookupInternal(const QType& type, const DNSName& qdomain, int zoneId, DNSPacket* p, bool include_disabled); bool getSerial(DomainInfo& di); bool upgradeToSchemav3(); diff --git a/modules/remotebackend/remotebackend.cc b/modules/remotebackend/remotebackend.cc index 61f1633028..daa5bbec71 100644 --- a/modules/remotebackend/remotebackend.cc +++ b/modules/remotebackend/remotebackend.cc @@ -225,6 +225,39 @@ void RemoteBackend::lookup(const QType& qtype, const DNSName& qdomain, int zoneI d_index = 0; } +// Similar to lookup above, but passes an extra include_disabled parameter. +void RemoteBackend::APILookup(const QType& qtype, const DNSName& qdomain, int zoneId, DNSPacket* pkt_p, bool include_disabled) +{ + if (d_index != -1) { + throw PDNSException("Attempt to lookup while one running"); + } + + string localIP = "0.0.0.0"; + string remoteIP = "0.0.0.0"; + string realRemote = "0.0.0.0/0"; + + if (pkt_p != nullptr) { + localIP = pkt_p->getLocal().toString(); + realRemote = pkt_p->getRealRemote().toString(); + remoteIP = pkt_p->getInnerRemote().toString(); + } + + Json query = Json::object{ + {"method", "APILookup"}, + {"parameters", Json::object{{"qtype", qtype.toString()}, {"qname", qdomain.toString()}, {"remote", remoteIP}, {"local", localIP}, {"real-remote", realRemote}, {"zone-id", zoneId}, {"include-disabled", include_disabled}}}}; + + if (!this->send(query) || !this->recv(d_result)) { + return; + } + + // OK. we have result parameters in result. do not process empty result. + if (!d_result["result"].is_array() || d_result["result"].array_items().empty()) { + return; + } + + d_index = 0; +} + bool RemoteBackend::list(const ZoneName& target, int domain_id, bool include_disabled) { if (d_index != -1) { diff --git a/modules/remotebackend/remotebackend.hh b/modules/remotebackend/remotebackend.hh index b91f373eea..e6d7062392 100644 --- a/modules/remotebackend/remotebackend.hh +++ b/modules/remotebackend/remotebackend.hh @@ -169,6 +169,7 @@ public: unsigned int getCapabilities() override; void lookup(const QType& qtype, const DNSName& qdomain, int zoneId = -1, DNSPacket* pkt_p = nullptr) override; + void APILookup(const QType& qtype, const DNSName& qdomain, int zoneId = -1, DNSPacket* pkt_p = nullptr, bool include_disabled = false) override; bool get(DNSResourceRecord& rr) override; bool list(const ZoneName& target, int domain_id, bool include_disabled = false) override; diff --git a/pdns/backends/gsql/gsqlbackend.cc b/pdns/backends/gsql/gsqlbackend.cc index 42f6b525cd..2b7fe5847d 100644 --- a/pdns/backends/gsql/gsqlbackend.cc +++ b/pdns/backends/gsql/gsqlbackend.cc @@ -63,6 +63,9 @@ GSQLBackend::GSQLBackend(const string &mode, const string &suffix) d_ANYNoIdQuery=getArg("any-query"); d_ANYIdQuery=getArg("any-id-query"); + d_APIIdQuery=getArg("api-id-query"); + d_APIANYIdQuery=getArg("api-any-id-query"); + d_listQuery=getArg("list-query"); d_listSubZoneQuery=getArg("list-subzone-query"); @@ -140,6 +143,8 @@ GSQLBackend::GSQLBackend(const string &mode, const string &suffix) d_IdQuery_stmt = nullptr; d_ANYNoIdQuery_stmt = nullptr; d_ANYIdQuery_stmt = nullptr; + d_APIIdQuery_stmt = nullptr; + d_APIANYIdQuery_stmt = nullptr; d_listQuery_stmt = nullptr; d_listSubZoneQuery_stmt = nullptr; d_InfoOfDomainsZoneQuery_stmt = nullptr; @@ -1456,6 +1461,44 @@ void GSQLBackend::lookup(const QType& qtype, const DNSName& qname, int domain_id d_qname=qname; } +void GSQLBackend::APILookup(const QType& qtype, const DNSName& qname, int domain_id, DNSPacket* /* pkt_p */, bool include_disabled) +{ + try { + reconnectIfNeeded(); + + if(qtype.getCode()!=QType::ANY) { + d_query_name = "api-id-query"; + d_query_stmt = &d_APIIdQuery_stmt; + // clang-format off + (*d_query_stmt)-> + bind("include_disabled", (int)include_disabled)-> + bind("qtype", qtype.toString())-> + bind("qname", qname)-> + bind("domain_id", domain_id); + // clang-format on + } else { + // qtype==ANY + d_query_name = "api-any-id-query"; + d_query_stmt = &d_APIANYIdQuery_stmt; + // clang-format off + (*d_query_stmt)-> + bind("include_disabled", (int)include_disabled)-> + bind("qname", qname)-> + bind("domain_id", domain_id); + // clang-format on + } + + (*d_query_stmt)-> + execute(); + } + catch(SSqlException &e) { + throw PDNSException("GSQLBackend unable to APILookup '" + qname.toLogString() + "(" + std::to_string(domain_id) + ")|" + qtype.toString() + "':"+e.txtReason()); + } + + d_list=false; + d_qname=qname; +} + bool GSQLBackend::list(const ZoneName &target, int domain_id, bool include_disabled) { DLOG(g_log<<"GSQLBackend constructing handle for list of domain id '"<prepare(d_IdQuery, 3); d_ANYNoIdQuery_stmt = d_db->prepare(d_ANYNoIdQuery, 1); d_ANYIdQuery_stmt = d_db->prepare(d_ANYIdQuery, 2); + d_APIIdQuery_stmt = d_db->prepare(d_APIIdQuery, 4); + d_APIANYIdQuery_stmt = d_db->prepare(d_APIANYIdQuery, 3); d_listQuery_stmt = d_db->prepare(d_listQuery, 2); d_listSubZoneQuery_stmt = d_db->prepare(d_listSubZoneQuery, 3); d_PrimaryOfDomainsZoneQuery_stmt = d_db->prepare(d_PrimaryOfDomainsZoneQuery, 1); @@ -129,6 +131,8 @@ protected: d_IdQuery_stmt.reset(); d_ANYNoIdQuery_stmt.reset(); d_ANYIdQuery_stmt.reset(); + d_APIIdQuery_stmt.reset(); + d_APIANYIdQuery_stmt.reset(); d_listQuery_stmt.reset(); d_listSubZoneQuery_stmt.reset(); d_PrimaryOfDomainsZoneQuery_stmt.reset(); @@ -196,6 +200,7 @@ protected: public: unsigned int getCapabilities() override; void lookup(const QType &, const DNSName &qdomain, int zoneId, DNSPacket *p=nullptr) override; + void APILookup(const QType &qtype, const DNSName &qname, int domain_id, DNSPacket *p=nullptr, bool include_disabled = false) override; bool list(const ZoneName &target, int domain_id, bool include_disabled=false) override; bool get(DNSResourceRecord &r) override; void getAllDomains(vector* domains, bool getSerial, bool include_disabled) override; @@ -294,6 +299,9 @@ private: string d_ANYNoIdQuery; string d_ANYIdQuery; + string d_APIIdQuery; + string d_APIANYIdQuery; + string d_listQuery; string d_listSubZoneQuery; string d_logprefix; @@ -375,6 +383,8 @@ private: unique_ptr d_IdQuery_stmt; unique_ptr d_ANYNoIdQuery_stmt; unique_ptr d_ANYIdQuery_stmt; + unique_ptr d_APIIdQuery_stmt; + unique_ptr d_APIANYIdQuery_stmt; unique_ptr d_listQuery_stmt; unique_ptr d_listSubZoneQuery_stmt; unique_ptr d_PrimaryOfDomainsZoneQuery_stmt; diff --git a/pdns/dnsbackend.cc b/pdns/dnsbackend.cc index fe33b44940..a3e529846d 100644 --- a/pdns/dnsbackend.cc +++ b/pdns/dnsbackend.cc @@ -67,6 +67,12 @@ int DNSBackend::getArgAsNum(const string& key) return arg().asNum(d_prefix + "-" + key); } +// Default API lookup has no support for disabled records and simply wraps lookup() +void DNSBackend::APILookup(const QType& qtype, const DNSName& qdomain, int zoneId, DNSPacket* pkt_p, bool /* include_disabled */) +{ + lookup(qtype, qdomain, zoneId, pkt_p); +} + void BackendFactory::declare(const string& suffix, const string& param, const string& explanation, const string& value) { string fullname = d_name + suffix + "-" + param; diff --git a/pdns/dnsbackend.hh b/pdns/dnsbackend.hh index 5b670f439a..6059b1077e 100644 --- a/pdns/dnsbackend.hh +++ b/pdns/dnsbackend.hh @@ -169,6 +169,7 @@ public: //! lookup() initiates a lookup. A lookup without results should not throw! virtual void lookup(const QType& qtype, const DNSName& qdomain, int zoneId = -1, DNSPacket* pkt_p = nullptr) = 0; + virtual void APILookup(const QType& qtype, const DNSName& qdomain, int zoneId = -1, DNSPacket* pkt_p = nullptr, bool include_disabled = false); virtual bool get(DNSResourceRecord&) = 0; //!< retrieves one DNSResource record, returns false if no more were available virtual bool get(DNSZoneRecord& zoneRecord);