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

PowerDNS / pdns / 19231068487

10 Nov 2025 12:07PM UTC coverage: 73.04% (+0.05%) from 72.995%
19231068487

Pull #16460

github

web-flow
Merge 8b17e3378 into 48c0dbfd8
Pull Request #16460: Avoid warning about mixed explicit/implicit returns

38284 of 63132 branches covered (60.64%)

Branch coverage included in aggregate %.

127543 of 163903 relevant lines covered (77.82%)

5791791.33 hits per line

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

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

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

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

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

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

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

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

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

76
    if (iter == d_cont.end()) {
11,515✔
77
      return 0;
11,472✔
78
    }
11,472✔
79
    return iter->value;
43✔
80
  }
11,515✔
81

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

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

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

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

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

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

116
private:
117
  cont_t d_cont;
118
};
119

120
static LockGuarded<nsspeeds_t> s_nsSpeeds;
121

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

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

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

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

147
  struct entry_t
148
  {
149
    entry_t(Key thing_, time_t ttd_, unsigned int count_, Reason reason_) :
150
      thing(std::move(thing_)), ttd(ttd_), count(count_), reason(reason_)
151
    {
226✔
152
    }
226✔
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
  {
28,198✔
165
    auto iter = d_cont.find(arg);
28,198✔
166
    if (iter == d_cont.end()) {
28,198✔
167
      return false;
28,130✔
168
    }
28,130✔
169
    if (now > iter->ttd || iter->count == 0) {
68✔
170
      d_cont.erase(iter);
6✔
171
      return false;
6✔
172
    }
6✔
173
    iter->count--;
62✔
174

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

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

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

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

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

212
  void clear(const Key& thing)
213
  {
27,156✔
214
    d_cont.erase(thing);
27,156✔
215
  }
27,156✔
216
  void prune(time_t now)
217
  {
142✔
218
    auto& ind = d_cont.template get<time_t>();
142✔
219
    ind.erase(ind.begin(), ind.upper_bound(now));
142✔
220
  }
142✔
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
  {
115✔
270
    auto& ind = get<time_t>();
115✔
271
    ind.erase(ind.begin(), ind.upper_bound(now));
115✔
272
  }
115✔
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,224,041✔
402
    g_log << Logger::Warning << x;   \
113,120✔
403
  }                                  \
113,120✔
404
  else if (d_lm == Store) {          \
1,224,041✔
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
{
91,728✔
411
  OptLog ret;
91,728✔
412
  if (d_lm == Log) {
91,728✔
413
    ret = {prefix, d_fixednow, g_log};
13,285✔
414
  }
13,285✔
415
  else if (d_lm == Store) {
78,443✔
416
    ret = {prefix, d_fixednow, d_trace};
114✔
417
  }
114✔
418
  return ret;
91,728✔
419
}
91,728✔
420

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

906
    return result;
9✔
907
  }
9✔
908

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

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

929
    return result;
6✔
930
  }
52✔
931

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

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

951
  return result;
24✔
952
}
30✔
953

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

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

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

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

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

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

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

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

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

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

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

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

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

1044
static LockGuarded<ednsstatus_t> s_ednsstatus;
1045

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1142
    // an <empty> can appear hear in case of authoritative (hosted) zones
1143
    timebuf_t tmp;
64✔
1144
    fprintf(filePtr.get(), "%s\t%s\t", iter.d_name.toLogString().c_str(), isoDateTimeMillis(iter.d_lastget, tmp));
64✔
1145
    bool first = true;
64✔
1146
    for (const auto& line : iter.d_collection) {
64✔
1147
      fprintf(filePtr.get(), "%s%s/%.3f/%.3f", first ? "" : "\t", line.first.toStringWithPortExcept(53).c_str(), line.second.peek() / 1000.0F, static_cast<float>(line.second.last()) / 1000.0F);
32✔
1148
      first = false;
32✔
1149
    }
32✔
1150
    fprintf(filePtr.get(), "\n");
64✔
1151
  }
64✔
1152
  return count;
4✔
1153
}
4✔
1154

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

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

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

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

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

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

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

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

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

1219
  // Get a copy to avoid holding the lock while doing I/O
1220
  const auto throttleMap = s_throttle.lock()->getThrottleMap();
×
1221
  for (const auto& iter : throttleMap) {
×
1222
    count++;
×
1223
    timebuf_t tmp;
×
1224
    // remote IP, dns name, qtype, count, ttd, reason
1225
    fprintf(filePtr.get(), "%s\t%s\t%s\t%u\t%s\t%s\n", std::get<0>(iter.thing).toString().c_str(), std::get<1>(iter.thing).toLogString().c_str(), std::get<2>(iter.thing).toString().c_str(), iter.count, timestamp(iter.ttd, tmp), Throttle::toString(iter.reason).c_str());
×
1226
  }
×
1227

1228
  return count;
×
1229
}
×
1230

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

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

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

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

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

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

1273
  return count;
×
1274
}
×
1275

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

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

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

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

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

1313
  return count;
×
1314
}
×
1315

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

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

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

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

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

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

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

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

1391
  // We get a copy, so the I/O does not need to happen while holding the lock
1392
  DoTMap copy;
×
1393
  {
×
1394
    copy = *s_dotMap.lock();
×
1395
  }
×
1396
  fprintf(filePtr.get(), "; %" PRIu64 " Busy entries\n", copy.d_numBusy);
×
1397
  for (const auto& iter : copy.d_map) {
×
1398
    count++;
×
1399
    timebuf_t tmp;
×
1400
    fprintf(filePtr.get(), "%s\t%s\t%" PRIu64 "\t%s\t%s\n", iter.d_address.toString().c_str(), iter.d_auth.toString().c_str(), iter.d_count, iter.toString().c_str(), timestamp(iter.d_ttd, tmp));
×
1401
  }
×
1402
  return count;
×
1403
}
×
1404

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

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

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

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

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

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

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

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

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

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

1465
  LWResult::Result ret{};
14,320✔
1466

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

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

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

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

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

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

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

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

1535
    break;
13,915✔
1536
  }
13,925✔
1537
  return ret;
14,320✔
1538
}
14,320✔
1539

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

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

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

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

1568
int SyncRes::doResolve(const DNSName& qname, const QType qtype, vector<DNSRecord>& ret, unsigned int depth, set<GetBestNSAnswer>& beenthere, Context& context) // NOLINT(readability-function-cognitive-complexity)
1569
{
22,849✔
1570
  auto prefix = getPrefix(depth);
22,849✔
1571
  auto luaconfsLocal = g_luaconfs.getLocal();
22,849✔
1572

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

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

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

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

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

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

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

1644
  const unsigned int qnamelen = qname.countLabels();
11,075✔
1645

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1816
/*! This function will check the cache and go out to the internet if the answer is not in cache
1817
 *
1818
 * \param qname The name we need an answer for
1819
 * \param qtype
1820
 * \param ret The vector of DNSRecords we need to fill with the answers
1821
 * \param depth The recursion depth we are in
1822
 * \param beenthere
1823
 * \param fromCache tells the caller the result came from the cache, may be nullptr
1824
 * \param stopAtDelegation if non-nullptr and pointed-to value is Stop requests the callee to stop at a delegation, if so pointed-to value is set to Stopped
1825
 * \return DNS RCODE or -1 (Error)
1826
 */
1827
int SyncRes::doResolveNoQNameMinimization(const DNSName& qname, const QType qtype, vector<DNSRecord>& ret, unsigned int depth, set<GetBestNSAnswer>& beenthere, Context& context, bool* fromCache, StopAtDelegation* stopAtDelegation) // NOLINT(readability-function-cognitive-complexity)
1828
{
43,675✔
1829
  context.extendedError.reset();
43,675✔
1830
  auto prefix = getPrefix(depth);
43,675✔
1831

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

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

1845
  int res = 0;
43,671✔
1846

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

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

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

1873
      bool wasForwardedOrAuthZone = false;
43,179✔
1874
      bool wasAuthZone = false;
43,179✔
1875
      bool wasForwardRecurse = false;
43,179✔
1876

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

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

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

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

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

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

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

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

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

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

1984
    if (d_cacheonly) {
26,800✔
1985
      return 0;
15,185✔
1986
    }
15,185✔
1987

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

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

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

2001
    NsSet nsset;
11,611✔
2002
    bool flawedNSSet = false;
11,611✔
2003

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

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

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

2047
    if (res == 0) {
11,611✔
2048
      return 0;
10,938✔
2049
    }
10,938✔
2050

2051
    LOG(prefix << qname << ": Failed (res=" << res << ")" << endl);
673✔
2052
    if (res >= 0) {
679✔
2053
      break;
225✔
2054
    }
225✔
2055
  }
673✔
2056
  return res < 0 ? RCode::ServFail : res;
654✔
2057
}
43,671✔
2058

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2261
      vector<DNSName> missing;
24,178✔
2262
      for (const auto& nsRecord : nsVector) {
139,875✔
2263
        if (nsRecord.d_ttl > (unsigned int)d_now.tv_sec) {
139,875!
2264
          vector<DNSRecord> aset;
139,875✔
2265
          QType nsqt{QType::ADDR};
139,875✔
2266
          if (s_doIPv4 && !s_doIPv6) {
139,877✔
2267
            nsqt = QType::A;
95,730✔
2268
          }
95,730✔
2269
          else if (!s_doIPv4 && s_doIPv6) {
2,147,527,793!
2270
            nsqt = QType::AAAA;
62✔
2271
          }
62✔
2272

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

2324
      if (!bestns.empty()) {
24,180✔
2325
        GetBestNSAnswer answer;
24,144✔
2326
        answer.qname = qname;
24,144✔
2327
        answer.qtype = qtype.getCode();
24,144✔
2328
        for (const auto& bestNSRecord : bestns) {
139,554✔
2329
          if (auto nsContent = getRR<NSRecordContent>(bestNSRecord)) {
139,558✔
2330
            answer.bestns.emplace(bestNSRecord.d_name, nsContent->getNS());
139,557✔
2331
          }
139,557✔
2332
        }
139,554✔
2333

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

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

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

2374
  SyncRes::domainmap_t::const_iterator ret;
126,409✔
2375
  do {
457,588✔
2376
    ret = t_sstorage.domainmap->find(*qname);
457,588✔
2377
    if (ret != t_sstorage.domainmap->end()) {
457,588✔
2378
      break;
2,314✔
2379
    }
2,314✔
2380
  } while (qname->chopOff());
457,588✔
2381
  return ret;
×
2382
}
152,745✔
2383

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

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

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

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

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

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

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

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

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

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

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

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

2513
    do {
82,069✔
2514
      dnameName.prependRawLabel(labels.back());
82,069✔
2515
      labels.pop_back();
82,069✔
2516
      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
82,069✔
2517
        break;
30,506✔
2518
      }
30,506✔
2519
      if (g_recCache->get(d_now.tv_sec, dnameName, QType::DNAME, flags, &cset, d_cacheRemote, d_routingTag, d_doDNSSEC ? &signatures : nullptr, d_doDNSSEC ? &authorityRecs : nullptr, &d_wasVariable, &context.state, &wasAuth, &authZone, &extra) > 0) {
51,563✔
2520
        foundName = std::move(dnameName);
16✔
2521
        foundQT = QType::DNAME;
16✔
2522
        d_fromAuthIP = extra.d_address;
16✔
2523
        break;
16✔
2524
      }
16✔
2525
    } while (!labels.empty());
51,563✔
2526
  }
30,524✔
2527

2528
  if (foundName.empty()) {
37,785✔
2529
    return false;
31,647✔
2530
  }
31,647✔
2531

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

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

2544
    if (record.d_ttl > (unsigned int)d_now.tv_sec) {
6,133!
2545

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

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

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

2565
      DNSRecord dnsRecord = record;
6,133✔
2566
      auto alreadyPresent = false;
6,133✔
2567

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

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

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

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

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

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

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

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

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

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

2665
      if (!d_followCNAME) {
6,109✔
2666
        res = RCode::NoError;
134✔
2667
        return true;
134✔
2668
      }
134✔
2669

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

2683
      set<GetBestNSAnswer> beenthere;
5,967✔
2684
      Context cnameContext;
5,967✔
2685
      // Be aware that going out on the network might be disabled (cache-only), for example because we are in QM Step0,
2686
      // so you can't trust that a real lookup will have been made.
2687
      res = doResolve(newTarget, qtype, ret, depth + 1, beenthere, cnameContext);
5,967✔
2688
      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,967✔
2689
      pdns::dedupRecords(ret); // multiple NSECS could have been added, #14120
5,967✔
2690
      updateValidationState(qname, context.state, cnameContext.state, prefix);
5,967✔
2691

2692
      return true;
5,967✔
2693
    }
5,971✔
2694
  }
6,133✔
2695
  throw ImmediateServFailException("Could not determine whether or not there was a CNAME or DNAME in cache for '" + qname.toLogString() + "'");
1✔
2696
}
6,134✔
2697

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

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

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

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

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

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

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

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

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

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

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

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

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

2818
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)
2819
{
37,068✔
2820
  bool giveNegative = false;
37,068✔
2821

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

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

2901
  if (giveNegative) {
37,068✔
2902

2903
    context.state = cachedState;
3,453✔
2904

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

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

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

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

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

2945
  MemRecursorCache::Extra extra;
33,615✔
2946
  if (g_recCache->get(d_now.tv_sec, sqname, sqt, flags, &cset, d_cacheRemote, d_routingTag, d_doDNSSEC ? &signatures : nullptr, d_doDNSSEC ? &authorityRecs : nullptr, &d_wasVariable, &cachedState, &wasCachedAuth, nullptr, &extra) > 0) {
2,147,501,201✔
2947
    d_fromAuthIP = extra.d_address;
7,304✔
2948

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

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

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

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

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

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

2998
    for (auto j = cset.cbegin(); j != cset.cend(); ++j) {
17,591✔
2999

3000
      LOG(j->getContent()->getZoneRepresentation());
10,287✔
3001

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

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

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

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

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

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

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

3063
  return false;
26,267✔
3064
}
26,311✔
3065

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

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

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

3091
  shuffle(rnameservers.begin(), rnameservers.end(), pdns::dns_random_engine());
13,686✔
3092
  speedOrder speedCompare;
13,686✔
3093
  stable_sort(rnameservers.begin(), rnameservers.end(), speedCompare);
13,686✔
3094

3095
  if (doLog()) {
13,686✔
3096
    LOG(prefix << qname << ": Nameservers: ");
1,620!
3097
    for (auto i = rnameservers.begin(); i != rnameservers.end(); ++i) {
4,466✔
3098
      if (i != rnameservers.begin()) {
2,846✔
3099
        LOG(", ");
1,226!
3100
        if (((i - rnameservers.begin()) % 3) == 0) {
1,226✔
3101
          LOG(endl
232!
3102
              << prefix << "             ");
232✔
3103
        }
232✔
3104
      }
1,226✔
3105
      LOG(i->first.toLogString() << "(" << fmtfloat(i->second / 1000.0) << "ms)");
2,846!
3106
    }
2,846✔
3107
    LOG(endl);
1,620!
3108
  }
1,620✔
3109
  return rnameservers;
13,686✔
3110
}
14,008✔
3111

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

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

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

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

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

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

3171
    if (rec.d_type == QType::RRSIG) {
40,091✔
3172
      auto rrsig = getRR<RRSIGRecordContent>(rec);
9,877✔
3173
      if (rrsig) {
9,877!
3174
        if (rrsig->d_type == QType::SOA) {
9,877✔
3175
          negEntry.authoritySOA.signatures.push_back(rec);
579✔
3176
          if (lowestTTL != nullptr && isRRSIGNotExpired(now, *rrsig)) {
579✔
3177
            *lowestTTL = min(*lowestTTL, rec.d_ttl);
288✔
3178
            *lowestTTL = min(*lowestTTL, getRRSIGTTL(now, rrsig));
288✔
3179
          }
288✔
3180
        }
579✔
3181
        if (nsecTypes.count(rrsig->d_type) != 0) {
9,877✔
3182
          negEntry.DNSSECRecords.signatures.push_back(rec);
9,298✔
3183
          if (lowestTTL != nullptr && isRRSIGNotExpired(now, *rrsig)) {
9,298✔
3184
            *lowestTTL = min(*lowestTTL, rec.d_ttl);
8,899✔
3185
            *lowestTTL = min(*lowestTTL, getRRSIGTTL(now, rrsig));
8,899✔
3186
          }
8,899✔
3187
        }
9,298✔
3188
      }
9,877✔
3189
      continue;
9,877✔
3190
    }
9,877✔
3191
    if (rec.d_type == QType::SOA) {
30,214✔
3192
      negEntry.authoritySOA.records.push_back(rec);
2,970✔
3193
      if (lowestTTL != nullptr) {
2,970✔
3194
        *lowestTTL = min(*lowestTTL, rec.d_ttl);
1,530✔
3195
      }
1,530✔
3196
      continue;
2,970✔
3197
    }
2,970✔
3198
    if (nsecTypes.count(rec.d_type) != 0) {
27,244✔
3199
      negEntry.DNSSECRecords.records.push_back(rec);
9,310✔
3200
      if (lowestTTL != nullptr) {
9,310✔
3201
        *lowestTTL = min(*lowestTTL, rec.d_ttl);
8,913✔
3202
      }
8,913✔
3203
      continue;
9,310✔
3204
    }
9,310✔
3205
  }
27,244✔
3206
}
7,626✔
3207

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

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

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

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

3256
  return true;
60✔
3257
}
63✔
3258

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

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

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

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

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

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

3303
  switch (d_appliedPolicy.d_kind) {
105!
3304

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

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

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

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

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

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

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

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

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

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

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

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

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

3436
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)
3437
{
14,103✔
3438
  vector<ComboAddress> result;
14,103✔
3439

3440
  size_t nonresolvingfails = 0;
14,103✔
3441
  if (!tns->first.empty()) {
14,103✔
3442
    if (s_nonresolvingnsmaxfails > 0) {
13,919✔
3443
      nonresolvingfails = s_nonresolving.lock()->value(tns->first);
11,441✔
3444
      if (nonresolvingfails >= s_nonresolvingnsmaxfails) {
11,441✔
3445
        LOG(prefix << qname << ": NS " << tns->first << " in non-resolving map, skipping" << endl);
21!
3446
        return result;
21✔
3447
      }
21✔
3448
    }
11,441✔
3449

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

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

3485
    sendRDQuery = nameservers[tns->first].second;
184✔
3486
    result = shuffleForwardSpeed(qname, nameservers[tns->first].first, prefix, sendRDQuery);
184✔
3487
    pierceDontQuery = true;
184✔
3488
  }
184✔
3489
  return result;
13,992✔
3490
}
14,103✔
3491

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

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

3535
bool SyncRes::validationEnabled()
3536
{
15,324✔
3537
  return g_dnssecmode != DNSSECMode::Off && g_dnssecmode != DNSSECMode::ProcessNoValidate;
15,324✔
3538
}
15,324✔
3539

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

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

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

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

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

3577
  return lowestTTD;
14,038✔
3578
}
14,038✔
3579

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

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

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

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

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

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

3615
  return vState::Indeterminate;
58,565✔
3616
}
58,565✔
3617

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

3622
  for (const auto& dsRecordContent : dsset) {
48,990✔
3623
    if (isSupportedDS(dsRecordContent, LogObject(prefix))) {
48,997✔
3624
      count++;
48,988✔
3625
    }
48,988✔
3626
  }
48,990✔
3627

3628
  return count;
23,362✔
3629
}
23,362✔
3630

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

3651
      d_cutStates[zone] = result;
22,932✔
3652
    }
22,932✔
3653
  } while (zone.chopOff());
75,921✔
3654
}
22,778✔
3655

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

3660
  if (result != vState::Indeterminate || onlyTA) {
6,074!
3661
    if (foundCut != nullptr) {
488!
3662
      *foundCut = (result != vState::Indeterminate);
×
3663
    }
×
3664

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

3678
    return result;
488✔
3679
  }
488✔
3680

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

3684
  Context context;
5,586✔
3685

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

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

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

3701
  uint8_t bestDigestType = 0;
5,580✔
3702

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

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

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

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

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

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

3770
  return context.state;
2,969✔
3771
}
5,580✔
3772

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

3777
  if (!shouldValidate()) {
18,860✔
3778
    return result;
2,173✔
3779
  }
2,173✔
3780

3781
  DNSName subdomain(name);
16,687✔
3782
  if (typeIsDS) {
16,687✔
3783
    subdomain.chopOff();
3,152✔
3784
  }
3,152✔
3785

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

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

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

3818
    while (!labelsToAdd.empty()) {
4,526✔
3819

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

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

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

3833
        if (dsState != vState::Secure) {
4,434✔
3834
          return dsState;
2,557✔
3835
        }
2,557✔
3836
      }
4,434✔
3837
    }
4,465✔
3838

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

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

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

3860
  return result;
7,903✔
3861
}
10,521✔
3862

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

3871
  DNSName signer = getSigner(signatures);
1,590✔
3872

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

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

3892
  skeyset_t tentativeKeys;
1,559✔
3893
  sortedRecords_t toSign;
1,559✔
3894

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

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

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

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

3915
  /* if we found at least one valid RRSIG covering the set,
3916
     all tentative keys are validated keys. Otherwise it means
3917
     we haven't found at least one DNSKEY and a matching RRSIG
3918
     covering this set, this looks Bogus. */
3919
  if (validatedKeys.size() != tentativeKeys.size()) {
1,559✔
3920
    LOG(prefix << zone << ": Let's check whether we missed a zone cut before returning a Bogus state from " << static_cast<const char*>(__func__) << "(" << zone << ")" << endl);
44!
3921
    /* try again to get the missed cuts, harder this time */
3922
    auto zState = getValidationStatus(zone, false, false, depth, prefix);
44✔
3923
    if (zState == vState::Secure) {
44!
3924
      /* too bad */
3925
      LOG(prefix << zone << ": After checking the zone cuts we are still in a Secure zone, returning Bogus state from " << static_cast<const char*>(__func__) << "(" << zone << ")" << endl);
44!
3926
      return state;
44✔
3927
    }
44✔
3928
    return zState;
×
3929
  }
44✔
3930

3931
  return state;
1,515✔
3932
}
1,559✔
3933

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

3940
  Context context;
5,363✔
3941

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

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

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

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

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

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

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

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

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

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

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

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

4082
  LOG(prefix << vStateToString(state) << "!" << endl);
35!
4083

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

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

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

4112
  if (wasForwardRecurse || lwr.d_aabit) {
13,701✔
4113
    /* easy */
4114
    return;
9,584✔
4115
  }
9,584✔
4116

4117
  for (const auto& rec : lwr.d_records) {
4,117✔
4118

4119
    if (rec.d_type == QType::OPT) {
4,065!
4120
      continue;
×
4121
    }
×
4122

4123
    if (rec.d_class != QClass::IN) {
4,065!
4124
      continue;
×
4125
    }
×
4126

4127
    if (rec.d_type == QType::ANY) {
4,065!
4128
      continue;
×
4129
    }
×
4130

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

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

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

4199
static bool isRedirection(QType qtype)
4200
{
21,761✔
4201
  return qtype == QType::CNAME || qtype == QType::DNAME;
21,761✔
4202
}
21,761✔
4203

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

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

4220
  for (auto rec = lwr.d_records.cbegin(); rec != lwr.d_records.cend(); ++rec, ++counter) {
98,484✔
4221

4222
    // Allow OPT record containing EDNS(0) data
4223
    if (rec->d_type == QType::OPT) {
84,783✔
4224
      continue;
11,359✔
4225
    }
11,359✔
4226

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

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

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

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

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

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

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

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

4345
  if (!haveAnswers && lwr.d_rcode == RCode::NoError) {
13,701✔
4346
    acceptDelegation = true;
5,529✔
4347
  }
5,529✔
4348

4349
  sanitizeRecordsPass2(prefix, lwr, qname, qtype, auth, allowedAnswerNames, allowedAdditionals, cnameSeen, acceptDelegation && !soaInAuth, skipvec, skipCount);
13,701✔
4350
}
13,701✔
4351

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

4358
    if (skipvec[counter]) {
84,785✔
4359
      continue;
4,363✔
4360
    }
4,363✔
4361
    // Allow OPT record containing EDNS(0) data
4362
    if (rec->d_type == QType::OPT) {
80,422✔
4363
      continue;
11,359✔
4364
    }
11,359✔
4365

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

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

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

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

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

4470
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, bool overTCP) // NOLINT(readability-function-cognitive-complexity)
4471
{
13,701✔
4472
  bool wasForwardRecurse = wasForwarded && rdQuery;
13,701✔
4473
  tcache_t tcache;
13,701✔
4474

4475
  fixupAnswer(prefix, lwr, qname, qtype, auth, wasForwarded, rdQuery);
13,701✔
4476
  sanitizeRecords(prefix, lwr, qname, qtype, auth, wasForwarded, rdQuery);
13,701✔
4477

4478
  MemRecursorCache::AuthRecsVec authorityRecs;
13,701✔
4479
  bool isCNAMEAnswer = false;
13,701✔
4480
  bool isDNAMEAnswer = false;
13,701✔
4481
  DNSName seenAuth;
13,701✔
4482

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

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

4513
  for (auto& rec : lwr.d_records) {
76,849✔
4514
    if (rec.d_type == QType::OPT || rec.d_class != QClass::IN) {
76,849!
4515
      continue;
11,359✔
4516
    }
11,359✔
4517

4518
    rec.d_ttl = min(s_maxcachettl, rec.d_ttl);
65,490✔
4519

4520
    if (!isCNAMEAnswer && rec.d_place == DNSResourceRecord::ANSWER && rec.d_type == QType::CNAME && (!(qtype == QType::CNAME)) && rec.d_name == qname && !isDNAMEAnswer) {
65,490!
4521
      isCNAMEAnswer = true;
1,611✔
4522
    }
1,611✔
4523
    if (!isDNAMEAnswer && rec.d_place == DNSResourceRecord::ANSWER && rec.d_type == QType::DNAME && qtype != QType::DNAME && qname.isPartOf(rec.d_name)) {
65,492!
4524
      isDNAMEAnswer = true;
32✔
4525
      isCNAMEAnswer = false;
32✔
4526
    }
32✔
4527

4528
    if (rec.d_type == QType::SOA && rec.d_place == DNSResourceRecord::AUTHORITY && qname.isPartOf(rec.d_name)) {
65,490!
4529
      seenAuth = rec.d_name;
1,536✔
4530
    }
1,536✔
4531

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

4558
        // cerr<<"Got an RRSIG for "<<DNSRecordContent::NumberToType(rrsig->d_type)<<" with name '"<<rec.d_name<<"' and place "<<rec.d_place<<endl;
4559
        tcache[{rec.d_name, rrsig->d_type, rec.d_place}].signatures.push_back(rrsig);
9,673✔
4560
        tcache[{rec.d_name, rrsig->d_type, rec.d_place}].signaturesTTL = std::min(tcache[{rec.d_name, rrsig->d_type, rec.d_place}].signaturesTTL, rec.d_ttl);
9,673✔
4561
      }
9,673✔
4562
    }
9,673✔
4563
  }
65,490✔
4564

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

4574
      if (nsecTypes.count(rec.d_type) != 0) {
2,486✔
4575
        authorityRecs.emplace_back(rec);
619✔
4576
      }
619✔
4577
      else if (rec.d_type == QType::RRSIG) {
1,867✔
4578
        auto rrsig = getRR<RRSIGRecordContent>(rec);
1,242✔
4579
        if (rrsig && nsecTypes.count(rrsig->d_type) != 0) {
1,242!
4580
          authorityRecs.emplace_back(rec);
619✔
4581
        }
619✔
4582
      }
1,242✔
4583
    }
2,486✔
4584
  }
617✔
4585

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

4593
    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 << " ");
65,489✔
4594

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

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

4638
        rec.d_ttl = min(s_maxcachettl, rec.d_ttl);
55,782✔
4639

4640
        DNSRecord dnsRecord(rec);
55,782✔
4641
        tcache[{rec.d_name, rec.d_type, rec.d_place}].d_ttl_time = d_now.tv_sec;
55,782✔
4642
        dnsRecord.d_ttl += d_now.tv_sec;
55,782✔
4643
        dnsRecord.d_place = DNSResourceRecord::ANSWER;
55,782✔
4644
        tcache[{rec.d_name, rec.d_type, rec.d_place}].records.push_back(std::move(dnsRecord));
55,782✔
4645
      }
55,782✔
4646
    }
65,488✔
4647
    else
2,147,483,647✔
4648
      LOG("NO!" << endl);
2,147,498,821!
4649
  }
65,457✔
4650

4651
  // supplant
4652
  for (auto& entry : tcache) {
39,250✔
4653
    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)
39,250✔
4654
      uint32_t lowestTTD = computeLowestTTD(entry.second.records, entry.second.signatures, entry.second.signaturesTTL, authorityRecs);
14,038✔
4655

4656
      for (auto& record : entry.second.records) {
30,598✔
4657
        record.d_ttl = lowestTTD; // boom
30,598✔
4658
      }
30,598✔
4659
    }
14,038✔
4660
  }
39,250✔
4661

4662
  bool seenBogusRRSet = false;
13,701✔
4663
  for (auto tCacheEntry = tcache.begin(); tCacheEntry != tcache.end(); ++tCacheEntry) {
52,948✔
4664

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

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

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

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

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

4739
    vState recordState = vState::Indeterminate;
39,221✔
4740

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

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

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

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

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

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

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

4817
      d_fromAuthIP = remoteIP;
35,021✔
4818

4819
      if (doCache) {
35,021✔
4820
        // Check if we are going to replace a non-auth (parent) NS recordset
4821
        if (isAA && tCacheEntry->first.type == QType::NS && s_save_parent_ns_set) {
34,987!
4822
          rememberParentSetIfNeeded(tCacheEntry->first.name, tCacheEntry->second.records, depth, prefix);
276✔
4823
        }
276✔
4824
        bool thisRRNeedsWildcardProof = false;
34,987✔
4825
        if (gatherWildcardProof) {
34,987✔
4826
          if (auto wcIt = wildcardCandidates.find(tCacheEntry->first.name); wcIt != wildcardCandidates.end() && wcIt->second) {
1,216✔
4827
            thisRRNeedsWildcardProof = true;
621✔
4828
          }
621✔
4829
        }
1,216✔
4830
        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, MemRecursorCache::Extra{remoteIP, overTCP}, d_refresh, tCacheEntry->second.d_ttl_time);
34,987✔
4831

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

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

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

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

4855
            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, MemRecursorCache::Extra{remoteIP, overTCP}, d_refresh, tCacheEntry->second.d_ttl_time);
593!
4856
          }
593✔
4857
        }
593✔
4858
      }
34,987✔
4859
    }
35,021✔
4860

4861
    if (seenAuth.empty() && !tCacheEntry->second.signatures.empty()) {
39,221✔
4862
      seenAuth = getSigner(tCacheEntry->second.signatures);
6,123✔
4863
    }
6,123✔
4864

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

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

4875
  if (gatherWildcardProof) {
13,701✔
4876
    if (auto wcIt = wildcardCandidates.find(qname); wcIt != wildcardCandidates.end() && !wcIt->second) {
617!
4877
      // 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
4878
      gatherWildcardProof = false;
2✔
4879
    }
2✔
4880
  }
617✔
4881

4882
  return RCode::NoError;
13,701✔
4883
}
13,701✔
4884

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

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

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

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

4935
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)
4936
{
613✔
4937
  /* positive answer synthesized from a wildcard */
4938
  NegCache::NegCacheEntry negEntry;
613✔
4939
  negEntry.d_name = qname;
613✔
4940
  negEntry.d_qtype = QType::ENT; // this encodes 'whole record'
613✔
4941
  uint32_t lowestTTL = rec.d_ttl;
613✔
4942
  harvestNXRecords(lwr.d_records, negEntry, d_now.tv_sec, &lowestTTL);
613✔
4943

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

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

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

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

4986
  for (auto& rec : lwr.d_records) {
76,758✔
4987
    if (rec.d_type == QType::OPT || rec.d_class != QClass::IN) {
76,758!
4988
      continue;
11,355✔
4989
    }
11,355✔
4990

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

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

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

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

5015
      NegCache::NegCacheEntry negEntry;
160✔
5016

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

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

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

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

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

5123
      done = true;
10,247✔
5124
      rcode = RCode::NoError;
10,247✔
5125

5126
      if (needWildcardProof) {
10,247✔
5127
        checkWildcardProof(qname, qtype, rec, lwr, state, depth, prefix, wildcardLabelsCount);
601✔
5128
      }
601✔
5129

5130
      ret.push_back(rec);
10,247✔
5131
    }
10,247✔
5132
    else if ((rec.d_type == QType::RRSIG || rec.d_type == QType::NSEC || rec.d_type == QType::NSEC3) && rec.d_place == DNSResourceRecord::ANSWER) {
52,119✔
5133
      if (rec.d_type != QType::RRSIG || rec.d_name == qname) {
3,045!
5134
        ret.push_back(rec); // enjoy your DNSSEC
3,012✔
5135
      }
3,012✔
5136
      else if (rec.d_type == QType::RRSIG && qname.isPartOf(rec.d_name)) {
33!
5137
        auto rrsig = getRR<RRSIGRecordContent>(rec);
24✔
5138
        if (rrsig != nullptr && rrsig->d_type == QType::DNAME) {
24!
5139
          ret.push_back(rec);
24✔
5140
        }
24✔
5141
      }
24✔
5142
    }
3,045✔
5143
    else if (rec.d_place == DNSResourceRecord::AUTHORITY && rec.d_type == QType::NS && qname.isPartOf(rec.d_name)) {
49,074!
5144
      if (moreSpecificThan(rec.d_name, auth)) {
16,017!
5145
        newauth = rec.d_name;
16,017✔
5146
        LOG(prefix << qname << ": Got NS record '" << rec.d_name << "' -> '" << rec.getContent()->getZoneRepresentation() << "'" << endl);
16,017✔
5147

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

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

5179
        if (recordState == vState::Secure) {
4,004✔
5180
          negEntry.d_auth = auth;
2,103✔
5181
          negEntry.d_name = newauth;
2,103✔
5182
          negEntry.d_qtype = QType::DS;
2,103✔
5183
          rec.d_ttl = min(s_maxnegttl, rec.d_ttl);
2,103✔
5184

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

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

5196
            g_negCache->add(negEntry);
2,099✔
5197

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

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

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

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

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

5257
        ret.push_back(rec);
1,366✔
5258
        negindic = true;
1,366✔
5259
        negIndicHasSignatures = !negEntry.authoritySOA.signatures.empty() || !negEntry.DNSSECRecords.signatures.empty();
1,366!
5260
      }
1,366✔
5261
    }
1,371✔
5262
  }
65,403✔
5263

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

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

5290
  return done;
13,679✔
5291
}
13,679✔
5292

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

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

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

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

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

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

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

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

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

5439
  if (remoteIP.sin4.sin_family == AF_INET) {
325✔
5440
    t_Counters.at(rec::Counter::outgoing4timeouts)++;
175✔
5441
  }
175✔
5442
  else {
150✔
5443
    t_Counters.at(rec::Counter::outgoing6timeouts)++;
150✔
5444
  }
150✔
5445

5446
  if (t_timeouts) {
325✔
5447
    t_timeouts->push_back(remoteIP);
10✔
5448
  }
10✔
5449
}
325✔
5450

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

5461
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)
5462
{
14,072✔
5463
  checkTotalTime(qname, qtype, extendedError);
14,072✔
5464

5465
  bool chained = false;
14,072✔
5466
  LWResult::Result resolveret = LWResult::Result::Success;
14,072✔
5467
  int preOutQueryRet = RCode::NoError;
14,072✔
5468

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

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

5502
  d_totUsec += lwr.d_usec;
14,068✔
5503

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

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

5514
  if (!dontThrottle) {
14,065✔
5515
    dontThrottle = shouldNotThrottle(&nsName, &remoteIP);
14,056✔
5516
  }
14,056✔
5517

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

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

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

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

5576
    return false;
401✔
5577
  }
401✔
5578

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

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

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

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

5632
  if (lwr.d_tcbit) {
13,587✔
5633
    truncated = true;
15✔
5634

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

5645
    return true;
13✔
5646
  }
15✔
5647

5648
  return true;
13,572✔
5649
}
13,587✔
5650

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

5665
  if (!d_followCNAME) {
1,628✔
5666
    rcode = RCode::NoError;
94✔
5667
    return;
94✔
5668
  }
94✔
5669

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

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

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

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

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

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

5704
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, bool overTCP)
5705
{
13,701✔
5706
  if (s_minimumTTL != 0) {
13,701✔
5707
    for (auto& rec : lwr.d_records) {
78,743✔
5708
      rec.d_ttl = max(rec.d_ttl, s_minimumTTL);
78,743✔
5709
    }
78,743✔
5710
  }
11,468✔
5711

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

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

5730
  LOG(prefix << qname << ": Determining status after receiving this packet" << endl);
13,701✔
5731

5732
  set<DNSName> nsset;
13,701✔
5733
  bool realreferral = false;
13,701✔
5734
  bool negindic = false;
13,701✔
5735
  bool negIndicHasSignatures = false;
13,701✔
5736
  DNSName newauth;
13,701✔
5737
  DNSName newtarget;
13,701✔
5738

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

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

5752
  if (!newtarget.empty()) {
7,340✔
5753
    handleNewTarget(prefix, qname, newtarget, qtype.getCode(), ret, *rcode, depth, lwr.d_records, state);
1,621✔
5754
    return true;
1,621✔
5755
  }
1,621✔
5756

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

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

5771
    if (d_doDNSSEC) {
170✔
5772
      addNXNSECS(ret, lwr.d_records);
136✔
5773
    }
136✔
5774

5775
    *rcode = RCode::NXDomain;
170✔
5776
    return true;
170✔
5777
  }
170✔
5778

5779
  if (nsset.empty() && lwr.d_rcode == 0 && (negindic || lwr.d_aabit || sendRDQuery)) {
5,549!
5780
    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,413!
5781

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

5793
    if (d_doDNSSEC) {
1,413✔
5794
      addNXNSECS(ret, lwr.d_records);
1,343✔
5795
    }
1,343✔
5796

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

5801
  if (realreferral) {
4,136✔
5802
    LOG(prefix << qname << ": Status=did not resolve, got " << (unsigned int)nsset.size() << " NS, ");
4,061✔
5803

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

5828
    return false;
4,059✔
5829
  }
4,061✔
5830

5831
  return false;
75✔
5832
}
4,136✔
5833

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

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

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

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

5864
  LOG(endl);
11,555✔
5865

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

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

5880
    for (auto tns = rnameservers.cbegin();; ++tns) {
14,495✔
5881
      if (addressQueriesForNS >= nsLimit) {
14,495✔
5882
        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✔
5883
      }
2✔
5884
      if (tns == rnameservers.cend()) {
14,493✔
5885
        LOG(prefix << qname << ": Failed to resolve via any of the " << (unsigned int)rnameservers.size() << " offered NS at level '" << auth << "'" << endl);
250✔
5886
        if (s_addExtendedResolutionDNSErrors) {
250✔
5887
          context.extendedError = EDNSExtendedError{static_cast<uint16_t>(EDNSExtendedError::code::NoReachableAuthority), "delegation " + auth.toLogString()};
62✔
5888
        }
62✔
5889
        if (!auth.isRoot() && flawedNSSet) {
250✔
5890
          LOG(prefix << qname << ": Ageing nameservers for level '" << auth << "', next query might succeed" << endl);
66!
5891
          if (g_recCache->doAgeCache(d_now.tv_sec, auth, QType::NS, 10)) {
66✔
5892
            t_Counters.at(rec::Counter::nsSetInvalidations)++;
18✔
5893
          }
18✔
5894
        }
66✔
5895
        return -1;
250✔
5896
      }
250✔
5897

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

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

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

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

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

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

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

5988
          bool truncated = false;
14,056✔
5989
          bool spoofed = false;
14,056✔
5990
          bool gotAnswer = false;
14,056✔
5991
          bool doDoT = false;
14,056✔
5992

5993
          if (doDoTtoAuth(tns->first)) {
14,056✔
5994
            remoteIP->setPort(853);
9✔
5995
            doDoT = true;
9✔
5996
          }
9✔
5997
          if (SyncRes::s_dot_to_port_853 && remoteIP->getPort() == 853) {
14,056✔
5998
            doDoT = true;
25✔
5999
          }
25✔
6000
          bool forceTCP = doDoT;
14,056✔
6001

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

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

6028
          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);
13,576✔
6029

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

6039
          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);
13,576✔
6040

6041
          /* we have received an answer, are we done ? */
6042
          bool done = processAnswer(depth, prefix, lwr, qname, qtype, auth, wasForwarded, ednsmask, sendRDQuery, nameservers, ret, luaconfsLocal->dfe, &gotNewServers, &rcode, context.state, *remoteIP, overTCP);
13,576✔
6043
          if (done) {
13,576✔
6044
            return rcode;
9,425✔
6045
          }
9,425✔
6046
          if (gotNewServers) {
4,151✔
6047
            if (stopAtDelegation != nullptr && *stopAtDelegation == Stop) {
4,055!
6048
              *stopAtDelegation = Stopped;
1,606✔
6049
              return rcode;
1,606✔
6050
            }
1,606✔
6051
            break;
2,449✔
6052
          }
4,055✔
6053
          /* was lame */
6054
          if (!shouldNotThrottle(&tns->first, &*remoteIP)) {
96✔
6055
            doThrottle(d_now.tv_sec, *remoteIP, qname, qtype, 60, 100, Throttle::Reason::Lame);
52✔
6056
          }
52✔
6057
        }
96✔
6058

6059
        if (gotNewServers) {
2,932✔
6060
          break;
2,449✔
6061
        }
2,449✔
6062

6063
        if (remoteIP == remoteIPs.cend()) { // we tried all IP addresses, none worked
483✔
6064
          continue;
349✔
6065
        }
349✔
6066
      }
483✔
6067
    }
14,243✔
6068
  }
14,008✔
6069
  return -1;
134✔
6070
}
11,555✔
6071

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

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

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

6127
boost::optional<Netmask> SyncRes::getEDNSSubnetMask(const DNSName& name, const ComboAddress& rem)
6128
{
14,066✔
6129
  if (d_outgoingECSNetwork && (s_ednsdomains.check(name) || s_ednsremotesubnets.match(rem))) {
14,066✔
6130
    return d_outgoingECSNetwork;
583✔
6131
  }
583✔
6132
  return boost::none;
13,483✔
6133
}
14,066✔
6134

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

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

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

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

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

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

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

6203
  return res;
24✔
6204
}
24✔
6205

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

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

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

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

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