• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

PowerDNS / pdns / 19937114032

04 Dec 2025 05:00PM UTC coverage: 73.093% (-0.04%) from 73.131%
19937114032

Pull #16598

github

web-flow
Merge bdc1eb026 into a67907d2f
Pull Request #16598: Codeql remove unused variables

38530 of 63422 branches covered (60.75%)

Branch coverage included in aggregate %.

128087 of 164529 relevant lines covered (77.85%)

6073228.55 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

80.97
/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
#include <optional>
23
#ifdef HAVE_CONFIG_H
24
#include <utility>
25

26
#include "config.h"
27
#endif
28

29
#include "arguments.hh"
30
#include "aggressive_nsec.hh"
31
#include "cachecleaner.hh"
32
#include "dns_random.hh"
33
#include "dnsparser.hh"
34
#include "dnsrecords.hh"
35
#include "ednssubnet.hh"
36
#include "logger.hh"
37
#include "lua-recursor4.hh"
38
#include "rec-lua-conf.hh"
39
#include "syncres.hh"
40
#include "dnssec.hh"
41
#include "validate-recursor.hh"
42
#include "rec-taskqueue.hh"
43
#include "shuffle.hh"
44
#include "rec-nsspeeds.hh"
45

46
rec::GlobalCounters g_Counters;
47
thread_local rec::TCounters t_Counters(g_Counters);
48

49
template <class T>
50
class fails_t : public boost::noncopyable
51
{
52
public:
53
  using counter_t = uint64_t;
54
  struct value_t
55
  {
56
    value_t(T arg) :
57
      key(std::move(arg)) {}
173✔
58
    T key;
59
    mutable counter_t value{0};
60
    time_t last{0};
61
  };
62

63
  using cont_t = multi_index_container<value_t,
64
                                       indexed_by<
65
                                         ordered_unique<tag<T>, member<value_t, T, &value_t::key>>,
66
                                         ordered_non_unique<tag<time_t>, member<value_t, time_t, &value_t::last>>>>;
67

68
  [[nodiscard]] cont_t getMapCopy() const
69
  {
×
70
    return d_cont;
×
71
  }
×
72

73
  [[nodiscard]] counter_t value(const T& arg) const
74
  {
10,535✔
75
    auto iter = d_cont.find(arg);
10,535✔
76

77
    if (iter == d_cont.end()) {
10,535✔
78
      return 0;
10,495✔
79
    }
10,495✔
80
    return iter->value;
40✔
81
  }
10,535✔
82

83
  counter_t incr(const T& key, const struct timeval& now)
84
  {
173✔
85
    auto iter = d_cont.insert(key).first;
173✔
86

87
    if (iter->value < std::numeric_limits<counter_t>::max()) {
173!
88
      iter->value++;
173✔
89
    }
173✔
90
    auto& ind = d_cont.template get<T>();
173✔
91
    time_t nowSecs = now.tv_sec;
173✔
92
    ind.modify(iter, [nowSecs](value_t& val) { val.last = nowSecs; });
173✔
93
    return iter->value;
173✔
94
  }
173✔
95

96
  void clear(const T& arg)
97
  {
12,597✔
98
    d_cont.erase(arg);
12,597✔
99
  }
12,597✔
100

101
  void clear()
102
  {
1,224✔
103
    d_cont.clear();
1,224✔
104
  }
1,224✔
105

106
  [[nodiscard]] size_t size() const
107
  {
1,606✔
108
    return d_cont.size();
1,606✔
109
  }
1,606✔
110

111
  void prune(time_t cutoff)
112
  {
260✔
113
    auto& ind = d_cont.template get<time_t>();
260✔
114
    ind.erase(ind.begin(), ind.upper_bound(cutoff));
260✔
115
  }
260✔
116

117
private:
118
  cont_t d_cont;
119
};
120

121
static LockGuarded<nsspeeds_t> s_nsSpeeds;
122

123
size_t SyncRes::getNSSpeedTable(size_t maxSize, std::string& ret)
124
{
2✔
125
  const auto copy = *s_nsSpeeds.lock();
2✔
126
  return copy.getPB(s_serverID, maxSize, ret);
2✔
127
}
2✔
128

129
size_t SyncRes::putIntoNSSpeedTable(const std::string& ret)
130
{
2✔
131
  auto lock = s_nsSpeeds.lock();
2✔
132
  return lock->putPB(time(nullptr) - 300, ret);
2✔
133
}
2✔
134

135
class Throttle
136
{
137
public:
138
  Throttle() = default;
191✔
139
  ~Throttle() = default;
×
140
  Throttle(Throttle&&) = delete;
141
  Throttle& operator=(const Throttle&) = default;
142
  Throttle& operator=(Throttle&&) = delete;
143
  Throttle(const Throttle&) = delete;
144

145
  using Key = std::tuple<ComboAddress, DNSName, QType>;
146
  using Reason = SyncRes::ThrottleReason;
147

148
  struct entry_t
149
  {
150
    entry_t(Key thing_, time_t ttd_, unsigned int count_, Reason reason_) :
151
      thing(std::move(thing_)), ttd(ttd_), count(count_), reason(reason_)
152
    {
240✔
153
    }
240✔
154
    Key thing;
155
    time_t ttd;
156
    mutable unsigned int count;
157
    Reason reason;
158
  };
159
  using cont_t = multi_index_container<entry_t,
160
                                       indexed_by<
161
                                         ordered_unique<tag<Key>, member<entry_t, Key, &entry_t::thing>>,
162
                                         ordered_non_unique<tag<time_t>, member<entry_t, time_t, &entry_t::ttd>>>>;
163

164
  bool shouldThrottle(time_t now, const Key& arg)
165
  {
26,266✔
166
    auto iter = d_cont.find(arg);
26,266✔
167
    if (iter == d_cont.end()) {
26,266✔
168
      return false;
26,199✔
169
    }
26,199✔
170
    if (now > iter->ttd || iter->count == 0) {
67✔
171
      d_cont.erase(iter);
6✔
172
      return false;
6✔
173
    }
6✔
174
    iter->count--;
61✔
175

176
    return true; // still listed, still blocked
61✔
177
  }
67✔
178

179
  void throttle(time_t now, const Key& arg, time_t ttl, unsigned int count, Reason reason)
180
  {
240✔
181
    auto iter = d_cont.find(arg);
240✔
182
    time_t ttd = now + ttl;
240✔
183
    if (iter == d_cont.end()) {
240!
184
      d_cont.emplace(arg, ttd, count, reason);
240✔
185
    }
240✔
186
    else if (ttd > iter->ttd || count > iter->count) {
×
187
      ttd = std::max(iter->ttd, ttd);
×
188
      count = std::max(iter->count, count);
×
189
      auto& ind = d_cont.template get<Key>();
×
190
      ind.modify(iter, [ttd, count, reason](entry_t& entry) {
×
191
        entry.ttd = ttd;
×
192
        entry.count = count;
×
193
        entry.reason = reason;
×
194
      });
×
195
    }
×
196
  }
240✔
197

198
  [[nodiscard]] size_t size() const
199
  {
799✔
200
    return d_cont.size();
799✔
201
  }
799✔
202

203
  [[nodiscard]] cont_t getThrottleMap() const
204
  {
×
205
    return d_cont;
×
206
  }
×
207

208
  void clear()
209
  {
612✔
210
    d_cont.clear();
612✔
211
  }
612✔
212

213
  void clear(const Key& thing)
214
  {
25,190✔
215
    d_cont.erase(thing);
25,190✔
216
  }
25,190✔
217
  void prune(time_t now)
218
  {
130✔
219
    auto& ind = d_cont.template get<time_t>();
130✔
220
    ind.erase(ind.begin(), ind.upper_bound(now));
130✔
221
  }
130✔
222

223
  static std::string toString(Reason reason)
224
  {
×
225
    static const std::array<std::string, 10> reasons = {
×
226
      "None",
×
227
      "ServerDown",
×
228
      "PermanentError",
×
229
      "Timeout",
×
230
      "ParseError",
×
231
      "RCodeServFail",
×
232
      "RCodeRefused",
×
233
      "RCodeOther",
×
234
      "TCPTruncate",
×
235
      "Lame"};
×
236
    const auto index = static_cast<unsigned int>(reason);
×
237
    if (index >= reasons.size()) {
×
238
      return "?";
×
239
    }
×
240
    return reasons.at(index);
×
241
  }
×
242

243
private:
244
  cont_t d_cont;
245
};
246

247
static LockGuarded<Throttle> s_throttle;
248

249
struct SavedParentEntry
250
{
251
  SavedParentEntry(DNSName name, map<DNSName, vector<ComboAddress>>&& nsAddresses, time_t ttd) :
252
    d_domain(std::move(name)), d_nsAddresses(std::move(nsAddresses)), d_ttd(ttd)
253
  {
4✔
254
  }
4✔
255
  DNSName d_domain;
256
  map<DNSName, vector<ComboAddress>> d_nsAddresses;
257
  time_t d_ttd;
258
  mutable uint64_t d_count{0};
259
};
260

261
using SavedParentNSSetBase = multi_index_container<
262
  SavedParentEntry,
263
  indexed_by<ordered_unique<tag<DNSName>, member<SavedParentEntry, DNSName, &SavedParentEntry::d_domain>>,
264
             ordered_non_unique<tag<time_t>, member<SavedParentEntry, time_t, &SavedParentEntry::d_ttd>>>>;
265

266
class SavedParentNSSet : public SavedParentNSSetBase
267
{
268
public:
269
  void prune(time_t now)
270
  {
100✔
271
    auto& ind = get<time_t>();
100✔
272
    ind.erase(ind.begin(), ind.upper_bound(now));
100✔
273
  }
100✔
274
  void inc(const DNSName& name)
275
  {
2✔
276
    auto iter = find(name);
2✔
277
    if (iter != end()) {
2!
278
      ++(*iter).d_count;
2✔
279
    }
2✔
280
  }
2✔
281
  [[nodiscard]] SavedParentNSSet getMapCopy() const
282
  {
×
283
    return *this;
×
284
  }
×
285
};
286

287
static LockGuarded<SavedParentNSSet> s_savedParentNSSet;
288

289
thread_local SyncRes::ThreadLocalStorage SyncRes::t_sstorage;
290
thread_local std::unique_ptr<addrringbuf_t> t_timeouts;
291

292
std::unique_ptr<NetmaskGroup> SyncRes::s_dontQuery{nullptr};
293
NetmaskGroup SyncRes::s_ednslocalsubnets;
294
NetmaskGroup SyncRes::s_ednsremotesubnets;
295
SuffixMatchNode SyncRes::s_ednsdomains;
296
EDNSSubnetOpts SyncRes::s_ecsScopeZero;
297
string SyncRes::s_serverID;
298
SyncRes::LogMode SyncRes::s_lm;
299
static LockGuarded<fails_t<ComboAddress>> s_fails;
300
static LockGuarded<fails_t<DNSName>> s_nonresolving;
301

302
struct DoTStatus
303
{
304
  DoTStatus(const ComboAddress& address, DNSName auth, time_t ttd) :
305
    d_address(address), d_auth(std::move(auth)), d_ttd(ttd)
306
  {
×
307
  }
×
308
  enum Status : uint8_t
309
  {
310
    Unknown,
311
    Busy,
312
    Bad,
313
    Good
314
  };
315
  ComboAddress d_address;
316
  DNSName d_auth;
317
  time_t d_ttd;
318
  mutable uint64_t d_count{0};
319
  mutable Status d_status{Unknown};
320
  std::string toString() const
321
  {
×
322
    const std::array<std::string, 4> names{"Unknown", "Busy", "Bad", "Good"};
×
323
    auto val = static_cast<unsigned int>(d_status);
×
324
    return val >= names.size() ? "?" : names.at(val);
×
325
  }
×
326
};
327

328
struct DoTMap
329
{
330
  multi_index_container<DoTStatus,
331
                        indexed_by<
332
                          ordered_unique<tag<ComboAddress>, member<DoTStatus, const ComboAddress, &DoTStatus::d_address>>,
333
                          ordered_non_unique<tag<time_t>, member<DoTStatus, time_t, &DoTStatus::d_ttd>>>>
334
    d_map;
335
  uint64_t d_numBusy{0};
336

337
  void prune(time_t cutoff)
338
  {
×
339
    auto& ind = d_map.template get<time_t>();
×
340
    ind.erase(ind.begin(), ind.upper_bound(cutoff));
×
341
  }
×
342
};
343

344
static LockGuarded<DoTMap> s_dotMap;
345

346
static const time_t dotFailWait = static_cast<time_t>(24) * 3600;
347
static const time_t dotSuccessWait = static_cast<time_t>(3) * 24 * 3600;
348
static bool shouldDoDoT(ComboAddress address, time_t now);
349

350
unsigned int SyncRes::s_maxnegttl;
351
unsigned int SyncRes::s_maxbogusttl;
352
unsigned int SyncRes::s_maxcachettl;
353
unsigned int SyncRes::s_maxqperq;
354
unsigned int SyncRes::s_maxnsperresolve;
355
unsigned int SyncRes::s_maxnsaddressqperq;
356
unsigned int SyncRes::s_maxtotusec;
357
unsigned int SyncRes::s_maxdepth;
358
unsigned int SyncRes::s_minimumTTL;
359
unsigned int SyncRes::s_minimumECSTTL;
360
unsigned int SyncRes::s_packetcachettl;
361
unsigned int SyncRes::s_packetcacheservfailttl;
362
unsigned int SyncRes::s_packetcachenegativettl;
363
unsigned int SyncRes::s_serverdownmaxfails;
364
unsigned int SyncRes::s_serverdownthrottletime;
365
unsigned int SyncRes::s_unthrottle_n;
366
unsigned int SyncRes::s_nonresolvingnsmaxfails;
367
unsigned int SyncRes::s_nonresolvingnsthrottletime;
368
unsigned int SyncRes::s_ecscachelimitttl;
369
unsigned int SyncRes::s_maxvalidationsperq;
370
unsigned int SyncRes::s_maxnsec3iterationsperq;
371
pdns::stat_t SyncRes::s_ecsqueries;
372
pdns::stat_t SyncRes::s_ecsresponses;
373
std::map<uint8_t, pdns::stat_t> SyncRes::s_ecsResponsesBySubnetSize4;
374
std::map<uint8_t, pdns::stat_t> SyncRes::s_ecsResponsesBySubnetSize6;
375

376
uint8_t SyncRes::s_ecsipv4limit;
377
uint8_t SyncRes::s_ecsipv6limit;
378
uint8_t SyncRes::s_ecsipv4cachelimit;
379
uint8_t SyncRes::s_ecsipv6cachelimit;
380
bool SyncRes::s_ecsipv4nevercache;
381
bool SyncRes::s_ecsipv6nevercache;
382

383
bool SyncRes::s_doIPv4;
384
bool SyncRes::s_doIPv6;
385
bool SyncRes::s_rootNXTrust;
386
bool SyncRes::s_noEDNS;
387
bool SyncRes::s_qnameminimization;
388
bool SyncRes::s_outAnyToTcp;
389
SyncRes::HardenNXD SyncRes::s_hardenNXD;
390
unsigned int SyncRes::s_refresh_ttlperc;
391
unsigned int SyncRes::s_locked_ttlperc;
392
int SyncRes::s_tcp_fast_open;
393
bool SyncRes::s_tcp_fast_open_connect;
394
bool SyncRes::s_dot_to_port_853;
395
int SyncRes::s_event_trace_enabled;
396
bool SyncRes::s_save_parent_ns_set;
397
unsigned int SyncRes::s_max_busy_dot_probes;
398
unsigned int SyncRes::s_max_CNAMES_followed;
399
bool SyncRes::s_addExtendedResolutionDNSErrors;
400

401
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
402
#define LOG(x)                       \
403
  if (d_lm == Log) {                 \
1,178,857✔
404
    g_log << Logger::Warning << x;   \
110,882✔
405
  }                                  \
110,882✔
406
  else if (d_lm == Store) {          \
1,178,857✔
407
    addTraceTS(d_fixednow, d_trace); \
1,450✔
408
    d_trace << x;                    \
1,450✔
409
  }
1,450✔
410

411
OptLog SyncRes::LogObject(const string& prefix)
412
{
90,383✔
413
  OptLog ret;
90,383✔
414
  if (d_lm == Log) {
90,383✔
415
    ret = {prefix, d_fixednow, g_log};
13,024✔
416
  }
13,024✔
417
  else if (d_lm == Store) {
77,359✔
418
    ret = {prefix, d_fixednow, d_trace};
114✔
419
  }
114✔
420
  return ret;
90,383✔
421
}
90,383✔
422

423
static bool pushResolveIfNotInNegCache(const DNSName& qname, QType qtype, const struct timeval& now)
424
{
1,014✔
425
  NegCache::NegCacheEntry negEntry;
1,014✔
426
  bool inNegCache = g_negCache->get(qname, qtype, now, negEntry, false);
1,014✔
427
  if (!inNegCache) {
1,014✔
428
    // There are a few cases where an answer is neither stored in the record cache nor in the neg cache.
429
    // An example is a SOA-less NODATA response. Rate limiting will kick in if those tasks are pushed too often.
430
    // We might want to fix these cases (and always either store positive or negative) some day.
431
    pushResolveTask(qname, qtype, now.tv_sec, now.tv_sec + 60, false);
1,000✔
432
  }
1,000✔
433
  return !inNegCache;
1,014✔
434
}
1,014✔
435

436
// A helper function to print a double with specific printf format.
437
// Not using boost::format since it is not thread safe while calling
438
// into locale handling code according to tsan.
439
// This allocates a string, but that's nothing compared to what
440
// boost::format is doing and may even be optimized away anyway.
441
static inline std::string fmtfloat(double value)
442
{
4,543✔
443
  std::array<char, 20> buf{};
4,543✔
444
  int ret = snprintf(buf.data(), buf.size(), "%0.2f", value);
4,543✔
445
  if (ret < 0 || ret >= static_cast<int>(buf.size())) {
4,543!
446
    return "?";
×
447
  }
×
448
  return {buf.data(), static_cast<size_t>(ret)};
4,543✔
449
}
4,543✔
450

451
static inline void accountAuthLatency(uint64_t usec, int family)
452
{
13,091✔
453
  if (family == AF_INET) {
13,091✔
454
    t_Counters.at(rec::Histogram::auth4Answers)(usec);
12,327✔
455
    t_Counters.at(rec::Histogram::cumulativeAuth4Answers)(usec);
12,327✔
456
  }
12,327✔
457
  else {
764✔
458
    t_Counters.at(rec::Histogram::auth6Answers)(usec);
764✔
459
    t_Counters.at(rec::Histogram::cumulativeAuth6Answers)(usec);
764✔
460
  }
764✔
461
}
13,091✔
462

463
SyncRes::SyncRes(const struct timeval& now) :
464
  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)
465
{
5,017✔
466
  d_validationContext.d_nsec3IterationsRemainingQuota = s_maxnsec3iterationsperq > 0 ? s_maxnsec3iterationsperq : std::numeric_limits<decltype(d_validationContext.d_nsec3IterationsRemainingQuota)>::max();
2,147,486,875✔
467
}
5,017✔
468

469
static void allowAdditionalEntry(std::unordered_set<DNSName>& allowedAdditionals, const DNSRecord& rec);
470

471
void SyncRes::resolveAdditionals(const DNSName& qname, QType qtype, AdditionalMode mode, std::vector<DNSRecord>& additionals, unsigned int depth, bool& additionalsNotInCache)
472
{
29✔
473
  vector<DNSRecord> addRecords;
29✔
474

475
  Context context;
29✔
476
  switch (mode) {
29!
477
  case AdditionalMode::ResolveImmediately: {
21✔
478
    set<GetBestNSAnswer> beenthere;
21✔
479
    int res = doResolve(qname, qtype, addRecords, depth, beenthere, context);
21✔
480
    if (res != 0) {
21!
481
      return;
×
482
    }
×
483
    // We're conservative here. We do not add Bogus records in any circumstance, we add Indeterminates only if no
484
    // validation is required.
485
    if (vStateIsBogus(context.state)) {
21!
486
      return;
×
487
    }
×
488
    if (shouldValidate() && context.state != vState::Secure && context.state != vState::Insecure) {
21!
489
      return;
×
490
    }
×
491
    for (auto& rec : addRecords) {
65✔
492
      if (rec.d_place == DNSResourceRecord::ANSWER) {
65✔
493
        additionals.push_back(std::move(rec));
21✔
494
      }
21✔
495
    }
65✔
496
    break;
21✔
497
  }
21✔
498
  case AdditionalMode::CacheOnly:
×
499
  case AdditionalMode::CacheOnlyRequireAuth: {
8✔
500
    // Peek into cache
501
    MemRecursorCache::Flags flags = mode == AdditionalMode::CacheOnlyRequireAuth ? MemRecursorCache::RequireAuth : MemRecursorCache::None;
8!
502
    if (g_recCache->get(d_now.tv_sec, qname, qtype, flags, &addRecords, d_cacheRemote, d_routingTag, nullptr, nullptr, nullptr, &context.state) <= 0) {
8✔
503
      return;
6✔
504
    }
6✔
505
    // See the comment for the ResolveImmediately case
506
    if (vStateIsBogus(context.state)) {
2!
507
      return;
×
508
    }
×
509
    if (shouldValidate() && context.state != vState::Secure && context.state != vState::Insecure) {
2!
510
      return;
×
511
    }
×
512
    for (auto& rec : addRecords) {
2✔
513
      if (rec.d_place == DNSResourceRecord::ANSWER) {
2!
514
        rec.d_ttl -= d_now.tv_sec;
2✔
515
        additionals.push_back(std::move(rec));
2✔
516
      }
2✔
517
    }
2✔
518
    break;
2✔
519
  }
2✔
520
  case AdditionalMode::ResolveDeferred: {
×
521
    const bool oldCacheOnly = setCacheOnly(true);
×
522
    set<GetBestNSAnswer> beenthere;
×
523
    int res = doResolve(qname, qtype, addRecords, depth, beenthere, context);
×
524
    setCacheOnly(oldCacheOnly);
×
525
    if (res == 0 && !addRecords.empty()) {
×
526
      // We're conservative here. We do not add Bogus records in any circumstance, we add Indeterminates only if no
527
      // validation is required.
528
      if (vStateIsBogus(context.state)) {
×
529
        return;
×
530
      }
×
531
      if (shouldValidate() && context.state != vState::Secure && context.state != vState::Insecure) {
×
532
        return;
×
533
      }
×
534
      bool found = false;
×
535
      for (auto& rec : addRecords) {
×
536
        if (rec.d_place == DNSResourceRecord::ANSWER) {
×
537
          found = true;
×
538
          additionals.push_back(std::move(rec));
×
539
        }
×
540
      }
×
541
      if (found) {
×
542
        return;
×
543
      }
×
544
    }
×
545
    // Not found in cache, check negcache and push task if also not in negcache
546
    if (pushResolveIfNotInNegCache(qname, qtype, d_now)) {
×
547
      additionalsNotInCache = true;
×
548
    }
×
549
    break;
×
550
  }
×
551
  case AdditionalMode::Ignore:
×
552
    break;
×
553
  }
29✔
554
}
29✔
555

556
// The main (recursive) function to add additionals
557
// qtype: the original query type to expand
558
// start: records to start from
559
// This function uses to state sets to avoid infinite recursion and allow depulication
560
// depth is the main recursion depth
561
// additionaldepth is the depth for addAdditionals itself
562
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)
563
{
17✔
564
  if (additionaldepth >= 5 || start.empty()) {
17!
565
    return;
×
566
  }
×
567

568
  auto luaLocal = g_luaconfs.getLocal();
17✔
569
  const auto iter = luaLocal->allowAdditionalQTypes.find(qtype);
17✔
570
  if (iter == luaLocal->allowAdditionalQTypes.end()) {
17✔
571
    return;
10✔
572
  }
10✔
573
  std::unordered_set<DNSName> addnames;
7✔
574
  for (const auto& rec : start) {
20✔
575
    if (rec.d_place == DNSResourceRecord::ANSWER) {
20!
576
      // currently, this function only knows about names, we could also take the target types that are dependent on
577
      // record contents into account
578
      // e.g. for NAPTR records, go only for SRV for flag value "s", or A/AAAA for flag value "a"
579
      allowAdditionalEntry(addnames, rec);
20✔
580
    }
20✔
581
  }
20✔
582

583
  // We maintain two sets for deduplication:
584
  // - uniqueCalls makes sure we never resolve a qname/qtype twice
585
  // - uniqueResults makes sure we never add the same qname/qytype RRSet to the result twice,
586
  //   but note that that set might contain multiple elements.
587

588
  auto mode = iter->second.second;
7✔
589
  for (const auto& targettype : iter->second.first) {
15✔
590
    for (const auto& addname : addnames) {
29✔
591
      std::vector<DNSRecord> records;
29✔
592
      bool inserted = uniqueCalls.emplace(addname, targettype).second;
29✔
593
      if (inserted) {
29!
594
        resolveAdditionals(addname, targettype, mode, records, depth, additionalsNotInCache);
29✔
595
      }
29✔
596
      if (!records.empty()) {
29✔
597
        for (auto record = records.begin(); record != records.end();) {
35✔
598
          QType covered = QType::ENT;
23✔
599
          if (record->d_type == QType::RRSIG) {
23✔
600
            if (auto rsig = getRR<RRSIGRecordContent>(*record); rsig != nullptr) {
10!
601
              covered = rsig->d_type;
10✔
602
            }
10✔
603
          }
10✔
604
          if (uniqueResults.count(std::tuple(record->d_name, QType(record->d_type), covered)) > 0) {
23!
605
            // A bit expensive for vectors, but they are small
606
            record = records.erase(record);
×
607
          }
×
608
          else {
23✔
609
            ++record;
23✔
610
          }
23✔
611
        }
23✔
612
        for (const auto& record : records) {
23✔
613
          additionals.push_back(record);
23✔
614
          QType covered = QType::ENT;
23✔
615
          if (record.d_type == QType::RRSIG) {
23✔
616
            if (auto rsig = getRR<RRSIGRecordContent>(record); rsig != nullptr) {
10!
617
              covered = rsig->d_type;
10✔
618
            }
10✔
619
          }
10✔
620
          uniqueResults.emplace(record.d_name, record.d_type, covered);
23✔
621
        }
23✔
622
        addAdditionals(targettype, records, additionals, uniqueCalls, uniqueResults, depth, additionaldepth + 1, additionalsNotInCache);
12✔
623
      }
12✔
624
    }
29✔
625
  }
15✔
626
}
7✔
627

628
// The entry point for other code
629
bool SyncRes::addAdditionals(QType qtype, vector<DNSRecord>& ret, unsigned int depth)
630
{
5✔
631
  // The additional records of interest
632
  std::vector<DNSRecord> additionals;
5✔
633

634
  // We only call resolve for a specific name/type combo once
635
  std::set<std::pair<DNSName, QType>> uniqueCalls;
5✔
636

637
  // Collect multiple name/qtype from a single resolve but do not add a new set from new resolve calls
638
  // For RRSIGs, the type covered is stored in the second Qtype
639
  std::set<std::tuple<DNSName, QType, QType>> uniqueResults;
5✔
640

641
  bool additionalsNotInCache = false;
5✔
642
  addAdditionals(qtype, ret, additionals, uniqueCalls, uniqueResults, depth, 0, additionalsNotInCache);
5✔
643

644
  for (auto& rec : additionals) {
23✔
645
    rec.d_place = DNSResourceRecord::ADDITIONAL;
23✔
646
    ret.push_back(std::move(rec));
23✔
647
  }
23✔
648
  return additionalsNotInCache;
5✔
649
}
5✔
650

651
/** everything begins here - this is the entry point just after receiving a packet */
652
int SyncRes::beginResolve(const DNSName& qname, const QType qtype, QClass qclass, vector<DNSRecord>& ret, unsigned int depth)
653
{
4,484✔
654
  auto newParent = d_eventTrace.add(RecEventTrace::SyncRes);
4,484✔
655
  auto oldParent = d_eventTrace.setParent(newParent);
4,484✔
656
  RecEventTrace::EventScope traceScope(oldParent, d_eventTrace);
4,484✔
657

658
  d_wasVariable = false;
4,484✔
659
  d_wasOutOfBand = false;
4,484✔
660
  d_cutStates.clear();
4,484✔
661

662
  if (doSpecialNamesResolve(qname, qtype, qclass, ret)) {
4,484✔
663
    d_queryValidationState = vState::Insecure; // this could fool our stats into thinking a validation took place
52✔
664
    return 0; // so do check before updating counters (we do now)
52✔
665
  }
52✔
666

667
  if (isUnsupported(qtype)) {
4,432✔
668
    return -1;
30✔
669
  }
30✔
670

671
  if (qclass == QClass::ANY) {
4,402!
672
    qclass = QClass::IN;
×
673
  }
×
674
  else if (qclass != QClass::IN) {
4,402✔
675
    return -1;
4✔
676
  }
4✔
677

678
  if (qtype == QType::DS) {
4,398✔
679
    d_externalDSQuery = qname;
38✔
680
  }
38✔
681
  else {
4,360✔
682
    d_externalDSQuery.clear();
4,360✔
683
  }
4,360✔
684

685
  set<GetBestNSAnswer> beenthere;
4,398✔
686
  Context context;
4,398✔
687
  int res = doResolve(qname, qtype, ret, depth, beenthere, context);
4,398✔
688
  d_queryValidationState = context.state;
4,398✔
689
  d_extendedError = context.extendedError;
4,398✔
690

691
  if (shouldValidate()) {
4,401✔
692
    if (d_queryValidationState != vState::Indeterminate) {
3,310✔
693
      t_Counters.at(rec::Counter::dnssecValidations)++;
3,206✔
694
    }
3,206✔
695
    auto xdnssec = g_xdnssec.getLocal();
3,310✔
696
    if (xdnssec->check(qname)) {
3,310!
697
      increaseXDNSSECStateCounter(d_queryValidationState);
×
698
    }
×
699
    else {
3,310✔
700
      increaseDNSSECStateCounter(d_queryValidationState);
3,310✔
701
    }
3,310✔
702
  }
3,310✔
703

704
  // Avoid calling addAdditionals() if we know we won't find anything
705
  auto luaLocal = g_luaconfs.getLocal();
4,398✔
706
  if (res == 0 && qclass == QClass::IN && luaLocal->allowAdditionalQTypes.find(qtype) != luaLocal->allowAdditionalQTypes.end()) {
4,398!
707
    bool additionalsNotInCache = addAdditionals(qtype, ret, depth);
5✔
708
    if (additionalsNotInCache) {
5!
709
      d_wasVariable = true;
×
710
    }
×
711
  }
5✔
712
  traceScope.close(res);
4,398✔
713
  return res;
4,398✔
714
}
4,402✔
715

716
/*! Handles all special, built-in names
717
 * Fills ret with an answer and returns true if it handled the query.
718
 *
719
 * Handles the following queries (and their ANY variants):
720
 *
721
 * - localhost. IN A
722
 * - localhost. IN AAAA
723
 * - 1.0.0.127.in-addr.arpa. IN PTR
724
 * - 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
725
 * - version.bind. CH TXT
726
 * - version.pdns. CH TXT
727
 * - id.server. CH TXT
728
 * - trustanchor.server CH TXT
729
 * - negativetrustanchor.server CH TXT
730
 */
731
bool SyncRes::doSpecialNamesResolve(const DNSName& qname, const QType qtype, const QClass qclass, vector<DNSRecord>& ret)
732
{
4,485✔
733
  static const DNSName arpa("1.0.0.127.in-addr.arpa.");
4,485✔
734
  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.");
4,485✔
735
  static const DNSName localhost("localhost.");
4,485✔
736
  static const DNSName versionbind("version.bind.");
4,485✔
737
  static const DNSName idserver("id.server.");
4,485✔
738
  static const DNSName versionpdns("version.pdns.");
4,485✔
739
  static const DNSName trustanchorserver("trustanchor.server.");
4,485✔
740
  static const DNSName negativetrustanchorserver("negativetrustanchor.server.");
4,485✔
741

742
  bool handled = false;
4,485✔
743
  vector<pair<QType::typeenum, string>> answers;
4,485✔
744

745
  if ((qname == arpa || qname == ip6_arpa) && qclass == QClass::IN) {
4,487!
746
    handled = true;
13✔
747
    if (qtype == QType::PTR || qtype == QType::ANY) {
13!
748
      answers.emplace_back(QType::PTR, "localhost.");
13✔
749
    }
13✔
750
  }
13✔
751

752
  if (qname.isPartOf(localhost) && qclass == QClass::IN) {
4,485!
753
    handled = true;
16✔
754
    if (qtype == QType::A || qtype == QType::ANY) {
16✔
755
      answers.emplace_back(QType::A, "127.0.0.1");
14✔
756
    }
14✔
757
    if (qtype == QType::AAAA || qtype == QType::ANY) {
16✔
758
      answers.emplace_back(QType::AAAA, "::1");
4✔
759
    }
4✔
760
  }
16✔
761

762
  if ((qname == versionbind || qname == idserver || qname == versionpdns) && qclass == QClass::CHAOS) {
4,487!
763
    handled = true;
21✔
764
    if (qtype == QType::TXT || qtype == QType::ANY) {
21!
765
      if (qname == versionbind || qname == versionpdns) {
21✔
766
        answers.emplace_back(QType::TXT, "\"" + ::arg()["version-string"] + "\"");
13✔
767
      }
13✔
768
      else if (s_serverID != "disabled") {
8!
769
        answers.emplace_back(QType::TXT, "\"" + s_serverID + "\"");
8✔
770
      }
8✔
771
    }
21✔
772
  }
21✔
773

774
  if (qname == trustanchorserver && qclass == QClass::CHAOS && ::arg().mustDo("allow-trust-anchor-query")) {
4,485!
775
    handled = true;
1✔
776
    if (qtype == QType::TXT || qtype == QType::ANY) {
1!
777
      auto luaLocal = g_luaconfs.getLocal();
1✔
778
      for (auto const& dsAnchor : luaLocal->dsAnchors) {
2✔
779
        ostringstream ans;
2✔
780
        ans << "\"";
2✔
781
        ans << dsAnchor.first.toString(); // Explicit toString to have a trailing dot
2✔
782
        for (auto const& dsRecord : dsAnchor.second) {
3✔
783
          ans << " ";
3✔
784
          ans << dsRecord.d_tag;
3✔
785
        }
3✔
786
        ans << "\"";
2✔
787
        answers.emplace_back(QType::TXT, ans.str());
2✔
788
      }
2✔
789
      d_wasVariable = true;
1✔
790
    }
1✔
791
  }
1✔
792

793
  if (qname == negativetrustanchorserver && qclass == QClass::CHAOS && ::arg().mustDo("allow-trust-anchor-query")) {
4,485!
794
    handled = true;
1✔
795
    if (qtype == QType::TXT || qtype == QType::ANY) {
1!
796
      auto luaLocal = g_luaconfs.getLocal();
1✔
797
      for (auto const& negAnchor : luaLocal->negAnchors) {
2✔
798
        ostringstream ans;
2✔
799
        ans << "\"";
2✔
800
        ans << negAnchor.first.toString(); // Explicit toString to have a trailing dot
2✔
801
        if (negAnchor.second.length() != 0) {
2✔
802
          ans << " " << txtEscape(negAnchor.second);
1✔
803
        }
1✔
804
        ans << "\"";
2✔
805
        answers.emplace_back(QType::TXT, ans.str());
2✔
806
      }
2✔
807
      d_wasVariable = true;
1✔
808
    }
1✔
809
  }
1✔
810

811
  if (handled && !answers.empty()) {
4,485!
812
    ret.clear();
52✔
813
    d_wasOutOfBand = true;
52✔
814

815
    DNSRecord dnsRecord;
52✔
816
    dnsRecord.d_name = qname;
52✔
817
    dnsRecord.d_place = DNSResourceRecord::ANSWER;
52✔
818
    dnsRecord.d_class = qclass;
52✔
819
    dnsRecord.d_ttl = 86400;
52✔
820
    for (const auto& ans : answers) {
56✔
821
      dnsRecord.d_type = ans.first;
56✔
822
      dnsRecord.setContent(DNSRecordContent::make(ans.first, qclass, ans.second));
56✔
823
      ret.push_back(dnsRecord);
56✔
824
    }
56✔
825
  }
52✔
826

827
  return handled;
4,485✔
828
}
4,485✔
829

830
//! This is the 'out of band resolver', in other words, the authoritative server
831
void SyncRes::AuthDomain::addSOA(std::vector<DNSRecord>& records) const
832
{
33✔
833
  SyncRes::AuthDomain::records_t::const_iterator ziter = d_records.find(std::tuple(getName(), QType::SOA));
33✔
834
  if (ziter != d_records.end()) {
33✔
835
    DNSRecord dnsRecord = *ziter;
23✔
836
    dnsRecord.d_place = DNSResourceRecord::AUTHORITY;
23✔
837
    records.push_back(std::move(dnsRecord));
23✔
838
  }
23✔
839
}
33✔
840

841
bool SyncRes::AuthDomain::operator==(const AuthDomain& rhs) const
842
{
88✔
843
  return d_records == rhs.d_records
88!
844
    && d_servers == rhs.d_servers
88!
845
    && d_name == rhs.d_name
88!
846
    && d_rdForward == rhs.d_rdForward;
88!
847
}
88✔
848

849
[[nodiscard]] std::string SyncRes::AuthDomain::print(const std::string& indent,
850
                                                     const std::string& indentLevel) const
851
{
176✔
852
  std::stringstream outputsStream;
176✔
853
  outputsStream << indent << "DNSName = " << d_name << std::endl;
176✔
854
  outputsStream << indent << "rdForward = " << d_rdForward << std::endl;
176✔
855
  outputsStream << indent << "Records {" << std::endl;
176✔
856
  auto recordContentIndentation = indent;
176✔
857
  recordContentIndentation += indentLevel;
176✔
858
  recordContentIndentation += indentLevel;
176✔
859
  for (const auto& record : d_records) {
536✔
860
    outputsStream << indent << indentLevel << "Record `" << record.d_name << "` {" << std::endl;
536✔
861
    outputsStream << record.print(recordContentIndentation);
536✔
862
    outputsStream << indent << indentLevel << "}" << std::endl;
536✔
863
  }
536✔
864
  outputsStream << indent << "}" << std::endl;
176✔
865
  outputsStream << indent << "Servers {" << std::endl;
176✔
866
  for (const auto& server : d_servers) {
176!
867
    outputsStream << indent << indentLevel << server.toString() << std::endl;
×
868
  }
×
869
  outputsStream << indent << "}" << std::endl;
176✔
870
  return outputsStream.str();
176✔
871
}
176✔
872

873
int SyncRes::AuthDomain::getRecords(const DNSName& qname, const QType qtype, std::vector<DNSRecord>& records) const
874
{
152✔
875
  int result = RCode::NoError;
152✔
876
  records.clear();
152✔
877

878
  // partial lookup
879
  std::pair<records_t::const_iterator, records_t::const_iterator> range = d_records.equal_range(std::tie(qname));
152✔
880

881
  SyncRes::AuthDomain::records_t::const_iterator ziter;
152✔
882
  bool somedata = false;
152✔
883

884
  for (ziter = range.first; ziter != range.second; ++ziter) {
313✔
885
    somedata = true;
161✔
886

887
    if (qtype == QType::ANY || ziter->d_type == qtype || ziter->d_type == QType::CNAME) {
161✔
888
      // let rest of nameserver do the legwork on this one
889
      records.push_back(*ziter);
116✔
890
    }
116✔
891
    else if (ziter->d_type == QType::NS && ziter->d_name.countLabels() > getName().countLabels()) {
45!
892
      // we hit a delegation point!
893
      DNSRecord dnsRecord = *ziter;
2✔
894
      dnsRecord.d_place = DNSResourceRecord::AUTHORITY;
2✔
895
      records.push_back(std::move(dnsRecord));
2✔
896
    }
2✔
897
  }
161✔
898

899
  if (!records.empty()) {
152✔
900
    /* We have found an exact match, we're done */
901
    return result;
113✔
902
  }
113✔
903

904
  if (somedata) {
39✔
905
    /* We have records for that name, but not of the wanted qtype */
906
    addSOA(records);
10✔
907

908
    return result;
10✔
909
  }
10✔
910

911
  DNSName wcarddomain(qname);
29✔
912
  while (wcarddomain != getName() && wcarddomain.chopOff()) {
74✔
913
    range = d_records.equal_range(std::tuple(g_wildcarddnsname + wcarddomain));
51✔
914
    if (range.first == range.second) {
51✔
915
      continue;
45✔
916
    }
45✔
917
    for (ziter = range.first; ziter != range.second; ++ziter) {
12✔
918
      DNSRecord dnsRecord = *ziter;
6✔
919
      // if we hit a CNAME, just answer that - rest of recursor will do the needful & follow
920
      if (dnsRecord.d_type == qtype || qtype == QType::ANY || dnsRecord.d_type == QType::CNAME) {
6!
921
        dnsRecord.d_name = qname;
4✔
922
        dnsRecord.d_place = DNSResourceRecord::ANSWER;
4✔
923
        records.push_back(std::move(dnsRecord));
4✔
924
      }
4✔
925
    }
6✔
926

927
    if (records.empty()) {
6✔
928
      addSOA(records);
2✔
929
    }
2✔
930

931
    return result;
6✔
932
  }
51✔
933

934
  /* Nothing for this name, no wildcard, let's see if there is some NS */
935
  DNSName nsdomain(qname);
23✔
936
  while (nsdomain.chopOff() && nsdomain != getName()) {
55✔
937
    range = d_records.equal_range(std::tuple(nsdomain, QType::NS));
32✔
938
    if (range.first == range.second) {
32✔
939
      continue;
30✔
940
    }
30✔
941
    for (ziter = range.first; ziter != range.second; ++ziter) {
4✔
942
      DNSRecord dnsRecord = *ziter;
2✔
943
      dnsRecord.d_place = DNSResourceRecord::AUTHORITY;
2✔
944
      records.push_back(std::move(dnsRecord));
2✔
945
    }
2✔
946
  }
2✔
947

948
  if (records.empty()) {
23✔
949
    addSOA(records);
21✔
950
    result = RCode::NXDomain;
21✔
951
  }
21✔
952

953
  return result;
23✔
954
}
29✔
955

956
bool SyncRes::doOOBResolve(const AuthDomain& domain, const DNSName& qname, const QType qtype, vector<DNSRecord>& ret, int& res)
957
{
152✔
958
  d_authzonequeries++;
152✔
959
  t_Counters.at(rec::Counter::authzonequeries)++;
152✔
960

961
  res = domain.getRecords(qname, qtype, ret);
152✔
962
  return true;
152✔
963
}
152✔
964

965
bool SyncRes::doOOBResolve(const DNSName& qname, const QType qtype, vector<DNSRecord>& ret, unsigned int /* depth */, const string& prefix, int& res)
966
{
153✔
967
  DNSName authdomain(qname);
153✔
968
  const auto iter = getBestAuthZone(&authdomain);
153✔
969
  if (iter == t_sstorage.domainmap->end() || !iter->second.isAuth()) {
153!
970
    LOG(prefix << qname << ": Auth storage has no zone for this query!" << endl);
1!
971
    return false;
1✔
972
  }
1✔
973

974
  LOG(prefix << qname << ": Auth storage has data, zone='" << authdomain << "'" << endl);
152!
975
  return doOOBResolve(iter->second, qname, qtype, ret, res);
152✔
976
}
153✔
977

978
bool SyncRes::isRecursiveForwardOrAuth(const DNSName& qname)
979
{
20,790✔
980
  DNSName authname(qname);
20,790✔
981
  const auto iter = getBestAuthZone(&authname);
20,790✔
982
  return iter != t_sstorage.domainmap->end() && (iter->second.isAuth() || iter->second.shouldRecurse());
20,790✔
983
}
20,790✔
984

985
bool SyncRes::isRecursiveForward(const DNSName& qname)
986
{
14,659✔
987
  DNSName authname(qname);
14,659✔
988
  const auto iter = getBestAuthZone(&authname);
14,659✔
989
  return iter != t_sstorage.domainmap->end() && iter->second.shouldRecurse();
14,659!
990
}
14,659✔
991

992
bool SyncRes::isForwardOrAuth(const DNSName& qname)
993
{
9✔
994
  DNSName authname(qname);
9✔
995
  const auto iter = getBestAuthZone(&authname);
9✔
996
  return iter != t_sstorage.domainmap->end();
9✔
997
}
9✔
998

999
const char* isoDateTimeMillis(const struct timeval& tval, timebuf_t& buf)
1000
{
64✔
1001
  const std::string s_timestampFormat = "%Y-%m-%dT%T";
64✔
1002
  struct tm tmval{};
64✔
1003
  size_t len = strftime(buf.data(), buf.size(), s_timestampFormat.c_str(), localtime_r(&tval.tv_sec, &tmval));
64✔
1004
  if (len == 0) {
64!
1005
    int ret = snprintf(buf.data(), buf.size(), "%lld", static_cast<long long>(tval.tv_sec));
×
1006
    if (ret < 0 || static_cast<size_t>(ret) >= buf.size()) {
×
1007
      buf[0] = '\0';
×
1008
      return buf.data();
×
1009
    }
×
1010
    len = ret;
×
1011
  }
×
1012

1013
  if (buf.size() > len + 4) {
64!
1014
    snprintf(&buf.at(len), buf.size() - len, ".%03ld", static_cast<long>(tval.tv_usec) / 1000);
64✔
1015
  }
64✔
1016
  return buf.data();
64✔
1017
}
64✔
1018

1019
struct ednsstatus_t : public multi_index_container<SyncRes::EDNSStatus,
1020
                                                   indexed_by<
1021
                                                     ordered_unique<tag<ComboAddress>, member<SyncRes::EDNSStatus, ComboAddress, &SyncRes::EDNSStatus::address>>,
1022
                                                     ordered_non_unique<tag<time_t>, member<SyncRes::EDNSStatus, time_t, &SyncRes::EDNSStatus::ttd>>>>
1023
{
1024
  // Get a copy
1025
  [[nodiscard]] ednsstatus_t getMap() const
1026
  {
×
1027
    return *this;
×
1028
  }
×
1029

1030
  static void setMode(index<ComboAddress>::type& ind, iterator iter, SyncRes::EDNSStatus::EDNSMode mode, time_t theTime)
1031
  {
455✔
1032
    if (iter->mode != mode || iter->ttd == 0) {
455!
1033
      ind.modify(iter, [=](SyncRes::EDNSStatus& status) { status.mode = mode; status.ttd = theTime + Expire; });
117✔
1034
    }
117✔
1035
  }
455✔
1036

1037
  void prune(time_t now)
1038
  {
100✔
1039
    auto& ind = get<time_t>();
100✔
1040
    ind.erase(ind.begin(), ind.upper_bound(now));
100✔
1041
  }
100✔
1042

1043
  static const time_t Expire = 7200;
1044
};
1045

1046
static LockGuarded<ednsstatus_t> s_ednsstatus;
1047

1048
SyncRes::EDNSStatus::EDNSMode SyncRes::getEDNSStatus(const ComboAddress& server)
1049
{
70✔
1050
  auto lock = s_ednsstatus.lock();
70✔
1051
  const auto& iter = lock->find(server);
70✔
1052
  if (iter == lock->end()) {
70✔
1053
    return EDNSStatus::EDNSOK;
64✔
1054
  }
64✔
1055
  return iter->mode;
6✔
1056
}
70✔
1057

1058
uint64_t SyncRes::getEDNSStatusesSize()
1059
{
661✔
1060
  return s_ednsstatus.lock()->size();
661✔
1061
}
661✔
1062

1063
void SyncRes::clearEDNSStatuses()
1064
{
612✔
1065
  s_ednsstatus.lock()->clear();
612✔
1066
}
612✔
1067

1068
void SyncRes::pruneEDNSStatuses(time_t cutoff)
1069
{
100✔
1070
  s_ednsstatus.lock()->prune(cutoff);
100✔
1071
}
100✔
1072

1073
uint64_t SyncRes::doEDNSDump(int fileDesc)
1074
{
×
1075
  int newfd = dup(fileDesc);
×
1076
  if (newfd == -1) {
×
1077
    return 0;
×
1078
  }
×
1079
  auto filePtr = pdns::UniqueFilePtr(fdopen(newfd, "w"));
×
1080
  if (!filePtr) {
×
1081
    close(newfd);
×
1082
    return 0;
×
1083
  }
×
1084
  uint64_t count = 0;
×
1085

1086
  fprintf(filePtr.get(), "; edns dump follows\n; ip\tstatus\tttd\n");
×
1087
  const auto copy = s_ednsstatus.lock()->getMap();
×
1088
  for (const auto& eds : copy) {
×
1089
    count++;
×
1090
    timebuf_t tmp;
×
1091
    fprintf(filePtr.get(), "%s\t%s\t%s\n", eds.address.toString().c_str(), eds.toString().c_str(), timestamp(eds.ttd, tmp));
×
1092
  }
×
1093
  return count;
×
1094
}
×
1095

1096
void SyncRes::pruneNSSpeeds(time_t limit)
1097
{
100✔
1098
  auto lock = s_nsSpeeds.lock();
100✔
1099
  auto& ind = lock->get<timeval>();
100✔
1100
  ind.erase(ind.begin(), ind.upper_bound(timeval{limit, 0}));
100✔
1101
}
100✔
1102

1103
uint64_t SyncRes::getNSSpeedsSize()
1104
{
803✔
1105
  return s_nsSpeeds.lock()->size();
803✔
1106
}
803✔
1107

1108
void SyncRes::submitNSSpeed(const DNSName& server, const ComboAddress& address, int usec, const struct timeval& now)
1109
{
12✔
1110
  auto lock = s_nsSpeeds.lock();
12✔
1111
  lock->find_or_enter(server, now).submit(address, usec, now);
12✔
1112
}
12✔
1113

1114
void SyncRes::clearNSSpeeds()
1115
{
614✔
1116
  s_nsSpeeds.lock()->clear();
614✔
1117
}
614✔
1118

1119
float SyncRes::getNSSpeed(const DNSName& server, const ComboAddress& address)
1120
{
20✔
1121
  auto lock = s_nsSpeeds.lock();
20✔
1122
  return lock->find_or_enter(server).d_collection[address].peek();
20✔
1123
}
20✔
1124

1125
uint64_t SyncRes::doDumpNSSpeeds(int fileDesc)
1126
{
4✔
1127
  int newfd = dup(fileDesc);
4✔
1128
  if (newfd == -1) {
4!
1129
    return 0;
×
1130
  }
×
1131
  auto filePtr = pdns::UniqueFilePtr(fdopen(newfd, "w"));
4✔
1132
  if (!filePtr) {
4!
1133
    close(newfd);
×
1134
    return 0;
×
1135
  }
×
1136

1137
  fprintf(filePtr.get(), "; nsspeed dump follows\n; nsname\ttimestamp\t[ip/decaying-ms/last-ms...]\n");
4✔
1138
  uint64_t count = 0;
4✔
1139

1140
  // Create a copy to avoid holding the lock while doing I/O
1141
  for (const auto& iter : *s_nsSpeeds.lock()) {
64✔
1142
    count++;
64✔
1143

1144
    // an <empty> can appear hear in case of authoritative (hosted) zones
1145
    timebuf_t tmp;
64✔
1146
    fprintf(filePtr.get(), "%s\t%s\t", iter.d_name.toLogString().c_str(), isoDateTimeMillis(iter.d_lastget, tmp));
64✔
1147
    bool first = true;
64✔
1148
    for (const auto& line : iter.d_collection) {
64✔
1149
      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);
32✔
1150
      first = false;
32✔
1151
    }
32✔
1152
    fprintf(filePtr.get(), "\n");
64✔
1153
  }
64✔
1154
  return count;
4✔
1155
}
4✔
1156

1157
uint64_t SyncRes::getThrottledServersSize()
1158
{
799✔
1159
  return s_throttle.lock()->size();
799✔
1160
}
799✔
1161

1162
void SyncRes::pruneThrottledServers(time_t now)
1163
{
130✔
1164
  s_throttle.lock()->prune(now);
130✔
1165
}
130✔
1166

1167
void SyncRes::clearThrottle()
1168
{
612✔
1169
  s_throttle.lock()->clear();
612✔
1170
}
612✔
1171

1172
bool SyncRes::isThrottled(time_t now, const ComboAddress& server, const DNSName& target, QType qtype)
1173
{
13,140✔
1174
  return s_throttle.lock()->shouldThrottle(now, std::tuple(server, target, qtype));
13,140✔
1175
}
13,140✔
1176

1177
bool SyncRes::isThrottled(time_t now, const ComboAddress& server)
1178
{
13,126✔
1179
  auto throttled = s_throttle.lock()->shouldThrottle(now, std::tuple(server, g_rootdnsname, 0));
13,126✔
1180
  if (throttled) {
13,126✔
1181
    // Give fully throttled servers a chance to be used, to avoid having one bad zone spoil the NS
1182
    // record for others using the same NS. If the NS answers, it will be unThrottled immediately
1183
    if (s_unthrottle_n > 0 && dns_random(s_unthrottle_n) == 0) {
24!
1184
      throttled = false;
×
1185
    }
×
1186
  }
24✔
1187
  return throttled;
13,126✔
1188
}
13,126✔
1189

1190
void SyncRes::unThrottle(const ComboAddress& server, const DNSName& name, QType qtype)
1191
{
12,595✔
1192
  s_throttle.lock()->clear(std::tuple(server, g_rootdnsname, 0));
12,595✔
1193
  s_throttle.lock()->clear(std::tuple(server, name, qtype));
12,595✔
1194
}
12,595✔
1195

1196
void SyncRes::doThrottle(time_t now, const ComboAddress& server, time_t duration, unsigned int tries, Throttle::Reason reason)
1197
{
6✔
1198
  s_throttle.lock()->throttle(now, std::tuple(server, g_rootdnsname, 0), duration, tries, reason);
6✔
1199
}
6✔
1200

1201
void SyncRes::doThrottle(time_t now, const ComboAddress& server, const DNSName& name, QType qtype, time_t duration, unsigned int tries, Throttle::Reason reason)
1202
{
234✔
1203
  s_throttle.lock()->throttle(now, std::tuple(server, name, qtype), duration, tries, reason);
234✔
1204
}
234✔
1205

1206
uint64_t SyncRes::doDumpThrottleMap(int fileDesc)
1207
{
×
1208
  int newfd = dup(fileDesc);
×
1209
  if (newfd == -1) {
×
1210
    return 0;
×
1211
  }
×
1212
  auto filePtr = pdns::UniqueFilePtr(fdopen(newfd, "w"));
×
1213
  if (!filePtr) {
×
1214
    close(newfd);
×
1215
    return 0;
×
1216
  }
×
1217
  fprintf(filePtr.get(), "; throttle map dump follows\n");
×
1218
  fprintf(filePtr.get(), "; remote IP\tqname\tqtype\tcount\tttd\treason\n");
×
1219
  uint64_t count = 0;
×
1220

1221
  // Get a copy to avoid holding the lock while doing I/O
1222
  const auto throttleMap = s_throttle.lock()->getThrottleMap();
×
1223
  for (const auto& iter : throttleMap) {
×
1224
    count++;
×
1225
    timebuf_t tmp;
×
1226
    // remote IP, dns name, qtype, count, ttd, reason
1227
    fprintf(filePtr.get(), "%s\t%s\t%s\t%u\t%s\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), Throttle::toString(iter.reason).c_str());
×
1228
  }
×
1229

1230
  return count;
×
1231
}
×
1232

1233
uint64_t SyncRes::getFailedServersSize()
1234
{
799✔
1235
  return s_fails.lock()->size();
799✔
1236
}
799✔
1237

1238
void SyncRes::clearFailedServers()
1239
{
612✔
1240
  s_fails.lock()->clear();
612✔
1241
}
612✔
1242

1243
void SyncRes::pruneFailedServers(time_t cutoff)
1244
{
130✔
1245
  s_fails.lock()->prune(cutoff);
130✔
1246
}
130✔
1247

1248
unsigned long SyncRes::getServerFailsCount(const ComboAddress& server)
1249
{
74✔
1250
  return s_fails.lock()->value(server);
74✔
1251
}
74✔
1252

1253
uint64_t SyncRes::doDumpFailedServers(int fileDesc)
1254
{
×
1255
  int newfd = dup(fileDesc);
×
1256
  if (newfd == -1) {
×
1257
    return 0;
×
1258
  }
×
1259
  auto filePtr = pdns::UniqueFilePtr(fdopen(newfd, "w"));
×
1260
  if (!filePtr) {
×
1261
    close(newfd);
×
1262
    return 0;
×
1263
  }
×
1264
  fprintf(filePtr.get(), "; failed servers dump follows\n");
×
1265
  fprintf(filePtr.get(), "; remote IP\tcount\ttimestamp\n");
×
1266
  uint64_t count = 0;
×
1267

1268
  // We get a copy, so the I/O does not need to happen while holding the lock
1269
  for (const auto& iter : s_fails.lock()->getMapCopy()) {
×
1270
    count++;
×
1271
    timebuf_t tmp;
×
1272
    fprintf(filePtr.get(), "%s\t%" PRIu64 "\t%s\n", iter.key.toString().c_str(), iter.value, timestamp(iter.last, tmp));
×
1273
  }
×
1274

1275
  return count;
×
1276
}
×
1277

1278
uint64_t SyncRes::getNonResolvingNSSize()
1279
{
807✔
1280
  return s_nonresolving.lock()->size();
807✔
1281
}
807✔
1282

1283
void SyncRes::clearNonResolvingNS()
1284
{
612✔
1285
  s_nonresolving.lock()->clear();
612✔
1286
}
612✔
1287

1288
void SyncRes::pruneNonResolving(time_t cutoff)
1289
{
130✔
1290
  s_nonresolving.lock()->prune(cutoff);
130✔
1291
}
130✔
1292

1293
uint64_t SyncRes::doDumpNonResolvingNS(int fileDesc)
1294
{
×
1295
  int newfd = dup(fileDesc);
×
1296
  if (newfd == -1) {
×
1297
    return 0;
×
1298
  }
×
1299
  auto filePtr = pdns::UniqueFilePtr(fdopen(newfd, "w"));
×
1300
  if (!filePtr) {
×
1301
    close(newfd);
×
1302
    return 0;
×
1303
  }
×
1304
  fprintf(filePtr.get(), "; non-resolving nameserver dump follows\n");
×
1305
  fprintf(filePtr.get(), "; name\tcount\ttimestamp\n");
×
1306
  uint64_t count = 0;
×
1307

1308
  // We get a copy, so the I/O does not need to happen while holding the lock
1309
  for (const auto& iter : s_nonresolving.lock()->getMapCopy()) {
×
1310
    count++;
×
1311
    timebuf_t tmp;
×
1312
    fprintf(filePtr.get(), "%s\t%" PRIu64 "\t%s\n", iter.key.toString().c_str(), iter.value, timestamp(iter.last, tmp));
×
1313
  }
×
1314

1315
  return count;
×
1316
}
×
1317

1318
void SyncRes::clearSaveParentsNSSets()
1319
{
612✔
1320
  s_savedParentNSSet.lock()->clear();
612✔
1321
}
612✔
1322

1323
size_t SyncRes::getSaveParentsNSSetsSize()
1324
{
659✔
1325
  return s_savedParentNSSet.lock()->size();
659✔
1326
}
659✔
1327

1328
void SyncRes::pruneSaveParentsNSSets(time_t now)
1329
{
100✔
1330
  s_savedParentNSSet.lock()->prune(now);
100✔
1331
}
100✔
1332

1333
uint64_t SyncRes::doDumpSavedParentNSSets(int fileDesc)
1334
{
×
1335
  int newfd = dup(fileDesc);
×
1336
  if (newfd == -1) {
×
1337
    return 0;
×
1338
  }
×
1339
  auto filePtr = pdns::UniqueFilePtr(fdopen(newfd, "w"));
×
1340
  if (!filePtr) {
×
1341
    close(newfd);
×
1342
    return 0;
×
1343
  }
×
1344
  fprintf(filePtr.get(), "; dump of saved parent nameserver sets successfully used follows\n");
×
1345
  fprintf(filePtr.get(), "; total entries: %zu\n", s_savedParentNSSet.lock()->size());
×
1346
  fprintf(filePtr.get(), "; domain\tsuccess\tttd\n");
×
1347
  uint64_t count = 0;
×
1348

1349
  // We get a copy, so the I/O does not need to happen while holding the lock
1350
  for (const auto& iter : s_savedParentNSSet.lock()->getMapCopy()) {
×
1351
    if (iter.d_count == 0) {
×
1352
      continue;
×
1353
    }
×
1354
    count++;
×
1355
    timebuf_t tmp;
×
1356
    fprintf(filePtr.get(), "%s\t%" PRIu64 "\t%s\n", iter.d_domain.toString().c_str(), iter.d_count, timestamp(iter.d_ttd, tmp));
×
1357
  }
×
1358
  return count;
×
1359
}
×
1360

1361
void SyncRes::pruneDoTProbeMap(time_t cutoff)
1362
{
×
1363
  auto lock = s_dotMap.lock();
×
1364
  auto& ind = lock->d_map.get<time_t>();
×
1365

1366
  for (auto i = ind.begin(); i != ind.end();) {
×
1367
    if (i->d_ttd >= cutoff) {
×
1368
      // We're done as we loop ordered by d_ttd
1369
      break;
×
1370
    }
×
1371
    if (i->d_status == DoTStatus::Status::Busy) {
×
1372
      lock->d_numBusy--;
×
1373
    }
×
1374
    i = ind.erase(i);
×
1375
  }
×
1376
}
×
1377

1378
uint64_t SyncRes::doDumpDoTProbeMap(int fileDesc)
1379
{
×
1380
  int newfd = dup(fileDesc);
×
1381
  if (newfd == -1) {
×
1382
    return 0;
×
1383
  }
×
1384
  auto filePtr = pdns::UniqueFilePtr(fdopen(newfd, "w"));
×
1385
  if (!filePtr) {
×
1386
    close(newfd);
×
1387
    return 0;
×
1388
  }
×
1389
  fprintf(filePtr.get(), "; DoT probing map follows\n");
×
1390
  fprintf(filePtr.get(), "; ip\tdomain\tcount\tstatus\tttd\n");
×
1391
  uint64_t count = 0;
×
1392

1393
  // We get a copy, so the I/O does not need to happen while holding the lock
1394
  DoTMap copy;
×
1395
  {
×
1396
    copy = *s_dotMap.lock();
×
1397
  }
×
1398
  fprintf(filePtr.get(), "; %" PRIu64 " Busy entries\n", copy.d_numBusy);
×
1399
  for (const auto& iter : copy.d_map) {
×
1400
    count++;
×
1401
    timebuf_t tmp;
×
1402
    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));
×
1403
  }
×
1404
  return count;
×
1405
}
×
1406

1407
/* so here is the story. First we complete the full resolution process for a domain name. And only THEN do we decide
1408
   to also do DNSSEC validation, which leads to new queries. To make this simple, we *always* ask for DNSSEC records
1409
   so that if there are RRSIGs for a name, we'll have them.
1410

1411
   However, some hosts simply can't answer questions which ask for DNSSEC. This can manifest itself as:
1412
   * No answer
1413
   * FormErr
1414
   * Nonsense answer
1415

1416
   The cause of "No answer" may be fragmentation, and it is tempting to probe if smaller answers would get through.
1417
   Another cause of "No answer" may simply be a network condition.
1418
   Nonsense answers are a clearer indication this host won't be able to do DNSSEC evah.
1419

1420
   Previous implementations have suffered from turning off DNSSEC questions for an authoritative server based on timeouts.
1421
   A clever idea is to only turn off DNSSEC if we know a domain isn't signed anyhow. The problem with that really
1422
   clever idea however is that at this point in PowerDNS, we may simply not know that yet. All the DNSSEC thinking happens
1423
   elsewhere. It may not have happened yet.
1424

1425
   For now this means we can't be clever, but will turn off DNSSEC if you reply with FormError or gibberish.
1426
*/
1427

1428
LWResult::Result SyncRes::asyncresolveWrapper(const OptLog& log, const ComboAddress& address, bool ednsMANDATORY, const DNSName& domain, [[maybe_unused]] const DNSName& auth, int type, bool doTCP, bool sendRDQuery, struct timeval* now, std::optional<Netmask>& srcmask, LWResult* res, bool* chained, const DNSName& nsName) const
1429
{
13,351✔
1430
  /* what is your QUEST?
1431
     the goal is to get as many remotes as possible on the best level of EDNS support
1432
     The levels are:
1433

1434
     1) EDNSOK: Honors EDNS0, absent from table
1435
     2) EDNSIGNORANT: Ignores EDNS0, gives replies without EDNS0
1436
     3) NOEDNS: Generates FORMERR on EDNS queries
1437

1438
     Everybody starts out assumed to be EDNSOK.
1439
     If EDNSOK, send out EDNS0
1440
        If you FORMERR us, go to NOEDNS,
1441
        If no EDNS in response, go to EDNSIGNORANT
1442
     If EDNSIGNORANT, keep on including EDNS0, see what happens
1443
        Same behaviour as EDNSOK
1444
     If NOEDNS, send bare queries
1445
  */
1446

1447
  // Read current status, defaulting to OK
1448
  SyncRes::EDNSStatus::EDNSMode mode = EDNSStatus::EDNSOK;
13,351✔
1449
  {
13,351✔
1450
    auto lock = s_ednsstatus.lock();
13,351✔
1451
    auto ednsstatus = lock->find(address); // does this include port? YES
13,351✔
1452
    if (ednsstatus != lock->end()) {
13,351✔
1453
      if (ednsstatus->ttd != 0 && ednsstatus->ttd < d_now.tv_sec) {
453!
1454
        lock->erase(ednsstatus);
×
1455
      }
×
1456
      else {
453✔
1457
        mode = ednsstatus->mode;
453✔
1458
      }
453✔
1459
    }
453✔
1460
  }
13,351✔
1461

1462
  int EDNSLevel = 0;
13,351✔
1463
  auto luaconfsLocal = g_luaconfs.getLocal();
13,351✔
1464
  ResolveContext ctx(d_initialRequestId, nsName, auth);
13,351✔
1465

1466
  LWResult::Result ret{};
13,351✔
1467

1468
  for (int tries = 0; tries < 2; ++tries) {
13,362✔
1469

1470
    if (mode == EDNSStatus::NOEDNS) {
13,361✔
1471
      t_Counters.at(rec::Counter::noEdnsOutQueries)++;
3✔
1472
      EDNSLevel = 0; // level != mode
3✔
1473
    }
3✔
1474
    else if (ednsMANDATORY || mode != EDNSStatus::NOEDNS) {
13,358!
1475
      EDNSLevel = 1;
13,358✔
1476
    }
13,358✔
1477

1478
    DNSName sendQname(domain);
13,361✔
1479
    if (g_lowercaseOutgoing) {
13,361✔
1480
      sendQname.makeUsLowerCase();
8✔
1481
    }
8✔
1482

1483
    if (d_asyncResolve) {
13,361✔
1484
      ret = d_asyncResolve(address, sendQname, type, doTCP, sendRDQuery, EDNSLevel, now, srcmask, ctx, res, chained);
2,526✔
1485
    }
2,526✔
1486
    else {
10,835✔
1487
      ret = asyncresolve(log, address, sendQname, type, doTCP, sendRDQuery, EDNSLevel, now, srcmask, ctx, d_outgoingProtobufServers, d_frameStreamServers, luaconfsLocal->outgoingProtobufExportConfig.exportTypes, res, chained);
10,835✔
1488
    }
10,835✔
1489

1490
    if (ret == LWResult::Result::PermanentError || LWResult::isLimitError(ret) || ret == LWResult::Result::Spoofed) {
13,361✔
1491
      break; // transport error, nothing to learn here
79✔
1492
    }
79✔
1493

1494
    if (ret == LWResult::Result::Timeout) { // timeout, not doing anything with it now
13,282✔
1495
      break;
343✔
1496
    }
343✔
1497

1498
    if (EDNSLevel == 1) {
12,939✔
1499
      // We sent out with EDNS
1500
      // ret is LWResult::Result::Success
1501
      // ednsstatus in table might be pruned or changed by another request/thread, so do a new lookup/insert if needed
1502
      auto lock = s_ednsstatus.lock(); // all three branches below need a lock
12,933✔
1503

1504
      // Determine new mode
1505
      if (ret == LWResult::Result::BindError) {
12,933!
1506
        // BindError is only generated when cookies are active and we failed to bind to a local
1507
        // address associated with a cookie, see RFC9018 section 3 last paragraph. We assume the
1508
        // called code has already erased the cookie info.
1509
        // This is the first path that re-iterates the loop
1510
        continue;
×
1511
      }
×
1512
      if (res->d_validpacket && res->d_haveEDNS && ret == LWResult::Result::BadCookie) {
12,934✔
1513
        // We assume the received cookie was stored and will be used in the second iteration
1514
        // This is the second path that re-iterates the loop
1515
        continue;
7✔
1516
      }
7✔
1517
      if (res->d_validpacket && !res->d_haveEDNS && res->d_rcode == RCode::FormErr) {
12,927✔
1518
        mode = EDNSStatus::NOEDNS;
3✔
1519
        auto ednsstatus = lock->insert(address).first;
3✔
1520
        auto& ind = lock->get<ComboAddress>();
3✔
1521
        lock->setMode(ind, ednsstatus, mode, d_now.tv_sec);
3✔
1522
        // This is the third path that re-iterates the loop
1523
        continue;
3✔
1524
      }
3✔
1525
      if (!res->d_haveEDNS) {
12,923✔
1526
        auto ednsstatus = lock->insert(address).first;
452✔
1527
        auto& ind = lock->get<ComboAddress>();
452✔
1528
        lock->setMode(ind, ednsstatus, EDNSStatus::EDNSIGNORANT, d_now.tv_sec);
452✔
1529
      }
452✔
1530
      else {
12,471✔
1531
        // New status is EDNSOK
1532
        lock->erase(address);
12,471✔
1533
      }
12,471✔
1534
    }
12,923✔
1535

1536
    break;
12,929✔
1537
  }
12,939✔
1538
  return ret;
13,351✔
1539
}
13,351✔
1540

1541
/* The parameters from rfc9156. */
1542
/* maximum number of QNAME minimization iterations */
1543
unsigned int SyncRes::s_max_minimize_count; // default is 10
1544
/* number of iterations that should only have one label appended */
1545
unsigned int SyncRes::s_minimize_one_label; // default is 4
1546

1547
static unsigned int qmStepLen(unsigned int labels, unsigned int qnamelen, unsigned int qmIteration)
1548
{
18,728✔
1549
  unsigned int step{};
18,728✔
1550

1551
  if (qmIteration < SyncRes::s_minimize_one_label) {
18,728✔
1552
    step = 1;
18,605✔
1553
  }
18,605✔
1554
  else if (qmIteration < SyncRes::s_max_minimize_count) {
123!
1555
    step = std::max(1U, (qnamelen - labels) / (SyncRes::s_max_minimize_count - qmIteration));
123✔
1556
  }
123✔
1557
  else {
×
1558
    step = qnamelen - labels;
×
1559
  }
×
1560
  unsigned int targetlen = std::min(labels + step, qnamelen);
18,728✔
1561
  return targetlen;
18,728✔
1562
}
18,728✔
1563

1564
static string resToString(int res)
1565
{
1,163✔
1566
  return res >= 0 ? RCode::to_s(res) : std::to_string(res);
1,163!
1567
}
1,163✔
1568

1569
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)
1570
{
22,881✔
1571
  auto prefix = getPrefix(depth);
22,881✔
1572
  auto luaconfsLocal = g_luaconfs.getLocal();
22,881✔
1573

1574
  /* Apply qname (including CNAME chain) filtering policies */
1575
  if (d_wantsRPZ && !d_appliedPolicy.wasHit()) {
22,882✔
1576
    if (luaconfsLocal->dfe.getQueryPolicy(qname, d_discardedPolicies, d_appliedPolicy)) {
22,859✔
1577
      mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());
100✔
1578
      bool done = false;
100✔
1579
      int rcode = RCode::NoError;
100✔
1580
      handlePolicyHit(prefix, qname, qtype, ret, done, rcode, depth);
100✔
1581
      if (done) {
100✔
1582
        return rcode;
64✔
1583
      }
64✔
1584
    }
100✔
1585
  }
22,859✔
1586

1587
  initZoneCutsFromTA(qname, prefix);
22,817✔
1588

1589
  // In the auth or recursive forward case, it does not make sense to do qname-minimization
1590
  if (!getQNameMinimization() || isRecursiveForwardOrAuth(qname)) {
22,817✔
1591
    return doResolveNoQNameMinimization(qname, qtype, ret, depth, beenthere, context);
2,354✔
1592
  }
2,354✔
1593

1594
  // The qname minimization algorithm is a simplified version of the one in RFC 7816 (bis).
1595
  // It could be simplified because the cache maintenance (both positive and negative)
1596
  // is already done by doResolveNoQNameMinimization().
1597
  //
1598
  // Sketch of algorithm:
1599
  // Check cache
1600
  //  If result found: done
1601
  //  Otherwise determine closes ancestor from cache data
1602
  //    Repeat querying A, adding more labels of the original qname
1603
  //    If we get a delegation continue at ancestor determination
1604
  //    Until we have the full name.
1605
  //
1606
  // The algorithm starts with adding a single label per iteration, and
1607
  // moves to three labels per iteration after three iterations.
1608

1609
  DNSName child;
20,463✔
1610
  prefix.append(string("QM "));
20,463✔
1611

1612
  LOG(prefix << qname << ": doResolve" << endl);
20,463✔
1613

1614
  // Look in cache only
1615
  vector<DNSRecord> retq;
20,463✔
1616
  bool old = setCacheOnly(true);
20,463✔
1617
  bool fromCache = false;
20,463✔
1618
  // For cache peeking, we tell doResolveNoQNameMinimization not to consider the (non-recursive) forward case.
1619
  // Otherwise all queries in a forward domain will be forwarded, while we want to consult the cache.
1620
  int res{};
20,463✔
1621
  try {
20,463✔
1622
    // The cache lookup below can have three types of result:
1623
    // Case 1: successful. In that case the records will be added to the end result below and we're done.
1624
    // Case 2: unsuccessful. In that case the records in retq will be discarded. E.g. there
1625
    // might be records as the lookup found a CNAME chain, but the target is missing from the cache.
1626
    // Case 3: an exception is thrown, in that case we're still interested in the (partial) results in retq.
1627
    // This can e.g. happen on a too-long CNAME chain.
1628
    res = doResolveNoQNameMinimization(qname, qtype, retq, depth, beenthere, context, &fromCache, nullptr);
20,463✔
1629
  }
20,463✔
1630
  catch (...) {
20,463✔
1631
    ret.insert(ret.end(), std::make_move_iterator(retq.begin()), std::make_move_iterator(retq.end()));
2✔
1632
    throw;
2✔
1633
  }
2✔
1634
  setCacheOnly(old);
20,453✔
1635
  if (fromCache) {
20,453✔
1636
    LOG(prefix << qname << ": Step0 Found in cache" << endl);
9,464✔
1637
    if (d_appliedPolicy.d_type != DNSFilterEngine::PolicyType::None && (d_appliedPolicy.d_kind == DNSFilterEngine::PolicyKind::NXDOMAIN || d_appliedPolicy.d_kind == DNSFilterEngine::PolicyKind::NODATA)) {
9,464✔
1638
      ret.clear();
4✔
1639
    }
4✔
1640
    ret.insert(ret.end(), std::make_move_iterator(retq.begin()), std::make_move_iterator(retq.end()));
9,464✔
1641
    return res;
9,464✔
1642
  }
9,464✔
1643
  LOG(prefix << qname << ": Step0 Not cached" << endl);
10,989✔
1644

1645
  const unsigned int qnamelen = qname.countLabels();
10,989✔
1646

1647
  DNSName fwdomain(qname);
10,989✔
1648
  const bool forwarded = getBestAuthZone(&fwdomain) != t_sstorage.domainmap->end();
10,989✔
1649
  if (forwarded) {
10,989✔
1650
    LOG(prefix << qname << ": Step0 qname is in a forwarded domain " << fwdomain << endl);
104!
1651
  }
104✔
1652

1653
  for (unsigned int i = 0; i <= qnamelen; i++) {
12,577✔
1654

1655
    // Step 1
1656
    vector<DNSRecord> bestns;
12,576✔
1657
    DNSName nsdomain(qname);
12,576✔
1658
    if (qtype == QType::DS) {
12,576✔
1659
      nsdomain.chopOff();
165✔
1660
    }
165✔
1661
    // the two retries allow getBestNSFromCache&co to reprime the root
1662
    // hints, in case they ever go missing
1663
    for (int tries = 0; tries < 2 && bestns.empty(); ++tries) {
25,195✔
1664
      bool flawedNSSet = false;
12,722✔
1665
      set<GetBestNSAnswer> beenthereIgnored;
12,722✔
1666
      getBestNSFromCache(nsdomain, qtype, bestns, &flawedNSSet, depth, prefix, beenthereIgnored, forwarded ? std::make_optional(fwdomain) : std::nullopt);
12,722✔
1667
      if (forwarded) {
12,722✔
1668
        break;
104✔
1669
      }
104✔
1670
    }
12,722✔
1671

1672
    if (bestns.empty()) {
12,576✔
1673
      if (!forwarded) {
104!
1674
        // Something terrible is wrong
1675
        LOG(prefix << qname << ": Step1 No ancestor found return ServFail" << endl);
×
1676
        return RCode::ServFail;
×
1677
      }
×
1678
      child = fwdomain;
104✔
1679
    }
104✔
1680
    else {
12,472✔
1681
      LOG(prefix << qname << ": Step1 Ancestor from cache is " << bestns[0].d_name << endl);
12,472✔
1682
      if (forwarded) {
12,472!
1683
        child = bestns[0].d_name.isPartOf(fwdomain) ? bestns[0].d_name : fwdomain;
×
1684
        LOG(prefix << qname << ": Step1 Final Ancestor (using forwarding info) is " << child << endl);
×
1685
      }
×
1686
      else {
12,472✔
1687
        child = bestns[0].d_name;
12,472✔
1688
      }
12,472✔
1689
    }
12,472✔
1690
    for (; i <= qnamelen; i++) {
18,729✔
1691
      // Step 2
1692
      unsigned int labels = child.countLabels();
18,728✔
1693
      unsigned int targetlen = qmStepLen(labels, qnamelen, i);
18,728✔
1694

1695
      while (labels < targetlen) {
33,906✔
1696
        child.prependRawLabel(qname.getRawLabel(qnamelen - labels - 1));
15,178✔
1697
        labels++;
15,178✔
1698
      }
15,178✔
1699
      // rfc9156 section-2.3, append labels if they start with an underscore
1700
      while (labels < qnamelen) {
18,728✔
1701
        auto prependLabel = qname.getRawLabel(qnamelen - labels - 1);
7,762✔
1702
        if (prependLabel.at(0) != '_') {
7,762!
1703
          break;
7,762✔
1704
        }
7,762✔
1705
        child.prependRawLabel(prependLabel);
×
1706
        labels++;
×
1707
      }
×
1708

1709
      LOG(prefix << qname << ": Step2 New child " << child << endl);
18,728✔
1710

1711
      // Step 3 resolve
1712
      if (child == qname) {
18,728✔
1713
        LOG(prefix << qname << ": Step3 Going to do final resolve" << endl);
10,965✔
1714
        res = doResolveNoQNameMinimization(qname, qtype, ret, depth, beenthere, context);
10,965✔
1715
        LOG(prefix << qname << ": Step3 Final resolve: " << resToString(res) << "/" << ret.size() << endl);
10,965✔
1716
        return res;
10,965✔
1717
      }
10,965✔
1718

1719
      // If we have seen this child during resolution already; we tried to QM it already or otherwise broken.
1720
      // fall back to no-QM
1721
      bool qmLoopDetected = false;
7,763✔
1722
      for (const auto& visitedNS : beenthere) {
7,763✔
1723
        if (visitedNS.qname == child) {
2,029!
1724
          qmLoopDetected = true;
×
1725
          break;
×
1726
        }
×
1727
      }
2,029✔
1728
      if (qmLoopDetected) {
7,763!
1729
        LOG(prefix << qname << ": Step4 loop detected as visited this child name already, fallback to no QM" << endl);
×
1730
        res = doResolveNoQNameMinimization(qname, qtype, ret, depth, beenthere, context);
×
1731
        LOG(prefix << qname << ": Step4 Final resolve: " << resToString(res) << "/" << ret.size() << endl);
×
1732
        return res;
×
1733
      }
×
1734

1735
      // Step 4
1736
      LOG(prefix << qname << ": Step4 Resolve A for child " << child << endl);
7,763✔
1737
      bool oldFollowCNAME = d_followCNAME;
7,763✔
1738
      d_followCNAME = false;
7,763✔
1739
      retq.clear();
7,763✔
1740
      StopAtDelegation stopAtDelegation = Stop;
7,763✔
1741
      res = doResolveNoQNameMinimization(child, QType::A, retq, depth, beenthere, context, nullptr, &stopAtDelegation);
7,763✔
1742
      d_followCNAME = oldFollowCNAME;
7,763✔
1743
      LOG(prefix << qname << ": Step4 Resolve " << child << "|A result is " << RCode::to_s(res) << "/" << retq.size() << "/" << stopAtDelegation << endl);
7,763✔
1744
      if (stopAtDelegation == Stopped) {
7,763✔
1745
        LOG(prefix << qname << ": Delegation seen, continue at step 1" << endl);
1,587✔
1746
        break;
1,587✔
1747
      }
1,587✔
1748

1749
      if (res != RCode::NoError) {
6,176✔
1750
        // Case 5: unexpected answer
1751
        LOG(prefix << qname << ": Step5: other rcode, last effort final resolve" << endl);
23!
1752
        setQNameMinimization(false);
23✔
1753
        setQMFallbackMode(true);
23✔
1754

1755
        auto oldEDE = context.extendedError;
23✔
1756
        res = doResolveNoQNameMinimization(qname, qtype, ret, depth + 1, beenthere, context);
23✔
1757

1758
        if (res == RCode::NoError) {
23✔
1759
          t_Counters.at(rec::Counter::qnameminfallbacksuccess)++;
4✔
1760
        }
4✔
1761
        else {
19✔
1762
          // as doResolveNoQNameMinimization clears the EDE, we put it back here, it is relevant but might not be set by the last effort attempt
1763
          if (!context.extendedError) {
19!
1764
            context.extendedError = std::move(oldEDE);
×
1765
          }
×
1766
        }
19✔
1767

1768
        LOG(prefix << qname << ": Step5 End resolve: " << resToString(res) << "/" << ret.size() << endl);
23!
1769
        return res;
23✔
1770
      }
23✔
1771
    }
6,176✔
1772
  }
12,576✔
1773

1774
  // Should not be reached
1775
  LOG(prefix << qname << ": Max iterations reached, return ServFail" << endl);
1!
1776
  return RCode::ServFail;
1✔
1777
}
10,989✔
1778

1779
unsigned int SyncRes::getAdjustedRecursionBound() const
1780
{
43,202✔
1781
  auto bound = s_maxdepth; // 40 is default value of s_maxdepth
43,202✔
1782
  if (getQMFallbackMode()) {
43,202✔
1783
    // We might have hit a depth level check, but we still want to allow some recursion levels in the fallback
1784
    // no-qname-minimization case. This has the effect that a qname minimization fallback case might reach 150% of
1785
    // maxdepth, taking care to not repeatedly increase the bound.
1786
    bound += s_maxdepth / 2;
37✔
1787
  }
37✔
1788
  return bound;
43,202✔
1789
}
43,202✔
1790

1791
static bool haveFinalAnswer(const DNSName& qname, QType qtype, int res, const vector<DNSRecord>& ret)
1792
{
3,005✔
1793
  if (res != RCode::NoError) {
3,005✔
1794
    return false;
6✔
1795
  }
6✔
1796

1797
  // This loop assumes the CNAME's records are in-order
1798
  DNSName target(qname);
2,999✔
1799
  for (const auto& record : ret) {
5,678✔
1800
    if (record.d_place == DNSResourceRecord::ANSWER && record.d_name == target) {
5,678✔
1801
      if (record.d_type == qtype) {
5,662✔
1802
        return true;
72✔
1803
      }
72✔
1804
      if (record.d_type == QType::CNAME) {
5,590!
1805
        if (auto ptr = getRR<CNAMERecordContent>(record)) {
5,590!
1806
          target = ptr->getTarget();
5,590✔
1807
        }
5,590✔
1808
        else {
×
1809
          return false;
×
1810
        }
×
1811
      }
5,590✔
1812
    }
5,590✔
1813
  }
5,678✔
1814
  return false;
2,927✔
1815
}
2,999✔
1816

1817
/*! This function will check the cache and go out to the internet if the answer is not in cache
1818
 *
1819
 * \param qname The name we need an answer for
1820
 * \param qtype
1821
 * \param ret The vector of DNSRecords we need to fill with the answers
1822
 * \param depth The recursion depth we are in
1823
 * \param beenthere
1824
 * \param fromCache tells the caller the result came from the cache, may be nullptr
1825
 * \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
1826
 * \return DNS RCODE or -1 (Error)
1827
 */
1828
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)
1829
{
43,205✔
1830
  context.extendedError.reset();
43,205✔
1831
  auto prefix = getPrefix(depth);
43,205✔
1832

1833
  LOG(prefix << qname << ": Wants " << (d_doDNSSEC ? "" : "NO ") << "DNSSEC processing, " << (d_requireAuthData ? "" : "NO ") << "auth data required by query for " << qtype << endl);
43,205!
1834

1835
  d_maxdepth = std::max(d_maxdepth, depth);
43,205✔
1836
  if (s_maxdepth > 0) {
43,206✔
1837
    auto bound = getAdjustedRecursionBound();
43,204✔
1838
    // Use a stricter bound if throttling
1839
    if (depth > bound || (d_outqueries > 10 && d_throttledqueries > 5 && depth > bound * 2 / 3)) {
43,205!
1840
      string msg = "More than " + std::to_string(bound) + " (adjusted max-recursion-depth) levels of recursion needed while resolving " + qname.toLogString();
4✔
1841
      LOG(prefix << qname << ": " << msg << endl);
4!
1842
      throw ImmediateServFailException(std::move(msg));
4✔
1843
    }
4✔
1844
  }
43,204✔
1845

1846
  int res = 0;
43,201✔
1847

1848
  const int iterations = !d_refresh && MemRecursorCache::s_maxServedStaleExtensions > 0 ? 2 : 1;
43,201✔
1849
  for (int loop = 0; loop < iterations; loop++) {
2,147,506,634✔
1850

1851
    d_serveStale = loop == 1;
43,215✔
1852
    if (d_serveStale) {
43,215✔
1853
      LOG(prefix << qname << ": Restart, with serve-stale enabled" << endl);
20!
1854
    }
20✔
1855
    // This is a difficult way of expressing "this is a normal query", i.e. not getRootNS.
1856
    if (!d_updatingRootNS || qtype.getCode() != QType::NS || !qname.isRoot()) {
43,215!
1857
      DNSName authname(qname);
42,756✔
1858
      const auto iter = getBestAuthZone(&authname);
42,756✔
1859

1860
      if (d_cacheonly) {
42,756✔
1861
        if (iter != t_sstorage.domainmap->end()) {
29,962✔
1862
          if (iter->second.isAuth()) {
259✔
1863
            LOG(prefix << qname << ": Cache only lookup for '" << qname << "|" << qtype << "', in auth zone" << endl);
14!
1864
            ret.clear();
14✔
1865
            d_wasOutOfBand = doOOBResolve(qname, qtype, ret, depth, prefix, res);
14✔
1866
            if (fromCache != nullptr) {
14!
1867
              *fromCache = d_wasOutOfBand;
×
1868
            }
×
1869
            return res;
14✔
1870
          }
14✔
1871
        }
259✔
1872
      }
29,962✔
1873

1874
      bool wasForwardedOrAuthZone = false;
42,742✔
1875
      bool wasAuthZone = false;
42,742✔
1876
      bool wasForwardRecurse = false;
42,742✔
1877

1878
      if (iter != t_sstorage.domainmap->end()) {
42,742✔
1879
        wasForwardedOrAuthZone = true;
791✔
1880

1881
        if (iter->second.isAuth()) {
791✔
1882
          wasAuthZone = true;
313✔
1883
        }
313✔
1884
        else if (iter->second.shouldRecurse()) {
478✔
1885
          wasForwardRecurse = true;
109✔
1886
        }
109✔
1887
      }
791✔
1888

1889
      /* When we are looking for a DS, we want to the non-CNAME cache check first
1890
         because we can actually have a DS (from the parent zone) AND a CNAME (from
1891
         the child zone), and what we really want is the DS */
1892
      if (qtype != QType::DS && doCNAMECacheCheck(qname, qtype, ret, depth, prefix, res, context, wasAuthZone, wasForwardRecurse, loop == 1)) { // will reroute us if needed
42,742✔
1893
        d_wasOutOfBand = wasAuthZone;
6,123✔
1894
        // Here we have an issue. If we were prevented from going out to the network (cache-only was set, possibly because we
1895
        // are in QM Step0) we might have a CNAME but not the corresponding target.
1896
        // It means that we will sometimes go to the next steps when we are in fact done, but that's fine since
1897
        // we will get the records from the cache, resulting in a small overhead.
1898
        // This might be a real problem if we had a RPZ hit, though, because we do not want the processing to continue, since
1899
        // RPZ rules will not be evaluated anymore (we already matched).
1900
        bool stoppedByPolicyHit = d_appliedPolicy.wasHit();
6,123✔
1901
        if (stoppedByPolicyHit && d_appliedPolicy.d_kind == DNSFilterEngine::PolicyKind::Custom && d_appliedPolicy.d_custom) {
6,123!
1902
          // if the custom RPZ record was a CNAME we still need a full chase
1903
          // tested by unit test test_following_cname_chain_with_rpz
1904
          if (!d_appliedPolicy.d_custom->empty() && d_appliedPolicy.d_custom->at(0)->getType() == QType::CNAME) {
6!
1905
            stoppedByPolicyHit = false;
5✔
1906
          }
5✔
1907
        }
6✔
1908
        if (fromCache != nullptr && (!d_cacheonly || stoppedByPolicyHit)) {
6,123✔
1909
          *fromCache = true;
3✔
1910
        }
3✔
1911
        /* Apply Post filtering policies */
1912

1913
        if (d_wantsRPZ && !d_appliedPolicy.wasHit()) {
6,123✔
1914
          auto luaLocal = g_luaconfs.getLocal();
6,113✔
1915
          if (luaLocal->dfe.getPostPolicy(ret, d_discardedPolicies, d_appliedPolicy)) {
6,113!
1916
            mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());
×
1917
            bool done = false;
×
1918
            handlePolicyHit(prefix, qname, qtype, ret, done, res, depth);
×
1919
            if (done && fromCache != nullptr) {
×
1920
              *fromCache = true;
×
1921
            }
×
1922
          }
×
1923
        }
6,113✔
1924
        // This handles the case mentioned above: if the full CNAME chain leading to the answer was
1925
        // constructed from the cache, indicate that.
1926
        if (fromCache != nullptr && !*fromCache && haveFinalAnswer(qname, qtype, res, ret)) {
6,123✔
1927
          *fromCache = true;
72✔
1928
        }
72✔
1929
        return res;
6,123✔
1930
      }
6,123✔
1931

1932
      if (doCacheCheck(qname, authname, wasForwardedOrAuthZone, wasAuthZone, wasForwardRecurse, qtype, ret, depth, prefix, res, context)) {
36,619✔
1933
        // we done
1934
        d_wasOutOfBand = wasAuthZone;
10,893✔
1935
        if (fromCache != nullptr) {
10,893✔
1936
          *fromCache = true;
9,389✔
1937
        }
9,389✔
1938

1939
        if (d_wantsRPZ && !d_appliedPolicy.wasHit()) {
10,893!
1940
          auto luaLocal = g_luaconfs.getLocal();
10,889✔
1941
          if (luaLocal->dfe.getPostPolicy(ret, d_discardedPolicies, d_appliedPolicy)) {
10,889✔
1942
            mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());
4✔
1943
            bool done = false;
4✔
1944
            handlePolicyHit(prefix, qname, qtype, ret, done, res, depth);
4✔
1945
          }
4✔
1946
        }
10,889✔
1947

1948
        return res;
10,893✔
1949
      }
10,893✔
1950

1951
      /* if we have not found a cached DS (or denial of), now is the time to look for a CNAME */
1952
      if (qtype == QType::DS && doCNAMECacheCheck(qname, qtype, ret, depth, prefix, res, context, wasAuthZone, wasForwardRecurse, loop == 1)) { // will reroute us if needed
25,726✔
1953
        d_wasOutOfBand = wasAuthZone;
18✔
1954
        // Here we have an issue. If we were prevented from going out to the network (cache-only was set, possibly because we
1955
        // are in QM Step0) we might have a CNAME but not the corresponding target.
1956
        // It means that we will sometimes go to the next steps when we are in fact done, but that's fine since
1957
        // we will get the records from the cache, resulting in a small overhead.
1958
        // This might be a real problem if we had a RPZ hit, though, because we do not want the processing to continue, since
1959
        // RPZ rules will not be evaluated anymore (we already matched).
1960
        const bool stoppedByPolicyHit = d_appliedPolicy.wasHit();
18✔
1961

1962
        if (fromCache != nullptr && (!d_cacheonly || stoppedByPolicyHit)) {
18!
1963
          *fromCache = true;
×
1964
        }
×
1965
        /* Apply Post filtering policies */
1966

1967
        if (d_wantsRPZ && !stoppedByPolicyHit) {
18!
1968
          auto luaLocal = g_luaconfs.getLocal();
18✔
1969
          if (luaLocal->dfe.getPostPolicy(ret, d_discardedPolicies, d_appliedPolicy)) {
18!
1970
            mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());
×
1971
            bool done = false;
×
1972
            handlePolicyHit(prefix, qname, qtype, ret, done, res, depth);
×
1973
            if (done && fromCache != nullptr) {
×
1974
              *fromCache = true;
×
1975
            }
×
1976
          }
×
1977
        }
18✔
1978
        if (fromCache != nullptr && !*fromCache && haveFinalAnswer(qname, qtype, res, ret)) {
18!
1979
          *fromCache = true;
×
1980
        }
×
1981
        return res;
18✔
1982
      }
18✔
1983
    }
25,726✔
1984

1985
    if (d_cacheonly) {
26,167✔
1986
      return 0;
15,100✔
1987
    }
15,100✔
1988

1989
    // When trying to serve-stale, we also only look at the cache. Don't look at d_serveStale, it
1990
    // might be changed by recursive calls (this should be fixed in a better way!).
1991
    if (loop == 1) {
11,067✔
1992
      return res;
4✔
1993
    }
4✔
1994

1995
    LOG(prefix << qname << ": No cache hit for '" << qname << "|" << qtype << "', trying to find an appropriate NS record" << endl);
11,063✔
1996

1997
    DNSName subdomain(qname);
11,063✔
1998
    if (qtype == QType::DS) {
11,063✔
1999
      subdomain.chopOff();
197✔
2000
    }
197✔
2001

2002
    NsSet nsset;
11,063✔
2003
    bool flawedNSSet = false;
11,063✔
2004

2005
    // the two retries allow getBestNSNamesFromCache&co to reprime the root
2006
    // hints, in case they ever go missing
2007
    for (int tries = 0; tries < 2 && nsset.empty(); ++tries) {
22,135✔
2008
      subdomain = getBestNSNamesFromCache(subdomain, qtype, nsset, &flawedNSSet, depth, prefix, beenthere); //  pass beenthere to both occasions
11,064✔
2009
    }
11,064✔
2010

2011
    res = doResolveAt(nsset, subdomain, flawedNSSet, qname, qtype, ret, depth, prefix, beenthere, context, stopAtDelegation, nullptr);
11,063✔
2012

2013
    if (res == -1 && s_save_parent_ns_set) {
11,063✔
2014
      // It did not work out, lets check if we have a saved parent NS set
2015
      map<DNSName, vector<ComboAddress>> fallBack;
246✔
2016
      {
246✔
2017
        auto lock = s_savedParentNSSet.lock();
246✔
2018
        auto domainData = lock->find(subdomain);
246✔
2019
        if (domainData != lock->end() && !domainData->d_nsAddresses.empty()) {
246!
2020
          nsset.clear();
2✔
2021
          // Build the nsset arg and fallBack data for the fallback doResolveAt() attempt
2022
          // Take a copy to be able to release the lock, NsSet is actually a map, go figure
2023
          for (const auto& nsAddress : domainData->d_nsAddresses) {
4✔
2024
            nsset.emplace(nsAddress.first, pair(std::vector<ComboAddress>(), false));
4✔
2025
            fallBack.emplace(nsAddress.first, nsAddress.second);
4✔
2026
          }
4✔
2027
        }
2✔
2028
      }
246✔
2029
      if (!fallBack.empty()) {
246✔
2030
        LOG(prefix << qname << ": Failure, but we have a saved parent NS set, trying that one" << endl);
2!
2031
        res = doResolveAt(nsset, subdomain, flawedNSSet, qname, qtype, ret, depth, prefix, beenthere, context, stopAtDelegation, &fallBack);
2✔
2032
        if (res == 0) {
2!
2033
          // It did work out
2034
          s_savedParentNSSet.lock()->inc(subdomain);
2✔
2035
        }
2✔
2036
      }
2✔
2037
    }
246✔
2038
    /* Apply Post filtering policies */
2039
    if (d_wantsRPZ && !d_appliedPolicy.wasHit()) {
11,071✔
2040
      auto luaLocal = g_luaconfs.getLocal();
10,848✔
2041
      if (luaLocal->dfe.getPostPolicy(ret, d_discardedPolicies, d_appliedPolicy)) {
10,848✔
2042
        mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());
5✔
2043
        bool done = false;
5✔
2044
        handlePolicyHit(prefix, qname, qtype, ret, done, res, depth);
5✔
2045
      }
5✔
2046
    }
10,848✔
2047

2048
    if (res == 0) {
11,066✔
2049
      return 0;
10,436✔
2050
    }
10,436✔
2051

2052
    LOG(prefix << qname << ": Failed (res=" << res << ")" << endl);
2,147,484,267✔
2053
    if (res >= 0) {
2,147,484,267✔
2054
      break;
189✔
2055
    }
189✔
2056
  }
2,147,484,267✔
2057
  return res < 0 ? RCode::ServFail : res;
613✔
2058
}
43,201✔
2059

2060
#if 0
2061
// for testing purposes
2062
static bool ipv6First(const ComboAddress& a, const ComboAddress& b)
2063
{
2064
  return !(a.sin4.sin_family < a.sin4.sin_family);
2065
}
2066
#endif
2067

2068
struct speedOrderCA
2069
{
2070
  speedOrderCA(std::map<ComboAddress, float>& speeds) :
2071
    d_speeds(speeds) {}
1,569✔
2072
  bool operator()(const ComboAddress& lhs, const ComboAddress& rhs) const
2073
  {
1,481✔
2074
    return d_speeds[lhs] < d_speeds[rhs];
1,481✔
2075
  }
1,481✔
2076
  std::map<ComboAddress, float>& d_speeds; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members): nothing wrong afaiks
2077
};
2078

2079
void SyncRes::selectNSOnSpeed(const DNSName& qname, const string& prefix, vector<ComboAddress>& ret)
2080
{
12,868✔
2081
  /* we need to remove from the nsSpeeds collection the existing IPs
2082
     for this nameserver that are no longer in the set, even if there
2083
     is only one or none at all in the current set.
2084
  */
2085
  map<ComboAddress, float> speeds;
12,868✔
2086
  {
12,868✔
2087
    auto lock = s_nsSpeeds.lock();
12,868✔
2088
    const auto& collection = lock->find_or_enter(qname, d_now);
12,868✔
2089
    float factor = collection.getFactor(d_now);
12,868✔
2090
    for (const auto& val : ret) {
14,149✔
2091
      speeds[val] = collection.d_collection[val].get(factor);
14,149✔
2092
    }
14,149✔
2093
    collection.purge(speeds);
12,868✔
2094
  }
12,868✔
2095

2096
  if (ret.size() > 1) {
12,868✔
2097
    shuffle(ret.begin(), ret.end(), pdns::dns_random_engine());
1,385✔
2098
    speedOrderCA speedOrder(speeds);
1,385✔
2099
    stable_sort(ret.begin(), ret.end(), speedOrder);
1,385✔
2100
  }
1,385✔
2101

2102
  if (doLog()) {
12,868✔
2103
    LOG(prefix << qname << ": Nameserver " << qname << " IPs: ");
1,591!
2104
    bool first = true;
1,591✔
2105
    for (const auto& addr : ret) {
1,599✔
2106
      if (first) {
1,599✔
2107
        first = false;
1,591✔
2108
      }
1,591✔
2109
      else {
8✔
2110
        LOG(", ");
8!
2111
      }
8✔
2112
      LOG((addr.toString()) << "(" << fmtfloat(speeds[addr] / 1000.0) << "ms)");
1,599!
2113
    }
1,599✔
2114
    LOG(endl);
1,591!
2115
  }
1,591✔
2116
}
12,868✔
2117

2118
template <typename T>
2119
static bool collectAddresses(const vector<DNSRecord>& cset, vector<ComboAddress>& ret)
2120
{
14,137✔
2121
  bool pushed = false;
14,137✔
2122
  for (const auto& record : cset) {
14,255✔
2123
    if (auto rec = getRR<T>(record)) {
14,253✔
2124
      ret.push_back(rec->getCA(53));
14,149✔
2125
      pushed = true;
14,149✔
2126
    }
14,149✔
2127
  }
14,253✔
2128
  return pushed;
14,137✔
2129
}
14,137✔
2130

2131
/** This function explicitly goes out for A or AAAA addresses
2132
 */
2133
vector<ComboAddress> SyncRes::getAddrs(const DNSName& qname, unsigned int depth, const string& prefix, set<GetBestNSAnswer>& beenthere, bool cacheOnly, unsigned int& addressQueriesForNS)
2134
{
12,958✔
2135
  typedef vector<DNSRecord> res_t;
12,958✔
2136
  typedef vector<ComboAddress> ret_t;
12,958✔
2137
  ret_t ret;
12,958✔
2138

2139
  bool oldCacheOnly = setCacheOnly(cacheOnly);
12,958✔
2140
  bool oldRequireAuthData = d_requireAuthData;
12,958✔
2141
  bool oldValidationRequested = d_DNSSECValidationRequested;
12,958✔
2142
  bool oldFollowCNAME = d_followCNAME;
12,958✔
2143
  bool seenV6 = false;
12,958✔
2144
  const unsigned int startqueries = d_outqueries;
12,958✔
2145
  d_requireAuthData = false;
12,958✔
2146
  d_DNSSECValidationRequested = false;
12,958✔
2147
  d_followCNAME = false;
12,958✔
2148

2149
  MemRecursorCache::Flags flags = MemRecursorCache::None;
12,958✔
2150
  if (d_serveStale) {
12,958!
2151
    flags |= MemRecursorCache::ServeStale;
×
2152
  }
×
2153
  try {
12,958✔
2154
    // First look for both A and AAAA in the cache
2155
    res_t cset;
12,958✔
2156
    if (s_doIPv4 && g_recCache->get(d_now.tv_sec, qname, QType::A, flags, &cset, d_cacheRemote, d_routingTag) > 0) {
12,958✔
2157
      collectAddresses<ARecordContent>(cset, ret);
11,307✔
2158
    }
11,307✔
2159
    if (s_doIPv6 && g_recCache->get(d_now.tv_sec, qname, QType::AAAA, flags, &cset, d_cacheRemote, d_routingTag) > 0) {
12,958✔
2160
      if (collectAddresses<AAAARecordContent>(cset, ret)) {
1,371!
2161
        seenV6 = true;
1,371✔
2162
      }
1,371✔
2163
    }
1,371✔
2164
    if (ret.empty()) {
12,958✔
2165
      // Neither A nor AAAA in the cache...
2166
      Context newContext1;
1,619✔
2167
      cset.clear();
1,619✔
2168
      // Go out to get A's
2169
      if (s_doIPv4 && doResolveNoQNameMinimization(qname, QType::A, cset, depth + 1, beenthere, newContext1) == 0) { // this consults cache, OR goes out
1,619✔
2170
        collectAddresses<ARecordContent>(cset, ret);
1,434✔
2171
      }
1,434✔
2172
      if (s_doIPv6) { // s_doIPv6 **IMPLIES** pdns::isQueryLocalAddressFamilyEnabled(AF_INET6) returned true
1,619✔
2173
        if (ret.empty()) {
52✔
2174
          // We only go out immediately to find IPv6 records if we did not find any IPv4 ones.
2175
          Context newContext2;
30✔
2176
          if (doResolveNoQNameMinimization(qname, QType::AAAA, cset, depth + 1, beenthere, newContext2) == 0) { // this consults cache, OR goes out
30✔
2177
            if (collectAddresses<AAAARecordContent>(cset, ret)) {
22!
2178
              seenV6 = true;
×
2179
            }
×
2180
          }
22✔
2181
        }
30✔
2182
        else {
22✔
2183
          // We have some IPv4 records, consult the cache, we might have encountered some IPv6 glue
2184
          cset.clear();
22✔
2185
          if (g_recCache->get(d_now.tv_sec, qname, QType::AAAA, flags, &cset, d_cacheRemote, d_routingTag) > 0) {
22✔
2186
            if (collectAddresses<AAAARecordContent>(cset, ret)) {
2!
2187
              seenV6 = true;
2✔
2188
            }
2✔
2189
          }
2✔
2190
        }
22✔
2191
      }
52✔
2192
    }
1,619✔
2193
    if (s_doIPv6 && !seenV6 && !cacheOnly) {
12,958!
2194
      // No IPv6 records in cache, check negcache and submit async task if negache does not have the data
2195
      // so that the next time the cache or the negcache will have data
2196
      pushResolveIfNotInNegCache(qname, QType::AAAA, d_now);
966✔
2197
    }
966✔
2198
  }
12,958✔
2199
  catch (const PolicyHitException&) {
12,958✔
2200
    // We ignore a policy hit while trying to retrieve the addresses
2201
    // of a NS and keep processing the current query
2202
  }
×
2203

2204
  if (ret.empty() && d_outqueries > startqueries) {
12,958✔
2205
    // We did 1 or more outgoing queries to resolve this NS name but returned empty handed
2206
    addressQueriesForNS++;
76✔
2207
  }
76✔
2208
  d_requireAuthData = oldRequireAuthData;
12,866✔
2209
  d_DNSSECValidationRequested = oldValidationRequested;
12,866✔
2210
  setCacheOnly(oldCacheOnly);
12,866✔
2211
  d_followCNAME = oldFollowCNAME;
12,866✔
2212

2213
  if (s_max_busy_dot_probes > 0 && s_dot_to_port_853) {
12,866!
2214
    for (auto& add : ret) {
×
2215
      if (shouldDoDoT(add, d_now.tv_sec)) {
×
2216
        add.setPort(853);
×
2217
      }
×
2218
    }
×
2219
  }
×
2220
  selectNSOnSpeed(qname, prefix, ret);
12,866✔
2221
  return ret;
12,866✔
2222
}
12,958✔
2223

2224
bool SyncRes::canUseRecords(const std::string& prefix, const DNSName& qname, const DNSName& name, QType qtype, vState state)
2225
{
159,475✔
2226
  if (vStateIsBogus(state)) {
159,475!
2227
    LOG(prefix << qname << ": Cannot use " << name << '/' << qtype << " records from cache: Bogus" << endl);
×
2228
    return false;
×
2229
  }
×
2230
  // We could validate Indeterminate authoritative records here.
2231
  return true;
159,475✔
2232
}
159,475✔
2233

2234
void SyncRes::getBestNSFromCache(const DNSName& qname, const QType qtype, vector<DNSRecord>& bestns, bool* flawedNSSet, unsigned int depth, const string& prefix, set<GetBestNSAnswer>& beenthere, const std::optional<DNSName>& cutOffDomain) // NOLINT(readability-function-cognitive-complexity)
2235
{
23,579✔
2236
  DNSName subdomain(qname);
23,579✔
2237
  bestns.clear();
23,579✔
2238
  bool brokeloop = false;
23,579✔
2239
  MemRecursorCache::Flags flags = MemRecursorCache::None;
23,579✔
2240
  if (d_serveStale) {
23,579!
2241
    flags |= MemRecursorCache::ServeStale;
×
2242
  }
×
2243
  do {
55,129✔
2244
    if (cutOffDomain && (subdomain == *cutOffDomain || !subdomain.isPartOf(*cutOffDomain))) {
55,129✔
2245
      break;
104✔
2246
    }
104✔
2247
    brokeloop = false;
55,025✔
2248
    LOG(prefix << qname << ": Checking if we have NS in cache for '" << subdomain << "'" << endl);
55,025✔
2249
    vector<DNSRecord> nsVector;
55,025✔
2250
    *flawedNSSet = false;
55,025✔
2251

2252
    vState state{vState::Indeterminate};
55,025✔
2253
    if (bool isAuth = false; g_recCache->get(d_now.tv_sec, subdomain, QType::NS, flags, &nsVector, d_cacheRemote, d_routingTag, nullptr, nullptr, nullptr, &state, &isAuth) > 0 && canUseRecords(prefix, qname, subdomain, QType::NS, state)) {
55,025!
2254
      if (s_maxnsperresolve > 0 && nsVector.size() > s_maxnsperresolve) {
23,516!
2255
        vector<DNSRecord> selected;
20✔
2256
        selected.reserve(s_maxnsperresolve);
20✔
2257
        std::sample(nsVector.cbegin(), nsVector.cend(), std::back_inserter(selected), s_maxnsperresolve, pdns::dns_random_engine());
20✔
2258
        nsVector = std::move(selected);
20✔
2259
      }
20✔
2260
      bestns.reserve(nsVector.size());
23,516✔
2261

2262
      vector<DNSName> missing;
23,516✔
2263
      for (const auto& nsRecord : nsVector) {
136,264✔
2264
        if (nsRecord.d_ttl > (unsigned int)d_now.tv_sec) {
136,266✔
2265
          vector<DNSRecord> aset;
136,265✔
2266
          QType nsqt{QType::ADDR};
136,265✔
2267
          if (s_doIPv4 && !s_doIPv6) {
136,267✔
2268
            nsqt = QType::A;
92,200✔
2269
          }
92,200✔
2270
          else if (!s_doIPv4 && s_doIPv6) {
2,147,527,709!
2271
            nsqt = QType::AAAA;
64✔
2272
          }
64✔
2273

2274
          auto nrr = getRR<NSRecordContent>(nsRecord);
136,265✔
2275
          state = vState::Indeterminate;
136,265✔
2276
          if (nrr && (!nrr->getNS().isPartOf(subdomain) || g_recCache->get(d_now.tv_sec, nrr->getNS(), nsqt, flags, doLog() ? &aset : nullptr, d_cacheRemote, d_routingTag, nullptr, nullptr, nullptr, &state) > 0)) {
136,269✔
2277
            // We make use of the fact that if get() is not called the state is still Indeterminate
2278
            if (!canUseRecords(prefix, qname, nrr->getNS(), nsqt, state)) {
135,959!
2279
              continue;
×
2280
            }
×
2281
            bestns.push_back(nsRecord);
135,959✔
2282
            LOG(prefix << qname << ": NS (with ip, or non-glue) in cache for '" << subdomain << "' -> '" << nrr->getNS() << "'");
135,959✔
2283
            LOG(", within bailiwick: " << nrr->getNS().isPartOf(subdomain));
135,959✔
2284
            if (!aset.empty()) {
135,959✔
2285
              LOG(", in cache, ttl=" << (unsigned int)(((time_t)aset.begin()->d_ttl - d_now.tv_sec)) << endl);
4,652!
2286
            }
4,652✔
2287
            else {
131,307✔
2288
              LOG(", not in cache / did not look at cache" << endl);
131,307✔
2289
            }
131,307✔
2290
          }
135,959✔
2291
          else if (nrr != nullptr) {
2,147,483,955✔
2292
            *flawedNSSet = true;
308✔
2293
            LOG(prefix << qname << ": NS in cache for '" << subdomain << "', but needs glue (" << nrr->getNS() << ") which we miss or is expired" << endl);
308!
2294
            missing.emplace_back(nrr->getNS());
308✔
2295
          }
308✔
2296
        }
136,265✔
2297
      }
136,264✔
2298
      if (*flawedNSSet && bestns.empty() && isAuth) {
23,516✔
2299
        // The authoritative (child) NS records did not produce any usable addresses, wipe them, so
2300
        // these useless records do not prevent parent records to be inserted into the cache
2301
        LOG(prefix << qname << ": Wiping flawed authoritative NS records for " << subdomain << endl);
2!
2302
        g_recCache->doWipeCache(subdomain, false, QType::NS);
2✔
2303
      }
2✔
2304
      if (!missing.empty() && missing.size() < nsVector.size()) {
23,516✔
2305
        // We miss glue, but we have a chance to resolve it
2306
        // Pick a few and push async tasks to resolve them
2307
        const unsigned int max = 2;
24✔
2308
        unsigned int counter = 0;
24✔
2309
        shuffle(missing.begin(), missing.end(), pdns::dns_random_engine());
24✔
2310
        for (const auto& name : missing) {
24!
2311
          if (s_doIPv4 && pushResolveIfNotInNegCache(name, QType::A, d_now)) {
24!
2312
            LOG(prefix << qname << ": A glue for " << subdomain << " NS " << name << " missing, pushed task to resolve" << endl);
24!
2313
            counter++;
24✔
2314
          }
24✔
2315
          if (s_doIPv6 && pushResolveIfNotInNegCache(name, QType::AAAA, d_now)) {
24!
2316
            LOG(prefix << qname << ": AAAA glue for " << subdomain << " NS " << name << " missing, pushed task to resolve" << endl);
24!
2317
            counter++;
24✔
2318
          }
24✔
2319
          if (counter >= max) {
24!
2320
            break;
24✔
2321
          }
24✔
2322
        }
24✔
2323
      }
24✔
2324

2325
      if (!bestns.empty()) {
23,516✔
2326
        GetBestNSAnswer answer;
23,479✔
2327
        answer.qname = qname;
23,479✔
2328
        answer.qtype = qtype.getCode();
23,479✔
2329
        for (const auto& bestNSRecord : bestns) {
135,956✔
2330
          if (auto nsContent = getRR<NSRecordContent>(bestNSRecord)) {
135,959✔
2331
            answer.bestns.emplace(bestNSRecord.d_name, nsContent->getNS());
135,958✔
2332
          }
135,958✔
2333
        }
135,956✔
2334

2335
        auto insertionPair = beenthere.insert(std::move(answer));
23,479✔
2336
        if (!insertionPair.second) {
23,479✔
2337
          brokeloop = true;
254✔
2338
          LOG(prefix << qname << ": We have NS in cache for '" << subdomain << "' but part of LOOP (already seen " << insertionPair.first->qname << ")! Trying less specific NS" << endl);
254!
2339
          ;
254✔
2340
          if (doLog()) {
254!
2341
            for (auto j = beenthere.begin(); j != beenthere.end(); ++j) {
×
2342
              bool neo = (j == insertionPair.first);
×
2343
              LOG(prefix << qname << ": Beenthere" << (neo ? "*" : "") << ": " << j->qname << "|" << DNSRecordContent::NumberToType(j->qtype) << " (" << (unsigned int)j->bestns.size() << ")" << endl);
×
2344
            }
×
2345
          }
×
2346
          bestns.clear();
254✔
2347
        }
254✔
2348
        else {
23,225✔
2349
          LOG(prefix << qname << ": We have NS in cache for '" << subdomain << "' (flawedNSSet=" << *flawedNSSet << ")" << endl);
23,225✔
2350
          return;
23,225✔
2351
        }
23,225✔
2352
      }
23,479✔
2353
    }
23,516✔
2354
    LOG(prefix << qname << ": No valid/useful NS in cache for '" << subdomain << "'" << endl);
31,800✔
2355

2356
    if (subdomain.isRoot() && !brokeloop) {
31,800✔
2357
      // We lost the root NS records
2358
      primeHints();
151✔
2359
      LOG(prefix << qname << ": Reprimed the root" << endl);
151!
2360
      /* let's prevent an infinite loop */
2361
      if (!d_updatingRootNS) {
151✔
2362
        auto log = g_slog->withName("housekeeping");
143✔
2363
        getRootNS(d_now, d_asyncResolve, depth, log);
143✔
2364
      }
143✔
2365
    }
151✔
2366
  } while (subdomain.chopOff());
31,800✔
2367
}
23,579✔
2368

2369
SyncRes::domainmap_t::const_iterator SyncRes::getBestAuthZone(DNSName* qname)
2370
{
146,980✔
2371
  if (t_sstorage.domainmap->empty()) {
146,980✔
2372
    return t_sstorage.domainmap->end();
26,326✔
2373
  }
26,326✔
2374

2375
  SyncRes::domainmap_t::const_iterator ret;
120,654✔
2376
  do {
432,494✔
2377
    ret = t_sstorage.domainmap->find(*qname);
432,494✔
2378
    if (ret != t_sstorage.domainmap->end()) {
432,494✔
2379
      break;
2,335✔
2380
    }
2,335✔
2381
  } while (qname->chopOff());
432,494✔
2382
  return ret;
×
2383
}
146,980✔
2384

2385
/** doesn't actually do the work, leaves that to getBestNSFromCache */
2386
DNSName SyncRes::getBestNSNamesFromCache(const DNSName& qname, const QType qtype, NsSet& nsset, bool* flawedNSSet, unsigned int depth, const string& prefix, set<GetBestNSAnswer>& beenthere)
2387
{
11,065✔
2388
  DNSName authOrForwDomain(qname);
11,065✔
2389

2390
  auto iter = getBestAuthZone(&authOrForwDomain);
11,065✔
2391
  // We have an auth, forwarder of forwarder-recurse
2392
  if (iter != t_sstorage.domainmap->end()) {
11,065✔
2393
    if (iter->second.isAuth()) {
325✔
2394
      // this gets picked up in doResolveAt, the empty DNSName, combined with the
2395
      // empty vector means 'we are auth for this zone'
2396
      nsset.insert({DNSName(), {{}, false}});
139✔
2397
      return authOrForwDomain;
139✔
2398
    }
139✔
2399
    if (iter->second.shouldRecurse()) {
186✔
2400
      // Again, picked up in doResolveAt. An empty DNSName, combined with a
2401
      // non-empty vector of ComboAddresses means 'this is a forwarded domain'
2402
      // This is actually picked up in retrieveAddressesForNS called from doResolveAt.
2403
      nsset.insert({DNSName(), {iter->second.d_servers, true}});
70✔
2404
      return authOrForwDomain;
70✔
2405
    }
70✔
2406
  }
186✔
2407

2408
  // We might have a (non-recursive) forwarder, but maybe the cache already contains
2409
  // a better NS
2410
  vector<DNSRecord> bestns;
10,856✔
2411
  DNSName nsFromCacheDomain(g_rootdnsname);
10,856✔
2412
  getBestNSFromCache(qname, qtype, bestns, flawedNSSet, depth, prefix, beenthere);
10,856✔
2413

2414
  // Pick up the auth domain
2415
  for (const auto& nsRecord : bestns) {
10,856✔
2416
    const auto nsContent = getRR<NSRecordContent>(nsRecord);
10,754✔
2417
    if (nsContent) {
10,754!
2418
      nsFromCacheDomain = nsRecord.d_name;
10,754✔
2419
      break;
10,754✔
2420
    }
10,754✔
2421
  }
10,754✔
2422

2423
  if (iter != t_sstorage.domainmap->end()) {
10,856✔
2424
    if (doLog()) {
116✔
2425
      LOG(prefix << qname << " authOrForwDomain: " << authOrForwDomain << " nsFromCacheDomain: " << nsFromCacheDomain << " isPartof: " << authOrForwDomain.isPartOf(nsFromCacheDomain) << endl);
104!
2426
    }
104✔
2427

2428
    // If the forwarder is better or equal to what's found in the cache, use forwarder. Note that name.isPartOf(name).
2429
    // So queries that get NS for authOrForwDomain itself go to the forwarder
2430
    if (authOrForwDomain.isPartOf(nsFromCacheDomain)) {
116!
2431
      if (doLog()) {
116✔
2432
        LOG(prefix << qname << ": Using forwarder as NS" << endl);
104!
2433
      }
104✔
2434
      nsset.insert({DNSName(), {iter->second.d_servers, false}});
116✔
2435
      return authOrForwDomain;
116✔
2436
    }
116✔
2437
    if (doLog()) {
×
2438
      LOG(prefix << qname << ": Using NS from cache" << endl);
×
2439
    }
×
2440
  }
×
2441
  for (const auto& bestn : bestns) {
63,748✔
2442
    // The actual resolver code will not even look at the ComboAddress or bool
2443
    const auto nsContent = getRR<NSRecordContent>(bestn);
63,748✔
2444
    if (nsContent) {
63,749✔
2445
      nsset.insert({nsContent->getNS(), {{}, false}});
63,748✔
2446
    }
63,748✔
2447
  }
63,748✔
2448
  return nsFromCacheDomain;
10,740✔
2449
}
10,856✔
2450

2451
void SyncRes::updateValidationStatusInCache(const DNSName& qname, const QType qtype, bool aaFlag, vState newState) const
2452
{
54✔
2453
  if (qtype == QType::ANY || qtype == QType::ADDR) {
54!
2454
    // not doing that
2455
    return;
×
2456
  }
×
2457

2458
  if (vStateIsBogus(newState)) {
54✔
2459
    g_recCache->updateValidationStatus(d_now.tv_sec, qname, qtype, d_cacheRemote, d_routingTag, aaFlag, newState, s_maxbogusttl + d_now.tv_sec);
20✔
2460
  }
20✔
2461
  else {
34✔
2462
    g_recCache->updateValidationStatus(d_now.tv_sec, qname, qtype, d_cacheRemote, d_routingTag, aaFlag, newState, std::nullopt);
34✔
2463
  }
34✔
2464
}
54✔
2465

2466
static pair<bool, unsigned int> scanForCNAMELoop(const DNSName& name, const vector<DNSRecord>& records)
2467
{
7,459✔
2468
  unsigned int numCNames = 0;
7,459✔
2469
  for (const auto& record : records) {
14,169✔
2470
    if (record.d_type == QType::CNAME && record.d_place == DNSResourceRecord::ANSWER) {
14,169!
2471
      ++numCNames;
13,972✔
2472
      if (name == record.d_name) {
13,972✔
2473
        return {true, numCNames};
6✔
2474
      }
6✔
2475
    }
13,972✔
2476
  }
14,169✔
2477
  return {false, numCNames};
7,453✔
2478
}
7,459✔
2479

2480
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)
2481
{
37,393✔
2482
  vector<DNSRecord> cset;
37,393✔
2483
  MemRecursorCache::SigRecs signatures = MemRecursorCache::s_emptySigRecs;
37,393✔
2484
  MemRecursorCache::AuthRecs authorityRecs = MemRecursorCache::s_emptyAuthRecs;
37,393✔
2485
  bool wasAuth = false;
37,393✔
2486
  uint32_t capTTL = std::numeric_limits<uint32_t>::max();
37,393✔
2487
  DNSName foundName;
37,393✔
2488
  DNSName authZone;
37,393✔
2489
  QType foundQT = QType::ENT;
37,393✔
2490

2491
  /* we don't require auth data for forward-recurse lookups */
2492
  MemRecursorCache::Flags flags = MemRecursorCache::None;
37,393✔
2493
  if (!wasForwardRecurse && d_requireAuthData) {
37,396✔
2494
    flags |= MemRecursorCache::RequireAuth;
35,642✔
2495
  }
35,642✔
2496
  if (d_refresh) {
37,393✔
2497
    flags |= MemRecursorCache::Refresh;
276✔
2498
  }
276✔
2499
  if (d_serveStale) {
37,393✔
2500
    flags |= MemRecursorCache::ServeStale;
20✔
2501
  }
20✔
2502
  MemRecursorCache::Extra extra;
37,393✔
2503
  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, &extra) > 0) {
2,147,504,754✔
2504
    foundName = qname;
6,187✔
2505
    foundQT = QType::CNAME;
6,187✔
2506
    d_fromAuthIP = extra.d_address;
6,187✔
2507
  }
6,187✔
2508

2509
  if (foundName.empty() && qname != g_rootdnsname) {
37,393✔
2510
    // look for a DNAME cache hit
2511
    auto labels = qname.getRawLabels();
30,145✔
2512
    DNSName dnameName(g_rootdnsname);
30,145✔
2513

2514
    do {
80,093✔
2515
      dnameName.prependRawLabel(labels.back());
80,093✔
2516
      labels.pop_back();
80,093✔
2517
      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
80,093✔
2518
        break;
30,128✔
2519
      }
30,128✔
2520
      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, &extra) > 0) {
2,147,503,841✔
2521
        foundName = std::move(dnameName);
16✔
2522
        foundQT = QType::DNAME;
16✔
2523
        d_fromAuthIP = extra.d_address;
16✔
2524
        break;
16✔
2525
      }
16✔
2526
    } while (!labels.empty());
49,965✔
2527
  }
30,145✔
2528

2529
  if (foundName.empty()) {
37,393✔
2530
    return false;
31,192✔
2531
  }
31,192✔
2532

2533
  if (qtype == QType::DS && authZone == qname) {
6,201✔
2534
    /* CNAME at APEX of the child zone, we can't use that to prove that
2535
       there is no DS */
2536
    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);
4!
2537
    return false;
4✔
2538
  }
4✔
2539

2540
  for (auto const& record : cset) {
6,200✔
2541
    if (record.d_class != QClass::IN) {
6,198!
2542
      continue;
×
2543
    }
×
2544

2545
    if (record.d_ttl > (unsigned int)d_now.tv_sec) {
6,198✔
2546

2547
      if (!wasAuthZone && shouldValidate() && (wasAuth || wasForwardRecurse) && context.state == vState::Indeterminate && d_requireAuthData) {
6,199!
2548
        /* This means we couldn't figure out the state when this entry was cached */
2549

2550
        vState recordState = getValidationStatus(foundName, !signatures->empty(), qtype == QType::DS, depth, prefix);
8✔
2551
        if (recordState == vState::Secure) {
8✔
2552
          LOG(prefix << qname << ": Got vState::Indeterminate state from the " << foundQT.toString() << " cache, validating.." << endl);
6!
2553
          context.state = SyncRes::validateRecordsWithSigs(depth, prefix, qname, qtype, foundName, foundQT, cset, *signatures);
6✔
2554
          if (context.state != vState::Indeterminate) {
6!
2555
            LOG(prefix << qname << ": Got vState::Indeterminate state from the " << foundQT.toString() << " cache, new validation result is " << context.state << endl);
6!
2556
            if (vStateIsBogus(context.state)) {
6✔
2557
              capTTL = s_maxbogusttl;
4✔
2558
            }
4✔
2559
            updateValidationStatusInCache(foundName, foundQT, wasAuth, context.state);
6✔
2560
          }
6✔
2561
        }
6✔
2562
      }
8✔
2563

2564
      LOG(prefix << qname << ": Found cache " << foundQT.toString() << " hit for '" << foundName << "|" << foundQT.toString() << "' to '" << record.getContent()->getZoneRepresentation() << "', validation state is " << context.state << endl);
6,197✔
2565

2566
      DNSRecord dnsRecord = record;
6,197✔
2567
      auto alreadyPresent = false;
6,197✔
2568

2569
      if (checkForDups) {
6,197✔
2570
        // This can happen on the 2nd iteration of the servestale loop, where the first iteration
2571
        // added a C/DNAME record, but the target resolve failed
2572
        for (const auto& dnsrec : ret) {
6✔
2573
          if (dnsrec.d_type == foundQT && dnsrec.d_name == record.d_name) {
2!
2574
            alreadyPresent = true;
2✔
2575
            break;
2✔
2576
          }
2✔
2577
        }
2✔
2578
      }
6✔
2579
      dnsRecord.d_ttl -= d_now.tv_sec;
6,197✔
2580
      dnsRecord.d_ttl = std::min(dnsRecord.d_ttl, capTTL);
6,197✔
2581
      const uint32_t ttl = dnsRecord.d_ttl;
6,197✔
2582
      if (!alreadyPresent) {
6,199✔
2583
        ret.reserve(ret.size() + 2 + signatures->size() + authorityRecs->size());
6,197✔
2584
        ret.push_back(dnsRecord);
6,197✔
2585

2586
        for (const auto& signature : *signatures) {
6,197✔
2587
          DNSRecord sigdr;
45✔
2588
          sigdr.d_type = QType::RRSIG;
45✔
2589
          sigdr.d_name = foundName;
45✔
2590
          sigdr.d_ttl = ttl;
45✔
2591
          sigdr.setContent(signature);
45✔
2592
          sigdr.d_place = DNSResourceRecord::ANSWER;
45✔
2593
          sigdr.d_class = QClass::IN;
45✔
2594
          ret.push_back(std::move(sigdr));
45✔
2595
        }
45✔
2596

2597
        for (const auto& rec : *authorityRecs) {
6,197!
2598
          DNSRecord authDR(rec);
×
2599
          authDR.d_ttl = ttl;
×
2600
          ret.push_back(std::move(authDR));
×
2601
        }
×
2602
      }
6,197✔
2603

2604
      DNSName newTarget;
6,197✔
2605
      if (foundQT == QType::DNAME) {
6,197✔
2606
        if (qtype == QType::DNAME && qname == foundName) { // client wanted the DNAME, no need to synthesize a CNAME
16!
2607
          res = RCode::NoError;
2✔
2608
          return true;
2✔
2609
        }
2✔
2610
        // Synthesize a CNAME
2611
        auto dnameRR = getRR<DNAMERecordContent>(record);
14✔
2612
        if (dnameRR == nullptr) {
14!
2613
          throw ImmediateServFailException("Unable to get record content for " + foundName.toLogString() + "|DNAME cache entry");
×
2614
        }
×
2615
        const auto& dnameSuffix = dnameRR->getTarget();
14✔
2616
        DNSName targetPrefix = qname.makeRelative(foundName);
14✔
2617
        try {
14✔
2618
          dnsRecord.d_type = QType::CNAME;
14✔
2619
          dnsRecord.d_name = targetPrefix + foundName;
14✔
2620
          newTarget = targetPrefix + dnameSuffix;
14✔
2621
          dnsRecord.setContent(std::make_shared<CNAMERecordContent>(CNAMERecordContent(newTarget)));
14✔
2622
          ret.push_back(dnsRecord);
14✔
2623
        }
14✔
2624
        catch (const std::exception& e) {
14✔
2625
          // We should probably catch an std::range_error here and set the rcode to YXDOMAIN (RFC 6672, section 2.2)
2626
          // But this is consistent with processRecords
2627
          throw ImmediateServFailException("Unable to perform DNAME substitution(DNAME owner: '" + foundName.toLogString() + "', DNAME target: '" + dnameSuffix.toLogString() + "', substituted name: '" + targetPrefix.toLogString() + "." + dnameSuffix.toLogString() + "' : " + e.what());
×
2628
        }
×
2629

2630
        LOG(prefix << qname << ": Synthesized " << dnsRecord.d_name << "|CNAME " << newTarget << endl);
14!
2631
      }
14✔
2632

2633
      if (qtype == QType::CNAME) { // perhaps they really wanted a CNAME!
6,195✔
2634
        res = RCode::NoError;
2✔
2635
        return true;
2✔
2636
      }
2✔
2637

2638
      if (qtype == QType::DS || qtype == QType::DNSKEY) {
6,195✔
2639
        res = RCode::NoError;
18✔
2640
        return true;
18✔
2641
      }
18✔
2642

2643
      // We have a DNAME _or_ CNAME cache hit and the client wants something else than those two.
2644
      // Let's find the answer!
2645
      if (foundQT == QType::CNAME) {
6,176✔
2646
        const auto cnameContent = getRR<CNAMERecordContent>(record);
6,164✔
2647
        if (cnameContent == nullptr) {
6,164!
2648
          throw ImmediateServFailException("Unable to get record content for " + foundName.toLogString() + "|CNAME cache entry");
×
2649
        }
×
2650
        newTarget = cnameContent->getTarget();
6,164✔
2651
      }
6,164✔
2652

2653
      if (qname == newTarget) {
6,175✔
2654
        string msg = "Got a CNAME referral (from cache) to self";
2✔
2655
        LOG(prefix << qname << ": " << msg << endl);
2!
2656
        throw ImmediateServFailException(std::move(msg));
2✔
2657
      }
2✔
2658

2659
      if (newTarget.isPartOf(qname)) {
6,173✔
2660
        // a.b.c. CNAME x.a.b.c will go to great depths with QM on
2661
        string msg = "Got a CNAME referral (from cache) to child, disabling QM";
2✔
2662
        LOG(prefix << qname << ": " << msg << endl);
2!
2663
        setQNameMinimization(false);
2✔
2664
      }
2✔
2665

2666
      if (!d_followCNAME) {
6,173✔
2667
        res = RCode::NoError;
127✔
2668
        return true;
127✔
2669
      }
127✔
2670

2671
      // Check to see if we already have seen the new target as a previous target or that we have a very long CNAME chain
2672
      const auto [CNAMELoop, numCNAMEs] = scanForCNAMELoop(newTarget, ret);
6,046✔
2673
      if (CNAMELoop) {
6,046✔
2674
        string msg = "got a CNAME referral (from cache) that causes a loop";
4✔
2675
        LOG(prefix << qname << ": Status=" << msg << endl);
4!
2676
        throw ImmediateServFailException(std::move(msg));
4✔
2677
      }
4✔
2678
      if (numCNAMEs > s_max_CNAMES_followed) {
6,042✔
2679
        string msg = "max number of CNAMEs exceeded";
4✔
2680
        LOG(prefix << qname << ": Status=" << msg << endl);
4!
2681
        throw ImmediateServFailException(std::move(msg));
4✔
2682
      }
4✔
2683

2684
      set<GetBestNSAnswer> beenthere;
6,038✔
2685
      Context cnameContext;
6,038✔
2686
      // Be aware that going out on the network might be disabled (cache-only), for example because we are in QM Step0,
2687
      // so you can't trust that a real lookup will have been made.
2688
      res = doResolve(newTarget, qtype, ret, depth + 1, beenthere, cnameContext);
6,038✔
2689
      LOG(prefix << qname << ": Updating validation state for response to " << qname << " from " << context.state << " with the state from the DNAME/CNAME quest: " << cnameContext.state << endl);
6,038✔
2690
      pdns::dedupRecords(ret); // multiple NSECS could have been added, #14120
6,038✔
2691
      updateValidationState(qname, context.state, cnameContext.state, prefix);
6,038✔
2692

2693
      return true;
6,038✔
2694
    }
6,042✔
2695
  }
6,198✔
2696
  throw ImmediateServFailException("Could not determine whether or not there was a CNAME or DNAME in cache for '" + qname.toLogString() + "'");
2,147,483,647✔
2697
}
6,197✔
2698

2699
namespace
2700
{
2701
struct CacheEntry
2702
{
2703
  vector<DNSRecord> records;
2704
  MemRecursorCache::SigRecsVec signatures;
2705
  time_t d_ttl_time{0};
2706
  uint32_t signaturesTTL{std::numeric_limits<uint32_t>::max()};
2707
};
2708
struct CacheKey
2709
{
2710
  DNSName name;
2711
  QType type;
2712
  DNSResourceRecord::Place place;
2713
  bool operator<(const CacheKey& rhs) const
2714
  {
407,449✔
2715
    return std::tie(type, place, name) < std::tie(rhs.type, rhs.place, rhs.name);
407,449✔
2716
  }
407,449✔
2717
};
2718
using tcache_t = map<CacheKey, CacheEntry>;
2719
}
2720

2721
static void reapRecordsFromNegCacheEntryForValidation(tcache_t& tcache, const vector<DNSRecord>& records)
2722
{
32✔
2723
  for (const auto& rec : records) {
32✔
2724
    if (rec.d_type == QType::RRSIG) {
22✔
2725
      auto rrsig = getRR<RRSIGRecordContent>(rec);
10✔
2726
      if (rrsig) {
10!
2727
        tcache[{rec.d_name, rrsig->d_type, rec.d_place}].signatures.push_back(rrsig);
10✔
2728
      }
10✔
2729
    }
10✔
2730
    else {
12✔
2731
      tcache[{rec.d_name, rec.d_type, rec.d_place}].records.push_back(rec);
12✔
2732
    }
12✔
2733
  }
22✔
2734
}
32✔
2735

2736
static bool negativeCacheEntryHasSOA(const NegCache::NegCacheEntry& negEntry)
2737
{
16✔
2738
  return !negEntry.authoritySOA.records.empty();
16✔
2739
}
16✔
2740

2741
static void reapRecordsForValidation(std::map<QType, CacheEntry>& entries, const vector<DNSRecord>& records)
2742
{
3✔
2743
  for (const auto& rec : records) {
5✔
2744
    entries[rec.d_type].records.push_back(rec);
5✔
2745
  }
5✔
2746
}
3✔
2747

2748
static void reapSignaturesForValidation(std::map<QType, CacheEntry>& entries, const MemRecursorCache::SigRecs& signatures)
2749
{
3✔
2750
  for (const auto& sig : *signatures) {
5✔
2751
    entries[sig->d_type].signatures.push_back(sig);
5✔
2752
  }
5✔
2753
}
3✔
2754

2755
/*!
2756
 * Convenience function to push the records from records into ret with a new TTL
2757
 *
2758
 * \param records DNSRecords that need to go into ret
2759
 * \param ttl     The new TTL for these records
2760
 * \param ret     The vector of DNSRecords that should contain the records with the modified TTL
2761
 */
2762
static void addTTLModifiedRecords(vector<DNSRecord>& records, const uint32_t ttl, vector<DNSRecord>& ret)
2763
{
13,115✔
2764
  for (auto& rec : records) {
13,115✔
2765
    rec.d_ttl = ttl;
10,750✔
2766
    ret.push_back(std::move(rec));
10,750✔
2767
  }
10,750✔
2768
}
13,115✔
2769

2770
void SyncRes::computeNegCacheValidationStatus(const NegCache::NegCacheEntry& negEntry, const DNSName& qname, const QType qtype, const int res, vState& state, unsigned int depth, const string& prefix)
2771
{
8✔
2772
  tcache_t tcache;
8✔
2773
  reapRecordsFromNegCacheEntryForValidation(tcache, negEntry.authoritySOA.records);
8✔
2774
  reapRecordsFromNegCacheEntryForValidation(tcache, negEntry.authoritySOA.signatures);
8✔
2775
  reapRecordsFromNegCacheEntryForValidation(tcache, negEntry.DNSSECRecords.records);
8✔
2776
  reapRecordsFromNegCacheEntryForValidation(tcache, negEntry.DNSSECRecords.signatures);
8✔
2777

2778
  for (const auto& entry : tcache) {
12✔
2779
    // this happens when we did store signatures, but passed on the records themselves
2780
    if (entry.second.records.empty()) {
12!
2781
      continue;
×
2782
    }
×
2783

2784
    const DNSName& owner = entry.first.name;
12✔
2785

2786
    vState recordState = getValidationStatus(owner, !entry.second.signatures.empty(), qtype == QType::DS, depth, prefix);
12✔
2787
    if (state == vState::Indeterminate) {
12✔
2788
      state = recordState;
8✔
2789
    }
8✔
2790

2791
    if (recordState == vState::Secure) {
12✔
2792
      recordState = SyncRes::validateRecordsWithSigs(depth, prefix, qname, qtype, owner, QType(entry.first.type), entry.second.records, entry.second.signatures);
10✔
2793
    }
10✔
2794

2795
    if (recordState != vState::Indeterminate && recordState != state) {
12!
2796
      updateValidationState(qname, state, recordState, prefix);
×
2797
      if (state != vState::Secure) {
×
2798
        break;
×
2799
      }
×
2800
    }
×
2801
  }
12✔
2802

2803
  if (state == vState::Secure) {
8✔
2804
    vState neValidationState = negEntry.d_validationState;
6✔
2805
    dState expectedState = res == RCode::NXDomain ? dState::NXDOMAIN : dState::NXQTYPE;
6!
2806
    dState denialState = getDenialValidationState(negEntry, expectedState, false, prefix);
6✔
2807
    updateDenialValidationState(qname, neValidationState, negEntry.d_name, state, denialState, expectedState, qtype == QType::DS, depth, prefix);
6✔
2808
  }
6✔
2809
  if (state != vState::Indeterminate) {
8!
2810
    /* validation succeeded, let's update the cache entry so we don't have to validate again */
2811
    std::optional<time_t> capTTD = std::nullopt;
8✔
2812
    if (vStateIsBogus(state)) {
8✔
2813
      capTTD = d_now.tv_sec + s_maxbogusttl;
2✔
2814
    }
2✔
2815
    g_negCache->updateValidationStatus(negEntry.d_name, negEntry.d_qtype, state, capTTD);
8✔
2816
  }
8✔
2817
}
8✔
2818

2819
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)
2820
{
36,571✔
2821
  bool giveNegative = false;
36,571✔
2822

2823
  // 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)
2824
  DNSName sqname(qname);
36,571✔
2825
  QType sqt(qtype);
36,571✔
2826
  uint32_t sttl = 0;
36,571✔
2827
  //  cout<<"Lookup for '"<<qname<<"|"<<qtype.toString()<<"' -> "<<getLastLabel(qname)<<endl;
2828
  vState cachedState{};
36,571✔
2829
  NegCache::NegCacheEntry negEntry;
36,571✔
2830

2831
  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.
36,571!
2832
    sttl = negEntry.d_ttd - d_now.tv_sec;
4✔
2833
    LOG(prefix << qname << ": Entire name '" << qname << "', is negatively cached via '" << negEntry.d_auth << "' & '" << negEntry.d_name << "' for another " << sttl << " seconds" << endl);
4!
2834
    res = RCode::NXDomain;
4✔
2835
    giveNegative = true;
4✔
2836
    cachedState = negEntry.d_validationState;
4✔
2837
    if (s_addExtendedResolutionDNSErrors) {
4✔
2838
      context.extendedError = EDNSExtendedError{static_cast<uint16_t>(EDNSExtendedError::code::Synthesized), "Result synthesized by root-nx-trust"};
2✔
2839
    }
2✔
2840
  }
4✔
2841
  else if (g_negCache->get(qname, qtype, d_now, negEntry, false, d_serveStale, d_refresh)) {
36,567✔
2842
    /* If we are looking for a DS, discard NXD if auth == qname
2843
       and ask for a specific denial instead */
2844
    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)) {
3,279!
2845
      /* 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
2846
         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
2847
         query. */
2848
      if (qtype == QType::DS && negEntry.d_qtype.getCode() != 0 && !d_externalDSQuery.empty() && qname == d_externalDSQuery && !negativeCacheEntryHasSOA(negEntry)) {
3,279✔
2849
        giveNegative = false;
6✔
2850
      }
6✔
2851
      else {
3,273✔
2852
        res = RCode::NXDomain;
3,273✔
2853
        sttl = negEntry.d_ttd - d_now.tv_sec;
3,273✔
2854
        giveNegative = true;
3,273✔
2855
        cachedState = negEntry.d_validationState;
3,273✔
2856
        if (negEntry.d_qtype.getCode() != 0) {
3,273✔
2857
          LOG(prefix << qname << "|" << qtype << ": Is negatively cached via '" << negEntry.d_auth << "' for another " << sttl << " seconds" << endl);
3,234!
2858
          res = RCode::NoError;
3,234✔
2859
          if (s_addExtendedResolutionDNSErrors) {
3,234✔
2860
            context.extendedError = EDNSExtendedError{static_cast<uint16_t>(EDNSExtendedError::code::Synthesized), "Result from negative cache"};
3,106✔
2861
          }
3,106✔
2862
        }
3,234✔
2863
        else {
39✔
2864
          LOG(prefix << qname << ": Entire name '" << qname << "' is negatively cached via '" << negEntry.d_auth << "' for another " << sttl << " seconds" << endl);
39!
2865
          if (s_addExtendedResolutionDNSErrors) {
39✔
2866
            context.extendedError = EDNSExtendedError{static_cast<uint16_t>(EDNSExtendedError::code::Synthesized), "Result from negative cache for entire name"};
27✔
2867
          }
27✔
2868
        }
39✔
2869
      }
3,273✔
2870
    }
3,279✔
2871
  }
3,279✔
2872
  else if (s_hardenNXD != HardenNXD::No && !qname.isRoot() && !wasForwardedOrAuthZone) {
33,288✔
2873
    auto labels = qname.getRawLabels();
31,491✔
2874
    DNSName negCacheName(g_rootdnsname);
31,491✔
2875
    negCacheName.prependRawLabel(labels.back());
31,491✔
2876
    labels.pop_back();
31,491✔
2877
    while (!labels.empty()) {
79,215✔
2878
      if (g_negCache->get(negCacheName, QType::ENT, d_now, negEntry, true, d_serveStale, d_refresh)) {
47,755✔
2879
        if (negEntry.d_validationState == vState::Indeterminate && validationEnabled()) {
35!
2880
          // LOG(prefix << negCacheName <<  " negatively cached and vState::Indeterminate, trying to validate NXDOMAIN" << endl);
2881
          // ...
2882
          // And get the updated ne struct
2883
          // t_sstorage.negcache.get(negCacheName, QType(0), d_now, ne, true);
2884
        }
×
2885
        if ((s_hardenNXD == HardenNXD::Yes && !vStateIsBogus(negEntry.d_validationState)) || negEntry.d_validationState == vState::Secure) {
35!
2886
          res = RCode::NXDomain;
31✔
2887
          sttl = negEntry.d_ttd - d_now.tv_sec;
31✔
2888
          giveNegative = true;
31✔
2889
          cachedState = negEntry.d_validationState;
31✔
2890
          LOG(prefix << qname << ": Name '" << negCacheName << "' and below, is negatively cached via '" << negEntry.d_auth << "' for another " << sttl << " seconds" << endl);
31!
2891
          if (s_addExtendedResolutionDNSErrors) {
31✔
2892
            context.extendedError = EDNSExtendedError{static_cast<uint16_t>(EDNSExtendedError::code::Synthesized), "Result synthesized by nothing-below-nxdomain (RFC8020)"};
15✔
2893
          }
15✔
2894
          break;
31✔
2895
        }
31✔
2896
      }
35✔
2897
      negCacheName.prependRawLabel(labels.back());
47,724✔
2898
      labels.pop_back();
47,724✔
2899
    }
47,724✔
2900
  }
31,491✔
2901

2902
  if (giveNegative) {
36,571✔
2903

2904
    context.state = cachedState;
3,308✔
2905

2906
    if (!wasAuthZone && shouldValidate() && context.state == vState::Indeterminate) {
3,308✔
2907
      LOG(prefix << qname << ": Got vState::Indeterminate state for records retrieved from the negative cache, validating.." << endl);
8!
2908
      computeNegCacheValidationStatus(negEntry, qname, qtype, res, context.state, depth, prefix);
8✔
2909

2910
      if (context.state != cachedState && vStateIsBogus(context.state)) {
8!
2911
        sttl = std::min(sttl, s_maxbogusttl);
2✔
2912
      }
2✔
2913
    }
8✔
2914

2915
    // Transplant SOA to the returned packet
2916
    addTTLModifiedRecords(negEntry.authoritySOA.records, sttl, ret);
3,308✔
2917
    if (d_doDNSSEC) {
3,308✔
2918
      addTTLModifiedRecords(negEntry.authoritySOA.signatures, sttl, ret);
3,269✔
2919
      addTTLModifiedRecords(negEntry.DNSSECRecords.records, sttl, ret);
3,269✔
2920
      addTTLModifiedRecords(negEntry.DNSSECRecords.signatures, sttl, ret);
3,269✔
2921
    }
3,269✔
2922

2923
    LOG(prefix << qname << ": Updating validation state with negative cache content for " << qname << " to " << context.state << endl);
3,308!
2924
    return true;
3,308✔
2925
  }
3,308✔
2926

2927
  vector<DNSRecord> cset;
33,263✔
2928
  bool found = false;
33,263✔
2929
  bool expired = false;
33,263✔
2930
  MemRecursorCache::SigRecs signatures = MemRecursorCache::s_emptySigRecs;
33,263✔
2931
  MemRecursorCache::AuthRecs authorityRecs = MemRecursorCache::s_emptyAuthRecs;
33,263✔
2932
  uint32_t ttl = 0;
33,263✔
2933
  uint32_t capTTL = std::numeric_limits<uint32_t>::max();
33,263✔
2934
  bool wasCachedAuth{};
33,263✔
2935
  MemRecursorCache::Flags flags = MemRecursorCache::None;
33,263✔
2936
  if (!wasForwardRecurse && d_requireAuthData) {
33,263✔
2937
    flags |= MemRecursorCache::RequireAuth;
31,521✔
2938
  }
31,521✔
2939
  if (d_serveStale) {
33,263✔
2940
    flags |= MemRecursorCache::ServeStale;
10✔
2941
  }
10✔
2942
  if (d_refresh) {
33,263✔
2943
    flags |= MemRecursorCache::Refresh;
276✔
2944
  }
276✔
2945

2946
  MemRecursorCache::Extra extra;
33,263✔
2947
  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, &extra) > 0) {
33,263✔
2948
    d_fromAuthIP = extra.d_address;
7,541✔
2949

2950
    LOG(prefix << sqname << ": Found cache hit for " << sqt.toString() << ": ");
7,541✔
2951

2952
    if (!wasAuthZone && shouldValidate() && (wasCachedAuth || wasForwardRecurse) && cachedState == vState::Indeterminate && d_requireAuthData) {
7,541!
2953

2954
      /* This means we couldn't figure out the state when this entry was cached */
2955
      vState recordState = getValidationStatus(qname, !signatures->empty(), qtype == QType::DS, depth, prefix);
43✔
2956

2957
      if (recordState == vState::Secure) {
43✔
2958
        LOG(prefix << sqname << ": Got vState::Indeterminate state from the cache, validating.." << endl);
35!
2959
        if (sqt == QType::DNSKEY && sqname == getSigner(*signatures)) {
35✔
2960
          cachedState = validateDNSKeys(sqname, cset, *signatures, depth, prefix);
2✔
2961
        }
2✔
2962
        else {
33✔
2963
          if (sqt == QType::ANY) {
33✔
2964
            std::map<QType, CacheEntry> types;
3✔
2965
            reapRecordsForValidation(types, cset);
3✔
2966
            reapSignaturesForValidation(types, signatures);
3✔
2967

2968
            for (const auto& type : types) {
5✔
2969
              vState cachedRecordState{};
5✔
2970
              if (type.first == QType::DNSKEY && sqname == getSigner(type.second.signatures)) {
5!
2971
                cachedRecordState = validateDNSKeys(sqname, type.second.records, type.second.signatures, depth, prefix);
×
2972
              }
×
2973
              else {
5✔
2974
                cachedRecordState = SyncRes::validateRecordsWithSigs(depth, prefix, qname, qtype, sqname, type.first, type.second.records, type.second.signatures);
5✔
2975
              }
5✔
2976
              updateDNSSECValidationState(cachedState, cachedRecordState);
5✔
2977
            }
5✔
2978
          }
3✔
2979
          else {
30✔
2980
            cachedState = SyncRes::validateRecordsWithSigs(depth, prefix, qname, qtype, sqname, sqt, cset, *signatures);
30✔
2981
          }
30✔
2982
        }
33✔
2983
      }
35✔
2984
      else {
8✔
2985
        cachedState = recordState;
8✔
2986
      }
8✔
2987

2988
      if (cachedState != vState::Indeterminate) {
43!
2989
        LOG(prefix << qname << ": Got vState::Indeterminate state from the cache, validation result is " << cachedState << endl);
43!
2990
        if (vStateIsBogus(cachedState)) {
43✔
2991
          capTTL = s_maxbogusttl;
10✔
2992
        }
10✔
2993
        if (sqt != QType::ANY && sqt != QType::ADDR) {
43!
2994
          updateValidationStatusInCache(sqname, sqt, wasCachedAuth, cachedState);
40✔
2995
        }
40✔
2996
      }
43✔
2997
    }
43✔
2998

2999
    for (auto j = cset.cbegin(); j != cset.cend(); ++j) {
18,275✔
3000

3001
      LOG(j->getContent()->getZoneRepresentation());
10,734✔
3002

3003
      if (j->d_class != QClass::IN) {
10,734!
3004
        continue;
×
3005
      }
×
3006

3007
      if (j->d_ttl > (unsigned int)d_now.tv_sec) {
10,734!
3008
        DNSRecord dnsRecord = *j;
10,734✔
3009
        dnsRecord.d_ttl -= d_now.tv_sec;
10,734✔
3010
        dnsRecord.d_ttl = std::min(dnsRecord.d_ttl, capTTL);
10,734✔
3011
        ttl = dnsRecord.d_ttl;
10,734✔
3012
        ret.push_back(dnsRecord);
10,734✔
3013
        LOG("[ttl=" << dnsRecord.d_ttl << "] ");
10,734✔
3014
        found = true;
10,734✔
3015
      }
10,734✔
3016
      else {
×
3017
        LOG("[expired] ");
×
3018
        expired = true;
×
3019
      }
×
3020
    }
10,734✔
3021

3022
    ret.reserve(ret.size() + signatures->size() + authorityRecs->size());
7,541✔
3023

3024
    for (const auto& signature : *signatures) {
7,541✔
3025
      DNSRecord dnsRecord;
7,011✔
3026
      dnsRecord.d_type = QType::RRSIG;
7,011✔
3027
      dnsRecord.d_name = sqname;
7,011✔
3028
      dnsRecord.d_ttl = ttl;
7,011✔
3029
      dnsRecord.setContent(signature);
7,011✔
3030
      dnsRecord.d_place = DNSResourceRecord::ANSWER;
7,011✔
3031
      dnsRecord.d_class = QClass::IN;
7,011✔
3032
      ret.push_back(std::move(dnsRecord));
7,011✔
3033
    }
7,011✔
3034

3035
    for (const auto& rec : *authorityRecs) {
7,541✔
3036
      DNSRecord dnsRecord(rec);
32✔
3037
      dnsRecord.d_ttl = ttl;
32✔
3038
      ret.push_back(std::move(dnsRecord));
32✔
3039
    }
32✔
3040

3041
    LOG(endl);
7,541✔
3042
    if (found && !expired) {
7,541!
3043
      if (!giveNegative) {
7,541!
3044
        res = 0;
7,541✔
3045
      }
7,541✔
3046
      LOG(prefix << qname << ": Updating validation state with cache content for " << qname << " to " << cachedState << endl);
7,541✔
3047
      context.state = cachedState;
7,541✔
3048
      return true;
7,541✔
3049
    }
7,541✔
3050
    LOG(prefix << qname << ": Cache had only stale entries" << endl);
×
3051
  }
×
3052

3053
  /* let's check if we have a NSEC covering that record */
3054
  if (g_aggressiveNSECCache && !wasForwardedOrAuthZone) {
25,722✔
3055
    if (g_aggressiveNSECCache->getDenial(d_now.tv_sec, qname, qtype, ret, res, d_cacheRemote, d_routingTag, d_doDNSSEC, d_validationContext, LogObject(prefix))) {
15,083✔
3056
      context.state = vState::Secure;
44✔
3057
      if (s_addExtendedResolutionDNSErrors) {
44✔
3058
        context.extendedError = EDNSExtendedError{static_cast<uint16_t>(EDNSExtendedError::code::Synthesized), "Result synthesized from aggressive NSEC cache (RFC8198)"};
28✔
3059
      }
28✔
3060
      return true;
44✔
3061
    }
44✔
3062
  }
15,083✔
3063

3064
  return false;
25,678✔
3065
}
25,722✔
3066

3067
bool SyncRes::moreSpecificThan(const DNSName& lhs, const DNSName& rhs)
3068
{
14,227✔
3069
  return (lhs.isPartOf(rhs) && lhs.countLabels() > rhs.countLabels());
14,227!
3070
}
14,227✔
3071

3072
struct speedOrder
3073
{
3074
  bool operator()(const std::pair<DNSName, float>& lhs, const std::pair<DNSName, float>& rhs) const
3075
  {
151,237✔
3076
    return lhs.second < rhs.second;
151,237✔
3077
  }
151,237✔
3078
};
3079

3080
std::vector<std::pair<DNSName, float>> SyncRes::shuffleInSpeedOrder(const DNSName& qname, NsSet& tnameservers, const string& prefix)
3081
{
13,022✔
3082
  std::vector<std::pair<DNSName, float>> rnameservers;
13,022✔
3083
  rnameservers.reserve(tnameservers.size());
13,022✔
3084
  for (const auto& tns : tnameservers) {
71,337✔
3085
    float speed = s_nsSpeeds.lock()->fastest(tns.first, d_now);
71,337✔
3086
    rnameservers.emplace_back(tns.first, speed);
71,337✔
3087
    if (tns.first.empty()) { // this was an authoritative OOB zone, don't pollute the nsSpeeds with that
71,337✔
3088
      return rnameservers;
323✔
3089
    }
323✔
3090
  }
71,337✔
3091

3092
  shuffle(rnameservers.begin(), rnameservers.end(), pdns::dns_random_engine());
12,699✔
3093
  speedOrder speedCompare;
12,699✔
3094
  stable_sort(rnameservers.begin(), rnameservers.end(), speedCompare);
12,699✔
3095

3096
  if (doLog()) {
12,699✔
3097
    LOG(prefix << qname << ": Nameservers: ");
1,580!
3098
    for (auto i = rnameservers.begin(); i != rnameservers.end(); ++i) {
4,354✔
3099
      if (i != rnameservers.begin()) {
2,774✔
3100
        LOG(", ");
1,194!
3101
        if (((i - rnameservers.begin()) % 3) == 0) {
1,194✔
3102
          LOG(endl
223!
3103
              << prefix << "             ");
223✔
3104
        }
223✔
3105
      }
1,194✔
3106
      LOG(i->first.toLogString() << "(" << fmtfloat(i->second / 1000.0) << "ms)");
2,774!
3107
    }
2,774✔
3108
    LOG(endl);
1,580!
3109
  }
1,580✔
3110
  return rnameservers;
12,699✔
3111
}
13,022✔
3112

3113
vector<ComboAddress> SyncRes::shuffleForwardSpeed(const DNSName& qname, const vector<ComboAddress>& rnameservers, const string& prefix, const bool wasRd)
3114
{
184✔
3115
  vector<ComboAddress> nameservers = rnameservers;
184✔
3116
  map<ComboAddress, float> speeds;
184✔
3117

3118
  for (const auto& val : nameservers) {
232✔
3119
    DNSName nsName = DNSName(val.toStringWithPort());
232✔
3120
    float speed = s_nsSpeeds.lock()->fastest(nsName, d_now);
232✔
3121
    speeds[val] = speed;
232✔
3122
  }
232✔
3123
  shuffle(nameservers.begin(), nameservers.end(), pdns::dns_random_engine());
184✔
3124
  speedOrderCA speedCompare(speeds);
184✔
3125
  stable_sort(nameservers.begin(), nameservers.end(), speedCompare);
184✔
3126

3127
  if (doLog()) {
184✔
3128
    LOG(prefix << qname << ": Nameservers: ");
126!
3129
    for (auto i = nameservers.cbegin(); i != nameservers.cend(); ++i) {
296✔
3130
      if (i != nameservers.cbegin()) {
170✔
3131
        LOG(", ");
44!
3132
        if (((i - nameservers.cbegin()) % 3) == 0) {
44!
3133
          LOG(endl
×
3134
              << prefix << "             ");
×
3135
        }
×
3136
      }
44✔
3137
      LOG((wasRd ? string("+") : string("-")) << i->toStringWithPort() << "(" << fmtfloat(speeds[*i] / 1000.0) << "ms)");
170!
3138
    }
170✔
3139
    LOG(endl);
126!
3140
  }
126✔
3141
  return nameservers;
184✔
3142
}
184✔
3143

3144
static uint32_t getRRSIGTTL(const time_t now, const std::shared_ptr<const RRSIGRecordContent>& rrsig)
3145
{
8,170✔
3146
  uint32_t res = 0;
8,170✔
3147
  if (now < rrsig->d_sigexpire) {
8,170!
3148
    // coverity[store_truncates_time_t]
3149
    res = static_cast<uint32_t>(rrsig->d_sigexpire) - now;
8,170✔
3150
  }
8,170✔
3151
  return res;
8,170✔
3152
}
8,170✔
3153

3154
static const set<QType> nsecTypes = {QType::NSEC, QType::NSEC3};
3155

3156
/* Fills the authoritySOA and DNSSECRecords fields from ne with those found in the records
3157
 *
3158
 * \param records The records to parse for the authority SOA and NSEC(3) records
3159
 * \param ne      The NegCacheEntry to be filled out (will not be cleared, only appended to
3160
 */
3161
static void harvestNXRecords(const vector<DNSRecord>& records, NegCache::NegCacheEntry& negEntry, const time_t now, uint32_t* lowestTTL)
3162
{
7,139✔
3163
  for (const auto& rec : records) {
62,148✔
3164
    if (rec.d_place != DNSResourceRecord::AUTHORITY) {
62,148✔
3165
      // RFC 4035 section 3.1.3. indicates that NSEC records MUST be placed in
3166
      // the AUTHORITY section. Section 3.1.1 indicates that RRSIGs for
3167
      // records MUST be in the same section as the records they cover.
3168
      // Hence, we ignore all records outside of the AUTHORITY section.
3169
      continue;
25,863✔
3170
    }
25,863✔
3171

3172
    if (rec.d_type == QType::RRSIG) {
36,285✔
3173
      auto rrsig = getRR<RRSIGRecordContent>(rec);
8,856✔
3174
      if (rrsig) {
8,856✔
3175
        if (rrsig->d_type == QType::SOA) {
8,855✔
3176
          negEntry.authoritySOA.signatures.push_back(rec);
579✔
3177
          if (lowestTTL != nullptr && isRRSIGNotExpired(now, *rrsig)) {
579✔
3178
            *lowestTTL = min(*lowestTTL, rec.d_ttl);
288✔
3179
            *lowestTTL = min(*lowestTTL, getRRSIGTTL(now, rrsig));
288✔
3180
          }
288✔
3181
        }
579✔
3182
        if (nsecTypes.count(rrsig->d_type) != 0) {
8,855✔
3183
          negEntry.DNSSECRecords.signatures.push_back(rec);
8,277✔
3184
          if (lowestTTL != nullptr && isRRSIGNotExpired(now, *rrsig)) {
8,277✔
3185
            *lowestTTL = min(*lowestTTL, rec.d_ttl);
7,882✔
3186
            *lowestTTL = min(*lowestTTL, getRRSIGTTL(now, rrsig));
7,882✔
3187
          }
7,882✔
3188
        }
8,277✔
3189
      }
8,855✔
3190
      continue;
8,856✔
3191
    }
8,856✔
3192
    if (rec.d_type == QType::SOA) {
27,429✔
3193
      negEntry.authoritySOA.records.push_back(rec);
2,990✔
3194
      if (lowestTTL != nullptr) {
2,990✔
3195
        *lowestTTL = min(*lowestTTL, rec.d_ttl);
1,540✔
3196
      }
1,540✔
3197
      continue;
2,990✔
3198
    }
2,990✔
3199
    if (nsecTypes.count(rec.d_type) != 0) {
24,439✔
3200
      negEntry.DNSSECRecords.records.push_back(rec);
8,289✔
3201
      if (lowestTTL != nullptr) {
8,289✔
3202
        *lowestTTL = min(*lowestTTL, rec.d_ttl);
7,896✔
3203
      }
7,896✔
3204
      continue;
8,289✔
3205
    }
8,289✔
3206
  }
24,439✔
3207
}
7,139✔
3208

3209
static cspmap_t harvestCSPFromNE(const NegCache::NegCacheEntry& negEntry)
3210
{
3,082✔
3211
  cspmap_t cspmap;
3,082✔
3212
  for (const auto& rec : negEntry.DNSSECRecords.signatures) {
5,301✔
3213
    if (rec.d_type == QType::RRSIG) {
5,301!
3214
      auto rrc = getRR<RRSIGRecordContent>(rec);
5,301✔
3215
      if (rrc) {
5,301!
3216
        cspmap[{rec.d_name, rrc->d_type}].signatures.push_back(rrc);
5,301✔
3217
      }
5,301✔
3218
    }
5,301✔
3219
  }
5,301✔
3220
  for (const auto& rec : negEntry.DNSSECRecords.records) {
5,303✔
3221
    cspmap[{rec.d_name, rec.d_type}].records.insert(rec.getContent());
5,303✔
3222
  }
5,303✔
3223
  return cspmap;
3,082✔
3224
}
3,082✔
3225

3226
// TODO remove after processRecords is fixed!
3227
// Adds the RRSIG for the SOA and the NSEC(3) + RRSIGs to ret
3228
static void addNXNSECS(vector<DNSRecord>& ret, const vector<DNSRecord>& records)
3229
{
1,493✔
3230
  NegCache::NegCacheEntry negEntry;
1,493✔
3231
  harvestNXRecords(records, negEntry, 0, nullptr);
1,493✔
3232
  ret.insert(ret.end(), negEntry.authoritySOA.signatures.begin(), negEntry.authoritySOA.signatures.end());
1,493✔
3233
  ret.insert(ret.end(), negEntry.DNSSECRecords.records.begin(), negEntry.DNSSECRecords.records.end());
1,493✔
3234
  ret.insert(ret.end(), negEntry.DNSSECRecords.signatures.begin(), negEntry.DNSSECRecords.signatures.end());
1,493✔
3235
}
1,493✔
3236

3237
static bool rpzHitShouldReplaceContent(const DNSName& qname, const QType qtype, const std::vector<DNSRecord>& records)
3238
{
63✔
3239
  if (qtype == QType::CNAME) {
63!
3240
    return true;
×
3241
  }
×
3242

3243
  for (const auto& record : records) { // NOLINT(readability-use-anyofallof): don't agree
63✔
3244
    if (record.d_type == QType::CNAME) {
9✔
3245
      if (auto content = getRR<CNAMERecordContent>(record)) {
3!
3246
        if (qname == content->getTarget()) {
3!
3247
          /* we have a CNAME whose target matches the entry we are about to
3248
             generate, so it will complete the current records, not replace
3249
             them
3250
          */
3251
          return false;
3✔
3252
        }
3✔
3253
      }
3✔
3254
    }
3✔
3255
  }
9✔
3256

3257
  return true;
60✔
3258
}
63✔
3259

3260
static void removeConflictingRecord(std::vector<DNSRecord>& records, const DNSName& name, const QType dtype)
3261
{
67✔
3262
  for (auto it = records.begin(); it != records.end();) {
73✔
3263
    bool remove = false;
6✔
3264

3265
    if (it->d_class == QClass::IN && (it->d_type == QType::CNAME || dtype == QType::CNAME || it->d_type == dtype) && it->d_name == name) {
6!
3266
      remove = true;
1✔
3267
    }
1✔
3268
    else if (it->d_class == QClass::IN && it->d_type == QType::RRSIG && it->d_name == name) {
5!
3269
      if (auto rrc = getRR<RRSIGRecordContent>(*it)) {
1!
3270
        if (rrc->d_type == QType::CNAME || rrc->d_type == dtype) {
1!
3271
          /* also remove any RRSIG that could conflict */
3272
          remove = true;
1✔
3273
        }
1✔
3274
      }
1✔
3275
    }
1✔
3276

3277
    if (remove) {
6✔
3278
      it = records.erase(it);
2✔
3279
    }
2✔
3280
    else {
4✔
3281
      ++it;
4✔
3282
    }
4✔
3283
  }
6✔
3284
}
67✔
3285

3286
void SyncRes::handlePolicyHit(const std::string& prefix, const DNSName& qname, const QType qtype, std::vector<DNSRecord>& ret, bool& done, int& rcode, unsigned int depth)
3287
{
109✔
3288
  if (d_pdl && d_pdl->policyHitEventFilter(d_requestor, qname, qtype, d_queryReceivedOverTCP, d_appliedPolicy, d_policyTags, d_discardedPolicies)) {
109✔
3289
    /* reset to no match */
3290
    d_appliedPolicy = DNSFilterEngine::Policy();
2✔
3291
    return;
2✔
3292
  }
2✔
3293

3294
  /* don't account truncate actions for TCP queries, since they are not applied */
3295
  if (d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::Truncate || !d_queryReceivedOverTCP) {
107✔
3296
    ++t_Counters.at(rec::PolicyHistogram::policy).at(d_appliedPolicy.d_kind);
104✔
3297
    ++t_Counters.at(rec::PolicyNameHits::policyName).counts[d_appliedPolicy.getName()];
104✔
3298
  }
104✔
3299

3300
  if (d_appliedPolicy.d_type != DNSFilterEngine::PolicyType::None) {
107!
3301
    LOG(prefix << qname << "|" << qtype << ':' << d_appliedPolicy.getLogString() << endl);
107!
3302
  }
107✔
3303

3304
  switch (d_appliedPolicy.d_kind) {
107!
3305

3306
  case DNSFilterEngine::PolicyKind::NoAction:
22✔
3307
    return;
22✔
3308

3309
  case DNSFilterEngine::PolicyKind::Drop:
6✔
3310
    ++t_Counters.at(rec::Counter::policyDrops);
6✔
3311
    throw ImmediateQueryDropException();
6✔
3312

3313
  case DNSFilterEngine::PolicyKind::NXDOMAIN:
6✔
3314
    ret.clear();
6✔
3315
    d_appliedPolicy.addSOAtoRPZResult(ret);
6✔
3316
    rcode = RCode::NXDomain;
6✔
3317
    done = true;
6✔
3318
    return;
6✔
3319

3320
  case DNSFilterEngine::PolicyKind::NODATA:
4✔
3321
    ret.clear();
4✔
3322
    d_appliedPolicy.addSOAtoRPZResult(ret);
4✔
3323
    rcode = RCode::NoError;
4✔
3324
    done = true;
4✔
3325
    return;
4✔
3326

3327
  case DNSFilterEngine::PolicyKind::Truncate:
6✔
3328
    if (!d_queryReceivedOverTCP) {
6✔
3329
      ret.clear();
3✔
3330
      rcode = RCode::NoError;
3✔
3331
      // Exception handling code in pdns_recursor clears ret as well, so no use to
3332
      // fill it here.
3333
      throw SendTruncatedAnswerException();
3✔
3334
    }
3✔
3335
    return;
3✔
3336

3337
  case DNSFilterEngine::PolicyKind::Custom: {
63✔
3338
    if (rpzHitShouldReplaceContent(qname, qtype, ret)) {
63✔
3339
      ret.clear();
60✔
3340
    }
60✔
3341

3342
    rcode = RCode::NoError;
63✔
3343
    done = true;
63✔
3344
    auto spoofed = d_appliedPolicy.getCustomRecords(qname, qtype.getCode());
63✔
3345
    for (auto& dnsRecord : spoofed) {
67✔
3346
      removeConflictingRecord(ret, dnsRecord.d_name, dnsRecord.d_type);
67✔
3347
    }
67✔
3348

3349
    for (auto& dnsRecord : spoofed) {
67✔
3350
      ret.push_back(dnsRecord);
67✔
3351

3352
      if (dnsRecord.d_name == qname && dnsRecord.d_type == QType::CNAME && qtype != QType::CNAME) {
67!
3353
        if (auto content = getRR<CNAMERecordContent>(dnsRecord)) {
10!
3354
          vState newTargetState = vState::Indeterminate;
10✔
3355
          handleNewTarget(prefix, qname, content->getTarget(), qtype.getCode(), ret, rcode, depth, {}, newTargetState);
10✔
3356
        }
10✔
3357
      }
10✔
3358
    }
67✔
3359
    d_appliedPolicy.addSOAtoRPZResult(ret);
63✔
3360
  }
63✔
3361
  }
107✔
3362
}
107✔
3363

3364
bool SyncRes::nameserversBlockedByRPZ(const DNSFilterEngine& dfe, const NsSet& nameservers)
3365
{
11,011✔
3366
  /* we skip RPZ processing if:
3367
     - it was disabled (d_wantsRPZ is false) ;
3368
     - we already got a RPZ hit (d_appliedPolicy.d_type != DNSFilterEngine::PolicyType::None) since
3369
     the only way we can get back here is that it was a 'pass-thru' (NoAction) meaning that we should not
3370
     process any further RPZ rules. Except that we need to process rules of higher priority..
3371
  */
3372
  if (d_wantsRPZ && !d_appliedPolicy.wasHit()) {
11,011✔
3373
    for (auto const& nameserver : nameservers) {
64,009✔
3374
      bool match = dfe.getProcessingPolicy(nameserver.first, d_discardedPolicies, d_appliedPolicy);
64,009✔
3375
      if (match) {
64,009!
3376
        mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());
×
3377
        if (d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) { // client query needs an RPZ response
×
3378
          LOG(", however nameserver " << nameserver.first << " was blocked by RPZ policy '" << d_appliedPolicy.getName() << "'" << endl);
×
3379
          return true;
×
3380
        }
×
3381
      }
×
3382

3383
      // Traverse all IP addresses for this NS to see if they have an RPN NSIP policy
3384
      for (auto const& address : nameserver.second.first) {
64,009✔
3385
        match = dfe.getProcessingPolicy(address, d_discardedPolicies, d_appliedPolicy);
234✔
3386
        if (match) {
234✔
3387
          mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());
3✔
3388
          if (d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) { // client query needs an RPZ response
3✔
3389
            LOG(", however nameserver " << nameserver.first << " IP address " << address.toString() << " was blocked by RPZ policy '" << d_appliedPolicy.getName() << "'" << endl);
2!
3390
            return true;
2✔
3391
          }
2✔
3392
        }
3✔
3393
      }
234✔
3394
    }
64,009✔
3395
  }
10,992✔
3396
  return false;
11,009✔
3397
}
11,011✔
3398

3399
bool SyncRes::nameserverIPBlockedByRPZ(const DNSFilterEngine& dfe, const ComboAddress& remoteIP)
3400
{
14,325✔
3401
  /* we skip RPZ processing if:
3402
     - it was disabled (d_wantsRPZ is false) ;
3403
     - we already got a RPZ hit (d_appliedPolicy.d_type != DNSFilterEngine::PolicyType::None) since
3404
     the only way we can get back here is that it was a 'pass-thru' (NoAction) meaning that we should not
3405
     process any further RPZ rules. Except that we need to process rules of higher priority..
3406
  */
3407
  if (d_wantsRPZ && !d_appliedPolicy.wasHit()) {
14,325✔
3408
    bool match = dfe.getProcessingPolicy(remoteIP, d_discardedPolicies, d_appliedPolicy);
14,305✔
3409
    if (match) {
14,305✔
3410
      mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());
4✔
3411
      if (d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) {
4!
3412
        LOG(" (blocked by RPZ policy '" + d_appliedPolicy.getName() + "')");
4!
3413
        return true;
4✔
3414
      }
4✔
3415
    }
4✔
3416
  }
14,305✔
3417
  return false;
14,321✔
3418
}
14,325✔
3419

3420
static bool shouldNotThrottle(const DNSName* name, const ComboAddress* address)
3421
{
13,175✔
3422
  if (name != nullptr) {
13,175!
3423
    auto dontThrottleNames = g_dontThrottleNames.getLocal();
13,175✔
3424
    if (dontThrottleNames->check(*name)) {
13,175!
3425
      return true;
×
3426
    }
×
3427
  }
13,175✔
3428
  if (address != nullptr) {
13,175✔
3429
    auto dontThrottleNetmasks = g_dontThrottleNetmasks.getLocal();
13,143✔
3430
    if (dontThrottleNetmasks->match(*address)) {
13,143✔
3431
      return true;
4✔
3432
    }
4✔
3433
  }
13,143✔
3434
  return false;
13,171✔
3435
}
13,175✔
3436

3437
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)
3438
{
13,130✔
3439
  vector<ComboAddress> result;
13,130✔
3440

3441
  size_t nonresolvingfails = 0;
13,130✔
3442
  if (!tns->first.empty()) {
13,130✔
3443
    if (s_nonresolvingnsmaxfails > 0) {
12,946✔
3444
      nonresolvingfails = s_nonresolving.lock()->value(tns->first);
10,461✔
3445
      if (nonresolvingfails >= s_nonresolvingnsmaxfails) {
10,461✔
3446
        LOG(prefix << qname << ": NS " << tns->first << " in non-resolving map, skipping" << endl);
18!
3447
        return result;
18✔
3448
      }
18✔
3449
    }
10,461✔
3450

3451
    LOG(prefix << qname << ": Trying to resolve NS '" << tns->first << "' (" << 1 + tns - rnameservers.begin() << "/" << (unsigned int)rnameservers.size() << ")" << endl);
12,927✔
3452
    const unsigned int oldOutQueries = d_outqueries;
12,927✔
3453
    try {
12,927✔
3454
      result = getAddrs(tns->first, depth, prefix, beenthere, cacheOnly, nretrieveAddressesForNS);
12,927✔
3455
    }
12,927✔
3456
    // Other exceptions should likely not throttle...
3457
    catch (const ImmediateServFailException& ex) {
12,927✔
3458
      if (s_nonresolvingnsmaxfails > 0 && d_outqueries > oldOutQueries) {
90!
3459
        if (!shouldNotThrottle(&tns->first, nullptr)) {
×
3460
          s_nonresolving.lock()->incr(tns->first, d_now);
×
3461
        }
×
3462
      }
×
3463
      throw ex;
90✔
3464
    }
90✔
3465
    if (s_nonresolvingnsmaxfails > 0 && d_outqueries > oldOutQueries) {
12,838✔
3466
      if (result.empty()) {
1,425✔
3467
        if (!shouldNotThrottle(&tns->first, nullptr)) {
32!
3468
          s_nonresolving.lock()->incr(tns->first, d_now);
32✔
3469
        }
32✔
3470
      }
32✔
3471
      else if (nonresolvingfails > 0) {
1,393✔
3472
        // Succeeding resolve, clear memory of recent failures
3473
        s_nonresolving.lock()->clear(tns->first);
2✔
3474
      }
2✔
3475
    }
1,425✔
3476
    pierceDontQuery = false;
12,838✔
3477
  }
12,838✔
3478
  else {
185✔
3479
    LOG(prefix << qname << ": Domain has hardcoded nameserver");
185✔
3480

3481
    if (nameservers[tns->first].first.size() > 1) {
185✔
3482
      LOG("s");
30!
3483
    }
30✔
3484
    LOG(endl);
185✔
3485

3486
    sendRDQuery = nameservers[tns->first].second;
185✔
3487
    result = shuffleForwardSpeed(qname, nameservers[tns->first].first, prefix, sendRDQuery);
185✔
3488
    pierceDontQuery = true;
185✔
3489
  }
185✔
3490
  return result;
13,023✔
3491
}
13,130✔
3492

3493
void SyncRes::checkMaxQperQ(const DNSName& qname) const
3494
{
13,365✔
3495
  if (d_outqueries + d_throttledqueries > s_maxqperq) {
13,365✔
3496
    throw ImmediateServFailException("more than " + std::to_string(s_maxqperq) + " (max-qperq) queries sent or throttled while resolving " + qname.toLogString());
4✔
3497
  }
4✔
3498
}
13,365✔
3499

3500
bool SyncRes::throttledOrBlocked(const std::string& prefix, const ComboAddress& remoteIP, const DNSName& qname, const QType qtype, bool pierceDontQuery)
3501
{
13,100✔
3502
  if (isThrottled(d_now.tv_sec, remoteIP)) {
13,100✔
3503
    LOG(prefix << qname << ": Server throttled " << endl);
2!
3504
    t_Counters.at(rec::Counter::throttledqueries)++;
2✔
3505
    d_throttledqueries++;
2✔
3506
    return true;
2✔
3507
  }
2✔
3508
  if (isThrottled(d_now.tv_sec, remoteIP, qname, qtype)) {
13,098✔
3509
    LOG(prefix << qname << ": Query throttled " << remoteIP.toString() << ", " << qname << "; " << qtype << endl);
5!
3510
    t_Counters.at(rec::Counter::throttledqueries)++;
5✔
3511
    d_throttledqueries++;
5✔
3512
    return true;
5✔
3513
  }
5✔
3514
  if (!pierceDontQuery && s_dontQuery && s_dontQuery->match(&remoteIP)) {
13,093✔
3515
    // We could have retrieved an NS from the cache in a forwarding domain
3516
    // Even in the case of !pierceDontQuery we still want to allow that NS
3517
    DNSName forwardCandidate(qname);
2✔
3518
    auto iter = getBestAuthZone(&forwardCandidate);
2✔
3519
    if (iter == t_sstorage.domainmap->end()) {
2!
3520
      LOG(prefix << qname << ": Not sending query to " << remoteIP.toString() << ", blocked by 'dont-query' setting" << endl);
2!
3521
      t_Counters.at(rec::Counter::dontqueries)++;
2✔
3522
      return true;
2✔
3523
    }
2✔
3524
    // The name (from the cache) is forwarded, but is it forwarded to an IP in known forwarders?
3525
    const auto& ips = iter->second.d_servers;
×
3526
    if (std::find(ips.cbegin(), ips.cend(), remoteIP) == ips.cend()) {
×
3527
      LOG(prefix << qname << ": Not sending query to " << remoteIP.toString() << ", blocked by 'dont-query' setting" << endl);
×
3528
      t_Counters.at(rec::Counter::dontqueries)++;
×
3529
      return true;
×
3530
    }
×
3531
    LOG(prefix << qname << ": Sending query to " << remoteIP.toString() << ", blocked by 'dont-query' but a forwarding/auth case" << endl);
×
3532
  }
×
3533
  return false;
13,091✔
3534
}
13,093✔
3535

3536
bool SyncRes::validationEnabled()
3537
{
13,993✔
3538
  return g_dnssecmode != DNSSECMode::Off && g_dnssecmode != DNSSECMode::ProcessNoValidate;
13,993✔
3539
}
13,993✔
3540

3541
uint32_t SyncRes::computeLowestTTD(const std::vector<DNSRecord>& records, const MemRecursorCache::SigRecsVec& signatures, uint32_t signaturesTTL, const MemRecursorCache::AuthRecsVec& authorityRecs) const
3542
{
12,679✔
3543
  uint32_t lowestTTD = std::numeric_limits<uint32_t>::max();
12,679✔
3544
  for (const auto& record : records) {
27,414✔
3545
    lowestTTD = min(lowestTTD, record.d_ttl);
27,414✔
3546
  }
27,414✔
3547

3548
  /* even if it was not requested for that request (Process, and neither AD nor DO set),
3549
     it might be requested at a later time so we need to be careful with the TTL. */
3550
  if (validationEnabled() && !signatures.empty()) {
12,679✔
3551
    /* if we are validating, we don't want to cache records after their signatures expire. */
3552
    /* records TTL are now TTD, let's add 'now' to the signatures lowest TTL */
3553
    lowestTTD = min(lowestTTD, static_cast<uint32_t>(signaturesTTL + d_now.tv_sec));
8,863✔
3554

3555
    for (const auto& sig : signatures) {
8,884✔
3556
      if (isRRSIGNotExpired(d_now.tv_sec, *sig)) {
8,884✔
3557
        // we don't decrement d_sigexpire by 'now' because we actually want a TTD, not a TTL */
3558
        lowestTTD = min(lowestTTD, static_cast<uint32_t>(sig->d_sigexpire));
8,870✔
3559
      }
8,870✔
3560
    }
8,884✔
3561
  }
8,863✔
3562

3563
  for (const auto& entry : authorityRecs) {
12,679✔
3564
    /* be careful, this is still a TTL here */
3565
    lowestTTD = min(lowestTTD, static_cast<uint32_t>(entry.d_ttl + d_now.tv_sec));
2,608✔
3566

3567
    if (entry.d_type == QType::RRSIG && validationEnabled()) {
2,608!
3568
      auto rrsig = getRR<RRSIGRecordContent>(entry);
1,304✔
3569
      if (rrsig) {
1,304!
3570
        if (isRRSIGNotExpired(d_now.tv_sec, *rrsig)) {
1,304!
3571
          // we don't decrement d_sigexpire by 'now' because we actually want a TTD, not a TTL */
3572
          lowestTTD = min(lowestTTD, static_cast<uint32_t>(rrsig->d_sigexpire));
1,304✔
3573
        }
1,304✔
3574
      }
1,304✔
3575
    }
1,304✔
3576
  }
2,608✔
3577

3578
  return lowestTTD;
12,679✔
3579
}
12,679✔
3580

3581
void SyncRes::updateValidationState(const DNSName& qname, vState& state, const vState stateUpdate, const string& prefix)
3582
{
17,173✔
3583
  LOG(prefix << qname << ": Validation state was " << state << ", state update is " << stateUpdate);
17,173✔
3584
  updateDNSSECValidationState(state, stateUpdate);
17,173✔
3585
  LOG(", validation state is now " << state << endl);
17,173✔
3586
}
17,173✔
3587

3588
vState SyncRes::getTA(const DNSName& zone, dsset_t& dsSet, const string& prefix)
3589
{
81,683✔
3590
  auto luaLocal = g_luaconfs.getLocal();
81,683✔
3591

3592
  if (luaLocal->dsAnchors.empty()) {
81,683✔
3593
    LOG(prefix << zone << ": No trust anchors configured, everything is Insecure" << endl);
44!
3594
    /* We have no TA, everything is insecure */
3595
    return vState::Insecure;
44✔
3596
  }
44✔
3597

3598
  std::string reason;
81,639✔
3599
  if (haveNegativeTrustAnchor(luaLocal->negAnchors, zone, reason)) {
81,639✔
3600
    LOG(prefix << zone << ": Got NTA" << endl);
15!
3601
    return vState::NTA;
15✔
3602
  }
15✔
3603

3604
  if (getTrustAnchor(luaLocal->dsAnchors, zone, dsSet)) {
81,624✔
3605
    if (!zone.isRoot()) {
23,352✔
3606
      LOG(prefix << zone << ": Got TA" << endl);
144!
3607
    }
144✔
3608
    return vState::TA;
23,352✔
3609
  }
23,352✔
3610

3611
  if (zone.isRoot()) {
58,272!
3612
    /* No TA for the root */
3613
    return vState::Insecure;
×
3614
  }
×
3615

3616
  return vState::Indeterminate;
58,272✔
3617
}
58,272✔
3618

3619
size_t SyncRes::countSupportedDS(const dsset_t& dsset, const string& prefix)
3620
{
23,351✔
3621
  size_t count = 0;
23,351✔
3622

3623
  for (const auto& dsRecordContent : dsset) {
48,964✔
3624
    if (isSupportedDS(dsRecordContent, LogObject(prefix))) {
48,968✔
3625
      count++;
48,960✔
3626
    }
48,960✔
3627
  }
48,964✔
3628

3629
  return count;
23,351✔
3630
}
23,351✔
3631

3632
void SyncRes::initZoneCutsFromTA(const DNSName& from, const string& prefix)
3633
{
22,808✔
3634
  DNSName zone(from);
22,808✔
3635
  do {
75,707✔
3636
    dsset_t dsSet;
75,707✔
3637
    vState result = getTA(zone, dsSet, prefix);
75,707✔
3638
    if (result != vState::Indeterminate) {
75,707✔
3639
      if (result == vState::TA) {
22,964✔
3640
        if (countSupportedDS(dsSet, prefix) == 0) {
22,905✔
3641
          dsSet.clear();
8✔
3642
          result = vState::Insecure;
8✔
3643
        }
8✔
3644
        else {
22,897✔
3645
          result = vState::Secure;
22,897✔
3646
        }
22,897✔
3647
      }
22,905✔
3648
      else if (result == vState::NTA) {
59✔
3649
        result = vState::Insecure;
15✔
3650
      }
15✔
3651

3652
      d_cutStates[zone] = result;
22,964✔
3653
    }
22,964✔
3654
  } while (zone.chopOff());
75,707✔
3655
}
22,808✔
3656

3657
vState SyncRes::getDSRecords(const DNSName& zone, dsset_t& dsSet, bool onlyTA, unsigned int depth, const string& prefix, bool bogusOnNXD, bool* foundCut)
3658
{
5,973✔
3659
  vState result = getTA(zone, dsSet, prefix);
5,973✔
3660

3661
  if (result != vState::Indeterminate || onlyTA) {
5,973!
3662
    if (foundCut != nullptr) {
449!
3663
      *foundCut = (result != vState::Indeterminate);
×
3664
    }
×
3665

3666
    if (result == vState::TA) {
449!
3667
      if (countSupportedDS(dsSet, prefix) == 0) {
449!
3668
        dsSet.clear();
×
3669
        result = vState::Insecure;
×
3670
      }
×
3671
      else {
449✔
3672
        result = vState::Secure;
449✔
3673
      }
449✔
3674
    }
449✔
3675
    else if (result == vState::NTA) {
×
3676
      result = vState::Insecure;
×
3677
    }
×
3678

3679
    return result;
449✔
3680
  }
449✔
3681

3682
  std::set<GetBestNSAnswer> beenthere;
5,524✔
3683
  std::vector<DNSRecord> dsrecords;
5,524✔
3684

3685
  Context context;
5,524✔
3686

3687
  const bool oldCacheOnly = setCacheOnly(false);
5,524✔
3688
  const bool oldQM = setQNameMinimization(!getQMFallbackMode());
5,524✔
3689
  int rcode = doResolve(zone, QType::DS, dsrecords, depth + 1, beenthere, context);
5,524✔
3690
  setCacheOnly(oldCacheOnly);
5,524✔
3691
  setQNameMinimization(oldQM);
5,524✔
3692

3693
  if (rcode == RCode::ServFail) {
5,524✔
3694
    throw ImmediateServFailException("Server Failure while retrieving DS records for " + zone.toLogString());
4✔
3695
  }
4✔
3696

3697
  if (rcode != RCode::NoError && (rcode != RCode::NXDomain || bogusOnNXD)) {
5,520!
3698
    LOG(prefix << zone << ": Returning Bogus state from " << static_cast<const char*>(__func__) << "(" << zone << ")" << endl);
2!
3699
    return vState::BogusUnableToGetDSs;
2✔
3700
  }
2✔
3701

3702
  uint8_t bestDigestType = 0;
5,518✔
3703

3704
  bool gotCNAME = false;
5,518✔
3705
  for (const auto& record : dsrecords) {
15,905✔
3706
    if (record.d_type == QType::DS) {
15,905✔
3707
      const auto dscontent = getRR<DSRecordContent>(record);
3,031✔
3708
      if (dscontent && isSupportedDS(*dscontent, LogObject(prefix))) {
3,031!
3709
        // Make GOST a lower prio than SHA256
3710
        if (dscontent->d_digesttype == DNSSEC::DIGEST_GOST && bestDigestType == DNSSEC::DIGEST_SHA256) {
3,031!
3711
          continue;
×
3712
        }
×
3713
        if (dscontent->d_digesttype > bestDigestType || (bestDigestType == DNSSEC::DIGEST_GOST && dscontent->d_digesttype == DNSSEC::DIGEST_SHA256)) {
3,031!
3714
          bestDigestType = dscontent->d_digesttype;
2,999✔
3715
        }
2,999✔
3716
        dsSet.insert(*dscontent);
3,031✔
3717
      }
3,031✔
3718
    }
3,031✔
3719
    else if (record.d_type == QType::CNAME && record.d_name == zone) {
12,874!
3720
      gotCNAME = true;
13✔
3721
    }
13✔
3722
  }
15,905✔
3723

3724
  /* RFC 4509 section 3: "Validator implementations SHOULD ignore DS RRs containing SHA-1
3725
   * digests if DS RRs with SHA-256 digests are present in the DS RRset."
3726
   * We interpret that as: do not use SHA-1 if SHA-256 or SHA-384 is available
3727
   */
3728
  for (auto dsrec = dsSet.begin(); dsrec != dsSet.end();) {
8,548✔
3729
    if (dsrec->d_digesttype == DNSSEC::DIGEST_SHA1 && dsrec->d_digesttype != bestDigestType) {
3,030✔
3730
      dsrec = dsSet.erase(dsrec);
7✔
3731
    }
7✔
3732
    else {
3,023✔
3733
      ++dsrec;
3,023✔
3734
    }
3,023✔
3735
  }
3,030✔
3736

3737
  if (rcode == RCode::NoError) {
5,518✔
3738
    if (dsSet.empty()) {
5,512✔
3739
      /* we have no DS, it's either:
3740
         - a delegation to a non-DNSSEC signed zone
3741
         - no delegation, we stay in the same zone
3742
      */
3743
      if (gotCNAME || denialProvesNoDelegation(zone, dsrecords, d_validationContext)) {
2,526✔
3744
        /* we are still inside the same zone */
3745

3746
        if (foundCut != nullptr) {
31✔
3747
          *foundCut = false;
25✔
3748
        }
25✔
3749
        return context.state;
31✔
3750
      }
31✔
3751

3752
      d_cutStates[zone] = context.state == vState::Secure ? vState::Insecure : context.state;
2,495✔
3753
      /* delegation with no DS, might be Secure -> Insecure */
3754
      if (foundCut != nullptr) {
2,495✔
3755
        *foundCut = true;
2,472✔
3756
      }
2,472✔
3757

3758
      /* a delegation with no DS is either:
3759
         - a signed zone (Secure) to an unsigned one (Insecure)
3760
         - an unsigned zone to another unsigned one (Insecure stays Insecure, Bogus stays Bogus)
3761
      */
3762
      return context.state == vState::Secure ? vState::Insecure : context.state;
2,495✔
3763
    }
2,526✔
3764
    /* we have a DS */
3765
    d_cutStates[zone] = context.state;
2,986✔
3766
    if (foundCut != nullptr) {
2,986✔
3767
      *foundCut = true;
1,910✔
3768
    }
1,910✔
3769
  }
2,986✔
3770

3771
  return context.state;
2,992✔
3772
}
5,518✔
3773

3774
vState SyncRes::getValidationStatus(const DNSName& name, bool wouldBeValid, bool typeIsDS, unsigned int depth, const string& prefix)
3775
{
18,324✔
3776
  vState result = vState::Indeterminate;
18,324✔
3777

3778
  if (!shouldValidate()) {
18,324✔
3779
    return result;
1,551✔
3780
  }
1,551✔
3781

3782
  DNSName subdomain(name);
16,773✔
3783
  if (typeIsDS) {
16,773✔
3784
    subdomain.chopOff();
3,252✔
3785
  }
3,252✔
3786

3787
  {
16,773✔
3788
    const auto& iter = d_cutStates.find(subdomain);
16,773✔
3789
    if (iter != d_cutStates.cend()) {
16,773✔
3790
      LOG(prefix << name << ": Got status " << iter->second << " for name " << subdomain << endl);
2,939!
3791
      return iter->second;
2,939✔
3792
    }
2,939✔
3793
  }
16,773✔
3794

3795
  /* look for the best match we have */
3796
  DNSName best(subdomain);
13,834✔
3797
  while (best.chopOff()) {
26,424!
3798
    const auto& iter = d_cutStates.find(best);
26,424✔
3799
    if (iter != d_cutStates.cend()) {
26,424✔
3800
      result = iter->second;
13,834✔
3801
      if (vStateIsBogus(result) || result == vState::Insecure) {
13,834✔
3802
        LOG(prefix << name << ": Got status " << result << " for name " << best << endl);
3,036!
3803
        return result;
3,036✔
3804
      }
3,036✔
3805
      break;
10,798✔
3806
    }
13,834✔
3807
  }
26,424✔
3808

3809
  /* by now we have the best match, it's likely Secure (otherwise we would not be there)
3810
     but we don't know if we missed a cut (or several).
3811
     We could see if we have DS (or denial of) in cache but let's not worry for now,
3812
     we will if we don't have a signature, or if the signer doesn't match what we expect */
3813
  if (!wouldBeValid && best != subdomain) {
10,798!
3814
    /* no signatures or Bogus, we likely missed a cut, let's try to find it */
3815
    LOG(prefix << name << ": No or invalid signature/proof for " << name << ", we likely missed a cut between " << best << " and " << subdomain << ", looking for it" << endl);
2,533!
3816
    DNSName dsName(best);
2,533✔
3817
    std::vector<string> labelsToAdd = subdomain.makeRelative(dsName).getRawLabels();
2,533✔
3818

3819
    while (!labelsToAdd.empty()) {
4,474✔
3820

3821
      dsName.prependRawLabel(labelsToAdd.back());
4,413✔
3822
      labelsToAdd.pop_back();
4,413✔
3823
      LOG(prefix << name << ": - Looking for a DS at " << dsName << endl);
4,413!
3824

3825
      bool foundCut = false;
4,413✔
3826
      dsset_t results;
4,413✔
3827
      vState dsState = getDSRecords(dsName, results, false, depth, prefix, false, &foundCut);
4,413✔
3828

3829
      if (foundCut) {
4,413✔
3830
        LOG(prefix << name << ": - Found cut at " << dsName << endl);
4,382!
3831
        LOG(prefix << name << ": New state for " << dsName << " is " << dsState << endl);
4,382!
3832
        d_cutStates[dsName] = dsState;
4,382✔
3833

3834
        if (dsState != vState::Secure) {
4,382✔
3835
          return dsState;
2,472✔
3836
        }
2,472✔
3837
      }
4,382✔
3838
    }
4,413✔
3839

3840
    /* we did not miss a cut, good luck */
3841
    return result;
61✔
3842
  }
2,533✔
3843

3844
#if 0
3845
  /* we don't need this, we actually do the right thing later */
3846
  DNSName signer = getSigner(signatures);
3847

3848
  if (!signer.empty() && name.isPartOf(signer)) {
3849
    if (signer == best) {
3850
      return result;
3851
    }
3852
    /* the zone cut is not the one we expected,
3853
       this is fine because we will retrieve the needed DNSKEYs and DSs
3854
       later, and even go Insecure if we missed a cut to Insecure (no DS)
3855
       and the signatures do not validate (we should not go Bogus in that
3856
       case) */
3857
  }
3858
  /* something is not right, but let's not worry about that for now.. */
3859
#endif
3860

3861
  return result;
8,265✔
3862
}
10,798✔
3863

3864
vState SyncRes::validateDNSKeys(const DNSName& zone, const std::vector<DNSRecord>& dnskeys, const MemRecursorCache::SigRecsVec& signatures, unsigned int depth, const string& prefix)
3865
{
1,541✔
3866
  dsset_t dsSet;
1,541✔
3867
  if (signatures.empty()) {
1,541!
3868
    LOG(prefix << zone << ": We have " << std::to_string(dnskeys.size()) << " DNSKEYs but no signature, going Bogus!" << endl);
×
3869
    return vState::BogusNoRRSIG;
×
3870
  }
×
3871

3872
  DNSName signer = getSigner(signatures);
1,541✔
3873

3874
  if (!signer.empty() && zone.isPartOf(signer)) {
1,541!
3875
    vState state = getDSRecords(signer, dsSet, false, depth, prefix);
1,541✔
3876

3877
    if (state != vState::Secure) {
1,541✔
3878
      return state;
31✔
3879
    }
31✔
3880
  }
1,541✔
3881
  else {
×
3882
    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);
×
3883
    /* try again to get the missed cuts, harder this time */
3884
    auto zState = getValidationStatus(zone, false, false, depth, prefix);
×
3885
    if (zState == vState::Secure) {
×
3886
      /* too bad */
3887
      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);
×
3888
      return vState::BogusNoValidRRSIG;
×
3889
    }
×
3890
    return zState;
×
3891
  }
×
3892

3893
  skeyset_t tentativeKeys;
1,510✔
3894
  sortedRecords_t toSign;
1,510✔
3895

3896
  for (const auto& dnskey : dnskeys) {
1,807✔
3897
    if (dnskey.d_type == QType::DNSKEY) {
1,807!
3898
      auto content = getRR<DNSKEYRecordContent>(dnskey);
1,807✔
3899
      if (content) {
1,807!
3900
        tentativeKeys.insert(content);
1,807✔
3901
        toSign.insert(content);
1,807✔
3902
      }
1,807✔
3903
    }
1,807✔
3904
  }
1,807✔
3905

3906
  LOG(prefix << zone << ": Trying to validate " << std::to_string(tentativeKeys.size()) << " DNSKEYs with " << std::to_string(dsSet.size()) << " DS" << endl);
1,510!
3907
  skeyset_t validatedKeys;
1,510✔
3908
  auto state = validateDNSKeysAgainstDS(d_now.tv_sec, zone, dsSet, tentativeKeys, toSign, signatures, validatedKeys, LogObject(prefix), d_validationContext);
1,510✔
3909

3910
  if (s_maxvalidationsperq != 0 && d_validationContext.d_validationsCounter > s_maxvalidationsperq) {
1,510!
3911
    throw ImmediateServFailException("Server Failure while validating DNSKEYs, too many signature validations for this query");
×
3912
  }
×
3913

3914
  LOG(prefix << zone << ": We now have " << std::to_string(validatedKeys.size()) << " DNSKEYs" << endl);
1,510!
3915

3916
  /* if we found at least one valid RRSIG covering the set,
3917
     all tentative keys are validated keys. Otherwise it means
3918
     we haven't found at least one DNSKEY and a matching RRSIG
3919
     covering this set, this looks Bogus. */
3920
  if (validatedKeys.size() != tentativeKeys.size()) {
1,510✔
3921
    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);
44!
3922
    /* try again to get the missed cuts, harder this time */
3923
    auto zState = getValidationStatus(zone, false, false, depth, prefix);
44✔
3924
    if (zState == vState::Secure) {
44!
3925
      /* too bad */
3926
      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);
44!
3927
      return state;
44✔
3928
    }
44✔
3929
    return zState;
×
3930
  }
44✔
3931

3932
  return state;
1,466✔
3933
}
1,510✔
3934

3935
vState SyncRes::getDNSKeys(const DNSName& signer, skeyset_t& keys, bool& servFailOccurred, unsigned int depth, const string& prefix)
3936
{
5,493✔
3937
  std::vector<DNSRecord> records;
5,493✔
3938
  std::set<GetBestNSAnswer> beenthere;
5,493✔
3939
  LOG(prefix << signer << ": Retrieving DNSKEYs" << endl);
5,493!
3940

3941
  Context context;
5,493✔
3942

3943
  const bool oldCacheOnly = setCacheOnly(false);
5,493✔
3944
  int rcode = doResolve(signer, QType::DNSKEY, records, depth + 1, beenthere, context);
5,493✔
3945
  setCacheOnly(oldCacheOnly);
5,493✔
3946

3947
  if (rcode == RCode::ServFail) {
5,493✔
3948
    servFailOccurred = true;
12✔
3949
    return vState::BogusUnableToGetDNSKEYs;
12✔
3950
  }
12✔
3951

3952
  if (rcode == RCode::NoError) {
5,481✔
3953
    if (context.state == vState::Secure) {
5,475✔
3954
      for (const auto& key : records) {
13,412✔
3955
        if (key.d_type == QType::DNSKEY) {
13,412✔
3956
          auto content = getRR<DNSKEYRecordContent>(key);
8,049✔
3957
          if (content) {
8,049!
3958
            keys.insert(std::move(content));
8,049✔
3959
          }
8,049✔
3960
        }
8,049✔
3961
      }
13,412✔
3962
    }
5,363✔
3963
    LOG(prefix << signer << ": Retrieved " << keys.size() << " DNSKeys, state is " << context.state << endl);
5,475!
3964
    return context.state;
5,475✔
3965
  }
5,475✔
3966

3967
  if (context.state == vState::Insecure) {
6✔
3968
    return context.state;
2✔
3969
  }
2✔
3970

3971
  LOG(prefix << signer << ": Returning Bogus state from " << static_cast<const char*>(__func__) << "(" << signer << ")" << endl);
4!
3972
  return vState::BogusUnableToGetDNSKEYs;
4✔
3973
}
6✔
3974

3975
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 MemRecursorCache::SigRecsVec& signatures)
3976
{
5,555✔
3977
  skeyset_t keys;
5,555✔
3978
  if (signatures.empty()) {
5,555✔
3979
    LOG(prefix << qname << ": Bogus!" << endl);
38!
3980
    return vState::BogusNoRRSIG;
38✔
3981
  }
38✔
3982

3983
  const DNSName signer = getSigner(signatures);
5,517✔
3984
  bool dsFailed = false;
5,517✔
3985
  if (!signer.empty() && name.isPartOf(signer)) {
5,517!
3986
    vState state = vState::Secure;
5,515✔
3987

3988
    if ((qtype == QType::DNSKEY || qtype == QType::DS) && signer == qname) {
5,515✔
3989
      /* we are already retrieving those keys, sorry */
3990
      if (type == QType::DS && signer == name && !signer.isRoot()) {
26!
3991
        /* Unless we are getting the DS of the root zone, we should never see a
3992
           DS (or a denial of a DS) signed by the DS itself, since we should be
3993
           requesting it from the parent zone. Something is very wrong */
3994
        LOG(prefix << qname << ": The DS for " << qname << " is signed by itself" << endl);
2!
3995
        state = vState::BogusSelfSignedDS;
2✔
3996
        dsFailed = true;
2✔
3997
      }
2✔
3998
      else if (qtype == QType::DS && signer == qname && !signer.isRoot()) {
24!
3999
        if (type == QType::SOA || type == QType::NSEC || type == QType::NSEC3) {
6!
4000
          /* if we are trying to validate the DS or more likely NSEC(3)s proving that it does not exist, we have a problem.
4001
             In that case let's go Bogus (we will check later if we missed a cut)
4002
          */
4003
          state = vState::BogusSelfSignedDS;
2✔
4004
          dsFailed = true;
2✔
4005
        }
2✔
4006
        else if (type == QType::CNAME) {
4!
4007
          state = vState::BogusUnableToGetDSs;
4✔
4008
          dsFailed = true;
4✔
4009
        }
4✔
4010
      }
6✔
4011
      else if (qtype == QType::DNSKEY && signer == qname) {
18!
4012
        /* that actually does happen when a server returns NS records in authority
4013
           along with the DNSKEY, leading us to trying to validate the RRSIGs for
4014
           the NS with the DNSKEY that we are about to process. */
4015
        if ((name == signer && type == QType::NSEC) || type == QType::NSEC3) {
14✔
4016
          /* if we are trying to validate the DNSKEY (should not happen here),
4017
             or more likely NSEC(3)s proving that it does not exist, we have a problem.
4018
             In that case let's see if the DS does exist, and if it does let's go Bogus
4019
          */
4020
          dsset_t results;
6✔
4021
          vState dsState = getDSRecords(signer, results, false, depth, prefix, true);
6✔
4022
          if (vStateIsBogus(dsState) || dsState == vState::Insecure) {
6!
4023
            state = dsState;
6✔
4024
            if (vStateIsBogus(dsState)) {
6!
4025
              dsFailed = true;
×
4026
            }
×
4027
          }
6✔
4028
          else {
×
4029
            LOG(prefix << qname << ": Unable to get the DS for " << signer << endl);
×
4030
            state = vState::BogusUnableToGetDNSKEYs;
×
4031
            dsFailed = true;
×
4032
          }
×
4033
        }
6✔
4034
        else {
8✔
4035
          /* return immediately since looking at the cuts is not going to change the
4036
             fact that we are looking at a signature done with the key we are trying to
4037
             obtain */
4038
          LOG(prefix << qname << ": We are looking at a signature done with the key we are trying to obtain " << signer << endl);
8!
4039
          return vState::Indeterminate;
8✔
4040
        }
8✔
4041
      }
14✔
4042
    }
26✔
4043
    bool servFailOccurred = false;
5,507✔
4044
    if (state == vState::Secure) {
5,507✔
4045
      state = getDNSKeys(signer, keys, servFailOccurred, depth, prefix);
5,493✔
4046
    }
5,493✔
4047

4048
    if (state != vState::Secure) {
5,507✔
4049
      if (!vStateIsBogus(state)) {
140✔
4050
        return state;
47✔
4051
      }
47✔
4052
      /* try again to get the missed cuts, harder this time */
4053
      LOG(prefix << signer << ": Checking whether we missed a zone cut for " << signer << " before returning a Bogus state for " << name << "|" << type.toString() << endl);
93!
4054
      auto zState = getValidationStatus(signer, false, dsFailed, depth, prefix);
93✔
4055
      if (zState == vState::Secure) {
93✔
4056
        if (state == vState::BogusUnableToGetDNSKEYs && servFailOccurred) {
75!
4057
          throw ImmediateServFailException("Server Failure while retrieving DNSKEY records for " + signer.toLogString());
8✔
4058
        }
8✔
4059
        /* too bad */
4060
        LOG(prefix << signer << ": We are still in a Secure zone, returning " << vStateToString(state) << endl);
67!
4061
        return state;
67✔
4062
      }
75✔
4063
      return zState;
18✔
4064
    }
93✔
4065
  }
5,507✔
4066

4067
  sortedRecords_t recordcontents;
5,369✔
4068
  for (const auto& record : records) {
5,907✔
4069
    recordcontents.insert(record.getContent());
5,907✔
4070
  }
5,907✔
4071

4072
  LOG(prefix << name << ": Going to validate " << recordcontents.size() << " record contents with " << signatures.size() << " sigs and " << keys.size() << " keys for " << name << "|" << type.toString() << endl);
5,369!
4073
  vState state = validateWithKeySet(d_now.tv_sec, name, recordcontents, signatures, keys, LogObject(prefix), d_validationContext, false);
5,369✔
4074
  if (s_maxvalidationsperq != 0 && d_validationContext.d_validationsCounter > s_maxvalidationsperq) {
5,369✔
4075
    throw ImmediateServFailException("Server Failure while validating records, too many signature validations for this query");
2✔
4076
  }
2✔
4077

4078
  if (state == vState::Secure) {
5,367✔
4079
    LOG(prefix << name << ": Secure!" << endl);
5,335!
4080
    return vState::Secure;
5,335✔
4081
  }
5,335✔
4082

4083
  LOG(prefix << vStateToString(state) << "!" << endl);
32!
4084

4085
  bool skipThisLevelWhenLookingForMissedCuts = false;
32✔
4086
  if (name == qname && qtype == QType::DS && (type == QType::NSEC || type == QType::NSEC3)) {
32!
4087
    /* so we have a NSEC(3) record likely proving that the DS we were looking for does not exist,
4088
       but we cannot validate it:
4089
       - if there actually is a cut at this level, we will not be able to validate it anyway
4090
       - if there is no cut at this level, the only thing that can save us is a cut above
4091
    */
4092
    LOG(prefix << name << ": We are trying to validate a " << type << " record for " << name << " likely proving that the DS we were initially looking for (" << qname << ") does not exist, no need to check a zone cut at this exact level" << endl);
2!
4093
    skipThisLevelWhenLookingForMissedCuts = true;
2✔
4094
  }
2✔
4095

4096
  /* try again to get the missed cuts, harder this time */
4097
  auto zState = getValidationStatus(name, false, type == QType::DS || skipThisLevelWhenLookingForMissedCuts, depth, prefix);
32✔
4098
  LOG(prefix << name << ": Checking whether we missed a zone cut before returning a Bogus state" << endl);
32!
4099
  if (zState == vState::Secure) {
32✔
4100
    /* too bad */
4101
    LOG(prefix << name << ": We are still in a Secure zone, returning " << vStateToString(state) << endl);
24!
4102
    return state;
24✔
4103
  }
24✔
4104
  return zState;
8✔
4105
}
32✔
4106

4107
/* This function will check whether the answer should have the AA bit set, and will set if it should be set and isn't.
4108
   This is unfortunately needed to deal with very crappy so-called DNS servers */
4109
void SyncRes::fixupAnswer(const std::string& prefix, LWResult& lwr, const DNSName& qname, const QType qtype, const DNSName& auth, bool wasForwarded, bool rdQuery)
4110
{
12,720✔
4111
  const bool wasForwardRecurse = wasForwarded && rdQuery;
12,720✔
4112

4113
  if (wasForwardRecurse || lwr.d_aabit) {
12,720✔
4114
    /* easy */
4115
    return;
9,062✔
4116
  }
9,062✔
4117

4118
  for (const auto& rec : lwr.d_records) {
3,658✔
4119

4120
    if (rec.d_type == QType::OPT) {
3,606!
4121
      continue;
×
4122
    }
×
4123

4124
    if (rec.d_class != QClass::IN) {
3,606!
4125
      continue;
×
4126
    }
×
4127

4128
    if (rec.d_type == QType::ANY) {
3,606!
4129
      continue;
×
4130
    }
×
4131

4132
    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)) {
3,606!
4133
      /* This is clearly an answer to the question we were asking, from an authoritative server that is allowed to send it.
4134
         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 */
4135
      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);
10!
4136
      lwr.d_aabit = true;
10✔
4137
      return;
10✔
4138
    }
10✔
4139

4140
    if (rec.d_place != DNSResourceRecord::ANSWER) {
3,596!
4141
      /* we have scanned all the records in the answer section, if any, we are done */
4142
      return;
3,596✔
4143
    }
3,596✔
4144
  }
3,596✔
4145
}
3,658✔
4146

4147
static void allowAdditionalEntry(std::unordered_set<DNSName>& allowedAdditionals, const DNSRecord& rec)
4148
{
28,931✔
4149
  // As we only use a limited amount of NS names for resolving, limit number of additional names as
4150
  // well.  s_maxnsperresolve is a proper limit for the NS case and is also reasonable for other
4151
  // qtypes.  Allow one extra for qname itself, which is always in allowedAdditionals.
4152
  if (SyncRes::s_maxnsperresolve > 0 && allowedAdditionals.size() > SyncRes::s_maxnsperresolve + 1) {
28,931!
4153
    return;
12✔
4154
  }
12✔
4155
  switch (rec.d_type) {
28,919✔
4156
  case QType::MX:
25✔
4157
    if (auto mxContent = getRR<MXRecordContent>(rec)) {
25!
4158
      allowedAdditionals.insert(mxContent->d_mxname);
25✔
4159
    }
25✔
4160
    break;
25✔
4161
  case QType::NS:
15,251✔
4162
    if (auto nsContent = getRR<NSRecordContent>(rec)) {
15,251!
4163
      allowedAdditionals.insert(nsContent->getNS());
15,251✔
4164
    }
15,251✔
4165
    break;
15,251✔
4166
  case QType::SRV:
5✔
4167
    if (auto srvContent = getRR<SRVRecordContent>(rec)) {
5!
4168
      allowedAdditionals.insert(srvContent->d_target);
5✔
4169
    }
5✔
4170
    break;
5✔
4171
  case QType::SVCB: /* fall-through */
×
4172
  case QType::HTTPS:
×
4173
    if (auto svcbContent = getRR<SVCBBaseRecordContent>(rec)) {
×
4174
      if (svcbContent->getPriority() > 0) {
×
4175
        DNSName target = svcbContent->getTarget();
×
4176
        if (target.isRoot()) {
×
4177
          target = rec.d_name;
×
4178
        }
×
4179
        allowedAdditionals.insert(std::move(target));
×
4180
      }
×
4181
      else {
×
4182
        // FIXME: Alias mode not implemented yet
4183
      }
×
4184
    }
×
4185
    break;
×
4186
  case QType::NAPTR:
6✔
4187
    if (auto naptrContent = getRR<NAPTRRecordContent>(rec)) {
6!
4188
      auto flags = naptrContent->getFlags();
6✔
4189
      toLowerInPlace(flags);
6✔
4190
      if (flags.find('a') != string::npos || flags.find('s') != string::npos) {
6!
4191
        allowedAdditionals.insert(naptrContent->getReplacement());
6✔
4192
      }
6✔
4193
    }
6✔
4194
    break;
6✔
4195
  default:
13,632✔
4196
    break;
13,632✔
4197
  }
28,919✔
4198
}
28,919✔
4199

4200
static bool isRedirection(QType qtype)
4201
{
20,796✔
4202
  return qtype == QType::CNAME || qtype == QType::DNAME;
20,796✔
4203
}
20,796✔
4204

4205
void SyncRes::sanitizeRecords(const std::string& prefix, LWResult& lwr, const DNSName& qname, const QType qtype, const DNSName& auth, bool wasForwarded, bool rdQuery)
4206
{
12,720✔
4207
  const bool wasForwardRecurse = wasForwarded && rdQuery;
12,720✔
4208
  /* list of names for which we will allow A and AAAA records in the additional section
4209
     to remain */
4210
  std::unordered_set<DNSName> allowedAdditionals = {qname};
12,720✔
4211
  std::unordered_set<DNSName> allowedAnswerNames = {qname};
12,720✔
4212
  bool cnameSeen = false;
12,720✔
4213
  bool haveAnswers = false;
12,720✔
4214
  bool acceptDelegation = false;
12,720✔
4215
  bool soaInAuth = false;
12,720✔
4216

4217
  std::vector<bool> skipvec(lwr.d_records.size(), false);
12,720✔
4218
  unsigned int counter = 0;
12,720✔
4219
  unsigned int skipCount = 0;
12,720✔
4220

4221
  for (auto rec = lwr.d_records.cbegin(); rec != lwr.d_records.cend(); ++rec, ++counter) {
85,543✔
4222

4223
    // Allow OPT record containing EDNS(0) data
4224
    if (rec->d_type == QType::OPT) {
72,823✔
4225
      continue;
10,374✔
4226
    }
10,374✔
4227

4228
    // Disallow QClass != IN
4229
    if (rec->d_class != QClass::IN) {
62,449!
4230
      LOG(prefix << qname << ": Removing non internet-classed data received from " << auth << endl);
×
4231
      skipvec[counter] = true;
×
4232
      ++skipCount;
×
4233
      continue;
×
4234
    }
×
4235

4236
    // Disallow QType ANY in responses
4237
    if (rec->d_type == QType::ANY) {
62,449✔
4238
      LOG(prefix << qname << ": Removing 'ANY'-typed data received from " << auth << endl);
2!
4239
      skipvec[counter] = true;
2✔
4240
      ++skipCount;
2✔
4241
      continue;
2✔
4242
    }
2✔
4243

4244
    // Disallow any name not part of auth requested (i.e. disallow x.y.z if asking a NS authoritative for x.w.z)
4245
    if (!rec->d_name.isPartOf(auth)) {
62,447✔
4246
      LOG(prefix << qname << ": Removing record '" << rec->toString() << "' in the " << DNSResourceRecord::placeString(rec->d_place) << " section received from " << auth << endl);
462!
4247
      skipvec[counter] = true;
462✔
4248
      ++skipCount;
462✔
4249
      continue;
462✔
4250
    }
462✔
4251

4252
    // Disallow QType DNAME in non-answer section or containing an answer that is not a parent of or equal to the question name
4253
    // i.e. disallowed bar.example.com. DNAME bar.example.net. when asking foo.example.com
4254
    // But allow it when asking for foo.bar.example.com.
4255
    if (rec->d_type == QType::DNAME && (rec->d_place != DNSResourceRecord::ANSWER || !qname.isPartOf(rec->d_name))) {
61,985!
4256
      LOG(prefix << qname << ": Removing invalid DNAME record '" << rec->toString() << "' in the " << DNSResourceRecord::placeString(rec->d_place) << " section received from " << auth << endl);
2!
4257
      skipvec[counter] = true;
2✔
4258
      ++skipCount;
2✔
4259
      continue;
2✔
4260
    }
2✔
4261

4262
    /* dealing with the records in answer */
4263
    if (rec->d_place == DNSResourceRecord::ANSWER) {
61,983✔
4264
      // Special case for Amazon CNAME records
4265
      if (!(lwr.d_aabit || wasForwardRecurse)) {
14,690!
4266
        /* for now we allow a CNAME for the exact qname in ANSWER with AA=0, because Amazon DNS servers
4267
           are sending such responses */
4268
        if (rec->d_type != QType::CNAME || qname != rec->d_name) {
×
4269
          LOG(prefix << qname << ": Removing record '" << rec->toString() << "' in the ANSWER section without the AA bit set received from " << auth << endl);
×
4270
          skipvec[counter] = true;
×
4271
          ++skipCount;
×
4272
          continue;
×
4273
        }
×
4274
      }
×
4275
      // Disallow answer records not answering the QType requested. ANY, CNAME, DNAME, RRSIG complicate matters here
4276
      if (qtype != QType::ANY && rec->d_type != qtype.getCode() && !isRedirection(rec->d_type) && rec->d_type != QType::RRSIG) {
14,690✔
4277
        LOG(prefix << qname << ": Removing irrelevant record '" << rec->toString() << "' in the ANSWER section received from " << auth << endl);
6!
4278
        skipvec[counter] = true;
6✔
4279
        ++skipCount;
6✔
4280
        continue;
6✔
4281
      }
6✔
4282

4283
      haveAnswers = true;
14,684✔
4284
      if (rec->d_type == QType::CNAME) {
14,684✔
4285
        if (auto cnametarget = getRR<CNAMERecordContent>(*rec); cnametarget != nullptr) {
1,572!
4286
          allowedAnswerNames.insert(cnametarget->getTarget());
1,572✔
4287
        }
1,572✔
4288
        cnameSeen = cnameSeen || qname == rec->d_name;
1,572✔
4289
      }
1,572✔
4290
      else if (rec->d_type == QType::DNAME) {
13,112✔
4291
        // We have checked the DNAME rec->d_name above, the actual answer will be synthesized in a later step
4292
        allowedAnswerNames.insert(rec->d_name);
32✔
4293
      }
32✔
4294
      allowAdditionalEntry(allowedAdditionals, *rec);
14,684✔
4295
    }
14,684✔
4296

4297
    /* dealing with the records in authority */
4298
    // Only allow NS, DS, SOA, RRSIG, NSEC, NSEC3 in AUTHORITY section
4299
    else if (rec->d_place == DNSResourceRecord::AUTHORITY) {
47,293✔
4300
      if (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) {
28,595✔
4301
        LOG(prefix << qname << ": Removing irrelevant record '" << rec->toString() << "' in the AUTHORITY section received from " << auth << endl);
2!
4302
        skipvec[counter] = true;
2✔
4303
        ++skipCount;
2✔
4304
        continue;
2✔
4305
      }
2✔
4306
      if (rec->d_type == QType::NS && (!rec->d_name.isPartOf(auth) || (rec->d_name == auth && !d_updatingRootNS) || !qname.isPartOf(rec->d_name))) {
28,593!
4307
        /*
4308
         * We don't want to pick up irrelevant NS records in AUTHORITY and their associated ADDITIONAL sections.
4309
         * So remove them and don't add them to allowedAdditionals.
4310
         */
4311
        LOG(prefix << qname << ": Removing NS record '" << rec->toString() << "' in the AUTHORITY section of a response received from " << auth << endl);
1,200!
4312
        skipvec[counter] = true;
1,200✔
4313
        ++skipCount;
1,200✔
4314
        continue;
1,200✔
4315
      }
1,200✔
4316

4317
      if (rec->d_type == QType::SOA) {
27,393✔
4318
        // Disallow a SOA record with a name that is not a parent of or equal to the name we asked
4319
        if (!qname.isPartOf(rec->d_name)) {
1,547✔
4320
          LOG(prefix << qname << ": Removing irrelevant SOA record '" << rec->toString() << "' in the AUTHORITY section received from " << auth << endl);
2!
4321
          skipvec[counter] = true;
2✔
4322
          ++skipCount;
2✔
4323
          continue;
2✔
4324
        }
2✔
4325
        // Disallow SOA without AA bit (except for forward with RD=1)
4326
        if (!(lwr.d_aabit || wasForwardRecurse)) {
1,545!
4327
          LOG(prefix << qname << ": Removing irrelevant record (AA not set) '" << rec->toString() << "' in the AUTHORITY section received from " << auth << endl);
×
4328
          skipvec[counter] = true;
×
4329
          ++skipCount;
×
4330
          continue;
×
4331
        }
×
4332
        soaInAuth = true;
1,545✔
4333
      }
1,545✔
4334
    }
27,393✔
4335
    /* dealing with records in additional */
4336
    else if (rec->d_place == DNSResourceRecord::ADDITIONAL) {
18,699✔
4337
      if (rec->d_type != QType::A && rec->d_type != QType::AAAA && rec->d_type != QType::RRSIG) {
18,699✔
4338
        LOG(prefix << qname << ": Removing irrelevant record '" << rec->toString() << "' in the ADDITIONAL section received from " << auth << endl);
2!
4339
        skipvec[counter] = true;
2✔
4340
        ++skipCount;
2✔
4341
        continue;
2✔
4342
      }
2✔
4343
    }
18,699✔
4344
  } // end of first loop, handled answer and most of authority section
61,983✔
4345

4346
  if (!haveAnswers && lwr.d_rcode == RCode::NoError) {
12,720✔
4347
    acceptDelegation = true;
5,115✔
4348
  }
5,115✔
4349

4350
  sanitizeRecordsPass2(prefix, lwr, qname, qtype, auth, allowedAnswerNames, allowedAdditionals, cnameSeen, acceptDelegation && !soaInAuth, skipvec, skipCount);
12,720✔
4351
}
12,720✔
4352

4353
void SyncRes::sanitizeRecordsPass2(const std::string& prefix, LWResult& lwr, const DNSName& qname, const QType qtype, const DNSName& auth, std::unordered_set<DNSName>& allowedAnswerNames, std::unordered_set<DNSName>& allowedAdditionals, bool cnameSeen, bool acceptDelegation, std::vector<bool>& skipvec, unsigned int& skipCount)
4354
{
12,720✔
4355
  // Second loop, we know now if the answer was NxDomain or NoData
4356
  unsigned int counter = 0;
12,720✔
4357
  for (auto rec = lwr.d_records.cbegin(); rec != lwr.d_records.cend(); ++rec, ++counter) {
85,544✔
4358

4359
    if (skipvec[counter]) {
72,824✔
4360
      continue;
1,678✔
4361
    }
1,678✔
4362
    // Allow OPT record containing EDNS(0) data
4363
    if (rec->d_type == QType::OPT) {
71,146✔
4364
      continue;
10,374✔
4365
    }
10,374✔
4366

4367
    if (rec->d_place == DNSResourceRecord::ANSWER) {
60,772✔
4368
      if (allowedAnswerNames.count(rec->d_name) == 0) {
14,684✔
4369
        LOG(prefix << qname << ": Removing irrelevent record '" << rec->toString() << "' in the ANSWER section received from " << auth << endl);
10!
4370
        skipvec[counter] = true;
10✔
4371
        ++skipCount;
10✔
4372
      }
10✔
4373
      // If we have a CNAME, skip answer records for the requested type
4374
      if (cnameSeen && rec->d_type == qtype && rec->d_name == qname && qtype != QType::CNAME) {
14,684!
4375
        LOG(prefix << qname << ": Removing answer record in presence of CNAME record '" << rec->toString() << "' in the ANSWER section received from " << auth << endl);
6!
4376
        skipvec[counter] = true;
6✔
4377
        ++skipCount;
6✔
4378
        continue;
6✔
4379
      }
6✔
4380
    }
14,684✔
4381
    if (rec->d_place == DNSResourceRecord::AUTHORITY && rec->d_type == QType::NS) {
60,766✔
4382
      if (!acceptDelegation) {
14,364✔
4383
        /*
4384
         * We don't want to pick up NS records in AUTHORITY and their ADDITIONAL sections of NXDomain answers and answers with answer records
4385
         * because they are somewhat easy to insert into a large, fragmented UDP response
4386
         * for an off-path attacker by injecting spoofed UDP fragments. So do not add these to allowedAdditionals.
4387
         */
4388
        LOG(prefix << qname << ": Removing NS record '" << rec->toString() << "' in the AUTHORITY section of a response received from " << auth << endl);
137!
4389
        skipvec[counter] = true;
137✔
4390
        ++skipCount;
137✔
4391
        continue;
137✔
4392
      }
137✔
4393
      allowAdditionalEntry(allowedAdditionals, *rec);
14,227✔
4394
    }
14,227✔
4395
    /* dealing with the records in additional */
4396
    else if (rec->d_place == DNSResourceRecord::ADDITIONAL) {
46,402✔
4397
      if (allowedAdditionals.count(rec->d_name) == 0) {
18,697✔
4398
        LOG(prefix << qname << ": Removing irrelevant record '" << rec->toString() << "' in the ADDITIONAL section received from " << auth << endl);
652!
4399
        skipvec[counter] = true;
652✔
4400
        ++skipCount;
652✔
4401
        continue;
652✔
4402
      }
652✔
4403
    }
18,697✔
4404
  }
60,766✔
4405
  if (skipCount > 0) {
12,720✔
4406
    std::vector<DNSRecord> vec;
361✔
4407
    vec.reserve(lwr.d_records.size() - skipCount);
361✔
4408
    for (counter = 0; counter < lwr.d_records.size(); ++counter) {
3,872✔
4409
      if (!skipvec[counter]) {
3,511✔
4410
        vec.emplace_back(std::move(lwr.d_records[counter]));
1,028✔
4411
      }
1,028✔
4412
    }
3,511✔
4413
    lwr.d_records = std::move(vec);
361✔
4414
  }
361✔
4415
#ifdef notyet
4416
  // As dedupping is relatively expensive and having dup records not really hurts as far as we have seen, do not dedup.
4417
  if (auto count = pdns::dedupRecords(lwr.d_records); count > 0) {
4418
    LOG(prefix << qname << ": Removed " << count << " duplicate records from response received from " << auth << endl);
4419
  }
4420
#endif
4421
}
12,720✔
4422

4423
void SyncRes::rememberParentSetIfNeeded(const DNSName& domain, const vector<DNSRecord>& newRecords, unsigned int depth, const string& prefix)
4424
{
255✔
4425
  vector<DNSRecord> existing;
255✔
4426
  bool wasAuth = false;
255✔
4427
  auto ttl = g_recCache->get(d_now.tv_sec, domain, QType::NS, MemRecursorCache::None, &existing, d_cacheRemote, d_routingTag, nullptr, nullptr, nullptr, nullptr, &wasAuth);
255✔
4428

4429
  if (ttl <= 0 || wasAuth) {
255✔
4430
    return;
12✔
4431
  }
12✔
4432
  {
243✔
4433
    auto lock = s_savedParentNSSet.lock();
243✔
4434
    if (lock->find(domain) != lock->end()) {
243!
4435
      // no relevant data, or we already stored the parent data
4436
      return;
×
4437
    }
×
4438
  }
243✔
4439

4440
  set<DNSName> authSet;
243✔
4441
  for (const auto& dnsRecord : newRecords) {
1,008✔
4442
    auto content = getRR<NSRecordContent>(dnsRecord);
1,008✔
4443
    authSet.insert(content->getNS());
1,008✔
4444
  }
1,008✔
4445
  // The glue IPs could also differ, but we're not checking that yet, we're only looking for parent NS records not
4446
  // in the child set
4447
  bool shouldSave = false;
243✔
4448
  for (const auto& dnsRecord : existing) {
1,008✔
4449
    auto content = getRR<NSRecordContent>(dnsRecord);
1,008✔
4450
    if (authSet.count(content->getNS()) == 0) {
1,008✔
4451
      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);
4!
4452
      shouldSave = true;
4✔
4453
      break;
4✔
4454
    }
4✔
4455
  }
1,008✔
4456

4457
  if (shouldSave) {
243✔
4458
    map<DNSName, vector<ComboAddress>> entries;
4✔
4459
    for (const auto& dnsRecord : existing) {
30✔
4460
      auto content = getRR<NSRecordContent>(dnsRecord);
30✔
4461
      const DNSName& name = content->getNS();
30✔
4462
      set<GetBestNSAnswer> beenthereIgnored;
30✔
4463
      unsigned int nretrieveAddressesForNSIgnored{};
30✔
4464
      auto addresses = getAddrs(name, depth, prefix, beenthereIgnored, true, nretrieveAddressesForNSIgnored);
30✔
4465
      entries.emplace(name, addresses);
30✔
4466
    }
30✔
4467
    s_savedParentNSSet.lock()->emplace(domain, std::move(entries), d_now.tv_sec + ttl);
4✔
4468
  }
4✔
4469
}
243✔
4470

4471
RCode::rcodes_ SyncRes::updateCacheFromRecords(unsigned int depth, const string& prefix, LWResult& lwr, const DNSName& qname, const QType qtype, const DNSName& auth, bool wasForwarded, const std::optional<Netmask>& ednsmask, vState& state, bool& needWildcardProof, bool& gatherWildcardProof, unsigned int& wildcardLabelsCount, bool rdQuery, const ComboAddress& remoteIP, bool overTCP) // NOLINT(readability-function-cognitive-complexity)
4472
{
12,720✔
4473
  bool wasForwardRecurse = wasForwarded && rdQuery;
12,720✔
4474
  tcache_t tcache;
12,720✔
4475

4476
  fixupAnswer(prefix, lwr, qname, qtype, auth, wasForwarded, rdQuery);
12,720✔
4477
  sanitizeRecords(prefix, lwr, qname, qtype, auth, wasForwarded, rdQuery);
12,720✔
4478

4479
  MemRecursorCache::AuthRecsVec authorityRecs;
12,720✔
4480
  bool isCNAMEAnswer = false;
12,720✔
4481
  bool isDNAMEAnswer = false;
12,720✔
4482
  DNSName seenAuth;
12,720✔
4483

4484
  // names that might be expanded from a wildcard, and thus require denial of existence proof
4485
  // this is the queried name and any part of the CNAME chain from the queried name
4486
  // the key is the name itself, the value is initially false and is set to true once we have
4487
  // confirmed it was actually expanded from a wildcard
4488
  std::map<DNSName, bool> wildcardCandidates{{qname, false}};
12,720✔
4489

4490
  if (rdQuery) {
12,720✔
4491
    std::unordered_map<DNSName, DNSName> cnames;
66✔
4492
    for (const auto& rec : lwr.d_records) {
169✔
4493
      if (rec.d_type != QType::CNAME || rec.d_class != QClass::IN) {
169!
4494
        continue;
157✔
4495
      }
157✔
4496
      if (auto content = getRR<CNAMERecordContent>(rec)) {
12!
4497
        cnames[rec.d_name] = DNSName(content->getTarget());
12✔
4498
      }
12✔
4499
    }
12✔
4500
    auto initial = qname;
66✔
4501
    while (true) {
76✔
4502
      auto cnameIt = cnames.find(initial);
76✔
4503
      if (cnameIt == cnames.end()) {
76✔
4504
        break;
64✔
4505
      }
64✔
4506
      initial = cnameIt->second;
12✔
4507
      if (!wildcardCandidates.emplace(initial, false).second) {
12✔
4508
        // CNAME Loop
4509
        break;
2✔
4510
      }
2✔
4511
    }
12✔
4512
  }
66✔
4513

4514
  for (auto& rec : lwr.d_records) {
70,334✔
4515
    if (rec.d_type == QType::OPT || rec.d_class != QClass::IN) {
70,334✔
4516
      continue;
10,374✔
4517
    }
10,374✔
4518

4519
    rec.d_ttl = min(s_maxcachettl, rec.d_ttl);
59,960✔
4520

4521
    if (!isCNAMEAnswer && rec.d_place == DNSResourceRecord::ANSWER && rec.d_type == QType::CNAME && (!(qtype == QType::CNAME)) && rec.d_name == qname && !isDNAMEAnswer) {
59,960!
4522
      isCNAMEAnswer = true;
1,553✔
4523
    }
1,553✔
4524
    if (!isDNAMEAnswer && rec.d_place == DNSResourceRecord::ANSWER && rec.d_type == QType::DNAME && qtype != QType::DNAME && qname.isPartOf(rec.d_name)) {
59,966!
4525
      isDNAMEAnswer = true;
32✔
4526
      isCNAMEAnswer = false;
32✔
4527
    }
32✔
4528

4529
    if (rec.d_type == QType::SOA && rec.d_place == DNSResourceRecord::AUTHORITY && qname.isPartOf(rec.d_name)) {
59,960!
4530
      seenAuth = rec.d_name;
1,545✔
4531
    }
1,545✔
4532

4533
    const auto labelCount = rec.d_name.countLabels();
59,960✔
4534
    if (rec.d_type == QType::RRSIG) {
59,960✔
4535
      auto rrsig = getRR<RRSIGRecordContent>(rec);
9,021✔
4536
      if (rrsig) {
9,022✔
4537
        /* As illustrated in rfc4035's Appendix B.6, the RRSIG label
4538
           count can be lower than the name's label count if it was
4539
           synthesized from the wildcard. Note that the difference might
4540
           be > 1. */
4541
        if (auto wcIt = wildcardCandidates.find(rec.d_name); wcIt != wildcardCandidates.end() && isWildcardExpanded(labelCount, *rrsig)) {
9,021✔
4542
          wcIt->second = true;
635✔
4543
          gatherWildcardProof = true;
635✔
4544
          if (!isWildcardExpandedOntoItself(rec.d_name, labelCount, *rrsig)) {
635✔
4545
            /* if we have a wildcard expanded onto itself, we don't need to prove
4546
               that the exact name doesn't exist because it actually does.
4547
               We still want to gather the corresponding NSEC/NSEC3 records
4548
               to pass them to our client in case it wants to validate by itself.
4549
            */
4550
            LOG(prefix << qname << ": RRSIG indicates the name was synthesized from a wildcard, we need a wildcard proof" << endl);
627!
4551
            needWildcardProof = true;
627✔
4552
          }
627✔
4553
          else {
8✔
4554
            LOG(prefix << qname << ": RRSIG indicates the name was synthesized from a wildcard expanded onto itself, we need to gather wildcard proof" << endl);
8!
4555
          }
8✔
4556
          wildcardLabelsCount = rrsig->d_labels;
635✔
4557
        }
635✔
4558

4559
        // cerr<<"Got an RRSIG for "<<DNSRecordContent::NumberToType(rrsig->d_type)<<" with name '"<<rec.d_name<<"' and place "<<rec.d_place<<endl;
4560
        tcache[{rec.d_name, rrsig->d_type, rec.d_place}].signatures.push_back(rrsig);
9,021✔
4561
        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);
9,021✔
4562
      }
9,021✔
4563
    }
9,021✔
4564
  }
59,960✔
4565

4566
  /* if we have a positive answer synthesized from a wildcard,
4567
     we need to store the corresponding NSEC/NSEC3 records proving
4568
     that the exact name did not exist in the negative cache */
4569
  if (gatherWildcardProof) {
12,720✔
4570
    for (const auto& rec : lwr.d_records) {
3,145✔
4571
      if (rec.d_type == QType::OPT || rec.d_class != QClass::IN) {
3,145!
4572
        continue;
603✔
4573
      }
603✔
4574

4575
      if (nsecTypes.count(rec.d_type) != 0) {
2,542✔
4576
        authorityRecs.emplace_back(rec);
633✔
4577
      }
633✔
4578
      else if (rec.d_type == QType::RRSIG) {
1,909✔
4579
        auto rrsig = getRR<RRSIGRecordContent>(rec);
1,270✔
4580
        if (rrsig && nsecTypes.count(rrsig->d_type) != 0) {
1,270!
4581
          authorityRecs.emplace_back(rec);
633✔
4582
        }
633✔
4583
      }
1,270✔
4584
    }
2,542✔
4585
  }
631✔
4586

4587
  // reap all answers from this packet that are acceptable
4588
  for (auto& rec : lwr.d_records) {
70,341✔
4589
    if (rec.d_type == QType::OPT) {
70,341✔
4590
      LOG(prefix << qname << ": OPT answer '" << rec.d_name << "' from '" << auth << "' nameservers" << endl);
10,374!
4591
      continue;
10,374✔
4592
    }
10,374✔
4593

4594
    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 << " ");
59,967✔
4595

4596
    // We called sanitizeRecords before, so all ANY, non-IN and non-aa/non-forwardrecurse answer records are already removed
4597

4598
    if (rec.d_name.isPartOf(auth)) {
59,968✔
4599
      if (rec.d_type == QType::RRSIG) {
59,967✔
4600
        LOG("RRSIG - separate" << endl);
9,022!
4601
      }
9,022✔
4602
      else if (rec.d_type == QType::DS && rec.d_name == auth) {
50,945✔
4603
        LOG("NO - DS provided by child zone" << endl);
2!
4604
      }
2✔
4605
      else {
50,943✔
4606
        bool haveLogged = false;
50,943✔
4607
        if (isDNAMEAnswer && rec.d_type == QType::CNAME) {
50,943✔
4608
          LOG("NO - we already have a DNAME answer for this domain" << endl);
30!
4609
          continue;
30✔
4610
        }
30✔
4611
        if (!t_sstorage.domainmap->empty()) {
50,914✔
4612
          // Check if we are authoritative for a zone in this answer
4613
          DNSName tmp_qname(rec.d_name);
46,553✔
4614
          // We may be auth for domain example.com, but the DS record needs to come from the parent (.com) nameserver
4615
          if (rec.d_type == QType::DS) {
46,553✔
4616
            tmp_qname.chopOff();
922✔
4617
          }
922✔
4618
          auto auth_domain_iter = getBestAuthZone(&tmp_qname);
46,553✔
4619
          if (auth_domain_iter != t_sstorage.domainmap->end() && auth.countLabels() <= auth_domain_iter->first.countLabels()) {
46,553✔
4620
            if (auth_domain_iter->first != auth) {
365✔
4621
              LOG("NO! - we are authoritative for the zone " << auth_domain_iter->first << endl);
2!
4622
              continue;
2✔
4623
            }
2✔
4624
            LOG("YES! - This answer was ");
363!
4625
            if (!wasForwarded) {
363✔
4626
              LOG("retrieved from the local auth store.");
139!
4627
            }
139✔
4628
            else {
224✔
4629
              LOG("received from a server we forward to.");
224!
4630
            }
224✔
4631
            haveLogged = true;
363✔
4632
            LOG(endl);
363!
4633
          }
363✔
4634
        }
46,553✔
4635
        if (!haveLogged) {
50,912✔
4636
          LOG("YES!" << endl);
50,549✔
4637
        }
50,549✔
4638

4639
        rec.d_ttl = min(s_maxcachettl, rec.d_ttl);
50,911✔
4640

4641
        DNSRecord dnsRecord(rec);
50,911✔
4642
        tcache[{rec.d_name, rec.d_type, rec.d_place}].d_ttl_time = d_now.tv_sec;
50,911✔
4643
        dnsRecord.d_ttl += d_now.tv_sec;
50,911✔
4644
        dnsRecord.d_place = DNSResourceRecord::ANSWER;
50,911✔
4645
        tcache[{rec.d_name, rec.d_type, rec.d_place}].records.push_back(std::move(dnsRecord));
50,911✔
4646
      }
50,911✔
4647
    }
59,967✔
4648
    else
2,147,483,647✔
4649
      LOG("NO!" << endl);
2,147,498,623!
4650
  }
59,935✔
4651

4652
  // supplant
4653
  for (auto& entry : tcache) {
36,179✔
4654
    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)
36,179✔
4655
      uint32_t lowestTTD = computeLowestTTD(entry.second.records, entry.second.signatures, entry.second.signaturesTTL, authorityRecs);
12,679✔
4656

4657
      for (auto& record : entry.second.records) {
27,414✔
4658
        record.d_ttl = lowestTTD; // boom
27,414✔
4659
      }
27,414✔
4660
    }
12,679✔
4661
  }
36,179✔
4662

4663
  bool seenBogusRRSet = false;
12,720✔
4664
  for (auto tCacheEntry = tcache.begin(); tCacheEntry != tcache.end(); ++tCacheEntry) {
48,896✔
4665

4666
    if (tCacheEntry->second.records.empty()) { // this happens when we did store signatures, but passed on the records themselves
36,176✔
4667
      continue;
3✔
4668
    }
3✔
4669

4670
    /* Even if the AA bit is set, additional data cannot be considered
4671
       as authoritative. This is especially important during validation
4672
       because keeping records in the additional section is allowed even
4673
       if the corresponding RRSIGs are not included, without setting the TC
4674
       bit, as stated in rfc4035's section 3.1.1.  Including RRSIG RRs in a Response:
4675
       "When placing a signed RRset in the Additional section, the name
4676
       server MUST also place its RRSIG RRs in the Additional section.
4677
       If space does not permit inclusion of both the RRset and its
4678
       associated RRSIG RRs, the name server MAY retain the RRset while
4679
       dropping the RRSIG RRs.  If this happens, the name server MUST NOT
4680
       set the TC bit solely because these RRSIG RRs didn't fit."
4681
    */
4682
    bool isAA = lwr.d_aabit && tCacheEntry->first.place != DNSResourceRecord::ADDITIONAL;
36,173✔
4683
    /* if we forwarded the query to a recursor, we can expect the answer to be signed,
4684
       even if the answer is not AA. Of course that's not only true inside a Secure
4685
       zone, but we check that below. */
4686
    bool expectSignature = tCacheEntry->first.place == DNSResourceRecord::ANSWER || ((lwr.d_aabit || wasForwardRecurse) && tCacheEntry->first.place != DNSResourceRecord::ADDITIONAL);
36,173✔
4687
    /* in a non authoritative answer, we only care about the DS record (or lack of)  */
4688
    if (!isAA && (tCacheEntry->first.type == QType::DS || tCacheEntry->first.type == QType::NSEC || tCacheEntry->first.type == QType::NSEC3) && tCacheEntry->first.place == DNSResourceRecord::AUTHORITY) {
36,173✔
4689
      expectSignature = true;
4,554✔
4690
    }
4,554✔
4691

4692
    if (isCNAMEAnswer && (tCacheEntry->first.place != DNSResourceRecord::ANSWER || tCacheEntry->first.type != QType::CNAME || tCacheEntry->first.name != qname)) {
36,173✔
4693
      /*
4694
        rfc2181 states:
4695
        Note that the answer section of an authoritative answer normally
4696
        contains only authoritative data.  However when the name sought is an
4697
        alias (see section 10.1.1) only the record describing that alias is
4698
        necessarily authoritative.  Clients should assume that other records
4699
        may have come from the server's cache.  Where authoritative answers
4700
        are required, the client should query again, using the canonical name
4701
        associated with the alias.
4702
      */
4703
      isAA = false;
332✔
4704
      expectSignature = false;
332✔
4705
    }
332✔
4706
    if (isDNAMEAnswer && (tCacheEntry->first.place != DNSResourceRecord::ANSWER || tCacheEntry->first.type != QType::DNAME || !qname.isPartOf(tCacheEntry->first.name))) {
36,173!
4707
      /* see above */
4708
      isAA = false;
2✔
4709
      expectSignature = false;
2✔
4710
    }
2✔
4711

4712
    if ((isCNAMEAnswer || isDNAMEAnswer) && tCacheEntry->first.place == DNSResourceRecord::AUTHORITY && tCacheEntry->first.type == QType::NS && auth == tCacheEntry->first.name) {
36,173!
4713
      /* These NS can't be authoritative since we have a CNAME/DNAME answer for which (see above) only the
4714
         record describing that alias is necessarily authoritative.
4715
         But if we allow the current auth, which might be serving the child zone, to raise the TTL
4716
         of non-authoritative NS in the cache, they might be able to keep a "ghost" zone alive forever,
4717
         even after the delegation is gone from the parent.
4718
         So let's just do nothing with them, we can fetch them directly if we need them.
4719
      */
4720
      LOG(prefix << qname << ": Skipping authority NS from '" << auth << "' nameservers in CNAME/DNAME answer " << tCacheEntry->first.name << "|" << DNSRecordContent::NumberToType(tCacheEntry->first.type) << endl);
×
4721
      continue;
×
4722
    }
×
4723

4724
    /*
4725
     * RFC 6672 section 5.3.1
4726
     *  In any response, a signed DNAME RR indicates a non-terminal
4727
     *  redirection of the query.  There might or might not be a server-
4728
     *  synthesized CNAME in the answer section; if there is, the CNAME will
4729
     *  never be signed.  For a DNSSEC validator, verification of the DNAME
4730
     *  RR and then that the CNAME was properly synthesized is sufficient
4731
     *  proof.
4732
     *
4733
     * We do the synthesis check in processRecords, here we make sure we
4734
     * don't validate the CNAME.
4735
     */
4736
    if (isDNAMEAnswer && tCacheEntry->first.type == QType::CNAME) {
36,173!
4737
      expectSignature = false;
×
4738
    }
×
4739

4740
    vState recordState = vState::Indeterminate;
36,173✔
4741

4742
    if (expectSignature && shouldValidate()) {
36,173✔
4743
      vState initialState = getValidationStatus(tCacheEntry->first.name, !tCacheEntry->second.signatures.empty(), tCacheEntry->first.type == QType::DS, depth, prefix);
10,855✔
4744
      LOG(prefix << qname << ": Got initial zone status " << initialState << " for record " << tCacheEntry->first.name << "|" << DNSRecordContent::NumberToType(tCacheEntry->first.type) << endl);
10,855!
4745

4746
      if (initialState == vState::Secure) {
10,855✔
4747
        if (tCacheEntry->first.type == QType::DNSKEY && tCacheEntry->first.place == DNSResourceRecord::ANSWER && tCacheEntry->first.name == getSigner(tCacheEntry->second.signatures)) {
7,043!
4748
          LOG(prefix << qname << ": Validating DNSKEY for " << tCacheEntry->first.name << endl);
1,539!
4749
          recordState = validateDNSKeys(tCacheEntry->first.name, tCacheEntry->second.records, tCacheEntry->second.signatures, depth, prefix);
1,539✔
4750
        }
1,539✔
4751
        else {
5,504✔
4752
          LOG(prefix << qname << ": Validating non-additional " << QType(tCacheEntry->first.type).toString() << " record for " << tCacheEntry->first.name << endl);
5,504!
4753
          recordState = validateRecordsWithSigs(depth, prefix, qname, qtype, tCacheEntry->first.name, QType(tCacheEntry->first.type), tCacheEntry->second.records, tCacheEntry->second.signatures);
5,504✔
4754
        }
5,504✔
4755
      }
7,043✔
4756
      else {
3,812✔
4757
        recordState = initialState;
3,812✔
4758
        LOG(prefix << qname << ": Skipping validation because the current state is " << recordState << endl);
3,812!
4759
      }
3,812✔
4760

4761
      LOG(prefix << qname << ": Validation result is " << recordState << ", current state is " << state << endl);
10,855!
4762
      if (state != recordState) {
10,855✔
4763
        updateValidationState(qname, state, recordState, prefix);
6,655✔
4764
      }
6,655✔
4765
    }
10,855✔
4766

4767
    if (vStateIsBogus(recordState)) {
36,173✔
4768
      seenBogusRRSet = true;
185✔
4769
      /* this is a TTD by now, be careful */
4770
      for (auto& record : tCacheEntry->second.records) {
502✔
4771
        auto newval = std::min(record.d_ttl, static_cast<uint32_t>(s_maxbogusttl + d_now.tv_sec));
502✔
4772
        record.d_ttl = newval;
502✔
4773
      }
502✔
4774
      tCacheEntry->second.d_ttl_time = d_now.tv_sec;
185✔
4775
    }
185✔
4776

4777
    /* We don't need to store NSEC3 records in the positive cache because:
4778
       - we don't allow direct NSEC3 queries
4779
       - denial of existence proofs in wildcard expanded positive responses are stored in authorityRecs
4780
       - denial of existence proofs for negative responses are stored in the negative cache
4781
       We also don't want to cache non-authoritative data except for:
4782
       - records coming from non forward-recurse servers (those will never be AA)
4783
       - DS (special case)
4784
       - NS, A and AAAA (used for infra queries)
4785
    */
4786
    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)) {
36,173✔
4787

4788
      bool doCache = true;
32,506✔
4789
      if (!isAA && seenBogusRRSet) {
32,506✔
4790
        LOG(prefix << qname << ": Not caching non-authoritative rrsets received with Bogus answer" << endl);
32!
4791
        doCache = false;
32✔
4792
      }
32✔
4793
      if (doCache && tCacheEntry->first.place == DNSResourceRecord::ANSWER && ednsmask) {
32,506✔
4794
        const bool isv4 = ednsmask->isIPv4();
77✔
4795
        if ((isv4 && s_ecsipv4nevercache) || (!isv4 && s_ecsipv6nevercache)) {
77!
4796
          doCache = false;
×
4797
        }
×
4798
        // If ednsmask is relevant, we do not want to cache if the scope prefix length is large and TTL is small
4799
        if (doCache && s_ecscachelimitttl > 0) {
77!
4800
          bool manyMaskBits = (isv4 && ednsmask->getBits() > s_ecsipv4cachelimit) || (!isv4 && ednsmask->getBits() > s_ecsipv6cachelimit);
6!
4801

4802
          if (manyMaskBits) {
6✔
4803
            uint32_t minttl = UINT32_MAX;
2✔
4804
            for (const auto& iter : tCacheEntry->second.records) {
2✔
4805
              if (iter.d_ttl < minttl) {
2!
4806
                minttl = iter.d_ttl;
2✔
4807
              }
2✔
4808
            }
2✔
4809
            bool ttlIsSmall = minttl < s_ecscachelimitttl + d_now.tv_sec;
2✔
4810
            if (ttlIsSmall) {
2!
4811
              // Case: many bits and ttlIsSmall
4812
              doCache = false;
2✔
4813
            }
2✔
4814
          }
2✔
4815
        }
6✔
4816
      }
77✔
4817

4818
      d_fromAuthIP = remoteIP;
32,506✔
4819

4820
      if (doCache) {
32,506✔
4821
        // Check if we are going to replace a non-auth (parent) NS recordset
4822
        if (isAA && tCacheEntry->first.type == QType::NS && s_save_parent_ns_set) {
32,472!
4823
          rememberParentSetIfNeeded(tCacheEntry->first.name, tCacheEntry->second.records, depth, prefix);
255✔
4824
        }
255✔
4825
        bool thisRRNeedsWildcardProof = false;
32,472✔
4826
        if (gatherWildcardProof) {
32,472✔
4827
          if (auto wcIt = wildcardCandidates.find(tCacheEntry->first.name); wcIt != wildcardCandidates.end() && wcIt->second) {
1,244✔
4828
            thisRRNeedsWildcardProof = true;
635✔
4829
          }
635✔
4830
        }
1,244✔
4831
        g_recCache->replace(d_now.tv_sec, tCacheEntry->first.name, tCacheEntry->first.type, tCacheEntry->second.records, tCacheEntry->second.signatures, thisRRNeedsWildcardProof ? authorityRecs : *MemRecursorCache::s_emptyAuthRecs, tCacheEntry->first.type == QType::DS ? true : isAA, auth, tCacheEntry->first.place == DNSResourceRecord::ANSWER ? ednsmask : std::nullopt, d_routingTag, recordState, MemRecursorCache::Extra{remoteIP, overTCP}, d_refresh, tCacheEntry->second.d_ttl_time);
32,472✔
4832

4833
        // Delete potential negcache entry. When a record recovers with serve-stale the negcache entry can cause the wrong entry to
4834
        // be served, as negcache entries are checked before record cache entries
4835
        if (NegCache::s_maxServedStaleExtensions > 0) {
32,472✔
4836
          g_negCache->wipeTyped(tCacheEntry->first.name, tCacheEntry->first.type);
192✔
4837
        }
192✔
4838

4839
        if (g_aggressiveNSECCache && thisRRNeedsWildcardProof && recordState == vState::Secure && tCacheEntry->first.place == DNSResourceRecord::ANSWER && !tCacheEntry->second.signatures.empty() && !d_routingTag && !ednsmask) {
32,473!
4840
          /* we have an answer synthesized from a wildcard and aggressive NSEC is enabled, we need to store the
4841
             wildcard in its non-expanded form in the cache to be able to synthesize wildcard answers later */
4842
          const auto& rrsig = tCacheEntry->second.signatures.at(0);
607✔
4843
          const auto labelCount = tCacheEntry->first.name.countLabels();
607✔
4844

4845
          if (isWildcardExpanded(labelCount, *rrsig) && !isWildcardExpandedOntoItself(tCacheEntry->first.name, labelCount, *rrsig)) {
607!
4846
            DNSName realOwner = getNSECOwnerName(tCacheEntry->first.name, tCacheEntry->second.signatures);
607✔
4847

4848
            std::vector<DNSRecord> content;
607✔
4849
            content.reserve(tCacheEntry->second.records.size());
607✔
4850
            for (const auto& record : tCacheEntry->second.records) {
607✔
4851
              DNSRecord nonExpandedRecord(record);
607✔
4852
              nonExpandedRecord.d_name = realOwner;
607✔
4853
              content.push_back(std::move(nonExpandedRecord));
607✔
4854
            }
607✔
4855

4856
            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, std::nullopt, std::nullopt, recordState, MemRecursorCache::Extra{remoteIP, overTCP}, d_refresh, tCacheEntry->second.d_ttl_time);
607!
4857
          }
607✔
4858
        }
607✔
4859
      }
32,472✔
4860
    }
32,506✔
4861

4862
    if (seenAuth.empty() && !tCacheEntry->second.signatures.empty()) {
36,173✔
4863
      seenAuth = getSigner(tCacheEntry->second.signatures);
5,769✔
4864
    }
5,769✔
4865

4866
    if (g_aggressiveNSECCache && (tCacheEntry->first.type == QType::NSEC || tCacheEntry->first.type == QType::NSEC3) && recordState == vState::Secure && !seenAuth.empty()) {
36,174!
4867
      // Good candidate for NSEC{,3} caching
4868
      g_aggressiveNSECCache->insertNSEC(seenAuth, tCacheEntry->first.name, tCacheEntry->second.records.at(0), tCacheEntry->second.signatures, tCacheEntry->first.type == QType::NSEC3, qname, qtype);
2,975✔
4869
    }
2,975✔
4870

4871
    if (tCacheEntry->first.place == DNSResourceRecord::ANSWER && ednsmask) {
36,173✔
4872
      d_wasVariable = true;
77✔
4873
    }
77✔
4874
  }
36,173✔
4875

4876
  if (gatherWildcardProof) {
12,720✔
4877
    if (auto wcIt = wildcardCandidates.find(qname); wcIt != wildcardCandidates.end() && !wcIt->second) {
631!
4878
      // the queried name was not expanded from a wildcard, a record in the CNAME chain was, so we don't need to gather wildcard proof now: we will do that when looking up the CNAME chain
4879
      gatherWildcardProof = false;
2✔
4880
    }
2✔
4881
  }
631✔
4882

4883
  return RCode::NoError;
12,720✔
4884
}
12,720✔
4885

4886
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)
4887
{
253✔
4888
  if (denialState == expectedState) {
253✔
4889
    neValidationState = vState::Secure;
232✔
4890
  }
232✔
4891
  else {
21✔
4892
    if (denialState == dState::OPTOUT) {
21✔
4893
      LOG(prefix << qname << ": OPT-out denial found for " << neName << endl);
7!
4894
      /* rfc5155 states:
4895
         "The AD bit, as defined by [RFC4035], MUST NOT be set when returning a
4896
         response containing a closest (provable) encloser proof in which the
4897
         NSEC3 RR that covers the "next closer" name has the Opt-Out bit set.
4898

4899
         This rule is based on what this closest encloser proof actually
4900
         proves: names that would be covered by the Opt-Out NSEC3 RR may or
4901
         may not exist as insecure delegations.  As such, not all the data in
4902
         responses containing such closest encloser proofs will have been
4903
         cryptographically verified, so the AD bit cannot be set."
4904

4905
         At best the Opt-Out NSEC3 RR proves that there is no signed DS (so no
4906
         secure delegation).
4907
      */
4908
      neValidationState = vState::Insecure;
7✔
4909
    }
7✔
4910
    else if (denialState == dState::INSECURE) {
14✔
4911
      LOG(prefix << qname << ": Insecure denial found for " << neName << ", returning Insecure" << endl);
2!
4912
      neValidationState = vState::Insecure;
2✔
4913
    }
2✔
4914
    else {
12✔
4915
      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);
12!
4916
      /* try again to get the missed cuts, harder this time */
4917
      auto zState = getValidationStatus(neName, false, isDS, depth, prefix);
12✔
4918
      if (zState != vState::Secure) {
12✔
4919
        neValidationState = zState;
4✔
4920
      }
4✔
4921
      else {
8✔
4922
        LOG(prefix << qname << ": Still in a secure zone with an invalid denial for " << neName << ", returning " << vStateToString(vState::BogusInvalidDenial) << endl);
8!
4923
        neValidationState = vState::BogusInvalidDenial;
8✔
4924
      }
8✔
4925
    }
12✔
4926
  }
21✔
4927
  updateValidationState(qname, state, neValidationState, prefix);
253✔
4928
}
253✔
4929

4930
dState SyncRes::getDenialValidationState(const NegCache::NegCacheEntry& negEntry, const dState expectedState, bool referralToUnsigned, const string& prefix)
4931
{
2,459✔
4932
  cspmap_t csp = harvestCSPFromNE(negEntry);
2,459✔
4933
  return getDenial(csp, negEntry.d_name, negEntry.d_qtype.getCode(), referralToUnsigned, expectedState == dState::NXQTYPE, d_validationContext, LogObject(prefix));
2,459✔
4934
}
2,459✔
4935

4936
void SyncRes::checkWildcardProof(const DNSName& qname, const QType& qtype, DNSRecord& rec, const LWResult& lwr, vState& state, unsigned int depth, const std::string& prefix, unsigned int wildcardLabelsCount)
4937
{
627✔
4938
  /* positive answer synthesized from a wildcard */
4939
  NegCache::NegCacheEntry negEntry;
627✔
4940
  negEntry.d_name = qname;
627✔
4941
  negEntry.d_qtype = QType::ENT; // this encodes 'whole record'
627✔
4942
  uint32_t lowestTTL = rec.d_ttl;
627✔
4943
  harvestNXRecords(lwr.d_records, negEntry, d_now.tv_sec, &lowestTTL);
627✔
4944

4945
  if (vStateIsBogus(state)) {
627!
4946
    negEntry.d_validationState = state;
×
4947
  }
×
4948
  else {
627✔
4949
    auto recordState = getValidationStatus(qname, !negEntry.authoritySOA.signatures.empty() || !negEntry.DNSSECRecords.signatures.empty(), false, depth, prefix);
627✔
4950

4951
    if (recordState == vState::Secure) {
627✔
4952
      /* We have a positive answer synthesized from a wildcard, we need to check that we have
4953
         proof that the exact name doesn't exist so the wildcard can be used,
4954
         as described in section 5.3.4 of RFC 4035 and 5.3 of RFC 7129.
4955
      */
4956
      cspmap_t csp = harvestCSPFromNE(negEntry);
623✔
4957
      dState res = getDenial(csp, qname, negEntry.d_qtype.getCode(), false, false, d_validationContext, LogObject(prefix), false, wildcardLabelsCount);
623✔
4958
      if (res != dState::NXDOMAIN) {
623✔
4959
        vState tmpState = vState::BogusInvalidDenial;
8✔
4960
        if (res == dState::INSECURE || res == dState::OPTOUT) {
8!
4961
          /* Some part could not be validated, for example a NSEC3 record with a too large number of iterations,
4962
             this is not enough to warrant a Bogus, but go Insecure. */
4963
          tmpState = vState::Insecure;
2✔
4964
          LOG(prefix << qname << ": Unable to validate denial in wildcard expanded positive response found for " << qname << ", returning Insecure, res=" << res << endl);
2!
4965
        }
2✔
4966
        else {
6✔
4967
          LOG(prefix << qname << ": Invalid denial in wildcard expanded positive response found for " << qname << ", returning Bogus, res=" << res << endl);
6!
4968
          rec.d_ttl = std::min(rec.d_ttl, s_maxbogusttl);
6✔
4969
        }
6✔
4970

4971
        updateValidationState(qname, state, tmpState, prefix);
8✔
4972
        /* we already stored the record with a different validation status, let's fix it */
4973
        updateValidationStatusInCache(qname, qtype, lwr.d_aabit, tmpState);
8✔
4974
      }
8✔
4975
    }
623✔
4976
  }
627✔
4977
}
627✔
4978

4979
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)
4980
{
12,701✔
4981
  bool done = false;
12,701✔
4982
  DNSName dnameTarget;
12,701✔
4983
  DNSName dnameOwner;
12,701✔
4984
  uint32_t dnameTTL = 0;
12,701✔
4985
  bool referralOnDS = false;
12,701✔
4986

4987
  for (auto& rec : lwr.d_records) {
70,261✔
4988
    if (rec.d_type == QType::OPT || rec.d_class != QClass::IN) {
70,261!
4989
      continue;
10,374✔
4990
    }
10,374✔
4991

4992
    if (rec.d_place == DNSResourceRecord::ANSWER && !(lwr.d_aabit || sendRDQuery)) {
59,887!
4993
      /* for now we allow a CNAME for the exact qname in ANSWER with AA=0, because Amazon DNS servers
4994
         are sending such responses */
4995
      if (rec.d_type != QType::CNAME || rec.d_name != qname) {
×
4996
        continue;
×
4997
      }
×
4998
    }
×
4999
    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);
59,887!
5000

5001
    bool putInNegCache = true;
59,887✔
5002
    if (negCacheIndication && qtype == QType::DS && isForwardOrAuth(qname)) {
59,887✔
5003
      // #10189, a NXDOMAIN to a DS query for a forwarded or auth domain should not NXDOMAIN the whole domain
5004
      putInNegCache = false;
7✔
5005
    }
7✔
5006

5007
    if (negCacheIndication) {
59,887✔
5008
      LOG(prefix << qname << ": Got negative caching indication for name '" << qname << "' (accept=" << rec.d_name.isPartOf(auth) << "), newtarget='" << newtarget << "'" << endl);
124!
5009

5010
      rec.d_ttl = min(rec.d_ttl, s_maxnegttl);
124✔
5011
      // only add a SOA if we're not going anywhere after this
5012
      if (newtarget.empty()) {
124✔
5013
        ret.push_back(rec);
118✔
5014
      }
118✔
5015

5016
      NegCache::NegCacheEntry negEntry;
124✔
5017

5018
      uint32_t lowestTTL = rec.d_ttl;
124✔
5019
      /* if we get an NXDomain answer with a CNAME, the name
5020
         does exist but the target does not */
5021
      negEntry.d_name = newtarget.empty() ? qname : newtarget;
124✔
5022
      negEntry.d_qtype = QType::ENT; // this encodes 'whole record'
124✔
5023
      negEntry.d_auth = rec.d_name;
124✔
5024
      harvestNXRecords(lwr.d_records, negEntry, d_now.tv_sec, &lowestTTL);
124✔
5025

5026
      if (vStateIsBogus(state)) {
124✔
5027
        negEntry.d_validationState = state;
7✔
5028
      }
7✔
5029
      else {
117✔
5030
        /* here we need to get the validation status of the zone telling us that the domain does not
5031
           exist, ie the owner of the SOA */
5032
        auto recordState = getValidationStatus(rec.d_name, !negEntry.authoritySOA.signatures.empty() || !negEntry.DNSSECRecords.signatures.empty(), false, depth, prefix);
117!
5033
        if (recordState == vState::Secure) {
117✔
5034
          dState denialState = getDenialValidationState(negEntry, dState::NXDOMAIN, false, prefix);
58✔
5035
          updateDenialValidationState(qname, negEntry.d_validationState, negEntry.d_name, state, denialState, dState::NXDOMAIN, false, depth, prefix);
58✔
5036
        }
58✔
5037
        else {
59✔
5038
          negEntry.d_validationState = recordState;
59✔
5039
          updateValidationState(qname, state, negEntry.d_validationState, prefix);
59✔
5040
        }
59✔
5041
      }
117✔
5042

5043
      if (vStateIsBogus(negEntry.d_validationState)) {
124✔
5044
        lowestTTL = min(lowestTTL, s_maxbogusttl);
7✔
5045
      }
7✔
5046

5047
      negEntry.d_ttd = d_now.tv_sec + lowestTTL;
124✔
5048
      negEntry.d_orig_ttl = lowestTTL;
124✔
5049
      /* if we get an NXDomain answer with a CNAME, let's not cache the
5050
         target, even the server was authoritative for it,
5051
         and do an additional query for the CNAME target.
5052
         We have a regression test making sure we do exactly that.
5053
      */
5054
      if (newtarget.empty() && putInNegCache) {
124✔
5055
        g_negCache->add(negEntry);
111✔
5056
        // doCNAMECacheCheck() checks record cache and does not look into negcache. That means that an old record might be found if
5057
        // serve-stale is active. Avoid that by explicitly zapping that CNAME record.
5058
        if (qtype == QType::CNAME && MemRecursorCache::s_maxServedStaleExtensions > 0) {
111!
5059
          g_recCache->doWipeCache(qname, false, qtype);
2✔
5060
        }
2✔
5061
        if (s_rootNXTrust && negEntry.d_auth.isRoot() && auth.isRoot() && lwr.d_aabit) {
111!
5062
          negEntry.d_name = negEntry.d_name.getLastLabel();
3✔
5063
          g_negCache->add(negEntry);
3✔
5064
        }
3✔
5065
      }
111✔
5066

5067
      negIndicHasSignatures = !negEntry.authoritySOA.signatures.empty() || !negEntry.DNSSECRecords.signatures.empty();
124!
5068
      negindic = true;
124✔
5069
    }
124✔
5070
    else if (rec.d_place == DNSResourceRecord::ANSWER && isRedirection(rec.d_type) && // CNAME or DNAME answer
59,763✔
5071
             !isRedirection(qtype.getCode())) { // But not in response to a CNAME or DNAME query
59,763!
5072
      if (rec.d_type == QType::CNAME && rec.d_name == qname) {
1,600✔
5073
        if (!dnameOwner.empty()) { // We synthesize ourselves
1,562✔
5074
          continue;
9✔
5075
        }
9✔
5076
        ret.push_back(rec);
1,553✔
5077
        if (auto content = getRR<CNAMERecordContent>(rec)) {
1,553!
5078
          newtarget = DNSName(content->getTarget());
1,553✔
5079
        }
1,553✔
5080
        if (needWildcardProof) {
1,553✔
5081
          checkWildcardProof(qname, QType::CNAME, rec, lwr, state, depth, prefix, wildcardLabelsCount);
12✔
5082
        }
12✔
5083
      }
1,553✔
5084
      else if (rec.d_type == QType::DNAME && qname.isPartOf(rec.d_name)) { // DNAME
38!
5085
        ret.push_back(rec);
32✔
5086
        if (auto content = getRR<DNAMERecordContent>(rec)) {
32!
5087
          dnameOwner = rec.d_name;
32✔
5088
          dnameTarget = content->getTarget();
32✔
5089
          dnameTTL = rec.d_ttl;
32✔
5090
          if (!newtarget.empty()) { // We had a CNAME before, remove it from ret so we don't cache it
32✔
5091
            ret.erase(std::remove_if(
21✔
5092
                        ret.begin(),
21✔
5093
                        ret.end(),
21✔
5094
                        [&qname](DNSRecord& dnsrecord) {
50✔
5095
                          return (dnsrecord.d_place == DNSResourceRecord::ANSWER && dnsrecord.d_type == QType::CNAME && dnsrecord.d_name == qname);
50!
5096
                        }),
50✔
5097
                      ret.end());
21✔
5098
          }
21✔
5099
          try {
32✔
5100
            newtarget = qname.makeRelative(dnameOwner) + dnameTarget;
32✔
5101
            if (needWildcardProof) {
32!
5102
              checkWildcardProof(qname, QType::DNAME, rec, lwr, state, depth, prefix, wildcardLabelsCount);
×
5103
            }
×
5104
          }
32✔
5105
          catch (const std::exception& e) {
32✔
5106
            // We should probably catch an std::range_error here and set the rcode to YXDOMAIN (RFC 6672, section 2.2)
5107
            // But there is no way to set the RCODE from this function
5108
            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());
×
5109
          }
×
5110
        }
32✔
5111
      }
32✔
5112
    }
1,600✔
5113
    /* if we have a positive answer synthesized from a wildcard, we need to
5114
       return the corresponding NSEC/NSEC3 records from the AUTHORITY section
5115
       proving that the exact name did not exist.
5116
       Except if this is a NODATA answer because then we will gather the NXNSEC records later */
5117
    else if (gatherWildcardProof && !negindic && (rec.d_type == QType::RRSIG || rec.d_type == QType::NSEC || rec.d_type == QType::NSEC3) && rec.d_place == DNSResourceRecord::AUTHORITY) {
58,163✔
5118
      ret.push_back(rec); // enjoy your DNSSEC
1,248✔
5119
    }
1,248✔
5120
    // for ANY answers we *must* have an authoritative answer, unless we are forwarding recursively
5121
    else if (rec.d_place == DNSResourceRecord::ANSWER && rec.d_name == qname && (rec.d_type == qtype.getCode() || ((lwr.d_aabit || sendRDQuery) && qtype == QType::ANY))) {
56,915!
5122
      LOG(prefix << qname << ": Answer is in: resolved to '" << rec.getContent()->getZoneRepresentation() << "|" << DNSRecordContent::NumberToType(rec.d_type) << "'" << endl);
9,475✔
5123

5124
      done = true;
9,475✔
5125
      rcode = RCode::NoError;
9,475✔
5126

5127
      if (needWildcardProof) {
9,475✔
5128
        checkWildcardProof(qname, qtype, rec, lwr, state, depth, prefix, wildcardLabelsCount);
615✔
5129
      }
615✔
5130

5131
      ret.push_back(rec);
9,475✔
5132
    }
9,475✔
5133
    else if ((rec.d_type == QType::RRSIG || rec.d_type == QType::NSEC || rec.d_type == QType::NSEC3) && rec.d_place == DNSResourceRecord::ANSWER) {
47,440✔
5134
      if (rec.d_type != QType::RRSIG || rec.d_name == qname) {
2,960!
5135
        ret.push_back(rec); // enjoy your DNSSEC
2,927✔
5136
      }
2,927✔
5137
      else if (rec.d_type == QType::RRSIG && qname.isPartOf(rec.d_name)) {
33!
5138
        auto rrsig = getRR<RRSIGRecordContent>(rec);
24✔
5139
        if (rrsig != nullptr && rrsig->d_type == QType::DNAME) {
24!
5140
          ret.push_back(rec);
24✔
5141
        }
24✔
5142
      }
24✔
5143
    }
2,960✔
5144
    else if (rec.d_place == DNSResourceRecord::AUTHORITY && rec.d_type == QType::NS && qname.isPartOf(rec.d_name)) {
44,480!
5145
      if (moreSpecificThan(rec.d_name, auth)) {
14,227!
5146
        newauth = rec.d_name;
14,227✔
5147
        LOG(prefix << qname << ": Got NS record '" << rec.d_name << "' -> '" << rec.getContent()->getZoneRepresentation() << "'" << endl);
14,227✔
5148

5149
        /* check if we have a referral from the parent zone to a child zone for a DS query, which is not right */
5150
        if (qtype == QType::DS && (newauth.isPartOf(qname) || qname == newauth)) {
14,227!
5151
          /* just got a referral from the parent zone when asking for a DS, looks like this server did not get the DNSSEC memo.. */
5152
          referralOnDS = true;
4✔
5153
        }
4✔
5154
        else {
14,223✔
5155
          realreferral = true;
14,223✔
5156
          if (auto content = getRR<NSRecordContent>(rec)) {
14,223!
5157
            nsset.insert(content->getNS());
14,223✔
5158
          }
14,223✔
5159
        }
14,223✔
5160
      }
14,227✔
5161
      else {
×
5162
        LOG(prefix << qname << ": Got upwards/level NS record '" << rec.d_name << "' -> '" << rec.getContent()->getZoneRepresentation() << "', had '" << auth << "'" << endl);
×
5163
        if (auto content = getRR<NSRecordContent>(rec)) {
×
5164
          nsset.insert(content->getNS());
×
5165
        }
×
5166
      }
×
5167
    }
14,227✔
5168
    else if (rec.d_place == DNSResourceRecord::AUTHORITY && rec.d_type == QType::DS && qname.isPartOf(rec.d_name)) {
30,253✔
5169
      LOG(prefix << qname << ": Got DS record '" << rec.d_name << "' -> '" << rec.getContent()->getZoneRepresentation() << "'" << endl);
1,095!
5170
    }
1,095✔
5171
    else if (realreferral && rec.d_place == DNSResourceRecord::AUTHORITY && (rec.d_type == QType::NSEC || rec.d_type == QType::NSEC3) && newauth.isPartOf(auth)) {
29,158!
5172
      /* we might have received a denial of the DS, let's check */
5173
      NegCache::NegCacheEntry negEntry;
3,483✔
5174
      uint32_t lowestTTL = rec.d_ttl;
3,483✔
5175
      harvestNXRecords(lwr.d_records, negEntry, d_now.tv_sec, &lowestTTL);
3,483✔
5176

5177
      if (!vStateIsBogus(state)) {
3,483!
5178
        auto recordState = getValidationStatus(newauth, !negEntry.authoritySOA.signatures.empty() || !negEntry.DNSSECRecords.signatures.empty(), true, depth, prefix);
3,483!
5179

5180
        if (recordState == vState::Secure) {
3,483✔
5181
          negEntry.d_auth = auth;
2,204✔
5182
          negEntry.d_name = newauth;
2,204✔
5183
          negEntry.d_qtype = QType::DS;
2,204✔
5184
          rec.d_ttl = min(s_maxnegttl, rec.d_ttl);
2,204✔
5185

5186
          dState denialState = getDenialValidationState(negEntry, dState::NXQTYPE, true, prefix);
2,204✔
5187

5188
          if (denialState == dState::NXQTYPE || denialState == dState::OPTOUT || denialState == dState::INSECURE) {
2,204!
5189
            negEntry.d_ttd = lowestTTL + d_now.tv_sec;
2,200✔
5190
            negEntry.d_orig_ttl = lowestTTL;
2,200✔
5191
            negEntry.d_validationState = vState::Secure;
2,200✔
5192
            if (denialState == dState::OPTOUT) {
2,200✔
5193
              negEntry.d_validationState = vState::Insecure;
2,116✔
5194
            }
2,116✔
5195
            LOG(prefix << qname << ": Got negative indication of DS record for '" << newauth << "'" << endl);
2,200!
5196

5197
            g_negCache->add(negEntry);
2,200✔
5198

5199
            /* 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
5200
               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
5201
               query. */
5202
            if (qtype == QType::DS && qname == newauth && (d_externalDSQuery.empty() || qname != d_externalDSQuery)) {
2,200!
5203
              /* we are actually done! */
5204
              negindic = true;
×
5205
              negIndicHasSignatures = !negEntry.authoritySOA.signatures.empty() || !negEntry.DNSSECRecords.signatures.empty();
×
5206
              nsset.clear();
×
5207
            }
×
5208
          }
2,200✔
5209
        }
2,204✔
5210
      }
3,483✔
5211
    }
3,483✔
5212
    else if (!done && rec.d_place == DNSResourceRecord::AUTHORITY && rec.d_type == QType::SOA && lwr.d_rcode == RCode::NoError && qname.isPartOf(rec.d_name)) {
25,675!
5213
      LOG(prefix << qname << ": Got negative caching indication for '" << qname << "|" << qtype << "'" << endl);
1,417!
5214

5215
      if (!newtarget.empty()) {
1,417✔
5216
        LOG(prefix << qname << ": Hang on! Got a redirect to '" << newtarget << "' already" << endl);
5!
5217
      }
5✔
5218
      else {
1,412✔
5219
        rec.d_ttl = min(s_maxnegttl, rec.d_ttl);
1,412✔
5220

5221
        NegCache::NegCacheEntry negEntry;
1,412✔
5222
        negEntry.d_auth = rec.d_name;
1,412✔
5223
        uint32_t lowestTTL = rec.d_ttl;
1,412✔
5224
        negEntry.d_name = qname;
1,412✔
5225
        negEntry.d_qtype = qtype;
1,412✔
5226
        harvestNXRecords(lwr.d_records, negEntry, d_now.tv_sec, &lowestTTL);
1,412✔
5227

5228
        if (vStateIsBogus(state)) {
1,412✔
5229
          negEntry.d_validationState = state;
7✔
5230
        }
7✔
5231
        else {
1,405✔
5232
          auto recordState = getValidationStatus(qname, !negEntry.authoritySOA.signatures.empty() || !negEntry.DNSSECRecords.signatures.empty(), qtype == QType::DS, depth, prefix);
1,405!
5233
          if (recordState == vState::Secure) {
1,405✔
5234
            dState denialState = getDenialValidationState(negEntry, dState::NXQTYPE, false, prefix);
191✔
5235
            updateDenialValidationState(qname, negEntry.d_validationState, negEntry.d_name, state, denialState, dState::NXQTYPE, qtype == QType::DS, depth, prefix);
191✔
5236
          }
191✔
5237
          else {
1,214✔
5238
            negEntry.d_validationState = recordState;
1,214✔
5239
            updateValidationState(qname, state, negEntry.d_validationState, prefix);
1,214✔
5240
          }
1,214✔
5241
        }
1,405✔
5242

5243
        if (vStateIsBogus(negEntry.d_validationState)) {
1,412✔
5244
          lowestTTL = min(lowestTTL, s_maxbogusttl);
13✔
5245
          rec.d_ttl = min(rec.d_ttl, s_maxbogusttl);
13✔
5246
        }
13✔
5247
        negEntry.d_ttd = d_now.tv_sec + lowestTTL;
1,412✔
5248
        negEntry.d_orig_ttl = lowestTTL;
1,412✔
5249
        if (qtype.getCode() != 0) { // prevents us from NXDOMAIN'ing a whole domain
1,412✔
5250
          // doCNAMECacheCheck() checks record cache and does not look into negcache. That means that an old record might be found if
5251
          // serve-stale is active. Avoid that by explicitly zapping that CNAME record.
5252
          if (qtype == QType::CNAME && MemRecursorCache::s_maxServedStaleExtensions > 0) {
1,410!
5253
            g_recCache->doWipeCache(qname, false, qtype);
2✔
5254
          }
2✔
5255
          g_negCache->add(negEntry);
1,410✔
5256
        }
1,410✔
5257

5258
        ret.push_back(rec);
1,412✔
5259
        negindic = true;
1,412✔
5260
        negIndicHasSignatures = !negEntry.authoritySOA.signatures.empty() || !negEntry.DNSSECRecords.signatures.empty();
1,412!
5261
      }
1,412✔
5262
    }
1,417✔
5263
  }
59,887✔
5264

5265
  if (!dnameTarget.empty()) {
12,701✔
5266
    // Synthesize a CNAME
5267
    auto cnamerec = DNSRecord();
32✔
5268
    cnamerec.d_name = qname;
32✔
5269
    cnamerec.d_type = QType::CNAME;
32✔
5270
    cnamerec.d_ttl = dnameTTL;
32✔
5271
    cnamerec.setContent(std::make_shared<CNAMERecordContent>(CNAMERecordContent(newtarget)));
32✔
5272
    ret.push_back(std::move(cnamerec));
32✔
5273
  }
32✔
5274

5275
  /* If we have seen a proper denial, let's forget that we also had a referral for a DS query.
5276
     Otherwise we need to deal with it. */
5277
  if (referralOnDS && !negindic) {
12,701!
5278
    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);
4!
5279
    if (!vStateIsBogus(state)) {
4!
5280
      auto recordState = getValidationStatus(qname, false, true, depth, prefix);
4✔
5281
      if (recordState == vState::Secure) {
4✔
5282
        /* we are in a secure zone, got a referral to the child zone on a DS query, no denial, that's wrong */
5283
        LOG(prefix << qname << ": NODATA without a negative indication (missing SOA in authority) in a DNSSEC secure zone, going Bogus" << endl);
2!
5284
        updateValidationState(qname, state, vState::BogusMissingNegativeIndication, prefix);
2✔
5285
      }
2✔
5286
    }
4✔
5287
    negindic = true;
4✔
5288
    negIndicHasSignatures = false;
4✔
5289
  }
4✔
5290

5291
  return done;
12,701✔
5292
}
12,701✔
5293

5294
static void submitTryDotTask(ComboAddress address, const DNSName& auth, const DNSName& nsname, time_t now)
5295
{
×
5296
  if (address.getPort() == 853) {
×
5297
    return;
×
5298
  }
×
5299
  address.setPort(853);
×
5300
  auto lock = s_dotMap.lock();
×
5301
  if (lock->d_numBusy >= SyncRes::s_max_busy_dot_probes) {
×
5302
    return;
×
5303
  }
×
5304
  auto iter = lock->d_map.emplace(DoTStatus{address, auth, now + dotFailWait}).first;
×
5305
  if (iter->d_status == DoTStatus::Busy) {
×
5306
    return;
×
5307
  }
×
5308
  if (iter->d_ttd > now) {
×
5309
    if (iter->d_status == DoTStatus::Bad) {
×
5310
      return;
×
5311
    }
×
5312
    if (iter->d_status == DoTStatus::Good) {
×
5313
      return;
×
5314
    }
×
5315
    // We only want to probe auths that we have seen before, auth that only come around once are not interesting
5316
    if (iter->d_status == DoTStatus::Unknown && iter->d_count == 0) {
×
5317
      return;
×
5318
    }
×
5319
  }
×
5320
  lock->d_map.modify(iter, [=](DoTStatus& status) { status.d_ttd = now + dotFailWait; });
×
5321
  bool pushed = pushTryDoTTask(auth, QType::SOA, address, std::numeric_limits<time_t>::max(), nsname);
×
5322
  if (pushed) {
×
5323
    iter->d_status = DoTStatus::Busy;
×
5324
    ++lock->d_numBusy;
×
5325
  }
×
5326
}
×
5327

5328
static bool shouldDoDoT(ComboAddress address, time_t now)
5329
{
×
5330
  address.setPort(853);
×
5331
  auto lock = s_dotMap.lock();
×
5332
  auto iter = lock->d_map.find(address);
×
5333
  if (iter == lock->d_map.end()) {
×
5334
    return false;
×
5335
  }
×
5336
  iter->d_count++;
×
5337
  return iter->d_status == DoTStatus::Good && iter->d_ttd > now;
×
5338
}
×
5339

5340
static void updateDoTStatus(ComboAddress address, DoTStatus::Status status, time_t time, bool updateBusy = false)
5341
{
×
5342
  address.setPort(853);
×
5343
  auto lock = s_dotMap.lock();
×
5344
  auto iter = lock->d_map.find(address);
×
5345
  if (iter != lock->d_map.end()) {
×
5346
    iter->d_status = status;
×
5347
    lock->d_map.modify(iter, [=](DoTStatus& statusToModify) { statusToModify.d_ttd = time; });
×
5348
    if (updateBusy) {
×
5349
      --lock->d_numBusy;
×
5350
    }
×
5351
  }
×
5352
}
×
5353

5354
bool SyncRes::tryDoT(const DNSName& qname, const QType qtype, const DNSName& nsName, ComboAddress address, time_t now)
5355
{
×
5356
  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));
×
5357

5358
  auto logHelper1 = [&log](const string& ename) {
×
5359
    log->info(Logr::Debug, "Failed to probe DoT records, got an exception", "exception", Logging::Loggable(ename));
×
5360
  };
×
5361
  auto logHelper2 = [&log](const string& msg, const string& ename) {
×
5362
    log->error(Logr::Debug, msg, "Failed to probe DoT records, got an exception", "exception", Logging::Loggable(ename));
×
5363
  };
×
5364
  LWResult lwr;
×
5365
  bool truncated{};
×
5366
  bool spoofed{};
×
5367
  std::optional<Netmask> netmask;
×
5368
  address.setPort(853);
×
5369
  // We use the fact that qname equals auth
5370
  bool isOK = false;
×
5371
  try {
×
5372
    std::optional<EDNSExtendedError> extendedError;
×
5373
    isOK = doResolveAtThisIP("", qname, qtype, lwr, netmask, qname, false, false, nsName, address, true, true, truncated, spoofed, extendedError, true);
×
5374
    isOK = isOK && lwr.d_rcode == RCode::NoError && !lwr.d_records.empty();
×
5375
  }
×
5376
  catch (const PDNSException& e) {
×
5377
    logHelper2(e.reason, "PDNSException");
×
5378
  }
×
5379
  catch (const ImmediateServFailException& e) {
×
5380
    logHelper2(e.reason, "ImmediateServFailException");
×
5381
  }
×
5382
  catch (const PolicyHitException& e) {
×
5383
    logHelper1("PolicyHitException");
×
5384
  }
×
5385
  catch (const std::exception& e) {
×
5386
    logHelper2(e.what(), "std::exception");
×
5387
  }
×
5388
  catch (...) {
×
5389
    logHelper1("other");
×
5390
  }
×
5391
  updateDoTStatus(address, isOK ? DoTStatus::Good : DoTStatus::Bad, now + (isOK ? dotSuccessWait : dotFailWait), true);
×
5392
  return isOK;
×
5393
}
×
5394

5395
void SyncRes::ednsStats(std::optional<Netmask>& ednsmask, const DNSName& qname, const string& prefix)
5396
{
13,094✔
5397
  if (!ednsmask) {
13,094✔
5398
    return;
13,014✔
5399
  }
13,014✔
5400
  s_ecsresponses++;
80✔
5401
  LOG(prefix << qname << ": Received EDNS Client Subnet Mask " << ednsmask->toString() << " on response" << endl);
80!
5402

5403
  if (ednsmask->getBits() > 0) {
80✔
5404
    if (ednsmask->isIPv4()) {
79✔
5405
      ++SyncRes::s_ecsResponsesBySubnetSize4.at(ednsmask->getBits() - 1);
67✔
5406
    }
67✔
5407
    else {
12✔
5408
      ++SyncRes::s_ecsResponsesBySubnetSize6.at(ednsmask->getBits() - 1);
12✔
5409
    }
12✔
5410
  }
79✔
5411
}
80✔
5412

5413
void SyncRes::updateQueryCounts(const string& prefix, const DNSName& qname, const ComboAddress& address, bool doTCP, bool doDoT)
5414
{
13,356✔
5415
  t_Counters.at(rec::Counter::outqueries)++;
13,356✔
5416
  d_outqueries++;
13,356✔
5417
  checkMaxQperQ(qname);
13,356✔
5418
  if (address.sin4.sin_family == AF_INET6) {
13,356✔
5419
    t_Counters.at(rec::Counter::ipv6queries)++;
765✔
5420
  }
765✔
5421
  if (doTCP) {
13,356✔
5422
    if (doDoT) {
44✔
5423
      LOG(prefix << qname << ": Using DoT with " << address.toStringWithPort() << endl);
25!
5424
      t_Counters.at(rec::Counter::dotoutqueries)++;
25✔
5425
      d_dotoutqueries++;
25✔
5426
    }
25✔
5427
    else {
19✔
5428
      LOG(prefix << qname << ": Using TCP with " << address.toStringWithPort() << endl);
19!
5429
      t_Counters.at(rec::Counter::tcpoutqueries)++;
19✔
5430
      d_tcpoutqueries++;
19✔
5431
    }
19✔
5432
  }
44✔
5433
}
13,356✔
5434

5435
void SyncRes::incTimeoutStats(const ComboAddress& remoteIP)
5436
{
343✔
5437
  d_timeouts++;
343✔
5438
  t_Counters.at(rec::Counter::outgoingtimeouts)++;
343✔
5439

5440
  if (remoteIP.sin4.sin_family == AF_INET) {
343✔
5441
    t_Counters.at(rec::Counter::outgoing4timeouts)++;
190✔
5442
  }
190✔
5443
  else {
153✔
5444
    t_Counters.at(rec::Counter::outgoing6timeouts)++;
153✔
5445
  }
153✔
5446

5447
  if (t_timeouts) {
343✔
5448
    t_timeouts->push_back(remoteIP);
25✔
5449
  }
25✔
5450
}
343✔
5451

5452
void SyncRes::checkTotalTime(const DNSName& qname, QType qtype, std::optional<EDNSExtendedError>& extendedError) const
5453
{
13,106✔
5454
  if (s_maxtotusec != 0 && d_totUsec > s_maxtotusec) {
13,106!
5455
    if (s_addExtendedResolutionDNSErrors) {
2!
5456
      extendedError = EDNSExtendedError{static_cast<uint16_t>(EDNSExtendedError::code::NoReachableAuthority), "Timeout waiting for answer(s)"};
×
5457
    }
×
5458
    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");
2✔
5459
  }
2✔
5460
}
13,106✔
5461

5462
bool SyncRes::doResolveAtThisIP(const std::string& prefix, const DNSName& qname, const QType qtype, LWResult& lwr, std::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, std::optional<EDNSExtendedError>& extendedError, bool dontThrottle)
5463
{
13,106✔
5464
  checkTotalTime(qname, qtype, extendedError);
13,106✔
5465

5466
  bool chained = false;
13,106✔
5467
  LWResult::Result resolveret = LWResult::Result::Success;
13,106✔
5468
  int preOutQueryRet = RCode::NoError;
13,106✔
5469

5470
  if (d_pdl && d_pdl->preoutquery(remoteIP, d_requestor, qname, qtype, doTCP, lwr.d_records, preOutQueryRet, d_eventTrace, timeval{0, 0})) {
13,106✔
5471
    LOG(prefix << qname << ": Query handled by Lua" << endl);
4!
5472
  }
4✔
5473
  else {
13,102✔
5474
    ednsmask = getEDNSSubnetMask(qname, remoteIP);
13,102✔
5475
    if (ednsmask) {
13,102✔
5476
      LOG(prefix << qname << ": Adding EDNS Client Subnet Mask " << ednsmask->toString() << " to query" << endl);
606!
5477
      s_ecsqueries++;
606✔
5478
    }
606✔
5479
    auto match = d_eventTrace.add(RecEventTrace::AuthRequest, qname.toLogString(), true, 0);
13,102✔
5480
    d_eventTrace.setValueName(match, "query.qname");
13,102✔
5481
    d_eventTrace.addExtraValues(match, {{"query.qtype", qtype.toString()}, {"auth.address", remoteIP.toStringWithPortExcept(53)}, {"auth.nsname", nsName.toLogString()}});
13,102✔
5482
    updateQueryCounts(prefix, qname, remoteIP, doTCP, doDoT);
13,102✔
5483
    resolveret = asyncresolveWrapper(LogObject(prefix), remoteIP, d_doDNSSEC, qname, auth, qtype.getCode(),
13,102✔
5484
                                     doTCP, sendRDQuery, &d_now, ednsmask, &lwr, &chained, nsName); // <- we go out on the wire!
13,102✔
5485
    d_eventTrace.add(RecEventTrace::AuthRequest, static_cast<int64_t>(lwr.d_rcode), false, match);
13,102✔
5486
    ednsStats(ednsmask, qname, prefix);
13,102✔
5487
    if (resolveret == LWResult::Result::ECSMissing) {
13,102✔
5488
      ednsmask = std::nullopt;
256✔
5489
      LOG(prefix << qname << ": Answer has no ECS, trying again without EDNS Client Subnet Mask" << endl);
256!
5490
      updateQueryCounts(prefix, qname, remoteIP, doTCP, doDoT);
256✔
5491
      match = d_eventTrace.add(RecEventTrace::AuthRequest, qname.toLogString() + '/' + qtype.toString(), true, 0);
256✔
5492
      resolveret = asyncresolveWrapper(LogObject(prefix), remoteIP, d_doDNSSEC, qname, auth, qtype.getCode(),
256✔
5493
                                       doTCP, sendRDQuery, &d_now, ednsmask, &lwr, &chained, nsName); // <- we go out on the wire!
256✔
5494
      d_eventTrace.add(RecEventTrace::AuthRequest, static_cast<int64_t>(lwr.d_rcode), false, match);
256✔
5495
    }
256✔
5496
  }
13,102✔
5497

5498
  /* preoutquery killed the query by setting dq.rcode to -3 */
5499
  if (preOutQueryRet == -3) {
13,106✔
5500
    throw ImmediateServFailException("Query killed by policy");
4✔
5501
  }
4✔
5502

5503
  d_totUsec += lwr.d_usec;
13,102✔
5504

5505
  if (resolveret == LWResult::Result::Spoofed || resolveret == LWResult::Result::BadCookie) {
13,102✔
5506
    spoofed = true;
3✔
5507
    return false;
3✔
5508
  }
3✔
5509

5510
  accountAuthLatency(lwr.d_usec, remoteIP.sin4.sin_family);
13,099✔
5511
  if (lwr.d_rcode >= 0 && lwr.d_rcode < static_cast<decltype(lwr.d_rcode)>(t_Counters.at(rec::RCode::auth).rcodeCounters.size())) {
13,099!
5512
    ++t_Counters.at(rec::RCode::auth).rcodeCounters.at(static_cast<uint8_t>(lwr.d_rcode));
13,091✔
5513
  }
13,091✔
5514

5515
  if (!dontThrottle) {
13,099✔
5516
    dontThrottle = shouldNotThrottle(&nsName, &remoteIP);
13,090✔
5517
  }
13,090✔
5518

5519
  if (resolveret != LWResult::Result::Success) {
13,099✔
5520
    /* Error while resolving */
5521
    switch (resolveret) {
420✔
5522
    case LWResult::Result::Timeout:
343✔
5523
      LOG(prefix << qname << ": Timeout resolving after " << lwr.d_usec / 1000.0 << " ms " << (doTCP ? "over TCP" : "") << endl);
343!
5524
      incTimeoutStats(remoteIP);
343✔
5525
      break;
343✔
5526
    case LWResult::Result::OSLimitError:
6✔
5527
      /* OS resource limit reached */
5528
      LOG(prefix << qname << ": Hit a local resource limit resolving" << (doTCP ? " over TCP" : "") << ", probable error: " << stringerror() << endl);
6!
5529
      t_Counters.at(rec::Counter::resourceLimits)++;
6✔
5530
      break;
6✔
5531
    case LWResult::Result::ChainLimitError:
×
5532
      /* Chain resource limit reached */
5533
      LOG(prefix << qname << ": Hit a chain limit resolving" << (doTCP ? " over TCP" : ""));
×
5534
      t_Counters.at(rec::Counter::chainLimits)++;
×
5535
      break;
×
5536
    default:
71✔
5537
      /* LWResult::Result::PermanentError */
5538
      t_Counters.at(rec::Counter::unreachables)++;
71✔
5539
      d_unreachables++;
71✔
5540
      // XXX questionable use of errno
5541
      LOG(prefix << qname << ": Error resolving from " << remoteIP.toString() << (doTCP ? " over TCP" : "") << ", possible error: " << stringerror() << endl);
71!
5542
      break;
71✔
5543
    }
420✔
5544

5545
    // don't account for resource limits, they are our own fault
5546
    // And don't throttle when the IP address is on the dontThrottleNetmasks list or the name is part of dontThrottleNames
5547
    if (!LWResult::isLimitError(resolveret) && !chained && !dontThrottle) {
420✔
5548
      uint32_t responseUsec = 1000000; // 1 sec for non-timeout cases
411✔
5549
      // Use the actual time if we saw a timeout
5550
      if (resolveret == LWResult::Result::Timeout) {
411✔
5551
        responseUsec = lwr.d_usec;
342✔
5552
      }
342✔
5553

5554
      s_nsSpeeds.lock()->find_or_enter(nsName.empty() ? DNSName(remoteIP.toStringWithPort()) : nsName, d_now).submit(remoteIP, static_cast<int>(responseUsec), d_now);
411✔
5555

5556
      // make sure we don't throttle the root
5557
      if (s_serverdownmaxfails > 0 && auth != g_rootdnsname && s_fails.lock()->incr(remoteIP, d_now) >= s_serverdownmaxfails) {
411!
5558
        LOG(prefix << qname << ": Max fails reached resolving on " << remoteIP.toString() << ". Going full throttle for " << s_serverdownthrottletime << " seconds" << endl);
×
5559
        // mark server as down
5560
        doThrottle(d_now.tv_sec, remoteIP, s_serverdownthrottletime, 10000, Throttle::Reason::ServerDown);
×
5561
      }
×
5562
      else if (resolveret == LWResult::Result::PermanentError) {
411✔
5563
        // unreachable, 1 minute or 100 queries
5564
        doThrottle(d_now.tv_sec, remoteIP, qname, qtype, 60, 100, Throttle::Reason::PermanentError);
69✔
5565
      }
69✔
5566
      else {
342✔
5567
        // If the actual response time was more than 80% of the default timeout, we throttle. On a
5568
        // busy rec we reduce the time we are willing to wait for an auth, it is unfair to throttle on
5569
        // such a shortened timeout.
5570
        if (responseUsec > g_networkTimeoutMsec * 800) {
342✔
5571
          // timeout, 10 seconds or 5 queries
5572
          doThrottle(d_now.tv_sec, remoteIP, qname, qtype, 10, 5, Throttle::Reason::Timeout);
39✔
5573
        }
39✔
5574
      }
342✔
5575
    }
411✔
5576

5577
    return false;
420✔
5578
  }
420✔
5579

5580
  if (!lwr.d_validpacket) {
12,679✔
5581
    LOG(prefix << qname << ": " << nsName << " (" << remoteIP.toString() << ") returned a packet we could not parse over " << (doTCP ? "TCP" : "UDP") << ", trying sibling IP or NS" << endl);
12!
5582
    if (!chained && !dontThrottle) {
12!
5583

5584
      // let's make sure we prefer a different server for some time, if there is one available
5585
      s_nsSpeeds.lock()->find_or_enter(nsName.empty() ? DNSName(remoteIP.toStringWithPort()) : nsName, d_now).submit(remoteIP, 1000000, d_now); // 1 sec
12!
5586

5587
      if (doTCP) {
12✔
5588
        // we can be more heavy-handed over TCP
5589
        doThrottle(d_now.tv_sec, remoteIP, qname, qtype, 60, 10, Throttle::Reason::ParseError);
4✔
5590
      }
4✔
5591
      else {
8✔
5592
        doThrottle(d_now.tv_sec, remoteIP, qname, qtype, 10, 2, Throttle::Reason::ParseError);
8✔
5593
      }
8✔
5594
    }
12✔
5595
    return false;
12✔
5596
  }
12✔
5597
  /* we got an answer */
5598
  if (lwr.d_rcode != RCode::NoError && lwr.d_rcode != RCode::NXDomain) {
12,667✔
5599
    LOG(prefix << qname << ": " << nsName << " (" << remoteIP.toString() << ") returned a " << RCode::to_s(lwr.d_rcode) << ", trying sibling IP or NS" << endl);
64!
5600
    if (!chained && !dontThrottle) {
64!
5601
      if (wasForwarded && lwr.d_rcode == RCode::ServFail) {
64✔
5602
        // 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
5603
        // 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
5604
        // at the very least we will detect that if our packets stop being answered
5605
        s_nsSpeeds.lock()->find_or_enter(nsName.empty() ? DNSName(remoteIP.toStringWithPort()) : nsName, d_now).submit(remoteIP, 1000000, d_now); // 1 sec
4!
5606
      }
4✔
5607
      else {
60✔
5608
        Throttle::Reason reason{};
60✔
5609
        switch (lwr.d_rcode) {
60✔
5610
        case RCode::ServFail:
×
5611
          reason = Throttle::Reason::RCodeServFail;
×
5612
          break;
×
5613
        case RCode::Refused:
8✔
5614
          reason = Throttle::Reason::RCodeRefused;
8✔
5615
          break;
8✔
5616
        default:
52✔
5617
          reason = Throttle::Reason::RCodeOther;
52✔
5618
          break;
52✔
5619
        }
60✔
5620
        doThrottle(d_now.tv_sec, remoteIP, qname, qtype, 60, 3, reason);
60✔
5621
      }
60✔
5622
    }
64✔
5623
    return false;
64✔
5624
  }
64✔
5625

5626
  /* this server sent a valid answer, mark it backup up if it was down */
5627
  if (s_serverdownmaxfails > 0) {
12,603✔
5628
    s_fails.lock()->clear(remoteIP);
12,594✔
5629
  }
12,594✔
5630
  // Clear all throttles for this IP, both general and specific throttles for qname-qtype
5631
  unThrottle(remoteIP, qname, qtype);
12,603✔
5632

5633
  if (lwr.d_tcbit) {
12,603✔
5634
    truncated = true;
14✔
5635

5636
    if (doTCP) {
14✔
5637
      LOG(prefix << qname << ": Truncated bit set, over TCP?" << endl);
2!
5638
      if (!dontThrottle) {
2!
5639
        /* let's treat that as a ServFail answer from this server */
5640
        doThrottle(d_now.tv_sec, remoteIP, qname, qtype, 60, 3, Throttle::Reason::TCPTruncate);
2✔
5641
      }
2✔
5642
      return false;
2✔
5643
    }
2✔
5644
    LOG(prefix << qname << ": Truncated bit set, over UDP" << endl);
12!
5645

5646
    return true;
12✔
5647
  }
14✔
5648

5649
  return true;
12,589✔
5650
}
12,603✔
5651

5652
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)
5653
{
1,574✔
5654
  if (newtarget == qname) {
1,574✔
5655
    LOG(prefix << qname << ": Status=got a CNAME referral to self, returning SERVFAIL" << endl);
3!
5656
    ret.clear();
3✔
5657
    rcode = RCode::ServFail;
3✔
5658
    return;
3✔
5659
  }
3✔
5660
  if (newtarget.isPartOf(qname)) {
1,571✔
5661
    // a.b.c. CNAME x.a.b.c will go to great depths with QM on
5662
    LOG(prefix << qname << ": Status=got a CNAME referral to child, disabling QM" << endl);
21!
5663
    setQNameMinimization(false);
21✔
5664
  }
21✔
5665

5666
  if (!d_followCNAME) {
1,571✔
5667
    rcode = RCode::NoError;
160✔
5668
    return;
160✔
5669
  }
160✔
5670

5671
  // Check to see if we already have seen the new target as a previous target or that the chain is too long
5672
  const auto [CNAMELoop, numCNAMEs] = scanForCNAMELoop(newtarget, ret);
1,411✔
5673
  if (CNAMELoop) {
1,411✔
5674
    LOG(prefix << qname << ": Status=got a CNAME referral that causes a loop, returning SERVFAIL" << endl);
2!
5675
    ret.clear();
2✔
5676
    rcode = RCode::ServFail;
2✔
5677
    return;
2✔
5678
  }
2✔
5679
  if (numCNAMEs > s_max_CNAMES_followed) {
1,409✔
5680
    LOG(prefix << qname << ": Status=got a CNAME referral, but chain too long, returning SERVFAIL" << endl);
2!
5681
    rcode = RCode::ServFail;
2✔
5682
    return;
2✔
5683
  }
2✔
5684

5685
  if (qtype == QType::DS || qtype == QType::DNSKEY) {
1,407!
5686
    LOG(prefix << qname << ": Status=got a CNAME referral, but we are looking for a DS or DNSKEY" << endl);
4!
5687

5688
    if (d_doDNSSEC) {
4!
5689
      addNXNSECS(ret, recordsFromAnswer);
4✔
5690
    }
4✔
5691

5692
    rcode = RCode::NoError;
4✔
5693
    return;
4✔
5694
  }
4✔
5695

5696
  LOG(prefix << qname << ": Status=got a CNAME referral, starting over with " << newtarget << endl);
1,403✔
5697

5698
  set<GetBestNSAnswer> beenthere;
1,403✔
5699
  Context cnameContext;
1,403✔
5700
  rcode = doResolve(newtarget, qtype, ret, depth + 1, beenthere, cnameContext);
1,403✔
5701
  LOG(prefix << qname << ": Updating validation state for response to " << qname << " from " << state << " with the state from the CNAME quest: " << cnameContext.state << endl);
1,403✔
5702
  updateValidationState(qname, state, cnameContext.state, prefix);
1,403✔
5703
}
1,403✔
5704

5705
bool SyncRes::processAnswer(unsigned int depth, const string& prefix, LWResult& lwr, const DNSName& qname, const QType qtype, DNSName& auth, bool wasForwarded, const std::optional<Netmask>& ednsmask, bool sendRDQuery, NsSet& nameservers, std::vector<DNSRecord>& ret, const DNSFilterEngine& dfe, bool* gotNewServers, int* rcode, vState& state, const ComboAddress& remoteIP, bool overTCP)
5706
{
12,720✔
5707
  if (s_minimumTTL != 0) {
12,720✔
5708
    for (auto& rec : lwr.d_records) {
66,776✔
5709
      rec.d_ttl = max(rec.d_ttl, s_minimumTTL);
66,776✔
5710
    }
66,776✔
5711
  }
10,484✔
5712

5713
  /* if the answer is ECS-specific, a minimum TTL is set for this kind of answers
5714
     and it's higher than the global minimum TTL */
5715
  if (ednsmask && s_minimumECSTTL > 0 && (s_minimumTTL == 0 || s_minimumECSTTL > s_minimumTTL)) {
12,720!
5716
    for (auto& rec : lwr.d_records) {
2✔
5717
      if (rec.d_place == DNSResourceRecord::ANSWER) {
2!
5718
        rec.d_ttl = max(rec.d_ttl, s_minimumECSTTL);
2✔
5719
      }
2✔
5720
    }
2✔
5721
  }
2✔
5722

5723
  bool needWildcardProof = false;
12,720✔
5724
  bool gatherWildcardProof = false;
12,720✔
5725
  unsigned int wildcardLabelsCount = 0;
12,720✔
5726
  *rcode = updateCacheFromRecords(depth, prefix, lwr, qname, qtype, auth, wasForwarded, ednsmask, state, needWildcardProof, gatherWildcardProof, wildcardLabelsCount, sendRDQuery, remoteIP, overTCP);
12,720✔
5727
  if (*rcode != RCode::NoError) {
12,720!
5728
    return true;
×
5729
  }
×
5730

5731
  LOG(prefix << qname << ": Determining status after receiving this packet" << endl);
12,720✔
5732

5733
  set<DNSName> nsset;
12,720✔
5734
  bool realreferral = false;
12,720✔
5735
  bool negindic = false;
12,720✔
5736
  bool negIndicHasSignatures = false;
12,720✔
5737
  DNSName newauth;
12,720✔
5738
  DNSName newtarget;
12,720✔
5739

5740
  bool done = processRecords(prefix, qname, qtype, auth, lwr, sendRDQuery, ret, nsset, newtarget, newauth, realreferral, negindic, state, needWildcardProof, gatherWildcardProof, wildcardLabelsCount, *rcode, negIndicHasSignatures, depth);
12,720✔
5741

5742
  // If we both have a CNAME and an answer, let the CNAME take precedence. This *should* not happen
5743
  // (because CNAMEs cannot co-exist with other records), but reality says otherwise. Other
5744
  // resolvers choose to follow the CNAME in this case as well. We removed the answer record from
5745
  // the records received from the auth when sanitizing, so `done' should not be set when a CNAME is
5746
  // present.
5747
  if (done) {
12,720✔
5748
    LOG(prefix << qname << ": Status=got results, this level of recursion done" << endl);
5,888✔
5749
    LOG(prefix << qname << ": Validation status is " << state << endl);
5,888✔
5750
    return true;
5,888✔
5751
  }
5,888✔
5752

5753
  if (!newtarget.empty()) {
6,832✔
5754
    handleNewTarget(prefix, qname, newtarget, qtype.getCode(), ret, *rcode, depth, lwr.d_records, state);
1,564✔
5755
    return true;
1,564✔
5756
  }
1,564✔
5757

5758
  if (lwr.d_rcode == RCode::NXDomain) {
5,268✔
5759
    LOG(prefix << qname << ": Status=NXDOMAIN, we are done " << (negindic ? "(have negative SOA)" : "") << endl);
134!
5760

5761
    auto tempState = getValidationStatus(qname, negIndicHasSignatures, qtype == QType::DS, depth, prefix);
134✔
5762
    if (tempState == vState::Secure && (lwr.d_aabit || sendRDQuery) && !negindic) {
134!
5763
      LOG(prefix << qname << ": NXDOMAIN without a negative indication (missing SOA in authority) in a DNSSEC secure zone, going Bogus" << endl);
4!
5764
      updateValidationState(qname, state, vState::BogusMissingNegativeIndication, prefix);
4✔
5765
    }
4✔
5766
    else {
130✔
5767
      /* we might not have validated any record, because we did get a NXDOMAIN without any SOA
5768
         from an insecure zone, for example */
5769
      updateValidationState(qname, state, tempState, prefix);
130✔
5770
    }
130✔
5771

5772
    if (d_doDNSSEC) {
134✔
5773
      addNXNSECS(ret, lwr.d_records);
100✔
5774
    }
100✔
5775

5776
    *rcode = RCode::NXDomain;
134✔
5777
    return true;
134✔
5778
  }
134✔
5779

5780
  if (nsset.empty() && lwr.d_rcode == 0 && (negindic || lwr.d_aabit || sendRDQuery)) {
5,134!
5781
    LOG(prefix << qname << ": Status=noerror, other types may exist, but we are done " << (negindic ? "(have negative SOA) " : "") << (lwr.d_aabit ? "(have aa bit) " : "") << endl);
1,459!
5782

5783
    auto tempState = getValidationStatus(qname, negIndicHasSignatures, qtype == QType::DS, depth, prefix);
1,459✔
5784
    if (tempState == vState::Secure && (lwr.d_aabit || sendRDQuery) && !negindic) {
1,459✔
5785
      LOG(prefix << qname << ": NODATA without a negative indication (missing SOA in authority) in a DNSSEC secure zone, going Bogus" << endl);
11!
5786
      updateValidationState(qname, state, vState::BogusMissingNegativeIndication, prefix);
11✔
5787
    }
11✔
5788
    else {
1,448✔
5789
      /* we might not have validated any record, because we did get a NODATA without any SOA
5790
         from an insecure zone, for example */
5791
      updateValidationState(qname, state, tempState, prefix);
1,448✔
5792
    }
1,448✔
5793

5794
    if (d_doDNSSEC) {
1,459✔
5795
      addNXNSECS(ret, lwr.d_records);
1,389✔
5796
    }
1,389✔
5797

5798
    *rcode = RCode::NoError;
1,459✔
5799
    return true;
1,459✔
5800
  }
1,459✔
5801

5802
  if (realreferral) {
3,675✔
5803
    LOG(prefix << qname << ": Status=did not resolve, got " << (unsigned int)nsset.size() << " NS, ");
3,602✔
5804

5805
    nameservers.clear();
3,602✔
5806
    for (auto const& nameserver : nsset) {
14,223✔
5807
      if (d_wantsRPZ && !d_appliedPolicy.wasHit()) {
14,223✔
5808
        bool match = dfe.getProcessingPolicy(nameserver, d_discardedPolicies, d_appliedPolicy);
14,219✔
5809
        if (match) {
14,219✔
5810
          mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());
2✔
5811
          if (d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) { // client query needs an RPZ response
2!
5812
            if (d_pdl && d_pdl->policyHitEventFilter(d_requestor, qname, qtype, d_queryReceivedOverTCP, d_appliedPolicy, d_policyTags, d_discardedPolicies)) {
2!
5813
              /* reset to no match */
5814
              d_appliedPolicy = DNSFilterEngine::Policy();
×
5815
            }
×
5816
            else {
2✔
5817
              LOG("however " << nameserver << " was blocked by RPZ policy '" << d_appliedPolicy.getName() << "'" << endl);
2!
5818
              throw PolicyHitException();
2✔
5819
            }
2✔
5820
          }
2✔
5821
        }
2✔
5822
      }
14,219✔
5823
      nameservers.insert({nameserver, {{}, false}});
14,221✔
5824
    }
14,221✔
5825
    LOG("looping to them" << endl);
3,600✔
5826
    *gotNewServers = true;
3,600✔
5827
    auth = std::move(newauth);
3,600✔
5828

5829
    return false;
3,600✔
5830
  }
3,602✔
5831

5832
  return false;
73✔
5833
}
3,675✔
5834

5835
bool SyncRes::doDoTtoAuth(const DNSName& nameServer)
5836
{
13,091✔
5837
  return g_DoTToAuthNames.getLocal()->check(nameServer);
13,091✔
5838
}
13,091✔
5839

5840
/** returns:
5841
 *  -1 in case of no results
5842
 *  rcode otherwise
5843
 */
5844
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
5845
int SyncRes::doResolveAt(NsSet& nameservers, DNSName auth, bool flawedNSSet, const DNSName& qname, const QType qtype,
5846
                         vector<DNSRecord>& ret,
5847
                         unsigned int depth, const string& prefix, set<GetBestNSAnswer>& beenthere, Context& context, StopAtDelegation* stopAtDelegation,
5848
                         map<DNSName, vector<ComboAddress>>* fallBack)
5849
{
11,010✔
5850
  auto luaconfsLocal = g_luaconfs.getLocal();
11,010✔
5851

5852
  LOG(prefix << qname << ": Cache consultations done, have " << (unsigned int)nameservers.size() << " NS to contact");
11,010✔
5853

5854
  if (nameserversBlockedByRPZ(luaconfsLocal->dfe, nameservers)) {
11,010✔
5855
    /* RPZ hit */
5856
    if (d_pdl && d_pdl->policyHitEventFilter(d_requestor, qname, qtype, d_queryReceivedOverTCP, d_appliedPolicy, d_policyTags, d_discardedPolicies)) {
2!
5857
      /* reset to no match */
5858
      d_appliedPolicy = DNSFilterEngine::Policy();
×
5859
    }
×
5860
    else {
2✔
5861
      throw PolicyHitException();
2✔
5862
    }
2✔
5863
  }
2✔
5864

5865
  LOG(endl);
11,008✔
5866

5867
  unsigned int addressQueriesForNS = 0;
11,008✔
5868
  for (;;) { // we may get more specific nameservers
13,022✔
5869
    auto rnameservers = shuffleInSpeedOrder(qname, nameservers, prefix);
13,022✔
5870

5871
    // We allow s_maxnsaddressqperq (default 10) queries with empty responses when resolving NS names.
5872
    // If a zone publishes many (more than s_maxnsaddressqperq) NS records, we allow less.
5873
    // This is to "punish" zones that publish many non-resolving NS names.
5874
    // We always allow 5 NS name resolving attempts with empty results.
5875
    unsigned int nsLimit = s_maxnsaddressqperq;
13,022✔
5876
    if (rnameservers.size() > nsLimit) {
13,022✔
5877
      int newLimit = static_cast<int>(nsLimit - (rnameservers.size() - nsLimit));
2,773✔
5878
      nsLimit = std::max(5, newLimit);
2,773✔
5879
    }
2,773✔
5880

5881
    for (auto tns = rnameservers.cbegin();; ++tns) {
13,518✔
5882
      if (addressQueriesForNS >= nsLimit) {
13,518✔
5883
        throw ImmediateServFailException(std::to_string(nsLimit) + " (adjusted max-ns-address-qperq) or more queries with empty results for NS addresses sent resolving " + qname.toLogString());
2✔
5884
      }
2✔
5885
      if (tns == rnameservers.cend()) {
13,516✔
5886
        LOG(prefix << qname << ": Failed to resolve via any of the " << (unsigned int)rnameservers.size() << " offered NS at level '" << auth << "'" << endl);
246✔
5887
        if (s_addExtendedResolutionDNSErrors) {
246✔
5888
          context.extendedError = EDNSExtendedError{static_cast<uint16_t>(EDNSExtendedError::code::NoReachableAuthority), "delegation " + auth.toLogString()};
62✔
5889
        }
62✔
5890
        if (!auth.isRoot() && flawedNSSet) {
246✔
5891
          LOG(prefix << qname << ": Ageing nameservers for level '" << auth << "', next query might succeed" << endl);
64!
5892
          if (g_recCache->doAgeCache(d_now.tv_sec, auth, QType::NS, 10)) {
64✔
5893
            t_Counters.at(rec::Counter::nsSetInvalidations)++;
17✔
5894
          }
17✔
5895
        }
64✔
5896
        return -1;
246✔
5897
      }
246✔
5898

5899
      bool cacheOnly = false;
13,270✔
5900
      // this line needs to identify the 'self-resolving' behaviour
5901
      if (qname == tns->first && (qtype.getCode() == QType::A || qtype.getCode() == QType::AAAA)) {
13,270!
5902
        /* we might have a glue entry in cache so let's try this NS
5903
           but only if we have enough in the cache to know how to reach it */
5904
        LOG(prefix << qname << ": Using NS to resolve itself, but only using what we have in cache (" << (1 + tns - rnameservers.cbegin()) << "/" << rnameservers.size() << ")" << endl);
71!
5905
        cacheOnly = true;
71✔
5906
      }
71✔
5907

5908
      typedef vector<ComboAddress> remoteIPs_t;
13,270✔
5909
      remoteIPs_t remoteIPs;
13,270✔
5910
      remoteIPs_t::iterator remoteIP;
13,270✔
5911
      bool pierceDontQuery = false;
13,270✔
5912
      bool sendRDQuery = false;
13,270✔
5913
      std::optional<Netmask> ednsmask;
13,270✔
5914
      LWResult lwr;
13,270✔
5915
      const bool wasForwarded = tns->first.empty() && (!nameservers[tns->first].first.empty());
13,270✔
5916
      int rcode = RCode::NoError;
13,270✔
5917
      bool gotNewServers = false;
13,270✔
5918

5919
      if (tns->first.empty() && !wasForwarded) {
13,270✔
5920
        static ComboAddress const s_oobRemote("255.255.255.255");
139✔
5921
        LOG(prefix << qname << ": Domain is out-of-band" << endl);
139!
5922
        /* setting state to indeterminate since validation is disabled for local auth zone,
5923
           and Insecure would be misleading. */
5924
        context.state = vState::Indeterminate;
139✔
5925
        d_wasOutOfBand = doOOBResolve(qname, qtype, lwr.d_records, depth, prefix, lwr.d_rcode);
139✔
5926
        lwr.d_tcbit = false;
139✔
5927
        lwr.d_aabit = true;
139✔
5928

5929
        /* we have received an answer, are we done ? */
5930
        bool done = processAnswer(depth, prefix, lwr, qname, qtype, auth, false, ednsmask, sendRDQuery, nameservers, ret, luaconfsLocal->dfe, &gotNewServers, &rcode, context.state, s_oobRemote, false);
139✔
5931
        if (done) {
139✔
5932
          return rcode;
135✔
5933
        }
135✔
5934
        if (gotNewServers) {
4!
5935
          if (stopAtDelegation != nullptr && *stopAtDelegation == Stop) {
4!
5936
            *stopAtDelegation = Stopped;
×
5937
            return rcode;
×
5938
          }
×
5939
          break;
4✔
5940
        }
4✔
5941
      }
4✔
5942
      else {
13,131✔
5943
        if (fallBack != nullptr) {
13,131✔
5944
          if (auto iter = fallBack->find(tns->first); iter != fallBack->end()) {
2!
5945
            remoteIPs = iter->second;
2✔
5946
          }
2✔
5947
        }
2✔
5948
        if (remoteIPs.empty()) {
13,132✔
5949
          remoteIPs = retrieveAddressesForNS(prefix, qname, tns, depth, beenthere, rnameservers, nameservers, sendRDQuery, pierceDontQuery, flawedNSSet, cacheOnly, addressQueriesForNS);
13,130✔
5950
        }
13,130✔
5951

5952
        if (remoteIPs.empty()) {
13,131✔
5953
          LOG(prefix << qname << ": Failed to get IP for NS " << tns->first << ", trying next if available" << endl);
134!
5954
          flawedNSSet = true;
134✔
5955
          continue;
134✔
5956
        }
134✔
5957
        bool hitPolicy{false};
12,997✔
5958
        LOG(prefix << qname << ": Resolved '" << auth << "' NS " << tns->first << " to: ");
12,997✔
5959
        for (remoteIP = remoteIPs.begin(); remoteIP != remoteIPs.end(); ++remoteIP) {
27,322✔
5960
          if (remoteIP != remoteIPs.begin()) {
14,325✔
5961
            LOG(", ");
1,417✔
5962
          }
1,417✔
5963
          LOG(remoteIP->toString());
14,325✔
5964
          if (nameserverIPBlockedByRPZ(luaconfsLocal->dfe, *remoteIP)) {
14,325✔
5965
            hitPolicy = true;
4✔
5966
          }
4✔
5967
        }
14,325✔
5968
        LOG(endl);
12,997✔
5969
        if (hitPolicy) { // implies d_wantsRPZ
12,997✔
5970
          /* RPZ hit */
5971
          if (d_pdl && d_pdl->policyHitEventFilter(d_requestor, qname, qtype, d_queryReceivedOverTCP, d_appliedPolicy, d_policyTags, d_discardedPolicies)) {
4!
5972
            /* reset to no match */
5973
            d_appliedPolicy = DNSFilterEngine::Policy();
×
5974
          }
×
5975
          else {
4✔
5976
            throw PolicyHitException();
4✔
5977
          }
4✔
5978
        }
4✔
5979

5980
        for (remoteIP = remoteIPs.begin(); remoteIP != remoteIPs.end(); ++remoteIP) {
13,592✔
5981
          LOG(prefix << qname << ": Trying IP " << remoteIP->toStringWithPort() << ", asking '" << qname << "|" << qtype << "'" << endl);
13,100✔
5982

5983
          if (throttledOrBlocked(prefix, *remoteIP, qname, qtype, pierceDontQuery)) {
13,100✔
5984
            // As d_throttledqueries might be increased, check the max-qperq condition
5985
            checkMaxQperQ(qname);
9✔
5986
            continue;
9✔
5987
          }
9✔
5988

5989
          bool truncated = false;
13,091✔
5990
          bool spoofed = false;
13,091✔
5991
          bool gotAnswer = false;
13,091✔
5992
          bool doDoT = false;
13,091✔
5993

5994
          if (doDoTtoAuth(tns->first)) {
13,091✔
5995
            remoteIP->setPort(853);
9✔
5996
            doDoT = true;
9✔
5997
          }
9✔
5998
          if (SyncRes::s_dot_to_port_853 && remoteIP->getPort() == 853) {
13,091✔
5999
            doDoT = true;
25✔
6000
          }
25✔
6001
          bool forceTCP = doDoT || (qtype == QType::ANY && s_outAnyToTcp);
13,091!
6002

6003
          if (!doDoT && s_max_busy_dot_probes > 0) {
13,091!
6004
            submitTryDotTask(*remoteIP, auth, tns->first, d_now.tv_sec);
×
6005
          }
×
6006
          bool overTCP = false;
13,091✔
6007
          if (!forceTCP) {
13,091✔
6008
            gotAnswer = doResolveAtThisIP(prefix, qname, qtype, lwr, ednsmask, auth, sendRDQuery, wasForwarded,
13,064✔
6009
                                          tns->first, *remoteIP, false, false, truncated, spoofed, context.extendedError);
13,064✔
6010
          }
13,064✔
6011
          if (spoofed) {
13,091✔
6012
            LOG(prefix << qname << ": potentially spoofed, retrying over TCP" << endl);
3!
6013
          }
3✔
6014
          if (forceTCP || (spoofed || (gotAnswer && truncated))) {
13,091✔
6015
            /* retry, over TCP this time */
6016
            gotAnswer = doResolveAtThisIP(prefix, qname, qtype, lwr, ednsmask, auth, sendRDQuery, wasForwarded,
42✔
6017
                                          tns->first, *remoteIP, true, doDoT, truncated, spoofed, context.extendedError);
42✔
6018
            overTCP = true;
42✔
6019
          }
42✔
6020

6021
          if (!gotAnswer) {
13,091✔
6022
            if (doDoT && s_max_busy_dot_probes > 0) {
498!
6023
              // This is quite pessimistic...
6024
              updateDoTStatus(*remoteIP, DoTStatus::Bad, d_now.tv_sec + dotFailWait);
×
6025
            }
×
6026
            continue;
498✔
6027
          }
498✔
6028

6029
          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);
12,593✔
6030

6031
          if (doDoT && s_max_busy_dot_probes > 0) {
12,593!
6032
            updateDoTStatus(*remoteIP, DoTStatus::Good, d_now.tv_sec + dotSuccessWait);
×
6033
          }
×
6034
          /*  // for you IPv6 fanatics :-)
6035
              if(remoteIP->sin4.sin_family==AF_INET6)
6036
              lwr.d_usec/=3;
6037
          */
6038
          //        cout<<"ms: "<<lwr.d_usec/1000.0<<", "<<g_avgLatency/1000.0<<'\n';
6039

6040
          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);
12,593✔
6041

6042
          /* we have received an answer, are we done ? */
6043
          bool done = processAnswer(depth, prefix, lwr, qname, qtype, auth, wasForwarded, ednsmask, sendRDQuery, nameservers, ret, luaconfsLocal->dfe, &gotNewServers, &rcode, context.state, *remoteIP, overTCP);
12,593✔
6044
          if (done) {
12,593✔
6045
            return rcode;
8,905✔
6046
          }
8,905✔
6047
          if (gotNewServers) {
3,688✔
6048
            if (stopAtDelegation != nullptr && *stopAtDelegation == Stop) {
3,596!
6049
              *stopAtDelegation = Stopped;
1,587✔
6050
              return rcode;
1,587✔
6051
            }
1,587✔
6052
            break;
2,009✔
6053
          }
3,596✔
6054
          /* was lame */
6055
          if (!shouldNotThrottle(&tns->first, &*remoteIP)) {
92✔
6056
            doThrottle(d_now.tv_sec, *remoteIP, qname, qtype, 60, 100, Throttle::Reason::Lame);
52✔
6057
          }
52✔
6058
        }
92✔
6059

6060
        if (gotNewServers) {
2,501✔
6061
          break;
2,009✔
6062
        }
2,009✔
6063

6064
        if (remoteIP == remoteIPs.cend()) { // we tried all IP addresses, none worked
493✔
6065
          continue;
363✔
6066
        }
363✔
6067
      }
492✔
6068
    }
13,270✔
6069
  }
13,022✔
6070
  return -1;
2,147,483,777✔
6071
}
11,008✔
6072

6073
void SyncRes::setQuerySource(const Netmask& netmask)
6074
{
×
6075
  if (!netmask.empty()) {
×
6076
    d_outgoingECSNetwork = netmask;
×
6077
  }
×
6078
  else {
×
6079
    d_outgoingECSNetwork = std::nullopt;
×
6080
  }
×
6081
}
×
6082

6083
void SyncRes::setQuerySource(const ComboAddress& requestor, const boost::optional<const EDNSSubnetOpts&>& incomingECS)
6084
{
3,252✔
6085
  d_requestor = requestor;
3,252✔
6086

6087
  if (incomingECS && incomingECS->getSourcePrefixLength() > 0) {
3,252✔
6088
    d_cacheRemote = incomingECS->getSource().getMaskedNetwork();
454✔
6089
    uint8_t bits = std::min(incomingECS->getSourcePrefixLength(), (incomingECS->getSource().isIPv4() ? s_ecsipv4limit : s_ecsipv6limit));
454✔
6090
    ComboAddress trunc = incomingECS->getSource().getNetwork();
454✔
6091
    trunc.truncate(bits);
454✔
6092
    d_outgoingECSNetwork = std::optional<Netmask>(Netmask(trunc, bits));
454✔
6093
  }
454✔
6094
  else {
2,798✔
6095
    d_cacheRemote = d_requestor;
2,798✔
6096
    if (!incomingECS && s_ednslocalsubnets.match(d_requestor)) {
2,798✔
6097
      ComboAddress trunc = d_requestor;
136✔
6098
      uint8_t bits = d_requestor.isIPv4() ? 32 : 128;
136✔
6099
      bits = std::min(bits, (trunc.isIPv4() ? s_ecsipv4limit : s_ecsipv6limit));
136✔
6100
      trunc.truncate(bits);
136✔
6101
      d_outgoingECSNetwork = std::optional<Netmask>(Netmask(trunc, bits));
136✔
6102
    }
136✔
6103
    else if (s_ecsScopeZero.getSourcePrefixLength() > 0) {
2,662✔
6104
      /* RFC7871 says we MUST NOT send any ECS if the source scope is 0.
6105
         But using an empty ECS in that case would mean inserting
6106
         a non ECS-specific entry into the cache, preventing any further
6107
         ECS-specific query to be sent.
6108
         So instead we use the trick described in section 7.1.2:
6109
         "The subsequent Recursive Resolver query to the Authoritative Nameserver
6110
         will then either not include an ECS option or MAY optionally include
6111
         its own address information, which is what the Authoritative
6112
         Nameserver will almost certainly use to generate any Tailored
6113
         Response in lieu of an option.  This allows the answer to be handled
6114
         by the same caching mechanism as other queries, with an explicit
6115
         indicator of the applicable scope.  Subsequent Stub Resolver queries
6116
         for /0 can then be answered from this cached response.
6117
      */
6118
      d_outgoingECSNetwork = std::optional<Netmask>(s_ecsScopeZero.getSource().getMaskedNetwork());
2,660✔
6119
      d_cacheRemote = s_ecsScopeZero.getSource().getNetwork();
2,660✔
6120
    }
2,660✔
6121
    else {
2✔
6122
      // ECS disabled because no scope-zero address could be derived.
6123
      d_outgoingECSNetwork = std::nullopt;
2✔
6124
    }
2✔
6125
  }
2,798✔
6126
}
3,252✔
6127

6128
std::optional<Netmask> SyncRes::getEDNSSubnetMask(const DNSName& name, const ComboAddress& rem)
6129
{
13,100✔
6130
  if (d_outgoingECSNetwork && (s_ednsdomains.check(name) || s_ednsremotesubnets.match(rem))) {
13,100✔
6131
    return d_outgoingECSNetwork;
606✔
6132
  }
606✔
6133
  return std::nullopt;
12,494✔
6134
}
13,100✔
6135

6136
void SyncRes::parseEDNSSubnetAllowlist(const std::string& alist)
6137
{
185✔
6138
  vector<string> parts;
185✔
6139
  stringtok(parts, alist, ",; ");
185✔
6140
  for (const auto& allow : parts) {
185✔
6141
    try {
31✔
6142
      s_ednsremotesubnets.addMask(Netmask(allow));
31✔
6143
    }
31✔
6144
    catch (...) {
31✔
6145
      s_ednsdomains.add(DNSName(allow));
23✔
6146
    }
23✔
6147
  }
31✔
6148
}
185✔
6149

6150
void SyncRes::parseEDNSSubnetAddFor(const std::string& subnetlist)
6151
{
185✔
6152
  vector<string> parts;
185✔
6153
  stringtok(parts, subnetlist, ",; ");
185✔
6154
  for (const auto& allow : parts) {
1,715✔
6155
    s_ednslocalsubnets.addMask(allow);
1,715✔
6156
  }
1,715✔
6157
}
185✔
6158

6159
// used by PowerDNSLua - note that this neglects to add the packet count & statistics back to pdns_recursor.cc
6160
int directResolve(const DNSName& qname, const QType qtype, const QClass qclass, vector<DNSRecord>& ret, const shared_ptr<RecursorLua4>& pdl, Logr::log_t log)
6161
{
24✔
6162
  return directResolve(qname, qtype, qclass, ret, pdl, SyncRes::s_qnameminimization, log);
24✔
6163
}
24✔
6164

6165
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)
6166
{
24✔
6167
  auto log = slog->withValues("qname", Logging::Loggable(qname), "qtype", Logging::Loggable(qtype));
24✔
6168

6169
  struct timeval now{};
24✔
6170
  gettimeofday(&now, nullptr);
24✔
6171

6172
  SyncRes resolver(now);
24✔
6173
  resolver.setQNameMinimization(qnamemin);
24✔
6174
  if (pdl) {
24!
6175
    resolver.setLuaEngine(pdl);
24✔
6176
  }
24✔
6177

6178
  int res = -1;
24✔
6179
  const std::string msg = "Exception while resolving";
24✔
6180
  try {
24✔
6181
    res = resolver.beginResolve(qname, qtype, qclass, ret, 0);
24✔
6182
  }
24✔
6183
  catch (const PDNSException& e) {
24✔
6184
    log->error(Logr::Warning, e.reason, msg, "exception", Logging::Loggable("PDNSException"));
×
6185
    ret.clear();
×
6186
  }
×
6187
  catch (const ImmediateServFailException& e) {
24✔
6188
    log->error(Logr::Warning, e.reason, msg, "exception", Logging::Loggable("ImmediateServFailException"));
×
6189
    ret.clear();
×
6190
  }
×
6191
  catch (const PolicyHitException& e) {
24✔
6192
    log->info(Logr::Warning, msg, "exception", Logging::Loggable("PolicyHitException"));
×
6193
    ret.clear();
×
6194
  }
×
6195
  catch (const std::exception& e) {
24✔
6196
    log->error(Logr::Warning, e.what(), msg, "exception", Logging::Loggable("std::exception"));
×
6197
    ret.clear();
×
6198
  }
×
6199
  catch (...) {
24✔
6200
    log->info(Logr::Warning, msg);
×
6201
    ret.clear();
×
6202
  }
×
6203

6204
  return res;
24✔
6205
}
24✔
6206

6207
int SyncRes::getRootNS(struct timeval now, asyncresolve_t asyncCallback, unsigned int depth, Logr::log_t log)
6208
{
231✔
6209
  if (::arg()["hint-file"] == "no-refresh") {
231!
6210
    return 0;
×
6211
  }
×
6212
  SyncRes resolver(now);
231✔
6213
  resolver.d_prefix = "[getRootNS]";
231✔
6214
  resolver.setDoEDNS0(true);
231✔
6215
  resolver.setUpdatingRootNS();
231✔
6216
  resolver.setDoDNSSEC(g_dnssecmode != DNSSECMode::Off);
231✔
6217
  resolver.setDNSSECValidationRequested(g_dnssecmode != DNSSECMode::Off && g_dnssecmode != DNSSECMode::ProcessNoValidate);
231✔
6218
  resolver.setAsyncCallback(std::move(asyncCallback));
231✔
6219
  resolver.setRefreshAlmostExpired(true);
231✔
6220

6221
  const string msg = "Failed to update . records";
231✔
6222
  vector<DNSRecord> ret;
231✔
6223
  int res = -1;
231✔
6224
  try {
231✔
6225
    res = resolver.beginResolve(g_rootdnsname, QType::NS, 1, ret, depth + 1);
231✔
6226
    if (g_dnssecmode != DNSSECMode::Off && g_dnssecmode != DNSSECMode::ProcessNoValidate) {
231✔
6227
      auto state = resolver.getValidationState();
184✔
6228
      if (vStateIsBogus(state)) {
184!
6229
        throw PDNSException("Got Bogus validation result for .|NS");
×
6230
      }
×
6231
    }
184✔
6232
  }
231✔
6233
  catch (const PDNSException& e) {
231✔
6234
    log->error(Logr::Error, e.reason, msg, "exception", Logging::Loggable("PDNSException"));
×
6235
  }
×
6236
  catch (const ImmediateServFailException& e) {
231✔
6237
    log->error(Logr::Error, e.reason, msg, "exception", Logging::Loggable("ImmediateServFailException"));
×
6238
  }
×
6239
  catch (const PolicyHitException& policyHit) {
231✔
6240
    log->info(Logr::Error, msg, "exception", Logging::Loggable("PolicyHitException"),
×
6241
              "policyName", Logging::Loggable(resolver.d_appliedPolicy.getName()));
×
6242
    ret.clear();
×
6243
  }
×
6244
  catch (const std::exception& e) {
231✔
6245
    log->error(Logr::Error, e.what(), msg, "exception", Logging::Loggable("std::exception"));
×
6246
  }
×
6247
  catch (...) {
231✔
6248
    log->info(Logr::Error, msg);
×
6249
  }
×
6250

6251
  if (res == 0) {
231✔
6252
    log->info(Logr::Debug, "Refreshed . records");
181✔
6253
  }
181✔
6254
  else {
50✔
6255
    log->info(Logr::Warning, msg, "rcode", Logging::Loggable(res));
50✔
6256
  }
50✔
6257
  return res;
231✔
6258
}
231✔
6259

6260
bool SyncRes::answerIsNOData(uint16_t requestedType, int rcode, const std::vector<DNSRecord>& records)
6261
{
351✔
6262
  if (rcode != RCode::NoError) {
351✔
6263
    return false;
47✔
6264
  }
47✔
6265

6266
  // NOLINTNEXTLINE(readability-use-anyofallof)
6267
  for (const auto& rec : records) {
367✔
6268
    if (rec.d_place == DNSResourceRecord::ANSWER && rec.d_type == requestedType) {
367✔
6269
      /* we have a record, of the right type, in the right section */
6270
      return false;
247✔
6271
    }
247✔
6272
  }
367✔
6273
  return true;
57✔
6274
#if 0
6275
  // This code should be equivalent to the code above, clang-tidy prefers any_of()
6276
  // I have doubts if that is easier to read
6277
  return !std::any_of(records.begin(), records.end(), [=](const DNSRecord& rec) {
6278
    return rec.d_place == DNSResourceRecord::ANSWER && rec.d_type == requestedType;
6279
  });
6280
#endif
6281
}
304✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc