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

PowerDNS / pdns / 17765900968

16 Sep 2025 12:33PM UTC coverage: 65.987% (-0.04%) from 66.029%
17765900968

Pull #16108

github

web-flow
Merge 4059c5fe8 into 2e297650d
Pull Request #16108: dnsdist: implement simple packet shuffle in cache

42424 of 93030 branches covered (45.6%)

Branch coverage included in aggregate %.

9 of 135 new or added lines in 6 files covered. (6.67%)

34 existing lines in 8 files now uncovered.

128910 of 166619 relevant lines covered (77.37%)

5500581.66 hits per line

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

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

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

28
#include "arguments.hh"
29
#include "aggressive_nsec.hh"
30
#include "cachecleaner.hh"
31
#include "dns_random.hh"
32
#include "dnsparser.hh"
33
#include "dnsrecords.hh"
34
#include "ednssubnet.hh"
35
#include "logger.hh"
36
#include "lua-recursor4.hh"
37
#include "rec-lua-conf.hh"
38
#include "syncres.hh"
39
#include "dnsseckeeper.hh"
40
#include "validate-recursor.hh"
41
#include "rec-taskqueue.hh"
42
#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)) {}
164✔
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,269✔
74
    auto iter = d_cont.find(arg);
10,269✔
75

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

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

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

95
  void clear(const T& arg)
96
  {
12,380✔
97
    d_cont.erase(arg);
12,380✔
98
  }
12,380✔
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,598✔
107
    return d_cont.size();
1,598✔
108
  }
1,598✔
109

110
  void prune(time_t cutoff)
111
  {
272✔
112
    auto& ind = d_cont.template get<time_t>();
272✔
113
    ind.erase(ind.begin(), ind.upper_bound(cutoff));
272✔
114
  }
272✔
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;
184✔
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
    {
218✔
152
    }
218✔
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
  {
25,694✔
165
    auto iter = d_cont.find(arg);
25,694✔
166
    if (iter == d_cont.end()) {
25,694✔
167
      return false;
25,626✔
168
    }
25,626✔
169
    if (now > iter->ttd || iter->count == 0) {
68✔
170
      d_cont.erase(iter);
6✔
171
      return false;
6✔
172
    }
6✔
173
    iter->count--;
62✔
174

175
    return true; // still listed, still blocked
62✔
176
  }
68✔
177

178
  void throttle(time_t now, const Key& arg, time_t ttl, unsigned int count, Reason reason)
179
  {
218✔
180
    auto iter = d_cont.find(arg);
218✔
181
    time_t ttd = now + ttl;
218✔
182
    if (iter == d_cont.end()) {
218!
183
      d_cont.emplace(arg, ttd, count, reason);
218✔
184
    }
218✔
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
  }
218✔
196

197
  [[nodiscard]] size_t size() const
198
  {
795✔
199
    return d_cont.size();
795✔
200
  }
795✔
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
  {
24,756✔
214
    d_cont.erase(thing);
24,756✔
215
  }
24,756✔
216
  void prune(time_t now)
217
  {
136✔
218
    auto& ind = d_cont.template get<time_t>();
136✔
219
    ind.erase(ind.begin(), ind.upper_bound(now));
136✔
220
  }
136✔
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
  {
112✔
270
    auto& ind = get<time_t>();
112✔
271
    ind.erase(ind.begin(), ind.upper_bound(now));
112✔
272
  }
112✔
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,162,762✔
402
    g_log << Logger::Warning << x;   \
110,830✔
403
  }                                  \
110,830✔
404
  else if (d_lm == Store) {          \
1,162,762✔
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,624✔
411
  OptLog ret;
89,624✔
412
  if (d_lm == Log) {
89,624✔
413
    ret = {prefix, d_fixednow, g_log};
13,162✔
414
  }
13,162✔
415
  else if (d_lm == Store) {
76,462✔
416
    ret = {prefix, d_fixednow, d_trace};
114✔
417
  }
114✔
418
  return ret;
89,624✔
419
}
89,624✔
420

421
static bool pushResolveIfNotInNegCache(const DNSName& qname, QType qtype, const struct timeval& now)
422
{
1,028✔
423
  NegCache::NegCacheEntry negEntry;
1,028✔
424
  bool inNegCache = g_negCache->get(qname, qtype, now, negEntry, false);
1,028✔
425
  if (!inNegCache) {
1,028✔
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,014✔
430
  }
1,014✔
431
  return !inNegCache;
1,028✔
432
}
1,028✔
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,459✔
441
  std::array<char, 20> buf{};
4,459✔
442
  int ret = snprintf(buf.data(), buf.size(), "%0.2f", value);
4,459✔
443
  if (ret < 0 || ret >= static_cast<int>(buf.size())) {
4,459!
444
    return "?";
×
445
  }
×
446
  return {buf.data(), static_cast<size_t>(ret)};
4,459✔
447
}
4,459✔
448

449
static inline void accountAuthLatency(uint64_t usec, int family)
450
{
12,847✔
451
  if (family == AF_INET) {
12,847✔
452
    t_Counters.at(rec::Histogram::auth4Answers)(usec);
12,038✔
453
    t_Counters.at(rec::Histogram::cumulativeAuth4Answers)(usec);
12,038✔
454
  }
12,038✔
455
  else {
809✔
456
    t_Counters.at(rec::Histogram::auth6Answers)(usec);
809✔
457
    t_Counters.at(rec::Histogram::cumulativeAuth6Answers)(usec);
809✔
458
  }
809✔
459
}
12,847✔
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
{
4,976✔
464
  d_validationContext.d_nsec3IterationsRemainingQuota = s_maxnsec3iterationsperq > 0 ? s_maxnsec3iterationsperq : std::numeric_limits<decltype(d_validationContext.d_nsec3IterationsRemainingQuota)>::max();
4,976✔
465
}
4,976✔
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,479✔
652
  auto newParent = d_eventTrace.add(RecEventTrace::SyncRes);
4,479✔
653
  auto oldParent = d_eventTrace.setParent(newParent);
4,479✔
654
  RecEventTrace::EventScope traceScope(oldParent, d_eventTrace);
4,479✔
655

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

660
  if (doSpecialNamesResolve(qname, qtype, qclass, ret)) {
4,479✔
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,427✔
666
    return -1;
30✔
667
  }
30✔
668

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

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

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

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

702
  // Avoid calling addAdditionals() if we know we won't find anything
703
  auto luaLocal = g_luaconfs.getLocal();
4,393✔
704
  if (res == 0 && qclass == QClass::IN && luaLocal->allowAdditionalQTypes.find(qtype) != luaLocal->allowAdditionalQTypes.end()) {
4,393!
705
    bool additionalsNotInCache = addAdditionals(qtype, ret, depth);
5✔
706
    if (additionalsNotInCache) {
5!
707
      d_wasVariable = true;
×
708
    }
×
709
  }
5✔
710
  traceScope.close(res);
4,393✔
711
  return res;
4,393✔
712
}
4,397✔
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,551✔
978
  DNSName authname(qname);
20,551✔
979
  const auto iter = getBestAuthZone(&authname);
20,551✔
980
  return iter != t_sstorage.domainmap->end() && (iter->second.isAuth() || iter->second.shouldRecurse());
20,551✔
981
}
20,551✔
982

983
bool SyncRes::isRecursiveForward(const DNSName& qname)
984
{
14,602✔
985
  DNSName authname(qname);
14,602✔
986
  const auto iter = getBestAuthZone(&authname);
14,602✔
987
  return iter != t_sstorage.domainmap->end() && iter->second.shouldRecurse();
14,602!
988
}
14,602✔
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
  {
452✔
1030
    if (iter->mode != mode || iter->ttd == 0) {
452!
1031
      ind.modify(iter, [=](SyncRes::EDNSStatus& status) { status.mode = mode; status.ttd = theTime + Expire; });
113✔
1032
    }
113✔
1033
  }
452✔
1034

1035
  void prune(time_t now)
1036
  {
112✔
1037
    auto& ind = get<time_t>();
112✔
1038
    ind.erase(ind.begin(), ind.upper_bound(now));
112✔
1039
  }
112✔
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
{
112✔
1068
  s_ednsstatus.lock()->prune(cutoff);
112✔
1069
}
112✔
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
{
112✔
1096
  auto lock = s_nsSpeeds.lock();
112✔
1097
  auto& ind = lock->get<timeval>();
112✔
1098
  ind.erase(ind.begin(), ind.upper_bound(timeval{limit, 0}));
112✔
1099
}
112✔
1100

1101
uint64_t SyncRes::getNSSpeedsSize()
1102
{
799✔
1103
  return s_nsSpeeds.lock()->size();
799✔
1104
}
799✔
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
{
795✔
1157
  return s_throttle.lock()->size();
795✔
1158
}
795✔
1159

1160
void SyncRes::pruneThrottledServers(time_t now)
1161
{
136✔
1162
  s_throttle.lock()->prune(now);
136✔
1163
}
136✔
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
{
12,854✔
1172
  return s_throttle.lock()->shouldThrottle(now, std::tuple(server, target, qtype));
12,854✔
1173
}
12,854✔
1174

1175
bool SyncRes::isThrottled(time_t now, const ComboAddress& server)
1176
{
12,840✔
1177
  auto throttled = s_throttle.lock()->shouldThrottle(now, std::tuple(server, g_rootdnsname, 0));
12,840✔
1178
  if (throttled) {
12,840✔
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;
12,840✔
1186
}
12,840✔
1187

1188
void SyncRes::unThrottle(const ComboAddress& server, const DNSName& name, QType qtype)
1189
{
12,377✔
1190
  s_throttle.lock()->clear(std::tuple(server, g_rootdnsname, 0));
12,377✔
1191
  s_throttle.lock()->clear(std::tuple(server, name, qtype));
12,377✔
1192
}
12,377✔
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
{
212✔
1201
  s_throttle.lock()->throttle(now, std::tuple(server, name, qtype), duration, tries, reason);
212✔
1202
}
212✔
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
{
795✔
1233
  return s_fails.lock()->size();
795✔
1234
}
795✔
1235

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

1241
void SyncRes::pruneFailedServers(time_t cutoff)
1242
{
136✔
1243
  s_fails.lock()->prune(cutoff);
136✔
1244
}
136✔
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
{
803✔
1278
  return s_nonresolving.lock()->size();
803✔
1279
}
803✔
1280

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

1286
void SyncRes::pruneNonResolving(time_t cutoff)
1287
{
136✔
1288
  s_nonresolving.lock()->prune(cutoff);
136✔
1289
}
136✔
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
{
112✔
1328
  s_savedParentNSSet.lock()->prune(now);
112✔
1329
}
112✔
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,108✔
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,108✔
1447
  {
13,108✔
1448
    auto lock = s_ednsstatus.lock();
13,108✔
1449
    auto ednsstatus = lock->find(address); // does this include port? YES
13,108✔
1450
    if (ednsstatus != lock->end()) {
13,108✔
1451
      if (ednsstatus->ttd != 0 && ednsstatus->ttd < d_now.tv_sec) {
456!
1452
        lock->erase(ednsstatus);
×
1453
      }
×
1454
      else {
456✔
1455
        mode = ednsstatus->mode;
456✔
1456
      }
456✔
1457
    }
456✔
1458
  }
13,108✔
1459

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

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

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

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

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

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

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

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

1497
    if (EDNSLevel == 1) {
12,722✔
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,717✔
1502

1503
      // Determine new mode
1504
      if (ret == LWResult::Result::BindError) {
12,717!
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,717✔
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,710✔
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,707✔
1525
        auto ednsstatus = lock->insert(address).first;
449✔
1526
        auto& ind = lock->get<ComboAddress>();
449✔
1527
        lock->setMode(ind, ednsstatus, EDNSStatus::EDNSIGNORANT, d_now.tv_sec);
449✔
1528
      }
449✔
1529
      else {
12,258✔
1530
        // New status is EDNSOK
1531
        lock->erase(address);
12,258✔
1532
      }
12,258✔
1533
    }
12,707✔
1534

1535
    break;
12,711✔
1536
  }
12,721✔
1537
  return ret;
13,108✔
1538
}
13,108✔
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,329✔
1548
  unsigned int step{};
18,329✔
1549

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

1563
static string resToString(int res)
1564
{
1,187✔
1565
  return res >= 0 ? RCode::to_s(res) : std::to_string(res);
1,187!
1566
}
1,187✔
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,874✔
1570
  auto prefix = getPrefix(depth);
22,874✔
1571
  auto luaconfsLocal = g_luaconfs.getLocal();
22,874✔
1572

1573
  /* Apply qname (including CNAME chain) filtering policies */
1574
  if (d_wantsRPZ && !d_appliedPolicy.wasHit()) {
22,876✔
1575
    if (luaconfsLocal->dfe.getQueryPolicy(qname, d_discardedPolicies, d_appliedPolicy)) {
22,856✔
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,856✔
1585

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

1588
  // In the auth or recursive forward case, it does not make sense to do qname-minimization
1589
  if (!getQNameMinimization() || isRecursiveForwardOrAuth(qname)) {
22,812✔
1590
    return doResolveNoQNameMinimization(qname, qtype, ret, depth, beenthere, context);
2,570✔
1591
  }
2,570✔
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,242✔
1609
  prefix.append(string("QM "));
20,242✔
1610

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

1613
  // Look in cache only
1614
  vector<DNSRecord> retq;
20,242✔
1615
  bool old = setCacheOnly(true);
20,242✔
1616
  bool fromCache = false;
20,242✔
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,242✔
1620
  try {
20,242✔
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,242✔
1628
  }
20,242✔
1629
  catch (...) {
20,242✔
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,233✔
1634
  if (fromCache) {
20,233✔
1635
    LOG(prefix << qname << ": Step0 Found in cache" << endl);
9,368✔
1636
    if (d_appliedPolicy.d_type != DNSFilterEngine::PolicyType::None && (d_appliedPolicy.d_kind == DNSFilterEngine::PolicyKind::NXDOMAIN || d_appliedPolicy.d_kind == DNSFilterEngine::PolicyKind::NODATA)) {
9,368✔
1637
      ret.clear();
4✔
1638
    }
4✔
1639
    ret.insert(ret.end(), std::make_move_iterator(retq.begin()), std::make_move_iterator(retq.end()));
9,368✔
1640
    return res;
9,368✔
1641
  }
9,368✔
1642
  LOG(prefix << qname << ": Step0 Not cached" << endl);
10,865✔
1643

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

1646
  DNSName fwdomain(qname);
10,865✔
1647
  const bool forwarded = getBestAuthZone(&fwdomain) != t_sstorage.domainmap->end();
10,865✔
1648
  if (forwarded) {
10,865✔
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,355✔
1653

1654
    // Step 1
1655
    vector<DNSRecord> bestns;
12,354✔
1656
    DNSName nsdomain(qname);
12,354✔
1657
    if (qtype == QType::DS) {
12,354✔
1658
      nsdomain.chopOff();
184✔
1659
    }
184✔
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,752✔
1663
      bool flawedNSSet = false;
12,502✔
1664
      set<GetBestNSAnswer> beenthereIgnored;
12,502✔
1665
      getBestNSFromCache(nsdomain, qtype, bestns, &flawedNSSet, depth, prefix, beenthereIgnored, boost::make_optional(forwarded, fwdomain));
12,502✔
1666
      if (forwarded) {
12,502✔
1667
        break;
104✔
1668
      }
104✔
1669
    }
12,502✔
1670

1671
    if (bestns.empty()) {
12,354✔
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,250✔
1680
      LOG(prefix << qname << ": Step1 Ancestor from cache is " << bestns[0].d_name << endl);
12,250✔
1681
      if (forwarded) {
12,250!
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,250✔
1686
        child = bestns[0].d_name;
12,250✔
1687
      }
12,250✔
1688
    }
12,250✔
1689
    for (; i <= qnamelen; i++) {
18,328✔
1690
      // Step 2
1691
      unsigned int labels = child.countLabels();
18,328✔
1692
      unsigned int targetlen = qmStepLen(labels, qnamelen, i);
18,328✔
1693

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

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

1710
      // Step 3 resolve
1711
      if (child == qname) {
18,328✔
1712
        LOG(prefix << qname << ": Step3 Going to do final resolve" << endl);
10,841✔
1713
        res = doResolveNoQNameMinimization(qname, qtype, ret, depth, beenthere, context);
10,841✔
1714
        LOG(prefix << qname << ": Step3 Final resolve: " << resToString(res) << "/" << ret.size() << endl);
10,841✔
1715
        return res;
10,841✔
1716
      }
10,841✔
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,487✔
1721
      for (const auto& visitedNS : beenthere) {
7,487✔
1722
        if (visitedNS.qname == child) {
1,672!
1723
          qmLoopDetected = true;
×
1724
          break;
×
1725
        }
×
1726
      }
1,672✔
1727
      if (qmLoopDetected) {
7,487!
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,487✔
1736
      bool oldFollowCNAME = d_followCNAME;
7,487✔
1737
      d_followCNAME = false;
7,487✔
1738
      retq.clear();
7,487✔
1739
      StopAtDelegation stopAtDelegation = Stop;
7,487✔
1740
      res = doResolveNoQNameMinimization(child, QType::A, retq, depth, beenthere, context, nullptr, &stopAtDelegation);
7,487✔
1741
      d_followCNAME = oldFollowCNAME;
7,487✔
1742
      LOG(prefix << qname << ": Step4 Resolve " << child << "|A result is " << RCode::to_s(res) << "/" << retq.size() << "/" << stopAtDelegation << endl);
7,487✔
1743
      if (stopAtDelegation == Stopped) {
7,487✔
1744
        LOG(prefix << qname << ": Delegation seen, continue at step 1" << endl);
1,491✔
1745
        break;
1,491✔
1746
      }
1,491✔
1747

1748
      if (res != RCode::NoError) {
5,996✔
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
    }
5,996✔
1771
  }
12,354✔
1772

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

1778
unsigned int SyncRes::getAdjustedRecursionBound() const
1779
{
42,732✔
1780
  auto bound = s_maxdepth; // 40 is default value of s_maxdepth
42,732✔
1781
  if (getQMFallbackMode()) {
42,732✔
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,732✔
1788
}
42,732✔
1789

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

1796
  // This loop assumes the CNAME's records are in-order
1797
  DNSName target(qname);
2,986✔
1798
  for (const auto& record : ret) {
5,717✔
1799
    if (record.d_place == DNSResourceRecord::ANSWER && record.d_name == target) {
5,717✔
1800
      if (record.d_type == qtype) {
5,701✔
1801
        return true;
91✔
1802
      }
91✔
1803
      if (record.d_type == QType::CNAME) {
5,610!
1804
        if (auto ptr = getRR<CNAMERecordContent>(record)) {
5,610!
1805
          target = ptr->getTarget();
5,610✔
1806
        }
5,610✔
1807
        else {
×
1808
          return false;
×
1809
        }
×
1810
      }
5,610✔
1811
    }
5,610✔
1812
  }
5,717✔
1813
  return false;
2,895✔
1814
}
2,986✔
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,730✔
1829
  context.extendedError.reset();
42,730✔
1830
  auto prefix = getPrefix(depth);
42,730✔
1831

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

1834
  d_maxdepth = std::max(d_maxdepth, depth);
42,730✔
1835
  if (s_maxdepth > 0) {
42,732✔
1836
    auto bound = getAdjustedRecursionBound();
42,730✔
1837
    // Use a stricter bound if throttling
1838
    if (depth > bound || (d_outqueries > 10 && d_throttledqueries > 5 && depth > bound * 2 / 3)) {
42,730!
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,730✔
1844

1845
  int res = 0;
42,726✔
1846

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

1850
    d_serveStale = loop == 1;
42,745✔
1851
    if (d_serveStale) {
42,745✔
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,745!
1856
      DNSName authname(qname);
42,250✔
1857
      const auto iter = getBestAuthZone(&authname);
42,250✔
1858

1859
      if (d_cacheonly) {
42,250✔
1860
        if (iter != t_sstorage.domainmap->end()) {
29,798✔
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,798✔
1872

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

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

1880
        if (iter->second.isAuth()) {
772✔
1881
          wasAuthZone = true;
304✔
1882
        }
304✔
1883
        else if (iter->second.shouldRecurse()) {
468✔
1884
          wasForwardRecurse = true;
99✔
1885
        }
99✔
1886
      }
772✔
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,236✔
1892
        d_wasOutOfBand = wasAuthZone;
6,205✔
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();
6,205✔
1900
        if (stoppedByPolicyHit && d_appliedPolicy.d_kind == DNSFilterEngine::PolicyKind::Custom && d_appliedPolicy.d_custom) {
6,205!
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)) {
6,205!
1908
          *fromCache = true;
3✔
1909
        }
3✔
1910
        /* Apply Post filtering policies */
1911

1912
        if (d_wantsRPZ && !d_appliedPolicy.wasHit()) {
6,205!
1913
          auto luaLocal = g_luaconfs.getLocal();
6,197✔
1914
          if (luaLocal->dfe.getPostPolicy(ret, d_discardedPolicies, d_appliedPolicy)) {
6,197!
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
        }
6,197✔
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)) {
6,205✔
1926
          *fromCache = true;
91✔
1927
        }
91✔
1928
        return res;
6,205✔
1929
      }
6,205✔
1930

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

1938
        if (d_wantsRPZ && !d_appliedPolicy.wasHit()) {
10,692!
1939
          auto luaLocal = g_luaconfs.getLocal();
10,687✔
1940
          if (luaLocal->dfe.getPostPolicy(ret, d_discardedPolicies, d_appliedPolicy)) {
10,687✔
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,687✔
1946

1947
        return res;
10,692✔
1948
      }
10,692✔
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,339✔
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,339✔
1983

1984
    if (d_cacheonly) {
25,816✔
1985
      return 0;
15,027✔
1986
    }
15,027✔
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,789✔
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,785✔
1995

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

2001
    NsSet nsset;
10,785✔
2002
    bool flawedNSSet = false;
10,785✔
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,574✔
2007
      subdomain = getBestNSNamesFromCache(subdomain, qtype, nsset, &flawedNSSet, depth, prefix, beenthere); //  pass beenthere to both occasions
10,785✔
2008
    }
10,785✔
2009

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

2012
    if (res == -1 && s_save_parent_ns_set) {
10,785✔
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,789✔
2039
      auto luaLocal = g_luaconfs.getLocal();
10,566✔
2040
      if (luaLocal->dfe.getPostPolicy(ret, d_discardedPolicies, d_appliedPolicy)) {
10,566✔
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,566✔
2046

2047
    if (res == 0) {
10,787✔
2048
      return 0;
10,152✔
2049
    }
10,152✔
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,726✔
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,540✔
2071
  bool operator()(const ComboAddress& lhs, const ComboAddress& rhs) const
2072
  {
1,420✔
2073
    return d_speeds[lhs] < d_speeds[rhs];
1,420✔
2074
  }
1,420✔
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,597✔
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,597✔
2085
  {
12,597✔
2086
    auto lock = s_nsSpeeds.lock();
12,597✔
2087
    const auto& collection = lock->find_or_enter(qname, d_now);
12,597✔
2088
    float factor = collection.getFactor(d_now);
12,597✔
2089
    for (const auto& val : ret) {
13,847✔
2090
      speeds[val] = collection.d_collection[val].get(factor);
13,847✔
2091
    }
13,847✔
2092
    collection.purge(speeds);
12,597✔
2093
  }
12,597✔
2094

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

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

2117
template <typename T>
2118
static bool collectAddresses(const vector<DNSRecord>& cset, vector<ComboAddress>& ret)
2119
{
13,855✔
2120
  bool pushed = false;
13,855✔
2121
  for (const auto& record : cset) {
13,954✔
2122
    if (auto rec = getRR<T>(record)) {
13,952✔
2123
      ret.push_back(rec->getCA(53));
13,847✔
2124
      pushed = true;
13,847✔
2125
    }
13,847✔
2126
  }
13,952✔
2127
  return pushed;
13,855✔
2128
}
13,855✔
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,687✔
2134
  typedef vector<DNSRecord> res_t;
12,687✔
2135
  typedef vector<ComboAddress> ret_t;
12,687✔
2136
  ret_t ret;
12,687✔
2137

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

2148
  MemRecursorCache::Flags flags = MemRecursorCache::None;
12,687✔
2149
  if (d_serveStale) {
12,687!
2150
    flags |= MemRecursorCache::ServeStale;
×
2151
  }
×
2152
  try {
12,687✔
2153
    // First look for both A and AAAA in the cache
2154
    res_t cset;
12,687✔
2155
    if (s_doIPv4 && g_recCache->get(d_now.tv_sec, qname, QType::A, flags, &cset, d_cacheRemote, d_routingTag) > 0) {
12,687✔
2156
      collectAddresses<ARecordContent>(cset, ret);
11,109✔
2157
    }
11,109✔
2158
    if (s_doIPv6 && g_recCache->get(d_now.tv_sec, qname, QType::AAAA, flags, &cset, d_cacheRemote, d_routingTag) > 0) {
12,687✔
2159
      if (collectAddresses<AAAARecordContent>(cset, ret)) {
1,362!
2160
        seenV6 = true;
1,362✔
2161
      }
1,362✔
2162
    }
1,362✔
2163
    if (ret.empty()) {
12,687✔
2164
      // Neither A nor AAAA in the cache...
2165
      Context newContext1;
1,546✔
2166
      cset.clear();
1,546✔
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,546✔
2169
        collectAddresses<ARecordContent>(cset, ret);
1,360✔
2170
      }
1,360✔
2171
      if (s_doIPv6) { // s_doIPv6 **IMPLIES** pdns::isQueryLocalAddressFamilyEnabled(AF_INET6) returned true
1,546✔
2172
        if (ret.empty()) {
54✔
2173
          // We only go out immediately to find IPv6 records if we did not find any IPv4 ones.
2174
          Context newContext2;
32✔
2175
          if (doResolveNoQNameMinimization(qname, QType::AAAA, cset, depth + 1, beenthere, newContext2) == 0) { // this consults cache, OR goes out
32✔
2176
            if (collectAddresses<AAAARecordContent>(cset, ret)) {
22!
2177
              seenV6 = true;
×
2178
            }
×
2179
          }
22✔
2180
        }
32✔
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
      }
54✔
2191
    }
1,546✔
2192
    if (s_doIPv6 && !seenV6 && !cacheOnly) {
12,687!
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);
970✔
2196
    }
970✔
2197
  }
12,687✔
2198
  catch (const PolicyHitException&) {
12,687✔
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,687✔
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,597✔
2208
  d_DNSSECValidationRequested = oldValidationRequested;
12,597✔
2209
  setCacheOnly(oldCacheOnly);
12,597✔
2210
  d_followCNAME = oldFollowCNAME;
12,597✔
2211

2212
  if (s_max_busy_dot_probes > 0 && s_dot_to_port_853) {
12,597!
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,597✔
2220
  return ret;
12,597✔
2221
}
12,687✔
2222

2223
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)
2224
{
23,090✔
2225
  DNSName subdomain(qname);
23,090✔
2226
  bestns.clear();
23,090✔
2227
  bool brokeloop = false;
23,090✔
2228
  MemRecursorCache::Flags flags = MemRecursorCache::None;
23,090✔
2229
  if (d_serveStale) {
23,090!
2230
    flags |= MemRecursorCache::ServeStale;
×
2231
  }
×
2232
  do {
53,390✔
2233
    if (cutOffDomain && (subdomain == *cutOffDomain || !subdomain.isPartOf(*cutOffDomain))) {
53,390✔
2234
      break;
104✔
2235
    }
104✔
2236
    brokeloop = false;
53,286✔
2237
    LOG(prefix << qname << ": Checking if we have NS in cache for '" << subdomain << "'" << endl);
53,286✔
2238
    vector<DNSRecord> nsVector;
53,286✔
2239
    *flawedNSSet = false;
53,286✔
2240

2241
    if (bool isAuth = false; g_recCache->get(d_now.tv_sec, subdomain, QType::NS, flags, &nsVector, d_cacheRemote, d_routingTag, nullptr, nullptr, nullptr, nullptr, &isAuth) > 0) {
53,286✔
2242
      if (s_maxnsperresolve > 0 && nsVector.size() > s_maxnsperresolve) {
23,034!
2243
        vector<DNSRecord> selected;
20✔
2244
        selected.reserve(s_maxnsperresolve);
20✔
2245
        std::sample(nsVector.cbegin(), nsVector.cend(), std::back_inserter(selected), s_maxnsperresolve, pdns::dns_random_engine());
20✔
2246
        nsVector = std::move(selected);
20✔
2247
      }
20✔
2248
      bestns.reserve(nsVector.size());
23,034✔
2249

2250
      vector<DNSName> missing;
23,034✔
2251
      for (const auto& nsRecord : nsVector) {
134,206✔
2252
        if (nsRecord.d_ttl > (unsigned int)d_now.tv_sec) {
134,206✔
2253
          vector<DNSRecord> aset;
134,205✔
2254
          QType nsqt{QType::ADDR};
134,205✔
2255
          if (s_doIPv4 && !s_doIPv6) {
134,206✔
2256
            nsqt = QType::A;
90,195✔
2257
          }
90,195✔
2258
          else if (!s_doIPv4 && s_doIPv6) {
2,147,527,656!
2259
            nsqt = QType::AAAA;
64✔
2260
          }
64✔
2261

2262
          auto nrr = getRR<NSRecordContent>(nsRecord);
134,205✔
2263
          if (nrr && (!nrr->getNS().isPartOf(subdomain) || g_recCache->get(d_now.tv_sec, nrr->getNS(), nsqt, flags, doLog() ? &aset : nullptr, d_cacheRemote, d_routingTag) > 0)) {
134,207✔
2264
            bestns.push_back(nsRecord);
133,894✔
2265
            LOG(prefix << qname << ": NS (with ip, or non-glue) in cache for '" << subdomain << "' -> '" << nrr->getNS() << "'");
133,894✔
2266
            LOG(", within bailiwick: " << nrr->getNS().isPartOf(subdomain));
133,894✔
2267
            if (!aset.empty()) {
133,894✔
2268
              LOG(", in cache, ttl=" << (unsigned int)(((time_t)aset.begin()->d_ttl - d_now.tv_sec)) << endl);
4,630!
2269
            }
4,630✔
2270
            else {
129,264✔
2271
              LOG(", not in cache / did not look at cache" << endl);
129,264✔
2272
            }
129,264✔
2273
          }
133,894✔
2274
          else if (nrr != nullptr) {
2,147,483,960✔
2275
            *flawedNSSet = true;
313✔
2276
            LOG(prefix << qname << ": NS in cache for '" << subdomain << "', but needs glue (" << nrr->getNS() << ") which we miss or is expired" << endl);
313!
2277
            missing.emplace_back(nrr->getNS());
313✔
2278
          }
313✔
2279
        }
134,205✔
2280
      }
134,206✔
2281
      if (*flawedNSSet && bestns.empty() && isAuth) {
23,034✔
2282
        // The authoritative (child) NS records did not produce any usable addresses, wipe them, so
2283
        // these useless records do not prevent parent records to be inserted into the cache
2284
        LOG(prefix << qname << ": Wiping flawed authoritative NS records for " << subdomain << endl);
2!
2285
        g_recCache->doWipeCache(subdomain, false, QType::NS);
2✔
2286
      }
2✔
2287
      if (!missing.empty() && missing.size() < nsVector.size()) {
23,034✔
2288
        // We miss glue, but we have a chance to resolve it
2289
        // Pick a few and push async tasks to resolve them
2290
        const unsigned int max = 2;
29✔
2291
        unsigned int counter = 0;
29✔
2292
        shuffle(missing.begin(), missing.end(), pdns::dns_random_engine());
29✔
2293
        for (const auto& name : missing) {
29!
2294
          if (s_doIPv4 && pushResolveIfNotInNegCache(name, QType::A, d_now)) {
29!
2295
            LOG(prefix << qname << ": A glue for " << subdomain << " NS " << name << " missing, pushed task to resolve" << endl);
29!
2296
            counter++;
29✔
2297
          }
29✔
2298
          if (s_doIPv6 && pushResolveIfNotInNegCache(name, QType::AAAA, d_now)) {
29!
2299
            LOG(prefix << qname << ": AAAA glue for " << subdomain << " NS " << name << " missing, pushed task to resolve" << endl);
29!
2300
            counter++;
29✔
2301
          }
29✔
2302
          if (counter >= max) {
29!
2303
            break;
29✔
2304
          }
29✔
2305
        }
29✔
2306
      }
29✔
2307

2308
      if (!bestns.empty()) {
23,034✔
2309
        GetBestNSAnswer answer;
22,998✔
2310
        answer.qname = qname;
22,998✔
2311
        answer.qtype = qtype.getCode();
22,998✔
2312
        for (const auto& bestNSRecord : bestns) {
133,889✔
2313
          if (auto nsContent = getRR<NSRecordContent>(bestNSRecord)) {
133,894✔
2314
            answer.bestns.emplace(bestNSRecord.d_name, nsContent->getNS());
133,894✔
2315
          }
133,894✔
2316
        }
133,889✔
2317

2318
        auto insertionPair = beenthere.insert(std::move(answer));
22,998✔
2319
        if (!insertionPair.second) {
22,998✔
2320
          brokeloop = true;
267✔
2321
          LOG(prefix << qname << ": We have NS in cache for '" << subdomain << "' but part of LOOP (already seen " << insertionPair.first->qname << ")! Trying less specific NS" << endl);
267!
2322
          ;
267✔
2323
          if (doLog()) {
267!
2324
            for (auto j = beenthere.begin(); j != beenthere.end(); ++j) {
×
2325
              bool neo = (j == insertionPair.first);
×
2326
              LOG(prefix << qname << ": Beenthere" << (neo ? "*" : "") << ": " << j->qname << "|" << DNSRecordContent::NumberToType(j->qtype) << " (" << (unsigned int)j->bestns.size() << ")" << endl);
×
2327
            }
×
2328
          }
×
2329
          bestns.clear();
267✔
2330
        }
267✔
2331
        else {
22,731✔
2332
          LOG(prefix << qname << ": We have NS in cache for '" << subdomain << "' (flawedNSSet=" << *flawedNSSet << ")" << endl);
22,731✔
2333
          return;
22,731✔
2334
        }
22,731✔
2335
      }
22,998✔
2336
    }
23,034✔
2337
    LOG(prefix << qname << ": No valid/useful NS in cache for '" << subdomain << "'" << endl);
30,555✔
2338

2339
    if (subdomain.isRoot() && !brokeloop) {
30,555✔
2340
      // We lost the root NS records
2341
      primeHints();
151✔
2342
      LOG(prefix << qname << ": Reprimed the root" << endl);
151!
2343
      /* let's prevent an infinite loop */
2344
      if (!d_updatingRootNS) {
151✔
2345
        auto log = g_slog->withName("housekeeping");
143✔
2346
        getRootNS(d_now, d_asyncResolve, depth, log);
143✔
2347
      }
143✔
2348
    }
151✔
2349
  } while (subdomain.chopOff());
30,555✔
2350
}
23,090✔
2351

2352
SyncRes::domainmap_t::const_iterator SyncRes::getBestAuthZone(DNSName* qname)
2353
{
146,932✔
2354
  if (t_sstorage.domainmap->empty()) {
146,932✔
2355
    return t_sstorage.domainmap->end();
26,331✔
2356
  }
26,331✔
2357

2358
  SyncRes::domainmap_t::const_iterator ret;
120,601✔
2359
  do {
431,100✔
2360
    ret = t_sstorage.domainmap->find(*qname);
431,100✔
2361
    if (ret != t_sstorage.domainmap->end()) {
431,100✔
2362
      break;
2,275✔
2363
    }
2,275✔
2364
  } while (qname->chopOff());
431,100✔
2365
  return ret;
×
2366
}
146,932✔
2367

2368
/** doesn't actually do the work, leaves that to getBestNSFromCache */
2369
DNSName SyncRes::getBestNSNamesFromCache(const DNSName& qname, const QType qtype, NsSet& nsset, bool* flawedNSSet, unsigned int depth, const string& prefix, set<GetBestNSAnswer>& beenthere)
2370
{
10,786✔
2371
  DNSName authOrForwDomain(qname);
10,786✔
2372

2373
  auto iter = getBestAuthZone(&authOrForwDomain);
10,786✔
2374
  // We have an auth, forwarder of forwarder-recurse
2375
  if (iter != t_sstorage.domainmap->end()) {
10,786✔
2376
    if (iter->second.isAuth()) {
314✔
2377
      // this gets picked up in doResolveAt, the empty DNSName, combined with the
2378
      // empty vector means 'we are auth for this zone'
2379
      nsset.insert({DNSName(), {{}, false}});
138✔
2380
      return authOrForwDomain;
138✔
2381
    }
138✔
2382
    if (iter->second.shouldRecurse()) {
176✔
2383
      // Again, picked up in doResolveAt. An empty DNSName, combined with a
2384
      // non-empty vector of ComboAddresses means 'this is a forwarded domain'
2385
      // This is actually picked up in retrieveAddressesForNS called from doResolveAt.
2386
      nsset.insert({DNSName(), {iter->second.d_servers, true}});
60✔
2387
      return authOrForwDomain;
60✔
2388
    }
60✔
2389
  }
176✔
2390

2391
  // We might have a (non-recursive) forwarder, but maybe the cache already contains
2392
  // a better NS
2393
  vector<DNSRecord> bestns;
10,588✔
2394
  DNSName nsFromCacheDomain(g_rootdnsname);
10,588✔
2395
  getBestNSFromCache(qname, qtype, bestns, flawedNSSet, depth, prefix, beenthere);
10,588✔
2396

2397
  // Pick up the auth domain
2398
  for (const auto& nsRecord : bestns) {
10,588✔
2399
    const auto nsContent = getRR<NSRecordContent>(nsRecord);
10,480✔
2400
    if (nsContent) {
10,480!
2401
      nsFromCacheDomain = nsRecord.d_name;
10,480✔
2402
      break;
10,480✔
2403
    }
10,480✔
2404
  }
10,480✔
2405

2406
  if (iter != t_sstorage.domainmap->end()) {
10,588✔
2407
    if (doLog()) {
116✔
2408
      LOG(prefix << qname << " authOrForwDomain: " << authOrForwDomain << " nsFromCacheDomain: " << nsFromCacheDomain << " isPartof: " << authOrForwDomain.isPartOf(nsFromCacheDomain) << endl);
104!
2409
    }
104✔
2410

2411
    // If the forwarder is better or equal to what's found in the cache, use forwarder. Note that name.isPartOf(name).
2412
    // So queries that get NS for authOrForwDomain itself go to the forwarder
2413
    if (authOrForwDomain.isPartOf(nsFromCacheDomain)) {
116!
2414
      if (doLog()) {
116✔
2415
        LOG(prefix << qname << ": Using forwarder as NS" << endl);
104!
2416
      }
104✔
2417
      nsset.insert({DNSName(), {iter->second.d_servers, false}});
116✔
2418
      return authOrForwDomain;
116✔
2419
    }
116✔
2420
    if (doLog()) {
×
2421
      LOG(prefix << qname << ": Using NS from cache" << endl);
×
2422
    }
×
2423
  }
×
2424
  for (const auto& bestn : bestns) {
62,448✔
2425
    // The actual resolver code will not even look at the ComboAddress or bool
2426
    const auto nsContent = getRR<NSRecordContent>(bestn);
62,448✔
2427
    if (nsContent) {
62,452✔
2428
      nsset.insert({nsContent->getNS(), {{}, false}});
62,450✔
2429
    }
62,450✔
2430
  }
62,448✔
2431
  return nsFromCacheDomain;
10,472✔
2432
}
10,588✔
2433

2434
void SyncRes::updateValidationStatusInCache(const DNSName& qname, const QType qtype, bool aaFlag, vState newState) const
2435
{
52✔
2436
  if (qtype == QType::ANY || qtype == QType::ADDR) {
52!
2437
    // not doing that
2438
    return;
×
2439
  }
×
2440

2441
  if (vStateIsBogus(newState)) {
52✔
2442
    g_recCache->updateValidationStatus(d_now.tv_sec, qname, qtype, d_cacheRemote, d_routingTag, aaFlag, newState, s_maxbogusttl + d_now.tv_sec);
20✔
2443
  }
20✔
2444
  else {
32✔
2445
    g_recCache->updateValidationStatus(d_now.tv_sec, qname, qtype, d_cacheRemote, d_routingTag, aaFlag, newState, boost::none);
32✔
2446
  }
32✔
2447
}
52✔
2448

2449
static pair<bool, unsigned int> scanForCNAMELoop(const DNSName& name, const vector<DNSRecord>& records)
2450
{
7,506✔
2451
  unsigned int numCNames = 0;
7,506✔
2452
  for (const auto& record : records) {
14,333✔
2453
    if (record.d_type == QType::CNAME && record.d_place == DNSResourceRecord::ANSWER) {
14,333!
2454
      ++numCNames;
14,136✔
2455
      if (name == record.d_name) {
14,136✔
2456
        return {true, numCNames};
6✔
2457
      }
6✔
2458
    }
14,136✔
2459
  }
14,333✔
2460
  return {false, numCNames};
7,500✔
2461
}
7,506✔
2462

2463
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)
2464
{
36,833✔
2465
  vector<DNSRecord> cset;
36,833✔
2466
  MemRecursorCache::SigRecs signatures = MemRecursorCache::s_emptySigRecs;
36,833✔
2467
  MemRecursorCache::AuthRecs authorityRecs = MemRecursorCache::s_emptyAuthRecs;
36,833✔
2468
  bool wasAuth = false;
36,833✔
2469
  uint32_t capTTL = std::numeric_limits<uint32_t>::max();
36,833✔
2470
  DNSName foundName;
36,833✔
2471
  DNSName authZone;
36,833✔
2472
  QType foundQT = QType::ENT;
36,833✔
2473

2474
  /* we don't require auth data for forward-recurse lookups */
2475
  MemRecursorCache::Flags flags = MemRecursorCache::None;
36,833✔
2476
  if (!wasForwardRecurse && d_requireAuthData) {
36,833✔
2477
    flags |= MemRecursorCache::RequireAuth;
35,161✔
2478
  }
35,161✔
2479
  if (d_refresh) {
36,833✔
2480
    flags |= MemRecursorCache::Refresh;
312✔
2481
  }
312✔
2482
  if (d_serveStale) {
36,833✔
2483
    flags |= MemRecursorCache::ServeStale;
20✔
2484
  }
20✔
2485
  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,833✔
2486
    foundName = qname;
6,269✔
2487
    foundQT = QType::CNAME;
6,269✔
2488
  }
6,269✔
2489

2490
  if (foundName.empty() && qname != g_rootdnsname) {
36,833✔
2491
    // look for a DNAME cache hit
2492
    auto labels = qname.getRawLabels();
29,456✔
2493
    DNSName dnameName(g_rootdnsname);
29,456✔
2494

2495
    do {
77,793✔
2496
      dnameName.prependRawLabel(labels.back());
77,793✔
2497
      labels.pop_back();
77,793✔
2498
      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
77,793✔
2499
        break;
29,438✔
2500
      }
29,438✔
2501
      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) {
48,355✔
2502
        foundName = std::move(dnameName);
16✔
2503
        foundQT = QType::DNAME;
16✔
2504
        break;
16✔
2505
      }
16✔
2506
    } while (!labels.empty());
48,355✔
2507
  }
29,456✔
2508

2509
  if (foundName.empty()) {
36,833✔
2510
    return false;
30,549✔
2511
  }
30,549✔
2512

2513
  if (qtype == QType::DS && authZone == qname) {
6,284✔
2514
    /* CNAME at APEX of the child zone, we can't use that to prove that
2515
       there is no DS */
2516
    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!
2517
    return false;
4✔
2518
  }
4✔
2519

2520
  for (auto const& record : cset) {
6,281✔
2521
    if (record.d_class != QClass::IN) {
6,281!
2522
      continue;
×
2523
    }
×
2524

2525
    if (record.d_ttl > (unsigned int)d_now.tv_sec) {
6,281!
2526

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

2530
        vState recordState = getValidationStatus(foundName, !signatures->empty(), qtype == QType::DS, depth, prefix);
8✔
2531
        if (recordState == vState::Secure) {
8✔
2532
          LOG(prefix << qname << ": Got vState::Indeterminate state from the " << foundQT.toString() << " cache, validating.." << endl);
6!
2533
          context.state = SyncRes::validateRecordsWithSigs(depth, prefix, qname, qtype, foundName, foundQT, cset, *signatures);
6✔
2534
          if (context.state != vState::Indeterminate) {
6!
2535
            LOG(prefix << qname << ": Got vState::Indeterminate state from the " << foundQT.toString() << " cache, new validation result is " << context.state << endl);
6!
2536
            if (vStateIsBogus(context.state)) {
6✔
2537
              capTTL = s_maxbogusttl;
4✔
2538
            }
4✔
2539
            updateValidationStatusInCache(foundName, foundQT, wasAuth, context.state);
6✔
2540
          }
6✔
2541
        }
6✔
2542
      }
8✔
2543

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

2546
      DNSRecord dnsRecord = record;
6,281✔
2547
      auto alreadyPresent = false;
6,281✔
2548

2549
      if (checkForDups) {
6,281✔
2550
        // This can happen on the 2nd iteration of the servestale loop, where the first iteration
2551
        // added a C/DNAME record, but the target resolve failed
2552
        for (const auto& dnsrec : ret) {
6✔
2553
          if (dnsrec.d_type == foundQT && dnsrec.d_name == record.d_name) {
2!
2554
            alreadyPresent = true;
2✔
2555
            break;
2✔
2556
          }
2✔
2557
        }
2✔
2558
      }
6✔
2559
      dnsRecord.d_ttl -= d_now.tv_sec;
6,281✔
2560
      dnsRecord.d_ttl = std::min(dnsRecord.d_ttl, capTTL);
6,281✔
2561
      const uint32_t ttl = dnsRecord.d_ttl;
6,281✔
2562
      if (!alreadyPresent) {
6,281✔
2563
        ret.reserve(ret.size() + 2 + signatures->size() + authorityRecs->size());
6,279✔
2564
        ret.push_back(dnsRecord);
6,279✔
2565

2566
        for (const auto& signature : *signatures) {
6,279✔
2567
          DNSRecord sigdr;
45✔
2568
          sigdr.d_type = QType::RRSIG;
45✔
2569
          sigdr.d_name = foundName;
45✔
2570
          sigdr.d_ttl = ttl;
45✔
2571
          sigdr.setContent(signature);
45✔
2572
          sigdr.d_place = DNSResourceRecord::ANSWER;
45✔
2573
          sigdr.d_class = QClass::IN;
45✔
2574
          ret.push_back(std::move(sigdr));
45✔
2575
        }
45✔
2576

2577
        for (const auto& rec : *authorityRecs) {
6,279!
2578
          DNSRecord authDR(rec);
×
2579
          authDR.d_ttl = ttl;
×
2580
          ret.push_back(std::move(authDR));
×
2581
        }
×
2582
      }
6,279✔
2583

2584
      DNSName newTarget;
6,281✔
2585
      if (foundQT == QType::DNAME) {
6,281✔
2586
        if (qtype == QType::DNAME && qname == foundName) { // client wanted the DNAME, no need to synthesize a CNAME
16!
2587
          res = RCode::NoError;
2✔
2588
          return true;
2✔
2589
        }
2✔
2590
        // Synthesize a CNAME
2591
        auto dnameRR = getRR<DNAMERecordContent>(record);
14✔
2592
        if (dnameRR == nullptr) {
14!
2593
          throw ImmediateServFailException("Unable to get record content for " + foundName.toLogString() + "|DNAME cache entry");
×
2594
        }
×
2595
        const auto& dnameSuffix = dnameRR->getTarget();
14✔
2596
        DNSName targetPrefix = qname.makeRelative(foundName);
14✔
2597
        try {
14✔
2598
          dnsRecord.d_type = QType::CNAME;
14✔
2599
          dnsRecord.d_name = targetPrefix + foundName;
14✔
2600
          newTarget = targetPrefix + dnameSuffix;
14✔
2601
          dnsRecord.setContent(std::make_shared<CNAMERecordContent>(CNAMERecordContent(newTarget)));
14✔
2602
          ret.push_back(dnsRecord);
14✔
2603
        }
14✔
2604
        catch (const std::exception& e) {
14✔
2605
          // We should probably catch an std::range_error here and set the rcode to YXDOMAIN (RFC 6672, section 2.2)
2606
          // But this is consistent with processRecords
2607
          throw ImmediateServFailException("Unable to perform DNAME substitution(DNAME owner: '" + foundName.toLogString() + "', DNAME target: '" + dnameSuffix.toLogString() + "', substituted name: '" + targetPrefix.toLogString() + "." + dnameSuffix.toLogString() + "' : " + e.what());
×
2608
        }
×
2609

2610
        LOG(prefix << qname << ": Synthesized " << dnsRecord.d_name << "|CNAME " << newTarget << endl);
14!
2611
      }
14✔
2612

2613
      if (qtype == QType::CNAME) { // perhaps they really wanted a CNAME!
6,279✔
2614
        res = RCode::NoError;
2✔
2615
        return true;
2✔
2616
      }
2✔
2617

2618
      if (qtype == QType::DS || qtype == QType::DNSKEY) {
6,277!
2619
        res = RCode::NoError;
18✔
2620
        return true;
18✔
2621
      }
18✔
2622

2623
      // We have a DNAME _or_ CNAME cache hit and the client wants something else than those two.
2624
      // Let's find the answer!
2625
      if (foundQT == QType::CNAME) {
6,259✔
2626
        const auto cnameContent = getRR<CNAMERecordContent>(record);
6,246✔
2627
        if (cnameContent == nullptr) {
6,246!
2628
          throw ImmediateServFailException("Unable to get record content for " + foundName.toLogString() + "|CNAME cache entry");
×
2629
        }
×
2630
        newTarget = cnameContent->getTarget();
6,246✔
2631
      }
6,246✔
2632

2633
      if (qname == newTarget) {
6,259✔
2634
        string msg = "Got a CNAME referral (from cache) to self";
2✔
2635
        LOG(prefix << qname << ": " << msg << endl);
2!
2636
        throw ImmediateServFailException(std::move(msg));
2✔
2637
      }
2✔
2638

2639
      if (newTarget.isPartOf(qname)) {
6,257✔
2640
        // a.b.c. CNAME x.a.b.c will go to great depths with QM on
2641
        string msg = "Got a CNAME referral (from cache) to child, disabling QM";
42✔
2642
        LOG(prefix << qname << ": " << msg << endl);
42!
2643
        setQNameMinimization(false);
42✔
2644
      }
42✔
2645

2646
      if (!d_followCNAME) {
6,257✔
2647
        res = RCode::NoError;
170✔
2648
        return true;
170✔
2649
      }
170✔
2650

2651
      // Check to see if we already have seen the new target as a previous target or that we have a very long CNAME chain
2652
      const auto [CNAMELoop, numCNAMEs] = scanForCNAMELoop(newTarget, ret);
6,087✔
2653
      if (CNAMELoop) {
6,087✔
2654
        string msg = "got a CNAME referral (from cache) that causes a loop";
4✔
2655
        LOG(prefix << qname << ": Status=" << msg << endl);
4!
2656
        throw ImmediateServFailException(std::move(msg));
4✔
2657
      }
4✔
2658
      if (numCNAMEs > s_max_CNAMES_followed) {
6,083✔
2659
        string msg = "max number of CNAMEs exceeded";
4✔
2660
        LOG(prefix << qname << ": Status=" << msg << endl);
4!
2661
        throw ImmediateServFailException(std::move(msg));
4✔
2662
      }
4✔
2663

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

2673
      return true;
6,079✔
2674
    }
6,083✔
2675
  }
6,281✔
2676
  throw ImmediateServFailException("Could not determine whether or not there was a CNAME or DNAME in cache for '" + qname.toLogString() + "'");
2,147,483,647✔
2677
}
6,280✔
2678

2679
namespace
2680
{
2681
struct CacheEntry
2682
{
2683
  vector<DNSRecord> records;
2684
  MemRecursorCache::SigRecsVec signatures;
2685
  time_t d_ttl_time{0};
2686
  uint32_t signaturesTTL{std::numeric_limits<uint32_t>::max()};
2687
};
2688
struct CacheKey
2689
{
2690
  DNSName name;
2691
  QType type;
2692
  DNSResourceRecord::Place place;
2693
  bool operator<(const CacheKey& rhs) const
2694
  {
415,793✔
2695
    return std::tie(type, place, name) < std::tie(rhs.type, rhs.place, rhs.name);
415,793✔
2696
  }
415,793✔
2697
};
2698
using tcache_t = map<CacheKey, CacheEntry>;
2699
}
2700

2701
static void reapRecordsFromNegCacheEntryForValidation(tcache_t& tcache, const vector<DNSRecord>& records)
2702
{
32✔
2703
  for (const auto& rec : records) {
32✔
2704
    if (rec.d_type == QType::RRSIG) {
22✔
2705
      auto rrsig = getRR<RRSIGRecordContent>(rec);
10✔
2706
      if (rrsig) {
10!
2707
        tcache[{rec.d_name, rrsig->d_type, rec.d_place}].signatures.push_back(rrsig);
10✔
2708
      }
10✔
2709
    }
10✔
2710
    else {
12✔
2711
      tcache[{rec.d_name, rec.d_type, rec.d_place}].records.push_back(rec);
12✔
2712
    }
12✔
2713
  }
22✔
2714
}
32✔
2715

2716
static bool negativeCacheEntryHasSOA(const NegCache::NegCacheEntry& negEntry)
2717
{
16✔
2718
  return !negEntry.authoritySOA.records.empty();
16✔
2719
}
16✔
2720

2721
static void reapRecordsForValidation(std::map<QType, CacheEntry>& entries, const vector<DNSRecord>& records)
2722
{
3✔
2723
  for (const auto& rec : records) {
5✔
2724
    entries[rec.d_type].records.push_back(rec);
5✔
2725
  }
5✔
2726
}
3✔
2727

2728
static void reapSignaturesForValidation(std::map<QType, CacheEntry>& entries, const MemRecursorCache::SigRecs& signatures)
2729
{
3✔
2730
  for (const auto& sig : *signatures) {
5✔
2731
    entries[sig->d_type].signatures.push_back(sig);
5✔
2732
  }
5✔
2733
}
3✔
2734

2735
/*!
2736
 * Convenience function to push the records from records into ret with a new TTL
2737
 *
2738
 * \param records DNSRecords that need to go into ret
2739
 * \param ttl     The new TTL for these records
2740
 * \param ret     The vector of DNSRecords that should contain the records with the modified TTL
2741
 */
2742
static void addTTLModifiedRecords(vector<DNSRecord>& records, const uint32_t ttl, vector<DNSRecord>& ret)
2743
{
12,854✔
2744
  for (auto& rec : records) {
12,854✔
2745
    rec.d_ttl = ttl;
10,876✔
2746
    ret.push_back(std::move(rec));
10,876✔
2747
  }
10,876✔
2748
}
12,854✔
2749

2750
void SyncRes::computeNegCacheValidationStatus(const NegCache::NegCacheEntry& negEntry, const DNSName& qname, const QType qtype, const int res, vState& state, unsigned int depth, const string& prefix)
2751
{
8✔
2752
  tcache_t tcache;
8✔
2753
  reapRecordsFromNegCacheEntryForValidation(tcache, negEntry.authoritySOA.records);
8✔
2754
  reapRecordsFromNegCacheEntryForValidation(tcache, negEntry.authoritySOA.signatures);
8✔
2755
  reapRecordsFromNegCacheEntryForValidation(tcache, negEntry.DNSSECRecords.records);
8✔
2756
  reapRecordsFromNegCacheEntryForValidation(tcache, negEntry.DNSSECRecords.signatures);
8✔
2757

2758
  for (const auto& entry : tcache) {
12✔
2759
    // this happens when we did store signatures, but passed on the records themselves
2760
    if (entry.second.records.empty()) {
12!
2761
      continue;
×
2762
    }
×
2763

2764
    const DNSName& owner = entry.first.name;
12✔
2765

2766
    vState recordState = getValidationStatus(owner, !entry.second.signatures.empty(), qtype == QType::DS, depth, prefix);
12✔
2767
    if (state == vState::Indeterminate) {
12✔
2768
      state = recordState;
8✔
2769
    }
8✔
2770

2771
    if (recordState == vState::Secure) {
12✔
2772
      recordState = SyncRes::validateRecordsWithSigs(depth, prefix, qname, qtype, owner, QType(entry.first.type), entry.second.records, entry.second.signatures);
10✔
2773
    }
10✔
2774

2775
    if (recordState != vState::Indeterminate && recordState != state) {
12!
2776
      updateValidationState(qname, state, recordState, prefix);
×
2777
      if (state != vState::Secure) {
×
2778
        break;
×
2779
      }
×
2780
    }
×
2781
  }
12✔
2782

2783
  if (state == vState::Secure) {
8✔
2784
    vState neValidationState = negEntry.d_validationState;
6✔
2785
    dState expectedState = res == RCode::NXDomain ? dState::NXDOMAIN : dState::NXQTYPE;
6!
2786
    dState denialState = getDenialValidationState(negEntry, expectedState, false, prefix);
6✔
2787
    updateDenialValidationState(qname, neValidationState, negEntry.d_name, state, denialState, expectedState, qtype == QType::DS, depth, prefix);
6✔
2788
  }
6✔
2789
  if (state != vState::Indeterminate) {
8!
2790
    /* validation succeeded, let's update the cache entry so we don't have to validate again */
2791
    boost::optional<time_t> capTTD = boost::none;
8✔
2792
    if (vStateIsBogus(state)) {
8✔
2793
      capTTD = d_now.tv_sec + s_maxbogusttl;
2✔
2794
    }
2✔
2795
    g_negCache->updateValidationStatus(negEntry.d_name, negEntry.d_qtype, state, capTTD);
8✔
2796
  }
8✔
2797
}
8✔
2798

2799
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)
2800
{
35,978✔
2801
  bool giveNegative = false;
35,978✔
2802

2803
  // 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)
2804
  DNSName sqname(qname);
35,978✔
2805
  QType sqt(qtype);
35,978✔
2806
  uint32_t sttl = 0;
35,978✔
2807
  //  cout<<"Lookup for '"<<qname<<"|"<<qtype.toString()<<"' -> "<<getLastLabel(qname)<<endl;
2808
  vState cachedState{};
35,978✔
2809
  NegCache::NegCacheEntry negEntry;
35,978✔
2810

2811
  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.
35,978!
2812
    sttl = negEntry.d_ttd - d_now.tv_sec;
4✔
2813
    LOG(prefix << qname << ": Entire name '" << qname << "', is negatively cached via '" << negEntry.d_auth << "' & '" << negEntry.d_name << "' for another " << sttl << " seconds" << endl);
4!
2814
    res = RCode::NXDomain;
4✔
2815
    giveNegative = true;
4✔
2816
    cachedState = negEntry.d_validationState;
4✔
2817
    if (s_addExtendedResolutionDNSErrors) {
4✔
2818
      context.extendedError = EDNSExtendedError{static_cast<uint16_t>(EDNSExtendedError::code::Synthesized), "Result synthesized by root-nx-trust"};
2✔
2819
    }
2✔
2820
  }
4✔
2821
  else if (g_negCache->get(qname, qtype, d_now, negEntry, false, d_serveStale, d_refresh)) {
35,974✔
2822
    /* If we are looking for a DS, discard NXD if auth == qname
2823
       and ask for a specific denial instead */
2824
    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,214!
2825
      /* 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
2826
         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
2827
         query. */
2828
      if (qtype == QType::DS && negEntry.d_qtype.getCode() != 0 && !d_externalDSQuery.empty() && qname == d_externalDSQuery && !negativeCacheEntryHasSOA(negEntry)) {
3,214✔
2829
        giveNegative = false;
6✔
2830
      }
6✔
2831
      else {
3,208✔
2832
        res = RCode::NXDomain;
3,208✔
2833
        sttl = negEntry.d_ttd - d_now.tv_sec;
3,208✔
2834
        giveNegative = true;
3,208✔
2835
        cachedState = negEntry.d_validationState;
3,208✔
2836
        if (negEntry.d_qtype.getCode() != 0) {
3,208✔
2837
          LOG(prefix << qname << "|" << qtype << ": Is negatively cached via '" << negEntry.d_auth << "' for another " << sttl << " seconds" << endl);
3,168!
2838
          res = RCode::NoError;
3,168✔
2839
          if (s_addExtendedResolutionDNSErrors) {
3,168✔
2840
            context.extendedError = EDNSExtendedError{static_cast<uint16_t>(EDNSExtendedError::code::Synthesized), "Result from negative cache"};
3,040✔
2841
          }
3,040✔
2842
        }
3,168✔
2843
        else {
40✔
2844
          LOG(prefix << qname << ": Entire name '" << qname << "' is negatively cached via '" << negEntry.d_auth << "' for another " << sttl << " seconds" << endl);
40!
2845
          if (s_addExtendedResolutionDNSErrors) {
40✔
2846
            context.extendedError = EDNSExtendedError{static_cast<uint16_t>(EDNSExtendedError::code::Synthesized), "Result from negative cache for entire name"};
28✔
2847
          }
28✔
2848
        }
40✔
2849
      }
3,208✔
2850
    }
3,214✔
2851
  }
3,214✔
2852
  else if (s_hardenNXD != HardenNXD::No && !qname.isRoot() && !wasForwardedOrAuthZone) {
32,760✔
2853
    auto labels = qname.getRawLabels();
30,931✔
2854
    DNSName negCacheName(g_rootdnsname);
30,931✔
2855
    negCacheName.prependRawLabel(labels.back());
30,931✔
2856
    labels.pop_back();
30,931✔
2857
    while (!labels.empty()) {
77,364✔
2858
      if (g_negCache->get(negCacheName, QType::ENT, d_now, negEntry, true, d_serveStale, d_refresh)) {
46,464✔
2859
        if (negEntry.d_validationState == vState::Indeterminate && validationEnabled()) {
35!
2860
          // LOG(prefix << negCacheName <<  " negatively cached and vState::Indeterminate, trying to validate NXDOMAIN" << endl);
2861
          // ...
2862
          // And get the updated ne struct
2863
          // t_sstorage.negcache.get(negCacheName, QType(0), d_now, ne, true);
2864
        }
×
2865
        if ((s_hardenNXD == HardenNXD::Yes && !vStateIsBogus(negEntry.d_validationState)) || negEntry.d_validationState == vState::Secure) {
35!
2866
          res = RCode::NXDomain;
31✔
2867
          sttl = negEntry.d_ttd - d_now.tv_sec;
31✔
2868
          giveNegative = true;
31✔
2869
          cachedState = negEntry.d_validationState;
31✔
2870
          LOG(prefix << qname << ": Name '" << negCacheName << "' and below, is negatively cached via '" << negEntry.d_auth << "' for another " << sttl << " seconds" << endl);
31!
2871
          if (s_addExtendedResolutionDNSErrors) {
31✔
2872
            context.extendedError = EDNSExtendedError{static_cast<uint16_t>(EDNSExtendedError::code::Synthesized), "Result synthesized by nothing-below-nxdomain (RFC8020)"};
15✔
2873
          }
15✔
2874
          break;
31✔
2875
        }
31✔
2876
      }
35✔
2877
      negCacheName.prependRawLabel(labels.back());
46,433✔
2878
      labels.pop_back();
46,433✔
2879
    }
46,433✔
2880
  }
30,931✔
2881

2882
  if (giveNegative) {
35,978✔
2883

2884
    context.state = cachedState;
3,243✔
2885

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

2890
      if (context.state != cachedState && vStateIsBogus(context.state)) {
8!
2891
        sttl = std::min(sttl, s_maxbogusttl);
2✔
2892
      }
2✔
2893
    }
8✔
2894

2895
    // Transplant SOA to the returned packet
2896
    addTTLModifiedRecords(negEntry.authoritySOA.records, sttl, ret);
3,243✔
2897
    if (d_doDNSSEC) {
3,243✔
2898
      addTTLModifiedRecords(negEntry.authoritySOA.signatures, sttl, ret);
3,203✔
2899
      addTTLModifiedRecords(negEntry.DNSSECRecords.records, sttl, ret);
3,203✔
2900
      addTTLModifiedRecords(negEntry.DNSSECRecords.signatures, sttl, ret);
3,203✔
2901
    }
3,203✔
2902

2903
    LOG(prefix << qname << ": Updating validation state with negative cache content for " << qname << " to " << context.state << endl);
3,243!
2904
    return true;
3,243✔
2905
  }
3,243✔
2906

2907
  vector<DNSRecord> cset;
32,735✔
2908
  bool found = false;
32,735✔
2909
  bool expired = false;
32,735✔
2910
  MemRecursorCache::SigRecs signatures = MemRecursorCache::s_emptySigRecs;
32,735✔
2911
  MemRecursorCache::AuthRecs authorityRecs = MemRecursorCache::s_emptyAuthRecs;
32,735✔
2912
  uint32_t ttl = 0;
32,735✔
2913
  uint32_t capTTL = std::numeric_limits<uint32_t>::max();
32,735✔
2914
  bool wasCachedAuth{};
32,735✔
2915
  MemRecursorCache::Flags flags = MemRecursorCache::None;
32,735✔
2916
  if (!wasForwardRecurse && d_requireAuthData) {
32,735✔
2917
    flags |= MemRecursorCache::RequireAuth;
31,074✔
2918
  }
31,074✔
2919
  if (d_serveStale) {
32,735✔
2920
    flags |= MemRecursorCache::ServeStale;
10✔
2921
  }
10✔
2922
  if (d_refresh) {
32,735✔
2923
    flags |= MemRecursorCache::Refresh;
312✔
2924
  }
312✔
2925
  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) {
32,735✔
2926

2927
    LOG(prefix << sqname << ": Found cache hit for " << sqt.toString() << ": ");
7,405✔
2928

2929
    if (!wasAuthZone && shouldValidate() && (wasCachedAuth || wasForwardRecurse) && cachedState == vState::Indeterminate && d_requireAuthData) {
7,405!
2930

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

2934
      if (recordState == vState::Secure) {
41✔
2935
        LOG(prefix << sqname << ": Got vState::Indeterminate state from the cache, validating.." << endl);
33!
2936
        if (sqt == QType::DNSKEY && sqname == getSigner(*signatures)) {
33✔
2937
          cachedState = validateDNSKeys(sqname, cset, *signatures, depth, prefix);
2✔
2938
        }
2✔
2939
        else {
31✔
2940
          if (sqt == QType::ANY) {
31✔
2941
            std::map<QType, CacheEntry> types;
3✔
2942
            reapRecordsForValidation(types, cset);
3✔
2943
            reapSignaturesForValidation(types, signatures);
3✔
2944

2945
            for (const auto& type : types) {
5✔
2946
              vState cachedRecordState{};
5✔
2947
              if (type.first == QType::DNSKEY && sqname == getSigner(type.second.signatures)) {
5!
2948
                cachedRecordState = validateDNSKeys(sqname, type.second.records, type.second.signatures, depth, prefix);
×
2949
              }
×
2950
              else {
5✔
2951
                cachedRecordState = SyncRes::validateRecordsWithSigs(depth, prefix, qname, qtype, sqname, type.first, type.second.records, type.second.signatures);
5✔
2952
              }
5✔
2953
              updateDNSSECValidationState(cachedState, cachedRecordState);
5✔
2954
            }
5✔
2955
          }
3✔
2956
          else {
28✔
2957
            cachedState = SyncRes::validateRecordsWithSigs(depth, prefix, qname, qtype, sqname, sqt, cset, *signatures);
28✔
2958
          }
28✔
2959
        }
31✔
2960
      }
33✔
2961
      else {
8✔
2962
        cachedState = recordState;
8✔
2963
      }
8✔
2964

2965
      if (cachedState != vState::Indeterminate) {
41!
2966
        LOG(prefix << qname << ": Got vState::Indeterminate state from the cache, validation result is " << cachedState << endl);
41!
2967
        if (vStateIsBogus(cachedState)) {
41✔
2968
          capTTL = s_maxbogusttl;
10✔
2969
        }
10✔
2970
        if (sqt != QType::ANY && sqt != QType::ADDR) {
41!
2971
          updateValidationStatusInCache(sqname, sqt, wasCachedAuth, cachedState);
38✔
2972
        }
38✔
2973
      }
41✔
2974
    }
41✔
2975

2976
    for (auto j = cset.cbegin(); j != cset.cend(); ++j) {
17,996✔
2977

2978
      LOG(j->getContent()->getZoneRepresentation());
10,591✔
2979

2980
      if (j->d_class != QClass::IN) {
10,591!
2981
        continue;
×
2982
      }
×
2983

2984
      if (j->d_ttl > (unsigned int)d_now.tv_sec) {
10,591✔
2985
        DNSRecord dnsRecord = *j;
10,590✔
2986
        dnsRecord.d_ttl -= d_now.tv_sec;
10,590✔
2987
        dnsRecord.d_ttl = std::min(dnsRecord.d_ttl, capTTL);
10,590✔
2988
        ttl = dnsRecord.d_ttl;
10,590✔
2989
        ret.push_back(dnsRecord);
10,590✔
2990
        LOG("[ttl=" << dnsRecord.d_ttl << "] ");
10,590✔
2991
        found = true;
10,590✔
2992
      }
10,590✔
2993
      else {
1✔
2994
        LOG("[expired] ");
1!
2995
        expired = true;
1✔
2996
      }
1✔
2997
    }
10,591✔
2998

2999
    ret.reserve(ret.size() + signatures->size() + authorityRecs->size());
7,405✔
3000

3001
    for (const auto& signature : *signatures) {
7,405✔
3002
      DNSRecord dnsRecord;
6,884✔
3003
      dnsRecord.d_type = QType::RRSIG;
6,884✔
3004
      dnsRecord.d_name = sqname;
6,884✔
3005
      dnsRecord.d_ttl = ttl;
6,884✔
3006
      dnsRecord.setContent(signature);
6,884✔
3007
      dnsRecord.d_place = DNSResourceRecord::ANSWER;
6,884✔
3008
      dnsRecord.d_class = QClass::IN;
6,884✔
3009
      ret.push_back(std::move(dnsRecord));
6,884✔
3010
    }
6,884✔
3011

3012
    for (const auto& rec : *authorityRecs) {
7,405✔
3013
      DNSRecord dnsRecord(rec);
32✔
3014
      dnsRecord.d_ttl = ttl;
32✔
3015
      ret.push_back(std::move(dnsRecord));
32✔
3016
    }
32✔
3017

3018
    LOG(endl);
7,405✔
3019
    if (found && !expired) {
7,405!
3020
      if (!giveNegative) {
7,405!
3021
        res = 0;
7,405✔
3022
      }
7,405✔
3023
      LOG(prefix << qname << ": Updating validation state with cache content for " << qname << " to " << cachedState << endl);
7,405✔
3024
      context.state = cachedState;
7,405✔
3025
      return true;
7,405✔
3026
    }
7,405✔
UNCOV
3027
    LOG(prefix << qname << ": Cache had only stale entries" << endl);
×
UNCOV
3028
  }
×
3029

3030
  /* let's check if we have a NSEC covering that record */
3031
  if (g_aggressiveNSECCache && !wasForwardedOrAuthZone) {
25,330✔
3032
    if (g_aggressiveNSECCache->getDenial(d_now.tv_sec, qname, qtype, ret, res, d_cacheRemote, d_routingTag, d_doDNSSEC, d_validationContext, LogObject(prefix))) {
14,702✔
3033
      context.state = vState::Secure;
44✔
3034
      if (s_addExtendedResolutionDNSErrors) {
44✔
3035
        context.extendedError = EDNSExtendedError{static_cast<uint16_t>(EDNSExtendedError::code::Synthesized), "Result synthesized from aggressive NSEC cache (RFC8198)"};
28✔
3036
      }
28✔
3037
      return true;
44✔
3038
    }
44✔
3039
  }
14,702✔
3040

3041
  return false;
25,286✔
3042
}
25,330✔
3043

3044
bool SyncRes::moreSpecificThan(const DNSName& lhs, const DNSName& rhs)
3045
{
15,013✔
3046
  return (lhs.isPartOf(rhs) && lhs.countLabels() > rhs.countLabels());
15,013!
3047
}
15,013✔
3048

3049
struct speedOrder
3050
{
3051
  bool operator()(const std::pair<DNSName, float>& lhs, const std::pair<DNSName, float>& rhs) const
3052
  {
148,370✔
3053
    return lhs.second < rhs.second;
148,370✔
3054
  }
148,370✔
3055
};
3056

3057
std::vector<std::pair<DNSName, float>> SyncRes::shuffleInSpeedOrder(const DNSName& qname, NsSet& tnameservers, const string& prefix)
3058
{
12,761✔
3059
  std::vector<std::pair<DNSName, float>> rnameservers;
12,761✔
3060
  rnameservers.reserve(tnameservers.size());
12,761✔
3061
  for (const auto& tns : tnameservers) {
70,042✔
3062
    float speed = s_nsSpeeds.lock()->fastest(tns.first, d_now);
70,042✔
3063
    rnameservers.emplace_back(tns.first, speed);
70,042✔
3064
    if (tns.first.empty()) { // this was an authoritative OOB zone, don't pollute the nsSpeeds with that
70,042✔
3065
      return rnameservers;
312✔
3066
    }
312✔
3067
  }
70,042✔
3068

3069
  shuffle(rnameservers.begin(), rnameservers.end(), pdns::dns_random_engine());
12,449✔
3070
  speedOrder speedCompare;
12,449✔
3071
  stable_sort(rnameservers.begin(), rnameservers.end(), speedCompare);
12,449✔
3072

3073
  if (doLog()) {
12,449✔
3074
    LOG(prefix << qname << ": Nameservers: ");
1,594!
3075
    for (auto i = rnameservers.begin(); i != rnameservers.end(); ++i) {
4,292✔
3076
      if (i != rnameservers.begin()) {
2,698✔
3077
        LOG(", ");
1,104!
3078
        if (((i - rnameservers.begin()) % 3) == 0) {
1,104✔
3079
          LOG(endl
200!
3080
              << prefix << "             ");
200✔
3081
        }
200✔
3082
      }
1,104✔
3083
      LOG(i->first.toLogString() << "(" << fmtfloat(i->second / 1000.0) << "ms)");
2,698!
3084
    }
2,698✔
3085
    LOG(endl);
1,594!
3086
  }
1,594✔
3087
  return rnameservers;
12,449✔
3088
}
12,761✔
3089

3090
vector<ComboAddress> SyncRes::shuffleForwardSpeed(const DNSName& qname, const vector<ComboAddress>& rnameservers, const string& prefix, const bool wasRd)
3091
{
174✔
3092
  vector<ComboAddress> nameservers = rnameservers;
174✔
3093
  map<ComboAddress, float> speeds;
174✔
3094

3095
  for (const auto& val : nameservers) {
210✔
3096
    DNSName nsName = DNSName(val.toStringWithPort());
210✔
3097
    float speed = s_nsSpeeds.lock()->fastest(nsName, d_now);
210✔
3098
    speeds[val] = speed;
210✔
3099
  }
210✔
3100
  shuffle(nameservers.begin(), nameservers.end(), pdns::dns_random_engine());
174✔
3101
  speedOrderCA speedCompare(speeds);
174✔
3102
  stable_sort(nameservers.begin(), nameservers.end(), speedCompare);
174✔
3103

3104
  if (doLog()) {
174✔
3105
    LOG(prefix << qname << ": Nameservers: ");
116!
3106
    for (auto i = nameservers.cbegin(); i != nameservers.cend(); ++i) {
264✔
3107
      if (i != nameservers.cbegin()) {
148✔
3108
        LOG(", ");
32!
3109
        if (((i - nameservers.cbegin()) % 3) == 0) {
32!
3110
          LOG(endl
×
3111
              << prefix << "             ");
×
3112
        }
×
3113
      }
32✔
3114
      LOG((wasRd ? string("+") : string("-")) << i->toStringWithPort() << "(" << fmtfloat(speeds[*i] / 1000.0) << "ms)");
148!
3115
    }
148✔
3116
    LOG(endl);
116!
3117
  }
116✔
3118
  return nameservers;
174✔
3119
}
174✔
3120

3121
static uint32_t getRRSIGTTL(const time_t now, const std::shared_ptr<const RRSIGRecordContent>& rrsig)
3122
{
8,034✔
3123
  uint32_t res = 0;
8,034✔
3124
  if (now < rrsig->d_sigexpire) {
8,034!
3125
    // coverity[store_truncates_time_t]
3126
    res = static_cast<uint32_t>(rrsig->d_sigexpire) - now;
8,034✔
3127
  }
8,034✔
3128
  return res;
8,034✔
3129
}
8,034✔
3130

3131
static const set<QType> nsecTypes = {QType::NSEC, QType::NSEC3};
3132

3133
/* Fills the authoritySOA and DNSSECRecords fields from ne with those found in the records
3134
 *
3135
 * \param records The records to parse for the authority SOA and NSEC(3) records
3136
 * \param ne      The NegCacheEntry to be filled out (will not be cleared, only appended to
3137
 */
3138
static void harvestNXRecords(const vector<DNSRecord>& records, NegCache::NegCacheEntry& negEntry, const time_t now, uint32_t* lowestTTL)
3139
{
6,911✔
3140
  for (const auto& rec : records) {
61,182✔
3141
    if (rec.d_place != DNSResourceRecord::AUTHORITY) {
61,182✔
3142
      // RFC 4035 section 3.1.3. indicates that NSEC records MUST be placed in
3143
      // the AUTHORITY section. Section 3.1.1 indicates that that RRSIGs for
3144
      // records MUST be in the same section as the records they cover.
3145
      // Hence, we ignore all records outside of the AUTHORITY section.
3146
      continue;
25,650✔
3147
    }
25,650✔
3148

3149
    if (rec.d_type == QType::RRSIG) {
35,532✔
3150
      auto rrsig = getRR<RRSIGRecordContent>(rec);
8,706✔
3151
      if (rrsig) {
8,706!
3152
        if (rrsig->d_type == QType::SOA) {
8,706✔
3153
          negEntry.authoritySOA.signatures.push_back(rec);
557✔
3154
          if (lowestTTL != nullptr && isRRSIGNotExpired(now, *rrsig)) {
557✔
3155
            *lowestTTL = min(*lowestTTL, rec.d_ttl);
277✔
3156
            *lowestTTL = min(*lowestTTL, getRRSIGTTL(now, rrsig));
277✔
3157
          }
277✔
3158
        }
557✔
3159
        if (nsecTypes.count(rrsig->d_type) != 0) {
8,706✔
3160
          negEntry.DNSSECRecords.signatures.push_back(rec);
8,149✔
3161
          if (lowestTTL != nullptr && isRRSIGNotExpired(now, *rrsig)) {
8,149✔
3162
            *lowestTTL = min(*lowestTTL, rec.d_ttl);
7,757✔
3163
            *lowestTTL = min(*lowestTTL, getRRSIGTTL(now, rrsig));
7,757✔
3164
          }
7,757✔
3165
        }
8,149✔
3166
      }
8,706✔
3167
      continue;
8,706✔
3168
    }
8,706✔
3169
    if (rec.d_type == QType::SOA) {
26,826✔
3170
      negEntry.authoritySOA.records.push_back(rec);
2,832✔
3171
      if (lowestTTL != nullptr) {
2,832✔
3172
        *lowestTTL = min(*lowestTTL, rec.d_ttl);
1,461✔
3173
      }
1,461✔
3174
      continue;
2,832✔
3175
    }
2,832✔
3176
    if (nsecTypes.count(rec.d_type) != 0) {
23,994✔
3177
      negEntry.DNSSECRecords.records.push_back(rec);
8,161✔
3178
      if (lowestTTL != nullptr) {
8,161✔
3179
        *lowestTTL = min(*lowestTTL, rec.d_ttl);
7,771✔
3180
      }
7,771✔
3181
      continue;
8,161✔
3182
    }
8,161✔
3183
  }
23,994✔
3184
}
6,911✔
3185

3186
static cspmap_t harvestCSPFromNE(const NegCache::NegCacheEntry& negEntry)
3187
{
3,025✔
3188
  cspmap_t cspmap;
3,025✔
3189
  for (const auto& rec : negEntry.DNSSECRecords.signatures) {
5,224✔
3190
    if (rec.d_type == QType::RRSIG) {
5,224!
3191
      auto rrc = getRR<RRSIGRecordContent>(rec);
5,224✔
3192
      if (rrc) {
5,224!
3193
        cspmap[{rec.d_name, rrc->d_type}].signatures.push_back(rrc);
5,224✔
3194
      }
5,224✔
3195
    }
5,224✔
3196
  }
5,224✔
3197
  for (const auto& rec : negEntry.DNSSECRecords.records) {
5,226✔
3198
    cspmap[{rec.d_name, rec.d_type}].records.insert(rec.getContent());
5,226✔
3199
  }
5,226✔
3200
  return cspmap;
3,025✔
3201
}
3,025✔
3202

3203
// TODO remove after processRecords is fixed!
3204
// Adds the RRSIG for the SOA and the NSEC(3) + RRSIGs to ret
3205
static void addNXNSECS(vector<DNSRecord>& ret, const vector<DNSRecord>& records)
3206
{
1,414✔
3207
  NegCache::NegCacheEntry negEntry;
1,414✔
3208
  harvestNXRecords(records, negEntry, 0, nullptr);
1,414✔
3209
  ret.insert(ret.end(), negEntry.authoritySOA.signatures.begin(), negEntry.authoritySOA.signatures.end());
1,414✔
3210
  ret.insert(ret.end(), negEntry.DNSSECRecords.records.begin(), negEntry.DNSSECRecords.records.end());
1,414✔
3211
  ret.insert(ret.end(), negEntry.DNSSECRecords.signatures.begin(), negEntry.DNSSECRecords.signatures.end());
1,414✔
3212
}
1,414✔
3213

3214
static bool rpzHitShouldReplaceContent(const DNSName& qname, const QType qtype, const std::vector<DNSRecord>& records)
3215
{
63✔
3216
  if (qtype == QType::CNAME) {
63!
3217
    return true;
×
3218
  }
×
3219

3220
  for (const auto& record : records) { // NOLINT(readability-use-anyofallof): don't agree
63✔
3221
    if (record.d_type == QType::CNAME) {
9✔
3222
      if (auto content = getRR<CNAMERecordContent>(record)) {
3!
3223
        if (qname == content->getTarget()) {
3!
3224
          /* we have a CNAME whose target matches the entry we are about to
3225
             generate, so it will complete the current records, not replace
3226
             them
3227
          */
3228
          return false;
3✔
3229
        }
3✔
3230
      }
3✔
3231
    }
3✔
3232
  }
9✔
3233

3234
  return true;
60✔
3235
}
63✔
3236

3237
static void removeConflictingRecord(std::vector<DNSRecord>& records, const DNSName& name, const QType dtype)
3238
{
67✔
3239
  for (auto it = records.begin(); it != records.end();) {
73✔
3240
    bool remove = false;
6✔
3241

3242
    if (it->d_class == QClass::IN && (it->d_type == QType::CNAME || dtype == QType::CNAME || it->d_type == dtype) && it->d_name == name) {
6!
3243
      remove = true;
1✔
3244
    }
1✔
3245
    else if (it->d_class == QClass::IN && it->d_type == QType::RRSIG && it->d_name == name) {
5!
3246
      if (auto rrc = getRR<RRSIGRecordContent>(*it)) {
1!
3247
        if (rrc->d_type == QType::CNAME || rrc->d_type == dtype) {
1!
3248
          /* also remove any RRSIG that could conflict */
3249
          remove = true;
1✔
3250
        }
1✔
3251
      }
1✔
3252
    }
1✔
3253

3254
    if (remove) {
6✔
3255
      it = records.erase(it);
2✔
3256
    }
2✔
3257
    else {
4✔
3258
      ++it;
4✔
3259
    }
4✔
3260
  }
6✔
3261
}
67✔
3262

3263
void SyncRes::handlePolicyHit(const std::string& prefix, const DNSName& qname, const QType qtype, std::vector<DNSRecord>& ret, bool& done, int& rcode, unsigned int depth)
3264
{
107✔
3265
  if (d_pdl && d_pdl->policyHitEventFilter(d_requestor, qname, qtype, d_queryReceivedOverTCP, d_appliedPolicy, d_policyTags, d_discardedPolicies)) {
107✔
3266
    /* reset to no match */
3267
    d_appliedPolicy = DNSFilterEngine::Policy();
2✔
3268
    return;
2✔
3269
  }
2✔
3270

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

3277
  if (d_appliedPolicy.d_type != DNSFilterEngine::PolicyType::None) {
105!
3278
    LOG(prefix << qname << "|" << qtype << ':' << d_appliedPolicy.getLogString() << endl);
105!
3279
  }
105✔
3280

3281
  switch (d_appliedPolicy.d_kind) {
105!
3282

3283
  case DNSFilterEngine::PolicyKind::NoAction:
22✔
3284
    return;
22✔
3285

3286
  case DNSFilterEngine::PolicyKind::Drop:
6✔
3287
    ++t_Counters.at(rec::Counter::policyDrops);
6✔
3288
    throw ImmediateQueryDropException();
6✔
3289

3290
  case DNSFilterEngine::PolicyKind::NXDOMAIN:
4✔
3291
    ret.clear();
4✔
3292
    d_appliedPolicy.addSOAtoRPZResult(ret);
4✔
3293
    rcode = RCode::NXDomain;
4✔
3294
    done = true;
4✔
3295
    return;
4✔
3296

3297
  case DNSFilterEngine::PolicyKind::NODATA:
4✔
3298
    ret.clear();
4✔
3299
    d_appliedPolicy.addSOAtoRPZResult(ret);
4✔
3300
    rcode = RCode::NoError;
4✔
3301
    done = true;
4✔
3302
    return;
4✔
3303

3304
  case DNSFilterEngine::PolicyKind::Truncate:
6✔
3305
    if (!d_queryReceivedOverTCP) {
6✔
3306
      ret.clear();
3✔
3307
      rcode = RCode::NoError;
3✔
3308
      // Exception handling code in pdns_recursor clears ret as well, so no use to
3309
      // fill it here.
3310
      throw SendTruncatedAnswerException();
3✔
3311
    }
3✔
3312
    return;
3✔
3313

3314
  case DNSFilterEngine::PolicyKind::Custom: {
63✔
3315
    if (rpzHitShouldReplaceContent(qname, qtype, ret)) {
63✔
3316
      ret.clear();
60✔
3317
    }
60✔
3318

3319
    rcode = RCode::NoError;
63✔
3320
    done = true;
63✔
3321
    auto spoofed = d_appliedPolicy.getCustomRecords(qname, qtype.getCode());
63✔
3322
    for (auto& dnsRecord : spoofed) {
67✔
3323
      removeConflictingRecord(ret, dnsRecord.d_name, dnsRecord.d_type);
67✔
3324
    }
67✔
3325

3326
    for (auto& dnsRecord : spoofed) {
67✔
3327
      ret.push_back(dnsRecord);
67✔
3328

3329
      if (dnsRecord.d_name == qname && dnsRecord.d_type == QType::CNAME && qtype != QType::CNAME) {
67!
3330
        if (auto content = getRR<CNAMERecordContent>(dnsRecord)) {
10!
3331
          vState newTargetState = vState::Indeterminate;
10✔
3332
          handleNewTarget(prefix, qname, content->getTarget(), qtype.getCode(), ret, rcode, depth, {}, newTargetState);
10✔
3333
        }
10✔
3334
      }
10✔
3335
    }
67✔
3336
    d_appliedPolicy.addSOAtoRPZResult(ret);
63✔
3337
  }
63✔
3338
  }
105✔
3339
}
105✔
3340

3341
bool SyncRes::nameserversBlockedByRPZ(const DNSFilterEngine& dfe, const NsSet& nameservers)
3342
{
10,729✔
3343
  /* we skip RPZ processing if:
3344
     - it was disabled (d_wantsRPZ is false) ;
3345
     - we already got a RPZ hit (d_appliedPolicy.d_type != DNSFilterEngine::PolicyType::None) since
3346
     the only way we can get back here is that it was a 'pass-thru' (NoAction) meaning that we should not
3347
     process any further RPZ rules. Except that we need to process rules of higher priority..
3348
  */
3349
  if (d_wantsRPZ && !d_appliedPolicy.wasHit()) {
10,729✔
3350
    for (auto const& nameserver : nameservers) {
62,703✔
3351
      bool match = dfe.getProcessingPolicy(nameserver.first, d_discardedPolicies, d_appliedPolicy);
62,703✔
3352
      if (match) {
62,703!
3353
        mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());
×
3354
        if (d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) { // client query needs an RPZ response
×
3355
          LOG(", however nameserver " << nameserver.first << " was blocked by RPZ policy '" << d_appliedPolicy.getName() << "'" << endl);
×
3356
          return true;
×
3357
        }
×
3358
      }
×
3359

3360
      // Traverse all IP addresses for this NS to see if they have an RPN NSIP policy
3361
      for (auto const& address : nameserver.second.first) {
62,703✔
3362
        match = dfe.getProcessingPolicy(address, d_discardedPolicies, d_appliedPolicy);
212✔
3363
        if (match) {
212✔
3364
          mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());
3✔
3365
          if (d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) { // client query needs an RPZ response
3✔
3366
            LOG(", however nameserver " << nameserver.first << " IP address " << address.toString() << " was blocked by RPZ policy '" << d_appliedPolicy.getName() << "'" << endl);
2!
3367
            return true;
2✔
3368
          }
2✔
3369
        }
3✔
3370
      }
212✔
3371
    }
62,703✔
3372
  }
10,710✔
3373
  return false;
10,727✔
3374
}
10,729✔
3375

3376
bool SyncRes::nameserverIPBlockedByRPZ(const DNSFilterEngine& dfe, const ComboAddress& remoteIP)
3377
{
14,001✔
3378
  /* we skip RPZ processing if:
3379
     - it was disabled (d_wantsRPZ is false) ;
3380
     - we already got a RPZ hit (d_appliedPolicy.d_type != DNSFilterEngine::PolicyType::None) since
3381
     the only way we can get back here is that it was a 'pass-thru' (NoAction) meaning that we should not
3382
     process any further RPZ rules. Except that we need to process rules of higher priority..
3383
  */
3384
  if (d_wantsRPZ && !d_appliedPolicy.wasHit()) {
14,001✔
3385
    bool match = dfe.getProcessingPolicy(remoteIP, d_discardedPolicies, d_appliedPolicy);
13,981✔
3386
    if (match) {
13,981✔
3387
      mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());
4✔
3388
      if (d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) {
4!
3389
        LOG(" (blocked by RPZ policy '" + d_appliedPolicy.getName() + "')");
4!
3390
        return true;
4✔
3391
      }
4✔
3392
    }
4✔
3393
  }
13,981✔
3394
  return false;
13,997✔
3395
}
14,001✔
3396

3397
static bool shouldNotThrottle(const DNSName* name, const ComboAddress* address)
3398
{
12,933✔
3399
  if (name != nullptr) {
12,933!
3400
    auto dontThrottleNames = g_dontThrottleNames.getLocal();
12,933✔
3401
    if (dontThrottleNames->check(*name)) {
12,933!
3402
      return true;
×
3403
    }
×
3404
  }
12,933✔
3405
  if (address != nullptr) {
12,933✔
3406
    auto dontThrottleNetmasks = g_dontThrottleNetmasks.getLocal();
12,900✔
3407
    if (dontThrottleNetmasks->match(*address)) {
12,900✔
3408
      return true;
4✔
3409
    }
4✔
3410
  }
12,900✔
3411
  return false;
12,929✔
3412
}
12,933✔
3413

3414
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)
3415
{
12,848✔
3416
  vector<ComboAddress> result;
12,848✔
3417

3418
  size_t nonresolvingfails = 0;
12,848✔
3419
  if (!tns->first.empty()) {
12,849✔
3420
    if (s_nonresolvingnsmaxfails > 0) {
12,675✔
3421
      nonresolvingfails = s_nonresolving.lock()->value(tns->first);
10,195✔
3422
      if (nonresolvingfails >= s_nonresolvingnsmaxfails) {
10,195✔
3423
        LOG(prefix << qname << ": NS " << tns->first << " in non-resolving map, skipping" << endl);
18!
3424
        return result;
18✔
3425
      }
18✔
3426
    }
10,195✔
3427

3428
    LOG(prefix << qname << ": Trying to resolve NS '" << tns->first << "' (" << 1 + tns - rnameservers.begin() << "/" << (unsigned int)rnameservers.size() << ")" << endl);
12,657✔
3429
    const unsigned int oldOutQueries = d_outqueries;
12,657✔
3430
    try {
12,657✔
3431
      result = getAddrs(tns->first, depth, prefix, beenthere, cacheOnly, nretrieveAddressesForNS);
12,657✔
3432
    }
12,657✔
3433
    // Other exceptions should likely not throttle...
3434
    catch (const ImmediateServFailException& ex) {
12,657✔
3435
      if (s_nonresolvingnsmaxfails > 0 && d_outqueries > oldOutQueries) {
90!
3436
        if (!shouldNotThrottle(&tns->first, nullptr)) {
×
3437
          s_nonresolving.lock()->incr(tns->first, d_now);
×
3438
        }
×
3439
      }
×
3440
      throw ex;
90✔
3441
    }
90✔
3442
    if (s_nonresolvingnsmaxfails > 0 && d_outqueries > oldOutQueries) {
12,567✔
3443
      if (result.empty()) {
1,351✔
3444
        if (!shouldNotThrottle(&tns->first, nullptr)) {
33!
3445
          s_nonresolving.lock()->incr(tns->first, d_now);
33✔
3446
        }
33✔
3447
      }
33✔
3448
      else if (nonresolvingfails > 0) {
1,318✔
3449
        // Succeeding resolve, clear memory of recent failures
3450
        s_nonresolving.lock()->clear(tns->first);
2✔
3451
      }
2✔
3452
    }
1,351✔
3453
    pierceDontQuery = false;
12,567✔
3454
  }
12,567✔
3455
  else {
2,147,483,821✔
3456
    LOG(prefix << qname << ": Domain has hardcoded nameserver");
2,147,483,821✔
3457

3458
    if (nameservers[tns->first].first.size() > 1) {
2,147,483,821✔
3459
      LOG("s");
24!
3460
    }
24✔
3461
    LOG(endl);
2,147,483,821✔
3462

3463
    sendRDQuery = nameservers[tns->first].second;
2,147,483,821✔
3464
    result = shuffleForwardSpeed(qname, nameservers[tns->first].first, prefix, sendRDQuery);
2,147,483,821✔
3465
    pierceDontQuery = true;
2,147,483,821✔
3466
  }
2,147,483,821✔
3467
  return result;
12,740✔
3468
}
12,848✔
3469

3470
void SyncRes::checkMaxQperQ(const DNSName& qname) const
3471
{
13,122✔
3472
  if (d_outqueries + d_throttledqueries > s_maxqperq) {
13,122✔
3473
    throw ImmediateServFailException("more than " + std::to_string(s_maxqperq) + " (max-qperq) queries sent or throttled while resolving " + qname.toLogString());
4✔
3474
  }
4✔
3475
}
13,122✔
3476

3477
bool SyncRes::throttledOrBlocked(const std::string& prefix, const ComboAddress& remoteIP, const DNSName& qname, const QType qtype, bool pierceDontQuery)
3478
{
12,814✔
3479
  if (isThrottled(d_now.tv_sec, remoteIP)) {
12,814✔
3480
    LOG(prefix << qname << ": Server throttled " << endl);
2!
3481
    t_Counters.at(rec::Counter::throttledqueries)++;
2✔
3482
    d_throttledqueries++;
2✔
3483
    return true;
2✔
3484
  }
2✔
3485
  if (isThrottled(d_now.tv_sec, remoteIP, qname, qtype)) {
12,812✔
3486
    LOG(prefix << qname << ": Query throttled " << remoteIP.toString() << ", " << qname << "; " << qtype << endl);
6!
3487
    t_Counters.at(rec::Counter::throttledqueries)++;
6✔
3488
    d_throttledqueries++;
6✔
3489
    return true;
6✔
3490
  }
6✔
3491
  if (!pierceDontQuery && s_dontQuery && s_dontQuery->match(&remoteIP)) {
12,806✔
3492
    // We could have retrieved an NS from the cache in a forwarding domain
3493
    // Even in the case of !pierceDontQuery we still want to allow that NS
3494
    DNSName forwardCandidate(qname);
2✔
3495
    auto iter = getBestAuthZone(&forwardCandidate);
2✔
3496
    if (iter == t_sstorage.domainmap->end()) {
2!
3497
      LOG(prefix << qname << ": Not sending query to " << remoteIP.toString() << ", blocked by 'dont-query' setting" << endl);
2!
3498
      t_Counters.at(rec::Counter::dontqueries)++;
2✔
3499
      return true;
2✔
3500
    }
2✔
3501
    // The name (from the cache) is forwarded, but is it forwarded to an IP in known forwarders?
3502
    const auto& ips = iter->second.d_servers;
×
3503
    if (std::find(ips.cbegin(), ips.cend(), remoteIP) == ips.cend()) {
×
3504
      LOG(prefix << qname << ": Not sending query to " << remoteIP.toString() << ", blocked by 'dont-query' setting" << endl);
×
3505
      t_Counters.at(rec::Counter::dontqueries)++;
×
3506
      return true;
×
3507
    }
×
3508
    LOG(prefix << qname << ": Sending query to " << remoteIP.toString() << ", blocked by 'dont-query' but a forwarding/auth case" << endl);
×
3509
  }
×
3510
  return false;
12,804✔
3511
}
12,806✔
3512

3513
bool SyncRes::validationEnabled()
3514
{
14,045✔
3515
  return g_dnssecmode != DNSSECMode::Off && g_dnssecmode != DNSSECMode::ProcessNoValidate;
14,045✔
3516
}
14,045✔
3517

3518
uint32_t SyncRes::computeLowestTTD(const std::vector<DNSRecord>& records, const MemRecursorCache::SigRecsVec& signatures, uint32_t signaturesTTL, const MemRecursorCache::AuthRecsVec& authorityRecs) const
3519
{
12,767✔
3520
  uint32_t lowestTTD = std::numeric_limits<uint32_t>::max();
12,767✔
3521
  for (const auto& record : records) {
28,347✔
3522
    lowestTTD = min(lowestTTD, record.d_ttl);
28,347✔
3523
  }
28,347✔
3524

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

3532
    for (const auto& sig : signatures) {
8,772✔
3533
      if (isRRSIGNotExpired(d_now.tv_sec, *sig)) {
8,772✔
3534
        // we don't decrement d_sigexpire by 'now' because we actually want a TTD, not a TTL */
3535
        lowestTTD = min(lowestTTD, static_cast<uint32_t>(sig->d_sigexpire));
8,759✔
3536
      }
8,759✔
3537
    }
8,772✔
3538
  }
8,750✔
3539

3540
  for (const auto& entry : authorityRecs) {
12,767✔
3541
    /* be careful, this is still a TTL here */
3542
    lowestTTD = min(lowestTTD, static_cast<uint32_t>(entry.d_ttl + d_now.tv_sec));
2,536✔
3543

3544
    if (entry.d_type == QType::RRSIG && validationEnabled()) {
2,536!
3545
      auto rrsig = getRR<RRSIGRecordContent>(entry);
1,268✔
3546
      if (rrsig) {
1,268!
3547
        if (isRRSIGNotExpired(d_now.tv_sec, *rrsig)) {
1,268!
3548
          // we don't decrement d_sigexpire by 'now' because we actually want a TTD, not a TTL */
3549
          lowestTTD = min(lowestTTD, static_cast<uint32_t>(rrsig->d_sigexpire));
1,268✔
3550
        }
1,268✔
3551
      }
1,268✔
3552
    }
1,268✔
3553
  }
2,536✔
3554

3555
  return lowestTTD;
12,767✔
3556
}
12,767✔
3557

3558
void SyncRes::updateValidationState(const DNSName& qname, vState& state, const vState stateUpdate, const string& prefix)
3559
{
17,127✔
3560
  LOG(prefix << qname << ": Validation state was " << state << ", state update is " << stateUpdate);
17,127✔
3561
  updateDNSSECValidationState(state, stateUpdate);
17,127✔
3562
  LOG(", validation state is now " << state << endl);
17,127✔
3563
}
17,127✔
3564

3565
vState SyncRes::getTA(const DNSName& zone, dsset_t& dsSet, const string& prefix)
3566
{
81,978✔
3567
  auto luaLocal = g_luaconfs.getLocal();
81,978✔
3568

3569
  if (luaLocal->dsAnchors.empty()) {
81,978✔
3570
    LOG(prefix << zone << ": No trust anchors configured, everything is Insecure" << endl);
44!
3571
    /* We have no TA, everything is insecure */
3572
    return vState::Insecure;
44✔
3573
  }
44✔
3574

3575
  std::string reason;
81,934✔
3576
  if (haveNegativeTrustAnchor(luaLocal->negAnchors, zone, reason)) {
81,934✔
3577
    LOG(prefix << zone << ": Got NTA" << endl);
15!
3578
    return vState::NTA;
15✔
3579
  }
15✔
3580

3581
  if (getTrustAnchor(luaLocal->dsAnchors, zone, dsSet)) {
81,919✔
3582
    if (!zone.isRoot()) {
23,384✔
3583
      LOG(prefix << zone << ": Got TA" << endl);
144!
3584
    }
144✔
3585
    return vState::TA;
23,384✔
3586
  }
23,384✔
3587

3588
  if (zone.isRoot()) {
58,535!
3589
    /* No TA for the root */
3590
    return vState::Insecure;
×
3591
  }
×
3592

3593
  return vState::Indeterminate;
58,535✔
3594
}
58,535✔
3595

3596
size_t SyncRes::countSupportedDS(const dsset_t& dsset, const string& prefix)
3597
{
23,384✔
3598
  size_t count = 0;
23,384✔
3599

3600
  for (const auto& dsRecordContent : dsset) {
49,021✔
3601
    if (isSupportedDS(dsRecordContent, LogObject(prefix))) {
49,023✔
3602
      count++;
49,014✔
3603
    }
49,014✔
3604
  }
49,021✔
3605

3606
  return count;
23,384✔
3607
}
23,384✔
3608

3609
void SyncRes::initZoneCutsFromTA(const DNSName& from, const string& prefix)
3610
{
22,804✔
3611
  DNSName zone(from);
22,804✔
3612
  do {
75,903✔
3613
    dsset_t dsSet;
75,903✔
3614
    vState result = getTA(zone, dsSet, prefix);
75,903✔
3615
    if (result != vState::Indeterminate) {
75,903✔
3616
      if (result == vState::TA) {
22,960✔
3617
        if (countSupportedDS(dsSet, prefix) == 0) {
22,901✔
3618
          dsSet.clear();
8✔
3619
          result = vState::Insecure;
8✔
3620
        }
8✔
3621
        else {
22,893✔
3622
          result = vState::Secure;
22,893✔
3623
        }
22,893✔
3624
      }
22,901✔
3625
      else if (result == vState::NTA) {
59✔
3626
        result = vState::Insecure;
15✔
3627
      }
15✔
3628

3629
      d_cutStates[zone] = result;
22,960✔
3630
    }
22,960✔
3631
  } while (zone.chopOff());
75,903✔
3632
}
22,804✔
3633

3634
vState SyncRes::getDSRecords(const DNSName& zone, dsset_t& dsSet, bool onlyTA, unsigned int depth, const string& prefix, bool bogusOnNXD, bool* foundCut)
3635
{
6,075✔
3636
  vState result = getTA(zone, dsSet, prefix);
6,075✔
3637

3638
  if (result != vState::Indeterminate || onlyTA) {
6,075!
3639
    if (foundCut != nullptr) {
484!
3640
      *foundCut = (result != vState::Indeterminate);
×
3641
    }
×
3642

3643
    if (result == vState::TA) {
484!
3644
      if (countSupportedDS(dsSet, prefix) == 0) {
484!
3645
        dsSet.clear();
×
3646
        result = vState::Insecure;
×
3647
      }
×
3648
      else {
484✔
3649
        result = vState::Secure;
484✔
3650
      }
484✔
3651
    }
484✔
3652
    else if (result == vState::NTA) {
×
3653
      result = vState::Insecure;
×
3654
    }
×
3655

3656
    return result;
484✔
3657
  }
484✔
3658

3659
  std::set<GetBestNSAnswer> beenthere;
5,591✔
3660
  std::vector<DNSRecord> dsrecords;
5,591✔
3661

3662
  Context context;
5,591✔
3663

3664
  const bool oldCacheOnly = setCacheOnly(false);
5,591✔
3665
  const bool oldQM = setQNameMinimization(!getQMFallbackMode());
5,591✔
3666
  int rcode = doResolve(zone, QType::DS, dsrecords, depth + 1, beenthere, context);
5,591✔
3667
  setCacheOnly(oldCacheOnly);
5,591✔
3668
  setQNameMinimization(oldQM);
5,591✔
3669

3670
  if (rcode == RCode::ServFail) {
5,591✔
3671
    throw ImmediateServFailException("Server Failure while retrieving DS records for " + zone.toLogString());
4✔
3672
  }
4✔
3673

3674
  if (rcode != RCode::NoError && (rcode != RCode::NXDomain || bogusOnNXD)) {
5,587!
3675
    LOG(prefix << zone << ": Returning Bogus state from " << static_cast<const char*>(__func__) << "(" << zone << ")" << endl);
2!
3676
    return vState::BogusUnableToGetDSs;
2✔
3677
  }
2✔
3678

3679
  uint8_t bestDigestType = 0;
5,585✔
3680

3681
  bool gotCNAME = false;
5,585✔
3682
  for (const auto& record : dsrecords) {
16,192✔
3683
    if (record.d_type == QType::DS) {
16,192✔
3684
      const auto dscontent = getRR<DSRecordContent>(record);
3,017✔
3685
      if (dscontent && isSupportedDS(*dscontent, LogObject(prefix))) {
3,017!
3686
        // Make GOST a lower prio than SHA256
3687
        if (dscontent->d_digesttype == DNSSECKeeper::DIGEST_GOST && bestDigestType == DNSSECKeeper::DIGEST_SHA256) {
3,017!
3688
          continue;
×
3689
        }
×
3690
        if (dscontent->d_digesttype > bestDigestType || (bestDigestType == DNSSECKeeper::DIGEST_GOST && dscontent->d_digesttype == DNSSECKeeper::DIGEST_SHA256)) {
3,017!
3691
          bestDigestType = dscontent->d_digesttype;
3,001✔
3692
        }
3,001✔
3693
        dsSet.insert(*dscontent);
3,017✔
3694
      }
3,017✔
3695
    }
3,017✔
3696
    else if (record.d_type == QType::CNAME && record.d_name == zone) {
13,175!
3697
      gotCNAME = true;
13✔
3698
    }
13✔
3699
  }
16,192✔
3700

3701
  /* RFC 4509 section 3: "Validator implementations SHOULD ignore DS RRs containing SHA-1
3702
   * digests if DS RRs with SHA-256 digests are present in the DS RRset."
3703
   * We interpret that as: do not use SHA-1 if SHA-256 or SHA-384 is available
3704
   */
3705
  for (auto dsrec = dsSet.begin(); dsrec != dsSet.end();) {
8,602✔
3706
    if (dsrec->d_digesttype == DNSSECKeeper::DIGEST_SHA1 && dsrec->d_digesttype != bestDigestType) {
3,017✔
3707
      dsrec = dsSet.erase(dsrec);
7✔
3708
    }
7✔
3709
    else {
3,010✔
3710
      ++dsrec;
3,010✔
3711
    }
3,010✔
3712
  }
3,017✔
3713

3714
  if (rcode == RCode::NoError) {
5,586✔
3715
    if (dsSet.empty()) {
5,580✔
3716
      /* we have no DS, it's either:
3717
         - a delegation to a non-DNSSEC signed zone
3718
         - no delegation, we stay in the same zone
3719
      */
3720
      if (gotCNAME || denialProvesNoDelegation(zone, dsrecords, d_validationContext)) {
2,589✔
3721
        /* we are still inside the same zone */
3722

3723
        if (foundCut != nullptr) {
31✔
3724
          *foundCut = false;
25✔
3725
        }
25✔
3726
        return context.state;
31✔
3727
      }
31✔
3728

3729
      d_cutStates[zone] = context.state == vState::Secure ? vState::Insecure : context.state;
2,558✔
3730
      /* delegation with no DS, might be Secure -> Insecure */
3731
      if (foundCut != nullptr) {
2,558✔
3732
        *foundCut = true;
2,535✔
3733
      }
2,535✔
3734

3735
      /* a delegation with no DS is either:
3736
         - a signed zone (Secure) to an unsigned one (Insecure)
3737
         - an unsigned zone to another unsigned one (Insecure stays Insecure, Bogus stays Bogus)
3738
      */
3739
      return context.state == vState::Secure ? vState::Insecure : context.state;
2,558✔
3740
    }
2,589✔
3741
    /* we have a DS */
3742
    d_cutStates[zone] = context.state;
2,991✔
3743
    if (foundCut != nullptr) {
2,991✔
3744
      *foundCut = true;
1,952✔
3745
    }
1,952✔
3746
  }
2,991✔
3747

3748
  return context.state;
2,996✔
3749
}
5,585✔
3750

3751
vState SyncRes::getValidationStatus(const DNSName& name, bool wouldBeValid, bool typeIsDS, unsigned int depth, const string& prefix)
3752
{
18,005✔
3753
  vState result = vState::Indeterminate;
18,005✔
3754

3755
  if (!shouldValidate()) {
18,005✔
3756
    return result;
1,527✔
3757
  }
1,527✔
3758

3759
  DNSName subdomain(name);
16,478✔
3760
  if (typeIsDS) {
16,478✔
3761
    subdomain.chopOff();
3,194✔
3762
  }
3,194✔
3763

3764
  {
16,478✔
3765
    const auto& iter = d_cutStates.find(subdomain);
16,478✔
3766
    if (iter != d_cutStates.cend()) {
16,478✔
3767
      LOG(prefix << name << ": Got status " << iter->second << " for name " << subdomain << endl);
3,061!
3768
      return iter->second;
3,061✔
3769
    }
3,061✔
3770
  }
16,478✔
3771

3772
  /* look for the best match we have */
3773
  DNSName best(subdomain);
13,417✔
3774
  while (best.chopOff()) {
25,783!
3775
    const auto& iter = d_cutStates.find(best);
25,783✔
3776
    if (iter != d_cutStates.cend()) {
25,783✔
3777
      result = iter->second;
13,417✔
3778
      if (vStateIsBogus(result) || result == vState::Insecure) {
13,417✔
3779
        LOG(prefix << name << ": Got status " << result << " for name " << best << endl);
2,804!
3780
        return result;
2,804✔
3781
      }
2,804✔
3782
      break;
10,613✔
3783
    }
13,417✔
3784
  }
25,783✔
3785

3786
  /* by now we have the best match, it's likely Secure (otherwise we would not be there)
3787
     but we don't know if we missed a cut (or several).
3788
     We could see if we have DS (or denial of) in cache but let's not worry for now,
3789
     we will if we don't have a signature, or if the signer doesn't match what we expect */
3790
  if (!wouldBeValid && best != subdomain) {
10,613!
3791
    /* no signatures or Bogus, we likely missed a cut, let's try to find it */
3792
    LOG(prefix << name << ": No or invalid signature/proof for " << name << ", we likely missed a cut between " << best << " and " << subdomain << ", looking for it" << endl);
2,596!
3793
    DNSName dsName(best);
2,596✔
3794
    std::vector<string> labelsToAdd = subdomain.makeRelative(dsName).getRawLabels();
2,596✔
3795

3796
    while (!labelsToAdd.empty()) {
4,579✔
3797

3798
      dsName.prependRawLabel(labelsToAdd.back());
4,518✔
3799
      labelsToAdd.pop_back();
4,518✔
3800
      LOG(prefix << name << ": - Looking for a DS at " << dsName << endl);
4,518!
3801

3802
      bool foundCut = false;
4,518✔
3803
      dsset_t results;
4,518✔
3804
      vState dsState = getDSRecords(dsName, results, false, depth, prefix, false, &foundCut);
4,518✔
3805

3806
      if (foundCut) {
4,518✔
3807
        LOG(prefix << name << ": - Found cut at " << dsName << endl);
4,487!
3808
        LOG(prefix << name << ": New state for " << dsName << " is " << dsState << endl);
4,487!
3809
        d_cutStates[dsName] = dsState;
4,487✔
3810

3811
        if (dsState != vState::Secure) {
4,487✔
3812
          return dsState;
2,535✔
3813
        }
2,535✔
3814
      }
4,487✔
3815
    }
4,518✔
3816

3817
    /* we did not miss a cut, good luck */
3818
    return result;
61✔
3819
  }
2,596✔
3820

3821
#if 0
3822
  /* we don't need this, we actually do the right thing later */
3823
  DNSName signer = getSigner(signatures);
3824

3825
  if (!signer.empty() && name.isPartOf(signer)) {
3826
    if (signer == best) {
3827
      return result;
3828
    }
3829
    /* the zone cut is not the one we expected,
3830
       this is fine because we will retrieve the needed DNSKEYs and DSs
3831
       later, and even go Insecure if we missed a cut to Insecure (no DS)
3832
       and the signatures do not validate (we should not go Bogus in that
3833
       case) */
3834
  }
3835
  /* something is not right, but let's not worry about that for now.. */
3836
#endif
3837

3838
  return result;
8,017✔
3839
}
10,613✔
3840

3841
vState SyncRes::validateDNSKeys(const DNSName& zone, const std::vector<DNSRecord>& dnskeys, const MemRecursorCache::SigRecsVec& signatures, unsigned int depth, const string& prefix)
3842
{
1,539✔
3843
  dsset_t dsSet;
1,539✔
3844
  if (signatures.empty()) {
1,539!
3845
    LOG(prefix << zone << ": We have " << std::to_string(dnskeys.size()) << " DNSKEYs but no signature, going Bogus!" << endl);
×
3846
    return vState::BogusNoRRSIG;
×
3847
  }
×
3848

3849
  DNSName signer = getSigner(signatures);
1,539✔
3850

3851
  if (!signer.empty() && zone.isPartOf(signer)) {
1,539!
3852
    vState state = getDSRecords(signer, dsSet, false, depth, prefix);
1,539✔
3853

3854
    if (state != vState::Secure) {
1,539✔
3855
      return state;
31✔
3856
    }
31✔
3857
  }
1,539✔
3858
  else {
×
3859
    LOG(prefix << zone << ": We have " << std::to_string(dnskeys.size()) << " DNSKEYs but the zone (" << zone << ") is not part of the signer (" << signer << "), check that we did not miss a zone cut" << endl);
×
3860
    /* try again to get the missed cuts, harder this time */
3861
    auto zState = getValidationStatus(zone, false, false, depth, prefix);
×
3862
    if (zState == vState::Secure) {
×
3863
      /* too bad */
3864
      LOG(prefix << zone << ": After checking the zone cuts again, we still have " << std::to_string(dnskeys.size()) << " DNSKEYs and the zone (" << zone << ") is still not part of the signer (" << signer << "), going Bogus!" << endl);
×
3865
      return vState::BogusNoValidRRSIG;
×
3866
    }
×
3867
    return zState;
×
3868
  }
×
3869

3870
  skeyset_t tentativeKeys;
1,508✔
3871
  sortedRecords_t toSign;
1,508✔
3872

3873
  for (const auto& dnskey : dnskeys) {
1,890✔
3874
    if (dnskey.d_type == QType::DNSKEY) {
1,890!
3875
      auto content = getRR<DNSKEYRecordContent>(dnskey);
1,890✔
3876
      if (content) {
1,890!
3877
        tentativeKeys.insert(content);
1,890✔
3878
        toSign.insert(content);
1,890✔
3879
      }
1,890✔
3880
    }
1,890✔
3881
  }
1,890✔
3882

3883
  LOG(prefix << zone << ": Trying to validate " << std::to_string(tentativeKeys.size()) << " DNSKEYs with " << std::to_string(dsSet.size()) << " DS" << endl);
1,508!
3884
  skeyset_t validatedKeys;
1,508✔
3885
  auto state = validateDNSKeysAgainstDS(d_now.tv_sec, zone, dsSet, tentativeKeys, toSign, signatures, validatedKeys, LogObject(prefix), d_validationContext);
1,508✔
3886

3887
  if (s_maxvalidationsperq != 0 && d_validationContext.d_validationsCounter > s_maxvalidationsperq) {
1,508!
3888
    throw ImmediateServFailException("Server Failure while validating DNSKEYs, too many signature validations for this query");
×
3889
  }
×
3890

3891
  LOG(prefix << zone << ": We now have " << std::to_string(validatedKeys.size()) << " DNSKEYs" << endl);
1,508!
3892

3893
  /* if we found at least one valid RRSIG covering the set,
3894
     all tentative keys are validated keys. Otherwise it means
3895
     we haven't found at least one DNSKEY and a matching RRSIG
3896
     covering this set, this looks Bogus. */
3897
  if (validatedKeys.size() != tentativeKeys.size()) {
1,508✔
3898
    LOG(prefix << zone << ": Let's check whether we missed a zone cut before returning a Bogus state from " << static_cast<const char*>(__func__) << "(" << zone << ")" << endl);
44!
3899
    /* try again to get the missed cuts, harder this time */
3900
    auto zState = getValidationStatus(zone, false, false, depth, prefix);
44✔
3901
    if (zState == vState::Secure) {
44!
3902
      /* too bad */
3903
      LOG(prefix << zone << ": After checking the zone cuts we are still in a Secure zone, returning Bogus state from " << static_cast<const char*>(__func__) << "(" << zone << ")" << endl);
44!
3904
      return state;
44✔
3905
    }
44✔
3906
    return zState;
×
3907
  }
44✔
3908

3909
  return state;
1,464✔
3910
}
1,508✔
3911

3912
vState SyncRes::getDNSKeys(const DNSName& signer, skeyset_t& keys, bool& servFailOccurred, unsigned int depth, const string& prefix)
3913
{
5,375✔
3914
  std::vector<DNSRecord> records;
5,375✔
3915
  std::set<GetBestNSAnswer> beenthere;
5,375✔
3916
  LOG(prefix << signer << ": Retrieving DNSKEYs" << endl);
5,375!
3917

3918
  Context context;
5,375✔
3919

3920
  const bool oldCacheOnly = setCacheOnly(false);
5,375✔
3921
  int rcode = doResolve(signer, QType::DNSKEY, records, depth + 1, beenthere, context);
5,375✔
3922
  setCacheOnly(oldCacheOnly);
5,375✔
3923

3924
  if (rcode == RCode::ServFail) {
5,375✔
3925
    servFailOccurred = true;
12✔
3926
    return vState::BogusUnableToGetDNSKEYs;
12✔
3927
  }
12✔
3928

3929
  if (rcode == RCode::NoError) {
5,363✔
3930
    if (context.state == vState::Secure) {
5,357✔
3931
      for (const auto& key : records) {
13,241✔
3932
        if (key.d_type == QType::DNSKEY) {
13,241✔
3933
          auto content = getRR<DNSKEYRecordContent>(key);
7,998✔
3934
          if (content) {
7,999✔
3935
            keys.insert(std::move(content));
7,999✔
3936
          }
7,999✔
3937
        }
7,998✔
3938
      }
13,241✔
3939
    }
5,245✔
3940
    LOG(prefix << signer << ": Retrieved " << keys.size() << " DNSKeys, state is " << context.state << endl);
5,357!
3941
    return context.state;
5,357✔
3942
  }
5,357✔
3943

3944
  if (context.state == vState::Insecure) {
6✔
3945
    return context.state;
2✔
3946
  }
2✔
3947

3948
  LOG(prefix << signer << ": Returning Bogus state from " << static_cast<const char*>(__func__) << "(" << signer << ")" << endl);
4!
3949
  return vState::BogusUnableToGetDNSKEYs;
4✔
3950
}
6✔
3951

3952
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)
3953
{
5,435✔
3954
  skeyset_t keys;
5,435✔
3955
  if (signatures.empty()) {
5,435✔
3956
    LOG(prefix << qname << ": Bogus!" << endl);
36!
3957
    return vState::BogusNoRRSIG;
36✔
3958
  }
36✔
3959

3960
  const DNSName signer = getSigner(signatures);
5,399✔
3961
  bool dsFailed = false;
5,399✔
3962
  if (!signer.empty() && name.isPartOf(signer)) {
5,399!
3963
    vState state = vState::Secure;
5,397✔
3964

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

4025
    if (state != vState::Secure) {
5,389✔
4026
      if (!vStateIsBogus(state)) {
140✔
4027
        return state;
47✔
4028
      }
47✔
4029
      /* try again to get the missed cuts, harder this time */
4030
      LOG(prefix << signer << ": Checking whether we missed a zone cut for " << signer << " before returning a Bogus state for " << name << "|" << type.toString() << endl);
93!
4031
      auto zState = getValidationStatus(signer, false, dsFailed, depth, prefix);
93✔
4032
      if (zState == vState::Secure) {
93✔
4033
        if (state == vState::BogusUnableToGetDNSKEYs && servFailOccurred) {
75!
4034
          throw ImmediateServFailException("Server Failure while retrieving DNSKEY records for " + signer.toLogString());
8✔
4035
        }
8✔
4036
        /* too bad */
4037
        LOG(prefix << signer << ": We are still in a Secure zone, returning " << vStateToString(state) << endl);
67!
4038
        return state;
67✔
4039
      }
75✔
4040
      return zState;
18✔
4041
    }
93✔
4042
  }
5,389✔
4043

4044
  sortedRecords_t recordcontents;
5,251✔
4045
  for (const auto& record : records) {
5,737✔
4046
    recordcontents.insert(record.getContent());
5,737✔
4047
  }
5,737✔
4048

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

4055
  if (state == vState::Secure) {
5,249✔
4056
    LOG(prefix << name << ": Secure!" << endl);
5,218!
4057
    return vState::Secure;
5,218✔
4058
  }
5,218✔
4059

4060
  LOG(prefix << vStateToString(state) << "!" << endl);
31!
4061

4062
  bool skipThisLevelWhenLookingForMissedCuts = false;
31✔
4063
  if (name == qname && qtype == QType::DS && (type == QType::NSEC || type == QType::NSEC3)) {
31!
4064
    /* so we have a NSEC(3) record likely proving that the DS we were looking for does not exist,
4065
       but we cannot validate it:
4066
       - if there actually is a cut at this level, we will not be able to validate it anyway
4067
       - if there is no cut at this level, the only thing that can save us is a cut above
4068
    */
4069
    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!
4070
    skipThisLevelWhenLookingForMissedCuts = true;
2✔
4071
  }
2✔
4072

4073
  /* try again to get the missed cuts, harder this time */
4074
  auto zState = getValidationStatus(name, false, type == QType::DS || skipThisLevelWhenLookingForMissedCuts, depth, prefix);
31✔
4075
  LOG(prefix << name << ": Checking whether we missed a zone cut before returning a Bogus state" << endl);
31!
4076
  if (zState == vState::Secure) {
31✔
4077
    /* too bad */
4078
    LOG(prefix << name << ": We are still in a Secure zone, returning " << vStateToString(state) << endl);
23!
4079
    return state;
23✔
4080
  }
23✔
4081
  return zState;
8✔
4082
}
31✔
4083

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

4090
  if (wasForwardRecurse || lwr.d_aabit) {
12,458✔
4091
    /* easy */
4092
    return;
8,875✔
4093
  }
8,875✔
4094

4095
  for (const auto& rec : lwr.d_records) {
3,583✔
4096

4097
    if (rec.d_type == QType::OPT) {
3,531!
4098
      continue;
×
4099
    }
×
4100

4101
    if (rec.d_class != QClass::IN) {
3,531!
4102
      continue;
×
4103
    }
×
4104

4105
    if (rec.d_type == QType::ANY) {
3,531!
4106
      continue;
×
4107
    }
×
4108

4109
    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,531!
4110
      /* This is clearly an answer to the question we were asking, from an authoritative server that is allowed to send it.
4111
         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 */
4112
      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!
4113
      lwr.d_aabit = true;
10✔
4114
      return;
10✔
4115
    }
10✔
4116

4117
    if (rec.d_place != DNSResourceRecord::ANSWER) {
3,521!
4118
      /* we have scanned all the records in the answer section, if any, we are done */
4119
      return;
3,521✔
4120
    }
3,521✔
4121
  }
3,521✔
4122
}
3,583✔
4123

4124
static void allowAdditionalEntry(std::unordered_set<DNSName>& allowedAdditionals, const DNSRecord& rec)
4125
{
29,849✔
4126
  // As we only use a limited amount of NS names for resolving, limit number of additional names as
4127
  // well.  s_maxnsperresolve is a proper limit for the NS case and is also reasonable for other
4128
  // qtypes.  Allow one extra for qname itself, which is always in allowedAdditionals.
4129
  if (SyncRes::s_maxnsperresolve > 0 && allowedAdditionals.size() > SyncRes::s_maxnsperresolve + 1) {
29,849!
4130
    return;
12✔
4131
  }
12✔
4132
  switch (rec.d_type) {
29,837✔
4133
  case QType::MX:
25✔
4134
    if (auto mxContent = getRR<MXRecordContent>(rec)) {
25!
4135
      allowedAdditionals.insert(mxContent->d_mxname);
25✔
4136
    }
25✔
4137
    break;
25✔
4138
  case QType::NS:
16,173✔
4139
    if (auto nsContent = getRR<NSRecordContent>(rec)) {
16,174✔
4140
      allowedAdditionals.insert(nsContent->getNS());
16,174✔
4141
    }
16,174✔
4142
    break;
16,173✔
4143
  case QType::SRV:
5✔
4144
    if (auto srvContent = getRR<SRVRecordContent>(rec)) {
5!
4145
      allowedAdditionals.insert(srvContent->d_target);
5✔
4146
    }
5✔
4147
    break;
5✔
4148
  case QType::SVCB: /* fall-through */
×
4149
  case QType::HTTPS:
×
4150
    if (auto svcbContent = getRR<SVCBBaseRecordContent>(rec)) {
×
4151
      if (svcbContent->getPriority() > 0) {
×
4152
        DNSName target = svcbContent->getTarget();
×
4153
        if (target.isRoot()) {
×
4154
          target = rec.d_name;
×
4155
        }
×
4156
        allowedAdditionals.insert(std::move(target));
×
4157
      }
×
4158
      else {
×
4159
        // FIXME: Alias mode not implemented yet
4160
      }
×
4161
    }
×
4162
    break;
×
4163
  case QType::NAPTR:
6✔
4164
    if (auto naptrContent = getRR<NAPTRRecordContent>(rec)) {
6!
4165
      auto flags = naptrContent->getFlags();
6✔
4166
      toLowerInPlace(flags);
6✔
4167
      if (flags.find('a') != string::npos || flags.find('s') != string::npos) {
6!
4168
        allowedAdditionals.insert(naptrContent->getReplacement());
6✔
4169
      }
6✔
4170
    }
6✔
4171
    break;
6✔
4172
  default:
13,627✔
4173
    break;
13,627✔
4174
  }
29,837✔
4175
}
29,837✔
4176

4177
static bool isRedirection(QType qtype)
4178
{
20,752✔
4179
  return qtype == QType::CNAME || qtype == QType::DNAME;
20,752✔
4180
}
20,752✔
4181

4182
void SyncRes::sanitizeRecords(const std::string& prefix, LWResult& lwr, const DNSName& qname, const QType qtype, const DNSName& auth, bool wasForwarded, bool rdQuery)
4183
{
12,458✔
4184
  const bool wasForwardRecurse = wasForwarded && rdQuery;
12,458✔
4185
  /* list of names for which we will allow A and AAAA records in the additional section
4186
     to remain */
4187
  std::unordered_set<DNSName> allowedAdditionals = {qname};
12,458✔
4188
  std::unordered_set<DNSName> allowedAnswerNames = {qname};
12,458✔
4189
  bool cnameSeen = false;
12,458✔
4190
  bool haveAnswers = false;
12,458✔
4191
  bool isNXDomain = false;
12,458✔
4192
  bool isNXQType = false;
12,458✔
4193

4194
  std::vector<bool> skipvec(lwr.d_records.size(), false);
12,458✔
4195
  unsigned int counter = 0;
12,458✔
4196
  unsigned int skipCount = 0;
12,458✔
4197

4198
  for (auto rec = lwr.d_records.cbegin(); rec != lwr.d_records.cend(); ++rec, ++counter) {
84,159✔
4199

4200
    // Allow OPT record containing EDNS(0) data
4201
    if (rec->d_type == QType::OPT) {
71,701✔
4202
      continue;
10,118✔
4203
    }
10,118✔
4204

4205
    // Disallow QClass != IN
4206
    if (rec->d_class != QClass::IN) {
61,583!
4207
      LOG(prefix << qname << ": Removing non internet-classed data received from " << auth << endl);
×
4208
      skipvec[counter] = true;
×
4209
      ++skipCount;
×
4210
      continue;
×
4211
    }
×
4212

4213
    // Disallow QType ANY in responses
4214
    if (rec->d_type == QType::ANY) {
61,583✔
4215
      LOG(prefix << qname << ": Removing 'ANY'-typed data received from " << auth << endl);
2!
4216
      skipvec[counter] = true;
2✔
4217
      ++skipCount;
2✔
4218
      continue;
2✔
4219
    }
2✔
4220

4221
    // Disallow any name not part of auth requested (i.e. disallow x.y.z if asking a NS authoritative for x.w.z)
4222
    if (!rec->d_name.isPartOf(auth)) {
61,581✔
4223
      LOG(prefix << qname << ": Removing record '" << rec->toString() << "' in the " << DNSResourceRecord::placeString(rec->d_place) << " section received from " << auth << endl);
522!
4224
      skipvec[counter] = true;
522✔
4225
      ++skipCount;
522✔
4226
      continue;
522✔
4227
    }
522✔
4228

4229
    // Disallow QType DNAME in non-answer section or containing an answer that is not a parent of or equal to the question name
4230
    // i.e. disallowed bar.example.com. DNAME bar.example.net. when asking foo.example.com
4231
    // But allow it when asking for foo.bar.example.com.
4232
    if (rec->d_type == QType::DNAME && (rec->d_place != DNSResourceRecord::ANSWER || !qname.isPartOf(rec->d_name))) {
61,059!
4233
      LOG(prefix << qname << ": Removing invalid DNAME record '" << rec->toString() << "' in the " << DNSResourceRecord::placeString(rec->d_place) << " section received from " << auth << endl);
2!
4234
      skipvec[counter] = true;
2✔
4235
      ++skipCount;
2✔
4236
      continue;
2✔
4237
    }
2✔
4238

4239
    /* dealing with the records in answer */
4240
    if (rec->d_place == DNSResourceRecord::ANSWER) {
61,057✔
4241
      // Special case for Amazon CNAME records
4242
      if (!(lwr.d_aabit || wasForwardRecurse)) {
14,730!
4243
        /* for now we allow a CNAME for the exact qname in ANSWER with AA=0, because Amazon DNS servers
4244
           are sending such responses */
4245
        if (rec->d_type != QType::CNAME || qname != rec->d_name) {
×
4246
          LOG(prefix << qname << ": Removing record '" << rec->toString() << "' in the ANSWER section without the AA bit set received from " << auth << endl);
×
4247
          skipvec[counter] = true;
×
4248
          ++skipCount;
×
4249
          continue;
×
4250
        }
×
4251
      }
×
4252
      // Disallow answer records not answering the QType requested. ANY, CNAME, DNAME, RRSIG complicate matters here
4253
      if (qtype != QType::ANY && rec->d_type != qtype.getCode() && !isRedirection(rec->d_type) && rec->d_type != QType::RRSIG) {
14,730✔
4254
        LOG(prefix << qname << ": Removing irrelevant record '" << rec->toString() << "' in the ANSWER section received from " << auth << endl);
6!
4255
        skipvec[counter] = true;
6✔
4256
        ++skipCount;
6✔
4257
        continue;
6✔
4258
      }
6✔
4259

4260
      haveAnswers = true;
14,724✔
4261
      if (rec->d_type == QType::CNAME) {
14,724✔
4262
        if (auto cnametarget = getRR<CNAMERecordContent>(*rec); cnametarget != nullptr) {
1,534!
4263
          allowedAnswerNames.insert(cnametarget->getTarget());
1,534✔
4264
        }
1,534✔
4265
        cnameSeen = cnameSeen || qname == rec->d_name;
1,534✔
4266
      }
1,534✔
4267
      else if (rec->d_type == QType::DNAME) {
13,190✔
4268
        // We have checked the DNAME rec->d_name above, the actual answer will be synthesized in a later step
4269
        allowedAnswerNames.insert(rec->d_name);
32✔
4270
      }
32✔
4271
      allowAdditionalEntry(allowedAdditionals, *rec);
14,724✔
4272
    }
14,724✔
4273

4274
    /* dealing with the records in authority */
4275
    // Only allow NS, DS, SOA, RRSIG, NSEC, NSEC3 in AUTHORITY section
4276
    else if (rec->d_place == DNSResourceRecord::AUTHORITY) {
46,327✔
4277
      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) {
27,789✔
4278
        LOG(prefix << qname << ": Removing irrelevant record '" << rec->toString() << "' in the AUTHORITY section received from " << auth << endl);
2!
4279
        skipvec[counter] = true;
2✔
4280
        ++skipCount;
2✔
4281
        continue;
2✔
4282
      }
2✔
4283
      if (rec->d_type == QType::NS && !d_updatingRootNS && rec->d_name == g_rootdnsname) {
27,787!
4284
        /*
4285
         * We don't want to pick up root NS records in AUTHORITY and their associated ADDITIONAL sections of random queries.
4286
         * So remove them and don't add them to allowedAdditionals.
4287
         */
4288
        LOG(prefix << qname << ": Removing NS record '" << rec->toString() << "' in the AUTHORITY section of a response received from " << auth << endl);
2!
4289
        skipvec[counter] = true;
2✔
4290
        ++skipCount;
2✔
4291
        continue;
2✔
4292
      }
2✔
4293

4294
      if (rec->d_type == QType::SOA) {
27,785✔
4295
        // Disallow a SOA record with a name that is not a parent of or equal to the name we asked
4296
        if (!qname.isPartOf(rec->d_name)) {
1,468✔
4297
          LOG(prefix << qname << ": Removing irrelevant SOA record '" << rec->toString() << "' in the AUTHORITY section received from " << auth << endl);
2!
4298
          skipvec[counter] = true;
2✔
4299
          ++skipCount;
2✔
4300
          continue;
2✔
4301
        }
2✔
4302
        // Disallow SOA without AA bit (except for forward with RD=1)
4303
        if (!(lwr.d_aabit || wasForwardRecurse)) {
1,466!
4304
          LOG(prefix << qname << ": Removing irrelevant record (AA not set) '" << rec->toString() << "' in the AUTHORITY section received from " << auth << endl);
×
4305
          skipvec[counter] = true;
×
4306
          ++skipCount;
×
4307
          continue;
×
4308
        }
×
4309

4310
        if (!haveAnswers) {
1,466✔
4311
          switch (lwr.d_rcode) {
1,451!
4312
          case RCode::NXDomain:
119✔
4313
            isNXDomain = true;
119✔
4314
            break;
119✔
4315
          case RCode::NoError:
1,332✔
4316
            isNXQType = true;
1,332✔
4317
            break;
1,332✔
4318
          }
1,451✔
4319
        }
1,451✔
4320
      }
1,466✔
4321
    }
27,785✔
4322
    /* dealing with records in additional */
4323
    else if (rec->d_place == DNSResourceRecord::ADDITIONAL) {
18,540✔
4324
      if (rec->d_type != QType::A && rec->d_type != QType::AAAA && rec->d_type != QType::RRSIG) {
18,540✔
4325
        LOG(prefix << qname << ": Removing irrelevant record '" << rec->toString() << "' in the ADDITIONAL section received from " << auth << endl);
2!
4326
        skipvec[counter] = true;
2✔
4327
        ++skipCount;
2✔
4328
        continue;
2✔
4329
      }
2✔
4330
    }
18,540✔
4331
  } // end of first loop, handled answer and most of authority section
61,057✔
4332

4333
  sanitizeRecordsPass2(prefix, lwr, qname, qtype, auth, allowedAnswerNames, allowedAdditionals, cnameSeen, isNXDomain, isNXQType, skipvec, skipCount);
12,458✔
4334
}
12,458✔
4335

4336
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 isNXDomain, bool isNXQType, std::vector<bool>& skipvec, unsigned int& skipCount)
4337
{
12,458✔
4338
  // Second loop, we know now if the answer was NxDomain or NoData
4339
  unsigned int counter = 0;
12,458✔
4340
  for (auto rec = lwr.d_records.cbegin(); rec != lwr.d_records.cend(); ++rec, ++counter) {
84,160✔
4341

4342
    if (skipvec[counter]) {
71,702✔
4343
      continue;
540✔
4344
    }
540✔
4345
    // Allow OPT record containing EDNS(0) data
4346
    if (rec->d_type == QType::OPT) {
71,162✔
4347
      continue;
10,118✔
4348
    }
10,118✔
4349

4350
    if (rec->d_place == DNSResourceRecord::ANSWER) {
61,044✔
4351
      if (allowedAnswerNames.count(rec->d_name) == 0) {
14,724✔
4352
        LOG(prefix << qname << ": Removing irrelevent record '" << rec->toString() << "' in the ANSWER section received from " << auth << endl);
10!
4353
        skipvec[counter] = true;
10✔
4354
        ++skipCount;
10✔
4355
      }
10✔
4356
      // If we have a CNAME, skip answer records for the requested type
4357
      if (cnameSeen && rec->d_type == qtype && rec->d_name == qname && qtype != QType::CNAME) {
14,724!
4358
        LOG(prefix << qname << ": Removing answer record in presence of CNAME record '" << rec->toString() << "' in the ANSWER section received from " << auth << endl);
6!
4359
        skipvec[counter] = true;
6✔
4360
        ++skipCount;
6✔
4361
        continue;
6✔
4362
      }
6✔
4363
    }
14,724✔
4364
    if (rec->d_place == DNSResourceRecord::AUTHORITY && rec->d_type == QType::NS) {
61,038✔
4365
      if (isNXDomain || isNXQType) {
15,107!
4366
        /*
4367
         * We don't want to pick up NS records in AUTHORITY and their ADDITIONAL sections of NXDomain answers
4368
         * because they are somewhat easy to insert into a large, fragmented UDP response
4369
         * for an off-path attacker by injecting spoofed UDP fragments. So do not add these to allowedAdditionals.
4370
         */
4371
        LOG(prefix << qname << ": Removing NS record '" << rec->toString() << "' in the AUTHORITY section of a " << (isNXDomain ? "NXD" : "NXQTYPE") << " response received from " << auth << endl);
2!
4372
        skipvec[counter] = true;
2✔
4373
        ++skipCount;
2✔
4374
        continue;
2✔
4375
      }
2✔
4376
      allowAdditionalEntry(allowedAdditionals, *rec);
15,105✔
4377
    }
15,105✔
4378
    /* dealing with the records in additional */
4379
    else if (rec->d_place == DNSResourceRecord::ADDITIONAL) {
45,931✔
4380
      if (allowedAdditionals.count(rec->d_name) == 0) {
18,538✔
4381
        LOG(prefix << qname << ": Removing irrelevant record '" << rec->toString() << "' in the ADDITIONAL section received from " << auth << endl);
10!
4382
        skipvec[counter] = true;
10✔
4383
        ++skipCount;
10✔
4384
        continue;
10✔
4385
      }
10✔
4386
    }
18,538✔
4387
  }
61,038✔
4388
  if (skipCount > 0) {
12,458✔
4389
    std::vector<DNSRecord> vec;
113✔
4390
    vec.reserve(lwr.d_records.size() - skipCount);
113✔
4391
    for (counter = 0; counter < lwr.d_records.size(); ++counter) {
1,018✔
4392
      if (!skipvec[counter]) {
905✔
4393
        vec.emplace_back(std::move(lwr.d_records[counter]));
337✔
4394
      }
337✔
4395
    }
905✔
4396
    lwr.d_records = std::move(vec);
113✔
4397
  }
113✔
4398
#ifdef notyet
4399
  // As dedupping is relatively expensive and having dup records not really hurts as far as we have seen, do not dedup.
4400
  if (auto count = pdns::dedupRecords(lwr.d_records); count > 0) {
4401
    LOG(prefix << qname << ": Removed " << count << " duplicate records from response received from " << auth << endl);
4402
  }
4403
#endif
4404
}
12,458✔
4405

4406
void SyncRes::rememberParentSetIfNeeded(const DNSName& domain, const vector<DNSRecord>& newRecords, unsigned int depth, const string& prefix)
4407
{
524✔
4408
  vector<DNSRecord> existing;
524✔
4409
  bool wasAuth = false;
524✔
4410
  auto ttl = g_recCache->get(d_now.tv_sec, domain, QType::NS, MemRecursorCache::None, &existing, d_cacheRemote, d_routingTag, nullptr, nullptr, nullptr, nullptr, &wasAuth);
524✔
4411

4412
  if (ttl <= 0 || wasAuth) {
524✔
4413
    return;
54✔
4414
  }
54✔
4415
  {
470✔
4416
    auto lock = s_savedParentNSSet.lock();
470✔
4417
    if (lock->find(domain) != lock->end()) {
470!
4418
      // no relevant data, or we already stored the parent data
4419
      return;
×
4420
    }
×
4421
  }
470✔
4422

4423
  set<DNSName> authSet;
470✔
4424
  for (const auto& dnsRecord : newRecords) {
2,114✔
4425
    auto content = getRR<NSRecordContent>(dnsRecord);
2,114✔
4426
    authSet.insert(content->getNS());
2,114✔
4427
  }
2,114✔
4428
  // The glue IPs could also differ, but we're not checking that yet, we're only looking for parent NS records not
4429
  // in the child set
4430
  bool shouldSave = false;
470✔
4431
  for (const auto& dnsRecord : existing) {
2,108✔
4432
    auto content = getRR<NSRecordContent>(dnsRecord);
2,108✔
4433
    if (authSet.count(content->getNS()) == 0) {
2,108✔
4434
      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!
4435
      shouldSave = true;
4✔
4436
      break;
4✔
4437
    }
4✔
4438
  }
2,108✔
4439

4440
  if (shouldSave) {
470✔
4441
    map<DNSName, vector<ComboAddress>> entries;
4✔
4442
    for (const auto& dnsRecord : existing) {
30✔
4443
      auto content = getRR<NSRecordContent>(dnsRecord);
30✔
4444
      const DNSName& name = content->getNS();
30✔
4445
      set<GetBestNSAnswer> beenthereIgnored;
30✔
4446
      unsigned int nretrieveAddressesForNSIgnored{};
30✔
4447
      auto addresses = getAddrs(name, depth, prefix, beenthereIgnored, true, nretrieveAddressesForNSIgnored);
30✔
4448
      entries.emplace(name, addresses);
30✔
4449
    }
30✔
4450
    s_savedParentNSSet.lock()->emplace(domain, std::move(entries), d_now.tv_sec + ttl);
4✔
4451
  }
4✔
4452
}
470✔
4453

4454
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)
4455
{
12,458✔
4456
  bool wasForwardRecurse = wasForwarded && rdQuery;
12,458✔
4457
  tcache_t tcache;
12,458✔
4458

4459
  fixupAnswer(prefix, lwr, qname, qtype, auth, wasForwarded, rdQuery);
12,458✔
4460
  sanitizeRecords(prefix, lwr, qname, qtype, auth, wasForwarded, rdQuery);
12,458✔
4461

4462
  MemRecursorCache::AuthRecsVec authorityRecs;
12,458✔
4463
  bool isCNAMEAnswer = false;
12,458✔
4464
  bool isDNAMEAnswer = false;
12,458✔
4465
  DNSName seenAuth;
12,458✔
4466

4467
  // names that might be expanded from a wildcard, and thus require denial of existence proof
4468
  // this is the queried name and any part of the CNAME chain from the queried name
4469
  // the key is the name itself, the value is initially false and is set to true once we have
4470
  // confirmed it was actually expanded from a wildcard
4471
  std::map<DNSName, bool> wildcardCandidates{{qname, false}};
12,458✔
4472

4473
  if (rdQuery) {
12,458✔
4474
    std::unordered_map<DNSName, DNSName> cnames;
58✔
4475
    for (const auto& rec : lwr.d_records) {
148✔
4476
      if (rec.d_type != QType::CNAME || rec.d_class != QClass::IN) {
148!
4477
        continue;
136✔
4478
      }
136✔
4479
      if (auto content = getRR<CNAMERecordContent>(rec)) {
12!
4480
        cnames[rec.d_name] = DNSName(content->getTarget());
12✔
4481
      }
12✔
4482
    }
12✔
4483
    auto initial = qname;
58✔
4484
    while (true) {
68✔
4485
      auto cnameIt = cnames.find(initial);
68✔
4486
      if (cnameIt == cnames.end()) {
68✔
4487
        break;
56✔
4488
      }
56✔
4489
      initial = cnameIt->second;
12✔
4490
      if (!wildcardCandidates.emplace(initial, false).second) {
12✔
4491
        // CNAME Loop
4492
        break;
2✔
4493
      }
2✔
4494
    }
12✔
4495
  }
58✔
4496

4497
  for (auto& rec : lwr.d_records) {
71,129✔
4498
    if (rec.d_type == QType::OPT || rec.d_class != QClass::IN) {
71,129✔
4499
      continue;
10,116✔
4500
    }
10,116✔
4501

4502
    rec.d_ttl = min(s_maxcachettl, rec.d_ttl);
61,013✔
4503

4504
    if (!isCNAMEAnswer && rec.d_place == DNSResourceRecord::ANSWER && rec.d_type == QType::CNAME && (!(qtype == QType::CNAME)) && rec.d_name == qname && !isDNAMEAnswer) {
61,013!
4505
      isCNAMEAnswer = true;
1,515✔
4506
    }
1,515✔
4507
    if (!isDNAMEAnswer && rec.d_place == DNSResourceRecord::ANSWER && rec.d_type == QType::DNAME && qtype != QType::DNAME && qname.isPartOf(rec.d_name)) {
61,015!
4508
      isDNAMEAnswer = true;
32✔
4509
      isCNAMEAnswer = false;
32✔
4510
    }
32✔
4511

4512
    if (rec.d_type == QType::SOA && rec.d_place == DNSResourceRecord::AUTHORITY && qname.isPartOf(rec.d_name)) {
61,013!
4513
      seenAuth = rec.d_name;
1,466✔
4514
    }
1,466✔
4515

4516
    const auto labelCount = rec.d_name.countLabels();
61,013✔
4517
    if (rec.d_type == QType::RRSIG) {
61,013✔
4518
      auto rrsig = getRR<RRSIGRecordContent>(rec);
8,906✔
4519
      if (rrsig) {
8,907✔
4520
        /* As illustrated in rfc4035's Appendix B.6, the RRSIG label
4521
           count can be lower than the name's label count if it was
4522
           synthesized from the wildcard. Note that the difference might
4523
           be > 1. */
4524
        if (auto wcIt = wildcardCandidates.find(rec.d_name); wcIt != wildcardCandidates.end() && isWildcardExpanded(labelCount, *rrsig)) {
8,907✔
4525
          wcIt->second = true;
617✔
4526
          gatherWildcardProof = true;
617✔
4527
          if (!isWildcardExpandedOntoItself(rec.d_name, labelCount, *rrsig)) {
617✔
4528
            /* if we have a wildcard expanded onto itself, we don't need to prove
4529
               that the exact name doesn't exist because it actually does.
4530
               We still want to gather the corresponding NSEC/NSEC3 records
4531
               to pass them to our client in case it wants to validate by itself.
4532
            */
4533
            LOG(prefix << qname << ": RRSIG indicates the name was synthesized from a wildcard, we need a wildcard proof" << endl);
609!
4534
            needWildcardProof = true;
609✔
4535
          }
609✔
4536
          else {
8✔
4537
            LOG(prefix << qname << ": RRSIG indicates the name was synthesized from a wildcard expanded onto itself, we need to gather wildcard proof" << endl);
8!
4538
          }
8✔
4539
          wildcardLabelsCount = rrsig->d_labels;
617✔
4540
        }
617✔
4541

4542
        // cerr<<"Got an RRSIG for "<<DNSRecordContent::NumberToType(rrsig->d_type)<<" with name '"<<rec.d_name<<"' and place "<<rec.d_place<<endl;
4543
        tcache[{rec.d_name, rrsig->d_type, rec.d_place}].signatures.push_back(rrsig);
8,907✔
4544
        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);
8,907✔
4545
      }
8,907✔
4546
    }
8,906✔
4547
  }
61,013✔
4548

4549
  /* if we have a positive answer synthesized from a wildcard,
4550
     we need to store the corresponding NSEC/NSEC3 records proving
4551
     that the exact name did not exist in the negative cache */
4552
  if (gatherWildcardProof) {
12,458✔
4553
    for (const auto& rec : lwr.d_records) {
3,055✔
4554
      if (rec.d_type == QType::OPT || rec.d_class != QClass::IN) {
3,055!
4555
        continue;
585✔
4556
      }
585✔
4557

4558
      if (nsecTypes.count(rec.d_type) != 0) {
2,470✔
4559
        authorityRecs.emplace_back(rec);
615✔
4560
      }
615✔
4561
      else if (rec.d_type == QType::RRSIG) {
1,855✔
4562
        auto rrsig = getRR<RRSIGRecordContent>(rec);
1,234✔
4563
        if (rrsig && nsecTypes.count(rrsig->d_type) != 0) {
1,234!
4564
          authorityRecs.emplace_back(rec);
615✔
4565
        }
615✔
4566
      }
1,234✔
4567
    }
2,470✔
4568
  }
613✔
4569

4570
  // reap all answers from this packet that are acceptable
4571
  for (auto& rec : lwr.d_records) {
71,133✔
4572
    if (rec.d_type == QType::OPT) {
71,133✔
4573
      LOG(prefix << qname << ": OPT answer '" << rec.d_name << "' from '" << auth << "' nameservers" << endl);
10,118!
4574
      continue;
10,118✔
4575
    }
10,118✔
4576

4577
    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 << " ");
61,015✔
4578

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

4581
    if (rec.d_name.isPartOf(auth)) {
61,017✔
4582
      if (rec.d_type == QType::RRSIG) {
61,014✔
4583
        LOG("RRSIG - separate" << endl);
8,907!
4584
      }
8,907✔
4585
      else if (rec.d_type == QType::DS && rec.d_name == auth) {
52,107✔
4586
        LOG("NO - DS provided by child zone" << endl);
2!
4587
      }
2✔
4588
      else {
52,105✔
4589
        bool haveLogged = false;
52,105✔
4590
        if (isDNAMEAnswer && rec.d_type == QType::CNAME) {
52,105✔
4591
          LOG("NO - we already have a DNAME answer for this domain" << endl);
30!
4592
          continue;
30✔
4593
        }
30✔
4594
        if (!t_sstorage.domainmap->empty()) {
52,076✔
4595
          // Check if we are authoritative for a zone in this answer
4596
          DNSName tmp_qname(rec.d_name);
47,715✔
4597
          // We may be auth for domain example.com, but the DS record needs to come from the parent (.com) nameserver
4598
          if (rec.d_type == QType::DS) {
47,715✔
4599
            tmp_qname.chopOff();
864✔
4600
          }
864✔
4601
          auto auth_domain_iter = getBestAuthZone(&tmp_qname);
47,715✔
4602
          if (auth_domain_iter != t_sstorage.domainmap->end() && auth.countLabels() <= auth_domain_iter->first.countLabels()) {
47,715✔
4603
            if (auth_domain_iter->first != auth) {
355✔
4604
              LOG("NO! - we are authoritative for the zone " << auth_domain_iter->first << endl);
2!
4605
              continue;
2✔
4606
            }
2✔
4607
            LOG("YES! - This answer was ");
353!
4608
            if (!wasForwarded) {
353✔
4609
              LOG("retrieved from the local auth store.");
138!
4610
            }
138✔
4611
            else {
215✔
4612
              LOG("received from a server we forward to.");
215!
4613
            }
215✔
4614
            haveLogged = true;
353✔
4615
            LOG(endl);
353!
4616
          }
353✔
4617
        }
47,715✔
4618
        if (!haveLogged) {
52,076✔
4619
          LOG("YES!" << endl);
51,722✔
4620
        }
51,722✔
4621

4622
        rec.d_ttl = min(s_maxcachettl, rec.d_ttl);
52,073✔
4623

4624
        DNSRecord dnsRecord(rec);
52,073✔
4625
        tcache[{rec.d_name, rec.d_type, rec.d_place}].d_ttl_time = d_now.tv_sec;
52,073✔
4626
        dnsRecord.d_ttl += d_now.tv_sec;
52,073✔
4627
        dnsRecord.d_place = DNSResourceRecord::ANSWER;
52,073✔
4628
        tcache[{rec.d_name, rec.d_type, rec.d_place}].records.push_back(std::move(dnsRecord));
52,073✔
4629
      }
52,073✔
4630
    }
61,014✔
4631
    else
2,147,483,647✔
4632
      LOG("NO!" << endl);
2,147,498,552!
4633
  }
60,983✔
4634

4635
  // supplant
4636
  for (auto& entry : tcache) {
36,495✔
4637
    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,495✔
4638
      uint32_t lowestTTD = computeLowestTTD(entry.second.records, entry.second.signatures, entry.second.signaturesTTL, authorityRecs);
12,767✔
4639

4640
      for (auto& record : entry.second.records) {
28,347✔
4641
        record.d_ttl = lowestTTD; // boom
28,347✔
4642
      }
28,347✔
4643
    }
12,767✔
4644
  }
36,495✔
4645

4646
  for (auto tCacheEntry = tcache.begin(); tCacheEntry != tcache.end(); ++tCacheEntry) {
48,952✔
4647

4648
    if (tCacheEntry->second.records.empty()) { // this happens when we did store signatures, but passed on the records themselves
36,494!
4649
      continue;
×
4650
    }
×
4651

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

4674
    if (isCNAMEAnswer && (tCacheEntry->first.place != DNSResourceRecord::ANSWER || tCacheEntry->first.type != QType::CNAME || tCacheEntry->first.name != qname)) {
36,494✔
4675
      /*
4676
        rfc2181 states:
4677
        Note that the answer section of an authoritative answer normally
4678
        contains only authoritative data.  However when the name sought is an
4679
        alias (see section 10.1.1) only the record describing that alias is
4680
        necessarily authoritative.  Clients should assume that other records
4681
        may have come from the server's cache.  Where authoritative answers
4682
        are required, the client should query again, using the canonical name
4683
        associated with the alias.
4684
      */
4685
      isAA = false;
285✔
4686
      expectSignature = false;
285✔
4687
    }
285✔
4688
    if (isDNAMEAnswer && (tCacheEntry->first.place != DNSResourceRecord::ANSWER || tCacheEntry->first.type != QType::DNAME || !qname.isPartOf(tCacheEntry->first.name))) {
36,494!
4689
      /* see above */
4690
      isAA = false;
6✔
4691
      expectSignature = false;
6✔
4692
    }
6✔
4693

4694
    if ((isCNAMEAnswer || isDNAMEAnswer) && tCacheEntry->first.place == DNSResourceRecord::AUTHORITY && tCacheEntry->first.type == QType::NS && auth == tCacheEntry->first.name) {
36,494✔
4695
      /* These NS can't be authoritative since we have a CNAME/DNAME answer for which (see above) only the
4696
         record describing that alias is necessarily authoritative.
4697
         But if we allow the current auth, which might be serving the child zone, to raise the TTL
4698
         of non-authoritative NS in the cache, they might be able to keep a "ghost" zone alive forever,
4699
         even after the delegation is gone from the parent.
4700
         So let's just do nothing with them, we can fetch them directly if we need them.
4701
      */
4702
      LOG(prefix << qname << ": Skipping authority NS from '" << auth << "' nameservers in CNAME/DNAME answer " << tCacheEntry->first.name << "|" << DNSRecordContent::NumberToType(tCacheEntry->first.type) << endl);
2!
4703
      continue;
2✔
4704
    }
2✔
4705

4706
    /*
4707
     * RFC 6672 section 5.3.1
4708
     *  In any response, a signed DNAME RR indicates a non-terminal
4709
     *  redirection of the query.  There might or might not be a server-
4710
     *  synthesized CNAME in the answer section; if there is, the CNAME will
4711
     *  never be signed.  For a DNSSEC validator, verification of the DNAME
4712
     *  RR and then that the CNAME was properly synthesized is sufficient
4713
     *  proof.
4714
     *
4715
     * We do the synthesis check in processRecords, here we make sure we
4716
     * don't validate the CNAME.
4717
     */
4718
    if (isDNAMEAnswer && tCacheEntry->first.type == QType::CNAME) {
36,492!
4719
      expectSignature = false;
×
4720
    }
×
4721

4722
    vState recordState = vState::Indeterminate;
36,492✔
4723

4724
    if (expectSignature && shouldValidate()) {
36,492✔
4725
      vState initialState = getValidationStatus(tCacheEntry->first.name, !tCacheEntry->second.signatures.empty(), tCacheEntry->first.type == QType::DS, depth, prefix);
10,766✔
4726
      LOG(prefix << qname << ": Got initial zone status " << initialState << " for record " << tCacheEntry->first.name << "|" << DNSRecordContent::NumberToType(tCacheEntry->first.type) << endl);
10,766!
4727

4728
      if (initialState == vState::Secure) {
10,766✔
4729
        if (tCacheEntry->first.type == QType::DNSKEY && tCacheEntry->first.place == DNSResourceRecord::ANSWER && tCacheEntry->first.name == getSigner(tCacheEntry->second.signatures)) {
6,923!
4730
          LOG(prefix << qname << ": Validating DNSKEY for " << tCacheEntry->first.name << endl);
1,537!
4731
          recordState = validateDNSKeys(tCacheEntry->first.name, tCacheEntry->second.records, tCacheEntry->second.signatures, depth, prefix);
1,537✔
4732
        }
1,537✔
4733
        else {
5,386✔
4734
          LOG(prefix << qname << ": Validating non-additional " << QType(tCacheEntry->first.type).toString() << " record for " << tCacheEntry->first.name << endl);
5,386!
4735
          recordState = validateRecordsWithSigs(depth, prefix, qname, qtype, tCacheEntry->first.name, QType(tCacheEntry->first.type), tCacheEntry->second.records, tCacheEntry->second.signatures);
5,386✔
4736
        }
5,386✔
4737
      }
6,923✔
4738
      else {
3,843✔
4739
        recordState = initialState;
3,843✔
4740
        LOG(prefix << qname << ": Skipping validation because the current state is " << recordState << endl);
3,843!
4741
      }
3,843✔
4742

4743
      LOG(prefix << qname << ": Validation result is " << recordState << ", current state is " << state << endl);
10,766!
4744
      if (state != recordState) {
10,766✔
4745
        updateValidationState(qname, state, recordState, prefix);
6,720✔
4746
      }
6,720✔
4747
    }
10,766✔
4748

4749
    if (vStateIsBogus(recordState)) {
36,492✔
4750
      /* this is a TTD by now, be careful */
4751
      for (auto& record : tCacheEntry->second.records) {
503✔
4752
        auto newval = std::min(record.d_ttl, static_cast<uint32_t>(s_maxbogusttl + d_now.tv_sec));
503✔
4753
        record.d_ttl = newval;
503✔
4754
      }
503✔
4755
      tCacheEntry->second.d_ttl_time = d_now.tv_sec;
182✔
4756
    }
182✔
4757

4758
    /* We don't need to store NSEC3 records in the positive cache because:
4759
       - we don't allow direct NSEC3 queries
4760
       - denial of existence proofs in wildcard expanded positive responses are stored in authorityRecs
4761
       - denial of existence proofs for negative responses are stored in the negative cache
4762
       We also don't want to cache non-authoritative data except for:
4763
       - records coming from non forward-recurse servers (those will never be AA)
4764
       - DS (special case)
4765
       - NS, A and AAAA (used for infra queries)
4766
    */
4767
    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,492✔
4768

4769
      bool doCache = true;
32,860✔
4770
      if (tCacheEntry->first.place == DNSResourceRecord::ANSWER && ednsmask) {
32,860✔
4771
        const bool isv4 = ednsmask->isIPv4();
77✔
4772
        if ((isv4 && s_ecsipv4nevercache) || (!isv4 && s_ecsipv6nevercache)) {
77!
4773
          doCache = false;
×
4774
        }
×
4775
        // If ednsmask is relevant, we do not want to cache if the scope prefix length is large and TTL is small
4776
        if (doCache && s_ecscachelimitttl > 0) {
77!
4777
          bool manyMaskBits = (isv4 && ednsmask->getBits() > s_ecsipv4cachelimit) || (!isv4 && ednsmask->getBits() > s_ecsipv6cachelimit);
6!
4778

4779
          if (manyMaskBits) {
6✔
4780
            uint32_t minttl = UINT32_MAX;
2✔
4781
            for (const auto& iter : tCacheEntry->second.records) {
2✔
4782
              if (iter.d_ttl < minttl) {
2!
4783
                minttl = iter.d_ttl;
2✔
4784
              }
2✔
4785
            }
2✔
4786
            bool ttlIsSmall = minttl < s_ecscachelimitttl + d_now.tv_sec;
2✔
4787
            if (ttlIsSmall) {
2!
4788
              // Case: many bits and ttlIsSmall
4789
              doCache = false;
2✔
4790
            }
2✔
4791
          }
2✔
4792
        }
6✔
4793
      }
77✔
4794

4795
      d_fromAuthIP = remoteIP;
32,860✔
4796

4797
      if (doCache) {
32,860✔
4798
        // Check if we are going to replace a non-auth (parent) NS recordset
4799
        if (isAA && tCacheEntry->first.type == QType::NS && s_save_parent_ns_set) {
32,858!
4800
          rememberParentSetIfNeeded(tCacheEntry->first.name, tCacheEntry->second.records, depth, prefix);
524✔
4801
        }
524✔
4802
        bool thisRRNeedsWildcardProof = false;
32,858✔
4803
        if (gatherWildcardProof) {
32,858✔
4804
          if (auto wcIt = wildcardCandidates.find(tCacheEntry->first.name); wcIt != wildcardCandidates.end() && wcIt->second) {
1,208✔
4805
            thisRRNeedsWildcardProof = true;
617✔
4806
          }
617✔
4807
        }
1,208✔
4808
        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,858✔
4809

4810
        // Delete potential negcache entry. When a record recovers with serve-stale the negcache entry can cause the wrong entry to
4811
        // be served, as negcache entries are checked before record cache entries
4812
        if (NegCache::s_maxServedStaleExtensions > 0) {
32,858✔
4813
          g_negCache->wipeTyped(tCacheEntry->first.name, tCacheEntry->first.type);
192✔
4814
        }
192✔
4815

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

4822
          if (isWildcardExpanded(labelCount, *rrsig) && !isWildcardExpandedOntoItself(tCacheEntry->first.name, labelCount, *rrsig)) {
589!
4823
            DNSName realOwner = getNSECOwnerName(tCacheEntry->first.name, tCacheEntry->second.signatures);
589✔
4824

4825
            std::vector<DNSRecord> content;
589✔
4826
            content.reserve(tCacheEntry->second.records.size());
589✔
4827
            for (const auto& record : tCacheEntry->second.records) {
589✔
4828
              DNSRecord nonExpandedRecord(record);
589✔
4829
              nonExpandedRecord.d_name = realOwner;
589✔
4830
              content.push_back(std::move(nonExpandedRecord));
589✔
4831
            }
589✔
4832

4833
            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);
589!
4834
          }
589✔
4835
        }
589✔
4836
      }
32,858✔
4837
    }
32,860✔
4838

4839
    if (seenAuth.empty() && !tCacheEntry->second.signatures.empty()) {
36,492✔
4840
      seenAuth = getSigner(tCacheEntry->second.signatures);
5,686✔
4841
    }
5,686✔
4842

4843
    if (g_aggressiveNSECCache && (tCacheEntry->first.type == QType::NSEC || tCacheEntry->first.type == QType::NSEC3) && recordState == vState::Secure && !seenAuth.empty()) {
36,492!
4844
      // Good candidate for NSEC{,3} caching
4845
      g_aggressiveNSECCache->insertNSEC(seenAuth, tCacheEntry->first.name, tCacheEntry->second.records.at(0), tCacheEntry->second.signatures, tCacheEntry->first.type == QType::NSEC3, qname, qtype);
2,926✔
4846
    }
2,926✔
4847

4848
    if (tCacheEntry->first.place == DNSResourceRecord::ANSWER && ednsmask) {
36,492✔
4849
      d_wasVariable = true;
77✔
4850
    }
77✔
4851
  }
36,492✔
4852

4853
  if (gatherWildcardProof) {
12,458✔
4854
    if (auto wcIt = wildcardCandidates.find(qname); wcIt != wildcardCandidates.end() && !wcIt->second) {
613!
4855
      // 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
4856
      gatherWildcardProof = false;
2✔
4857
    }
2✔
4858
  }
613✔
4859

4860
  return RCode::NoError;
12,458✔
4861
}
12,458✔
4862

4863
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)
4864
{
242✔
4865
  if (denialState == expectedState) {
242✔
4866
    neValidationState = vState::Secure;
213✔
4867
  }
213✔
4868
  else {
29✔
4869
    if (denialState == dState::OPTOUT) {
29✔
4870
      LOG(prefix << qname << ": OPT-out denial found for " << neName << endl);
15!
4871
      /* rfc5155 states:
4872
         "The AD bit, as defined by [RFC4035], MUST NOT be set when returning a
4873
         response containing a closest (provable) encloser proof in which the
4874
         NSEC3 RR that covers the "next closer" name has the Opt-Out bit set.
4875

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

4882
         At best the Opt-Out NSEC3 RR proves that there is no signed DS (so no
4883
         secure delegation).
4884
      */
4885
      neValidationState = vState::Insecure;
15✔
4886
    }
15✔
4887
    else if (denialState == dState::INSECURE) {
14✔
4888
      LOG(prefix << qname << ": Insecure denial found for " << neName << ", returning Insecure" << endl);
2!
4889
      neValidationState = vState::Insecure;
2✔
4890
    }
2✔
4891
    else {
12✔
4892
      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!
4893
      /* try again to get the missed cuts, harder this time */
4894
      auto zState = getValidationStatus(neName, false, isDS, depth, prefix);
12✔
4895
      if (zState != vState::Secure) {
12✔
4896
        neValidationState = zState;
4✔
4897
      }
4✔
4898
      else {
8✔
4899
        LOG(prefix << qname << ": Still in a secure zone with an invalid denial for " << neName << ", returning " << vStateToString(vState::BogusInvalidDenial) << endl);
8!
4900
        neValidationState = vState::BogusInvalidDenial;
8✔
4901
      }
8✔
4902
    }
12✔
4903
  }
29✔
4904
  updateValidationState(qname, state, neValidationState, prefix);
242✔
4905
}
242✔
4906

4907
dState SyncRes::getDenialValidationState(const NegCache::NegCacheEntry& negEntry, const dState expectedState, bool referralToUnsigned, const string& prefix)
4908
{
2,420✔
4909
  cspmap_t csp = harvestCSPFromNE(negEntry);
2,420✔
4910
  return getDenial(csp, negEntry.d_name, negEntry.d_qtype.getCode(), referralToUnsigned, expectedState == dState::NXQTYPE, d_validationContext, LogObject(prefix));
2,420✔
4911
}
2,420✔
4912

4913
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)
4914
{
609✔
4915
  /* positive answer synthesized from a wildcard */
4916
  NegCache::NegCacheEntry negEntry;
609✔
4917
  negEntry.d_name = qname;
609✔
4918
  negEntry.d_qtype = QType::ENT; // this encodes 'whole record'
609✔
4919
  uint32_t lowestTTL = rec.d_ttl;
609✔
4920
  harvestNXRecords(lwr.d_records, negEntry, d_now.tv_sec, &lowestTTL);
609✔
4921

4922
  if (vStateIsBogus(state)) {
609!
4923
    negEntry.d_validationState = state;
×
4924
  }
×
4925
  else {
609✔
4926
    auto recordState = getValidationStatus(qname, !negEntry.authoritySOA.signatures.empty() || !negEntry.DNSSECRecords.signatures.empty(), false, depth, prefix);
609✔
4927

4928
    if (recordState == vState::Secure) {
609✔
4929
      /* We have a positive answer synthesized from a wildcard, we need to check that we have
4930
         proof that the exact name doesn't exist so the wildcard can be used,
4931
         as described in section 5.3.4 of RFC 4035 and 5.3 of RFC 7129.
4932
      */
4933
      cspmap_t csp = harvestCSPFromNE(negEntry);
605✔
4934
      dState res = getDenial(csp, qname, negEntry.d_qtype.getCode(), false, false, d_validationContext, LogObject(prefix), false, wildcardLabelsCount);
605✔
4935
      if (res != dState::NXDOMAIN) {
605✔
4936
        vState tmpState = vState::BogusInvalidDenial;
8✔
4937
        if (res == dState::INSECURE || res == dState::OPTOUT) {
8!
4938
          /* Some part could not be validated, for example a NSEC3 record with a too large number of iterations,
4939
             this is not enough to warrant a Bogus, but go Insecure. */
4940
          tmpState = vState::Insecure;
2✔
4941
          LOG(prefix << qname << ": Unable to validate denial in wildcard expanded positive response found for " << qname << ", returning Insecure, res=" << res << endl);
2!
4942
        }
2✔
4943
        else {
6✔
4944
          LOG(prefix << qname << ": Invalid denial in wildcard expanded positive response found for " << qname << ", returning Bogus, res=" << res << endl);
6!
4945
          rec.d_ttl = std::min(rec.d_ttl, s_maxbogusttl);
6✔
4946
        }
6✔
4947

4948
        updateValidationState(qname, state, tmpState, prefix);
8✔
4949
        /* we already stored the record with a different validation status, let's fix it */
4950
        updateValidationStatusInCache(qname, qtype, lwr.d_aabit, tmpState);
8✔
4951
      }
8✔
4952
    }
605✔
4953
  }
609✔
4954
}
609✔
4955

4956
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)
4957
{
12,440✔
4958
  bool done = false;
12,440✔
4959
  DNSName dnameTarget;
12,440✔
4960
  DNSName dnameOwner;
12,440✔
4961
  uint32_t dnameTTL = 0;
12,440✔
4962
  bool referralOnDS = false;
12,440✔
4963

4964
  for (auto& rec : lwr.d_records) {
71,051✔
4965
    if (rec.d_type == QType::OPT || rec.d_class != QClass::IN) {
71,051✔
4966
      continue;
10,118✔
4967
    }
10,118✔
4968

4969
    if (rec.d_place == DNSResourceRecord::ANSWER && !(lwr.d_aabit || sendRDQuery)) {
60,933!
4970
      /* for now we allow a CNAME for the exact qname in ANSWER with AA=0, because Amazon DNS servers
4971
         are sending such responses */
4972
      if (rec.d_type != QType::CNAME || rec.d_name != qname) {
×
4973
        continue;
×
4974
      }
×
4975
    }
×
4976
    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,933!
4977

4978
    bool putInNegCache = true;
60,933✔
4979
    if (negCacheIndication && qtype == QType::DS && isForwardOrAuth(qname)) {
60,933✔
4980
      // #10189, a NXDOMAIN to a DS query for a forwarded or auth domain should not NXDOMAIN the whole domain
4981
      putInNegCache = false;
7✔
4982
    }
7✔
4983

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

4987
      rec.d_ttl = min(rec.d_ttl, s_maxnegttl);
125✔
4988
      // only add a SOA if we're not going anywhere after this
4989
      if (newtarget.empty()) {
125✔
4990
        ret.push_back(rec);
119✔
4991
      }
119✔
4992

4993
      NegCache::NegCacheEntry negEntry;
125✔
4994

4995
      uint32_t lowestTTL = rec.d_ttl;
125✔
4996
      /* if we get an NXDomain answer with a CNAME, the name
4997
         does exist but the target does not */
4998
      negEntry.d_name = newtarget.empty() ? qname : newtarget;
125✔
4999
      negEntry.d_qtype = QType::ENT; // this encodes 'whole record'
125✔
5000
      negEntry.d_auth = rec.d_name;
125✔
5001
      harvestNXRecords(lwr.d_records, negEntry, d_now.tv_sec, &lowestTTL);
125✔
5002

5003
      if (vStateIsBogus(state)) {
125✔
5004
        negEntry.d_validationState = state;
7✔
5005
      }
7✔
5006
      else {
118✔
5007
        /* here we need to get the validation status of the zone telling us that the domain does not
5008
           exist, ie the owner of the SOA */
5009
        auto recordState = getValidationStatus(rec.d_name, !negEntry.authoritySOA.signatures.empty() || !negEntry.DNSSECRecords.signatures.empty(), false, depth, prefix);
118!
5010
        if (recordState == vState::Secure) {
118✔
5011
          dState denialState = getDenialValidationState(negEntry, dState::NXDOMAIN, false, prefix);
58✔
5012
          updateDenialValidationState(qname, negEntry.d_validationState, negEntry.d_name, state, denialState, dState::NXDOMAIN, false, depth, prefix);
58✔
5013
        }
58✔
5014
        else {
60✔
5015
          negEntry.d_validationState = recordState;
60✔
5016
          updateValidationState(qname, state, negEntry.d_validationState, prefix);
60✔
5017
        }
60✔
5018
      }
118✔
5019

5020
      if (vStateIsBogus(negEntry.d_validationState)) {
125✔
5021
        lowestTTL = min(lowestTTL, s_maxbogusttl);
7✔
5022
      }
7✔
5023

5024
      negEntry.d_ttd = d_now.tv_sec + lowestTTL;
125✔
5025
      negEntry.d_orig_ttl = lowestTTL;
125✔
5026
      /* if we get an NXDomain answer with a CNAME, let's not cache the
5027
         target, even the server was authoritative for it,
5028
         and do an additional query for the CNAME target.
5029
         We have a regression test making sure we do exactly that.
5030
      */
5031
      if (newtarget.empty() && putInNegCache) {
125✔
5032
        g_negCache->add(negEntry);
112✔
5033
        // doCNAMECacheCheck() checks record cache and does not look into negcache. That means that an old record might be found if
5034
        // serve-stale is active. Avoid that by explicitly zapping that CNAME record.
5035
        if (qtype == QType::CNAME && MemRecursorCache::s_maxServedStaleExtensions > 0) {
112!
5036
          g_recCache->doWipeCache(qname, false, qtype);
2✔
5037
        }
2✔
5038
        if (s_rootNXTrust && negEntry.d_auth.isRoot() && auth.isRoot() && lwr.d_aabit) {
112!
5039
          negEntry.d_name = negEntry.d_name.getLastLabel();
3✔
5040
          g_negCache->add(negEntry);
3✔
5041
        }
3✔
5042
      }
112✔
5043

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

5101
      done = true;
9,606✔
5102
      rcode = RCode::NoError;
9,606✔
5103

5104
      if (needWildcardProof) {
9,606✔
5105
        checkWildcardProof(qname, qtype, rec, lwr, state, depth, prefix, wildcardLabelsCount);
597✔
5106
      }
597✔
5107

5108
      ret.push_back(rec);
9,606✔
5109
    }
9,606✔
5110
    else if ((rec.d_type == QType::RRSIG || rec.d_type == QType::NSEC || rec.d_type == QType::NSEC3) && rec.d_place == DNSResourceRecord::ANSWER) {
48,428✔
5111
      if (rec.d_type != QType::RRSIG || rec.d_name == qname) {
2,951!
5112
        ret.push_back(rec); // enjoy your DNSSEC
2,918✔
5113
      }
2,918✔
5114
      else if (rec.d_type == QType::RRSIG && qname.isPartOf(rec.d_name)) {
33!
5115
        auto rrsig = getRR<RRSIGRecordContent>(rec);
24✔
5116
        if (rrsig != nullptr && rrsig->d_type == QType::DNAME) {
24!
5117
          ret.push_back(rec);
24✔
5118
        }
24✔
5119
      }
24✔
5120
    }
2,951✔
5121
    else if (rec.d_place == DNSResourceRecord::AUTHORITY && rec.d_type == QType::NS && qname.isPartOf(rec.d_name)) {
45,477✔
5122
      if (moreSpecificThan(rec.d_name, auth)) {
15,013✔
5123
        newauth = rec.d_name;
13,866✔
5124
        LOG(prefix << qname << ": Got NS record '" << rec.d_name << "' -> '" << rec.getContent()->getZoneRepresentation() << "'" << endl);
13,866✔
5125

5126
        /* check if we have a referral from the parent zone to a child zone for a DS query, which is not right */
5127
        if (qtype == QType::DS && (newauth.isPartOf(qname) || qname == newauth)) {
13,866!
5128
          /* just got a referral from the parent zone when asking for a DS, looks like this server did not get the DNSSEC memo.. */
5129
          referralOnDS = true;
4✔
5130
        }
4✔
5131
        else {
13,862✔
5132
          realreferral = true;
13,862✔
5133
          if (auto content = getRR<NSRecordContent>(rec)) {
13,862✔
5134
            nsset.insert(content->getNS());
13,861✔
5135
          }
13,861✔
5136
        }
13,862✔
5137
      }
13,866✔
5138
      else {
1,147✔
5139
        LOG(prefix << qname << ": Got upwards/level NS record '" << rec.d_name << "' -> '" << rec.getContent()->getZoneRepresentation() << "', had '" << auth << "'" << endl);
1,147!
5140
        if (auto content = getRR<NSRecordContent>(rec)) {
1,147!
5141
          nsset.insert(content->getNS());
1,147✔
5142
        }
1,147✔
5143
      }
1,147✔
5144
    }
15,013✔
5145
    else if (rec.d_place == DNSResourceRecord::AUTHORITY && rec.d_type == QType::DS && qname.isPartOf(rec.d_name)) {
30,464✔
5146
      LOG(prefix << qname << ": Got DS record '" << rec.d_name << "' -> '" << rec.getContent()->getZoneRepresentation() << "'" << endl);
1,026!
5147
    }
1,026✔
5148
    else if (realreferral && rec.d_place == DNSResourceRecord::AUTHORITY && (rec.d_type == QType::NSEC || rec.d_type == QType::NSEC3) && newauth.isPartOf(auth)) {
29,438!
5149
      /* we might have received a denial of the DS, let's check */
5150
      NegCache::NegCacheEntry negEntry;
3,431✔
5151
      uint32_t lowestTTL = rec.d_ttl;
3,431✔
5152
      harvestNXRecords(lwr.d_records, negEntry, d_now.tv_sec, &lowestTTL);
3,431✔
5153

5154
      if (!vStateIsBogus(state)) {
3,431!
5155
        auto recordState = getValidationStatus(newauth, !negEntry.authoritySOA.signatures.empty() || !negEntry.DNSSECRecords.signatures.empty(), true, depth, prefix);
3,431!
5156

5157
        if (recordState == vState::Secure) {
3,431✔
5158
          negEntry.d_auth = auth;
2,175✔
5159
          negEntry.d_name = newauth;
2,175✔
5160
          negEntry.d_qtype = QType::DS;
2,175✔
5161
          rec.d_ttl = min(s_maxnegttl, rec.d_ttl);
2,175✔
5162

5163
          dState denialState = getDenialValidationState(negEntry, dState::NXQTYPE, true, prefix);
2,175✔
5164

5165
          if (denialState == dState::NXQTYPE || denialState == dState::OPTOUT || denialState == dState::INSECURE) {
2,176!
5166
            negEntry.d_ttd = lowestTTL + d_now.tv_sec;
2,172✔
5167
            negEntry.d_orig_ttl = lowestTTL;
2,172✔
5168
            negEntry.d_validationState = vState::Secure;
2,172✔
5169
            if (denialState == dState::OPTOUT) {
2,172✔
5170
              negEntry.d_validationState = vState::Insecure;
2,088✔
5171
            }
2,088✔
5172
            LOG(prefix << qname << ": Got negative indication of DS record for '" << newauth << "'" << endl);
2,172!
5173

5174
            g_negCache->add(negEntry);
2,172✔
5175

5176
            /* 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
5177
               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
5178
               query. */
5179
            if (qtype == QType::DS && qname == newauth && (d_externalDSQuery.empty() || qname != d_externalDSQuery)) {
2,172!
5180
              /* we are actually done! */
5181
              negindic = true;
×
5182
              negIndicHasSignatures = !negEntry.authoritySOA.signatures.empty() || !negEntry.DNSSECRecords.signatures.empty();
×
5183
              nsset.clear();
×
5184
            }
×
5185
          }
2,172✔
5186
        }
2,175✔
5187
      }
3,431✔
5188
    }
3,431✔
5189
    else if (!done && rec.d_place == DNSResourceRecord::AUTHORITY && rec.d_type == QType::SOA && lwr.d_rcode == RCode::NoError && qname.isPartOf(rec.d_name)) {
26,007!
5190
      LOG(prefix << qname << ": Got negative caching indication for '" << qname << "|" << qtype << "'" << endl);
1,337!
5191

5192
      if (!newtarget.empty()) {
1,337✔
5193
        LOG(prefix << qname << ": Hang on! Got a redirect to '" << newtarget << "' already" << endl);
5!
5194
      }
5✔
5195
      else {
1,332✔
5196
        rec.d_ttl = min(s_maxnegttl, rec.d_ttl);
1,332✔
5197

5198
        NegCache::NegCacheEntry negEntry;
1,332✔
5199
        negEntry.d_auth = rec.d_name;
1,332✔
5200
        uint32_t lowestTTL = rec.d_ttl;
1,332✔
5201
        negEntry.d_name = qname;
1,332✔
5202
        negEntry.d_qtype = qtype;
1,332✔
5203
        harvestNXRecords(lwr.d_records, negEntry, d_now.tv_sec, &lowestTTL);
1,332✔
5204

5205
        if (vStateIsBogus(state)) {
1,332✔
5206
          negEntry.d_validationState = state;
7✔
5207
        }
7✔
5208
        else {
1,325✔
5209
          auto recordState = getValidationStatus(qname, !negEntry.authoritySOA.signatures.empty() || !negEntry.DNSSECRecords.signatures.empty(), qtype == QType::DS, depth, prefix);
1,325!
5210
          if (recordState == vState::Secure) {
1,325✔
5211
            dState denialState = getDenialValidationState(negEntry, dState::NXQTYPE, false, prefix);
180✔
5212
            updateDenialValidationState(qname, negEntry.d_validationState, negEntry.d_name, state, denialState, dState::NXQTYPE, qtype == QType::DS, depth, prefix);
180✔
5213
          }
180✔
5214
          else {
1,145✔
5215
            negEntry.d_validationState = recordState;
1,145✔
5216
            updateValidationState(qname, state, negEntry.d_validationState, prefix);
1,145✔
5217
          }
1,145✔
5218
        }
1,325✔
5219

5220
        if (vStateIsBogus(negEntry.d_validationState)) {
1,332✔
5221
          lowestTTL = min(lowestTTL, s_maxbogusttl);
13✔
5222
          rec.d_ttl = min(rec.d_ttl, s_maxbogusttl);
13✔
5223
        }
13✔
5224
        negEntry.d_ttd = d_now.tv_sec + lowestTTL;
1,332✔
5225
        negEntry.d_orig_ttl = lowestTTL;
1,332✔
5226
        if (qtype.getCode() != 0) { // prevents us from NXDOMAIN'ing a whole domain
1,332✔
5227
          // doCNAMECacheCheck() checks record cache and does not look into negcache. That means that an old record might be found if
5228
          // serve-stale is active. Avoid that by explicitly zapping that CNAME record.
5229
          if (qtype == QType::CNAME && MemRecursorCache::s_maxServedStaleExtensions > 0) {
1,330!
5230
            g_recCache->doWipeCache(qname, false, qtype);
2✔
5231
          }
2✔
5232
          g_negCache->add(negEntry);
1,330✔
5233
        }
1,330✔
5234

5235
        ret.push_back(rec);
1,332✔
5236
        negindic = true;
1,332✔
5237
        negIndicHasSignatures = !negEntry.authoritySOA.signatures.empty() || !negEntry.DNSSECRecords.signatures.empty();
1,332!
5238
      }
1,332✔
5239
    }
1,337✔
5240
  }
60,933✔
5241

5242
  if (!dnameTarget.empty()) {
12,440✔
5243
    // Synthesize a CNAME
5244
    auto cnamerec = DNSRecord();
32✔
5245
    cnamerec.d_name = qname;
32✔
5246
    cnamerec.d_type = QType::CNAME;
32✔
5247
    cnamerec.d_ttl = dnameTTL;
32✔
5248
    cnamerec.setContent(std::make_shared<CNAMERecordContent>(CNAMERecordContent(newtarget)));
32✔
5249
    ret.push_back(std::move(cnamerec));
32✔
5250
  }
32✔
5251

5252
  /* If we have seen a proper denial, let's forget that we also had a referral for a DS query.
5253
     Otherwise we need to deal with it. */
5254
  if (referralOnDS && !negindic) {
12,440!
5255
    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!
5256
    if (!vStateIsBogus(state)) {
4!
5257
      auto recordState = getValidationStatus(qname, false, true, depth, prefix);
4✔
5258
      if (recordState == vState::Secure) {
4✔
5259
        /* we are in a secure zone, got a referral to the child zone on a DS query, no denial, that's wrong */
5260
        LOG(prefix << qname << ": NODATA without a negative indication (missing SOA in authority) in a DNSSEC secure zone, going Bogus" << endl);
2!
5261
        updateValidationState(qname, state, vState::BogusMissingNegativeIndication, prefix);
2✔
5262
      }
2✔
5263
    }
4✔
5264
    negindic = true;
4✔
5265
    negIndicHasSignatures = false;
4✔
5266
  }
4✔
5267

5268
  return done;
12,440✔
5269
}
12,440✔
5270

5271
static void submitTryDotTask(ComboAddress address, const DNSName& auth, const DNSName& nsname, time_t now)
5272
{
×
5273
  if (address.getPort() == 853) {
×
5274
    return;
×
5275
  }
×
5276
  address.setPort(853);
×
5277
  auto lock = s_dotMap.lock();
×
5278
  if (lock->d_numBusy >= SyncRes::s_max_busy_dot_probes) {
×
5279
    return;
×
5280
  }
×
5281
  auto iter = lock->d_map.emplace(DoTStatus{address, auth, now + dotFailWait}).first;
×
5282
  if (iter->d_status == DoTStatus::Busy) {
×
5283
    return;
×
5284
  }
×
5285
  if (iter->d_ttd > now) {
×
5286
    if (iter->d_status == DoTStatus::Bad) {
×
5287
      return;
×
5288
    }
×
5289
    if (iter->d_status == DoTStatus::Good) {
×
5290
      return;
×
5291
    }
×
5292
    // We only want to probe auths that we have seen before, auth that only come around once are not interesting
5293
    if (iter->d_status == DoTStatus::Unknown && iter->d_count == 0) {
×
5294
      return;
×
5295
    }
×
5296
  }
×
5297
  lock->d_map.modify(iter, [=](DoTStatus& status) { status.d_ttd = now + dotFailWait; });
×
5298
  bool pushed = pushTryDoTTask(auth, QType::SOA, address, std::numeric_limits<time_t>::max(), nsname);
×
5299
  if (pushed) {
×
5300
    iter->d_status = DoTStatus::Busy;
×
5301
    ++lock->d_numBusy;
×
5302
  }
×
5303
}
×
5304

5305
static bool shouldDoDoT(ComboAddress address, time_t now)
5306
{
×
5307
  address.setPort(853);
×
5308
  auto lock = s_dotMap.lock();
×
5309
  auto iter = lock->d_map.find(address);
×
5310
  if (iter == lock->d_map.end()) {
×
5311
    return false;
×
5312
  }
×
5313
  iter->d_count++;
×
5314
  return iter->d_status == DoTStatus::Good && iter->d_ttd > now;
×
5315
}
×
5316

5317
static void updateDoTStatus(ComboAddress address, DoTStatus::Status status, time_t time, bool updateBusy = false)
5318
{
×
5319
  address.setPort(853);
×
5320
  auto lock = s_dotMap.lock();
×
5321
  auto iter = lock->d_map.find(address);
×
5322
  if (iter != lock->d_map.end()) {
×
5323
    iter->d_status = status;
×
5324
    lock->d_map.modify(iter, [=](DoTStatus& statusToModify) { statusToModify.d_ttd = time; });
×
5325
    if (updateBusy) {
×
5326
      --lock->d_numBusy;
×
5327
    }
×
5328
  }
×
5329
}
×
5330

5331
bool SyncRes::tryDoT(const DNSName& qname, const QType qtype, const DNSName& nsName, ComboAddress address, time_t now)
5332
{
×
5333
  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));
×
5334

5335
  auto logHelper1 = [&log](const string& ename) {
×
5336
    log->info(Logr::Debug, "Failed to probe DoT records, got an exception", "exception", Logging::Loggable(ename));
×
5337
  };
×
5338
  auto logHelper2 = [&log](const string& msg, const string& ename) {
×
5339
    log->error(Logr::Debug, msg, "Failed to probe DoT records, got an exception", "exception", Logging::Loggable(ename));
×
5340
  };
×
5341
  LWResult lwr;
×
5342
  bool truncated{};
×
5343
  bool spoofed{};
×
5344
  boost::optional<Netmask> netmask;
×
5345
  address.setPort(853);
×
5346
  // We use the fact that qname equals auth
5347
  bool isOK = false;
×
5348
  try {
×
5349
    boost::optional<EDNSExtendedError> extendedError;
×
5350
    isOK = doResolveAtThisIP("", qname, qtype, lwr, netmask, qname, false, false, nsName, address, true, true, truncated, spoofed, extendedError, true);
×
5351
    isOK = isOK && lwr.d_rcode == RCode::NoError && !lwr.d_records.empty();
×
5352
  }
×
5353
  catch (const PDNSException& e) {
×
5354
    logHelper2(e.reason, "PDNSException");
×
5355
  }
×
5356
  catch (const ImmediateServFailException& e) {
×
5357
    logHelper2(e.reason, "ImmediateServFailException");
×
5358
  }
×
5359
  catch (const PolicyHitException& e) {
×
5360
    logHelper1("PolicyHitException");
×
5361
  }
×
5362
  catch (const std::exception& e) {
×
5363
    logHelper2(e.what(), "std::exception");
×
5364
  }
×
5365
  catch (...) {
×
5366
    logHelper1("other");
×
5367
  }
×
5368
  updateDoTStatus(address, isOK ? DoTStatus::Good : DoTStatus::Bad, now + (isOK ? dotSuccessWait : dotFailWait), true);
×
5369
  return isOK;
×
5370
}
×
5371

5372
void SyncRes::ednsStats(boost::optional<Netmask>& ednsmask, const DNSName& qname, const string& prefix)
5373
{
12,851✔
5374
  if (!ednsmask) {
12,851✔
5375
    return;
12,772✔
5376
  }
12,772✔
5377
  s_ecsresponses++;
79✔
5378
  LOG(prefix << qname << ": Received EDNS Client Subnet Mask " << ednsmask->toString() << " on response" << endl);
79!
5379

5380
  if (ednsmask->getBits() > 0) {
79!
5381
    if (ednsmask->isIPv4()) {
79✔
5382
      ++SyncRes::s_ecsResponsesBySubnetSize4.at(ednsmask->getBits() - 1);
67✔
5383
    }
67✔
5384
    else {
12✔
5385
      ++SyncRes::s_ecsResponsesBySubnetSize6.at(ednsmask->getBits() - 1);
12✔
5386
    }
12✔
5387
  }
79✔
5388
}
79✔
5389

5390
void SyncRes::updateQueryCounts(const string& prefix, const DNSName& qname, const ComboAddress& address, bool doTCP, bool doDoT)
5391
{
13,112✔
5392
  t_Counters.at(rec::Counter::outqueries)++;
13,112✔
5393
  d_outqueries++;
13,112✔
5394
  checkMaxQperQ(qname);
13,112✔
5395
  if (address.sin4.sin_family == AF_INET6) {
13,112✔
5396
    t_Counters.at(rec::Counter::ipv6queries)++;
810✔
5397
  }
810✔
5398
  if (doTCP) {
13,112✔
5399
    if (doDoT) {
70✔
5400
      LOG(prefix << qname << ": Using DoT with " << address.toStringWithPort() << endl);
9!
5401
      t_Counters.at(rec::Counter::dotoutqueries)++;
9✔
5402
      d_dotoutqueries++;
9✔
5403
    }
9✔
5404
    else {
61✔
5405
      LOG(prefix << qname << ": Using TCP with " << address.toStringWithPort() << endl);
61!
5406
      t_Counters.at(rec::Counter::tcpoutqueries)++;
61✔
5407
      d_tcpoutqueries++;
61✔
5408
    }
61✔
5409
  }
70✔
5410
}
13,112✔
5411

5412
void SyncRes::incTimeoutStats(const ComboAddress& remoteIP)
5413
{
320✔
5414
  d_timeouts++;
320✔
5415
  t_Counters.at(rec::Counter::outgoingtimeouts)++;
320✔
5416

5417
  if (remoteIP.sin4.sin_family == AF_INET) {
320✔
5418
    t_Counters.at(rec::Counter::outgoing4timeouts)++;
169✔
5419
  }
169✔
5420
  else {
151✔
5421
    t_Counters.at(rec::Counter::outgoing6timeouts)++;
151✔
5422
  }
151✔
5423

5424
  if (t_timeouts) {
320✔
5425
    t_timeouts->push_back(remoteIP);
4✔
5426
  }
4✔
5427
}
320✔
5428

5429
void SyncRes::checkTotalTime(const DNSName& qname, QType qtype, boost::optional<EDNSExtendedError>& extendedError) const
5430
{
12,863✔
5431
  if (s_maxtotusec != 0 && d_totUsec > s_maxtotusec) {
12,863!
5432
    if (s_addExtendedResolutionDNSErrors) {
2!
5433
      extendedError = EDNSExtendedError{static_cast<uint16_t>(EDNSExtendedError::code::NoReachableAuthority), "Timeout waiting for answer(s)"};
×
5434
    }
×
5435
    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✔
5436
  }
2✔
5437
}
12,863✔
5438

5439
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)
5440
{
12,863✔
5441
  checkTotalTime(qname, qtype, extendedError);
12,863✔
5442

5443
  bool chained = false;
12,863✔
5444
  LWResult::Result resolveret = LWResult::Result::Success;
12,863✔
5445
  int preOutQueryRet = RCode::NoError;
12,863✔
5446

5447
  if (d_pdl && d_pdl->preoutquery(remoteIP, d_requestor, qname, qtype, doTCP, lwr.d_records, preOutQueryRet, d_eventTrace, timeval{0, 0})) {
12,863✔
5448
    LOG(prefix << qname << ": Query handled by Lua" << endl);
4!
5449
  }
4✔
5450
  else {
12,859✔
5451
    ednsmask = getEDNSSubnetMask(qname, remoteIP);
12,859✔
5452
    if (ednsmask) {
12,859✔
5453
      LOG(prefix << qname << ": Adding EDNS Client Subnet Mask " << ednsmask->toString() << " to query" << endl);
575!
5454
      s_ecsqueries++;
575✔
5455
    }
575✔
5456
    auto match = d_eventTrace.add(RecEventTrace::AuthRequest, qname.toLogString() + '/' + qtype.toString(), true, 0);
12,859✔
5457
    updateQueryCounts(prefix, qname, remoteIP, doTCP, doDoT);
12,859✔
5458
    resolveret = asyncresolveWrapper(LogObject(prefix), remoteIP, d_doDNSSEC, qname, auth, qtype.getCode(),
12,859✔
5459
                                     doTCP, sendRDQuery, &d_now, ednsmask, &lwr, &chained, nsName); // <- we go out on the wire!
12,859✔
5460
    d_eventTrace.add(RecEventTrace::AuthRequest, static_cast<int64_t>(lwr.d_rcode), false, match);
12,859✔
5461
    ednsStats(ednsmask, qname, prefix);
12,859✔
5462
    if (resolveret == LWResult::Result::ECSMissing) {
12,859✔
5463
      ednsmask = boost::none;
255✔
5464
      LOG(prefix << qname << ": Answer has no ECS, trying again without EDNS Client Subnet Mask" << endl);
255!
5465
      updateQueryCounts(prefix, qname, remoteIP, doTCP, doDoT);
255✔
5466
      match = d_eventTrace.add(RecEventTrace::AuthRequest, qname.toLogString() + '/' + qtype.toString(), true, 0);
255✔
5467
      resolveret = asyncresolveWrapper(LogObject(prefix), remoteIP, d_doDNSSEC, qname, auth, qtype.getCode(),
255✔
5468
                                       doTCP, sendRDQuery, &d_now, ednsmask, &lwr, &chained, nsName); // <- we go out on the wire!
255✔
5469
      d_eventTrace.add(RecEventTrace::AuthRequest, static_cast<int64_t>(lwr.d_rcode), false, match);
255✔
5470
    }
255✔
5471
  }
12,859✔
5472

5473
  /* preoutquery killed the query by setting dq.rcode to -3 */
5474
  if (preOutQueryRet == -3) {
12,863✔
5475
    throw ImmediateServFailException("Query killed by policy");
4✔
5476
  }
4✔
5477

5478
  d_totUsec += lwr.d_usec;
12,859✔
5479

5480
  if (resolveret == LWResult::Result::Spoofed || resolveret == LWResult::Result::BadCookie) {
12,859✔
5481
    spoofed = true;
3✔
5482
    return false;
3✔
5483
  }
3✔
5484

5485
  accountAuthLatency(lwr.d_usec, remoteIP.sin4.sin_family);
12,856✔
5486
  if (lwr.d_rcode >= 0 && lwr.d_rcode < static_cast<decltype(lwr.d_rcode)>(t_Counters.at(rec::RCode::auth).rcodeCounters.size())) {
12,856✔
5487
    ++t_Counters.at(rec::RCode::auth).rcodeCounters.at(static_cast<uint8_t>(lwr.d_rcode));
12,848✔
5488
  }
12,848✔
5489

5490
  if (!dontThrottle) {
12,856✔
5491
    dontThrottle = shouldNotThrottle(&nsName, &remoteIP);
12,847✔
5492
  }
12,847✔
5493

5494
  if (resolveret != LWResult::Result::Success) {
12,856✔
5495
    /* Error while resolving */
5496
    switch (resolveret) {
393✔
5497
    case LWResult::Result::Timeout:
320✔
5498
      LOG(prefix << qname << ": Timeout resolving after " << lwr.d_usec / 1000.0 << " ms " << (doTCP ? "over TCP" : "") << endl);
320!
5499
      incTimeoutStats(remoteIP);
320✔
5500
      break;
320✔
5501
    case LWResult::Result::OSLimitError:
6✔
5502
      /* OS resource limit reached */
5503
      LOG(prefix << qname << ": Hit a local resource limit resolving" << (doTCP ? " over TCP" : "") << ", probable error: " << stringerror() << endl);
6!
5504
      t_Counters.at(rec::Counter::resourceLimits)++;
6✔
5505
      break;
6✔
5506
    case LWResult::Result::ChainLimitError:
×
5507
      /* Chain resource limit reached */
5508
      LOG(prefix << qname << ": Hit a chain limit resolving" << (doTCP ? " over TCP" : ""));
×
5509
      t_Counters.at(rec::Counter::chainLimits)++;
×
5510
      break;
×
5511
    default:
67✔
5512
      /* LWResult::Result::PermanentError */
5513
      t_Counters.at(rec::Counter::unreachables)++;
67✔
5514
      d_unreachables++;
67✔
5515
      // XXX questionable use of errno
5516
      LOG(prefix << qname << ": Error resolving from " << remoteIP.toString() << (doTCP ? " over TCP" : "") << ", possible error: " << stringerror() << endl);
67!
5517
      break;
67✔
5518
    }
393✔
5519

5520
    // don't account for resource limits, they are our own fault
5521
    // And don't throttle when the IP address is on the dontThrottleNetmasks list or the name is part of dontThrottleNames
5522
    if (!LWResult::isLimitError(resolveret) && !chained && !dontThrottle) {
393!
5523
      uint32_t responseUsec = 1000000; // 1 sec for non-timeout cases
385✔
5524
      // Use the actual time if we saw a timeout
5525
      if (resolveret == LWResult::Result::Timeout) {
385✔
5526
        responseUsec = lwr.d_usec;
320✔
5527
      }
320✔
5528

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

5531
      // make sure we don't throttle the root
5532
      if (s_serverdownmaxfails > 0 && auth != g_rootdnsname && s_fails.lock()->incr(remoteIP, d_now) >= s_serverdownmaxfails) {
385!
5533
        LOG(prefix << qname << ": Max fails reached resolving on " << remoteIP.toString() << ". Going full throttle for " << s_serverdownthrottletime << " seconds" << endl);
×
5534
        // mark server as down
5535
        doThrottle(d_now.tv_sec, remoteIP, s_serverdownthrottletime, 10000, Throttle::Reason::ServerDown);
×
5536
      }
×
5537
      else if (resolveret == LWResult::Result::PermanentError) {
385✔
5538
        // unreachable, 1 minute or 100 queries
5539
        doThrottle(d_now.tv_sec, remoteIP, qname, qtype, 60, 100, Throttle::Reason::PermanentError);
65✔
5540
      }
65✔
5541
      else {
320✔
5542
        // If the actual response time was more than 80% of the default timeout, we throttle. On a
5543
        // busy rec we reduce the time we are willing to wait for an auth, it is unfair to throttle on
5544
        // such a shortened timeout.
5545
        if (responseUsec > g_networkTimeoutMsec * 800) {
320✔
5546
          // timeout, 10 seconds or 5 queries
5547
          doThrottle(d_now.tv_sec, remoteIP, qname, qtype, 10, 5, Throttle::Reason::Timeout);
20✔
5548
        }
20✔
5549
      }
320✔
5550
    }
385✔
5551

5552
    return false;
393✔
5553
  }
393✔
5554

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

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

5562
      if (doTCP) {
12✔
5563
        // we can be more heavy-handed over TCP
5564
        doThrottle(d_now.tv_sec, remoteIP, qname, qtype, 60, 10, Throttle::Reason::ParseError);
4✔
5565
      }
4✔
5566
      else {
8✔
5567
        doThrottle(d_now.tv_sec, remoteIP, qname, qtype, 10, 2, Throttle::Reason::ParseError);
8✔
5568
      }
8✔
5569
    }
12✔
5570
    return false;
12✔
5571
  }
12✔
5572
  /* we got an answer */
5573
  if (lwr.d_rcode != RCode::NoError && lwr.d_rcode != RCode::NXDomain) {
12,451✔
5574
    LOG(prefix << qname << ": " << nsName << " (" << remoteIP.toString() << ") returned a " << RCode::to_s(lwr.d_rcode) << ", trying sibling IP or NS" << endl);
65!
5575
    if (!chained && !dontThrottle) {
65!
5576
      if (wasForwarded && lwr.d_rcode == RCode::ServFail) {
65✔
5577
        // 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
5578
        // 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
5579
        // at the very least we will detect that if our packets stop being answered
5580
        s_nsSpeeds.lock()->find_or_enter(nsName.empty() ? DNSName(remoteIP.toStringWithPort()) : nsName, d_now).submit(remoteIP, 1000000, d_now); // 1 sec
4!
5581
      }
4✔
5582
      else {
61✔
5583
        Throttle::Reason reason{};
61✔
5584
        switch (lwr.d_rcode) {
61✔
5585
        case RCode::ServFail:
1✔
5586
          reason = Throttle::Reason::RCodeServFail;
1✔
5587
          break;
1✔
5588
        case RCode::Refused:
8✔
5589
          reason = Throttle::Reason::RCodeRefused;
8✔
5590
          break;
8✔
5591
        default:
52✔
5592
          reason = Throttle::Reason::RCodeOther;
52✔
5593
          break;
52✔
5594
        }
61✔
5595
        doThrottle(d_now.tv_sec, remoteIP, qname, qtype, 60, 3, reason);
61✔
5596
      }
61✔
5597
    }
65✔
5598
    return false;
65✔
5599
  }
65✔
5600

5601
  /* this server sent a valid answer, mark it backup up if it was down */
5602
  if (s_serverdownmaxfails > 0) {
12,386✔
5603
    s_fails.lock()->clear(remoteIP);
12,373✔
5604
  }
12,373✔
5605
  // Clear all throttles for this IP, both general and specific throttles for qname-qtype
5606
  unThrottle(remoteIP, qname, qtype);
12,386✔
5607

5608
  if (lwr.d_tcbit) {
12,386✔
5609
    truncated = true;
58✔
5610

5611
    if (doTCP) {
58✔
5612
      LOG(prefix << qname << ": Truncated bit set, over TCP?" << endl);
2!
5613
      if (!dontThrottle) {
2!
5614
        /* let's treat that as a ServFail answer from this server */
5615
        doThrottle(d_now.tv_sec, remoteIP, qname, qtype, 60, 3, Throttle::Reason::TCPTruncate);
2✔
5616
      }
2✔
5617
      return false;
2✔
5618
    }
2✔
5619
    LOG(prefix << qname << ": Truncated bit set, over UDP" << endl);
56!
5620

5621
    return true;
56✔
5622
  }
58✔
5623

5624
  return true;
12,328✔
5625
}
12,386✔
5626

5627
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)
5628
{
1,536✔
5629
  if (newtarget == qname) {
1,536✔
5630
    LOG(prefix << qname << ": Status=got a CNAME referral to self, returning SERVFAIL" << endl);
3!
5631
    ret.clear();
3✔
5632
    rcode = RCode::ServFail;
3✔
5633
    return;
3✔
5634
  }
3✔
5635
  if (newtarget.isPartOf(qname)) {
1,533✔
5636
    // a.b.c. CNAME x.a.b.c will go to great depths with QM on
5637
    LOG(prefix << qname << ": Status=got a CNAME referral to child, disabling QM" << endl);
29!
5638
    setQNameMinimization(false);
29✔
5639
  }
29✔
5640

5641
  if (!d_followCNAME) {
1,533✔
5642
    rcode = RCode::NoError;
114✔
5643
    return;
114✔
5644
  }
114✔
5645

5646
  // Check to see if we already have seen the new target as a previous target or that the chain is too long
5647
  const auto [CNAMELoop, numCNAMEs] = scanForCNAMELoop(newtarget, ret);
1,419✔
5648
  if (CNAMELoop) {
1,419✔
5649
    LOG(prefix << qname << ": Status=got a CNAME referral that causes a loop, returning SERVFAIL" << endl);
2!
5650
    ret.clear();
2✔
5651
    rcode = RCode::ServFail;
2✔
5652
    return;
2✔
5653
  }
2✔
5654
  if (numCNAMEs > s_max_CNAMES_followed) {
1,417✔
5655
    LOG(prefix << qname << ": Status=got a CNAME referral, but chain too long, returning SERVFAIL" << endl);
2!
5656
    rcode = RCode::ServFail;
2✔
5657
    return;
2✔
5658
  }
2✔
5659

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

5663
    if (d_doDNSSEC) {
4!
5664
      addNXNSECS(ret, recordsFromAnswer);
4✔
5665
    }
4✔
5666

5667
    rcode = RCode::NoError;
4✔
5668
    return;
4✔
5669
  }
4✔
5670

5671
  LOG(prefix << qname << ": Status=got a CNAME referral, starting over with " << newtarget << endl);
1,411✔
5672

5673
  set<GetBestNSAnswer> beenthere;
1,411✔
5674
  Context cnameContext;
1,411✔
5675
  rcode = doResolve(newtarget, qtype, ret, depth + 1, beenthere, cnameContext);
1,411✔
5676
  LOG(prefix << qname << ": Updating validation state for response to " << qname << " from " << state << " with the state from the CNAME quest: " << cnameContext.state << endl);
1,411✔
5677
  updateValidationState(qname, state, cnameContext.state, prefix);
1,411✔
5678
}
1,411✔
5679

5680
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)
5681
{
12,458✔
5682
  if (s_minimumTTL != 0) {
12,458✔
5683
    for (auto& rec : lwr.d_records) {
65,658✔
5684
      rec.d_ttl = max(rec.d_ttl, s_minimumTTL);
65,658✔
5685
    }
65,658✔
5686
  }
10,225✔
5687

5688
  /* if the answer is ECS-specific, a minimum TTL is set for this kind of answers
5689
     and it's higher than the global minimum TTL */
5690
  if (ednsmask && s_minimumECSTTL > 0 && (s_minimumTTL == 0 || s_minimumECSTTL > s_minimumTTL)) {
12,458!
5691
    for (auto& rec : lwr.d_records) {
2✔
5692
      if (rec.d_place == DNSResourceRecord::ANSWER) {
2!
5693
        rec.d_ttl = max(rec.d_ttl, s_minimumECSTTL);
2✔
5694
      }
2✔
5695
    }
2✔
5696
  }
2✔
5697

5698
  bool needWildcardProof = false;
12,458✔
5699
  bool gatherWildcardProof = false;
12,458✔
5700
  unsigned int wildcardLabelsCount = 0;
12,458✔
5701
  *rcode = updateCacheFromRecords(depth, prefix, lwr, qname, qtype, auth, wasForwarded, ednsmask, state, needWildcardProof, gatherWildcardProof, wildcardLabelsCount, sendRDQuery, remoteIP);
12,458✔
5702
  if (*rcode != RCode::NoError) {
12,458!
5703
    return true;
×
5704
  }
×
5705

5706
  LOG(prefix << qname << ": Determining status after receiving this packet" << endl);
12,458✔
5707

5708
  set<DNSName> nsset;
12,458✔
5709
  bool realreferral = false;
12,458✔
5710
  bool negindic = false;
12,458✔
5711
  bool negIndicHasSignatures = false;
12,458✔
5712
  DNSName newauth;
12,458✔
5713
  DNSName newtarget;
12,458✔
5714

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

5717
  // If we both have a CNAME and an answer, let the CNAME take precedence. This *should* not happen
5718
  // (because CNAMEs cannot co-exist with other records), but reality says otherwise. Other
5719
  // resolvers choose to follow the CNAME in this case as well. We removed the answer record from
5720
  // the records received from the auth when sanitizing, so `done' should not be set when a CNAME is
5721
  // present.
5722
  if (done) {
12,458✔
5723
    LOG(prefix << qname << ": Status=got results, this level of recursion done" << endl);
5,819✔
5724
    LOG(prefix << qname << ": Validation status is " << state << endl);
5,819✔
5725
    return true;
5,819✔
5726
  }
5,819✔
5727

5728
  if (!newtarget.empty()) {
6,639✔
5729
    handleNewTarget(prefix, qname, newtarget, qtype.getCode(), ret, *rcode, depth, lwr.d_records, state);
1,526✔
5730
    return true;
1,526✔
5731
  }
1,526✔
5732

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

5736
    auto tempState = getValidationStatus(qname, negIndicHasSignatures, qtype == QType::DS, depth, prefix);
135✔
5737
    if (tempState == vState::Secure && (lwr.d_aabit || sendRDQuery) && !negindic) {
135!
5738
      LOG(prefix << qname << ": NXDOMAIN without a negative indication (missing SOA in authority) in a DNSSEC secure zone, going Bogus" << endl);
4!
5739
      updateValidationState(qname, state, vState::BogusMissingNegativeIndication, prefix);
4✔
5740
    }
4✔
5741
    else {
131✔
5742
      /* we might not have validated any record, because we did get a NXDOMAIN without any SOA
5743
         from an insecure zone, for example */
5744
      updateValidationState(qname, state, tempState, prefix);
131✔
5745
    }
131✔
5746

5747
    if (d_doDNSSEC) {
135✔
5748
      addNXNSECS(ret, lwr.d_records);
101✔
5749
    }
101✔
5750

5751
    *rcode = RCode::NXDomain;
135✔
5752
    return true;
135✔
5753
  }
135✔
5754

5755
  if (nsset.empty() && lwr.d_rcode == 0 && (negindic || lwr.d_aabit || sendRDQuery)) {
4,978!
5756
    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,379!
5757

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

5769
    if (d_doDNSSEC) {
1,379✔
5770
      addNXNSECS(ret, lwr.d_records);
1,309✔
5771
    }
1,309✔
5772

5773
    *rcode = RCode::NoError;
1,379✔
5774
    return true;
1,379✔
5775
  }
1,379✔
5776

5777
  if (realreferral) {
3,599✔
5778
    LOG(prefix << qname << ": Status=did not resolve, got " << (unsigned int)nsset.size() << " NS, ");
3,527✔
5779

5780
    nameservers.clear();
3,527✔
5781
    for (auto const& nameserver : nsset) {
13,772✔
5782
      if (d_wantsRPZ && !d_appliedPolicy.wasHit()) {
13,772✔
5783
        bool match = dfe.getProcessingPolicy(nameserver, d_discardedPolicies, d_appliedPolicy);
13,768✔
5784
        if (match) {
13,768✔
5785
          mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());
2✔
5786
          if (d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) { // client query needs an RPZ response
2!
5787
            if (d_pdl && d_pdl->policyHitEventFilter(d_requestor, qname, qtype, d_queryReceivedOverTCP, d_appliedPolicy, d_policyTags, d_discardedPolicies)) {
2!
5788
              /* reset to no match */
5789
              d_appliedPolicy = DNSFilterEngine::Policy();
×
5790
            }
×
5791
            else {
2✔
5792
              LOG("however " << nameserver << " was blocked by RPZ policy '" << d_appliedPolicy.getName() << "'" << endl);
2!
5793
              throw PolicyHitException();
2✔
5794
            }
2✔
5795
          }
2✔
5796
        }
2✔
5797
      }
13,768✔
5798
      nameservers.insert({nameserver, {{}, false}});
13,770✔
5799
    }
13,770✔
5800
    LOG("looping to them" << endl);
3,525✔
5801
    *gotNewServers = true;
3,525✔
5802
    auth = std::move(newauth);
3,525✔
5803

5804
    return false;
3,525✔
5805
  }
3,527✔
5806

5807
  return false;
72✔
5808
}
3,599✔
5809

5810
bool SyncRes::doDoTtoAuth(const DNSName& nameServer)
5811
{
12,804✔
5812
  return g_DoTToAuthNames.getLocal()->check(nameServer);
12,804✔
5813
}
12,804✔
5814

5815
/** returns:
5816
 *  -1 in case of no results
5817
 *  rcode otherwise
5818
 */
5819
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
5820
int SyncRes::doResolveAt(NsSet& nameservers, DNSName auth, bool flawedNSSet, const DNSName& qname, const QType qtype,
5821
                         vector<DNSRecord>& ret,
5822
                         unsigned int depth, const string& prefix, set<GetBestNSAnswer>& beenthere, Context& context, StopAtDelegation* stopAtDelegation,
5823
                         map<DNSName, vector<ComboAddress>>* fallBack)
5824
{
10,728✔
5825
  auto luaconfsLocal = g_luaconfs.getLocal();
10,728✔
5826

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

5829
  if (nameserversBlockedByRPZ(luaconfsLocal->dfe, nameservers)) {
10,728✔
5830
    /* RPZ hit */
5831
    if (d_pdl && d_pdl->policyHitEventFilter(d_requestor, qname, qtype, d_queryReceivedOverTCP, d_appliedPolicy, d_policyTags, d_discardedPolicies)) {
2!
5832
      /* reset to no match */
5833
      d_appliedPolicy = DNSFilterEngine::Policy();
×
5834
    }
×
5835
    else {
2✔
5836
      throw PolicyHitException();
2✔
5837
    }
2✔
5838
  }
2✔
5839

5840
  LOG(endl);
10,726✔
5841

5842
  unsigned int addressQueriesForNS = 0;
10,726✔
5843
  for (;;) { // we may get more specific nameservers
12,761✔
5844
    auto rnameservers = shuffleInSpeedOrder(qname, nameservers, prefix);
12,761✔
5845

5846
    // We allow s_maxnsaddressqperq (default 10) queries with empty responses when resolving NS names.
5847
    // If a zone publishes many (more than s_maxnsaddressqperq) NS records, we allow less.
5848
    // This is to "punish" zones that publish many non-resolving NS names.
5849
    // We always allow 5 NS name resolving attempts with empty results.
5850
    unsigned int nsLimit = s_maxnsaddressqperq;
12,761✔
5851
    if (rnameservers.size() > nsLimit) {
12,761✔
5852
      int newLimit = static_cast<int>(nsLimit - (rnameservers.size() - nsLimit));
2,800✔
5853
      nsLimit = std::max(5, newLimit);
2,800✔
5854
    }
2,800✔
5855

5856
    for (auto tns = rnameservers.cbegin();; ++tns) {
13,238✔
5857
      if (addressQueriesForNS >= nsLimit) {
13,238✔
5858
        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✔
5859
      }
2✔
5860
      if (tns == rnameservers.cend()) {
13,236✔
5861
        LOG(prefix << qname << ": Failed to resolve via any of the " << (unsigned int)rnameservers.size() << " offered NS at level '" << auth << "'" << endl);
247✔
5862
        if (s_addExtendedResolutionDNSErrors) {
247✔
5863
          context.extendedError = EDNSExtendedError{static_cast<uint16_t>(EDNSExtendedError::code::NoReachableAuthority), "delegation " + auth.toLogString()};
59✔
5864
        }
59✔
5865
        if (!auth.isRoot() && flawedNSSet) {
247✔
5866
          LOG(prefix << qname << ": Ageing nameservers for level '" << auth << "', next query might succeed" << endl);
65!
5867
          if (g_recCache->doAgeCache(d_now.tv_sec, auth, QType::NS, 10)) {
65✔
5868
            t_Counters.at(rec::Counter::nsSetInvalidations)++;
16✔
5869
          }
16✔
5870
        }
65✔
5871
        return -1;
247✔
5872
      }
247✔
5873

5874
      bool cacheOnly = false;
12,989✔
5875
      // this line needs to identify the 'self-resolving' behaviour
5876
      if (qname == tns->first && (qtype.getCode() == QType::A || qtype.getCode() == QType::AAAA)) {
12,989!
5877
        /* we might have a glue entry in cache so let's try this NS
5878
           but only if we have enough in the cache to know how to reach it */
5879
        LOG(prefix << qname << ": Using NS to resolve itself, but only using what we have in cache (" << (1 + tns - rnameservers.cbegin()) << "/" << rnameservers.size() << ")" << endl);
60!
5880
        cacheOnly = true;
60✔
5881
      }
60✔
5882

5883
      typedef vector<ComboAddress> remoteIPs_t;
12,989✔
5884
      remoteIPs_t remoteIPs;
12,989✔
5885
      remoteIPs_t::iterator remoteIP;
12,989✔
5886
      bool pierceDontQuery = false;
12,989✔
5887
      bool sendRDQuery = false;
12,989✔
5888
      boost::optional<Netmask> ednsmask;
12,989✔
5889
      LWResult lwr;
12,989✔
5890
      const bool wasForwarded = tns->first.empty() && (!nameservers[tns->first].first.empty());
12,989✔
5891
      int rcode = RCode::NoError;
12,989✔
5892
      bool gotNewServers = false;
12,989✔
5893

5894
      if (tns->first.empty() && !wasForwarded) {
12,989✔
5895
        static ComboAddress const s_oobRemote("255.255.255.255");
138✔
5896
        LOG(prefix << qname << ": Domain is out-of-band" << endl);
138!
5897
        /* setting state to indeterminate since validation is disabled for local auth zone,
5898
           and Insecure would be misleading. */
5899
        context.state = vState::Indeterminate;
138✔
5900
        d_wasOutOfBand = doOOBResolve(qname, qtype, lwr.d_records, depth, prefix, lwr.d_rcode);
138✔
5901
        lwr.d_tcbit = false;
138✔
5902
        lwr.d_aabit = true;
138✔
5903

5904
        /* we have received an answer, are we done ? */
5905
        bool done = processAnswer(depth, prefix, lwr, qname, qtype, auth, false, ednsmask, sendRDQuery, nameservers, ret, luaconfsLocal->dfe, &gotNewServers, &rcode, context.state, s_oobRemote);
138✔
5906
        if (done) {
138✔
5907
          return rcode;
134✔
5908
        }
134✔
5909
        if (gotNewServers) {
4!
5910
          if (stopAtDelegation != nullptr && *stopAtDelegation == Stop) {
4!
5911
            *stopAtDelegation = Stopped;
×
5912
            return rcode;
×
5913
          }
×
5914
          break;
4✔
5915
        }
4✔
5916
      }
4✔
5917
      else {
12,851✔
5918
        if (fallBack != nullptr) {
12,851✔
5919
          if (auto iter = fallBack->find(tns->first); iter != fallBack->end()) {
2!
5920
            remoteIPs = iter->second;
2✔
5921
          }
2✔
5922
        }
2✔
5923
        if (remoteIPs.empty()) {
12,851✔
5924
          remoteIPs = retrieveAddressesForNS(prefix, qname, tns, depth, beenthere, rnameservers, nameservers, sendRDQuery, pierceDontQuery, flawedNSSet, cacheOnly, addressQueriesForNS);
12,848✔
5925
        }
12,848✔
5926

5927
        if (remoteIPs.empty()) {
12,851✔
5928
          LOG(prefix << qname << ": Failed to get IP for NS " << tns->first << ", trying next if available" << endl);
136!
5929
          flawedNSSet = true;
136✔
5930
          continue;
136✔
5931
        }
136✔
5932
        bool hitPolicy{false};
12,715✔
5933
        LOG(prefix << qname << ": Resolved '" << auth << "' NS " << tns->first << " to: ");
12,715✔
5934
        for (remoteIP = remoteIPs.begin(); remoteIP != remoteIPs.end(); ++remoteIP) {
26,716✔
5935
          if (remoteIP != remoteIPs.begin()) {
14,001✔
5936
            LOG(", ");
1,376✔
5937
          }
1,376✔
5938
          LOG(remoteIP->toString());
14,001✔
5939
          if (nameserverIPBlockedByRPZ(luaconfsLocal->dfe, *remoteIP)) {
14,001✔
5940
            hitPolicy = true;
4✔
5941
          }
4✔
5942
        }
14,001✔
5943
        LOG(endl);
12,715✔
5944
        if (hitPolicy) { // implies d_wantsRPZ
12,715✔
5945
          /* RPZ hit */
5946
          if (d_pdl && d_pdl->policyHitEventFilter(d_requestor, qname, qtype, d_queryReceivedOverTCP, d_appliedPolicy, d_policyTags, d_discardedPolicies)) {
4!
5947
            /* reset to no match */
5948
            d_appliedPolicy = DNSFilterEngine::Policy();
×
5949
          }
×
5950
          else {
4✔
5951
            throw PolicyHitException();
4✔
5952
          }
4✔
5953
        }
4✔
5954

5955
        for (remoteIP = remoteIPs.begin(); remoteIP != remoteIPs.end(); ++remoteIP) {
13,285✔
5956
          LOG(prefix << qname << ": Trying IP " << remoteIP->toStringWithPort() << ", asking '" << qname << "|" << qtype << "'" << endl);
12,814✔
5957

5958
          if (throttledOrBlocked(prefix, *remoteIP, qname, qtype, pierceDontQuery)) {
12,814✔
5959
            // As d_throttledqueries might be increased, check the max-qperq condition
5960
            checkMaxQperQ(qname);
10✔
5961
            continue;
10✔
5962
          }
10✔
5963

5964
          bool truncated = false;
12,804✔
5965
          bool spoofed = false;
12,804✔
5966
          bool gotAnswer = false;
12,804✔
5967
          bool doDoT = false;
12,804✔
5968

5969
          if (doDoTtoAuth(tns->first)) {
12,804✔
5970
            remoteIP->setPort(853);
3✔
5971
            doDoT = true;
3✔
5972
          }
3✔
5973
          if (SyncRes::s_dot_to_port_853 && remoteIP->getPort() == 853) {
12,804✔
5974
            doDoT = true;
9✔
5975
          }
9✔
5976
          bool forceTCP = doDoT;
12,804✔
5977

5978
          if (!doDoT && s_max_busy_dot_probes > 0) {
12,804!
5979
            submitTryDotTask(*remoteIP, auth, tns->first, d_now.tv_sec);
×
5980
          }
×
5981
          if (!forceTCP) {
12,804✔
5982
            gotAnswer = doResolveAtThisIP(prefix, qname, qtype, lwr, ednsmask, auth, sendRDQuery, wasForwarded,
12,795✔
5983
                                          tns->first, *remoteIP, false, false, truncated, spoofed, context.extendedError);
12,795✔
5984
          }
12,795✔
5985
          if (spoofed) {
12,804✔
5986
            LOG(prefix << qname << ": potentially spoofed, retrying over TCP" << endl);
3!
5987
          }
3✔
5988
          if (forceTCP || (spoofed || (gotAnswer && truncated))) {
12,804✔
5989
            /* retry, over TCP this time */
5990
            gotAnswer = doResolveAtThisIP(prefix, qname, qtype, lwr, ednsmask, auth, sendRDQuery, wasForwarded,
68✔
5991
                                          tns->first, *remoteIP, true, doDoT, truncated, spoofed, context.extendedError);
68✔
5992
          }
68✔
5993

5994
          if (!gotAnswer) {
12,804✔
5995
            if (doDoT && s_max_busy_dot_probes > 0) {
472!
5996
              // This is quite pessimistic...
5997
              updateDoTStatus(*remoteIP, DoTStatus::Bad, d_now.tv_sec + dotFailWait);
×
5998
            }
×
5999
            continue;
472✔
6000
          }
472✔
6001

6002
          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,332✔
6003

6004
          if (doDoT && s_max_busy_dot_probes > 0) {
12,332!
6005
            updateDoTStatus(*remoteIP, DoTStatus::Good, d_now.tv_sec + dotSuccessWait);
×
6006
          }
×
6007
          /*  // for you IPv6 fanatics :-)
6008
              if(remoteIP->sin4.sin_family==AF_INET6)
6009
              lwr.d_usec/=3;
6010
          */
6011
          //        cout<<"ms: "<<lwr.d_usec/1000.0<<", "<<g_avgLatency/1000.0<<'\n';
6012

6013
          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,332✔
6014

6015
          /* we have received an answer, are we done ? */
6016
          bool done = processAnswer(depth, prefix, lwr, qname, qtype, auth, wasForwarded, ednsmask, sendRDQuery, nameservers, ret, luaconfsLocal->dfe, &gotNewServers, &rcode, context.state, *remoteIP);
12,332✔
6017
          if (done) {
12,332✔
6018
            return rcode;
8,719✔
6019
          }
8,719✔
6020
          if (gotNewServers) {
3,613✔
6021
            if (stopAtDelegation != nullptr && *stopAtDelegation == Stop) {
3,521!
6022
              *stopAtDelegation = Stopped;
1,491✔
6023
              return rcode;
1,491✔
6024
            }
1,491✔
6025
            break;
2,030✔
6026
          }
3,521✔
6027
          /* was lame */
6028
          if (!shouldNotThrottle(&tns->first, &*remoteIP)) {
92✔
6029
            doThrottle(d_now.tv_sec, *remoteIP, qname, qtype, 60, 100, Throttle::Reason::Lame);
52✔
6030
          }
52✔
6031
        }
92✔
6032

6033
        if (gotNewServers) {
2,501✔
6034
          break;
2,030✔
6035
        }
2,030✔
6036

6037
        if (remoteIP == remoteIPs.cend()) { // we tried all IP addresses, none worked
471✔
6038
          continue;
341✔
6039
        }
341✔
6040
      }
471✔
6041
    }
12,989✔
6042
  }
12,761✔
6043
  return -1;
2,147,483,777✔
6044
}
10,726✔
6045

6046
void SyncRes::setQuerySource(const Netmask& netmask)
6047
{
×
6048
  if (!netmask.empty()) {
×
6049
    d_outgoingECSNetwork = netmask;
×
6050
  }
×
6051
  else {
×
6052
    d_outgoingECSNetwork = boost::none;
×
6053
  }
×
6054
}
×
6055

6056
void SyncRes::setQuerySource(const ComboAddress& requestor, const boost::optional<const EDNSSubnetOpts&>& incomingECS)
6057
{
3,225✔
6058
  d_requestor = requestor;
3,225✔
6059

6060
  if (incomingECS && incomingECS->getSourcePrefixLength() > 0) {
3,225✔
6061
    d_cacheRemote = incomingECS->getSource().getMaskedNetwork();
436✔
6062
    uint8_t bits = std::min(incomingECS->getSourcePrefixLength(), (incomingECS->getSource().isIPv4() ? s_ecsipv4limit : s_ecsipv6limit));
436✔
6063
    ComboAddress trunc = incomingECS->getSource().getNetwork();
436✔
6064
    trunc.truncate(bits);
436✔
6065
    d_outgoingECSNetwork = boost::optional<Netmask>(Netmask(trunc, bits));
436✔
6066
  }
436✔
6067
  else {
2,789✔
6068
    d_cacheRemote = d_requestor;
2,789✔
6069
    if (!incomingECS && s_ednslocalsubnets.match(d_requestor)) {
2,789✔
6070
      ComboAddress trunc = d_requestor;
136✔
6071
      uint8_t bits = d_requestor.isIPv4() ? 32 : 128;
136✔
6072
      bits = std::min(bits, (trunc.isIPv4() ? s_ecsipv4limit : s_ecsipv6limit));
136✔
6073
      trunc.truncate(bits);
136✔
6074
      d_outgoingECSNetwork = boost::optional<Netmask>(Netmask(trunc, bits));
136✔
6075
    }
136✔
6076
    else if (s_ecsScopeZero.getSourcePrefixLength() > 0) {
2,653!
6077
      /* RFC7871 says we MUST NOT send any ECS if the source scope is 0.
6078
         But using an empty ECS in that case would mean inserting
6079
         a non ECS-specific entry into the cache, preventing any further
6080
         ECS-specific query to be sent.
6081
         So instead we use the trick described in section 7.1.2:
6082
         "The subsequent Recursive Resolver query to the Authoritative Nameserver
6083
         will then either not include an ECS option or MAY optionally include
6084
         its own address information, which is what the Authoritative
6085
         Nameserver will almost certainly use to generate any Tailored
6086
         Response in lieu of an option.  This allows the answer to be handled
6087
         by the same caching mechanism as other queries, with an explicit
6088
         indicator of the applicable scope.  Subsequent Stub Resolver queries
6089
         for /0 can then be answered from this cached response.
6090
      */
6091
      d_outgoingECSNetwork = boost::optional<Netmask>(s_ecsScopeZero.getSource().getMaskedNetwork());
2,653✔
6092
      d_cacheRemote = s_ecsScopeZero.getSource().getNetwork();
2,653✔
6093
    }
2,653✔
UNCOV
6094
    else {
×
6095
      // ECS disabled because no scope-zero address could be derived.
UNCOV
6096
      d_outgoingECSNetwork = boost::none;
×
UNCOV
6097
    }
×
6098
  }
2,789✔
6099
}
3,225✔
6100

6101
boost::optional<Netmask> SyncRes::getEDNSSubnetMask(const DNSName& name, const ComboAddress& rem)
6102
{
12,857✔
6103
  if (d_outgoingECSNetwork && (s_ednsdomains.check(name) || s_ednsremotesubnets.match(rem))) {
12,857✔
6104
    return d_outgoingECSNetwork;
575✔
6105
  }
575✔
6106
  return boost::none;
12,282✔
6107
}
12,857✔
6108

6109
void SyncRes::parseEDNSSubnetAllowlist(const std::string& alist)
6110
{
356✔
6111
  vector<string> parts;
356✔
6112
  stringtok(parts, alist, ",; ");
356✔
6113
  for (const auto& allow : parts) {
356✔
6114
    try {
31✔
6115
      s_ednsremotesubnets.addMask(Netmask(allow));
31✔
6116
    }
31✔
6117
    catch (...) {
31✔
6118
      s_ednsdomains.add(DNSName(allow));
23✔
6119
    }
23✔
6120
  }
31✔
6121
}
356✔
6122

6123
void SyncRes::parseEDNSSubnetAddFor(const std::string& subnetlist)
6124
{
178✔
6125
  vector<string> parts;
178✔
6126
  stringtok(parts, subnetlist, ",; ");
178✔
6127
  for (const auto& allow : parts) {
1,638✔
6128
    s_ednslocalsubnets.addMask(allow);
1,638✔
6129
  }
1,638✔
6130
}
178✔
6131

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

6138
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)
6139
{
24✔
6140
  auto log = slog->withValues("qname", Logging::Loggable(qname), "qtype", Logging::Loggable(qtype));
24✔
6141

6142
  struct timeval now{};
24✔
6143
  gettimeofday(&now, nullptr);
24✔
6144

6145
  SyncRes resolver(now);
24✔
6146
  resolver.setQNameMinimization(qnamemin);
24✔
6147
  if (pdl) {
24!
6148
    resolver.setLuaEngine(pdl);
24✔
6149
  }
24✔
6150

6151
  int res = -1;
24✔
6152
  const std::string msg = "Exception while resolving";
24✔
6153
  try {
24✔
6154
    res = resolver.beginResolve(qname, qtype, qclass, ret, 0);
24✔
6155
  }
24✔
6156
  catch (const PDNSException& e) {
24✔
6157
    log->error(Logr::Warning, e.reason, msg, "exception", Logging::Loggable("PDNSException"));
×
6158
    ret.clear();
×
6159
  }
×
6160
  catch (const ImmediateServFailException& e) {
24✔
6161
    log->error(Logr::Warning, e.reason, msg, "exception", Logging::Loggable("ImmediateServFailException"));
×
6162
    ret.clear();
×
6163
  }
×
6164
  catch (const PolicyHitException& e) {
24✔
6165
    log->info(Logr::Warning, msg, "exception", Logging::Loggable("PolicyHitException"));
×
6166
    ret.clear();
×
6167
  }
×
6168
  catch (const std::exception& e) {
24✔
6169
    log->error(Logr::Warning, e.what(), msg, "exception", Logging::Loggable("std::exception"));
×
6170
    ret.clear();
×
6171
  }
×
6172
  catch (...) {
24✔
6173
    log->info(Logr::Warning, msg);
×
6174
    ret.clear();
×
6175
  }
×
6176

6177
  return res;
24✔
6178
}
24✔
6179

6180
int SyncRes::getRootNS(struct timeval now, asyncresolve_t asyncCallback, unsigned int depth, Logr::log_t log)
6181
{
251✔
6182
  if (::arg()["hint-file"] == "no-refresh") {
251!
6183
    return 0;
×
6184
  }
×
6185
  SyncRes resolver(now);
251✔
6186
  resolver.d_prefix = "[getRootNS]";
251✔
6187
  resolver.setDoEDNS0(true);
251✔
6188
  resolver.setUpdatingRootNS();
251✔
6189
  resolver.setDoDNSSEC(g_dnssecmode != DNSSECMode::Off);
251✔
6190
  resolver.setDNSSECValidationRequested(g_dnssecmode != DNSSECMode::Off && g_dnssecmode != DNSSECMode::ProcessNoValidate);
251✔
6191
  resolver.setAsyncCallback(std::move(asyncCallback));
251✔
6192
  resolver.setRefreshAlmostExpired(true);
251✔
6193

6194
  const string msg = "Failed to update . records";
251✔
6195
  vector<DNSRecord> ret;
251✔
6196
  int res = -1;
251✔
6197
  try {
251✔
6198
    res = resolver.beginResolve(g_rootdnsname, QType::NS, 1, ret, depth + 1);
251✔
6199
    if (g_dnssecmode != DNSSECMode::Off && g_dnssecmode != DNSSECMode::ProcessNoValidate) {
251✔
6200
      auto state = resolver.getValidationState();
204✔
6201
      if (vStateIsBogus(state)) {
204!
6202
        throw PDNSException("Got Bogus validation result for .|NS");
×
6203
      }
×
6204
    }
204✔
6205
  }
251✔
6206
  catch (const PDNSException& e) {
251✔
6207
    log->error(Logr::Error, e.reason, msg, "exception", Logging::Loggable("PDNSException"));
×
6208
  }
×
6209
  catch (const ImmediateServFailException& e) {
251✔
6210
    log->error(Logr::Error, e.reason, msg, "exception", Logging::Loggable("ImmediateServFailException"));
×
6211
  }
×
6212
  catch (const PolicyHitException& policyHit) {
251✔
6213
    log->info(Logr::Error, msg, "exception", Logging::Loggable("PolicyHitException"),
×
6214
              "policyName", Logging::Loggable(resolver.d_appliedPolicy.getName()));
×
6215
    ret.clear();
×
6216
  }
×
6217
  catch (const std::exception& e) {
251✔
6218
    log->error(Logr::Error, e.what(), msg, "exception", Logging::Loggable("std::exception"));
×
6219
  }
×
6220
  catch (...) {
251✔
6221
    log->info(Logr::Error, msg);
×
6222
  }
×
6223

6224
  if (res == 0) {
251✔
6225
    log->info(Logr::Debug, "Refreshed . records");
202✔
6226
  }
202✔
6227
  else {
49✔
6228
    log->info(Logr::Warning, msg, "rcode", Logging::Loggable(res));
49✔
6229
  }
49✔
6230
  return res;
251✔
6231
}
251✔
6232

6233
bool SyncRes::answerIsNOData(uint16_t requestedType, int rcode, const std::vector<DNSRecord>& records)
6234
{
351✔
6235
  if (rcode != RCode::NoError) {
351✔
6236
    return false;
47✔
6237
  }
47✔
6238

6239
  // NOLINTNEXTLINE(readability-use-anyofallof)
6240
  for (const auto& rec : records) {
363✔
6241
    if (rec.d_place == DNSResourceRecord::ANSWER && rec.d_type == requestedType) {
363✔
6242
      /* we have a record, of the right type, in the right section */
6243
      return false;
247✔
6244
    }
247✔
6245
  }
363✔
6246
  return true;
57✔
6247
#if 0
6248
  // This code should be equivalent to the code above, clang-tidy prefers any_of()
6249
  // I have doubts if that is easier to read
6250
  return !std::any_of(records.begin(), records.end(), [=](const DNSRecord& rec) {
6251
    return rec.d_place == DNSResourceRecord::ANSWER && rec.d_type == requestedType;
6252
  });
6253
#endif
6254
}
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