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

PowerDNS / pdns / 17611467588

10 Sep 2025 10:53AM UTC coverage: 66.01% (+0.03%) from 65.978%
17611467588

Pull #16108

github

web-flow
Merge b6eb1a724 into 29382c4af
Pull Request #16108: dnsdist: implement simple packet shuffle in cache

42255 of 92634 branches covered (45.62%)

Branch coverage included in aggregate %.

9 of 120 new or added lines in 6 files covered. (7.5%)

12 existing lines in 5 files now uncovered.

128490 of 166031 relevant lines covered (77.39%)

5519579.39 hits per line

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

81.22
/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)) {}
168✔
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,364✔
74
    auto iter = d_cont.find(arg);
10,364✔
75

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

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

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

95
  void clear(const T& arg)
96
  {
12,475✔
97
    d_cont.erase(arg);
12,475✔
98
  }
12,475✔
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
  {
256✔
112
    auto& ind = d_cont.template get<time_t>();
256✔
113
    ind.erase(ind.begin(), ind.upper_bound(cutoff));
256✔
114
  }
256✔
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;
181✔
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
    {
220✔
152
    }
220✔
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,876✔
165
    auto iter = d_cont.find(arg);
25,876✔
166
    if (iter == d_cont.end()) {
25,876✔
167
      return false;
25,809✔
168
    }
25,809✔
169
    if (now > iter->ttd || iter->count == 0) {
67✔
170
      d_cont.erase(iter);
6✔
171
      return false;
6✔
172
    }
6✔
173
    iter->count--;
61✔
174

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

178
  void throttle(time_t now, const Key& arg, time_t ttl, unsigned int count, Reason reason)
179
  {
220✔
180
    auto iter = d_cont.find(arg);
220✔
181
    time_t ttd = now + ttl;
220✔
182
    if (iter == d_cont.end()) {
220!
183
      d_cont.emplace(arg, ttd, count, reason);
220✔
184
    }
220✔
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
  }
220✔
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,946✔
214
    d_cont.erase(thing);
24,946✔
215
  }
24,946✔
216
  void prune(time_t now)
217
  {
128✔
218
    auto& ind = d_cont.template get<time_t>();
128✔
219
    ind.erase(ind.begin(), ind.upper_bound(now));
128✔
220
  }
128✔
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
  {
106✔
270
    auto& ind = get<time_t>();
106✔
271
    ind.erase(ind.begin(), ind.upper_bound(now));
106✔
272
  }
106✔
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,904✔
402
    g_log << Logger::Warning << x;   \
107,591✔
403
  }                                  \
107,591✔
404
  else if (d_lm == Store) {          \
1,162,904✔
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
{
75,944✔
411
  OptLog ret;
75,944✔
412
  if (d_lm == Log) {
75,944✔
413
    ret = {prefix, d_fixednow, g_log};
11,122✔
414
  }
11,122✔
415
  else if (d_lm == Store) {
64,822✔
416
    ret = {prefix, d_fixednow, d_trace};
98✔
417
  }
98✔
418
  return ret;
75,944✔
419
}
75,944✔
420

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

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

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

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

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

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

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

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

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

702
  // Avoid calling addAdditionals() if we know we won't find anything
703
  auto luaLocal = g_luaconfs.getLocal();
4,370✔
704
  if (res == 0 && qclass == QClass::IN && luaLocal->allowAdditionalQTypes.find(qtype) != luaLocal->allowAdditionalQTypes.end()) {
4,370!
705
    bool additionalsNotInCache = addAdditionals(qtype, ret, depth);
5✔
706
    if (additionalsNotInCache) {
5!
707
      d_wasVariable = true;
×
708
    }
×
709
  }
5✔
710
  traceScope.close(res);
4,370✔
711
  return res;
4,370✔
712
}
4,374✔
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,453✔
731
  static const DNSName arpa("1.0.0.127.in-addr.arpa.");
4,453✔
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,453✔
733
  static const DNSName localhost("localhost.");
4,453✔
734
  static const DNSName versionbind("version.bind.");
4,453✔
735
  static const DNSName idserver("id.server.");
4,453✔
736
  static const DNSName versionpdns("version.pdns.");
4,453✔
737
  static const DNSName trustanchorserver("trustanchor.server.");
4,453✔
738
  static const DNSName negativetrustanchorserver("negativetrustanchor.server.");
4,453✔
739

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

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

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

760
  if ((qname == versionbind || qname == idserver || qname == versionpdns) && qclass == QClass::CHAOS) {
4,453!
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,453!
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,453!
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,453!
810
    ret.clear();
46✔
811
    d_wasOutOfBand = true;
46✔
812

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

825
  return handled;
4,453✔
826
}
4,453✔
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
{
149✔
873
  int result = RCode::NoError;
149✔
874
  records.clear();
149✔
875

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

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

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

885
    if (qtype == QType::ANY || ziter->d_type == qtype || ziter->d_type == QType::CNAME) {
155✔
886
      // let rest of nameserver do the legwork on this one
887
      records.push_back(*ziter);
113✔
888
    }
113✔
889
    else if (ziter->d_type == QType::NS && ziter->d_name.countLabels() > getName().countLabels()) {
42!
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
  }
155✔
896

897
  if (!records.empty()) {
149✔
898
    /* We have found an exact match, we're done */
899
    return result;
110✔
900
  }
110✔
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
{
149✔
956
  d_authzonequeries++;
149✔
957
  t_Counters.at(rec::Counter::authzonequeries)++;
149✔
958

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

963
bool SyncRes::doOOBResolve(const DNSName& qname, const QType qtype, vector<DNSRecord>& ret, unsigned int /* depth */, const string& prefix, int& res)
964
{
150✔
965
  DNSName authdomain(qname);
150✔
966
  const auto iter = getBestAuthZone(&authdomain);
150✔
967
  if (iter == t_sstorage.domainmap->end() || !iter->second.isAuth()) {
150!
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);
149!
973
  return doOOBResolve(iter->second, qname, qtype, ret, res);
149✔
974
}
150✔
975

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

983
bool SyncRes::isRecursiveForward(const DNSName& qname)
984
{
14,586✔
985
  DNSName authname(qname);
14,586✔
986
  const auto iter = getBestAuthZone(&authname);
14,586✔
987
  return iter != t_sstorage.domainmap->end() && iter->second.shouldRecurse();
14,586!
988
}
14,586✔
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
static const char* timestamp(time_t arg, timebuf_t& buf)
1018
{
×
1019
  const std::string s_timestampFormat = "%Y-%m-%dT%T";
×
1020
  struct tm tmval{};
×
1021
  size_t len = strftime(buf.data(), buf.size(), s_timestampFormat.c_str(), localtime_r(&arg, &tmval));
×
1022
  if (len == 0) {
×
1023
    int ret = snprintf(buf.data(), buf.size(), "%lld", static_cast<long long>(arg));
×
1024
    if (ret < 0 || static_cast<size_t>(ret) >= buf.size()) {
×
1025
      buf[0] = '\0';
×
1026
    }
×
1027
  }
×
1028
  return buf.data();
×
1029
}
×
1030

1031
struct ednsstatus_t : public multi_index_container<SyncRes::EDNSStatus,
1032
                                                   indexed_by<
1033
                                                     ordered_unique<tag<ComboAddress>, member<SyncRes::EDNSStatus, ComboAddress, &SyncRes::EDNSStatus::address>>,
1034
                                                     ordered_non_unique<tag<time_t>, member<SyncRes::EDNSStatus, time_t, &SyncRes::EDNSStatus::ttd>>>>
1035
{
1036
  // Get a copy
1037
  [[nodiscard]] ednsstatus_t getMap() const
1038
  {
×
1039
    return *this;
×
1040
  }
×
1041

1042
  static void setMode(index<ComboAddress>::type& ind, iterator iter, SyncRes::EDNSStatus::EDNSMode mode, time_t theTime)
1043
  {
452✔
1044
    if (iter->mode != mode || iter->ttd == 0) {
452!
1045
      ind.modify(iter, [=](SyncRes::EDNSStatus& status) { status.mode = mode; status.ttd = theTime + Expire; });
112✔
1046
    }
112✔
1047
  }
452✔
1048

1049
  void prune(time_t now)
1050
  {
106✔
1051
    auto& ind = get<time_t>();
106✔
1052
    ind.erase(ind.begin(), ind.upper_bound(now));
106✔
1053
  }
106✔
1054

1055
  static const time_t Expire = 7200;
1056
};
1057

1058
static LockGuarded<ednsstatus_t> s_ednsstatus;
1059

1060
SyncRes::EDNSStatus::EDNSMode SyncRes::getEDNSStatus(const ComboAddress& server)
1061
{
70✔
1062
  auto lock = s_ednsstatus.lock();
70✔
1063
  const auto& iter = lock->find(server);
70✔
1064
  if (iter == lock->end()) {
70✔
1065
    return EDNSStatus::EDNSOK;
64✔
1066
  }
64✔
1067
  return iter->mode;
6✔
1068
}
70✔
1069

1070
uint64_t SyncRes::getEDNSStatusesSize()
1071
{
661✔
1072
  return s_ednsstatus.lock()->size();
661✔
1073
}
661✔
1074

1075
void SyncRes::clearEDNSStatuses()
1076
{
612✔
1077
  s_ednsstatus.lock()->clear();
612✔
1078
}
612✔
1079

1080
void SyncRes::pruneEDNSStatuses(time_t cutoff)
1081
{
106✔
1082
  s_ednsstatus.lock()->prune(cutoff);
106✔
1083
}
106✔
1084

1085
uint64_t SyncRes::doEDNSDump(int fileDesc)
1086
{
×
1087
  int newfd = dup(fileDesc);
×
1088
  if (newfd == -1) {
×
1089
    return 0;
×
1090
  }
×
1091
  auto filePtr = pdns::UniqueFilePtr(fdopen(newfd, "w"));
×
1092
  if (!filePtr) {
×
1093
    close(newfd);
×
1094
    return 0;
×
1095
  }
×
1096
  uint64_t count = 0;
×
1097

1098
  fprintf(filePtr.get(), "; edns dump follows\n; ip\tstatus\tttd\n");
×
1099
  const auto copy = s_ednsstatus.lock()->getMap();
×
1100
  for (const auto& eds : copy) {
×
1101
    count++;
×
1102
    timebuf_t tmp;
×
1103
    fprintf(filePtr.get(), "%s\t%s\t%s\n", eds.address.toString().c_str(), eds.toString().c_str(), timestamp(eds.ttd, tmp));
×
1104
  }
×
1105
  return count;
×
1106
}
×
1107

1108
void SyncRes::pruneNSSpeeds(time_t limit)
1109
{
106✔
1110
  auto lock = s_nsSpeeds.lock();
106✔
1111
  auto& ind = lock->get<timeval>();
106✔
1112
  ind.erase(ind.begin(), ind.upper_bound(timeval{limit, 0}));
106✔
1113
}
106✔
1114

1115
uint64_t SyncRes::getNSSpeedsSize()
1116
{
799✔
1117
  return s_nsSpeeds.lock()->size();
799✔
1118
}
799✔
1119

1120
void SyncRes::submitNSSpeed(const DNSName& server, const ComboAddress& address, int usec, const struct timeval& now)
1121
{
12✔
1122
  auto lock = s_nsSpeeds.lock();
12✔
1123
  lock->find_or_enter(server, now).submit(address, usec, now);
12✔
1124
}
12✔
1125

1126
void SyncRes::clearNSSpeeds()
1127
{
614✔
1128
  s_nsSpeeds.lock()->clear();
614✔
1129
}
614✔
1130

1131
float SyncRes::getNSSpeed(const DNSName& server, const ComboAddress& address)
1132
{
20✔
1133
  auto lock = s_nsSpeeds.lock();
20✔
1134
  return lock->find_or_enter(server).d_collection[address].peek();
20✔
1135
}
20✔
1136

1137
uint64_t SyncRes::doDumpNSSpeeds(int fileDesc)
1138
{
4✔
1139
  int newfd = dup(fileDesc);
4✔
1140
  if (newfd == -1) {
4!
1141
    return 0;
×
1142
  }
×
1143
  auto filePtr = pdns::UniqueFilePtr(fdopen(newfd, "w"));
4✔
1144
  if (!filePtr) {
4!
1145
    close(newfd);
×
1146
    return 0;
×
1147
  }
×
1148

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

1152
  // Create a copy to avoid holding the lock while doing I/O
1153
  for (const auto& iter : *s_nsSpeeds.lock()) {
64✔
1154
    count++;
64✔
1155

1156
    // an <empty> can appear hear in case of authoritative (hosted) zones
1157
    timebuf_t tmp;
64✔
1158
    fprintf(filePtr.get(), "%s\t%s\t", iter.d_name.toLogString().c_str(), isoDateTimeMillis(iter.d_lastget, tmp));
64✔
1159
    bool first = true;
64✔
1160
    for (const auto& line : iter.d_collection) {
64✔
1161
      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✔
1162
      first = false;
32✔
1163
    }
32✔
1164
    fprintf(filePtr.get(), "\n");
64✔
1165
  }
64✔
1166
  return count;
4✔
1167
}
4✔
1168

1169
uint64_t SyncRes::getThrottledServersSize()
1170
{
795✔
1171
  return s_throttle.lock()->size();
795✔
1172
}
795✔
1173

1174
void SyncRes::pruneThrottledServers(time_t now)
1175
{
128✔
1176
  s_throttle.lock()->prune(now);
128✔
1177
}
128✔
1178

1179
void SyncRes::clearThrottle()
1180
{
612✔
1181
  s_throttle.lock()->clear();
612✔
1182
}
612✔
1183

1184
bool SyncRes::isThrottled(time_t now, const ComboAddress& server, const DNSName& target, QType qtype)
1185
{
12,945✔
1186
  return s_throttle.lock()->shouldThrottle(now, std::tuple(server, target, qtype));
12,945✔
1187
}
12,945✔
1188

1189
bool SyncRes::isThrottled(time_t now, const ComboAddress& server)
1190
{
12,931✔
1191
  auto throttled = s_throttle.lock()->shouldThrottle(now, std::tuple(server, g_rootdnsname, 0));
12,931✔
1192
  if (throttled) {
12,931✔
1193
    // Give fully throttled servers a chance to be used, to avoid having one bad zone spoil the NS
1194
    // record for others using the same NS. If the NS answers, it will be unThrottled immediately
1195
    if (s_unthrottle_n > 0 && dns_random(s_unthrottle_n) == 0) {
24!
1196
      throttled = false;
×
1197
    }
×
1198
  }
24✔
1199
  return throttled;
12,931✔
1200
}
12,931✔
1201

1202
void SyncRes::unThrottle(const ComboAddress& server, const DNSName& name, QType qtype)
1203
{
12,473✔
1204
  s_throttle.lock()->clear(std::tuple(server, g_rootdnsname, 0));
12,473✔
1205
  s_throttle.lock()->clear(std::tuple(server, name, qtype));
12,473✔
1206
}
12,473✔
1207

1208
void SyncRes::doThrottle(time_t now, const ComboAddress& server, time_t duration, unsigned int tries, Throttle::Reason reason)
1209
{
6✔
1210
  s_throttle.lock()->throttle(now, std::tuple(server, g_rootdnsname, 0), duration, tries, reason);
6✔
1211
}
6✔
1212

1213
void SyncRes::doThrottle(time_t now, const ComboAddress& server, const DNSName& name, QType qtype, time_t duration, unsigned int tries, Throttle::Reason reason)
1214
{
214✔
1215
  s_throttle.lock()->throttle(now, std::tuple(server, name, qtype), duration, tries, reason);
214✔
1216
}
214✔
1217

1218
uint64_t SyncRes::doDumpThrottleMap(int fileDesc)
1219
{
×
1220
  int newfd = dup(fileDesc);
×
1221
  if (newfd == -1) {
×
1222
    return 0;
×
1223
  }
×
1224
  auto filePtr = pdns::UniqueFilePtr(fdopen(newfd, "w"));
×
1225
  if (!filePtr) {
×
1226
    close(newfd);
×
1227
    return 0;
×
1228
  }
×
1229
  fprintf(filePtr.get(), "; throttle map dump follows\n");
×
1230
  fprintf(filePtr.get(), "; remote IP\tqname\tqtype\tcount\tttd\treason\n");
×
1231
  uint64_t count = 0;
×
1232

1233
  // Get a copy to avoid holding the lock while doing I/O
1234
  const auto throttleMap = s_throttle.lock()->getThrottleMap();
×
1235
  for (const auto& iter : throttleMap) {
×
1236
    count++;
×
1237
    timebuf_t tmp;
×
1238
    // remote IP, dns name, qtype, count, ttd, reason
1239
    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());
×
1240
  }
×
1241

1242
  return count;
×
1243
}
×
1244

1245
uint64_t SyncRes::getFailedServersSize()
1246
{
795✔
1247
  return s_fails.lock()->size();
795✔
1248
}
795✔
1249

1250
void SyncRes::clearFailedServers()
1251
{
612✔
1252
  s_fails.lock()->clear();
612✔
1253
}
612✔
1254

1255
void SyncRes::pruneFailedServers(time_t cutoff)
1256
{
128✔
1257
  s_fails.lock()->prune(cutoff);
128✔
1258
}
128✔
1259

1260
unsigned long SyncRes::getServerFailsCount(const ComboAddress& server)
1261
{
74✔
1262
  return s_fails.lock()->value(server);
74✔
1263
}
74✔
1264

1265
uint64_t SyncRes::doDumpFailedServers(int fileDesc)
1266
{
×
1267
  int newfd = dup(fileDesc);
×
1268
  if (newfd == -1) {
×
1269
    return 0;
×
1270
  }
×
1271
  auto filePtr = pdns::UniqueFilePtr(fdopen(newfd, "w"));
×
1272
  if (!filePtr) {
×
1273
    close(newfd);
×
1274
    return 0;
×
1275
  }
×
1276
  fprintf(filePtr.get(), "; failed servers dump follows\n");
×
1277
  fprintf(filePtr.get(), "; remote IP\tcount\ttimestamp\n");
×
1278
  uint64_t count = 0;
×
1279

1280
  // We get a copy, so the I/O does not need to happen while holding the lock
1281
  for (const auto& iter : s_fails.lock()->getMapCopy()) {
×
1282
    count++;
×
1283
    timebuf_t tmp;
×
1284
    fprintf(filePtr.get(), "%s\t%" PRIu64 "\t%s\n", iter.key.toString().c_str(), iter.value, timestamp(iter.last, tmp));
×
1285
  }
×
1286

1287
  return count;
×
1288
}
×
1289

1290
uint64_t SyncRes::getNonResolvingNSSize()
1291
{
803✔
1292
  return s_nonresolving.lock()->size();
803✔
1293
}
803✔
1294

1295
void SyncRes::clearNonResolvingNS()
1296
{
612✔
1297
  s_nonresolving.lock()->clear();
612✔
1298
}
612✔
1299

1300
void SyncRes::pruneNonResolving(time_t cutoff)
1301
{
128✔
1302
  s_nonresolving.lock()->prune(cutoff);
128✔
1303
}
128✔
1304

1305
uint64_t SyncRes::doDumpNonResolvingNS(int fileDesc)
1306
{
×
1307
  int newfd = dup(fileDesc);
×
1308
  if (newfd == -1) {
×
1309
    return 0;
×
1310
  }
×
1311
  auto filePtr = pdns::UniqueFilePtr(fdopen(newfd, "w"));
×
1312
  if (!filePtr) {
×
1313
    close(newfd);
×
1314
    return 0;
×
1315
  }
×
1316
  fprintf(filePtr.get(), "; non-resolving nameserver dump follows\n");
×
1317
  fprintf(filePtr.get(), "; name\tcount\ttimestamp\n");
×
1318
  uint64_t count = 0;
×
1319

1320
  // We get a copy, so the I/O does not need to happen while holding the lock
1321
  for (const auto& iter : s_nonresolving.lock()->getMapCopy()) {
×
1322
    count++;
×
1323
    timebuf_t tmp;
×
1324
    fprintf(filePtr.get(), "%s\t%" PRIu64 "\t%s\n", iter.key.toString().c_str(), iter.value, timestamp(iter.last, tmp));
×
1325
  }
×
1326

1327
  return count;
×
1328
}
×
1329

1330
void SyncRes::clearSaveParentsNSSets()
1331
{
612✔
1332
  s_savedParentNSSet.lock()->clear();
612✔
1333
}
612✔
1334

1335
size_t SyncRes::getSaveParentsNSSetsSize()
1336
{
659✔
1337
  return s_savedParentNSSet.lock()->size();
659✔
1338
}
659✔
1339

1340
void SyncRes::pruneSaveParentsNSSets(time_t now)
1341
{
106✔
1342
  s_savedParentNSSet.lock()->prune(now);
106✔
1343
}
106✔
1344

1345
uint64_t SyncRes::doDumpSavedParentNSSets(int fileDesc)
1346
{
×
1347
  int newfd = dup(fileDesc);
×
1348
  if (newfd == -1) {
×
1349
    return 0;
×
1350
  }
×
1351
  auto filePtr = pdns::UniqueFilePtr(fdopen(newfd, "w"));
×
1352
  if (!filePtr) {
×
1353
    close(newfd);
×
1354
    return 0;
×
1355
  }
×
1356
  fprintf(filePtr.get(), "; dump of saved parent nameserver sets succesfully used follows\n");
×
1357
  fprintf(filePtr.get(), "; total entries: %zu\n", s_savedParentNSSet.lock()->size());
×
1358
  fprintf(filePtr.get(), "; domain\tsuccess\tttd\n");
×
1359
  uint64_t count = 0;
×
1360

1361
  // We get a copy, so the I/O does not need to happen while holding the lock
1362
  for (const auto& iter : s_savedParentNSSet.lock()->getMapCopy()) {
×
1363
    if (iter.d_count == 0) {
×
1364
      continue;
×
1365
    }
×
1366
    count++;
×
1367
    timebuf_t tmp;
×
1368
    fprintf(filePtr.get(), "%s\t%" PRIu64 "\t%s\n", iter.d_domain.toString().c_str(), iter.d_count, timestamp(iter.d_ttd, tmp));
×
1369
  }
×
1370
  return count;
×
1371
}
×
1372

1373
void SyncRes::pruneDoTProbeMap(time_t cutoff)
1374
{
×
1375
  auto lock = s_dotMap.lock();
×
1376
  auto& ind = lock->d_map.get<time_t>();
×
1377

1378
  for (auto i = ind.begin(); i != ind.end();) {
×
1379
    if (i->d_ttd >= cutoff) {
×
1380
      // We're done as we loop ordered by d_ttd
1381
      break;
×
1382
    }
×
1383
    if (i->d_status == DoTStatus::Status::Busy) {
×
1384
      lock->d_numBusy--;
×
1385
    }
×
1386
    i = ind.erase(i);
×
1387
  }
×
1388
}
×
1389

1390
uint64_t SyncRes::doDumpDoTProbeMap(int fileDesc)
1391
{
×
1392
  int newfd = dup(fileDesc);
×
1393
  if (newfd == -1) {
×
1394
    return 0;
×
1395
  }
×
1396
  auto filePtr = pdns::UniqueFilePtr(fdopen(newfd, "w"));
×
1397
  if (!filePtr) {
×
1398
    close(newfd);
×
1399
    return 0;
×
1400
  }
×
1401
  fprintf(filePtr.get(), "; DoT probing map follows\n");
×
1402
  fprintf(filePtr.get(), "; ip\tdomain\tcount\tstatus\tttd\n");
×
1403
  uint64_t count = 0;
×
1404

1405
  // We get a copy, so the I/O does not need to happen while holding the lock
1406
  DoTMap copy;
×
1407
  {
×
1408
    copy = *s_dotMap.lock();
×
1409
  }
×
1410
  fprintf(filePtr.get(), "; %" PRIu64 " Busy entries\n", copy.d_numBusy);
×
1411
  for (const auto& iter : copy.d_map) {
×
1412
    count++;
×
1413
    timebuf_t tmp;
×
1414
    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));
×
1415
  }
×
1416
  return count;
×
1417
}
×
1418

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

1423
   However, some hosts simply can't answer questions which ask for DNSSEC. This can manifest itself as:
1424
   * No answer
1425
   * FormErr
1426
   * Nonsense answer
1427

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

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

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

1440
LWResult::Result SyncRes::asyncresolveWrapper(const ComboAddress& address, bool ednsMANDATORY, const DNSName& domain, [[maybe_unused]] const DNSName& auth, int type, bool doTCP, bool sendRDQuery, struct timeval* now, boost::optional<Netmask>& srcmask, LWResult* res, bool* chained, const DNSName& nsName) const
1441
{
13,206✔
1442
  /* what is your QUEST?
1443
     the goal is to get as many remotes as possible on the best level of EDNS support
1444
     The levels are:
1445

1446
     1) EDNSOK: Honors EDNS0, absent from table
1447
     2) EDNSIGNORANT: Ignores EDNS0, gives replies without EDNS0
1448
     3) NOEDNS: Generates FORMERR on EDNS queries
1449

1450
     Everybody starts out assumed to be EDNSOK.
1451
     If EDNSOK, send out EDNS0
1452
        If you FORMERR us, go to NOEDNS,
1453
        If no EDNS in response, go to EDNSIGNORANT
1454
     If EDNSIGNORANT, keep on including EDNS0, see what happens
1455
        Same behaviour as EDNSOK
1456
     If NOEDNS, send bare queries
1457
  */
1458

1459
  // Read current status, defaulting to OK
1460
  SyncRes::EDNSStatus::EDNSMode mode = EDNSStatus::EDNSOK;
13,206✔
1461
  {
13,206✔
1462
    auto lock = s_ednsstatus.lock();
13,206✔
1463
    auto ednsstatus = lock->find(address); // does this include port? YES
13,206✔
1464
    if (ednsstatus != lock->end()) {
13,206✔
1465
      if (ednsstatus->ttd != 0 && ednsstatus->ttd < d_now.tv_sec) {
455!
1466
        lock->erase(ednsstatus);
×
1467
      }
×
1468
      else {
455✔
1469
        mode = ednsstatus->mode;
455✔
1470
      }
455✔
1471
    }
455✔
1472
  }
13,206✔
1473

1474
  int EDNSLevel = 0;
13,206✔
1475
  auto luaconfsLocal = g_luaconfs.getLocal();
13,206✔
1476
  ResolveContext ctx(d_initialRequestId, nsName);
13,206✔
1477
#ifdef HAVE_FSTRM
11,895✔
1478
  ctx.d_auth = auth;
11,895✔
1479
#endif
11,895✔
1480

1481
  LWResult::Result ret{};
13,206✔
1482

1483
  for (int tries = 0; tries < 2; ++tries) {
13,209!
1484

1485
    if (mode == EDNSStatus::NOEDNS) {
13,209✔
1486
      t_Counters.at(rec::Counter::noEdnsOutQueries)++;
3✔
1487
      EDNSLevel = 0; // level != mode
3✔
1488
    }
3✔
1489
    else if (ednsMANDATORY || mode != EDNSStatus::NOEDNS) {
13,206!
1490
      EDNSLevel = 1;
13,206✔
1491
    }
13,206✔
1492

1493
    DNSName sendQname(domain);
13,209✔
1494
    if (g_lowercaseOutgoing) {
13,209✔
1495
      sendQname.makeUsLowerCase();
8✔
1496
    }
8✔
1497

1498
    if (d_asyncResolve) {
13,209✔
1499
      ret = d_asyncResolve(address, sendQname, type, doTCP, sendRDQuery, EDNSLevel, now, srcmask, ctx, res, chained);
2,525✔
1500
    }
2,525✔
1501
    else {
10,684✔
1502
      ret = asyncresolve(address, sendQname, type, doTCP, sendRDQuery, EDNSLevel, now, srcmask, ctx, d_outgoingProtobufServers, d_frameStreamServers, luaconfsLocal->outgoingProtobufExportConfig.exportTypes, res, chained);
10,684✔
1503
    }
10,684✔
1504

1505
    if (ret == LWResult::Result::PermanentError || LWResult::isLimitError(ret) || ret == LWResult::Result::Spoofed) {
13,209✔
1506
      break; // transport error, nothing to learn here
76✔
1507
    }
76✔
1508

1509
    if (ret == LWResult::Result::Timeout) { // timeout, not doing anything with it now
13,133✔
1510
      break;
324✔
1511
    }
324✔
1512

1513
    if (EDNSLevel == 1) {
12,809✔
1514
      // We sent out with EDNS
1515
      // ret is LWResult::Result::Success
1516
      // ednsstatus in table might be pruned or changed by another request/thread, so do a new lookup/insert if needed
1517
      auto lock = s_ednsstatus.lock(); // all three branches below need a lock
12,804✔
1518

1519
      // Determine new mode
1520
      if (res->d_validpacket && !res->d_haveEDNS && res->d_rcode == RCode::FormErr) {
12,804✔
1521
        mode = EDNSStatus::NOEDNS;
3✔
1522
        auto ednsstatus = lock->insert(address).first;
3✔
1523
        auto& ind = lock->get<ComboAddress>();
3✔
1524
        lock->setMode(ind, ednsstatus, mode, d_now.tv_sec);
3✔
1525
        // This is the only path that re-iterates the loop
1526
        continue;
3✔
1527
      }
3✔
1528
      if (!res->d_haveEDNS) {
12,801✔
1529
        auto ednsstatus = lock->insert(address).first;
449✔
1530
        auto& ind = lock->get<ComboAddress>();
449✔
1531
        lock->setMode(ind, ednsstatus, EDNSStatus::EDNSIGNORANT, d_now.tv_sec);
449✔
1532
      }
449✔
1533
      else {
12,352✔
1534
        // New status is EDNSOK
1535
        lock->erase(address);
12,352✔
1536
      }
12,352✔
1537
    }
12,801✔
1538

1539
    break;
12,806✔
1540
  }
12,809✔
1541
  return ret;
13,206✔
1542
}
13,206✔
1543

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

1550
static unsigned int qmStepLen(unsigned int labels, unsigned int qnamelen, unsigned int qmIteration)
1551
{
18,262✔
1552
  unsigned int step{};
18,262✔
1553

1554
  if (qmIteration < SyncRes::s_minimize_one_label) {
18,262✔
1555
    step = 1;
18,174✔
1556
  }
18,174✔
1557
  else if (qmIteration < SyncRes::s_max_minimize_count) {
90✔
1558
    step = std::max(1U, (qnamelen - labels) / (SyncRes::s_max_minimize_count - qmIteration));
90✔
1559
  }
90✔
1560
  else {
2,147,483,647✔
1561
    step = qnamelen - labels;
2,147,483,647✔
1562
  }
2,147,483,647✔
1563
  unsigned int targetlen = std::min(labels + step, qnamelen);
18,262✔
1564
  return targetlen;
18,262✔
1565
}
18,262✔
1566

1567
static string resToString(int res)
1568
{
1,143✔
1569
  return res >= 0 ? RCode::to_s(res) : std::to_string(res);
1,143!
1570
}
1,143✔
1571

1572
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)
1573
{
22,648✔
1574
  auto prefix = getPrefix(depth);
22,648✔
1575
  auto luaconfsLocal = g_luaconfs.getLocal();
22,648✔
1576

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

1590
  initZoneCutsFromTA(qname, prefix);
22,586✔
1591

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

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

1612
  DNSName child;
20,047✔
1613
  prefix.append(string("QM "));
20,047✔
1614

1615
  LOG(prefix << qname << ": doResolve" << endl);
20,047✔
1616

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

1648
  const unsigned int qnamelen = qname.countLabels();
10,724✔
1649

1650
  DNSName fwdomain(qname);
10,724✔
1651
  const bool forwarded = getBestAuthZone(&fwdomain) != t_sstorage.domainmap->end();
10,724✔
1652
  if (forwarded) {
10,724✔
1653
    LOG(prefix << qname << ": Step0 qname is in a forwarded domain " << fwdomain << endl);
96!
1654
  }
96✔
1655

1656
  for (unsigned int i = 0; i <= qnamelen; i++) {
12,203✔
1657

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

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

1698
      while (labels < targetlen) {
32,934✔
1699
        child.prependRawLabel(qname.getRawLabel(qnamelen - labels - 1));
14,673✔
1700
        labels++;
14,673✔
1701
      }
14,673✔
1702
      // rfc9156 section-2.3, append labels if they start with an underscore
1703
      while (labels < qnamelen) {
18,261✔
1704
        auto prependLabel = qname.getRawLabel(qnamelen - labels - 1);
7,562✔
1705
        if (prependLabel.at(0) != '_') {
7,562!
1706
          break;
7,562✔
1707
        }
7,562✔
1708
        child.prependRawLabel(prependLabel);
×
1709
        labels++;
×
1710
      }
×
1711

1712
      LOG(prefix << qname << ": Step2 New child " << child << endl);
18,261✔
1713

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

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

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

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

1758
        auto oldEDE = context.extendedError;
23✔
1759
        res = doResolveNoQNameMinimization(qname, qtype, ret, depth + 1, beenthere, context);
23✔
1760

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

1771
        LOG(prefix << qname << ": Step5 End resolve: " << resToString(res) << "/" << ret.size() << endl);
23!
1772
        return res;
23✔
1773
      }
23✔
1774
    }
6,081✔
1775
  }
12,203✔
1776

1777
  // Should not be reached
1778
  LOG(prefix << qname << ": Max iterations reached, return ServFail" << endl);
2,147,483,647!
1779
  return RCode::ServFail;
2,147,483,647✔
1780
}
10,724✔
1781

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

1794
static bool haveFinalAnswer(const DNSName& qname, QType qtype, int res, const vector<DNSRecord>& ret)
1795
{
2,903✔
1796
  if (res != RCode::NoError) {
2,903✔
1797
    return false;
6✔
1798
  }
6✔
1799

1800
  // This loop assumes the CNAME's records are in-order
1801
  DNSName target(qname);
2,897✔
1802
  for (const auto& record : ret) {
5,526✔
1803
    if (record.d_place == DNSResourceRecord::ANSWER && record.d_name == target) {
5,526✔
1804
      if (record.d_type == qtype) {
5,509✔
1805
        return true;
75✔
1806
      }
75✔
1807
      if (record.d_type == QType::CNAME) {
5,434!
1808
        if (auto ptr = getRR<CNAMERecordContent>(record)) {
5,435✔
1809
          target = ptr->getTarget();
5,435✔
1810
        }
5,435✔
1811
        else {
2,147,483,647✔
1812
          return false;
2,147,483,647✔
1813
        }
2,147,483,647✔
1814
      }
5,434✔
1815
    }
5,434✔
1816
  }
5,526✔
1817
  return false;
2,823✔
1818
}
2,897✔
1819

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

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

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

1849
  int res = 0;
42,473✔
1850

1851
  const int iterations = !d_refresh && MemRecursorCache::s_maxServedStaleExtensions > 0 ? 2 : 1;
42,473✔
1852
  for (int loop = 0; loop < iterations; loop++) {
2,147,506,434✔
1853

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

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

1877
      bool wasForwardedOrAuthZone = false;
41,995✔
1878
      bool wasAuthZone = false;
41,995✔
1879
      bool wasForwardRecurse = false;
41,995✔
1880

1881
      if (iter != t_sstorage.domainmap->end()) {
41,995✔
1882
        wasForwardedOrAuthZone = true;
754✔
1883

1884
        if (iter->second.isAuth()) {
754✔
1885
          wasAuthZone = true;
302✔
1886
        }
302✔
1887
        else if (iter->second.shouldRecurse()) {
452✔
1888
          wasForwardRecurse = true;
99✔
1889
        }
99✔
1890
      }
754✔
1891

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

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

1935
      if (doCacheCheck(qname, authname, wasForwardedOrAuthZone, wasAuthZone, wasForwardRecurse, qtype, ret, depth, prefix, res, context)) {
35,967✔
1936
        // we done
1937
        d_wasOutOfBand = wasAuthZone;
10,672✔
1938
        if (fromCache != nullptr) {
10,672✔
1939
          *fromCache = true;
9,236✔
1940
        }
9,236✔
1941

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

1951
        return res;
10,672✔
1952
      }
10,672✔
1953

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

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

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

1988
    if (d_cacheonly) {
25,763✔
1989
      return 0;
14,884✔
1990
    }
14,884✔
1991

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

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

2000
    DNSName subdomain(qname);
10,875✔
2001
    if (qtype == QType::DS) {
10,875✔
2002
      subdomain.chopOff();
216✔
2003
    }
216✔
2004

2005
    NsSet nsset;
10,875✔
2006
    bool flawedNSSet = false;
10,875✔
2007

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

2014
    res = doResolveAt(nsset, subdomain, flawedNSSet, qname, qtype, ret, depth, prefix, beenthere, context, stopAtDelegation, nullptr);
10,875✔
2015

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

2051
    if (res == 0) {
10,877✔
2052
      return 0;
10,243✔
2053
    }
10,243✔
2054

2055
    LOG(prefix << qname << ": Failed (res=" << res << ")" << endl);
2,147,484,271✔
2056
    if (res >= 0) {
2,147,484,271✔
2057
      break;
190✔
2058
    }
190✔
2059
  }
2,147,484,271✔
2060
  return res < 0 ? RCode::ServFail : res;
2,147,484,251✔
2061
}
42,473✔
2062

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

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

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

2099
  if (ret.size() > 1) {
12,695✔
2100
    shuffle(ret.begin(), ret.end(), pdns::dns_random_engine());
1,456✔
2101
    speedOrderCA speedOrder(speeds);
1,456✔
2102
    stable_sort(ret.begin(), ret.end(), speedOrder);
1,456✔
2103
  }
1,456✔
2104

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

2121
template <typename T>
2122
static bool collectAddresses(const vector<DNSRecord>& cset, vector<ComboAddress>& ret)
2123
{
13,953✔
2124
  bool pushed = false;
13,953✔
2125
  for (const auto& record : cset) {
14,225✔
2126
    if (auto rec = getRR<T>(record)) {
14,223✔
2127
      ret.push_back(rec->getCA(53));
14,088✔
2128
      pushed = true;
14,088✔
2129
    }
14,088✔
2130
  }
14,223✔
2131
  return pushed;
13,953✔
2132
}
13,953✔
2133

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

2142
  bool oldCacheOnly = setCacheOnly(cacheOnly);
12,785✔
2143
  bool oldRequireAuthData = d_requireAuthData;
12,785✔
2144
  bool oldValidationRequested = d_DNSSECValidationRequested;
12,785✔
2145
  bool oldFollowCNAME = d_followCNAME;
12,785✔
2146
  bool seenV6 = false;
12,785✔
2147
  const unsigned int startqueries = d_outqueries;
12,785✔
2148
  d_requireAuthData = false;
12,785✔
2149
  d_DNSSECValidationRequested = false;
12,785✔
2150
  d_followCNAME = false;
12,785✔
2151

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

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

2216
  if (s_max_busy_dot_probes > 0 && s_dot_to_port_853) {
12,695!
2217
    for (auto& add : ret) {
×
2218
      if (shouldDoDoT(add, d_now.tv_sec)) {
×
2219
        add.setPort(853);
×
2220
      }
×
2221
    }
×
2222
  }
×
2223
  selectNSOnSpeed(qname, prefix, ret);
12,695✔
2224
  return ret;
12,695✔
2225
}
12,785✔
2226

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

2245
    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,353✔
2246
      if (s_maxnsperresolve > 0 && nsVector.size() > s_maxnsperresolve) {
22,989✔
2247
        vector<DNSRecord> selected;
20✔
2248
        selected.reserve(s_maxnsperresolve);
20✔
2249
        std::sample(nsVector.cbegin(), nsVector.cend(), std::back_inserter(selected), s_maxnsperresolve, pdns::dns_random_engine());
20✔
2250
        nsVector = std::move(selected);
20✔
2251
      }
20✔
2252
      bestns.reserve(nsVector.size());
22,989✔
2253

2254
      vector<DNSName> missing;
22,989✔
2255
      for (const auto& nsRecord : nsVector) {
134,514✔
2256
        if (nsRecord.d_ttl > (unsigned int)d_now.tv_sec) {
134,515✔
2257
          vector<DNSRecord> aset;
134,515✔
2258
          QType nsqt{QType::ADDR};
134,515✔
2259
          if (s_doIPv4 && !s_doIPv6) {
134,516✔
2260
            nsqt = QType::A;
90,491✔
2261
          }
90,491✔
2262
          else if (!s_doIPv4 && s_doIPv6) {
2,147,527,669!
2263
            nsqt = QType::AAAA;
64✔
2264
          }
64✔
2265

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

2312
      if (!bestns.empty()) {
22,989✔
2313
        GetBestNSAnswer answer;
22,952✔
2314
        answer.qname = qname;
22,952✔
2315
        answer.qtype = qtype.getCode();
22,952✔
2316
        for (const auto& bestNSRecord : bestns) {
134,196✔
2317
          if (auto nsContent = getRR<NSRecordContent>(bestNSRecord)) {
134,202✔
2318
            answer.bestns.emplace(bestNSRecord.d_name, nsContent->getNS());
134,199✔
2319
          }
134,199✔
2320
        }
134,196✔
2321

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

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

2356
SyncRes::domainmap_t::const_iterator SyncRes::getBestAuthZone(DNSName* qname)
2357
{
146,362✔
2358
  if (t_sstorage.domainmap->empty()) {
146,362✔
2359
    return t_sstorage.domainmap->end();
26,334✔
2360
  }
26,334✔
2361

2362
  SyncRes::domainmap_t::const_iterator ret;
120,028✔
2363
  do {
428,326✔
2364
    ret = t_sstorage.domainmap->find(*qname);
428,326✔
2365
    if (ret != t_sstorage.domainmap->end()) {
428,326✔
2366
      break;
2,217✔
2367
    }
2,217✔
2368
  } while (qname->chopOff());
428,326✔
2369
  return ret;
×
2370
}
146,362✔
2371

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

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

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

2401
  // Pick up the auth domain
2402
  for (const auto& nsRecord : bestns) {
10,684✔
2403
    const auto nsContent = getRR<NSRecordContent>(nsRecord);
10,574✔
2404
    if (nsContent) {
10,574!
2405
      nsFromCacheDomain = nsRecord.d_name;
10,574✔
2406
      break;
10,574✔
2407
    }
10,574✔
2408
  }
10,574✔
2409

2410
  if (iter != t_sstorage.domainmap->end()) {
10,684✔
2411
    if (doLog()) {
108✔
2412
      LOG(prefix << qname << " authOrForwDomain: " << authOrForwDomain << " nsFromCacheDomain: " << nsFromCacheDomain << " isPartof: " << authOrForwDomain.isPartOf(nsFromCacheDomain) << endl);
96!
2413
    }
96✔
2414

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

2438
void SyncRes::updateValidationStatusInCache(const DNSName& qname, const QType qtype, bool aaFlag, vState newState) const
2439
{
68✔
2440
  if (qtype == QType::ANY || qtype == QType::ADDR) {
68!
2441
    // not doing that
2442
    return;
×
2443
  }
×
2444

2445
  if (vStateIsBogus(newState)) {
68✔
2446
    g_recCache->updateValidationStatus(d_now.tv_sec, qname, qtype, d_cacheRemote, d_routingTag, aaFlag, newState, s_maxbogusttl + d_now.tv_sec);
20✔
2447
  }
20✔
2448
  else {
48✔
2449
    g_recCache->updateValidationStatus(d_now.tv_sec, qname, qtype, d_cacheRemote, d_routingTag, aaFlag, newState, boost::none);
48✔
2450
  }
48✔
2451
}
68✔
2452

2453
static pair<bool, unsigned int> scanForCNAMELoop(const DNSName& name, const vector<DNSRecord>& records)
2454
{
7,338✔
2455
  unsigned int numCNames = 0;
7,338✔
2456
  for (const auto& record : records) {
14,015✔
2457
    if (record.d_type == QType::CNAME && record.d_place == DNSResourceRecord::ANSWER) {
14,015!
2458
      ++numCNames;
13,820✔
2459
      if (name == record.d_name) {
13,820✔
2460
        return {true, numCNames};
6✔
2461
      }
6✔
2462
    }
13,820✔
2463
  }
14,015✔
2464
  return {false, numCNames};
7,332✔
2465
}
7,338✔
2466

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

2478
  /* we don't require auth data for forward-recurse lookups */
2479
  MemRecursorCache::Flags flags = MemRecursorCache::None;
36,617✔
2480
  if (!wasForwardRecurse && d_requireAuthData) {
36,618✔
2481
    flags |= MemRecursorCache::RequireAuth;
34,903✔
2482
  }
34,903✔
2483
  if (d_refresh) {
36,617✔
2484
    flags |= MemRecursorCache::Refresh;
301✔
2485
  }
301✔
2486
  if (d_serveStale) {
36,617✔
2487
    flags |= MemRecursorCache::ServeStale;
20✔
2488
  }
20✔
2489
  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) {
2,147,504,556✔
2490
    foundName = qname;
6,091✔
2491
    foundQT = QType::CNAME;
6,091✔
2492
  }
6,091✔
2493

2494
  if (foundName.empty() && qname != g_rootdnsname) {
36,617✔
2495
    // look for a DNAME cache hit
2496
    auto labels = qname.getRawLabels();
29,388✔
2497
    DNSName dnameName(g_rootdnsname);
29,388✔
2498

2499
    do {
77,643✔
2500
      dnameName.prependRawLabel(labels.back());
77,643✔
2501
      labels.pop_back();
77,643✔
2502
      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,643✔
2503
        break;
29,372✔
2504
      }
29,372✔
2505
      if (g_recCache->get(d_now.tv_sec, dnameName, QType::DNAME, flags, &cset, d_cacheRemote, d_routingTag, d_doDNSSEC ? &signatures : nullptr, d_doDNSSEC ? &authorityRecs : nullptr, &d_wasVariable, &context.state, &wasAuth, &authZone, &d_fromAuthIP) > 0) {
2,147,503,599✔
2506
        foundName = std::move(dnameName);
16✔
2507
        foundQT = QType::DNAME;
16✔
2508
        break;
16✔
2509
      }
16✔
2510
    } while (!labels.empty());
48,271✔
2511
  }
29,388✔
2512

2513
  if (foundName.empty()) {
36,617✔
2514
    return false;
30,510✔
2515
  }
30,510✔
2516

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

2524
  for (auto const& record : cset) {
6,104✔
2525
    if (record.d_class != QClass::IN) {
6,104!
2526
      continue;
×
2527
    }
×
2528

2529
    if (record.d_ttl > (unsigned int)d_now.tv_sec) {
6,104!
2530

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

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

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

2550
      DNSRecord dnsRecord = record;
6,104✔
2551
      auto alreadyPresent = false;
6,104✔
2552

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

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

2581
        for (const auto& rec : *authorityRecs) {
6,101!
2582
          DNSRecord authDR(rec);
×
2583
          authDR.d_ttl = ttl;
×
2584
          ret.push_back(std::move(authDR));
×
2585
        }
×
2586
      }
6,101✔
2587

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

2614
        LOG(prefix << qname << ": Synthesized " << dnsRecord.d_name << "|CNAME " << newTarget << endl);
14!
2615
      }
14✔
2616

2617
      if (qtype == QType::CNAME) { // perhaps they really wanted a CNAME!
6,102✔
2618
        res = RCode::NoError;
2✔
2619
        return true;
2✔
2620
      }
2✔
2621

2622
      if (qtype == QType::DS || qtype == QType::DNSKEY) {
6,100!
2623
        res = RCode::NoError;
18✔
2624
        return true;
18✔
2625
      }
18✔
2626

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

2637
      if (qname == newTarget) {
6,082✔
2638
        string msg = "Got a CNAME referral (from cache) to self";
2✔
2639
        LOG(prefix << qname << ": " << msg << endl);
2!
2640
        throw ImmediateServFailException(std::move(msg));
2✔
2641
      }
2✔
2642

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

2650
      if (!d_followCNAME) {
6,080✔
2651
        res = RCode::NoError;
164✔
2652
        return true;
164✔
2653
      }
164✔
2654

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

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

2677
      return true;
5,908✔
2678
    }
5,912✔
2679
  }
6,104✔
2680
  throw ImmediateServFailException("Could not determine whether or not there was a CNAME or DNAME in cache for '" + qname.toLogString() + "'");
2,147,483,647✔
2681
}
6,103✔
2682

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

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

2720
static bool negativeCacheEntryHasSOA(const NegCache::NegCacheEntry& negEntry)
2721
{
16✔
2722
  return !negEntry.authoritySOA.records.empty();
16✔
2723
}
16✔
2724

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

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

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

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

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

2768
    const DNSName& owner = entry.first.name;
12✔
2769

2770
    vState recordState = getValidationStatus(owner, !entry.second.signatures.empty(), qtype == QType::DS, depth, prefix);
12✔
2771
    if (state == vState::Indeterminate) {
12✔
2772
      state = recordState;
8✔
2773
    }
8✔
2774

2775
    if (recordState == vState::Secure) {
12✔
2776
      recordState = SyncRes::validateRecordsWithSigs(depth, prefix, qname, qtype, owner, QType(entry.first.type), entry.second.records, entry.second.signatures);
10✔
2777
    }
10✔
2778

2779
    if (recordState != vState::Indeterminate && recordState != state) {
12!
2780
      updateValidationState(qname, state, recordState, prefix);
×
2781
      if (state != vState::Secure) {
×
2782
        break;
×
2783
      }
×
2784
    }
×
2785
  }
12✔
2786

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

2803
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)
2804
{
35,918✔
2805
  bool giveNegative = false;
35,918✔
2806

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

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

2886
  if (giveNegative) {
35,918✔
2887

2888
    context.state = cachedState;
3,283✔
2889

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

2894
      if (context.state != cachedState && vStateIsBogus(context.state)) {
8!
2895
        sttl = std::min(sttl, s_maxbogusttl);
2✔
2896
      }
2✔
2897
    }
8✔
2898

2899
    // Transplant SOA to the returned packet
2900
    addTTLModifiedRecords(negEntry.authoritySOA.records, sttl, ret);
3,283✔
2901
    if (d_doDNSSEC) {
3,283✔
2902
      addTTLModifiedRecords(negEntry.authoritySOA.signatures, sttl, ret);
3,244✔
2903
      addTTLModifiedRecords(negEntry.DNSSECRecords.records, sttl, ret);
3,244✔
2904
      addTTLModifiedRecords(negEntry.DNSSECRecords.signatures, sttl, ret);
3,244✔
2905
    }
3,244✔
2906

2907
    LOG(prefix << qname << ": Updating validation state with negative cache content for " << qname << " to " << context.state << endl);
3,283!
2908
    return true;
3,283✔
2909
  }
3,283✔
2910

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

2931
    LOG(prefix << sqname << ": Found cache hit for " << sqt.toString() << ": ");
7,345✔
2932

2933
    if (!wasAuthZone && shouldValidate() && (wasCachedAuth || wasForwardRecurse) && cachedState == vState::Indeterminate && d_requireAuthData) {
7,345!
2934

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

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

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

2969
      if (cachedState != vState::Indeterminate) {
57!
2970
        LOG(prefix << qname << ": Got vState::Indeterminate state from the cache, validation result is " << cachedState << endl);
57!
2971
        if (vStateIsBogus(cachedState)) {
57✔
2972
          capTTL = s_maxbogusttl;
10✔
2973
        }
10✔
2974
        if (sqt != QType::ANY && sqt != QType::ADDR) {
57!
2975
          updateValidationStatusInCache(sqname, sqt, wasCachedAuth, cachedState);
54✔
2976
        }
54✔
2977
      }
57✔
2978
    }
57✔
2979

2980
    for (auto j = cset.cbegin(); j != cset.cend(); ++j) {
17,960✔
2981

2982
      LOG(j->getContent()->getZoneRepresentation());
10,615✔
2983

2984
      if (j->d_class != QClass::IN) {
10,615!
2985
        continue;
×
2986
      }
×
2987

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

3003
    ret.reserve(ret.size() + signatures->size() + authorityRecs->size());
7,345✔
3004

3005
    for (const auto& signature : *signatures) {
7,345✔
3006
      DNSRecord dnsRecord;
6,824✔
3007
      dnsRecord.d_type = QType::RRSIG;
6,824✔
3008
      dnsRecord.d_name = sqname;
6,824✔
3009
      dnsRecord.d_ttl = ttl;
6,824✔
3010
      dnsRecord.setContent(signature);
6,824✔
3011
      dnsRecord.d_place = DNSResourceRecord::ANSWER;
6,824✔
3012
      dnsRecord.d_class = QClass::IN;
6,824✔
3013
      ret.push_back(std::move(dnsRecord));
6,824✔
3014
    }
6,824✔
3015

3016
    for (const auto& rec : *authorityRecs) {
7,345✔
3017
      DNSRecord dnsRecord(rec);
34✔
3018
      dnsRecord.d_ttl = ttl;
34✔
3019
      ret.push_back(std::move(dnsRecord));
34✔
3020
    }
34✔
3021

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

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

3045
  return false;
25,246✔
3046
}
25,290✔
3047

3048
bool SyncRes::moreSpecificThan(const DNSName& lhs, const DNSName& rhs)
3049
{
14,734✔
3050
  return (lhs.isPartOf(rhs) && lhs.countLabels() > rhs.countLabels());
14,734✔
3051
}
14,734✔
3052

3053
struct speedOrder
3054
{
3055
  bool operator()(const std::pair<DNSName, float>& lhs, const std::pair<DNSName, float>& rhs) const
3056
  {
149,317✔
3057
    return lhs.second < rhs.second;
149,317✔
3058
  }
149,317✔
3059
};
3060

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

3073
  shuffle(rnameservers.begin(), rnameservers.end(), pdns::dns_random_engine());
12,546✔
3074
  speedOrder speedCompare;
12,546✔
3075
  stable_sort(rnameservers.begin(), rnameservers.end(), speedCompare);
12,546✔
3076

3077
  if (doLog()) {
12,546✔
3078
    LOG(prefix << qname << ": Nameservers: ");
1,550!
3079
    for (auto i = rnameservers.begin(); i != rnameservers.end(); ++i) {
4,182✔
3080
      if (i != rnameservers.begin()) {
2,632✔
3081
        LOG(", ");
1,082!
3082
        if (((i - rnameservers.begin()) % 3) == 0) {
1,082✔
3083
          LOG(endl
197!
3084
              << prefix << "             ");
197✔
3085
        }
197✔
3086
      }
1,082✔
3087
      LOG(i->first.toLogString() << "(" << fmtfloat(i->second / 1000.0) << "ms)");
2,632!
3088
    }
2,632✔
3089
    LOG(endl);
1,550!
3090
  }
1,550✔
3091
  return rnameservers;
12,546✔
3092
}
12,848✔
3093

3094
vector<ComboAddress> SyncRes::shuffleForwardSpeed(const DNSName& qname, const vector<ComboAddress>& rnameservers, const string& prefix, const bool wasRd)
3095
{
166✔
3096
  vector<ComboAddress> nameservers = rnameservers;
166✔
3097
  map<ComboAddress, float> speeds;
166✔
3098

3099
  for (const auto& val : nameservers) {
194✔
3100
    DNSName nsName = DNSName(val.toStringWithPort());
194✔
3101
    float speed = s_nsSpeeds.lock()->fastest(nsName, d_now);
194✔
3102
    speeds[val] = speed;
194✔
3103
  }
194✔
3104
  shuffle(nameservers.begin(), nameservers.end(), pdns::dns_random_engine());
166✔
3105
  speedOrderCA speedCompare(speeds);
166✔
3106
  stable_sort(nameservers.begin(), nameservers.end(), speedCompare);
166✔
3107

3108
  if (doLog()) {
166✔
3109
    LOG(prefix << qname << ": Nameservers: ");
108!
3110
    for (auto i = nameservers.cbegin(); i != nameservers.cend(); ++i) {
240✔
3111
      if (i != nameservers.cbegin()) {
132✔
3112
        LOG(", ");
24!
3113
        if (((i - nameservers.cbegin()) % 3) == 0) {
24!
3114
          LOG(endl
×
3115
              << prefix << "             ");
×
3116
        }
×
3117
      }
24✔
3118
      LOG((wasRd ? string("+") : string("-")) << i->toStringWithPort() << "(" << fmtfloat(speeds[*i] / 1000.0) << "ms)");
132!
3119
    }
132✔
3120
    LOG(endl);
108!
3121
  }
108✔
3122
  return nameservers;
166✔
3123
}
166✔
3124

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

3135
static const set<QType> nsecTypes = {QType::NSEC, QType::NSEC3};
3136

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

3153
    if (rec.d_type == QType::RRSIG) {
35,137✔
3154
      auto rrsig = getRR<RRSIGRecordContent>(rec);
8,699✔
3155
      if (rrsig) {
8,699!
3156
        if (rrsig->d_type == QType::SOA) {
8,699✔
3157
          negEntry.authoritySOA.signatures.push_back(rec);
569✔
3158
          if (lowestTTL != nullptr && isRRSIGNotExpired(now, *rrsig)) {
569✔
3159
            *lowestTTL = min(*lowestTTL, rec.d_ttl);
283✔
3160
            *lowestTTL = min(*lowestTTL, getRRSIGTTL(now, rrsig));
283✔
3161
          }
283✔
3162
        }
569✔
3163
        if (nsecTypes.count(rrsig->d_type) != 0) {
8,699✔
3164
          negEntry.DNSSECRecords.signatures.push_back(rec);
8,131✔
3165
          if (lowestTTL != nullptr && isRRSIGNotExpired(now, *rrsig)) {
8,131✔
3166
            *lowestTTL = min(*lowestTTL, rec.d_ttl);
7,727✔
3167
            *lowestTTL = min(*lowestTTL, getRRSIGTTL(now, rrsig));
7,727✔
3168
          }
7,727✔
3169
        }
8,131✔
3170
      }
8,699✔
3171
      continue;
8,699✔
3172
    }
8,699✔
3173
    if (rec.d_type == QType::SOA) {
26,438✔
3174
      negEntry.authoritySOA.records.push_back(rec);
2,912✔
3175
      if (lowestTTL != nullptr) {
2,912✔
3176
        *lowestTTL = min(*lowestTTL, rec.d_ttl);
1,501✔
3177
      }
1,501✔
3178
      continue;
2,912✔
3179
    }
2,912✔
3180
    if (nsecTypes.count(rec.d_type) != 0) {
23,526✔
3181
      negEntry.DNSSECRecords.records.push_back(rec);
8,143✔
3182
      if (lowestTTL != nullptr) {
8,143✔
3183
        *lowestTTL = min(*lowestTTL, rec.d_ttl);
7,742✔
3184
      }
7,742✔
3185
      continue;
8,143✔
3186
    }
8,143✔
3187
  }
23,526✔
3188
}
6,973✔
3189

3190
static cspmap_t harvestCSPFromNE(const NegCache::NegCacheEntry& negEntry)
3191
{
2,981✔
3192
  cspmap_t cspmap;
2,981✔
3193
  for (const auto& rec : negEntry.DNSSECRecords.signatures) {
5,131✔
3194
    if (rec.d_type == QType::RRSIG) {
5,131✔
3195
      auto rrc = getRR<RRSIGRecordContent>(rec);
5,129✔
3196
      if (rrc) {
5,131✔
3197
        cspmap[{rec.d_name, rrc->d_type}].signatures.push_back(rrc);
5,131✔
3198
      }
5,131✔
3199
    }
5,129✔
3200
  }
5,131✔
3201
  for (const auto& rec : negEntry.DNSSECRecords.records) {
5,133✔
3202
    cspmap[{rec.d_name, rec.d_type}].records.insert(rec.getContent());
5,133✔
3203
  }
5,133✔
3204
  return cspmap;
2,981✔
3205
}
2,981✔
3206

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

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

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

3238
  return true;
60✔
3239
}
63✔
3240

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

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

3258
    if (remove) {
6✔
3259
      it = records.erase(it);
2✔
3260
    }
2✔
3261
    else {
4✔
3262
      ++it;
4✔
3263
    }
4✔
3264
  }
6✔
3265
}
67✔
3266

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

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

3281
  if (d_appliedPolicy.d_type != DNSFilterEngine::PolicyType::None) {
105!
3282
    LOG(prefix << qname << "|" << qtype << ':' << d_appliedPolicy.getLogString() << endl);
105!
3283
  }
105✔
3284

3285
  switch (d_appliedPolicy.d_kind) {
105!
3286

3287
  case DNSFilterEngine::PolicyKind::NoAction:
22✔
3288
    return;
22✔
3289

3290
  case DNSFilterEngine::PolicyKind::Drop:
6✔
3291
    ++t_Counters.at(rec::Counter::policyDrops);
6✔
3292
    throw ImmediateQueryDropException();
6✔
3293

3294
  case DNSFilterEngine::PolicyKind::NXDOMAIN:
4✔
3295
    ret.clear();
4✔
3296
    d_appliedPolicy.addSOAtoRPZResult(ret);
4✔
3297
    rcode = RCode::NXDomain;
4✔
3298
    done = true;
4✔
3299
    return;
4✔
3300

3301
  case DNSFilterEngine::PolicyKind::NODATA:
4✔
3302
    ret.clear();
4✔
3303
    d_appliedPolicy.addSOAtoRPZResult(ret);
4✔
3304
    rcode = RCode::NoError;
4✔
3305
    done = true;
4✔
3306
    return;
4✔
3307

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

3318
  case DNSFilterEngine::PolicyKind::Custom: {
63✔
3319
    if (rpzHitShouldReplaceContent(qname, qtype, ret)) {
63✔
3320
      ret.clear();
60✔
3321
    }
60✔
3322

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

3330
    for (auto& dnsRecord : spoofed) {
67✔
3331
      ret.push_back(dnsRecord);
67✔
3332

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

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

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

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

3401
static bool shouldNotThrottle(const DNSName* name, const ComboAddress* address)
3402
{
13,033✔
3403
  if (name != nullptr) {
13,033✔
3404
    auto dontThrottleNames = g_dontThrottleNames.getLocal();
13,031✔
3405
    if (dontThrottleNames->check(*name)) {
13,031!
3406
      return true;
×
3407
    }
×
3408
  }
13,031✔
3409
  if (address != nullptr) {
13,033✔
3410
    auto dontThrottleNetmasks = g_dontThrottleNetmasks.getLocal();
13,000✔
3411
    if (dontThrottleNetmasks->match(*address)) {
13,000✔
3412
      return true;
4✔
3413
    }
4✔
3414
  }
13,000✔
3415
  return false;
13,029✔
3416
}
13,033✔
3417

3418
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)
3419
{
12,938✔
3420
  vector<ComboAddress> result;
12,938✔
3421

3422
  size_t nonresolvingfails = 0;
12,938✔
3423
  if (!tns->first.empty()) {
12,938✔
3424
    if (s_nonresolvingnsmaxfails > 0) {
12,772✔
3425
      nonresolvingfails = s_nonresolving.lock()->value(tns->first);
10,290✔
3426
      if (nonresolvingfails >= s_nonresolvingnsmaxfails) {
10,290✔
3427
        LOG(prefix << qname << ": NS " << tns->first << " in non-resolving map, skipping" << endl);
17!
3428
        return result;
17✔
3429
      }
17✔
3430
    }
10,290✔
3431

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

3462
    if (nameservers[tns->first].first.size() > 1) {
166✔
3463
      LOG("s");
16!
3464
    }
16✔
3465
    LOG(endl);
166✔
3466

3467
    sendRDQuery = nameservers[tns->first].second;
166✔
3468
    result = shuffleForwardSpeed(qname, nameservers[tns->first].first, prefix, sendRDQuery);
166✔
3469
    pierceDontQuery = true;
166✔
3470
  }
166✔
3471
  return result;
12,831✔
3472
}
12,938✔
3473

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

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

3517
bool SyncRes::validationEnabled()
3518
{
14,080✔
3519
  return g_dnssecmode != DNSSECMode::Off && g_dnssecmode != DNSSECMode::ProcessNoValidate;
14,083✔
3520
}
14,080✔
3521

3522
uint32_t SyncRes::computeLowestTTD(const std::vector<DNSRecord>& records, const MemRecursorCache::SigRecsVec& signatures, uint32_t signaturesTTL, const MemRecursorCache::AuthRecsVec& authorityRecs) const
3523
{
12,793✔
3524
  uint32_t lowestTTD = std::numeric_limits<uint32_t>::max();
12,793✔
3525
  for (const auto& record : records) {
28,080✔
3526
    lowestTTD = min(lowestTTD, record.d_ttl);
28,080✔
3527
  }
28,080✔
3528

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

3536
    for (const auto& sig : signatures) {
8,816✔
3537
      if (isRRSIGNotExpired(d_now.tv_sec, *sig)) {
8,817✔
3538
        // we don't decrement d_sigexpire by 'now' because we actually want a TTD, not a TTL */
3539
        lowestTTD = min(lowestTTD, static_cast<uint32_t>(sig->d_sigexpire));
8,804✔
3540
      }
8,804✔
3541
    }
8,815✔
3542
  }
8,795✔
3543

3544
  for (const auto& entry : authorityRecs) {
12,793✔
3545
    /* be careful, this is still a TTL here */
3546
    lowestTTD = min(lowestTTD, static_cast<uint32_t>(entry.d_ttl + d_now.tv_sec));
2,560✔
3547

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

3559
  return lowestTTD;
12,793✔
3560
}
12,793✔
3561

3562
void SyncRes::updateValidationState(const DNSName& qname, vState& state, const vState stateUpdate, const string& prefix)
3563
{
17,009✔
3564
  LOG(prefix << qname << ": Validation state was " << state << ", state update is " << stateUpdate);
17,009✔
3565
  updateDNSSECValidationState(state, stateUpdate);
17,009✔
3566
  LOG(", validation state is now " << state << endl);
17,009✔
3567
}
17,009✔
3568

3569
vState SyncRes::getTA(const DNSName& zone, dsset_t& dsSet, const string& prefix)
3570
{
81,024✔
3571
  auto luaLocal = g_luaconfs.getLocal();
81,024✔
3572

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

3579
  std::string reason;
80,980✔
3580
  if (haveNegativeTrustAnchor(luaLocal->negAnchors, zone, reason)) {
80,980✔
3581
    LOG(prefix << zone << ": Got NTA" << endl);
15!
3582
    return vState::NTA;
15✔
3583
  }
15✔
3584

3585
  if (getTrustAnchor(luaLocal->dsAnchors, zone, dsSet)) {
80,965✔
3586
    if (!zone.isRoot()) {
23,161✔
3587
      LOG(prefix << zone << ": Got TA" << endl);
144!
3588
    }
144✔
3589
    return vState::TA;
23,161✔
3590
  }
23,161✔
3591

3592
  if (zone.isRoot()) {
57,804!
3593
    /* No TA for the root */
3594
    return vState::Insecure;
×
3595
  }
×
3596

3597
  return vState::Indeterminate;
57,804✔
3598
}
57,804✔
3599

3600
size_t SyncRes::countSupportedDS(const dsset_t& dsset, const string& prefix)
3601
{
23,161✔
3602
  size_t count = 0;
23,161✔
3603

3604
  for (const auto& dsRecordContent : dsset) {
48,502✔
3605
    if (isSupportedDS(dsRecordContent, LogObject(prefix))) {
48,512✔
3606
      count++;
48,504✔
3607
    }
48,504✔
3608
  }
48,502✔
3609

3610
  return count;
23,161✔
3611
}
23,161✔
3612

3613
void SyncRes::initZoneCutsFromTA(const DNSName& from, const string& prefix)
3614
{
22,579✔
3615
  DNSName zone(from);
22,579✔
3616
  do {
74,970✔
3617
    dsset_t dsSet;
74,970✔
3618
    vState result = getTA(zone, dsSet, prefix);
74,970✔
3619
    if (result != vState::Indeterminate) {
74,970✔
3620
      if (result == vState::TA) {
22,734✔
3621
        if (countSupportedDS(dsSet, prefix) == 0) {
22,675✔
3622
          dsSet.clear();
8✔
3623
          result = vState::Insecure;
8✔
3624
        }
8✔
3625
        else {
22,667✔
3626
          result = vState::Secure;
22,667✔
3627
        }
22,667✔
3628
      }
22,675✔
3629
      else if (result == vState::NTA) {
59✔
3630
        result = vState::Insecure;
15✔
3631
      }
15✔
3632

3633
      d_cutStates[zone] = result;
22,734✔
3634
    }
22,734✔
3635
  } while (zone.chopOff());
74,970✔
3636
}
22,579✔
3637

3638
vState SyncRes::getDSRecords(const DNSName& zone, dsset_t& dsSet, bool onlyTA, unsigned int depth, const string& prefix, bool bogusOnNXD, bool* foundCut)
3639
{
6,058✔
3640
  vState result = getTA(zone, dsSet, prefix);
6,058✔
3641

3642
  if (result != vState::Indeterminate || onlyTA) {
6,058!
3643
    if (foundCut != nullptr) {
486!
3644
      *foundCut = (result != vState::Indeterminate);
×
3645
    }
×
3646

3647
    if (result == vState::TA) {
486!
3648
      if (countSupportedDS(dsSet, prefix) == 0) {
486!
3649
        dsSet.clear();
×
3650
        result = vState::Insecure;
×
3651
      }
×
3652
      else {
486✔
3653
        result = vState::Secure;
486✔
3654
      }
486✔
3655
    }
486✔
3656
    else if (result == vState::NTA) {
×
3657
      result = vState::Insecure;
×
3658
    }
×
3659

3660
    return result;
486✔
3661
  }
486✔
3662

3663
  std::set<GetBestNSAnswer> beenthere;
5,572✔
3664
  std::vector<DNSRecord> dsrecords;
5,572✔
3665

3666
  Context context;
5,572✔
3667

3668
  const bool oldCacheOnly = setCacheOnly(false);
5,572✔
3669
  const bool oldQM = setQNameMinimization(!getQMFallbackMode());
5,572✔
3670
  int rcode = doResolve(zone, QType::DS, dsrecords, depth + 1, beenthere, context);
5,572✔
3671
  setCacheOnly(oldCacheOnly);
5,572✔
3672
  setQNameMinimization(oldQM);
5,572✔
3673

3674
  if (rcode == RCode::ServFail) {
5,572✔
3675
    throw ImmediateServFailException("Server Failure while retrieving DS records for " + zone.toLogString());
4✔
3676
  }
4✔
3677

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

3683
  uint8_t bestDigestType = 0;
5,566✔
3684

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

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

3718
  if (rcode == RCode::NoError) {
5,566✔
3719
    if (dsSet.empty()) {
5,560✔
3720
      /* we have no DS, it's either:
3721
         - a delegation to a non-DNSSEC signed zone
3722
         - no delegation, we stay in the same zone
3723
      */
3724
      if (gotCNAME || denialProvesNoDelegation(zone, dsrecords, d_validationContext)) {
2,582✔
3725
        /* we are still inside the same zone */
3726

3727
        if (foundCut != nullptr) {
31✔
3728
          *foundCut = false;
25✔
3729
        }
25✔
3730
        return context.state;
31✔
3731
      }
31✔
3732

3733
      d_cutStates[zone] = context.state == vState::Secure ? vState::Insecure : context.state;
2,551✔
3734
      /* delegation with no DS, might be Secure -> Insecure */
3735
      if (foundCut != nullptr) {
2,551✔
3736
        *foundCut = true;
2,530✔
3737
      }
2,530✔
3738

3739
      /* a delegation with no DS is either:
3740
         - a signed zone (Secure) to an unsigned one (Insecure)
3741
         - an unsigned zone to another unsigned one (Insecure stays Insecure, Bogus stays Bogus)
3742
      */
3743
      return context.state == vState::Secure ? vState::Insecure : context.state;
2,551✔
3744
    }
2,582✔
3745
    /* we have a DS */
3746
    d_cutStates[zone] = context.state;
2,978✔
3747
    if (foundCut != nullptr) {
2,978✔
3748
      *foundCut = true;
1,909✔
3749
    }
1,909✔
3750
  }
2,978✔
3751

3752
  return context.state;
2,984✔
3753
}
5,566✔
3754

3755
vState SyncRes::getValidationStatus(const DNSName& name, bool wouldBeValid, bool typeIsDS, unsigned int depth, const string& prefix)
3756
{
18,124✔
3757
  vState result = vState::Indeterminate;
18,124✔
3758

3759
  if (!shouldValidate()) {
18,124✔
3760
    return result;
1,559✔
3761
  }
1,559✔
3762

3763
  DNSName subdomain(name);
16,565✔
3764
  if (typeIsDS) {
16,565✔
3765
    subdomain.chopOff();
3,181✔
3766
  }
3,181✔
3767

3768
  {
16,565✔
3769
    const auto& iter = d_cutStates.find(subdomain);
16,565✔
3770
    if (iter != d_cutStates.cend()) {
16,565✔
3771
      LOG(prefix << name << ": Got status " << iter->second << " for name " << subdomain << endl);
3,167!
3772
      return iter->second;
3,167✔
3773
    }
3,167✔
3774
  }
16,565✔
3775

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

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

3800
    while (!labelsToAdd.empty()) {
4,531✔
3801

3802
      dsName.prependRawLabel(labelsToAdd.back());
4,470✔
3803
      labelsToAdd.pop_back();
4,470✔
3804
      LOG(prefix << name << ": - Looking for a DS at " << dsName << endl);
4,470!
3805

3806
      bool foundCut = false;
4,470✔
3807
      dsset_t results;
4,470✔
3808
      vState dsState = getDSRecords(dsName, results, false, depth, prefix, false, &foundCut);
4,470✔
3809

3810
      if (foundCut) {
4,470✔
3811
        LOG(prefix << name << ": - Found cut at " << dsName << endl);
4,438!
3812
        LOG(prefix << name << ": New state for " << dsName << " is " << dsState << endl);
4,438!
3813
        d_cutStates[dsName] = dsState;
4,438✔
3814

3815
        if (dsState != vState::Secure) {
4,438✔
3816
          return dsState;
2,530✔
3817
        }
2,530✔
3818
      }
4,438✔
3819
    }
4,470✔
3820

3821
    /* we did not miss a cut, good luck */
3822
    return result;
61✔
3823
  }
2,591✔
3824

3825
#if 0
3826
  /* we don't need this, we actually do the right thing later */
3827
  DNSName signer = getSigner(signatures);
3828

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

3842
  return result;
7,895✔
3843
}
10,486✔
3844

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

3853
  DNSName signer = getSigner(signatures);
1,569✔
3854

3855
  if (!signer.empty() && zone.isPartOf(signer)) {
1,569!
3856
    vState state = getDSRecords(signer, dsSet, false, depth, prefix);
1,569✔
3857

3858
    if (state != vState::Secure) {
1,569✔
3859
      return state;
29✔
3860
    }
29✔
3861
  }
1,569✔
3862
  else {
×
3863
    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);
×
3864
    /* try again to get the missed cuts, harder this time */
3865
    auto zState = getValidationStatus(zone, false, false, depth, prefix);
×
3866
    if (zState == vState::Secure) {
×
3867
      /* too bad */
3868
      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);
×
3869
      return vState::BogusNoValidRRSIG;
×
3870
    }
×
3871
    return zState;
×
3872
  }
×
3873

3874
  skeyset_t tentativeKeys;
1,540✔
3875
  sortedRecords_t toSign;
1,540✔
3876

3877
  for (const auto& dnskey : dnskeys) {
2,023✔
3878
    if (dnskey.d_type == QType::DNSKEY) {
2,023!
3879
      auto content = getRR<DNSKEYRecordContent>(dnskey);
2,023✔
3880
      if (content) {
2,023!
3881
        tentativeKeys.insert(content);
2,023✔
3882
        toSign.insert(content);
2,023✔
3883
      }
2,023✔
3884
    }
2,023✔
3885
  }
2,023✔
3886

3887
  LOG(prefix << zone << ": Trying to validate " << std::to_string(tentativeKeys.size()) << " DNSKEYs with " << std::to_string(dsSet.size()) << " DS" << endl);
1,540!
3888
  skeyset_t validatedKeys;
1,540✔
3889
  auto state = validateDNSKeysAgainstDS(d_now.tv_sec, zone, dsSet, tentativeKeys, toSign, signatures, validatedKeys, LogObject(prefix), d_validationContext);
1,540✔
3890

3891
  if (s_maxvalidationsperq != 0 && d_validationContext.d_validationsCounter > s_maxvalidationsperq) {
1,540!
3892
    throw ImmediateServFailException("Server Failure while validating DNSKEYs, too many signature validations for this query");
×
3893
  }
×
3894

3895
  LOG(prefix << zone << ": We now have " << std::to_string(validatedKeys.size()) << " DNSKEYs" << endl);
1,540!
3896

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

3913
  return state;
1,498✔
3914
}
1,540✔
3915

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

3922
  Context context;
5,363✔
3923

3924
  const bool oldCacheOnly = setCacheOnly(false);
5,363✔
3925
  int rcode = doResolve(signer, QType::DNSKEY, records, depth + 1, beenthere, context);
5,363✔
3926
  setCacheOnly(oldCacheOnly);
5,363✔
3927

3928
  if (rcode == RCode::ServFail) {
5,363✔
3929
    servFailOccurred = true;
12✔
3930
    return vState::BogusUnableToGetDNSKEYs;
12✔
3931
  }
12✔
3932

3933
  if (rcode == RCode::NoError) {
5,351✔
3934
    if (context.state == vState::Secure) {
5,345✔
3935
      for (const auto& key : records) {
13,401✔
3936
        if (key.d_type == QType::DNSKEY) {
13,401✔
3937
          auto content = getRR<DNSKEYRecordContent>(key);
8,164✔
3938
          if (content) {
8,164!
3939
            keys.insert(std::move(content));
8,164✔
3940
          }
8,164✔
3941
        }
8,164✔
3942
      }
13,401✔
3943
    }
5,237✔
3944
    LOG(prefix << signer << ": Retrieved " << keys.size() << " DNSKeys, state is " << context.state << endl);
5,345!
3945
    return context.state;
5,345✔
3946
  }
5,345✔
3947

3948
  if (context.state == vState::Insecure) {
6✔
3949
    return context.state;
2✔
3950
  }
2✔
3951

3952
  LOG(prefix << signer << ": Returning Bogus state from " << static_cast<const char*>(__func__) << "(" << signer << ")" << endl);
4!
3953
  return vState::BogusUnableToGetDNSKEYs;
4✔
3954
}
6✔
3955

3956
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)
3957
{
5,423✔
3958
  skeyset_t keys;
5,423✔
3959
  if (signatures.empty()) {
5,423✔
3960
    LOG(prefix << qname << ": Bogus!" << endl);
36!
3961
    return vState::BogusNoRRSIG;
36✔
3962
  }
36✔
3963

3964
  const DNSName signer = getSigner(signatures);
5,387✔
3965
  bool dsFailed = false;
5,387✔
3966
  if (!signer.empty() && name.isPartOf(signer)) {
5,387!
3967
    vState state = vState::Secure;
5,385✔
3968

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

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

4048
  sortedRecords_t recordcontents;
5,243✔
4049
  for (const auto& record : records) {
5,729✔
4050
    recordcontents.insert(record.getContent());
5,729✔
4051
  }
5,729✔
4052

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

4059
  if (state == vState::Secure) {
5,241✔
4060
    LOG(prefix << name << ": Secure!" << endl);
5,207!
4061
    return vState::Secure;
5,207✔
4062
  }
5,207✔
4063

4064
  LOG(prefix << vStateToString(state) << "!" << endl);
34!
4065

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

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

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

4094
  if (wasForwardRecurse || lwr.d_aabit) {
12,543✔
4095
    /* easy */
4096
    return;
8,979✔
4097
  }
8,979✔
4098

4099
  for (const auto& rec : lwr.d_records) {
3,564✔
4100

4101
    if (rec.d_type == QType::OPT) {
3,512!
4102
      continue;
×
4103
    }
×
4104

4105
    if (rec.d_class != QClass::IN) {
3,512!
4106
      continue;
×
4107
    }
×
4108

4109
    if (rec.d_type == QType::ANY) {
3,512!
4110
      continue;
×
4111
    }
×
4112

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

4121
    if (rec.d_place != DNSResourceRecord::ANSWER) {
3,502!
4122
      /* we have scanned all the records in the answer section, if any, we are done */
4123
      return;
3,502✔
4124
    }
3,502✔
4125
  }
3,502✔
4126
}
3,564✔
4127

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

4181
static bool isRedirection(QType qtype)
4182
{
20,831✔
4183
  return qtype == QType::CNAME || qtype == QType::DNAME;
20,831✔
4184
}
20,831✔
4185

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

4198
  std::vector<bool> skipvec(lwr.d_records.size(), false);
12,543✔
4199
  unsigned int counter = 0;
12,543✔
4200
  unsigned int skipCount = 0;
12,543✔
4201

4202
  for (auto rec = lwr.d_records.cbegin(); rec != lwr.d_records.cend(); ++rec, ++counter) {
84,231✔
4203

4204
    // Allow OPT record containing EDNS(0) data
4205
    if (rec->d_type == QType::OPT) {
71,688✔
4206
      continue;
10,204✔
4207
    }
10,204✔
4208

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

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

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

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

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

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

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

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

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

4337
  sanitizeRecordsPass2(prefix, lwr, qname, qtype, auth, allowedAnswerNames, allowedAdditionals, cnameSeen, isNXDomain, isNXQType, skipvec, skipCount);
12,543✔
4338
}
12,543✔
4339

4340
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)
4341
{
12,543✔
4342
  // Second loop, we know now if the answer was NxDomain or NoData
4343
  unsigned int counter = 0;
12,543✔
4344
  for (auto rec = lwr.d_records.cbegin(); rec != lwr.d_records.cend(); ++rec, ++counter) {
84,230✔
4345

4346
    if (skipvec[counter]) {
71,687✔
4347
      continue;
463✔
4348
    }
463✔
4349
    // Allow OPT record containing EDNS(0) data
4350
    if (rec->d_type == QType::OPT) {
71,224✔
4351
      continue;
10,204✔
4352
    }
10,204✔
4353

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

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

4416
  if (ttl <= 0 || wasAuth) {
488✔
4417
    return;
46✔
4418
  }
46✔
4419
  {
442✔
4420
    auto lock = s_savedParentNSSet.lock();
442✔
4421
    if (lock->find(domain) != lock->end()) {
442!
4422
      // no relevant data, or we already stored the parent data
4423
      return;
×
4424
    }
×
4425
  }
442✔
4426

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

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

4458
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)
4459
{
12,543✔
4460
  bool wasForwardRecurse = wasForwarded && rdQuery;
12,543✔
4461
  tcache_t tcache;
12,543✔
4462

4463
  fixupAnswer(prefix, lwr, qname, qtype, auth, wasForwarded, rdQuery);
12,543✔
4464
  sanitizeRecords(prefix, lwr, qname, qtype, auth, wasForwarded, rdQuery);
12,543✔
4465

4466
  MemRecursorCache::AuthRecsVec authorityRecs;
12,543✔
4467
  bool isCNAMEAnswer = false;
12,543✔
4468
  bool isDNAMEAnswer = false;
12,543✔
4469
  DNSName seenAuth;
12,543✔
4470

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

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

4501
  for (auto& rec : lwr.d_records) {
71,195✔
4502
    if (rec.d_type == QType::OPT || rec.d_class != QClass::IN) {
71,195✔
4503
      continue;
10,203✔
4504
    }
10,203✔
4505

4506
    rec.d_ttl = min(s_maxcachettl, rec.d_ttl);
60,992✔
4507

4508
    if (!isCNAMEAnswer && rec.d_place == DNSResourceRecord::ANSWER && rec.d_type == QType::CNAME && (!(qtype == QType::CNAME)) && rec.d_name == qname && !isDNAMEAnswer) {
60,992!
4509
      isCNAMEAnswer = true;
1,533✔
4510
    }
1,533✔
4511
    if (!isDNAMEAnswer && rec.d_place == DNSResourceRecord::ANSWER && rec.d_type == QType::DNAME && qtype != QType::DNAME && qname.isPartOf(rec.d_name)) {
60,994!
4512
      isDNAMEAnswer = true;
32✔
4513
      isCNAMEAnswer = false;
32✔
4514
    }
32✔
4515

4516
    if (rec.d_type == QType::SOA && rec.d_place == DNSResourceRecord::AUTHORITY && qname.isPartOf(rec.d_name)) {
60,992!
4517
      seenAuth = rec.d_name;
1,506✔
4518
    }
1,506✔
4519

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

4546
        // cerr<<"Got an RRSIG for "<<DNSRecordContent::NumberToType(rrsig->d_type)<<" with name '"<<rec.d_name<<"' and place "<<rec.d_place<<endl;
4547
        tcache[{rec.d_name, rrsig->d_type, rec.d_place}].signatures.push_back(rrsig);
8,951✔
4548
        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,951✔
4549
      }
8,951✔
4550
    }
8,952✔
4551
  }
60,992✔
4552

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

4562
      if (nsecTypes.count(rec.d_type) != 0) {
2,494✔
4563
        authorityRecs.emplace_back(rec);
621✔
4564
      }
621✔
4565
      else if (rec.d_type == QType::RRSIG) {
1,873✔
4566
        auto rrsig = getRR<RRSIGRecordContent>(rec);
1,246✔
4567
        if (rrsig && nsecTypes.count(rrsig->d_type) != 0) {
1,246!
4568
          authorityRecs.emplace_back(rec);
621✔
4569
        }
621✔
4570
      }
1,246✔
4571
    }
2,494✔
4572
  }
619✔
4573

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

4581
    LOG(prefix << qname << ": Accept answer '" << rec.d_name << "|" << DNSRecordContent::NumberToType(rec.d_type) << "|" << rec.getContent()->getZoneRepresentation() << "' from '" << auth << "' nameservers? ttl=" << rec.d_ttl << ", place=" << (int)rec.d_place << " ");
60,990✔
4582

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

4585
    if (rec.d_name.isPartOf(auth)) {
60,995✔
4586
      if (rec.d_type == QType::RRSIG) {
60,995✔
4587
        LOG("RRSIG - separate" << endl);
8,952!
4588
      }
8,952✔
4589
      else if (rec.d_type == QType::DS && rec.d_name == auth) {
52,043✔
4590
        LOG("NO - DS provided by child zone" << endl);
2!
4591
      }
2✔
4592
      else {
52,041✔
4593
        bool haveLogged = false;
52,041✔
4594
        if (isDNAMEAnswer && rec.d_type == QType::CNAME) {
52,041✔
4595
          LOG("NO - we already have a DNAME answer for this domain" << endl);
30!
4596
          continue;
30✔
4597
        }
30✔
4598
        if (!t_sstorage.domainmap->empty()) {
52,011✔
4599
          // Check if we are authoritative for a zone in this answer
4600
          DNSName tmp_qname(rec.d_name);
47,644✔
4601
          // We may be auth for domain example.com, but the DS record needs to come from the parent (.com) nameserver
4602
          if (rec.d_type == QType::DS) {
47,644✔
4603
            tmp_qname.chopOff();
860✔
4604
          }
860✔
4605
          auto auth_domain_iter = getBestAuthZone(&tmp_qname);
47,644✔
4606
          if (auth_domain_iter != t_sstorage.domainmap->end() && auth.countLabels() <= auth_domain_iter->first.countLabels()) {
47,644✔
4607
            if (auth_domain_iter->first != auth) {
345✔
4608
              LOG("NO! - we are authoritative for the zone " << auth_domain_iter->first << endl);
2!
4609
              continue;
2✔
4610
            }
2✔
4611
            LOG("YES! - This answer was ");
343!
4612
            if (!wasForwarded) {
343✔
4613
              LOG("retrieved from the local auth store.");
136!
4614
            }
136✔
4615
            else {
207✔
4616
              LOG("received from a server we forward to.");
207!
4617
            }
207✔
4618
            haveLogged = true;
343✔
4619
            LOG(endl);
343!
4620
          }
343✔
4621
        }
47,644✔
4622
        if (!haveLogged) {
52,009✔
4623
          LOG("YES!" << endl);
51,666✔
4624
        }
51,666✔
4625

4626
        rec.d_ttl = min(s_maxcachettl, rec.d_ttl);
52,009✔
4627

4628
        DNSRecord dnsRecord(rec);
52,009✔
4629
        tcache[{rec.d_name, rec.d_type, rec.d_place}].d_ttl_time = d_now.tv_sec;
52,009✔
4630
        dnsRecord.d_ttl += d_now.tv_sec;
52,009✔
4631
        dnsRecord.d_place = DNSResourceRecord::ANSWER;
52,009✔
4632
        tcache[{rec.d_name, rec.d_type, rec.d_place}].records.push_back(std::move(dnsRecord));
52,009✔
4633
      }
52,009✔
4634
    }
60,995✔
4635
    else
2,147,483,647✔
4636
      LOG("NO!" << endl);
2,147,498,417!
4637
  }
60,958✔
4638

4639
  // supplant
4640
  for (auto& entry : tcache) {
36,718✔
4641
    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,718✔
4642
      uint32_t lowestTTD = computeLowestTTD(entry.second.records, entry.second.signatures, entry.second.signaturesTTL, authorityRecs);
12,793✔
4643

4644
      for (auto& record : entry.second.records) {
28,081✔
4645
        record.d_ttl = lowestTTD; // boom
28,081✔
4646
      }
28,081✔
4647
    }
12,793✔
4648
  }
36,718✔
4649

4650
  for (auto tCacheEntry = tcache.begin(); tCacheEntry != tcache.end(); ++tCacheEntry) {
49,258✔
4651

4652
    if (tCacheEntry->second.records.empty()) { // this happens when we did store signatures, but passed on the records themselves
36,715!
4653
      continue;
×
4654
    }
×
4655

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

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

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

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

4726
    vState recordState = vState::Indeterminate;
36,713✔
4727

4728
    if (expectSignature && shouldValidate()) {
36,713✔
4729
      vState initialState = getValidationStatus(tCacheEntry->first.name, !tCacheEntry->second.signatures.empty(), tCacheEntry->first.type == QType::DS, depth, prefix);
10,810✔
4730
      LOG(prefix << qname << ": Got initial zone status " << initialState << " for record " << tCacheEntry->first.name << "|" << DNSRecordContent::NumberToType(tCacheEntry->first.type) << endl);
10,810!
4731

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

4747
      LOG(prefix << qname << ": Validation result is " << recordState << ", current state is " << state << endl);
10,810!
4748
      if (state != recordState) {
10,810✔
4749
        updateValidationState(qname, state, recordState, prefix);
6,690✔
4750
      }
6,690✔
4751
    }
10,810✔
4752

4753
    if (vStateIsBogus(recordState)) {
36,713✔
4754
      /* this is a TTD by now, be careful */
4755
      for (auto& record : tCacheEntry->second.records) {
509✔
4756
        auto newval = std::min(record.d_ttl, static_cast<uint32_t>(s_maxbogusttl + d_now.tv_sec));
509✔
4757
        record.d_ttl = newval;
509✔
4758
      }
509✔
4759
      tCacheEntry->second.d_ttl_time = d_now.tv_sec;
180✔
4760
    }
180✔
4761

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

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

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

4799
      d_fromAuthIP = remoteIP;
33,096✔
4800

4801
      if (doCache) {
33,096✔
4802
        // Check if we are going to replace a non-auth (parent) NS recordset
4803
        if (isAA && tCacheEntry->first.type == QType::NS && s_save_parent_ns_set) {
33,091!
4804
          rememberParentSetIfNeeded(tCacheEntry->first.name, tCacheEntry->second.records, depth, prefix);
488✔
4805
        }
488✔
4806
        bool thisRRNeedsWildcardProof = false;
33,091✔
4807
        if (gatherWildcardProof) {
33,091✔
4808
          if (auto wcIt = wildcardCandidates.find(tCacheEntry->first.name); wcIt != wildcardCandidates.end() && wcIt->second) {
1,220✔
4809
            thisRRNeedsWildcardProof = true;
623✔
4810
          }
623✔
4811
        }
1,220✔
4812
        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);
33,091✔
4813

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

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

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

4829
            std::vector<DNSRecord> content;
595✔
4830
            content.reserve(tCacheEntry->second.records.size());
595✔
4831
            for (const auto& record : tCacheEntry->second.records) {
595✔
4832
              DNSRecord nonExpandedRecord(record);
595✔
4833
              nonExpandedRecord.d_name = realOwner;
595✔
4834
              content.push_back(std::move(nonExpandedRecord));
595✔
4835
            }
595✔
4836

4837
            g_recCache->replace(d_now.tv_sec, realOwner, QType(tCacheEntry->first.type), content, tCacheEntry->second.signatures, /* no additional records in that case */ {}, tCacheEntry->first.type == QType::DS ? true : isAA, auth, boost::none, boost::none, recordState, remoteIP, d_refresh, tCacheEntry->second.d_ttl_time);
595!
4838
          }
595✔
4839
        }
595✔
4840
      }
33,091✔
4841
    }
33,096✔
4842

4843
    if (seenAuth.empty() && !tCacheEntry->second.signatures.empty()) {
36,713✔
4844
      seenAuth = getSigner(tCacheEntry->second.signatures);
5,714✔
4845
    }
5,714✔
4846

4847
    if (g_aggressiveNSECCache && (tCacheEntry->first.type == QType::NSEC || tCacheEntry->first.type == QType::NSEC3) && recordState == vState::Secure && !seenAuth.empty()) {
36,713✔
4848
      // Good candidate for NSEC{,3} caching
4849
      g_aggressiveNSECCache->insertNSEC(seenAuth, tCacheEntry->first.name, tCacheEntry->second.records.at(0), tCacheEntry->second.signatures, tCacheEntry->first.type == QType::NSEC3, qname, qtype);
2,887✔
4850
    }
2,887✔
4851

4852
    if (tCacheEntry->first.place == DNSResourceRecord::ANSWER && ednsmask) {
36,713✔
4853
      d_wasVariable = true;
77✔
4854
    }
77✔
4855
  }
36,713✔
4856

4857
  if (gatherWildcardProof) {
12,543✔
4858
    if (auto wcIt = wildcardCandidates.find(qname); wcIt != wildcardCandidates.end() && !wcIt->second) {
619!
4859
      // 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
4860
      gatherWildcardProof = false;
2✔
4861
    }
2✔
4862
  }
619✔
4863

4864
  return RCode::NoError;
12,543✔
4865
}
12,543✔
4866

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

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

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

4911
dState SyncRes::getDenialValidationState(const NegCache::NegCacheEntry& negEntry, const dState expectedState, bool referralToUnsigned, const string& prefix)
4912
{
2,370✔
4913
  cspmap_t csp = harvestCSPFromNE(negEntry);
2,370✔
4914
  return getDenial(csp, negEntry.d_name, negEntry.d_qtype.getCode(), referralToUnsigned, expectedState == dState::NXQTYPE, d_validationContext, LogObject(prefix));
2,370✔
4915
}
2,370✔
4916

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

4926
  if (vStateIsBogus(state)) {
615!
4927
    negEntry.d_validationState = state;
×
4928
  }
×
4929
  else {
615✔
4930
    auto recordState = getValidationStatus(qname, !negEntry.authoritySOA.signatures.empty() || !negEntry.DNSSECRecords.signatures.empty(), false, depth, prefix);
615✔
4931

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

4952
        updateValidationState(qname, state, tmpState, prefix);
8✔
4953
        /* we already stored the record with a different validation status, let's fix it */
4954
        updateValidationStatusInCache(qname, qtype, lwr.d_aabit, tmpState);
8✔
4955
      }
8✔
4956
    }
611✔
4957
  }
615✔
4958
}
615✔
4959

4960
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)
4961
{
12,525✔
4962
  bool done = false;
12,525✔
4963
  DNSName dnameTarget;
12,525✔
4964
  DNSName dnameOwner;
12,525✔
4965
  uint32_t dnameTTL = 0;
12,525✔
4966
  bool referralOnDS = false;
12,525✔
4967

4968
  for (auto& rec : lwr.d_records) {
71,119✔
4969
    if (rec.d_type == QType::OPT || rec.d_class != QClass::IN) {
71,119✔
4970
      continue;
10,204✔
4971
    }
10,204✔
4972

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

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

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

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

4997
      NegCache::NegCacheEntry negEntry;
125✔
4998

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

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

5024
      if (vStateIsBogus(negEntry.d_validationState)) {
125✔
5025
        lowestTTL = min(lowestTTL, s_maxbogusttl);
7✔
5026
      }
7✔
5027

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

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

5105
      done = true;
9,547✔
5106
      rcode = RCode::NoError;
9,547✔
5107

5108
      if (needWildcardProof) {
9,547✔
5109
        checkWildcardProof(qname, qtype, rec, lwr, state, depth, prefix, wildcardLabelsCount);
603✔
5110
      }
603✔
5111

5112
      ret.push_back(rec);
9,547✔
5113
    }
9,547✔
5114
    else if ((rec.d_type == QType::RRSIG || rec.d_type == QType::NSEC || rec.d_type == QType::NSEC3) && rec.d_place == DNSResourceRecord::ANSWER) {
48,439✔
5115
      if (rec.d_type != QType::RRSIG || rec.d_name == qname) {
2,992!
5116
        ret.push_back(rec); // enjoy your DNSSEC
2,959✔
5117
      }
2,959✔
5118
      else if (rec.d_type == QType::RRSIG && qname.isPartOf(rec.d_name)) {
33!
5119
        auto rrsig = getRR<RRSIGRecordContent>(rec);
24✔
5120
        if (rrsig != nullptr && rrsig->d_type == QType::DNAME) {
24!
5121
          ret.push_back(rec);
24✔
5122
        }
24✔
5123
      }
24✔
5124
    }
2,992✔
5125
    else if (rec.d_place == DNSResourceRecord::AUTHORITY && rec.d_type == QType::NS && qname.isPartOf(rec.d_name)) {
45,447✔
5126
      if (moreSpecificThan(rec.d_name, auth)) {
14,734✔
5127
        newauth = rec.d_name;
13,771✔
5128
        LOG(prefix << qname << ": Got NS record '" << rec.d_name << "' -> '" << rec.getContent()->getZoneRepresentation() << "'" << endl);
13,771✔
5129

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

5158
      if (!vStateIsBogus(state)) {
3,407!
5159
        auto recordState = getValidationStatus(newauth, !negEntry.authoritySOA.signatures.empty() || !negEntry.DNSSECRecords.signatures.empty(), true, depth, prefix);
3,407!
5160

5161
        if (recordState == vState::Secure) {
3,407✔
5162
          negEntry.d_auth = auth;
2,120✔
5163
          negEntry.d_name = newauth;
2,120✔
5164
          negEntry.d_qtype = QType::DS;
2,120✔
5165
          rec.d_ttl = min(s_maxnegttl, rec.d_ttl);
2,120✔
5166

5167
          dState denialState = getDenialValidationState(negEntry, dState::NXQTYPE, true, prefix);
2,120✔
5168

5169
          if (denialState == dState::NXQTYPE || denialState == dState::OPTOUT || denialState == dState::INSECURE) {
2,120!
5170
            negEntry.d_ttd = lowestTTL + d_now.tv_sec;
2,116✔
5171
            negEntry.d_orig_ttl = lowestTTL;
2,116✔
5172
            negEntry.d_validationState = vState::Secure;
2,116✔
5173
            if (denialState == dState::OPTOUT) {
2,116✔
5174
              negEntry.d_validationState = vState::Insecure;
2,034✔
5175
            }
2,034✔
5176
            LOG(prefix << qname << ": Got negative indication of DS record for '" << newauth << "'" << endl);
2,116!
5177

5178
            g_negCache->add(negEntry);
2,116✔
5179

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

5196
      if (!newtarget.empty()) {
1,377✔
5197
        LOG(prefix << qname << ": Hang on! Got a redirect to '" << newtarget << "' already" << endl);
5!
5198
      }
5✔
5199
      else {
1,372✔
5200
        rec.d_ttl = min(s_maxnegttl, rec.d_ttl);
1,372✔
5201

5202
        NegCache::NegCacheEntry negEntry;
1,372✔
5203
        negEntry.d_auth = rec.d_name;
1,372✔
5204
        uint32_t lowestTTL = rec.d_ttl;
1,372✔
5205
        negEntry.d_name = qname;
1,372✔
5206
        negEntry.d_qtype = qtype;
1,372✔
5207
        harvestNXRecords(lwr.d_records, negEntry, d_now.tv_sec, &lowestTTL);
1,372✔
5208

5209
        if (vStateIsBogus(state)) {
1,372✔
5210
          negEntry.d_validationState = state;
7✔
5211
        }
7✔
5212
        else {
1,365✔
5213
          auto recordState = getValidationStatus(qname, !negEntry.authoritySOA.signatures.empty() || !negEntry.DNSSECRecords.signatures.empty(), qtype == QType::DS, depth, prefix);
1,365!
5214
          if (recordState == vState::Secure) {
1,365✔
5215
            dState denialState = getDenialValidationState(negEntry, dState::NXQTYPE, false, prefix);
186✔
5216
            updateDenialValidationState(qname, negEntry.d_validationState, negEntry.d_name, state, denialState, dState::NXQTYPE, qtype == QType::DS, depth, prefix);
186✔
5217
          }
186✔
5218
          else {
1,179✔
5219
            negEntry.d_validationState = recordState;
1,179✔
5220
            updateValidationState(qname, state, negEntry.d_validationState, prefix);
1,179✔
5221
          }
1,179✔
5222
        }
1,365✔
5223

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

5239
        ret.push_back(rec);
1,372✔
5240
        negindic = true;
1,372✔
5241
        negIndicHasSignatures = !negEntry.authoritySOA.signatures.empty() || !negEntry.DNSSECRecords.signatures.empty();
1,372!
5242
      }
1,372✔
5243
    }
1,377✔
5244
  }
60,915✔
5245

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

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

5272
  return done;
12,525✔
5273
}
12,525✔
5274

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

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

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

5335
bool SyncRes::tryDoT(const DNSName& qname, const QType qtype, const DNSName& nsName, ComboAddress address, time_t now)
5336
{
×
5337
  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));
×
5338

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

5376
void SyncRes::ednsStats(boost::optional<Netmask>& ednsmask, const DNSName& qname, const string& prefix)
5377
{
12,949✔
5378
  if (!ednsmask) {
12,949✔
5379
    return;
12,870✔
5380
  }
12,870✔
5381
  s_ecsresponses++;
79✔
5382
  LOG(prefix << qname << ": Received EDNS Client Subnet Mask " << ednsmask->toString() << " on response" << endl);
79!
5383

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

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

5416
void SyncRes::incTimeoutStats(const ComboAddress& remoteIP)
5417
{
324✔
5418
  d_timeouts++;
324✔
5419
  t_Counters.at(rec::Counter::outgoingtimeouts)++;
324✔
5420

5421
  if (remoteIP.sin4.sin_family == AF_INET) {
324✔
5422
    t_Counters.at(rec::Counter::outgoing4timeouts)++;
171✔
5423
  }
171✔
5424
  else {
153✔
5425
    t_Counters.at(rec::Counter::outgoing6timeouts)++;
153✔
5426
  }
153✔
5427

5428
  if (t_timeouts) {
324✔
5429
    t_timeouts->push_back(remoteIP);
5✔
5430
  }
5✔
5431
}
324✔
5432

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

5443
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)
5444
{
12,961✔
5445
  checkTotalTime(qname, qtype, extendedError);
12,961✔
5446

5447
  bool chained = false;
12,961✔
5448
  LWResult::Result resolveret = LWResult::Result::Success;
12,961✔
5449
  int preOutQueryRet = RCode::NoError;
12,961✔
5450

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

5477
  /* preoutquery killed the query by setting dq.rcode to -3 */
5478
  if (preOutQueryRet == -3) {
12,961✔
5479
    throw ImmediateServFailException("Query killed by policy");
4✔
5480
  }
4✔
5481

5482
  d_totUsec += lwr.d_usec;
12,957✔
5483

5484
  if (resolveret == LWResult::Result::Spoofed) {
12,957✔
5485
    spoofed = true;
1✔
5486
    return false;
1✔
5487
  }
1✔
5488

5489
  accountAuthLatency(lwr.d_usec, remoteIP.sin4.sin_family);
12,956✔
5490
  if (lwr.d_rcode >= 0 && lwr.d_rcode < static_cast<decltype(lwr.d_rcode)>(t_Counters.at(rec::RCode::auth).rcodeCounters.size())) {
12,956!
5491
    ++t_Counters.at(rec::RCode::auth).rcodeCounters.at(static_cast<uint8_t>(lwr.d_rcode));
12,947✔
5492
  }
12,947✔
5493

5494
  if (!dontThrottle) {
12,956✔
5495
    dontThrottle = shouldNotThrottle(&nsName, &remoteIP);
12,947✔
5496
  }
12,947✔
5497

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

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

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

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

5554
    return false;
399✔
5555
  }
399✔
5556

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

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

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

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

5610
  if (lwr.d_tcbit) {
12,481✔
5611
    truncated = true;
66✔
5612

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

5623
    return true;
64✔
5624
  }
66✔
5625

5626
  return true;
12,415✔
5627
}
12,481✔
5628

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

5643
  if (!d_followCNAME) {
1,551✔
5644
    rcode = RCode::NoError;
129✔
5645
    return;
129✔
5646
  }
129✔
5647

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

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

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

5669
    rcode = RCode::NoError;
4✔
5670
    return;
4✔
5671
  }
4✔
5672

5673
  LOG(prefix << qname << ": Status=got a CNAME referral, starting over with " << newtarget << endl);
1,414✔
5674

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

5682
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)
5683
{
12,543✔
5684
  if (s_minimumTTL != 0) {
12,543✔
5685
    for (auto& rec : lwr.d_records) {
65,642✔
5686
      rec.d_ttl = max(rec.d_ttl, s_minimumTTL);
65,642✔
5687
    }
65,642✔
5688
  }
10,309✔
5689

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

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

5708
  LOG(prefix << qname << ": Determining status after receiving this packet" << endl);
12,543✔
5709

5710
  set<DNSName> nsset;
12,543✔
5711
  bool realreferral = false;
12,543✔
5712
  bool negindic = false;
12,543✔
5713
  bool negIndicHasSignatures = false;
12,543✔
5714
  DNSName newauth;
12,543✔
5715
  DNSName newtarget;
12,543✔
5716

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

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

5730
  if (!newtarget.empty()) {
6,678✔
5731
    handleNewTarget(prefix, qname, newtarget, qtype.getCode(), ret, *rcode, depth, lwr.d_records, state);
1,544✔
5732
    return true;
1,544✔
5733
  }
1,544✔
5734

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

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

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

5753
    *rcode = RCode::NXDomain;
135✔
5754
    return true;
135✔
5755
  }
135✔
5756

5757
  if (nsset.empty() && lwr.d_rcode == 0 && (negindic || lwr.d_aabit || sendRDQuery)) {
4,999!
5758
    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,419!
5759

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

5771
    if (d_doDNSSEC) {
1,419✔
5772
      addNXNSECS(ret, lwr.d_records);
1,349✔
5773
    }
1,349✔
5774

5775
    *rcode = RCode::NoError;
1,419✔
5776
    return true;
1,419✔
5777
  }
1,419✔
5778

5779
  if (realreferral) {
3,580✔
5780
    LOG(prefix << qname << ": Status=did not resolve, got " << (unsigned int)nsset.size() << " NS, ");
3,508✔
5781

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

5806
    return false;
3,506✔
5807
  }
3,508✔
5808

5809
  return false;
72✔
5810
}
3,580✔
5811

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

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

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

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

5842
  LOG(endl);
10,820✔
5843

5844
  unsigned int addressQueriesForNS = 0;
10,820✔
5845
  for (;;) { // we may get more specific nameservers
12,848✔
5846
    auto rnameservers = shuffleInSpeedOrder(qname, nameservers, prefix);
12,848✔
5847

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

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

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

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

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

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

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

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

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

5966
          bool truncated = false;
12,896✔
5967
          bool spoofed = false;
12,896✔
5968
          bool gotAnswer = false;
12,896✔
5969
          bool doDoT = false;
12,896✔
5970

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

5980
          if (!doDoT && s_max_busy_dot_probes > 0) {
12,896!
5981
            submitTryDotTask(*remoteIP, auth, tns->first, d_now.tv_sec);
×
5982
          }
×
5983
          if (!forceTCP) {
12,896✔
5984
            gotAnswer = doResolveAtThisIP(prefix, qname, qtype, lwr, ednsmask, auth, sendRDQuery, wasForwarded,
12,887✔
5985
                                          tns->first, *remoteIP, false, false, truncated, spoofed, context.extendedError);
12,887✔
5986
          }
12,887✔
5987
          if (forceTCP || (spoofed || (gotAnswer && truncated))) {
12,896✔
5988
            /* retry, over TCP this time */
5989
            gotAnswer = doResolveAtThisIP(prefix, qname, qtype, lwr, ednsmask, auth, sendRDQuery, wasForwarded,
74✔
5990
                                          tns->first, *remoteIP, true, doDoT, truncated, spoofed, context.extendedError);
74✔
5991
          }
74✔
5992

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

6001
          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,419✔
6002

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

6012
          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,419✔
6013

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

6032
        if (gotNewServers) {
2,497✔
6033
          break;
2,024✔
6034
        }
2,024✔
6035

6036
        if (remoteIP == remoteIPs.cend()) { // we tried all IP addresses, none worked
2,147,484,118✔
6037
          continue;
343✔
6038
        }
343✔
6039
      }
2,147,484,118✔
6040
    }
13,075✔
6041
  }
12,848✔
6042
  return -1;
130✔
6043
}
10,820✔
6044

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

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

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

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

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

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

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

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

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

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

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

6176
  return res;
24✔
6177
}
24✔
6178

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

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

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

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

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