]>
Commit | Line | Data |
---|---|---|
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 | |
50 | extern StatBag S; | |
51 | ||
03eca04c | 52 | LockGuarded<vector<UeberBackend*>> UeberBackend::d_instances; |
12c86877 | 53 | |
12c86877 | 54 | // initially we are blocked |
03eca04c FM |
55 | bool UeberBackend::d_go = false; |
56 | bool UeberBackend::s_doANYLookupsOnly = false; | |
0ddde5fb RG |
57 | std::mutex UeberBackend::d_mut; |
58 | std::condition_variable UeberBackend::d_cond; | |
d04143d5 | 59 | AtomicCounter* UeberBackend::s_backendQueries = nullptr; |
12c86877 | 60 | |
12c86877 | 61 | //! Loads a module and reports it to all UeberBackend threads |
03eca04c | 62 | bool 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 |
77 | bool 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 | 107 | void 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 | 123 | bool 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 | 133 | bool 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 |
143 | bool 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 | 153 | bool 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 | 163 | bool 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 | 173 | bool 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 | 183 | bool 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 |
193 | bool 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 | 204 | bool 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 |
214 | bool 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 | 223 | bool 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 | 233 | bool 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 | 243 | bool 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 | 253 | bool 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 | 263 | bool 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 |
273 | void UeberBackend::reload() |
274 | { | |
03eca04c | 275 | for (auto& backend : backends) { |
d7f67000 | 276 | backend->reload(); |
12c86877 BH |
277 | } |
278 | } | |
279 | ||
03eca04c FM |
280 | void 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 | 299 | void 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 | 312 | void 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 | 319 | void 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 |
326 | bool 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 |
336 | bool 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 |
377 | UeberBackend::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 | 395 | static 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 | 437 | static 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 | 448 | bool 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 | 548 | bool 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 | 579 | bool 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 | 589 | bool 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 | ||
599 | bool 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 | 609 | bool 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 | 619 | UeberBackend::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 | 632 | enum 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 | 654 | void 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 | 664 | void 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 | 681 | void 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 | |
688 | UeberBackend::~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 | 702 | void 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 |
759 | void 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 | 766 | bool 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 | // | |
806 | bool 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 | ||
816 | bool 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 | ||
829 | bool 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 | ||
841 | bool 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 | 853 | bool 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 | 864 | bool 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 | 875 | AtomicCounter UeberBackend::handle::instances(0); |
12c86877 BH |
876 | |
877 | UeberBackend::handle::handle() | |
878 | { | |
e6a9dde5 | 879 | // g_log<<Logger::Warning<<"Handle instances: "<<instances<<endl; |
16f7d28d | 880 | ++instances; |
12c86877 BH |
881 | } |
882 | ||
883 | UeberBackend::handle::~handle() | |
884 | { | |
16f7d28d | 885 | --instances; |
12c86877 BH |
886 | } |
887 | ||
346d1042 | 888 | bool 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 | } |