From b51bb9a2a39cdd2d6c1d18a5f135a1f6a5afd0a5 Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Wed, 27 May 2020 10:25:06 +0200 Subject: [PATCH] auth: Add unit tests for the UeberBackend --- modules/bindbackend/bindbackend2.cc | 6 +- modules/geoipbackend/geoipbackend.cc | 4 +- modules/gmysqlbackend/gmysqlbackend.cc | 4 +- modules/godbcbackend/godbcbackend.cc | 4 +- modules/gpgsqlbackend/gpgsqlbackend.cc | 4 +- modules/gsqlite3backend/gsqlite3backend.cc | 4 +- modules/ldapbackend/ldapbackend.cc | 4 +- modules/lmdbbackend/lmdbbackend.cc | 4 +- modules/lua2backend/lua2backend.cc | 4 +- modules/pipebackend/pipebackend.cc | 4 +- modules/randombackend/randombackend.cc | 4 +- modules/remotebackend/remotebackend.cc | 4 +- modules/tinydnsbackend/tinydnsbackend.cc | 4 +- pdns/Makefile.am | 3 +- pdns/dnsbackend.cc | 50 +- pdns/dnsbackend.hh | 5 +- pdns/pdnsutil.cc | 3 + pdns/test-ueberbackend_cc.cc | 1126 ++++++++++++++++++++ pdns/ueberbackend.cc | 2 +- pdns/ueberbackend.hh | 7 +- 20 files changed, 1197 insertions(+), 53 deletions(-) create mode 100644 pdns/test-ueberbackend_cc.cc diff --git a/modules/bindbackend/bindbackend2.cc b/modules/bindbackend/bindbackend2.cc index a882614323..d29345c12c 100644 --- a/modules/bindbackend/bindbackend2.cc +++ b/modules/bindbackend/bindbackend2.cc @@ -1443,7 +1443,7 @@ class Bind2Factory : public BackendFactory public: Bind2Factory() : BackendFactory("bind") {} - void declareArguments(const string &suffix="") + void declareArguments(const string &suffix="") override { declare(suffix,"ignore-broken-records","Ignore records that are out-of-bound for the zone.","no"); declare(suffix,"config","Location of named.conf",""); @@ -1456,13 +1456,13 @@ class Bind2Factory : public BackendFactory declare(suffix,"hybrid","Store DNSSEC metadata in other backend","no"); } - DNSBackend *make(const string &suffix="") + DNSBackend *make(const string &suffix="") override { assertEmptySuffix(suffix); return new Bind2Backend(suffix); } - DNSBackend *makeMetadataOnly(const string &suffix="") + DNSBackend *makeMetadataOnly(const string &suffix="") override { assertEmptySuffix(suffix); return new Bind2Backend(suffix, false); diff --git a/modules/geoipbackend/geoipbackend.cc b/modules/geoipbackend/geoipbackend.cc index 612c9a5298..7b66211810 100644 --- a/modules/geoipbackend/geoipbackend.cc +++ b/modules/geoipbackend/geoipbackend.cc @@ -929,13 +929,13 @@ class GeoIPFactory : public BackendFactory{ public: GeoIPFactory() : BackendFactory("geoip") {} - void declareArguments(const string &suffix = "") { + void declareArguments(const string &suffix = "") override { declare(suffix, "zones-file", "YAML file to load zone(s) configuration", ""); declare(suffix, "database-files", "File(s) to load geoip data from ([driver:]path[;opt=value]", ""); declare(suffix, "dnssec-keydir", "Directory to hold dnssec keys (also turns DNSSEC on)", ""); } - DNSBackend *make(const string &suffix) { + DNSBackend *make(const string &suffix) override { return new GeoIPBackend(suffix); } }; diff --git a/modules/gmysqlbackend/gmysqlbackend.cc b/modules/gmysqlbackend/gmysqlbackend.cc index ef80c9d88a..5a3e93a34f 100644 --- a/modules/gmysqlbackend/gmysqlbackend.cc +++ b/modules/gmysqlbackend/gmysqlbackend.cc @@ -69,7 +69,7 @@ class gMySQLFactory : public BackendFactory public: gMySQLFactory(const string &mode) : BackendFactory(mode),d_mode(mode) {} - void declareArguments(const string &suffix="") + void declareArguments(const string &suffix="") override { declare(suffix,"dbname","Database name to connect to","powerdns"); declare(suffix,"user","Database backend user to connect as","powerdns"); @@ -159,7 +159,7 @@ public: declare(suffix, "search-comments-query", "", "SELECT domain_id,name,type,modified_at,account,comment FROM comments WHERE name LIKE ? OR comment LIKE ? LIMIT ?"); } - DNSBackend *make(const string &suffix="") + DNSBackend *make(const string &suffix="") override { return new gMySQLBackend(d_mode,suffix); } diff --git a/modules/godbcbackend/godbcbackend.cc b/modules/godbcbackend/godbcbackend.cc index 02139c24c4..e9ada2e91d 100644 --- a/modules/godbcbackend/godbcbackend.cc +++ b/modules/godbcbackend/godbcbackend.cc @@ -61,7 +61,7 @@ public: } //! Declares all needed arguments. - void declareArguments( const std::string & suffix = "" ) + void declareArguments( const std::string & suffix = "" ) override { declare( suffix, "datasource", "Datasource (DSN) to use","PowerDNS"); declare( suffix, "username", "User to connect as","powerdns"); @@ -146,7 +146,7 @@ public: } //! Constructs a new gODBCBackend object. - DNSBackend *make(const string & suffix = "" ) + DNSBackend *make(const string & suffix = "" ) override { return new gODBCBackend( d_mode, suffix ); } diff --git a/modules/gpgsqlbackend/gpgsqlbackend.cc b/modules/gpgsqlbackend/gpgsqlbackend.cc index 53833ee65a..699d8ebfce 100644 --- a/modules/gpgsqlbackend/gpgsqlbackend.cc +++ b/modules/gpgsqlbackend/gpgsqlbackend.cc @@ -80,7 +80,7 @@ class gPgSQLFactory : public BackendFactory public: gPgSQLFactory(const string &mode) : BackendFactory(mode),d_mode(mode) {} - void declareArguments(const string &suffix="") + void declareArguments(const string &suffix="") override { declare(suffix,"dbname","Backend database name to connect to",""); declare(suffix,"user","Database backend user to connect as",""); @@ -167,7 +167,7 @@ public: } - DNSBackend *make(const string &suffix="") + DNSBackend *make(const string &suffix="") override { return new gPgSQLBackend(d_mode,suffix); } diff --git a/modules/gsqlite3backend/gsqlite3backend.cc b/modules/gsqlite3backend/gsqlite3backend.cc index d697b83bd5..37dee92289 100644 --- a/modules/gsqlite3backend/gsqlite3backend.cc +++ b/modules/gsqlite3backend/gsqlite3backend.cc @@ -72,7 +72,7 @@ public: } //! Declares all needed arguments. - void declareArguments( const std::string & suffix = "" ) + void declareArguments( const std::string & suffix = "" ) override { declare(suffix, "database", "Filename of the SQLite3 database", "powerdns.sqlite"); declare(suffix, "pragma-synchronous", "Set this to 0 for blazing speed", ""); @@ -156,7 +156,7 @@ public: } //! Constructs a new gSQLite3Backend object. - DNSBackend *make( const string & suffix = "" ) + DNSBackend *make( const string & suffix = "" ) override { return new gSQLite3Backend( d_mode, suffix ); } diff --git a/modules/ldapbackend/ldapbackend.cc b/modules/ldapbackend/ldapbackend.cc index 7078bf5513..f428a1adf0 100644 --- a/modules/ldapbackend/ldapbackend.cc +++ b/modules/ldapbackend/ldapbackend.cc @@ -277,7 +277,7 @@ class LdapFactory : public BackendFactory LdapFactory() : BackendFactory( "ldap" ) {} - void declareArguments( const string &suffix="" ) + void declareArguments( const string &suffix="" ) override { declare( suffix, "host", "One or more LDAP server with ports or LDAP URIs (separated by spaces)","ldap://127.0.0.1:389/" ); declare( suffix, "starttls", "Use TLS to encrypt connection (unused for LDAP URIs)", "no" ); @@ -297,7 +297,7 @@ class LdapFactory : public BackendFactory } - DNSBackend* make( const string &suffix="" ) + DNSBackend* make( const string &suffix="" ) override { return new LdapBackend( suffix ); } diff --git a/modules/lmdbbackend/lmdbbackend.cc b/modules/lmdbbackend/lmdbbackend.cc index b768905076..95985034a4 100644 --- a/modules/lmdbbackend/lmdbbackend.cc +++ b/modules/lmdbbackend/lmdbbackend.cc @@ -1558,7 +1558,7 @@ class LMDBFactory : public BackendFactory { public: LMDBFactory() : BackendFactory("lmdb") {} - void declareArguments(const string &suffix="") + void declareArguments(const string &suffix="") override { declare(suffix,"filename","Filename for lmdb","./pdns.lmdb"); declare(suffix,"sync-mode","Synchronisation mode: nosync, nometasync, mapasync, sync","mapasync"); @@ -1566,7 +1566,7 @@ public: declare(suffix,"shards","Records database will be split into this number of shards", (sizeof(long) == 4) ? "2" : "64"); declare(suffix,"schema-version","Maximum allowed schema version to run on this DB. If a lower version is found, auto update is performed", SCHEMAVERSION_TEXT); } - DNSBackend *make(const string &suffix="") + DNSBackend *make(const string &suffix="") override { return new LMDBBackend(suffix); } diff --git a/modules/lua2backend/lua2backend.cc b/modules/lua2backend/lua2backend.cc index 378203eccd..b3ce01e0d5 100644 --- a/modules/lua2backend/lua2backend.cc +++ b/modules/lua2backend/lua2backend.cc @@ -31,14 +31,14 @@ class Lua2Factory : public BackendFactory public: Lua2Factory() : BackendFactory("lua2") {} - void declareArguments(const string &suffix="") + void declareArguments(const string &suffix="") override { declare(suffix,"filename","Filename of the script for lua backend","powerdns-luabackend.lua"); declare(suffix,"query-logging","Logging of the Lua2 Backend","no"); declare(suffix,"api","Lua backend API version","2"); } - DNSBackend *make(const string &suffix="") + DNSBackend *make(const string &suffix="") override { const std::string apiSet = "lua2" + suffix + "-api"; const int api = ::arg().asNum(apiSet); diff --git a/modules/pipebackend/pipebackend.cc b/modules/pipebackend/pipebackend.cc index 103d39e1f1..208ef2e5d4 100644 --- a/modules/pipebackend/pipebackend.cc +++ b/modules/pipebackend/pipebackend.cc @@ -357,7 +357,7 @@ class PipeFactory : public BackendFactory public: PipeFactory() : BackendFactory("pipe") {} - void declareArguments(const string &suffix="") + void declareArguments(const string &suffix="") override { declare(suffix,"command","Command to execute for piping questions to",""); declare(suffix,"timeout","Number of milliseconds to wait for an answer","2000"); @@ -365,7 +365,7 @@ class PipeFactory : public BackendFactory declare(suffix,"abi-version","Version of the pipe backend ABI","1"); } - DNSBackend *make(const string &suffix="") + DNSBackend *make(const string &suffix="") override { return new PipeBackend(suffix); } diff --git a/modules/randombackend/randombackend.cc b/modules/randombackend/randombackend.cc index 577ba63904..20b179b8d3 100644 --- a/modules/randombackend/randombackend.cc +++ b/modules/randombackend/randombackend.cc @@ -102,11 +102,11 @@ class RandomFactory : public BackendFactory { public: RandomFactory() : BackendFactory("random") {} - void declareArguments(const string &suffix="") + void declareArguments(const string &suffix="") override { declare(suffix,"hostname","Hostname which is to be random","random.example.com"); } - DNSBackend *make(const string &suffix="") + DNSBackend *make(const string &suffix="") override { return new RandomBackend(suffix); } diff --git a/modules/remotebackend/remotebackend.cc b/modules/remotebackend/remotebackend.cc index 77e74ab893..220b518f0e 100644 --- a/modules/remotebackend/remotebackend.cc +++ b/modules/remotebackend/remotebackend.cc @@ -1036,13 +1036,13 @@ class RemoteBackendFactory : public BackendFactory public: RemoteBackendFactory() : BackendFactory("remote") {} - void declareArguments(const std::string &suffix="") + void declareArguments(const std::string &suffix="") override { declare(suffix,"dnssec","Enable dnssec support","no"); declare(suffix,"connection-string","Connection string",""); } - DNSBackend *make(const std::string &suffix="") + DNSBackend *make(const std::string &suffix="") override { return new RemoteBackend(suffix); } diff --git a/modules/tinydnsbackend/tinydnsbackend.cc b/modules/tinydnsbackend/tinydnsbackend.cc index 3f77b0e2f8..1de99d5e02 100644 --- a/modules/tinydnsbackend/tinydnsbackend.cc +++ b/modules/tinydnsbackend/tinydnsbackend.cc @@ -348,7 +348,7 @@ class TinyDNSFactory: public BackendFactory public: TinyDNSFactory() : BackendFactory("tinydns") {} - void declareArguments(const string &suffix="") { + void declareArguments(const string &suffix="") override { declare(suffix, "notify-on-startup", "Tell the TinyDNSBackend to notify all the slave nameservers on startup. Default is no.", "no"); declare(suffix, "dbfile", "Location of the cdb data file", "data.cdb"); declare(suffix, "tai-adjust", "This adjusts the TAI value if timestamps are used. These seconds will be added to the start point (1970) and will allow you to adjust for leap seconds. The default is 11.", "11"); @@ -356,7 +356,7 @@ public: declare(suffix, "ignore-bogus-records", "The data.cdb file might have some incorrect record data, this causes PowerDNS to fail, where tinydns would send out truncated data. This option makes powerdns ignore that data!", "no"); } - DNSBackend *make(const string &suffix="") { + DNSBackend *make(const string &suffix="") override { return new TinyDNSBackend(suffix); } }; diff --git a/pdns/Makefile.am b/pdns/Makefile.am index bde0e29c5d..ff2dc1058f 100644 --- a/pdns/Makefile.am +++ b/pdns/Makefile.am @@ -1343,11 +1343,12 @@ testrunner_SOURCES = \ test-sha_hh.cc \ test-statbag_cc.cc \ test-tsig.cc \ + test-ueberbackend_cc.cc \ test-zoneparser_tng_cc.cc \ testrunner.cc \ threadname.hh threadname.cc \ tsigverifier.cc tsigverifier.hh \ - ueberbackend.cc \ + ueberbackend.cc ueberbackend.hh \ unix_utility.cc \ zoneparser-tng.cc zoneparser-tng.hh diff --git a/pdns/dnsbackend.cc b/pdns/dnsbackend.cc index 5869df3be1..5c8d295029 100644 --- a/pdns/dnsbackend.cc +++ b/pdns/dnsbackend.cc @@ -85,6 +85,15 @@ void BackendMakerClass::report(BackendFactory *bf) d_repository[bf->getName()]=bf; } +void BackendMakerClass::clear() +{ + d_instances.clear(); + for (auto& repo : d_repository) { + delete repo.second; + repo.second = nullptr; + } + d_repository.clear(); +} vector BackendMakerClass::getModules() { @@ -164,41 +173,50 @@ void BackendMakerClass::launch(const string &instr) } } -int BackendMakerClass::numLauncheable() +size_t BackendMakerClass::numLauncheable() const { return d_instances.size(); } -vectorBackendMakerClass::all(bool metadataOnly) +vector BackendMakerClass::all(bool metadataOnly) { - vectorret; + vector ret; if(d_instances.empty()) throw PDNSException("No database backends configured for launch, unable to function"); + ret.reserve(d_instances.size()); + try { - for(vector >::const_iterator i=d_instances.begin();i!=d_instances.end();++i) { - DNSBackend *made; - if(metadataOnly) - made = d_repository[i->first]->makeMetadataOnly(i->second); - else - made = d_repository[i->first]->make(i->second); - if(!made) - throw PDNSException("Unable to launch backend '"+i->first+"'"); + for (const auto& instance : d_instances) { + DNSBackend *made = nullptr; + + if (metadataOnly) { + made = d_repository[instance.first]->makeMetadataOnly(instance.second); + } + else { + made = d_repository[instance.first]->make(instance.second); + } + + if (!made) { + throw PDNSException("Unable to launch backend '" + instance.first + "'"); + } ret.push_back(made); } } - catch(PDNSException &ae) { + catch(const PDNSException &ae) { g_log<::const_iterator i=ret.begin();i!=ret.end();++i) - delete *i; + for (auto i : ret) { + delete i; + } throw; } catch(...) { // and cleanup g_log<::const_iterator i=ret.begin();i!=ret.end();++i) - delete *i; + for (auto i : ret) { + delete i; + } throw; } diff --git a/pdns/dnsbackend.hh b/pdns/dnsbackend.hh index b599dca391..479aa5cba4 100644 --- a/pdns/dnsbackend.hh +++ b/pdns/dnsbackend.hh @@ -412,10 +412,11 @@ class BackendMakerClass public: void report(BackendFactory *bf); void launch(const string &instr); - vectorall(bool skipBIND=false); + vector all(bool skipBIND=false); void load(const string &module); - int numLauncheable(); + size_t numLauncheable() const; vector getModules(); + void clear(); private: void load_all(); diff --git a/pdns/pdnsutil.cc b/pdns/pdnsutil.cc index ce1fc75625..6f36fc8522 100644 --- a/pdns/pdnsutil.cc +++ b/pdns/pdnsutil.cc @@ -2,6 +2,9 @@ #ifdef HAVE_CONFIG_H #include "config.h" #endif + +#include + #include "dnsseckeeper.hh" #include "dnssecinfra.hh" #include "statbag.hh" diff --git a/pdns/test-ueberbackend_cc.cc b/pdns/test-ueberbackend_cc.cc new file mode 100644 index 0000000000..2ca4a5613e --- /dev/null +++ b/pdns/test-ueberbackend_cc.cc @@ -0,0 +1,1126 @@ +#define BOOST_TEST_DYN_LINK +#define BOOST_TEST_NO_MAIN + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include + +#include +#include +#include +#include +#include + +#include "arguments.hh" +#include "auth-querycache.hh" +#include "ueberbackend.hh" + +class SimpleBackend : public DNSBackend +{ +public: + struct SimpleDNSRecord + { + SimpleDNSRecord(const DNSName& name, uint16_t type, const std::string& content, uint32_t ttl): d_content(content), d_name(name), d_ttl(ttl), d_type(type) + { + } + + std::string d_content; + DNSName d_name; + uint32_t d_ttl; + uint16_t d_type; + }; + + struct OrderedNameTypeTag; + + typedef multi_index_container< + SimpleDNSRecord, + indexed_by < + ordered_non_unique, + composite_key< + SimpleDNSRecord, + member, + member + >, + composite_key_compare > + > + > + > RecordStorage; + + struct SimpleDNSZone + { + SimpleDNSZone(const DNSName& name, uint64_t id): d_records(std::make_shared()), d_name(name), d_id(id) + { + } + std::shared_ptr d_records; + DNSName d_name; + uint64_t d_id; + }; + + struct HashedNameTag {}; + struct IDTag {}; + + typedef multi_index_container< + SimpleDNSZone, + indexed_by < + ordered_unique, member >, + hashed_unique, member > + > + > ZoneStorage; + + struct SimpleMetaData + { + SimpleMetaData(const DNSName& name, const std::string& kind, const std::vector& values): d_name(name), d_kind(kind), d_values(values) + { + } + + DNSName d_name; + std::string d_kind; + std::vector d_values; + }; + + struct OrderedNameKindTag {}; + + typedef multi_index_container< + SimpleMetaData, + indexed_by < + ordered_unique, + composite_key< + SimpleMetaData, + member, + member + >, + composite_key_compare > + > + > + > MetaDataStorage; + + // Initialize our backend ID from the suffix, skipping the '-' that DNSBackend adds there + SimpleBackend(const std::string& suffix): d_suffix(suffix), d_backendId(pdns_stou(suffix.substr(1))) + { + } + + bool findZone(const DNSName& qdomain, int zoneId, std::shared_ptr& records, uint64_t& currentZoneId) const + { + currentZoneId = -1; + records.reset(); + + if (zoneId != -1) { + const auto& idx = boost::multi_index::get(s_zones.at(d_backendId)); + auto it = idx.find(zoneId); + if (it == idx.end()) { + return false; + } + records = it->d_records; + currentZoneId = it->d_id; + } + else { + const auto& idx = boost::multi_index::get(s_zones.at(d_backendId)); + auto it = idx.find(qdomain); + if (it == idx.end()) { + return false; + } + records = it->d_records; + currentZoneId = it->d_id; + } + + return true; + } + + void lookup(const QType& qtype, const DNSName& qdomain, int zoneId = -1, DNSPacket *pkt_p = nullptr) override + { + d_currentScopeMask = 0; + findZone(qdomain, zoneId, d_records, d_currentZone); + + if (d_records) { + if (qdomain == DNSName("geo.powerdns.com.") && pkt_p != nullptr && pkt_p->getRealRemote() == Netmask("192.0.2.1")) { + d_currentScopeMask = 32; + } + + auto& idx = d_records->get(); + if (qtype == QType::ANY) { + auto range = idx.equal_range(qdomain); + d_iter = range.first; + d_end = range.second; + } + else { + auto range = idx.equal_range(boost::make_tuple(qdomain, qtype.getCode())); + d_iter = range.first; + d_end = range.second; + } + } + } + + bool get(DNSResourceRecord& drr) override + { + if (!d_records) { + return false; + } + + if (d_iter == d_end) { + return false; + } + + drr.qname = d_iter->d_name; + drr.domain_id = d_currentZone; + drr.content = d_iter->d_content; + drr.qtype = d_iter->d_type; + drr.ttl = d_iter->d_ttl; + + // drr.auth = d_iter->auth; might bring pain at some point, let's not cross that bridge until then + drr.auth = true; + drr.scopeMask = d_currentScopeMask; + + ++d_iter; + return true; + } + + bool list(const DNSName& target, int zoneId, bool include_disabled = false) override + { + findZone(target, zoneId, d_records, d_currentZone); + + if (d_records) { + d_iter = d_records->begin(); + d_end = d_records->end(); + return true; + } + + return false; + } + + bool getDomainMetadata(const DNSName& name, const std::string& kind, std::vector& meta) override + { + const auto& idx = boost::multi_index::get(s_metadata.at(d_backendId)); + auto it = idx.find(boost::make_tuple(name, kind)); + if (it == idx.end()) { + /* funnily enough, we are expected to return true even though we might not know that zone */ + return true; + } + + meta = it->d_values; + return true; + } + + bool setDomainMetadata(const DNSName& name, const std::string& kind, const std::vector& meta) override + { + auto& idx = boost::multi_index::get(s_metadata.at(d_backendId)); + auto it = idx.find(boost::make_tuple(name, kind)); + if (it == idx.end()) { + s_metadata.at(d_backendId).insert(SimpleMetaData(name, kind, meta)); + return true; + } + idx.replace(it, SimpleMetaData(name, kind, meta)); + return true; + } + + /* this is not thread-safe */ + static std::unordered_map s_zones; + static std::unordered_map s_metadata; + +protected: + std::string d_suffix; + std::shared_ptr d_records{nullptr}; + RecordStorage::index::type::const_iterator d_iter; + RecordStorage::index::type::const_iterator d_end; + const uint64_t d_backendId; + uint64_t d_currentZone{0}; + uint8_t d_currentScopeMask{0}; +}; + +class SimpleBackendBestAuth : public SimpleBackend +{ +public: + SimpleBackendBestAuth(const std::string& suffix): SimpleBackend(suffix) + { + } + + bool getAuth(const DNSName& target, SOAData* sd) override + { + static const DNSName best("d.0.1.0.0.2.ip6.arpa."); + + ++d_authLookupCount; + + if (target.isPartOf(best)) { + /* return the best SOA right away */ + std::shared_ptr records; + uint64_t zoneId; + if (!findZone(best, -1, records, zoneId)) { + return false; + } + + auto& idx = records->get(); + auto range = idx.equal_range(boost::make_tuple(best, QType::SOA)); + if (range.first == range.second) { + return false; + } + + fillSOAData(range.first->d_content, *sd); + sd->ttl = range.first->d_ttl; + sd->qname = best; + sd->domain_id = zoneId; + return true; + } + + return getSOA(target, *sd); + } + + size_t d_authLookupCount{0}; +}; + +class SimpleBackendNoMeta : public SimpleBackend +{ +public: + SimpleBackendNoMeta(const std::string& suffix): SimpleBackend(suffix) + { + } + + bool getDomainMetadata(const DNSName& name, const std::string& kind, std::vector& meta) override + { + return false; + } + + bool setDomainMetadata(const DNSName& name, const std::string& kind, const std::vector& meta) override + { + return false; + } +}; + +std::unordered_map SimpleBackend::s_zones; +std::unordered_map SimpleBackend::s_metadata; + +class SimpleBackendFactory : public BackendFactory +{ +public: + SimpleBackendFactory(): BackendFactory("SimpleBackend") + { + } + + DNSBackend *make(const string& suffix="") override + { + return new SimpleBackend(suffix); + } +}; + +class SimpleBackendBestAuthFactory : public BackendFactory +{ +public: + SimpleBackendBestAuthFactory(): BackendFactory("SimpleBackendBestAuth") + { + } + + DNSBackend *make(const string& suffix="") override + { + return new SimpleBackendBestAuth(suffix); + } +}; + +class SimpleBackendNoMetaFactory : public BackendFactory +{ +public: + SimpleBackendNoMetaFactory(): BackendFactory("SimpleBackendNoMeta") + { + } + + DNSBackend *make(const string& suffix="") override + { + return new SimpleBackendNoMeta(suffix); + } +}; + +struct UeberBackendSetupArgFixture { + UeberBackendSetupArgFixture() { + extern AuthQueryCache QC; + ::arg().set("query-cache-ttl")="0"; + ::arg().set("negquery-cache-ttl")="0"; + QC.cleanup(); + BackendMakers().clear(); + SimpleBackend::s_zones.clear(); + SimpleBackend::s_metadata.clear(); + }; +}; + +static void testWithoutThenWithCache(std::function func) +{ + extern AuthQueryCache QC; + + { + /* disable the cache */ + ::arg().set("query-cache-ttl")="0"; + ::arg().set("negquery-cache-ttl")="0"; + QC.cleanup(); + + UeberBackend ub; + func(ub); + } + + { + /* enable the cache */ + ::arg().set("query-cache-ttl")="20"; + ::arg().set("negquery-cache-ttl")="60"; + QC.cleanup(); + + UeberBackend ub; + /* a first time to fill the cache */ + func(ub); + /* a second time to make sure every call has been tried with the cache filled */ + func(ub); + } +} + +BOOST_FIXTURE_TEST_SUITE(test_ueberbackend_cc, UeberBackendSetupArgFixture) + +static std::vector getRecords(UeberBackend& ub, const DNSName& name, uint16_t qtype, int zoneId, const DNSPacket* pkt) +{ + std::vector result; + + ub.lookup(QType(qtype), name, zoneId, const_cast(pkt)); + + DNSZoneRecord dzr; + while (ub.get(dzr)) + { + result.push_back(std::move(dzr)); + } + + return result; +} + +static void checkRecordExists(const std::vector& records, const DNSName& name, uint16_t type, int zoneId, uint8_t scopeMask, bool auth) +{ + BOOST_REQUIRE_GE(records.size(), 1); + for (const auto& record : records) { + if (record.domain_id == zoneId && + record.dr.d_type == type && + record.dr.d_name == name && + record.auth == auth && + record.scopeMask == scopeMask) { + return; + } + } + BOOST_CHECK_MESSAGE(false, "Record " + name.toString() + "/" + QType(type).getName() + " - " + std::to_string(zoneId) + " not found"); +} + +BOOST_AUTO_TEST_CASE(test_simple) { + + try { + SimpleBackend::SimpleDNSZone zoneA(DNSName("powerdns.com."), 1); + zoneA.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("powerdns.com."), QType::SOA, "ns1.powerdns.com. powerdns.com. 3 600 600 3600000 604800", 3600)); + zoneA.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("powerdns.com."), QType::AAAA, "2001:db8::1", 60)); + zoneA.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("www.powerdns.com."), QType::A, "192.168.0.1", 60)); + zoneA.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("geo.powerdns.com."), QType::A, "192.168.0.42", 60)); + SimpleBackend::s_zones[1].insert(zoneA); + + BackendMakers().report(new SimpleBackendFactory()); + BackendMakers().launch("SimpleBackend:1"); + UeberBackend::go(); + + auto testFunction = [](UeberBackend& ub) -> void { + { + // test SOA with unknown zone id == -1 + auto records = getRecords(ub, DNSName("powerdns.com."), QType::SOA, -1, nullptr); + BOOST_REQUIRE_EQUAL(records.size(), 1); + checkRecordExists(records, DNSName("powerdns.com."), QType::SOA, 1, 0, true); + } + + { + // test ANY with zone id == -1 + auto records = getRecords(ub, DNSName("powerdns.com."), QType::ANY, -1, nullptr); + BOOST_REQUIRE_EQUAL(records.size(), 2); + checkRecordExists(records, DNSName("powerdns.com."), QType::SOA, 1, 0, true); + checkRecordExists(records, DNSName("powerdns.com."), QType::AAAA, 1, 0, true); + } + + { + // test AAAA with zone id == -1 + auto records = getRecords(ub, DNSName("powerdns.com."), QType::AAAA, -1, nullptr); + BOOST_REQUIRE_EQUAL(records.size(), 1); + checkRecordExists(records, DNSName("powerdns.com."), QType::AAAA, 1, 0, true); + } + + { + // test NODATA with zone id == -1 + auto records = getRecords(ub, DNSName("powerdns.com."), QType::PTR, -1, nullptr); + BOOST_REQUIRE_EQUAL(records.size(), 0); + } + + { + // test ANY with zone id set + auto records = getRecords(ub, DNSName("powerdns.com."), QType::ANY, 1, nullptr); + BOOST_REQUIRE_EQUAL(records.size(), 2); + checkRecordExists(records, DNSName("powerdns.com."), QType::SOA, 1, 0, true); + checkRecordExists(records, DNSName("powerdns.com."), QType::AAAA, 1, 0, true); + } + + { + // test AAAA with zone id set + auto records = getRecords(ub, DNSName("powerdns.com."), QType::AAAA, 1, nullptr); + BOOST_REQUIRE_EQUAL(records.size(), 1); + checkRecordExists(records, DNSName("powerdns.com."), QType::AAAA, 1, 0, true); + } + + { + // test NODATA with zone id set + auto records = getRecords(ub, DNSName("powerdns.com."), QType::PTR, 1, nullptr); + BOOST_REQUIRE_EQUAL(records.size(), 0); + } + + { + // test ANY with wrong zone id + auto records = getRecords(ub, DNSName("powerdns.com."), QType::ANY, 65535, nullptr); + BOOST_REQUIRE_EQUAL(records.size(), 0); + } + + { + // test a DNS packet is correctly passed and that the corresponding scope is passed back + DNSPacket pkt(true); + ComboAddress remote("192.0.2.1"); + pkt.setRemote(&remote); + auto records = getRecords(ub, DNSName("geo.powerdns.com."), QType::ANY, 1, &pkt); + BOOST_REQUIRE_EQUAL(records.size(), 1); + checkRecordExists(records, DNSName("geo.powerdns.com."), QType::A, 1, 32, true); + // and that we don't get the same result for a different client + remote = ComboAddress("192.0.2.2"); + pkt.setRemote(&remote); + records = getRecords(ub, DNSName("geo.powerdns.com."), QType::ANY, 1, &pkt); + BOOST_REQUIRE_EQUAL(records.size(), 1); + checkRecordExists(records, DNSName("geo.powerdns.com."), QType::A, 1, 0, true); + } + + }; + testWithoutThenWithCache(testFunction); + } + catch(const PDNSException& e) { + cerr<insert(SimpleBackend::SimpleDNSRecord(DNSName("powerdns.com."), QType::SOA, "ns1.powerdns.com. powerdns.com. 3 600 600 3600000 604800", 3600)); + zoneA.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("powerdns.com."), QType::AAAA, "2001:db8::1", 60)); + zoneA.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("www.powerdns.com."), QType::A, "192.168.0.1", 60)); + zoneA.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("geo.powerdns.com."), QType::A, "192.168.0.42", 60)); + SimpleBackend::s_zones[1].insert(zoneA); + + SimpleBackend::SimpleDNSZone zoneB(DNSName("powerdns.org."), 2); + zoneB.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("powerdns.org."), QType::SOA, "ns1.powerdns.org. powerdns.org. 3 600 600 3600000 604800", 3600)); + zoneB.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("powerdns.org."), QType::AAAA, "2001:db8::2", 60)); + zoneB.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("www.powerdns.org."), QType::AAAA, "2001:db8::2", 60)); + zoneB.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("geo.powerdns.org."), QType::AAAA, "2001:db8::42", 60)); + SimpleBackend::s_zones[2].insert(zoneB); + + BackendMakers().report(new SimpleBackendFactory()); + BackendMakers().launch("SimpleBackend:1, SimpleBackend:2"); + UeberBackend::go(); + + auto testFunction = [](UeberBackend& ub) -> void { + { + // test SOA with unknown zone id == -1 + auto records = getRecords(ub, DNSName("powerdns.com."), QType::SOA, -1, nullptr); + BOOST_REQUIRE_EQUAL(records.size(), 1); + checkRecordExists(records, DNSName("powerdns.com."), QType::SOA, 1, 0, true); + + records = getRecords(ub, DNSName("powerdns.org."), QType::SOA, -1, nullptr); + BOOST_REQUIRE_EQUAL(records.size(), 1); + checkRecordExists(records, DNSName("powerdns.org."), QType::SOA, 2, 0, true); + } + + { + // test ANY with zone id == -1 + auto records = getRecords(ub, DNSName("powerdns.com."), QType::ANY, -1, nullptr); + BOOST_REQUIRE_EQUAL(records.size(), 2); + checkRecordExists(records, DNSName("powerdns.com."), QType::SOA, 1, 0, true); + checkRecordExists(records, DNSName("powerdns.com."), QType::AAAA, 1, 0, true); + + records = getRecords(ub, DNSName("powerdns.org."), QType::ANY, -1, nullptr); + BOOST_REQUIRE_EQUAL(records.size(), 2); + checkRecordExists(records, DNSName("powerdns.org."), QType::SOA, 2, 0, true); + checkRecordExists(records, DNSName("powerdns.org."), QType::AAAA, 2, 0, true); + } + + { + // test AAAA with zone id == -1 + auto records = getRecords(ub, DNSName("powerdns.com."), QType::AAAA, -1, nullptr); + BOOST_REQUIRE_EQUAL(records.size(), 1); + checkRecordExists(records, DNSName("powerdns.com."), QType::AAAA, 1, 0, true); + + records = getRecords(ub, DNSName("powerdns.org."), QType::AAAA, -1, nullptr); + BOOST_REQUIRE_EQUAL(records.size(), 1); + checkRecordExists(records, DNSName("powerdns.org."), QType::AAAA, 2, 0, true); + } + + { + // test NODATA with zone id == -1 + auto records = getRecords(ub, DNSName("powerdns.com."), QType::PTR, -1, nullptr); + BOOST_REQUIRE_EQUAL(records.size(), 0); + + records = getRecords(ub, DNSName("powerdns.org."), QType::PTR, -1, nullptr); + BOOST_REQUIRE_EQUAL(records.size(), 0); + } + + { + // test ANY with zone id set + auto records = getRecords(ub, DNSName("powerdns.com."), QType::ANY, 1, nullptr); + BOOST_REQUIRE_EQUAL(records.size(), 2); + checkRecordExists(records, DNSName("powerdns.com."), QType::SOA, 1, 0, true); + checkRecordExists(records, DNSName("powerdns.com."), QType::AAAA, 1, 0, true); + + records = getRecords(ub, DNSName("powerdns.org."), QType::ANY, 2, nullptr); + BOOST_REQUIRE_EQUAL(records.size(), 2); + checkRecordExists(records, DNSName("powerdns.org."), QType::SOA, 2, 0, true); + checkRecordExists(records, DNSName("powerdns.org."), QType::AAAA, 2, 0, true); + } + + { + // test AAAA with zone id set + auto records = getRecords(ub, DNSName("powerdns.com."), QType::AAAA, 1, nullptr); + BOOST_REQUIRE_EQUAL(records.size(), 1); + checkRecordExists(records, DNSName("powerdns.com."), QType::AAAA, 1, 0, true); + + records = getRecords(ub, DNSName("www.powerdns.org."), QType::AAAA, 2, nullptr); + BOOST_REQUIRE_EQUAL(records.size(), 1); + checkRecordExists(records, DNSName("www.powerdns.org."), QType::AAAA, 2, 0, true); + } + + { + // test NODATA with zone id set + auto records = getRecords(ub, DNSName("powerdns.com."), QType::PTR, 1, nullptr); + BOOST_REQUIRE_EQUAL(records.size(), 0); + + records = getRecords(ub, DNSName("powerdns.org."), QType::PTR, 2, nullptr); + BOOST_REQUIRE_EQUAL(records.size(), 0); + } + + { + // test ANY with wrong zone id + auto records = getRecords(ub, DNSName("powerdns.com."), QType::ANY, 2, nullptr); + BOOST_REQUIRE_EQUAL(records.size(), 0); + + records = getRecords(ub, DNSName("powerdns.org."), QType::ANY, 1, nullptr); + BOOST_REQUIRE_EQUAL(records.size(), 0); + + records = getRecords(ub, DNSName("not-powerdns.com."), QType::ANY, 65535, nullptr); + BOOST_REQUIRE_EQUAL(records.size(), 0); + } + + { + // test a DNS packet is correctly passed and that the corresponding scope is passed back + DNSPacket pkt(true); + ComboAddress remote("192.0.2.1"); + pkt.setRemote(&remote); + auto records = getRecords(ub, DNSName("geo.powerdns.com."), QType::ANY, 1, &pkt); + BOOST_REQUIRE_EQUAL(records.size(), 1); + checkRecordExists(records, DNSName("geo.powerdns.com."), QType::A, 1, 32, true); + // and that we don't get the same result for a different client + remote = ComboAddress("192.0.2.2"); + pkt.setRemote(&remote); + records = getRecords(ub, DNSName("geo.powerdns.com."), QType::ANY, 1, &pkt); + BOOST_REQUIRE_EQUAL(records.size(), 1); + checkRecordExists(records, DNSName("geo.powerdns.com."), QType::A, 1, 0, true); + } + + }; + testWithoutThenWithCache(testFunction); + } + catch(const PDNSException& e) { + cerr<insert(SimpleBackend::SimpleDNSRecord(DNSName("powerdns.com."), QType::SOA, "ns1.powerdns.com. powerdns.com. 3 600 600 3600000 604800", 3600)); + zoneA.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("powerdns.com."), QType::NS, "ns1.powerdns.com.", 3600)); + zoneA.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("powerdns.com."), QType::A, "192.168.0.1", 60)); + SimpleBackend::s_zones[1].insert(zoneA); + + SimpleBackend::SimpleDNSZone zoneB(DNSName("powerdns.com."), 1); + zoneB.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("powerdns.com."), QType::A, "192.168.0.2", 60)); + zoneB.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("powerdns.com."), QType::AAAA, "2001:db8::1", 60)); + zoneB.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("www.powerdns.com."), QType::A, "192.168.0.1", 60)); + zoneB.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("geo.powerdns.com."), QType::A, "192.168.0.42", 60)); + SimpleBackend::s_zones[2].insert(zoneB); + + BackendMakers().report(new SimpleBackendFactory()); + BackendMakers().launch("SimpleBackend:1, SimpleBackend:2"); + UeberBackend::go(); + + auto testFunction = [](UeberBackend& ub) -> void { + { + // test SOA with unknown zone id == -1 + auto records = getRecords(ub, DNSName("powerdns.com."), QType::SOA, -1, nullptr); + BOOST_REQUIRE_EQUAL(records.size(), 1); + checkRecordExists(records, DNSName("powerdns.com."), QType::SOA, 1, 0, true); + } + + { + // test ANY with zone id == -1 + auto records = getRecords(ub, DNSName("powerdns.com."), QType::ANY, -1, nullptr); + // /!\ only 3 records are returned since we don't allow spreading the same name over several backends + BOOST_REQUIRE_EQUAL(records.size(), 3); + checkRecordExists(records, DNSName("powerdns.com."), QType::SOA, 1, 0, true); + checkRecordExists(records, DNSName("powerdns.com."), QType::NS, 1, 0, true); + checkRecordExists(records, DNSName("powerdns.com."), QType::A, 1, 0, true); + //checkRecordExists(records, DNSName("powerdns.com."), QType::AAAA, 1, 0, true); + } + + { + // test AAAA with zone id == -1 + auto records = getRecords(ub, DNSName("powerdns.com."), QType::AAAA, -1, nullptr); + // /!\ the AAAA will be found on an exact search, but not on an ANY one + BOOST_REQUIRE_EQUAL(records.size(), 1); + checkRecordExists(records, DNSName("powerdns.com."), QType::AAAA, 1, 0, true); + } + + { + // test NODATA with zone id == -1 + auto records = getRecords(ub, DNSName("powerdns.com."), QType::PTR, -1, nullptr); + BOOST_REQUIRE_EQUAL(records.size(), 0); + } + + { + // test ANY with zone id set + auto records = getRecords(ub, DNSName("powerdns.com."), QType::ANY, 1, nullptr); + // /!\ only 3 records are returned since we don't allow spreading the same name over several backends + BOOST_REQUIRE_EQUAL(records.size(), 3); + checkRecordExists(records, DNSName("powerdns.com."), QType::SOA, 1, 0, true); + checkRecordExists(records, DNSName("powerdns.com."), QType::NS, 1, 0, true); + checkRecordExists(records, DNSName("powerdns.com."), QType::A, 1, 0, true); + //checkRecordExists(records, DNSName("powerdns.com."), QType::AAAA, 1, 0, true); + } + + { + // test AAAA with zone id set + auto records = getRecords(ub, DNSName("powerdns.com."), QType::AAAA, 1, nullptr); + // /!\ the AAAA will be found on an exact search, but not on an ANY one + BOOST_REQUIRE_EQUAL(records.size(), 1); + checkRecordExists(records, DNSName("powerdns.com."), QType::AAAA, 1, 0, true); + } + + { + // test www - A with zone id set (only in the second backend) + auto records = getRecords(ub, DNSName("www.powerdns.com."), QType::A, 1, nullptr); + BOOST_REQUIRE_EQUAL(records.size(), 1); + checkRecordExists(records, DNSName("www.powerdns.com."), QType::A, 1, 0, true); + } + + { + // test NODATA with zone id set + auto records = getRecords(ub, DNSName("powerdns.com."), QType::PTR, 1, nullptr); + BOOST_REQUIRE_EQUAL(records.size(), 0); + } + + { + // test ANY with wrong zone id + auto records = getRecords(ub, DNSName("powerdns.com."), QType::ANY, 2, nullptr); + BOOST_REQUIRE_EQUAL(records.size(), 0); + } + + { + // test a DNS packet is correctly passed and that the corresponding scope is passed back + DNSPacket pkt(true); + ComboAddress remote("192.0.2.1"); + pkt.setRemote(&remote); + auto records = getRecords(ub, DNSName("geo.powerdns.com."), QType::ANY, 1, &pkt); + BOOST_REQUIRE_EQUAL(records.size(), 1); + checkRecordExists(records, DNSName("geo.powerdns.com."), QType::A, 1, 32, true); + // and that we don't get the same result for a different client + remote = ComboAddress("192.0.2.2"); + pkt.setRemote(&remote); + records = getRecords(ub, DNSName("geo.powerdns.com."), QType::ANY, 1, &pkt); + BOOST_REQUIRE_EQUAL(records.size(), 1); + checkRecordExists(records, DNSName("geo.powerdns.com."), QType::A, 1, 0, true); + } + + }; + testWithoutThenWithCache(testFunction); + } + catch(const PDNSException& e) { + cerr<insert(SimpleBackend::SimpleDNSRecord(DNSName("powerdns.com."), QType::SOA, "ns1.powerdns.com. powerdns.com. 3 600 600 3600000 604800", 3600)); + zoneA.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("powerdns.com."), QType::NS, "ns1.powerdns.com.", 3600)); + zoneA.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("powerdns.com."), QType::A, "192.168.0.1", 60)); + zoneA.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("powerdns.com."), QType::A, "192.168.0.2", 60)); + zoneA.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("powerdns.com."), QType::AAAA, "2001:db8::1", 60)); + SimpleBackend::s_zones[1].insert(zoneA); + + SimpleBackend::SimpleDNSZone zoneB(DNSName("powerdns.com."), 1); + zoneB.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("www.powerdns.com."), QType::A, "192.168.0.1", 60)); + zoneB.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("www.powerdns.com."), QType::AAAA, "192.168.0.1", 60)); + zoneB.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("geo.powerdns.com."), QType::A, "192.168.0.42", 60)); + SimpleBackend::s_zones[2].insert(zoneB); + + BackendMakers().report(new SimpleBackendFactory()); + BackendMakers().launch("SimpleBackend:1, SimpleBackend:2"); + UeberBackend::go(); + + auto testFunction = [](UeberBackend& ub) -> void { + { + // test SOA with unknown zone id == -1 + auto records = getRecords(ub, DNSName("powerdns.com."), QType::SOA, -1, nullptr); + BOOST_REQUIRE_EQUAL(records.size(), 1); + checkRecordExists(records, DNSName("powerdns.com."), QType::SOA, 1, 0, true); + } + + { + // test ANY with zone id == -1 + auto records = getRecords(ub, DNSName("powerdns.com."), QType::ANY, -1, nullptr); + BOOST_REQUIRE_EQUAL(records.size(), 5); + checkRecordExists(records, DNSName("powerdns.com."), QType::SOA, 1, 0, true); + checkRecordExists(records, DNSName("powerdns.com."), QType::NS, 1, 0, true); + checkRecordExists(records, DNSName("powerdns.com."), QType::A, 1, 0, true); + checkRecordExists(records, DNSName("powerdns.com."), QType::AAAA, 1, 0, true); + } + + { + // test AAAA with zone id == -1 + auto records = getRecords(ub, DNSName("powerdns.com."), QType::AAAA, -1, nullptr); + BOOST_REQUIRE_EQUAL(records.size(), 1); + checkRecordExists(records, DNSName("powerdns.com."), QType::AAAA, 1, 0, true); + } + + { + // test NODATA with zone id == -1 + auto records = getRecords(ub, DNSName("powerdns.com."), QType::PTR, -1, nullptr); + BOOST_REQUIRE_EQUAL(records.size(), 0); + } + + { + // test ANY with zone id set + auto records = getRecords(ub, DNSName("powerdns.com."), QType::ANY, 1, nullptr); + BOOST_REQUIRE_EQUAL(records.size(), 5); + checkRecordExists(records, DNSName("powerdns.com."), QType::SOA, 1, 0, true); + checkRecordExists(records, DNSName("powerdns.com."), QType::NS, 1, 0, true); + checkRecordExists(records, DNSName("powerdns.com."), QType::A, 1, 0, true); + checkRecordExists(records, DNSName("powerdns.com."), QType::AAAA, 1, 0, true); + } + + { + // test AAAA with zone id set + auto records = getRecords(ub, DNSName("powerdns.com."), QType::AAAA, 1, nullptr); + BOOST_REQUIRE_EQUAL(records.size(), 1); + checkRecordExists(records, DNSName("powerdns.com."), QType::AAAA, 1, 0, true); + } + + { + // test www - A with zone id set (only in the second backend) + auto records = getRecords(ub, DNSName("www.powerdns.com."), QType::A, 1, nullptr); + BOOST_REQUIRE_EQUAL(records.size(), 1); + checkRecordExists(records, DNSName("www.powerdns.com."), QType::A, 1, 0, true); + } + + { + // test NODATA with zone id set + auto records = getRecords(ub, DNSName("powerdns.com."), QType::PTR, 1, nullptr); + BOOST_REQUIRE_EQUAL(records.size(), 0); + } + + { + // test ANY with wrong zone id + auto records = getRecords(ub, DNSName("powerdns.com."), QType::ANY, 2, nullptr); + BOOST_REQUIRE_EQUAL(records.size(), 0); + } + + { + // test a DNS packet is correctly passed and that the corresponding scope is passed back + DNSPacket pkt(true); + ComboAddress remote("192.0.2.1"); + pkt.setRemote(&remote); + auto records = getRecords(ub, DNSName("geo.powerdns.com."), QType::ANY, 1, &pkt); + BOOST_REQUIRE_EQUAL(records.size(), 1); + checkRecordExists(records, DNSName("geo.powerdns.com."), QType::A, 1, 32, true); + // and that we don't get the same result for a different client + remote = ComboAddress("192.0.2.2"); + pkt.setRemote(&remote); + records = getRecords(ub, DNSName("geo.powerdns.com."), QType::ANY, 1, &pkt); + BOOST_REQUIRE_EQUAL(records.size(), 1); + checkRecordExists(records, DNSName("geo.powerdns.com."), QType::A, 1, 0, true); + } + + }; + testWithoutThenWithCache(testFunction); + } + catch(const PDNSException& e) { + cerr<insert(SimpleBackend::SimpleDNSRecord(DNSName("com."), QType::SOA, "a.gtld-servers.net. nstld.verisign-grs.com. 3 600 600 3600000 604800", 3600)); + zoneA.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("powerdns.com."), QType::NS, "ns1.powerdns.com.", 3600)); + zoneA.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("powerdns.com."), QType::DS, "44030 8 3 7DD75AE1565051F9563CF8DF976AC99CDCA51E3463019C81BD2BB083 82F3854E", 3600)); + zoneA.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("ns1.powerdns.com."), QType::A, "192.0.2.1", 3600)); + SimpleBackend::s_zones[1].insert(zoneA); + + SimpleBackend::SimpleDNSZone zoneB(DNSName("powerdns.com."), 2); + zoneB.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("powerdns.com."), QType::SOA, "ns1.powerdns.com. powerdns.com. 3 600 600 3600000 604800", 3600)); + zoneB.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("powerdns.com."), QType::AAAA, "2001:db8::2", 60)); + zoneB.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("powerdns.com."), QType::NS, "ns1.powerdns.com.", 3600)); + zoneB.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("ns1.powerdns.com."), QType::A, "192.0.2.1", 3600)); + SimpleBackend::s_zones[2].insert(zoneB); + + BackendMakers().report(new SimpleBackendFactory()); + BackendMakers().launch("SimpleBackend:1, SimpleBackend:2"); + UeberBackend::go(); + + auto testFunction = [](UeberBackend& ub) -> void { + { + // test SOA with unknown zone id == -1 + auto records = getRecords(ub, DNSName("com."), QType::SOA, -1, nullptr); + BOOST_REQUIRE_EQUAL(records.size(), 1); + checkRecordExists(records, DNSName("com."), QType::SOA, 1, 0, true); + + records = getRecords(ub, DNSName("powerdns.com."), QType::SOA, -1, nullptr); + BOOST_REQUIRE_EQUAL(records.size(), 1); + checkRecordExists(records, DNSName("powerdns.com."), QType::SOA, 2, 0, true); + } + + { + // test ANY with zone id == -1 + auto records = getRecords(ub, DNSName("powerdns.com."), QType::ANY, -1, nullptr); + BOOST_REQUIRE_EQUAL(records.size(), 3); + checkRecordExists(records, DNSName("powerdns.com."), QType::SOA, 2, 0, true); + checkRecordExists(records, DNSName("powerdns.com."), QType::NS, 2, 0, true); + checkRecordExists(records, DNSName("powerdns.com."), QType::AAAA, 2, 0, true); + } + + { + // test getAuth() for DS + SOAData sd; + BOOST_REQUIRE(ub.getAuth(DNSName("powerdns.com."), QType::DS, &sd)); + BOOST_CHECK_EQUAL(sd.qname.toString(), "com."); + BOOST_CHECK_EQUAL(sd.domain_id, 1); + } + + { + // test getAuth() for A + SOAData sd; + BOOST_REQUIRE(ub.getAuth(DNSName("powerdns.com."), QType::A, &sd)); + BOOST_CHECK_EQUAL(sd.qname.toString(), "powerdns.com."); + BOOST_CHECK_EQUAL(sd.domain_id, 2); + } + + }; + testWithoutThenWithCache(testFunction); + } + catch(const PDNSException& e) { + cerr<insert(SimpleBackend::SimpleDNSRecord(DNSName("d.0.1.0.0.2.ip6.arpa."), QType::SOA, "ns.apnic.net. read-txt-record-of-zone-first-dns-admin.apnic.net. 3005126844 7200 1800 604800 3600", 3600)); + zoneA.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("2.4.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa."), QType::PTR, "a.reverse.", 3600)); + SimpleBackend::s_zones[1].insert(zoneA); + + SimpleBackend::SimpleDNSZone zoneB(DNSName("0.1.0.0.2.ip6.arpa."), 2); + zoneB.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("0.1.0.0.2.ip6.arpa."), QType::SOA, "ns.apnic.net. read-txt-record-of-zone-first-dns-admin.apnic.net. 3005126844 7200 1800 604800 3600", 3600)); + SimpleBackend::s_zones[2].insert(zoneB); + + BackendMakers().report(new SimpleBackendFactory()); + BackendMakers().report(new SimpleBackendBestAuthFactory()); + BackendMakers().launch("SimpleBackendBestAuth:1, SimpleBackend:2"); + UeberBackend::go(); + + auto testFunction = [](UeberBackend& ub) -> void { + { + auto sbba = dynamic_cast(ub.backends.at(0)); + BOOST_REQUIRE(sbba != nullptr); + sbba->d_authLookupCount = 0; + + // test getAuth() + SOAData sd; + BOOST_REQUIRE(ub.getAuth(DNSName("2.4.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa."), QType::PTR, &sd)); + BOOST_CHECK_EQUAL(sd.qname.toString(), "d.0.1.0.0.2.ip6.arpa."); + BOOST_CHECK_EQUAL(sd.domain_id, 1); + // check that only one auth lookup occurred to this backend + BOOST_CHECK_EQUAL(sbba->d_authLookupCount, 1); + } + + }; + + testWithoutThenWithCache(testFunction); + } + catch(const PDNSException& e) { + cerr<insert(SimpleBackend::SimpleDNSRecord(DNSName("powerdns.com."), QType::SOA, "ns1.powerdns.com. powerdns.com. 3 600 600 3600000 604800", 3600)); + zoneA.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("powerdns.com."), QType::AAAA, "2001:db8::1", 60)); + zoneA.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("www.powerdns.com."), QType::A, "192.168.0.1", 60)); + zoneA.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("geo.powerdns.com."), QType::A, "192.168.0.42", 60)); + SimpleBackend::s_zones[1].insert(zoneA); + SimpleBackend::s_metadata[1].insert(SimpleBackend::SimpleMetaData(DNSName("powerdns.com."), "test-data-a", { "value1", "value2"})); + + SimpleBackend::SimpleDNSZone zoneB(DNSName("powerdns.org."), 2); + zoneB.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("powerdns.org."), QType::SOA, "ns1.powerdns.org. powerdns.org. 3 600 600 3600000 604800", 3600)); + zoneB.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("powerdns.org."), QType::AAAA, "2001:db8::2", 60)); + zoneB.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("www.powerdns.org."), QType::AAAA, "2001:db8::2", 60)); + zoneB.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("geo.powerdns.org."), QType::AAAA, "2001:db8::42", 60)); + SimpleBackend::s_zones[2].insert(zoneB); + SimpleBackend::s_metadata[2].insert(SimpleBackend::SimpleMetaData(DNSName("powerdns.org."), "test-data-b", { "value1", "value2"})); + + BackendMakers().report(new SimpleBackendFactory()); + BackendMakers().launch("SimpleBackend:1, SimpleBackend:2"); + UeberBackend::go(); + + auto testFunction = [](UeberBackend& ub) -> void { + { + // check the initial values + std::vector values; + BOOST_CHECK(ub.getDomainMetadata(DNSName("powerdns.com."), "test-data-a", values)); + BOOST_REQUIRE_EQUAL(values.size(), 2); + BOOST_CHECK_EQUAL(values.at(0), "value1"); + BOOST_CHECK_EQUAL(values.at(1), "value2"); + values.clear(); + BOOST_CHECK(ub.getDomainMetadata(DNSName("powerdns.com."), "test-data-b", values)); + BOOST_CHECK_EQUAL(values.size(), 0); + values.clear(); + BOOST_CHECK(ub.getDomainMetadata(DNSName("powerdns.org."), "test-data-a", values)); + BOOST_CHECK_EQUAL(values.size(), 0); + values.clear(); + BOOST_CHECK(ub.getDomainMetadata(DNSName("powerdns.org."), "test-data-b", values)); + BOOST_CHECK_EQUAL(values.size(), 0); + } + + { + // update the values + BOOST_CHECK(ub.setDomainMetadata(DNSName("powerdns.com."), "test-data-a", { "value3" })); + BOOST_CHECK(ub.setDomainMetadata(DNSName("powerdns.org."), "test-data-a", { "value4" })); + BOOST_CHECK(ub.setDomainMetadata(DNSName("powerdns.org."), "test-data-b", { "value5" })); + } + + // check the updated values + { + std::vector values; + BOOST_CHECK(ub.getDomainMetadata(DNSName("powerdns.com."), "test-data-a", values)); + BOOST_REQUIRE_EQUAL(values.size(), 1); + BOOST_CHECK_EQUAL(values.at(0), "value3"); + values.clear(); + BOOST_CHECK(ub.getDomainMetadata(DNSName("powerdns.org."), "test-data-a", values)); + BOOST_REQUIRE_EQUAL(values.size(), 1); + BOOST_CHECK_EQUAL(values.at(0), "value4"); + values.clear(); + BOOST_CHECK(ub.getDomainMetadata(DNSName("powerdns.org."), "test-data-b", values)); + BOOST_REQUIRE_EQUAL(values.size(), 1); + BOOST_CHECK_EQUAL(values.at(0), "value5"); + } + + { + // check that it has not been updated in the second backend + const auto& it = SimpleBackend::s_metadata[2].find(boost::make_tuple(DNSName("powerdns.org."), "test-data-b")); + BOOST_REQUIRE(it != SimpleBackend::s_metadata[2].end()); + BOOST_REQUIRE_EQUAL(it->d_values.size(), 2); + BOOST_CHECK_EQUAL(it->d_values.at(0), "value1"); + BOOST_CHECK_EQUAL(it->d_values.at(1), "value2"); + } + }; + + UeberBackend ub; + testFunction(ub); + } + catch(const PDNSException& e) { + cerr< #include #include -#include #include #include -#include -#include -#include -#include #include + #include "dnspacket.hh" #include "dnsbackend.hh" - #include "namespaces.hh" /** This is a very magic backend that allows us to load modules dynamically, -- 2.47.2