]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/recursordist/syncres.cc
acde0be1de4b134ffd1fa3cba9ab708f0f1068ca
[thirdparty/pdns.git] / pdns / recursordist / syncres.cc
1 /*
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 */
22 #ifdef HAVE_CONFIG_H
23 #include <utility>
24
25 #include "config.h"
26 #endif
27
28 #include "arguments.hh"
29 #include "aggressive_nsec.hh"
30 #include "cachecleaner.hh"
31 #include "dns_random.hh"
32 #include "dnsparser.hh"
33 #include "dnsrecords.hh"
34 #include "ednssubnet.hh"
35 #include "logger.hh"
36 #include "lua-recursor4.hh"
37 #include "rec-lua-conf.hh"
38 #include "syncres.hh"
39 #include "dnsseckeeper.hh"
40 #include "validate-recursor.hh"
41 #include "rec-taskqueue.hh"
42
43 rec::GlobalCounters g_Counters;
44 thread_local rec::TCounters t_Counters(g_Counters);
45
46 template <class T>
47 class fails_t : public boost::noncopyable
48 {
49 public:
50 using counter_t = uint64_t;
51 struct value_t
52 {
53 value_t(T arg) :
54 key(std::move(arg)) {}
55 T key;
56 mutable counter_t value{0};
57 time_t last{0};
58 };
59
60 using cont_t = multi_index_container<value_t,
61 indexed_by<
62 ordered_unique<tag<T>, member<value_t, T, &value_t::key>>,
63 ordered_non_unique<tag<time_t>, member<value_t, time_t, &value_t::last>>>>;
64
65 [[nodiscard]] cont_t getMapCopy() const
66 {
67 return d_cont;
68 }
69
70 [[nodiscard]] counter_t value(const T& arg) const
71 {
72 auto iter = d_cont.find(arg);
73
74 if (iter == d_cont.end()) {
75 return 0;
76 }
77 return iter->value;
78 }
79
80 counter_t incr(const T& key, const struct timeval& now)
81 {
82 auto iter = d_cont.insert(key).first;
83
84 if (iter->value < std::numeric_limits<counter_t>::max()) {
85 iter->value++;
86 }
87 auto& ind = d_cont.template get<T>();
88 time_t nowSecs = now.tv_sec;
89 ind.modify(iter, [nowSecs](value_t& val) { val.last = nowSecs; });
90 return iter->value;
91 }
92
93 void clear(const T& arg)
94 {
95 d_cont.erase(arg);
96 }
97
98 void clear()
99 {
100 d_cont.clear();
101 }
102
103 [[nodiscard]] size_t size() const
104 {
105 return d_cont.size();
106 }
107
108 void prune(time_t cutoff)
109 {
110 auto& ind = d_cont.template get<time_t>();
111 ind.erase(ind.begin(), ind.upper_bound(cutoff));
112 }
113
114 private:
115 cont_t d_cont;
116 };
117
118 /** Class that implements a decaying EWMA.
119 This class keeps an exponentially weighted moving average which, additionally, decays over time.
120 The decaying is only done on get.
121 */
122
123 //! This represents a number of decaying Ewmas, used to store performance per nameserver-name.
124 /** Modelled to work mostly like the underlying DecayingEwma */
125 class DecayingEwmaCollection
126 {
127 private:
128 struct DecayingEwma
129 {
130 public:
131 void submit(int arg, const struct timeval& last, const struct timeval& now)
132 {
133 d_last = arg;
134 auto val = static_cast<float>(arg);
135 if (d_val == 0) {
136 d_val = val;
137 }
138 else {
139 auto diff = makeFloat(last - now);
140 auto factor = expf(diff) / 2.0F; // might be '0.5', or 0.0001
141 d_val = (1.0F - factor) * val + factor * d_val;
142 }
143 }
144
145 float get(float factor)
146 {
147 return d_val *= factor;
148 }
149
150 [[nodiscard]] float peek() const
151 {
152 return d_val;
153 }
154
155 [[nodiscard]] int last() const
156 {
157 return d_last;
158 }
159
160 float d_val{0};
161 int d_last{0};
162 };
163
164 public:
165 DecayingEwmaCollection(DNSName name, const struct timeval val = {0, 0}) :
166 d_name(std::move(name)), d_lastget(val)
167 {
168 }
169
170 void submit(const ComboAddress& remote, int usecs, const struct timeval& now) const
171 {
172 d_collection[remote].submit(usecs, d_lastget, now);
173 }
174
175 float getFactor(const struct timeval& now) const
176 {
177 float diff = makeFloat(d_lastget - now);
178 return expf(diff / 60.0F); // is 1.0 or less
179 }
180
181 bool stale(time_t limit) const
182 {
183 return limit > d_lastget.tv_sec;
184 }
185
186 void purge(const std::map<ComboAddress, float>& keep) const
187 {
188 for (auto iter = d_collection.begin(); iter != d_collection.end();) {
189 if (keep.find(iter->first) != keep.end()) {
190 ++iter;
191 }
192 else {
193 iter = d_collection.erase(iter);
194 }
195 }
196 }
197
198 // d_collection is the modifyable part of the record, we index on DNSName and timeval, and DNSName never changes
199 mutable std::map<ComboAddress, DecayingEwma> d_collection;
200 DNSName d_name;
201 struct timeval d_lastget;
202 };
203
204 class nsspeeds_t : public multi_index_container<DecayingEwmaCollection,
205 indexed_by<
206 hashed_unique<tag<DNSName>, member<DecayingEwmaCollection, const DNSName, &DecayingEwmaCollection::d_name>>,
207 ordered_non_unique<tag<timeval>, member<DecayingEwmaCollection, timeval, &DecayingEwmaCollection::d_lastget>>>>
208 {
209 public:
210 const auto& find_or_enter(const DNSName& name, const struct timeval& now)
211 {
212 const auto iter = insert(DecayingEwmaCollection{name, now}).first;
213 return *iter;
214 }
215
216 const auto& find_or_enter(const DNSName& name)
217 {
218 const auto iter = insert(DecayingEwmaCollection{name}).first;
219 return *iter;
220 }
221
222 float fastest(const DNSName& name, const struct timeval& now)
223 {
224 auto& ind = get<DNSName>();
225 auto iter = insert(DecayingEwmaCollection{name, now}).first;
226 if (iter->d_collection.empty()) {
227 return 0;
228 }
229 // This could happen if find(DNSName) entered an entry; it's used only by test code
230 if (iter->d_lastget.tv_sec == 0 && iter->d_lastget.tv_usec == 0) {
231 ind.modify(iter, [&](DecayingEwmaCollection& dec) { dec.d_lastget = now; });
232 }
233
234 float ret = std::numeric_limits<float>::max();
235 const float factor = iter->getFactor(now);
236 for (auto& entry : iter->d_collection) {
237 if (float tmp = entry.second.get(factor); tmp < ret) {
238 ret = tmp;
239 }
240 }
241 ind.modify(iter, [&](DecayingEwmaCollection& dec) { dec.d_lastget = now; });
242 return ret;
243 }
244 };
245
246 static LockGuarded<nsspeeds_t> s_nsSpeeds;
247
248 template <class Thing>
249 class Throttle : public boost::noncopyable
250 {
251 public:
252 struct entry_t
253 {
254 entry_t(const Thing& thing_, time_t ttd_, unsigned int count_) :
255 thing(thing_), ttd(ttd_), count(count_)
256 {
257 }
258 Thing thing;
259 time_t ttd;
260 mutable unsigned int count;
261 };
262 using cont_t = multi_index_container<entry_t,
263 indexed_by<
264 ordered_unique<tag<Thing>, member<entry_t, Thing, &entry_t::thing>>,
265 ordered_non_unique<tag<time_t>, member<entry_t, time_t, &entry_t::ttd>>>>;
266
267 bool shouldThrottle(time_t now, const Thing& arg)
268 {
269 auto iter = d_cont.find(arg);
270 if (iter == d_cont.end()) {
271 return false;
272 }
273 if (now > iter->ttd || iter->count == 0) {
274 d_cont.erase(iter);
275 return false;
276 }
277 iter->count--;
278
279 return true; // still listed, still blocked
280 }
281
282 void throttle(time_t now, const Thing& arg, time_t ttl, unsigned int count)
283 {
284 auto iter = d_cont.find(arg);
285 time_t ttd = now + ttl;
286 if (iter == d_cont.end()) {
287 d_cont.emplace(arg, ttd, count);
288 }
289 else if (ttd > iter->ttd || count > iter->count) {
290 ttd = std::max(iter->ttd, ttd);
291 count = std::max(iter->count, count);
292 auto& ind = d_cont.template get<Thing>();
293 ind.modify(iter, [ttd, count](entry_t& entry) { entry.ttd = ttd; entry.count = count; });
294 }
295 }
296
297 [[nodiscard]] size_t size() const
298 {
299 return d_cont.size();
300 }
301
302 [[nodiscard]] cont_t getThrottleMap() const
303 {
304 return d_cont;
305 }
306
307 void clear()
308 {
309 d_cont.clear();
310 }
311
312 void clear(const Thing& thing)
313 {
314 d_cont.erase(thing);
315 }
316 void prune(time_t now)
317 {
318 auto& ind = d_cont.template get<time_t>();
319 ind.erase(ind.begin(), ind.upper_bound(now));
320 }
321
322 private:
323 cont_t d_cont;
324 };
325
326 static LockGuarded<Throttle<std::tuple<ComboAddress, DNSName, QType>>> s_throttle;
327
328 struct SavedParentEntry
329 {
330 SavedParentEntry(DNSName name, map<DNSName, vector<ComboAddress>>&& nsAddresses, time_t ttd) :
331 d_domain(std::move(name)), d_nsAddresses(std::move(nsAddresses)), d_ttd(ttd)
332 {
333 }
334 DNSName d_domain;
335 map<DNSName, vector<ComboAddress>> d_nsAddresses;
336 time_t d_ttd;
337 mutable uint64_t d_count{0};
338 };
339
340 using SavedParentNSSetBase = multi_index_container<
341 SavedParentEntry,
342 indexed_by<ordered_unique<tag<DNSName>, member<SavedParentEntry, DNSName, &SavedParentEntry::d_domain>>,
343 ordered_non_unique<tag<time_t>, member<SavedParentEntry, time_t, &SavedParentEntry::d_ttd>>>>;
344
345 class SavedParentNSSet : public SavedParentNSSetBase
346 {
347 public:
348 void prune(time_t now)
349 {
350 auto& ind = get<time_t>();
351 ind.erase(ind.begin(), ind.upper_bound(now));
352 }
353 void inc(const DNSName& name)
354 {
355 auto iter = find(name);
356 if (iter != end()) {
357 ++(*iter).d_count;
358 }
359 }
360 [[nodiscard]] SavedParentNSSet getMapCopy() const
361 {
362 return *this;
363 }
364 };
365
366 static LockGuarded<SavedParentNSSet> s_savedParentNSSet;
367
368 thread_local SyncRes::ThreadLocalStorage SyncRes::t_sstorage;
369 thread_local std::unique_ptr<addrringbuf_t> t_timeouts;
370
371 std::unique_ptr<NetmaskGroup> SyncRes::s_dontQuery{nullptr};
372 NetmaskGroup SyncRes::s_ednslocalsubnets;
373 NetmaskGroup SyncRes::s_ednsremotesubnets;
374 SuffixMatchNode SyncRes::s_ednsdomains;
375 EDNSSubnetOpts SyncRes::s_ecsScopeZero;
376 string SyncRes::s_serverID;
377 SyncRes::LogMode SyncRes::s_lm;
378 const std::unordered_set<QType> SyncRes::s_redirectionQTypes = {QType::CNAME, QType::DNAME};
379 static LockGuarded<fails_t<ComboAddress>> s_fails;
380 static LockGuarded<fails_t<DNSName>> s_nonresolving;
381
382 struct DoTStatus
383 {
384 DoTStatus(const ComboAddress& address, DNSName auth, time_t ttd) :
385 d_address(address), d_auth(std::move(auth)), d_ttd(ttd)
386 {
387 }
388 enum Status : uint8_t
389 {
390 Unknown,
391 Busy,
392 Bad,
393 Good
394 };
395 ComboAddress d_address;
396 DNSName d_auth;
397 time_t d_ttd;
398 mutable uint64_t d_count{0};
399 mutable Status d_status{Unknown};
400 std::string toString() const
401 {
402 const std::array<std::string, 4> names{"Unknown", "Busy", "Bad", "Good"};
403 auto val = static_cast<unsigned int>(d_status);
404 return val >= names.size() ? "?" : names.at(val);
405 }
406 };
407
408 struct DoTMap
409 {
410 multi_index_container<DoTStatus,
411 indexed_by<
412 ordered_unique<tag<ComboAddress>, member<DoTStatus, const ComboAddress, &DoTStatus::d_address>>,
413 ordered_non_unique<tag<time_t>, member<DoTStatus, time_t, &DoTStatus::d_ttd>>>>
414 d_map;
415 uint64_t d_numBusy{0};
416
417 void prune(time_t cutoff)
418 {
419 auto& ind = d_map.template get<time_t>();
420 ind.erase(ind.begin(), ind.upper_bound(cutoff));
421 }
422 };
423
424 static LockGuarded<DoTMap> s_dotMap;
425
426 static const time_t dotFailWait = static_cast<time_t>(24) * 3600;
427 static const time_t dotSuccessWait = static_cast<time_t>(3) * 24 * 3600;
428 static bool shouldDoDoT(ComboAddress address, time_t now);
429
430 unsigned int SyncRes::s_maxnegttl;
431 unsigned int SyncRes::s_maxbogusttl;
432 unsigned int SyncRes::s_maxcachettl;
433 unsigned int SyncRes::s_maxqperq;
434 unsigned int SyncRes::s_maxnsperresolve;
435 unsigned int SyncRes::s_maxnsaddressqperq;
436 unsigned int SyncRes::s_maxtotusec;
437 unsigned int SyncRes::s_maxdepth;
438 unsigned int SyncRes::s_minimumTTL;
439 unsigned int SyncRes::s_minimumECSTTL;
440 unsigned int SyncRes::s_packetcachettl;
441 unsigned int SyncRes::s_packetcacheservfailttl;
442 unsigned int SyncRes::s_packetcachenegativettl;
443 unsigned int SyncRes::s_serverdownmaxfails;
444 unsigned int SyncRes::s_serverdownthrottletime;
445 unsigned int SyncRes::s_unthrottle_n;
446 unsigned int SyncRes::s_nonresolvingnsmaxfails;
447 unsigned int SyncRes::s_nonresolvingnsthrottletime;
448 unsigned int SyncRes::s_ecscachelimitttl;
449 pdns::stat_t SyncRes::s_ecsqueries;
450 pdns::stat_t SyncRes::s_ecsresponses;
451 std::map<uint8_t, pdns::stat_t> SyncRes::s_ecsResponsesBySubnetSize4;
452 std::map<uint8_t, pdns::stat_t> SyncRes::s_ecsResponsesBySubnetSize6;
453
454 uint8_t SyncRes::s_ecsipv4limit;
455 uint8_t SyncRes::s_ecsipv6limit;
456 uint8_t SyncRes::s_ecsipv4cachelimit;
457 uint8_t SyncRes::s_ecsipv6cachelimit;
458 bool SyncRes::s_ecsipv4nevercache;
459 bool SyncRes::s_ecsipv6nevercache;
460
461 bool SyncRes::s_doIPv4;
462 bool SyncRes::s_doIPv6;
463 bool SyncRes::s_rootNXTrust;
464 bool SyncRes::s_noEDNS;
465 bool SyncRes::s_qnameminimization;
466 SyncRes::HardenNXD SyncRes::s_hardenNXD;
467 unsigned int SyncRes::s_refresh_ttlperc;
468 unsigned int SyncRes::s_locked_ttlperc;
469 int SyncRes::s_tcp_fast_open;
470 bool SyncRes::s_tcp_fast_open_connect;
471 bool SyncRes::s_dot_to_port_853;
472 int SyncRes::s_event_trace_enabled;
473 bool SyncRes::s_save_parent_ns_set;
474 unsigned int SyncRes::s_max_busy_dot_probes;
475 unsigned int SyncRes::s_max_CNAMES_followed = 10;
476 bool SyncRes::s_addExtendedResolutionDNSErrors;
477
478 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
479 #define LOG(x) \
480 if (d_lm == Log) { \
481 g_log << Logger::Warning << x; \
482 } \
483 else if (d_lm == Store) { \
484 addTraceTS(d_fixednow, d_trace); \
485 d_trace << x; \
486 }
487
488 OptLog SyncRes::LogObject(const string& prefix)
489 {
490 OptLog ret;
491 if (d_lm == Log) {
492 ret = {prefix, d_fixednow, g_log};
493 }
494 else if (d_lm == Store) {
495 ret = {prefix, d_fixednow, d_trace};
496 }
497 return ret;
498 }
499
500 static bool pushResolveIfNotInNegCache(const DNSName& qname, QType qtype, const struct timeval& now)
501 {
502 NegCache::NegCacheEntry negEntry;
503 bool inNegCache = g_negCache->get(qname, qtype, now, negEntry, false);
504 if (!inNegCache) {
505 // There are a few cases where an answer is neither stored in the record cache nor in the neg cache.
506 // An example is a SOA-less NODATA response. Rate limiting will kick in if those tasks are pushed too often.
507 // We might want to fix these cases (and always either store positive or negative) some day.
508 pushResolveTask(qname, qtype, now.tv_sec, now.tv_sec + 60, false);
509 }
510 return !inNegCache;
511 }
512
513 // A helper function to print a double with specific printf format.
514 // Not using boost::format since it is not thread safe while calling
515 // into locale handling code according to tsan.
516 // This allocates a string, but that's nothing compared to what
517 // boost::format is doing and may even be optimized away anyway.
518 static inline std::string fmtfloat(double value)
519 {
520 std::array<char, 20> buf{};
521 int ret = snprintf(buf.data(), buf.size(), "%0.2f", value);
522 if (ret < 0 || ret >= static_cast<int>(buf.size())) {
523 return "?";
524 }
525 return {buf.data(), static_cast<size_t>(ret)};
526 }
527
528 static inline void accountAuthLatency(uint64_t usec, int family)
529 {
530 if (family == AF_INET) {
531 t_Counters.at(rec::Histogram::auth4Answers)(usec);
532 t_Counters.at(rec::Histogram::cumulativeAuth4Answers)(usec);
533 }
534 else {
535 t_Counters.at(rec::Histogram::auth6Answers)(usec);
536 t_Counters.at(rec::Histogram::cumulativeAuth6Answers)(usec);
537 }
538 }
539
540 SyncRes::SyncRes(const struct timeval& now) :
541 d_authzonequeries(0), d_outqueries(0), d_tcpoutqueries(0), d_dotoutqueries(0), d_throttledqueries(0), d_timeouts(0), d_unreachables(0), d_totUsec(0), d_fixednow(now), d_now(now), d_cacheonly(false), d_doDNSSEC(false), d_doEDNS0(false), d_qNameMinimization(s_qnameminimization), d_lm(s_lm)
542
543 {
544 }
545
546 static void allowAdditionalEntry(std::unordered_set<DNSName>& allowedAdditionals, const DNSRecord& rec);
547
548 void SyncRes::resolveAdditionals(const DNSName& qname, QType qtype, AdditionalMode mode, std::vector<DNSRecord>& additionals, unsigned int depth, bool& additionalsNotInCache)
549 {
550 vector<DNSRecord> addRecords;
551
552 Context context;
553 switch (mode) {
554 case AdditionalMode::ResolveImmediately: {
555 set<GetBestNSAnswer> beenthere;
556 int res = doResolve(qname, qtype, addRecords, depth, beenthere, context);
557 if (res != 0) {
558 return;
559 }
560 // We're conservative here. We do not add Bogus records in any circumstance, we add Indeterminates only if no
561 // validation is required.
562 if (vStateIsBogus(context.state)) {
563 return;
564 }
565 if (shouldValidate() && context.state != vState::Secure && context.state != vState::Insecure) {
566 return;
567 }
568 for (auto& rec : addRecords) {
569 if (rec.d_place == DNSResourceRecord::ANSWER) {
570 additionals.push_back(std::move(rec));
571 }
572 }
573 break;
574 }
575 case AdditionalMode::CacheOnly:
576 case AdditionalMode::CacheOnlyRequireAuth: {
577 // Peek into cache
578 MemRecursorCache::Flags flags = mode == AdditionalMode::CacheOnlyRequireAuth ? MemRecursorCache::RequireAuth : MemRecursorCache::None;
579 if (g_recCache->get(d_now.tv_sec, qname, qtype, flags, &addRecords, d_cacheRemote, d_routingTag, nullptr, nullptr, nullptr, &context.state) <= 0) {
580 return;
581 }
582 // See the comment for the ResolveImmediately case
583 if (vStateIsBogus(context.state)) {
584 return;
585 }
586 if (shouldValidate() && context.state != vState::Secure && context.state != vState::Insecure) {
587 return;
588 }
589 for (auto& rec : addRecords) {
590 if (rec.d_place == DNSResourceRecord::ANSWER) {
591 rec.d_ttl -= d_now.tv_sec;
592 additionals.push_back(std::move(rec));
593 }
594 }
595 break;
596 }
597 case AdditionalMode::ResolveDeferred: {
598 const bool oldCacheOnly = setCacheOnly(true);
599 set<GetBestNSAnswer> beenthere;
600 int res = doResolve(qname, qtype, addRecords, depth, beenthere, context);
601 setCacheOnly(oldCacheOnly);
602 if (res == 0 && !addRecords.empty()) {
603 // We're conservative here. We do not add Bogus records in any circumstance, we add Indeterminates only if no
604 // validation is required.
605 if (vStateIsBogus(context.state)) {
606 return;
607 }
608 if (shouldValidate() && context.state != vState::Secure && context.state != vState::Insecure) {
609 return;
610 }
611 bool found = false;
612 for (auto& rec : addRecords) {
613 if (rec.d_place == DNSResourceRecord::ANSWER) {
614 found = true;
615 additionals.push_back(std::move(rec));
616 }
617 }
618 if (found) {
619 return;
620 }
621 }
622 // Not found in cache, check negcache and push task if also not in negcache
623 if (pushResolveIfNotInNegCache(qname, qtype, d_now)) {
624 additionalsNotInCache = true;
625 }
626 break;
627 }
628 case AdditionalMode::Ignore:
629 break;
630 }
631 }
632
633 // The main (recursive) function to add additionals
634 // qtype: the original query type to expand
635 // start: records to start from
636 // This function uses to state sets to avoid infinite recursion and allow depulication
637 // depth is the main recursion depth
638 // additionaldepth is the depth for addAdditionals itself
639 void SyncRes::addAdditionals(QType qtype, const vector<DNSRecord>& start, vector<DNSRecord>& additionals, std::set<std::pair<DNSName, QType>>& uniqueCalls, std::set<std::tuple<DNSName, QType, QType>>& uniqueResults, unsigned int depth, unsigned additionaldepth, bool& additionalsNotInCache)
640 {
641 if (additionaldepth >= 5 || start.empty()) {
642 return;
643 }
644
645 auto luaLocal = g_luaconfs.getLocal();
646 const auto iter = luaLocal->allowAdditionalQTypes.find(qtype);
647 if (iter == luaLocal->allowAdditionalQTypes.end()) {
648 return;
649 }
650 std::unordered_set<DNSName> addnames;
651 for (const auto& rec : start) {
652 if (rec.d_place == DNSResourceRecord::ANSWER) {
653 // currently, this function only knows about names, we could also take the target types that are dependent on
654 // record contents into account
655 // e.g. for NAPTR records, go only for SRV for flag value "s", or A/AAAA for flag value "a"
656 allowAdditionalEntry(addnames, rec);
657 }
658 }
659
660 // We maintain two sets for deduplication:
661 // - uniqueCalls makes sure we never resolve a qname/qtype twice
662 // - uniqueResults makes sure we never add the same qname/qytype RRSet to the result twice,
663 // but note that that set might contain multiple elements.
664
665 auto mode = iter->second.second;
666 for (const auto& targettype : iter->second.first) {
667 for (const auto& addname : addnames) {
668 std::vector<DNSRecord> records;
669 bool inserted = uniqueCalls.emplace(addname, targettype).second;
670 if (inserted) {
671 resolveAdditionals(addname, targettype, mode, records, depth, additionalsNotInCache);
672 }
673 if (!records.empty()) {
674 for (auto record = records.begin(); record != records.end();) {
675 QType covered = QType::ENT;
676 if (record->d_type == QType::RRSIG) {
677 if (auto rsig = getRR<RRSIGRecordContent>(*record); rsig != nullptr) {
678 covered = rsig->d_type;
679 }
680 }
681 if (uniqueResults.count(std::tuple(record->d_name, QType(record->d_type), covered)) > 0) {
682 // A bit expensive for vectors, but they are small
683 record = records.erase(record);
684 }
685 else {
686 ++record;
687 }
688 }
689 for (const auto& record : records) {
690 additionals.push_back(record);
691 QType covered = QType::ENT;
692 if (record.d_type == QType::RRSIG) {
693 if (auto rsig = getRR<RRSIGRecordContent>(record); rsig != nullptr) {
694 covered = rsig->d_type;
695 }
696 }
697 uniqueResults.emplace(record.d_name, record.d_type, covered);
698 }
699 addAdditionals(targettype, records, additionals, uniqueCalls, uniqueResults, depth, additionaldepth + 1, additionalsNotInCache);
700 }
701 }
702 }
703 }
704
705 // The entry point for other code
706 bool SyncRes::addAdditionals(QType qtype, vector<DNSRecord>& ret, unsigned int depth)
707 {
708 // The additional records of interest
709 std::vector<DNSRecord> additionals;
710
711 // We only call resolve for a specific name/type combo once
712 std::set<std::pair<DNSName, QType>> uniqueCalls;
713
714 // Collect multiple name/qtype from a single resolve but do not add a new set from new resolve calls
715 // For RRSIGs, the type covered is stored in the second Qtype
716 std::set<std::tuple<DNSName, QType, QType>> uniqueResults;
717
718 bool additionalsNotInCache = false;
719 addAdditionals(qtype, ret, additionals, uniqueCalls, uniqueResults, depth, 0, additionalsNotInCache);
720
721 for (auto& rec : additionals) {
722 rec.d_place = DNSResourceRecord::ADDITIONAL;
723 ret.push_back(std::move(rec));
724 }
725 return additionalsNotInCache;
726 }
727
728 /** everything begins here - this is the entry point just after receiving a packet */
729 int SyncRes::beginResolve(const DNSName& qname, const QType qtype, QClass qclass, vector<DNSRecord>& ret, unsigned int depth)
730 {
731 d_eventTrace.add(RecEventTrace::SyncRes);
732 t_Counters.at(rec::Counter::syncresqueries)++;
733 d_wasVariable = false;
734 d_wasOutOfBand = false;
735 d_cutStates.clear();
736
737 if (doSpecialNamesResolve(qname, qtype, qclass, ret)) {
738 d_queryValidationState = vState::Insecure; // this could fool our stats into thinking a validation took place
739 return 0; // so do check before updating counters (we do now)
740 }
741
742 if (isUnsupported(qtype)) {
743 return -1;
744 }
745
746 if (qclass == QClass::ANY) {
747 qclass = QClass::IN;
748 }
749 else if (qclass != QClass::IN) {
750 return -1;
751 }
752
753 if (qtype == QType::DS) {
754 d_externalDSQuery = qname;
755 }
756 else {
757 d_externalDSQuery.clear();
758 }
759
760 set<GetBestNSAnswer> beenthere;
761 Context context;
762 int res = doResolve(qname, qtype, ret, depth, beenthere, context);
763 d_queryValidationState = context.state;
764 d_extendedError = context.extendedError;
765
766 if (shouldValidate()) {
767 if (d_queryValidationState != vState::Indeterminate) {
768 t_Counters.at(rec::Counter::dnssecValidations)++;
769 }
770 auto xdnssec = g_xdnssec.getLocal();
771 if (xdnssec->check(qname)) {
772 increaseXDNSSECStateCounter(d_queryValidationState);
773 }
774 else {
775 increaseDNSSECStateCounter(d_queryValidationState);
776 }
777 }
778
779 // Avoid calling addAdditionals() if we know we won't find anything
780 auto luaLocal = g_luaconfs.getLocal();
781 if (res == 0 && qclass == QClass::IN && luaLocal->allowAdditionalQTypes.find(qtype) != luaLocal->allowAdditionalQTypes.end()) {
782 bool additionalsNotInCache = addAdditionals(qtype, ret, depth);
783 if (additionalsNotInCache) {
784 d_wasVariable = true;
785 }
786 }
787 d_eventTrace.add(RecEventTrace::SyncRes, res, false);
788 return res;
789 }
790
791 /*! Handles all special, built-in names
792 * Fills ret with an answer and returns true if it handled the query.
793 *
794 * Handles the following queries (and their ANY variants):
795 *
796 * - localhost. IN A
797 * - localhost. IN AAAA
798 * - 1.0.0.127.in-addr.arpa. IN PTR
799 * - 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa. IN PTR
800 * - version.bind. CH TXT
801 * - version.pdns. CH TXT
802 * - id.server. CH TXT
803 * - trustanchor.server CH TXT
804 * - negativetrustanchor.server CH TXT
805 */
806 bool SyncRes::doSpecialNamesResolve(const DNSName& qname, const QType qtype, const QClass qclass, vector<DNSRecord>& ret)
807 {
808 static const DNSName arpa("1.0.0.127.in-addr.arpa.");
809 static const DNSName ip6_arpa("1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.");
810 static const DNSName localhost("localhost.");
811 static const DNSName versionbind("version.bind.");
812 static const DNSName idserver("id.server.");
813 static const DNSName versionpdns("version.pdns.");
814 static const DNSName trustanchorserver("trustanchor.server.");
815 static const DNSName negativetrustanchorserver("negativetrustanchor.server.");
816
817 bool handled = false;
818 vector<pair<QType::typeenum, string>> answers;
819
820 if ((qname == arpa || qname == ip6_arpa) && qclass == QClass::IN) {
821 handled = true;
822 if (qtype == QType::PTR || qtype == QType::ANY) {
823 answers.emplace_back(QType::PTR, "localhost.");
824 }
825 }
826
827 if (qname.isPartOf(localhost) && qclass == QClass::IN) {
828 handled = true;
829 if (qtype == QType::A || qtype == QType::ANY) {
830 answers.emplace_back(QType::A, "127.0.0.1");
831 }
832 if (qtype == QType::AAAA || qtype == QType::ANY) {
833 answers.emplace_back(QType::AAAA, "::1");
834 }
835 }
836
837 if ((qname == versionbind || qname == idserver || qname == versionpdns) && qclass == QClass::CHAOS) {
838 handled = true;
839 if (qtype == QType::TXT || qtype == QType::ANY) {
840 if (qname == versionbind || qname == versionpdns) {
841 answers.emplace_back(QType::TXT, "\"" + ::arg()["version-string"] + "\"");
842 }
843 else if (s_serverID != "disabled") {
844 answers.emplace_back(QType::TXT, "\"" + s_serverID + "\"");
845 }
846 }
847 }
848
849 if (qname == trustanchorserver && qclass == QClass::CHAOS && ::arg().mustDo("allow-trust-anchor-query")) {
850 handled = true;
851 if (qtype == QType::TXT || qtype == QType::ANY) {
852 auto luaLocal = g_luaconfs.getLocal();
853 for (auto const& dsAnchor : luaLocal->dsAnchors) {
854 ostringstream ans;
855 ans << "\"";
856 ans << dsAnchor.first.toString(); // Explicit toString to have a trailing dot
857 for (auto const& dsRecord : dsAnchor.second) {
858 ans << " ";
859 ans << dsRecord.d_tag;
860 }
861 ans << "\"";
862 answers.emplace_back(QType::TXT, ans.str());
863 }
864 }
865 }
866
867 if (qname == negativetrustanchorserver && qclass == QClass::CHAOS && ::arg().mustDo("allow-trust-anchor-query")) {
868 handled = true;
869 if (qtype == QType::TXT || qtype == QType::ANY) {
870 auto luaLocal = g_luaconfs.getLocal();
871 for (auto const& negAnchor : luaLocal->negAnchors) {
872 ostringstream ans;
873 ans << "\"";
874 ans << negAnchor.first.toString(); // Explicit toString to have a trailing dot
875 if (negAnchor.second.length() != 0) {
876 ans << " " << negAnchor.second;
877 }
878 ans << "\"";
879 answers.emplace_back(QType::TXT, ans.str());
880 }
881 }
882 }
883
884 if (handled && !answers.empty()) {
885 ret.clear();
886 d_wasOutOfBand = true;
887
888 DNSRecord dnsRecord;
889 dnsRecord.d_name = qname;
890 dnsRecord.d_place = DNSResourceRecord::ANSWER;
891 dnsRecord.d_class = qclass;
892 dnsRecord.d_ttl = 86400;
893 for (const auto& ans : answers) {
894 dnsRecord.d_type = ans.first;
895 dnsRecord.setContent(DNSRecordContent::make(ans.first, qclass, ans.second));
896 ret.push_back(dnsRecord);
897 }
898 }
899
900 return handled;
901 }
902
903 //! This is the 'out of band resolver', in other words, the authoritative server
904 void SyncRes::AuthDomain::addSOA(std::vector<DNSRecord>& records) const
905 {
906 SyncRes::AuthDomain::records_t::const_iterator ziter = d_records.find(std::tuple(getName(), QType::SOA));
907 if (ziter != d_records.end()) {
908 DNSRecord dnsRecord = *ziter;
909 dnsRecord.d_place = DNSResourceRecord::AUTHORITY;
910 records.push_back(dnsRecord);
911 }
912 }
913
914 bool SyncRes::AuthDomain::operator==(const AuthDomain& rhs) const
915 {
916 return d_records == rhs.d_records
917 && d_servers == rhs.d_servers
918 && d_name == rhs.d_name
919 && d_rdForward == rhs.d_rdForward;
920 }
921
922 [[nodiscard]] std::string SyncRes::AuthDomain::print(const std::string& indent,
923 const std::string& indentLevel) const
924 {
925 std::stringstream outputsStream;
926 outputsStream << indent << "DNSName = " << d_name << std::endl;
927 outputsStream << indent << "rdForward = " << d_rdForward << std::endl;
928 outputsStream << indent << "Records {" << std::endl;
929 auto recordContentIndentation = indent;
930 recordContentIndentation += indentLevel;
931 recordContentIndentation += indentLevel;
932 for (const auto& record : d_records) {
933 outputsStream << indent << indentLevel << "Record `" << record.d_name << "` {" << std::endl;
934 outputsStream << record.print(recordContentIndentation);
935 outputsStream << indent << indentLevel << "}" << std::endl;
936 }
937 outputsStream << indent << "}" << std::endl;
938 outputsStream << indent << "Servers {" << std::endl;
939 for (const auto& server : d_servers) {
940 outputsStream << indent << indentLevel << server.toString() << std::endl;
941 }
942 outputsStream << indent << "}" << std::endl;
943 return outputsStream.str();
944 }
945
946 int SyncRes::AuthDomain::getRecords(const DNSName& qname, const QType qtype, std::vector<DNSRecord>& records) const
947 {
948 int result = RCode::NoError;
949 records.clear();
950
951 // partial lookup
952 std::pair<records_t::const_iterator, records_t::const_iterator> range = d_records.equal_range(std::tie(qname));
953
954 SyncRes::AuthDomain::records_t::const_iterator ziter;
955 bool somedata = false;
956
957 for (ziter = range.first; ziter != range.second; ++ziter) {
958 somedata = true;
959
960 if (qtype == QType::ANY || ziter->d_type == qtype || ziter->d_type == QType::CNAME) {
961 // let rest of nameserver do the legwork on this one
962 records.push_back(*ziter);
963 }
964 else if (ziter->d_type == QType::NS && ziter->d_name.countLabels() > getName().countLabels()) {
965 // we hit a delegation point!
966 DNSRecord dnsRecord = *ziter;
967 dnsRecord.d_place = DNSResourceRecord::AUTHORITY;
968 records.push_back(dnsRecord);
969 }
970 }
971
972 if (!records.empty()) {
973 /* We have found an exact match, we're done */
974 return result;
975 }
976
977 if (somedata) {
978 /* We have records for that name, but not of the wanted qtype */
979 addSOA(records);
980
981 return result;
982 }
983
984 DNSName wcarddomain(qname);
985 while (wcarddomain != getName() && wcarddomain.chopOff()) {
986 range = d_records.equal_range(std::tuple(g_wildcarddnsname + wcarddomain));
987 if (range.first == range.second) {
988 continue;
989 }
990 for (ziter = range.first; ziter != range.second; ++ziter) {
991 DNSRecord dnsRecord = *ziter;
992 // if we hit a CNAME, just answer that - rest of recursor will do the needful & follow
993 if (dnsRecord.d_type == qtype || qtype == QType::ANY || dnsRecord.d_type == QType::CNAME) {
994 dnsRecord.d_name = qname;
995 dnsRecord.d_place = DNSResourceRecord::ANSWER;
996 records.push_back(dnsRecord);
997 }
998 }
999
1000 if (records.empty()) {
1001 addSOA(records);
1002 }
1003
1004 return result;
1005 }
1006
1007 /* Nothing for this name, no wildcard, let's see if there is some NS */
1008 DNSName nsdomain(qname);
1009 while (nsdomain.chopOff() && nsdomain != getName()) {
1010 range = d_records.equal_range(std::tuple(nsdomain, QType::NS));
1011 if (range.first == range.second) {
1012 continue;
1013 }
1014 for (ziter = range.first; ziter != range.second; ++ziter) {
1015 DNSRecord dnsRecord = *ziter;
1016 dnsRecord.d_place = DNSResourceRecord::AUTHORITY;
1017 records.push_back(dnsRecord);
1018 }
1019 }
1020
1021 if (records.empty()) {
1022 addSOA(records);
1023 result = RCode::NXDomain;
1024 }
1025
1026 return result;
1027 }
1028
1029 bool SyncRes::doOOBResolve(const AuthDomain& domain, const DNSName& qname, const QType qtype, vector<DNSRecord>& ret, int& res)
1030 {
1031 d_authzonequeries++;
1032 t_Counters.at(rec::Counter::authzonequeries)++;
1033
1034 res = domain.getRecords(qname, qtype, ret);
1035 return true;
1036 }
1037
1038 bool SyncRes::doOOBResolve(const DNSName& qname, const QType qtype, vector<DNSRecord>& ret, unsigned int /* depth */, const string& prefix, int& res)
1039 {
1040 DNSName authdomain(qname);
1041 const auto iter = getBestAuthZone(&authdomain);
1042 if (iter == t_sstorage.domainmap->end() || !iter->second.isAuth()) {
1043 LOG(prefix << qname << ": Auth storage has no zone for this query!" << endl);
1044 return false;
1045 }
1046
1047 LOG(prefix << qname << ": Auth storage has data, zone='" << authdomain << "'" << endl);
1048 return doOOBResolve(iter->second, qname, qtype, ret, res);
1049 }
1050
1051 bool SyncRes::isRecursiveForwardOrAuth(const DNSName& qname)
1052 {
1053 DNSName authname(qname);
1054 const auto iter = getBestAuthZone(&authname);
1055 return iter != t_sstorage.domainmap->end() && (iter->second.isAuth() || iter->second.shouldRecurse());
1056 }
1057
1058 bool SyncRes::isForwardOrAuth(const DNSName& qname)
1059 {
1060 DNSName authname(qname);
1061 const auto iter = getBestAuthZone(&authname);
1062 return iter != t_sstorage.domainmap->end();
1063 }
1064
1065 const char* isoDateTimeMillis(const struct timeval& tval, timebuf_t& buf)
1066 {
1067 const std::string s_timestampFormat = "%Y-%m-%dT%T";
1068 struct tm tmval
1069 {
1070 };
1071 size_t len = strftime(buf.data(), buf.size(), s_timestampFormat.c_str(), localtime_r(&tval.tv_sec, &tmval));
1072 if (len == 0) {
1073 int ret = snprintf(buf.data(), buf.size(), "%lld", static_cast<long long>(tval.tv_sec));
1074 if (ret < 0 || static_cast<size_t>(ret) >= buf.size()) {
1075 buf[0] = '\0';
1076 return buf.data();
1077 }
1078 len = ret;
1079 }
1080
1081 if (buf.size() > len + 4) {
1082 snprintf(&buf.at(len), buf.size() - len, ".%03ld", static_cast<long>(tval.tv_usec) / 1000);
1083 }
1084 return buf.data();
1085 }
1086
1087 static const char* timestamp(time_t arg, timebuf_t& buf)
1088 {
1089 const std::string s_timestampFormat = "%Y-%m-%dT%T";
1090 struct tm tmval
1091 {
1092 };
1093 size_t len = strftime(buf.data(), buf.size(), s_timestampFormat.c_str(), localtime_r(&arg, &tmval));
1094 if (len == 0) {
1095 int ret = snprintf(buf.data(), buf.size(), "%lld", static_cast<long long>(arg));
1096 if (ret < 0 || static_cast<size_t>(ret) >= buf.size()) {
1097 buf[0] = '\0';
1098 }
1099 }
1100 return buf.data();
1101 }
1102
1103 struct ednsstatus_t : public multi_index_container<SyncRes::EDNSStatus,
1104 indexed_by<
1105 ordered_unique<tag<ComboAddress>, member<SyncRes::EDNSStatus, ComboAddress, &SyncRes::EDNSStatus::address>>,
1106 ordered_non_unique<tag<time_t>, member<SyncRes::EDNSStatus, time_t, &SyncRes::EDNSStatus::ttd>>>>
1107 {
1108 // Get a copy
1109 [[nodiscard]] ednsstatus_t getMap() const
1110 {
1111 return *this;
1112 }
1113
1114 static void setMode(index<ComboAddress>::type& ind, iterator iter, SyncRes::EDNSStatus::EDNSMode mode, time_t theTime)
1115 {
1116 if (iter->mode != mode || iter->ttd == 0) {
1117 ind.modify(iter, [=](SyncRes::EDNSStatus& status) { status.mode = mode; status.ttd = theTime + Expire; });
1118 }
1119 }
1120
1121 void prune(time_t now)
1122 {
1123 auto& ind = get<time_t>();
1124 ind.erase(ind.begin(), ind.upper_bound(now));
1125 }
1126
1127 static const time_t Expire = 7200;
1128 };
1129
1130 static LockGuarded<ednsstatus_t> s_ednsstatus;
1131
1132 SyncRes::EDNSStatus::EDNSMode SyncRes::getEDNSStatus(const ComboAddress& server)
1133 {
1134 auto lock = s_ednsstatus.lock();
1135 const auto& iter = lock->find(server);
1136 if (iter == lock->end()) {
1137 return EDNSStatus::EDNSOK;
1138 }
1139 return iter->mode;
1140 }
1141
1142 uint64_t SyncRes::getEDNSStatusesSize()
1143 {
1144 return s_ednsstatus.lock()->size();
1145 }
1146
1147 void SyncRes::clearEDNSStatuses()
1148 {
1149 s_ednsstatus.lock()->clear();
1150 }
1151
1152 void SyncRes::pruneEDNSStatuses(time_t cutoff)
1153 {
1154 s_ednsstatus.lock()->prune(cutoff);
1155 }
1156
1157 uint64_t SyncRes::doEDNSDump(int fileDesc)
1158 {
1159 int newfd = dup(fileDesc);
1160 if (newfd == -1) {
1161 return 0;
1162 }
1163 auto filePtr = std::unique_ptr<FILE, int (*)(FILE*)>(fdopen(newfd, "w"), fclose);
1164 if (!filePtr) {
1165 close(newfd);
1166 return 0;
1167 }
1168 uint64_t count = 0;
1169
1170 fprintf(filePtr.get(), "; edns dump follows\n; ip\tstatus\tttd\n");
1171 const auto copy = s_ednsstatus.lock()->getMap();
1172 for (const auto& eds : copy) {
1173 count++;
1174 timebuf_t tmp;
1175 fprintf(filePtr.get(), "%s\t%s\t%s\n", eds.address.toString().c_str(), eds.toString().c_str(), timestamp(eds.ttd, tmp));
1176 }
1177 return count;
1178 }
1179
1180 void SyncRes::pruneNSSpeeds(time_t limit)
1181 {
1182 auto lock = s_nsSpeeds.lock();
1183 auto& ind = lock->get<timeval>();
1184 ind.erase(ind.begin(), ind.upper_bound(timeval{limit, 0}));
1185 }
1186
1187 uint64_t SyncRes::getNSSpeedsSize()
1188 {
1189 return s_nsSpeeds.lock()->size();
1190 }
1191
1192 void SyncRes::submitNSSpeed(const DNSName& server, const ComboAddress& address, int usec, const struct timeval& now)
1193 {
1194 auto lock = s_nsSpeeds.lock();
1195 lock->find_or_enter(server, now).submit(address, usec, now);
1196 }
1197
1198 void SyncRes::clearNSSpeeds()
1199 {
1200 s_nsSpeeds.lock()->clear();
1201 }
1202
1203 float SyncRes::getNSSpeed(const DNSName& server, const ComboAddress& address)
1204 {
1205 auto lock = s_nsSpeeds.lock();
1206 return lock->find_or_enter(server).d_collection[address].peek();
1207 }
1208
1209 uint64_t SyncRes::doDumpNSSpeeds(int fileDesc)
1210 {
1211 int newfd = dup(fileDesc);
1212 if (newfd == -1) {
1213 return 0;
1214 }
1215 auto filePtr = std::unique_ptr<FILE, int (*)(FILE*)>(fdopen(newfd, "w"), fclose);
1216 if (!filePtr) {
1217 close(newfd);
1218 return 0;
1219 }
1220
1221 fprintf(filePtr.get(), "; nsspeed dump follows\n; nsname\ttimestamp\t[ip/decaying-ms/last-ms...]\n");
1222 uint64_t count = 0;
1223
1224 // Create a copy to avoid holding the lock while doing I/O
1225 for (const auto& iter : *s_nsSpeeds.lock()) {
1226 count++;
1227
1228 // an <empty> can appear hear in case of authoritative (hosted) zones
1229 timebuf_t tmp;
1230 fprintf(filePtr.get(), "%s\t%s\t", iter.d_name.toLogString().c_str(), isoDateTimeMillis(iter.d_lastget, tmp));
1231 bool first = true;
1232 for (const auto& line : iter.d_collection) {
1233 fprintf(filePtr.get(), "%s%s/%.3f/%.3f", first ? "" : "\t", line.first.toStringWithPortExcept(53).c_str(), line.second.peek() / 1000.0F, static_cast<float>(line.second.last()) / 1000.0F);
1234 first = false;
1235 }
1236 fprintf(filePtr.get(), "\n");
1237 }
1238 return count;
1239 }
1240
1241 uint64_t SyncRes::getThrottledServersSize()
1242 {
1243 return s_throttle.lock()->size();
1244 }
1245
1246 void SyncRes::pruneThrottledServers(time_t now)
1247 {
1248 s_throttle.lock()->prune(now);
1249 }
1250
1251 void SyncRes::clearThrottle()
1252 {
1253 s_throttle.lock()->clear();
1254 }
1255
1256 bool SyncRes::isThrottled(time_t now, const ComboAddress& server, const DNSName& target, QType qtype)
1257 {
1258 return s_throttle.lock()->shouldThrottle(now, std::tuple(server, target, qtype));
1259 }
1260
1261 bool SyncRes::isThrottled(time_t now, const ComboAddress& server)
1262 {
1263 auto throttled = s_throttle.lock()->shouldThrottle(now, std::tuple(server, g_rootdnsname, 0));
1264 if (throttled) {
1265 // Give fully throttled servers a chance to be used, to avoid having one bad zone spoil the NS
1266 // record for others using the same NS. If the NS answers, it will be unThrottled immediately
1267 if (s_unthrottle_n > 0 && dns_random(s_unthrottle_n) == 0) {
1268 throttled = false;
1269 }
1270 }
1271 return throttled;
1272 }
1273
1274 void SyncRes::unThrottle(const ComboAddress& server, const DNSName& name, QType qtype)
1275 {
1276 s_throttle.lock()->clear(std::tuple(server, g_rootdnsname, 0));
1277 s_throttle.lock()->clear(std::tuple(server, name, qtype));
1278 }
1279
1280 void SyncRes::doThrottle(time_t now, const ComboAddress& server, time_t duration, unsigned int tries)
1281 {
1282 s_throttle.lock()->throttle(now, std::tuple(server, g_rootdnsname, 0), duration, tries);
1283 }
1284
1285 void SyncRes::doThrottle(time_t now, const ComboAddress& server, const DNSName& name, QType qtype, time_t duration, unsigned int tries)
1286 {
1287 s_throttle.lock()->throttle(now, std::tuple(server, name, qtype), duration, tries);
1288 }
1289
1290 uint64_t SyncRes::doDumpThrottleMap(int fileDesc)
1291 {
1292 int newfd = dup(fileDesc);
1293 if (newfd == -1) {
1294 return 0;
1295 }
1296 auto filePtr = std::unique_ptr<FILE, int (*)(FILE*)>(fdopen(newfd, "w"), fclose);
1297 if (!filePtr) {
1298 close(newfd);
1299 return 0;
1300 }
1301 fprintf(filePtr.get(), "; throttle map dump follows\n");
1302 fprintf(filePtr.get(), "; remote IP\tqname\tqtype\tcount\tttd\n");
1303 uint64_t count = 0;
1304
1305 // Get a copy to avoid holding the lock while doing I/O
1306 const auto throttleMap = s_throttle.lock()->getThrottleMap();
1307 for (const auto& iter : throttleMap) {
1308 count++;
1309 timebuf_t tmp;
1310 // remote IP, dns name, qtype, count, ttd
1311 fprintf(filePtr.get(), "%s\t%s\t%s\t%u\t%s\n", std::get<0>(iter.thing).toString().c_str(), std::get<1>(iter.thing).toLogString().c_str(), std::get<2>(iter.thing).toString().c_str(), iter.count, timestamp(iter.ttd, tmp));
1312 }
1313
1314 return count;
1315 }
1316
1317 uint64_t SyncRes::getFailedServersSize()
1318 {
1319 return s_fails.lock()->size();
1320 }
1321
1322 void SyncRes::clearFailedServers()
1323 {
1324 s_fails.lock()->clear();
1325 }
1326
1327 void SyncRes::pruneFailedServers(time_t cutoff)
1328 {
1329 s_fails.lock()->prune(cutoff);
1330 }
1331
1332 unsigned long SyncRes::getServerFailsCount(const ComboAddress& server)
1333 {
1334 return s_fails.lock()->value(server);
1335 }
1336
1337 uint64_t SyncRes::doDumpFailedServers(int fileDesc)
1338 {
1339 int newfd = dup(fileDesc);
1340 if (newfd == -1) {
1341 return 0;
1342 }
1343 auto filePtr = std::unique_ptr<FILE, int (*)(FILE*)>(fdopen(newfd, "w"), fclose);
1344 if (!filePtr) {
1345 close(newfd);
1346 return 0;
1347 }
1348 fprintf(filePtr.get(), "; failed servers dump follows\n");
1349 fprintf(filePtr.get(), "; remote IP\tcount\ttimestamp\n");
1350 uint64_t count = 0;
1351
1352 // We get a copy, so the I/O does not need to happen while holding the lock
1353 for (const auto& iter : s_fails.lock()->getMapCopy()) {
1354 count++;
1355 timebuf_t tmp;
1356 fprintf(filePtr.get(), "%s\t%" PRIu64 "\t%s\n", iter.key.toString().c_str(), iter.value, timestamp(iter.last, tmp));
1357 }
1358
1359 return count;
1360 }
1361
1362 uint64_t SyncRes::getNonResolvingNSSize()
1363 {
1364 return s_nonresolving.lock()->size();
1365 }
1366
1367 void SyncRes::clearNonResolvingNS()
1368 {
1369 s_nonresolving.lock()->clear();
1370 }
1371
1372 void SyncRes::pruneNonResolving(time_t cutoff)
1373 {
1374 s_nonresolving.lock()->prune(cutoff);
1375 }
1376
1377 uint64_t SyncRes::doDumpNonResolvingNS(int fileDesc)
1378 {
1379 int newfd = dup(fileDesc);
1380 if (newfd == -1) {
1381 return 0;
1382 }
1383 auto filePtr = std::unique_ptr<FILE, int (*)(FILE*)>(fdopen(newfd, "w"), fclose);
1384 if (!filePtr) {
1385 close(newfd);
1386 return 0;
1387 }
1388 fprintf(filePtr.get(), "; non-resolving nameserver dump follows\n");
1389 fprintf(filePtr.get(), "; name\tcount\ttimestamp\n");
1390 uint64_t count = 0;
1391
1392 // We get a copy, so the I/O does not need to happen while holding the lock
1393 for (const auto& iter : s_nonresolving.lock()->getMapCopy()) {
1394 count++;
1395 timebuf_t tmp;
1396 fprintf(filePtr.get(), "%s\t%" PRIu64 "\t%s\n", iter.key.toString().c_str(), iter.value, timestamp(iter.last, tmp));
1397 }
1398
1399 return count;
1400 }
1401
1402 void SyncRes::clearSaveParentsNSSets()
1403 {
1404 s_savedParentNSSet.lock()->clear();
1405 }
1406
1407 size_t SyncRes::getSaveParentsNSSetsSize()
1408 {
1409 return s_savedParentNSSet.lock()->size();
1410 }
1411
1412 void SyncRes::pruneSaveParentsNSSets(time_t now)
1413 {
1414 s_savedParentNSSet.lock()->prune(now);
1415 }
1416
1417 uint64_t SyncRes::doDumpSavedParentNSSets(int fileDesc)
1418 {
1419 int newfd = dup(fileDesc);
1420 if (newfd == -1) {
1421 return 0;
1422 }
1423 auto filePtr = std::unique_ptr<FILE, int (*)(FILE*)>(fdopen(newfd, "w"), fclose);
1424 if (!filePtr) {
1425 close(newfd);
1426 return 0;
1427 }
1428 fprintf(filePtr.get(), "; dump of saved parent nameserver sets succesfully used follows\n");
1429 fprintf(filePtr.get(), "; total entries: %zu\n", s_savedParentNSSet.lock()->size());
1430 fprintf(filePtr.get(), "; domain\tsuccess\tttd\n");
1431 uint64_t count = 0;
1432
1433 // We get a copy, so the I/O does not need to happen while holding the lock
1434 for (const auto& iter : s_savedParentNSSet.lock()->getMapCopy()) {
1435 if (iter.d_count == 0) {
1436 continue;
1437 }
1438 count++;
1439 timebuf_t tmp;
1440 fprintf(filePtr.get(), "%s\t%" PRIu64 "\t%s\n", iter.d_domain.toString().c_str(), iter.d_count, timestamp(iter.d_ttd, tmp));
1441 }
1442 return count;
1443 }
1444
1445 void SyncRes::pruneDoTProbeMap(time_t cutoff)
1446 {
1447 auto lock = s_dotMap.lock();
1448 auto& ind = lock->d_map.get<time_t>();
1449
1450 for (auto i = ind.begin(); i != ind.end();) {
1451 if (i->d_ttd >= cutoff) {
1452 // We're done as we loop ordered by d_ttd
1453 break;
1454 }
1455 if (i->d_status == DoTStatus::Status::Busy) {
1456 lock->d_numBusy--;
1457 }
1458 i = ind.erase(i);
1459 }
1460 }
1461
1462 uint64_t SyncRes::doDumpDoTProbeMap(int fileDesc)
1463 {
1464 int newfd = dup(fileDesc);
1465 if (newfd == -1) {
1466 return 0;
1467 }
1468 auto filePtr = std::unique_ptr<FILE, int (*)(FILE*)>(fdopen(newfd, "w"), fclose);
1469 if (!filePtr) {
1470 close(newfd);
1471 return 0;
1472 }
1473 fprintf(filePtr.get(), "; DoT probing map follows\n");
1474 fprintf(filePtr.get(), "; ip\tdomain\tcount\tstatus\tttd\n");
1475 uint64_t count = 0;
1476
1477 // We get a copy, so the I/O does not need to happen while holding the lock
1478 DoTMap copy;
1479 {
1480 copy = *s_dotMap.lock();
1481 }
1482 fprintf(filePtr.get(), "; %" PRIu64 " Busy entries\n", copy.d_numBusy);
1483 for (const auto& iter : copy.d_map) {
1484 count++;
1485 timebuf_t tmp;
1486 fprintf(filePtr.get(), "%s\t%s\t%" PRIu64 "\t%s\t%s\n", iter.d_address.toString().c_str(), iter.d_auth.toString().c_str(), iter.d_count, iter.toString().c_str(), timestamp(iter.d_ttd, tmp));
1487 }
1488 return count;
1489 }
1490
1491 /* so here is the story. First we complete the full resolution process for a domain name. And only THEN do we decide
1492 to also do DNSSEC validation, which leads to new queries. To make this simple, we *always* ask for DNSSEC records
1493 so that if there are RRSIGs for a name, we'll have them.
1494
1495 However, some hosts simply can't answer questions which ask for DNSSEC. This can manifest itself as:
1496 * No answer
1497 * FormErr
1498 * Nonsense answer
1499
1500 The cause of "No answer" may be fragmentation, and it is tempting to probe if smaller answers would get through.
1501 Another cause of "No answer" may simply be a network condition.
1502 Nonsense answers are a clearer indication this host won't be able to do DNSSEC evah.
1503
1504 Previous implementations have suffered from turning off DNSSEC questions for an authoritative server based on timeouts.
1505 A clever idea is to only turn off DNSSEC if we know a domain isn't signed anyhow. The problem with that really
1506 clever idea however is that at this point in PowerDNS, we may simply not know that yet. All the DNSSEC thinking happens
1507 elsewhere. It may not have happened yet.
1508
1509 For now this means we can't be clever, but will turn off DNSSEC if you reply with FormError or gibberish.
1510 */
1511
1512 LWResult::Result SyncRes::asyncresolveWrapper(const ComboAddress& address, bool ednsMANDATORY, const DNSName& domain, [[maybe_unused]] const DNSName& auth, int type, bool doTCP, bool sendRDQuery, struct timeval* now, boost::optional<Netmask>& srcmask, LWResult* res, bool* chained, const DNSName& nsName) const
1513 {
1514 /* what is your QUEST?
1515 the goal is to get as many remotes as possible on the best level of EDNS support
1516 The levels are:
1517
1518 1) EDNSOK: Honors EDNS0, absent from table
1519 2) EDNSIGNORANT: Ignores EDNS0, gives replies without EDNS0
1520 3) NOEDNS: Generates FORMERR on EDNS queries
1521
1522 Everybody starts out assumed to be EDNSOK.
1523 If EDNSOK, send out EDNS0
1524 If you FORMERR us, go to NOEDNS,
1525 If no EDNS in response, go to EDNSIGNORANT
1526 If EDNSIGNORANT, keep on including EDNS0, see what happens
1527 Same behaviour as EDNSOK
1528 If NOEDNS, send bare queries
1529 */
1530
1531 // Read current status, defaulting to OK
1532 SyncRes::EDNSStatus::EDNSMode mode = EDNSStatus::EDNSOK;
1533 {
1534 auto lock = s_ednsstatus.lock();
1535 auto ednsstatus = lock->find(address); // does this include port? YES
1536 if (ednsstatus != lock->end()) {
1537 if (ednsstatus->ttd != 0 && ednsstatus->ttd < d_now.tv_sec) {
1538 lock->erase(ednsstatus);
1539 }
1540 else {
1541 mode = ednsstatus->mode;
1542 }
1543 }
1544 }
1545
1546 int EDNSLevel = 0;
1547 auto luaconfsLocal = g_luaconfs.getLocal();
1548 ResolveContext ctx(d_initialRequestId, nsName);
1549 #ifdef HAVE_FSTRM
1550 ctx.d_auth = auth;
1551 #endif
1552
1553 LWResult::Result ret{};
1554
1555 for (int tries = 0; tries < 2; ++tries) {
1556
1557 if (mode == EDNSStatus::NOEDNS) {
1558 t_Counters.at(rec::Counter::noEdnsOutQueries)++;
1559 EDNSLevel = 0; // level != mode
1560 }
1561 else if (ednsMANDATORY || mode != EDNSStatus::NOEDNS) {
1562 EDNSLevel = 1;
1563 }
1564
1565 DNSName sendQname(domain);
1566 if (g_lowercaseOutgoing) {
1567 sendQname.makeUsLowerCase();
1568 }
1569
1570 if (d_asyncResolve) {
1571 ret = d_asyncResolve(address, sendQname, type, doTCP, sendRDQuery, EDNSLevel, now, srcmask, ctx, res, chained);
1572 }
1573 else {
1574 ret = asyncresolve(address, sendQname, type, doTCP, sendRDQuery, EDNSLevel, now, srcmask, ctx, d_outgoingProtobufServers, d_frameStreamServers, luaconfsLocal->outgoingProtobufExportConfig.exportTypes, res, chained);
1575 }
1576
1577 if (ret == LWResult::Result::PermanentError || ret == LWResult::Result::OSLimitError || ret == LWResult::Result::Spoofed) {
1578 break; // transport error, nothing to learn here
1579 }
1580
1581 if (ret == LWResult::Result::Timeout) { // timeout, not doing anything with it now
1582 break;
1583 }
1584
1585 if (EDNSLevel == 1) {
1586 // We sent out with EDNS
1587 // ret is LWResult::Result::Success
1588 // ednsstatus in table might be pruned or changed by another request/thread, so do a new lookup/insert if needed
1589 auto lock = s_ednsstatus.lock(); // all three branches below need a lock
1590
1591 // Determine new mode
1592 if (res->d_validpacket && !res->d_haveEDNS && res->d_rcode == RCode::FormErr) {
1593 mode = EDNSStatus::NOEDNS;
1594 auto ednsstatus = lock->insert(address).first;
1595 auto& ind = lock->get<ComboAddress>();
1596 lock->setMode(ind, ednsstatus, mode, d_now.tv_sec);
1597 // This is the only path that re-iterates the loop
1598 continue;
1599 }
1600 if (!res->d_haveEDNS) {
1601 auto ednsstatus = lock->insert(address).first;
1602 auto& ind = lock->get<ComboAddress>();
1603 lock->setMode(ind, ednsstatus, EDNSStatus::EDNSIGNORANT, d_now.tv_sec);
1604 }
1605 else {
1606 // New status is EDNSOK
1607 lock->erase(address);
1608 }
1609 }
1610
1611 break;
1612 }
1613 return ret;
1614 }
1615
1616 /* The parameters from rfc9156. */
1617 /* maximum number of QNAME minimization iterations */
1618 unsigned int SyncRes::s_max_minimize_count; // default is 10
1619 /* number of iterations that should only have one label appended */
1620 unsigned int SyncRes::s_minimize_one_label; // default is 4
1621
1622 static unsigned int qmStepLen(unsigned int labels, unsigned int qnamelen, unsigned int qmIteration)
1623 {
1624 unsigned int step{};
1625
1626 if (qmIteration < SyncRes::s_minimize_one_label) {
1627 step = 1;
1628 }
1629 else if (qmIteration < SyncRes::s_max_minimize_count) {
1630 step = std::max(1U, (qnamelen - labels) / (SyncRes::s_max_minimize_count - qmIteration));
1631 }
1632 else {
1633 step = qnamelen - labels;
1634 }
1635 unsigned int targetlen = std::min(labels + step, qnamelen);
1636 return targetlen;
1637 }
1638
1639 static string resToString(int res)
1640 {
1641 return res >= 0 ? RCode::to_s(res) : std::to_string(res);
1642 }
1643
1644 int SyncRes::doResolve(const DNSName& qname, const QType qtype, vector<DNSRecord>& ret, unsigned int depth, set<GetBestNSAnswer>& beenthere, Context& context) // NOLINT(readability-function-cognitive-complexity)
1645 {
1646 auto prefix = getPrefix(depth);
1647 auto luaconfsLocal = g_luaconfs.getLocal();
1648
1649 /* Apply qname (including CNAME chain) filtering policies */
1650 if (d_wantsRPZ && !d_appliedPolicy.wasHit()) {
1651 if (luaconfsLocal->dfe.getQueryPolicy(qname, d_discardedPolicies, d_appliedPolicy)) {
1652 mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());
1653 bool done = false;
1654 int rcode = RCode::NoError;
1655 handlePolicyHit(prefix, qname, qtype, ret, done, rcode, depth);
1656 if (done) {
1657 return rcode;
1658 }
1659 }
1660 }
1661
1662 initZoneCutsFromTA(qname, prefix);
1663
1664 // In the auth or recursive forward case, it does not make sense to do qname-minimization
1665 if (!getQNameMinimization() || isRecursiveForwardOrAuth(qname)) {
1666 return doResolveNoQNameMinimization(qname, qtype, ret, depth, beenthere, context);
1667 }
1668
1669 // The qname minimization algorithm is a simplified version of the one in RFC 7816 (bis).
1670 // It could be simplified because the cache maintenance (both positive and negative)
1671 // is already done by doResolveNoQNameMinimization().
1672 //
1673 // Sketch of algorithm:
1674 // Check cache
1675 // If result found: done
1676 // Otherwise determine closes ancestor from cache data
1677 // Repeat querying A, adding more labels of the original qname
1678 // If we get a delegation continue at ancestor determination
1679 // Until we have the full name.
1680 //
1681 // The algorithm starts with adding a single label per iteration, and
1682 // moves to three labels per iteration after three iterations.
1683
1684 DNSName child;
1685 prefix.append(string("QM "));
1686
1687 LOG(prefix << qname << ": doResolve" << endl);
1688
1689 // Look in cache only
1690 vector<DNSRecord> retq;
1691 bool old = setCacheOnly(true);
1692 bool fromCache = false;
1693 // For cache peeking, we tell doResolveNoQNameMinimization not to consider the (non-recursive) forward case.
1694 // Otherwise all queries in a forward domain will be forwarded, while we want to consult the cache.
1695 int res = doResolveNoQNameMinimization(qname, qtype, retq, depth, beenthere, context, &fromCache, nullptr);
1696 setCacheOnly(old);
1697 if (fromCache) {
1698 LOG(prefix << qname << ": Step0 Found in cache" << endl);
1699 if (d_appliedPolicy.d_type != DNSFilterEngine::PolicyType::None && (d_appliedPolicy.d_kind == DNSFilterEngine::PolicyKind::NXDOMAIN || d_appliedPolicy.d_kind == DNSFilterEngine::PolicyKind::NODATA)) {
1700 ret.clear();
1701 }
1702 ret.insert(ret.end(), retq.begin(), retq.end());
1703
1704 return res;
1705 }
1706 LOG(prefix << qname << ": Step0 Not cached" << endl);
1707
1708 const unsigned int qnamelen = qname.countLabels();
1709
1710 DNSName fwdomain(qname);
1711 const bool forwarded = getBestAuthZone(&fwdomain) != t_sstorage.domainmap->end();
1712 if (forwarded) {
1713 LOG(prefix << qname << ": Step0 qname is in a forwarded domain " << fwdomain << endl);
1714 }
1715
1716 for (unsigned int i = 0; i <= qnamelen; i++) {
1717
1718 // Step 1
1719 vector<DNSRecord> bestns;
1720 DNSName nsdomain(qname);
1721 if (qtype == QType::DS) {
1722 nsdomain.chopOff();
1723 }
1724 // the two retries allow getBestNSFromCache&co to reprime the root
1725 // hints, in case they ever go missing
1726 for (int tries = 0; tries < 2 && bestns.empty(); ++tries) {
1727 bool flawedNSSet = false;
1728 set<GetBestNSAnswer> beenthereIgnored;
1729 getBestNSFromCache(nsdomain, qtype, bestns, &flawedNSSet, depth, prefix, beenthereIgnored, boost::make_optional(forwarded, fwdomain));
1730 if (forwarded) {
1731 break;
1732 }
1733 }
1734
1735 if (bestns.empty()) {
1736 if (!forwarded) {
1737 // Something terrible is wrong
1738 LOG(prefix << qname << ": Step1 No ancestor found return ServFail" << endl);
1739 return RCode::ServFail;
1740 }
1741 child = fwdomain;
1742 }
1743 else {
1744 LOG(prefix << qname << ": Step1 Ancestor from cache is " << bestns[0].d_name << endl);
1745 if (forwarded) {
1746 child = bestns[0].d_name.isPartOf(fwdomain) ? bestns[0].d_name : fwdomain;
1747 LOG(prefix << qname << ": Step1 Final Ancestor (using forwarding info) is " << child << endl);
1748 }
1749 else {
1750 child = bestns[0].d_name;
1751 }
1752 }
1753 for (; i <= qnamelen; i++) {
1754 // Step 2
1755 unsigned int labels = child.countLabels();
1756 unsigned int targetlen = qmStepLen(labels, qnamelen, i);
1757
1758 while (labels < targetlen) {
1759 child.prependRawLabel(qname.getRawLabel(qnamelen - labels - 1));
1760 labels++;
1761 }
1762 // rfc9156 section-2.3, append labels if they start with an underscore
1763 while (labels < qnamelen) {
1764 auto prependLabel = qname.getRawLabel(qnamelen - labels - 1);
1765 if (prependLabel.at(0) != '_') {
1766 break;
1767 }
1768 child.prependRawLabel(prependLabel);
1769 labels++;
1770 }
1771
1772 LOG(prefix << qname << ": Step2 New child " << child << endl);
1773
1774 // Step 3 resolve
1775 if (child == qname) {
1776 LOG(prefix << qname << ": Step3 Going to do final resolve" << endl);
1777 res = doResolveNoQNameMinimization(qname, qtype, ret, depth, beenthere, context);
1778 LOG(prefix << qname << ": Step3 Final resolve: " << resToString(res) << "/" << ret.size() << endl);
1779 return res;
1780 }
1781
1782 // If we have seen this child during resolution already; we tried to QM it already or otherwise broken.
1783 // fall back to no-QM
1784 bool qmLoopDetected = false;
1785 for (const auto& visitedNS : beenthere) {
1786 if (visitedNS.qname == child) {
1787 qmLoopDetected = true;
1788 break;
1789 }
1790 }
1791 if (qmLoopDetected) {
1792 LOG(prefix << qname << ": Step4 loop detected as visited this child name already, fallback to no QM" << endl);
1793 res = doResolveNoQNameMinimization(qname, qtype, ret, depth, beenthere, context);
1794 LOG(prefix << qname << ": Step4 Final resolve: " << resToString(res) << "/" << ret.size() << endl);
1795 return res;
1796 }
1797
1798 // Step 4
1799 LOG(prefix << qname << ": Step4 Resolve A for child " << child << endl);
1800 bool oldFollowCNAME = d_followCNAME;
1801 d_followCNAME = false;
1802 retq.resize(0);
1803 StopAtDelegation stopAtDelegation = Stop;
1804 res = doResolveNoQNameMinimization(child, QType::A, retq, depth, beenthere, context, nullptr, &stopAtDelegation);
1805 d_followCNAME = oldFollowCNAME;
1806 LOG(prefix << qname << ": Step4 Resolve " << child << "|A result is " << RCode::to_s(res) << "/" << retq.size() << "/" << stopAtDelegation << endl);
1807 if (stopAtDelegation == Stopped) {
1808 LOG(prefix << qname << ": Delegation seen, continue at step 1" << endl);
1809 break;
1810 }
1811
1812 if (res != RCode::NoError) {
1813 // Case 5: unexpected answer
1814 LOG(prefix << qname << ": Step5: other rcode, last effort final resolve" << endl);
1815 setQNameMinimization(false);
1816 setQMFallbackMode(true);
1817
1818 auto oldEDE = context.extendedError;
1819 res = doResolveNoQNameMinimization(qname, qtype, ret, depth + 1, beenthere, context);
1820
1821 if (res == RCode::NoError) {
1822 t_Counters.at(rec::Counter::qnameminfallbacksuccess)++;
1823 }
1824 else {
1825 // as doResolveNoQNameMinimization clears the EDE, we put it back here, it is relevant but might not be set by the last effort attempt
1826 if (!context.extendedError) {
1827 context.extendedError = std::move(oldEDE);
1828 }
1829 }
1830
1831 LOG(prefix << qname << ": Step5 End resolve: " << resToString(res) << "/" << ret.size() << endl);
1832 return res;
1833 }
1834 }
1835 }
1836
1837 // Should not be reached
1838 LOG(prefix << qname << ": Max iterations reached, return ServFail" << endl);
1839 return RCode::ServFail;
1840 }
1841
1842 unsigned int SyncRes::getAdjustedRecursionBound() const
1843 {
1844 auto bound = s_maxdepth; // 40 is default value of s_maxdepth
1845 if (getQMFallbackMode()) {
1846 // We might have hit a depth level check, but we still want to allow some recursion levels in the fallback
1847 // no-qname-minimization case. This has the effect that a qname minimization fallback case might reach 150% of
1848 // maxdepth, taking care to not repeatedly increase the bound.
1849 bound += s_maxdepth / 2;
1850 }
1851 return bound;
1852 }
1853
1854 /*! This function will check the cache and go out to the internet if the answer is not in cache
1855 *
1856 * \param qname The name we need an answer for
1857 * \param qtype
1858 * \param ret The vector of DNSRecords we need to fill with the answers
1859 * \param depth The recursion depth we are in
1860 * \param beenthere
1861 * \param fromCache tells the caller the result came from the cache, may be nullptr
1862 * \param stopAtDelegation if non-nullptr and pointed-to value is Stop requests the callee to stop at a delegation, if so pointed-to value is set to Stopped
1863 * \return DNS RCODE or -1 (Error)
1864 */
1865 int SyncRes::doResolveNoQNameMinimization(const DNSName& qname, const QType qtype, vector<DNSRecord>& ret, unsigned int depth, set<GetBestNSAnswer>& beenthere, Context& context, bool* fromCache, StopAtDelegation* stopAtDelegation) // NOLINT(readability-function-cognitive-complexity)
1866 {
1867 context.extendedError.reset();
1868 auto prefix = getPrefix(depth);
1869
1870 LOG(prefix << qname << ": Wants " << (d_doDNSSEC ? "" : "NO ") << "DNSSEC processing, " << (d_requireAuthData ? "" : "NO ") << "auth data required by query for " << qtype << endl);
1871
1872 d_maxdepth = std::max(d_maxdepth, depth);
1873 if (s_maxdepth > 0) {
1874 auto bound = getAdjustedRecursionBound();
1875 // Use a stricter bound if throttling
1876 if (depth > bound || (d_outqueries > 10 && d_throttledqueries > 5 && depth > bound * 2 / 3)) {
1877 string msg = "More than " + std::to_string(bound) + " (adjusted max-recursion-depth) levels of recursion needed while resolving " + qname.toLogString();
1878 LOG(prefix << qname << ": " << msg << endl);
1879 throw ImmediateServFailException(std::move(msg));
1880 }
1881 }
1882
1883 int res = 0;
1884
1885 const int iterations = !d_refresh && MemRecursorCache::s_maxServedStaleExtensions > 0 ? 2 : 1;
1886 for (int loop = 0; loop < iterations; loop++) {
1887
1888 d_serveStale = loop == 1;
1889 if (d_serveStale) {
1890 LOG(prefix << qname << ": Restart, with serve-stale enabled" << endl);
1891 }
1892 // This is a difficult way of expressing "this is a normal query", i.e. not getRootNS.
1893 if (!d_updatingRootNS || qtype.getCode() != QType::NS || !qname.isRoot()) {
1894 DNSName authname(qname);
1895 const auto iter = getBestAuthZone(&authname);
1896
1897 if (d_cacheonly) {
1898 if (iter != t_sstorage.domainmap->end()) {
1899 if (iter->second.isAuth()) {
1900 LOG(prefix << qname << ": Cache only lookup for '" << qname << "|" << qtype << "', in auth zone" << endl);
1901 ret.clear();
1902 d_wasOutOfBand = doOOBResolve(qname, qtype, ret, depth, prefix, res);
1903 if (fromCache != nullptr) {
1904 *fromCache = d_wasOutOfBand;
1905 }
1906 return res;
1907 }
1908 }
1909 }
1910
1911 bool wasForwardedOrAuthZone = false;
1912 bool wasAuthZone = false;
1913 bool wasForwardRecurse = false;
1914
1915 if (iter != t_sstorage.domainmap->end()) {
1916 wasForwardedOrAuthZone = true;
1917
1918 if (iter->second.isAuth()) {
1919 wasAuthZone = true;
1920 }
1921 else if (iter->second.shouldRecurse()) {
1922 wasForwardRecurse = true;
1923 }
1924 }
1925
1926 /* When we are looking for a DS, we want to the non-CNAME cache check first
1927 because we can actually have a DS (from the parent zone) AND a CNAME (from
1928 the child zone), and what we really want is the DS */
1929 if (qtype != QType::DS && doCNAMECacheCheck(qname, qtype, ret, depth, prefix, res, context, wasAuthZone, wasForwardRecurse, loop == 1)) { // will reroute us if needed
1930 d_wasOutOfBand = wasAuthZone;
1931 // Here we have an issue. If we were prevented from going out to the network (cache-only was set, possibly because we
1932 // are in QM Step0) we might have a CNAME but not the corresponding target.
1933 // It means that we will sometimes go to the next steps when we are in fact done, but that's fine since
1934 // we will get the records from the cache, resulting in a small overhead.
1935 // This might be a real problem if we had a RPZ hit, though, because we do not want the processing to continue, since
1936 // RPZ rules will not be evaluated anymore (we already matched).
1937 const bool stoppedByPolicyHit = d_appliedPolicy.wasHit();
1938
1939 if (fromCache != nullptr && (!d_cacheonly || stoppedByPolicyHit)) {
1940 *fromCache = true;
1941 }
1942 /* Apply Post filtering policies */
1943
1944 if (d_wantsRPZ && !stoppedByPolicyHit) {
1945 auto luaLocal = g_luaconfs.getLocal();
1946 if (luaLocal->dfe.getPostPolicy(ret, d_discardedPolicies, d_appliedPolicy)) {
1947 mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());
1948 bool done = false;
1949 handlePolicyHit(prefix, qname, qtype, ret, done, res, depth);
1950 if (done && fromCache != nullptr) {
1951 *fromCache = true;
1952 }
1953 }
1954 }
1955 return res;
1956 }
1957
1958 if (doCacheCheck(qname, authname, wasForwardedOrAuthZone, wasAuthZone, wasForwardRecurse, qtype, ret, depth, prefix, res, context)) {
1959 // we done
1960 d_wasOutOfBand = wasAuthZone;
1961 if (fromCache != nullptr) {
1962 *fromCache = true;
1963 }
1964
1965 if (d_wantsRPZ && !d_appliedPolicy.wasHit()) {
1966 auto luaLocal = g_luaconfs.getLocal();
1967 if (luaLocal->dfe.getPostPolicy(ret, d_discardedPolicies, d_appliedPolicy)) {
1968 mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());
1969 bool done = false;
1970 handlePolicyHit(prefix, qname, qtype, ret, done, res, depth);
1971 }
1972 }
1973
1974 return res;
1975 }
1976
1977 /* if we have not found a cached DS (or denial of), now is the time to look for a CNAME */
1978 if (qtype == QType::DS && doCNAMECacheCheck(qname, qtype, ret, depth, prefix, res, context, wasAuthZone, wasForwardRecurse, loop == 1)) { // will reroute us if needed
1979 d_wasOutOfBand = wasAuthZone;
1980 // Here we have an issue. If we were prevented from going out to the network (cache-only was set, possibly because we
1981 // are in QM Step0) we might have a CNAME but not the corresponding target.
1982 // It means that we will sometimes go to the next steps when we are in fact done, but that's fine since
1983 // we will get the records from the cache, resulting in a small overhead.
1984 // This might be a real problem if we had a RPZ hit, though, because we do not want the processing to continue, since
1985 // RPZ rules will not be evaluated anymore (we already matched).
1986 const bool stoppedByPolicyHit = d_appliedPolicy.wasHit();
1987
1988 if (fromCache != nullptr && (!d_cacheonly || stoppedByPolicyHit)) {
1989 *fromCache = true;
1990 }
1991 /* Apply Post filtering policies */
1992
1993 if (d_wantsRPZ && !stoppedByPolicyHit) {
1994 auto luaLocal = g_luaconfs.getLocal();
1995 if (luaLocal->dfe.getPostPolicy(ret, d_discardedPolicies, d_appliedPolicy)) {
1996 mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());
1997 bool done = false;
1998 handlePolicyHit(prefix, qname, qtype, ret, done, res, depth);
1999 if (done && fromCache != nullptr) {
2000 *fromCache = true;
2001 }
2002 }
2003 }
2004
2005 return res;
2006 }
2007 }
2008
2009 if (d_cacheonly) {
2010 return 0;
2011 }
2012
2013 // When trying to serve-stale, we also only look at the cache. Don't look at d_serveStale, it
2014 // might be changed by recursive calls (this should be fixed in a better way!).
2015 if (loop == 1) {
2016 return res;
2017 }
2018
2019 LOG(prefix << qname << ": No cache hit for '" << qname << "|" << qtype << "', trying to find an appropriate NS record" << endl);
2020
2021 DNSName subdomain(qname);
2022 if (qtype == QType::DS) {
2023 subdomain.chopOff();
2024 }
2025
2026 NsSet nsset;
2027 bool flawedNSSet = false;
2028
2029 // the two retries allow getBestNSNamesFromCache&co to reprime the root
2030 // hints, in case they ever go missing
2031 for (int tries = 0; tries < 2 && nsset.empty(); ++tries) {
2032 subdomain = getBestNSNamesFromCache(subdomain, qtype, nsset, &flawedNSSet, depth, prefix, beenthere); // pass beenthere to both occasions
2033 }
2034
2035 res = doResolveAt(nsset, subdomain, flawedNSSet, qname, qtype, ret, depth, prefix, beenthere, context, stopAtDelegation, nullptr);
2036
2037 if (res == -1 && s_save_parent_ns_set) {
2038 // It did not work out, lets check if we have a saved parent NS set
2039 map<DNSName, vector<ComboAddress>> fallBack;
2040 {
2041 auto lock = s_savedParentNSSet.lock();
2042 auto domainData = lock->find(subdomain);
2043 if (domainData != lock->end() && !domainData->d_nsAddresses.empty()) {
2044 nsset.clear();
2045 // Build the nsset arg and fallBack data for the fallback doResolveAt() attempt
2046 // Take a copy to be able to release the lock, NsSet is actually a map, go figure
2047 for (const auto& nsAddress : domainData->d_nsAddresses) {
2048 nsset.emplace(nsAddress.first, pair(std::vector<ComboAddress>(), false));
2049 fallBack.emplace(nsAddress.first, nsAddress.second);
2050 }
2051 }
2052 }
2053 if (!fallBack.empty()) {
2054 LOG(prefix << qname << ": Failure, but we have a saved parent NS set, trying that one" << endl);
2055 res = doResolveAt(nsset, subdomain, flawedNSSet, qname, qtype, ret, depth, prefix, beenthere, context, stopAtDelegation, &fallBack);
2056 if (res == 0) {
2057 // It did work out
2058 s_savedParentNSSet.lock()->inc(subdomain);
2059 }
2060 }
2061 }
2062 /* Apply Post filtering policies */
2063 if (d_wantsRPZ && !d_appliedPolicy.wasHit()) {
2064 auto luaLocal = g_luaconfs.getLocal();
2065 if (luaLocal->dfe.getPostPolicy(ret, d_discardedPolicies, d_appliedPolicy)) {
2066 mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());
2067 bool done = false;
2068 handlePolicyHit(prefix, qname, qtype, ret, done, res, depth);
2069 }
2070 }
2071
2072 if (res == 0) {
2073 return 0;
2074 }
2075
2076 LOG(prefix << qname << ": Failed (res=" << res << ")" << endl);
2077 if (res >= 0) {
2078 break;
2079 }
2080 }
2081 return res < 0 ? RCode::ServFail : res;
2082 }
2083
2084 #if 0
2085 // for testing purposes
2086 static bool ipv6First(const ComboAddress& a, const ComboAddress& b)
2087 {
2088 return !(a.sin4.sin_family < a.sin4.sin_family);
2089 }
2090 #endif
2091
2092 struct speedOrderCA
2093 {
2094 speedOrderCA(std::map<ComboAddress, float>& speeds) :
2095 d_speeds(speeds) {}
2096 bool operator()(const ComboAddress& lhs, const ComboAddress& rhs) const
2097 {
2098 return d_speeds[lhs] < d_speeds[rhs];
2099 }
2100 std::map<ComboAddress, float>& d_speeds; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members): nothing wrong afaiks
2101 };
2102
2103 void SyncRes::selectNSOnSpeed(const DNSName& qname, const string& prefix, vector<ComboAddress>& ret)
2104 {
2105 /* we need to remove from the nsSpeeds collection the existing IPs
2106 for this nameserver that are no longer in the set, even if there
2107 is only one or none at all in the current set.
2108 */
2109 map<ComboAddress, float> speeds;
2110 {
2111 auto lock = s_nsSpeeds.lock();
2112 const auto& collection = lock->find_or_enter(qname, d_now);
2113 float factor = collection.getFactor(d_now);
2114 for (const auto& val : ret) {
2115 speeds[val] = collection.d_collection[val].get(factor);
2116 }
2117 collection.purge(speeds);
2118 }
2119
2120 if (ret.size() > 1) {
2121 shuffle(ret.begin(), ret.end(), pdns::dns_random_engine());
2122 speedOrderCA speedOrder(speeds);
2123 stable_sort(ret.begin(), ret.end(), speedOrder);
2124 }
2125
2126 if (doLog()) {
2127 LOG(prefix << qname << ": Nameserver " << qname << " IPs: ");
2128 bool first = true;
2129 for (const auto& addr : ret) {
2130 if (first) {
2131 first = false;
2132 }
2133 else {
2134 LOG(", ");
2135 }
2136 LOG((addr.toString()) << "(" << fmtfloat(speeds[addr] / 1000.0) << "ms)");
2137 }
2138 LOG(endl);
2139 }
2140 }
2141
2142 template <typename T>
2143 static bool collectAddresses(const vector<DNSRecord>& cset, vector<ComboAddress>& ret)
2144 {
2145 bool pushed = false;
2146 for (const auto& record : cset) {
2147 if (auto rec = getRR<T>(record)) {
2148 ret.push_back(rec->getCA(53));
2149 pushed = true;
2150 }
2151 }
2152 return pushed;
2153 }
2154
2155 /** This function explicitly goes out for A or AAAA addresses
2156 */
2157 vector<ComboAddress> SyncRes::getAddrs(const DNSName& qname, unsigned int depth, const string& prefix, set<GetBestNSAnswer>& beenthere, bool cacheOnly, unsigned int& addressQueriesForNS)
2158 {
2159 typedef vector<DNSRecord> res_t;
2160 typedef vector<ComboAddress> ret_t;
2161 ret_t ret;
2162
2163 bool oldCacheOnly = setCacheOnly(cacheOnly);
2164 bool oldRequireAuthData = d_requireAuthData;
2165 bool oldValidationRequested = d_DNSSECValidationRequested;
2166 bool oldFollowCNAME = d_followCNAME;
2167 bool seenV6 = false;
2168 const unsigned int startqueries = d_outqueries;
2169 d_requireAuthData = false;
2170 d_DNSSECValidationRequested = false;
2171 d_followCNAME = false;
2172
2173 MemRecursorCache::Flags flags = MemRecursorCache::None;
2174 if (d_serveStale) {
2175 flags |= MemRecursorCache::ServeStale;
2176 }
2177 try {
2178 // First look for both A and AAAA in the cache
2179 res_t cset;
2180 if (s_doIPv4 && g_recCache->get(d_now.tv_sec, qname, QType::A, flags, &cset, d_cacheRemote, d_routingTag) > 0) {
2181 collectAddresses<ARecordContent>(cset, ret);
2182 }
2183 if (s_doIPv6 && g_recCache->get(d_now.tv_sec, qname, QType::AAAA, flags, &cset, d_cacheRemote, d_routingTag) > 0) {
2184 if (collectAddresses<AAAARecordContent>(cset, ret)) {
2185 seenV6 = true;
2186 }
2187 }
2188 if (ret.empty()) {
2189 // Neither A nor AAAA in the cache...
2190 Context newContext1;
2191 cset.clear();
2192 // Go out to get A's
2193 if (s_doIPv4 && doResolveNoQNameMinimization(qname, QType::A, cset, depth + 1, beenthere, newContext1) == 0) { // this consults cache, OR goes out
2194 collectAddresses<ARecordContent>(cset, ret);
2195 }
2196 if (s_doIPv6) { // s_doIPv6 **IMPLIES** pdns::isQueryLocalAddressFamilyEnabled(AF_INET6) returned true
2197 if (ret.empty()) {
2198 // We only go out immediately to find IPv6 records if we did not find any IPv4 ones.
2199 Context newContext2;
2200 if (doResolveNoQNameMinimization(qname, QType::AAAA, cset, depth + 1, beenthere, newContext2) == 0) { // this consults cache, OR goes out
2201 if (collectAddresses<AAAARecordContent>(cset, ret)) {
2202 seenV6 = true;
2203 }
2204 }
2205 }
2206 else {
2207 // We have some IPv4 records, consult the cache, we might have encountered some IPv6 glue
2208 cset.clear();
2209 if (g_recCache->get(d_now.tv_sec, qname, QType::AAAA, flags, &cset, d_cacheRemote, d_routingTag) > 0) {
2210 if (collectAddresses<AAAARecordContent>(cset, ret)) {
2211 seenV6 = true;
2212 }
2213 }
2214 }
2215 }
2216 }
2217 if (s_doIPv6 && !seenV6 && !cacheOnly) {
2218 // No IPv6 records in cache, check negcache and submit async task if negache does not have the data
2219 // so that the next time the cache or the negcache will have data
2220 pushResolveIfNotInNegCache(qname, QType::AAAA, d_now);
2221 }
2222 }
2223 catch (const PolicyHitException&) {
2224 // We ignore a policy hit while trying to retrieve the addresses
2225 // of a NS and keep processing the current query
2226 }
2227
2228 if (ret.empty() && d_outqueries > startqueries) {
2229 // We did 1 or more outgoing queries to resolve this NS name but returned empty handed
2230 addressQueriesForNS++;
2231 }
2232 d_requireAuthData = oldRequireAuthData;
2233 d_DNSSECValidationRequested = oldValidationRequested;
2234 setCacheOnly(oldCacheOnly);
2235 d_followCNAME = oldFollowCNAME;
2236
2237 if (s_max_busy_dot_probes > 0 && s_dot_to_port_853) {
2238 for (auto& add : ret) {
2239 if (shouldDoDoT(add, d_now.tv_sec)) {
2240 add.setPort(853);
2241 }
2242 }
2243 }
2244 selectNSOnSpeed(qname, prefix, ret);
2245 return ret;
2246 }
2247
2248 void SyncRes::getBestNSFromCache(const DNSName& qname, const QType qtype, vector<DNSRecord>& bestns, bool* flawedNSSet, unsigned int depth, const string& prefix, set<GetBestNSAnswer>& beenthere, const boost::optional<DNSName>& cutOffDomain) // NOLINT(readability-function-cognitive-complexity)
2249 {
2250 DNSName subdomain(qname);
2251 bestns.clear();
2252 bool brokeloop = false;
2253 MemRecursorCache::Flags flags = MemRecursorCache::None;
2254 if (d_serveStale) {
2255 flags |= MemRecursorCache::ServeStale;
2256 }
2257 do {
2258 if (cutOffDomain && (subdomain == *cutOffDomain || !subdomain.isPartOf(*cutOffDomain))) {
2259 break;
2260 }
2261 brokeloop = false;
2262 LOG(prefix << qname << ": Checking if we have NS in cache for '" << subdomain << "'" << endl);
2263 vector<DNSRecord> nsVector;
2264 *flawedNSSet = false;
2265
2266 if (bool isAuth = false; g_recCache->get(d_now.tv_sec, subdomain, QType::NS, flags, &nsVector, d_cacheRemote, d_routingTag, nullptr, nullptr, nullptr, nullptr, &isAuth) > 0) {
2267 if (s_maxnsperresolve > 0 && nsVector.size() > s_maxnsperresolve) {
2268 vector<DNSRecord> selected;
2269 selected.reserve(s_maxnsperresolve);
2270 std::sample(nsVector.cbegin(), nsVector.cend(), std::back_inserter(selected), s_maxnsperresolve, pdns::dns_random_engine());
2271 nsVector = std::move(selected);
2272 }
2273 bestns.reserve(nsVector.size());
2274
2275 vector<DNSName> missing;
2276 for (const auto& nsRecord : nsVector) {
2277 if (nsRecord.d_ttl > (unsigned int)d_now.tv_sec) {
2278 vector<DNSRecord> aset;
2279 QType nsqt{QType::ADDR};
2280 if (s_doIPv4 && !s_doIPv6) {
2281 nsqt = QType::A;
2282 }
2283 else if (!s_doIPv4 && s_doIPv6) {
2284 nsqt = QType::AAAA;
2285 }
2286
2287 auto nrr = getRR<NSRecordContent>(nsRecord);
2288 if (nrr && (!nrr->getNS().isPartOf(subdomain) || g_recCache->get(d_now.tv_sec, nrr->getNS(), nsqt, flags, doLog() ? &aset : nullptr, d_cacheRemote, d_routingTag) > 0)) {
2289 bestns.push_back(nsRecord);
2290 LOG(prefix << qname << ": NS (with ip, or non-glue) in cache for '" << subdomain << "' -> '" << nrr->getNS() << "'");
2291 LOG(", within bailiwick: " << nrr->getNS().isPartOf(subdomain));
2292 if (!aset.empty()) {
2293 LOG(", in cache, ttl=" << (unsigned int)(((time_t)aset.begin()->d_ttl - d_now.tv_sec)) << endl);
2294 }
2295 else {
2296 LOG(", not in cache / did not look at cache" << endl);
2297 }
2298 }
2299 else if (nrr != nullptr) {
2300 *flawedNSSet = true;
2301 LOG(prefix << qname << ": NS in cache for '" << subdomain << "', but needs glue (" << nrr->getNS() << ") which we miss or is expired" << endl);
2302 missing.emplace_back(nrr->getNS());
2303 }
2304 }
2305 }
2306 if (*flawedNSSet && bestns.empty() && isAuth) {
2307 // The authoritative (child) NS records did not produce any usable addresses, wipe them, so
2308 // these useless records do not prevent parent records to be inserted into the cache
2309 LOG(prefix << qname << ": Wiping flawed authoritative NS records for " << subdomain << endl);
2310 g_recCache->doWipeCache(subdomain, false, QType::NS);
2311 }
2312 if (!missing.empty() && missing.size() < nsVector.size()) {
2313 // We miss glue, but we have a chance to resolve it, since we do have address(es) for at least one NS
2314 for (const auto& name : missing) {
2315 if (s_doIPv4 && pushResolveIfNotInNegCache(name, QType::A, d_now)) {
2316 LOG(prefix << qname << ": A glue for " << subdomain << " NS " << name << " missing, pushed task to resolve" << endl);
2317 }
2318 if (s_doIPv6 && pushResolveIfNotInNegCache(name, QType::AAAA, d_now)) {
2319 LOG(prefix << qname << ": AAAA glue for " << subdomain << " NS " << name << " missing, pushed task to resolve" << endl);
2320 }
2321 }
2322 }
2323
2324 if (!bestns.empty()) {
2325 GetBestNSAnswer answer;
2326 answer.qname = qname;
2327 answer.qtype = qtype.getCode();
2328 for (const auto& bestNSRecord : bestns) {
2329 if (auto nsContent = getRR<NSRecordContent>(bestNSRecord)) {
2330 answer.bestns.emplace(bestNSRecord.d_name, nsContent->getNS());
2331 }
2332 }
2333
2334 auto insertionPair = beenthere.insert(std::move(answer));
2335 if (!insertionPair.second) {
2336 brokeloop = true;
2337 LOG(prefix << qname << ": We have NS in cache for '" << subdomain << "' but part of LOOP (already seen " << insertionPair.first->qname << ")! Trying less specific NS" << endl);
2338 ;
2339 if (doLog()) {
2340 for (auto j = beenthere.begin(); j != beenthere.end(); ++j) {
2341 bool neo = (j == insertionPair.first);
2342 LOG(prefix << qname << ": Beenthere" << (neo ? "*" : "") << ": " << j->qname << "|" << DNSRecordContent::NumberToType(j->qtype) << " (" << (unsigned int)j->bestns.size() << ")" << endl);
2343 }
2344 }
2345 bestns.clear();
2346 }
2347 else {
2348 LOG(prefix << qname << ": We have NS in cache for '" << subdomain << "' (flawedNSSet=" << *flawedNSSet << ")" << endl);
2349 return;
2350 }
2351 }
2352 }
2353 LOG(prefix << qname << ": No valid/useful NS in cache for '" << subdomain << "'" << endl);
2354
2355 if (subdomain.isRoot() && !brokeloop) {
2356 // We lost the root NS records
2357 primeHints();
2358 LOG(prefix << qname << ": Reprimed the root" << endl);
2359 /* let's prevent an infinite loop */
2360 if (!d_updatingRootNS) {
2361 auto log = g_slog->withName("housekeeping");
2362 getRootNS(d_now, d_asyncResolve, depth, log);
2363 }
2364 }
2365 } while (subdomain.chopOff());
2366 }
2367
2368 SyncRes::domainmap_t::const_iterator SyncRes::getBestAuthZone(DNSName* qname)
2369 {
2370 if (t_sstorage.domainmap->empty()) {
2371 return t_sstorage.domainmap->end();
2372 }
2373
2374 SyncRes::domainmap_t::const_iterator ret;
2375 do {
2376 ret = t_sstorage.domainmap->find(*qname);
2377 if (ret != t_sstorage.domainmap->end()) {
2378 break;
2379 }
2380 } while (qname->chopOff());
2381 return ret;
2382 }
2383
2384 /** doesn't actually do the work, leaves that to getBestNSFromCache */
2385 DNSName SyncRes::getBestNSNamesFromCache(const DNSName& qname, const QType qtype, NsSet& nsset, bool* flawedNSSet, unsigned int depth, const string& prefix, set<GetBestNSAnswer>& beenthere)
2386 {
2387 DNSName authOrForwDomain(qname);
2388
2389 auto iter = getBestAuthZone(&authOrForwDomain);
2390 // We have an auth, forwarder of forwarder-recurse
2391 if (iter != t_sstorage.domainmap->end()) {
2392 if (iter->second.isAuth()) {
2393 // this gets picked up in doResolveAt, the empty DNSName, combined with the
2394 // empty vector means 'we are auth for this zone'
2395 nsset.insert({DNSName(), {{}, false}});
2396 return authOrForwDomain;
2397 }
2398 if (iter->second.shouldRecurse()) {
2399 // Again, picked up in doResolveAt. An empty DNSName, combined with a
2400 // non-empty vector of ComboAddresses means 'this is a forwarded domain'
2401 // This is actually picked up in retrieveAddressesForNS called from doResolveAt.
2402 nsset.insert({DNSName(), {iter->second.d_servers, true}});
2403 return authOrForwDomain;
2404 }
2405 }
2406
2407 // We might have a (non-recursive) forwarder, but maybe the cache already contains
2408 // a better NS
2409 vector<DNSRecord> bestns;
2410 DNSName nsFromCacheDomain(g_rootdnsname);
2411 getBestNSFromCache(qname, qtype, bestns, flawedNSSet, depth, prefix, beenthere);
2412
2413 // Pick up the auth domain
2414 for (const auto& nsRecord : bestns) {
2415 const auto nsContent = getRR<NSRecordContent>(nsRecord);
2416 if (nsContent) {
2417 nsFromCacheDomain = nsRecord.d_name;
2418 break;
2419 }
2420 }
2421
2422 if (iter != t_sstorage.domainmap->end()) {
2423 if (doLog()) {
2424 LOG(prefix << qname << " authOrForwDomain: " << authOrForwDomain << " nsFromCacheDomain: " << nsFromCacheDomain << " isPartof: " << authOrForwDomain.isPartOf(nsFromCacheDomain) << endl);
2425 }
2426
2427 // If the forwarder is better or equal to what's found in the cache, use forwarder. Note that name.isPartOf(name).
2428 // So queries that get NS for authOrForwDomain itself go to the forwarder
2429 if (authOrForwDomain.isPartOf(nsFromCacheDomain)) {
2430 if (doLog()) {
2431 LOG(prefix << qname << ": Using forwarder as NS" << endl);
2432 }
2433 nsset.insert({DNSName(), {iter->second.d_servers, false}});
2434 return authOrForwDomain;
2435 }
2436 if (doLog()) {
2437 LOG(prefix << qname << ": Using NS from cache" << endl);
2438 }
2439 }
2440 for (const auto& bestn : bestns) {
2441 // The actual resolver code will not even look at the ComboAddress or bool
2442 const auto nsContent = getRR<NSRecordContent>(bestn);
2443 if (nsContent) {
2444 nsset.insert({nsContent->getNS(), {{}, false}});
2445 }
2446 }
2447 return nsFromCacheDomain;
2448 }
2449
2450 void SyncRes::updateValidationStatusInCache(const DNSName& qname, const QType qtype, bool aaFlag, vState newState) const
2451 {
2452 if (qtype == QType::ANY || qtype == QType::ADDR) {
2453 // not doing that
2454 return;
2455 }
2456
2457 if (vStateIsBogus(newState)) {
2458 g_recCache->updateValidationStatus(d_now.tv_sec, qname, qtype, d_cacheRemote, d_routingTag, aaFlag, newState, s_maxbogusttl + d_now.tv_sec);
2459 }
2460 else {
2461 g_recCache->updateValidationStatus(d_now.tv_sec, qname, qtype, d_cacheRemote, d_routingTag, aaFlag, newState, boost::none);
2462 }
2463 }
2464
2465 static pair<bool, unsigned int> scanForCNAMELoop(const DNSName& name, const vector<DNSRecord>& records)
2466 {
2467 unsigned int numCNames = 0;
2468 for (const auto& record : records) {
2469 if (record.d_type == QType::CNAME && record.d_place == DNSResourceRecord::ANSWER) {
2470 ++numCNames;
2471 if (name == record.d_name) {
2472 return {true, numCNames};
2473 }
2474 }
2475 }
2476 return {false, numCNames};
2477 }
2478
2479 bool SyncRes::doCNAMECacheCheck(const DNSName& qname, const QType qtype, vector<DNSRecord>& ret, unsigned int depth, const string& prefix, int& res, Context& context, bool wasAuthZone, bool wasForwardRecurse, bool checkForDups) // NOLINT(readability-function-cognitive-complexity)
2480 {
2481 vector<DNSRecord> cset;
2482 vector<std::shared_ptr<const RRSIGRecordContent>> signatures;
2483 vector<std::shared_ptr<DNSRecord>> authorityRecs;
2484 bool wasAuth = false;
2485 uint32_t capTTL = std::numeric_limits<uint32_t>::max();
2486 DNSName foundName;
2487 DNSName authZone;
2488 QType foundQT = QType::ENT;
2489
2490 /* we don't require auth data for forward-recurse lookups */
2491 MemRecursorCache::Flags flags = MemRecursorCache::None;
2492 if (!wasForwardRecurse && d_requireAuthData) {
2493 flags |= MemRecursorCache::RequireAuth;
2494 }
2495 if (d_refresh) {
2496 flags |= MemRecursorCache::Refresh;
2497 }
2498 if (d_serveStale) {
2499 flags |= MemRecursorCache::ServeStale;
2500 }
2501 if (g_recCache->get(d_now.tv_sec, qname, QType::CNAME, flags, &cset, d_cacheRemote, d_routingTag, d_doDNSSEC ? &signatures : nullptr, d_doDNSSEC ? &authorityRecs : nullptr, &d_wasVariable, &context.state, &wasAuth, &authZone, &d_fromAuthIP) > 0) {
2502 foundName = qname;
2503 foundQT = QType::CNAME;
2504 }
2505
2506 if (foundName.empty() && qname != g_rootdnsname) {
2507 // look for a DNAME cache hit
2508 auto labels = qname.getRawLabels();
2509 DNSName dnameName(g_rootdnsname);
2510
2511 do {
2512 dnameName.prependRawLabel(labels.back());
2513 labels.pop_back();
2514 if (dnameName == qname && qtype != QType::DNAME) { // The client does not want a DNAME, but we've reached the QNAME already. So there is no match
2515 break;
2516 }
2517 if (g_recCache->get(d_now.tv_sec, dnameName, QType::DNAME, flags, &cset, d_cacheRemote, d_routingTag, d_doDNSSEC ? &signatures : nullptr, d_doDNSSEC ? &authorityRecs : nullptr, &d_wasVariable, &context.state, &wasAuth, &authZone, &d_fromAuthIP) > 0) {
2518 foundName = dnameName;
2519 foundQT = QType::DNAME;
2520 break;
2521 }
2522 } while (!labels.empty());
2523 }
2524
2525 if (foundName.empty()) {
2526 return false;
2527 }
2528
2529 if (qtype == QType::DS && authZone == qname) {
2530 /* CNAME at APEX of the child zone, we can't use that to prove that
2531 there is no DS */
2532 LOG(prefix << qname << ": Found a " << foundQT.toString() << " cache hit of '" << qname << "' from " << authZone << ", but such a record at the apex of the child zone does not prove that there is no DS in the parent zone" << endl);
2533 return false;
2534 }
2535
2536 for (auto const& record : cset) {
2537 if (record.d_class != QClass::IN) {
2538 continue;
2539 }
2540
2541 if (record.d_ttl > (unsigned int)d_now.tv_sec) {
2542
2543 if (!wasAuthZone && shouldValidate() && (wasAuth || wasForwardRecurse) && context.state == vState::Indeterminate && d_requireAuthData) {
2544 /* This means we couldn't figure out the state when this entry was cached */
2545
2546 vState recordState = getValidationStatus(foundName, !signatures.empty(), qtype == QType::DS, depth, prefix);
2547 if (recordState == vState::Secure) {
2548 LOG(prefix << qname << ": Got vState::Indeterminate state from the " << foundQT.toString() << " cache, validating.." << endl);
2549 context.state = SyncRes::validateRecordsWithSigs(depth, prefix, qname, qtype, foundName, foundQT, cset, signatures);
2550 if (context.state != vState::Indeterminate) {
2551 LOG(prefix << qname << ": Got vState::Indeterminate state from the " << foundQT.toString() << " cache, new validation result is " << context.state << endl);
2552 if (vStateIsBogus(context.state)) {
2553 capTTL = s_maxbogusttl;
2554 }
2555 updateValidationStatusInCache(foundName, foundQT, wasAuth, context.state);
2556 }
2557 }
2558 }
2559
2560 LOG(prefix << qname << ": Found cache " << foundQT.toString() << " hit for '" << foundName << "|" << foundQT.toString() << "' to '" << record.getContent()->getZoneRepresentation() << "', validation state is " << context.state << endl);
2561
2562 DNSRecord dnsRecord = record;
2563 auto alreadyPresent = false;
2564
2565 if (checkForDups) {
2566 // This can happen on the 2nd iteration of the servestale loop, where the first iteration
2567 // added a C/DNAME record, but the target resolve failed
2568 for (const auto& dnsrec : ret) {
2569 if (dnsrec.d_type == foundQT && dnsrec.d_name == record.d_name) {
2570 alreadyPresent = true;
2571 break;
2572 }
2573 }
2574 }
2575 dnsRecord.d_ttl -= d_now.tv_sec;
2576 dnsRecord.d_ttl = std::min(dnsRecord.d_ttl, capTTL);
2577 const uint32_t ttl = dnsRecord.d_ttl;
2578 if (!alreadyPresent) {
2579 ret.reserve(ret.size() + 2 + signatures.size() + authorityRecs.size());
2580 ret.push_back(dnsRecord);
2581
2582 for (const auto& signature : signatures) {
2583 DNSRecord sigdr;
2584 sigdr.d_type = QType::RRSIG;
2585 sigdr.d_name = foundName;
2586 sigdr.d_ttl = ttl;
2587 sigdr.setContent(signature);
2588 sigdr.d_place = DNSResourceRecord::ANSWER;
2589 sigdr.d_class = QClass::IN;
2590 ret.push_back(sigdr);
2591 }
2592
2593 for (const auto& rec : authorityRecs) {
2594 DNSRecord authDR(*rec);
2595 authDR.d_ttl = ttl;
2596 ret.push_back(authDR);
2597 }
2598 }
2599
2600 DNSName newTarget;
2601 if (foundQT == QType::DNAME) {
2602 if (qtype == QType::DNAME && qname == foundName) { // client wanted the DNAME, no need to synthesize a CNAME
2603 res = RCode::NoError;
2604 return true;
2605 }
2606 // Synthesize a CNAME
2607 auto dnameRR = getRR<DNAMERecordContent>(record);
2608 if (dnameRR == nullptr) {
2609 throw ImmediateServFailException("Unable to get record content for " + foundName.toLogString() + "|DNAME cache entry");
2610 }
2611 const auto& dnameSuffix = dnameRR->getTarget();
2612 DNSName targetPrefix = qname.makeRelative(foundName);
2613 try {
2614 dnsRecord.d_type = QType::CNAME;
2615 dnsRecord.d_name = targetPrefix + foundName;
2616 newTarget = targetPrefix + dnameSuffix;
2617 dnsRecord.setContent(std::make_shared<CNAMERecordContent>(CNAMERecordContent(newTarget)));
2618 ret.push_back(dnsRecord);
2619 }
2620 catch (const std::exception& e) {
2621 // We should probably catch an std::range_error here and set the rcode to YXDOMAIN (RFC 6672, section 2.2)
2622 // But this is consistent with processRecords
2623 throw ImmediateServFailException("Unable to perform DNAME substitution(DNAME owner: '" + foundName.toLogString() + "', DNAME target: '" + dnameSuffix.toLogString() + "', substituted name: '" + targetPrefix.toLogString() + "." + dnameSuffix.toLogString() + "' : " + e.what());
2624 }
2625
2626 LOG(prefix << qname << ": Synthesized " << dnsRecord.d_name << "|CNAME " << newTarget << endl);
2627 }
2628
2629 if (qtype == QType::CNAME) { // perhaps they really wanted a CNAME!
2630 res = RCode::NoError;
2631 return true;
2632 }
2633
2634 if (qtype == QType::DS || qtype == QType::DNSKEY) {
2635 res = RCode::NoError;
2636 return true;
2637 }
2638
2639 // We have a DNAME _or_ CNAME cache hit and the client wants something else than those two.
2640 // Let's find the answer!
2641 if (foundQT == QType::CNAME) {
2642 const auto cnameContent = getRR<CNAMERecordContent>(record);
2643 if (cnameContent == nullptr) {
2644 throw ImmediateServFailException("Unable to get record content for " + foundName.toLogString() + "|CNAME cache entry");
2645 }
2646 newTarget = cnameContent->getTarget();
2647 }
2648
2649 if (qname == newTarget) {
2650 string msg = "Got a CNAME referral (from cache) to self";
2651 LOG(prefix << qname << ": " << msg << endl);
2652 throw ImmediateServFailException(std::move(msg));
2653 }
2654
2655 if (newTarget.isPartOf(qname)) {
2656 // a.b.c. CNAME x.a.b.c will go to great depths with QM on
2657 string msg = "Got a CNAME referral (from cache) to child, disabling QM";
2658 LOG(prefix << qname << ": " << msg << endl);
2659 setQNameMinimization(false);
2660 }
2661
2662 if (!d_followCNAME) {
2663 res = RCode::NoError;
2664 return true;
2665 }
2666
2667 // Check to see if we already have seen the new target as a previous target or that we have a very long CNAME chain
2668 const auto [CNAMELoop, numCNAMEs] = scanForCNAMELoop(newTarget, ret);
2669 if (CNAMELoop) {
2670 string msg = "got a CNAME referral (from cache) that causes a loop";
2671 LOG(prefix << qname << ": Status=" << msg << endl);
2672 throw ImmediateServFailException(std::move(msg));
2673 }
2674 if (numCNAMEs > s_max_CNAMES_followed) {
2675 string msg = "max number of CNAMEs exceeded";
2676 LOG(prefix << qname << ": Status=" << msg << endl);
2677 throw ImmediateServFailException(std::move(msg));
2678 }
2679
2680 set<GetBestNSAnswer> beenthere;
2681 Context cnameContext;
2682 // Be aware that going out on the network might be disabled (cache-only), for example because we are in QM Step0,
2683 // so you can't trust that a real lookup will have been made.
2684 res = doResolve(newTarget, qtype, ret, depth + 1, beenthere, cnameContext);
2685 LOG(prefix << qname << ": Updating validation state for response to " << qname << " from " << context.state << " with the state from the DNAME/CNAME quest: " << cnameContext.state << endl);
2686 updateValidationState(qname, context.state, cnameContext.state, prefix);
2687
2688 return true;
2689 }
2690 }
2691 throw ImmediateServFailException("Could not determine whether or not there was a CNAME or DNAME in cache for '" + qname.toLogString() + "'");
2692 }
2693
2694 namespace
2695 {
2696 struct CacheEntry
2697 {
2698 vector<DNSRecord> records;
2699 vector<shared_ptr<const RRSIGRecordContent>> signatures;
2700 time_t d_ttl_time{0};
2701 uint32_t signaturesTTL{std::numeric_limits<uint32_t>::max()};
2702 };
2703 struct CacheKey
2704 {
2705 DNSName name;
2706 QType type;
2707 DNSResourceRecord::Place place;
2708 bool operator<(const CacheKey& rhs) const
2709 {
2710 return std::tie(type, place, name) < std::tie(rhs.type, rhs.place, rhs.name);
2711 }
2712 };
2713 using tcache_t = map<CacheKey, CacheEntry>;
2714 }
2715
2716 static void reapRecordsFromNegCacheEntryForValidation(tcache_t& tcache, const vector<DNSRecord>& records)
2717 {
2718 for (const auto& rec : records) {
2719 if (rec.d_type == QType::RRSIG) {
2720 auto rrsig = getRR<RRSIGRecordContent>(rec);
2721 if (rrsig) {
2722 tcache[{rec.d_name, rrsig->d_type, rec.d_place}].signatures.push_back(rrsig);
2723 }
2724 }
2725 else {
2726 tcache[{rec.d_name, rec.d_type, rec.d_place}].records.push_back(rec);
2727 }
2728 }
2729 }
2730
2731 static bool negativeCacheEntryHasSOA(const NegCache::NegCacheEntry& negEntry)
2732 {
2733 return !negEntry.authoritySOA.records.empty();
2734 }
2735
2736 static void reapRecordsForValidation(std::map<QType, CacheEntry>& entries, const vector<DNSRecord>& records)
2737 {
2738 for (const auto& rec : records) {
2739 entries[rec.d_type].records.push_back(rec);
2740 }
2741 }
2742
2743 static void reapSignaturesForValidation(std::map<QType, CacheEntry>& entries, const vector<std::shared_ptr<const RRSIGRecordContent>>& signatures)
2744 {
2745 for (const auto& sig : signatures) {
2746 entries[sig->d_type].signatures.push_back(sig);
2747 }
2748 }
2749
2750 /*!
2751 * Convenience function to push the records from records into ret with a new TTL
2752 *
2753 * \param records DNSRecords that need to go into ret
2754 * \param ttl The new TTL for these records
2755 * \param ret The vector of DNSRecords that should contain the records with the modified TTL
2756 */
2757 static void addTTLModifiedRecords(vector<DNSRecord>& records, const uint32_t ttl, vector<DNSRecord>& ret)
2758 {
2759 for (auto& rec : records) {
2760 rec.d_ttl = ttl;
2761 ret.push_back(std::move(rec));
2762 }
2763 }
2764
2765 void SyncRes::computeNegCacheValidationStatus(const NegCache::NegCacheEntry& negEntry, const DNSName& qname, const QType qtype, const int res, vState& state, unsigned int depth, const string& prefix)
2766 {
2767 tcache_t tcache;
2768 reapRecordsFromNegCacheEntryForValidation(tcache, negEntry.authoritySOA.records);
2769 reapRecordsFromNegCacheEntryForValidation(tcache, negEntry.authoritySOA.signatures);
2770 reapRecordsFromNegCacheEntryForValidation(tcache, negEntry.DNSSECRecords.records);
2771 reapRecordsFromNegCacheEntryForValidation(tcache, negEntry.DNSSECRecords.signatures);
2772
2773 for (const auto& entry : tcache) {
2774 // this happens when we did store signatures, but passed on the records themselves
2775 if (entry.second.records.empty()) {
2776 continue;
2777 }
2778
2779 const DNSName& owner = entry.first.name;
2780
2781 vState recordState = getValidationStatus(owner, !entry.second.signatures.empty(), qtype == QType::DS, depth, prefix);
2782 if (state == vState::Indeterminate) {
2783 state = recordState;
2784 }
2785
2786 if (recordState == vState::Secure) {
2787 recordState = SyncRes::validateRecordsWithSigs(depth, prefix, qname, qtype, owner, QType(entry.first.type), entry.second.records, entry.second.signatures);
2788 }
2789
2790 if (recordState != vState::Indeterminate && recordState != state) {
2791 updateValidationState(qname, state, recordState, prefix);
2792 if (state != vState::Secure) {
2793 break;
2794 }
2795 }
2796 }
2797
2798 if (state == vState::Secure) {
2799 vState neValidationState = negEntry.d_validationState;
2800 dState expectedState = res == RCode::NXDomain ? dState::NXDOMAIN : dState::NXQTYPE;
2801 dState denialState = getDenialValidationState(negEntry, expectedState, false, prefix);
2802 updateDenialValidationState(qname, neValidationState, negEntry.d_name, state, denialState, expectedState, qtype == QType::DS, depth, prefix);
2803 }
2804 if (state != vState::Indeterminate) {
2805 /* validation succeeded, let's update the cache entry so we don't have to validate again */
2806 boost::optional<time_t> capTTD = boost::none;
2807 if (vStateIsBogus(state)) {
2808 capTTD = d_now.tv_sec + s_maxbogusttl;
2809 }
2810 g_negCache->updateValidationStatus(negEntry.d_name, negEntry.d_qtype, state, capTTD);
2811 }
2812 }
2813
2814 bool SyncRes::doCacheCheck(const DNSName& qname, const DNSName& authname, bool wasForwardedOrAuthZone, bool wasAuthZone, bool wasForwardRecurse, QType qtype, vector<DNSRecord>& ret, unsigned int depth, const string& prefix, int& res, Context& context) // NOLINT(readability-function-cognitive-complexity)
2815 {
2816 bool giveNegative = false;
2817
2818 // sqname and sqtype are used contain 'higher' names if we have them (e.g. powerdns.com|SOA when we find a negative entry for doesnotexist.powerdns.com|A)
2819 DNSName sqname(qname);
2820 QType sqt(qtype);
2821 uint32_t sttl = 0;
2822 // cout<<"Lookup for '"<<qname<<"|"<<qtype.toString()<<"' -> "<<getLastLabel(qname)<<endl;
2823 vState cachedState{};
2824 NegCache::NegCacheEntry negEntry;
2825
2826 if (s_rootNXTrust && g_negCache->getRootNXTrust(qname, d_now, negEntry, d_serveStale, d_refresh) && negEntry.d_auth.isRoot() && (!wasForwardedOrAuthZone || authname.isRoot())) { // when forwarding, the root may only neg-cache if it was forwarded to.
2827 sttl = negEntry.d_ttd - d_now.tv_sec;
2828 LOG(prefix << qname << ": Entire name '" << qname << "', is negatively cached via '" << negEntry.d_auth << "' & '" << negEntry.d_name << "' for another " << sttl << " seconds" << endl);
2829 res = RCode::NXDomain;
2830 giveNegative = true;
2831 cachedState = negEntry.d_validationState;
2832 if (s_addExtendedResolutionDNSErrors) {
2833 context.extendedError = EDNSExtendedError{static_cast<uint16_t>(EDNSExtendedError::code::Synthesized), "Result synthesized by root-nx-trust"};
2834 }
2835 }
2836 else if (g_negCache->get(qname, qtype, d_now, negEntry, false, d_serveStale, d_refresh)) {
2837 /* If we are looking for a DS, discard NXD if auth == qname
2838 and ask for a specific denial instead */
2839 if (qtype != QType::DS || negEntry.d_qtype.getCode() != 0 || negEntry.d_auth != qname || g_negCache->get(qname, qtype, d_now, negEntry, true, d_serveStale, d_refresh)) {
2840 /* Careful! If the client is asking for a DS that does not exist, we need to provide the SOA along with the NSEC(3) proof
2841 and we might not have it if we picked up the proof from a delegation, in which case we need to keep on to do the actual DS
2842 query. */
2843 if (qtype == QType::DS && negEntry.d_qtype.getCode() != 0 && !d_externalDSQuery.empty() && qname == d_externalDSQuery && !negativeCacheEntryHasSOA(negEntry)) {
2844 giveNegative = false;
2845 }
2846 else {
2847 res = RCode::NXDomain;
2848 sttl = negEntry.d_ttd - d_now.tv_sec;
2849 giveNegative = true;
2850 cachedState = negEntry.d_validationState;
2851 if (negEntry.d_qtype.getCode() != 0) {
2852 LOG(prefix << qname << "|" << qtype << ": Is negatively cached via '" << negEntry.d_auth << "' for another " << sttl << " seconds" << endl);
2853 res = RCode::NoError;
2854 if (s_addExtendedResolutionDNSErrors) {
2855 context.extendedError = EDNSExtendedError{static_cast<uint16_t>(EDNSExtendedError::code::Synthesized), "Result from negative cache"};
2856 }
2857 }
2858 else {
2859 LOG(prefix << qname << ": Entire name '" << qname << "' is negatively cached via '" << negEntry.d_auth << "' for another " << sttl << " seconds" << endl);
2860 if (s_addExtendedResolutionDNSErrors) {
2861 context.extendedError = EDNSExtendedError{static_cast<uint16_t>(EDNSExtendedError::code::Synthesized), "Result from negative cache for entire name"};
2862 }
2863 }
2864 }
2865 }
2866 }
2867 else if (s_hardenNXD != HardenNXD::No && !qname.isRoot() && !wasForwardedOrAuthZone) {
2868 auto labels = qname.getRawLabels();
2869 DNSName negCacheName(g_rootdnsname);
2870 negCacheName.prependRawLabel(labels.back());
2871 labels.pop_back();
2872 while (!labels.empty()) {
2873 if (g_negCache->get(negCacheName, QType::ENT, d_now, negEntry, true, d_serveStale, d_refresh)) {
2874 if (negEntry.d_validationState == vState::Indeterminate && validationEnabled()) {
2875 // LOG(prefix << negCacheName << " negatively cached and vState::Indeterminate, trying to validate NXDOMAIN" << endl);
2876 // ...
2877 // And get the updated ne struct
2878 // t_sstorage.negcache.get(negCacheName, QType(0), d_now, ne, true);
2879 }
2880 if ((s_hardenNXD == HardenNXD::Yes && !vStateIsBogus(negEntry.d_validationState)) || negEntry.d_validationState == vState::Secure) {
2881 res = RCode::NXDomain;
2882 sttl = negEntry.d_ttd - d_now.tv_sec;
2883 giveNegative = true;
2884 cachedState = negEntry.d_validationState;
2885 LOG(prefix << qname << ": Name '" << negCacheName << "' and below, is negatively cached via '" << negEntry.d_auth << "' for another " << sttl << " seconds" << endl);
2886 if (s_addExtendedResolutionDNSErrors) {
2887 context.extendedError = EDNSExtendedError{static_cast<uint16_t>(EDNSExtendedError::code::Synthesized), "Result synthesized by nothing-below-nxdomain (RFC8020)"};
2888 }
2889 break;
2890 }
2891 }
2892 negCacheName.prependRawLabel(labels.back());
2893 labels.pop_back();
2894 }
2895 }
2896
2897 if (giveNegative) {
2898
2899 context.state = cachedState;
2900
2901 if (!wasAuthZone && shouldValidate() && context.state == vState::Indeterminate) {
2902 LOG(prefix << qname << ": Got vState::Indeterminate state for records retrieved from the negative cache, validating.." << endl);
2903 computeNegCacheValidationStatus(negEntry, qname, qtype, res, context.state, depth, prefix);
2904
2905 if (context.state != cachedState && vStateIsBogus(context.state)) {
2906 sttl = std::min(sttl, s_maxbogusttl);
2907 }
2908 }
2909
2910 // Transplant SOA to the returned packet
2911 addTTLModifiedRecords(negEntry.authoritySOA.records, sttl, ret);
2912 if (d_doDNSSEC) {
2913 addTTLModifiedRecords(negEntry.authoritySOA.signatures, sttl, ret);
2914 addTTLModifiedRecords(negEntry.DNSSECRecords.records, sttl, ret);
2915 addTTLModifiedRecords(negEntry.DNSSECRecords.signatures, sttl, ret);
2916 }
2917
2918 LOG(prefix << qname << ": Updating validation state with negative cache content for " << qname << " to " << context.state << endl);
2919 return true;
2920 }
2921
2922 vector<DNSRecord> cset;
2923 bool found = false;
2924 bool expired = false;
2925 vector<std::shared_ptr<const RRSIGRecordContent>> signatures;
2926 vector<std::shared_ptr<DNSRecord>> authorityRecs;
2927 uint32_t ttl = 0;
2928 uint32_t capTTL = std::numeric_limits<uint32_t>::max();
2929 bool wasCachedAuth{};
2930 MemRecursorCache::Flags flags = MemRecursorCache::None;
2931 if (!wasForwardRecurse && d_requireAuthData) {
2932 flags |= MemRecursorCache::RequireAuth;
2933 }
2934 if (d_serveStale) {
2935 flags |= MemRecursorCache::ServeStale;
2936 }
2937 if (d_refresh) {
2938 flags |= MemRecursorCache::Refresh;
2939 }
2940 if (g_recCache->get(d_now.tv_sec, sqname, sqt, flags, &cset, d_cacheRemote, d_routingTag, d_doDNSSEC ? &signatures : nullptr, d_doDNSSEC ? &authorityRecs : nullptr, &d_wasVariable, &cachedState, &wasCachedAuth, nullptr, &d_fromAuthIP) > 0) {
2941
2942 LOG(prefix << sqname << ": Found cache hit for " << sqt.toString() << ": ");
2943
2944 if (!wasAuthZone && shouldValidate() && (wasCachedAuth || wasForwardRecurse) && cachedState == vState::Indeterminate && d_requireAuthData) {
2945
2946 /* This means we couldn't figure out the state when this entry was cached */
2947 vState recordState = getValidationStatus(qname, !signatures.empty(), qtype == QType::DS, depth, prefix);
2948
2949 if (recordState == vState::Secure) {
2950 LOG(prefix << sqname << ": Got vState::Indeterminate state from the cache, validating.." << endl);
2951 if (sqt == QType::DNSKEY && sqname == getSigner(signatures)) {
2952 cachedState = validateDNSKeys(sqname, cset, signatures, depth, prefix);
2953 }
2954 else {
2955 if (sqt == QType::ANY) {
2956 std::map<QType, CacheEntry> types;
2957 reapRecordsForValidation(types, cset);
2958 reapSignaturesForValidation(types, signatures);
2959
2960 for (const auto& type : types) {
2961 vState cachedRecordState{};
2962 if (type.first == QType::DNSKEY && sqname == getSigner(type.second.signatures)) {
2963 cachedRecordState = validateDNSKeys(sqname, type.second.records, type.second.signatures, depth, prefix);
2964 }
2965 else {
2966 cachedRecordState = SyncRes::validateRecordsWithSigs(depth, prefix, qname, qtype, sqname, type.first, type.second.records, type.second.signatures);
2967 }
2968 updateDNSSECValidationState(cachedState, cachedRecordState);
2969 }
2970 }
2971 else {
2972 cachedState = SyncRes::validateRecordsWithSigs(depth, prefix, qname, qtype, sqname, sqt, cset, signatures);
2973 }
2974 }
2975 }
2976 else {
2977 cachedState = recordState;
2978 }
2979
2980 if (cachedState != vState::Indeterminate) {
2981 LOG(prefix << qname << ": Got vState::Indeterminate state from the cache, validation result is " << cachedState << endl);
2982 if (vStateIsBogus(cachedState)) {
2983 capTTL = s_maxbogusttl;
2984 }
2985 if (sqt != QType::ANY && sqt != QType::ADDR) {
2986 updateValidationStatusInCache(sqname, sqt, wasCachedAuth, cachedState);
2987 }
2988 }
2989 }
2990
2991 for (auto j = cset.cbegin(); j != cset.cend(); ++j) {
2992
2993 LOG(j->getContent()->getZoneRepresentation());
2994
2995 if (j->d_class != QClass::IN) {
2996 continue;
2997 }
2998
2999 if (j->d_ttl > (unsigned int)d_now.tv_sec) {
3000 DNSRecord dnsRecord = *j;
3001 dnsRecord.d_ttl -= d_now.tv_sec;
3002 dnsRecord.d_ttl = std::min(dnsRecord.d_ttl, capTTL);
3003 ttl = dnsRecord.d_ttl;
3004 ret.push_back(dnsRecord);
3005 LOG("[ttl=" << dnsRecord.d_ttl << "] ");
3006 found = true;
3007 }
3008 else {
3009 LOG("[expired] ");
3010 expired = true;
3011 }
3012 }
3013
3014 ret.reserve(ret.size() + signatures.size() + authorityRecs.size());
3015
3016 for (const auto& signature : signatures) {
3017 DNSRecord dnsRecord;
3018 dnsRecord.d_type = QType::RRSIG;
3019 dnsRecord.d_name = sqname;
3020 dnsRecord.d_ttl = ttl;
3021 dnsRecord.setContent(signature);
3022 dnsRecord.d_place = DNSResourceRecord::ANSWER;
3023 dnsRecord.d_class = QClass::IN;
3024 ret.push_back(dnsRecord);
3025 }
3026
3027 for (const auto& rec : authorityRecs) {
3028 DNSRecord dnsRecord(*rec);
3029 dnsRecord.d_ttl = ttl;
3030 ret.push_back(dnsRecord);
3031 }
3032
3033 LOG(endl);
3034 if (found && !expired) {
3035 if (!giveNegative) {
3036 res = 0;
3037 }
3038 LOG(prefix << qname << ": Updating validation state with cache content for " << qname << " to " << cachedState << endl);
3039 context.state = cachedState;
3040 return true;
3041 }
3042 LOG(prefix << qname << ": Cache had only stale entries" << endl);
3043 }
3044
3045 /* let's check if we have a NSEC covering that record */
3046 if (g_aggressiveNSECCache && !wasForwardedOrAuthZone) {
3047 if (g_aggressiveNSECCache->getDenial(d_now.tv_sec, qname, qtype, ret, res, d_cacheRemote, d_routingTag, d_doDNSSEC, LogObject(prefix))) {
3048 context.state = vState::Secure;
3049 if (s_addExtendedResolutionDNSErrors) {
3050 context.extendedError = EDNSExtendedError{static_cast<uint16_t>(EDNSExtendedError::code::Synthesized), "Result synthesized from aggressive NSEC cache (RFC8198)"};
3051 }
3052 return true;
3053 }
3054 }
3055
3056 return false;
3057 }
3058
3059 bool SyncRes::moreSpecificThan(const DNSName& lhs, const DNSName& rhs)
3060 {
3061 return (lhs.isPartOf(rhs) && lhs.countLabels() > rhs.countLabels());
3062 }
3063
3064 struct speedOrder
3065 {
3066 bool operator()(const std::pair<DNSName, float>& lhs, const std::pair<DNSName, float>& rhs) const
3067 {
3068 return lhs.second < rhs.second;
3069 }
3070 };
3071
3072 std::vector<std::pair<DNSName, float>> SyncRes::shuffleInSpeedOrder(const DNSName& qname, NsSet& tnameservers, const string& prefix)
3073 {
3074 std::vector<std::pair<DNSName, float>> rnameservers;
3075 rnameservers.reserve(tnameservers.size());
3076 for (const auto& tns : tnameservers) {
3077 float speed = s_nsSpeeds.lock()->fastest(tns.first, d_now);
3078 rnameservers.emplace_back(tns.first, speed);
3079 if (tns.first.empty()) { // this was an authoritative OOB zone, don't pollute the nsSpeeds with that
3080 return rnameservers;
3081 }
3082 }
3083
3084 shuffle(rnameservers.begin(), rnameservers.end(), pdns::dns_random_engine());
3085 speedOrder speedCompare;
3086 stable_sort(rnameservers.begin(), rnameservers.end(), speedCompare);
3087
3088 if (doLog()) {
3089 LOG(prefix << qname << ": Nameservers: ");
3090 for (auto i = rnameservers.begin(); i != rnameservers.end(); ++i) {
3091 if (i != rnameservers.begin()) {
3092 LOG(", ");
3093 if (((i - rnameservers.begin()) % 3) == 0) {
3094 LOG(endl
3095 << prefix << " ");
3096 }
3097 }
3098 LOG(i->first.toLogString() << "(" << fmtfloat(i->second / 1000.0) << "ms)");
3099 }
3100 LOG(endl);
3101 }
3102 return rnameservers;
3103 }
3104
3105 vector<ComboAddress> SyncRes::shuffleForwardSpeed(const DNSName& qname, const vector<ComboAddress>& rnameservers, const string& prefix, const bool wasRd)
3106 {
3107 vector<ComboAddress> nameservers = rnameservers;
3108 map<ComboAddress, float> speeds;
3109
3110 for (const auto& val : nameservers) {
3111 DNSName nsName = DNSName(val.toStringWithPort());
3112 float speed = s_nsSpeeds.lock()->fastest(nsName, d_now);
3113 speeds[val] = speed;
3114 }
3115 shuffle(nameservers.begin(), nameservers.end(), pdns::dns_random_engine());
3116 speedOrderCA speedCompare(speeds);
3117 stable_sort(nameservers.begin(), nameservers.end(), speedCompare);
3118
3119 if (doLog()) {
3120 LOG(prefix << qname << ": Nameservers: ");
3121 for (auto i = nameservers.cbegin(); i != nameservers.cend(); ++i) {
3122 if (i != nameservers.cbegin()) {
3123 LOG(", ");
3124 if (((i - nameservers.cbegin()) % 3) == 0) {
3125 LOG(endl
3126 << prefix << " ");
3127 }
3128 }
3129 LOG((wasRd ? string("+") : string("-")) << i->toStringWithPort() << "(" << fmtfloat(speeds[*i] / 1000.0) << "ms)");
3130 }
3131 LOG(endl);
3132 }
3133 return nameservers;
3134 }
3135
3136 static uint32_t getRRSIGTTL(const time_t now, const std::shared_ptr<const RRSIGRecordContent>& rrsig)
3137 {
3138 uint32_t res = 0;
3139 if (now < rrsig->d_sigexpire) {
3140 // coverity[store_truncates_time_t]
3141 res = static_cast<uint32_t>(rrsig->d_sigexpire) - now;
3142 }
3143 return res;
3144 }
3145
3146 static const set<QType> nsecTypes = {QType::NSEC, QType::NSEC3};
3147
3148 /* Fills the authoritySOA and DNSSECRecords fields from ne with those found in the records
3149 *
3150 * \param records The records to parse for the authority SOA and NSEC(3) records
3151 * \param ne The NegCacheEntry to be filled out (will not be cleared, only appended to
3152 */
3153 static void harvestNXRecords(const vector<DNSRecord>& records, NegCache::NegCacheEntry& negEntry, const time_t now, uint32_t* lowestTTL)
3154 {
3155 for (const auto& rec : records) {
3156 if (rec.d_place != DNSResourceRecord::AUTHORITY) {
3157 // RFC 4035 section 3.1.3. indicates that NSEC records MUST be placed in
3158 // the AUTHORITY section. Section 3.1.1 indicates that that RRSIGs for
3159 // records MUST be in the same section as the records they cover.
3160 // Hence, we ignore all records outside of the AUTHORITY section.
3161 continue;
3162 }
3163
3164 if (rec.d_type == QType::RRSIG) {
3165 auto rrsig = getRR<RRSIGRecordContent>(rec);
3166 if (rrsig) {
3167 if (rrsig->d_type == QType::SOA) {
3168 negEntry.authoritySOA.signatures.push_back(rec);
3169 if (lowestTTL != nullptr && isRRSIGNotExpired(now, *rrsig)) {
3170 *lowestTTL = min(*lowestTTL, rec.d_ttl);
3171 *lowestTTL = min(*lowestTTL, getRRSIGTTL(now, rrsig));
3172 }
3173 }
3174 if (nsecTypes.count(rrsig->d_type) != 0) {
3175 negEntry.DNSSECRecords.signatures.push_back(rec);
3176 if (lowestTTL != nullptr && isRRSIGNotExpired(now, *rrsig)) {
3177 *lowestTTL = min(*lowestTTL, rec.d_ttl);
3178 *lowestTTL = min(*lowestTTL, getRRSIGTTL(now, rrsig));
3179 }
3180 }
3181 }
3182 continue;
3183 }
3184 if (rec.d_type == QType::SOA) {
3185 negEntry.authoritySOA.records.push_back(rec);
3186 if (lowestTTL != nullptr) {
3187 *lowestTTL = min(*lowestTTL, rec.d_ttl);
3188 }
3189 continue;
3190 }
3191 if (nsecTypes.count(rec.d_type) != 0) {
3192 negEntry.DNSSECRecords.records.push_back(rec);
3193 if (lowestTTL != nullptr) {
3194 *lowestTTL = min(*lowestTTL, rec.d_ttl);
3195 }
3196 continue;
3197 }
3198 }
3199 }
3200
3201 static cspmap_t harvestCSPFromNE(const NegCache::NegCacheEntry& negEntry)
3202 {
3203 cspmap_t cspmap;
3204 for (const auto& rec : negEntry.DNSSECRecords.signatures) {
3205 if (rec.d_type == QType::RRSIG) {
3206 auto rrc = getRR<RRSIGRecordContent>(rec);
3207 if (rrc) {
3208 cspmap[{rec.d_name, rrc->d_type}].signatures.push_back(rrc);
3209 }
3210 }
3211 }
3212 for (const auto& rec : negEntry.DNSSECRecords.records) {
3213 cspmap[{rec.d_name, rec.d_type}].records.insert(rec.getContent());
3214 }
3215 return cspmap;
3216 }
3217
3218 // TODO remove after processRecords is fixed!
3219 // Adds the RRSIG for the SOA and the NSEC(3) + RRSIGs to ret
3220 static void addNXNSECS(vector<DNSRecord>& ret, const vector<DNSRecord>& records)
3221 {
3222 NegCache::NegCacheEntry negEntry;
3223 harvestNXRecords(records, negEntry, 0, nullptr);
3224 ret.insert(ret.end(), negEntry.authoritySOA.signatures.begin(), negEntry.authoritySOA.signatures.end());
3225 ret.insert(ret.end(), negEntry.DNSSECRecords.records.begin(), negEntry.DNSSECRecords.records.end());
3226 ret.insert(ret.end(), negEntry.DNSSECRecords.signatures.begin(), negEntry.DNSSECRecords.signatures.end());
3227 }
3228
3229 static bool rpzHitShouldReplaceContent(const DNSName& qname, const QType qtype, const std::vector<DNSRecord>& records)
3230 {
3231 if (qtype == QType::CNAME) {
3232 return true;
3233 }
3234
3235 for (const auto& record : records) { // NOLINT(readability-use-anyofallof): don't agree
3236 if (record.d_type == QType::CNAME) {
3237 if (auto content = getRR<CNAMERecordContent>(record)) {
3238 if (qname == content->getTarget()) {
3239 /* we have a CNAME whose target matches the entry we are about to
3240 generate, so it will complete the current records, not replace
3241 them
3242 */
3243 return false;
3244 }
3245 }
3246 }
3247 }
3248
3249 return true;
3250 }
3251
3252 static void removeConflictingRecord(std::vector<DNSRecord>& records, const DNSName& name, const QType dtype)
3253 {
3254 for (auto it = records.begin(); it != records.end();) {
3255 bool remove = false;
3256
3257 if (it->d_class == QClass::IN && (it->d_type == QType::CNAME || dtype == QType::CNAME || it->d_type == dtype) && it->d_name == name) {
3258 remove = true;
3259 }
3260 else if (it->d_class == QClass::IN && it->d_type == QType::RRSIG && it->d_name == name) {
3261 if (auto rrc = getRR<RRSIGRecordContent>(*it)) {
3262 if (rrc->d_type == QType::CNAME || rrc->d_type == dtype) {
3263 /* also remove any RRSIG that could conflict */
3264 remove = true;
3265 }
3266 }
3267 }
3268
3269 if (remove) {
3270 it = records.erase(it);
3271 }
3272 else {
3273 ++it;
3274 }
3275 }
3276 }
3277
3278 void SyncRes::handlePolicyHit(const std::string& prefix, const DNSName& qname, const QType qtype, std::vector<DNSRecord>& ret, bool& done, int& rcode, unsigned int depth)
3279 {
3280 if (d_pdl && d_pdl->policyHitEventFilter(d_requestor, qname, qtype, d_queryReceivedOverTCP, d_appliedPolicy, d_policyTags, d_discardedPolicies)) {
3281 /* reset to no match */
3282 d_appliedPolicy = DNSFilterEngine::Policy();
3283 return;
3284 }
3285
3286 /* don't account truncate actions for TCP queries, since they are not applied */
3287 if (d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::Truncate || !d_queryReceivedOverTCP) {
3288 ++t_Counters.at(rec::PolicyHistogram::policy).at(d_appliedPolicy.d_kind);
3289 ++t_Counters.at(rec::PolicyNameHits::policyName).counts[d_appliedPolicy.getName()];
3290 }
3291
3292 if (d_appliedPolicy.d_type != DNSFilterEngine::PolicyType::None) {
3293 LOG(prefix << qname << "|" << qtype << ':' << d_appliedPolicy.getLogString() << endl);
3294 }
3295
3296 switch (d_appliedPolicy.d_kind) {
3297
3298 case DNSFilterEngine::PolicyKind::NoAction:
3299 return;
3300
3301 case DNSFilterEngine::PolicyKind::Drop:
3302 ++t_Counters.at(rec::Counter::policyDrops);
3303 throw ImmediateQueryDropException();
3304
3305 case DNSFilterEngine::PolicyKind::NXDOMAIN:
3306 ret.clear();
3307 d_appliedPolicy.addSOAtoRPZResult(ret);
3308 rcode = RCode::NXDomain;
3309 done = true;
3310 return;
3311
3312 case DNSFilterEngine::PolicyKind::NODATA:
3313 ret.clear();
3314 d_appliedPolicy.addSOAtoRPZResult(ret);
3315 rcode = RCode::NoError;
3316 done = true;
3317 return;
3318
3319 case DNSFilterEngine::PolicyKind::Truncate:
3320 if (!d_queryReceivedOverTCP) {
3321 ret.clear();
3322 rcode = RCode::NoError;
3323 // Exception handling code in pdns_recursor clears ret as well, so no use to
3324 // fill it here.
3325 throw SendTruncatedAnswerException();
3326 }
3327 return;
3328
3329 case DNSFilterEngine::PolicyKind::Custom: {
3330 if (rpzHitShouldReplaceContent(qname, qtype, ret)) {
3331 ret.clear();
3332 }
3333
3334 rcode = RCode::NoError;
3335 done = true;
3336 auto spoofed = d_appliedPolicy.getCustomRecords(qname, qtype.getCode());
3337 for (auto& dnsRecord : spoofed) {
3338 removeConflictingRecord(ret, dnsRecord.d_name, dnsRecord.d_type);
3339 }
3340
3341 for (auto& dnsRecord : spoofed) {
3342 ret.push_back(dnsRecord);
3343
3344 if (dnsRecord.d_name == qname && dnsRecord.d_type == QType::CNAME && qtype != QType::CNAME) {
3345 if (auto content = getRR<CNAMERecordContent>(dnsRecord)) {
3346 vState newTargetState = vState::Indeterminate;
3347 handleNewTarget(prefix, qname, content->getTarget(), qtype.getCode(), ret, rcode, depth, {}, newTargetState);
3348 }
3349 }
3350 }
3351 d_appliedPolicy.addSOAtoRPZResult(ret);
3352 }
3353 }
3354 }
3355
3356 bool SyncRes::nameserversBlockedByRPZ(const DNSFilterEngine& dfe, const NsSet& nameservers)
3357 {
3358 /* we skip RPZ processing if:
3359 - it was disabled (d_wantsRPZ is false) ;
3360 - we already got a RPZ hit (d_appliedPolicy.d_type != DNSFilterEngine::PolicyType::None) since
3361 the only way we can get back here is that it was a 'pass-thru' (NoAction) meaning that we should not
3362 process any further RPZ rules. Except that we need to process rules of higher priority..
3363 */
3364 if (d_wantsRPZ && !d_appliedPolicy.wasHit()) {
3365 for (auto const& nameserver : nameservers) {
3366 bool match = dfe.getProcessingPolicy(nameserver.first, d_discardedPolicies, d_appliedPolicy);
3367 if (match) {
3368 mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());
3369 if (d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) { // client query needs an RPZ response
3370 LOG(", however nameserver " << nameserver.first << " was blocked by RPZ policy '" << d_appliedPolicy.getName() << "'" << endl);
3371 return true;
3372 }
3373 }
3374
3375 // Traverse all IP addresses for this NS to see if they have an RPN NSIP policy
3376 for (auto const& address : nameserver.second.first) {
3377 match = dfe.getProcessingPolicy(address, d_discardedPolicies, d_appliedPolicy);
3378 if (match) {
3379 mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());
3380 if (d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) { // client query needs an RPZ response
3381 LOG(", however nameserver " << nameserver.first << " IP address " << address.toString() << " was blocked by RPZ policy '" << d_appliedPolicy.getName() << "'" << endl);
3382 return true;
3383 }
3384 }
3385 }
3386 }
3387 }
3388 return false;
3389 }
3390
3391 bool SyncRes::nameserverIPBlockedByRPZ(const DNSFilterEngine& dfe, const ComboAddress& remoteIP)
3392 {
3393 /* we skip RPZ processing if:
3394 - it was disabled (d_wantsRPZ is false) ;
3395 - we already got a RPZ hit (d_appliedPolicy.d_type != DNSFilterEngine::PolicyType::None) since
3396 the only way we can get back here is that it was a 'pass-thru' (NoAction) meaning that we should not
3397 process any further RPZ rules. Except that we need to process rules of higher priority..
3398 */
3399 if (d_wantsRPZ && !d_appliedPolicy.wasHit()) {
3400 bool match = dfe.getProcessingPolicy(remoteIP, d_discardedPolicies, d_appliedPolicy);
3401 if (match) {
3402 mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());
3403 if (d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) {
3404 LOG(" (blocked by RPZ policy '" + d_appliedPolicy.getName() + "')");
3405 return true;
3406 }
3407 }
3408 }
3409 return false;
3410 }
3411
3412 vector<ComboAddress> SyncRes::retrieveAddressesForNS(const std::string& prefix, const DNSName& qname, std::vector<std::pair<DNSName, float>>::const_iterator& tns, const unsigned int depth, set<GetBestNSAnswer>& beenthere, const vector<std::pair<DNSName, float>>& rnameservers, NsSet& nameservers, bool& sendRDQuery, bool& pierceDontQuery, bool& /* flawedNSSet */, bool cacheOnly, unsigned int& nretrieveAddressesForNS)
3413 {
3414 vector<ComboAddress> result;
3415
3416 size_t nonresolvingfails = 0;
3417 if (!tns->first.empty()) {
3418 if (s_nonresolvingnsmaxfails > 0) {
3419 nonresolvingfails = s_nonresolving.lock()->value(tns->first);
3420 if (nonresolvingfails >= s_nonresolvingnsmaxfails) {
3421 LOG(prefix << qname << ": NS " << tns->first << " in non-resolving map, skipping" << endl);
3422 return result;
3423 }
3424 }
3425
3426 LOG(prefix << qname << ": Trying to resolve NS '" << tns->first << "' (" << 1 + tns - rnameservers.begin() << "/" << (unsigned int)rnameservers.size() << ")" << endl);
3427 const unsigned int oldOutQueries = d_outqueries;
3428 try {
3429 result = getAddrs(tns->first, depth, prefix, beenthere, cacheOnly, nretrieveAddressesForNS);
3430 }
3431 // Other exceptions should likely not throttle...
3432 catch (const ImmediateServFailException& ex) {
3433 if (s_nonresolvingnsmaxfails > 0 && d_outqueries > oldOutQueries) {
3434 auto dontThrottleNames = g_dontThrottleNames.getLocal();
3435 if (!dontThrottleNames->check(tns->first)) {
3436 s_nonresolving.lock()->incr(tns->first, d_now);
3437 }
3438 }
3439 throw ex;
3440 }
3441 if (s_nonresolvingnsmaxfails > 0 && d_outqueries > oldOutQueries) {
3442 if (result.empty()) {
3443 auto dontThrottleNames = g_dontThrottleNames.getLocal();
3444 if (!dontThrottleNames->check(tns->first)) {
3445 s_nonresolving.lock()->incr(tns->first, d_now);
3446 }
3447 }
3448 else if (nonresolvingfails > 0) {
3449 // Succeeding resolve, clear memory of recent failures
3450 s_nonresolving.lock()->clear(tns->first);
3451 }
3452 }
3453 pierceDontQuery = false;
3454 }
3455 else {
3456 LOG(prefix << qname << ": Domain has hardcoded nameserver");
3457
3458 if (nameservers[tns->first].first.size() > 1) {
3459 LOG("s");
3460 }
3461 LOG(endl);
3462
3463 sendRDQuery = nameservers[tns->first].second;
3464 result = shuffleForwardSpeed(qname, nameservers[tns->first].first, prefix, sendRDQuery);
3465 pierceDontQuery = true;
3466 }
3467 return result;
3468 }
3469
3470 void SyncRes::checkMaxQperQ(const DNSName& qname) const
3471 {
3472 if (d_outqueries + d_throttledqueries > s_maxqperq) {
3473 throw ImmediateServFailException("more than " + std::to_string(s_maxqperq) + " (max-qperq) queries sent or throttled while resolving " + qname.toLogString());
3474 }
3475 }
3476
3477 bool SyncRes::throttledOrBlocked(const std::string& prefix, const ComboAddress& remoteIP, const DNSName& qname, const QType qtype, bool pierceDontQuery)
3478 {
3479 if (isThrottled(d_now.tv_sec, remoteIP)) {
3480 LOG(prefix << qname << ": Server throttled " << endl);
3481 t_Counters.at(rec::Counter::throttledqueries)++;
3482 d_throttledqueries++;
3483 return true;
3484 }
3485 if (isThrottled(d_now.tv_sec, remoteIP, qname, qtype)) {
3486 LOG(prefix << qname << ": Query throttled " << remoteIP.toString() << ", " << qname << "; " << qtype << endl);
3487 t_Counters.at(rec::Counter::throttledqueries)++;
3488 d_throttledqueries++;
3489 return true;
3490 }
3491 if (!pierceDontQuery && s_dontQuery && s_dontQuery->match(&remoteIP)) {
3492 // We could have retrieved an NS from the cache in a forwarding domain
3493 // Even in the case of !pierceDontQuery we still want to allow that NS
3494 DNSName forwardCandidate(qname);
3495 auto iter = getBestAuthZone(&forwardCandidate);
3496 if (iter == t_sstorage.domainmap->end()) {
3497 LOG(prefix << qname << ": Not sending query to " << remoteIP.toString() << ", blocked by 'dont-query' setting" << endl);
3498 t_Counters.at(rec::Counter::dontqueries)++;
3499 return true;
3500 }
3501 // The name (from the cache) is forwarded, but is it forwarded to an IP in known forwarders?
3502 const auto& ips = iter->second.d_servers;
3503 if (std::find(ips.cbegin(), ips.cend(), remoteIP) == ips.cend()) {
3504 LOG(prefix << qname << ": Not sending query to " << remoteIP.toString() << ", blocked by 'dont-query' setting" << endl);
3505 t_Counters.at(rec::Counter::dontqueries)++;
3506 return true;
3507 }
3508 LOG(prefix << qname << ": Sending query to " << remoteIP.toString() << ", blocked by 'dont-query' but a forwarding/auth case" << endl);
3509 }
3510 return false;
3511 }
3512
3513 bool SyncRes::validationEnabled()
3514 {
3515 return g_dnssecmode != DNSSECMode::Off && g_dnssecmode != DNSSECMode::ProcessNoValidate;
3516 }
3517
3518 uint32_t SyncRes::computeLowestTTD(const std::vector<DNSRecord>& records, const std::vector<std::shared_ptr<const RRSIGRecordContent>>& signatures, uint32_t signaturesTTL, const std::vector<std::shared_ptr<DNSRecord>>& authorityRecs) const
3519 {
3520 uint32_t lowestTTD = std::numeric_limits<uint32_t>::max();
3521 for (const auto& record : records) {
3522 lowestTTD = min(lowestTTD, record.d_ttl);
3523 }
3524
3525 /* even if it was not requested for that request (Process, and neither AD nor DO set),
3526 it might be requested at a later time so we need to be careful with the TTL. */
3527 if (validationEnabled() && !signatures.empty()) {
3528 /* if we are validating, we don't want to cache records after their signatures expire. */
3529 /* records TTL are now TTD, let's add 'now' to the signatures lowest TTL */
3530 lowestTTD = min(lowestTTD, static_cast<uint32_t>(signaturesTTL + d_now.tv_sec));
3531
3532 for (const auto& sig : signatures) {
3533 if (isRRSIGNotExpired(d_now.tv_sec, *sig)) {
3534 // we don't decrement d_sigexpire by 'now' because we actually want a TTD, not a TTL */
3535 lowestTTD = min(lowestTTD, static_cast<uint32_t>(sig->d_sigexpire));
3536 }
3537 }
3538 }
3539
3540 for (const auto& entry : authorityRecs) {
3541 /* be careful, this is still a TTL here */
3542 lowestTTD = min(lowestTTD, static_cast<uint32_t>(entry->d_ttl + d_now.tv_sec));
3543
3544 if (entry->d_type == QType::RRSIG && validationEnabled()) {
3545 auto rrsig = getRR<RRSIGRecordContent>(*entry);
3546 if (rrsig) {
3547 if (isRRSIGNotExpired(d_now.tv_sec, *rrsig)) {
3548 // we don't decrement d_sigexpire by 'now' because we actually want a TTD, not a TTL */
3549 lowestTTD = min(lowestTTD, static_cast<uint32_t>(rrsig->d_sigexpire));
3550 }
3551 }
3552 }
3553 }
3554
3555 return lowestTTD;
3556 }
3557
3558 void SyncRes::updateValidationState(const DNSName& qname, vState& state, const vState stateUpdate, const string& prefix)
3559 {
3560 LOG(prefix << qname << ": Validation state was " << state << ", state update is " << stateUpdate);
3561 updateDNSSECValidationState(state, stateUpdate);
3562 LOG(", validation state is now " << state << endl);
3563 }
3564
3565 vState SyncRes::getTA(const DNSName& zone, dsmap_t& dsMap, const string& prefix)
3566 {
3567 auto luaLocal = g_luaconfs.getLocal();
3568
3569 if (luaLocal->dsAnchors.empty()) {
3570 LOG(prefix << zone << ": No trust anchors configured, everything is Insecure" << endl);
3571 /* We have no TA, everything is insecure */
3572 return vState::Insecure;
3573 }
3574
3575 std::string reason;
3576 if (haveNegativeTrustAnchor(luaLocal->negAnchors, zone, reason)) {
3577 LOG(prefix << zone << ": Got NTA" << endl);
3578 return vState::NTA;
3579 }
3580
3581 if (getTrustAnchor(luaLocal->dsAnchors, zone, dsMap)) {
3582 if (!zone.isRoot()) {
3583 LOG(prefix << zone << ": Got TA" << endl);
3584 }
3585 return vState::TA;
3586 }
3587
3588 if (zone.isRoot()) {
3589 /* No TA for the root */
3590 return vState::Insecure;
3591 }
3592
3593 return vState::Indeterminate;
3594 }
3595
3596 size_t SyncRes::countSupportedDS(const dsmap_t& dsmap, const string& prefix)
3597 {
3598 size_t count = 0;
3599
3600 for (const auto& dsRecordContent : dsmap) {
3601 if (isSupportedDS(dsRecordContent, LogObject(prefix))) {
3602 count++;
3603 }
3604 }
3605
3606 return count;
3607 }
3608
3609 void SyncRes::initZoneCutsFromTA(const DNSName& from, const string& prefix)
3610 {
3611 DNSName zone(from);
3612 do {
3613 dsmap_t dsMap;
3614 vState result = getTA(zone, dsMap, prefix);
3615 if (result != vState::Indeterminate) {
3616 if (result == vState::TA) {
3617 if (countSupportedDS(dsMap, prefix) == 0) {
3618 dsMap.clear();
3619 result = vState::Insecure;
3620 }
3621 else {
3622 result = vState::Secure;
3623 }
3624 }
3625 else if (result == vState::NTA) {
3626 result = vState::Insecure;
3627 }
3628
3629 d_cutStates[zone] = result;
3630 }
3631 } while (zone.chopOff());
3632 }
3633
3634 vState SyncRes::getDSRecords(const DNSName& zone, dsmap_t& dsMap, bool onlyTA, unsigned int depth, const string& prefix, bool bogusOnNXD, bool* foundCut)
3635 {
3636 vState result = getTA(zone, dsMap, prefix);
3637
3638 if (result != vState::Indeterminate || onlyTA) {
3639 if (foundCut != nullptr) {
3640 *foundCut = (result != vState::Indeterminate);
3641 }
3642
3643 if (result == vState::TA) {
3644 if (countSupportedDS(dsMap, prefix) == 0) {
3645 dsMap.clear();
3646 result = vState::Insecure;
3647 }
3648 else {
3649 result = vState::Secure;
3650 }
3651 }
3652 else if (result == vState::NTA) {
3653 result = vState::Insecure;
3654 }
3655
3656 return result;
3657 }
3658
3659 std::set<GetBestNSAnswer> beenthere;
3660 std::vector<DNSRecord> dsrecords;
3661
3662 Context context;
3663
3664 const bool oldCacheOnly = setCacheOnly(false);
3665 const bool oldQM = setQNameMinimization(!getQMFallbackMode());
3666 int rcode = doResolve(zone, QType::DS, dsrecords, depth + 1, beenthere, context);
3667 setCacheOnly(oldCacheOnly);
3668 setQNameMinimization(oldQM);
3669
3670 if (rcode == RCode::ServFail) {
3671 throw ImmediateServFailException("Server Failure while retrieving DS records for " + zone.toLogString());
3672 }
3673
3674 if (rcode != RCode::NoError && (rcode != RCode::NXDomain || bogusOnNXD)) {
3675 LOG(prefix << zone << ": Returning Bogus state from " << static_cast<const char*>(__func__) << "(" << zone << ")" << endl);
3676 return vState::BogusUnableToGetDSs;
3677 }
3678
3679 uint8_t bestDigestType = 0;
3680
3681 bool gotCNAME = false;
3682 for (const auto& record : dsrecords) {
3683 if (record.d_type == QType::DS) {
3684 const auto dscontent = getRR<DSRecordContent>(record);
3685 if (dscontent && isSupportedDS(*dscontent, LogObject(prefix))) {
3686 // Make GOST a lower prio than SHA256
3687 if (dscontent->d_digesttype == DNSSECKeeper::DIGEST_GOST && bestDigestType == DNSSECKeeper::DIGEST_SHA256) {
3688 continue;
3689 }
3690 if (dscontent->d_digesttype > bestDigestType || (bestDigestType == DNSSECKeeper::DIGEST_GOST && dscontent->d_digesttype == DNSSECKeeper::DIGEST_SHA256)) {
3691 bestDigestType = dscontent->d_digesttype;
3692 }
3693 dsMap.insert(*dscontent);
3694 }
3695 }
3696 else if (record.d_type == QType::CNAME && record.d_name == zone) {
3697 gotCNAME = true;
3698 }
3699 }
3700
3701 /* RFC 4509 section 3: "Validator implementations SHOULD ignore DS RRs containing SHA-1
3702 * digests if DS RRs with SHA-256 digests are present in the DS RRset."
3703 * We interpret that as: do not use SHA-1 if SHA-256 or SHA-384 is available
3704 */
3705 for (auto dsrec = dsMap.begin(); dsrec != dsMap.end();) {
3706 if (dsrec->d_digesttype == DNSSECKeeper::DIGEST_SHA1 && dsrec->d_digesttype != bestDigestType) {
3707 dsrec = dsMap.erase(dsrec);
3708 }
3709 else {
3710 ++dsrec;
3711 }
3712 }
3713
3714 if (rcode == RCode::NoError) {
3715 if (dsMap.empty()) {
3716 /* we have no DS, it's either:
3717 - a delegation to a non-DNSSEC signed zone
3718 - no delegation, we stay in the same zone
3719 */
3720 if (gotCNAME || denialProvesNoDelegation(zone, dsrecords)) {
3721 /* we are still inside the same zone */
3722
3723 if (foundCut != nullptr) {
3724 *foundCut = false;
3725 }
3726 return context.state;
3727 }
3728
3729 d_cutStates[zone] = context.state == vState::Secure ? vState::Insecure : context.state;
3730 /* delegation with no DS, might be Secure -> Insecure */
3731 if (foundCut != nullptr) {
3732 *foundCut = true;
3733 }
3734
3735 /* a delegation with no DS is either:
3736 - a signed zone (Secure) to an unsigned one (Insecure)
3737 - an unsigned zone to another unsigned one (Insecure stays Insecure, Bogus stays Bogus)
3738 */
3739 return context.state == vState::Secure ? vState::Insecure : context.state;
3740 }
3741 /* we have a DS */
3742 d_cutStates[zone] = context.state;
3743 if (foundCut != nullptr) {
3744 *foundCut = true;
3745 }
3746 }
3747
3748 return context.state;
3749 }
3750
3751 vState SyncRes::getValidationStatus(const DNSName& name, bool wouldBeValid, bool typeIsDS, unsigned int depth, const string& prefix)
3752 {
3753 vState result = vState::Indeterminate;
3754
3755 if (!shouldValidate()) {
3756 return result;
3757 }
3758
3759 DNSName subdomain(name);
3760 if (typeIsDS) {
3761 subdomain.chopOff();
3762 }
3763
3764 {
3765 const auto& iter = d_cutStates.find(subdomain);
3766 if (iter != d_cutStates.cend()) {
3767 LOG(prefix << name << ": Got status " << iter->second << " for name " << subdomain << endl);
3768 return iter->second;
3769 }
3770 }
3771
3772 /* look for the best match we have */
3773 DNSName best(subdomain);
3774 while (best.chopOff()) {
3775 const auto& iter = d_cutStates.find(best);
3776 if (iter != d_cutStates.cend()) {
3777 result = iter->second;
3778 if (vStateIsBogus(result) || result == vState::Insecure) {
3779 LOG(prefix << name << ": Got status " << result << " for name " << best << endl);
3780 return result;
3781 }
3782 break;
3783 }
3784 }
3785
3786 /* by now we have the best match, it's likely Secure (otherwise we would not be there)
3787 but we don't know if we missed a cut (or several).
3788 We could see if we have DS (or denial of) in cache but let's not worry for now,
3789 we will if we don't have a signature, or if the signer doesn't match what we expect */
3790 if (!wouldBeValid && best != subdomain) {
3791 /* no signatures or Bogus, we likely missed a cut, let's try to find it */
3792 LOG(prefix << name << ": No or invalid signature/proof for " << name << ", we likely missed a cut between " << best << " and " << subdomain << ", looking for it" << endl);
3793 DNSName dsName(best);
3794 std::vector<string> labelsToAdd = subdomain.makeRelative(dsName).getRawLabels();
3795
3796 while (!labelsToAdd.empty()) {
3797
3798 dsName.prependRawLabel(labelsToAdd.back());
3799 labelsToAdd.pop_back();
3800 LOG(prefix << name << ": - Looking for a DS at " << dsName << endl);
3801
3802 bool foundCut = false;
3803 dsmap_t results;
3804 vState dsState = getDSRecords(dsName, results, false, depth, prefix, false, &foundCut);
3805
3806 if (foundCut) {
3807 LOG(prefix << name << ": - Found cut at " << dsName << endl);
3808 LOG(prefix << name << ": New state for " << dsName << " is " << dsState << endl);
3809 d_cutStates[dsName] = dsState;
3810
3811 if (dsState != vState::Secure) {
3812 return dsState;
3813 }
3814 }
3815 }
3816
3817 /* we did not miss a cut, good luck */
3818 return result;
3819 }
3820
3821 #if 0
3822 /* we don't need this, we actually do the right thing later */
3823 DNSName signer = getSigner(signatures);
3824
3825 if (!signer.empty() && name.isPartOf(signer)) {
3826 if (signer == best) {
3827 return result;
3828 }
3829 /* the zone cut is not the one we expected,
3830 this is fine because we will retrieve the needed DNSKEYs and DSs
3831 later, and even go Insecure if we missed a cut to Insecure (no DS)
3832 and the signatures do not validate (we should not go Bogus in that
3833 case) */
3834 }
3835 /* something is not right, but let's not worry about that for now.. */
3836 #endif
3837
3838 return result;
3839 }
3840
3841 vState SyncRes::validateDNSKeys(const DNSName& zone, const std::vector<DNSRecord>& dnskeys, const std::vector<std::shared_ptr<const RRSIGRecordContent>>& signatures, unsigned int depth, const string& prefix)
3842 {
3843 dsmap_t dsMap;
3844 if (signatures.empty()) {
3845 LOG(prefix << zone << ": We have " << std::to_string(dnskeys.size()) << " DNSKEYs but no signature, going Bogus!" << endl);
3846 return vState::BogusNoRRSIG;
3847 }
3848
3849 DNSName signer = getSigner(signatures);
3850
3851 if (!signer.empty() && zone.isPartOf(signer)) {
3852 vState state = getDSRecords(signer, dsMap, false, depth, prefix);
3853
3854 if (state != vState::Secure) {
3855 return state;
3856 }
3857 }
3858 else {
3859 LOG(prefix << zone << ": We have " << std::to_string(dnskeys.size()) << " DNSKEYs but the zone (" << zone << ") is not part of the signer (" << signer << "), check that we did not miss a zone cut" << endl);
3860 /* try again to get the missed cuts, harder this time */
3861 auto zState = getValidationStatus(zone, false, false, depth, prefix);
3862 if (zState == vState::Secure) {
3863 /* too bad */
3864 LOG(prefix << zone << ": After checking the zone cuts again, we still have " << std::to_string(dnskeys.size()) << " DNSKEYs and the zone (" << zone << ") is still not part of the signer (" << signer << "), going Bogus!" << endl);
3865 return vState::BogusNoValidRRSIG;
3866 }
3867 return zState;
3868 }
3869
3870 skeyset_t tentativeKeys;
3871 sortedRecords_t toSign;
3872
3873 for (const auto& dnskey : dnskeys) {
3874 if (dnskey.d_type == QType::DNSKEY) {
3875 auto content = getRR<DNSKEYRecordContent>(dnskey);
3876 if (content) {
3877 tentativeKeys.insert(content);
3878 toSign.insert(content);
3879 }
3880 }
3881 }
3882
3883 LOG(prefix << zone << ": Trying to validate " << std::to_string(tentativeKeys.size()) << " DNSKEYs with " << std::to_string(dsMap.size()) << " DS" << endl);
3884 skeyset_t validatedKeys;
3885 auto state = validateDNSKeysAgainstDS(d_now.tv_sec, zone, dsMap, tentativeKeys, toSign, signatures, validatedKeys, LogObject(prefix));
3886
3887 LOG(prefix << zone << ": We now have " << std::to_string(validatedKeys.size()) << " DNSKEYs" << endl);
3888
3889 /* if we found at least one valid RRSIG covering the set,
3890 all tentative keys are validated keys. Otherwise it means
3891 we haven't found at least one DNSKEY and a matching RRSIG
3892 covering this set, this looks Bogus. */
3893 if (validatedKeys.size() != tentativeKeys.size()) {
3894 LOG(prefix << zone << ": Let's check whether we missed a zone cut before returning a Bogus state from " << static_cast<const char*>(__func__) << "(" << zone << ")" << endl);
3895 /* try again to get the missed cuts, harder this time */
3896 auto zState = getValidationStatus(zone, false, false, depth, prefix);
3897 if (zState == vState::Secure) {
3898 /* too bad */
3899 LOG(prefix << zone << ": After checking the zone cuts we are still in a Secure zone, returning Bogus state from " << static_cast<const char*>(__func__) << "(" << zone << ")" << endl);
3900 return state;
3901 }
3902 return zState;
3903 }
3904
3905 return state;
3906 }
3907
3908 vState SyncRes::getDNSKeys(const DNSName& signer, skeyset_t& keys, bool& servFailOccurred, unsigned int depth, const string& prefix)
3909 {
3910 std::vector<DNSRecord> records;
3911 std::set<GetBestNSAnswer> beenthere;
3912 LOG(prefix << signer << ": Retrieving DNSKEYs" << endl);
3913
3914 Context context;
3915
3916 const bool oldCacheOnly = setCacheOnly(false);
3917 int rcode = doResolve(signer, QType::DNSKEY, records, depth + 1, beenthere, context);
3918 setCacheOnly(oldCacheOnly);
3919
3920 if (rcode == RCode::ServFail) {
3921 servFailOccurred = true;
3922 return vState::BogusUnableToGetDNSKEYs;
3923 }
3924
3925 if (rcode == RCode::NoError) {
3926 if (context.state == vState::Secure) {
3927 for (const auto& key : records) {
3928 if (key.d_type == QType::DNSKEY) {
3929 auto content = getRR<DNSKEYRecordContent>(key);
3930 if (content) {
3931 keys.insert(content);
3932 }
3933 }
3934 }
3935 }
3936 LOG(prefix << signer << ": Retrieved " << keys.size() << " DNSKeys, state is " << context.state << endl);
3937 return context.state;
3938 }
3939
3940 if (context.state == vState::Insecure) {
3941 return context.state;
3942 }
3943
3944 LOG(prefix << signer << ": Returning Bogus state from " << static_cast<const char*>(__func__) << "(" << signer << ")" << endl);
3945 return vState::BogusUnableToGetDNSKEYs;
3946 }
3947
3948 vState SyncRes::validateRecordsWithSigs(unsigned int depth, const string& prefix, const DNSName& qname, const QType qtype, const DNSName& name, const QType type, const std::vector<DNSRecord>& records, const std::vector<std::shared_ptr<const RRSIGRecordContent>>& signatures)
3949 {
3950 skeyset_t keys;
3951 if (signatures.empty()) {
3952 LOG(prefix << qname << ": Bogus!" << endl);
3953 return vState::BogusNoRRSIG;
3954 }
3955
3956 const DNSName signer = getSigner(signatures);
3957 bool dsFailed = false;
3958 if (!signer.empty() && name.isPartOf(signer)) {
3959 vState state = vState::Secure;
3960
3961 if ((qtype == QType::DNSKEY || qtype == QType::DS) && signer == qname) {
3962 /* we are already retrieving those keys, sorry */
3963 if (type == QType::DS && signer == name && !signer.isRoot()) {
3964 /* Unless we are getting the DS of the root zone, we should never see a
3965 DS (or a denial of a DS) signed by the DS itself, since we should be
3966 requesting it from the parent zone. Something is very wrong */
3967 LOG(prefix << qname << ": The DS for " << qname << " is signed by itself" << endl);
3968 state = vState::BogusSelfSignedDS;
3969 dsFailed = true;
3970 }
3971 else if (qtype == QType::DS && signer == qname && !signer.isRoot()) {
3972 if (type == QType::SOA || type == QType::NSEC || type == QType::NSEC3) {
3973 /* if we are trying to validate the DS or more likely NSEC(3)s proving that it does not exist, we have a problem.
3974 In that case let's go Bogus (we will check later if we missed a cut)
3975 */
3976 state = vState::BogusSelfSignedDS;
3977 dsFailed = true;
3978 }
3979 else if (type == QType::CNAME) {
3980 state = vState::BogusUnableToGetDSs;
3981 dsFailed = true;
3982 }
3983 }
3984 else if (qtype == QType::DNSKEY && signer == qname) {
3985 /* that actually does happen when a server returns NS records in authority
3986 along with the DNSKEY, leading us to trying to validate the RRSIGs for
3987 the NS with the DNSKEY that we are about to process. */
3988 if ((name == signer && type == QType::NSEC) || type == QType::NSEC3) {
3989 /* if we are trying to validate the DNSKEY (should not happen here),
3990 or more likely NSEC(3)s proving that it does not exist, we have a problem.
3991 In that case let's see if the DS does exist, and if it does let's go Bogus
3992 */
3993 dsmap_t results;
3994 vState dsState = getDSRecords(signer, results, false, depth, prefix, true);
3995 if (vStateIsBogus(dsState) || dsState == vState::Insecure) {
3996 state = dsState;
3997 if (vStateIsBogus(dsState)) {
3998 dsFailed = true;
3999 }
4000 }
4001 else {
4002 LOG(prefix << qname << ": Unable to get the DS for " << signer << endl);
4003 state = vState::BogusUnableToGetDNSKEYs;
4004 dsFailed = true;
4005 }
4006 }
4007 else {
4008 /* return immediately since looking at the cuts is not going to change the
4009 fact that we are looking at a signature done with the key we are trying to
4010 obtain */
4011 LOG(prefix << qname << ": We are looking at a signature done with the key we are trying to obtain " << signer << endl);
4012 return vState::Indeterminate;
4013 }
4014 }
4015 }
4016 bool servFailOccurred = false;
4017 if (state == vState::Secure) {
4018 state = getDNSKeys(signer, keys, servFailOccurred, depth, prefix);
4019 }
4020
4021 if (state != vState::Secure) {
4022 if (!vStateIsBogus(state)) {
4023 return state;
4024 }
4025 /* try again to get the missed cuts, harder this time */
4026 LOG(prefix << signer << ": Checking whether we missed a zone cut for " << signer << " before returning a Bogus state for " << name << "|" << type.toString() << endl);
4027 auto zState = getValidationStatus(signer, false, dsFailed, depth, prefix);
4028 if (zState == vState::Secure) {
4029 if (state == vState::BogusUnableToGetDNSKEYs && servFailOccurred) {
4030 throw ImmediateServFailException("Server Failure while retrieving DNSKEY records for " + signer.toLogString());
4031 }
4032 /* too bad */
4033 LOG(prefix << signer << ": We are still in a Secure zone, returning " << vStateToString(state) << endl);
4034 return state;
4035 }
4036 return zState;
4037 }
4038 }
4039
4040 sortedRecords_t recordcontents;
4041 for (const auto& record : records) {
4042 recordcontents.insert(record.getContent());
4043 }
4044
4045 LOG(prefix << name << ": Going to validate " << recordcontents.size() << " record contents with " << signatures.size() << " sigs and " << keys.size() << " keys for " << name << "|" << type.toString() << endl);
4046 vState state = validateWithKeySet(d_now.tv_sec, name, recordcontents, signatures, keys, LogObject(prefix), false);
4047 if (state == vState::Secure) {
4048 LOG(prefix << name << ": Secure!" << endl);
4049 return vState::Secure;
4050 }
4051
4052 LOG(prefix << vStateToString(state) << "!" << endl);
4053 /* try again to get the missed cuts, harder this time */
4054 auto zState = getValidationStatus(name, false, type == QType::DS, depth, prefix);
4055 LOG(prefix << name << ": Checking whether we missed a zone cut before returning a Bogus state" << endl);
4056 if (zState == vState::Secure) {
4057 /* too bad */
4058 LOG(prefix << name << ": We are still in a Secure zone, returning " << vStateToString(state) << endl);
4059 return state;
4060 }
4061 return zState;
4062 }
4063
4064 /* This function will check whether the answer should have the AA bit set, and will set if it should be set and isn't.
4065 This is unfortunately needed to deal with very crappy so-called DNS servers */
4066 void SyncRes::fixupAnswer(const std::string& prefix, LWResult& lwr, const DNSName& qname, const QType qtype, const DNSName& auth, bool wasForwarded, bool rdQuery)
4067 {
4068 const bool wasForwardRecurse = wasForwarded && rdQuery;
4069
4070 if (wasForwardRecurse || lwr.d_aabit) {
4071 /* easy */
4072 return;
4073 }
4074
4075 for (const auto& rec : lwr.d_records) {
4076
4077 if (rec.d_type == QType::OPT) {
4078 continue;
4079 }
4080
4081 if (rec.d_class != QClass::IN) {
4082 continue;
4083 }
4084
4085 if (rec.d_type == QType::ANY) {
4086 continue;
4087 }
4088
4089 if (rec.d_place == DNSResourceRecord::ANSWER && (rec.d_type == qtype || rec.d_type == QType::CNAME || qtype == QType::ANY) && rec.d_name == qname && rec.d_name.isPartOf(auth)) {
4090 /* This is clearly an answer to the question we were asking, from an authoritative server that is allowed to send it.
4091 We are going to assume this server is broken and does not know it should set the AA bit, even though it is DNS 101 */
4092 LOG(prefix << qname << ": Received a record for " << rec.d_name << "|" << DNSRecordContent::NumberToType(rec.d_type) << " in the answer section from " << auth << ", without the AA bit set. Assuming this server is clueless and setting the AA bit." << endl);
4093 lwr.d_aabit = true;
4094 return;
4095 }
4096
4097 if (rec.d_place != DNSResourceRecord::ANSWER) {
4098 /* we have scanned all the records in the answer section, if any, we are done */
4099 return;
4100 }
4101 }
4102 }
4103
4104 static void allowAdditionalEntry(std::unordered_set<DNSName>& allowedAdditionals, const DNSRecord& rec)
4105 {
4106 switch (rec.d_type) {
4107 case QType::MX:
4108 if (auto mxContent = getRR<MXRecordContent>(rec)) {
4109 allowedAdditionals.insert(mxContent->d_mxname);
4110 }
4111 break;
4112 case QType::NS:
4113 if (auto nsContent = getRR<NSRecordContent>(rec)) {
4114 allowedAdditionals.insert(nsContent->getNS());
4115 }
4116 break;
4117 case QType::SRV:
4118 if (auto srvContent = getRR<SRVRecordContent>(rec)) {
4119 allowedAdditionals.insert(srvContent->d_target);
4120 }
4121 break;
4122 case QType::SVCB: /* fall-through */
4123 case QType::HTTPS:
4124 if (auto svcbContent = getRR<SVCBBaseRecordContent>(rec)) {
4125 if (svcbContent->getPriority() > 0) {
4126 DNSName target = svcbContent->getTarget();
4127 if (target.isRoot()) {
4128 target = rec.d_name;
4129 }
4130 allowedAdditionals.insert(target);
4131 }
4132 else {
4133 // FIXME: Alias mode not implemented yet
4134 }
4135 }
4136 break;
4137 case QType::NAPTR:
4138 if (auto naptrContent = getRR<NAPTRRecordContent>(rec)) {
4139 auto flags = naptrContent->getFlags();
4140 toLowerInPlace(flags);
4141 if (flags.find('a') != string::npos || flags.find('s') != string::npos) {
4142 allowedAdditionals.insert(naptrContent->getReplacement());
4143 }
4144 }
4145 break;
4146 default:
4147 break;
4148 }
4149 }
4150
4151 void SyncRes::sanitizeRecords(const std::string& prefix, LWResult& lwr, const DNSName& qname, const QType qtype, const DNSName& auth, bool wasForwarded, bool rdQuery)
4152 {
4153 const bool wasForwardRecurse = wasForwarded && rdQuery;
4154 /* list of names for which we will allow A and AAAA records in the additional section
4155 to remain */
4156 std::unordered_set<DNSName> allowedAdditionals = {qname};
4157 bool haveAnswers = false;
4158 bool isNXDomain = false;
4159 bool isNXQType = false;
4160
4161 for (auto rec = lwr.d_records.begin(); rec != lwr.d_records.end();) {
4162
4163 if (rec->d_type == QType::OPT) {
4164 ++rec;
4165 continue;
4166 }
4167
4168 if (rec->d_class != QClass::IN) {
4169 LOG(prefix << qname << ": Removing non internet-classed data received from " << auth << endl);
4170 rec = lwr.d_records.erase(rec);
4171 continue;
4172 }
4173
4174 if (rec->d_type == QType::ANY) {
4175 LOG(prefix << qname << ": Removing 'ANY'-typed data received from " << auth << endl);
4176 rec = lwr.d_records.erase(rec);
4177 continue;
4178 }
4179
4180 if (!rec->d_name.isPartOf(auth)) {
4181 LOG(prefix << qname << ": Removing record '" << rec->d_name << "|" << DNSRecordContent::NumberToType(rec->d_type) << "|" << rec->getContent()->getZoneRepresentation() << "' in the " << (int)rec->d_place << " section received from " << auth << endl);
4182 rec = lwr.d_records.erase(rec);
4183 continue;
4184 }
4185
4186 /* dealing with the records in answer */
4187 if (!(lwr.d_aabit || wasForwardRecurse) && rec->d_place == DNSResourceRecord::ANSWER) {
4188 /* for now we allow a CNAME for the exact qname in ANSWER with AA=0, because Amazon DNS servers
4189 are sending such responses */
4190 if (rec->d_type != QType::CNAME || qname != rec->d_name) {
4191 LOG(prefix << qname << ": Removing record '" << rec->d_name << "|" << DNSRecordContent::NumberToType(rec->d_type) << "|" << rec->getContent()->getZoneRepresentation() << "' in the answer section without the AA bit set received from " << auth << endl);
4192 rec = lwr.d_records.erase(rec);
4193 continue;
4194 }
4195 }
4196
4197 if (rec->d_type == QType::DNAME && (rec->d_place != DNSResourceRecord::ANSWER || !qname.isPartOf(rec->d_name))) {
4198 LOG(prefix << qname << ": Removing invalid DNAME record '" << rec->d_name << "|" << DNSRecordContent::NumberToType(rec->d_type) << "|" << rec->getContent()->getZoneRepresentation() << "' in the " << (int)rec->d_place << " section received from " << auth << endl);
4199 rec = lwr.d_records.erase(rec);
4200 continue;
4201 }
4202
4203 if (rec->d_place == DNSResourceRecord::ANSWER && (qtype != QType::ANY && rec->d_type != qtype.getCode() && s_redirectionQTypes.count(rec->d_type) == 0 && rec->d_type != QType::SOA && rec->d_type != QType::RRSIG)) {
4204 LOG(prefix << qname << ": Removing irrelevant record '" << rec->d_name << "|" << DNSRecordContent::NumberToType(rec->d_type) << "|" << rec->getContent()->getZoneRepresentation() << "' in the ANSWER section received from " << auth << endl);
4205 rec = lwr.d_records.erase(rec);
4206 continue;
4207 }
4208
4209 if (rec->d_place == DNSResourceRecord::ANSWER && !haveAnswers) {
4210 haveAnswers = true;
4211 }
4212
4213 if (rec->d_place == DNSResourceRecord::ANSWER) {
4214 allowAdditionalEntry(allowedAdditionals, *rec);
4215 }
4216
4217 /* dealing with the records in authority */
4218 if (rec->d_place == DNSResourceRecord::AUTHORITY && rec->d_type != QType::NS && rec->d_type != QType::DS && rec->d_type != QType::SOA && rec->d_type != QType::RRSIG && rec->d_type != QType::NSEC && rec->d_type != QType::NSEC3) {
4219 LOG(prefix << qname << ": Removing irrelevant record '" << rec->d_name << "|" << DNSRecordContent::NumberToType(rec->d_type) << "|" << rec->getContent()->getZoneRepresentation() << "' in the AUTHORITY section received from " << auth << endl);
4220 rec = lwr.d_records.erase(rec);
4221 continue;
4222 }
4223
4224 if (rec->d_place == DNSResourceRecord::AUTHORITY && rec->d_type == QType::SOA) {
4225 if (!qname.isPartOf(rec->d_name)) {
4226 LOG(prefix << qname << ": Removing irrelevant SOA record '" << rec->d_name << "|" << rec->getContent()->getZoneRepresentation() << "' in the AUTHORITY section received from " << auth << endl);
4227 rec = lwr.d_records.erase(rec);
4228 continue;
4229 }
4230
4231 if (!(lwr.d_aabit || wasForwardRecurse)) {
4232 LOG(prefix << qname << ": Removing irrelevant record (AA not set) '" << rec->d_name << "|" << DNSRecordContent::NumberToType(rec->d_type) << "|" << rec->getContent()->getZoneRepresentation() << "' in the AUTHORITY section received from " << auth << endl);
4233 rec = lwr.d_records.erase(rec);
4234 continue;
4235 }
4236
4237 if (!haveAnswers) {
4238 if (lwr.d_rcode == RCode::NXDomain) {
4239 isNXDomain = true;
4240 }
4241 else if (lwr.d_rcode == RCode::NoError) {
4242 isNXQType = true;
4243 }
4244 }
4245 }
4246
4247 if (rec->d_place == DNSResourceRecord::AUTHORITY && rec->d_type == QType::NS && (isNXDomain || isNXQType)) {
4248 /*
4249 * We don't want to pick up NS records in AUTHORITY and their ADDITIONAL sections of NXDomain answers
4250 * because they are somewhat easy to insert into a large, fragmented UDP response
4251 * for an off-path attacker by injecting spoofed UDP fragments. So do not add these to allowedAdditionals.
4252 */
4253 LOG(prefix << qname << ": Removing NS record '" << rec->d_name << "|" << DNSRecordContent::NumberToType(rec->d_type) << "|" << rec->getContent()->getZoneRepresentation() << "' in the " << (int)rec->d_place << " section of a " << (isNXDomain ? "NXD" : "NXQTYPE") << " response received from " << auth << endl);
4254 rec = lwr.d_records.erase(rec);
4255 continue;
4256 }
4257
4258 if (rec->d_place == DNSResourceRecord::AUTHORITY && rec->d_type == QType::NS && !d_updatingRootNS && rec->d_name == g_rootdnsname) {
4259 /*
4260 * We don't want to pick up root NS records in AUTHORITY and their associated ADDITIONAL sections of random queries.
4261 * So don't add them to allowedAdditionals.
4262 */
4263 LOG(prefix << qname << ": Removing NS record '" << rec->d_name << "|" << DNSRecordContent::NumberToType(rec->d_type) << "|" << rec->getContent()->getZoneRepresentation() << "' in the " << (int)rec->d_place << " section of a response received from " << auth << endl);
4264 rec = lwr.d_records.erase(rec);
4265 continue;
4266 }
4267
4268 if (rec->d_place == DNSResourceRecord::AUTHORITY && rec->d_type == QType::NS) {
4269 allowAdditionalEntry(allowedAdditionals, *rec);
4270 }
4271
4272 /* dealing with the records in additional */
4273 if (rec->d_place == DNSResourceRecord::ADDITIONAL && rec->d_type != QType::A && rec->d_type != QType::AAAA && rec->d_type != QType::RRSIG) {
4274 LOG(prefix << qname << ": Removing irrelevant record '" << rec->d_name << "|" << DNSRecordContent::NumberToType(rec->d_type) << "|" << rec->getContent()->getZoneRepresentation() << "' in the ADDITIONAL section received from " << auth << endl);
4275 rec = lwr.d_records.erase(rec);
4276 continue;
4277 }
4278
4279 if (rec->d_place == DNSResourceRecord::ADDITIONAL && allowedAdditionals.count(rec->d_name) == 0) {
4280 LOG(prefix << qname << ": Removing irrelevant additional record '" << rec->d_name << "|" << DNSRecordContent::NumberToType(rec->d_type) << "|" << rec->getContent()->getZoneRepresentation() << "' in the ADDITIONAL section received from " << auth << endl);
4281 rec = lwr.d_records.erase(rec);
4282 continue;
4283 }
4284
4285 ++rec;
4286 }
4287 }
4288
4289 void SyncRes::rememberParentSetIfNeeded(const DNSName& domain, const vector<DNSRecord>& newRecords, unsigned int depth, const string& prefix)
4290 {
4291 vector<DNSRecord> existing;
4292 bool wasAuth = false;
4293 auto ttl = g_recCache->get(d_now.tv_sec, domain, QType::NS, MemRecursorCache::None, &existing, d_cacheRemote, d_routingTag, nullptr, nullptr, nullptr, nullptr, &wasAuth);
4294
4295 if (ttl <= 0 || wasAuth) {
4296 return;
4297 }
4298 {
4299 auto lock = s_savedParentNSSet.lock();
4300 if (lock->find(domain) != lock->end()) {
4301 // no relevant data, or we already stored the parent data
4302 return;
4303 }
4304 }
4305
4306 set<DNSName> authSet;
4307 for (const auto& dnsRecord : newRecords) {
4308 auto content = getRR<NSRecordContent>(dnsRecord);
4309 authSet.insert(content->getNS());
4310 }
4311 // The glue IPs could also differ, but we're not checking that yet, we're only looking for parent NS records not
4312 // in the child set
4313 bool shouldSave = false;
4314 for (const auto& dnsRecord : existing) {
4315 auto content = getRR<NSRecordContent>(dnsRecord);
4316 if (authSet.count(content->getNS()) == 0) {
4317 LOG(prefix << domain << ": At least one parent-side NS was not in the child-side NS set, remembering parent NS set and cached IPs" << endl);
4318 shouldSave = true;
4319 break;
4320 }
4321 }
4322
4323 if (shouldSave) {
4324 map<DNSName, vector<ComboAddress>> entries;
4325 for (const auto& dnsRecord : existing) {
4326 auto content = getRR<NSRecordContent>(dnsRecord);
4327 const DNSName& name = content->getNS();
4328 set<GetBestNSAnswer> beenthereIgnored;
4329 unsigned int nretrieveAddressesForNSIgnored{};
4330 auto addresses = getAddrs(name, depth, prefix, beenthereIgnored, true, nretrieveAddressesForNSIgnored);
4331 entries.emplace(name, addresses);
4332 }
4333 s_savedParentNSSet.lock()->emplace(domain, std::move(entries), d_now.tv_sec + ttl);
4334 }
4335 }
4336
4337 RCode::rcodes_ SyncRes::updateCacheFromRecords(unsigned int depth, const string& prefix, LWResult& lwr, const DNSName& qname, const QType qtype, const DNSName& auth, bool wasForwarded, const boost::optional<Netmask>& ednsmask, vState& state, bool& needWildcardProof, bool& gatherWildcardProof, unsigned int& wildcardLabelsCount, bool rdQuery, const ComboAddress& remoteIP) // NOLINT(readability-function-cognitive-complexity)
4338 {
4339 bool wasForwardRecurse = wasForwarded && rdQuery;
4340 tcache_t tcache;
4341
4342 fixupAnswer(prefix, lwr, qname, qtype, auth, wasForwarded, rdQuery);
4343 sanitizeRecords(prefix, lwr, qname, qtype, auth, wasForwarded, rdQuery);
4344
4345 std::vector<std::shared_ptr<DNSRecord>> authorityRecs;
4346 const unsigned int labelCount = qname.countLabels();
4347 bool isCNAMEAnswer = false;
4348 bool isDNAMEAnswer = false;
4349 DNSName seenAuth;
4350
4351 for (auto& rec : lwr.d_records) {
4352 if (rec.d_type == QType::OPT || rec.d_class != QClass::IN) {
4353 continue;
4354 }
4355
4356 rec.d_ttl = min(s_maxcachettl, rec.d_ttl);
4357
4358 if (!isCNAMEAnswer && rec.d_place == DNSResourceRecord::ANSWER && rec.d_type == QType::CNAME && (!(qtype == QType::CNAME)) && rec.d_name == qname && !isDNAMEAnswer) {
4359 isCNAMEAnswer = true;
4360 }
4361 if (!isDNAMEAnswer && rec.d_place == DNSResourceRecord::ANSWER && rec.d_type == QType::DNAME && qtype != QType::DNAME && qname.isPartOf(rec.d_name)) {
4362 isDNAMEAnswer = true;
4363 isCNAMEAnswer = false;
4364 }
4365
4366 if (rec.d_type == QType::SOA && rec.d_place == DNSResourceRecord::AUTHORITY && qname.isPartOf(rec.d_name)) {
4367 seenAuth = rec.d_name;
4368 }
4369
4370 if (rec.d_type == QType::RRSIG) {
4371 auto rrsig = getRR<RRSIGRecordContent>(rec);
4372 if (rrsig) {
4373 /* As illustrated in rfc4035's Appendix B.6, the RRSIG label
4374 count can be lower than the name's label count if it was
4375 synthesized from the wildcard. Note that the difference might
4376 be > 1. */
4377 if (rec.d_name == qname && isWildcardExpanded(labelCount, *rrsig)) {
4378 gatherWildcardProof = true;
4379 if (!isWildcardExpandedOntoItself(rec.d_name, labelCount, *rrsig)) {
4380 /* if we have a wildcard expanded onto itself, we don't need to prove
4381 that the exact name doesn't exist because it actually does.
4382 We still want to gather the corresponding NSEC/NSEC3 records
4383 to pass them to our client in case it wants to validate by itself.
4384 */
4385 LOG(prefix << qname << ": RRSIG indicates the name was synthesized from a wildcard, we need a wildcard proof" << endl);
4386 needWildcardProof = true;
4387 }
4388 else {
4389 LOG(prefix << qname << ": RRSIG indicates the name was synthesized from a wildcard expanded onto itself, we need to gather wildcard proof" << endl);
4390 }
4391 wildcardLabelsCount = rrsig->d_labels;
4392 }
4393
4394 // cerr<<"Got an RRSIG for "<<DNSRecordContent::NumberToType(rrsig->d_type)<<" with name '"<<rec.d_name<<"' and place "<<rec.d_place<<endl;
4395 tcache[{rec.d_name, rrsig->d_type, rec.d_place}].signatures.push_back(rrsig);
4396 tcache[{rec.d_name, rrsig->d_type, rec.d_place}].signaturesTTL = std::min(tcache[{rec.d_name, rrsig->d_type, rec.d_place}].signaturesTTL, rec.d_ttl);
4397 }
4398 }
4399 }
4400
4401 /* if we have a positive answer synthesized from a wildcard,
4402 we need to store the corresponding NSEC/NSEC3 records proving
4403 that the exact name did not exist in the negative cache */
4404 if (gatherWildcardProof) {
4405 for (const auto& rec : lwr.d_records) {
4406 if (rec.d_type == QType::OPT || rec.d_class != QClass::IN) {
4407 continue;
4408 }
4409
4410 if (nsecTypes.count(rec.d_type) != 0) {
4411 authorityRecs.push_back(std::make_shared<DNSRecord>(rec));
4412 }
4413 else if (rec.d_type == QType::RRSIG) {
4414 auto rrsig = getRR<RRSIGRecordContent>(rec);
4415 if (rrsig && nsecTypes.count(rrsig->d_type) != 0) {
4416 authorityRecs.push_back(std::make_shared<DNSRecord>(rec));
4417 }
4418 }
4419 }
4420 }
4421
4422 // reap all answers from this packet that are acceptable
4423 for (auto& rec : lwr.d_records) {
4424 if (rec.d_type == QType::OPT) {
4425 LOG(prefix << qname << ": OPT answer '" << rec.d_name << "' from '" << auth << "' nameservers" << endl);
4426 continue;
4427 }
4428
4429 LOG(prefix << qname << ": Accept answer '" << rec.d_name << "|" << DNSRecordContent::NumberToType(rec.d_type) << "|" << rec.getContent()->getZoneRepresentation() << "' from '" << auth << "' nameservers? ttl=" << rec.d_ttl << ", place=" << (int)rec.d_place << " ");
4430
4431 // We called sanitizeRecords before, so all ANY, non-IN and non-aa/non-forwardrecurse answer records are already removed
4432
4433 if (rec.d_name.isPartOf(auth)) {
4434 if (rec.d_type == QType::RRSIG) {
4435 LOG("RRSIG - separate" << endl);
4436 }
4437 else if (rec.d_type == QType::DS && rec.d_name == auth) {
4438 LOG("NO - DS provided by child zone" << endl);
4439 }
4440 else {
4441 bool haveLogged = false;
4442 if (isDNAMEAnswer && rec.d_type == QType::CNAME) {
4443 LOG("NO - we already have a DNAME answer for this domain" << endl);
4444 continue;
4445 }
4446 if (!t_sstorage.domainmap->empty()) {
4447 // Check if we are authoritative for a zone in this answer
4448 DNSName tmp_qname(rec.d_name);
4449 // We may be auth for domain example.com, but the DS record needs to come from the parent (.com) nameserver
4450 if (rec.d_type == QType::DS) {
4451 tmp_qname.chopOff();
4452 }
4453 auto auth_domain_iter = getBestAuthZone(&tmp_qname);
4454 if (auth_domain_iter != t_sstorage.domainmap->end() && auth.countLabels() <= auth_domain_iter->first.countLabels()) {
4455 if (auth_domain_iter->first != auth) {
4456 LOG("NO! - we are authoritative for the zone " << auth_domain_iter->first << endl);
4457 continue;
4458 }
4459 LOG("YES! - This answer was ");
4460 if (!wasForwarded) {
4461 LOG("retrieved from the local auth store.");
4462 }
4463 else {
4464 LOG("received from a server we forward to.");
4465 }
4466 haveLogged = true;
4467 LOG(endl);
4468 }
4469 }
4470 if (!haveLogged) {
4471 LOG("YES!" << endl);
4472 }
4473
4474 rec.d_ttl = min(s_maxcachettl, rec.d_ttl);
4475
4476 DNSRecord dnsRecord(rec);
4477 tcache[{rec.d_name, rec.d_type, rec.d_place}].d_ttl_time = d_now.tv_sec;
4478 dnsRecord.d_ttl += d_now.tv_sec;
4479 dnsRecord.d_place = DNSResourceRecord::ANSWER;
4480 tcache[{rec.d_name, rec.d_type, rec.d_place}].records.push_back(dnsRecord);
4481 }
4482 }
4483 else
4484 LOG("NO!" << endl);
4485 }
4486
4487 // supplant
4488 for (auto& entry : tcache) {
4489 if ((entry.second.records.size() + entry.second.signatures.size() + authorityRecs.size()) > 1) { // need to group the ttl to be the minimum of the RRSET (RFC 2181, 5.2)
4490 uint32_t lowestTTD = computeLowestTTD(entry.second.records, entry.second.signatures, entry.second.signaturesTTL, authorityRecs);
4491
4492 for (auto& record : entry.second.records) {
4493 record.d_ttl = lowestTTD; // boom
4494 }
4495 }
4496 }
4497
4498 for (auto tCacheEntry = tcache.begin(); tCacheEntry != tcache.end(); ++tCacheEntry) {
4499
4500 if (tCacheEntry->second.records.empty()) { // this happens when we did store signatures, but passed on the records themselves
4501 continue;
4502 }
4503
4504 /* Even if the AA bit is set, additional data cannot be considered
4505 as authoritative. This is especially important during validation
4506 because keeping records in the additional section is allowed even
4507 if the corresponding RRSIGs are not included, without setting the TC
4508 bit, as stated in rfc4035's section 3.1.1. Including RRSIG RRs in a Response:
4509 "When placing a signed RRset in the Additional section, the name
4510 server MUST also place its RRSIG RRs in the Additional section.
4511 If space does not permit inclusion of both the RRset and its
4512 associated RRSIG RRs, the name server MAY retain the RRset while
4513 dropping the RRSIG RRs. If this happens, the name server MUST NOT
4514 set the TC bit solely because these RRSIG RRs didn't fit."
4515 */
4516 bool isAA = lwr.d_aabit && tCacheEntry->first.place != DNSResourceRecord::ADDITIONAL;
4517 /* if we forwarded the query to a recursor, we can expect the answer to be signed,
4518 even if the answer is not AA. Of course that's not only true inside a Secure
4519 zone, but we check that below. */
4520 bool expectSignature = tCacheEntry->first.place == DNSResourceRecord::ANSWER || ((lwr.d_aabit || wasForwardRecurse) && tCacheEntry->first.place != DNSResourceRecord::ADDITIONAL);
4521 /* in a non authoritative answer, we only care about the DS record (or lack of) */
4522 if (!isAA && (tCacheEntry->first.type == QType::DS || tCacheEntry->first.type == QType::NSEC || tCacheEntry->first.type == QType::NSEC3) && tCacheEntry->first.place == DNSResourceRecord::AUTHORITY) {
4523 expectSignature = true;
4524 }
4525
4526 if (isCNAMEAnswer && (tCacheEntry->first.place != DNSResourceRecord::ANSWER || tCacheEntry->first.type != QType::CNAME || tCacheEntry->first.name != qname)) {
4527 /*
4528 rfc2181 states:
4529 Note that the answer section of an authoritative answer normally
4530 contains only authoritative data. However when the name sought is an
4531 alias (see section 10.1.1) only the record describing that alias is
4532 necessarily authoritative. Clients should assume that other records
4533 may have come from the server's cache. Where authoritative answers
4534 are required, the client should query again, using the canonical name
4535 associated with the alias.
4536 */
4537 isAA = false;
4538 expectSignature = false;
4539 }
4540 if (isDNAMEAnswer && (tCacheEntry->first.place != DNSResourceRecord::ANSWER || tCacheEntry->first.type != QType::DNAME || !qname.isPartOf(tCacheEntry->first.name))) {
4541 /* see above */
4542 isAA = false;
4543 expectSignature = false;
4544 }
4545
4546 if ((isCNAMEAnswer || isDNAMEAnswer) && tCacheEntry->first.place == DNSResourceRecord::AUTHORITY && tCacheEntry->first.type == QType::NS && auth == tCacheEntry->first.name) {
4547 /* These NS can't be authoritative since we have a CNAME/DNAME answer for which (see above) only the
4548 record describing that alias is necessarily authoritative.
4549 But if we allow the current auth, which might be serving the child zone, to raise the TTL
4550 of non-authoritative NS in the cache, they might be able to keep a "ghost" zone alive forever,
4551 even after the delegation is gone from the parent.
4552 So let's just do nothing with them, we can fetch them directly if we need them.
4553 */
4554 LOG(prefix << qname << ": Skipping authority NS from '" << auth << "' nameservers in CNAME/DNAME answer " << tCacheEntry->first.name << "|" << DNSRecordContent::NumberToType(tCacheEntry->first.type) << endl);
4555 continue;
4556 }
4557
4558 /*
4559 * RFC 6672 section 5.3.1
4560 * In any response, a signed DNAME RR indicates a non-terminal
4561 * redirection of the query. There might or might not be a server-
4562 * synthesized CNAME in the answer section; if there is, the CNAME will
4563 * never be signed. For a DNSSEC validator, verification of the DNAME
4564 * RR and then that the CNAME was properly synthesized is sufficient
4565 * proof.
4566 *
4567 * We do the synthesis check in processRecords, here we make sure we
4568 * don't validate the CNAME.
4569 */
4570 if (isDNAMEAnswer && tCacheEntry->first.type == QType::CNAME) {
4571 expectSignature = false;
4572 }
4573
4574 vState recordState = vState::Indeterminate;
4575
4576 if (expectSignature && shouldValidate()) {
4577 vState initialState = getValidationStatus(tCacheEntry->first.name, !tCacheEntry->second.signatures.empty(), tCacheEntry->first.type == QType::DS, depth, prefix);
4578 LOG(prefix << qname << ": Got initial zone status " << initialState << " for record " << tCacheEntry->first.name << "|" << DNSRecordContent::NumberToType(tCacheEntry->first.type) << endl);
4579
4580 if (initialState == vState::Secure) {
4581 if (tCacheEntry->first.type == QType::DNSKEY && tCacheEntry->first.place == DNSResourceRecord::ANSWER && tCacheEntry->first.name == getSigner(tCacheEntry->second.signatures)) {
4582 LOG(prefix << qname << ": Validating DNSKEY for " << tCacheEntry->first.name << endl);
4583 recordState = validateDNSKeys(tCacheEntry->first.name, tCacheEntry->second.records, tCacheEntry->second.signatures, depth, prefix);
4584 }
4585 else {
4586 LOG(prefix << qname << ": Validating non-additional " << QType(tCacheEntry->first.type).toString() << " record for " << tCacheEntry->first.name << endl);
4587 recordState = validateRecordsWithSigs(depth, prefix, qname, qtype, tCacheEntry->first.name, QType(tCacheEntry->first.type), tCacheEntry->second.records, tCacheEntry->second.signatures);
4588 }
4589 }
4590 else {
4591 recordState = initialState;
4592 LOG(prefix << qname << ": Skipping validation because the current state is " << recordState << endl);
4593 }
4594
4595 LOG(prefix << qname << ": Validation result is " << recordState << ", current state is " << state << endl);
4596 if (state != recordState) {
4597 updateValidationState(qname, state, recordState, prefix);
4598 }
4599 }
4600
4601 if (vStateIsBogus(recordState)) {
4602 /* this is a TTD by now, be careful */
4603 for (auto& record : tCacheEntry->second.records) {
4604 auto newval = std::min(record.d_ttl, static_cast<uint32_t>(s_maxbogusttl + d_now.tv_sec));
4605 record.d_ttl = newval;
4606 }
4607 tCacheEntry->second.d_ttl_time = d_now.tv_sec;
4608 }
4609
4610 /* We don't need to store NSEC3 records in the positive cache because:
4611 - we don't allow direct NSEC3 queries
4612 - denial of existence proofs in wildcard expanded positive responses are stored in authorityRecs
4613 - denial of existence proofs for negative responses are stored in the negative cache
4614 We also don't want to cache non-authoritative data except for:
4615 - records coming from non forward-recurse servers (those will never be AA)
4616 - DS (special case)
4617 - NS, A and AAAA (used for infra queries)
4618 */
4619 if (tCacheEntry->first.type != QType::NSEC3 && (tCacheEntry->first.type == QType::DS || tCacheEntry->first.type == QType::NS || tCacheEntry->first.type == QType::A || tCacheEntry->first.type == QType::AAAA || isAA || wasForwardRecurse)) {
4620
4621 bool doCache = true;
4622 if (tCacheEntry->first.place == DNSResourceRecord::ANSWER && ednsmask) {
4623 const bool isv4 = ednsmask->isIPv4();
4624 if ((isv4 && s_ecsipv4nevercache) || (!isv4 && s_ecsipv6nevercache)) {
4625 doCache = false;
4626 }
4627 // If ednsmask is relevant, we do not want to cache if the scope prefix length is large and TTL is small
4628 if (doCache && s_ecscachelimitttl > 0) {
4629 bool manyMaskBits = (isv4 && ednsmask->getBits() > s_ecsipv4cachelimit) || (!isv4 && ednsmask->getBits() > s_ecsipv6cachelimit);
4630
4631 if (manyMaskBits) {
4632 uint32_t minttl = UINT32_MAX;
4633 for (const auto& iter : tCacheEntry->second.records) {
4634 if (iter.d_ttl < minttl) {
4635 minttl = iter.d_ttl;
4636 }
4637 }
4638 bool ttlIsSmall = minttl < s_ecscachelimitttl + d_now.tv_sec;
4639 if (ttlIsSmall) {
4640 // Case: many bits and ttlIsSmall
4641 doCache = false;
4642 }
4643 }
4644 }
4645 }
4646
4647 d_fromAuthIP = remoteIP;
4648
4649 if (doCache) {
4650 // Check if we are going to replace a non-auth (parent) NS recordset
4651 if (isAA && tCacheEntry->first.type == QType::NS && s_save_parent_ns_set) {
4652 rememberParentSetIfNeeded(tCacheEntry->first.name, tCacheEntry->second.records, depth, prefix);
4653 }
4654 g_recCache->replace(d_now.tv_sec, tCacheEntry->first.name, tCacheEntry->first.type, tCacheEntry->second.records, tCacheEntry->second.signatures, authorityRecs, tCacheEntry->first.type == QType::DS ? true : isAA, auth, tCacheEntry->first.place == DNSResourceRecord::ANSWER ? ednsmask : boost::none, d_routingTag, recordState, remoteIP, d_refresh, tCacheEntry->second.d_ttl_time);
4655
4656 // Delete potential negcache entry. When a record recovers with serve-stale the negcache entry can cause the wrong entry to
4657 // be served, as negcache entries are checked before record cache entries
4658 if (NegCache::s_maxServedStaleExtensions > 0) {
4659 g_negCache->wipeTyped(tCacheEntry->first.name, tCacheEntry->first.type);
4660 }
4661
4662 if (g_aggressiveNSECCache && needWildcardProof && recordState == vState::Secure && tCacheEntry->first.place == DNSResourceRecord::ANSWER && tCacheEntry->first.name == qname && !tCacheEntry->second.signatures.empty() && !d_routingTag && !ednsmask) {
4663 /* we have an answer synthesized from a wildcard and aggressive NSEC is enabled, we need to store the
4664 wildcard in its non-expanded form in the cache to be able to synthesize wildcard answers later */
4665 const auto& rrsig = tCacheEntry->second.signatures.at(0);
4666
4667 if (isWildcardExpanded(labelCount, *rrsig) && !isWildcardExpandedOntoItself(tCacheEntry->first.name, labelCount, *rrsig)) {
4668 DNSName realOwner = getNSECOwnerName(tCacheEntry->first.name, tCacheEntry->second.signatures);
4669
4670 std::vector<DNSRecord> content;
4671 content.reserve(tCacheEntry->second.records.size());
4672 for (const auto& record : tCacheEntry->second.records) {
4673 DNSRecord nonExpandedRecord(record);
4674 nonExpandedRecord.d_name = realOwner;
4675 content.push_back(std::move(nonExpandedRecord));
4676 }
4677
4678 g_recCache->replace(d_now.tv_sec, realOwner, QType(tCacheEntry->first.type), content, tCacheEntry->second.signatures, /* no additional records in that case */ {}, tCacheEntry->first.type == QType::DS ? true : isAA, auth, boost::none, boost::none, recordState, remoteIP, d_refresh, tCacheEntry->second.d_ttl_time);
4679 }
4680 }
4681 }
4682 }
4683
4684 if (seenAuth.empty() && !tCacheEntry->second.signatures.empty()) {
4685 seenAuth = getSigner(tCacheEntry->second.signatures);
4686 }
4687
4688 if (g_aggressiveNSECCache && (tCacheEntry->first.type == QType::NSEC || tCacheEntry->first.type == QType::NSEC3) && recordState == vState::Secure && !seenAuth.empty()) {
4689 // Good candidate for NSEC{,3} caching
4690 g_aggressiveNSECCache->insertNSEC(seenAuth, tCacheEntry->first.name, tCacheEntry->second.records.at(0), tCacheEntry->second.signatures, tCacheEntry->first.type == QType::NSEC3);
4691 }
4692
4693 if (tCacheEntry->first.place == DNSResourceRecord::ANSWER && ednsmask) {
4694 d_wasVariable = true;
4695 }
4696 }
4697
4698 return RCode::NoError;
4699 }
4700
4701 void SyncRes::updateDenialValidationState(const DNSName& qname, vState& neValidationState, const DNSName& neName, vState& state, const dState denialState, const dState expectedState, bool isDS, unsigned int depth, const string& prefix)
4702 {
4703 if (denialState == expectedState) {
4704 neValidationState = vState::Secure;
4705 }
4706 else {
4707 if (denialState == dState::OPTOUT) {
4708 LOG(prefix << qname << ": OPT-out denial found for " << neName << endl);
4709 /* rfc5155 states:
4710 "The AD bit, as defined by [RFC4035], MUST NOT be set when returning a
4711 response containing a closest (provable) encloser proof in which the
4712 NSEC3 RR that covers the "next closer" name has the Opt-Out bit set.
4713
4714 This rule is based on what this closest encloser proof actually
4715 proves: names that would be covered by the Opt-Out NSEC3 RR may or
4716 may not exist as insecure delegations. As such, not all the data in
4717 responses containing such closest encloser proofs will have been
4718 cryptographically verified, so the AD bit cannot be set."
4719
4720 At best the Opt-Out NSEC3 RR proves that there is no signed DS (so no
4721 secure delegation).
4722 */
4723 neValidationState = vState::Insecure;
4724 }
4725 else if (denialState == dState::INSECURE) {
4726 LOG(prefix << qname << ": Insecure denial found for " << neName << ", returning Insecure" << endl);
4727 neValidationState = vState::Insecure;
4728 }
4729 else {
4730 LOG(prefix << qname << ": Invalid denial found for " << neName << ", res=" << denialState << ", expectedState=" << expectedState << ", checking whether we have missed a zone cut before returning a Bogus state" << endl);
4731 /* try again to get the missed cuts, harder this time */
4732 auto zState = getValidationStatus(neName, false, isDS, depth, prefix);
4733 if (zState != vState::Secure) {
4734 neValidationState = zState;
4735 }
4736 else {
4737 LOG(prefix << qname << ": Still in a secure zone with an invalid denial for " << neName << ", returning " << vStateToString(vState::BogusInvalidDenial) << endl);
4738 neValidationState = vState::BogusInvalidDenial;
4739 }
4740 }
4741 }
4742 updateValidationState(qname, state, neValidationState, prefix);
4743 }
4744
4745 dState SyncRes::getDenialValidationState(const NegCache::NegCacheEntry& negEntry, const dState expectedState, bool referralToUnsigned, const string& prefix)
4746 {
4747 cspmap_t csp = harvestCSPFromNE(negEntry);
4748 return getDenial(csp, negEntry.d_name, negEntry.d_qtype.getCode(), referralToUnsigned, expectedState == dState::NXQTYPE, LogObject(prefix));
4749 }
4750
4751 bool SyncRes::processRecords(const std::string& prefix, const DNSName& qname, const QType qtype, const DNSName& auth, LWResult& lwr, const bool sendRDQuery, vector<DNSRecord>& ret, set<DNSName>& nsset, DNSName& newtarget, DNSName& newauth, bool& realreferral, bool& negindic, vState& state, const bool needWildcardProof, const bool gatherWildcardProof, const unsigned int wildcardLabelsCount, int& rcode, bool& negIndicHasSignatures, unsigned int depth) // // NOLINT(readability-function-cognitive-complexity)
4752 {
4753 bool done = false;
4754 DNSName dnameTarget;
4755 DNSName dnameOwner;
4756 uint32_t dnameTTL = 0;
4757 bool referralOnDS = false;
4758
4759 for (auto& rec : lwr.d_records) {
4760 if (rec.d_type == QType::OPT || rec.d_class != QClass::IN) {
4761 continue;
4762 }
4763
4764 if (rec.d_place == DNSResourceRecord::ANSWER && !(lwr.d_aabit || sendRDQuery)) {
4765 /* for now we allow a CNAME for the exact qname in ANSWER with AA=0, because Amazon DNS servers
4766 are sending such responses */
4767 if (rec.d_type != QType::CNAME || rec.d_name != qname) {
4768 continue;
4769 }
4770 }
4771 const bool negCacheIndication = rec.d_place == DNSResourceRecord::AUTHORITY && rec.d_type == QType::SOA && lwr.d_rcode == RCode::NXDomain && qname.isPartOf(rec.d_name) && rec.d_name.isPartOf(auth);
4772
4773 bool putInNegCache = true;
4774 if (negCacheIndication && qtype == QType::DS && isForwardOrAuth(qname)) {
4775 // #10189, a NXDOMAIN to a DS query for a forwarded or auth domain should not NXDOMAIN the whole domain
4776 putInNegCache = false;
4777 }
4778
4779 if (negCacheIndication) {
4780 LOG(prefix << qname << ": Got negative caching indication for name '" << qname << "' (accept=" << rec.d_name.isPartOf(auth) << "), newtarget='" << newtarget << "'" << endl);
4781
4782 rec.d_ttl = min(rec.d_ttl, s_maxnegttl);
4783 // only add a SOA if we're not going anywhere after this
4784 if (newtarget.empty()) {
4785 ret.push_back(rec);
4786 }
4787
4788 NegCache::NegCacheEntry negEntry;
4789
4790 uint32_t lowestTTL = rec.d_ttl;
4791 /* if we get an NXDomain answer with a CNAME, the name
4792 does exist but the target does not */
4793 negEntry.d_name = newtarget.empty() ? qname : newtarget;
4794 negEntry.d_qtype = QType::ENT; // this encodes 'whole record'
4795 negEntry.d_auth = rec.d_name;
4796 harvestNXRecords(lwr.d_records, negEntry, d_now.tv_sec, &lowestTTL);
4797
4798 if (vStateIsBogus(state)) {
4799 negEntry.d_validationState = state;
4800 }
4801 else {
4802 /* here we need to get the validation status of the zone telling us that the domain does not
4803 exist, ie the owner of the SOA */
4804 auto recordState = getValidationStatus(rec.d_name, !negEntry.authoritySOA.signatures.empty() || !negEntry.DNSSECRecords.signatures.empty(), false, depth, prefix);
4805 if (recordState == vState::Secure) {
4806 dState denialState = getDenialValidationState(negEntry, dState::NXDOMAIN, false, prefix);
4807 updateDenialValidationState(qname, negEntry.d_validationState, negEntry.d_name, state, denialState, dState::NXDOMAIN, false, depth, prefix);
4808 }
4809 else {
4810 negEntry.d_validationState = recordState;
4811 updateValidationState(qname, state, negEntry.d_validationState, prefix);
4812 }
4813 }
4814
4815 if (vStateIsBogus(negEntry.d_validationState)) {
4816 lowestTTL = min(lowestTTL, s_maxbogusttl);
4817 }
4818
4819 negEntry.d_ttd = d_now.tv_sec + lowestTTL;
4820 negEntry.d_orig_ttl = lowestTTL;
4821 /* if we get an NXDomain answer with a CNAME, let's not cache the
4822 target, even the server was authoritative for it,
4823 and do an additional query for the CNAME target.
4824 We have a regression test making sure we do exactly that.
4825 */
4826 if (newtarget.empty() && putInNegCache) {
4827 g_negCache->add(negEntry);
4828 // doCNAMECacheCheck() checks record cache and does not look into negcache. That means that an old record might be found if
4829 // serve-stale is active. Avoid that by explicitly zapping that CNAME record.
4830 if (qtype == QType::CNAME && MemRecursorCache::s_maxServedStaleExtensions > 0) {
4831 g_recCache->doWipeCache(qname, false, qtype);
4832 }
4833 if (s_rootNXTrust && negEntry.d_auth.isRoot() && auth.isRoot() && lwr.d_aabit) {
4834 negEntry.d_name = negEntry.d_name.getLastLabel();
4835 g_negCache->add(negEntry);
4836 }
4837 }
4838
4839 negIndicHasSignatures = !negEntry.authoritySOA.signatures.empty() || !negEntry.DNSSECRecords.signatures.empty();
4840 negindic = true;
4841 }
4842 else if (rec.d_place == DNSResourceRecord::ANSWER && s_redirectionQTypes.count(rec.d_type) > 0 && // CNAME or DNAME answer
4843 s_redirectionQTypes.count(qtype.getCode()) == 0) { // But not in response to a CNAME or DNAME query
4844 if (rec.d_type == QType::CNAME && rec.d_name == qname) {
4845 if (!dnameOwner.empty()) { // We synthesize ourselves
4846 continue;
4847 }
4848 ret.push_back(rec);
4849 if (auto content = getRR<CNAMERecordContent>(rec)) {
4850 newtarget = DNSName(content->getTarget());
4851 }
4852 }
4853 else if (rec.d_type == QType::DNAME && qname.isPartOf(rec.d_name)) { // DNAME
4854 ret.push_back(rec);
4855 if (auto content = getRR<DNAMERecordContent>(rec)) {
4856 dnameOwner = rec.d_name;
4857 dnameTarget = content->getTarget();
4858 dnameTTL = rec.d_ttl;
4859 if (!newtarget.empty()) { // We had a CNAME before, remove it from ret so we don't cache it
4860 ret.erase(std::remove_if(
4861 ret.begin(),
4862 ret.end(),
4863 [&qname](DNSRecord& dnsrecord) {
4864 return (dnsrecord.d_place == DNSResourceRecord::ANSWER && dnsrecord.d_type == QType::CNAME && dnsrecord.d_name == qname);
4865 }),
4866 ret.end());
4867 }
4868 try {
4869 newtarget = qname.makeRelative(dnameOwner) + dnameTarget;
4870 }
4871 catch (const std::exception& e) {
4872 // We should probably catch an std::range_error here and set the rcode to YXDOMAIN (RFC 6672, section 2.2)
4873 // But there is no way to set the RCODE from this function
4874 throw ImmediateServFailException("Unable to perform DNAME substitution(DNAME owner: '" + dnameOwner.toLogString() + "', DNAME target: '" + dnameTarget.toLogString() + "', substituted name: '" + qname.makeRelative(dnameOwner).toLogString() + "." + dnameTarget.toLogString() + "' : " + e.what());
4875 }
4876 }
4877 }
4878 }
4879 /* if we have a positive answer synthesized from a wildcard, we need to
4880 return the corresponding NSEC/NSEC3 records from the AUTHORITY section
4881 proving that the exact name did not exist.
4882 Except if this is a NODATA answer because then we will gather the NXNSEC records later */
4883 else if (gatherWildcardProof && !negindic && (rec.d_type == QType::RRSIG || rec.d_type == QType::NSEC || rec.d_type == QType::NSEC3) && rec.d_place == DNSResourceRecord::AUTHORITY) {
4884 ret.push_back(rec); // enjoy your DNSSEC
4885 }
4886 // for ANY answers we *must* have an authoritative answer, unless we are forwarding recursively
4887 else if (rec.d_place == DNSResourceRecord::ANSWER && rec.d_name == qname && (rec.d_type == qtype.getCode() || ((lwr.d_aabit || sendRDQuery) && qtype == QType::ANY))) {
4888 LOG(prefix << qname << ": Answer is in: resolved to '" << rec.getContent()->getZoneRepresentation() << "|" << DNSRecordContent::NumberToType(rec.d_type) << "'" << endl);
4889
4890 done = true;
4891 rcode = RCode::NoError;
4892
4893 if (needWildcardProof) {
4894 /* positive answer synthesized from a wildcard */
4895 NegCache::NegCacheEntry negEntry;
4896 negEntry.d_name = qname;
4897 negEntry.d_qtype = QType::ENT; // this encodes 'whole record'
4898 uint32_t lowestTTL = rec.d_ttl;
4899 harvestNXRecords(lwr.d_records, negEntry, d_now.tv_sec, &lowestTTL);
4900
4901 if (vStateIsBogus(state)) {
4902 negEntry.d_validationState = state;
4903 }
4904 else {
4905 auto recordState = getValidationStatus(qname, !negEntry.authoritySOA.signatures.empty() || !negEntry.DNSSECRecords.signatures.empty(), false, depth, prefix);
4906
4907 if (recordState == vState::Secure) {
4908 /* We have a positive answer synthesized from a wildcard, we need to check that we have
4909 proof that the exact name doesn't exist so the wildcard can be used,
4910 as described in section 5.3.4 of RFC 4035 and 5.3 of RFC 7129.
4911 */
4912 cspmap_t csp = harvestCSPFromNE(negEntry);
4913 dState res = getDenial(csp, qname, negEntry.d_qtype.getCode(), false, false, LogObject(prefix), false, wildcardLabelsCount);
4914 if (res != dState::NXDOMAIN) {
4915 vState tmpState = vState::BogusInvalidDenial;
4916 if (res == dState::INSECURE || res == dState::OPTOUT) {
4917 /* Some part could not be validated, for example a NSEC3 record with a too large number of iterations,
4918 this is not enough to warrant a Bogus, but go Insecure. */
4919 tmpState = vState::Insecure;
4920 LOG(prefix << qname << ": Unable to validate denial in wildcard expanded positive response found for " << qname << ", returning Insecure, res=" << res << endl);
4921 }
4922 else {
4923 LOG(prefix << qname << ": Invalid denial in wildcard expanded positive response found for " << qname << ", returning Bogus, res=" << res << endl);
4924 rec.d_ttl = std::min(rec.d_ttl, s_maxbogusttl);
4925 }
4926
4927 updateValidationState(qname, state, tmpState, prefix);
4928 /* we already stored the record with a different validation status, let's fix it */
4929 updateValidationStatusInCache(qname, qtype, lwr.d_aabit, tmpState);
4930 }
4931 }
4932 }
4933 }
4934
4935 ret.push_back(rec);
4936 }
4937 else if ((rec.d_type == QType::RRSIG || rec.d_type == QType::NSEC || rec.d_type == QType::NSEC3) && rec.d_place == DNSResourceRecord::ANSWER) {
4938 if (rec.d_type != QType::RRSIG || rec.d_name == qname) {
4939 ret.push_back(rec); // enjoy your DNSSEC
4940 }
4941 else if (rec.d_type == QType::RRSIG && qname.isPartOf(rec.d_name)) {
4942 auto rrsig = getRR<RRSIGRecordContent>(rec);
4943 if (rrsig != nullptr && rrsig->d_type == QType::DNAME) {
4944 ret.push_back(rec);
4945 }
4946 }
4947 }
4948 else if (rec.d_place == DNSResourceRecord::AUTHORITY && rec.d_type == QType::NS && qname.isPartOf(rec.d_name)) {
4949 if (moreSpecificThan(rec.d_name, auth)) {
4950 newauth = rec.d_name;
4951 LOG(prefix << qname << ": Got NS record '" << rec.d_name << "' -> '" << rec.getContent()->getZoneRepresentation() << "'" << endl);
4952
4953 /* check if we have a referral from the parent zone to a child zone for a DS query, which is not right */
4954 if (qtype == QType::DS && (newauth.isPartOf(qname) || qname == newauth)) {
4955 /* just got a referral from the parent zone when asking for a DS, looks like this server did not get the DNSSEC memo.. */
4956 referralOnDS = true;
4957 }
4958 else {
4959 realreferral = true;
4960 if (auto content = getRR<NSRecordContent>(rec)) {
4961 nsset.insert(content->getNS());
4962 }
4963 }
4964 }
4965 else {
4966 LOG(prefix << qname << ": Got upwards/level NS record '" << rec.d_name << "' -> '" << rec.getContent()->getZoneRepresentation() << "', had '" << auth << "'" << endl);
4967 if (auto content = getRR<NSRecordContent>(rec)) {
4968 nsset.insert(content->getNS());
4969 }
4970 }
4971 }
4972 else if (rec.d_place == DNSResourceRecord::AUTHORITY && rec.d_type == QType::DS && qname.isPartOf(rec.d_name)) {
4973 LOG(prefix << qname << ": Got DS record '" << rec.d_name << "' -> '" << rec.getContent()->getZoneRepresentation() << "'" << endl);
4974 }
4975 else if (realreferral && rec.d_place == DNSResourceRecord::AUTHORITY && (rec.d_type == QType::NSEC || rec.d_type == QType::NSEC3) && newauth.isPartOf(auth)) {
4976 /* we might have received a denial of the DS, let's check */
4977 NegCache::NegCacheEntry negEntry;
4978 uint32_t lowestTTL = rec.d_ttl;
4979 harvestNXRecords(lwr.d_records, negEntry, d_now.tv_sec, &lowestTTL);
4980
4981 if (!vStateIsBogus(state)) {
4982 auto recordState = getValidationStatus(newauth, !negEntry.authoritySOA.signatures.empty() || !negEntry.DNSSECRecords.signatures.empty(), true, depth, prefix);
4983
4984 if (recordState == vState::Secure) {
4985 negEntry.d_auth = auth;
4986 negEntry.d_name = newauth;
4987 negEntry.d_qtype = QType::DS;
4988 rec.d_ttl = min(s_maxnegttl, rec.d_ttl);
4989
4990 dState denialState = getDenialValidationState(negEntry, dState::NXQTYPE, true, prefix);
4991
4992 if (denialState == dState::NXQTYPE || denialState == dState::OPTOUT || denialState == dState::INSECURE) {
4993 negEntry.d_ttd = lowestTTL + d_now.tv_sec;
4994 negEntry.d_orig_ttl = lowestTTL;
4995 negEntry.d_validationState = vState::Secure;
4996 if (denialState == dState::OPTOUT) {
4997 negEntry.d_validationState = vState::Insecure;
4998 }
4999 LOG(prefix << qname << ": Got negative indication of DS record for '" << newauth << "'" << endl);
5000
5001 g_negCache->add(negEntry);
5002
5003 /* Careful! If the client is asking for a DS that does not exist, we need to provide the SOA along with the NSEC(3) proof
5004 and we might not have it if we picked up the proof from a delegation, in which case we need to keep on to do the actual DS
5005 query. */
5006 if (qtype == QType::DS && qname == newauth && (d_externalDSQuery.empty() || qname != d_externalDSQuery)) {
5007 /* we are actually done! */
5008 negindic = true;
5009 negIndicHasSignatures = !negEntry.authoritySOA.signatures.empty() || !negEntry.DNSSECRecords.signatures.empty();
5010 nsset.clear();
5011 }
5012 }
5013 }
5014 }
5015 }
5016 else if (!done && rec.d_place == DNSResourceRecord::AUTHORITY && rec.d_type == QType::SOA && lwr.d_rcode == RCode::NoError && qname.isPartOf(rec.d_name)) {
5017 LOG(prefix << qname << ": Got negative caching indication for '" << qname << "|" << qtype << "'" << endl);
5018
5019 if (!newtarget.empty()) {
5020 LOG(prefix << qname << ": Hang on! Got a redirect to '" << newtarget << "' already" << endl);
5021 }
5022 else {
5023 rec.d_ttl = min(s_maxnegttl, rec.d_ttl);
5024
5025 NegCache::NegCacheEntry negEntry;
5026 negEntry.d_auth = rec.d_name;
5027 uint32_t lowestTTL = rec.d_ttl;
5028 negEntry.d_name = qname;
5029 negEntry.d_qtype = qtype;
5030 harvestNXRecords(lwr.d_records, negEntry, d_now.tv_sec, &lowestTTL);
5031
5032 if (vStateIsBogus(state)) {
5033 negEntry.d_validationState = state;
5034 }
5035 else {
5036 auto recordState = getValidationStatus(qname, !negEntry.authoritySOA.signatures.empty() || !negEntry.DNSSECRecords.signatures.empty(), qtype == QType::DS, depth, prefix);
5037 if (recordState == vState::Secure) {
5038 dState denialState = getDenialValidationState(negEntry, dState::NXQTYPE, false, prefix);
5039 updateDenialValidationState(qname, negEntry.d_validationState, negEntry.d_name, state, denialState, dState::NXQTYPE, qtype == QType::DS, depth, prefix);
5040 }
5041 else {
5042 negEntry.d_validationState = recordState;
5043 updateValidationState(qname, state, negEntry.d_validationState, prefix);
5044 }
5045 }
5046
5047 if (vStateIsBogus(negEntry.d_validationState)) {
5048 lowestTTL = min(lowestTTL, s_maxbogusttl);
5049 rec.d_ttl = min(rec.d_ttl, s_maxbogusttl);
5050 }
5051 negEntry.d_ttd = d_now.tv_sec + lowestTTL;
5052 negEntry.d_orig_ttl = lowestTTL;
5053 if (qtype.getCode() != 0) { // prevents us from NXDOMAIN'ing a whole domain
5054 // doCNAMECacheCheck() checks record cache and does not look into negcache. That means that an old record might be found if
5055 // serve-stale is active. Avoid that by explicitly zapping that CNAME record.
5056 if (qtype == QType::CNAME && MemRecursorCache::s_maxServedStaleExtensions > 0) {
5057 g_recCache->doWipeCache(qname, false, qtype);
5058 }
5059 g_negCache->add(negEntry);
5060 }
5061
5062 ret.push_back(rec);
5063 negindic = true;
5064 negIndicHasSignatures = !negEntry.authoritySOA.signatures.empty() || !negEntry.DNSSECRecords.signatures.empty();
5065 }
5066 }
5067 }
5068
5069 if (!dnameTarget.empty()) {
5070 // Synthesize a CNAME
5071 auto cnamerec = DNSRecord();
5072 cnamerec.d_name = qname;
5073 cnamerec.d_type = QType::CNAME;
5074 cnamerec.d_ttl = dnameTTL;
5075 cnamerec.setContent(std::make_shared<CNAMERecordContent>(CNAMERecordContent(newtarget)));
5076 ret.push_back(std::move(cnamerec));
5077 }
5078
5079 /* If we have seen a proper denial, let's forget that we also had a referral for a DS query.
5080 Otherwise we need to deal with it. */
5081 if (referralOnDS && !negindic) {
5082 LOG(prefix << qname << ": Got a referral to the child zone for a DS query without a negative indication (missing SOA in authority), treating that as a NODATA" << endl);
5083 if (!vStateIsBogus(state)) {
5084 auto recordState = getValidationStatus(qname, false, true, depth, prefix);
5085 if (recordState == vState::Secure) {
5086 /* we are in a secure zone, got a referral to the child zone on a DS query, no denial, that's wrong */
5087 LOG(prefix << qname << ": NODATA without a negative indication (missing SOA in authority) in a DNSSEC secure zone, going Bogus" << endl);
5088 updateValidationState(qname, state, vState::BogusMissingNegativeIndication, prefix);
5089 }
5090 }
5091 negindic = true;
5092 negIndicHasSignatures = false;
5093 }
5094
5095 return done;
5096 }
5097
5098 static void submitTryDotTask(ComboAddress address, const DNSName& auth, const DNSName& nsname, time_t now)
5099 {
5100 if (address.getPort() == 853) {
5101 return;
5102 }
5103 address.setPort(853);
5104 auto lock = s_dotMap.lock();
5105 if (lock->d_numBusy >= SyncRes::s_max_busy_dot_probes) {
5106 return;
5107 }
5108 auto iter = lock->d_map.emplace(DoTStatus{address, auth, now + dotFailWait}).first;
5109 if (iter->d_status == DoTStatus::Busy) {
5110 return;
5111 }
5112 if (iter->d_ttd > now) {
5113 if (iter->d_status == DoTStatus::Bad) {
5114 return;
5115 }
5116 if (iter->d_status == DoTStatus::Good) {
5117 return;
5118 }
5119 // We only want to probe auths that we have seen before, auth that only come around once are not interesting
5120 if (iter->d_status == DoTStatus::Unknown && iter->d_count == 0) {
5121 return;
5122 }
5123 }
5124 lock->d_map.modify(iter, [=](DoTStatus& status) { status.d_ttd = now + dotFailWait; });
5125 bool pushed = pushTryDoTTask(auth, QType::SOA, address, std::numeric_limits<time_t>::max(), nsname);
5126 if (pushed) {
5127 iter->d_status = DoTStatus::Busy;
5128 ++lock->d_numBusy;
5129 }
5130 }
5131
5132 static bool shouldDoDoT(ComboAddress address, time_t now)
5133 {
5134 address.setPort(853);
5135 auto lock = s_dotMap.lock();
5136 auto iter = lock->d_map.find(address);
5137 if (iter == lock->d_map.end()) {
5138 return false;
5139 }
5140 iter->d_count++;
5141 return iter->d_status == DoTStatus::Good && iter->d_ttd > now;
5142 }
5143
5144 static void updateDoTStatus(ComboAddress address, DoTStatus::Status status, time_t time, bool updateBusy = false)
5145 {
5146 address.setPort(853);
5147 auto lock = s_dotMap.lock();
5148 auto iter = lock->d_map.find(address);
5149 if (iter != lock->d_map.end()) {
5150 iter->d_status = status;
5151 lock->d_map.modify(iter, [=](DoTStatus& statusToModify) { statusToModify.d_ttd = time; });
5152 if (updateBusy) {
5153 --lock->d_numBusy;
5154 }
5155 }
5156 }
5157
5158 bool SyncRes::tryDoT(const DNSName& qname, const QType qtype, const DNSName& nsName, ComboAddress address, time_t now)
5159 {
5160 auto log = g_slog->withName("taskq")->withValues("method", Logging::Loggable("tryDoT"), "name", Logging::Loggable(qname), "qtype", Logging::Loggable(QType(qtype).toString()), "ip", Logging::Loggable(address));
5161
5162 auto logHelper1 = [&log](const string& ename) {
5163 log->info(Logr::Debug, "Failed to probe DoT records, got an exception", "exception", Logging::Loggable(ename));
5164 };
5165 auto logHelper2 = [&log](const string& msg, const string& ename) {
5166 log->error(Logr::Debug, msg, "Failed to probe DoT records, got an exception", "exception", Logging::Loggable(ename));
5167 };
5168 LWResult lwr;
5169 bool truncated{};
5170 bool spoofed{};
5171 boost::optional<Netmask> netmask;
5172 address.setPort(853);
5173 // We use the fact that qname equals auth
5174 bool isOK = false;
5175 try {
5176 boost::optional<EDNSExtendedError> extendedError;
5177 isOK = doResolveAtThisIP("", qname, qtype, lwr, netmask, qname, false, false, nsName, address, true, true, truncated, spoofed, extendedError, true);
5178 isOK = isOK && lwr.d_rcode == RCode::NoError && !lwr.d_records.empty();
5179 }
5180 catch (const PDNSException& e) {
5181 logHelper2(e.reason, "PDNSException");
5182 }
5183 catch (const ImmediateServFailException& e) {
5184 logHelper2(e.reason, "ImmediateServFailException");
5185 }
5186 catch (const PolicyHitException& e) {
5187 logHelper1("PolicyHitException");
5188 }
5189 catch (const std::exception& e) {
5190 logHelper2(e.what(), "std::exception");
5191 }
5192 catch (...) {
5193 logHelper1("other");
5194 }
5195 updateDoTStatus(address, isOK ? DoTStatus::Good : DoTStatus::Bad, now + (isOK ? dotSuccessWait : dotFailWait), true);
5196 return isOK;
5197 }
5198
5199 void SyncRes::ednsStats(boost::optional<Netmask>& ednsmask, const DNSName& qname, const string& prefix)
5200 {
5201 if (!ednsmask) {
5202 return;
5203 }
5204 s_ecsresponses++;
5205 LOG(prefix << qname << ": Received EDNS Client Subnet Mask " << ednsmask->toString() << " on response" << endl);
5206
5207 if (ednsmask->getBits() > 0) {
5208 if (ednsmask->isIPv4()) {
5209 ++SyncRes::s_ecsResponsesBySubnetSize4.at(ednsmask->getBits() - 1);
5210 }
5211 else {
5212 ++SyncRes::s_ecsResponsesBySubnetSize6.at(ednsmask->getBits() - 1);
5213 }
5214 }
5215 }
5216
5217 void SyncRes::updateQueryCounts(const string& prefix, const DNSName& qname, const ComboAddress& address, bool doTCP, bool doDoT)
5218 {
5219 t_Counters.at(rec::Counter::outqueries)++;
5220 d_outqueries++;
5221 checkMaxQperQ(qname);
5222 if (address.sin4.sin_family == AF_INET6) {
5223 t_Counters.at(rec::Counter::ipv6queries)++;
5224 }
5225 if (doTCP) {
5226 if (doDoT) {
5227 LOG(prefix << qname << ": Using DoT with " << address.toStringWithPort() << endl);
5228 t_Counters.at(rec::Counter::dotoutqueries)++;
5229 d_dotoutqueries++;
5230 }
5231 else {
5232 LOG(prefix << qname << ": Using TCP with " << address.toStringWithPort() << endl);
5233 t_Counters.at(rec::Counter::tcpoutqueries)++;
5234 d_tcpoutqueries++;
5235 }
5236 }
5237 }
5238
5239 bool SyncRes::doResolveAtThisIP(const std::string& prefix, const DNSName& qname, const QType qtype, LWResult& lwr, boost::optional<Netmask>& ednsmask, const DNSName& auth, bool const sendRDQuery, const bool wasForwarded, const DNSName& nsName, const ComboAddress& remoteIP, bool doTCP, bool doDoT, bool& truncated, bool& spoofed, boost::optional<EDNSExtendedError>& extendedError, bool dontThrottle) // NOLINT(readability-function-cognitive-complexity)
5240 {
5241 bool chained = false;
5242 LWResult::Result resolveret = LWResult::Result::Success;
5243
5244 if (s_maxtotusec != 0 && d_totUsec > s_maxtotusec) {
5245 if (s_addExtendedResolutionDNSErrors) {
5246 extendedError = EDNSExtendedError{static_cast<uint16_t>(EDNSExtendedError::code::NoReachableAuthority), "Timeout waiting for answer(s)"};
5247 }
5248 throw ImmediateServFailException("Too much time waiting for " + qname.toLogString() + "|" + qtype.toString() + ", timeouts: " + std::to_string(d_timeouts) + ", throttles: " + std::to_string(d_throttledqueries) + ", queries: " + std::to_string(d_outqueries) + ", " + std::to_string(d_totUsec / 1000) + " ms");
5249 }
5250
5251 int preOutQueryRet = RCode::NoError;
5252 if (d_pdl && d_pdl->preoutquery(remoteIP, d_requestor, qname, qtype, doTCP, lwr.d_records, preOutQueryRet, d_eventTrace, timeval{0, 0})) {
5253 LOG(prefix << qname << ": Query handled by Lua" << endl);
5254 }
5255 else {
5256 ednsmask = getEDNSSubnetMask(qname, remoteIP);
5257 if (ednsmask) {
5258 LOG(prefix << qname << ": Adding EDNS Client Subnet Mask " << ednsmask->toString() << " to query" << endl);
5259 s_ecsqueries++;
5260 }
5261 updateQueryCounts(prefix, qname, remoteIP, doTCP, doDoT);
5262 resolveret = asyncresolveWrapper(remoteIP, d_doDNSSEC, qname, auth, qtype.getCode(),
5263 doTCP, sendRDQuery, &d_now, ednsmask, &lwr, &chained, nsName); // <- we go out on the wire!
5264 ednsStats(ednsmask, qname, prefix);
5265 }
5266
5267 /* preoutquery killed the query by setting dq.rcode to -3 */
5268 if (preOutQueryRet == -3) {
5269 throw ImmediateServFailException("Query killed by policy");
5270 }
5271
5272 d_totUsec += lwr.d_usec;
5273
5274 if (resolveret == LWResult::Result::Spoofed) {
5275 spoofed = true;
5276 return false;
5277 }
5278
5279 accountAuthLatency(lwr.d_usec, remoteIP.sin4.sin_family);
5280 ++t_Counters.at(rec::RCode::auth).rcodeCounters.at(static_cast<uint8_t>(lwr.d_rcode));
5281
5282 if (!dontThrottle) {
5283 auto dontThrottleNames = g_dontThrottleNames.getLocal();
5284 auto dontThrottleNetmasks = g_dontThrottleNetmasks.getLocal();
5285 dontThrottle = dontThrottleNames->check(nsName) || dontThrottleNetmasks->match(remoteIP);
5286 }
5287
5288 if (resolveret != LWResult::Result::Success) {
5289 /* Error while resolving */
5290 if (resolveret == LWResult::Result::Timeout) {
5291 /* Time out */
5292
5293 LOG(prefix << qname << ": Timeout resolving after " << lwr.d_usec / 1000.0 << " ms " << (doTCP ? "over TCP" : "") << endl);
5294 d_timeouts++;
5295 t_Counters.at(rec::Counter::outgoingtimeouts)++;
5296
5297 if (remoteIP.sin4.sin_family == AF_INET) {
5298 t_Counters.at(rec::Counter::outgoing4timeouts)++;
5299 }
5300 else {
5301 t_Counters.at(rec::Counter::outgoing6timeouts)++;
5302 }
5303
5304 if (t_timeouts) {
5305 t_timeouts->push_back(remoteIP);
5306 }
5307 }
5308 else if (resolveret == LWResult::Result::OSLimitError) {
5309 /* OS resource limit reached */
5310 LOG(prefix << qname << ": Hit a local resource limit resolving" << (doTCP ? " over TCP" : "") << ", probable error: " << stringerror() << endl);
5311 t_Counters.at(rec::Counter::resourceLimits)++;
5312 }
5313 else {
5314 /* LWResult::Result::PermanentError */
5315 t_Counters.at(rec::Counter::unreachables)++;
5316 d_unreachables++;
5317 // XXX questionable use of errno
5318 LOG(prefix << qname << ": Error resolving from " << remoteIP.toString() << (doTCP ? " over TCP" : "") << ", possible error: " << stringerror() << endl);
5319 }
5320
5321 // don't account for resource limits, they are our own fault
5322 // And don't throttle when the IP address is on the dontThrottleNetmasks list or the name is part of dontThrottleNames
5323 if (resolveret != LWResult::Result::OSLimitError && !chained && !dontThrottle) {
5324 s_nsSpeeds.lock()->find_or_enter(nsName.empty() ? DNSName(remoteIP.toStringWithPort()) : nsName, d_now).submit(remoteIP, 1000000, d_now); // 1 sec
5325
5326 // make sure we don't throttle the root
5327 if (s_serverdownmaxfails > 0 && auth != g_rootdnsname && s_fails.lock()->incr(remoteIP, d_now) >= s_serverdownmaxfails) {
5328 LOG(prefix << qname << ": Max fails reached resolving on " << remoteIP.toString() << ". Going full throttle for " << s_serverdownthrottletime << " seconds" << endl);
5329 // mark server as down
5330 doThrottle(d_now.tv_sec, remoteIP, s_serverdownthrottletime, 10000);
5331 }
5332 else if (resolveret == LWResult::Result::PermanentError) {
5333 // unreachable, 1 minute or 100 queries
5334 doThrottle(d_now.tv_sec, remoteIP, qname, qtype, 60, 100);
5335 }
5336 else {
5337 // timeout, 10 seconds or 5 queries
5338 doThrottle(d_now.tv_sec, remoteIP, qname, qtype, 10, 5);
5339 }
5340 }
5341
5342 return false;
5343 }
5344
5345 if (!lwr.d_validpacket) {
5346 LOG(prefix << qname << ": " << nsName << " (" << remoteIP.toString() << ") returned a packet we could not parse over " << (doTCP ? "TCP" : "UDP") << ", trying sibling IP or NS" << endl);
5347 if (!chained && !dontThrottle) {
5348
5349 // let's make sure we prefer a different server for some time, if there is one available
5350 s_nsSpeeds.lock()->find_or_enter(nsName.empty() ? DNSName(remoteIP.toStringWithPort()) : nsName, d_now).submit(remoteIP, 1000000, d_now); // 1 sec
5351
5352 if (doTCP) {
5353 // we can be more heavy-handed over TCP
5354 doThrottle(d_now.tv_sec, remoteIP, qname, qtype, 60, 10);
5355 }
5356 else {
5357 doThrottle(d_now.tv_sec, remoteIP, qname, qtype, 10, 2);
5358 }
5359 }
5360 return false;
5361 }
5362 /* we got an answer */
5363 if (lwr.d_rcode != RCode::NoError && lwr.d_rcode != RCode::NXDomain) {
5364 LOG(prefix << qname << ": " << nsName << " (" << remoteIP.toString() << ") returned a " << RCode::to_s(lwr.d_rcode) << ", trying sibling IP or NS" << endl);
5365 if (!chained && !dontThrottle) {
5366 if (wasForwarded && lwr.d_rcode == RCode::ServFail) {
5367 // rather than throttling what could be the only server we have for this destination, let's make sure we try a different one if there is one available
5368 // on the other hand, we might keep hammering a server under attack if there is no other alternative, or the alternative is overwhelmed as well, but
5369 // at the very least we will detect that if our packets stop being answered
5370 s_nsSpeeds.lock()->find_or_enter(nsName.empty() ? DNSName(remoteIP.toStringWithPort()) : nsName, d_now).submit(remoteIP, 1000000, d_now); // 1 sec
5371 }
5372 else {
5373 doThrottle(d_now.tv_sec, remoteIP, qname, qtype, 60, 3);
5374 }
5375 }
5376 return false;
5377 }
5378
5379 /* this server sent a valid answer, mark it backup up if it was down */
5380 if (s_serverdownmaxfails > 0) {
5381 s_fails.lock()->clear(remoteIP);
5382 }
5383 // Clear all throttles for this IP, both general and specific throttles for qname-qtype
5384 unThrottle(remoteIP, qname, qtype);
5385
5386 if (lwr.d_tcbit) {
5387 truncated = true;
5388
5389 if (doTCP) {
5390 LOG(prefix << qname << ": Truncated bit set, over TCP?" << endl);
5391 if (!dontThrottle) {
5392 /* let's treat that as a ServFail answer from this server */
5393 doThrottle(d_now.tv_sec, remoteIP, qname, qtype, 60, 3);
5394 }
5395 return false;
5396 }
5397 LOG(prefix << qname << ": Truncated bit set, over UDP" << endl);
5398
5399 return true;
5400 }
5401
5402 return true;
5403 }
5404
5405 void SyncRes::handleNewTarget(const std::string& prefix, const DNSName& qname, const DNSName& newtarget, const QType qtype, std::vector<DNSRecord>& ret, int& rcode, unsigned int depth, const std::vector<DNSRecord>& recordsFromAnswer, vState& state)
5406 {
5407 if (newtarget == qname) {
5408 LOG(prefix << qname << ": Status=got a CNAME referral to self, returning SERVFAIL" << endl);
5409 ret.clear();
5410 rcode = RCode::ServFail;
5411 return;
5412 }
5413 if (newtarget.isPartOf(qname)) {
5414 // a.b.c. CNAME x.a.b.c will go to great depths with QM on
5415 LOG(prefix << qname << ": Status=got a CNAME referral to child, disabling QM" << endl);
5416 setQNameMinimization(false);
5417 }
5418
5419 if (!d_followCNAME) {
5420 rcode = RCode::NoError;
5421 return;
5422 }
5423
5424 // Check to see if we already have seen the new target as a previous target or that the chain is too long
5425 const auto [CNAMELoop, numCNAMEs] = scanForCNAMELoop(newtarget, ret);
5426 if (CNAMELoop) {
5427 LOG(prefix << qname << ": Status=got a CNAME referral that causes a loop, returning SERVFAIL" << endl);
5428 ret.clear();
5429 rcode = RCode::ServFail;
5430 return;
5431 }
5432 if (numCNAMEs > s_max_CNAMES_followed) {
5433 LOG(prefix << qname << ": Status=got a CNAME referral, but chain too long, returning SERVFAIL" << endl);
5434 rcode = RCode::ServFail;
5435 return;
5436 }
5437
5438 if (qtype == QType::DS || qtype == QType::DNSKEY) {
5439 LOG(prefix << qname << ": Status=got a CNAME referral, but we are looking for a DS or DNSKEY" << endl);
5440
5441 if (d_doDNSSEC) {
5442 addNXNSECS(ret, recordsFromAnswer);
5443 }
5444
5445 rcode = RCode::NoError;
5446 return;
5447 }
5448
5449 LOG(prefix << qname << ": Status=got a CNAME referral, starting over with " << newtarget << endl);
5450
5451 set<GetBestNSAnswer> beenthere;
5452 Context cnameContext;
5453 rcode = doResolve(newtarget, qtype, ret, depth + 1, beenthere, cnameContext);
5454 LOG(prefix << qname << ": Updating validation state for response to " << qname << " from " << state << " with the state from the CNAME quest: " << cnameContext.state << endl);
5455 updateValidationState(qname, state, cnameContext.state, prefix);
5456 }
5457
5458 bool SyncRes::processAnswer(unsigned int depth, const string& prefix, LWResult& lwr, const DNSName& qname, const QType qtype, DNSName& auth, bool wasForwarded, const boost::optional<Netmask>& ednsmask, bool sendRDQuery, NsSet& nameservers, std::vector<DNSRecord>& ret, const DNSFilterEngine& dfe, bool* gotNewServers, int* rcode, vState& state, const ComboAddress& remoteIP)
5459 {
5460 if (s_minimumTTL != 0) {
5461 for (auto& rec : lwr.d_records) {
5462 rec.d_ttl = max(rec.d_ttl, s_minimumTTL);
5463 }
5464 }
5465
5466 /* if the answer is ECS-specific, a minimum TTL is set for this kind of answers
5467 and it's higher than the global minimum TTL */
5468 if (ednsmask && s_minimumECSTTL > 0 && (s_minimumTTL == 0 || s_minimumECSTTL > s_minimumTTL)) {
5469 for (auto& rec : lwr.d_records) {
5470 if (rec.d_place == DNSResourceRecord::ANSWER) {
5471 rec.d_ttl = max(rec.d_ttl, s_minimumECSTTL);
5472 }
5473 }
5474 }
5475
5476 bool needWildcardProof = false;
5477 bool gatherWildcardProof = false;
5478 unsigned int wildcardLabelsCount = 0;
5479 *rcode = updateCacheFromRecords(depth, prefix, lwr, qname, qtype, auth, wasForwarded, ednsmask, state, needWildcardProof, gatherWildcardProof, wildcardLabelsCount, sendRDQuery, remoteIP);
5480 if (*rcode != RCode::NoError) {
5481 return true;
5482 }
5483
5484 LOG(prefix << qname << ": Determining status after receiving this packet" << endl);
5485
5486 set<DNSName> nsset;
5487 bool realreferral = false;
5488 bool negindic = false;
5489 bool negIndicHasSignatures = false;
5490 DNSName newauth;
5491 DNSName newtarget;
5492
5493 bool done = processRecords(prefix, qname, qtype, auth, lwr, sendRDQuery, ret, nsset, newtarget, newauth, realreferral, negindic, state, needWildcardProof, gatherWildcardProof, wildcardLabelsCount, *rcode, negIndicHasSignatures, depth);
5494
5495 if (done) {
5496 LOG(prefix << qname << ": Status=got results, this level of recursion done" << endl);
5497 LOG(prefix << qname << ": Validation status is " << state << endl);
5498 return true;
5499 }
5500
5501 if (!newtarget.empty()) {
5502 handleNewTarget(prefix, qname, newtarget, qtype.getCode(), ret, *rcode, depth, lwr.d_records, state);
5503 return true;
5504 }
5505
5506 if (lwr.d_rcode == RCode::NXDomain) {
5507 LOG(prefix << qname << ": Status=NXDOMAIN, we are done " << (negindic ? "(have negative SOA)" : "") << endl);
5508
5509 auto tempState = getValidationStatus(qname, negIndicHasSignatures, qtype == QType::DS, depth, prefix);
5510 if (tempState == vState::Secure && (lwr.d_aabit || sendRDQuery) && !negindic) {
5511 LOG(prefix << qname << ": NXDOMAIN without a negative indication (missing SOA in authority) in a DNSSEC secure zone, going Bogus" << endl);
5512 updateValidationState(qname, state, vState::BogusMissingNegativeIndication, prefix);
5513 }
5514 else {
5515 /* we might not have validated any record, because we did get a NXDOMAIN without any SOA
5516 from an insecure zone, for example */
5517 updateValidationState(qname, state, tempState, prefix);
5518 }
5519
5520 if (d_doDNSSEC) {
5521 addNXNSECS(ret, lwr.d_records);
5522 }
5523
5524 *rcode = RCode::NXDomain;
5525 return true;
5526 }
5527
5528 if (nsset.empty() && lwr.d_rcode == 0 && (negindic || lwr.d_aabit || sendRDQuery)) {
5529 LOG(prefix << qname << ": Status=noerror, other types may exist, but we are done " << (negindic ? "(have negative SOA) " : "") << (lwr.d_aabit ? "(have aa bit) " : "") << endl);
5530
5531 auto tempState = getValidationStatus(qname, negIndicHasSignatures, qtype == QType::DS, depth, prefix);
5532 if (tempState == vState::Secure && (lwr.d_aabit || sendRDQuery) && !negindic) {
5533 LOG(prefix << qname << ": NODATA without a negative indication (missing SOA in authority) in a DNSSEC secure zone, going Bogus" << endl);
5534 updateValidationState(qname, state, vState::BogusMissingNegativeIndication, prefix);
5535 }
5536 else {
5537 /* we might not have validated any record, because we did get a NODATA without any SOA
5538 from an insecure zone, for example */
5539 updateValidationState(qname, state, tempState, prefix);
5540 }
5541
5542 if (d_doDNSSEC) {
5543 addNXNSECS(ret, lwr.d_records);
5544 }
5545
5546 *rcode = RCode::NoError;
5547 return true;
5548 }
5549
5550 if (realreferral) {
5551 LOG(prefix << qname << ": Status=did not resolve, got " << (unsigned int)nsset.size() << " NS, ");
5552
5553 nameservers.clear();
5554 for (auto const& nameserver : nsset) {
5555 if (d_wantsRPZ && !d_appliedPolicy.wasHit()) {
5556 bool match = dfe.getProcessingPolicy(nameserver, d_discardedPolicies, d_appliedPolicy);
5557 if (match) {
5558 mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());
5559 if (d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) { // client query needs an RPZ response
5560 if (d_pdl && d_pdl->policyHitEventFilter(d_requestor, qname, qtype, d_queryReceivedOverTCP, d_appliedPolicy, d_policyTags, d_discardedPolicies)) {
5561 /* reset to no match */
5562 d_appliedPolicy = DNSFilterEngine::Policy();
5563 }
5564 else {
5565 LOG("however " << nameserver << " was blocked by RPZ policy '" << d_appliedPolicy.getName() << "'" << endl);
5566 throw PolicyHitException();
5567 }
5568 }
5569 }
5570 }
5571 nameservers.insert({nameserver, {{}, false}});
5572 }
5573 LOG("looping to them" << endl);
5574 *gotNewServers = true;
5575 auth = std::move(newauth);
5576
5577 return false;
5578 }
5579
5580 return false;
5581 }
5582
5583 bool SyncRes::doDoTtoAuth(const DNSName& nameServer)
5584 {
5585 return g_DoTToAuthNames.getLocal()->check(nameServer);
5586 }
5587
5588 /** returns:
5589 * -1 in case of no results
5590 * rcode otherwise
5591 */
5592 // NOLINTNEXTLINE(readability-function-cognitive-complexity)
5593 int SyncRes::doResolveAt(NsSet& nameservers, DNSName auth, bool flawedNSSet, const DNSName& qname, const QType qtype,
5594 vector<DNSRecord>& ret,
5595 unsigned int depth, const string& prefix, set<GetBestNSAnswer>& beenthere, Context& context, StopAtDelegation* stopAtDelegation,
5596 map<DNSName, vector<ComboAddress>>* fallBack)
5597 {
5598 auto luaconfsLocal = g_luaconfs.getLocal();
5599
5600 LOG(prefix << qname << ": Cache consultations done, have " << (unsigned int)nameservers.size() << " NS to contact");
5601
5602 if (nameserversBlockedByRPZ(luaconfsLocal->dfe, nameservers)) {
5603 /* RPZ hit */
5604 if (d_pdl && d_pdl->policyHitEventFilter(d_requestor, qname, qtype, d_queryReceivedOverTCP, d_appliedPolicy, d_policyTags, d_discardedPolicies)) {
5605 /* reset to no match */
5606 d_appliedPolicy = DNSFilterEngine::Policy();
5607 }
5608 else {
5609 throw PolicyHitException();
5610 }
5611 }
5612
5613 LOG(endl);
5614
5615 unsigned int addressQueriesForNS = 0;
5616 for (;;) { // we may get more specific nameservers
5617 auto rnameservers = shuffleInSpeedOrder(qname, nameservers, prefix);
5618
5619 // We allow s_maxnsaddressqperq (default 10) queries with empty responses when resolving NS names.
5620 // If a zone publishes many (more than s_maxnsaddressqperq) NS records, we allow less.
5621 // This is to "punish" zones that publish many non-resolving NS names.
5622 // We always allow 5 NS name resolving attempts with empty results.
5623 unsigned int nsLimit = s_maxnsaddressqperq;
5624 if (rnameservers.size() > nsLimit) {
5625 int newLimit = static_cast<int>(nsLimit - (rnameservers.size() - nsLimit));
5626 nsLimit = std::max(5, newLimit);
5627 }
5628
5629 for (auto tns = rnameservers.cbegin();; ++tns) {
5630 if (addressQueriesForNS >= nsLimit) {
5631 throw ImmediateServFailException(std::to_string(nsLimit) + " (adjusted max-ns-address-qperq) or more queries with empty results for NS addresses sent resolving " + qname.toLogString());
5632 }
5633 if (tns == rnameservers.cend()) {
5634 LOG(prefix << qname << ": Failed to resolve via any of the " << (unsigned int)rnameservers.size() << " offered NS at level '" << auth << "'" << endl);
5635 if (s_addExtendedResolutionDNSErrors) {
5636 context.extendedError = EDNSExtendedError{static_cast<uint16_t>(EDNSExtendedError::code::NoReachableAuthority), "delegation " + auth.toLogString()};
5637 }
5638 if (!auth.isRoot() && flawedNSSet) {
5639 LOG(prefix << qname << ": Ageing nameservers for level '" << auth << "', next query might succeed" << endl);
5640 if (g_recCache->doAgeCache(d_now.tv_sec, auth, QType::NS, 10)) {
5641 t_Counters.at(rec::Counter::nsSetInvalidations)++;
5642 }
5643 }
5644 return -1;
5645 }
5646
5647 bool cacheOnly = false;
5648 // this line needs to identify the 'self-resolving' behaviour
5649 if (qname == tns->first && (qtype.getCode() == QType::A || qtype.getCode() == QType::AAAA)) {
5650 /* we might have a glue entry in cache so let's try this NS
5651 but only if we have enough in the cache to know how to reach it */
5652 LOG(prefix << qname << ": Using NS to resolve itself, but only using what we have in cache (" << (1 + tns - rnameservers.cbegin()) << "/" << rnameservers.size() << ")" << endl);
5653 cacheOnly = true;
5654 }
5655
5656 typedef vector<ComboAddress> remoteIPs_t;
5657 remoteIPs_t remoteIPs;
5658 remoteIPs_t::iterator remoteIP;
5659 bool pierceDontQuery = false;
5660 bool sendRDQuery = false;
5661 boost::optional<Netmask> ednsmask;
5662 LWResult lwr;
5663 const bool wasForwarded = tns->first.empty() && (!nameservers[tns->first].first.empty());
5664 int rcode = RCode::NoError;
5665 bool gotNewServers = false;
5666
5667 if (tns->first.empty() && !wasForwarded) {
5668 static ComboAddress const s_oobRemote("255.255.255.255");
5669 LOG(prefix << qname << ": Domain is out-of-band" << endl);
5670 /* setting state to indeterminate since validation is disabled for local auth zone,
5671 and Insecure would be misleading. */
5672 context.state = vState::Indeterminate;
5673 d_wasOutOfBand = doOOBResolve(qname, qtype, lwr.d_records, depth, prefix, lwr.d_rcode);
5674 lwr.d_tcbit = false;
5675 lwr.d_aabit = true;
5676
5677 /* we have received an answer, are we done ? */
5678 bool done = processAnswer(depth, prefix, lwr, qname, qtype, auth, false, ednsmask, sendRDQuery, nameservers, ret, luaconfsLocal->dfe, &gotNewServers, &rcode, context.state, s_oobRemote);
5679 if (done) {
5680 return rcode;
5681 }
5682 if (gotNewServers) {
5683 if (stopAtDelegation != nullptr && *stopAtDelegation == Stop) {
5684 *stopAtDelegation = Stopped;
5685 return rcode;
5686 }
5687 break;
5688 }
5689 }
5690 else {
5691 if (fallBack != nullptr) {
5692 if (auto iter = fallBack->find(tns->first); iter != fallBack->end()) {
5693 remoteIPs = iter->second;
5694 }
5695 }
5696 if (remoteIPs.empty()) {
5697 remoteIPs = retrieveAddressesForNS(prefix, qname, tns, depth, beenthere, rnameservers, nameservers, sendRDQuery, pierceDontQuery, flawedNSSet, cacheOnly, addressQueriesForNS);
5698 }
5699
5700 if (remoteIPs.empty()) {
5701 LOG(prefix << qname << ": Failed to get IP for NS " << tns->first << ", trying next if available" << endl);
5702 flawedNSSet = true;
5703 continue;
5704 }
5705 bool hitPolicy{false};
5706 LOG(prefix << qname << ": Resolved '" << auth << "' NS " << tns->first << " to: ");
5707 for (remoteIP = remoteIPs.begin(); remoteIP != remoteIPs.end(); ++remoteIP) {
5708 if (remoteIP != remoteIPs.begin()) {
5709 LOG(", ");
5710 }
5711 LOG(remoteIP->toString());
5712 if (nameserverIPBlockedByRPZ(luaconfsLocal->dfe, *remoteIP)) {
5713 hitPolicy = true;
5714 }
5715 }
5716 LOG(endl);
5717 if (hitPolicy) { // implies d_wantsRPZ
5718 /* RPZ hit */
5719 if (d_pdl && d_pdl->policyHitEventFilter(d_requestor, qname, qtype, d_queryReceivedOverTCP, d_appliedPolicy, d_policyTags, d_discardedPolicies)) {
5720 /* reset to no match */
5721 d_appliedPolicy = DNSFilterEngine::Policy();
5722 }
5723 else {
5724 throw PolicyHitException();
5725 }
5726 }
5727
5728 for (remoteIP = remoteIPs.begin(); remoteIP != remoteIPs.end(); ++remoteIP) {
5729 LOG(prefix << qname << ": Trying IP " << remoteIP->toStringWithPort() << ", asking '" << qname << "|" << qtype << "'" << endl);
5730
5731 if (throttledOrBlocked(prefix, *remoteIP, qname, qtype, pierceDontQuery)) {
5732 // As d_throttledqueries might be increased, check the max-qperq condition
5733 checkMaxQperQ(qname);
5734 continue;
5735 }
5736
5737 bool truncated = false;
5738 bool spoofed = false;
5739 bool gotAnswer = false;
5740 bool doDoT = false;
5741
5742 if (doDoTtoAuth(tns->first)) {
5743 remoteIP->setPort(853);
5744 doDoT = true;
5745 }
5746 if (SyncRes::s_dot_to_port_853 && remoteIP->getPort() == 853) {
5747 doDoT = true;
5748 }
5749 bool forceTCP = doDoT;
5750
5751 if (!doDoT && s_max_busy_dot_probes > 0) {
5752 submitTryDotTask(*remoteIP, auth, tns->first, d_now.tv_sec);
5753 }
5754 if (!forceTCP) {
5755 gotAnswer = doResolveAtThisIP(prefix, qname, qtype, lwr, ednsmask, auth, sendRDQuery, wasForwarded,
5756 tns->first, *remoteIP, false, false, truncated, spoofed, context.extendedError);
5757 }
5758 if (forceTCP || (spoofed || (gotAnswer && truncated))) {
5759 /* retry, over TCP this time */
5760 gotAnswer = doResolveAtThisIP(prefix, qname, qtype, lwr, ednsmask, auth, sendRDQuery, wasForwarded,
5761 tns->first, *remoteIP, true, doDoT, truncated, spoofed, context.extendedError);
5762 }
5763
5764 if (!gotAnswer) {
5765 if (doDoT && s_max_busy_dot_probes > 0) {
5766 // This is quite pessimistic...
5767 updateDoTStatus(*remoteIP, DoTStatus::Bad, d_now.tv_sec + dotFailWait);
5768 }
5769 continue;
5770 }
5771
5772 LOG(prefix << qname << ": Got " << (unsigned int)lwr.d_records.size() << " answers from " << tns->first << " (" << remoteIP->toString() << "), rcode=" << lwr.d_rcode << " (" << RCode::to_s(lwr.d_rcode) << "), aa=" << lwr.d_aabit << ", in " << lwr.d_usec / 1000 << "ms" << endl);
5773
5774 if (doDoT && s_max_busy_dot_probes > 0) {
5775 updateDoTStatus(*remoteIP, DoTStatus::Good, d_now.tv_sec + dotSuccessWait);
5776 }
5777 /* // for you IPv6 fanatics :-)
5778 if(remoteIP->sin4.sin_family==AF_INET6)
5779 lwr.d_usec/=3;
5780 */
5781 // cout<<"ms: "<<lwr.d_usec/1000.0<<", "<<g_avgLatency/1000.0<<'\n';
5782
5783 s_nsSpeeds.lock()->find_or_enter(tns->first.empty() ? DNSName(remoteIP->toStringWithPort()) : tns->first, d_now).submit(*remoteIP, static_cast<int>(lwr.d_usec), d_now);
5784
5785 /* we have received an answer, are we done ? */
5786 bool done = processAnswer(depth, prefix, lwr, qname, qtype, auth, wasForwarded, ednsmask, sendRDQuery, nameservers, ret, luaconfsLocal->dfe, &gotNewServers, &rcode, context.state, *remoteIP);
5787 if (done) {
5788 return rcode;
5789 }
5790 if (gotNewServers) {
5791 if (stopAtDelegation != nullptr && *stopAtDelegation == Stop) {
5792 *stopAtDelegation = Stopped;
5793 return rcode;
5794 }
5795 break;
5796 }
5797 /* was lame */
5798 doThrottle(d_now.tv_sec, *remoteIP, qname, qtype, 60, 100);
5799 }
5800
5801 if (gotNewServers) {
5802 break;
5803 }
5804
5805 if (remoteIP == remoteIPs.cend()) { // we tried all IP addresses, none worked
5806 continue;
5807 }
5808 }
5809 }
5810 }
5811 return -1;
5812 }
5813
5814 void SyncRes::setQuerySource(const Netmask& netmask)
5815 {
5816 if (!netmask.empty()) {
5817 d_outgoingECSNetwork = netmask;
5818 }
5819 else {
5820 d_outgoingECSNetwork = boost::none;
5821 }
5822 }
5823
5824 void SyncRes::setQuerySource(const ComboAddress& requestor, const boost::optional<const EDNSSubnetOpts&>& incomingECS)
5825 {
5826 d_requestor = requestor;
5827
5828 if (incomingECS && incomingECS->source.getBits() > 0) {
5829 d_cacheRemote = incomingECS->source.getMaskedNetwork();
5830 uint8_t bits = std::min(incomingECS->source.getBits(), (incomingECS->source.isIPv4() ? s_ecsipv4limit : s_ecsipv6limit));
5831 ComboAddress trunc = incomingECS->source.getNetwork();
5832 trunc.truncate(bits);
5833 d_outgoingECSNetwork = boost::optional<Netmask>(Netmask(trunc, bits));
5834 }
5835 else {
5836 d_cacheRemote = d_requestor;
5837 if (!incomingECS && s_ednslocalsubnets.match(d_requestor)) {
5838 ComboAddress trunc = d_requestor;
5839 uint8_t bits = d_requestor.isIPv4() ? 32 : 128;
5840 bits = std::min(bits, (trunc.isIPv4() ? s_ecsipv4limit : s_ecsipv6limit));
5841 trunc.truncate(bits);
5842 d_outgoingECSNetwork = boost::optional<Netmask>(Netmask(trunc, bits));
5843 }
5844 else if (s_ecsScopeZero.source.getBits() > 0) {
5845 /* RFC7871 says we MUST NOT send any ECS if the source scope is 0.
5846 But using an empty ECS in that case would mean inserting
5847 a non ECS-specific entry into the cache, preventing any further
5848 ECS-specific query to be sent.
5849 So instead we use the trick described in section 7.1.2:
5850 "The subsequent Recursive Resolver query to the Authoritative Nameserver
5851 will then either not include an ECS option or MAY optionally include
5852 its own address information, which is what the Authoritative
5853 Nameserver will almost certainly use to generate any Tailored
5854 Response in lieu of an option. This allows the answer to be handled
5855 by the same caching mechanism as other queries, with an explicit
5856 indicator of the applicable scope. Subsequent Stub Resolver queries
5857 for /0 can then be answered from this cached response.
5858 */
5859 d_outgoingECSNetwork = boost::optional<Netmask>(s_ecsScopeZero.source.getMaskedNetwork());
5860 d_cacheRemote = s_ecsScopeZero.source.getNetwork();
5861 }
5862 else {
5863 // ECS disabled because no scope-zero address could be derived.
5864 d_outgoingECSNetwork = boost::none;
5865 }
5866 }
5867 }
5868
5869 boost::optional<Netmask> SyncRes::getEDNSSubnetMask(const DNSName& name, const ComboAddress& rem)
5870 {
5871 if (d_outgoingECSNetwork && (s_ednsdomains.check(name) || s_ednsremotesubnets.match(rem))) {
5872 return d_outgoingECSNetwork;
5873 }
5874 return boost::none;
5875 }
5876
5877 void SyncRes::parseEDNSSubnetAllowlist(const std::string& alist)
5878 {
5879 vector<string> parts;
5880 stringtok(parts, alist, ",; ");
5881 for (const auto& allow : parts) {
5882 try {
5883 s_ednsremotesubnets.addMask(Netmask(allow));
5884 }
5885 catch (...) {
5886 s_ednsdomains.add(DNSName(allow));
5887 }
5888 }
5889 }
5890
5891 void SyncRes::parseEDNSSubnetAddFor(const std::string& subnetlist)
5892 {
5893 vector<string> parts;
5894 stringtok(parts, subnetlist, ",; ");
5895 for (const auto& allow : parts) {
5896 s_ednslocalsubnets.addMask(allow);
5897 }
5898 }
5899
5900 // used by PowerDNSLua - note that this neglects to add the packet count & statistics back to pdns_recursor.cc
5901 int directResolve(const DNSName& qname, const QType qtype, const QClass qclass, vector<DNSRecord>& ret, const shared_ptr<RecursorLua4>& pdl, Logr::log_t log)
5902 {
5903 return directResolve(qname, qtype, qclass, ret, pdl, SyncRes::s_qnameminimization, log);
5904 }
5905
5906 int directResolve(const DNSName& qname, const QType qtype, const QClass qclass, vector<DNSRecord>& ret, const shared_ptr<RecursorLua4>& pdl, bool qnamemin, Logr::log_t slog)
5907 {
5908 auto log = slog->withValues("qname", Logging::Loggable(qname), "qtype", Logging::Loggable(qtype));
5909
5910 struct timeval now
5911 {
5912 };
5913 gettimeofday(&now, nullptr);
5914
5915 SyncRes resolver(now);
5916 resolver.setQNameMinimization(qnamemin);
5917 if (pdl) {
5918 resolver.setLuaEngine(pdl);
5919 }
5920
5921 int res = -1;
5922 const std::string msg = "Exception while resolving";
5923 try {
5924 res = resolver.beginResolve(qname, qtype, qclass, ret, 0);
5925 }
5926 catch (const PDNSException& e) {
5927 SLOG(g_log << Logger::Warning << "Failed to resolve " << qname << ", got pdns exception: " << e.reason << endl,
5928 log->error(Logr::Warning, e.reason, msg, "exception", Logging::Loggable("PDNSException")));
5929 ret.clear();
5930 }
5931 catch (const ImmediateServFailException& e) {
5932 SLOG(g_log << Logger::Warning << "Failed to resolve " << qname << ", got ImmediateServFailException: " << e.reason << endl,
5933 log->error(Logr::Warning, e.reason, msg, "exception", Logging::Loggable("ImmediateServFailException")));
5934 ret.clear();
5935 }
5936 catch (const PolicyHitException& e) {
5937 SLOG(g_log << Logger::Warning << "Failed to resolve " << qname << ", got a policy hit" << endl,
5938 log->info(Logr::Warning, msg, "exception", Logging::Loggable("PolicyHitException")));
5939 ret.clear();
5940 }
5941 catch (const std::exception& e) {
5942 SLOG(g_log << Logger::Warning << "Failed to resolve " << qname << ", got STL error: " << e.what() << endl,
5943 log->error(Logr::Warning, e.what(), msg, "exception", Logging::Loggable("std::exception")));
5944 ret.clear();
5945 }
5946 catch (...) {
5947 SLOG(g_log << Logger::Warning << "Failed to resolve " << qname << ", got an exception" << endl,
5948 log->info(Logr::Warning, msg));
5949 ret.clear();
5950 }
5951
5952 return res;
5953 }
5954
5955 int SyncRes::getRootNS(struct timeval now, asyncresolve_t asyncCallback, unsigned int depth, Logr::log_t log)
5956 {
5957 if (::arg()["hint-file"] == "no-refresh") {
5958 return 0;
5959 }
5960 SyncRes resolver(now);
5961 resolver.d_prefix = "[getRootNS]";
5962 resolver.setDoEDNS0(true);
5963 resolver.setUpdatingRootNS();
5964 resolver.setDoDNSSEC(g_dnssecmode != DNSSECMode::Off);
5965 resolver.setDNSSECValidationRequested(g_dnssecmode != DNSSECMode::Off && g_dnssecmode != DNSSECMode::ProcessNoValidate);
5966 resolver.setAsyncCallback(std::move(asyncCallback));
5967 resolver.setRefreshAlmostExpired(true);
5968
5969 const string msg = "Failed to update . records";
5970 vector<DNSRecord> ret;
5971 int res = -1;
5972 try {
5973 res = resolver.beginResolve(g_rootdnsname, QType::NS, 1, ret, depth + 1);
5974 if (g_dnssecmode != DNSSECMode::Off && g_dnssecmode != DNSSECMode::ProcessNoValidate) {
5975 auto state = resolver.getValidationState();
5976 if (vStateIsBogus(state)) {
5977 throw PDNSException("Got Bogus validation result for .|NS");
5978 }
5979 }
5980 }
5981 catch (const PDNSException& e) {
5982 SLOG(g_log << Logger::Error << "Failed to update . records, got an exception: " << e.reason << endl,
5983 log->error(Logr::Error, e.reason, msg, "exception", Logging::Loggable("PDNSException")));
5984 }
5985 catch (const ImmediateServFailException& e) {
5986 SLOG(g_log << Logger::Error << "Failed to update . records, got an exception: " << e.reason << endl,
5987 log->error(Logr::Error, e.reason, msg, "exception", Logging::Loggable("ImmediateServFailException")));
5988 }
5989 catch (const PolicyHitException& e) {
5990 SLOG(g_log << Logger::Error << "Failed to update . records, got a policy hit" << endl,
5991 log->info(Logr::Error, msg, "exception", Logging::Loggable("PolicyHitException")));
5992 ret.clear();
5993 }
5994 catch (const std::exception& e) {
5995 SLOG(g_log << Logger::Error << "Failed to update . records, got an exception: " << e.what() << endl,
5996 log->error(Logr::Error, e.what(), msg, "exception", Logging::Loggable("std::exception")));
5997 }
5998 catch (...) {
5999 SLOG(g_log << Logger::Error << "Failed to update . records, got an exception" << endl,
6000 log->info(Logr::Error, msg));
6001 }
6002
6003 if (res == 0) {
6004 SLOG(g_log << Logger::Debug << "Refreshed . records" << endl,
6005 log->info(Logr::Debug, "Refreshed . records"));
6006 }
6007 else {
6008 SLOG(g_log << Logger::Warning << "Failed to update root NS records, RCODE=" << res << endl,
6009 log->info(Logr::Warning, msg, "rcode", Logging::Loggable(res)));
6010 }
6011 return res;
6012 }
6013
6014 bool SyncRes::answerIsNOData(uint16_t requestedType, int rcode, const std::vector<DNSRecord>& records)
6015 {
6016 if (rcode != RCode::NoError) {
6017 return false;
6018 }
6019
6020 // NOLINTNEXTLINE(readability-use-anyofallof)
6021 for (const auto& rec : records) {
6022 if (rec.d_place == DNSResourceRecord::ANSWER && rec.d_type == requestedType) {
6023 /* we have a record, of the right type, in the right section */
6024 return false;
6025 }
6026 }
6027 return true;
6028 #if 0
6029 // This code should be equivalent to the code above, clang-tidy prefers any_of()
6030 // I have doubts if that is easier to read
6031 return !std::any_of(records.begin(), records.end(), [=](const DNSRecord& rec) {
6032 return rec.d_place == DNSResourceRecord::ANSWER && rec.d_type == requestedType;
6033 });
6034 #endif
6035 }