]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/ueberbackend.cc
rec: mention rust compiler in compiling docs
[thirdparty/pdns.git] / pdns / ueberbackend.cc
CommitLineData
12c86877 1/*
6edbf68a
PL
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
d2868094 22#include <memory>
870a0fe4
AT
23#ifdef HAVE_CONFIG_H
24#include "config.h"
25#endif
cb433f9c
BH
26#include <boost/archive/binary_iarchive.hpp>
27#include <boost/archive/binary_oarchive.hpp>
28
bf269e28 29#include "auth-querycache.hh"
1f0fb39a 30#include "auth-zonecache.hh"
12c86877 31#include "utility.hh"
c6566265 32
dc6aa7f5 33#include <cerrno>
b0ecae96
FM
34#include <dlfcn.h>
35#include <functional>
12c86877 36#include <iostream>
b0ecae96 37#include <map>
12c86877 38#include <sstream>
61c1733f 39#include <string>
b0ecae96 40#include <sys/types.h>
fa8fd4d2 41
12c86877
BH
42#include "dns.hh"
43#include "arguments.hh"
44#include "dnsbackend.hh"
45#include "ueberbackend.hh"
46#include "dnspacket.hh"
47#include "logger.hh"
48#include "statbag.hh"
12c86877
BH
49
50extern StatBag S;
51
03eca04c 52LockGuarded<vector<UeberBackend*>> UeberBackend::d_instances;
12c86877 53
12c86877 54// initially we are blocked
03eca04c
FM
55bool UeberBackend::d_go = false;
56bool UeberBackend::s_doANYLookupsOnly = false;
0ddde5fb
RG
57std::mutex UeberBackend::d_mut;
58std::condition_variable UeberBackend::d_cond;
d04143d5 59AtomicCounter* UeberBackend::s_backendQueries = nullptr;
12c86877 60
12c86877 61//! Loads a module and reports it to all UeberBackend threads
03eca04c 62bool UeberBackend::loadmodule(const string& name)
12c86877 63{
03eca04c 64 g_log << Logger::Warning << "Loading '" << name << "'" << endl;
74caf870 65
03eca04c 66 void* dlib = dlopen(name.c_str(), RTLD_NOW);
74caf870 67
03eca04c 68 if (dlib == nullptr) {
d330c984 69 // NOLINTNEXTLINE(concurrency-mt-unsafe): There's no thread-safe alternative to dlerror().
03eca04c 70 g_log << Logger::Error << "Unable to load module '" << name << "': " << dlerror() << endl;
12c86877
BH
71 return false;
72 }
74caf870 73
12c86877 74 return true;
12c86877
BH
75}
76
d4168b30
CHB
77bool UeberBackend::loadModules(const vector<string>& modules, const string& path)
78{
03eca04c 79 for (const auto& module : modules) {
61c1733f
FM
80 bool res = false;
81
03eca04c 82 if (module.find('.') == string::npos) {
61c1733f
FM
83 auto fullPath = path;
84 fullPath += "/lib";
85 fullPath += module;
86 fullPath += "backend.so";
87 res = UeberBackend::loadmodule(fullPath);
03eca04c
FM
88 }
89 else if (module[0] == '/' || (module[0] == '.' && module[1] == '/') || (module[0] == '.' && module[1] == '.')) {
d4168b30
CHB
90 // absolute or current path
91 res = UeberBackend::loadmodule(module);
03eca04c
FM
92 }
93 else {
61c1733f
FM
94 auto fullPath = path;
95 fullPath += "/";
96 fullPath += module;
97 res = UeberBackend::loadmodule(fullPath);
d4168b30
CHB
98 }
99
61c1733f 100 if (!res) {
d4168b30
CHB
101 return false;
102 }
103 }
104 return true;
105}
106
e52fb6a4 107void UeberBackend::go()
12c86877 108{
cd96bd73 109 if (::arg().mustDo("consistent-backends")) {
bd8df5bc
RG
110 s_doANYLookupsOnly = true;
111 }
112
d04143d5
RG
113 S.declare("backend-queries", "Number of queries sent to the backend(s)");
114 s_backendQueries = S.getPointer("backend-queries");
115
0ddde5fb
RG
116 {
117 std::unique_lock<std::mutex> l(d_mut);
118 d_go = true;
119 }
120 d_cond.notify_all();
12c86877
BH
121}
122
e255cb4b 123bool UeberBackend::getDomainInfo(const DNSName& domain, DomainInfo& domainInfo, bool getSerial)
12c86877 124{
d2868094 125 for (auto& backend : backends) {
e255cb4b 126 if (backend->getDomainInfo(domain, domainInfo, getSerial)) {
12c86877 127 return true;
e255cb4b
FM
128 }
129 }
12c86877
BH
130 return false;
131}
132
d525b58b 133bool UeberBackend::createDomain(const DNSName& domain, const DomainInfo::DomainKind kind, const vector<ComboAddress>& primaries, const string& account)
487cf033 134{
d2868094 135 for (auto& backend : backends) {
d525b58b 136 if (backend->createDomain(domain, kind, primaries, account)) {
f4922e19 137 return true;
487cf033
CH
138 }
139 }
f4922e19 140 return false;
487cf033
CH
141}
142
7fa35c07
KM
143bool UeberBackend::doesDNSSEC()
144{
d2868094 145 for (auto& backend : backends) {
d6461d40 146 if (backend->doesDNSSEC()) {
7fa35c07 147 return true;
d6461d40 148 }
7fa35c07
KM
149 }
150 return false;
151}
152
05e0440e 153bool UeberBackend::addDomainKey(const DNSName& name, const DNSBackend::KeyData& key, int64_t& keyID)
c0273500 154{
05e0440e 155 keyID = -1;
d2868094 156 for (auto& backend : backends) {
05e0440e 157 if (backend->addDomainKey(name, key, keyID)) {
82cc0761 158 return true;
05e0440e 159 }
c0273500 160 }
82cc0761 161 return false;
c0273500 162}
9c1c5d49 163bool UeberBackend::getDomainKeys(const DNSName& name, std::vector<DNSBackend::KeyData>& keys)
c0273500 164{
d2868094 165 for (auto& backend : backends) {
05e0440e 166 if (backend->getDomainKeys(name, keys)) {
c0273500 167 return true;
05e0440e 168 }
c0273500
BH
169 }
170 return false;
171}
172
03eca04c 173bool UeberBackend::getAllDomainMetadata(const DNSName& name, std::map<std::string, std::vector<std::string>>& meta)
ac993e0a 174{
d2868094 175 for (auto& backend : backends) {
05e0440e 176 if (backend->getAllDomainMetadata(name, meta)) {
ac993e0a 177 return true;
05e0440e 178 }
ac993e0a
AT
179 }
180 return false;
181}
182
675fa24c 183bool UeberBackend::getDomainMetadata(const DNSName& name, const std::string& kind, std::vector<std::string>& meta)
c0273500 184{
d2868094 185 for (auto& backend : backends) {
05e0440e 186 if (backend->getDomainMetadata(name, kind, meta)) {
c0273500 187 return true;
05e0440e 188 }
c0273500
BH
189 }
190 return false;
191}
192
ddeea7a6
KM
193bool UeberBackend::getDomainMetadata(const DNSName& name, const std::string& kind, std::string& meta)
194{
ddeea7a6
KM
195 meta.clear();
196 std::vector<string> tmp;
05e0440e
FM
197 const bool ret = getDomainMetadata(name, kind, tmp);
198 if (ret && !tmp.empty()) {
ddeea7a6
KM
199 meta = *tmp.begin();
200 }
201 return ret;
202}
203
675fa24c 204bool UeberBackend::setDomainMetadata(const DNSName& name, const std::string& kind, const std::vector<std::string>& meta)
c0273500 205{
d2868094 206 for (auto& backend : backends) {
05e0440e 207 if (backend->setDomainMetadata(name, kind, meta)) {
c0273500 208 return true;
05e0440e 209 }
c0273500
BH
210 }
211 return false;
212}
213
ddeea7a6
KM
214bool UeberBackend::setDomainMetadata(const DNSName& name, const std::string& kind, const std::string& meta)
215{
216 std::vector<string> tmp;
217 if (!meta.empty()) {
218 tmp.push_back(meta);
219 }
220 return setDomainMetadata(name, kind, tmp);
221}
222
05e0440e 223bool UeberBackend::activateDomainKey(const DNSName& name, unsigned int keyID)
4496f66f 224{
d2868094 225 for (auto& backend : backends) {
05e0440e 226 if (backend->activateDomainKey(name, keyID)) {
4496f66f 227 return true;
05e0440e 228 }
4496f66f
BH
229 }
230 return false;
231}
232
05e0440e 233bool UeberBackend::deactivateDomainKey(const DNSName& name, unsigned int keyID)
4496f66f 234{
d2868094 235 for (auto& backend : backends) {
05e0440e 236 if (backend->deactivateDomainKey(name, keyID)) {
4496f66f 237 return true;
05e0440e 238 }
4496f66f
BH
239 }
240 return false;
241}
242
05e0440e 243bool UeberBackend::publishDomainKey(const DNSName& name, unsigned int keyID)
33918299 244{
d2868094 245 for (auto& backend : backends) {
05e0440e 246 if (backend->publishDomainKey(name, keyID)) {
33918299 247 return true;
05e0440e 248 }
33918299
RG
249 }
250 return false;
251}
252
05e0440e 253bool UeberBackend::unpublishDomainKey(const DNSName& name, unsigned int keyID)
33918299 254{
d2868094 255 for (auto& backend : backends) {
05e0440e 256 if (backend->unpublishDomainKey(name, keyID)) {
33918299 257 return true;
05e0440e 258 }
33918299
RG
259 }
260 return false;
261}
262
05e0440e 263bool UeberBackend::removeDomainKey(const DNSName& name, unsigned int keyID)
4496f66f 264{
d2868094 265 for (auto& backend : backends) {
05e0440e 266 if (backend->removeDomainKey(name, keyID)) {
4496f66f 267 return true;
05e0440e 268 }
4496f66f
BH
269 }
270 return false;
271}
272
12c86877
BH
273void UeberBackend::reload()
274{
03eca04c 275 for (auto& backend : backends) {
d7f67000 276 backend->reload();
12c86877
BH
277 }
278}
279
03eca04c
FM
280void UeberBackend::updateZoneCache()
281{
1f0fb39a 282 if (!g_zoneCache.isEnabled()) {
d5385b35
CH
283 return;
284 }
285
905dae56 286 vector<std::tuple<DNSName, int>> zone_indices;
1f0fb39a 287 g_zoneCache.setReplacePending();
d5385b35 288
01f43bae 289 for (auto& backend : backends) {
1f0fb39a 290 vector<DomainInfo> zones;
01f43bae 291 backend->getAllDomains(&zones, false, true);
346d1042
FM
292 for (auto& domainInfo : zones) {
293 zone_indices.emplace_back(std::move(domainInfo.zone), (int)domainInfo.id); // this cast should not be necessary
d5385b35
CH
294 }
295 }
1f0fb39a 296 g_zoneCache.replace(zone_indices);
d5385b35
CH
297}
298
03eca04c 299void UeberBackend::rediscover(string* status)
12c86877 300{
346d1042 301 for (auto backend = backends.begin(); backend != backends.end(); ++backend) {
973ad2b5 302 string tmpstr;
346d1042 303 (*backend)->rediscover(&tmpstr);
01f43bae 304 if (status != nullptr) {
346d1042 305 *status += tmpstr + (backend != backends.begin() ? "\n" : "");
01f43bae 306 }
12c86877 307 }
d5385b35 308
1f0fb39a 309 updateZoneCache();
12c86877
BH
310}
311
c02c999b 312void UeberBackend::getUnfreshSecondaryInfos(vector<DomainInfo>* domains)
12c86877 313{
03eca04c 314 for (auto& backend : backends) {
c02c999b 315 backend->getUnfreshSecondaryInfos(domains);
03eca04c 316 }
12c86877
BH
317}
318
d525b58b 319void UeberBackend::getUpdatedPrimaries(vector<DomainInfo>& domains, std::unordered_set<DNSName>& catalogs, CatalogHashMap& catalogHashes)
12c86877 320{
03eca04c 321 for (auto& backend : backends) {
d525b58b 322 backend->getUpdatedPrimaries(domains, catalogs, catalogHashes);
12c86877
BH
323 }
324}
325
9c56d755
KM
326bool UeberBackend::inTransaction()
327{
d2868094 328 for (auto& backend : backends) {
ea5eed3b 329 if (backend->inTransaction()) {
9c56d755 330 return true;
ea5eed3b 331 }
9c56d755
KM
332 }
333 return false;
334}
335
591e66f4
FM
336bool UeberBackend::fillSOAFromZoneRecord(DNSName& shorter, const int zoneId, SOAData* const soaData)
337{
338 // Zone exists in zone cache, directly look up SOA.
339 lookup(QType(QType::SOA), shorter, zoneId, nullptr);
340
341 DNSZoneRecord zoneRecord;
342 if (!get(zoneRecord)) {
343 DLOG(g_log << Logger::Info << "Backend returned no SOA for zone '" << shorter.toLogString() << "', which it reported as existing " << endl);
344 return false;
345 }
346
347 if (zoneRecord.dr.d_name != shorter) {
348 throw PDNSException("getAuth() returned an SOA for the wrong zone. Zone '" + zoneRecord.dr.d_name.toLogString() + "' is not equal to looked up zone '" + shorter.toLogString() + "'");
349 }
350
351 // Fill soaData.
352 soaData->qname = zoneRecord.dr.d_name;
353
354 try {
355 fillSOAData(zoneRecord, *soaData);
356 }
357 catch (...) {
358 g_log << Logger::Warning << "Backend returned a broken SOA for zone '" << shorter.toLogString() << "'" << endl;
359
360 while (get(zoneRecord)) {
361 ;
362 }
363
364 return false;
365 }
366
d2868094 367 soaData->db = backends.size() == 1 ? backends.begin()->get() : nullptr;
591e66f4
FM
368
369 // Leave database handle in a consistent state.
370 while (get(zoneRecord)) {
371 ;
372 }
373
374 return true;
375}
376
c6a2025d
FM
377UeberBackend::CacheResult UeberBackend::fillSOAFromCache(SOAData* soaData, DNSName& shorter)
378{
379 auto cacheResult = cacheHas(d_question, d_answers);
380
381 if (cacheResult == CacheResult::Hit && !d_answers.empty() && d_cache_ttl != 0U) {
382 DLOG(g_log << Logger::Error << "has pos cache entry: " << shorter << endl);
383 fillSOAData(d_answers[0], *soaData);
384
d2868094 385 soaData->db = backends.size() == 1 ? backends.begin()->get() : nullptr;
c6a2025d
FM
386 soaData->qname = shorter;
387 }
388 else if (cacheResult == CacheResult::NegativeMatch && d_negcache_ttl != 0U) {
389 DLOG(g_log << Logger::Error << "has neg cache entry: " << shorter << endl);
390 }
391
392 return cacheResult;
393}
394
d2868094 395static std::vector<std::unique_ptr<DNSBackend>>::iterator findBestMatchingBackend(std::vector<std::unique_ptr<DNSBackend>>& backends, std::vector<std::pair<std::size_t, SOAData>>& bestMatches, const DNSName& shorter, SOAData* soaData)
8570c9e0
FM
396{
397 auto backend = backends.begin();
398 for (auto bestMatch = bestMatches.begin(); backend != backends.end() && bestMatch != bestMatches.end(); ++backend, ++bestMatch) {
399
400 DLOG(g_log << Logger::Error << "backend: " << backend - backends.begin() << ", qname: " << shorter << endl);
401
402 if (bestMatch->first < shorter.wirelength()) {
403 DLOG(g_log << Logger::Error << "skipped, we already found a shorter best match in this backend: " << bestMatch->second.qname << endl);
404 continue;
405 }
406
407 if (bestMatch->first == shorter.wirelength()) {
408 DLOG(g_log << Logger::Error << "use shorter best match: " << bestMatch->second.qname << endl);
409 *soaData = bestMatch->second;
410 break;
411 }
412
413 DLOG(g_log << Logger::Error << "lookup: " << shorter << endl);
414
415 if ((*backend)->getAuth(shorter, soaData)) {
416 DLOG(g_log << Logger::Error << "got: " << soaData->qname << endl);
417
418 if (!soaData->qname.empty() && !shorter.isPartOf(soaData->qname)) {
419 throw PDNSException("getAuth() returned an SOA for the wrong zone. Zone '" + soaData->qname.toLogString() + "' is not part of '" + shorter.toLogString() + "'");
420 }
421
422 bestMatch->first = soaData->qname.wirelength();
423 bestMatch->second = *soaData;
424
425 if (soaData->qname == shorter) {
426 break;
427 }
428 }
429 else {
430 DLOG(g_log << Logger::Error << "no match for: " << shorter << endl);
431 }
432 }
433
434 return backend;
435}
436
51193f95 437static bool foundTarget(const DNSName& target, const DNSName& shorter, const QType& qtype, [[maybe_unused]] SOAData* soaData, const bool found)
8d4a47dc 438{
8d4a47dc 439 if (found == (qtype == QType::DS) || target != shorter) {
51193f95 440 DLOG(g_log << Logger::Error << "found: " << soaData->qname << endl);
8d4a47dc
FM
441 return true;
442 }
443
51193f95 444 DLOG(g_log << Logger::Error << "chasing next: " << soaData->qname << endl);
8d4a47dc
FM
445 return false;
446}
447
212a3555 448bool UeberBackend::getAuth(const DNSName& target, const QType& qtype, SOAData* soaData, bool cachedOk)
c14bc34a 449{
942d4729
KM
450 // A backend can respond to our authority request with the 'best' match it
451 // has. For example, when asked for a.b.c.example.com. it might respond with
452 // com. We then store that and keep querying the other backends in case one
453 // of them has a more specific zone but don't bother asking this specific
454 // backend again for b.c.example.com., c.example.com. and example.com.
37029707 455 // If a backend has no match it may respond with an empty qname.
212a3555 456
5b9ac871 457 bool found = false;
942d4729 458 DNSName shorter(target);
212a3555
FM
459 vector<pair<size_t, SOAData>> bestMatches(backends.size(), pair(target.wirelength() + 1, SOAData()));
460
1a5d85f7
FM
461 bool first = true;
462 while (first || shorter.chopOff()) {
463 first = false;
464
d5385b35 465 int zoneId{-1};
212a3555 466
03eca04c 467 if (cachedOk && g_zoneCache.isEnabled()) {
1f0fb39a 468 if (g_zoneCache.getEntry(shorter, zoneId)) {
591e66f4 469 if (fillSOAFromZoneRecord(shorter, zoneId, soaData)) {
1a5d85f7
FM
470 if (foundTarget(target, shorter, qtype, soaData, found)) {
471 return true;
472 }
473
474 found = true;
212a3555 475 }
1a5d85f7
FM
476
477 continue;
d5385b35 478 }
212a3555
FM
479
480 // Zone does not exist, try again with a shorter name.
d5385b35
CH
481 continue;
482 }
5b9ac871 483
8b728851
RG
484 d_question.qtype = QType::SOA;
485 d_question.qname = shorter;
d5385b35 486 d_question.zoneId = zoneId;
8b728851 487
212a3555 488 // Check cache.
01f43bae 489 if (cachedOk && (d_cache_ttl != 0 || d_negcache_ttl != 0)) {
c6a2025d
FM
490 auto cacheResult = fillSOAFromCache(soaData, shorter);
491 if (cacheResult == CacheResult::Hit) {
1a5d85f7
FM
492 if (foundTarget(target, shorter, qtype, soaData, found)) {
493 return true;
494 }
495
496 found = true;
497 continue;
498 }
499
500 if (cacheResult == CacheResult::NegativeMatch) {
5b9ac871 501 continue;
c14bc34a 502 }
c14bc34a 503 }
f70be3c3 504
8570c9e0 505 // Check backends.
5b9ac871 506 {
8570c9e0 507 auto backend = findBestMatchingBackend(backends, bestMatches, shorter, soaData);
c14bc34a 508
5b9ac871 509 // Add to cache
212a3555
FM
510 if (backend == backends.end()) {
511 if (d_negcache_ttl != 0U) {
03eca04c
FM
512 DLOG(g_log << Logger::Error << "add neg cache entry:" << shorter << endl);
513 d_question.qname = shorter;
5b9ac871
KM
514 addNegCache(d_question);
515 }
212a3555 516
5b9ac871 517 continue;
03eca04c 518 }
01f43bae
FM
519
520 if (d_cache_ttl != 0) {
212a3555
FM
521 DLOG(g_log << Logger::Error << "add pos cache entry: " << soaData->qname << endl);
522
c14bc34a 523 d_question.qtype = QType::SOA;
212a3555 524 d_question.qname = soaData->qname;
d5385b35 525 d_question.zoneId = zoneId;
c14bc34a 526
212a3555
FM
527 DNSZoneRecord resourceRecord;
528 resourceRecord.dr.d_name = soaData->qname;
529 resourceRecord.dr.d_type = QType::SOA;
530 resourceRecord.dr.setContent(makeSOAContent(*soaData));
531 resourceRecord.dr.d_ttl = soaData->ttl;
532 resourceRecord.domain_id = soaData->domain_id;
90ba52e0 533
8690b6ff 534 addCache(d_question, {std::move(resourceRecord)});
5b9ac871 535 }
c14bc34a
MZ
536 }
537
8d4a47dc 538 if (foundTarget(target, shorter, qtype, soaData, found)) {
5b9ac871 539 return true;
03eca04c 540 }
8d4a47dc
FM
541
542 found = true;
1a5d85f7 543 }
212a3555 544
5b9ac871 545 return found;
c14bc34a
MZ
546}
547
988be9b3 548bool UeberBackend::getSOAUncached(const DNSName& domain, SOAData& soaData)
79ba7763 549{
03eca04c
FM
550 d_question.qtype = QType::SOA;
551 d_question.qname = domain;
552 d_question.zoneId = -1;
79ba7763 553
d2868094 554 for (auto& backend : backends) {
988be9b3
FM
555 if (backend->getSOA(domain, soaData)) {
556 if (domain != soaData.qname) {
557 throw PDNSException("getSOA() returned an SOA for the wrong zone. Question: '" + domain.toLogString() + "', answer: '" + soaData.qname.toLogString() + "'");
37029707 558 }
988be9b3
FM
559 if (d_cache_ttl != 0U) {
560 DNSZoneRecord zoneRecord;
561 zoneRecord.dr.d_name = soaData.qname;
562 zoneRecord.dr.d_type = QType::SOA;
563 zoneRecord.dr.setContent(makeSOAContent(soaData));
564 zoneRecord.dr.d_ttl = soaData.ttl;
565 zoneRecord.domain_id = soaData.domain_id;
566
3b45a434 567 addCache(d_question, {std::move(zoneRecord)});
f1d64762 568 }
12c86877
BH
569 return true;
570 }
4136873c 571 }
12c86877 572
988be9b3 573 if (d_negcache_ttl != 0U) {
b87f301b 574 addNegCache(d_question);
4136873c 575 }
12c86877
BH
576 return false;
577}
578
d525b58b 579bool UeberBackend::autoPrimaryAdd(const AutoPrimary& primary)
3d26f6dc 580{
d2868094 581 for (auto& backend : backends) {
d525b58b 582 if (backend->autoPrimaryAdd(primary)) {
3d26f6dc 583 return true;
1aabe47f
FM
584 }
585 }
3d26f6dc
E
586 return false;
587}
588
03eca04c 589bool UeberBackend::autoPrimaryRemove(const AutoPrimary& primary)
782e9b24 590{
d2868094 591 for (auto& backend : backends) {
1aabe47f 592 if (backend->autoPrimaryRemove(primary)) {
782e9b24 593 return true;
1aabe47f
FM
594 }
595 }
782e9b24
AT
596 return false;
597}
598
599bool UeberBackend::autoPrimariesList(std::vector<AutoPrimary>& primaries)
600{
d2868094 601 for (auto& backend : backends) {
1aabe47f 602 if (backend->autoPrimariesList(primaries)) {
03eca04c 603 return true;
1aabe47f
FM
604 }
605 }
03eca04c 606 return false;
782e9b24
AT
607}
608
d525b58b 609bool UeberBackend::autoPrimaryBackend(const string& ip, const DNSName& domain, const vector<DNSResourceRecord>& nsset, string* nameserver, string* account, DNSBackend** dnsBackend)
12c86877 610{
d2868094 611 for (auto& backend : backends) {
d525b58b 612 if (backend->autoPrimaryBackend(ip, domain, nsset, nameserver, account, dnsBackend)) {
12c86877 613 return true;
1aabe47f
FM
614 }
615 }
12c86877 616 return false;
12c86877
BH
617}
618
346d1042 619UeberBackend::UeberBackend(const string& pname)
12c86877 620{
0ddde5fb 621 {
351cdb4c 622 d_instances.lock()->push_back(this); // report to the static list of ourself
0ddde5fb 623 }
12c86877 624
f1d64762
MZ
625 d_cache_ttl = ::arg().asNum("query-cache-ttl");
626 d_negcache_ttl = ::arg().asNum("negquery-cache-ttl");
12c86877 627
03eca04c 628 backends = BackendMakers().all(pname == "key-only");
12c86877
BH
629}
630
cb433f9c 631// returns -1 for miss, 0 for negative match, 1 for hit
6e9d4b0d 632enum UeberBackend::CacheResult UeberBackend::cacheHas(const Question& question, vector<DNSZoneRecord>& resourceRecords) const
12c86877 633{
bf269e28 634 extern AuthQueryCache QC;
12c86877 635
2f25a437 636 if (d_cache_ttl == 0 && d_negcache_ttl == 0) {
6e9d4b0d 637 return CacheResult::Miss;
12c86877
BH
638 }
639
6e9d4b0d 640 resourceRecords.clear();
e6a9dde5 641 // g_log<<Logger::Warning<<"looking up: '"<<q.qname+"'|N|"+q.qtype.getName()+"|"+itoa(q.zoneId)<<endl;
cf71f03f 642
6e9d4b0d 643 bool ret = QC.getEntry(question.qname, question.qtype, resourceRecords, question.zoneId); // think about lowercasing here
03eca04c 644 if (!ret) {
6e9d4b0d 645 return CacheResult::Miss;
12c86877 646 }
6e9d4b0d
FM
647 if (resourceRecords.empty()) { // negatively cached
648 return CacheResult::NegativeMatch;
2f25a437 649 }
03eca04c 650
6e9d4b0d 651 return CacheResult::Hit;
12c86877
BH
652}
653
346d1042 654void UeberBackend::addNegCache(const Question& question) const
12c86877 655{
bf269e28 656 extern AuthQueryCache QC;
2f25a437 657 if (d_negcache_ttl == 0) {
12c86877 658 return;
2f25a437 659 }
ee5e1751 660 // we should also not be storing negative answers if a pipebackend does scopeMask, but we can't pass a negative scopeMask in an empty set!
346d1042 661 QC.insert(question.qname, question.qtype, vector<DNSZoneRecord>(), d_negcache_ttl, question.zoneId);
12c86877
BH
662}
663
346d1042 664void UeberBackend::addCache(const Question& question, vector<DNSZoneRecord>&& rrs) const
12c86877 665{
bf269e28 666 extern AuthQueryCache QC;
46c6efbe 667
2f25a437 668 if (d_cache_ttl == 0) {
12c86877 669 return;
2f25a437 670 }
f1d64762 671
346d1042
FM
672 for (const auto& resourceRecord : rrs) {
673 if (resourceRecord.scopeMask != 0) {
03eca04c 674 return;
2f25a437 675 }
429ae4cb 676 }
677
346d1042 678 QC.insert(question.qname, question.qtype, std::move(rrs), d_cache_ttl, question.zoneId);
12c86877
BH
679}
680
03eca04c 681void UeberBackend::alsoNotifies(const DNSName& domain, set<string>* ips)
27d94a79 682{
a4baf63a 683 for (auto& backend : backends) {
03eca04c 684 backend->alsoNotifies(domain, ips);
a4baf63a 685 }
27d94a79 686}
12c86877
BH
687
688UeberBackend::~UeberBackend()
689{
03eca04c 690 DLOG(g_log << Logger::Error << "UeberBackend destructor called, removing ourselves from instances, and deleting our backends" << endl);
d2868094
FM
691
692 {
693 auto instances = d_instances.lock();
694 [[maybe_unused]] auto end = remove(instances->begin(), instances->end(), this);
695 instances->resize(instances->size() - 1);
696 }
697
698 backends.clear();
12c86877
BH
699}
700
701// this handle is more magic than most
03eca04c 702void UeberBackend::lookup(const QType& qtype, const DNSName& qname, int zoneId, DNSPacket* pkt_p)
12c86877 703{
03eca04c
FM
704 if (d_stale) {
705 g_log << Logger::Error << "Stale ueberbackend received question, signalling that we want to be recycled" << endl;
3f81d239 706 throw PDNSException("We are stale, please recycle");
12c86877
BH
707 }
708
03eca04c 709 DLOG(g_log << "UeberBackend received question for " << qtype << " of " << qname << endl);
0ddde5fb 710 if (!d_go) {
03eca04c 711 g_log << Logger::Error << "UeberBackend is blocked, waiting for 'go'" << endl;
0ddde5fb 712 std::unique_lock<std::mutex> l(d_mut);
a285530b 713 d_cond.wait(l, [] { return d_go; });
03eca04c 714 g_log << Logger::Error << "Broadcast received, unblocked" << endl;
12c86877
BH
715 }
716
03eca04c 717 d_qtype = qtype.getCode();
c27c8d96 718
03eca04c
FM
719 d_handle.i = 0;
720 d_handle.qtype = s_doANYLookupsOnly ? QType::ANY : qtype;
721 d_handle.qname = qname;
722 d_handle.zoneId = zoneId;
723 d_handle.pkt_p = pkt_p;
12c86877 724
a285530b 725 if (backends.empty()) {
03eca04c
FM
726 g_log << Logger::Error << "No database backends available - unable to answer questions." << endl;
727 d_stale = true; // please recycle us!
3f81d239 728 throw PDNSException("We are stale, please recycle");
12c86877 729 }
a285530b
FM
730
731 d_question.qtype = d_handle.qtype;
732 d_question.qname = qname;
733 d_question.zoneId = d_handle.zoneId;
734
735 auto cacheResult = cacheHas(d_question, d_answers);
736 if (cacheResult == CacheResult::Miss) { // nothing
737 // cout<<"UeberBackend::lookup("<<qname<<"|"<<DNSRecordContent::NumberToType(qtype.getCode())<<"): uncached"<<endl;
738 d_negcached = d_cached = false;
739 d_answers.clear();
d2868094 740 (d_handle.d_hinterBackend = backends[d_handle.i++].get())->lookup(d_handle.qtype, d_handle.qname, d_handle.zoneId, d_handle.pkt_p);
a285530b
FM
741 ++(*s_backendQueries);
742 }
743 else if (cacheResult == CacheResult::NegativeMatch) {
744 // cout<<"UeberBackend::lookup("<<qname<<"|"<<DNSRecordContent::NumberToType(qtype.getCode())<<"): NEGcached"<<endl;
745 d_negcached = true;
746 d_cached = false;
747 d_answers.clear();
748 }
12c86877 749 else {
a285530b
FM
750 // cout<<"UeberBackend::lookup("<<qname<<"|"<<DNSRecordContent::NumberToType(qtype.getCode())<<"): CACHED"<<endl;
751 d_negcached = false;
752 d_cached = true;
753 d_cachehandleiter = d_answers.begin();
12c86877
BH
754 }
755
03eca04c 756 d_handle.parent = this;
12c86877
BH
757}
758
39dafc3b
KM
759void UeberBackend::getAllDomains(vector<DomainInfo>* domains, bool getSerial, bool include_disabled)
760{
03eca04c 761 for (auto& backend : backends) {
39dafc3b 762 backend->getAllDomains(domains, getSerial, include_disabled);
1325e8a2
PD
763 }
764}
765
346d1042 766bool UeberBackend::get(DNSZoneRecord& resourceRecord)
12c86877 767{
aa7b2405 768 // cout<<"UeberBackend::get(DNSZoneRecord) called"<<endl;
03eca04c
FM
769 if (d_negcached) {
770 return false;
12c86877
BH
771 }
772
03eca04c
FM
773 if (d_cached) {
774 while (d_cachehandleiter != d_answers.end()) {
346d1042
FM
775 resourceRecord = *d_cachehandleiter++;
776 if ((d_qtype == QType::ANY || resourceRecord.dr.d_type == d_qtype)) {
6d665794
KM
777 return true;
778 }
cb433f9c
BH
779 }
780 return false;
12c86877 781 }
6d665794 782
346d1042
FM
783 while (d_handle.get(resourceRecord)) {
784 resourceRecord.dr.d_place = DNSResourceRecord::ANSWER;
785 d_answers.push_back(resourceRecord);
786 if ((d_qtype == QType::ANY || resourceRecord.dr.d_type == d_qtype)) {
6d665794 787 return true;
aa7b2405 788 }
12c86877 789 }
2ac9fd56 790
6d665794 791 // cout<<"end of ueberbackend get, seeing if we should cache"<<endl;
03eca04c 792 if (d_answers.empty()) {
6d665794
KM
793 // cout<<"adding negcache"<<endl;
794 addNegCache(d_question);
795 }
796 else {
797 // cout<<"adding query cache"<<endl;
798 addCache(d_question, std::move(d_answers));
799 }
800 d_answers.clear();
801 return false;
12c86877
BH
802}
803
40361bf2
KM
804// TSIG
805//
806bool UeberBackend::setTSIGKey(const DNSName& name, const DNSName& algorithm, const string& content)
807{
d2868094 808 for (auto& backend : backends) {
a4baf63a 809 if (backend->setTSIGKey(name, algorithm, content)) {
40361bf2
KM
810 return true;
811 }
812 }
813 return false;
814}
815
816bool UeberBackend::getTSIGKey(const DNSName& name, DNSName& algorithm, string& content)
817{
818 algorithm.clear();
819 content.clear();
820
d2868094 821 for (auto& backend : backends) {
a4baf63a 822 if (backend->getTSIGKey(name, algorithm, content)) {
40361bf2
KM
823 break;
824 }
825 }
826 return (!algorithm.empty() && !content.empty());
827}
828
829bool UeberBackend::getTSIGKeys(std::vector<struct TSIGKey>& keys)
830{
831 keys.clear();
832
d2868094 833 for (auto& backend : backends) {
a4baf63a 834 if (backend->getTSIGKeys(keys)) {
40361bf2
KM
835 return true;
836 }
837 }
838 return false;
839}
840
841bool UeberBackend::deleteTSIGKey(const DNSName& name)
842{
d2868094 843 for (auto& backend : backends) {
a4baf63a 844 if (backend->deleteTSIGKey(name)) {
40361bf2
KM
845 return true;
846 }
847 }
848 return false;
849}
850
851// API Search
852//
fd9af6ec 853bool UeberBackend::searchRecords(const string& pattern, size_t maxResults, vector<DNSResourceRecord>& result)
9f8e226e 854{
346d1042 855 bool ret = false;
fd9af6ec
FM
856 for (auto backend = backends.begin(); result.size() < maxResults && backend != backends.end(); ++backend) {
857 if ((*backend)->searchRecords(pattern, maxResults - result.size(), result)) {
346d1042 858 ret = true;
01f43bae
FM
859 }
860 }
346d1042 861 return ret;
9f8e226e
AT
862}
863
fd9af6ec 864bool UeberBackend::searchComments(const string& pattern, size_t maxResults, vector<Comment>& result)
9f8e226e 865{
346d1042 866 bool ret = false;
fd9af6ec
FM
867 for (auto backend = backends.begin(); result.size() < maxResults && backend != backends.end(); ++backend) {
868 if ((*backend)->searchComments(pattern, maxResults - result.size(), result)) {
346d1042 869 ret = true;
01f43bae
FM
870 }
871 }
346d1042 872 return ret;
9f8e226e 873}
12c86877 874
16f7d28d 875AtomicCounter UeberBackend::handle::instances(0);
12c86877
BH
876
877UeberBackend::handle::handle()
878{
e6a9dde5 879 // g_log<<Logger::Warning<<"Handle instances: "<<instances<<endl;
16f7d28d 880 ++instances;
12c86877
BH
881}
882
883UeberBackend::handle::~handle()
884{
16f7d28d 885 --instances;
12c86877
BH
886}
887
346d1042 888bool UeberBackend::handle::get(DNSZoneRecord& record)
12c86877 889{
03eca04c
FM
890 DLOG(g_log << "Ueber get() was called for a " << qtype << " record" << endl);
891 bool isMore = false;
346d1042 892 while (d_hinterBackend != nullptr && !(isMore = d_hinterBackend->get(record))) { // this backend out of answers
03eca04c
FM
893 if (i < parent->backends.size()) {
894 DLOG(g_log << "Backend #" << i << " of " << parent->backends.size()
895 << " out of answers, taking next" << endl);
896
d2868094 897 d_hinterBackend = parent->backends[i++].get();
03eca04c 898 d_hinterBackend->lookup(qtype, qname, zoneId, pkt_p);
d04143d5 899 ++(*s_backendQueries);
12c86877 900 }
01f43bae 901 else {
12c86877 902 break;
01f43bae 903 }
12c86877 904
03eca04c 905 DLOG(g_log << "Now asking backend #" << i << endl);
12c86877
BH
906 }
907
03eca04c
FM
908 if (!isMore && i == parent->backends.size()) {
909 DLOG(g_log << "UeberBackend reached end of backends" << endl);
12c86877
BH
910 return false;
911 }
912
03eca04c
FM
913 DLOG(g_log << "Found an answering backend - will not try another one" << endl);
914 i = parent->backends.size(); // don't go on to the next backend
12c86877
BH
915 return true;
916}