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

PowerDNS / pdns / 18709198868

22 Oct 2025 07:49AM UTC coverage: 73.016% (+0.01%) from 73.006%
18709198868

Pull #16338

github

web-flow
Merge 53c598698 into 8eda540cb
Pull Request #16338: rec: tighten delegation accept

38301 of 63162 branches covered (60.64%)

Branch coverage included in aggregate %.

30 of 35 new or added lines in 2 files covered. (85.71%)

31 existing lines in 8 files now uncovered.

127486 of 163895 relevant lines covered (77.79%)

6129150.01 hits per line

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

80.99
/pdns/recursordist/syncres.cc
1
/*
2
 * This file is part of PowerDNS or dnsdist.
3
 * Copyright -- PowerDNS.COM B.V. and its contributors
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of version 2 of the GNU General Public License as
7
 * published by the Free Software Foundation.
8
 *
9
 * In addition, for the avoidance of any doubt, permission is granted to
10
 * link this program with OpenSSL and to (re)distribute the binaries
11
 * produced as the result of such linking.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU General Public License
19
 * along with this program; if not, write to the Free Software
20
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21
 */
22
#ifdef HAVE_CONFIG_H
23
#include <utility>
24

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

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

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

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

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

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

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

76
    if (iter == d_cont.end()) {
10,460✔
77
      return 0;
10,418✔
78
    }
10,418✔
79
    return iter->value;
42✔
80
  }
10,460✔
81

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

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

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

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

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

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

116
private:
117
  cont_t d_cont;
118
};
119

120
static LockGuarded<nsspeeds_t> s_nsSpeeds;
121

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

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

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

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

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

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

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

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

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

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

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

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

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

242
private:
243
  cont_t d_cont;
244
};
245

246
static LockGuarded<Throttle> s_throttle;
247

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

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

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

286
static LockGuarded<SavedParentNSSet> s_savedParentNSSet;
287

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

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

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

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

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

343
static LockGuarded<DoTMap> s_dotMap;
344

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

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

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

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

399
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
400
#define LOG(x)                       \
401
  if (d_lm == Log) {                 \
1,172,092✔
402
    g_log << Logger::Warning << x;   \
112,258✔
403
  }                                  \
112,258✔
404
  else if (d_lm == Store) {          \
1,172,092✔
405
    addTraceTS(d_fixednow, d_trace); \
1,450✔
406
    d_trace << x;                    \
1,450✔
407
  }
1,450✔
408

409
OptLog SyncRes::LogObject(const string& prefix)
410
{
89,709✔
411
  OptLog ret;
89,709✔
412
  if (d_lm == Log) {
89,709✔
413
    ret = {prefix, d_fixednow, g_log};
13,228✔
414
  }
13,228✔
415
  else if (d_lm == Store) {
76,481✔
416
    ret = {prefix, d_fixednow, d_trace};
114✔
417
  }
114✔
418
  return ret;
89,709✔
419
}
89,709✔
420

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

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

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

461
SyncRes::SyncRes(const struct timeval& now) :
462
  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)
463
{
5,011✔
464
  d_validationContext.d_nsec3IterationsRemainingQuota = s_maxnsec3iterationsperq > 0 ? s_maxnsec3iterationsperq : std::numeric_limits<decltype(d_validationContext.d_nsec3IterationsRemainingQuota)>::max();
5,011✔
465
}
5,011✔
466

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

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

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

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

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

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

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

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

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

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

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

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

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

656
  d_wasVariable = false;
4,483✔
657
  d_wasOutOfBand = false;
4,483✔
658
  d_cutStates.clear();
4,483✔
659

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

665
  if (isUnsupported(qtype)) {
4,431✔
666
    return -1;
30✔
667
  }
30✔
668

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

676
  if (qtype == QType::DS) {
4,397✔
677
    d_externalDSQuery = qname;
38✔
678
  }
38✔
679
  else {
4,359✔
680
    d_externalDSQuery.clear();
4,359✔
681
  }
4,359✔
682

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

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

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

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

740
  bool handled = false;
4,482✔
741
  vector<pair<QType::typeenum, string>> answers;
4,482✔
742

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

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

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

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

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

809
  if (handled && !answers.empty()) {
4,482!
810
    ret.clear();
52✔
811
    d_wasOutOfBand = true;
52✔
812

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

825
  return handled;
4,482✔
826
}
4,482✔
827

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

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

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

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

876
  // partial lookup
877
  std::pair<records_t::const_iterator, records_t::const_iterator> range = d_records.equal_range(std::tie(qname));
151✔
878

879
  SyncRes::AuthDomain::records_t::const_iterator ziter;
151✔
880
  bool somedata = false;
151✔
881

882
  for (ziter = range.first; ziter != range.second; ++ziter) {
310✔
883
    somedata = true;
159✔
884

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

897
  if (!records.empty()) {
151✔
898
    /* We have found an exact match, we're done */
899
    return result;
112✔
900
  }
112✔
901

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

906
    return result;
9✔
907
  }
9✔
908

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

925
    if (records.empty()) {
6✔
926
      addSOA(records);
2✔
927
    }
2✔
928

929
    return result;
6✔
930
  }
52✔
931

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

946
  if (records.empty()) {
24✔
947
    addSOA(records);
22✔
948
    result = RCode::NXDomain;
22✔
949
  }
22✔
950

951
  return result;
24✔
952
}
30✔
953

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

959
  res = domain.getRecords(qname, qtype, ret);
151✔
960
  return true;
151✔
961
}
151✔
962

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

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

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

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

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

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

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

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

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

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

1041
  static const time_t Expire = 7200;
1042
};
1043

1044
static LockGuarded<ednsstatus_t> s_ednsstatus;
1045

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

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

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

1066
void SyncRes::pruneEDNSStatuses(time_t cutoff)
1067
{
111✔
1068
  s_ednsstatus.lock()->prune(cutoff);
111✔
1069
}
111✔
1070

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

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

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

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

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

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

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

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

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

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

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

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

1160
void SyncRes::pruneThrottledServers(time_t now)
1161
{
140✔
1162
  s_throttle.lock()->prune(now);
140✔
1163
}
140✔
1164

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

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

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

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

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

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

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

1219
  // Get a copy to avoid holding the lock while doing I/O
1220
  const auto throttleMap = s_throttle.lock()->getThrottleMap();
×
1221
  for (const auto& iter : throttleMap) {
×
1222
    count++;
×
1223
    timebuf_t tmp;
×
1224
    // remote IP, dns name, qtype, count, ttd, reason
1225
    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());
×
1226
  }
×
1227

1228
  return count;
×
1229
}
×
1230

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

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

1241
void SyncRes::pruneFailedServers(time_t cutoff)
1242
{
140✔
1243
  s_fails.lock()->prune(cutoff);
140✔
1244
}
140✔
1245

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

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

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

1273
  return count;
×
1274
}
×
1275

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

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

1286
void SyncRes::pruneNonResolving(time_t cutoff)
1287
{
140✔
1288
  s_nonresolving.lock()->prune(cutoff);
140✔
1289
}
140✔
1290

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

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

1313
  return count;
×
1314
}
×
1315

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

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

1326
void SyncRes::pruneSaveParentsNSSets(time_t now)
1327
{
111✔
1328
  s_savedParentNSSet.lock()->prune(now);
111✔
1329
}
111✔
1330

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

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

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

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

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

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

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

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

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

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

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

1426
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, boost::optional<Netmask>& srcmask, LWResult* res, bool* chained, const DNSName& nsName) const
1427
{
13,280✔
1428
  /* what is your QUEST?
1429
     the goal is to get as many remotes as possible on the best level of EDNS support
1430
     The levels are:
1431

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

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

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

1460
  int EDNSLevel = 0;
13,280✔
1461
  auto luaconfsLocal = g_luaconfs.getLocal();
13,280✔
1462
  ResolveContext ctx(d_initialRequestId, nsName);
13,280✔
1463
  ctx.d_auth = auth;
13,280✔
1464

1465
  LWResult::Result ret{};
13,280✔
1466

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

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

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

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

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

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

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

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

1535
    break;
12,877✔
1536
  }
12,887✔
1537
  return ret;
13,280✔
1538
}
13,280✔
1539

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

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

1550
  if (qmIteration < SyncRes::s_minimize_one_label) {
18,570✔
1551
    step = 1;
18,451✔
1552
  }
18,451✔
1553
  else if (qmIteration < SyncRes::s_max_minimize_count) {
121✔
1554
    step = std::max(1U, (qnamelen - labels) / (SyncRes::s_max_minimize_count - qmIteration));
121✔
1555
  }
121✔
1556
  else {
2,147,483,647✔
1557
    step = qnamelen - labels;
2,147,483,647✔
1558
  }
2,147,483,647✔
1559
  unsigned int targetlen = std::min(labels + step, qnamelen);
18,570✔
1560
  return targetlen;
18,570✔
1561
}
18,570✔
1562

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

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

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

1586
  initZoneCutsFromTA(qname, prefix);
22,613✔
1587

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

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

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

1611
  LOG(prefix << qname << ": doResolve" << endl);
20,262✔
1612

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

1644
  const unsigned int qnamelen = qname.countLabels();
10,874✔
1645

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

1652
  for (unsigned int i = 0; i <= qnamelen; i++) {
12,403!
1653

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1796
  // This loop assumes the CNAME's records are in-order
1797
  DNSName target(qname);
2,937✔
1798
  for (const auto& record : ret) {
5,600✔
1799
    if (record.d_place == DNSResourceRecord::ANSWER && record.d_name == target) {
5,600✔
1800
      if (record.d_type == qtype) {
5,583✔
1801
        return true;
89✔
1802
      }
89✔
1803
      if (record.d_type == QType::CNAME) {
5,494!
1804
        if (auto ptr = getRR<CNAMERecordContent>(record)) {
5,495✔
1805
          target = ptr->getTarget();
5,495✔
1806
        }
5,495✔
1807
        else {
2,147,483,647✔
1808
          return false;
2,147,483,647✔
1809
        }
2,147,483,647✔
1810
      }
5,494✔
1811
    }
5,494✔
1812
  }
5,600✔
1813
  return false;
2,849✔
1814
}
2,937✔
1815

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

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

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

1845
  int res = 0;
42,822✔
1846

1847
  const int iterations = !d_refresh && MemRecursorCache::s_maxServedStaleExtensions > 0 ? 2 : 1;
42,822✔
1848
  for (int loop = 0; loop < iterations; loop++) {
2,147,506,641✔
1849

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

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

1873
      bool wasForwardedOrAuthZone = false;
42,341✔
1874
      bool wasAuthZone = false;
42,341✔
1875
      bool wasForwardRecurse = false;
42,341✔
1876

1877
      if (iter != t_sstorage.domainmap->end()) {
42,341✔
1878
        wasForwardedOrAuthZone = true;
782✔
1879

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

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

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

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

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

1947
        return res;
10,847✔
1948
      }
10,847✔
1949

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

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

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

1984
    if (d_cacheonly) {
25,976✔
1985
      return 0;
14,986✔
1986
    }
14,986✔
1987

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

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

1996
    DNSName subdomain(qname);
10,986✔
1997
    if (qtype == QType::DS) {
10,986✔
1998
      subdomain.chopOff();
198✔
1999
    }
198✔
2000

2001
    NsSet nsset;
10,986✔
2002
    bool flawedNSSet = false;
10,986✔
2003

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

2010
    res = doResolveAt(nsset, subdomain, flawedNSSet, qname, qtype, ret, depth, prefix, beenthere, context, stopAtDelegation, nullptr);
10,986✔
2011

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

2047
    if (res == 0) {
10,990✔
2048
      return 0;
10,355✔
2049
    }
10,355✔
2050

2051
    LOG(prefix << qname << ": Failed (res=" << res << ")" << endl);
2,147,484,269✔
2052
    if (res >= 0) {
2,147,484,269✔
2053
      break;
190✔
2054
    }
190✔
2055
  }
2,147,484,269✔
2056
  return res < 0 ? RCode::ServFail : res;
2,147,484,249✔
2057
}
42,822✔
2058

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

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

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

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

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

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

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

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

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

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

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

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

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

2251
    vState state{vState::Indeterminate};
54,258✔
2252
    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)) {
54,258✔
2253
      if (s_maxnsperresolve > 0 && nsVector.size() > s_maxnsperresolve) {
23,272✔
2254
        vector<DNSRecord> selected;
20✔
2255
        selected.reserve(s_maxnsperresolve);
20✔
2256
        std::sample(nsVector.cbegin(), nsVector.cend(), std::back_inserter(selected), s_maxnsperresolve, pdns::dns_random_engine());
20✔
2257
        nsVector = std::move(selected);
20✔
2258
      }
20✔
2259
      bestns.reserve(nsVector.size());
23,271✔
2260

2261
      vector<DNSName> missing;
23,271✔
2262
      for (const auto& nsRecord : nsVector) {
135,780✔
2263
        if (nsRecord.d_ttl > (unsigned int)d_now.tv_sec) {
135,780✔
2264
          vector<DNSRecord> aset;
135,779✔
2265
          QType nsqt{QType::ADDR};
135,779✔
2266
          if (s_doIPv4 && !s_doIPv6) {
135,781✔
2267
            nsqt = QType::A;
91,623✔
2268
          }
91,623✔
2269
          else if (!s_doIPv4 && s_doIPv6) {
44,156!
2270
            nsqt = QType::AAAA;
64✔
2271
          }
64✔
2272

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

2324
      if (!bestns.empty()) {
23,273✔
2325
        GetBestNSAnswer answer;
23,237✔
2326
        answer.qname = qname;
23,237✔
2327
        answer.qtype = qtype.getCode();
23,237✔
2328
        for (const auto& bestNSRecord : bestns) {
135,466✔
2329
          if (auto nsContent = getRR<NSRecordContent>(bestNSRecord)) {
135,470✔
2330
            answer.bestns.emplace(bestNSRecord.d_name, nsContent->getNS());
135,469✔
2331
          }
135,469✔
2332
        }
135,466✔
2333

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

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

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

2374
  SyncRes::domainmap_t::const_iterator ret;
119,977✔
2375
  do {
428,195✔
2376
    ret = t_sstorage.domainmap->find(*qname);
428,195✔
2377
    if (ret != t_sstorage.domainmap->end()) {
428,195✔
2378
      break;
2,314✔
2379
    }
2,314✔
2380
  } while (qname->chopOff());
428,195✔
2381
  return ret;
×
2382
}
146,311✔
2383

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

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

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

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

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

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

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

2457
  if (vStateIsBogus(newState)) {
52✔
2458
    g_recCache->updateValidationStatus(d_now.tv_sec, qname, qtype, d_cacheRemote, d_routingTag, aaFlag, newState, s_maxbogusttl + d_now.tv_sec);
20✔
2459
  }
20✔
2460
  else {
32✔
2461
    g_recCache->updateValidationStatus(d_now.tv_sec, qname, qtype, d_cacheRemote, d_routingTag, aaFlag, newState, boost::none);
32✔
2462
  }
32✔
2463
}
52✔
2464

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

2479
bool SyncRes::doCNAMECacheCheck(const DNSName& qname, const QType qtype, vector<DNSRecord>& ret, unsigned int depth, const string& prefix, int& res, Context& context, bool wasAuthZone, bool wasForwardRecurse, bool checkForDups) // NOLINT(readability-function-cognitive-complexity)
2480
{
36,998✔
2481
  vector<DNSRecord> cset;
36,998✔
2482
  MemRecursorCache::SigRecs signatures = MemRecursorCache::s_emptySigRecs;
36,998✔
2483
  MemRecursorCache::AuthRecs authorityRecs = MemRecursorCache::s_emptyAuthRecs;
36,998✔
2484
  bool wasAuth = false;
36,998✔
2485
  uint32_t capTTL = std::numeric_limits<uint32_t>::max();
36,998✔
2486
  DNSName foundName;
36,998✔
2487
  DNSName authZone;
36,998✔
2488
  QType foundQT = QType::ENT;
36,998✔
2489

2490
  /* we don't require auth data for forward-recurse lookups */
2491
  MemRecursorCache::Flags flags = MemRecursorCache::None;
36,998✔
2492
  if (!wasForwardRecurse && d_requireAuthData) {
36,998✔
2493
    flags |= MemRecursorCache::RequireAuth;
35,263✔
2494
  }
35,263✔
2495
  if (d_refresh) {
36,998✔
2496
    flags |= MemRecursorCache::Refresh;
304✔
2497
  }
304✔
2498
  if (d_serveStale) {
36,998✔
2499
    flags |= MemRecursorCache::ServeStale;
20✔
2500
  }
20✔
2501
  if (g_recCache->get(d_now.tv_sec, qname, QType::CNAME, flags, &cset, d_cacheRemote, d_routingTag, d_doDNSSEC ? &signatures : nullptr, d_doDNSSEC ? &authorityRecs : nullptr, &d_wasVariable, &context.state, &wasAuth, &authZone, &d_fromAuthIP) > 0) {
36,998✔
2502
    foundName = qname;
6,048✔
2503
    foundQT = QType::CNAME;
6,048✔
2504
  }
6,048✔
2505

2506
  if (foundName.empty() && qname != g_rootdnsname) {
36,998✔
2507
    // look for a DNAME cache hit
2508
    auto labels = qname.getRawLabels();
29,809✔
2509
    DNSName dnameName(g_rootdnsname);
29,809✔
2510

2511
    do {
79,013✔
2512
      dnameName.prependRawLabel(labels.back());
79,013✔
2513
      labels.pop_back();
79,013✔
2514
      if (dnameName == qname && qtype != QType::DNAME) { // The client does not want a DNAME, but we've reached the QNAME already. So there is no match
79,013✔
2515
        break;
29,792✔
2516
      }
29,792✔
2517
      if (g_recCache->get(d_now.tv_sec, dnameName, QType::DNAME, flags, &cset, d_cacheRemote, d_routingTag, d_doDNSSEC ? &signatures : nullptr, d_doDNSSEC ? &authorityRecs : nullptr, &d_wasVariable, &context.state, &wasAuth, &authZone, &d_fromAuthIP) > 0) {
2,147,503,761✔
2518
        foundName = std::move(dnameName);
16✔
2519
        foundQT = QType::DNAME;
16✔
2520
        break;
16✔
2521
      }
16✔
2522
    } while (!labels.empty());
49,221✔
2523
  }
29,809✔
2524

2525
  if (foundName.empty()) {
36,998✔
2526
    return false;
30,934✔
2527
  }
30,934✔
2528

2529
  if (qtype == QType::DS && authZone == qname) {
6,064✔
2530
    /* CNAME at APEX of the child zone, we can't use that to prove that
2531
       there is no DS */
2532
    LOG(prefix << qname << ": Found a " << foundQT.toString() << " cache hit of '" << qname << "' from " << authZone << ", but such a record at the apex of the child zone does not prove that there is no DS in the parent zone" << endl);
4!
2533
    return false;
4✔
2534
  }
4✔
2535

2536
  for (auto const& record : cset) {
6,060✔
2537
    if (record.d_class != QClass::IN) {
6,059!
2538
      continue;
×
2539
    }
×
2540

2541
    if (record.d_ttl > (unsigned int)d_now.tv_sec) {
6,060✔
2542

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

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

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

2562
      DNSRecord dnsRecord = record;
6,059✔
2563
      auto alreadyPresent = false;
6,059✔
2564

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

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

2593
        for (const auto& rec : *authorityRecs) {
6,058!
2594
          DNSRecord authDR(rec);
×
2595
          authDR.d_ttl = ttl;
×
2596
          ret.push_back(std::move(authDR));
×
2597
        }
×
2598
      }
6,058✔
2599

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

2626
        LOG(prefix << qname << ": Synthesized " << dnsRecord.d_name << "|CNAME " << newTarget << endl);
14!
2627
      }
14✔
2628

2629
      if (qtype == QType::CNAME) { // perhaps they really wanted a CNAME!
6,057✔
2630
        res = RCode::NoError;
2✔
2631
        return true;
2✔
2632
      }
2✔
2633

2634
      if (qtype == QType::DS || qtype == QType::DNSKEY) {
6,056!
2635
        res = RCode::NoError;
18✔
2636
        return true;
18✔
2637
      }
18✔
2638

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

2649
      if (qname == newTarget) {
6,037✔
2650
        string msg = "Got a CNAME referral (from cache) to self";
2✔
2651
        LOG(prefix << qname << ": " << msg << endl);
2!
2652
        throw ImmediateServFailException(std::move(msg));
2✔
2653
      }
2✔
2654

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

2662
      if (!d_followCNAME) {
6,035✔
2663
        res = RCode::NoError;
131✔
2664
        return true;
131✔
2665
      }
131✔
2666

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

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

2689
      return true;
5,896✔
2690
    }
5,900✔
2691
  }
6,059✔
2692
  throw ImmediateServFailException("Could not determine whether or not there was a CNAME or DNAME in cache for '" + qname.toLogString() + "'");
1✔
2693
}
6,060✔
2694

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

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

2732
static bool negativeCacheEntryHasSOA(const NegCache::NegCacheEntry& negEntry)
2733
{
16✔
2734
  return !negEntry.authoritySOA.records.empty();
16✔
2735
}
16✔
2736

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

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

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

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

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

2780
    const DNSName& owner = entry.first.name;
12✔
2781

2782
    vState recordState = getValidationStatus(owner, !entry.second.signatures.empty(), qtype == QType::DS, depth, prefix);
12✔
2783
    if (state == vState::Indeterminate) {
12✔
2784
      state = recordState;
8✔
2785
    }
8✔
2786

2787
    if (recordState == vState::Secure) {
12✔
2788
      recordState = SyncRes::validateRecordsWithSigs(depth, prefix, qname, qtype, owner, QType(entry.first.type), entry.second.records, entry.second.signatures);
10✔
2789
    }
10✔
2790

2791
    if (recordState != vState::Indeterminate && recordState != state) {
12!
2792
      updateValidationState(qname, state, recordState, prefix);
×
2793
      if (state != vState::Secure) {
×
2794
        break;
×
2795
      }
×
2796
    }
×
2797
  }
12✔
2798

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

2815
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)
2816
{
36,301✔
2817
  bool giveNegative = false;
36,301✔
2818

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

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

2898
  if (giveNegative) {
36,301✔
2899

2900
    context.state = cachedState;
3,349✔
2901

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

2906
      if (context.state != cachedState && vStateIsBogus(context.state)) {
8!
2907
        sttl = std::min(sttl, s_maxbogusttl);
2✔
2908
      }
2✔
2909
    }
8✔
2910

2911
    // Transplant SOA to the returned packet
2912
    addTTLModifiedRecords(negEntry.authoritySOA.records, sttl, ret);
3,349✔
2913
    if (d_doDNSSEC) {
3,349✔
2914
      addTTLModifiedRecords(negEntry.authoritySOA.signatures, sttl, ret);
3,310✔
2915
      addTTLModifiedRecords(negEntry.DNSSECRecords.records, sttl, ret);
3,310✔
2916
      addTTLModifiedRecords(negEntry.DNSSECRecords.signatures, sttl, ret);
3,310✔
2917
    }
3,310✔
2918

2919
    LOG(prefix << qname << ": Updating validation state with negative cache content for " << qname << " to " << context.state << endl);
3,349!
2920
    return true;
3,349✔
2921
  }
3,349✔
2922

2923
  vector<DNSRecord> cset;
32,952✔
2924
  bool found = false;
32,952✔
2925
  bool expired = false;
32,952✔
2926
  MemRecursorCache::SigRecs signatures = MemRecursorCache::s_emptySigRecs;
32,952✔
2927
  MemRecursorCache::AuthRecs authorityRecs = MemRecursorCache::s_emptyAuthRecs;
32,952✔
2928
  uint32_t ttl = 0;
32,952✔
2929
  uint32_t capTTL = std::numeric_limits<uint32_t>::max();
32,952✔
2930
  bool wasCachedAuth{};
32,952✔
2931
  MemRecursorCache::Flags flags = MemRecursorCache::None;
32,952✔
2932
  if (!wasForwardRecurse && d_requireAuthData) {
32,956✔
2933
    flags |= MemRecursorCache::RequireAuth;
31,230✔
2934
  }
31,230✔
2935
  if (d_serveStale) {
32,952✔
2936
    flags |= MemRecursorCache::ServeStale;
10✔
2937
  }
10✔
2938
  if (d_refresh) {
32,952✔
2939
    flags |= MemRecursorCache::Refresh;
304✔
2940
  }
304✔
2941
  if (g_recCache->get(d_now.tv_sec, sqname, sqt, flags, &cset, d_cacheRemote, d_routingTag, d_doDNSSEC ? &signatures : nullptr, d_doDNSSEC ? &authorityRecs : nullptr, &d_wasVariable, &cachedState, &wasCachedAuth, nullptr, &d_fromAuthIP) > 0) {
2,147,501,205✔
2942

2943
    LOG(prefix << sqname << ": Found cache hit for " << sqt.toString() << ": ");
7,454✔
2944

2945
    if (!wasAuthZone && shouldValidate() && (wasCachedAuth || wasForwardRecurse) && cachedState == vState::Indeterminate && d_requireAuthData) {
7,454!
2946

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

2950
      if (recordState == vState::Secure) {
41✔
2951
        LOG(prefix << sqname << ": Got vState::Indeterminate state from the cache, validating.." << endl);
33!
2952
        if (sqt == QType::DNSKEY && sqname == getSigner(*signatures)) {
33✔
2953
          cachedState = validateDNSKeys(sqname, cset, *signatures, depth, prefix);
2✔
2954
        }
2✔
2955
        else {
31✔
2956
          if (sqt == QType::ANY) {
31✔
2957
            std::map<QType, CacheEntry> types;
3✔
2958
            reapRecordsForValidation(types, cset);
3✔
2959
            reapSignaturesForValidation(types, signatures);
3✔
2960

2961
            for (const auto& type : types) {
5✔
2962
              vState cachedRecordState{};
5✔
2963
              if (type.first == QType::DNSKEY && sqname == getSigner(type.second.signatures)) {
5!
2964
                cachedRecordState = validateDNSKeys(sqname, type.second.records, type.second.signatures, depth, prefix);
×
2965
              }
×
2966
              else {
5✔
2967
                cachedRecordState = SyncRes::validateRecordsWithSigs(depth, prefix, qname, qtype, sqname, type.first, type.second.records, type.second.signatures);
5✔
2968
              }
5✔
2969
              updateDNSSECValidationState(cachedState, cachedRecordState);
5✔
2970
            }
5✔
2971
          }
3✔
2972
          else {
28✔
2973
            cachedState = SyncRes::validateRecordsWithSigs(depth, prefix, qname, qtype, sqname, sqt, cset, *signatures);
28✔
2974
          }
28✔
2975
        }
31✔
2976
      }
33✔
2977
      else {
8✔
2978
        cachedState = recordState;
8✔
2979
      }
8✔
2980

2981
      if (cachedState != vState::Indeterminate) {
41!
2982
        LOG(prefix << qname << ": Got vState::Indeterminate state from the cache, validation result is " << cachedState << endl);
41!
2983
        if (vStateIsBogus(cachedState)) {
41✔
2984
          capTTL = s_maxbogusttl;
10✔
2985
        }
10✔
2986
        if (sqt != QType::ANY && sqt != QType::ADDR) {
41!
2987
          updateValidationStatusInCache(sqname, sqt, wasCachedAuth, cachedState);
38✔
2988
        }
38✔
2989
      }
41✔
2990
    }
41✔
2991

2992
    for (auto j = cset.cbegin(); j != cset.cend(); ++j) {
18,084✔
2993

2994
      LOG(j->getContent()->getZoneRepresentation());
10,630✔
2995

2996
      if (j->d_class != QClass::IN) {
10,630!
2997
        continue;
×
2998
      }
×
2999

3000
      if (j->d_ttl > (unsigned int)d_now.tv_sec) {
10,630!
3001
        DNSRecord dnsRecord = *j;
10,630✔
3002
        dnsRecord.d_ttl -= d_now.tv_sec;
10,630✔
3003
        dnsRecord.d_ttl = std::min(dnsRecord.d_ttl, capTTL);
10,630✔
3004
        ttl = dnsRecord.d_ttl;
10,630✔
3005
        ret.push_back(dnsRecord);
10,630✔
3006
        LOG("[ttl=" << dnsRecord.d_ttl << "] ");
10,630✔
3007
        found = true;
10,630✔
3008
      }
10,630✔
3009
      else {
×
3010
        LOG("[expired] ");
×
3011
        expired = true;
×
3012
      }
×
3013
    }
10,630✔
3014

3015
    ret.reserve(ret.size() + signatures->size() + authorityRecs->size());
7,454✔
3016

3017
    for (const auto& signature : *signatures) {
7,454✔
3018
      DNSRecord dnsRecord;
6,909✔
3019
      dnsRecord.d_type = QType::RRSIG;
6,909✔
3020
      dnsRecord.d_name = sqname;
6,909✔
3021
      dnsRecord.d_ttl = ttl;
6,909✔
3022
      dnsRecord.setContent(signature);
6,909✔
3023
      dnsRecord.d_place = DNSResourceRecord::ANSWER;
6,909✔
3024
      dnsRecord.d_class = QClass::IN;
6,909✔
3025
      ret.push_back(std::move(dnsRecord));
6,909✔
3026
    }
6,909✔
3027

3028
    for (const auto& rec : *authorityRecs) {
7,454✔
3029
      DNSRecord dnsRecord(rec);
34✔
3030
      dnsRecord.d_ttl = ttl;
34✔
3031
      ret.push_back(std::move(dnsRecord));
34✔
3032
    }
34✔
3033

3034
    LOG(endl);
7,454✔
3035
    if (found && !expired) {
7,454!
3036
      if (!giveNegative) {
7,454!
3037
        res = 0;
7,454✔
3038
      }
7,454✔
3039
      LOG(prefix << qname << ": Updating validation state with cache content for " << qname << " to " << cachedState << endl);
7,454✔
3040
      context.state = cachedState;
7,454✔
3041
      return true;
7,454✔
3042
    }
7,454✔
3043
    LOG(prefix << qname << ": Cache had only stale entries" << endl);
×
3044
  }
×
3045

3046
  /* let's check if we have a NSEC covering that record */
3047
  if (g_aggressiveNSECCache && !wasForwardedOrAuthZone) {
25,502✔
3048
    if (g_aggressiveNSECCache->getDenial(d_now.tv_sec, qname, qtype, ret, res, d_cacheRemote, d_routingTag, d_doDNSSEC, d_validationContext, LogObject(prefix))) {
14,861✔
3049
      context.state = vState::Secure;
44✔
3050
      if (s_addExtendedResolutionDNSErrors) {
44✔
3051
        context.extendedError = EDNSExtendedError{static_cast<uint16_t>(EDNSExtendedError::code::Synthesized), "Result synthesized from aggressive NSEC cache (RFC8198)"};
28✔
3052
      }
28✔
3053
      return true;
44✔
3054
    }
44✔
3055
  }
14,861✔
3056

3057
  return false;
25,454✔
3058
}
25,498✔
3059

3060
bool SyncRes::moreSpecificThan(const DNSName& lhs, const DNSName& rhs)
3061
{
13,990✔
3062
  return (lhs.isPartOf(rhs) && lhs.countLabels() > rhs.countLabels());
13,990!
3063
}
13,990✔
3064

3065
struct speedOrder
3066
{
3067
  bool operator()(const std::pair<DNSName, float>& lhs, const std::pair<DNSName, float>& rhs) const
3068
  {
150,413✔
3069
    return lhs.second < rhs.second;
150,413✔
3070
  }
150,413✔
3071
};
3072

3073
std::vector<std::pair<DNSName, float>> SyncRes::shuffleInSpeedOrder(const DNSName& qname, NsSet& tnameservers, const string& prefix)
3074
{
12,956✔
3075
  std::vector<std::pair<DNSName, float>> rnameservers;
12,956✔
3076
  rnameservers.reserve(tnameservers.size());
12,956✔
3077
  for (const auto& tns : tnameservers) {
71,163✔
3078
    float speed = s_nsSpeeds.lock()->fastest(tns.first, d_now);
71,163✔
3079
    rnameservers.emplace_back(tns.first, speed);
71,163✔
3080
    if (tns.first.empty()) { // this was an authoritative OOB zone, don't pollute the nsSpeeds with that
71,163✔
3081
      return rnameservers;
322✔
3082
    }
322✔
3083
  }
71,163✔
3084

3085
  shuffle(rnameservers.begin(), rnameservers.end(), pdns::dns_random_engine());
12,634✔
3086
  speedOrder speedCompare;
12,634✔
3087
  stable_sort(rnameservers.begin(), rnameservers.end(), speedCompare);
12,634✔
3088

3089
  if (doLog()) {
12,634✔
3090
    LOG(prefix << qname << ": Nameservers: ");
1,604!
3091
    for (auto i = rnameservers.begin(); i != rnameservers.end(); ++i) {
4,399✔
3092
      if (i != rnameservers.begin()) {
2,795✔
3093
        LOG(", ");
1,191!
3094
        if (((i - rnameservers.begin()) % 3) == 0) {
1,191✔
3095
          LOG(endl
226!
3096
              << prefix << "             ");
226✔
3097
        }
226✔
3098
      }
1,191✔
3099
      LOG(i->first.toLogString() << "(" << fmtfloat(i->second / 1000.0) << "ms)");
2,795!
3100
    }
2,795✔
3101
    LOG(endl);
1,604!
3102
  }
1,604✔
3103
  return rnameservers;
12,634✔
3104
}
12,956✔
3105

3106
vector<ComboAddress> SyncRes::shuffleForwardSpeed(const DNSName& qname, const vector<ComboAddress>& rnameservers, const string& prefix, const bool wasRd)
3107
{
184✔
3108
  vector<ComboAddress> nameservers = rnameservers;
184✔
3109
  map<ComboAddress, float> speeds;
184✔
3110

3111
  for (const auto& val : nameservers) {
232✔
3112
    DNSName nsName = DNSName(val.toStringWithPort());
232✔
3113
    float speed = s_nsSpeeds.lock()->fastest(nsName, d_now);
232✔
3114
    speeds[val] = speed;
232✔
3115
  }
232✔
3116
  shuffle(nameservers.begin(), nameservers.end(), pdns::dns_random_engine());
184✔
3117
  speedOrderCA speedCompare(speeds);
184✔
3118
  stable_sort(nameservers.begin(), nameservers.end(), speedCompare);
184✔
3119

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

3137
static uint32_t getRRSIGTTL(const time_t now, const std::shared_ptr<const RRSIGRecordContent>& rrsig)
3138
{
7,976✔
3139
  uint32_t res = 0;
7,976✔
3140
  if (now < rrsig->d_sigexpire) {
7,976!
3141
    // coverity[store_truncates_time_t]
3142
    res = static_cast<uint32_t>(rrsig->d_sigexpire) - now;
7,976✔
3143
  }
7,976✔
3144
  return res;
7,976✔
3145
}
7,976✔
3146

3147
static const set<QType> nsecTypes = {QType::NSEC, QType::NSEC3};
3148

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

3165
    if (rec.d_type == QType::RRSIG) {
35,048✔
3166
      auto rrsig = getRR<RRSIGRecordContent>(rec);
8,672✔
3167
      if (rrsig) {
8,672!
3168
        if (rrsig->d_type == QType::SOA) {
8,672✔
3169
          negEntry.authoritySOA.signatures.push_back(rec);
587✔
3170
          if (lowestTTL != nullptr && isRRSIGNotExpired(now, *rrsig)) {
587✔
3171
            *lowestTTL = min(*lowestTTL, rec.d_ttl);
292✔
3172
            *lowestTTL = min(*lowestTTL, getRRSIGTTL(now, rrsig));
292✔
3173
          }
292✔
3174
        }
587✔
3175
        if (nsecTypes.count(rrsig->d_type) != 0) {
8,672✔
3176
          negEntry.DNSSECRecords.signatures.push_back(rec);
8,085✔
3177
          if (lowestTTL != nullptr && isRRSIGNotExpired(now, *rrsig)) {
8,085✔
3178
            *lowestTTL = min(*lowestTTL, rec.d_ttl);
7,684✔
3179
            *lowestTTL = min(*lowestTTL, getRRSIGTTL(now, rrsig));
7,684✔
3180
          }
7,684✔
3181
        }
8,085✔
3182
      }
8,672✔
3183
      continue;
8,672✔
3184
    }
8,672✔
3185
    if (rec.d_type == QType::SOA) {
26,376✔
3186
      negEntry.authoritySOA.records.push_back(rec);
2,932✔
3187
      if (lowestTTL != nullptr) {
2,932✔
3188
        *lowestTTL = min(*lowestTTL, rec.d_ttl);
1,511✔
3189
      }
1,511✔
3190
      continue;
2,932✔
3191
    }
2,932✔
3192
    if (nsecTypes.count(rec.d_type) != 0) {
23,444✔
3193
      negEntry.DNSSECRecords.records.push_back(rec);
8,097✔
3194
      if (lowestTTL != nullptr) {
8,097✔
3195
        *lowestTTL = min(*lowestTTL, rec.d_ttl);
7,698✔
3196
      }
7,698✔
3197
      continue;
8,097✔
3198
    }
8,097✔
3199
  }
23,444✔
3200
}
6,973✔
3201

3202
static cspmap_t harvestCSPFromNE(const NegCache::NegCacheEntry& negEntry)
3203
{
3,002✔
3204
  cspmap_t cspmap;
3,002✔
3205
  for (const auto& rec : negEntry.DNSSECRecords.signatures) {
5,151✔
3206
    if (rec.d_type == QType::RRSIG) {
5,151!
3207
      auto rrc = getRR<RRSIGRecordContent>(rec);
5,151✔
3208
      if (rrc) {
5,151!
3209
        cspmap[{rec.d_name, rrc->d_type}].signatures.push_back(rrc);
5,151✔
3210
      }
5,151✔
3211
    }
5,151✔
3212
  }
5,151✔
3213
  for (const auto& rec : negEntry.DNSSECRecords.records) {
5,153✔
3214
    cspmap[{rec.d_name, rec.d_type}].records.insert(rec.getContent());
5,153✔
3215
  }
5,153✔
3216
  return cspmap;
3,002✔
3217
}
3,002✔
3218

3219
// TODO remove after processRecords is fixed!
3220
// Adds the RRSIG for the SOA and the NSEC(3) + RRSIGs to ret
3221
static void addNXNSECS(vector<DNSRecord>& ret, const vector<DNSRecord>& records)
3222
{
1,464✔
3223
  NegCache::NegCacheEntry negEntry;
1,464✔
3224
  harvestNXRecords(records, negEntry, 0, nullptr);
1,464✔
3225
  ret.insert(ret.end(), negEntry.authoritySOA.signatures.begin(), negEntry.authoritySOA.signatures.end());
1,464✔
3226
  ret.insert(ret.end(), negEntry.DNSSECRecords.records.begin(), negEntry.DNSSECRecords.records.end());
1,464✔
3227
  ret.insert(ret.end(), negEntry.DNSSECRecords.signatures.begin(), negEntry.DNSSECRecords.signatures.end());
1,464✔
3228
}
1,464✔
3229

3230
static bool rpzHitShouldReplaceContent(const DNSName& qname, const QType qtype, const std::vector<DNSRecord>& records)
3231
{
63✔
3232
  if (qtype == QType::CNAME) {
63!
3233
    return true;
×
3234
  }
×
3235

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

3250
  return true;
60✔
3251
}
63✔
3252

3253
static void removeConflictingRecord(std::vector<DNSRecord>& records, const DNSName& name, const QType dtype)
3254
{
67✔
3255
  for (auto it = records.begin(); it != records.end();) {
73✔
3256
    bool remove = false;
6✔
3257

3258
    if (it->d_class == QClass::IN && (it->d_type == QType::CNAME || dtype == QType::CNAME || it->d_type == dtype) && it->d_name == name) {
6!
3259
      remove = true;
1✔
3260
    }
1✔
3261
    else if (it->d_class == QClass::IN && it->d_type == QType::RRSIG && it->d_name == name) {
5!
3262
      if (auto rrc = getRR<RRSIGRecordContent>(*it)) {
1!
3263
        if (rrc->d_type == QType::CNAME || rrc->d_type == dtype) {
1!
3264
          /* also remove any RRSIG that could conflict */
3265
          remove = true;
1✔
3266
        }
1✔
3267
      }
1✔
3268
    }
1✔
3269

3270
    if (remove) {
6✔
3271
      it = records.erase(it);
2✔
3272
    }
2✔
3273
    else {
4✔
3274
      ++it;
4✔
3275
    }
4✔
3276
  }
6✔
3277
}
67✔
3278

3279
void SyncRes::handlePolicyHit(const std::string& prefix, const DNSName& qname, const QType qtype, std::vector<DNSRecord>& ret, bool& done, int& rcode, unsigned int depth)
3280
{
107✔
3281
  if (d_pdl && d_pdl->policyHitEventFilter(d_requestor, qname, qtype, d_queryReceivedOverTCP, d_appliedPolicy, d_policyTags, d_discardedPolicies)) {
107✔
3282
    /* reset to no match */
3283
    d_appliedPolicy = DNSFilterEngine::Policy();
2✔
3284
    return;
2✔
3285
  }
2✔
3286

3287
  /* don't account truncate actions for TCP queries, since they are not applied */
3288
  if (d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::Truncate || !d_queryReceivedOverTCP) {
105✔
3289
    ++t_Counters.at(rec::PolicyHistogram::policy).at(d_appliedPolicy.d_kind);
102✔
3290
    ++t_Counters.at(rec::PolicyNameHits::policyName).counts[d_appliedPolicy.getName()];
102✔
3291
  }
102✔
3292

3293
  if (d_appliedPolicy.d_type != DNSFilterEngine::PolicyType::None) {
105!
3294
    LOG(prefix << qname << "|" << qtype << ':' << d_appliedPolicy.getLogString() << endl);
105!
3295
  }
105✔
3296

3297
  switch (d_appliedPolicy.d_kind) {
105!
3298

3299
  case DNSFilterEngine::PolicyKind::NoAction:
22✔
3300
    return;
22✔
3301

3302
  case DNSFilterEngine::PolicyKind::Drop:
6✔
3303
    ++t_Counters.at(rec::Counter::policyDrops);
6✔
3304
    throw ImmediateQueryDropException();
6✔
3305

3306
  case DNSFilterEngine::PolicyKind::NXDOMAIN:
4✔
3307
    ret.clear();
4✔
3308
    d_appliedPolicy.addSOAtoRPZResult(ret);
4✔
3309
    rcode = RCode::NXDomain;
4✔
3310
    done = true;
4✔
3311
    return;
4✔
3312

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

3320
  case DNSFilterEngine::PolicyKind::Truncate:
6✔
3321
    if (!d_queryReceivedOverTCP) {
6✔
3322
      ret.clear();
3✔
3323
      rcode = RCode::NoError;
3✔
3324
      // Exception handling code in pdns_recursor clears ret as well, so no use to
3325
      // fill it here.
3326
      throw SendTruncatedAnswerException();
3✔
3327
    }
3✔
3328
    return;
3✔
3329

3330
  case DNSFilterEngine::PolicyKind::Custom: {
63✔
3331
    if (rpzHitShouldReplaceContent(qname, qtype, ret)) {
63✔
3332
      ret.clear();
60✔
3333
    }
60✔
3334

3335
    rcode = RCode::NoError;
63✔
3336
    done = true;
63✔
3337
    auto spoofed = d_appliedPolicy.getCustomRecords(qname, qtype.getCode());
63✔
3338
    for (auto& dnsRecord : spoofed) {
67✔
3339
      removeConflictingRecord(ret, dnsRecord.d_name, dnsRecord.d_type);
67✔
3340
    }
67✔
3341

3342
    for (auto& dnsRecord : spoofed) {
67✔
3343
      ret.push_back(dnsRecord);
67✔
3344

3345
      if (dnsRecord.d_name == qname && dnsRecord.d_type == QType::CNAME && qtype != QType::CNAME) {
67!
3346
        if (auto content = getRR<CNAMERecordContent>(dnsRecord)) {
10!
3347
          vState newTargetState = vState::Indeterminate;
10✔
3348
          handleNewTarget(prefix, qname, content->getTarget(), qtype.getCode(), ret, rcode, depth, {}, newTargetState);
10✔
3349
        }
10✔
3350
      }
10✔
3351
    }
67✔
3352
    d_appliedPolicy.addSOAtoRPZResult(ret);
63✔
3353
  }
63✔
3354
  }
105✔
3355
}
105✔
3356

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

3376
      // Traverse all IP addresses for this NS to see if they have an RPN NSIP policy
3377
      for (auto const& address : nameserver.second.first) {
63,976✔
3378
        match = dfe.getProcessingPolicy(address, d_discardedPolicies, d_appliedPolicy);
234✔
3379
        if (match) {
234✔
3380
          mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());
3✔
3381
          if (d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) { // client query needs an RPZ response
3✔
3382
            LOG(", however nameserver " << nameserver.first << " IP address " << address.toString() << " was blocked by RPZ policy '" << d_appliedPolicy.getName() << "'" << endl);
2!
3383
            return true;
2✔
3384
          }
2✔
3385
        }
3✔
3386
      }
234✔
3387
    }
63,976✔
3388
  }
10,912✔
3389
  return false;
10,930✔
3390
}
10,932✔
3391

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

3413
static bool shouldNotThrottle(const DNSName* name, const ComboAddress* address)
3414
{
13,094✔
3415
  if (name != nullptr) {
13,094!
3416
    auto dontThrottleNames = g_dontThrottleNames.getLocal();
13,094✔
3417
    if (dontThrottleNames->check(*name)) {
13,094!
3418
      return true;
×
3419
    }
×
3420
  }
13,094✔
3421
  if (address != nullptr) {
13,094✔
3422
    auto dontThrottleNetmasks = g_dontThrottleNetmasks.getLocal();
13,061✔
3423
    if (dontThrottleNetmasks->match(*address)) {
13,061✔
3424
      return true;
4✔
3425
    }
4✔
3426
  }
13,061✔
3427
  return false;
13,090✔
3428
}
13,094✔
3429

3430
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)
3431
{
13,050✔
3432
  vector<ComboAddress> result;
13,050✔
3433

3434
  size_t nonresolvingfails = 0;
13,050✔
3435
  if (!tns->first.empty()) {
13,050✔
3436
    if (s_nonresolvingnsmaxfails > 0) {
12,866✔
3437
      nonresolvingfails = s_nonresolving.lock()->value(tns->first);
10,386✔
3438
      if (nonresolvingfails >= s_nonresolvingnsmaxfails) {
10,386✔
3439
        LOG(prefix << qname << ": NS " << tns->first << " in non-resolving map, skipping" << endl);
20!
3440
        return result;
20✔
3441
      }
20✔
3442
    }
10,386✔
3443

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

3474
    if (nameservers[tns->first].first.size() > 1) {
184✔
3475
      LOG("s");
30!
3476
    }
30✔
3477
    LOG(endl);
184✔
3478

3479
    sendRDQuery = nameservers[tns->first].second;
184✔
3480
    result = shuffleForwardSpeed(qname, nameservers[tns->first].first, prefix, sendRDQuery);
184✔
3481
    pierceDontQuery = true;
184✔
3482
  }
184✔
3483
  return result;
12,940✔
3484
}
13,050✔
3485

3486
void SyncRes::checkMaxQperQ(const DNSName& qname) const
3487
{
13,294✔
3488
  if (d_outqueries + d_throttledqueries > s_maxqperq) {
13,294✔
3489
    throw ImmediateServFailException("more than " + std::to_string(s_maxqperq) + " (max-qperq) queries sent or throttled while resolving " + qname.toLogString());
4✔
3490
  }
4✔
3491
}
13,294✔
3492

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

3529
bool SyncRes::validationEnabled()
3530
{
13,945✔
3531
  return g_dnssecmode != DNSSECMode::Off && g_dnssecmode != DNSSECMode::ProcessNoValidate;
13,945✔
3532
}
13,945✔
3533

3534
uint32_t SyncRes::computeLowestTTD(const std::vector<DNSRecord>& records, const MemRecursorCache::SigRecsVec& signatures, uint32_t signaturesTTL, const MemRecursorCache::AuthRecsVec& authorityRecs) const
3535
{
12,655✔
3536
  uint32_t lowestTTD = std::numeric_limits<uint32_t>::max();
12,655✔
3537
  for (const auto& record : records) {
27,259✔
3538
    lowestTTD = min(lowestTTD, record.d_ttl);
27,259✔
3539
  }
27,259✔
3540

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

3548
    for (const auto& sig : signatures) {
8,895✔
3549
      if (isRRSIGNotExpired(d_now.tv_sec, *sig)) {
8,895✔
3550
        // we don't decrement d_sigexpire by 'now' because we actually want a TTD, not a TTL */
3551
        lowestTTD = min(lowestTTD, static_cast<uint32_t>(sig->d_sigexpire));
8,881✔
3552
      }
8,881✔
3553
    }
8,895✔
3554
  }
8,874✔
3555

3556
  for (const auto& entry : authorityRecs) {
12,655✔
3557
    /* be careful, this is still a TTL here */
3558
    lowestTTD = min(lowestTTD, static_cast<uint32_t>(entry.d_ttl + d_now.tv_sec));
2,560✔
3559

3560
    if (entry.d_type == QType::RRSIG && validationEnabled()) {
2,560!
3561
      auto rrsig = getRR<RRSIGRecordContent>(entry);
1,280✔
3562
      if (rrsig) {
1,280!
3563
        if (isRRSIGNotExpired(d_now.tv_sec, *rrsig)) {
1,280!
3564
          // we don't decrement d_sigexpire by 'now' because we actually want a TTD, not a TTL */
3565
          lowestTTD = min(lowestTTD, static_cast<uint32_t>(rrsig->d_sigexpire));
1,280✔
3566
        }
1,280✔
3567
      }
1,280✔
3568
    }
1,280✔
3569
  }
2,560✔
3570

3571
  return lowestTTD;
12,655✔
3572
}
12,655✔
3573

3574
void SyncRes::updateValidationState(const DNSName& qname, vState& state, const vState stateUpdate, const string& prefix)
3575
{
16,938✔
3576
  LOG(prefix << qname << ": Validation state was " << state << ", state update is " << stateUpdate);
16,938✔
3577
  updateDNSSECValidationState(state, stateUpdate);
16,938✔
3578
  LOG(", validation state is now " << state << endl);
16,938✔
3579
}
16,938✔
3580

3581
vState SyncRes::getTA(const DNSName& zone, dsset_t& dsSet, const string& prefix)
3582
{
80,720✔
3583
  auto luaLocal = g_luaconfs.getLocal();
80,720✔
3584

3585
  if (luaLocal->dsAnchors.empty()) {
80,720✔
3586
    LOG(prefix << zone << ": No trust anchors configured, everything is Insecure" << endl);
44!
3587
    /* We have no TA, everything is insecure */
3588
    return vState::Insecure;
44✔
3589
  }
44✔
3590

3591
  std::string reason;
80,676✔
3592
  if (haveNegativeTrustAnchor(luaLocal->negAnchors, zone, reason)) {
80,676✔
3593
    LOG(prefix << zone << ": Got NTA" << endl);
15!
3594
    return vState::NTA;
15✔
3595
  }
15✔
3596

3597
  if (getTrustAnchor(luaLocal->dsAnchors, zone, dsSet)) {
80,661✔
3598
    if (!zone.isRoot()) {
23,188✔
3599
      LOG(prefix << zone << ": Got TA" << endl);
144!
3600
    }
144✔
3601
    return vState::TA;
23,188✔
3602
  }
23,188✔
3603

3604
  if (zone.isRoot()) {
57,473!
3605
    /* No TA for the root */
3606
    return vState::Insecure;
×
3607
  }
×
3608

3609
  return vState::Indeterminate;
57,473✔
3610
}
57,473✔
3611

3612
size_t SyncRes::countSupportedDS(const dsset_t& dsset, const string& prefix)
3613
{
23,188✔
3614
  size_t count = 0;
23,188✔
3615

3616
  for (const auto& dsRecordContent : dsset) {
48,649✔
3617
    if (isSupportedDS(dsRecordContent, LogObject(prefix))) {
48,655✔
3618
      count++;
48,643✔
3619
    }
48,643✔
3620
  }
48,649✔
3621

3622
  return count;
23,188✔
3623
}
23,188✔
3624

3625
void SyncRes::initZoneCutsFromTA(const DNSName& from, const string& prefix)
3626
{
22,604✔
3627
  DNSName zone(from);
22,604✔
3628
  do {
74,716✔
3629
    dsset_t dsSet;
74,716✔
3630
    vState result = getTA(zone, dsSet, prefix);
74,716✔
3631
    if (result != vState::Indeterminate) {
74,716✔
3632
      if (result == vState::TA) {
22,760✔
3633
        if (countSupportedDS(dsSet, prefix) == 0) {
22,701✔
3634
          dsSet.clear();
8✔
3635
          result = vState::Insecure;
8✔
3636
        }
8✔
3637
        else {
22,693✔
3638
          result = vState::Secure;
22,693✔
3639
        }
22,693✔
3640
      }
22,701✔
3641
      else if (result == vState::NTA) {
59✔
3642
        result = vState::Insecure;
15✔
3643
      }
15✔
3644

3645
      d_cutStates[zone] = result;
22,760✔
3646
    }
22,760✔
3647
  } while (zone.chopOff());
74,716✔
3648
}
22,604✔
3649

3650
vState SyncRes::getDSRecords(const DNSName& zone, dsset_t& dsSet, bool onlyTA, unsigned int depth, const string& prefix, bool bogusOnNXD, bool* foundCut)
3651
{
6,006✔
3652
  vState result = getTA(zone, dsSet, prefix);
6,006✔
3653

3654
  if (result != vState::Indeterminate || onlyTA) {
6,006!
3655
    if (foundCut != nullptr) {
488!
3656
      *foundCut = (result != vState::Indeterminate);
×
3657
    }
×
3658

3659
    if (result == vState::TA) {
488!
3660
      if (countSupportedDS(dsSet, prefix) == 0) {
488!
3661
        dsSet.clear();
×
3662
        result = vState::Insecure;
×
3663
      }
×
3664
      else {
488✔
3665
        result = vState::Secure;
488✔
3666
      }
488✔
3667
    }
488✔
3668
    else if (result == vState::NTA) {
×
3669
      result = vState::Insecure;
×
3670
    }
×
3671

3672
    return result;
488✔
3673
  }
488✔
3674

3675
  std::set<GetBestNSAnswer> beenthere;
5,518✔
3676
  std::vector<DNSRecord> dsrecords;
5,518✔
3677

3678
  Context context;
5,518✔
3679

3680
  const bool oldCacheOnly = setCacheOnly(false);
5,518✔
3681
  const bool oldQM = setQNameMinimization(!getQMFallbackMode());
5,518✔
3682
  int rcode = doResolve(zone, QType::DS, dsrecords, depth + 1, beenthere, context);
5,518✔
3683
  setCacheOnly(oldCacheOnly);
5,518✔
3684
  setQNameMinimization(oldQM);
5,518✔
3685

3686
  if (rcode == RCode::ServFail) {
5,518✔
3687
    throw ImmediateServFailException("Server Failure while retrieving DS records for " + zone.toLogString());
4✔
3688
  }
4✔
3689

3690
  if (rcode != RCode::NoError && (rcode != RCode::NXDomain || bogusOnNXD)) {
5,514!
3691
    LOG(prefix << zone << ": Returning Bogus state from " << static_cast<const char*>(__func__) << "(" << zone << ")" << endl);
2!
3692
    return vState::BogusUnableToGetDSs;
2✔
3693
  }
2✔
3694

3695
  uint8_t bestDigestType = 0;
5,512✔
3696

3697
  bool gotCNAME = false;
5,512✔
3698
  for (const auto& record : dsrecords) {
15,888✔
3699
    if (record.d_type == QType::DS) {
15,888✔
3700
      const auto dscontent = getRR<DSRecordContent>(record);
3,032✔
3701
      if (dscontent && isSupportedDS(*dscontent, LogObject(prefix))) {
3,032!
3702
        // Make GOST a lower prio than SHA256
3703
        if (dscontent->d_digesttype == DNSSEC::DIGEST_GOST && bestDigestType == DNSSEC::DIGEST_SHA256) {
3,031!
3704
          continue;
×
3705
        }
×
3706
        if (dscontent->d_digesttype > bestDigestType || (bestDigestType == DNSSEC::DIGEST_GOST && dscontent->d_digesttype == DNSSEC::DIGEST_SHA256)) {
3,031!
3707
          bestDigestType = dscontent->d_digesttype;
2,995✔
3708
        }
2,995✔
3709
        dsSet.insert(*dscontent);
3,031✔
3710
      }
3,031✔
3711
    }
3,032✔
3712
    else if (record.d_type == QType::CNAME && record.d_name == zone) {
12,856!
3713
      gotCNAME = true;
13✔
3714
    }
13✔
3715
  }
15,888✔
3716

3717
  /* RFC 4509 section 3: "Validator implementations SHOULD ignore DS RRs containing SHA-1
3718
   * digests if DS RRs with SHA-256 digests are present in the DS RRset."
3719
   * We interpret that as: do not use SHA-1 if SHA-256 or SHA-384 is available
3720
   */
3721
  for (auto dsrec = dsSet.begin(); dsrec != dsSet.end();) {
8,544✔
3722
    if (dsrec->d_digesttype == DNSSEC::DIGEST_SHA1 && dsrec->d_digesttype != bestDigestType) {
3,032✔
3723
      dsrec = dsSet.erase(dsrec);
7✔
3724
    }
7✔
3725
    else {
3,025✔
3726
      ++dsrec;
3,025✔
3727
    }
3,025✔
3728
  }
3,032✔
3729

3730
  if (rcode == RCode::NoError) {
5,512✔
3731
    if (dsSet.empty()) {
5,506✔
3732
      /* we have no DS, it's either:
3733
         - a delegation to a non-DNSSEC signed zone
3734
         - no delegation, we stay in the same zone
3735
      */
3736
      if (gotCNAME || denialProvesNoDelegation(zone, dsrecords, d_validationContext)) {
2,522✔
3737
        /* we are still inside the same zone */
3738

3739
        if (foundCut != nullptr) {
31✔
3740
          *foundCut = false;
25✔
3741
        }
25✔
3742
        return context.state;
31✔
3743
      }
31✔
3744

3745
      d_cutStates[zone] = context.state == vState::Secure ? vState::Insecure : context.state;
2,491✔
3746
      /* delegation with no DS, might be Secure -> Insecure */
3747
      if (foundCut != nullptr) {
2,491✔
3748
        *foundCut = true;
2,468✔
3749
      }
2,468✔
3750

3751
      /* a delegation with no DS is either:
3752
         - a signed zone (Secure) to an unsigned one (Insecure)
3753
         - an unsigned zone to another unsigned one (Insecure stays Insecure, Bogus stays Bogus)
3754
      */
3755
      return context.state == vState::Secure ? vState::Insecure : context.state;
2,491✔
3756
    }
2,522✔
3757
    /* we have a DS */
3758
    d_cutStates[zone] = context.state;
2,984✔
3759
    if (foundCut != nullptr) {
2,984✔
3760
      *foundCut = true;
1,893✔
3761
    }
1,893✔
3762
  }
2,984✔
3763

3764
  return context.state;
2,990✔
3765
}
5,512✔
3766

3767
vState SyncRes::getValidationStatus(const DNSName& name, bool wouldBeValid, bool typeIsDS, unsigned int depth, const string& prefix)
3768
{
18,100✔
3769
  vState result = vState::Indeterminate;
18,100✔
3770

3771
  if (!shouldValidate()) {
18,100✔
3772
    return result;
1,527✔
3773
  }
1,527✔
3774

3775
  DNSName subdomain(name);
16,573✔
3776
  if (typeIsDS) {
16,573✔
3777
    subdomain.chopOff();
3,206✔
3778
  }
3,206✔
3779

3780
  {
16,573✔
3781
    const auto& iter = d_cutStates.find(subdomain);
16,573✔
3782
    if (iter != d_cutStates.cend()) {
16,573✔
3783
      LOG(prefix << name << ": Got status " << iter->second << " for name " << subdomain << endl);
2,971!
3784
      return iter->second;
2,971✔
3785
    }
2,971✔
3786
  }
16,573✔
3787

3788
  /* look for the best match we have */
3789
  DNSName best(subdomain);
13,602✔
3790
  while (best.chopOff()) {
26,064!
3791
    const auto& iter = d_cutStates.find(best);
26,064✔
3792
    if (iter != d_cutStates.cend()) {
26,064✔
3793
      result = iter->second;
13,602✔
3794
      if (vStateIsBogus(result) || result == vState::Insecure) {
13,602✔
3795
        LOG(prefix << name << ": Got status " << result << " for name " << best << endl);
2,919!
3796
        return result;
2,919✔
3797
      }
2,919✔
3798
      break;
10,683✔
3799
    }
13,602✔
3800
  }
26,064✔
3801

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

3812
    while (!labelsToAdd.empty()) {
4,453✔
3813

3814
      dsName.prependRawLabel(labelsToAdd.back());
4,392✔
3815
      labelsToAdd.pop_back();
4,392✔
3816
      LOG(prefix << name << ": - Looking for a DS at " << dsName << endl);
4,392!
3817

3818
      bool foundCut = false;
4,392✔
3819
      dsset_t results;
4,392✔
3820
      vState dsState = getDSRecords(dsName, results, false, depth, prefix, false, &foundCut);
4,392✔
3821

3822
      if (foundCut) {
4,392✔
3823
        LOG(prefix << name << ": - Found cut at " << dsName << endl);
4,360!
3824
        LOG(prefix << name << ": New state for " << dsName << " is " << dsState << endl);
4,360!
3825
        d_cutStates[dsName] = dsState;
4,360✔
3826

3827
        if (dsState != vState::Secure) {
4,360✔
3828
          return dsState;
2,468✔
3829
        }
2,468✔
3830
      }
4,360✔
3831
    }
4,392✔
3832

3833
    /* we did not miss a cut, good luck */
3834
    return result;
61✔
3835
  }
2,529✔
3836

3837
#if 0
3838
  /* we don't need this, we actually do the right thing later */
3839
  DNSName signer = getSigner(signatures);
3840

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

3854
  return result;
8,154✔
3855
}
10,683✔
3856

3857
vState SyncRes::validateDNSKeys(const DNSName& zone, const std::vector<DNSRecord>& dnskeys, const MemRecursorCache::SigRecsVec& signatures, unsigned int depth, const string& prefix)
3858
{
1,595✔
3859
  dsset_t dsSet;
1,595✔
3860
  if (signatures.empty()) {
1,595!
3861
    LOG(prefix << zone << ": We have " << std::to_string(dnskeys.size()) << " DNSKEYs but no signature, going Bogus!" << endl);
×
3862
    return vState::BogusNoRRSIG;
×
3863
  }
×
3864

3865
  DNSName signer = getSigner(signatures);
1,595✔
3866

3867
  if (!signer.empty() && zone.isPartOf(signer)) {
1,595!
3868
    vState state = getDSRecords(signer, dsSet, false, depth, prefix);
1,595✔
3869

3870
    if (state != vState::Secure) {
1,595✔
3871
      return state;
31✔
3872
    }
31✔
3873
  }
1,595✔
3874
  else {
×
3875
    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);
×
3876
    /* try again to get the missed cuts, harder this time */
3877
    auto zState = getValidationStatus(zone, false, false, depth, prefix);
×
3878
    if (zState == vState::Secure) {
×
3879
      /* too bad */
3880
      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);
×
3881
      return vState::BogusNoValidRRSIG;
×
3882
    }
×
3883
    return zState;
×
3884
  }
×
3885

3886
  skeyset_t tentativeKeys;
1,564✔
3887
  sortedRecords_t toSign;
1,564✔
3888

3889
  for (const auto& dnskey : dnskeys) {
1,924✔
3890
    if (dnskey.d_type == QType::DNSKEY) {
1,924!
3891
      auto content = getRR<DNSKEYRecordContent>(dnskey);
1,924✔
3892
      if (content) {
1,924!
3893
        tentativeKeys.insert(content);
1,924✔
3894
        toSign.insert(content);
1,924✔
3895
      }
1,924✔
3896
    }
1,924✔
3897
  }
1,924✔
3898

3899
  LOG(prefix << zone << ": Trying to validate " << std::to_string(tentativeKeys.size()) << " DNSKEYs with " << std::to_string(dsSet.size()) << " DS" << endl);
1,564!
3900
  skeyset_t validatedKeys;
1,564✔
3901
  auto state = validateDNSKeysAgainstDS(d_now.tv_sec, zone, dsSet, tentativeKeys, toSign, signatures, validatedKeys, LogObject(prefix), d_validationContext);
1,564✔
3902

3903
  if (s_maxvalidationsperq != 0 && d_validationContext.d_validationsCounter > s_maxvalidationsperq) {
1,564!
3904
    throw ImmediateServFailException("Server Failure while validating DNSKEYs, too many signature validations for this query");
×
3905
  }
×
3906

3907
  LOG(prefix << zone << ": We now have " << std::to_string(validatedKeys.size()) << " DNSKEYs" << endl);
1,564!
3908

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

3925
  return state;
1,522✔
3926
}
1,564✔
3927

3928
vState SyncRes::getDNSKeys(const DNSName& signer, skeyset_t& keys, bool& servFailOccurred, unsigned int depth, const string& prefix)
3929
{
5,448✔
3930
  std::vector<DNSRecord> records;
5,448✔
3931
  std::set<GetBestNSAnswer> beenthere;
5,448✔
3932
  LOG(prefix << signer << ": Retrieving DNSKEYs" << endl);
5,448!
3933

3934
  Context context;
5,448✔
3935

3936
  const bool oldCacheOnly = setCacheOnly(false);
5,448✔
3937
  int rcode = doResolve(signer, QType::DNSKEY, records, depth + 1, beenthere, context);
5,448✔
3938
  setCacheOnly(oldCacheOnly);
5,448✔
3939

3940
  if (rcode == RCode::ServFail) {
5,448✔
3941
    servFailOccurred = true;
12✔
3942
    return vState::BogusUnableToGetDNSKEYs;
12✔
3943
  }
12✔
3944

3945
  if (rcode == RCode::NoError) {
5,436✔
3946
    if (context.state == vState::Secure) {
5,430✔
3947
      for (const auto& key : records) {
13,297✔
3948
        if (key.d_type == QType::DNSKEY) {
13,297✔
3949
          auto content = getRR<DNSKEYRecordContent>(key);
7,981✔
3950
          if (content) {
7,982✔
3951
            keys.insert(std::move(content));
7,982✔
3952
          }
7,982✔
3953
        }
7,981✔
3954
      }
13,297✔
3955
    }
5,318✔
3956
    LOG(prefix << signer << ": Retrieved " << keys.size() << " DNSKeys, state is " << context.state << endl);
5,430!
3957
    return context.state;
5,430✔
3958
  }
5,430✔
3959

3960
  if (context.state == vState::Insecure) {
6✔
3961
    return context.state;
2✔
3962
  }
2✔
3963

3964
  LOG(prefix << signer << ": Returning Bogus state from " << static_cast<const char*>(__func__) << "(" << signer << ")" << endl);
4!
3965
  return vState::BogusUnableToGetDNSKEYs;
4✔
3966
}
6✔
3967

3968
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)
3969
{
5,510✔
3970
  skeyset_t keys;
5,510✔
3971
  if (signatures.empty()) {
5,510✔
3972
    LOG(prefix << qname << ": Bogus!" << endl);
38!
3973
    return vState::BogusNoRRSIG;
38✔
3974
  }
38✔
3975

3976
  const DNSName signer = getSigner(signatures);
5,472✔
3977
  bool dsFailed = false;
5,472✔
3978
  if (!signer.empty() && name.isPartOf(signer)) {
5,472!
3979
    vState state = vState::Secure;
5,470✔
3980

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

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

4060
  sortedRecords_t recordcontents;
5,324✔
4061
  for (const auto& record : records) {
5,863✔
4062
    recordcontents.insert(record.getContent());
5,863✔
4063
  }
5,863✔
4064

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

4071
  if (state == vState::Secure) {
5,322✔
4072
    LOG(prefix << name << ": Secure!" << endl);
5,290!
4073
    return vState::Secure;
5,290✔
4074
  }
5,290✔
4075

4076
  LOG(prefix << vStateToString(state) << "!" << endl);
32!
4077

4078
  bool skipThisLevelWhenLookingForMissedCuts = false;
32✔
4079
  if (name == qname && qtype == QType::DS && (type == QType::NSEC || type == QType::NSEC3)) {
32!
4080
    /* so we have a NSEC(3) record likely proving that the DS we were looking for does not exist,
4081
       but we cannot validate it:
4082
       - if there actually is a cut at this level, we will not be able to validate it anyway
4083
       - if there is no cut at this level, the only thing that can save us is a cut above
4084
    */
4085
    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!
4086
    skipThisLevelWhenLookingForMissedCuts = true;
2✔
4087
  }
2✔
4088

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

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

4106
  if (wasForwardRecurse || lwr.d_aabit) {
12,653✔
4107
    /* easy */
4108
    return;
9,040✔
4109
  }
9,040✔
4110

4111
  for (const auto& rec : lwr.d_records) {
3,613✔
4112

4113
    if (rec.d_type == QType::OPT) {
3,561!
4114
      continue;
×
4115
    }
×
4116

4117
    if (rec.d_class != QClass::IN) {
3,561!
4118
      continue;
×
4119
    }
×
4120

4121
    if (rec.d_type == QType::ANY) {
3,561!
4122
      continue;
×
4123
    }
×
4124

4125
    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,561!
4126
      /* This is clearly an answer to the question we were asking, from an authoritative server that is allowed to send it.
4127
         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 */
4128
      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!
4129
      lwr.d_aabit = true;
10✔
4130
      return;
10✔
4131
    }
10✔
4132

4133
    if (rec.d_place != DNSResourceRecord::ANSWER) {
3,551!
4134
      /* we have scanned all the records in the answer section, if any, we are done */
4135
      return;
3,551✔
4136
    }
3,551✔
4137
  }
3,551✔
4138
}
3,613✔
4139

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

4193
static bool isRedirection(QType qtype)
4194
{
20,925✔
4195
  return qtype == QType::CNAME || qtype == QType::DNAME;
20,925✔
4196
}
20,925✔
4197

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

4210
  std::vector<bool> skipvec(lwr.d_records.size(), false);
12,653✔
4211
  unsigned int counter = 0;
12,653✔
4212
  unsigned int skipCount = 0;
12,653✔
4213

4214
  for (auto rec = lwr.d_records.cbegin(); rec != lwr.d_records.cend(); ++rec, ++counter) {
85,493✔
4215

4216
    // Allow OPT record containing EDNS(0) data
4217
    if (rec->d_type == QType::OPT) {
72,840✔
4218
      continue;
10,312✔
4219
    }
10,312✔
4220

4221
    // Disallow QClass != IN
4222
    if (rec->d_class != QClass::IN) {
62,528!
4223
      LOG(prefix << qname << ": Removing non internet-classed data received from " << auth << endl);
×
4224
      skipvec[counter] = true;
×
4225
      ++skipCount;
×
4226
      continue;
×
4227
    }
×
4228

4229
    // Disallow QType ANY in responses
4230
    if (rec->d_type == QType::ANY) {
62,528✔
4231
      LOG(prefix << qname << ": Removing 'ANY'-typed data received from " << auth << endl);
2!
4232
      skipvec[counter] = true;
2✔
4233
      ++skipCount;
2✔
4234
      continue;
2✔
4235
    }
2✔
4236

4237
    // Disallow any name not part of auth requested (i.e. disallow x.y.z if asking a NS authoritative for x.w.z)
4238
    if (!rec->d_name.isPartOf(auth)) {
62,526✔
4239
      LOG(prefix << qname << ": Removing record '" << rec->toString() << "' in the " << DNSResourceRecord::placeString(rec->d_place) << " section received from " << auth << endl);
531!
4240
      skipvec[counter] = true;
531✔
4241
      ++skipCount;
531✔
4242
      continue;
531✔
4243
    }
531✔
4244

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

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

4276
      haveAnswers = true;
14,784✔
4277
      if (rec->d_type == QType::CNAME) {
14,784✔
4278
        if (auto cnametarget = getRR<CNAMERecordContent>(*rec); cnametarget != nullptr) {
1,558!
4279
          allowedAnswerNames.insert(cnametarget->getTarget());
1,558✔
4280
        }
1,558✔
4281
        cnameSeen = cnameSeen || qname == rec->d_name;
1,558✔
4282
      }
1,558✔
4283
      else if (rec->d_type == QType::DNAME) {
13,226✔
4284
        // We have checked the DNAME rec->d_name above, the actual answer will be synthesized in a later step
4285
        allowedAnswerNames.insert(rec->d_name);
32✔
4286
      }
32✔
4287
      allowAdditionalEntry(allowedAdditionals, *rec);
14,784✔
4288
    }
14,784✔
4289

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

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

4339
  if (!haveAnswers && lwr.d_rcode == RCode::NoError) {
12,653✔
4340
    acceptDelegation = true;
5,040✔
4341
  }
5,040✔
4342

4343
  sanitizeRecordsPass2(prefix, lwr, qname, qtype, auth, allowedAnswerNames, allowedAdditionals, cnameSeen, acceptDelegation && !soaInAuth, skipvec, skipCount);
12,653✔
4344
}
12,653✔
4345

4346
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)
4347
{
12,653✔
4348
  // Second loop, we know now if the answer was NxDomain or NoData
4349
  unsigned int counter = 0;
12,653✔
4350
  for (auto rec = lwr.d_records.cbegin(); rec != lwr.d_records.cend(); ++rec, ++counter) {
85,492✔
4351

4352
    if (skipvec[counter]) {
72,839✔
4353
      continue;
1,558✔
4354
    }
1,558✔
4355
    // Allow OPT record containing EDNS(0) data
4356
    if (rec->d_type == QType::OPT) {
71,281✔
4357
      continue;
10,311✔
4358
    }
10,311✔
4359

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

4416
void SyncRes::rememberParentSetIfNeeded(const DNSName& domain, const vector<DNSRecord>& newRecords, unsigned int depth, const string& prefix)
4417
{
272✔
4418
  vector<DNSRecord> existing;
272✔
4419
  bool wasAuth = false;
272✔
4420
  auto ttl = g_recCache->get(d_now.tv_sec, domain, QType::NS, MemRecursorCache::None, &existing, d_cacheRemote, d_routingTag, nullptr, nullptr, nullptr, nullptr, &wasAuth);
272✔
4421

4422
  if (ttl <= 0 || wasAuth) {
272✔
4423
    return;
13✔
4424
  }
13✔
4425
  {
259✔
4426
    auto lock = s_savedParentNSSet.lock();
259✔
4427
    if (lock->find(domain) != lock->end()) {
259!
4428
      // no relevant data, or we already stored the parent data
4429
      return;
×
4430
    }
×
4431
  }
259✔
4432

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

4450
  if (shouldSave) {
259✔
4451
    map<DNSName, vector<ComboAddress>> entries;
4✔
4452
    for (const auto& dnsRecord : existing) {
30✔
4453
      auto content = getRR<NSRecordContent>(dnsRecord);
30✔
4454
      const DNSName& name = content->getNS();
30✔
4455
      set<GetBestNSAnswer> beenthereIgnored;
30✔
4456
      unsigned int nretrieveAddressesForNSIgnored{};
30✔
4457
      auto addresses = getAddrs(name, depth, prefix, beenthereIgnored, true, nretrieveAddressesForNSIgnored);
30✔
4458
      entries.emplace(name, addresses);
30✔
4459
    }
30✔
4460
    s_savedParentNSSet.lock()->emplace(domain, std::move(entries), d_now.tv_sec + ttl);
4✔
4461
  }
4✔
4462
}
259✔
4463

4464
RCode::rcodes_ SyncRes::updateCacheFromRecords(unsigned int depth, const string& prefix, LWResult& lwr, const DNSName& qname, const QType qtype, const DNSName& auth, bool wasForwarded, const boost::optional<Netmask>& ednsmask, vState& state, bool& needWildcardProof, bool& gatherWildcardProof, unsigned int& wildcardLabelsCount, bool rdQuery, const ComboAddress& remoteIP) // NOLINT(readability-function-cognitive-complexity)
4465
{
12,653✔
4466
  bool wasForwardRecurse = wasForwarded && rdQuery;
12,653✔
4467
  tcache_t tcache;
12,653✔
4468

4469
  fixupAnswer(prefix, lwr, qname, qtype, auth, wasForwarded, rdQuery);
12,653✔
4470
  sanitizeRecords(prefix, lwr, qname, qtype, auth, wasForwarded, rdQuery);
12,653✔
4471

4472
  MemRecursorCache::AuthRecsVec authorityRecs;
12,653✔
4473
  bool isCNAMEAnswer = false;
12,653✔
4474
  bool isDNAMEAnswer = false;
12,653✔
4475
  DNSName seenAuth;
12,653✔
4476

4477
  // names that might be expanded from a wildcard, and thus require denial of existence proof
4478
  // this is the queried name and any part of the CNAME chain from the queried name
4479
  // the key is the name itself, the value is initially false and is set to true once we have
4480
  // confirmed it was actually expanded from a wildcard
4481
  std::map<DNSName, bool> wildcardCandidates{{qname, false}};
12,653✔
4482

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

4507
  for (auto& rec : lwr.d_records) {
70,501✔
4508
    if (rec.d_type == QType::OPT || rec.d_class != QClass::IN) {
70,501!
4509
      continue;
10,312✔
4510
    }
10,312✔
4511

4512
    rec.d_ttl = min(s_maxcachettl, rec.d_ttl);
60,189✔
4513

4514
    if (!isCNAMEAnswer && rec.d_place == DNSResourceRecord::ANSWER && rec.d_type == QType::CNAME && (!(qtype == QType::CNAME)) && rec.d_name == qname && !isDNAMEAnswer) {
60,189!
4515
      isCNAMEAnswer = true;
1,539✔
4516
    }
1,539✔
4517
    if (!isDNAMEAnswer && rec.d_place == DNSResourceRecord::ANSWER && rec.d_type == QType::DNAME && qtype != QType::DNAME && qname.isPartOf(rec.d_name)) {
60,189!
4518
      isDNAMEAnswer = true;
32✔
4519
      isCNAMEAnswer = false;
32✔
4520
    }
32✔
4521

4522
    if (rec.d_type == QType::SOA && rec.d_place == DNSResourceRecord::AUTHORITY && qname.isPartOf(rec.d_name)) {
60,189!
4523
      seenAuth = rec.d_name;
1,516✔
4524
    }
1,516✔
4525

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

4552
        // cerr<<"Got an RRSIG for "<<DNSRecordContent::NumberToType(rrsig->d_type)<<" with name '"<<rec.d_name<<"' and place "<<rec.d_place<<endl;
4553
        tcache[{rec.d_name, rrsig->d_type, rec.d_place}].signatures.push_back(rrsig);
9,038✔
4554
        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,038✔
4555
      }
9,038✔
4556
    }
9,038✔
4557
  }
60,189✔
4558

4559
  /* if we have a positive answer synthesized from a wildcard,
4560
     we need to store the corresponding NSEC/NSEC3 records proving
4561
     that the exact name did not exist in the negative cache */
4562
  if (gatherWildcardProof) {
12,653✔
4563
    for (const auto& rec : lwr.d_records) {
3,085✔
4564
      if (rec.d_type == QType::OPT || rec.d_class != QClass::IN) {
3,085!
4565
        continue;
591✔
4566
      }
591✔
4567

4568
      if (nsecTypes.count(rec.d_type) != 0) {
2,494✔
4569
        authorityRecs.emplace_back(rec);
621✔
4570
      }
621✔
4571
      else if (rec.d_type == QType::RRSIG) {
1,873✔
4572
        auto rrsig = getRR<RRSIGRecordContent>(rec);
1,246✔
4573
        if (rrsig && nsecTypes.count(rrsig->d_type) != 0) {
1,246!
4574
          authorityRecs.emplace_back(rec);
621✔
4575
        }
621✔
4576
      }
1,246✔
4577
    }
2,494✔
4578
  }
619✔
4579

4580
  // reap all answers from this packet that are acceptable
4581
  for (auto& rec : lwr.d_records) {
70,494✔
4582
    if (rec.d_type == QType::OPT) {
70,494✔
4583
      LOG(prefix << qname << ": OPT answer '" << rec.d_name << "' from '" << auth << "' nameservers" << endl);
10,312!
4584
      continue;
10,312✔
4585
    }
10,312✔
4586

4587
    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 << " ");
60,182✔
4588

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

4591
    if (rec.d_name.isPartOf(auth)) {
60,185✔
4592
      if (rec.d_type == QType::RRSIG) {
60,183✔
4593
        LOG("RRSIG - separate" << endl);
9,037!
4594
      }
9,037✔
4595
      else if (rec.d_type == QType::DS && rec.d_name == auth) {
51,146✔
4596
        LOG("NO - DS provided by child zone" << endl);
2!
4597
      }
2✔
4598
      else {
51,144✔
4599
        bool haveLogged = false;
51,144✔
4600
        if (isDNAMEAnswer && rec.d_type == QType::CNAME) {
51,144✔
4601
          LOG("NO - we already have a DNAME answer for this domain" << endl);
30!
4602
          continue;
30✔
4603
        }
30✔
4604
        if (!t_sstorage.domainmap->empty()) {
51,117✔
4605
          // Check if we are authoritative for a zone in this answer
4606
          DNSName tmp_qname(rec.d_name);
46,765✔
4607
          // We may be auth for domain example.com, but the DS record needs to come from the parent (.com) nameserver
4608
          if (rec.d_type == QType::DS) {
46,765✔
4609
            tmp_qname.chopOff();
949✔
4610
          }
949✔
4611
          auto auth_domain_iter = getBestAuthZone(&tmp_qname);
46,765✔
4612
          if (auth_domain_iter != t_sstorage.domainmap->end() && auth.countLabels() <= auth_domain_iter->first.countLabels()) {
46,765✔
4613
            if (auth_domain_iter->first != auth) {
364✔
4614
              LOG("NO! - we are authoritative for the zone " << auth_domain_iter->first << endl);
2!
4615
              continue;
2✔
4616
            }
2✔
4617
            LOG("YES! - This answer was ");
362!
4618
            if (!wasForwarded) {
362✔
4619
              LOG("retrieved from the local auth store.");
138!
4620
            }
138✔
4621
            else {
224✔
4622
              LOG("received from a server we forward to.");
224!
4623
            }
224✔
4624
            haveLogged = true;
362✔
4625
            LOG(endl);
362!
4626
          }
362✔
4627
        }
46,765✔
4628
        if (!haveLogged) {
51,117✔
4629
          LOG("YES!" << endl);
50,755✔
4630
        }
50,755✔
4631

4632
        rec.d_ttl = min(s_maxcachettl, rec.d_ttl);
51,112✔
4633

4634
        DNSRecord dnsRecord(rec);
51,112✔
4635
        tcache[{rec.d_name, rec.d_type, rec.d_place}].d_ttl_time = d_now.tv_sec;
51,112✔
4636
        dnsRecord.d_ttl += d_now.tv_sec;
51,112✔
4637
        dnsRecord.d_place = DNSResourceRecord::ANSWER;
51,112✔
4638
        tcache[{rec.d_name, rec.d_type, rec.d_place}].records.push_back(std::move(dnsRecord));
51,112✔
4639
      }
51,112✔
4640
    }
60,183✔
4641
    else
2,147,483,647✔
4642
      LOG("NO!" << endl);
2,147,498,679!
4643
  }
60,150✔
4644

4645
  // supplant
4646
  for (auto& entry : tcache) {
36,522✔
4647
    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,522✔
4648
      uint32_t lowestTTD = computeLowestTTD(entry.second.records, entry.second.signatures, entry.second.signaturesTTL, authorityRecs);
12,655✔
4649

4650
      for (auto& record : entry.second.records) {
27,259✔
4651
        record.d_ttl = lowestTTD; // boom
27,259✔
4652
      }
27,259✔
4653
    }
12,655✔
4654
  }
36,522✔
4655

4656
  bool seenBogusRRSet = false;
12,653✔
4657
  for (auto tCacheEntry = tcache.begin(); tCacheEntry != tcache.end(); ++tCacheEntry) {
49,173✔
4658

4659
    if (tCacheEntry->second.records.empty()) { // this happens when we did store signatures, but passed on the records themselves
36,520✔
4660
      continue;
8✔
4661
    }
8✔
4662

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

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

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

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

4733
    vState recordState = vState::Indeterminate;
36,512✔
4734

4735
    if (expectSignature && shouldValidate()) {
36,512✔
4736
      vState initialState = getValidationStatus(tCacheEntry->first.name, !tCacheEntry->second.signatures.empty(), tCacheEntry->first.type == QType::DS, depth, prefix);
10,800✔
4737
      LOG(prefix << qname << ": Got initial zone status " << initialState << " for record " << tCacheEntry->first.name << "|" << DNSRecordContent::NumberToType(tCacheEntry->first.type) << endl);
10,800!
4738

4739
      if (initialState == vState::Secure) {
10,800✔
4740
        if (tCacheEntry->first.type == QType::DNSKEY && tCacheEntry->first.place == DNSResourceRecord::ANSWER && tCacheEntry->first.name == getSigner(tCacheEntry->second.signatures)) {
7,054!
4741
          LOG(prefix << qname << ": Validating DNSKEY for " << tCacheEntry->first.name << endl);
1,593!
4742
          recordState = validateDNSKeys(tCacheEntry->first.name, tCacheEntry->second.records, tCacheEntry->second.signatures, depth, prefix);
1,593✔
4743
        }
1,593✔
4744
        else {
5,461✔
4745
          LOG(prefix << qname << ": Validating non-additional " << QType(tCacheEntry->first.type).toString() << " record for " << tCacheEntry->first.name << endl);
5,461!
4746
          recordState = validateRecordsWithSigs(depth, prefix, qname, qtype, tCacheEntry->first.name, QType(tCacheEntry->first.type), tCacheEntry->second.records, tCacheEntry->second.signatures);
5,461✔
4747
        }
5,461✔
4748
      }
7,054✔
4749
      else {
3,746✔
4750
        recordState = initialState;
3,746✔
4751
        LOG(prefix << qname << ": Skipping validation because the current state is " << recordState << endl);
3,746!
4752
      }
3,746✔
4753

4754
      LOG(prefix << qname << ": Validation result is " << recordState << ", current state is " << state << endl);
10,800!
4755
      if (state != recordState) {
10,800✔
4756
        updateValidationState(qname, state, recordState, prefix);
6,630✔
4757
      }
6,630✔
4758
    }
10,800✔
4759

4760
    if (vStateIsBogus(recordState)) {
36,512✔
4761
      seenBogusRRSet = true;
185✔
4762
      /* this is a TTD by now, be careful */
4763
      for (auto& record : tCacheEntry->second.records) {
502✔
4764
        auto newval = std::min(record.d_ttl, static_cast<uint32_t>(s_maxbogusttl + d_now.tv_sec));
502✔
4765
        record.d_ttl = newval;
502✔
4766
      }
502✔
4767
      tCacheEntry->second.d_ttl_time = d_now.tv_sec;
185✔
4768
    }
185✔
4769

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

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

4795
          if (manyMaskBits) {
6✔
4796
            uint32_t minttl = UINT32_MAX;
2✔
4797
            for (const auto& iter : tCacheEntry->second.records) {
2✔
4798
              if (iter.d_ttl < minttl) {
2!
4799
                minttl = iter.d_ttl;
2✔
4800
              }
2✔
4801
            }
2✔
4802
            bool ttlIsSmall = minttl < s_ecscachelimitttl + d_now.tv_sec;
2✔
4803
            if (ttlIsSmall) {
2!
4804
              // Case: many bits and ttlIsSmall
4805
              doCache = false;
2✔
4806
            }
2✔
4807
          }
2✔
4808
        }
6✔
4809
      }
77✔
4810

4811
      d_fromAuthIP = remoteIP;
32,936✔
4812

4813
      if (doCache) {
32,936✔
4814
        // Check if we are going to replace a non-auth (parent) NS recordset
4815
        if (isAA && tCacheEntry->first.type == QType::NS && s_save_parent_ns_set) {
32,902!
4816
          rememberParentSetIfNeeded(tCacheEntry->first.name, tCacheEntry->second.records, depth, prefix);
272✔
4817
        }
272✔
4818
        bool thisRRNeedsWildcardProof = false;
32,902✔
4819
        if (gatherWildcardProof) {
32,902✔
4820
          if (auto wcIt = wildcardCandidates.find(tCacheEntry->first.name); wcIt != wildcardCandidates.end() && wcIt->second) {
1,220✔
4821
            thisRRNeedsWildcardProof = true;
623✔
4822
          }
623✔
4823
        }
1,220✔
4824
        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 : boost::none, d_routingTag, recordState, remoteIP, d_refresh, tCacheEntry->second.d_ttl_time);
32,902✔
4825

4826
        // Delete potential negcache entry. When a record recovers with serve-stale the negcache entry can cause the wrong entry to
4827
        // be served, as negcache entries are checked before record cache entries
4828
        if (NegCache::s_maxServedStaleExtensions > 0) {
32,902✔
4829
          g_negCache->wipeTyped(tCacheEntry->first.name, tCacheEntry->first.type);
192✔
4830
        }
192✔
4831

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

4838
          if (isWildcardExpanded(labelCount, *rrsig) && !isWildcardExpandedOntoItself(tCacheEntry->first.name, labelCount, *rrsig)) {
595!
4839
            DNSName realOwner = getNSECOwnerName(tCacheEntry->first.name, tCacheEntry->second.signatures);
595✔
4840

4841
            std::vector<DNSRecord> content;
595✔
4842
            content.reserve(tCacheEntry->second.records.size());
595✔
4843
            for (const auto& record : tCacheEntry->second.records) {
595✔
4844
              DNSRecord nonExpandedRecord(record);
595✔
4845
              nonExpandedRecord.d_name = realOwner;
595✔
4846
              content.push_back(std::move(nonExpandedRecord));
595✔
4847
            }
595✔
4848

4849
            g_recCache->replace(d_now.tv_sec, realOwner, QType(tCacheEntry->first.type), content, tCacheEntry->second.signatures, /* no additional records in that case */ {}, tCacheEntry->first.type == QType::DS ? true : isAA, auth, boost::none, boost::none, recordState, remoteIP, d_refresh, tCacheEntry->second.d_ttl_time);
595!
4850
          }
595✔
4851
        }
595✔
4852
      }
32,902✔
4853
    }
32,936✔
4854

4855
    if (seenAuth.empty() && !tCacheEntry->second.signatures.empty()) {
36,512✔
4856
      seenAuth = getSigner(tCacheEntry->second.signatures);
5,807✔
4857
    }
5,807✔
4858

4859
    if (g_aggressiveNSECCache && (tCacheEntry->first.type == QType::NSEC || tCacheEntry->first.type == QType::NSEC3) && recordState == vState::Secure && !seenAuth.empty()) {
36,512!
4860
      // Good candidate for NSEC{,3} caching
4861
      g_aggressiveNSECCache->insertNSEC(seenAuth, tCacheEntry->first.name, tCacheEntry->second.records.at(0), tCacheEntry->second.signatures, tCacheEntry->first.type == QType::NSEC3, qname, qtype);
2,897✔
4862
    }
2,897✔
4863

4864
    if (tCacheEntry->first.place == DNSResourceRecord::ANSWER && ednsmask) {
36,512✔
4865
      d_wasVariable = true;
77✔
4866
    }
77✔
4867
  }
36,512✔
4868

4869
  if (gatherWildcardProof) {
12,653✔
4870
    if (auto wcIt = wildcardCandidates.find(qname); wcIt != wildcardCandidates.end() && !wcIt->second) {
619!
4871
      // 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
4872
      gatherWildcardProof = false;
2✔
4873
    }
2✔
4874
  }
619✔
4875

4876
  return RCode::NoError;
12,653✔
4877
}
12,653✔
4878

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

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

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

4923
dState SyncRes::getDenialValidationState(const NegCache::NegCacheEntry& negEntry, const dState expectedState, bool referralToUnsigned, const string& prefix)
4924
{
2,391✔
4925
  cspmap_t csp = harvestCSPFromNE(negEntry);
2,391✔
4926
  return getDenial(csp, negEntry.d_name, negEntry.d_qtype.getCode(), referralToUnsigned, expectedState == dState::NXQTYPE, d_validationContext, LogObject(prefix));
2,391✔
4927
}
2,391✔
4928

4929
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)
4930
{
615✔
4931
  /* positive answer synthesized from a wildcard */
4932
  NegCache::NegCacheEntry negEntry;
615✔
4933
  negEntry.d_name = qname;
615✔
4934
  negEntry.d_qtype = QType::ENT; // this encodes 'whole record'
615✔
4935
  uint32_t lowestTTL = rec.d_ttl;
615✔
4936
  harvestNXRecords(lwr.d_records, negEntry, d_now.tv_sec, &lowestTTL);
615✔
4937

4938
  if (vStateIsBogus(state)) {
615!
4939
    negEntry.d_validationState = state;
×
4940
  }
×
4941
  else {
615✔
4942
    auto recordState = getValidationStatus(qname, !negEntry.authoritySOA.signatures.empty() || !negEntry.DNSSECRecords.signatures.empty(), false, depth, prefix);
615✔
4943

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

4964
        updateValidationState(qname, state, tmpState, prefix);
8✔
4965
        /* we already stored the record with a different validation status, let's fix it */
4966
        updateValidationStatusInCache(qname, qtype, lwr.d_aabit, tmpState);
8✔
4967
      }
8✔
4968
    }
611✔
4969
  }
615✔
4970
}
615✔
4971

4972
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)
4973
{
12,635✔
4974
  bool done = false;
12,635✔
4975
  DNSName dnameTarget;
12,635✔
4976
  DNSName dnameOwner;
12,635✔
4977
  uint32_t dnameTTL = 0;
12,635✔
4978
  bool referralOnDS = false;
12,635✔
4979

4980
  for (auto& rec : lwr.d_records) {
70,420✔
4981
    if (rec.d_type == QType::OPT || rec.d_class != QClass::IN) {
70,420✔
4982
      continue;
10,312✔
4983
    }
10,312✔
4984

4985
    if (rec.d_place == DNSResourceRecord::ANSWER && !(lwr.d_aabit || sendRDQuery)) {
60,108!
4986
      /* for now we allow a CNAME for the exact qname in ANSWER with AA=0, because Amazon DNS servers
4987
         are sending such responses */
4988
      if (rec.d_type != QType::CNAME || rec.d_name != qname) {
×
4989
        continue;
×
4990
      }
×
4991
    }
×
4992
    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);
60,108!
4993

4994
    bool putInNegCache = true;
60,108✔
4995
    if (negCacheIndication && qtype == QType::DS && isForwardOrAuth(qname)) {
60,108✔
4996
      // #10189, a NXDOMAIN to a DS query for a forwarded or auth domain should not NXDOMAIN the whole domain
4997
      putInNegCache = false;
7✔
4998
    }
7✔
4999

5000
    if (negCacheIndication) {
60,108✔
5001
      LOG(prefix << qname << ": Got negative caching indication for name '" << qname << "' (accept=" << rec.d_name.isPartOf(auth) << "), newtarget='" << newtarget << "'" << endl);
125!
5002

5003
      rec.d_ttl = min(rec.d_ttl, s_maxnegttl);
125✔
5004
      // only add a SOA if we're not going anywhere after this
5005
      if (newtarget.empty()) {
125✔
5006
        ret.push_back(rec);
119✔
5007
      }
119✔
5008

5009
      NegCache::NegCacheEntry negEntry;
125✔
5010

5011
      uint32_t lowestTTL = rec.d_ttl;
125✔
5012
      /* if we get an NXDomain answer with a CNAME, the name
5013
         does exist but the target does not */
5014
      negEntry.d_name = newtarget.empty() ? qname : newtarget;
125✔
5015
      negEntry.d_qtype = QType::ENT; // this encodes 'whole record'
125✔
5016
      negEntry.d_auth = rec.d_name;
125✔
5017
      harvestNXRecords(lwr.d_records, negEntry, d_now.tv_sec, &lowestTTL);
125✔
5018

5019
      if (vStateIsBogus(state)) {
125✔
5020
        negEntry.d_validationState = state;
7✔
5021
      }
7✔
5022
      else {
118✔
5023
        /* here we need to get the validation status of the zone telling us that the domain does not
5024
           exist, ie the owner of the SOA */
5025
        auto recordState = getValidationStatus(rec.d_name, !negEntry.authoritySOA.signatures.empty() || !negEntry.DNSSECRecords.signatures.empty(), false, depth, prefix);
118!
5026
        if (recordState == vState::Secure) {
118✔
5027
          dState denialState = getDenialValidationState(negEntry, dState::NXDOMAIN, false, prefix);
58✔
5028
          updateDenialValidationState(qname, negEntry.d_validationState, negEntry.d_name, state, denialState, dState::NXDOMAIN, false, depth, prefix);
58✔
5029
        }
58✔
5030
        else {
60✔
5031
          negEntry.d_validationState = recordState;
60✔
5032
          updateValidationState(qname, state, negEntry.d_validationState, prefix);
60✔
5033
        }
60✔
5034
      }
118✔
5035

5036
      if (vStateIsBogus(negEntry.d_validationState)) {
125✔
5037
        lowestTTL = min(lowestTTL, s_maxbogusttl);
7✔
5038
      }
7✔
5039

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

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

5117
      done = true;
9,535✔
5118
      rcode = RCode::NoError;
9,535✔
5119

5120
      if (needWildcardProof) {
9,535✔
5121
        checkWildcardProof(qname, qtype, rec, lwr, state, depth, prefix, wildcardLabelsCount);
603✔
5122
      }
603✔
5123

5124
      ret.push_back(rec);
9,535✔
5125
    }
9,535✔
5126
    else if ((rec.d_type == QType::RRSIG || rec.d_type == QType::NSEC || rec.d_type == QType::NSEC3) && rec.d_place == DNSResourceRecord::ANSWER) {
47,638✔
5127
      if (rec.d_type != QType::RRSIG || rec.d_name == qname) {
3,016!
5128
        ret.push_back(rec); // enjoy your DNSSEC
2,983✔
5129
      }
2,983✔
5130
      else if (rec.d_type == QType::RRSIG && qname.isPartOf(rec.d_name)) {
33!
5131
        auto rrsig = getRR<RRSIGRecordContent>(rec);
24✔
5132
        if (rrsig != nullptr && rrsig->d_type == QType::DNAME) {
24!
5133
          ret.push_back(rec);
24✔
5134
        }
24✔
5135
      }
24✔
5136
    }
3,016✔
5137
    else if (rec.d_place == DNSResourceRecord::AUTHORITY && rec.d_type == QType::NS && qname.isPartOf(rec.d_name)) {
44,622!
5138
      if (moreSpecificThan(rec.d_name, auth)) {
13,990!
5139
        newauth = rec.d_name;
13,990✔
5140
        LOG(prefix << qname << ": Got NS record '" << rec.d_name << "' -> '" << rec.getContent()->getZoneRepresentation() << "'" << endl);
13,990✔
5141

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

5170
      if (!vStateIsBogus(state)) {
3,387!
5171
        auto recordState = getValidationStatus(newauth, !negEntry.authoritySOA.signatures.empty() || !negEntry.DNSSECRecords.signatures.empty(), true, depth, prefix);
3,387!
5172

5173
        if (recordState == vState::Secure) {
3,387✔
5174
          negEntry.d_auth = auth;
2,132✔
5175
          negEntry.d_name = newauth;
2,132✔
5176
          negEntry.d_qtype = QType::DS;
2,132✔
5177
          rec.d_ttl = min(s_maxnegttl, rec.d_ttl);
2,132✔
5178

5179
          dState denialState = getDenialValidationState(negEntry, dState::NXQTYPE, true, prefix);
2,132✔
5180

5181
          if (denialState == dState::NXQTYPE || denialState == dState::OPTOUT || denialState == dState::INSECURE) {
2,132!
5182
            negEntry.d_ttd = lowestTTL + d_now.tv_sec;
2,128✔
5183
            negEntry.d_orig_ttl = lowestTTL;
2,128✔
5184
            negEntry.d_validationState = vState::Secure;
2,128✔
5185
            if (denialState == dState::OPTOUT) {
2,128✔
5186
              negEntry.d_validationState = vState::Insecure;
2,044✔
5187
            }
2,044✔
5188
            LOG(prefix << qname << ": Got negative indication of DS record for '" << newauth << "'" << endl);
2,128!
5189

5190
            g_negCache->add(negEntry);
2,128✔
5191

5192
            /* 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
5193
               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
5194
               query. */
5195
            if (qtype == QType::DS && qname == newauth && (d_externalDSQuery.empty() || qname != d_externalDSQuery)) {
2,128!
5196
              /* we are actually done! */
5197
              negindic = true;
×
5198
              negIndicHasSignatures = !negEntry.authoritySOA.signatures.empty() || !negEntry.DNSSECRecords.signatures.empty();
×
5199
              nsset.clear();
×
5200
            }
×
5201
          }
2,128✔
5202
        }
2,132✔
5203
      }
3,387✔
5204
    }
3,387✔
5205
    else if (!done && rec.d_place == DNSResourceRecord::AUTHORITY && rec.d_type == QType::SOA && lwr.d_rcode == RCode::NoError && qname.isPartOf(rec.d_name)) {
26,122!
5206
      LOG(prefix << qname << ": Got negative caching indication for '" << qname << "|" << qtype << "'" << endl);
1,387!
5207

5208
      if (!newtarget.empty()) {
1,387✔
5209
        LOG(prefix << qname << ": Hang on! Got a redirect to '" << newtarget << "' already" << endl);
5!
5210
      }
5✔
5211
      else {
1,382✔
5212
        rec.d_ttl = min(s_maxnegttl, rec.d_ttl);
1,382✔
5213

5214
        NegCache::NegCacheEntry negEntry;
1,382✔
5215
        negEntry.d_auth = rec.d_name;
1,382✔
5216
        uint32_t lowestTTL = rec.d_ttl;
1,382✔
5217
        negEntry.d_name = qname;
1,382✔
5218
        negEntry.d_qtype = qtype;
1,382✔
5219
        harvestNXRecords(lwr.d_records, negEntry, d_now.tv_sec, &lowestTTL);
1,382✔
5220

5221
        if (vStateIsBogus(state)) {
1,382✔
5222
          negEntry.d_validationState = state;
7✔
5223
        }
7✔
5224
        else {
1,375✔
5225
          auto recordState = getValidationStatus(qname, !negEntry.authoritySOA.signatures.empty() || !negEntry.DNSSECRecords.signatures.empty(), qtype == QType::DS, depth, prefix);
1,375!
5226
          if (recordState == vState::Secure) {
1,375✔
5227
            dState denialState = getDenialValidationState(negEntry, dState::NXQTYPE, false, prefix);
195✔
5228
            updateDenialValidationState(qname, negEntry.d_validationState, negEntry.d_name, state, denialState, dState::NXQTYPE, qtype == QType::DS, depth, prefix);
195✔
5229
          }
195✔
5230
          else {
1,180✔
5231
            negEntry.d_validationState = recordState;
1,180✔
5232
            updateValidationState(qname, state, negEntry.d_validationState, prefix);
1,180✔
5233
          }
1,180✔
5234
        }
1,375✔
5235

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

5251
        ret.push_back(rec);
1,382✔
5252
        negindic = true;
1,382✔
5253
        negIndicHasSignatures = !negEntry.authoritySOA.signatures.empty() || !negEntry.DNSSECRecords.signatures.empty();
1,382!
5254
      }
1,382✔
5255
    }
1,387✔
5256
  }
60,108✔
5257

5258
  if (!dnameTarget.empty()) {
12,635✔
5259
    // Synthesize a CNAME
5260
    auto cnamerec = DNSRecord();
32✔
5261
    cnamerec.d_name = qname;
32✔
5262
    cnamerec.d_type = QType::CNAME;
32✔
5263
    cnamerec.d_ttl = dnameTTL;
32✔
5264
    cnamerec.setContent(std::make_shared<CNAMERecordContent>(CNAMERecordContent(newtarget)));
32✔
5265
    ret.push_back(std::move(cnamerec));
32✔
5266
  }
32✔
5267

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

5284
  return done;
12,635✔
5285
}
12,635✔
5286

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

5321
static bool shouldDoDoT(ComboAddress address, time_t now)
5322
{
×
5323
  address.setPort(853);
×
5324
  auto lock = s_dotMap.lock();
×
5325
  auto iter = lock->d_map.find(address);
×
5326
  if (iter == lock->d_map.end()) {
×
5327
    return false;
×
5328
  }
×
5329
  iter->d_count++;
×
5330
  return iter->d_status == DoTStatus::Good && iter->d_ttd > now;
×
5331
}
×
5332

5333
static void updateDoTStatus(ComboAddress address, DoTStatus::Status status, time_t time, bool updateBusy = false)
5334
{
×
5335
  address.setPort(853);
×
5336
  auto lock = s_dotMap.lock();
×
5337
  auto iter = lock->d_map.find(address);
×
5338
  if (iter != lock->d_map.end()) {
×
5339
    iter->d_status = status;
×
5340
    lock->d_map.modify(iter, [=](DoTStatus& statusToModify) { statusToModify.d_ttd = time; });
×
5341
    if (updateBusy) {
×
5342
      --lock->d_numBusy;
×
5343
    }
×
5344
  }
×
5345
}
×
5346

5347
bool SyncRes::tryDoT(const DNSName& qname, const QType qtype, const DNSName& nsName, ComboAddress address, time_t now)
5348
{
×
5349
  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));
×
5350

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

5388
void SyncRes::ednsStats(boost::optional<Netmask>& ednsmask, const DNSName& qname, const string& prefix)
5389
{
13,012✔
5390
  if (!ednsmask) {
13,012✔
5391
    return;
12,932✔
5392
  }
12,932✔
5393
  s_ecsresponses++;
80✔
5394
  LOG(prefix << qname << ": Received EDNS Client Subnet Mask " << ednsmask->toString() << " on response" << endl);
80!
5395

5396
  if (ednsmask->getBits() > 0) {
80✔
5397
    if (ednsmask->isIPv4()) {
79✔
5398
      ++SyncRes::s_ecsResponsesBySubnetSize4.at(ednsmask->getBits() - 1);
67✔
5399
    }
67✔
5400
    else {
12✔
5401
      ++SyncRes::s_ecsResponsesBySubnetSize6.at(ednsmask->getBits() - 1);
12✔
5402
    }
12✔
5403
  }
79✔
5404
}
80✔
5405

5406
void SyncRes::updateQueryCounts(const string& prefix, const DNSName& qname, const ComboAddress& address, bool doTCP, bool doDoT)
5407
{
13,286✔
5408
  t_Counters.at(rec::Counter::outqueries)++;
13,286✔
5409
  d_outqueries++;
13,286✔
5410
  checkMaxQperQ(qname);
13,286✔
5411
  if (address.sin4.sin_family == AF_INET6) {
13,286✔
5412
    t_Counters.at(rec::Counter::ipv6queries)++;
790✔
5413
  }
790✔
5414
  if (doTCP) {
13,286✔
5415
    if (doDoT) {
45✔
5416
      LOG(prefix << qname << ": Using DoT with " << address.toStringWithPort() << endl);
25!
5417
      t_Counters.at(rec::Counter::dotoutqueries)++;
25✔
5418
      d_dotoutqueries++;
25✔
5419
    }
25✔
5420
    else {
20✔
5421
      LOG(prefix << qname << ": Using TCP with " << address.toStringWithPort() << endl);
20!
5422
      t_Counters.at(rec::Counter::tcpoutqueries)++;
20✔
5423
      d_tcpoutqueries++;
20✔
5424
    }
20✔
5425
  }
45✔
5426
}
13,286✔
5427

5428
void SyncRes::incTimeoutStats(const ComboAddress& remoteIP)
5429
{
326✔
5430
  d_timeouts++;
326✔
5431
  t_Counters.at(rec::Counter::outgoingtimeouts)++;
326✔
5432

5433
  if (remoteIP.sin4.sin_family == AF_INET) {
326✔
5434
    t_Counters.at(rec::Counter::outgoing4timeouts)++;
175✔
5435
  }
175✔
5436
  else {
151✔
5437
    t_Counters.at(rec::Counter::outgoing6timeouts)++;
151✔
5438
  }
151✔
5439

5440
  if (t_timeouts) {
326✔
5441
    t_timeouts->push_back(remoteIP);
9✔
5442
  }
9✔
5443
}
326✔
5444

5445
void SyncRes::checkTotalTime(const DNSName& qname, QType qtype, boost::optional<EDNSExtendedError>& extendedError) const
5446
{
13,024✔
5447
  if (s_maxtotusec != 0 && d_totUsec > s_maxtotusec) {
13,024!
5448
    if (s_addExtendedResolutionDNSErrors) {
2!
5449
      extendedError = EDNSExtendedError{static_cast<uint16_t>(EDNSExtendedError::code::NoReachableAuthority), "Timeout waiting for answer(s)"};
×
5450
    }
×
5451
    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✔
5452
  }
2✔
5453
}
13,024✔
5454

5455
bool SyncRes::doResolveAtThisIP(const std::string& prefix, const DNSName& qname, const QType qtype, LWResult& lwr, boost::optional<Netmask>& ednsmask, const DNSName& auth, bool const sendRDQuery, const bool wasForwarded, const DNSName& nsName, const ComboAddress& remoteIP, bool doTCP, bool doDoT, bool& truncated, bool& spoofed, boost::optional<EDNSExtendedError>& extendedError, bool dontThrottle)
5456
{
13,024✔
5457
  checkTotalTime(qname, qtype, extendedError);
13,024✔
5458

5459
  bool chained = false;
13,024✔
5460
  LWResult::Result resolveret = LWResult::Result::Success;
13,024✔
5461
  int preOutQueryRet = RCode::NoError;
13,024✔
5462

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

5491
  /* preoutquery killed the query by setting dq.rcode to -3 */
5492
  if (preOutQueryRet == -3) {
13,024✔
5493
    throw ImmediateServFailException("Query killed by policy");
4✔
5494
  }
4✔
5495

5496
  d_totUsec += lwr.d_usec;
13,020✔
5497

5498
  if (resolveret == LWResult::Result::Spoofed || resolveret == LWResult::Result::BadCookie) {
13,020✔
5499
    spoofed = true;
3✔
5500
    return false;
3✔
5501
  }
3✔
5502

5503
  accountAuthLatency(lwr.d_usec, remoteIP.sin4.sin_family);
13,017✔
5504
  if (lwr.d_rcode >= 0 && lwr.d_rcode < static_cast<decltype(lwr.d_rcode)>(t_Counters.at(rec::RCode::auth).rcodeCounters.size())) {
13,017!
5505
    ++t_Counters.at(rec::RCode::auth).rcodeCounters.at(static_cast<uint8_t>(lwr.d_rcode));
13,009✔
5506
  }
13,009✔
5507

5508
  if (!dontThrottle) {
13,017✔
5509
    dontThrottle = shouldNotThrottle(&nsName, &remoteIP);
13,007✔
5510
  }
13,007✔
5511

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

5538
    // don't account for resource limits, they are our own fault
5539
    // And don't throttle when the IP address is on the dontThrottleNetmasks list or the name is part of dontThrottleNames
5540
    if (!LWResult::isLimitError(resolveret) && !chained && !dontThrottle) {
401!
5541
      uint32_t responseUsec = 1000000; // 1 sec for non-timeout cases
393✔
5542
      // Use the actual time if we saw a timeout
5543
      if (resolveret == LWResult::Result::Timeout) {
393✔
5544
        responseUsec = lwr.d_usec;
326✔
5545
      }
326✔
5546

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

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

5570
    return false;
401✔
5571
  }
401✔
5572

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

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

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

5619
  /* this server sent a valid answer, mark it backup up if it was down */
5620
  if (s_serverdownmaxfails > 0) {
12,540✔
5621
    s_fails.lock()->clear(remoteIP);
12,532✔
5622
  }
12,532✔
5623
  // Clear all throttles for this IP, both general and specific throttles for qname-qtype
5624
  unThrottle(remoteIP, qname, qtype);
12,540✔
5625

5626
  if (lwr.d_tcbit) {
12,540✔
5627
    truncated = true;
17✔
5628

5629
    if (doTCP) {
17✔
5630
      LOG(prefix << qname << ": Truncated bit set, over TCP?" << endl);
2!
5631
      if (!dontThrottle) {
2!
5632
        /* let's treat that as a ServFail answer from this server */
5633
        doThrottle(d_now.tv_sec, remoteIP, qname, qtype, 60, 3, Throttle::Reason::TCPTruncate);
2✔
5634
      }
2✔
5635
      return false;
2✔
5636
    }
2✔
5637
    LOG(prefix << qname << ": Truncated bit set, over UDP" << endl);
15!
5638

5639
    return true;
15✔
5640
  }
17✔
5641

5642
  return true;
12,523✔
5643
}
12,540✔
5644

5645
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)
5646
{
1,560✔
5647
  if (newtarget == qname) {
1,560✔
5648
    LOG(prefix << qname << ": Status=got a CNAME referral to self, returning SERVFAIL" << endl);
3!
5649
    ret.clear();
3✔
5650
    rcode = RCode::ServFail;
3✔
5651
    return;
3✔
5652
  }
3✔
5653
  if (newtarget.isPartOf(qname)) {
1,557✔
5654
    // a.b.c. CNAME x.a.b.c will go to great depths with QM on
5655
    LOG(prefix << qname << ": Status=got a CNAME referral to child, disabling QM" << endl);
21!
5656
    setQNameMinimization(false);
21✔
5657
  }
21✔
5658

5659
  if (!d_followCNAME) {
1,557✔
5660
    rcode = RCode::NoError;
155✔
5661
    return;
155✔
5662
  }
155✔
5663

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

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

5681
    if (d_doDNSSEC) {
4!
5682
      addNXNSECS(ret, recordsFromAnswer);
4✔
5683
    }
4✔
5684

5685
    rcode = RCode::NoError;
4✔
5686
    return;
4✔
5687
  }
4✔
5688

5689
  LOG(prefix << qname << ": Status=got a CNAME referral, starting over with " << newtarget << endl);
1,394✔
5690

5691
  set<GetBestNSAnswer> beenthere;
1,394✔
5692
  Context cnameContext;
1,394✔
5693
  rcode = doResolve(newtarget, qtype, ret, depth + 1, beenthere, cnameContext);
1,394✔
5694
  LOG(prefix << qname << ": Updating validation state for response to " << qname << " from " << state << " with the state from the CNAME quest: " << cnameContext.state << endl);
1,394✔
5695
  updateValidationState(qname, state, cnameContext.state, prefix);
1,394✔
5696
}
1,394✔
5697

5698
bool SyncRes::processAnswer(unsigned int depth, const string& prefix, LWResult& lwr, const DNSName& qname, const QType qtype, DNSName& auth, bool wasForwarded, const boost::optional<Netmask>& ednsmask, bool sendRDQuery, NsSet& nameservers, std::vector<DNSRecord>& ret, const DNSFilterEngine& dfe, bool* gotNewServers, int* rcode, vState& state, const ComboAddress& remoteIP)
5699
{
12,653✔
5700
  if (s_minimumTTL != 0) {
12,653✔
5701
    for (auto& rec : lwr.d_records) {
66,799✔
5702
      rec.d_ttl = max(rec.d_ttl, s_minimumTTL);
66,799✔
5703
    }
66,799✔
5704
  }
10,421✔
5705

5706
  /* if the answer is ECS-specific, a minimum TTL is set for this kind of answers
5707
     and it's higher than the global minimum TTL */
5708
  if (ednsmask && s_minimumECSTTL > 0 && (s_minimumTTL == 0 || s_minimumECSTTL > s_minimumTTL)) {
12,653!
5709
    for (auto& rec : lwr.d_records) {
2✔
5710
      if (rec.d_place == DNSResourceRecord::ANSWER) {
2!
5711
        rec.d_ttl = max(rec.d_ttl, s_minimumECSTTL);
2✔
5712
      }
2✔
5713
    }
2✔
5714
  }
2✔
5715

5716
  bool needWildcardProof = false;
12,653✔
5717
  bool gatherWildcardProof = false;
12,653✔
5718
  unsigned int wildcardLabelsCount = 0;
12,653✔
5719
  *rcode = updateCacheFromRecords(depth, prefix, lwr, qname, qtype, auth, wasForwarded, ednsmask, state, needWildcardProof, gatherWildcardProof, wildcardLabelsCount, sendRDQuery, remoteIP);
12,653✔
5720
  if (*rcode != RCode::NoError) {
12,653!
5721
    return true;
×
5722
  }
×
5723

5724
  LOG(prefix << qname << ": Determining status after receiving this packet" << endl);
12,653✔
5725

5726
  set<DNSName> nsset;
12,653✔
5727
  bool realreferral = false;
12,653✔
5728
  bool negindic = false;
12,653✔
5729
  bool negIndicHasSignatures = false;
12,653✔
5730
  DNSName newauth;
12,653✔
5731
  DNSName newtarget;
12,653✔
5732

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

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

5746
  if (!newtarget.empty()) {
6,743✔
5747
    handleNewTarget(prefix, qname, newtarget, qtype.getCode(), ret, *rcode, depth, lwr.d_records, state);
1,550✔
5748
    return true;
1,550✔
5749
  }
1,550✔
5750

5751
  if (lwr.d_rcode == RCode::NXDomain) {
5,193✔
5752
    LOG(prefix << qname << ": Status=NXDOMAIN, we are done " << (negindic ? "(have negative SOA)" : "") << endl);
135!
5753

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

5765
    if (d_doDNSSEC) {
135✔
5766
      addNXNSECS(ret, lwr.d_records);
101✔
5767
    }
101✔
5768

5769
    *rcode = RCode::NXDomain;
135✔
5770
    return true;
135✔
5771
  }
135✔
5772

5773
  if (nsset.empty() && lwr.d_rcode == 0 && (negindic || lwr.d_aabit || sendRDQuery)) {
5,058!
5774
    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,429!
5775

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

5787
    if (d_doDNSSEC) {
1,429✔
5788
      addNXNSECS(ret, lwr.d_records);
1,359✔
5789
    }
1,359✔
5790

5791
    *rcode = RCode::NoError;
1,429✔
5792
    return true;
1,429✔
5793
  }
1,429✔
5794

5795
  if (realreferral) {
3,629✔
5796
    LOG(prefix << qname << ": Status=did not resolve, got " << (unsigned int)nsset.size() << " NS, ");
3,557✔
5797

5798
    nameservers.clear();
3,557✔
5799
    for (auto const& nameserver : nsset) {
13,986✔
5800
      if (d_wantsRPZ && !d_appliedPolicy.wasHit()) {
13,986✔
5801
        bool match = dfe.getProcessingPolicy(nameserver, d_discardedPolicies, d_appliedPolicy);
13,982✔
5802
        if (match) {
13,982✔
5803
          mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());
2✔
5804
          if (d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) { // client query needs an RPZ response
2!
5805
            if (d_pdl && d_pdl->policyHitEventFilter(d_requestor, qname, qtype, d_queryReceivedOverTCP, d_appliedPolicy, d_policyTags, d_discardedPolicies)) {
2!
5806
              /* reset to no match */
5807
              d_appliedPolicy = DNSFilterEngine::Policy();
×
5808
            }
×
5809
            else {
2✔
5810
              LOG("however " << nameserver << " was blocked by RPZ policy '" << d_appliedPolicy.getName() << "'" << endl);
2!
5811
              throw PolicyHitException();
2✔
5812
            }
2✔
5813
          }
2✔
5814
        }
2✔
5815
      }
13,982✔
5816
      nameservers.insert({nameserver, {{}, false}});
13,984✔
5817
    }
13,984✔
5818
    LOG("looping to them" << endl);
3,555✔
5819
    *gotNewServers = true;
3,555✔
5820
    auth = std::move(newauth);
3,555✔
5821

5822
    return false;
3,555✔
5823
  }
3,557✔
5824

5825
  return false;
72✔
5826
}
3,629✔
5827

5828
bool SyncRes::doDoTtoAuth(const DNSName& nameServer)
5829
{
13,006✔
5830
  return g_DoTToAuthNames.getLocal()->check(nameServer);
13,006✔
5831
}
13,006✔
5832

5833
/** returns:
5834
 *  -1 in case of no results
5835
 *  rcode otherwise
5836
 */
5837
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
5838
int SyncRes::doResolveAt(NsSet& nameservers, DNSName auth, bool flawedNSSet, const DNSName& qname, const QType qtype,
5839
                         vector<DNSRecord>& ret,
5840
                         unsigned int depth, const string& prefix, set<GetBestNSAnswer>& beenthere, Context& context, StopAtDelegation* stopAtDelegation,
5841
                         map<DNSName, vector<ComboAddress>>* fallBack)
5842
{
10,932✔
5843
  auto luaconfsLocal = g_luaconfs.getLocal();
10,932✔
5844

5845
  LOG(prefix << qname << ": Cache consultations done, have " << (unsigned int)nameservers.size() << " NS to contact");
10,932✔
5846

5847
  if (nameserversBlockedByRPZ(luaconfsLocal->dfe, nameservers)) {
10,932✔
5848
    /* RPZ hit */
5849
    if (d_pdl && d_pdl->policyHitEventFilter(d_requestor, qname, qtype, d_queryReceivedOverTCP, d_appliedPolicy, d_policyTags, d_discardedPolicies)) {
2!
5850
      /* reset to no match */
5851
      d_appliedPolicy = DNSFilterEngine::Policy();
×
5852
    }
×
5853
    else {
2✔
5854
      throw PolicyHitException();
2✔
5855
    }
2✔
5856
  }
2✔
5857

5858
  LOG(endl);
10,930✔
5859

5860
  unsigned int addressQueriesForNS = 0;
10,930✔
5861
  for (;;) { // we may get more specific nameservers
12,954✔
5862
    auto rnameservers = shuffleInSpeedOrder(qname, nameservers, prefix);
12,954✔
5863

5864
    // We allow s_maxnsaddressqperq (default 10) queries with empty responses when resolving NS names.
5865
    // If a zone publishes many (more than s_maxnsaddressqperq) NS records, we allow less.
5866
    // This is to "punish" zones that publish many non-resolving NS names.
5867
    // We always allow 5 NS name resolving attempts with empty results.
5868
    unsigned int nsLimit = s_maxnsaddressqperq;
12,954✔
5869
    if (rnameservers.size() > nsLimit) {
12,954✔
5870
      int newLimit = static_cast<int>(nsLimit - (rnameservers.size() - nsLimit));
2,793✔
5871
      nsLimit = std::max(5, newLimit);
2,793✔
5872
    }
2,793✔
5873

5874
    for (auto tns = rnameservers.cbegin();; ++tns) {
2,147,489,336✔
5875
      if (addressQueriesForNS >= nsLimit) {
13,438✔
5876
        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✔
5877
      }
2✔
5878
      if (tns == rnameservers.cend()) {
13,436✔
5879
        LOG(prefix << qname << ": Failed to resolve via any of the " << (unsigned int)rnameservers.size() << " offered NS at level '" << auth << "'" << endl);
247✔
5880
        if (s_addExtendedResolutionDNSErrors) {
247✔
5881
          context.extendedError = EDNSExtendedError{static_cast<uint16_t>(EDNSExtendedError::code::NoReachableAuthority), "delegation " + auth.toLogString()};
60✔
5882
        }
60✔
5883
        if (!auth.isRoot() && flawedNSSet) {
247✔
5884
          LOG(prefix << qname << ": Ageing nameservers for level '" << auth << "', next query might succeed" << endl);
65!
5885
          if (g_recCache->doAgeCache(d_now.tv_sec, auth, QType::NS, 10)) {
65✔
5886
            t_Counters.at(rec::Counter::nsSetInvalidations)++;
18✔
5887
          }
18✔
5888
        }
65✔
5889
        return -1;
247✔
5890
      }
247✔
5891

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

5901
      typedef vector<ComboAddress> remoteIPs_t;
13,189✔
5902
      remoteIPs_t remoteIPs;
13,189✔
5903
      remoteIPs_t::iterator remoteIP;
13,189✔
5904
      bool pierceDontQuery = false;
13,189✔
5905
      bool sendRDQuery = false;
13,189✔
5906
      boost::optional<Netmask> ednsmask;
13,189✔
5907
      LWResult lwr;
13,189✔
5908
      const bool wasForwarded = tns->first.empty() && (!nameservers[tns->first].first.empty());
13,189✔
5909
      int rcode = RCode::NoError;
13,189✔
5910
      bool gotNewServers = false;
13,189✔
5911

5912
      if (tns->first.empty() && !wasForwarded) {
13,189✔
5913
        static ComboAddress const s_oobRemote("255.255.255.255");
138✔
5914
        LOG(prefix << qname << ": Domain is out-of-band" << endl);
138!
5915
        /* setting state to indeterminate since validation is disabled for local auth zone,
5916
           and Insecure would be misleading. */
5917
        context.state = vState::Indeterminate;
138✔
5918
        d_wasOutOfBand = doOOBResolve(qname, qtype, lwr.d_records, depth, prefix, lwr.d_rcode);
138✔
5919
        lwr.d_tcbit = false;
138✔
5920
        lwr.d_aabit = true;
138✔
5921

5922
        /* we have received an answer, are we done ? */
5923
        bool done = processAnswer(depth, prefix, lwr, qname, qtype, auth, false, ednsmask, sendRDQuery, nameservers, ret, luaconfsLocal->dfe, &gotNewServers, &rcode, context.state, s_oobRemote);
138✔
5924
        if (done) {
138✔
5925
          return rcode;
134✔
5926
        }
134✔
5927
        if (gotNewServers) {
4!
5928
          if (stopAtDelegation != nullptr && *stopAtDelegation == Stop) {
4!
5929
            *stopAtDelegation = Stopped;
×
5930
            return rcode;
×
5931
          }
×
5932
          break;
4✔
5933
        }
4✔
5934
      }
4✔
5935
      else {
13,051✔
5936
        if (fallBack != nullptr) {
13,051✔
5937
          if (auto iter = fallBack->find(tns->first); iter != fallBack->end()) {
2!
5938
            remoteIPs = iter->second;
2✔
5939
          }
2✔
5940
        }
2✔
5941
        if (remoteIPs.empty()) {
13,051✔
5942
          remoteIPs = retrieveAddressesForNS(prefix, qname, tns, depth, beenthere, rnameservers, nameservers, sendRDQuery, pierceDontQuery, flawedNSSet, cacheOnly, addressQueriesForNS);
13,049✔
5943
        }
13,049✔
5944

5945
        if (remoteIPs.empty()) {
13,051✔
5946
          LOG(prefix << qname << ": Failed to get IP for NS " << tns->first << ", trying next if available" << endl);
136!
5947
          flawedNSSet = true;
136✔
5948
          continue;
136✔
5949
        }
136✔
5950
        bool hitPolicy{false};
12,915✔
5951
        LOG(prefix << qname << ": Resolved '" << auth << "' NS " << tns->first << " to: ");
12,915✔
5952
        for (remoteIP = remoteIPs.begin(); remoteIP != remoteIPs.end(); ++remoteIP) {
27,163✔
5953
          if (remoteIP != remoteIPs.begin()) {
14,248✔
5954
            LOG(", ");
1,422✔
5955
          }
1,422✔
5956
          LOG(remoteIP->toString());
14,248✔
5957
          if (nameserverIPBlockedByRPZ(luaconfsLocal->dfe, *remoteIP)) {
14,248✔
5958
            hitPolicy = true;
4✔
5959
          }
4✔
5960
        }
14,248✔
5961
        LOG(endl);
12,915✔
5962
        if (hitPolicy) { // implies d_wantsRPZ
12,915✔
5963
          /* RPZ hit */
5964
          if (d_pdl && d_pdl->policyHitEventFilter(d_requestor, qname, qtype, d_queryReceivedOverTCP, d_appliedPolicy, d_policyTags, d_discardedPolicies)) {
4!
5965
            /* reset to no match */
5966
            d_appliedPolicy = DNSFilterEngine::Policy();
×
5967
          }
×
5968
          else {
4✔
5969
            throw PolicyHitException();
4✔
5970
          }
4✔
5971
        }
4✔
5972

5973
        for (remoteIP = remoteIPs.begin(); remoteIP != remoteIPs.end(); ++remoteIP) {
13,492✔
5974
          LOG(prefix << qname << ": Trying IP " << remoteIP->toStringWithPort() << ", asking '" << qname << "|" << qtype << "'" << endl);
13,015✔
5975

5976
          if (throttledOrBlocked(prefix, *remoteIP, qname, qtype, pierceDontQuery)) {
13,015✔
5977
            // As d_throttledqueries might be increased, check the max-qperq condition
5978
            checkMaxQperQ(qname);
9✔
5979
            continue;
9✔
5980
          }
9✔
5981

5982
          bool truncated = false;
13,006✔
5983
          bool spoofed = false;
13,006✔
5984
          bool gotAnswer = false;
13,006✔
5985
          bool doDoT = false;
13,006✔
5986

5987
          if (doDoTtoAuth(tns->first)) {
13,006✔
5988
            remoteIP->setPort(853);
9✔
5989
            doDoT = true;
9✔
5990
          }
9✔
5991
          if (SyncRes::s_dot_to_port_853 && remoteIP->getPort() == 853) {
13,006✔
5992
            doDoT = true;
25✔
5993
          }
25✔
5994
          bool forceTCP = doDoT;
13,006✔
5995

5996
          if (!doDoT && s_max_busy_dot_probes > 0) {
13,006!
5997
            submitTryDotTask(*remoteIP, auth, tns->first, d_now.tv_sec);
×
5998
          }
×
5999
          if (!forceTCP) {
13,006✔
6000
            gotAnswer = doResolveAtThisIP(prefix, qname, qtype, lwr, ednsmask, auth, sendRDQuery, wasForwarded,
12,981✔
6001
                                          tns->first, *remoteIP, false, false, truncated, spoofed, context.extendedError);
12,981✔
6002
          }
12,981✔
6003
          if (spoofed) {
13,006✔
6004
            LOG(prefix << qname << ": potentially spoofed, retrying over TCP" << endl);
3!
6005
          }
3✔
6006
          if (forceTCP || (spoofed || (gotAnswer && truncated))) {
13,006✔
6007
            /* retry, over TCP this time */
6008
            gotAnswer = doResolveAtThisIP(prefix, qname, qtype, lwr, ednsmask, auth, sendRDQuery, wasForwarded,
43✔
6009
                                          tns->first, *remoteIP, true, doDoT, truncated, spoofed, context.extendedError);
43✔
6010
          }
43✔
6011

6012
          if (!gotAnswer) {
13,006✔
6013
            if (doDoT && s_max_busy_dot_probes > 0) {
479!
6014
              // This is quite pessimistic...
6015
              updateDoTStatus(*remoteIP, DoTStatus::Bad, d_now.tv_sec + dotFailWait);
×
6016
            }
×
6017
            continue;
479✔
6018
          }
479✔
6019

6020
          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,527✔
6021

6022
          if (doDoT && s_max_busy_dot_probes > 0) {
12,527!
6023
            updateDoTStatus(*remoteIP, DoTStatus::Good, d_now.tv_sec + dotSuccessWait);
×
6024
          }
×
6025
          /*  // for you IPv6 fanatics :-)
6026
              if(remoteIP->sin4.sin_family==AF_INET6)
6027
              lwr.d_usec/=3;
6028
          */
6029
          //        cout<<"ms: "<<lwr.d_usec/1000.0<<", "<<g_avgLatency/1000.0<<'\n';
6030

6031
          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,527✔
6032

6033
          /* we have received an answer, are we done ? */
6034
          bool done = processAnswer(depth, prefix, lwr, qname, qtype, auth, wasForwarded, ednsmask, sendRDQuery, nameservers, ret, luaconfsLocal->dfe, &gotNewServers, &rcode, context.state, *remoteIP);
12,527✔
6035
          if (done) {
12,527✔
6036
            return rcode;
8,884✔
6037
          }
8,884✔
6038
          if (gotNewServers) {
3,643✔
6039
            if (stopAtDelegation != nullptr && *stopAtDelegation == Stop) {
3,551!
6040
              *stopAtDelegation = Stopped;
1,529✔
6041
              return rcode;
1,529✔
6042
            }
1,529✔
6043
            break;
2,022✔
6044
          }
3,551✔
6045
          /* was lame */
6046
          if (!shouldNotThrottle(&tns->first, &*remoteIP)) {
92✔
6047
            doThrottle(d_now.tv_sec, *remoteIP, qname, qtype, 60, 100, Throttle::Reason::Lame);
52✔
6048
          }
52✔
6049
        }
92✔
6050

6051
        if (gotNewServers) {
2,499✔
6052
          break;
2,022✔
6053
        }
2,022✔
6054

6055
        if (remoteIP == remoteIPs.cend()) { // we tried all IP addresses, none worked
2,147,484,118✔
6056
          continue;
347✔
6057
        }
347✔
6058
      }
2,147,484,118✔
6059
    }
13,189✔
6060
  }
12,954✔
6061
  return -1;
130✔
6062
}
10,930✔
6063

6064
void SyncRes::setQuerySource(const Netmask& netmask)
6065
{
×
6066
  if (!netmask.empty()) {
×
6067
    d_outgoingECSNetwork = netmask;
×
6068
  }
×
6069
  else {
×
6070
    d_outgoingECSNetwork = boost::none;
×
6071
  }
×
6072
}
×
6073

6074
void SyncRes::setQuerySource(const ComboAddress& requestor, const boost::optional<const EDNSSubnetOpts&>& incomingECS)
6075
{
3,229✔
6076
  d_requestor = requestor;
3,229✔
6077

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

6119
boost::optional<Netmask> SyncRes::getEDNSSubnetMask(const DNSName& name, const ComboAddress& rem)
6120
{
13,018✔
6121
  if (d_outgoingECSNetwork && (s_ednsdomains.check(name) || s_ednsremotesubnets.match(rem))) {
13,018✔
6122
    return d_outgoingECSNetwork;
601✔
6123
  }
601✔
6124
  return boost::none;
12,417✔
6125
}
13,018✔
6126

6127
void SyncRes::parseEDNSSubnetAllowlist(const std::string& alist)
6128
{
370✔
6129
  vector<string> parts;
370✔
6130
  stringtok(parts, alist, ",; ");
370✔
6131
  for (const auto& allow : parts) {
370✔
6132
    try {
31✔
6133
      s_ednsremotesubnets.addMask(Netmask(allow));
31✔
6134
    }
31✔
6135
    catch (...) {
31✔
6136
      s_ednsdomains.add(DNSName(allow));
23✔
6137
    }
23✔
6138
  }
31✔
6139
}
370✔
6140

6141
void SyncRes::parseEDNSSubnetAddFor(const std::string& subnetlist)
6142
{
185✔
6143
  vector<string> parts;
185✔
6144
  stringtok(parts, subnetlist, ",; ");
185✔
6145
  for (const auto& allow : parts) {
1,715✔
6146
    s_ednslocalsubnets.addMask(allow);
1,715✔
6147
  }
1,715✔
6148
}
185✔
6149

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

6156
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)
6157
{
24✔
6158
  auto log = slog->withValues("qname", Logging::Loggable(qname), "qtype", Logging::Loggable(qtype));
24✔
6159

6160
  struct timeval now{};
24✔
6161
  gettimeofday(&now, nullptr);
24✔
6162

6163
  SyncRes resolver(now);
24✔
6164
  resolver.setQNameMinimization(qnamemin);
24✔
6165
  if (pdl) {
24!
6166
    resolver.setLuaEngine(pdl);
24✔
6167
  }
24✔
6168

6169
  int res = -1;
24✔
6170
  const std::string msg = "Exception while resolving";
24✔
6171
  try {
24✔
6172
    res = resolver.beginResolve(qname, qtype, qclass, ret, 0);
24✔
6173
  }
24✔
6174
  catch (const PDNSException& e) {
24✔
6175
    log->error(Logr::Warning, e.reason, msg, "exception", Logging::Loggable("PDNSException"));
×
6176
    ret.clear();
×
6177
  }
×
6178
  catch (const ImmediateServFailException& e) {
24✔
6179
    log->error(Logr::Warning, e.reason, msg, "exception", Logging::Loggable("ImmediateServFailException"));
×
6180
    ret.clear();
×
6181
  }
×
6182
  catch (const PolicyHitException& e) {
24✔
6183
    log->info(Logr::Warning, msg, "exception", Logging::Loggable("PolicyHitException"));
×
6184
    ret.clear();
×
6185
  }
×
6186
  catch (const std::exception& e) {
24✔
6187
    log->error(Logr::Warning, e.what(), msg, "exception", Logging::Loggable("std::exception"));
×
6188
    ret.clear();
×
6189
  }
×
6190
  catch (...) {
24✔
6191
    log->info(Logr::Warning, msg);
×
6192
    ret.clear();
×
6193
  }
×
6194

6195
  return res;
24✔
6196
}
24✔
6197

6198
int SyncRes::getRootNS(struct timeval now, asyncresolve_t asyncCallback, unsigned int depth, Logr::log_t log)
6199
{
246✔
6200
  if (::arg()["hint-file"] == "no-refresh") {
246!
6201
    return 0;
×
6202
  }
×
6203
  SyncRes resolver(now);
246✔
6204
  resolver.d_prefix = "[getRootNS]";
246✔
6205
  resolver.setDoEDNS0(true);
246✔
6206
  resolver.setUpdatingRootNS();
246✔
6207
  resolver.setDoDNSSEC(g_dnssecmode != DNSSECMode::Off);
246✔
6208
  resolver.setDNSSECValidationRequested(g_dnssecmode != DNSSECMode::Off && g_dnssecmode != DNSSECMode::ProcessNoValidate);
246✔
6209
  resolver.setAsyncCallback(std::move(asyncCallback));
246✔
6210
  resolver.setRefreshAlmostExpired(true);
246✔
6211

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

6242
  if (res == 0) {
246✔
6243
    log->info(Logr::Debug, "Refreshed . records");
198✔
6244
  }
198✔
6245
  else {
48✔
6246
    log->info(Logr::Warning, msg, "rcode", Logging::Loggable(res));
48✔
6247
  }
48✔
6248
  return res;
246✔
6249
}
246✔
6250

6251
bool SyncRes::answerIsNOData(uint16_t requestedType, int rcode, const std::vector<DNSRecord>& records)
6252
{
351✔
6253
  if (rcode != RCode::NoError) {
351✔
6254
    return false;
47✔
6255
  }
47✔
6256

6257
  // NOLINTNEXTLINE(readability-use-anyofallof)
6258
  for (const auto& rec : records) {
365✔
6259
    if (rec.d_place == DNSResourceRecord::ANSWER && rec.d_type == requestedType) {
365✔
6260
      /* we have a record, of the right type, in the right section */
6261
      return false;
247✔
6262
    }
247✔
6263
  }
365✔
6264
  return true;
57✔
6265
#if 0
6266
  // This code should be equivalent to the code above, clang-tidy prefers any_of()
6267
  // I have doubts if that is easier to read
6268
  return !std::any_of(records.begin(), records.end(), [=](const DNSRecord& rec) {
6269
    return rec.d_place == DNSResourceRecord::ANSWER && rec.d_type == requestedType;
6270
  });
6271
#endif
6272
}
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