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

PowerDNS / pdns / 12323094430

13 Dec 2024 09:11PM UTC coverage: 64.759% (-0.02%) from 64.78%
12323094430

Pull #14970

github

web-flow
Merge 3e4597ff7 into 3dfd8e317
Pull Request #14970: boost > std optional

37533 of 88820 branches covered (42.26%)

Branch coverage included in aggregate %.

17 of 19 new or added lines in 4 files covered. (89.47%)

79 existing lines in 16 files now uncovered.

125890 of 163537 relevant lines covered (76.98%)

4110788.26 hits per line

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

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

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

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

43
rec::GlobalCounters g_Counters;
44
thread_local rec::TCounters t_Counters(g_Counters);
45

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

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

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

70
  [[nodiscard]] counter_t value(const T& arg) const
71
  {
8,780✔
72
    auto iter = d_cont.find(arg);
8,780✔
73

74
    if (iter == d_cont.end()) {
8,780✔
75
      return 0;
8,742✔
76
    }
8,742✔
77
    return iter->value;
38✔
78
  }
8,780✔
79

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

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

93
  void clear(const T& arg)
94
  {
10,775✔
95
    d_cont.erase(arg);
10,775✔
96
  }
10,775✔
97

98
  void clear()
99
  {
1,192✔
100
    d_cont.clear();
1,192✔
101
  }
1,192✔
102

103
  [[nodiscard]] size_t size() const
104
  {
1,518✔
105
    return d_cont.size();
1,518✔
106
  }
1,518✔
107

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

114
private:
115
  cont_t d_cont;
116
};
117

118
/** Class that implements a decaying EWMA.
119
    This class keeps an exponentially weighted moving average which, additionally, decays over time.
120
    The decaying is only done on get.
121
*/
122

123
//! This represents a number of decaying Ewmas, used to store performance per nameserver-name.
124
/** Modelled to work mostly like the underlying DecayingEwma */
125
class DecayingEwmaCollection
126
{
127
private:
128
  struct DecayingEwma
129
  {
130
  public:
131
    void submit(int arg, const struct timeval& last, const struct timeval& now)
132
    {
11,158✔
133
      d_last = arg;
11,158✔
134
      auto val = static_cast<float>(arg);
11,158✔
135
      if (d_val == 0) {
11,158✔
136
        d_val = val;
5,411✔
137
      }
5,411✔
138
      else {
5,747✔
139
        auto diff = makeFloat(last - now);
5,747✔
140
        auto factor = expf(diff) / 2.0F; // might be '0.5', or 0.0001
5,747✔
141
        d_val = (1.0F - factor) * val + factor * d_val;
5,747✔
142
      }
5,747✔
143
    }
11,158✔
144

145
    float get(float factor)
146
    {
43,769✔
147
      return d_val *= factor;
43,769✔
148
    }
43,769✔
149

150
    [[nodiscard]] float peek() const
151
    {
20✔
152
      return d_val;
20✔
153
    }
20✔
154

155
    [[nodiscard]] int last() const
156
    {
×
157
      return d_last;
×
158
    }
×
159

160
    float d_val{0};
161
    int d_last{0};
162
  };
163

164
public:
165
  DecayingEwmaCollection(DNSName name, const struct timeval val = {0, 0}) :
166
    d_name(std::move(name)), d_lastget(val)
167
  {
79,159✔
168
  }
79,159✔
169

170
  void submit(const ComboAddress& remote, int usecs, const struct timeval& now) const
171
  {
11,158✔
172
    d_collection[remote].submit(usecs, d_lastget, now);
11,158✔
173
  }
11,158✔
174

175
  float getFactor(const struct timeval& now) const
176
  {
41,319✔
177
    float diff = makeFloat(d_lastget - now);
41,319✔
178
    return expf(diff / 60.0F); // is 1.0 or less
41,319✔
179
  }
41,319✔
180

181
  bool stale(time_t limit) const
182
  {
×
183
    return limit > d_lastget.tv_sec;
×
184
  }
×
185

186
  void purge(const std::map<ComboAddress, float>& keep) const
187
  {
11,057✔
188
    for (auto iter = d_collection.begin(); iter != d_collection.end();) {
23,457✔
189
      if (keep.find(iter->first) != keep.end()) {
12,400✔
190
        ++iter;
12,399✔
191
      }
12,399✔
192
      else {
1✔
193
        iter = d_collection.erase(iter);
1✔
194
      }
1✔
195
    }
12,400✔
196
  }
11,057✔
197

198
  // d_collection is the modifyable part of the record, we index on DNSName and timeval, and DNSName never changes
199
  mutable std::map<ComboAddress, DecayingEwma> d_collection;
200
  DNSName d_name;
201
  struct timeval d_lastget;
202
};
203

204
class nsspeeds_t : public multi_index_container<DecayingEwmaCollection,
205
                                                indexed_by<
206
                                                  hashed_unique<tag<DNSName>, member<DecayingEwmaCollection, const DNSName, &DecayingEwmaCollection::d_name>>,
207
                                                  ordered_non_unique<tag<timeval>, member<DecayingEwmaCollection, timeval, &DecayingEwmaCollection::d_lastget>>>>
208
{
209
public:
210
  const auto& find_or_enter(const DNSName& name, const struct timeval& now)
211
  {
22,215✔
212
    const auto iter = insert(DecayingEwmaCollection{name, now}).first;
22,215✔
213
    return *iter;
22,215✔
214
  }
22,215✔
215

216
  const auto& find_or_enter(const DNSName& name)
217
  {
20✔
218
    const auto iter = insert(DecayingEwmaCollection{name}).first;
20✔
219
    return *iter;
20✔
220
  }
20✔
221

222
  float fastest(const DNSName& name, const struct timeval& now)
223
  {
56,924✔
224
    auto& ind = get<DNSName>();
56,924✔
225
    auto iter = insert(DecayingEwmaCollection{name, now}).first;
56,924✔
226
    if (iter->d_collection.empty()) {
56,924✔
227
      return 0;
26,662✔
228
    }
26,662✔
229
    // This could happen if find(DNSName) entered an entry; it's used only by test code
230
    if (iter->d_lastget.tv_sec == 0 && iter->d_lastget.tv_usec == 0) {
30,262!
231
      ind.modify(iter, [&](DecayingEwmaCollection& dec) { dec.d_lastget = now; });
×
232
    }
×
233

234
    float ret = std::numeric_limits<float>::max();
30,262✔
235
    const float factor = iter->getFactor(now);
30,262✔
236
    for (auto& entry : iter->d_collection) {
31,370✔
237
      if (float tmp = entry.second.get(factor); tmp < ret) {
31,370✔
238
        ret = tmp;
30,308✔
239
      }
30,308✔
240
    }
31,370✔
241
    ind.modify(iter, [&](DecayingEwmaCollection& dec) { dec.d_lastget = now; });
30,262✔
242
    return ret;
30,262✔
243
  }
56,924✔
244
};
245

246
static LockGuarded<nsspeeds_t> s_nsSpeeds;
247

248
class Throttle
249
{
250
public:
251
  Throttle() = default;
158✔
252
  ~Throttle() = default;
×
253
  Throttle(Throttle&&) = delete;
254
  Throttle& operator=(const Throttle&) = default;
255
  Throttle& operator=(Throttle&&) = delete;
256
  Throttle(const Throttle&) = delete;
257

258
  using Key = std::tuple<ComboAddress, DNSName, QType>;
259
  using Reason = SyncRes::ThrottleReason;
260

261
  struct entry_t
262
  {
263
    entry_t(Key thing_, time_t ttd_, unsigned int count_, Reason reason_) :
264
      thing(std::move(thing_)), ttd(ttd_), count(count_), reason(reason_)
265
    {
202✔
266
    }
202✔
267
    Key thing;
268
    time_t ttd;
269
    mutable unsigned int count;
270
    Reason reason;
271
  };
272
  using cont_t = multi_index_container<entry_t,
273
                                       indexed_by<
274
                                         ordered_unique<tag<Key>, member<entry_t, Key, &entry_t::thing>>,
275
                                         ordered_non_unique<tag<time_t>, member<entry_t, time_t, &entry_t::ttd>>>>;
276

277
  bool shouldThrottle(time_t now, const Key& arg)
278
  {
22,530✔
279
    auto iter = d_cont.find(arg);
22,530✔
280
    if (iter == d_cont.end()) {
22,530✔
281
      return false;
22,468✔
282
    }
22,468✔
283
    if (now > iter->ttd || iter->count == 0) {
62✔
284
      d_cont.erase(iter);
4✔
285
      return false;
4✔
286
    }
4✔
287
    iter->count--;
58✔
288

289
    return true; // still listed, still blocked
58✔
290
  }
62✔
291

292
  void throttle(time_t now, const Key& arg, time_t ttl, unsigned int count, Reason reason)
293
  {
202✔
294
    auto iter = d_cont.find(arg);
202✔
295
    time_t ttd = now + ttl;
202✔
296
    if (iter == d_cont.end()) {
202!
297
      d_cont.emplace(arg, ttd, count, reason);
202✔
298
    }
202✔
299
    else if (ttd > iter->ttd || count > iter->count) {
×
300
      ttd = std::max(iter->ttd, ttd);
×
301
      count = std::max(iter->count, count);
×
302
      auto& ind = d_cont.template get<Key>();
×
303
      ind.modify(iter, [ttd, count, reason](entry_t& entry) {
×
304
        entry.ttd = ttd;
×
305
        entry.count = count;
×
306
        entry.reason = reason;
×
307
      });
×
308
    }
×
309
  }
202✔
310

311
  [[nodiscard]] size_t size() const
312
  {
755✔
313
    return d_cont.size();
755✔
314
  }
755✔
315

316
  [[nodiscard]] cont_t getThrottleMap() const
317
  {
×
318
    return d_cont;
×
319
  }
×
320

321
  void clear()
322
  {
596✔
323
    d_cont.clear();
596✔
324
  }
596✔
325

326
  void clear(const Key& thing)
327
  {
21,546✔
328
    d_cont.erase(thing);
21,546✔
329
  }
21,546✔
330
  void prune(time_t now)
331
  {
109✔
332
    auto& ind = d_cont.template get<time_t>();
109✔
333
    ind.erase(ind.begin(), ind.upper_bound(now));
109✔
334
  }
109✔
335

336
  static std::string toString(Reason reason)
337
  {
×
338
    static const std::array<std::string, 10> reasons = {
×
339
      "None",
×
340
      "ServerDown",
×
341
      "PermanentError",
×
342
      "Timeout",
×
343
      "ParseError",
×
344
      "RCodeServFail",
×
345
      "RCodeRefused",
×
346
      "RCodeOther",
×
347
      "TCPTruncate",
×
348
      "Lame"};
×
349
    const auto index = static_cast<unsigned int>(reason);
×
350
    if (index >= reasons.size()) {
×
351
      return "?";
×
352
    }
×
353
    return reasons.at(index);
×
354
  }
×
355

356
private:
357
  cont_t d_cont;
358
};
359

360
static LockGuarded<Throttle> s_throttle;
361

362
struct SavedParentEntry
363
{
364
  SavedParentEntry(DNSName name, map<DNSName, vector<ComboAddress>>&& nsAddresses, time_t ttd) :
365
    d_domain(std::move(name)), d_nsAddresses(std::move(nsAddresses)), d_ttd(ttd)
366
  {
4✔
367
  }
4✔
368
  DNSName d_domain;
369
  map<DNSName, vector<ComboAddress>> d_nsAddresses;
370
  time_t d_ttd;
371
  mutable uint64_t d_count{0};
372
};
373

374
using SavedParentNSSetBase = multi_index_container<
375
  SavedParentEntry,
376
  indexed_by<ordered_unique<tag<DNSName>, member<SavedParentEntry, DNSName, &SavedParentEntry::d_domain>>,
377
             ordered_non_unique<tag<time_t>, member<SavedParentEntry, time_t, &SavedParentEntry::d_ttd>>>>;
378

379
class SavedParentNSSet : public SavedParentNSSetBase
380
{
381
public:
382
  void prune(time_t now)
383
  {
80✔
384
    auto& ind = get<time_t>();
80✔
385
    ind.erase(ind.begin(), ind.upper_bound(now));
80✔
386
  }
80✔
387
  void inc(const DNSName& name)
388
  {
2✔
389
    auto iter = find(name);
2✔
390
    if (iter != end()) {
2!
391
      ++(*iter).d_count;
2✔
392
    }
2✔
393
  }
2✔
394
  [[nodiscard]] SavedParentNSSet getMapCopy() const
395
  {
×
396
    return *this;
×
397
  }
×
398
};
399

400
static LockGuarded<SavedParentNSSet> s_savedParentNSSet;
401

402
thread_local SyncRes::ThreadLocalStorage SyncRes::t_sstorage;
403
thread_local std::unique_ptr<addrringbuf_t> t_timeouts;
404

405
std::unique_ptr<NetmaskGroup> SyncRes::s_dontQuery{nullptr};
406
NetmaskGroup SyncRes::s_ednslocalsubnets;
407
NetmaskGroup SyncRes::s_ednsremotesubnets;
408
SuffixMatchNode SyncRes::s_ednsdomains;
409
EDNSSubnetOpts SyncRes::s_ecsScopeZero;
410
string SyncRes::s_serverID;
411
SyncRes::LogMode SyncRes::s_lm;
412
static LockGuarded<fails_t<ComboAddress>> s_fails;
413
static LockGuarded<fails_t<DNSName>> s_nonresolving;
414

415
struct DoTStatus
416
{
417
  DoTStatus(const ComboAddress& address, DNSName auth, time_t ttd) :
418
    d_address(address), d_auth(std::move(auth)), d_ttd(ttd)
419
  {
×
420
  }
×
421
  enum Status : uint8_t
422
  {
423
    Unknown,
424
    Busy,
425
    Bad,
426
    Good
427
  };
428
  ComboAddress d_address;
429
  DNSName d_auth;
430
  time_t d_ttd;
431
  mutable uint64_t d_count{0};
432
  mutable Status d_status{Unknown};
433
  std::string toString() const
434
  {
×
435
    const std::array<std::string, 4> names{"Unknown", "Busy", "Bad", "Good"};
×
436
    auto val = static_cast<unsigned int>(d_status);
×
437
    return val >= names.size() ? "?" : names.at(val);
×
438
  }
×
439
};
440

441
struct DoTMap
442
{
443
  multi_index_container<DoTStatus,
444
                        indexed_by<
445
                          ordered_unique<tag<ComboAddress>, member<DoTStatus, const ComboAddress, &DoTStatus::d_address>>,
446
                          ordered_non_unique<tag<time_t>, member<DoTStatus, time_t, &DoTStatus::d_ttd>>>>
447
    d_map;
448
  uint64_t d_numBusy{0};
449

450
  void prune(time_t cutoff)
451
  {
×
452
    auto& ind = d_map.template get<time_t>();
×
453
    ind.erase(ind.begin(), ind.upper_bound(cutoff));
×
454
  }
×
455
};
456

457
static LockGuarded<DoTMap> s_dotMap;
458

459
static const time_t dotFailWait = static_cast<time_t>(24) * 3600;
460
static const time_t dotSuccessWait = static_cast<time_t>(3) * 24 * 3600;
461
static bool shouldDoDoT(ComboAddress address, time_t now);
462

463
unsigned int SyncRes::s_maxnegttl;
464
unsigned int SyncRes::s_maxbogusttl;
465
unsigned int SyncRes::s_maxcachettl;
466
unsigned int SyncRes::s_maxqperq;
467
unsigned int SyncRes::s_maxnsperresolve;
468
unsigned int SyncRes::s_maxnsaddressqperq;
469
unsigned int SyncRes::s_maxtotusec;
470
unsigned int SyncRes::s_maxdepth;
471
unsigned int SyncRes::s_minimumTTL;
472
unsigned int SyncRes::s_minimumECSTTL;
473
unsigned int SyncRes::s_packetcachettl;
474
unsigned int SyncRes::s_packetcacheservfailttl;
475
unsigned int SyncRes::s_packetcachenegativettl;
476
unsigned int SyncRes::s_serverdownmaxfails;
477
unsigned int SyncRes::s_serverdownthrottletime;
478
unsigned int SyncRes::s_unthrottle_n;
479
unsigned int SyncRes::s_nonresolvingnsmaxfails;
480
unsigned int SyncRes::s_nonresolvingnsthrottletime;
481
unsigned int SyncRes::s_ecscachelimitttl;
482
unsigned int SyncRes::s_maxvalidationsperq;
483
unsigned int SyncRes::s_maxnsec3iterationsperq;
484
pdns::stat_t SyncRes::s_ecsqueries;
485
pdns::stat_t SyncRes::s_ecsresponses;
486
std::map<uint8_t, pdns::stat_t> SyncRes::s_ecsResponsesBySubnetSize4;
487
std::map<uint8_t, pdns::stat_t> SyncRes::s_ecsResponsesBySubnetSize6;
488

489
uint8_t SyncRes::s_ecsipv4limit;
490
uint8_t SyncRes::s_ecsipv6limit;
491
uint8_t SyncRes::s_ecsipv4cachelimit;
492
uint8_t SyncRes::s_ecsipv6cachelimit;
493
bool SyncRes::s_ecsipv4nevercache;
494
bool SyncRes::s_ecsipv6nevercache;
495

496
bool SyncRes::s_doIPv4;
497
bool SyncRes::s_doIPv6;
498
bool SyncRes::s_rootNXTrust;
499
bool SyncRes::s_noEDNS;
500
bool SyncRes::s_qnameminimization;
501
SyncRes::HardenNXD SyncRes::s_hardenNXD;
502
unsigned int SyncRes::s_refresh_ttlperc;
503
unsigned int SyncRes::s_locked_ttlperc;
504
int SyncRes::s_tcp_fast_open;
505
bool SyncRes::s_tcp_fast_open_connect;
506
bool SyncRes::s_dot_to_port_853;
507
int SyncRes::s_event_trace_enabled;
508
bool SyncRes::s_save_parent_ns_set;
509
unsigned int SyncRes::s_max_busy_dot_probes;
510
unsigned int SyncRes::s_max_CNAMES_followed;
511
bool SyncRes::s_addExtendedResolutionDNSErrors;
512

513
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
514
#define LOG(x)                       \
515
  if (d_lm == Log) {                 \
827,193✔
516
    g_log << Logger::Warning << x;   \
101,548✔
517
  }                                  \
101,548✔
518
  else if (d_lm == Store) {          \
827,193✔
519
    addTraceTS(d_fixednow, d_trace); \
16✔
520
    d_trace << x;                    \
16✔
521
  }
16✔
522

523
OptLog SyncRes::LogObject(const string& prefix)
524
{
57,903✔
525
  OptLog ret;
57,903✔
526
  if (d_lm == Log) {
57,903✔
527
    ret = {prefix, d_fixednow, g_log};
10,715✔
528
  }
10,715✔
529
  else if (d_lm == Store) {
47,188✔
530
    ret = {prefix, d_fixednow, d_trace};
3✔
531
  }
3✔
532
  return ret;
57,903✔
533
}
57,903✔
534

535
static bool pushResolveIfNotInNegCache(const DNSName& qname, QType qtype, const struct timeval& now)
536
{
974✔
537
  NegCache::NegCacheEntry negEntry;
974✔
538
  bool inNegCache = g_negCache->get(qname, qtype, now, negEntry, false);
974✔
539
  if (!inNegCache) {
974✔
540
    // There are a few cases where an answer is neither stored in the record cache nor in the neg cache.
541
    // An example is a SOA-less NODATA response. Rate limiting will kick in if those tasks are pushed too often.
542
    // We might want to fix these cases (and always either store positive or negative) some day.
543
    pushResolveTask(qname, qtype, now.tv_sec, now.tv_sec + 60, false);
960✔
544
  }
960✔
545
  return !inNegCache;
974✔
546
}
974✔
547

548
// A helper function to print a double with specific printf format.
549
// Not using boost::format since it is not thread safe while calling
550
// into locale handling code according to tsan.
551
// This allocates a string, but that's nothing compared to what
552
// boost::format is doing and may even be optimized away anyway.
553
static inline std::string fmtfloat(double value)
554
{
3,903✔
555
  std::array<char, 20> buf{};
3,903✔
556
  int ret = snprintf(buf.data(), buf.size(), "%0.2f", value);
3,903✔
557
  if (ret < 0 || ret >= static_cast<int>(buf.size())) {
3,903!
558
    return "?";
×
559
  }
×
560
  return {buf.data(), static_cast<size_t>(ret)};
3,903✔
561
}
3,903✔
562

563
static inline void accountAuthLatency(uint64_t usec, int family)
564
{
11,228✔
565
  if (family == AF_INET) {
11,228✔
566
    t_Counters.at(rec::Histogram::auth4Answers)(usec);
10,429✔
567
    t_Counters.at(rec::Histogram::cumulativeAuth4Answers)(usec);
10,429✔
568
  }
10,429✔
569
  else {
799✔
570
    t_Counters.at(rec::Histogram::auth6Answers)(usec);
799✔
571
    t_Counters.at(rec::Histogram::cumulativeAuth6Answers)(usec);
799✔
572
  }
799✔
573
}
11,228✔
574

575
SyncRes::SyncRes(const struct timeval& now) :
576
  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)
577
{
3,972✔
578
  d_validationContext.d_nsec3IterationsRemainingQuota = s_maxnsec3iterationsperq > 0 ? s_maxnsec3iterationsperq : std::numeric_limits<decltype(d_validationContext.d_nsec3IterationsRemainingQuota)>::max();
3,972✔
579
}
3,972✔
580

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

583
void SyncRes::resolveAdditionals(const DNSName& qname, QType qtype, AdditionalMode mode, std::vector<DNSRecord>& additionals, unsigned int depth, bool& additionalsNotInCache)
584
{
29✔
585
  vector<DNSRecord> addRecords;
29✔
586

587
  Context context;
29✔
588
  switch (mode) {
29!
589
  case AdditionalMode::ResolveImmediately: {
21✔
590
    set<GetBestNSAnswer> beenthere;
21✔
591
    int res = doResolve(qname, qtype, addRecords, depth, beenthere, context);
21✔
592
    if (res != 0) {
21!
593
      return;
×
594
    }
×
595
    // We're conservative here. We do not add Bogus records in any circumstance, we add Indeterminates only if no
596
    // validation is required.
597
    if (vStateIsBogus(context.state)) {
21!
598
      return;
×
599
    }
×
600
    if (shouldValidate() && context.state != vState::Secure && context.state != vState::Insecure) {
21!
601
      return;
×
602
    }
×
603
    for (auto& rec : addRecords) {
65✔
604
      if (rec.d_place == DNSResourceRecord::ANSWER) {
65✔
605
        additionals.push_back(std::move(rec));
21✔
606
      }
21✔
607
    }
65✔
608
    break;
21✔
609
  }
21✔
610
  case AdditionalMode::CacheOnly:
×
611
  case AdditionalMode::CacheOnlyRequireAuth: {
8✔
612
    // Peek into cache
613
    MemRecursorCache::Flags flags = mode == AdditionalMode::CacheOnlyRequireAuth ? MemRecursorCache::RequireAuth : MemRecursorCache::None;
8!
614
    if (g_recCache->get(d_now.tv_sec, qname, qtype, flags, &addRecords, d_cacheRemote, d_routingTag, nullptr, nullptr, nullptr, &context.state) <= 0) {
8✔
615
      return;
6✔
616
    }
6✔
617
    // See the comment for the ResolveImmediately case
618
    if (vStateIsBogus(context.state)) {
2!
619
      return;
×
620
    }
×
621
    if (shouldValidate() && context.state != vState::Secure && context.state != vState::Insecure) {
2!
622
      return;
×
623
    }
×
624
    for (auto& rec : addRecords) {
2✔
625
      if (rec.d_place == DNSResourceRecord::ANSWER) {
2!
626
        rec.d_ttl -= d_now.tv_sec;
2✔
627
        additionals.push_back(std::move(rec));
2✔
628
      }
2✔
629
    }
2✔
630
    break;
2✔
631
  }
2✔
632
  case AdditionalMode::ResolveDeferred: {
×
633
    const bool oldCacheOnly = setCacheOnly(true);
×
634
    set<GetBestNSAnswer> beenthere;
×
635
    int res = doResolve(qname, qtype, addRecords, depth, beenthere, context);
×
636
    setCacheOnly(oldCacheOnly);
×
637
    if (res == 0 && !addRecords.empty()) {
×
638
      // We're conservative here. We do not add Bogus records in any circumstance, we add Indeterminates only if no
639
      // validation is required.
640
      if (vStateIsBogus(context.state)) {
×
641
        return;
×
642
      }
×
643
      if (shouldValidate() && context.state != vState::Secure && context.state != vState::Insecure) {
×
644
        return;
×
645
      }
×
646
      bool found = false;
×
647
      for (auto& rec : addRecords) {
×
648
        if (rec.d_place == DNSResourceRecord::ANSWER) {
×
649
          found = true;
×
650
          additionals.push_back(std::move(rec));
×
651
        }
×
652
      }
×
653
      if (found) {
×
654
        return;
×
655
      }
×
656
    }
×
657
    // Not found in cache, check negcache and push task if also not in negcache
658
    if (pushResolveIfNotInNegCache(qname, qtype, d_now)) {
×
659
      additionalsNotInCache = true;
×
660
    }
×
661
    break;
×
662
  }
×
663
  case AdditionalMode::Ignore:
×
664
    break;
×
665
  }
29✔
666
}
29✔
667

668
// The main (recursive) function to add additionals
669
// qtype: the original query type to expand
670
// start: records to start from
671
// This function uses to state sets to avoid infinite recursion and allow depulication
672
// depth is the main recursion depth
673
// additionaldepth is the depth for addAdditionals itself
674
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)
675
{
17✔
676
  if (additionaldepth >= 5 || start.empty()) {
17!
677
    return;
×
678
  }
×
679

680
  auto luaLocal = g_luaconfs.getLocal();
17✔
681
  const auto iter = luaLocal->allowAdditionalQTypes.find(qtype);
17✔
682
  if (iter == luaLocal->allowAdditionalQTypes.end()) {
17✔
683
    return;
10✔
684
  }
10✔
685
  std::unordered_set<DNSName> addnames;
7✔
686
  for (const auto& rec : start) {
20✔
687
    if (rec.d_place == DNSResourceRecord::ANSWER) {
20!
688
      // currently, this function only knows about names, we could also take the target types that are dependent on
689
      // record contents into account
690
      // e.g. for NAPTR records, go only for SRV for flag value "s", or A/AAAA for flag value "a"
691
      allowAdditionalEntry(addnames, rec);
20✔
692
    }
20✔
693
  }
20✔
694

695
  // We maintain two sets for deduplication:
696
  // - uniqueCalls makes sure we never resolve a qname/qtype twice
697
  // - uniqueResults makes sure we never add the same qname/qytype RRSet to the result twice,
698
  //   but note that that set might contain multiple elements.
699

700
  auto mode = iter->second.second;
7✔
701
  for (const auto& targettype : iter->second.first) {
15✔
702
    for (const auto& addname : addnames) {
29✔
703
      std::vector<DNSRecord> records;
29✔
704
      bool inserted = uniqueCalls.emplace(addname, targettype).second;
29✔
705
      if (inserted) {
29!
706
        resolveAdditionals(addname, targettype, mode, records, depth, additionalsNotInCache);
29✔
707
      }
29✔
708
      if (!records.empty()) {
29✔
709
        for (auto record = records.begin(); record != records.end();) {
35✔
710
          QType covered = QType::ENT;
23✔
711
          if (record->d_type == QType::RRSIG) {
23✔
712
            if (auto rsig = getRR<RRSIGRecordContent>(*record); rsig != nullptr) {
10!
713
              covered = rsig->d_type;
10✔
714
            }
10✔
715
          }
10✔
716
          if (uniqueResults.count(std::tuple(record->d_name, QType(record->d_type), covered)) > 0) {
23!
717
            // A bit expensive for vectors, but they are small
718
            record = records.erase(record);
×
719
          }
×
720
          else {
23✔
721
            ++record;
23✔
722
          }
23✔
723
        }
23✔
724
        for (const auto& record : records) {
23✔
725
          additionals.push_back(record);
23✔
726
          QType covered = QType::ENT;
23✔
727
          if (record.d_type == QType::RRSIG) {
23✔
728
            if (auto rsig = getRR<RRSIGRecordContent>(record); rsig != nullptr) {
10!
729
              covered = rsig->d_type;
10✔
730
            }
10✔
731
          }
10✔
732
          uniqueResults.emplace(record.d_name, record.d_type, covered);
23✔
733
        }
23✔
734
        addAdditionals(targettype, records, additionals, uniqueCalls, uniqueResults, depth, additionaldepth + 1, additionalsNotInCache);
12✔
735
      }
12✔
736
    }
29✔
737
  }
15✔
738
}
7✔
739

740
// The entry point for other code
741
bool SyncRes::addAdditionals(QType qtype, vector<DNSRecord>& ret, unsigned int depth)
742
{
5✔
743
  // The additional records of interest
744
  std::vector<DNSRecord> additionals;
5✔
745

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

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

753
  bool additionalsNotInCache = false;
5✔
754
  addAdditionals(qtype, ret, additionals, uniqueCalls, uniqueResults, depth, 0, additionalsNotInCache);
5✔
755

756
  for (auto& rec : additionals) {
23✔
757
    rec.d_place = DNSResourceRecord::ADDITIONAL;
23✔
758
    ret.push_back(std::move(rec));
23✔
759
  }
23✔
760
  return additionalsNotInCache;
5✔
761
}
5✔
762

763
/** everything begins here - this is the entry point just after receiving a packet */
764
int SyncRes::beginResolve(const DNSName& qname, const QType qtype, QClass qclass, vector<DNSRecord>& ret, unsigned int depth)
765
{
3,599✔
766
  d_eventTrace.add(RecEventTrace::SyncRes);
3,599✔
767
  d_wasVariable = false;
3,599✔
768
  d_wasOutOfBand = false;
3,599✔
769
  d_cutStates.clear();
3,599✔
770

771
  if (doSpecialNamesResolve(qname, qtype, qclass, ret)) {
3,599✔
772
    d_queryValidationState = vState::Insecure; // this could fool our stats into thinking a validation took place
46✔
773
    return 0; // so do check before updating counters (we do now)
46✔
774
  }
46✔
775

776
  if (isUnsupported(qtype)) {
3,553✔
777
    return -1;
30✔
778
  }
30✔
779

780
  if (qclass == QClass::ANY) {
3,523!
781
    qclass = QClass::IN;
×
782
  }
×
783
  else if (qclass != QClass::IN) {
3,523✔
784
    return -1;
4✔
785
  }
4✔
786

787
  if (qtype == QType::DS) {
3,519✔
788
    d_externalDSQuery = qname;
38✔
789
  }
38✔
790
  else {
3,481✔
791
    d_externalDSQuery.clear();
3,481✔
792
  }
3,481✔
793

794
  set<GetBestNSAnswer> beenthere;
3,519✔
795
  Context context;
3,519✔
796
  int res = doResolve(qname, qtype, ret, depth, beenthere, context);
3,519✔
797
  d_queryValidationState = context.state;
3,519✔
798
  d_extendedError = context.extendedError;
3,519✔
799

800
  if (shouldValidate()) {
3,520✔
801
    if (d_queryValidationState != vState::Indeterminate) {
2,554✔
802
      t_Counters.at(rec::Counter::dnssecValidations)++;
2,475✔
803
    }
2,475✔
804
    auto xdnssec = g_xdnssec.getLocal();
2,554✔
805
    if (xdnssec->check(qname)) {
2,554!
806
      increaseXDNSSECStateCounter(d_queryValidationState);
×
807
    }
×
808
    else {
2,554✔
809
      increaseDNSSECStateCounter(d_queryValidationState);
2,554✔
810
    }
2,554✔
811
  }
2,554✔
812

813
  // Avoid calling addAdditionals() if we know we won't find anything
814
  auto luaLocal = g_luaconfs.getLocal();
3,519✔
815
  if (res == 0 && qclass == QClass::IN && luaLocal->allowAdditionalQTypes.find(qtype) != luaLocal->allowAdditionalQTypes.end()) {
3,519!
816
    bool additionalsNotInCache = addAdditionals(qtype, ret, depth);
5✔
817
    if (additionalsNotInCache) {
5!
818
      d_wasVariable = true;
×
819
    }
×
820
  }
5✔
821
  d_eventTrace.add(RecEventTrace::SyncRes, res, false);
3,519✔
822
  return res;
3,519✔
823
}
3,523✔
824

825
/*! Handles all special, built-in names
826
 * Fills ret with an answer and returns true if it handled the query.
827
 *
828
 * Handles the following queries (and their ANY variants):
829
 *
830
 * - localhost. IN A
831
 * - localhost. IN AAAA
832
 * - 1.0.0.127.in-addr.arpa. IN PTR
833
 * - 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
834
 * - version.bind. CH TXT
835
 * - version.pdns. CH TXT
836
 * - id.server. CH TXT
837
 * - trustanchor.server CH TXT
838
 * - negativetrustanchor.server CH TXT
839
 */
840
bool SyncRes::doSpecialNamesResolve(const DNSName& qname, const QType qtype, const QClass qclass, vector<DNSRecord>& ret)
841
{
3,599✔
842
  static const DNSName arpa("1.0.0.127.in-addr.arpa.");
3,599✔
843
  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.");
3,599✔
844
  static const DNSName localhost("localhost.");
3,599✔
845
  static const DNSName versionbind("version.bind.");
3,599✔
846
  static const DNSName idserver("id.server.");
3,599✔
847
  static const DNSName versionpdns("version.pdns.");
3,599✔
848
  static const DNSName trustanchorserver("trustanchor.server.");
3,599✔
849
  static const DNSName negativetrustanchorserver("negativetrustanchor.server.");
3,599✔
850

851
  bool handled = false;
3,599✔
852
  vector<pair<QType::typeenum, string>> answers;
3,599✔
853

854
  if ((qname == arpa || qname == ip6_arpa) && qclass == QClass::IN) {
3,599!
855
    handled = true;
11✔
856
    if (qtype == QType::PTR || qtype == QType::ANY) {
11!
857
      answers.emplace_back(QType::PTR, "localhost.");
11✔
858
    }
11✔
859
  }
11✔
860

861
  if (qname.isPartOf(localhost) && qclass == QClass::IN) {
3,599!
862
    handled = true;
12✔
863
    if (qtype == QType::A || qtype == QType::ANY) {
12✔
864
      answers.emplace_back(QType::A, "127.0.0.1");
10✔
865
    }
10✔
866
    if (qtype == QType::AAAA || qtype == QType::ANY) {
12✔
867
      answers.emplace_back(QType::AAAA, "::1");
4✔
868
    }
4✔
869
  }
12✔
870

871
  if ((qname == versionbind || qname == idserver || qname == versionpdns) && qclass == QClass::CHAOS) {
3,600!
872
    handled = true;
21✔
873
    if (qtype == QType::TXT || qtype == QType::ANY) {
21!
874
      if (qname == versionbind || qname == versionpdns) {
21✔
875
        answers.emplace_back(QType::TXT, "\"" + ::arg()["version-string"] + "\"");
13✔
876
      }
13✔
877
      else if (s_serverID != "disabled") {
8!
878
        answers.emplace_back(QType::TXT, "\"" + s_serverID + "\"");
8✔
879
      }
8✔
880
    }
21✔
881
  }
21✔
882

883
  if (qname == trustanchorserver && qclass == QClass::CHAOS && ::arg().mustDo("allow-trust-anchor-query")) {
3,599!
884
    handled = true;
1✔
885
    if (qtype == QType::TXT || qtype == QType::ANY) {
1!
886
      auto luaLocal = g_luaconfs.getLocal();
1✔
887
      for (auto const& dsAnchor : luaLocal->dsAnchors) {
2✔
888
        ostringstream ans;
2✔
889
        ans << "\"";
2✔
890
        ans << dsAnchor.first.toString(); // Explicit toString to have a trailing dot
2✔
891
        for (auto const& dsRecord : dsAnchor.second) {
3✔
892
          ans << " ";
3✔
893
          ans << dsRecord.d_tag;
3✔
894
        }
3✔
895
        ans << "\"";
2✔
896
        answers.emplace_back(QType::TXT, ans.str());
2✔
897
      }
2✔
898
    }
1✔
899
  }
1✔
900

901
  if (qname == negativetrustanchorserver && qclass == QClass::CHAOS && ::arg().mustDo("allow-trust-anchor-query")) {
3,599!
902
    handled = true;
1✔
903
    if (qtype == QType::TXT || qtype == QType::ANY) {
1!
904
      auto luaLocal = g_luaconfs.getLocal();
1✔
905
      for (auto const& negAnchor : luaLocal->negAnchors) {
2✔
906
        ostringstream ans;
2✔
907
        ans << "\"";
2✔
908
        ans << negAnchor.first.toString(); // Explicit toString to have a trailing dot
2✔
909
        if (negAnchor.second.length() != 0) {
2✔
910
          ans << " " << negAnchor.second;
1✔
911
        }
1✔
912
        ans << "\"";
2✔
913
        answers.emplace_back(QType::TXT, ans.str());
2✔
914
      }
2✔
915
    }
1✔
916
  }
1✔
917

918
  if (handled && !answers.empty()) {
3,599!
919
    ret.clear();
46✔
920
    d_wasOutOfBand = true;
46✔
921

922
    DNSRecord dnsRecord;
46✔
923
    dnsRecord.d_name = qname;
46✔
924
    dnsRecord.d_place = DNSResourceRecord::ANSWER;
46✔
925
    dnsRecord.d_class = qclass;
46✔
926
    dnsRecord.d_ttl = 86400;
46✔
927
    for (const auto& ans : answers) {
50✔
928
      dnsRecord.d_type = ans.first;
50✔
929
      dnsRecord.setContent(DNSRecordContent::make(ans.first, qclass, ans.second));
50✔
930
      ret.push_back(dnsRecord);
50✔
931
    }
50✔
932
  }
46✔
933

934
  return handled;
3,599✔
935
}
3,599✔
936

937
//! This is the 'out of band resolver', in other words, the authoritative server
938
void SyncRes::AuthDomain::addSOA(std::vector<DNSRecord>& records) const
939
{
25✔
940
  SyncRes::AuthDomain::records_t::const_iterator ziter = d_records.find(std::tuple(getName(), QType::SOA));
25✔
941
  if (ziter != d_records.end()) {
25!
942
    DNSRecord dnsRecord = *ziter;
25✔
943
    dnsRecord.d_place = DNSResourceRecord::AUTHORITY;
25✔
944
    records.push_back(dnsRecord);
25✔
945
  }
25✔
946
}
25✔
947

948
bool SyncRes::AuthDomain::operator==(const AuthDomain& rhs) const
949
{
88✔
950
  return d_records == rhs.d_records
88!
951
    && d_servers == rhs.d_servers
88!
952
    && d_name == rhs.d_name
88!
953
    && d_rdForward == rhs.d_rdForward;
88!
954
}
88✔
955

956
[[nodiscard]] std::string SyncRes::AuthDomain::print(const std::string& indent,
957
                                                     const std::string& indentLevel) const
958
{
176✔
959
  std::stringstream outputsStream;
176✔
960
  outputsStream << indent << "DNSName = " << d_name << std::endl;
176✔
961
  outputsStream << indent << "rdForward = " << d_rdForward << std::endl;
176✔
962
  outputsStream << indent << "Records {" << std::endl;
176✔
963
  auto recordContentIndentation = indent;
176✔
964
  recordContentIndentation += indentLevel;
176✔
965
  recordContentIndentation += indentLevel;
176✔
966
  for (const auto& record : d_records) {
536✔
967
    outputsStream << indent << indentLevel << "Record `" << record.d_name << "` {" << std::endl;
536✔
968
    outputsStream << record.print(recordContentIndentation);
536✔
969
    outputsStream << indent << indentLevel << "}" << std::endl;
536✔
970
  }
536✔
971
  outputsStream << indent << "}" << std::endl;
176✔
972
  outputsStream << indent << "Servers {" << std::endl;
176✔
973
  for (const auto& server : d_servers) {
176!
974
    outputsStream << indent << indentLevel << server.toString() << std::endl;
×
975
  }
×
976
  outputsStream << indent << "}" << std::endl;
176✔
977
  return outputsStream.str();
176✔
978
}
176✔
979

980
int SyncRes::AuthDomain::getRecords(const DNSName& qname, const QType qtype, std::vector<DNSRecord>& records) const
981
{
140✔
982
  int result = RCode::NoError;
140✔
983
  records.clear();
140✔
984

985
  // partial lookup
986
  std::pair<records_t::const_iterator, records_t::const_iterator> range = d_records.equal_range(std::tie(qname));
140✔
987

988
  SyncRes::AuthDomain::records_t::const_iterator ziter;
140✔
989
  bool somedata = false;
140✔
990

991
  for (ziter = range.first; ziter != range.second; ++ziter) {
299✔
992
    somedata = true;
159✔
993

994
    if (qtype == QType::ANY || ziter->d_type == qtype || ziter->d_type == QType::CNAME) {
159✔
995
      // let rest of nameserver do the legwork on this one
996
      records.push_back(*ziter);
112✔
997
    }
112✔
998
    else if (ziter->d_type == QType::NS && ziter->d_name.countLabels() > getName().countLabels()) {
47✔
999
      // we hit a delegation point!
1000
      DNSRecord dnsRecord = *ziter;
2✔
1001
      dnsRecord.d_place = DNSResourceRecord::AUTHORITY;
2✔
1002
      records.push_back(dnsRecord);
2✔
1003
    }
2✔
1004
  }
159✔
1005

1006
  if (!records.empty()) {
140✔
1007
    /* We have found an exact match, we're done */
1008
    return result;
109✔
1009
  }
109✔
1010

1011
  if (somedata) {
31✔
1012
    /* We have records for that name, but not of the wanted qtype */
1013
    addSOA(records);
10✔
1014

1015
    return result;
10✔
1016
  }
10✔
1017

1018
  DNSName wcarddomain(qname);
21✔
1019
  while (wcarddomain != getName() && wcarddomain.chopOff()) {
38!
1020
    range = d_records.equal_range(std::tuple(g_wildcarddnsname + wcarddomain));
23✔
1021
    if (range.first == range.second) {
23✔
1022
      continue;
17✔
1023
    }
17✔
1024
    for (ziter = range.first; ziter != range.second; ++ziter) {
12✔
1025
      DNSRecord dnsRecord = *ziter;
6✔
1026
      // if we hit a CNAME, just answer that - rest of recursor will do the needful & follow
1027
      if (dnsRecord.d_type == qtype || qtype == QType::ANY || dnsRecord.d_type == QType::CNAME) {
6!
1028
        dnsRecord.d_name = qname;
4✔
1029
        dnsRecord.d_place = DNSResourceRecord::ANSWER;
4✔
1030
        records.push_back(dnsRecord);
4✔
1031
      }
4✔
1032
    }
6✔
1033

1034
    if (records.empty()) {
6✔
1035
      addSOA(records);
2✔
1036
    }
2✔
1037

1038
    return result;
6✔
1039
  }
23✔
1040

1041
  /* Nothing for this name, no wildcard, let's see if there is some NS */
1042
  DNSName nsdomain(qname);
15✔
1043
  while (nsdomain.chopOff() && nsdomain != getName()) {
17!
1044
    range = d_records.equal_range(std::tuple(nsdomain, QType::NS));
2✔
1045
    if (range.first == range.second) {
2!
1046
      continue;
×
1047
    }
×
1048
    for (ziter = range.first; ziter != range.second; ++ziter) {
4✔
1049
      DNSRecord dnsRecord = *ziter;
2✔
1050
      dnsRecord.d_place = DNSResourceRecord::AUTHORITY;
2✔
1051
      records.push_back(dnsRecord);
2✔
1052
    }
2✔
1053
  }
2✔
1054

1055
  if (records.empty()) {
15✔
1056
    addSOA(records);
13✔
1057
    result = RCode::NXDomain;
13✔
1058
  }
13✔
1059

1060
  return result;
15✔
1061
}
21✔
1062

1063
bool SyncRes::doOOBResolve(const AuthDomain& domain, const DNSName& qname, const QType qtype, vector<DNSRecord>& ret, int& res)
1064
{
140✔
1065
  d_authzonequeries++;
140✔
1066
  t_Counters.at(rec::Counter::authzonequeries)++;
140✔
1067

1068
  res = domain.getRecords(qname, qtype, ret);
140✔
1069
  return true;
140✔
1070
}
140✔
1071

1072
bool SyncRes::doOOBResolve(const DNSName& qname, const QType qtype, vector<DNSRecord>& ret, unsigned int /* depth */, const string& prefix, int& res)
1073
{
141✔
1074
  DNSName authdomain(qname);
141✔
1075
  const auto iter = getBestAuthZone(&authdomain);
141✔
1076
  if (iter == t_sstorage.domainmap->end() || !iter->second.isAuth()) {
141!
1077
    LOG(prefix << qname << ": Auth storage has no zone for this query!" << endl);
1!
1078
    return false;
1✔
1079
  }
1✔
1080

1081
  LOG(prefix << qname << ": Auth storage has data, zone='" << authdomain << "'" << endl);
140!
1082
  return doOOBResolve(iter->second, qname, qtype, ret, res);
140✔
1083
}
141✔
1084

1085
bool SyncRes::isRecursiveForwardOrAuth(const DNSName& qname)
1086
{
13,508✔
1087
  DNSName authname(qname);
13,508✔
1088
  const auto iter = getBestAuthZone(&authname);
13,508✔
1089
  return iter != t_sstorage.domainmap->end() && (iter->second.isAuth() || iter->second.shouldRecurse());
13,508✔
1090
}
13,508✔
1091

1092
bool SyncRes::isForwardOrAuth(const DNSName& qname)
1093
{
9✔
1094
  DNSName authname(qname);
9✔
1095
  const auto iter = getBestAuthZone(&authname);
9✔
1096
  return iter != t_sstorage.domainmap->end();
9✔
1097
}
9✔
1098

1099
const char* isoDateTimeMillis(const struct timeval& tval, timebuf_t& buf)
1100
{
×
1101
  const std::string s_timestampFormat = "%Y-%m-%dT%T";
×
1102
  struct tm tmval
×
1103
  {
×
1104
  };
×
1105
  size_t len = strftime(buf.data(), buf.size(), s_timestampFormat.c_str(), localtime_r(&tval.tv_sec, &tmval));
×
1106
  if (len == 0) {
×
1107
    int ret = snprintf(buf.data(), buf.size(), "%lld", static_cast<long long>(tval.tv_sec));
×
1108
    if (ret < 0 || static_cast<size_t>(ret) >= buf.size()) {
×
1109
      buf[0] = '\0';
×
1110
      return buf.data();
×
1111
    }
×
1112
    len = ret;
×
1113
  }
×
1114

1115
  if (buf.size() > len + 4) {
×
1116
    snprintf(&buf.at(len), buf.size() - len, ".%03ld", static_cast<long>(tval.tv_usec) / 1000);
×
1117
  }
×
1118
  return buf.data();
×
1119
}
×
1120

1121
static const char* timestamp(time_t arg, timebuf_t& buf)
1122
{
×
1123
  const std::string s_timestampFormat = "%Y-%m-%dT%T";
×
1124
  struct tm tmval
×
1125
  {
×
1126
  };
×
1127
  size_t len = strftime(buf.data(), buf.size(), s_timestampFormat.c_str(), localtime_r(&arg, &tmval));
×
1128
  if (len == 0) {
×
1129
    int ret = snprintf(buf.data(), buf.size(), "%lld", static_cast<long long>(arg));
×
1130
    if (ret < 0 || static_cast<size_t>(ret) >= buf.size()) {
×
1131
      buf[0] = '\0';
×
1132
    }
×
1133
  }
×
1134
  return buf.data();
×
1135
}
×
1136

1137
struct ednsstatus_t : public multi_index_container<SyncRes::EDNSStatus,
1138
                                                   indexed_by<
1139
                                                     ordered_unique<tag<ComboAddress>, member<SyncRes::EDNSStatus, ComboAddress, &SyncRes::EDNSStatus::address>>,
1140
                                                     ordered_non_unique<tag<time_t>, member<SyncRes::EDNSStatus, time_t, &SyncRes::EDNSStatus::ttd>>>>
1141
{
1142
  // Get a copy
1143
  [[nodiscard]] ednsstatus_t getMap() const
1144
  {
×
1145
    return *this;
×
1146
  }
×
1147

1148
  static void setMode(index<ComboAddress>::type& ind, iterator iter, SyncRes::EDNSStatus::EDNSMode mode, time_t theTime)
1149
  {
159✔
1150
    if (iter->mode != mode || iter->ttd == 0) {
159!
1151
      ind.modify(iter, [=](SyncRes::EDNSStatus& status) { status.mode = mode; status.ttd = theTime + Expire; });
89✔
1152
    }
89✔
1153
  }
159✔
1154

1155
  void prune(time_t now)
1156
  {
80✔
1157
    auto& ind = get<time_t>();
80✔
1158
    ind.erase(ind.begin(), ind.upper_bound(now));
80✔
1159
  }
80✔
1160

1161
  static const time_t Expire = 7200;
1162
};
1163

1164
static LockGuarded<ednsstatus_t> s_ednsstatus;
1165

1166
SyncRes::EDNSStatus::EDNSMode SyncRes::getEDNSStatus(const ComboAddress& server)
1167
{
70✔
1168
  auto lock = s_ednsstatus.lock();
70✔
1169
  const auto& iter = lock->find(server);
70✔
1170
  if (iter == lock->end()) {
70✔
1171
    return EDNSStatus::EDNSOK;
64✔
1172
  }
64✔
1173
  return iter->mode;
6✔
1174
}
70✔
1175

1176
uint64_t SyncRes::getEDNSStatusesSize()
1177
{
636✔
1178
  return s_ednsstatus.lock()->size();
636✔
1179
}
636✔
1180

1181
void SyncRes::clearEDNSStatuses()
1182
{
596✔
1183
  s_ednsstatus.lock()->clear();
596✔
1184
}
596✔
1185

1186
void SyncRes::pruneEDNSStatuses(time_t cutoff)
1187
{
80✔
1188
  s_ednsstatus.lock()->prune(cutoff);
80✔
1189
}
80✔
1190

1191
uint64_t SyncRes::doEDNSDump(int fileDesc)
1192
{
×
1193
  int newfd = dup(fileDesc);
×
1194
  if (newfd == -1) {
×
1195
    return 0;
×
1196
  }
×
1197
  auto filePtr = pdns::UniqueFilePtr(fdopen(newfd, "w"));
×
1198
  if (!filePtr) {
×
1199
    close(newfd);
×
1200
    return 0;
×
1201
  }
×
1202
  uint64_t count = 0;
×
1203

1204
  fprintf(filePtr.get(), "; edns dump follows\n; ip\tstatus\tttd\n");
×
1205
  const auto copy = s_ednsstatus.lock()->getMap();
×
1206
  for (const auto& eds : copy) {
×
1207
    count++;
×
1208
    timebuf_t tmp;
×
1209
    fprintf(filePtr.get(), "%s\t%s\t%s\n", eds.address.toString().c_str(), eds.toString().c_str(), timestamp(eds.ttd, tmp));
×
1210
  }
×
1211
  return count;
×
1212
}
×
1213

1214
void SyncRes::pruneNSSpeeds(time_t limit)
1215
{
81✔
1216
  auto lock = s_nsSpeeds.lock();
81✔
1217
  auto& ind = lock->get<timeval>();
81✔
1218
  ind.erase(ind.begin(), ind.upper_bound(timeval{limit, 0}));
81✔
1219
}
81✔
1220

1221
uint64_t SyncRes::getNSSpeedsSize()
1222
{
755✔
1223
  return s_nsSpeeds.lock()->size();
755✔
1224
}
755✔
1225

1226
void SyncRes::submitNSSpeed(const DNSName& server, const ComboAddress& address, int usec, const struct timeval& now)
1227
{
12✔
1228
  auto lock = s_nsSpeeds.lock();
12✔
1229
  lock->find_or_enter(server, now).submit(address, usec, now);
12✔
1230
}
12✔
1231

1232
void SyncRes::clearNSSpeeds()
1233
{
596✔
1234
  s_nsSpeeds.lock()->clear();
596✔
1235
}
596✔
1236

1237
float SyncRes::getNSSpeed(const DNSName& server, const ComboAddress& address)
1238
{
20✔
1239
  auto lock = s_nsSpeeds.lock();
20✔
1240
  return lock->find_or_enter(server).d_collection[address].peek();
20✔
1241
}
20✔
1242

1243
uint64_t SyncRes::doDumpNSSpeeds(int fileDesc)
1244
{
×
1245
  int newfd = dup(fileDesc);
×
1246
  if (newfd == -1) {
×
1247
    return 0;
×
1248
  }
×
1249
  auto filePtr = pdns::UniqueFilePtr(fdopen(newfd, "w"));
×
1250
  if (!filePtr) {
×
1251
    close(newfd);
×
1252
    return 0;
×
1253
  }
×
1254

1255
  fprintf(filePtr.get(), "; nsspeed dump follows\n; nsname\ttimestamp\t[ip/decaying-ms/last-ms...]\n");
×
1256
  uint64_t count = 0;
×
1257

1258
  // Create a copy to avoid holding the lock while doing I/O
1259
  for (const auto& iter : *s_nsSpeeds.lock()) {
×
1260
    count++;
×
1261

1262
    // an <empty> can appear hear in case of authoritative (hosted) zones
1263
    timebuf_t tmp;
×
1264
    fprintf(filePtr.get(), "%s\t%s\t", iter.d_name.toLogString().c_str(), isoDateTimeMillis(iter.d_lastget, tmp));
×
1265
    bool first = true;
×
1266
    for (const auto& line : iter.d_collection) {
×
1267
      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);
×
1268
      first = false;
×
1269
    }
×
1270
    fprintf(filePtr.get(), "\n");
×
1271
  }
×
1272
  return count;
×
1273
}
×
1274

1275
uint64_t SyncRes::getThrottledServersSize()
1276
{
755✔
1277
  return s_throttle.lock()->size();
755✔
1278
}
755✔
1279

1280
void SyncRes::pruneThrottledServers(time_t now)
1281
{
109✔
1282
  s_throttle.lock()->prune(now);
109✔
1283
}
109✔
1284

1285
void SyncRes::clearThrottle()
1286
{
596✔
1287
  s_throttle.lock()->clear();
596✔
1288
}
596✔
1289

1290
bool SyncRes::isThrottled(time_t now, const ComboAddress& server, const DNSName& target, QType qtype)
1291
{
11,272✔
1292
  return s_throttle.lock()->shouldThrottle(now, std::tuple(server, target, qtype));
11,272✔
1293
}
11,272✔
1294

1295
bool SyncRes::isThrottled(time_t now, const ComboAddress& server)
1296
{
11,258✔
1297
  auto throttled = s_throttle.lock()->shouldThrottle(now, std::tuple(server, g_rootdnsname, 0));
11,258✔
1298
  if (throttled) {
11,258✔
1299
    // Give fully throttled servers a chance to be used, to avoid having one bad zone spoil the NS
1300
    // record for others using the same NS. If the NS answers, it will be unThrottled immediately
1301
    if (s_unthrottle_n > 0 && dns_random(s_unthrottle_n) == 0) {
24!
1302
      throttled = false;
×
1303
    }
×
1304
  }
24✔
1305
  return throttled;
11,258✔
1306
}
11,258✔
1307

1308
void SyncRes::unThrottle(const ComboAddress& server, const DNSName& name, QType qtype)
1309
{
10,773✔
1310
  s_throttle.lock()->clear(std::tuple(server, g_rootdnsname, 0));
10,773✔
1311
  s_throttle.lock()->clear(std::tuple(server, name, qtype));
10,773✔
1312
}
10,773✔
1313

1314
void SyncRes::doThrottle(time_t now, const ComboAddress& server, time_t duration, unsigned int tries, Throttle::Reason reason)
1315
{
6✔
1316
  s_throttle.lock()->throttle(now, std::tuple(server, g_rootdnsname, 0), duration, tries, reason);
6✔
1317
}
6✔
1318

1319
void SyncRes::doThrottle(time_t now, const ComboAddress& server, const DNSName& name, QType qtype, time_t duration, unsigned int tries, Throttle::Reason reason)
1320
{
196✔
1321
  s_throttle.lock()->throttle(now, std::tuple(server, name, qtype), duration, tries, reason);
196✔
1322
}
196✔
1323

1324
uint64_t SyncRes::doDumpThrottleMap(int fileDesc)
1325
{
×
1326
  int newfd = dup(fileDesc);
×
1327
  if (newfd == -1) {
×
1328
    return 0;
×
1329
  }
×
1330
  auto filePtr = pdns::UniqueFilePtr(fdopen(newfd, "w"));
×
1331
  if (!filePtr) {
×
1332
    close(newfd);
×
1333
    return 0;
×
1334
  }
×
1335
  fprintf(filePtr.get(), "; throttle map dump follows\n");
×
1336
  fprintf(filePtr.get(), "; remote IP\tqname\tqtype\tcount\tttd\treason\n");
×
1337
  uint64_t count = 0;
×
1338

1339
  // Get a copy to avoid holding the lock while doing I/O
1340
  const auto throttleMap = s_throttle.lock()->getThrottleMap();
×
1341
  for (const auto& iter : throttleMap) {
×
1342
    count++;
×
1343
    timebuf_t tmp;
×
1344
    // remote IP, dns name, qtype, count, ttd, reason
1345
    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());
×
1346
  }
×
1347

1348
  return count;
×
1349
}
×
1350

1351
uint64_t SyncRes::getFailedServersSize()
1352
{
755✔
1353
  return s_fails.lock()->size();
755✔
1354
}
755✔
1355

1356
void SyncRes::clearFailedServers()
1357
{
596✔
1358
  s_fails.lock()->clear();
596✔
1359
}
596✔
1360

1361
void SyncRes::pruneFailedServers(time_t cutoff)
1362
{
109✔
1363
  s_fails.lock()->prune(cutoff);
109✔
1364
}
109✔
1365

1366
unsigned long SyncRes::getServerFailsCount(const ComboAddress& server)
1367
{
74✔
1368
  return s_fails.lock()->value(server);
74✔
1369
}
74✔
1370

1371
uint64_t SyncRes::doDumpFailedServers(int fileDesc)
1372
{
×
1373
  int newfd = dup(fileDesc);
×
1374
  if (newfd == -1) {
×
1375
    return 0;
×
1376
  }
×
1377
  auto filePtr = pdns::UniqueFilePtr(fdopen(newfd, "w"));
×
1378
  if (!filePtr) {
×
1379
    close(newfd);
×
1380
    return 0;
×
1381
  }
×
1382
  fprintf(filePtr.get(), "; failed servers dump follows\n");
×
1383
  fprintf(filePtr.get(), "; remote IP\tcount\ttimestamp\n");
×
1384
  uint64_t count = 0;
×
1385

1386
  // We get a copy, so the I/O does not need to happen while holding the lock
1387
  for (const auto& iter : s_fails.lock()->getMapCopy()) {
×
1388
    count++;
×
1389
    timebuf_t tmp;
×
1390
    fprintf(filePtr.get(), "%s\t%" PRIu64 "\t%s\n", iter.key.toString().c_str(), iter.value, timestamp(iter.last, tmp));
×
1391
  }
×
1392

1393
  return count;
×
1394
}
×
1395

1396
uint64_t SyncRes::getNonResolvingNSSize()
1397
{
763✔
1398
  return s_nonresolving.lock()->size();
763✔
1399
}
763✔
1400

1401
void SyncRes::clearNonResolvingNS()
1402
{
596✔
1403
  s_nonresolving.lock()->clear();
596✔
1404
}
596✔
1405

1406
void SyncRes::pruneNonResolving(time_t cutoff)
1407
{
109✔
1408
  s_nonresolving.lock()->prune(cutoff);
109✔
1409
}
109✔
1410

1411
uint64_t SyncRes::doDumpNonResolvingNS(int fileDesc)
1412
{
×
1413
  int newfd = dup(fileDesc);
×
1414
  if (newfd == -1) {
×
1415
    return 0;
×
1416
  }
×
1417
  auto filePtr = pdns::UniqueFilePtr(fdopen(newfd, "w"));
×
1418
  if (!filePtr) {
×
1419
    close(newfd);
×
1420
    return 0;
×
1421
  }
×
1422
  fprintf(filePtr.get(), "; non-resolving nameserver dump follows\n");
×
1423
  fprintf(filePtr.get(), "; name\tcount\ttimestamp\n");
×
1424
  uint64_t count = 0;
×
1425

1426
  // We get a copy, so the I/O does not need to happen while holding the lock
1427
  for (const auto& iter : s_nonresolving.lock()->getMapCopy()) {
×
1428
    count++;
×
1429
    timebuf_t tmp;
×
1430
    fprintf(filePtr.get(), "%s\t%" PRIu64 "\t%s\n", iter.key.toString().c_str(), iter.value, timestamp(iter.last, tmp));
×
1431
  }
×
1432

1433
  return count;
×
1434
}
×
1435

1436
void SyncRes::clearSaveParentsNSSets()
1437
{
596✔
1438
  s_savedParentNSSet.lock()->clear();
596✔
1439
}
596✔
1440

1441
size_t SyncRes::getSaveParentsNSSetsSize()
1442
{
634✔
1443
  return s_savedParentNSSet.lock()->size();
634✔
1444
}
634✔
1445

1446
void SyncRes::pruneSaveParentsNSSets(time_t now)
1447
{
80✔
1448
  s_savedParentNSSet.lock()->prune(now);
80✔
1449
}
80✔
1450

1451
uint64_t SyncRes::doDumpSavedParentNSSets(int fileDesc)
1452
{
×
1453
  int newfd = dup(fileDesc);
×
1454
  if (newfd == -1) {
×
1455
    return 0;
×
1456
  }
×
1457
  auto filePtr = pdns::UniqueFilePtr(fdopen(newfd, "w"));
×
1458
  if (!filePtr) {
×
1459
    close(newfd);
×
1460
    return 0;
×
1461
  }
×
1462
  fprintf(filePtr.get(), "; dump of saved parent nameserver sets succesfully used follows\n");
×
1463
  fprintf(filePtr.get(), "; total entries: %zu\n", s_savedParentNSSet.lock()->size());
×
1464
  fprintf(filePtr.get(), "; domain\tsuccess\tttd\n");
×
1465
  uint64_t count = 0;
×
1466

1467
  // We get a copy, so the I/O does not need to happen while holding the lock
1468
  for (const auto& iter : s_savedParentNSSet.lock()->getMapCopy()) {
×
1469
    if (iter.d_count == 0) {
×
1470
      continue;
×
1471
    }
×
1472
    count++;
×
1473
    timebuf_t tmp;
×
1474
    fprintf(filePtr.get(), "%s\t%" PRIu64 "\t%s\n", iter.d_domain.toString().c_str(), iter.d_count, timestamp(iter.d_ttd, tmp));
×
1475
  }
×
1476
  return count;
×
1477
}
×
1478

1479
void SyncRes::pruneDoTProbeMap(time_t cutoff)
1480
{
×
1481
  auto lock = s_dotMap.lock();
×
1482
  auto& ind = lock->d_map.get<time_t>();
×
1483

1484
  for (auto i = ind.begin(); i != ind.end();) {
×
1485
    if (i->d_ttd >= cutoff) {
×
1486
      // We're done as we loop ordered by d_ttd
1487
      break;
×
1488
    }
×
1489
    if (i->d_status == DoTStatus::Status::Busy) {
×
1490
      lock->d_numBusy--;
×
1491
    }
×
1492
    i = ind.erase(i);
×
1493
  }
×
1494
}
×
1495

1496
uint64_t SyncRes::doDumpDoTProbeMap(int fileDesc)
1497
{
×
1498
  int newfd = dup(fileDesc);
×
1499
  if (newfd == -1) {
×
1500
    return 0;
×
1501
  }
×
1502
  auto filePtr = pdns::UniqueFilePtr(fdopen(newfd, "w"));
×
1503
  if (!filePtr) {
×
1504
    close(newfd);
×
1505
    return 0;
×
1506
  }
×
1507
  fprintf(filePtr.get(), "; DoT probing map follows\n");
×
1508
  fprintf(filePtr.get(), "; ip\tdomain\tcount\tstatus\tttd\n");
×
1509
  uint64_t count = 0;
×
1510

1511
  // We get a copy, so the I/O does not need to happen while holding the lock
1512
  DoTMap copy;
×
1513
  {
×
1514
    copy = *s_dotMap.lock();
×
1515
  }
×
1516
  fprintf(filePtr.get(), "; %" PRIu64 " Busy entries\n", copy.d_numBusy);
×
1517
  for (const auto& iter : copy.d_map) {
×
1518
    count++;
×
1519
    timebuf_t tmp;
×
1520
    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));
×
1521
  }
×
1522
  return count;
×
1523
}
×
1524

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

1529
   However, some hosts simply can't answer questions which ask for DNSSEC. This can manifest itself as:
1530
   * No answer
1531
   * FormErr
1532
   * Nonsense answer
1533

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

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

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

1546
LWResult::Result SyncRes::asyncresolveWrapper(const ComboAddress& address, bool ednsMANDATORY, const DNSName& domain, [[maybe_unused]] const DNSName& auth, int type, bool doTCP, bool sendRDQuery, struct timeval* now, boost::optional<Netmask>& srcmask, LWResult* res, bool* chained, const DNSName& nsName) const
1547
{
11,230✔
1548
  /* what is your QUEST?
1549
     the goal is to get as many remotes as possible on the best level of EDNS support
1550
     The levels are:
1551

1552
     1) EDNSOK: Honors EDNS0, absent from table
1553
     2) EDNSIGNORANT: Ignores EDNS0, gives replies without EDNS0
1554
     3) NOEDNS: Generates FORMERR on EDNS queries
1555

1556
     Everybody starts out assumed to be EDNSOK.
1557
     If EDNSOK, send out EDNS0
1558
        If you FORMERR us, go to NOEDNS,
1559
        If no EDNS in response, go to EDNSIGNORANT
1560
     If EDNSIGNORANT, keep on including EDNS0, see what happens
1561
        Same behaviour as EDNSOK
1562
     If NOEDNS, send bare queries
1563
  */
1564

1565
  // Read current status, defaulting to OK
1566
  SyncRes::EDNSStatus::EDNSMode mode = EDNSStatus::EDNSOK;
11,230✔
1567
  {
11,230✔
1568
    auto lock = s_ednsstatus.lock();
11,230✔
1569
    auto ednsstatus = lock->find(address); // does this include port? YES
11,230✔
1570
    if (ednsstatus != lock->end()) {
11,230✔
1571
      if (ednsstatus->ttd != 0 && ednsstatus->ttd < d_now.tv_sec) {
80!
1572
        lock->erase(ednsstatus);
×
1573
      }
×
1574
      else {
80✔
1575
        mode = ednsstatus->mode;
80✔
1576
      }
80✔
1577
    }
80✔
1578
  }
11,230✔
1579

1580
  int EDNSLevel = 0;
11,230✔
1581
  auto luaconfsLocal = g_luaconfs.getLocal();
11,230✔
1582
  ResolveContext ctx(d_initialRequestId, nsName);
11,230✔
1583
#ifdef HAVE_FSTRM
9,944✔
1584
  ctx.d_auth = auth;
9,944✔
1585
#endif
9,944✔
1586

1587
  LWResult::Result ret{};
11,230✔
1588

1589
  for (int tries = 0; tries < 2; ++tries) {
11,233!
1590

1591
    if (mode == EDNSStatus::NOEDNS) {
11,233✔
1592
      t_Counters.at(rec::Counter::noEdnsOutQueries)++;
3✔
1593
      EDNSLevel = 0; // level != mode
3✔
1594
    }
3✔
1595
    else if (ednsMANDATORY || mode != EDNSStatus::NOEDNS) {
11,230!
1596
      EDNSLevel = 1;
11,230✔
1597
    }
11,230✔
1598

1599
    DNSName sendQname(domain);
11,233✔
1600
    if (g_lowercaseOutgoing) {
11,233✔
1601
      sendQname.makeUsLowerCase();
8✔
1602
    }
8✔
1603

1604
    if (d_asyncResolve) {
11,233✔
1605
      ret = d_asyncResolve(address, sendQname, type, doTCP, sendRDQuery, EDNSLevel, now, srcmask, ctx, res, chained);
2,469✔
1606
    }
2,469✔
1607
    else {
8,764✔
1608
      ret = asyncresolve(address, sendQname, type, doTCP, sendRDQuery, EDNSLevel, now, srcmask, ctx, d_outgoingProtobufServers, d_frameStreamServers, luaconfsLocal->outgoingProtobufExportConfig.exportTypes, res, chained);
8,764✔
1609
    }
8,764✔
1610

1611
    if (ret == LWResult::Result::PermanentError || LWResult::isLimitError(ret) || ret == LWResult::Result::Spoofed) {
11,233!
1612
      break; // transport error, nothing to learn here
50✔
1613
    }
50✔
1614

1615
    if (ret == LWResult::Result::Timeout) { // timeout, not doing anything with it now
11,183✔
1616
      break;
333✔
1617
    }
333✔
1618

1619
    if (EDNSLevel == 1) {
10,850✔
1620
      // We sent out with EDNS
1621
      // ret is LWResult::Result::Success
1622
      // ednsstatus in table might be pruned or changed by another request/thread, so do a new lookup/insert if needed
1623
      auto lock = s_ednsstatus.lock(); // all three branches below need a lock
10,844✔
1624

1625
      // Determine new mode
1626
      if (res->d_validpacket && !res->d_haveEDNS && res->d_rcode == RCode::FormErr) {
10,845✔
1627
        mode = EDNSStatus::NOEDNS;
3✔
1628
        auto ednsstatus = lock->insert(address).first;
3✔
1629
        auto& ind = lock->get<ComboAddress>();
3✔
1630
        lock->setMode(ind, ednsstatus, mode, d_now.tv_sec);
3✔
1631
        // This is the only path that re-iterates the loop
1632
        continue;
3✔
1633
      }
3✔
1634
      if (!res->d_haveEDNS) {
10,841✔
1635
        auto ednsstatus = lock->insert(address).first;
156✔
1636
        auto& ind = lock->get<ComboAddress>();
156✔
1637
        lock->setMode(ind, ednsstatus, EDNSStatus::EDNSIGNORANT, d_now.tv_sec);
156✔
1638
      }
156✔
1639
      else {
10,685✔
1640
        // New status is EDNSOK
1641
        lock->erase(address);
10,685✔
1642
      }
10,685✔
1643
    }
10,841✔
1644

1645
    break;
10,847✔
1646
  }
10,850✔
1647
  return ret;
11,230✔
1648
}
11,230✔
1649

1650
/* The parameters from rfc9156. */
1651
/* maximum number of QNAME minimization iterations */
1652
unsigned int SyncRes::s_max_minimize_count; // default is 10
1653
/* number of iterations that should only have one label appended */
1654
unsigned int SyncRes::s_minimize_one_label; // default is 4
1655

1656
static unsigned int qmStepLen(unsigned int labels, unsigned int qnamelen, unsigned int qmIteration)
1657
{
8,990✔
1658
  unsigned int step{};
8,990✔
1659

1660
  if (qmIteration < SyncRes::s_minimize_one_label) {
8,990✔
1661
    step = 1;
8,909✔
1662
  }
8,909✔
1663
  else if (qmIteration < SyncRes::s_max_minimize_count) {
81!
1664
    step = std::max(1U, (qnamelen - labels) / (SyncRes::s_max_minimize_count - qmIteration));
81✔
1665
  }
81✔
1666
  else {
×
1667
    step = qnamelen - labels;
×
1668
  }
×
1669
  unsigned int targetlen = std::min(labels + step, qnamelen);
8,990✔
1670
  return targetlen;
8,990✔
1671
}
8,990✔
1672

1673
static string resToString(int res)
1674
{
1,045✔
1675
  return res >= 0 ? RCode::to_s(res) : std::to_string(res);
1,045!
1676
}
1,045✔
1677

1678
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)
1679
{
15,788✔
1680
  auto prefix = getPrefix(depth);
15,788✔
1681
  auto luaconfsLocal = g_luaconfs.getLocal();
15,788✔
1682

1683
  /* Apply qname (including CNAME chain) filtering policies */
1684
  if (d_wantsRPZ && !d_appliedPolicy.wasHit()) {
15,789✔
1685
    if (luaconfsLocal->dfe.getQueryPolicy(qname, d_discardedPolicies, d_appliedPolicy)) {
15,780✔
1686
      mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());
94✔
1687
      bool done = false;
94✔
1688
      int rcode = RCode::NoError;
94✔
1689
      handlePolicyHit(prefix, qname, qtype, ret, done, rcode, depth);
94✔
1690
      if (done) {
94✔
1691
        return rcode;
58✔
1692
      }
58✔
1693
    }
94✔
1694
  }
15,780✔
1695

1696
  initZoneCutsFromTA(qname, prefix);
15,730✔
1697

1698
  // In the auth or recursive forward case, it does not make sense to do qname-minimization
1699
  if (!getQNameMinimization() || isRecursiveForwardOrAuth(qname)) {
15,730✔
1700
    return doResolveNoQNameMinimization(qname, qtype, ret, depth, beenthere, context);
2,509✔
1701
  }
2,509✔
1702

1703
  // The qname minimization algorithm is a simplified version of the one in RFC 7816 (bis).
1704
  // It could be simplified because the cache maintenance (both positive and negative)
1705
  // is already done by doResolveNoQNameMinimization().
1706
  //
1707
  // Sketch of algorithm:
1708
  // Check cache
1709
  //  If result found: done
1710
  //  Otherwise determine closes ancestor from cache data
1711
  //    Repeat querying A, adding more labels of the original qname
1712
  //    If we get a delegation continue at ancestor determination
1713
  //    Until we have the full name.
1714
  //
1715
  // The algorithm starts with adding a single label per iteration, and
1716
  // moves to three labels per iteration after three iterations.
1717

1718
  DNSName child;
13,221✔
1719
  prefix.append(string("QM "));
13,221✔
1720

1721
  LOG(prefix << qname << ": doResolve" << endl);
13,221!
1722

1723
  // Look in cache only
1724
  bool old = setCacheOnly(true);
13,221✔
1725
  bool fromCache = false;
13,221✔
1726
  // For cache peeking, we tell doResolveNoQNameMinimization not to consider the (non-recursive) forward case.
1727
  // Otherwise all queries in a forward domain will be forwarded, while we want to consult the cache.
1728
  const auto retSizeBeforeCall = ret.size();
13,221✔
1729
  int res = doResolveNoQNameMinimization(qname, qtype, ret, depth, beenthere, context, &fromCache, nullptr);
13,221✔
1730
  setCacheOnly(old);
13,221✔
1731
  if (fromCache) {
13,221✔
1732
    LOG(prefix << qname << ": Step0 Found in cache" << endl);
7,344!
1733
    if (d_appliedPolicy.d_type != DNSFilterEngine::PolicyType::None && (d_appliedPolicy.d_kind == DNSFilterEngine::PolicyKind::NXDOMAIN || d_appliedPolicy.d_kind == DNSFilterEngine::PolicyKind::NODATA)) {
7,344✔
1734
      ret.clear();
4✔
1735
    }
4✔
1736
    return res;
7,344✔
1737
  }
7,344✔
1738
  // Erase unwanted cache lookup preliminary results in the returned records
1739
  ret.resize(retSizeBeforeCall);
5,877✔
1740
  LOG(prefix << qname << ": Step0 Not cached" << endl);
5,877!
1741

1742
  const unsigned int qnamelen = qname.countLabels();
5,877✔
1743

1744
  DNSName fwdomain(qname);
5,877✔
1745
  const bool forwarded = getBestAuthZone(&fwdomain) != t_sstorage.domainmap->end();
5,877✔
1746
  if (forwarded) {
5,877✔
1747
    LOG(prefix << qname << ": Step0 qname is in a forwarded domain " << fwdomain << endl);
64!
1748
  }
64✔
1749

1750
  for (unsigned int i = 0; i <= qnamelen; i++) {
7,422✔
1751

1752
    // Step 1
1753
    vector<DNSRecord> bestns;
7,391✔
1754
    DNSName nsdomain(qname);
7,391✔
1755
    if (qtype == QType::DS) {
7,391✔
1756
      nsdomain.chopOff();
160✔
1757
    }
160✔
1758
    // the two retries allow getBestNSFromCache&co to reprime the root
1759
    // hints, in case they ever go missing
1760
    for (int tries = 0; tries < 2 && bestns.empty(); ++tries) {
14,865✔
1761
      bool flawedNSSet = false;
7,535✔
1762
      set<GetBestNSAnswer> beenthereIgnored;
7,535✔
1763
      getBestNSFromCache(nsdomain, qtype, bestns, &flawedNSSet, depth, prefix, beenthereIgnored, boost::make_optional(forwarded, fwdomain));
7,535✔
1764
      if (forwarded) {
7,535✔
1765
        break;
64✔
1766
      }
64✔
1767
    }
7,535✔
1768

1769
    if (bestns.empty()) {
7,391✔
1770
      if (!forwarded) {
64!
1771
        // Something terrible is wrong
1772
        LOG(prefix << qname << ": Step1 No ancestor found return ServFail" << endl);
×
1773
        return RCode::ServFail;
×
1774
      }
×
1775
      child = fwdomain;
64✔
1776
    }
64✔
1777
    else {
7,327✔
1778
      LOG(prefix << qname << ": Step1 Ancestor from cache is " << bestns[0].d_name << endl);
7,327!
1779
      if (forwarded) {
7,327!
1780
        child = bestns[0].d_name.isPartOf(fwdomain) ? bestns[0].d_name : fwdomain;
×
1781
        LOG(prefix << qname << ": Step1 Final Ancestor (using forwarding info) is " << child << endl);
×
1782
      }
×
1783
      else {
7,327✔
1784
        child = bestns[0].d_name;
7,327✔
1785
      }
7,327✔
1786
    }
7,327✔
1787
    for (; i <= qnamelen; i++) {
8,991!
1788
      // Step 2
1789
      unsigned int labels = child.countLabels();
8,991✔
1790
      unsigned int targetlen = qmStepLen(labels, qnamelen, i);
8,991✔
1791

1792
      while (labels < targetlen) {
16,308✔
1793
        child.prependRawLabel(qname.getRawLabel(qnamelen - labels - 1));
7,317✔
1794
        labels++;
7,317✔
1795
      }
7,317✔
1796
      // rfc9156 section-2.3, append labels if they start with an underscore
1797
      while (labels < qnamelen) {
8,991✔
1798
        auto prependLabel = qname.getRawLabel(qnamelen - labels - 1);
3,160✔
1799
        if (prependLabel.at(0) != '_') {
3,160!
1800
          break;
3,160✔
1801
        }
3,160✔
1802
        child.prependRawLabel(prependLabel);
×
1803
        labels++;
×
1804
      }
×
1805

1806
      LOG(prefix << qname << ": Step2 New child " << child << endl);
8,991!
1807

1808
      // Step 3 resolve
1809
      if (child == qname) {
8,991✔
1810
        LOG(prefix << qname << ": Step3 Going to do final resolve" << endl);
5,831!
1811
        res = doResolveNoQNameMinimization(qname, qtype, ret, depth, beenthere, context);
5,831✔
1812
        LOG(prefix << qname << ": Step3 Final resolve: " << resToString(res) << "/" << ret.size() << endl);
5,831!
1813
        return res;
5,831✔
1814
      }
5,831✔
1815

1816
      // If we have seen this child during resolution already; we tried to QM it already or otherwise broken.
1817
      // fall back to no-QM
1818
      bool qmLoopDetected = false;
3,160✔
1819
      for (const auto& visitedNS : beenthere) {
3,164✔
1820
        if (visitedNS.qname == child) {
1,642!
1821
          qmLoopDetected = true;
×
1822
          break;
×
1823
        }
×
1824
      }
1,642✔
1825
      if (qmLoopDetected) {
3,160!
1826
        LOG(prefix << qname << ": Step4 loop detected as visited this child name already, fallback to no QM" << endl);
×
1827
        res = doResolveNoQNameMinimization(qname, qtype, ret, depth, beenthere, context);
×
1828
        LOG(prefix << qname << ": Step4 Final resolve: " << resToString(res) << "/" << ret.size() << endl);
×
1829
        return res;
×
1830
      }
×
1831

1832
      // Step 4
1833
      LOG(prefix << qname << ": Step4 Resolve A for child " << child << endl);
3,160!
1834
      bool oldFollowCNAME = d_followCNAME;
3,160✔
1835
      d_followCNAME = false;
3,160✔
1836
      vector<DNSRecord> retq;
3,160✔
1837
      StopAtDelegation stopAtDelegation = Stop;
3,160✔
1838
      res = doResolveNoQNameMinimization(child, QType::A, retq, depth, beenthere, context, nullptr, &stopAtDelegation);
3,160✔
1839
      d_followCNAME = oldFollowCNAME;
3,160✔
1840
      LOG(prefix << qname << ": Step4 Resolve " << child << "|A result is " << RCode::to_s(res) << "/" << retq.size() << "/" << stopAtDelegation << endl);
3,160!
1841
      if (stopAtDelegation == Stopped) {
3,160✔
1842
        LOG(prefix << qname << ": Delegation seen, continue at step 1" << endl);
1,544!
1843
        break;
1,544✔
1844
      }
1,544✔
1845

1846
      if (res != RCode::NoError) {
1,616✔
1847
        // Case 5: unexpected answer
1848
        LOG(prefix << qname << ": Step5: other rcode, last effort final resolve" << endl);
16!
1849
        setQNameMinimization(false);
16✔
1850
        setQMFallbackMode(true);
16✔
1851

1852
        auto oldEDE = context.extendedError;
16✔
1853
        res = doResolveNoQNameMinimization(qname, qtype, ret, depth + 1, beenthere, context);
16✔
1854

1855
        if (res == RCode::NoError) {
16!
1856
          t_Counters.at(rec::Counter::qnameminfallbacksuccess)++;
×
1857
        }
×
1858
        else {
16✔
1859
          // as doResolveNoQNameMinimization clears the EDE, we put it back here, it is relevant but might not be set by the last effort attempt
1860
          if (!context.extendedError) {
16!
1861
            context.extendedError = std::move(oldEDE);
×
1862
          }
×
1863
        }
16✔
1864

1865
        LOG(prefix << qname << ": Step5 End resolve: " << resToString(res) << "/" << ret.size() << endl);
16!
1866
        return res;
16✔
1867
      }
16✔
1868
    }
1,616✔
1869
  }
7,391✔
1870

1871
  // Should not be reached
1872
  LOG(prefix << qname << ": Max iterations reached, return ServFail" << endl);
2,147,483,678!
1873
  return RCode::ServFail;
2,147,483,678✔
1874
}
5,877✔
1875

1876
unsigned int SyncRes::getAdjustedRecursionBound() const
1877
{
26,090✔
1878
  auto bound = s_maxdepth; // 40 is default value of s_maxdepth
26,090✔
1879
  if (getQMFallbackMode()) {
26,090✔
1880
    // We might have hit a depth level check, but we still want to allow some recursion levels in the fallback
1881
    // no-qname-minimization case. This has the effect that a qname minimization fallback case might reach 150% of
1882
    // maxdepth, taking care to not repeatedly increase the bound.
1883
    bound += s_maxdepth / 2;
16✔
1884
  }
16✔
1885
  return bound;
26,090✔
1886
}
26,090✔
1887

1888
/*! This function will check the cache and go out to the internet if the answer is not in cache
1889
 *
1890
 * \param qname The name we need an answer for
1891
 * \param qtype
1892
 * \param ret The vector of DNSRecords we need to fill with the answers
1893
 * \param depth The recursion depth we are in
1894
 * \param beenthere
1895
 * \param fromCache tells the caller the result came from the cache, may be nullptr
1896
 * \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
1897
 * \return DNS RCODE or -1 (Error)
1898
 */
1899
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)
1900
{
26,087✔
1901
  context.extendedError.reset();
26,087✔
1902
  auto prefix = getPrefix(depth);
26,087✔
1903

1904
  LOG(prefix << qname << ": Wants " << (d_doDNSSEC ? "" : "NO ") << "DNSSEC processing, " << (d_requireAuthData ? "" : "NO ") << "auth data required by query for " << qtype << endl);
26,087!
1905

1906
  d_maxdepth = std::max(d_maxdepth, depth);
26,087✔
1907
  if (s_maxdepth > 0) {
26,094✔
1908
    auto bound = getAdjustedRecursionBound();
26,090✔
1909
    // Use a stricter bound if throttling
1910
    if (depth > bound || (d_outqueries > 10 && d_throttledqueries > 5 && depth > bound * 2 / 3)) {
26,094!
1911
      string msg = "More than " + std::to_string(bound) + " (adjusted max-recursion-depth) levels of recursion needed while resolving " + qname.toLogString();
4✔
1912
      LOG(prefix << qname << ": " << msg << endl);
4!
1913
      throw ImmediateServFailException(std::move(msg));
4✔
1914
    }
4✔
1915
  }
26,090✔
1916

1917
  int res = 0;
26,083✔
1918

1919
  const int iterations = !d_refresh && MemRecursorCache::s_maxServedStaleExtensions > 0 ? 2 : 1;
26,083✔
1920
  for (int loop = 0; loop < iterations; loop++) {
2,147,493,463✔
1921

1922
    d_serveStale = loop == 1;
26,109✔
1923
    if (d_serveStale) {
26,109✔
1924
      LOG(prefix << qname << ": Restart, with serve-stale enabled" << endl);
20!
1925
    }
20✔
1926
    // This is a difficult way of expressing "this is a normal query", i.e. not getRootNS.
1927
    if (!d_updatingRootNS || qtype.getCode() != QType::NS || !qname.isRoot()) {
26,109!
1928
      DNSName authname(qname);
25,667✔
1929
      const auto iter = getBestAuthZone(&authname);
25,667✔
1930

1931
      if (d_cacheonly) {
25,667✔
1932
        if (iter != t_sstorage.domainmap->end()) {
14,501✔
1933
          if (iter->second.isAuth()) {
156✔
1934
            LOG(prefix << qname << ": Cache only lookup for '" << qname << "|" << qtype << "', in auth zone" << endl);
2!
1935
            ret.clear();
2✔
1936
            d_wasOutOfBand = doOOBResolve(qname, qtype, ret, depth, prefix, res);
2✔
1937
            if (fromCache != nullptr) {
2!
1938
              *fromCache = d_wasOutOfBand;
×
1939
            }
×
1940
            return res;
2✔
1941
          }
2✔
1942
        }
156✔
1943
      }
14,501✔
1944

1945
      bool wasForwardedOrAuthZone = false;
25,665✔
1946
      bool wasAuthZone = false;
25,665✔
1947
      bool wasForwardRecurse = false;
25,665✔
1948

1949
      if (iter != t_sstorage.domainmap->end()) {
25,665✔
1950
        wasForwardedOrAuthZone = true;
633✔
1951

1952
        if (iter->second.isAuth()) {
633✔
1953
          wasAuthZone = true;
296✔
1954
        }
296✔
1955
        else if (iter->second.shouldRecurse()) {
337✔
1956
          wasForwardRecurse = true;
99✔
1957
        }
99✔
1958
      }
633✔
1959

1960
      /* When we are looking for a DS, we want to the non-CNAME cache check first
1961
         because we can actually have a DS (from the parent zone) AND a CNAME (from
1962
         the child zone), and what we really want is the DS */
1963
      if (qtype != QType::DS && doCNAMECacheCheck(qname, qtype, ret, depth, prefix, res, context, wasAuthZone, wasForwardRecurse, loop == 1)) { // will reroute us if needed
25,665✔
1964
        d_wasOutOfBand = wasAuthZone;
2,126✔
1965
        // Here we have an issue. If we were prevented from going out to the network (cache-only was set, possibly because we
1966
        // are in QM Step0) we might have a CNAME but not the corresponding target.
1967
        // It means that we will sometimes go to the next steps when we are in fact done, but that's fine since
1968
        // we will get the records from the cache, resulting in a small overhead.
1969
        // This might be a real problem if we had a RPZ hit, though, because we do not want the processing to continue, since
1970
        // RPZ rules will not be evaluated anymore (we already matched).
1971
        const bool stoppedByPolicyHit = d_appliedPolicy.wasHit();
2,126✔
1972

1973
        if (fromCache != nullptr && (!d_cacheonly || stoppedByPolicyHit)) {
2,126!
1974
          *fromCache = true;
3✔
1975
        }
3✔
1976
        /* Apply Post filtering policies */
1977

1978
        if (d_wantsRPZ && !stoppedByPolicyHit) {
2,126!
1979
          auto luaLocal = g_luaconfs.getLocal();
2,122✔
1980
          if (luaLocal->dfe.getPostPolicy(ret, d_discardedPolicies, d_appliedPolicy)) {
2,122!
1981
            mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());
×
1982
            bool done = false;
×
1983
            handlePolicyHit(prefix, qname, qtype, ret, done, res, depth);
×
1984
            if (done && fromCache != nullptr) {
×
1985
              *fromCache = true;
×
1986
            }
×
1987
          }
×
1988
        }
2,122✔
1989
        return res;
2,126✔
1990
      }
2,126✔
1991

1992
      if (doCacheCheck(qname, authname, wasForwardedOrAuthZone, wasAuthZone, wasForwardRecurse, qtype, ret, depth, prefix, res, context)) {
23,539✔
1993
        // we done
1994
        d_wasOutOfBand = wasAuthZone;
8,580✔
1995
        if (fromCache != nullptr) {
8,580✔
1996
          *fromCache = true;
7,340✔
1997
        }
7,340✔
1998

1999
        if (d_wantsRPZ && !d_appliedPolicy.wasHit()) {
8,580✔
2000
          auto luaLocal = g_luaconfs.getLocal();
8,576✔
2001
          if (luaLocal->dfe.getPostPolicy(ret, d_discardedPolicies, d_appliedPolicy)) {
8,576✔
2002
            mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());
4✔
2003
            bool done = false;
4✔
2004
            handlePolicyHit(prefix, qname, qtype, ret, done, res, depth);
4✔
2005
          }
4✔
2006
        }
8,576✔
2007

2008
        return res;
8,580✔
2009
      }
8,580✔
2010

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

2022
        if (fromCache != nullptr && (!d_cacheonly || stoppedByPolicyHit)) {
18!
2023
          *fromCache = true;
×
2024
        }
×
2025
        /* Apply Post filtering policies */
2026

2027
        if (d_wantsRPZ && !stoppedByPolicyHit) {
18!
2028
          auto luaLocal = g_luaconfs.getLocal();
18✔
2029
          if (luaLocal->dfe.getPostPolicy(ret, d_discardedPolicies, d_appliedPolicy)) {
18!
2030
            mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());
×
2031
            bool done = false;
×
2032
            handlePolicyHit(prefix, qname, qtype, ret, done, res, depth);
×
2033
            if (done && fromCache != nullptr) {
×
2034
              *fromCache = true;
×
2035
            }
×
2036
          }
×
2037
        }
18✔
2038

2039
        return res;
18✔
2040
      }
18✔
2041
    }
14,959✔
2042

2043
    if (d_cacheonly) {
15,383✔
2044
      return 0;
5,721✔
2045
    }
5,721✔
2046

2047
    // When trying to serve-stale, we also only look at the cache. Don't look at d_serveStale, it
2048
    // might be changed by recursive calls (this should be fixed in a better way!).
2049
    if (loop == 1) {
9,662✔
2050
      return res;
4✔
2051
    }
4✔
2052

2053
    LOG(prefix << qname << ": No cache hit for '" << qname << "|" << qtype << "', trying to find an appropriate NS record" << endl);
9,658✔
2054

2055
    DNSName subdomain(qname);
9,658✔
2056
    if (qtype == QType::DS) {
9,658✔
2057
      subdomain.chopOff();
190✔
2058
    }
190✔
2059

2060
    NsSet nsset;
9,658✔
2061
    bool flawedNSSet = false;
9,658✔
2062

2063
    // the two retries allow getBestNSNamesFromCache&co to reprime the root
2064
    // hints, in case they ever go missing
2065
    for (int tries = 0; tries < 2 && nsset.empty(); ++tries) {
19,316✔
2066
      subdomain = getBestNSNamesFromCache(subdomain, qtype, nsset, &flawedNSSet, depth, prefix, beenthere); //  pass beenthere to both occasions
9,656✔
2067
    }
9,656✔
2068

2069
    res = doResolveAt(nsset, subdomain, flawedNSSet, qname, qtype, ret, depth, prefix, beenthere, context, stopAtDelegation, nullptr);
9,658✔
2070

2071
    if (res == -1 && s_save_parent_ns_set) {
9,658✔
2072
      // It did not work out, lets check if we have a saved parent NS set
2073
      map<DNSName, vector<ComboAddress>> fallBack;
217✔
2074
      {
217✔
2075
        auto lock = s_savedParentNSSet.lock();
217✔
2076
        auto domainData = lock->find(subdomain);
217✔
2077
        if (domainData != lock->end() && !domainData->d_nsAddresses.empty()) {
217!
2078
          nsset.clear();
2✔
2079
          // Build the nsset arg and fallBack data for the fallback doResolveAt() attempt
2080
          // Take a copy to be able to release the lock, NsSet is actually a map, go figure
2081
          for (const auto& nsAddress : domainData->d_nsAddresses) {
4✔
2082
            nsset.emplace(nsAddress.first, pair(std::vector<ComboAddress>(), false));
4✔
2083
            fallBack.emplace(nsAddress.first, nsAddress.second);
4✔
2084
          }
4✔
2085
        }
2✔
2086
      }
217✔
2087
      if (!fallBack.empty()) {
217✔
2088
        LOG(prefix << qname << ": Failure, but we have a saved parent NS set, trying that one" << endl);
2!
2089
        res = doResolveAt(nsset, subdomain, flawedNSSet, qname, qtype, ret, depth, prefix, beenthere, context, stopAtDelegation, &fallBack);
2✔
2090
        if (res == 0) {
2!
2091
          // It did work out
2092
          s_savedParentNSSet.lock()->inc(subdomain);
2✔
2093
        }
2✔
2094
      }
2✔
2095
    }
217✔
2096
    /* Apply Post filtering policies */
2097
    if (d_wantsRPZ && !d_appliedPolicy.wasHit()) {
9,660✔
2098
      auto luaLocal = g_luaconfs.getLocal();
9,447✔
2099
      if (luaLocal->dfe.getPostPolicy(ret, d_discardedPolicies, d_appliedPolicy)) {
9,447✔
2100
        mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());
5✔
2101
        bool done = false;
5✔
2102
        handlePolicyHit(prefix, qname, qtype, ret, done, res, depth);
5✔
2103
      }
5✔
2104
    }
9,447✔
2105

2106
    if (res == 0) {
9,658✔
2107
      return 0;
9,060✔
2108
    }
9,060✔
2109

2110
    LOG(prefix << qname << ": Failed (res=" << res << ")" << endl);
598✔
2111
    if (res >= 0) {
600✔
2112
      break;
181✔
2113
    }
181✔
2114
  }
598✔
2115
  return res < 0 ? RCode::ServFail : res;
2,147,484,213✔
2116
}
26,083✔
2117

2118
#if 0
2119
// for testing purposes
2120
static bool ipv6First(const ComboAddress& a, const ComboAddress& b)
2121
{
2122
  return !(a.sin4.sin_family < a.sin4.sin_family);
2123
}
2124
#endif
2125

2126
struct speedOrderCA
2127
{
2128
  speedOrderCA(std::map<ComboAddress, float>& speeds) :
2129
    d_speeds(speeds) {}
1,553✔
2130
  bool operator()(const ComboAddress& lhs, const ComboAddress& rhs) const
2131
  {
1,550✔
2132
    return d_speeds[lhs] < d_speeds[rhs];
1,550✔
2133
  }
1,550✔
2134
  std::map<ComboAddress, float>& d_speeds; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members): nothing wrong afaiks
2135
};
2136

2137
void SyncRes::selectNSOnSpeed(const DNSName& qname, const string& prefix, vector<ComboAddress>& ret)
2138
{
11,057✔
2139
  /* we need to remove from the nsSpeeds collection the existing IPs
2140
     for this nameserver that are no longer in the set, even if there
2141
     is only one or none at all in the current set.
2142
  */
2143
  map<ComboAddress, float> speeds;
11,057✔
2144
  {
11,057✔
2145
    auto lock = s_nsSpeeds.lock();
11,057✔
2146
    const auto& collection = lock->find_or_enter(qname, d_now);
11,057✔
2147
    float factor = collection.getFactor(d_now);
11,057✔
2148
    for (const auto& val : ret) {
12,399✔
2149
      speeds[val] = collection.d_collection[val].get(factor);
12,399✔
2150
    }
12,399✔
2151
    collection.purge(speeds);
11,057✔
2152
  }
11,057✔
2153

2154
  if (ret.size() > 1) {
11,057✔
2155
    shuffle(ret.begin(), ret.end(), pdns::dns_random_engine());
1,420✔
2156
    speedOrderCA speedOrder(speeds);
1,420✔
2157
    stable_sort(ret.begin(), ret.end(), speedOrder);
1,420✔
2158
  }
1,420✔
2159

2160
  if (doLog()) {
11,057✔
2161
    LOG(prefix << qname << ": Nameserver " << qname << " IPs: ");
1,499!
2162
    bool first = true;
1,499✔
2163
    for (const auto& addr : ret) {
1,505✔
2164
      if (first) {
1,505✔
2165
        first = false;
1,499✔
2166
      }
1,499✔
2167
      else {
6✔
2168
        LOG(", ");
6!
2169
      }
6✔
2170
      LOG((addr.toString()) << "(" << fmtfloat(speeds[addr] / 1000.0) << "ms)");
1,505!
2171
    }
1,505✔
2172
    LOG(endl);
1,499!
2173
  }
1,499✔
2174
}
11,057✔
2175

2176
template <typename T>
2177
static bool collectAddresses(const vector<DNSRecord>& cset, vector<ComboAddress>& ret)
2178
{
12,303✔
2179
  bool pushed = false;
12,303✔
2180
  for (const auto& record : cset) {
12,524✔
2181
    if (auto rec = getRR<T>(record)) {
12,522✔
2182
      ret.push_back(rec->getCA(53));
12,399✔
2183
      pushed = true;
12,399✔
2184
    }
12,399✔
2185
  }
12,522✔
2186
  return pushed;
12,303✔
2187
}
12,303✔
2188

2189
/** This function explicitly goes out for A or AAAA addresses
2190
 */
2191
vector<ComboAddress> SyncRes::getAddrs(const DNSName& qname, unsigned int depth, const string& prefix, set<GetBestNSAnswer>& beenthere, bool cacheOnly, unsigned int& addressQueriesForNS)
2192
{
11,147✔
2193
  typedef vector<DNSRecord> res_t;
11,147✔
2194
  typedef vector<ComboAddress> ret_t;
11,147✔
2195
  ret_t ret;
11,147✔
2196

2197
  bool oldCacheOnly = setCacheOnly(cacheOnly);
11,147✔
2198
  bool oldRequireAuthData = d_requireAuthData;
11,147✔
2199
  bool oldValidationRequested = d_DNSSECValidationRequested;
11,147✔
2200
  bool oldFollowCNAME = d_followCNAME;
11,147✔
2201
  bool seenV6 = false;
11,147✔
2202
  const unsigned int startqueries = d_outqueries;
11,147✔
2203
  d_requireAuthData = false;
11,147✔
2204
  d_DNSSECValidationRequested = false;
11,147✔
2205
  d_followCNAME = false;
11,147✔
2206

2207
  MemRecursorCache::Flags flags = MemRecursorCache::None;
11,147✔
2208
  if (d_serveStale) {
11,147!
2209
    flags |= MemRecursorCache::ServeStale;
×
2210
  }
×
2211
  try {
11,147✔
2212
    // First look for both A and AAAA in the cache
2213
    res_t cset;
11,147✔
2214
    if (s_doIPv4 && g_recCache->get(d_now.tv_sec, qname, QType::A, flags, &cset, d_cacheRemote, d_routingTag) > 0) {
11,147✔
2215
      collectAddresses<ARecordContent>(cset, ret);
9,778✔
2216
    }
9,778✔
2217
    if (s_doIPv6 && g_recCache->get(d_now.tv_sec, qname, QType::AAAA, flags, &cset, d_cacheRemote, d_routingTag) > 0) {
11,147✔
2218
      if (collectAddresses<AAAARecordContent>(cset, ret)) {
1,352!
2219
        seenV6 = true;
1,352✔
2220
      }
1,352✔
2221
    }
1,352✔
2222
    if (ret.empty()) {
11,147✔
2223
      // Neither A nor AAAA in the cache...
2224
      Context newContext1;
1,338✔
2225
      cset.clear();
1,338✔
2226
      // Go out to get A's
2227
      if (s_doIPv4 && doResolveNoQNameMinimization(qname, QType::A, cset, depth + 1, beenthere, newContext1) == 0) { // this consults cache, OR goes out
1,338✔
2228
        collectAddresses<ARecordContent>(cset, ret);
1,150✔
2229
      }
1,150✔
2230
      if (s_doIPv6) { // s_doIPv6 **IMPLIES** pdns::isQueryLocalAddressFamilyEnabled(AF_INET6) returned true
1,338✔
2231
        if (ret.empty()) {
53✔
2232
          // We only go out immediately to find IPv6 records if we did not find any IPv4 ones.
2233
          Context newContext2;
31✔
2234
          if (doResolveNoQNameMinimization(qname, QType::AAAA, cset, depth + 1, beenthere, newContext2) == 0) { // this consults cache, OR goes out
31✔
2235
            if (collectAddresses<AAAARecordContent>(cset, ret)) {
22!
2236
              seenV6 = true;
×
2237
            }
×
2238
          }
22✔
2239
        }
31✔
2240
        else {
22✔
2241
          // We have some IPv4 records, consult the cache, we might have encountered some IPv6 glue
2242
          cset.clear();
22✔
2243
          if (g_recCache->get(d_now.tv_sec, qname, QType::AAAA, flags, &cset, d_cacheRemote, d_routingTag) > 0) {
22✔
2244
            if (collectAddresses<AAAARecordContent>(cset, ret)) {
2!
2245
              seenV6 = true;
2✔
2246
            }
2✔
2247
          }
2✔
2248
        }
22✔
2249
      }
53✔
2250
    }
1,338✔
2251
    if (s_doIPv6 && !seenV6 && !cacheOnly) {
11,147!
2252
      // No IPv6 records in cache, check negcache and submit async task if negache does not have the data
2253
      // so that the next time the cache or the negcache will have data
2254
      pushResolveIfNotInNegCache(qname, QType::AAAA, d_now);
926✔
2255
    }
926✔
2256
  }
11,147✔
2257
  catch (const PolicyHitException&) {
11,147✔
2258
    // We ignore a policy hit while trying to retrieve the addresses
2259
    // of a NS and keep processing the current query
2260
  }
×
2261

2262
  if (ret.empty() && d_outqueries > startqueries) {
11,147✔
2263
    // We did 1 or more outgoing queries to resolve this NS name but returned empty handed
2264
    addressQueriesForNS++;
77✔
2265
  }
77✔
2266
  d_requireAuthData = oldRequireAuthData;
11,056✔
2267
  d_DNSSECValidationRequested = oldValidationRequested;
11,056✔
2268
  setCacheOnly(oldCacheOnly);
11,056✔
2269
  d_followCNAME = oldFollowCNAME;
11,056✔
2270

2271
  if (s_max_busy_dot_probes > 0 && s_dot_to_port_853) {
11,056!
2272
    for (auto& add : ret) {
×
2273
      if (shouldDoDoT(add, d_now.tv_sec)) {
×
2274
        add.setPort(853);
×
2275
      }
×
2276
    }
×
2277
  }
×
2278
  selectNSOnSpeed(qname, prefix, ret);
11,056✔
2279
  return ret;
11,056✔
2280
}
11,147✔
2281

2282
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)
2283
{
16,995✔
2284
  DNSName subdomain(qname);
16,995✔
2285
  bestns.clear();
16,995✔
2286
  bool brokeloop = false;
16,995✔
2287
  MemRecursorCache::Flags flags = MemRecursorCache::None;
16,995✔
2288
  if (d_serveStale) {
16,995!
2289
    flags |= MemRecursorCache::ServeStale;
×
2290
  }
×
2291
  do {
38,108✔
2292
    if (cutOffDomain && (subdomain == *cutOffDomain || !subdomain.isPartOf(*cutOffDomain))) {
38,108✔
2293
      break;
64✔
2294
    }
64✔
2295
    brokeloop = false;
38,044✔
2296
    LOG(prefix << qname << ": Checking if we have NS in cache for '" << subdomain << "'" << endl);
38,044!
2297
    vector<DNSRecord> nsVector;
38,044✔
2298
    *flawedNSSet = false;
38,044✔
2299

2300
    if (bool isAuth = false; g_recCache->get(d_now.tv_sec, subdomain, QType::NS, flags, &nsVector, d_cacheRemote, d_routingTag, nullptr, nullptr, nullptr, nullptr, &isAuth) > 0) {
38,044✔
2301
      if (s_maxnsperresolve > 0 && nsVector.size() > s_maxnsperresolve) {
16,983!
2302
        vector<DNSRecord> selected;
20✔
2303
        selected.reserve(s_maxnsperresolve);
20✔
2304
        std::sample(nsVector.cbegin(), nsVector.cend(), std::back_inserter(selected), s_maxnsperresolve, pdns::dns_random_engine());
20✔
2305
        nsVector = std::move(selected);
20✔
2306
      }
20✔
2307
      bestns.reserve(nsVector.size());
16,983✔
2308

2309
      vector<DNSName> missing;
16,983✔
2310
      for (const auto& nsRecord : nsVector) {
86,243✔
2311
        if (nsRecord.d_ttl > (unsigned int)d_now.tv_sec) {
86,243✔
2312
          vector<DNSRecord> aset;
86,242✔
2313
          QType nsqt{QType::ADDR};
86,242✔
2314
          if (s_doIPv4 && !s_doIPv6) {
86,245✔
2315
            nsqt = QType::A;
71,363✔
2316
          }
71,363✔
2317
          else if (!s_doIPv4 && s_doIPv6) {
2,147,498,529!
2318
            nsqt = QType::AAAA;
59✔
2319
          }
59✔
2320

2321
          auto nrr = getRR<NSRecordContent>(nsRecord);
86,242✔
2322
          if (nrr && (!nrr->getNS().isPartOf(subdomain) || g_recCache->get(d_now.tv_sec, nrr->getNS(), nsqt, flags, doLog() ? &aset : nullptr, d_cacheRemote, d_routingTag) > 0)) {
86,247✔
2323
            bestns.push_back(nsRecord);
85,923✔
2324
            LOG(prefix << qname << ": NS (with ip, or non-glue) in cache for '" << subdomain << "' -> '" << nrr->getNS() << "'");
85,923!
2325
            LOG(", within bailiwick: " << nrr->getNS().isPartOf(subdomain));
85,923!
2326
            if (!aset.empty()) {
85,923✔
2327
              LOG(", in cache, ttl=" << (unsigned int)(((time_t)aset.begin()->d_ttl - d_now.tv_sec)) << endl);
4,023!
2328
            }
4,023✔
2329
            else {
81,900✔
2330
              LOG(", not in cache / did not look at cache" << endl);
81,900!
2331
            }
81,900✔
2332
          }
85,923✔
2333
          else if (nrr != nullptr) {
2,147,483,968✔
2334
            *flawedNSSet = true;
321✔
2335
            LOG(prefix << qname << ": NS in cache for '" << subdomain << "', but needs glue (" << nrr->getNS() << ") which we miss or is expired" << endl);
321!
2336
            missing.emplace_back(nrr->getNS());
321✔
2337
          }
321✔
2338
        }
86,242✔
2339
      }
86,243✔
2340
      if (*flawedNSSet && bestns.empty() && isAuth) {
16,983✔
2341
        // The authoritative (child) NS records did not produce any usable addresses, wipe them, so
2342
        // these useless records do not prevent parent records to be inserted into the cache
2343
        LOG(prefix << qname << ": Wiping flawed authoritative NS records for " << subdomain << endl);
3!
2344
        g_recCache->doWipeCache(subdomain, false, QType::NS);
3✔
2345
      }
3✔
2346
      if (!missing.empty() && missing.size() < nsVector.size()) {
16,983✔
2347
        // We miss glue, but we have a chance to resolve it
2348
        // Pick a few and push async tasks to resolve them
2349
        const unsigned int max = 2;
24✔
2350
        unsigned int counter = 0;
24✔
2351
        shuffle(missing.begin(), missing.end(), pdns::dns_random_engine());
24✔
2352
        for (const auto& name : missing) {
24!
2353
          if (s_doIPv4 && pushResolveIfNotInNegCache(name, QType::A, d_now)) {
24!
2354
            LOG(prefix << qname << ": A glue for " << subdomain << " NS " << name << " missing, pushed task to resolve" << endl);
24!
2355
            counter++;
24✔
2356
          }
24✔
2357
          if (s_doIPv6 && pushResolveIfNotInNegCache(name, QType::AAAA, d_now)) {
24!
2358
            LOG(prefix << qname << ": AAAA glue for " << subdomain << " NS " << name << " missing, pushed task to resolve" << endl);
24!
2359
            counter++;
24✔
2360
          }
24✔
2361
          if (counter >= max) {
24!
2362
            break;
24✔
2363
          }
24✔
2364
        }
24✔
2365
      }
24✔
2366

2367
      if (!bestns.empty()) {
16,983✔
2368
        GetBestNSAnswer answer;
16,946✔
2369
        answer.qname = qname;
16,946✔
2370
        answer.qtype = qtype.getCode();
16,946✔
2371
        for (const auto& bestNSRecord : bestns) {
85,919✔
2372
          if (auto nsContent = getRR<NSRecordContent>(bestNSRecord)) {
85,924✔
2373
            answer.bestns.emplace(bestNSRecord.d_name, nsContent->getNS());
85,922✔
2374
          }
85,922✔
2375
        }
85,919✔
2376

2377
        auto insertionPair = beenthere.insert(std::move(answer));
16,946✔
2378
        if (!insertionPair.second) {
16,946✔
2379
          brokeloop = true;
271✔
2380
          LOG(prefix << qname << ": We have NS in cache for '" << subdomain << "' but part of LOOP (already seen " << insertionPair.first->qname << ")! Trying less specific NS" << endl);
271!
2381
          ;
271✔
2382
          if (doLog()) {
271!
2383
            for (auto j = beenthere.begin(); j != beenthere.end(); ++j) {
×
2384
              bool neo = (j == insertionPair.first);
×
2385
              LOG(prefix << qname << ": Beenthere" << (neo ? "*" : "") << ": " << j->qname << "|" << DNSRecordContent::NumberToType(j->qtype) << " (" << (unsigned int)j->bestns.size() << ")" << endl);
×
2386
            }
×
2387
          }
×
2388
          bestns.clear();
271✔
2389
        }
271✔
2390
        else {
16,675✔
2391
          LOG(prefix << qname << ": We have NS in cache for '" << subdomain << "' (flawedNSSet=" << *flawedNSSet << ")" << endl);
16,675!
2392
          return;
16,675✔
2393
        }
16,675✔
2394
      }
16,946✔
2395
    }
16,983✔
2396
    LOG(prefix << qname << ": No valid/useful NS in cache for '" << subdomain << "'" << endl);
21,369!
2397

2398
    if (subdomain.isRoot() && !brokeloop) {
21,369✔
2399
      // We lost the root NS records
2400
      primeHints();
151✔
2401
      LOG(prefix << qname << ": Reprimed the root" << endl);
151!
2402
      /* let's prevent an infinite loop */
2403
      if (!d_updatingRootNS) {
151✔
2404
        auto log = g_slog->withName("housekeeping");
144✔
2405
        getRootNS(d_now, d_asyncResolve, depth, log);
144✔
2406
      }
144✔
2407
    }
151✔
2408
  } while (subdomain.chopOff());
21,369✔
2409
}
16,995✔
2410

2411
SyncRes::domainmap_t::const_iterator SyncRes::getBestAuthZone(DNSName* qname)
2412
{
89,803✔
2413
  if (t_sstorage.domainmap->empty()) {
89,803✔
2414
    return t_sstorage.domainmap->end();
5,440✔
2415
  }
5,440✔
2416

2417
  SyncRes::domainmap_t::const_iterator ret;
84,363✔
2418
  do {
295,825✔
2419
    ret = t_sstorage.domainmap->find(*qname);
295,825✔
2420
    if (ret != t_sstorage.domainmap->end()) {
295,825✔
2421
      break;
1,886✔
2422
    }
1,886✔
2423
  } while (qname->chopOff());
295,825✔
2424
  return ret;
×
2425
}
89,803✔
2426

2427
/** doesn't actually do the work, leaves that to getBestNSFromCache */
2428
DNSName SyncRes::getBestNSNamesFromCache(const DNSName& qname, const QType qtype, NsSet& nsset, bool* flawedNSSet, unsigned int depth, const string& prefix, set<GetBestNSAnswer>& beenthere)
2429
{
9,655✔
2430
  DNSName authOrForwDomain(qname);
9,655✔
2431

2432
  auto iter = getBestAuthZone(&authOrForwDomain);
9,655✔
2433
  // We have an auth, forwarder of forwarder-recurse
2434
  if (iter != t_sstorage.domainmap->end()) {
9,655✔
2435
    if (iter->second.isAuth()) {
274✔
2436
      // this gets picked up in doResolveAt, the empty DNSName, combined with the
2437
      // empty vector means 'we are auth for this zone'
2438
      nsset.insert({DNSName(), {{}, false}});
139✔
2439
      return authOrForwDomain;
139✔
2440
    }
139✔
2441
    if (iter->second.shouldRecurse()) {
135✔
2442
      // Again, picked up in doResolveAt. An empty DNSName, combined with a
2443
      // non-empty vector of ComboAddresses means 'this is a forwarded domain'
2444
      // This is actually picked up in retrieveAddressesForNS called from doResolveAt.
2445
      nsset.insert({DNSName(), {iter->second.d_servers, true}});
59✔
2446
      return authOrForwDomain;
59✔
2447
    }
59✔
2448
  }
135✔
2449

2450
  // We might have a (non-recursive) forwarder, but maybe the cache already contains
2451
  // a better NS
2452
  vector<DNSRecord> bestns;
9,457✔
2453
  DNSName nsFromCacheDomain(g_rootdnsname);
9,457✔
2454
  getBestNSFromCache(qname, qtype, bestns, flawedNSSet, depth, prefix, beenthere);
9,457✔
2455

2456
  // Pick up the auth domain
2457
  for (const auto& nsRecord : bestns) {
9,458✔
2458
    const auto nsContent = getRR<NSRecordContent>(nsRecord);
9,348✔
2459
    if (nsContent) {
9,348✔
2460
      nsFromCacheDomain = nsRecord.d_name;
9,347✔
2461
      break;
9,347✔
2462
    }
9,347✔
2463
  }
9,348✔
2464

2465
  if (iter != t_sstorage.domainmap->end()) {
9,457✔
2466
    if (doLog()) {
76✔
2467
      LOG(prefix << qname << " authOrForwDomain: " << authOrForwDomain << " nsFromCacheDomain: " << nsFromCacheDomain << " isPartof: " << authOrForwDomain.isPartOf(nsFromCacheDomain) << endl);
64!
2468
    }
64✔
2469

2470
    // If the forwarder is better or equal to what's found in the cache, use forwarder. Note that name.isPartOf(name).
2471
    // So queries that get NS for authOrForwDomain itself go to the forwarder
2472
    if (authOrForwDomain.isPartOf(nsFromCacheDomain)) {
76!
2473
      if (doLog()) {
76✔
2474
        LOG(prefix << qname << ": Using forwarder as NS" << endl);
64!
2475
      }
64✔
2476
      nsset.insert({DNSName(), {iter->second.d_servers, false}});
76✔
2477
      return authOrForwDomain;
76✔
2478
    }
76✔
2479
    if (doLog()) {
×
2480
      LOG(prefix << qname << ": Using NS from cache" << endl);
×
2481
    }
×
2482
  }
×
2483
  for (const auto& bestn : bestns) {
51,396✔
2484
    // The actual resolver code will not even look at the ComboAddress or bool
2485
    const auto nsContent = getRR<NSRecordContent>(bestn);
51,396✔
2486
    if (nsContent) {
51,398✔
2487
      nsset.insert({nsContent->getNS(), {{}, false}});
51,397✔
2488
    }
51,397✔
2489
  }
51,396✔
2490
  return nsFromCacheDomain;
9,381✔
2491
}
9,457✔
2492

2493
void SyncRes::updateValidationStatusInCache(const DNSName& qname, const QType qtype, bool aaFlag, vState newState) const
2494
{
64✔
2495
  if (qtype == QType::ANY || qtype == QType::ADDR) {
64!
2496
    // not doing that
2497
    return;
×
2498
  }
×
2499

2500
  if (vStateIsBogus(newState)) {
64✔
2501
    g_recCache->updateValidationStatus(d_now.tv_sec, qname, qtype, d_cacheRemote, d_routingTag, aaFlag, newState, s_maxbogusttl + d_now.tv_sec);
20✔
2502
  }
20✔
2503
  else {
44✔
2504
    g_recCache->updateValidationStatus(d_now.tv_sec, qname, qtype, d_cacheRemote, d_routingTag, aaFlag, newState, boost::none);
44✔
2505
  }
44✔
2506
}
64✔
2507

2508
static pair<bool, unsigned int> scanForCNAMELoop(const DNSName& name, const vector<DNSRecord>& records)
2509
{
3,207✔
2510
  unsigned int numCNames = 0;
3,207✔
2511
  for (const auto& record : records) {
8,422✔
2512
    if (record.d_type == QType::CNAME && record.d_place == DNSResourceRecord::ANSWER) {
8,422!
2513
      ++numCNames;
8,231✔
2514
      if (name == record.d_name) {
8,231✔
2515
        return {true, numCNames};
6✔
2516
      }
6✔
2517
    }
8,231✔
2518
  }
8,422✔
2519
  return {false, numCNames};
3,201✔
2520
}
3,207✔
2521

2522
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)
2523
{
21,065✔
2524
  vector<DNSRecord> cset;
21,065✔
2525
  vector<std::shared_ptr<const RRSIGRecordContent>> signatures;
21,065✔
2526
  vector<std::shared_ptr<DNSRecord>> authorityRecs;
21,065✔
2527
  bool wasAuth = false;
21,065✔
2528
  uint32_t capTTL = std::numeric_limits<uint32_t>::max();
21,065✔
2529
  DNSName foundName;
21,065✔
2530
  DNSName authZone;
21,065✔
2531
  QType foundQT = QType::ENT;
21,065✔
2532

2533
  /* we don't require auth data for forward-recurse lookups */
2534
  MemRecursorCache::Flags flags = MemRecursorCache::None;
21,065✔
2535
  if (!wasForwardRecurse && d_requireAuthData) {
21,066✔
2536
    flags |= MemRecursorCache::RequireAuth;
19,603✔
2537
  }
19,603✔
2538
  if (d_refresh) {
21,065✔
2539
    flags |= MemRecursorCache::Refresh;
300✔
2540
  }
300✔
2541
  if (d_serveStale) {
21,065✔
2542
    flags |= MemRecursorCache::ServeStale;
20✔
2543
  }
20✔
2544
  if (g_recCache->get(d_now.tv_sec, qname, QType::CNAME, flags, &cset, d_cacheRemote, d_routingTag, d_doDNSSEC ? &signatures : nullptr, d_doDNSSEC ? &authorityRecs : nullptr, &d_wasVariable, &context.state, &wasAuth, &authZone, &d_fromAuthIP) > 0) {
21,065✔
2545
    foundName = qname;
2,190✔
2546
    foundQT = QType::CNAME;
2,190✔
2547
  }
2,190✔
2548

2549
  if (foundName.empty() && qname != g_rootdnsname) {
21,065✔
2550
    // look for a DNAME cache hit
2551
    auto labels = qname.getRawLabels();
17,471✔
2552
    DNSName dnameName(g_rootdnsname);
17,471✔
2553

2554
    do {
48,511✔
2555
      dnameName.prependRawLabel(labels.back());
48,511✔
2556
      labels.pop_back();
48,511✔
2557
      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
48,511✔
2558
        break;
17,455✔
2559
      }
17,455✔
2560
      if (g_recCache->get(d_now.tv_sec, dnameName, QType::DNAME, flags, &cset, d_cacheRemote, d_routingTag, d_doDNSSEC ? &signatures : nullptr, d_doDNSSEC ? &authorityRecs : nullptr, &d_wasVariable, &context.state, &wasAuth, &authZone, &d_fromAuthIP) > 0) {
2,147,490,908✔
2561
        foundName = dnameName;
16✔
2562
        foundQT = QType::DNAME;
16✔
2563
        break;
16✔
2564
      }
16✔
2565
    } while (!labels.empty());
31,056✔
2566
  }
17,471✔
2567

2568
  if (foundName.empty()) {
21,065✔
2569
    return false;
18,859✔
2570
  }
18,859✔
2571

2572
  if (qtype == QType::DS && authZone == qname) {
2,206✔
2573
    /* CNAME at APEX of the child zone, we can't use that to prove that
2574
       there is no DS */
2575
    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!
2576
    return false;
4✔
2577
  }
4✔
2578

2579
  for (auto const& record : cset) {
2,203✔
2580
    if (record.d_class != QClass::IN) {
2,202!
2581
      continue;
×
2582
    }
×
2583

2584
    if (record.d_ttl > (unsigned int)d_now.tv_sec) {
2,202✔
2585

2586
      if (!wasAuthZone && shouldValidate() && (wasAuth || wasForwardRecurse) && context.state == vState::Indeterminate && d_requireAuthData) {
2,201!
2587
        /* This means we couldn't figure out the state when this entry was cached */
2588

2589
        vState recordState = getValidationStatus(foundName, !signatures.empty(), qtype == QType::DS, depth, prefix);
8✔
2590
        if (recordState == vState::Secure) {
8✔
2591
          LOG(prefix << qname << ": Got vState::Indeterminate state from the " << foundQT.toString() << " cache, validating.." << endl);
6!
2592
          context.state = SyncRes::validateRecordsWithSigs(depth, prefix, qname, qtype, foundName, foundQT, cset, signatures);
6✔
2593
          if (context.state != vState::Indeterminate) {
6!
2594
            LOG(prefix << qname << ": Got vState::Indeterminate state from the " << foundQT.toString() << " cache, new validation result is " << context.state << endl);
6!
2595
            if (vStateIsBogus(context.state)) {
6✔
2596
              capTTL = s_maxbogusttl;
4✔
2597
            }
4✔
2598
            updateValidationStatusInCache(foundName, foundQT, wasAuth, context.state);
6✔
2599
          }
6✔
2600
        }
6✔
2601
      }
8✔
2602

2603
      LOG(prefix << qname << ": Found cache " << foundQT.toString() << " hit for '" << foundName << "|" << foundQT.toString() << "' to '" << record.getContent()->getZoneRepresentation() << "', validation state is " << context.state << endl);
2,201!
2604

2605
      DNSRecord dnsRecord = record;
2,201✔
2606
      auto alreadyPresent = false;
2,201✔
2607

2608
      if (checkForDups) {
2,201✔
2609
        // This can happen on the 2nd iteration of the servestale loop, where the first iteration
2610
        // added a C/DNAME record, but the target resolve failed
2611
        for (const auto& dnsrec : ret) {
6✔
2612
          if (dnsrec.d_type == foundQT && dnsrec.d_name == record.d_name) {
2!
2613
            alreadyPresent = true;
2✔
2614
            break;
2✔
2615
          }
2✔
2616
        }
2✔
2617
      }
6✔
2618
      dnsRecord.d_ttl -= d_now.tv_sec;
2,201✔
2619
      dnsRecord.d_ttl = std::min(dnsRecord.d_ttl, capTTL);
2,201✔
2620
      const uint32_t ttl = dnsRecord.d_ttl;
2,201✔
2621
      if (!alreadyPresent) {
2,202✔
2622
        ret.reserve(ret.size() + 2 + signatures.size() + authorityRecs.size());
2,199✔
2623
        ret.push_back(dnsRecord);
2,199✔
2624

2625
        for (const auto& signature : signatures) {
2,199✔
2626
          DNSRecord sigdr;
45✔
2627
          sigdr.d_type = QType::RRSIG;
45✔
2628
          sigdr.d_name = foundName;
45✔
2629
          sigdr.d_ttl = ttl;
45✔
2630
          sigdr.setContent(signature);
45✔
2631
          sigdr.d_place = DNSResourceRecord::ANSWER;
45✔
2632
          sigdr.d_class = QClass::IN;
45✔
2633
          ret.push_back(sigdr);
45✔
2634
        }
45✔
2635

2636
        for (const auto& rec : authorityRecs) {
2,199!
2637
          DNSRecord authDR(*rec);
×
2638
          authDR.d_ttl = ttl;
×
2639
          ret.push_back(authDR);
×
2640
        }
×
2641
      }
2,199✔
2642

2643
      DNSName newTarget;
2,201✔
2644
      if (foundQT == QType::DNAME) {
2,201✔
2645
        if (qtype == QType::DNAME && qname == foundName) { // client wanted the DNAME, no need to synthesize a CNAME
16!
2646
          res = RCode::NoError;
2✔
2647
          return true;
2✔
2648
        }
2✔
2649
        // Synthesize a CNAME
2650
        auto dnameRR = getRR<DNAMERecordContent>(record);
14✔
2651
        if (dnameRR == nullptr) {
14!
2652
          throw ImmediateServFailException("Unable to get record content for " + foundName.toLogString() + "|DNAME cache entry");
×
2653
        }
×
2654
        const auto& dnameSuffix = dnameRR->getTarget();
14✔
2655
        DNSName targetPrefix = qname.makeRelative(foundName);
14✔
2656
        try {
14✔
2657
          dnsRecord.d_type = QType::CNAME;
14✔
2658
          dnsRecord.d_name = targetPrefix + foundName;
14✔
2659
          newTarget = targetPrefix + dnameSuffix;
14✔
2660
          dnsRecord.setContent(std::make_shared<CNAMERecordContent>(CNAMERecordContent(newTarget)));
14✔
2661
          ret.push_back(dnsRecord);
14✔
2662
        }
14✔
2663
        catch (const std::exception& e) {
14✔
2664
          // We should probably catch an std::range_error here and set the rcode to YXDOMAIN (RFC 6672, section 2.2)
2665
          // But this is consistent with processRecords
2666
          throw ImmediateServFailException("Unable to perform DNAME substitution(DNAME owner: '" + foundName.toLogString() + "', DNAME target: '" + dnameSuffix.toLogString() + "', substituted name: '" + targetPrefix.toLogString() + "." + dnameSuffix.toLogString() + "' : " + e.what());
×
2667
        }
×
2668

2669
        LOG(prefix << qname << ": Synthesized " << dnsRecord.d_name << "|CNAME " << newTarget << endl);
14!
2670
      }
14✔
2671

2672
      if (qtype == QType::CNAME) { // perhaps they really wanted a CNAME!
2,199✔
2673
        res = RCode::NoError;
2✔
2674
        return true;
2✔
2675
      }
2✔
2676

2677
      if (qtype == QType::DS || qtype == QType::DNSKEY) {
2,198✔
2678
        res = RCode::NoError;
18✔
2679
        return true;
18✔
2680
      }
18✔
2681

2682
      // We have a DNAME _or_ CNAME cache hit and the client wants something else than those two.
2683
      // Let's find the answer!
2684
      if (foundQT == QType::CNAME) {
2,179✔
2685
        const auto cnameContent = getRR<CNAMERecordContent>(record);
2,164✔
2686
        if (cnameContent == nullptr) {
2,164!
2687
          throw ImmediateServFailException("Unable to get record content for " + foundName.toLogString() + "|CNAME cache entry");
×
2688
        }
×
2689
        newTarget = cnameContent->getTarget();
2,164✔
2690
      }
2,164✔
2691

2692
      if (qname == newTarget) {
2,179✔
2693
        string msg = "Got a CNAME referral (from cache) to self";
2✔
2694
        LOG(prefix << qname << ": " << msg << endl);
2!
2695
        throw ImmediateServFailException(std::move(msg));
2✔
2696
      }
2✔
2697

2698
      if (newTarget.isPartOf(qname)) {
2,177✔
2699
        // a.b.c. CNAME x.a.b.c will go to great depths with QM on
2700
        string msg = "Got a CNAME referral (from cache) to child, disabling QM";
45✔
2701
        LOG(prefix << qname << ": " << msg << endl);
45!
2702
        setQNameMinimization(false);
45✔
2703
      }
45✔
2704

2705
      if (!d_followCNAME) {
2,177✔
2706
        res = RCode::NoError;
146✔
2707
        return true;
146✔
2708
      }
146✔
2709

2710
      // Check to see if we already have seen the new target as a previous target or that we have a very long CNAME chain
2711
      const auto [CNAMELoop, numCNAMEs] = scanForCNAMELoop(newTarget, ret);
2,031✔
2712
      if (CNAMELoop) {
2,031✔
2713
        string msg = "got a CNAME referral (from cache) that causes a loop";
4✔
2714
        LOG(prefix << qname << ": Status=" << msg << endl);
4!
2715
        throw ImmediateServFailException(std::move(msg));
4✔
2716
      }
4✔
2717
      if (numCNAMEs > s_max_CNAMES_followed) {
2,027✔
2718
        string msg = "max number of CNAMEs exceeded";
4✔
2719
        LOG(prefix << qname << ": Status=" << msg << endl);
4!
2720
        throw ImmediateServFailException(std::move(msg));
4✔
2721
      }
4✔
2722

2723
      set<GetBestNSAnswer> beenthere;
2,023✔
2724
      Context cnameContext;
2,023✔
2725
      // Be aware that going out on the network might be disabled (cache-only), for example because we are in QM Step0,
2726
      // so you can't trust that a real lookup will have been made.
2727
      res = doResolve(newTarget, qtype, ret, depth + 1, beenthere, cnameContext);
2,023✔
2728
      LOG(prefix << qname << ": Updating validation state for response to " << qname << " from " << context.state << " with the state from the DNAME/CNAME quest: " << cnameContext.state << endl);
2,023!
2729
      updateValidationState(qname, context.state, cnameContext.state, prefix);
2,023✔
2730

2731
      return true;
2,023✔
2732
    }
2,027✔
2733
  }
2,202✔
2734
  throw ImmediateServFailException("Could not determine whether or not there was a CNAME or DNAME in cache for '" + qname.toLogString() + "'");
2,147,483,647✔
2735
}
2,202✔
2736

2737
namespace
2738
{
2739
struct CacheEntry
2740
{
2741
  vector<DNSRecord> records;
2742
  vector<shared_ptr<const RRSIGRecordContent>> signatures;
2743
  time_t d_ttl_time{0};
2744
  uint32_t signaturesTTL{std::numeric_limits<uint32_t>::max()};
2745
};
2746
struct CacheKey
2747
{
2748
  DNSName name;
2749
  QType type;
2750
  DNSResourceRecord::Place place;
2751
  bool operator<(const CacheKey& rhs) const
2752
  {
301,238✔
2753
    return std::tie(type, place, name) < std::tie(rhs.type, rhs.place, rhs.name);
301,238✔
2754
  }
301,238✔
2755
};
2756
using tcache_t = map<CacheKey, CacheEntry>;
2757
}
2758

2759
static void reapRecordsFromNegCacheEntryForValidation(tcache_t& tcache, const vector<DNSRecord>& records)
2760
{
32✔
2761
  for (const auto& rec : records) {
32✔
2762
    if (rec.d_type == QType::RRSIG) {
22✔
2763
      auto rrsig = getRR<RRSIGRecordContent>(rec);
10✔
2764
      if (rrsig) {
10!
2765
        tcache[{rec.d_name, rrsig->d_type, rec.d_place}].signatures.push_back(rrsig);
10✔
2766
      }
10✔
2767
    }
10✔
2768
    else {
12✔
2769
      tcache[{rec.d_name, rec.d_type, rec.d_place}].records.push_back(rec);
12✔
2770
    }
12✔
2771
  }
22✔
2772
}
32✔
2773

2774
static bool negativeCacheEntryHasSOA(const NegCache::NegCacheEntry& negEntry)
2775
{
16✔
2776
  return !negEntry.authoritySOA.records.empty();
16✔
2777
}
16✔
2778

2779
static void reapRecordsForValidation(std::map<QType, CacheEntry>& entries, const vector<DNSRecord>& records)
2780
{
3✔
2781
  for (const auto& rec : records) {
5✔
2782
    entries[rec.d_type].records.push_back(rec);
5✔
2783
  }
5✔
2784
}
3✔
2785

2786
static void reapSignaturesForValidation(std::map<QType, CacheEntry>& entries, const vector<std::shared_ptr<const RRSIGRecordContent>>& signatures)
2787
{
3✔
2788
  for (const auto& sig : signatures) {
5✔
2789
    entries[sig->d_type].signatures.push_back(sig);
5✔
2790
  }
5✔
2791
}
3✔
2792

2793
/*!
2794
 * Convenience function to push the records from records into ret with a new TTL
2795
 *
2796
 * \param records DNSRecords that need to go into ret
2797
 * \param ttl     The new TTL for these records
2798
 * \param ret     The vector of DNSRecords that should contain the records with the modified TTL
2799
 */
2800
static void addTTLModifiedRecords(vector<DNSRecord>& records, const uint32_t ttl, vector<DNSRecord>& ret)
2801
{
10,419✔
2802
  for (auto& rec : records) {
10,421✔
2803
    rec.d_ttl = ttl;
8,729✔
2804
    ret.push_back(std::move(rec));
8,729✔
2805
  }
8,729✔
2806
}
10,419✔
2807

2808
void SyncRes::computeNegCacheValidationStatus(const NegCache::NegCacheEntry& negEntry, const DNSName& qname, const QType qtype, const int res, vState& state, unsigned int depth, const string& prefix)
2809
{
8✔
2810
  tcache_t tcache;
8✔
2811
  reapRecordsFromNegCacheEntryForValidation(tcache, negEntry.authoritySOA.records);
8✔
2812
  reapRecordsFromNegCacheEntryForValidation(tcache, negEntry.authoritySOA.signatures);
8✔
2813
  reapRecordsFromNegCacheEntryForValidation(tcache, negEntry.DNSSECRecords.records);
8✔
2814
  reapRecordsFromNegCacheEntryForValidation(tcache, negEntry.DNSSECRecords.signatures);
8✔
2815

2816
  for (const auto& entry : tcache) {
12✔
2817
    // this happens when we did store signatures, but passed on the records themselves
2818
    if (entry.second.records.empty()) {
12!
2819
      continue;
×
2820
    }
×
2821

2822
    const DNSName& owner = entry.first.name;
12✔
2823

2824
    vState recordState = getValidationStatus(owner, !entry.second.signatures.empty(), qtype == QType::DS, depth, prefix);
12✔
2825
    if (state == vState::Indeterminate) {
12✔
2826
      state = recordState;
8✔
2827
    }
8✔
2828

2829
    if (recordState == vState::Secure) {
12✔
2830
      recordState = SyncRes::validateRecordsWithSigs(depth, prefix, qname, qtype, owner, QType(entry.first.type), entry.second.records, entry.second.signatures);
10✔
2831
    }
10✔
2832

2833
    if (recordState != vState::Indeterminate && recordState != state) {
12!
2834
      updateValidationState(qname, state, recordState, prefix);
×
2835
      if (state != vState::Secure) {
×
2836
        break;
×
2837
      }
×
2838
    }
×
2839
  }
12✔
2840

2841
  if (state == vState::Secure) {
8✔
2842
    vState neValidationState = negEntry.d_validationState;
6✔
2843
    dState expectedState = res == RCode::NXDomain ? dState::NXDOMAIN : dState::NXQTYPE;
6!
2844
    dState denialState = getDenialValidationState(negEntry, expectedState, false, prefix);
6✔
2845
    updateDenialValidationState(qname, neValidationState, negEntry.d_name, state, denialState, expectedState, qtype == QType::DS, depth, prefix);
6✔
2846
  }
6✔
2847
  if (state != vState::Indeterminate) {
8!
2848
    /* validation succeeded, let's update the cache entry so we don't have to validate again */
2849
    boost::optional<time_t> capTTD = boost::none;
8✔
2850
    if (vStateIsBogus(state)) {
8✔
2851
      capTTD = d_now.tv_sec + s_maxbogusttl;
2✔
2852
    }
2✔
2853
    g_negCache->updateValidationStatus(negEntry.d_name, negEntry.d_qtype, state, capTTD);
8✔
2854
  }
8✔
2855
}
8✔
2856

2857
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)
2858
{
23,486✔
2859
  bool giveNegative = false;
23,486✔
2860

2861
  // 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)
2862
  DNSName sqname(qname);
23,486✔
2863
  QType sqt(qtype);
23,486✔
2864
  uint32_t sttl = 0;
23,486✔
2865
  //  cout<<"Lookup for '"<<qname<<"|"<<qtype.toString()<<"' -> "<<getLastLabel(qname)<<endl;
2866
  vState cachedState{};
23,486✔
2867
  NegCache::NegCacheEntry negEntry;
23,486✔
2868

2869
  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.
23,488!
2870
    sttl = negEntry.d_ttd - d_now.tv_sec;
4✔
2871
    LOG(prefix << qname << ": Entire name '" << qname << "', is negatively cached via '" << negEntry.d_auth << "' & '" << negEntry.d_name << "' for another " << sttl << " seconds" << endl);
4!
2872
    res = RCode::NXDomain;
4✔
2873
    giveNegative = true;
4✔
2874
    cachedState = negEntry.d_validationState;
4✔
2875
    if (s_addExtendedResolutionDNSErrors) {
4✔
2876
      context.extendedError = EDNSExtendedError{static_cast<uint16_t>(EDNSExtendedError::code::Synthesized), "Result synthesized by root-nx-trust"};
2✔
2877
    }
2✔
2878
  }
4✔
2879
  else if (g_negCache->get(qname, qtype, d_now, negEntry, false, d_serveStale, d_refresh)) {
23,482✔
2880
    /* If we are looking for a DS, discard NXD if auth == qname
2881
       and ask for a specific denial instead */
2882
    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)) {
2,607!
2883
      /* 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
2884
         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
2885
         query. */
2886
      if (qtype == QType::DS && negEntry.d_qtype.getCode() != 0 && !d_externalDSQuery.empty() && qname == d_externalDSQuery && !negativeCacheEntryHasSOA(negEntry)) {
2,607✔
2887
        giveNegative = false;
6✔
2888
      }
6✔
2889
      else {
2,601✔
2890
        res = RCode::NXDomain;
2,601✔
2891
        sttl = negEntry.d_ttd - d_now.tv_sec;
2,601✔
2892
        giveNegative = true;
2,601✔
2893
        cachedState = negEntry.d_validationState;
2,601✔
2894
        if (negEntry.d_qtype.getCode() != 0) {
2,601✔
2895
          LOG(prefix << qname << "|" << qtype << ": Is negatively cached via '" << negEntry.d_auth << "' for another " << sttl << " seconds" << endl);
2,562!
2896
          res = RCode::NoError;
2,562✔
2897
          if (s_addExtendedResolutionDNSErrors) {
2,562✔
2898
            context.extendedError = EDNSExtendedError{static_cast<uint16_t>(EDNSExtendedError::code::Synthesized), "Result from negative cache"};
2,434✔
2899
          }
2,434✔
2900
        }
2,562✔
2901
        else {
39✔
2902
          LOG(prefix << qname << ": Entire name '" << qname << "' is negatively cached via '" << negEntry.d_auth << "' for another " << sttl << " seconds" << endl);
39!
2903
          if (s_addExtendedResolutionDNSErrors) {
39✔
2904
            context.extendedError = EDNSExtendedError{static_cast<uint16_t>(EDNSExtendedError::code::Synthesized), "Result from negative cache for entire name"};
27✔
2905
          }
27✔
2906
        }
39✔
2907
      }
2,601✔
2908
    }
2,607✔
2909
  }
2,607✔
2910
  else if (s_hardenNXD != HardenNXD::No && !qname.isRoot() && !wasForwardedOrAuthZone) {
20,878✔
2911
    auto labels = qname.getRawLabels();
18,894✔
2912
    DNSName negCacheName(g_rootdnsname);
18,894✔
2913
    negCacheName.prependRawLabel(labels.back());
18,894✔
2914
    labels.pop_back();
18,894✔
2915
    while (!labels.empty()) {
48,375✔
2916
      if (g_negCache->get(negCacheName, QType::ENT, d_now, negEntry, true, d_serveStale, d_refresh)) {
29,510✔
2917
        if (negEntry.d_validationState == vState::Indeterminate && validationEnabled()) {
33!
2918
          // LOG(prefix << negCacheName <<  " negatively cached and vState::Indeterminate, trying to validate NXDOMAIN" << endl);
2919
          // ...
2920
          // And get the updated ne struct
2921
          // t_sstorage.negcache.get(negCacheName, QType(0), d_now, ne, true);
2922
        }
×
2923
        if ((s_hardenNXD == HardenNXD::Yes && !vStateIsBogus(negEntry.d_validationState)) || negEntry.d_validationState == vState::Secure) {
33!
2924
          res = RCode::NXDomain;
29✔
2925
          sttl = negEntry.d_ttd - d_now.tv_sec;
29✔
2926
          giveNegative = true;
29✔
2927
          cachedState = negEntry.d_validationState;
29✔
2928
          LOG(prefix << qname << ": Name '" << negCacheName << "' and below, is negatively cached via '" << negEntry.d_auth << "' for another " << sttl << " seconds" << endl);
29!
2929
          if (s_addExtendedResolutionDNSErrors) {
29✔
2930
            context.extendedError = EDNSExtendedError{static_cast<uint16_t>(EDNSExtendedError::code::Synthesized), "Result synthesized by nothing-below-nxdomain (RFC8020)"};
13✔
2931
          }
13✔
2932
          break;
29✔
2933
        }
29✔
2934
      }
33✔
2935
      negCacheName.prependRawLabel(labels.back());
29,481✔
2936
      labels.pop_back();
29,481✔
2937
    }
29,481✔
2938
  }
18,894✔
2939

2940
  if (giveNegative) {
23,486✔
2941

2942
    context.state = cachedState;
2,634✔
2943

2944
    if (!wasAuthZone && shouldValidate() && context.state == vState::Indeterminate) {
2,634✔
2945
      LOG(prefix << qname << ": Got vState::Indeterminate state for records retrieved from the negative cache, validating.." << endl);
8!
2946
      computeNegCacheValidationStatus(negEntry, qname, qtype, res, context.state, depth, prefix);
8✔
2947

2948
      if (context.state != cachedState && vStateIsBogus(context.state)) {
8!
2949
        sttl = std::min(sttl, s_maxbogusttl);
2✔
2950
      }
2✔
2951
    }
8✔
2952

2953
    // Transplant SOA to the returned packet
2954
    addTTLModifiedRecords(negEntry.authoritySOA.records, sttl, ret);
2,634✔
2955
    if (d_doDNSSEC) {
2,634✔
2956
      addTTLModifiedRecords(negEntry.authoritySOA.signatures, sttl, ret);
2,595✔
2957
      addTTLModifiedRecords(negEntry.DNSSECRecords.records, sttl, ret);
2,595✔
2958
      addTTLModifiedRecords(negEntry.DNSSECRecords.signatures, sttl, ret);
2,595✔
2959
    }
2,595✔
2960

2961
    LOG(prefix << qname << ": Updating validation state with negative cache content for " << qname << " to " << context.state << endl);
2,634!
2962
    return true;
2,634✔
2963
  }
2,634✔
2964

2965
  vector<DNSRecord> cset;
20,852✔
2966
  bool found = false;
20,852✔
2967
  bool expired = false;
20,852✔
2968
  vector<std::shared_ptr<const RRSIGRecordContent>> signatures;
20,852✔
2969
  vector<std::shared_ptr<DNSRecord>> authorityRecs;
20,852✔
2970
  uint32_t ttl = 0;
20,852✔
2971
  uint32_t capTTL = std::numeric_limits<uint32_t>::max();
20,852✔
2972
  bool wasCachedAuth{};
20,852✔
2973
  MemRecursorCache::Flags flags = MemRecursorCache::None;
20,852✔
2974
  if (!wasForwardRecurse && d_requireAuthData) {
20,854✔
2975
    flags |= MemRecursorCache::RequireAuth;
19,402✔
2976
  }
19,402✔
2977
  if (d_serveStale) {
20,852✔
2978
    flags |= MemRecursorCache::ServeStale;
10✔
2979
  }
10✔
2980
  if (d_refresh) {
20,852✔
2981
    flags |= MemRecursorCache::Refresh;
300✔
2982
  }
300✔
2983
  if (g_recCache->get(d_now.tv_sec, sqname, sqt, flags, &cset, d_cacheRemote, d_routingTag, d_doDNSSEC ? &signatures : nullptr, d_doDNSSEC ? &authorityRecs : nullptr, &d_wasVariable, &cachedState, &wasCachedAuth, nullptr, &d_fromAuthIP) > 0) {
2,147,492,245✔
2984

2985
    LOG(prefix << sqname << ": Found cache hit for " << sqt.toString() << ": ");
5,904!
2986

2987
    if (!wasAuthZone && shouldValidate() && (wasCachedAuth || wasForwardRecurse) && cachedState == vState::Indeterminate && d_requireAuthData) {
5,904!
2988

2989
      /* This means we couldn't figure out the state when this entry was cached */
2990
      vState recordState = getValidationStatus(qname, !signatures.empty(), qtype == QType::DS, depth, prefix);
53✔
2991

2992
      if (recordState == vState::Secure) {
53✔
2993
        LOG(prefix << sqname << ": Got vState::Indeterminate state from the cache, validating.." << endl);
45!
2994
        if (sqt == QType::DNSKEY && sqname == getSigner(signatures)) {
45✔
2995
          cachedState = validateDNSKeys(sqname, cset, signatures, depth, prefix);
2✔
2996
        }
2✔
2997
        else {
43✔
2998
          if (sqt == QType::ANY) {
43✔
2999
            std::map<QType, CacheEntry> types;
3✔
3000
            reapRecordsForValidation(types, cset);
3✔
3001
            reapSignaturesForValidation(types, signatures);
3✔
3002

3003
            for (const auto& type : types) {
5✔
3004
              vState cachedRecordState{};
5✔
3005
              if (type.first == QType::DNSKEY && sqname == getSigner(type.second.signatures)) {
5!
3006
                cachedRecordState = validateDNSKeys(sqname, type.second.records, type.second.signatures, depth, prefix);
×
3007
              }
×
3008
              else {
5✔
3009
                cachedRecordState = SyncRes::validateRecordsWithSigs(depth, prefix, qname, qtype, sqname, type.first, type.second.records, type.second.signatures);
5✔
3010
              }
5✔
3011
              updateDNSSECValidationState(cachedState, cachedRecordState);
5✔
3012
            }
5✔
3013
          }
3✔
3014
          else {
40✔
3015
            cachedState = SyncRes::validateRecordsWithSigs(depth, prefix, qname, qtype, sqname, sqt, cset, signatures);
40✔
3016
          }
40✔
3017
        }
43✔
3018
      }
45✔
3019
      else {
8✔
3020
        cachedState = recordState;
8✔
3021
      }
8✔
3022

3023
      if (cachedState != vState::Indeterminate) {
53!
3024
        LOG(prefix << qname << ": Got vState::Indeterminate state from the cache, validation result is " << cachedState << endl);
53!
3025
        if (vStateIsBogus(cachedState)) {
53✔
3026
          capTTL = s_maxbogusttl;
10✔
3027
        }
10✔
3028
        if (sqt != QType::ANY && sqt != QType::ADDR) {
53!
3029
          updateValidationStatusInCache(sqname, sqt, wasCachedAuth, cachedState);
50✔
3030
        }
50✔
3031
      }
53✔
3032
    }
53✔
3033

3034
    for (auto j = cset.cbegin(); j != cset.cend(); ++j) {
14,732✔
3035

3036
      LOG(j->getContent()->getZoneRepresentation());
8,828!
3037

3038
      if (j->d_class != QClass::IN) {
8,828!
3039
        continue;
×
3040
      }
×
3041

3042
      if (j->d_ttl > (unsigned int)d_now.tv_sec) {
8,828✔
3043
        DNSRecord dnsRecord = *j;
8,826✔
3044
        dnsRecord.d_ttl -= d_now.tv_sec;
8,826✔
3045
        dnsRecord.d_ttl = std::min(dnsRecord.d_ttl, capTTL);
8,826✔
3046
        ttl = dnsRecord.d_ttl;
8,826✔
3047
        ret.push_back(dnsRecord);
8,826✔
3048
        LOG("[ttl=" << dnsRecord.d_ttl << "] ");
8,826!
3049
        found = true;
8,826✔
3050
      }
8,826✔
3051
      else {
2✔
3052
        LOG("[expired] ");
2!
3053
        expired = true;
2✔
3054
      }
2✔
3055
    }
8,828✔
3056

3057
    ret.reserve(ret.size() + signatures.size() + authorityRecs.size());
5,904✔
3058

3059
    for (const auto& signature : signatures) {
5,904✔
3060
      DNSRecord dnsRecord;
5,285✔
3061
      dnsRecord.d_type = QType::RRSIG;
5,285✔
3062
      dnsRecord.d_name = sqname;
5,285✔
3063
      dnsRecord.d_ttl = ttl;
5,285✔
3064
      dnsRecord.setContent(signature);
5,285✔
3065
      dnsRecord.d_place = DNSResourceRecord::ANSWER;
5,285✔
3066
      dnsRecord.d_class = QClass::IN;
5,285✔
3067
      ret.push_back(dnsRecord);
5,285✔
3068
    }
5,285✔
3069

3070
    for (const auto& rec : authorityRecs) {
5,904✔
3071
      DNSRecord dnsRecord(*rec);
32✔
3072
      dnsRecord.d_ttl = ttl;
32✔
3073
      ret.push_back(dnsRecord);
32✔
3074
    }
32✔
3075

3076
    LOG(endl);
5,904!
3077
    if (found && !expired) {
5,904!
3078
      if (!giveNegative) {
5,903✔
3079
        res = 0;
5,902✔
3080
      }
5,902✔
3081
      LOG(prefix << qname << ": Updating validation state with cache content for " << qname << " to " << cachedState << endl);
5,903!
3082
      context.state = cachedState;
5,903✔
3083
      return true;
5,903✔
3084
    }
5,903✔
3085
    LOG(prefix << qname << ": Cache had only stale entries" << endl);
1!
3086
  }
1✔
3087

3088
  /* let's check if we have a NSEC covering that record */
3089
  if (g_aggressiveNSECCache && !wasForwardedOrAuthZone) {
14,951✔
3090
    if (g_aggressiveNSECCache->getDenial(d_now.tv_sec, qname, qtype, ret, res, d_cacheRemote, d_routingTag, d_doDNSSEC, d_validationContext, LogObject(prefix))) {
12,690✔
3091
      context.state = vState::Secure;
42✔
3092
      if (s_addExtendedResolutionDNSErrors) {
42✔
3093
        context.extendedError = EDNSExtendedError{static_cast<uint16_t>(EDNSExtendedError::code::Synthesized), "Result synthesized from aggressive NSEC cache (RFC8198)"};
26✔
3094
      }
26✔
3095
      return true;
42✔
3096
    }
42✔
3097
  }
12,690✔
3098

3099
  return false;
14,907✔
3100
}
14,949✔
3101

3102
bool SyncRes::moreSpecificThan(const DNSName& lhs, const DNSName& rhs)
3103
{
10,865✔
3104
  return (lhs.isPartOf(rhs) && lhs.countLabels() > rhs.countLabels());
10,865!
3105
}
10,865✔
3106

3107
struct speedOrder
3108
{
3109
  bool operator()(const std::pair<DNSName, float>& lhs, const std::pair<DNSName, float>& rhs) const
3110
  {
116,525✔
3111
    return lhs.second < rhs.second;
116,525✔
3112
  }
116,525✔
3113
};
3114

3115
std::vector<std::pair<DNSName, float>> SyncRes::shuffleInSpeedOrder(const DNSName& qname, NsSet& tnameservers, const string& prefix)
3116
{
11,169✔
3117
  std::vector<std::pair<DNSName, float>> rnameservers;
11,169✔
3118
  rnameservers.reserve(tnameservers.size());
11,169✔
3119
  for (const auto& tns : tnameservers) {
56,761✔
3120
    float speed = s_nsSpeeds.lock()->fastest(tns.first, d_now);
56,761✔
3121
    rnameservers.emplace_back(tns.first, speed);
56,761✔
3122
    if (tns.first.empty()) { // this was an authoritative OOB zone, don't pollute the nsSpeeds with that
56,761✔
3123
      return rnameservers;
272✔
3124
    }
272✔
3125
  }
56,761✔
3126

3127
  shuffle(rnameservers.begin(), rnameservers.end(), pdns::dns_random_engine());
10,897✔
3128
  speedOrder speedCompare;
10,897✔
3129
  stable_sort(rnameservers.begin(), rnameservers.end(), speedCompare);
10,897✔
3130

3131
  if (doLog()) {
10,897✔
3132
    LOG(prefix << qname << ": Nameservers: ");
1,487!
3133
    for (auto i = rnameservers.begin(); i != rnameservers.end(); ++i) {
3,786✔
3134
      if (i != rnameservers.begin()) {
2,299✔
3135
        LOG(", ");
812!
3136
        if (((i - rnameservers.begin()) % 3) == 0) {
812✔
3137
          LOG(endl
116!
3138
              << prefix << "             ");
116✔
3139
        }
116✔
3140
      }
812✔
3141
      LOG(i->first.toLogString() << "(" << fmtfloat(i->second / 1000.0) << "ms)");
2,299!
3142
    }
2,299✔
3143
    LOG(endl);
1,487!
3144
  }
1,487✔
3145
  return rnameservers;
10,897✔
3146
}
11,169✔
3147

3148
vector<ComboAddress> SyncRes::shuffleForwardSpeed(const DNSName& qname, const vector<ComboAddress>& rnameservers, const string& prefix, const bool wasRd)
3149
{
133✔
3150
  vector<ComboAddress> nameservers = rnameservers;
133✔
3151
  map<ComboAddress, float> speeds;
133✔
3152

3153
  for (const auto& val : nameservers) {
161✔
3154
    DNSName nsName = DNSName(val.toStringWithPort());
161✔
3155
    float speed = s_nsSpeeds.lock()->fastest(nsName, d_now);
161✔
3156
    speeds[val] = speed;
161✔
3157
  }
161✔
3158
  shuffle(nameservers.begin(), nameservers.end(), pdns::dns_random_engine());
133✔
3159
  speedOrderCA speedCompare(speeds);
133✔
3160
  stable_sort(nameservers.begin(), nameservers.end(), speedCompare);
133✔
3161

3162
  if (doLog()) {
133✔
3163
    LOG(prefix << qname << ": Nameservers: ");
75!
3164
    for (auto i = nameservers.cbegin(); i != nameservers.cend(); ++i) {
174✔
3165
      if (i != nameservers.cbegin()) {
99✔
3166
        LOG(", ");
24!
3167
        if (((i - nameservers.cbegin()) % 3) == 0) {
24!
3168
          LOG(endl
×
3169
              << prefix << "             ");
×
3170
        }
×
3171
      }
24✔
3172
      LOG((wasRd ? string("+") : string("-")) << i->toStringWithPort() << "(" << fmtfloat(speeds[*i] / 1000.0) << "ms)");
99!
3173
    }
99✔
3174
    LOG(endl);
75!
3175
  }
75✔
3176
  return nameservers;
133✔
3177
}
133✔
3178

3179
static uint32_t getRRSIGTTL(const time_t now, const std::shared_ptr<const RRSIGRecordContent>& rrsig)
3180
{
5,554✔
3181
  uint32_t res = 0;
5,554✔
3182
  if (now < rrsig->d_sigexpire) {
5,554!
3183
    // coverity[store_truncates_time_t]
3184
    res = static_cast<uint32_t>(rrsig->d_sigexpire) - now;
5,554✔
3185
  }
5,554✔
3186
  return res;
5,554✔
3187
}
5,554✔
3188

3189
static const set<QType> nsecTypes = {QType::NSEC, QType::NSEC3};
3190

3191
/* Fills the authoritySOA and DNSSECRecords fields from ne with those found in the records
3192
 *
3193
 * \param records The records to parse for the authority SOA and NSEC(3) records
3194
 * \param ne      The NegCacheEntry to be filled out (will not be cleared, only appended to
3195
 */
3196
static void harvestNXRecords(const vector<DNSRecord>& records, NegCache::NegCacheEntry& negEntry, const time_t now, uint32_t* lowestTTL)
3197
{
5,033✔
3198
  for (const auto& rec : records) {
41,988✔
3199
    if (rec.d_place != DNSResourceRecord::AUTHORITY) {
41,988✔
3200
      // RFC 4035 section 3.1.3. indicates that NSEC records MUST be placed in
3201
      // the AUTHORITY section. Section 3.1.1 indicates that that RRSIGs for
3202
      // records MUST be in the same section as the records they cover.
3203
      // Hence, we ignore all records outside of the AUTHORITY section.
3204
      continue;
17,079✔
3205
    }
17,079✔
3206

3207
    if (rec.d_type == QType::RRSIG) {
24,909✔
3208
      auto rrsig = getRR<RRSIGRecordContent>(rec);
6,186✔
3209
      if (rrsig) {
6,188✔
3210
        if (rrsig->d_type == QType::SOA) {
6,188✔
3211
          negEntry.authoritySOA.signatures.push_back(rec);
541✔
3212
          if (lowestTTL != nullptr && isRRSIGNotExpired(now, *rrsig)) {
541!
3213
            *lowestTTL = min(*lowestTTL, rec.d_ttl);
277✔
3214
            *lowestTTL = min(*lowestTTL, getRRSIGTTL(now, rrsig));
277✔
3215
          }
277✔
3216
        }
541✔
3217
        if (nsecTypes.count(rrsig->d_type) != 0) {
6,188✔
3218
          negEntry.DNSSECRecords.signatures.push_back(rec);
5,647✔
3219
          if (lowestTTL != nullptr && isRRSIGNotExpired(now, *rrsig)) {
5,647!
3220
            *lowestTTL = min(*lowestTTL, rec.d_ttl);
5,277✔
3221
            *lowestTTL = min(*lowestTTL, getRRSIGTTL(now, rrsig));
5,277✔
3222
          }
5,277✔
3223
        }
5,647✔
3224
      }
6,188✔
3225
      continue;
6,186✔
3226
    }
6,186✔
3227
    if (rec.d_type == QType::SOA) {
18,723✔
3228
      negEntry.authoritySOA.records.push_back(rec);
2,378✔
3229
      if (lowestTTL != nullptr) {
2,378✔
3230
        *lowestTTL = min(*lowestTTL, rec.d_ttl);
1,234✔
3231
      }
1,234✔
3232
      continue;
2,378✔
3233
    }
2,378✔
3234
    if (nsecTypes.count(rec.d_type) != 0) {
16,345✔
3235
      negEntry.DNSSECRecords.records.push_back(rec);
5,659✔
3236
      if (lowestTTL != nullptr) {
5,659✔
3237
        *lowestTTL = min(*lowestTTL, rec.d_ttl);
5,283✔
3238
      }
5,283✔
3239
      continue;
5,659✔
3240
    }
5,659✔
3241
  }
16,345✔
3242
}
5,033✔
3243

3244
static cspmap_t harvestCSPFromNE(const NegCache::NegCacheEntry& negEntry)
3245
{
2,057✔
3246
  cspmap_t cspmap;
2,057✔
3247
  for (const auto& rec : negEntry.DNSSECRecords.signatures) {
3,664✔
3248
    if (rec.d_type == QType::RRSIG) {
3,664✔
3249
      auto rrc = getRR<RRSIGRecordContent>(rec);
3,663✔
3250
      if (rrc) {
3,664✔
3251
        cspmap[{rec.d_name, rrc->d_type}].signatures.push_back(rrc);
3,663✔
3252
      }
3,663✔
3253
    }
3,663✔
3254
  }
3,664✔
3255
  for (const auto& rec : negEntry.DNSSECRecords.records) {
3,666✔
3256
    cspmap[{rec.d_name, rec.d_type}].records.insert(rec.getContent());
3,666✔
3257
  }
3,666✔
3258
  return cspmap;
2,057✔
3259
}
2,057✔
3260

3261
// TODO remove after processRecords is fixed!
3262
// Adds the RRSIG for the SOA and the NSEC(3) + RRSIGs to ret
3263
static void addNXNSECS(vector<DNSRecord>& ret, const vector<DNSRecord>& records)
3264
{
1,187✔
3265
  NegCache::NegCacheEntry negEntry;
1,187✔
3266
  harvestNXRecords(records, negEntry, 0, nullptr);
1,187✔
3267
  ret.insert(ret.end(), negEntry.authoritySOA.signatures.begin(), negEntry.authoritySOA.signatures.end());
1,187✔
3268
  ret.insert(ret.end(), negEntry.DNSSECRecords.records.begin(), negEntry.DNSSECRecords.records.end());
1,187✔
3269
  ret.insert(ret.end(), negEntry.DNSSECRecords.signatures.begin(), negEntry.DNSSECRecords.signatures.end());
1,187✔
3270
}
1,187✔
3271

3272
static bool rpzHitShouldReplaceContent(const DNSName& qname, const QType qtype, const std::vector<DNSRecord>& records)
3273
{
59✔
3274
  if (qtype == QType::CNAME) {
59!
3275
    return true;
×
3276
  }
×
3277

3278
  for (const auto& record : records) { // NOLINT(readability-use-anyofallof): don't agree
59✔
3279
    if (record.d_type == QType::CNAME) {
7✔
3280
      if (auto content = getRR<CNAMERecordContent>(record)) {
4!
3281
        if (qname == content->getTarget()) {
4!
3282
          /* we have a CNAME whose target matches the entry we are about to
3283
             generate, so it will complete the current records, not replace
3284
             them
3285
          */
3286
          return false;
4✔
3287
        }
4✔
3288
      }
4✔
3289
    }
4✔
3290
  }
7✔
3291

3292
  return true;
55✔
3293
}
59✔
3294

3295
static void removeConflictingRecord(std::vector<DNSRecord>& records, const DNSName& name, const QType dtype)
3296
{
63✔
3297
  for (auto it = records.begin(); it != records.end();) {
73✔
3298
    bool remove = false;
10✔
3299

3300
    if (it->d_class == QClass::IN && (it->d_type == QType::CNAME || dtype == QType::CNAME || it->d_type == dtype) && it->d_name == name) {
10!
3301
      remove = true;
2✔
3302
    }
2✔
3303
    else if (it->d_class == QClass::IN && it->d_type == QType::RRSIG && it->d_name == name) {
8!
3304
      if (auto rrc = getRR<RRSIGRecordContent>(*it)) {
2!
3305
        if (rrc->d_type == QType::CNAME || rrc->d_type == dtype) {
2!
3306
          /* also remove any RRSIG that could conflict */
3307
          remove = true;
2✔
3308
        }
2✔
3309
      }
2✔
3310
    }
2✔
3311

3312
    if (remove) {
10✔
3313
      it = records.erase(it);
4✔
3314
    }
4✔
3315
    else {
6✔
3316
      ++it;
6✔
3317
    }
6✔
3318
  }
10✔
3319
}
63✔
3320

3321
void SyncRes::handlePolicyHit(const std::string& prefix, const DNSName& qname, const QType qtype, std::vector<DNSRecord>& ret, bool& done, int& rcode, unsigned int depth)
3322
{
103✔
3323
  if (d_pdl && d_pdl->policyHitEventFilter(d_requestor, qname, qtype, d_queryReceivedOverTCP, d_appliedPolicy, d_policyTags, d_discardedPolicies)) {
103✔
3324
    /* reset to no match */
3325
    d_appliedPolicy = DNSFilterEngine::Policy();
2✔
3326
    return;
2✔
3327
  }
2✔
3328

3329
  /* don't account truncate actions for TCP queries, since they are not applied */
3330
  if (d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::Truncate || !d_queryReceivedOverTCP) {
101✔
3331
    ++t_Counters.at(rec::PolicyHistogram::policy).at(d_appliedPolicy.d_kind);
98✔
3332
    ++t_Counters.at(rec::PolicyNameHits::policyName).counts[d_appliedPolicy.getName()];
98✔
3333
  }
98✔
3334

3335
  if (d_appliedPolicy.d_type != DNSFilterEngine::PolicyType::None) {
101!
3336
    LOG(prefix << qname << "|" << qtype << ':' << d_appliedPolicy.getLogString() << endl);
101!
3337
  }
101✔
3338

3339
  switch (d_appliedPolicy.d_kind) {
101!
3340

3341
  case DNSFilterEngine::PolicyKind::NoAction:
22✔
3342
    return;
22✔
3343

3344
  case DNSFilterEngine::PolicyKind::Drop:
6✔
3345
    ++t_Counters.at(rec::Counter::policyDrops);
6✔
3346
    throw ImmediateQueryDropException();
6✔
3347

3348
  case DNSFilterEngine::PolicyKind::NXDOMAIN:
4✔
3349
    ret.clear();
4✔
3350
    d_appliedPolicy.addSOAtoRPZResult(ret);
4✔
3351
    rcode = RCode::NXDomain;
4✔
3352
    done = true;
4✔
3353
    return;
4✔
3354

3355
  case DNSFilterEngine::PolicyKind::NODATA:
4✔
3356
    ret.clear();
4✔
3357
    d_appliedPolicy.addSOAtoRPZResult(ret);
4✔
3358
    rcode = RCode::NoError;
4✔
3359
    done = true;
4✔
3360
    return;
4✔
3361

3362
  case DNSFilterEngine::PolicyKind::Truncate:
6✔
3363
    if (!d_queryReceivedOverTCP) {
6✔
3364
      ret.clear();
3✔
3365
      rcode = RCode::NoError;
3✔
3366
      // Exception handling code in pdns_recursor clears ret as well, so no use to
3367
      // fill it here.
3368
      throw SendTruncatedAnswerException();
3✔
3369
    }
3✔
3370
    return;
3✔
3371

3372
  case DNSFilterEngine::PolicyKind::Custom: {
59✔
3373
    if (rpzHitShouldReplaceContent(qname, qtype, ret)) {
59✔
3374
      ret.clear();
55✔
3375
    }
55✔
3376

3377
    rcode = RCode::NoError;
59✔
3378
    done = true;
59✔
3379
    auto spoofed = d_appliedPolicy.getCustomRecords(qname, qtype.getCode());
59✔
3380
    for (auto& dnsRecord : spoofed) {
63✔
3381
      removeConflictingRecord(ret, dnsRecord.d_name, dnsRecord.d_type);
63✔
3382
    }
63✔
3383

3384
    for (auto& dnsRecord : spoofed) {
63✔
3385
      ret.push_back(dnsRecord);
63✔
3386

3387
      if (dnsRecord.d_name == qname && dnsRecord.d_type == QType::CNAME && qtype != QType::CNAME) {
63!
3388
        if (auto content = getRR<CNAMERecordContent>(dnsRecord)) {
6!
3389
          vState newTargetState = vState::Indeterminate;
6✔
3390
          handleNewTarget(prefix, qname, content->getTarget(), qtype.getCode(), ret, rcode, depth, {}, newTargetState);
6✔
3391
        }
6✔
3392
      }
6✔
3393
    }
63✔
3394
    d_appliedPolicy.addSOAtoRPZResult(ret);
59✔
3395
  }
59✔
3396
  }
101✔
3397
}
101✔
3398

3399
bool SyncRes::nameserversBlockedByRPZ(const DNSFilterEngine& dfe, const NsSet& nameservers)
3400
{
9,598✔
3401
  /* we skip RPZ processing if:
3402
     - it was disabled (d_wantsRPZ is false) ;
3403
     - we already got a RPZ hit (d_appliedPolicy.d_type != DNSFilterEngine::PolicyType::None) since
3404
     the only way we can get back here is that it was a 'pass-thru' (NoAction) meaning that we should not
3405
     process any further RPZ rules. Except that we need to process rules of higher priority..
3406
  */
3407
  if (d_wantsRPZ && !d_appliedPolicy.wasHit()) {
9,598✔
3408
    for (auto const& nameserver : nameservers) {
51,645✔
3409
      bool match = dfe.getProcessingPolicy(nameserver.first, d_discardedPolicies, d_appliedPolicy);
51,645✔
3410
      if (match) {
51,645!
3411
        mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());
×
3412
        if (d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) { // client query needs an RPZ response
×
3413
          LOG(", however nameserver " << nameserver.first << " was blocked by RPZ policy '" << d_appliedPolicy.getName() << "'" << endl);
×
3414
          return true;
×
3415
        }
×
3416
      }
×
3417

3418
      // Traverse all IP addresses for this NS to see if they have an RPN NSIP policy
3419
      for (auto const& address : nameserver.second.first) {
51,645✔
3420
        match = dfe.getProcessingPolicy(address, d_discardedPolicies, d_appliedPolicy);
163✔
3421
        if (match) {
163✔
3422
          mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());
3✔
3423
          if (d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) { // client query needs an RPZ response
3✔
3424
            LOG(", however nameserver " << nameserver.first << " IP address " << address.toString() << " was blocked by RPZ policy '" << d_appliedPolicy.getName() << "'" << endl);
2!
3425
            return true;
2✔
3426
          }
2✔
3427
        }
3✔
3428
      }
163✔
3429
    }
51,645✔
3430
  }
9,591✔
3431
  return false;
9,596✔
3432
}
9,598✔
3433

3434
bool SyncRes::nameserverIPBlockedByRPZ(const DNSFilterEngine& dfe, const ComboAddress& remoteIP)
3435
{
12,504✔
3436
  /* we skip RPZ processing if:
3437
     - it was disabled (d_wantsRPZ is false) ;
3438
     - we already got a RPZ hit (d_appliedPolicy.d_type != DNSFilterEngine::PolicyType::None) since
3439
     the only way we can get back here is that it was a 'pass-thru' (NoAction) meaning that we should not
3440
     process any further RPZ rules. Except that we need to process rules of higher priority..
3441
  */
3442
  if (d_wantsRPZ && !d_appliedPolicy.wasHit()) {
12,504!
3443
    bool match = dfe.getProcessingPolicy(remoteIP, d_discardedPolicies, d_appliedPolicy);
12,498✔
3444
    if (match) {
12,498✔
3445
      mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());
4✔
3446
      if (d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) {
4!
3447
        LOG(" (blocked by RPZ policy '" + d_appliedPolicy.getName() + "')");
4!
3448
        return true;
4✔
3449
      }
4✔
3450
    }
4✔
3451
  }
12,498✔
3452
  return false;
12,500✔
3453
}
12,504✔
3454

3455
static bool shouldNotThrottle(const DNSName* name, const ComboAddress* address)
3456
{
11,312✔
3457
  if (name != nullptr) {
11,312!
3458
    auto dontThrottleNames = g_dontThrottleNames.getLocal();
11,312✔
3459
    if (dontThrottleNames->check(*name)) {
11,312!
3460
      return true;
×
3461
    }
×
3462
  }
11,312✔
3463
  if (address != nullptr) {
11,313✔
3464
    auto dontThrottleNetmasks = g_dontThrottleNetmasks.getLocal();
11,280✔
3465
    if (dontThrottleNetmasks->match(*address)) {
11,280!
3466
      return true;
×
3467
    }
×
3468
  }
11,280✔
3469
  return false;
11,312✔
3470
}
11,312✔
3471

3472
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)
3473
{
11,266✔
3474
  vector<ComboAddress> result;
11,266✔
3475

3476
  size_t nonresolvingfails = 0;
11,266✔
3477
  if (!tns->first.empty()) {
11,266✔
3478
    if (s_nonresolvingnsmaxfails > 0) {
11,133✔
3479
      nonresolvingfails = s_nonresolving.lock()->value(tns->first);
8,706✔
3480
      if (nonresolvingfails >= s_nonresolvingnsmaxfails) {
8,706✔
3481
        LOG(prefix << qname << ": NS " << tns->first << " in non-resolving map, skipping" << endl);
16!
3482
        return result;
16✔
3483
      }
16✔
3484
    }
8,706✔
3485

3486
    LOG(prefix << qname << ": Trying to resolve NS '" << tns->first << "' (" << 1 + tns - rnameservers.begin() << "/" << (unsigned int)rnameservers.size() << ")" << endl);
11,116!
3487
    const unsigned int oldOutQueries = d_outqueries;
11,116✔
3488
    try {
11,116✔
3489
      result = getAddrs(tns->first, depth, prefix, beenthere, cacheOnly, nretrieveAddressesForNS);
11,116✔
3490
    }
11,116✔
3491
    // Other exceptions should likely not throttle...
3492
    catch (const ImmediateServFailException& ex) {
11,116✔
3493
      if (s_nonresolvingnsmaxfails > 0 && d_outqueries > oldOutQueries) {
90!
3494
        if (!shouldNotThrottle(&tns->first, nullptr)) {
×
3495
          s_nonresolving.lock()->incr(tns->first, d_now);
×
3496
        }
×
3497
      }
×
3498
      throw ex;
90✔
3499
    }
90✔
3500
    if (s_nonresolvingnsmaxfails > 0 && d_outqueries > oldOutQueries) {
11,027✔
3501
      if (result.empty()) {
1,140✔
3502
        if (!shouldNotThrottle(&tns->first, nullptr)) {
33!
3503
          s_nonresolving.lock()->incr(tns->first, d_now);
33✔
3504
        }
33✔
3505
      }
33✔
3506
      else if (nonresolvingfails > 0) {
1,107✔
3507
        // Succeeding resolve, clear memory of recent failures
3508
        s_nonresolving.lock()->clear(tns->first);
2✔
3509
      }
2✔
3510
    }
1,140✔
3511
    pierceDontQuery = false;
11,027✔
3512
  }
11,027✔
3513
  else {
134✔
3514
    LOG(prefix << qname << ": Domain has hardcoded nameserver");
134✔
3515

3516
    if (nameservers[tns->first].first.size() > 1) {
134✔
3517
      LOG("s");
16!
3518
    }
16✔
3519
    LOG(endl);
134✔
3520

3521
    sendRDQuery = nameservers[tns->first].second;
134✔
3522
    result = shuffleForwardSpeed(qname, nameservers[tns->first].first, prefix, sendRDQuery);
134✔
3523
    pierceDontQuery = true;
134✔
3524
  }
134✔
3525
  return result;
11,161✔
3526
}
11,266✔
3527

3528
void SyncRes::checkMaxQperQ(const DNSName& qname) const
3529
{
11,240✔
3530
  if (d_outqueries + d_throttledqueries > s_maxqperq) {
11,240✔
3531
    throw ImmediateServFailException("more than " + std::to_string(s_maxqperq) + " (max-qperq) queries sent or throttled while resolving " + qname.toLogString());
4✔
3532
  }
4✔
3533
}
11,240✔
3534

3535
bool SyncRes::throttledOrBlocked(const std::string& prefix, const ComboAddress& remoteIP, const DNSName& qname, const QType qtype, bool pierceDontQuery)
3536
{
11,232✔
3537
  if (isThrottled(d_now.tv_sec, remoteIP)) {
11,232✔
3538
    LOG(prefix << qname << ": Server throttled " << endl);
2!
3539
    t_Counters.at(rec::Counter::throttledqueries)++;
2✔
3540
    d_throttledqueries++;
2✔
3541
    return true;
2✔
3542
  }
2✔
3543
  if (isThrottled(d_now.tv_sec, remoteIP, qname, qtype)) {
11,230✔
3544
    LOG(prefix << qname << ": Query throttled " << remoteIP.toString() << ", " << qname << "; " << qtype << endl);
2!
3545
    t_Counters.at(rec::Counter::throttledqueries)++;
2✔
3546
    d_throttledqueries++;
2✔
3547
    return true;
2✔
3548
  }
2✔
3549
  if (!pierceDontQuery && s_dontQuery && s_dontQuery->match(&remoteIP)) {
11,228✔
3550
    // We could have retrieved an NS from the cache in a forwarding domain
3551
    // Even in the case of !pierceDontQuery we still want to allow that NS
3552
    DNSName forwardCandidate(qname);
2✔
3553
    auto iter = getBestAuthZone(&forwardCandidate);
2✔
3554
    if (iter == t_sstorage.domainmap->end()) {
2!
3555
      LOG(prefix << qname << ": Not sending query to " << remoteIP.toString() << ", blocked by 'dont-query' setting" << endl);
2!
3556
      t_Counters.at(rec::Counter::dontqueries)++;
2✔
3557
      return true;
2✔
3558
    }
2✔
3559
    // The name (from the cache) is forwarded, but is it forwarded to an IP in known forwarders?
3560
    const auto& ips = iter->second.d_servers;
×
3561
    if (std::find(ips.cbegin(), ips.cend(), remoteIP) == ips.cend()) {
×
3562
      LOG(prefix << qname << ": Not sending query to " << remoteIP.toString() << ", blocked by 'dont-query' setting" << endl);
×
3563
      t_Counters.at(rec::Counter::dontqueries)++;
×
3564
      return true;
×
3565
    }
×
3566
    LOG(prefix << qname << ": Sending query to " << remoteIP.toString() << ", blocked by 'dont-query' but a forwarding/auth case" << endl);
×
3567
  }
×
3568
  return false;
11,226✔
3569
}
11,228✔
3570

3571
bool SyncRes::validationEnabled()
3572
{
11,043✔
3573
  return g_dnssecmode != DNSSECMode::Off && g_dnssecmode != DNSSECMode::ProcessNoValidate;
11,043✔
3574
}
11,043✔
3575

3576
uint32_t SyncRes::computeLowestTTD(const std::vector<DNSRecord>& records, const std::vector<std::shared_ptr<const RRSIGRecordContent>>& signatures, uint32_t signaturesTTL, const std::vector<std::shared_ptr<DNSRecord>>& authorityRecs) const
3577
{
10,509✔
3578
  uint32_t lowestTTD = std::numeric_limits<uint32_t>::max();
10,509✔
3579
  for (const auto& record : records) {
21,566✔
3580
    lowestTTD = min(lowestTTD, record.d_ttl);
21,566✔
3581
  }
21,566✔
3582

3583
  /* even if it was not requested for that request (Process, and neither AD nor DO set),
3584
     it might be requested at a later time so we need to be careful with the TTL. */
3585
  if (validationEnabled() && !signatures.empty()) {
10,509✔
3586
    /* if we are validating, we don't want to cache records after their signatures expire. */
3587
    /* records TTL are now TTD, let's add 'now' to the signatures lowest TTL */
3588
    lowestTTD = min(lowestTTD, static_cast<uint32_t>(signaturesTTL + d_now.tv_sec));
7,297✔
3589

3590
    for (const auto& sig : signatures) {
7,317✔
3591
      if (isRRSIGNotExpired(d_now.tv_sec, *sig)) {
7,317✔
3592
        // we don't decrement d_sigexpire by 'now' because we actually want a TTD, not a TTL */
3593
        lowestTTD = min(lowestTTD, static_cast<uint32_t>(sig->d_sigexpire));
7,304✔
3594
      }
7,304✔
3595
    }
7,317✔
3596
  }
7,297✔
3597

3598
  for (const auto& entry : authorityRecs) {
10,509✔
3599
    /* be careful, this is still a TTL here */
3600
    lowestTTD = min(lowestTTD, static_cast<uint32_t>(entry->d_ttl + d_now.tv_sec));
1,048✔
3601

3602
    if (entry->d_type == QType::RRSIG && validationEnabled()) {
1,048!
3603
      auto rrsig = getRR<RRSIGRecordContent>(*entry);
524✔
3604
      if (rrsig) {
524!
3605
        if (isRRSIGNotExpired(d_now.tv_sec, *rrsig)) {
524!
3606
          // we don't decrement d_sigexpire by 'now' because we actually want a TTD, not a TTL */
3607
          lowestTTD = min(lowestTTD, static_cast<uint32_t>(rrsig->d_sigexpire));
524✔
3608
        }
524✔
3609
      }
524✔
3610
    }
524✔
3611
  }
1,048✔
3612

3613
  return lowestTTD;
10,509✔
3614
}
10,509✔
3615

3616
void SyncRes::updateValidationState(const DNSName& qname, vState& state, const vState stateUpdate, const string& prefix)
3617
{
11,356✔
3618
  LOG(prefix << qname << ": Validation state was " << state << ", state update is " << stateUpdate);
11,356!
3619
  updateDNSSECValidationState(state, stateUpdate);
11,356✔
3620
  LOG(", validation state is now " << state << endl);
11,356!
3621
}
11,356✔
3622

3623
vState SyncRes::getTA(const DNSName& zone, dsset_t& dsSet, const string& prefix)
3624
{
55,222✔
3625
  auto luaLocal = g_luaconfs.getLocal();
55,222✔
3626

3627
  if (luaLocal->dsAnchors.empty()) {
55,222✔
3628
    LOG(prefix << zone << ": No trust anchors configured, everything is Insecure" << endl);
44!
3629
    /* We have no TA, everything is insecure */
3630
    return vState::Insecure;
44✔
3631
  }
44✔
3632

3633
  std::string reason;
55,178✔
3634
  if (haveNegativeTrustAnchor(luaLocal->negAnchors, zone, reason)) {
55,178✔
3635
    LOG(prefix << zone << ": Got NTA" << endl);
15!
3636
    return vState::NTA;
15✔
3637
  }
15✔
3638

3639
  if (getTrustAnchor(luaLocal->dsAnchors, zone, dsSet)) {
55,163✔
3640
    if (!zone.isRoot()) {
16,473✔
3641
      LOG(prefix << zone << ": Got TA" << endl);
144!
3642
    }
144✔
3643
    return vState::TA;
16,473✔
3644
  }
16,473✔
3645

3646
  if (zone.isRoot()) {
38,690!
3647
    /* No TA for the root */
3648
    return vState::Insecure;
×
3649
  }
×
3650

3651
  return vState::Indeterminate;
38,690✔
3652
}
38,690✔
3653

3654
size_t SyncRes::countSupportedDS(const dsset_t& dsset, const string& prefix)
3655
{
16,473✔
3656
  size_t count = 0;
16,473✔
3657

3658
  for (const auto& dsRecordContent : dsset) {
34,578✔
3659
    if (isSupportedDS(dsRecordContent, LogObject(prefix))) {
34,587✔
3660
      count++;
34,578✔
3661
    }
34,578✔
3662
  }
34,578✔
3663

3664
  return count;
16,473✔
3665
}
16,473✔
3666

3667
void SyncRes::initZoneCutsFromTA(const DNSName& from, const string& prefix)
3668
{
15,720✔
3669
  DNSName zone(from);
15,720✔
3670
  do {
49,797✔
3671
    dsset_t dsSet;
49,797✔
3672
    vState result = getTA(zone, dsSet, prefix);
49,797✔
3673
    if (result != vState::Indeterminate) {
49,797✔
3674
      if (result == vState::TA) {
15,877✔
3675
        if (countSupportedDS(dsSet, prefix) == 0) {
15,818✔
3676
          dsSet.clear();
8✔
3677
          result = vState::Insecure;
8✔
3678
        }
8✔
3679
        else {
15,810✔
3680
          result = vState::Secure;
15,810✔
3681
        }
15,810✔
3682
      }
15,818✔
3683
      else if (result == vState::NTA) {
59✔
3684
        result = vState::Insecure;
15✔
3685
      }
15✔
3686

3687
      d_cutStates[zone] = result;
15,877✔
3688
    }
15,877✔
3689
  } while (zone.chopOff());
49,797✔
3690
}
15,720✔
3691

3692
vState SyncRes::getDSRecords(const DNSName& zone, dsset_t& dsSet, bool onlyTA, unsigned int depth, const string& prefix, bool bogusOnNXD, bool* foundCut)
3693
{
5,423✔
3694
  vState result = getTA(zone, dsSet, prefix);
5,423✔
3695

3696
  if (result != vState::Indeterminate || onlyTA) {
5,423!
3697
    if (foundCut != nullptr) {
655!
3698
      *foundCut = (result != vState::Indeterminate);
×
3699
    }
×
3700

3701
    if (result == vState::TA) {
655!
3702
      if (countSupportedDS(dsSet, prefix) == 0) {
655!
3703
        dsSet.clear();
×
3704
        result = vState::Insecure;
×
3705
      }
×
3706
      else {
655✔
3707
        result = vState::Secure;
655✔
3708
      }
655✔
3709
    }
655✔
3710
    else if (result == vState::NTA) {
×
3711
      result = vState::Insecure;
×
3712
    }
×
3713

3714
    return result;
655✔
3715
  }
655✔
3716

3717
  std::set<GetBestNSAnswer> beenthere;
4,768✔
3718
  std::vector<DNSRecord> dsrecords;
4,768✔
3719

3720
  Context context;
4,768✔
3721

3722
  const bool oldCacheOnly = setCacheOnly(false);
4,768✔
3723
  const bool oldQM = setQNameMinimization(!getQMFallbackMode());
4,768✔
3724
  int rcode = doResolve(zone, QType::DS, dsrecords, depth + 1, beenthere, context);
4,768✔
3725
  setCacheOnly(oldCacheOnly);
4,768✔
3726
  setQNameMinimization(oldQM);
4,768✔
3727

3728
  if (rcode == RCode::ServFail) {
4,768✔
3729
    throw ImmediateServFailException("Server Failure while retrieving DS records for " + zone.toLogString());
4✔
3730
  }
4✔
3731

3732
  if (rcode != RCode::NoError && (rcode != RCode::NXDomain || bogusOnNXD)) {
4,764!
3733
    LOG(prefix << zone << ": Returning Bogus state from " << static_cast<const char*>(__func__) << "(" << zone << ")" << endl);
2!
3734
    return vState::BogusUnableToGetDSs;
2✔
3735
  }
2✔
3736

3737
  uint8_t bestDigestType = 0;
4,762✔
3738

3739
  bool gotCNAME = false;
4,762✔
3740
  for (const auto& record : dsrecords) {
13,498✔
3741
    if (record.d_type == QType::DS) {
13,498✔
3742
      const auto dscontent = getRR<DSRecordContent>(record);
2,698✔
3743
      if (dscontent && isSupportedDS(*dscontent, LogObject(prefix))) {
2,698!
3744
        // Make GOST a lower prio than SHA256
3745
        if (dscontent->d_digesttype == DNSSECKeeper::DIGEST_GOST && bestDigestType == DNSSECKeeper::DIGEST_SHA256) {
2,698!
3746
          continue;
×
3747
        }
×
3748
        if (dscontent->d_digesttype > bestDigestType || (bestDigestType == DNSSECKeeper::DIGEST_GOST && dscontent->d_digesttype == DNSSECKeeper::DIGEST_SHA256)) {
2,698!
3749
          bestDigestType = dscontent->d_digesttype;
2,685✔
3750
        }
2,685✔
3751
        dsSet.insert(*dscontent);
2,698✔
3752
      }
2,698✔
3753
    }
2,698✔
3754
    else if (record.d_type == QType::CNAME && record.d_name == zone) {
10,800!
3755
      gotCNAME = true;
13✔
3756
    }
13✔
3757
  }
13,498✔
3758

3759
  /* RFC 4509 section 3: "Validator implementations SHOULD ignore DS RRs containing SHA-1
3760
   * digests if DS RRs with SHA-256 digests are present in the DS RRset."
3761
   * We interpret that as: do not use SHA-1 if SHA-256 or SHA-384 is available
3762
   */
3763
  for (auto dsrec = dsSet.begin(); dsrec != dsSet.end();) {
7,459✔
3764
    if (dsrec->d_digesttype == DNSSECKeeper::DIGEST_SHA1 && dsrec->d_digesttype != bestDigestType) {
2,697✔
3765
      dsrec = dsSet.erase(dsrec);
6✔
3766
    }
6✔
3767
    else {
2,691✔
3768
      ++dsrec;
2,691✔
3769
    }
2,691✔
3770
  }
2,697✔
3771

3772
  if (rcode == RCode::NoError) {
4,762✔
3773
    if (dsSet.empty()) {
4,756✔
3774
      /* we have no DS, it's either:
3775
         - a delegation to a non-DNSSEC signed zone
3776
         - no delegation, we stay in the same zone
3777
      */
3778
      if (gotCNAME || denialProvesNoDelegation(zone, dsrecords, d_validationContext)) {
2,081✔
3779
        /* we are still inside the same zone */
3780

3781
        if (foundCut != nullptr) {
31✔
3782
          *foundCut = false;
25✔
3783
        }
25✔
3784
        return context.state;
31✔
3785
      }
31✔
3786

3787
      d_cutStates[zone] = context.state == vState::Secure ? vState::Insecure : context.state;
2,050✔
3788
      /* delegation with no DS, might be Secure -> Insecure */
3789
      if (foundCut != nullptr) {
2,050✔
3790
        *foundCut = true;
2,029✔
3791
      }
2,029✔
3792

3793
      /* a delegation with no DS is either:
3794
         - a signed zone (Secure) to an unsigned one (Insecure)
3795
         - an unsigned zone to another unsigned one (Insecure stays Insecure, Bogus stays Bogus)
3796
      */
3797
      return context.state == vState::Secure ? vState::Insecure : context.state;
2,050✔
3798
    }
2,081✔
3799
    /* we have a DS */
3800
    d_cutStates[zone] = context.state;
2,675✔
3801
    if (foundCut != nullptr) {
2,675✔
3802
      *foundCut = true;
1,599✔
3803
    }
1,599✔
3804
  }
2,675✔
3805

3806
  return context.state;
2,681✔
3807
}
4,762✔
3808

3809
vState SyncRes::getValidationStatus(const DNSName& name, bool wouldBeValid, bool typeIsDS, unsigned int depth, const string& prefix)
3810
{
14,572✔
3811
  vState result = vState::Indeterminate;
14,572✔
3812

3813
  if (!shouldValidate()) {
14,572✔
3814
    return result;
1,063✔
3815
  }
1,063✔
3816

3817
  DNSName subdomain(name);
13,509✔
3818
  if (typeIsDS) {
13,509✔
3819
    subdomain.chopOff();
2,901✔
3820
  }
2,901✔
3821

3822
  {
13,509✔
3823
    const auto& iter = d_cutStates.find(subdomain);
13,509✔
3824
    if (iter != d_cutStates.cend()) {
13,509✔
3825
      LOG(prefix << name << ": Got status " << iter->second << " for name " << subdomain << endl);
3,073!
3826
      return iter->second;
3,073✔
3827
    }
3,073✔
3828
  }
13,509✔
3829

3830
  /* look for the best match we have */
3831
  DNSName best(subdomain);
10,436✔
3832
  while (best.chopOff()) {
18,710!
3833
    const auto& iter = d_cutStates.find(best);
18,710✔
3834
    if (iter != d_cutStates.cend()) {
18,710✔
3835
      result = iter->second;
10,436✔
3836
      if (vStateIsBogus(result) || result == vState::Insecure) {
10,436✔
3837
        LOG(prefix << name << ": Got status " << result << " for name " << best << endl);
2,314!
3838
        return result;
2,314✔
3839
      }
2,314✔
3840
      break;
8,122✔
3841
    }
10,436✔
3842
  }
18,710✔
3843

3844
  /* by now we have the best match, it's likely Secure (otherwise we would not be there)
3845
     but we don't know if we missed a cut (or several).
3846
     We could see if we have DS (or denial of) in cache but let's not worry for now,
3847
     we will if we don't have a signature, or if the signer doesn't match what we expect */
3848
  if (!wouldBeValid && best != subdomain) {
8,122!
3849
    /* no signatures or Bogus, we likely missed a cut, let's try to find it */
3850
    LOG(prefix << name << ": No or invalid signature/proof for " << name << ", we likely missed a cut between " << best << " and " << subdomain << ", looking for it" << endl);
2,090!
3851
    DNSName dsName(best);
2,090✔
3852
    std::vector<string> labelsToAdd = subdomain.makeRelative(dsName).getRawLabels();
2,090✔
3853

3854
    while (!labelsToAdd.empty()) {
3,720✔
3855

3856
      dsName.prependRawLabel(labelsToAdd.back());
3,659✔
3857
      labelsToAdd.pop_back();
3,659✔
3858
      LOG(prefix << name << ": - Looking for a DS at " << dsName << endl);
3,659!
3859

3860
      bool foundCut = false;
3,659✔
3861
      dsset_t results;
3,659✔
3862
      vState dsState = getDSRecords(dsName, results, false, depth, prefix, false, &foundCut);
3,659✔
3863

3864
      if (foundCut) {
3,659✔
3865
        LOG(prefix << name << ": - Found cut at " << dsName << endl);
3,626!
3866
        LOG(prefix << name << ": New state for " << dsName << " is " << dsState << endl);
3,626!
3867
        d_cutStates[dsName] = dsState;
3,626✔
3868

3869
        if (dsState != vState::Secure) {
3,626✔
3870
          return dsState;
2,029✔
3871
        }
2,029✔
3872
      }
3,626✔
3873
    }
3,659✔
3874

3875
    /* we did not miss a cut, good luck */
3876
    return result;
61✔
3877
  }
2,090✔
3878

3879
#if 0
3880
  /* we don't need this, we actually do the right thing later */
3881
  DNSName signer = getSigner(signatures);
3882

3883
  if (!signer.empty() && name.isPartOf(signer)) {
3884
    if (signer == best) {
3885
      return result;
3886
    }
3887
    /* the zone cut is not the one we expected,
3888
       this is fine because we will retrieve the needed DNSKEYs and DSs
3889
       later, and even go Insecure if we missed a cut to Insecure (no DS)
3890
       and the signatures do not validate (we should not go Bogus in that
3891
       case) */
3892
  }
3893
  /* something is not right, but let's not worry about that for now.. */
3894
#endif
3895

3896
  return result;
6,032✔
3897
}
8,122✔
3898

3899
vState SyncRes::validateDNSKeys(const DNSName& zone, const std::vector<DNSRecord>& dnskeys, const std::vector<std::shared_ptr<const RRSIGRecordContent>>& signatures, unsigned int depth, const string& prefix)
3900
{
1,745✔
3901
  dsset_t dsSet;
1,745✔
3902
  if (signatures.empty()) {
1,745!
3903
    LOG(prefix << zone << ": We have " << std::to_string(dnskeys.size()) << " DNSKEYs but no signature, going Bogus!" << endl);
×
3904
    return vState::BogusNoRRSIG;
×
3905
  }
×
3906

3907
  DNSName signer = getSigner(signatures);
1,745✔
3908

3909
  if (!signer.empty() && zone.isPartOf(signer)) {
1,745!
3910
    vState state = getDSRecords(signer, dsSet, false, depth, prefix);
1,745✔
3911

3912
    if (state != vState::Secure) {
1,745✔
3913
      return state;
30✔
3914
    }
30✔
3915
  }
1,745✔
3916
  else {
×
3917
    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);
×
3918
    /* try again to get the missed cuts, harder this time */
3919
    auto zState = getValidationStatus(zone, false, false, depth, prefix);
×
3920
    if (zState == vState::Secure) {
×
3921
      /* too bad */
3922
      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);
×
3923
      return vState::BogusNoValidRRSIG;
×
3924
    }
×
3925
    return zState;
×
3926
  }
×
3927

3928
  skeyset_t tentativeKeys;
1,715✔
3929
  sortedRecords_t toSign;
1,715✔
3930

3931
  for (const auto& dnskey : dnskeys) {
1,934✔
3932
    if (dnskey.d_type == QType::DNSKEY) {
1,934!
3933
      auto content = getRR<DNSKEYRecordContent>(dnskey);
1,934✔
3934
      if (content) {
1,934!
3935
        tentativeKeys.insert(content);
1,934✔
3936
        toSign.insert(content);
1,934✔
3937
      }
1,934✔
3938
    }
1,934✔
3939
  }
1,934✔
3940

3941
  LOG(prefix << zone << ": Trying to validate " << std::to_string(tentativeKeys.size()) << " DNSKEYs with " << std::to_string(dsSet.size()) << " DS" << endl);
1,715!
3942
  skeyset_t validatedKeys;
1,715✔
3943
  auto state = validateDNSKeysAgainstDS(d_now.tv_sec, zone, dsSet, tentativeKeys, toSign, signatures, validatedKeys, LogObject(prefix), d_validationContext);
1,715✔
3944

3945
  if (s_maxvalidationsperq != 0 && d_validationContext.d_validationsCounter > s_maxvalidationsperq) {
1,715!
3946
    throw ImmediateServFailException("Server Failure while validating DNSKEYs, too many signature validations for this query");
×
3947
  }
×
3948

3949
  LOG(prefix << zone << ": We now have " << std::to_string(validatedKeys.size()) << " DNSKEYs" << endl);
1,715!
3950

3951
  /* if we found at least one valid RRSIG covering the set,
3952
     all tentative keys are validated keys. Otherwise it means
3953
     we haven't found at least one DNSKEY and a matching RRSIG
3954
     covering this set, this looks Bogus. */
3955
  if (validatedKeys.size() != tentativeKeys.size()) {
1,715✔
3956
    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!
3957
    /* try again to get the missed cuts, harder this time */
3958
    auto zState = getValidationStatus(zone, false, false, depth, prefix);
44✔
3959
    if (zState == vState::Secure) {
44!
3960
      /* too bad */
3961
      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!
3962
      return state;
44✔
3963
    }
44✔
3964
    return zState;
×
3965
  }
44✔
3966

3967
  return state;
1,671✔
3968
}
1,715✔
3969

3970
vState SyncRes::getDNSKeys(const DNSName& signer, skeyset_t& keys, bool& servFailOccurred, unsigned int depth, const string& prefix)
3971
{
4,288✔
3972
  std::vector<DNSRecord> records;
4,288✔
3973
  std::set<GetBestNSAnswer> beenthere;
4,288✔
3974
  LOG(prefix << signer << ": Retrieving DNSKEYs" << endl);
4,288!
3975

3976
  Context context;
4,288✔
3977

3978
  const bool oldCacheOnly = setCacheOnly(false);
4,288✔
3979
  int rcode = doResolve(signer, QType::DNSKEY, records, depth + 1, beenthere, context);
4,288✔
3980
  setCacheOnly(oldCacheOnly);
4,288✔
3981

3982
  if (rcode == RCode::ServFail) {
4,288✔
3983
    servFailOccurred = true;
12✔
3984
    return vState::BogusUnableToGetDNSKEYs;
12✔
3985
  }
12✔
3986

3987
  if (rcode == RCode::NoError) {
4,276✔
3988
    if (context.state == vState::Secure) {
4,270✔
3989
      for (const auto& key : records) {
10,125✔
3990
        if (key.d_type == QType::DNSKEY) {
10,125✔
3991
          auto content = getRR<DNSKEYRecordContent>(key);
5,966✔
3992
          if (content) {
5,966!
3993
            keys.insert(content);
5,966✔
3994
          }
5,966✔
3995
        }
5,966✔
3996
      }
10,125✔
3997
    }
4,159✔
3998
    LOG(prefix << signer << ": Retrieved " << keys.size() << " DNSKeys, state is " << context.state << endl);
4,270!
3999
    return context.state;
4,270✔
4000
  }
4,270✔
4001

4002
  if (context.state == vState::Insecure) {
6✔
4003
    return context.state;
2✔
4004
  }
2✔
4005

4006
  LOG(prefix << signer << ": Returning Bogus state from " << static_cast<const char*>(__func__) << "(" << signer << ")" << endl);
4!
4007
  return vState::BogusUnableToGetDNSKEYs;
4✔
4008
}
6✔
4009

4010
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 std::vector<std::shared_ptr<const RRSIGRecordContent>>& signatures)
4011
{
4,348✔
4012
  skeyset_t keys;
4,348✔
4013
  if (signatures.empty()) {
4,348✔
4014
    LOG(prefix << qname << ": Bogus!" << endl);
36!
4015
    return vState::BogusNoRRSIG;
36✔
4016
  }
36✔
4017

4018
  const DNSName signer = getSigner(signatures);
4,312✔
4019
  bool dsFailed = false;
4,312✔
4020
  if (!signer.empty() && name.isPartOf(signer)) {
4,312!
4021
    vState state = vState::Secure;
4,310✔
4022

4023
    if ((qtype == QType::DNSKEY || qtype == QType::DS) && signer == qname) {
4,310✔
4024
      /* we are already retrieving those keys, sorry */
4025
      if (type == QType::DS && signer == name && !signer.isRoot()) {
26!
4026
        /* Unless we are getting the DS of the root zone, we should never see a
4027
           DS (or a denial of a DS) signed by the DS itself, since we should be
4028
           requesting it from the parent zone. Something is very wrong */
4029
        LOG(prefix << qname << ": The DS for " << qname << " is signed by itself" << endl);
2!
4030
        state = vState::BogusSelfSignedDS;
2✔
4031
        dsFailed = true;
2✔
4032
      }
2✔
4033
      else if (qtype == QType::DS && signer == qname && !signer.isRoot()) {
24!
4034
        if (type == QType::SOA || type == QType::NSEC || type == QType::NSEC3) {
6!
4035
          /* if we are trying to validate the DS or more likely NSEC(3)s proving that it does not exist, we have a problem.
4036
             In that case let's go Bogus (we will check later if we missed a cut)
4037
          */
4038
          state = vState::BogusSelfSignedDS;
2✔
4039
          dsFailed = true;
2✔
4040
        }
2✔
4041
        else if (type == QType::CNAME) {
4!
4042
          state = vState::BogusUnableToGetDSs;
4✔
4043
          dsFailed = true;
4✔
4044
        }
4✔
4045
      }
6✔
4046
      else if (qtype == QType::DNSKEY && signer == qname) {
18!
4047
        /* that actually does happen when a server returns NS records in authority
4048
           along with the DNSKEY, leading us to trying to validate the RRSIGs for
4049
           the NS with the DNSKEY that we are about to process. */
4050
        if ((name == signer && type == QType::NSEC) || type == QType::NSEC3) {
14✔
4051
          /* if we are trying to validate the DNSKEY (should not happen here),
4052
             or more likely NSEC(3)s proving that it does not exist, we have a problem.
4053
             In that case let's see if the DS does exist, and if it does let's go Bogus
4054
          */
4055
          dsset_t results;
6✔
4056
          vState dsState = getDSRecords(signer, results, false, depth, prefix, true);
6✔
4057
          if (vStateIsBogus(dsState) || dsState == vState::Insecure) {
6!
4058
            state = dsState;
6✔
4059
            if (vStateIsBogus(dsState)) {
6!
4060
              dsFailed = true;
×
4061
            }
×
4062
          }
6✔
4063
          else {
×
4064
            LOG(prefix << qname << ": Unable to get the DS for " << signer << endl);
×
4065
            state = vState::BogusUnableToGetDNSKEYs;
×
4066
            dsFailed = true;
×
4067
          }
×
4068
        }
6✔
4069
        else {
8✔
4070
          /* return immediately since looking at the cuts is not going to change the
4071
             fact that we are looking at a signature done with the key we are trying to
4072
             obtain */
4073
          LOG(prefix << qname << ": We are looking at a signature done with the key we are trying to obtain " << signer << endl);
8!
4074
          return vState::Indeterminate;
8✔
4075
        }
8✔
4076
      }
14✔
4077
    }
26✔
4078
    bool servFailOccurred = false;
4,302✔
4079
    if (state == vState::Secure) {
4,302✔
4080
      state = getDNSKeys(signer, keys, servFailOccurred, depth, prefix);
4,288✔
4081
    }
4,288✔
4082

4083
    if (state != vState::Secure) {
4,302✔
4084
      if (!vStateIsBogus(state)) {
139✔
4085
        return state;
45✔
4086
      }
45✔
4087
      /* try again to get the missed cuts, harder this time */
4088
      LOG(prefix << signer << ": Checking whether we missed a zone cut for " << signer << " before returning a Bogus state for " << name << "|" << type.toString() << endl);
94!
4089
      auto zState = getValidationStatus(signer, false, dsFailed, depth, prefix);
94✔
4090
      if (zState == vState::Secure) {
94✔
4091
        if (state == vState::BogusUnableToGetDNSKEYs && servFailOccurred) {
75!
4092
          throw ImmediateServFailException("Server Failure while retrieving DNSKEY records for " + signer.toLogString());
8✔
4093
        }
8✔
4094
        /* too bad */
4095
        LOG(prefix << signer << ": We are still in a Secure zone, returning " << vStateToString(state) << endl);
67!
4096
        return state;
67✔
4097
      }
75✔
4098
      return zState;
19✔
4099
    }
94✔
4100
  }
4,302✔
4101

4102
  sortedRecords_t recordcontents;
4,165✔
4103
  for (const auto& record : records) {
4,585✔
4104
    recordcontents.insert(record.getContent());
4,585✔
4105
  }
4,585✔
4106

4107
  LOG(prefix << name << ": Going to validate " << recordcontents.size() << " record contents with " << signatures.size() << " sigs and " << keys.size() << " keys for " << name << "|" << type.toString() << endl);
4,165!
4108
  vState state = validateWithKeySet(d_now.tv_sec, name, recordcontents, signatures, keys, LogObject(prefix), d_validationContext, false);
4,165✔
4109
  if (s_maxvalidationsperq != 0 && d_validationContext.d_validationsCounter > s_maxvalidationsperq) {
4,165✔
4110
    throw ImmediateServFailException("Server Failure while validating records, too many signature validations for this query");
2✔
4111
  }
2✔
4112

4113
  if (state == vState::Secure) {
4,163✔
4114
    LOG(prefix << name << ": Secure!" << endl);
4,129!
4115
    return vState::Secure;
4,129✔
4116
  }
4,129✔
4117

4118
  LOG(prefix << vStateToString(state) << "!" << endl);
34!
4119

4120
  bool skipThisLevelWhenLookingForMissedCuts = false;
34✔
4121
  if (name == qname && qtype == QType::DS && (type == QType::NSEC || type == QType::NSEC3)) {
34!
4122
    /* so we have a NSEC(3) record likely proving that the DS we were looking for does not exist,
4123
       but we cannot validate it:
4124
       - if there actually is a cut at this level, we will not be able to validate it anyway
4125
       - if there is no cut at this level, the only thing that can save us is a cut above
4126
    */
4127
    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!
4128
    skipThisLevelWhenLookingForMissedCuts = true;
2✔
4129
  }
2✔
4130

4131
  /* try again to get the missed cuts, harder this time */
4132
  auto zState = getValidationStatus(name, false, type == QType::DS || skipThisLevelWhenLookingForMissedCuts, depth, prefix);
34✔
4133
  LOG(prefix << name << ": Checking whether we missed a zone cut before returning a Bogus state" << endl);
34!
4134
  if (zState == vState::Secure) {
34✔
4135
    /* too bad */
4136
    LOG(prefix << name << ": We are still in a Secure zone, returning " << vStateToString(state) << endl);
26!
4137
    return state;
26✔
4138
  }
26✔
4139
  return zState;
8✔
4140
}
34✔
4141

4142
/* This function will check whether the answer should have the AA bit set, and will set if it should be set and isn't.
4143
   This is unfortunately needed to deal with very crappy so-called DNS servers */
4144
void SyncRes::fixupAnswer(const std::string& prefix, LWResult& lwr, const DNSName& qname, const QType qtype, const DNSName& auth, bool wasForwarded, bool rdQuery)
4145
{
10,896✔
4146
  const bool wasForwardRecurse = wasForwarded && rdQuery;
10,896✔
4147

4148
  if (wasForwardRecurse || lwr.d_aabit) {
10,896✔
4149
    /* easy */
4150
    return;
7,721✔
4151
  }
7,721✔
4152

4153
  for (const auto& rec : lwr.d_records) {
3,175✔
4154

4155
    if (rec.d_type == QType::OPT) {
3,123!
4156
      continue;
×
4157
    }
×
4158

4159
    if (rec.d_class != QClass::IN) {
3,123!
4160
      continue;
×
4161
    }
×
4162

4163
    if (rec.d_type == QType::ANY) {
3,123!
4164
      continue;
×
4165
    }
×
4166

4167
    if (rec.d_place == DNSResourceRecord::ANSWER && (rec.d_type == qtype || rec.d_type == QType::CNAME || qtype == QType::ANY) && rec.d_name == qname && rec.d_name.isPartOf(auth)) {
3,123!
4168
      /* This is clearly an answer to the question we were asking, from an authoritative server that is allowed to send it.
4169
         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 */
4170
      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!
4171
      lwr.d_aabit = true;
10✔
4172
      return;
10✔
4173
    }
10✔
4174

4175
    if (rec.d_place != DNSResourceRecord::ANSWER) {
3,113!
4176
      /* we have scanned all the records in the answer section, if any, we are done */
4177
      return;
3,113✔
4178
    }
3,113✔
4179
  }
3,113✔
4180
}
3,175✔
4181

4182
static void allowAdditionalEntry(std::unordered_set<DNSName>& allowedAdditionals, const DNSRecord& rec)
4183
{
23,665✔
4184
  // As we only use a limited amount of NS names for resolving, limit number of additional names as
4185
  // well.  s_maxnsperresolve is a proper limit for the NS case and is also reasonable for other
4186
  // qtypes.  Allow one extra for qname itself, which is always in allowedAdditionals.
4187
  if (SyncRes::s_maxnsperresolve > 0 && allowedAdditionals.size() > SyncRes::s_maxnsperresolve + 1) {
23,665!
4188
    return;
12✔
4189
  }
12✔
4190
  switch (rec.d_type) {
23,653✔
4191
  case QType::MX:
25✔
4192
    if (auto mxContent = getRR<MXRecordContent>(rec)) {
25!
4193
      allowedAdditionals.insert(mxContent->d_mxname);
25✔
4194
    }
25✔
4195
    break;
25✔
4196
  case QType::NS:
11,864✔
4197
    if (auto nsContent = getRR<NSRecordContent>(rec)) {
11,864✔
4198
      allowedAdditionals.insert(nsContent->getNS());
11,863✔
4199
    }
11,863✔
4200
    break;
11,864✔
4201
  case QType::SRV:
5✔
4202
    if (auto srvContent = getRR<SRVRecordContent>(rec)) {
5!
4203
      allowedAdditionals.insert(srvContent->d_target);
5✔
4204
    }
5✔
4205
    break;
5✔
4206
  case QType::SVCB: /* fall-through */
×
4207
  case QType::HTTPS:
×
4208
    if (auto svcbContent = getRR<SVCBBaseRecordContent>(rec)) {
×
4209
      if (svcbContent->getPriority() > 0) {
×
4210
        DNSName target = svcbContent->getTarget();
×
4211
        if (target.isRoot()) {
×
4212
          target = rec.d_name;
×
4213
        }
×
4214
        allowedAdditionals.insert(target);
×
4215
      }
×
4216
      else {
×
4217
        // FIXME: Alias mode not implemented yet
4218
      }
×
4219
    }
×
4220
    break;
×
4221
  case QType::NAPTR:
6✔
4222
    if (auto naptrContent = getRR<NAPTRRecordContent>(rec)) {
6!
4223
      auto flags = naptrContent->getFlags();
6✔
4224
      toLowerInPlace(flags);
6✔
4225
      if (flags.find('a') != string::npos || flags.find('s') != string::npos) {
6!
4226
        allowedAdditionals.insert(naptrContent->getReplacement());
6✔
4227
      }
6✔
4228
    }
6✔
4229
    break;
6✔
4230
  default:
11,753✔
4231
    break;
11,753✔
4232
  }
23,653✔
4233
}
23,653✔
4234

4235
static bool isRedirection(QType qtype)
4236
{
18,155✔
4237
  return qtype == QType::CNAME || qtype == QType::DNAME;
18,155✔
4238
}
18,155✔
4239

4240
void SyncRes::sanitizeRecords(const std::string& prefix, LWResult& lwr, const DNSName& qname, const QType qtype, const DNSName& auth, bool wasForwarded, bool rdQuery)
4241
{
10,896✔
4242
  const bool wasForwardRecurse = wasForwarded && rdQuery;
10,896✔
4243
  /* list of names for which we will allow A and AAAA records in the additional section
4244
     to remain */
4245
  std::unordered_set<DNSName> allowedAdditionals = {qname};
10,896✔
4246
  std::unordered_set<DNSName> allowedAnswerNames = {qname};
10,896✔
4247
  bool haveAnswers = false;
10,896✔
4248
  bool isNXDomain = false;
10,896✔
4249
  bool isNXQType = false;
10,896✔
4250

4251
  std::vector<bool> skipvec(lwr.d_records.size(), false);
10,896✔
4252
  unsigned int counter = 0;
10,896✔
4253
  unsigned int skipCount = 0;
10,896✔
4254

4255
  for (auto rec = lwr.d_records.cbegin(); rec != lwr.d_records.cend(); ++rec, ++counter) {
66,654✔
4256

4257
    // Allow OPT record containing EDNS(0) data
4258
    if (rec->d_type == QType::OPT) {
55,758✔
4259
      continue;
8,605✔
4260
    }
8,605✔
4261

4262
    // Disallow QClass != IN
4263
    if (rec->d_class != QClass::IN) {
47,153!
4264
      LOG(prefix << qname << ": Removing non internet-classed data received from " << auth << endl);
×
4265
      skipvec[counter] = true;
×
4266
      ++skipCount;
×
4267
      continue;
×
4268
    }
×
4269

4270
    // Disallow QType ANY in responses
4271
    if (rec->d_type == QType::ANY) {
47,153✔
4272
      LOG(prefix << qname << ": Removing 'ANY'-typed data received from " << auth << endl);
2!
4273
      skipvec[counter] = true;
2✔
4274
      ++skipCount;
2✔
4275
      continue;
2✔
4276
    }
2✔
4277

4278
    // Disallow any name not part of auth requested (i.e. disallow x.y.z if asking a NS authoritative for x.w.z)
4279
    if (!rec->d_name.isPartOf(auth)) {
47,151✔
4280
      LOG(prefix << qname << ": Removing record '" << rec->toString() << "' in the " << DNSResourceRecord::placeString(rec->d_place) << " section received from " << auth << endl);
342!
4281
      skipvec[counter] = true;
342✔
4282
      ++skipCount;
342✔
4283
      continue;
342✔
4284
    }
342✔
4285

4286
    // Disallow QType DNAME in non-answer section or containing an answer that is not a parent of or equal to the question name
4287
    // i.e. disallowed bar.example.com. DNAME bar.example.net. when asking foo.example.com
4288
    // But allow it when asking for foo.bar.example.com.
4289
    if (rec->d_type == QType::DNAME && (rec->d_place != DNSResourceRecord::ANSWER || !qname.isPartOf(rec->d_name))) {
46,809!
4290
      LOG(prefix << qname << ": Removing invalid DNAME record '" << rec->toString() << "' in the " << DNSResourceRecord::placeString(rec->d_place) << " section received from " << auth << endl);
2!
4291
      skipvec[counter] = true;
2✔
4292
      ++skipCount;
2✔
4293
      continue;
2✔
4294
    }
2✔
4295

4296
    /* dealing with the records in answer */
4297
    if (rec->d_place == DNSResourceRecord::ANSWER) {
46,807✔
4298
      // Special case for Amazon CNAME records
4299
      if (!(lwr.d_aabit || wasForwardRecurse)) {
12,771!
4300
        /* for now we allow a CNAME for the exact qname in ANSWER with AA=0, because Amazon DNS servers
4301
           are sending such responses */
4302
        if (rec->d_type != QType::CNAME || qname != rec->d_name) {
×
4303
          LOG(prefix << qname << ": Removing record '" << rec->toString() << "' in the ANSWER section without the AA bit set received from " << auth << endl);
×
4304
          skipvec[counter] = true;
×
4305
          ++skipCount;
×
4306
          continue;
×
4307
        }
×
4308
      }
×
4309
      // Disallow answer records not answering the QType requested. ANY, CNAME, DNAME, RRSIG complicate matters here
4310
      if (qtype != QType::ANY && rec->d_type != qtype.getCode() && !isRedirection(rec->d_type) && rec->d_type != QType::RRSIG) {
12,771✔
4311
        LOG(prefix << qname << ": Removing irrelevant record '" << rec->toString() << "' in the ANSWER section received from " << auth << endl);
6!
4312
        skipvec[counter] = true;
6✔
4313
        ++skipCount;
6✔
4314
        continue;
6✔
4315
      }
6✔
4316

4317
      haveAnswers = true;
12,765✔
4318
      if (rec->d_type == QType::CNAME) {
12,765✔
4319
        if (auto cnametarget = getRR<CNAMERecordContent>(*rec); cnametarget != nullptr) {
1,315✔
4320
          allowedAnswerNames.insert(cnametarget->getTarget());
1,314✔
4321
        }
1,314✔
4322
      }
1,315✔
4323
      else if (rec->d_type == QType::DNAME) {
11,450✔
4324
        // We have checked the DNAME rec->d_name above, the actual answer will be synthesized in a later step
4325
        allowedAnswerNames.insert(rec->d_name);
32✔
4326
      }
32✔
4327
      allowAdditionalEntry(allowedAdditionals, *rec);
12,765✔
4328
    }
12,765✔
4329

4330
    /* dealing with the records in authority */
4331
    // Only allow NS, DS, SOA, RRSIG, NSEC, NSEC3 in AUTHORITY section
4332
    else if (rec->d_place == DNSResourceRecord::AUTHORITY) {
34,036✔
4333
      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) {
20,865✔
4334
        LOG(prefix << qname << ": Removing irrelevant record '" << rec->toString() << "' in the AUTHORITY section received from " << auth << endl);
2!
4335
        skipvec[counter] = true;
2✔
4336
        ++skipCount;
2✔
4337
        continue;
2✔
4338
      }
2✔
4339
      if (rec->d_type == QType::NS && !d_updatingRootNS && rec->d_name == g_rootdnsname) {
20,863!
4340
        /*
4341
         * We don't want to pick up root NS records in AUTHORITY and their associated ADDITIONAL sections of random queries.
4342
         * So remove them and don't add them to allowedAdditionals.
4343
         */
4344
        LOG(prefix << qname << ": Removing NS record '" << rec->toString() << "' in the AUTHORITY section of a response received from " << auth << endl);
2!
4345
        skipvec[counter] = true;
2✔
4346
        ++skipCount;
2✔
4347
        continue;
2✔
4348
      }
2✔
4349

4350
      if (rec->d_type == QType::SOA) {
20,861✔
4351
        // Disallow a SOA record with a name that is not a parent of or equal to the name we asked
4352
        if (!qname.isPartOf(rec->d_name)) {
1,241✔
4353
          LOG(prefix << qname << ": Removing irrelevant SOA record '" << rec->toString() << "' in the AUTHORITY section received from " << auth << endl);
2!
4354
          skipvec[counter] = true;
2✔
4355
          ++skipCount;
2✔
4356
          continue;
2✔
4357
        }
2✔
4358
        // Disallow SOA without AA bit (except for forward with RD=1)
4359
        if (!(lwr.d_aabit || wasForwardRecurse)) {
1,239!
4360
          LOG(prefix << qname << ": Removing irrelevant record (AA not set) '" << rec->toString() << "' in the AUTHORITY section received from " << auth << endl);
×
4361
          skipvec[counter] = true;
×
4362
          ++skipCount;
×
4363
          continue;
×
4364
        }
×
4365

4366
        if (!haveAnswers) {
1,239✔
4367
          switch (lwr.d_rcode) {
1,224!
4368
          case RCode::NXDomain:
118✔
4369
            isNXDomain = true;
118✔
4370
            break;
118✔
4371
          case RCode::NoError:
1,106✔
4372
            isNXQType = true;
1,106✔
4373
            break;
1,106✔
4374
          }
1,224✔
4375
        }
1,224✔
4376
      }
1,239✔
4377
    }
20,861✔
4378
    /* dealing with records in additional */
4379
    else if (rec->d_place == DNSResourceRecord::ADDITIONAL) {
13,176✔
4380
      if (rec->d_type != QType::A && rec->d_type != QType::AAAA && rec->d_type != QType::RRSIG) {
13,175✔
4381
        LOG(prefix << qname << ": Removing irrelevant record '" << rec->toString() << "' in the ADDITIONAL section received from " << auth << endl);
2!
4382
        skipvec[counter] = true;
2✔
4383
        ++skipCount;
2✔
4384
        continue;
2✔
4385
      }
2✔
4386
    }
13,175✔
4387
  } // end of first loop, handled answer and most of authority section
46,807✔
4388

4389
  sanitizeRecordsPass2(prefix, lwr, qname, auth, allowedAnswerNames, allowedAdditionals, isNXDomain, isNXQType, skipvec, skipCount);
10,896✔
4390
}
10,896✔
4391

4392
void SyncRes::sanitizeRecordsPass2(const std::string& prefix, LWResult& lwr, const DNSName& qname, const DNSName& auth, std::unordered_set<DNSName>& allowedAnswerNames, std::unordered_set<DNSName>& allowedAdditionals, bool isNXDomain, bool isNXQType, std::vector<bool>& skipvec, unsigned int& skipCount)
4393
{
10,896✔
4394
  // Second loop, we know now if the answer was NxDomain or NoData
4395
  unsigned int counter = 0;
10,896✔
4396
  for (auto rec = lwr.d_records.cbegin(); rec != lwr.d_records.cend(); ++rec, ++counter) {
66,659✔
4397

4398
    if (skipvec[counter]) {
55,763✔
4399
      continue;
360✔
4400
    }
360✔
4401
    // Allow OPT record containing EDNS(0) data
4402
    if (rec->d_type == QType::OPT) {
55,403✔
4403
      continue;
8,606✔
4404
    }
8,606✔
4405

4406
    if (rec->d_place == DNSResourceRecord::ANSWER) {
46,797✔
4407
      if (allowedAnswerNames.count(rec->d_name) == 0) {
12,765✔
4408
        LOG(prefix << qname << ": Removing irrelevent record '" << rec->toString() << "' in the ANSWER section received from " << auth << endl);
10!
4409
        skipvec[counter] = true;
10✔
4410
        ++skipCount;
10✔
4411
        continue;
10✔
4412
      }
10✔
4413
    }
12,765✔
4414
    if (rec->d_place == DNSResourceRecord::AUTHORITY && rec->d_type == QType::NS) {
46,787✔
4415
      if (isNXDomain || isNXQType) {
10,881✔
4416
        /*
4417
         * We don't want to pick up NS records in AUTHORITY and their ADDITIONAL sections of NXDomain answers
4418
         * because they are somewhat easy to insert into a large, fragmented UDP response
4419
         * for an off-path attacker by injecting spoofed UDP fragments. So do not add these to allowedAdditionals.
4420
         */
4421
        LOG(prefix << qname << ": Removing NS record '" << rec->toString() << "' in the AUTHORITY section of a " << (isNXDomain ? "NXD" : "NXQTYPE") << " response received from " << auth << endl);
2!
4422
        skipvec[counter] = true;
2✔
4423
        ++skipCount;
2✔
4424
        continue;
2✔
4425
      }
2✔
4426
      allowAdditionalEntry(allowedAdditionals, *rec);
10,879✔
4427
    }
10,879✔
4428
    /* dealing with the records in additional */
4429
    else if (rec->d_place == DNSResourceRecord::ADDITIONAL) {
35,906✔
4430
      if (allowedAdditionals.count(rec->d_name) == 0) {
13,173✔
4431
        LOG(prefix << qname << ": Removing irrelevant record '" << rec->toString() << "' in the ADDITIONAL section received from " << auth << endl);
10!
4432
        skipvec[counter] = true;
10✔
4433
        ++skipCount;
10✔
4434
        continue;
10✔
4435
      }
10✔
4436
    }
13,173✔
4437
  }
46,787✔
4438
  if (skipCount > 0) {
10,896✔
4439
    std::vector<DNSRecord> vec;
89✔
4440
    vec.reserve(lwr.d_records.size() - skipCount);
89✔
4441
    for (counter = 0; counter < lwr.d_records.size(); ++counter) {
670✔
4442
      if (!skipvec[counter]) {
581✔
4443
        vec.emplace_back(std::move(lwr.d_records[counter]));
199✔
4444
      }
199✔
4445
    }
581✔
4446
    lwr.d_records = std::move(vec);
89✔
4447
  }
89✔
4448
}
10,896✔
4449

4450
void SyncRes::rememberParentSetIfNeeded(const DNSName& domain, const vector<DNSRecord>& newRecords, unsigned int depth, const string& prefix)
4451
{
439✔
4452
  vector<DNSRecord> existing;
439✔
4453
  bool wasAuth = false;
439✔
4454
  auto ttl = g_recCache->get(d_now.tv_sec, domain, QType::NS, MemRecursorCache::None, &existing, d_cacheRemote, d_routingTag, nullptr, nullptr, nullptr, nullptr, &wasAuth);
439✔
4455

4456
  if (ttl <= 0 || wasAuth) {
439✔
4457
    return;
48✔
4458
  }
48✔
4459
  {
391✔
4460
    auto lock = s_savedParentNSSet.lock();
391✔
4461
    if (lock->find(domain) != lock->end()) {
391!
4462
      // no relevant data, or we already stored the parent data
4463
      return;
×
4464
    }
×
4465
  }
391✔
4466

4467
  set<DNSName> authSet;
391✔
4468
  for (const auto& dnsRecord : newRecords) {
1,583✔
4469
    auto content = getRR<NSRecordContent>(dnsRecord);
1,583✔
4470
    authSet.insert(content->getNS());
1,583✔
4471
  }
1,583✔
4472
  // The glue IPs could also differ, but we're not checking that yet, we're only looking for parent NS records not
4473
  // in the child set
4474
  bool shouldSave = false;
391✔
4475
  for (const auto& dnsRecord : existing) {
1,583✔
4476
    auto content = getRR<NSRecordContent>(dnsRecord);
1,583✔
4477
    if (authSet.count(content->getNS()) == 0) {
1,583✔
4478
      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!
4479
      shouldSave = true;
4✔
4480
      break;
4✔
4481
    }
4✔
4482
  }
1,583✔
4483

4484
  if (shouldSave) {
391✔
4485
    map<DNSName, vector<ComboAddress>> entries;
4✔
4486
    for (const auto& dnsRecord : existing) {
30✔
4487
      auto content = getRR<NSRecordContent>(dnsRecord);
30✔
4488
      const DNSName& name = content->getNS();
30✔
4489
      set<GetBestNSAnswer> beenthereIgnored;
30✔
4490
      unsigned int nretrieveAddressesForNSIgnored{};
30✔
4491
      auto addresses = getAddrs(name, depth, prefix, beenthereIgnored, true, nretrieveAddressesForNSIgnored);
30✔
4492
      entries.emplace(name, addresses);
30✔
4493
    }
30✔
4494
    s_savedParentNSSet.lock()->emplace(domain, std::move(entries), d_now.tv_sec + ttl);
4✔
4495
  }
4✔
4496
}
391✔
4497

4498
RCode::rcodes_ SyncRes::updateCacheFromRecords(unsigned int depth, const string& prefix, LWResult& lwr, const DNSName& qname, const QType qtype, const DNSName& auth, bool wasForwarded, const boost::optional<Netmask>& ednsmask, vState& state, bool& needWildcardProof, bool& gatherWildcardProof, unsigned int& wildcardLabelsCount, bool rdQuery, const ComboAddress& remoteIP) // NOLINT(readability-function-cognitive-complexity)
4499
{
10,896✔
4500
  bool wasForwardRecurse = wasForwarded && rdQuery;
10,896✔
4501
  tcache_t tcache;
10,896✔
4502

4503
  fixupAnswer(prefix, lwr, qname, qtype, auth, wasForwarded, rdQuery);
10,896✔
4504
  sanitizeRecords(prefix, lwr, qname, qtype, auth, wasForwarded, rdQuery);
10,896✔
4505

4506
  std::vector<std::shared_ptr<DNSRecord>> authorityRecs;
10,896✔
4507
  bool isCNAMEAnswer = false;
10,896✔
4508
  bool isDNAMEAnswer = false;
10,896✔
4509
  DNSName seenAuth;
10,896✔
4510

4511
  // names that might be expanded from a wildcard, and thus require denial of existence proof
4512
  // this is the queried name and any part of the CNAME chain from the queried name
4513
  // the key is the name itself, the value is initially false and is set to true once we have
4514
  // confirmed it was actually expanded from a wildcard
4515
  std::map<DNSName, bool> wildcardCandidates{{qname, false}};
10,896✔
4516

4517
  if (rdQuery) {
10,896✔
4518
    std::unordered_map<DNSName, DNSName> cnames;
58✔
4519
    for (const auto& rec : lwr.d_records) {
144✔
4520
      if (rec.d_type != QType::CNAME || rec.d_class != QClass::IN) {
144!
4521
        continue;
132✔
4522
      }
132✔
4523
      if (auto content = getRR<CNAMERecordContent>(rec)) {
12!
4524
        cnames[rec.d_name] = DNSName(content->getTarget());
12✔
4525
      }
12✔
4526
    }
12✔
4527
    auto initial = qname;
58✔
4528
    while (true) {
68✔
4529
      auto cnameIt = cnames.find(initial);
68✔
4530
      if (cnameIt == cnames.end()) {
68✔
4531
        break;
56✔
4532
      }
56✔
4533
      initial = cnameIt->second;
12✔
4534
      if (!wildcardCandidates.emplace(initial, false).second) {
12✔
4535
        // CNAME Loop
4536
        break;
2✔
4537
      }
2✔
4538
    }
12✔
4539
  }
58✔
4540

4541
  for (auto& rec : lwr.d_records) {
55,375✔
4542
    if (rec.d_type == QType::OPT || rec.d_class != QClass::IN) {
55,375!
4543
      continue;
8,605✔
4544
    }
8,605✔
4545

4546
    rec.d_ttl = min(s_maxcachettl, rec.d_ttl);
46,770✔
4547

4548
    if (!isCNAMEAnswer && rec.d_place == DNSResourceRecord::ANSWER && rec.d_type == QType::CNAME && (!(qtype == QType::CNAME)) && rec.d_name == qname && !isDNAMEAnswer) {
46,770✔
4549
      isCNAMEAnswer = true;
1,281✔
4550
    }
1,281✔
4551
    if (!isDNAMEAnswer && rec.d_place == DNSResourceRecord::ANSWER && rec.d_type == QType::DNAME && qtype != QType::DNAME && qname.isPartOf(rec.d_name)) {
46,774!
4552
      isDNAMEAnswer = true;
32✔
4553
      isCNAMEAnswer = false;
32✔
4554
    }
32✔
4555

4556
    if (rec.d_type == QType::SOA && rec.d_place == DNSResourceRecord::AUTHORITY && qname.isPartOf(rec.d_name)) {
46,770!
4557
      seenAuth = rec.d_name;
1,239✔
4558
    }
1,239✔
4559

4560
    const auto labelCount = rec.d_name.countLabels();
46,770✔
4561
    if (rec.d_type == QType::RRSIG) {
46,770✔
4562
      auto rrsig = getRR<RRSIGRecordContent>(rec);
7,452✔
4563
      if (rrsig) {
7,452!
4564
        /* As illustrated in rfc4035's Appendix B.6, the RRSIG label
4565
           count can be lower than the name's label count if it was
4566
           synthesized from the wildcard. Note that the difference might
4567
           be > 1. */
4568
        if (auto wcIt = wildcardCandidates.find(rec.d_name); wcIt != wildcardCandidates.end() && isWildcardExpanded(labelCount, *rrsig)) {
7,452✔
4569
          wcIt->second = true;
245✔
4570
          gatherWildcardProof = true;
245✔
4571
          if (!isWildcardExpandedOntoItself(rec.d_name, labelCount, *rrsig)) {
245✔
4572
            /* if we have a wildcard expanded onto itself, we don't need to prove
4573
               that the exact name doesn't exist because it actually does.
4574
               We still want to gather the corresponding NSEC/NSEC3 records
4575
               to pass them to our client in case it wants to validate by itself.
4576
            */
4577
            LOG(prefix << qname << ": RRSIG indicates the name was synthesized from a wildcard, we need a wildcard proof" << endl);
237!
4578
            needWildcardProof = true;
237✔
4579
          }
237✔
4580
          else {
8✔
4581
            LOG(prefix << qname << ": RRSIG indicates the name was synthesized from a wildcard expanded onto itself, we need to gather wildcard proof" << endl);
8!
4582
          }
8✔
4583
          wildcardLabelsCount = rrsig->d_labels;
245✔
4584
        }
245✔
4585

4586
        // cerr<<"Got an RRSIG for "<<DNSRecordContent::NumberToType(rrsig->d_type)<<" with name '"<<rec.d_name<<"' and place "<<rec.d_place<<endl;
4587
        tcache[{rec.d_name, rrsig->d_type, rec.d_place}].signatures.push_back(rrsig);
7,452✔
4588
        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);
7,452✔
4589
      }
7,452✔
4590
    }
7,452✔
4591
  }
46,770✔
4592

4593
  /* if we have a positive answer synthesized from a wildcard,
4594
     we need to store the corresponding NSEC/NSEC3 records proving
4595
     that the exact name did not exist in the negative cache */
4596
  if (gatherWildcardProof) {
10,896✔
4597
    for (const auto& rec : lwr.d_records) {
1,195✔
4598
      if (rec.d_type == QType::OPT || rec.d_class != QClass::IN) {
1,195!
4599
        continue;
213✔
4600
      }
213✔
4601

4602
      if (nsecTypes.count(rec.d_type) != 0) {
982✔
4603
        authorityRecs.push_back(std::make_shared<DNSRecord>(rec));
243✔
4604
      }
243✔
4605
      else if (rec.d_type == QType::RRSIG) {
739✔
4606
        auto rrsig = getRR<RRSIGRecordContent>(rec);
490✔
4607
        if (rrsig && nsecTypes.count(rrsig->d_type) != 0) {
490!
4608
          authorityRecs.push_back(std::make_shared<DNSRecord>(rec));
243✔
4609
        }
243✔
4610
      }
490✔
4611
    }
982✔
4612
  }
241✔
4613

4614
  // reap all answers from this packet that are acceptable
4615
  for (auto& rec : lwr.d_records) {
55,378✔
4616
    if (rec.d_type == QType::OPT) {
55,378✔
4617
      LOG(prefix << qname << ": OPT answer '" << rec.d_name << "' from '" << auth << "' nameservers" << endl);
8,606!
4618
      continue;
8,606✔
4619
    }
8,606✔
4620

4621
    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 << " ");
46,772!
4622

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

4625
    if (rec.d_name.isPartOf(auth)) {
46,775✔
4626
      if (rec.d_type == QType::RRSIG) {
46,774✔
4627
        LOG("RRSIG - separate" << endl);
7,452!
4628
      }
7,452✔
4629
      else if (rec.d_type == QType::DS && rec.d_name == auth) {
39,322✔
4630
        LOG("NO - DS provided by child zone" << endl);
2!
4631
      }
2✔
4632
      else {
39,320✔
4633
        bool haveLogged = false;
39,320✔
4634
        if (isDNAMEAnswer && rec.d_type == QType::CNAME) {
39,320✔
4635
          LOG("NO - we already have a DNAME answer for this domain" << endl);
30!
4636
          continue;
30✔
4637
        }
30✔
4638
        if (!t_sstorage.domainmap->empty()) {
39,292✔
4639
          // Check if we are authoritative for a zone in this answer
4640
          DNSName tmp_qname(rec.d_name);
34,975✔
4641
          // We may be auth for domain example.com, but the DS record needs to come from the parent (.com) nameserver
4642
          if (rec.d_type == QType::DS) {
34,975✔
4643
            tmp_qname.chopOff();
1,046✔
4644
          }
1,046✔
4645
          auto auth_domain_iter = getBestAuthZone(&tmp_qname);
34,975✔
4646
          if (auth_domain_iter != t_sstorage.domainmap->end() && auth.countLabels() <= auth_domain_iter->first.countLabels()) {
34,975✔
4647
            if (auth_domain_iter->first != auth) {
320✔
4648
              LOG("NO! - we are authoritative for the zone " << auth_domain_iter->first << endl);
2!
4649
              continue;
2✔
4650
            }
2✔
4651
            LOG("YES! - This answer was ");
318!
4652
            if (!wasForwarded) {
318✔
4653
              LOG("retrieved from the local auth store.");
143!
4654
            }
143✔
4655
            else {
175✔
4656
              LOG("received from a server we forward to.");
175!
4657
            }
175✔
4658
            haveLogged = true;
318✔
4659
            LOG(endl);
318!
4660
          }
318✔
4661
        }
34,975✔
4662
        if (!haveLogged) {
39,288✔
4663
          LOG("YES!" << endl);
38,968!
4664
        }
38,968✔
4665

4666
        rec.d_ttl = min(s_maxcachettl, rec.d_ttl);
39,288✔
4667

4668
        DNSRecord dnsRecord(rec);
39,288✔
4669
        tcache[{rec.d_name, rec.d_type, rec.d_place}].d_ttl_time = d_now.tv_sec;
39,288✔
4670
        dnsRecord.d_ttl += d_now.tv_sec;
39,288✔
4671
        dnsRecord.d_place = DNSResourceRecord::ANSWER;
39,288✔
4672
        tcache[{rec.d_name, rec.d_type, rec.d_place}].records.push_back(dnsRecord);
39,288✔
4673
      }
39,288✔
4674
    }
46,774✔
4675
    else
2,147,483,647✔
4676
      LOG("NO!" << endl);
2,147,499,023!
4677
  }
46,740✔
4678

4679
  // supplant
4680
  for (auto& entry : tcache) {
28,233✔
4681
    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)
28,233✔
4682
      uint32_t lowestTTD = computeLowestTTD(entry.second.records, entry.second.signatures, entry.second.signaturesTTL, authorityRecs);
10,509✔
4683

4684
      for (auto& record : entry.second.records) {
21,566✔
4685
        record.d_ttl = lowestTTD; // boom
21,566✔
4686
      }
21,566✔
4687
    }
10,509✔
4688
  }
28,233✔
4689

4690
  for (auto tCacheEntry = tcache.begin(); tCacheEntry != tcache.end(); ++tCacheEntry) {
39,126✔
4691

4692
    if (tCacheEntry->second.records.empty()) { // this happens when we did store signatures, but passed on the records themselves
28,230!
4693
      continue;
×
4694
    }
×
4695

4696
    /* Even if the AA bit is set, additional data cannot be considered
4697
       as authoritative. This is especially important during validation
4698
       because keeping records in the additional section is allowed even
4699
       if the corresponding RRSIGs are not included, without setting the TC
4700
       bit, as stated in rfc4035's section 3.1.1.  Including RRSIG RRs in a Response:
4701
       "When placing a signed RRset in the Additional section, the name
4702
       server MUST also place its RRSIG RRs in the Additional section.
4703
       If space does not permit inclusion of both the RRset and its
4704
       associated RRSIG RRs, the name server MAY retain the RRset while
4705
       dropping the RRSIG RRs.  If this happens, the name server MUST NOT
4706
       set the TC bit solely because these RRSIG RRs didn't fit."
4707
    */
4708
    bool isAA = lwr.d_aabit && tCacheEntry->first.place != DNSResourceRecord::ADDITIONAL;
28,230✔
4709
    /* if we forwarded the query to a recursor, we can expect the answer to be signed,
4710
       even if the answer is not AA. Of course that's not only true inside a Secure
4711
       zone, but we check that below. */
4712
    bool expectSignature = tCacheEntry->first.place == DNSResourceRecord::ANSWER || ((lwr.d_aabit || wasForwardRecurse) && tCacheEntry->first.place != DNSResourceRecord::ADDITIONAL);
28,230✔
4713
    /* in a non authoritative answer, we only care about the DS record (or lack of)  */
4714
    if (!isAA && (tCacheEntry->first.type == QType::DS || tCacheEntry->first.type == QType::NSEC || tCacheEntry->first.type == QType::NSEC3) && tCacheEntry->first.place == DNSResourceRecord::AUTHORITY) {
28,230✔
4715
      expectSignature = true;
3,609✔
4716
    }
3,609✔
4717

4718
    if (isCNAMEAnswer && (tCacheEntry->first.place != DNSResourceRecord::ANSWER || tCacheEntry->first.type != QType::CNAME || tCacheEntry->first.name != qname)) {
28,230✔
4719
      /*
4720
        rfc2181 states:
4721
        Note that the answer section of an authoritative answer normally
4722
        contains only authoritative data.  However when the name sought is an
4723
        alias (see section 10.1.1) only the record describing that alias is
4724
        necessarily authoritative.  Clients should assume that other records
4725
        may have come from the server's cache.  Where authoritative answers
4726
        are required, the client should query again, using the canonical name
4727
        associated with the alias.
4728
      */
4729
      isAA = false;
260✔
4730
      expectSignature = false;
260✔
4731
    }
260✔
4732
    if (isDNAMEAnswer && (tCacheEntry->first.place != DNSResourceRecord::ANSWER || tCacheEntry->first.type != QType::DNAME || !qname.isPartOf(tCacheEntry->first.name))) {
28,230!
4733
      /* see above */
4734
      isAA = false;
6✔
4735
      expectSignature = false;
6✔
4736
    }
6✔
4737

4738
    if ((isCNAMEAnswer || isDNAMEAnswer) && tCacheEntry->first.place == DNSResourceRecord::AUTHORITY && tCacheEntry->first.type == QType::NS && auth == tCacheEntry->first.name) {
28,230✔
4739
      /* These NS can't be authoritative since we have a CNAME/DNAME answer for which (see above) only the
4740
         record describing that alias is necessarily authoritative.
4741
         But if we allow the current auth, which might be serving the child zone, to raise the TTL
4742
         of non-authoritative NS in the cache, they might be able to keep a "ghost" zone alive forever,
4743
         even after the delegation is gone from the parent.
4744
         So let's just do nothing with them, we can fetch them directly if we need them.
4745
      */
4746
      LOG(prefix << qname << ": Skipping authority NS from '" << auth << "' nameservers in CNAME/DNAME answer " << tCacheEntry->first.name << "|" << DNSRecordContent::NumberToType(tCacheEntry->first.type) << endl);
2!
4747
      continue;
2✔
4748
    }
2✔
4749

4750
    /*
4751
     * RFC 6672 section 5.3.1
4752
     *  In any response, a signed DNAME RR indicates a non-terminal
4753
     *  redirection of the query.  There might or might not be a server-
4754
     *  synthesized CNAME in the answer section; if there is, the CNAME will
4755
     *  never be signed.  For a DNSSEC validator, verification of the DNAME
4756
     *  RR and then that the CNAME was properly synthesized is sufficient
4757
     *  proof.
4758
     *
4759
     * We do the synthesis check in processRecords, here we make sure we
4760
     * don't validate the CNAME.
4761
     */
4762
    if (isDNAMEAnswer && tCacheEntry->first.type == QType::CNAME) {
28,228!
4763
      expectSignature = false;
×
4764
    }
×
4765

4766
    vState recordState = vState::Indeterminate;
28,228✔
4767

4768
    if (expectSignature && shouldValidate()) {
28,228✔
4769
      vState initialState = getValidationStatus(tCacheEntry->first.name, !tCacheEntry->second.signatures.empty(), tCacheEntry->first.type == QType::DS, depth, prefix);
9,199✔
4770
      LOG(prefix << qname << ": Got initial zone status " << initialState << " for record " << tCacheEntry->first.name << "|" << DNSRecordContent::NumberToType(tCacheEntry->first.type) << endl);
9,199!
4771

4772
      if (initialState == vState::Secure) {
9,199✔
4773
        if (tCacheEntry->first.type == QType::DNSKEY && tCacheEntry->first.place == DNSResourceRecord::ANSWER && tCacheEntry->first.name == getSigner(tCacheEntry->second.signatures)) {
6,030!
4774
          LOG(prefix << qname << ": Validating DNSKEY for " << tCacheEntry->first.name << endl);
1,743!
4775
          recordState = validateDNSKeys(tCacheEntry->first.name, tCacheEntry->second.records, tCacheEntry->second.signatures, depth, prefix);
1,743✔
4776
        }
1,743✔
4777
        else {
4,287✔
4778
          LOG(prefix << qname << ": Validating non-additional " << QType(tCacheEntry->first.type).toString() << " record for " << tCacheEntry->first.name << endl);
4,287!
4779
          recordState = validateRecordsWithSigs(depth, prefix, qname, qtype, tCacheEntry->first.name, QType(tCacheEntry->first.type), tCacheEntry->second.records, tCacheEntry->second.signatures);
4,287✔
4780
        }
4,287✔
4781
      }
6,030✔
4782
      else {
3,169✔
4783
        recordState = initialState;
3,169✔
4784
        LOG(prefix << qname << ": Skipping validation because the current state is " << recordState << endl);
3,169!
4785
      }
3,169✔
4786

4787
      LOG(prefix << qname << ": Validation result is " << recordState << ", current state is " << state << endl);
9,199!
4788
      if (state != recordState) {
9,199✔
4789
        updateValidationState(qname, state, recordState, prefix);
5,706✔
4790
      }
5,706✔
4791
    }
9,199✔
4792

4793
    if (vStateIsBogus(recordState)) {
28,228✔
4794
      /* this is a TTD by now, be careful */
4795
      for (auto& record : tCacheEntry->second.records) {
520✔
4796
        auto newval = std::min(record.d_ttl, static_cast<uint32_t>(s_maxbogusttl + d_now.tv_sec));
520✔
4797
        record.d_ttl = newval;
520✔
4798
      }
520✔
4799
      tCacheEntry->second.d_ttl_time = d_now.tv_sec;
188✔
4800
    }
188✔
4801

4802
    /* We don't need to store NSEC3 records in the positive cache because:
4803
       - we don't allow direct NSEC3 queries
4804
       - denial of existence proofs in wildcard expanded positive responses are stored in authorityRecs
4805
       - denial of existence proofs for negative responses are stored in the negative cache
4806
       We also don't want to cache non-authoritative data except for:
4807
       - records coming from non forward-recurse servers (those will never be AA)
4808
       - DS (special case)
4809
       - NS, A and AAAA (used for infra queries)
4810
    */
4811
    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)) {
28,228✔
4812

4813
      bool doCache = true;
25,641✔
4814
      if (tCacheEntry->first.place == DNSResourceRecord::ANSWER && ednsmask) {
25,641✔
4815
        const bool isv4 = ednsmask->isIPv4();
56✔
4816
        if ((isv4 && s_ecsipv4nevercache) || (!isv4 && s_ecsipv6nevercache)) {
56!
4817
          doCache = false;
×
4818
        }
×
4819
        // If ednsmask is relevant, we do not want to cache if the scope prefix length is large and TTL is small
4820
        if (doCache && s_ecscachelimitttl > 0) {
56!
4821
          bool manyMaskBits = (isv4 && ednsmask->getBits() > s_ecsipv4cachelimit) || (!isv4 && ednsmask->getBits() > s_ecsipv6cachelimit);
6!
4822

4823
          if (manyMaskBits) {
6✔
4824
            uint32_t minttl = UINT32_MAX;
2✔
4825
            for (const auto& iter : tCacheEntry->second.records) {
2✔
4826
              if (iter.d_ttl < minttl) {
2!
4827
                minttl = iter.d_ttl;
2✔
4828
              }
2✔
4829
            }
2✔
4830
            bool ttlIsSmall = minttl < s_ecscachelimitttl + d_now.tv_sec;
2✔
4831
            if (ttlIsSmall) {
2!
4832
              // Case: many bits and ttlIsSmall
4833
              doCache = false;
2✔
4834
            }
2✔
4835
          }
2✔
4836
        }
6✔
4837
      }
56✔
4838

4839
      d_fromAuthIP = remoteIP;
25,641✔
4840

4841
      if (doCache) {
25,641✔
4842
        // Check if we are going to replace a non-auth (parent) NS recordset
4843
        if (isAA && tCacheEntry->first.type == QType::NS && s_save_parent_ns_set) {
25,638!
4844
          rememberParentSetIfNeeded(tCacheEntry->first.name, tCacheEntry->second.records, depth, prefix);
439✔
4845
        }
439✔
4846
        bool thisRRNeedsWildcardProof = false;
25,638✔
4847
        if (gatherWildcardProof) {
25,638✔
4848
          if (auto wcIt = wildcardCandidates.find(tCacheEntry->first.name); wcIt != wildcardCandidates.end() && wcIt->second) {
464✔
4849
            thisRRNeedsWildcardProof = true;
245✔
4850
          }
245✔
4851
        }
464✔
4852
        g_recCache->replace(d_now.tv_sec, tCacheEntry->first.name, tCacheEntry->first.type, tCacheEntry->second.records, tCacheEntry->second.signatures, thisRRNeedsWildcardProof ? authorityRecs : std::vector<std::shared_ptr<DNSRecord>>(), tCacheEntry->first.type == QType::DS ? true : isAA, auth, tCacheEntry->first.place == DNSResourceRecord::ANSWER ? ednsmask : boost::none, d_routingTag, recordState, remoteIP, d_refresh, tCacheEntry->second.d_ttl_time);
25,638✔
4853

4854
        // Delete potential negcache entry. When a record recovers with serve-stale the negcache entry can cause the wrong entry to
4855
        // be served, as negcache entries are checked before record cache entries
4856
        if (NegCache::s_maxServedStaleExtensions > 0) {
25,638✔
4857
          g_negCache->wipeTyped(tCacheEntry->first.name, tCacheEntry->first.type);
192✔
4858
        }
192✔
4859

4860
        if (g_aggressiveNSECCache && thisRRNeedsWildcardProof && recordState == vState::Secure && tCacheEntry->first.place == DNSResourceRecord::ANSWER && !tCacheEntry->second.signatures.empty() && !d_routingTag && !ednsmask) {
25,640!
4861
          /* we have an answer synthesized from a wildcard and aggressive NSEC is enabled, we need to store the
4862
             wildcard in its non-expanded form in the cache to be able to synthesize wildcard answers later */
4863
          const auto& rrsig = tCacheEntry->second.signatures.at(0);
217✔
4864
          const auto labelCount = tCacheEntry->first.name.countLabels();
217✔
4865

4866
          if (isWildcardExpanded(labelCount, *rrsig) && !isWildcardExpandedOntoItself(tCacheEntry->first.name, labelCount, *rrsig)) {
217!
4867
            DNSName realOwner = getNSECOwnerName(tCacheEntry->first.name, tCacheEntry->second.signatures);
217✔
4868

4869
            std::vector<DNSRecord> content;
217✔
4870
            content.reserve(tCacheEntry->second.records.size());
217✔
4871
            for (const auto& record : tCacheEntry->second.records) {
217✔
4872
              DNSRecord nonExpandedRecord(record);
217✔
4873
              nonExpandedRecord.d_name = realOwner;
217✔
4874
              content.push_back(std::move(nonExpandedRecord));
217✔
4875
            }
217✔
4876

4877
            g_recCache->replace(d_now.tv_sec, realOwner, QType(tCacheEntry->first.type), content, tCacheEntry->second.signatures, /* no additional records in that case */ {}, tCacheEntry->first.type == QType::DS ? true : isAA, auth, boost::none, boost::none, recordState, remoteIP, d_refresh, tCacheEntry->second.d_ttl_time);
217!
4878
          }
217✔
4879
        }
217✔
4880
      }
25,638✔
4881
    }
25,641✔
4882

4883
    if (seenAuth.empty() && !tCacheEntry->second.signatures.empty()) {
28,228✔
4884
      seenAuth = getSigner(tCacheEntry->second.signatures);
5,161✔
4885
    }
5,161✔
4886

4887
    if (g_aggressiveNSECCache && (tCacheEntry->first.type == QType::NSEC || tCacheEntry->first.type == QType::NSEC3) && recordState == vState::Secure && !seenAuth.empty()) {
28,229!
4888
      // Good candidate for NSEC{,3} caching
4889
      g_aggressiveNSECCache->insertNSEC(seenAuth, tCacheEntry->first.name, tCacheEntry->second.records.at(0), tCacheEntry->second.signatures, tCacheEntry->first.type == QType::NSEC3, qname, qtype);
1,952✔
4890
    }
1,952✔
4891

4892
    if (tCacheEntry->first.place == DNSResourceRecord::ANSWER && ednsmask) {
28,228✔
4893
      d_wasVariable = true;
56✔
4894
    }
56✔
4895
  }
28,228✔
4896

4897
  if (gatherWildcardProof) {
10,896✔
4898
    if (auto wcIt = wildcardCandidates.find(qname); wcIt != wildcardCandidates.end() && !wcIt->second) {
241!
4899
      // 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
4900
      gatherWildcardProof = false;
2✔
4901
    }
2✔
4902
  }
241✔
4903

4904
  return RCode::NoError;
10,896✔
4905
}
10,896✔
4906

4907
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)
4908
{
234✔
4909
  if (denialState == expectedState) {
234✔
4910
    neValidationState = vState::Secure;
209✔
4911
  }
209✔
4912
  else {
25✔
4913
    if (denialState == dState::OPTOUT) {
25✔
4914
      LOG(prefix << qname << ": OPT-out denial found for " << neName << endl);
11!
4915
      /* rfc5155 states:
4916
         "The AD bit, as defined by [RFC4035], MUST NOT be set when returning a
4917
         response containing a closest (provable) encloser proof in which the
4918
         NSEC3 RR that covers the "next closer" name has the Opt-Out bit set.
4919

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

4926
         At best the Opt-Out NSEC3 RR proves that there is no signed DS (so no
4927
         secure delegation).
4928
      */
4929
      neValidationState = vState::Insecure;
11✔
4930
    }
11✔
4931
    else if (denialState == dState::INSECURE) {
14✔
4932
      LOG(prefix << qname << ": Insecure denial found for " << neName << ", returning Insecure" << endl);
2!
4933
      neValidationState = vState::Insecure;
2✔
4934
    }
2✔
4935
    else {
12✔
4936
      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!
4937
      /* try again to get the missed cuts, harder this time */
4938
      auto zState = getValidationStatus(neName, false, isDS, depth, prefix);
12✔
4939
      if (zState != vState::Secure) {
12✔
4940
        neValidationState = zState;
4✔
4941
      }
4✔
4942
      else {
8✔
4943
        LOG(prefix << qname << ": Still in a secure zone with an invalid denial for " << neName << ", returning " << vStateToString(vState::BogusInvalidDenial) << endl);
8!
4944
        neValidationState = vState::BogusInvalidDenial;
8✔
4945
      }
8✔
4946
    }
12✔
4947
  }
25✔
4948
  updateValidationState(qname, state, neValidationState, prefix);
234✔
4949
}
234✔
4950

4951
dState SyncRes::getDenialValidationState(const NegCache::NegCacheEntry& negEntry, const dState expectedState, bool referralToUnsigned, const string& prefix)
4952
{
1,824✔
4953
  cspmap_t csp = harvestCSPFromNE(negEntry);
1,824✔
4954
  return getDenial(csp, negEntry.d_name, negEntry.d_qtype.getCode(), referralToUnsigned, expectedState == dState::NXQTYPE, d_validationContext, LogObject(prefix));
1,824✔
4955
}
1,824✔
4956

4957
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)
4958
{
237✔
4959
  /* positive answer synthesized from a wildcard */
4960
  NegCache::NegCacheEntry negEntry;
237✔
4961
  negEntry.d_name = qname;
237✔
4962
  negEntry.d_qtype = QType::ENT; // this encodes 'whole record'
237✔
4963
  uint32_t lowestTTL = rec.d_ttl;
237✔
4964
  harvestNXRecords(lwr.d_records, negEntry, d_now.tv_sec, &lowestTTL);
237✔
4965

4966
  if (vStateIsBogus(state)) {
237!
4967
    negEntry.d_validationState = state;
×
4968
  }
×
4969
  else {
237✔
4970
    auto recordState = getValidationStatus(qname, !negEntry.authoritySOA.signatures.empty() || !negEntry.DNSSECRecords.signatures.empty(), false, depth, prefix);
237✔
4971

4972
    if (recordState == vState::Secure) {
237✔
4973
      /* We have a positive answer synthesized from a wildcard, we need to check that we have
4974
         proof that the exact name doesn't exist so the wildcard can be used,
4975
         as described in section 5.3.4 of RFC 4035 and 5.3 of RFC 7129.
4976
      */
4977
      cspmap_t csp = harvestCSPFromNE(negEntry);
233✔
4978
      dState res = getDenial(csp, qname, negEntry.d_qtype.getCode(), false, false, d_validationContext, LogObject(prefix), false, wildcardLabelsCount);
233✔
4979
      if (res != dState::NXDOMAIN) {
233✔
4980
        vState tmpState = vState::BogusInvalidDenial;
8✔
4981
        if (res == dState::INSECURE || res == dState::OPTOUT) {
8!
4982
          /* Some part could not be validated, for example a NSEC3 record with a too large number of iterations,
4983
             this is not enough to warrant a Bogus, but go Insecure. */
4984
          tmpState = vState::Insecure;
2✔
4985
          LOG(prefix << qname << ": Unable to validate denial in wildcard expanded positive response found for " << qname << ", returning Insecure, res=" << res << endl);
2!
4986
        }
2✔
4987
        else {
6✔
4988
          LOG(prefix << qname << ": Invalid denial in wildcard expanded positive response found for " << qname << ", returning Bogus, res=" << res << endl);
6!
4989
          rec.d_ttl = std::min(rec.d_ttl, s_maxbogusttl);
6✔
4990
        }
6✔
4991

4992
        updateValidationState(qname, state, tmpState, prefix);
8✔
4993
        /* we already stored the record with a different validation status, let's fix it */
4994
        updateValidationStatusInCache(qname, qtype, lwr.d_aabit, tmpState);
8✔
4995
      }
8✔
4996
    }
233✔
4997
  }
237✔
4998
}
237✔
4999

5000
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)
5001
{
10,877✔
5002
  bool done = false;
10,877✔
5003
  DNSName dnameTarget;
10,877✔
5004
  DNSName dnameOwner;
10,877✔
5005
  uint32_t dnameTTL = 0;
10,877✔
5006
  bool referralOnDS = false;
10,877✔
5007

5008
  for (auto& rec : lwr.d_records) {
55,299✔
5009
    if (rec.d_type == QType::OPT || rec.d_class != QClass::IN) {
55,299✔
5010
      continue;
8,606✔
5011
    }
8,606✔
5012

5013
    if (rec.d_place == DNSResourceRecord::ANSWER && !(lwr.d_aabit || sendRDQuery)) {
46,693!
5014
      /* for now we allow a CNAME for the exact qname in ANSWER with AA=0, because Amazon DNS servers
5015
         are sending such responses */
5016
      if (rec.d_type != QType::CNAME || rec.d_name != qname) {
×
5017
        continue;
×
5018
      }
×
5019
    }
×
5020
    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);
46,693!
5021

5022
    bool putInNegCache = true;
46,693✔
5023
    if (negCacheIndication && qtype == QType::DS && isForwardOrAuth(qname)) {
46,693✔
5024
      // #10189, a NXDOMAIN to a DS query for a forwarded or auth domain should not NXDOMAIN the whole domain
5025
      putInNegCache = false;
7✔
5026
    }
7✔
5027

5028
    if (negCacheIndication) {
46,693✔
5029
      LOG(prefix << qname << ": Got negative caching indication for name '" << qname << "' (accept=" << rec.d_name.isPartOf(auth) << "), newtarget='" << newtarget << "'" << endl);
124!
5030

5031
      rec.d_ttl = min(rec.d_ttl, s_maxnegttl);
124✔
5032
      // only add a SOA if we're not going anywhere after this
5033
      if (newtarget.empty()) {
124✔
5034
        ret.push_back(rec);
118✔
5035
      }
118✔
5036

5037
      NegCache::NegCacheEntry negEntry;
124✔
5038

5039
      uint32_t lowestTTL = rec.d_ttl;
124✔
5040
      /* if we get an NXDomain answer with a CNAME, the name
5041
         does exist but the target does not */
5042
      negEntry.d_name = newtarget.empty() ? qname : newtarget;
124✔
5043
      negEntry.d_qtype = QType::ENT; // this encodes 'whole record'
124✔
5044
      negEntry.d_auth = rec.d_name;
124✔
5045
      harvestNXRecords(lwr.d_records, negEntry, d_now.tv_sec, &lowestTTL);
124✔
5046

5047
      if (vStateIsBogus(state)) {
124✔
5048
        negEntry.d_validationState = state;
7✔
5049
      }
7✔
5050
      else {
117✔
5051
        /* here we need to get the validation status of the zone telling us that the domain does not
5052
           exist, ie the owner of the SOA */
5053
        auto recordState = getValidationStatus(rec.d_name, !negEntry.authoritySOA.signatures.empty() || !negEntry.DNSSECRecords.signatures.empty(), false, depth, prefix);
117!
5054
        if (recordState == vState::Secure) {
117✔
5055
          dState denialState = getDenialValidationState(negEntry, dState::NXDOMAIN, false, prefix);
56✔
5056
          updateDenialValidationState(qname, negEntry.d_validationState, negEntry.d_name, state, denialState, dState::NXDOMAIN, false, depth, prefix);
56✔
5057
        }
56✔
5058
        else {
61✔
5059
          negEntry.d_validationState = recordState;
61✔
5060
          updateValidationState(qname, state, negEntry.d_validationState, prefix);
61✔
5061
        }
61✔
5062
      }
117✔
5063

5064
      if (vStateIsBogus(negEntry.d_validationState)) {
124✔
5065
        lowestTTL = min(lowestTTL, s_maxbogusttl);
7✔
5066
      }
7✔
5067

5068
      negEntry.d_ttd = d_now.tv_sec + lowestTTL;
124✔
5069
      negEntry.d_orig_ttl = lowestTTL;
124✔
5070
      /* if we get an NXDomain answer with a CNAME, let's not cache the
5071
         target, even the server was authoritative for it,
5072
         and do an additional query for the CNAME target.
5073
         We have a regression test making sure we do exactly that.
5074
      */
5075
      if (newtarget.empty() && putInNegCache) {
124✔
5076
        g_negCache->add(negEntry);
111✔
5077
        // doCNAMECacheCheck() checks record cache and does not look into negcache. That means that an old record might be found if
5078
        // serve-stale is active. Avoid that by explicitly zapping that CNAME record.
5079
        if (qtype == QType::CNAME && MemRecursorCache::s_maxServedStaleExtensions > 0) {
111!
5080
          g_recCache->doWipeCache(qname, false, qtype);
2✔
5081
        }
2✔
5082
        if (s_rootNXTrust && negEntry.d_auth.isRoot() && auth.isRoot() && lwr.d_aabit) {
111!
5083
          negEntry.d_name = negEntry.d_name.getLastLabel();
3✔
5084
          g_negCache->add(negEntry);
3✔
5085
        }
3✔
5086
      }
111✔
5087

5088
      negIndicHasSignatures = !negEntry.authoritySOA.signatures.empty() || !negEntry.DNSSECRecords.signatures.empty();
124!
5089
      negindic = true;
124✔
5090
    }
124✔
5091
    else if (rec.d_place == DNSResourceRecord::ANSWER && isRedirection(rec.d_type) && // CNAME or DNAME answer
46,569✔
5092
             !isRedirection(qtype.getCode())) { // But not in response to a CNAME or DNAME query
46,569!
5093
      if (rec.d_type == QType::CNAME && rec.d_name == qname) {
1,343✔
5094
        if (!dnameOwner.empty()) { // We synthesize ourselves
1,290✔
5095
          continue;
9✔
5096
        }
9✔
5097
        ret.push_back(rec);
1,281✔
5098
        if (auto content = getRR<CNAMERecordContent>(rec)) {
1,281!
5099
          newtarget = DNSName(content->getTarget());
1,281✔
5100
        }
1,281✔
5101
        if (needWildcardProof) {
1,281✔
5102
          checkWildcardProof(qname, QType::CNAME, rec, lwr, state, depth, prefix, wildcardLabelsCount);
12✔
5103
        }
12✔
5104
      }
1,281✔
5105
      else if (rec.d_type == QType::DNAME && qname.isPartOf(rec.d_name)) { // DNAME
53!
5106
        ret.push_back(rec);
32✔
5107
        if (auto content = getRR<DNAMERecordContent>(rec)) {
32!
5108
          dnameOwner = rec.d_name;
32✔
5109
          dnameTarget = content->getTarget();
32✔
5110
          dnameTTL = rec.d_ttl;
32✔
5111
          if (!newtarget.empty()) { // We had a CNAME before, remove it from ret so we don't cache it
32✔
5112
            ret.erase(std::remove_if(
21✔
5113
                        ret.begin(),
21✔
5114
                        ret.end(),
21✔
5115
                        [&qname](DNSRecord& dnsrecord) {
51✔
5116
                          return (dnsrecord.d_place == DNSResourceRecord::ANSWER && dnsrecord.d_type == QType::CNAME && dnsrecord.d_name == qname);
51!
5117
                        }),
51✔
5118
                      ret.end());
21✔
5119
          }
21✔
5120
          try {
32✔
5121
            newtarget = qname.makeRelative(dnameOwner) + dnameTarget;
32✔
5122
            if (needWildcardProof) {
32!
5123
              checkWildcardProof(qname, QType::DNAME, rec, lwr, state, depth, prefix, wildcardLabelsCount);
×
5124
            }
×
5125
          }
32✔
5126
          catch (const std::exception& e) {
32✔
5127
            // We should probably catch an std::range_error here and set the rcode to YXDOMAIN (RFC 6672, section 2.2)
5128
            // But there is no way to set the RCODE from this function
5129
            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());
×
5130
          }
×
5131
        }
32✔
5132
      }
32✔
5133
    }
1,343✔
5134
    /* if we have a positive answer synthesized from a wildcard, we need to
5135
       return the corresponding NSEC/NSEC3 records from the AUTHORITY section
5136
       proving that the exact name did not exist.
5137
       Except if this is a NODATA answer because then we will gather the NXNSEC records later */
5138
    else if (gatherWildcardProof && !negindic && (rec.d_type == QType::RRSIG || rec.d_type == QType::NSEC || rec.d_type == QType::NSEC3) && rec.d_place == DNSResourceRecord::AUTHORITY) {
45,226✔
5139
      ret.push_back(rec); // enjoy your DNSSEC
468✔
5140
    }
468✔
5141
    // for ANY answers we *must* have an authoritative answer, unless we are forwarding recursively
5142
    else if (rec.d_place == DNSResourceRecord::ANSWER && rec.d_name == qname && (rec.d_type == qtype.getCode() || ((lwr.d_aabit || sendRDQuery) && qtype == QType::ANY))) {
44,758!
5143
      LOG(prefix << qname << ": Answer is in: resolved to '" << rec.getContent()->getZoneRepresentation() << "|" << DNSRecordContent::NumberToType(rec.d_type) << "'" << endl);
8,238!
5144

5145
      done = true;
8,238✔
5146
      rcode = RCode::NoError;
8,238✔
5147

5148
      if (needWildcardProof) {
8,238✔
5149
        checkWildcardProof(qname, qtype, rec, lwr, state, depth, prefix, wildcardLabelsCount);
225✔
5150
      }
225✔
5151

5152
      ret.push_back(rec);
8,238✔
5153
    }
8,238✔
5154
    else if ((rec.d_type == QType::RRSIG || rec.d_type == QType::NSEC || rec.d_type == QType::NSEC3) && rec.d_place == DNSResourceRecord::ANSWER) {
36,520✔
5155
      if (rec.d_type != QType::RRSIG || rec.d_name == qname) {
2,745!
5156
        ret.push_back(rec); // enjoy your DNSSEC
2,712✔
5157
      }
2,712✔
5158
      else if (rec.d_type == QType::RRSIG && qname.isPartOf(rec.d_name)) {
33!
5159
        auto rrsig = getRR<RRSIGRecordContent>(rec);
24✔
5160
        if (rrsig != nullptr && rrsig->d_type == QType::DNAME) {
24!
5161
          ret.push_back(rec);
24✔
5162
        }
24✔
5163
      }
24✔
5164
    }
2,745✔
5165
    else if (rec.d_place == DNSResourceRecord::AUTHORITY && rec.d_type == QType::NS && qname.isPartOf(rec.d_name)) {
33,775✔
5166
      if (moreSpecificThan(rec.d_name, auth)) {
10,865✔
5167
        newauth = rec.d_name;
10,195✔
5168
        LOG(prefix << qname << ": Got NS record '" << rec.d_name << "' -> '" << rec.getContent()->getZoneRepresentation() << "'" << endl);
10,195!
5169

5170
        /* check if we have a referral from the parent zone to a child zone for a DS query, which is not right */
5171
        if (qtype == QType::DS && (newauth.isPartOf(qname) || qname == newauth)) {
10,195!
5172
          /* just got a referral from the parent zone when asking for a DS, looks like this server did not get the DNSSEC memo.. */
5173
          referralOnDS = true;
4✔
5174
        }
4✔
5175
        else {
10,191✔
5176
          realreferral = true;
10,191✔
5177
          if (auto content = getRR<NSRecordContent>(rec)) {
10,191!
5178
            nsset.insert(content->getNS());
10,191✔
5179
          }
10,191✔
5180
        }
10,191✔
5181
      }
10,195✔
5182
      else {
670✔
5183
        LOG(prefix << qname << ": Got upwards/level NS record '" << rec.d_name << "' -> '" << rec.getContent()->getZoneRepresentation() << "', had '" << auth << "'" << endl);
670!
5184
        if (auto content = getRR<NSRecordContent>(rec)) {
670!
5185
          nsset.insert(content->getNS());
670✔
5186
        }
670✔
5187
      }
670✔
5188
    }
10,865✔
5189
    else if (rec.d_place == DNSResourceRecord::AUTHORITY && rec.d_type == QType::DS && qname.isPartOf(rec.d_name)) {
22,910✔
5190
      LOG(prefix << qname << ": Got DS record '" << rec.d_name << "' -> '" << rec.getContent()->getZoneRepresentation() << "'" << endl);
1,230!
5191
    }
1,230✔
5192
    else if (realreferral && rec.d_place == DNSResourceRecord::AUTHORITY && (rec.d_type == QType::NSEC || rec.d_type == QType::NSEC3) && newauth.isPartOf(auth)) {
21,680✔
5193
      /* we might have received a denial of the DS, let's check */
5194
      NegCache::NegCacheEntry negEntry;
2,378✔
5195
      uint32_t lowestTTL = rec.d_ttl;
2,378✔
5196
      harvestNXRecords(lwr.d_records, negEntry, d_now.tv_sec, &lowestTTL);
2,378✔
5197

5198
      if (!vStateIsBogus(state)) {
2,379✔
5199
        auto recordState = getValidationStatus(newauth, !negEntry.authoritySOA.signatures.empty() || !negEntry.DNSSECRecords.signatures.empty(), true, depth, prefix);
2,379!
5200

5201
        if (recordState == vState::Secure) {
2,379✔
5202
          negEntry.d_auth = auth;
1,588✔
5203
          negEntry.d_name = newauth;
1,588✔
5204
          negEntry.d_qtype = QType::DS;
1,588✔
5205
          rec.d_ttl = min(s_maxnegttl, rec.d_ttl);
1,588✔
5206

5207
          dState denialState = getDenialValidationState(negEntry, dState::NXQTYPE, true, prefix);
1,588✔
5208

5209
          if (denialState == dState::NXQTYPE || denialState == dState::OPTOUT || denialState == dState::INSECURE) {
1,588!
5210
            negEntry.d_ttd = lowestTTL + d_now.tv_sec;
1,584✔
5211
            negEntry.d_orig_ttl = lowestTTL;
1,584✔
5212
            negEntry.d_validationState = vState::Secure;
1,584✔
5213
            if (denialState == dState::OPTOUT) {
1,584✔
5214
              negEntry.d_validationState = vState::Insecure;
1,502✔
5215
            }
1,502✔
5216
            LOG(prefix << qname << ": Got negative indication of DS record for '" << newauth << "'" << endl);
1,584!
5217

5218
            g_negCache->add(negEntry);
1,584✔
5219

5220
            /* 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
5221
               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
5222
               query. */
5223
            if (qtype == QType::DS && qname == newauth && (d_externalDSQuery.empty() || qname != d_externalDSQuery)) {
1,584!
5224
              /* we are actually done! */
5225
              negindic = true;
×
5226
              negIndicHasSignatures = !negEntry.authoritySOA.signatures.empty() || !negEntry.DNSSECRecords.signatures.empty();
×
5227
              nsset.clear();
×
5228
            }
×
5229
          }
1,584✔
5230
        }
1,588✔
5231
      }
2,379✔
5232
    }
2,378✔
5233
    else if (!done && rec.d_place == DNSResourceRecord::AUTHORITY && rec.d_type == QType::SOA && lwr.d_rcode == RCode::NoError && qname.isPartOf(rec.d_name)) {
19,302!
5234
      LOG(prefix << qname << ": Got negative caching indication for '" << qname << "|" << qtype << "'" << endl);
1,111!
5235

5236
      if (!newtarget.empty()) {
1,111✔
5237
        LOG(prefix << qname << ": Hang on! Got a redirect to '" << newtarget << "' already" << endl);
5!
5238
      }
5✔
5239
      else {
1,106✔
5240
        rec.d_ttl = min(s_maxnegttl, rec.d_ttl);
1,106✔
5241

5242
        NegCache::NegCacheEntry negEntry;
1,106✔
5243
        negEntry.d_auth = rec.d_name;
1,106✔
5244
        uint32_t lowestTTL = rec.d_ttl;
1,106✔
5245
        negEntry.d_name = qname;
1,106✔
5246
        negEntry.d_qtype = qtype;
1,106✔
5247
        harvestNXRecords(lwr.d_records, negEntry, d_now.tv_sec, &lowestTTL);
1,106✔
5248

5249
        if (vStateIsBogus(state)) {
1,106✔
5250
          negEntry.d_validationState = state;
7✔
5251
        }
7✔
5252
        else {
1,099✔
5253
          auto recordState = getValidationStatus(qname, !negEntry.authoritySOA.signatures.empty() || !negEntry.DNSSECRecords.signatures.empty(), qtype == QType::DS, depth, prefix);
1,099!
5254
          if (recordState == vState::Secure) {
1,099✔
5255
            dState denialState = getDenialValidationState(negEntry, dState::NXQTYPE, false, prefix);
174✔
5256
            updateDenialValidationState(qname, negEntry.d_validationState, negEntry.d_name, state, denialState, dState::NXQTYPE, qtype == QType::DS, depth, prefix);
174✔
5257
          }
174✔
5258
          else {
925✔
5259
            negEntry.d_validationState = recordState;
925✔
5260
            updateValidationState(qname, state, negEntry.d_validationState, prefix);
925✔
5261
          }
925✔
5262
        }
1,099✔
5263

5264
        if (vStateIsBogus(negEntry.d_validationState)) {
1,106✔
5265
          lowestTTL = min(lowestTTL, s_maxbogusttl);
13✔
5266
          rec.d_ttl = min(rec.d_ttl, s_maxbogusttl);
13✔
5267
        }
13✔
5268
        negEntry.d_ttd = d_now.tv_sec + lowestTTL;
1,106✔
5269
        negEntry.d_orig_ttl = lowestTTL;
1,106✔
5270
        if (qtype.getCode() != 0) { // prevents us from NXDOMAIN'ing a whole domain
1,106✔
5271
          // doCNAMECacheCheck() checks record cache and does not look into negcache. That means that an old record might be found if
5272
          // serve-stale is active. Avoid that by explicitly zapping that CNAME record.
5273
          if (qtype == QType::CNAME && MemRecursorCache::s_maxServedStaleExtensions > 0) {
1,104!
5274
            g_recCache->doWipeCache(qname, false, qtype);
2✔
5275
          }
2✔
5276
          g_negCache->add(negEntry);
1,104✔
5277
        }
1,104✔
5278

5279
        ret.push_back(rec);
1,106✔
5280
        negindic = true;
1,106✔
5281
        negIndicHasSignatures = !negEntry.authoritySOA.signatures.empty() || !negEntry.DNSSECRecords.signatures.empty();
1,106!
5282
      }
1,106✔
5283
    }
1,111✔
5284
  }
46,693✔
5285

5286
  if (!dnameTarget.empty()) {
10,877✔
5287
    // Synthesize a CNAME
5288
    auto cnamerec = DNSRecord();
32✔
5289
    cnamerec.d_name = qname;
32✔
5290
    cnamerec.d_type = QType::CNAME;
32✔
5291
    cnamerec.d_ttl = dnameTTL;
32✔
5292
    cnamerec.setContent(std::make_shared<CNAMERecordContent>(CNAMERecordContent(newtarget)));
32✔
5293
    ret.push_back(std::move(cnamerec));
32✔
5294
  }
32✔
5295

5296
  /* If we have seen a proper denial, let's forget that we also had a referral for a DS query.
5297
     Otherwise we need to deal with it. */
5298
  if (referralOnDS && !negindic) {
10,877!
5299
    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!
5300
    if (!vStateIsBogus(state)) {
4!
5301
      auto recordState = getValidationStatus(qname, false, true, depth, prefix);
4✔
5302
      if (recordState == vState::Secure) {
4✔
5303
        /* we are in a secure zone, got a referral to the child zone on a DS query, no denial, that's wrong */
5304
        LOG(prefix << qname << ": NODATA without a negative indication (missing SOA in authority) in a DNSSEC secure zone, going Bogus" << endl);
2!
5305
        updateValidationState(qname, state, vState::BogusMissingNegativeIndication, prefix);
2✔
5306
      }
2✔
5307
    }
4✔
5308
    negindic = true;
4✔
5309
    negIndicHasSignatures = false;
4✔
5310
  }
4✔
5311

5312
  return done;
10,877✔
5313
}
10,877✔
5314

5315
static void submitTryDotTask(ComboAddress address, const DNSName& auth, const DNSName& nsname, time_t now)
5316
{
×
5317
  if (address.getPort() == 853) {
×
5318
    return;
×
5319
  }
×
5320
  address.setPort(853);
×
5321
  auto lock = s_dotMap.lock();
×
5322
  if (lock->d_numBusy >= SyncRes::s_max_busy_dot_probes) {
×
5323
    return;
×
5324
  }
×
5325
  auto iter = lock->d_map.emplace(DoTStatus{address, auth, now + dotFailWait}).first;
×
5326
  if (iter->d_status == DoTStatus::Busy) {
×
5327
    return;
×
5328
  }
×
5329
  if (iter->d_ttd > now) {
×
5330
    if (iter->d_status == DoTStatus::Bad) {
×
5331
      return;
×
5332
    }
×
5333
    if (iter->d_status == DoTStatus::Good) {
×
5334
      return;
×
5335
    }
×
5336
    // We only want to probe auths that we have seen before, auth that only come around once are not interesting
5337
    if (iter->d_status == DoTStatus::Unknown && iter->d_count == 0) {
×
5338
      return;
×
5339
    }
×
5340
  }
×
5341
  lock->d_map.modify(iter, [=](DoTStatus& status) { status.d_ttd = now + dotFailWait; });
×
5342
  bool pushed = pushTryDoTTask(auth, QType::SOA, address, std::numeric_limits<time_t>::max(), nsname);
×
5343
  if (pushed) {
×
5344
    iter->d_status = DoTStatus::Busy;
×
5345
    ++lock->d_numBusy;
×
5346
  }
×
5347
}
×
5348

5349
static bool shouldDoDoT(ComboAddress address, time_t now)
5350
{
×
5351
  address.setPort(853);
×
5352
  auto lock = s_dotMap.lock();
×
5353
  auto iter = lock->d_map.find(address);
×
5354
  if (iter == lock->d_map.end()) {
×
5355
    return false;
×
5356
  }
×
5357
  iter->d_count++;
×
5358
  return iter->d_status == DoTStatus::Good && iter->d_ttd > now;
×
5359
}
×
5360

5361
static void updateDoTStatus(ComboAddress address, DoTStatus::Status status, time_t time, bool updateBusy = false)
5362
{
×
5363
  address.setPort(853);
×
5364
  auto lock = s_dotMap.lock();
×
5365
  auto iter = lock->d_map.find(address);
×
5366
  if (iter != lock->d_map.end()) {
×
5367
    iter->d_status = status;
×
5368
    lock->d_map.modify(iter, [=](DoTStatus& statusToModify) { statusToModify.d_ttd = time; });
×
5369
    if (updateBusy) {
×
5370
      --lock->d_numBusy;
×
5371
    }
×
5372
  }
×
5373
}
×
5374

5375
bool SyncRes::tryDoT(const DNSName& qname, const QType qtype, const DNSName& nsName, ComboAddress address, time_t now)
5376
{
×
5377
  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));
×
5378

5379
  auto logHelper1 = [&log](const string& ename) {
×
5380
    log->info(Logr::Debug, "Failed to probe DoT records, got an exception", "exception", Logging::Loggable(ename));
×
5381
  };
×
5382
  auto logHelper2 = [&log](const string& msg, const string& ename) {
×
5383
    log->error(Logr::Debug, msg, "Failed to probe DoT records, got an exception", "exception", Logging::Loggable(ename));
×
5384
  };
×
5385
  LWResult lwr;
×
5386
  bool truncated{};
×
5387
  bool spoofed{};
×
5388
  boost::optional<Netmask> netmask;
×
5389
  address.setPort(853);
×
5390
  // We use the fact that qname equals auth
5391
  bool isOK = false;
×
5392
  try {
×
5393
    boost::optional<EDNSExtendedError> extendedError;
×
5394
    isOK = doResolveAtThisIP("", qname, qtype, lwr, netmask, qname, false, false, nsName, address, true, true, truncated, spoofed, extendedError, true);
×
5395
    isOK = isOK && lwr.d_rcode == RCode::NoError && !lwr.d_records.empty();
×
5396
  }
×
5397
  catch (const PDNSException& e) {
×
5398
    logHelper2(e.reason, "PDNSException");
×
5399
  }
×
5400
  catch (const ImmediateServFailException& e) {
×
5401
    logHelper2(e.reason, "ImmediateServFailException");
×
5402
  }
×
5403
  catch (const PolicyHitException& e) {
×
5404
    logHelper1("PolicyHitException");
×
5405
  }
×
5406
  catch (const std::exception& e) {
×
5407
    logHelper2(e.what(), "std::exception");
×
5408
  }
×
5409
  catch (...) {
×
5410
    logHelper1("other");
×
5411
  }
×
5412
  updateDoTStatus(address, isOK ? DoTStatus::Good : DoTStatus::Bad, now + (isOK ? dotSuccessWait : dotFailWait), true);
×
5413
  return isOK;
×
5414
}
×
5415

5416
void SyncRes::ednsStats(boost::optional<Netmask>& ednsmask, const DNSName& qname, const string& prefix)
5417
{
11,228✔
5418
  if (!ednsmask) {
11,228✔
5419
    return;
11,170✔
5420
  }
11,170✔
5421
  s_ecsresponses++;
58✔
5422
  LOG(prefix << qname << ": Received EDNS Client Subnet Mask " << ednsmask->toString() << " on response" << endl);
58!
5423

5424
  if (ednsmask->getBits() > 0) {
58!
5425
    if (ednsmask->isIPv4()) {
58✔
5426
      ++SyncRes::s_ecsResponsesBySubnetSize4.at(ednsmask->getBits() - 1);
51✔
5427
    }
51✔
5428
    else {
7✔
5429
      ++SyncRes::s_ecsResponsesBySubnetSize6.at(ednsmask->getBits() - 1);
7✔
5430
    }
7✔
5431
  }
58✔
5432
}
58✔
5433

5434
void SyncRes::updateQueryCounts(const string& prefix, const DNSName& qname, const ComboAddress& address, bool doTCP, bool doDoT)
5435
{
11,234✔
5436
  t_Counters.at(rec::Counter::outqueries)++;
11,234✔
5437
  d_outqueries++;
11,234✔
5438
  checkMaxQperQ(qname);
11,234✔
5439
  if (address.sin4.sin_family == AF_INET6) {
11,234✔
5440
    t_Counters.at(rec::Counter::ipv6queries)++;
800✔
5441
  }
800✔
5442
  if (doTCP) {
11,234✔
5443
    if (doDoT) {
23✔
5444
      LOG(prefix << qname << ": Using DoT with " << address.toStringWithPort() << endl);
9!
5445
      t_Counters.at(rec::Counter::dotoutqueries)++;
9✔
5446
      d_dotoutqueries++;
9✔
5447
    }
9✔
5448
    else {
14✔
5449
      LOG(prefix << qname << ": Using TCP with " << address.toStringWithPort() << endl);
14!
5450
      t_Counters.at(rec::Counter::tcpoutqueries)++;
14✔
5451
      d_tcpoutqueries++;
14✔
5452
    }
14✔
5453
  }
23✔
5454
}
11,234✔
5455

5456
void SyncRes::incTimeoutStats(const ComboAddress& remoteIP)
5457
{
333✔
5458
  d_timeouts++;
333✔
5459
  t_Counters.at(rec::Counter::outgoingtimeouts)++;
333✔
5460

5461
  if (remoteIP.sin4.sin_family == AF_INET) {
333✔
5462
    t_Counters.at(rec::Counter::outgoing4timeouts)++;
181✔
5463
  }
181✔
5464
  else {
152✔
5465
    t_Counters.at(rec::Counter::outgoing6timeouts)++;
152✔
5466
  }
152✔
5467

5468
  if (t_timeouts) {
333✔
5469
    t_timeouts->push_back(remoteIP);
14✔
5470
  }
14✔
5471
}
333✔
5472

5473
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)
5474
{
11,240✔
5475
  bool chained = false;
11,240✔
5476
  LWResult::Result resolveret = LWResult::Result::Success;
11,240✔
5477

5478
  if (s_maxtotusec != 0 && d_totUsec > s_maxtotusec) {
11,240!
5479
    if (s_addExtendedResolutionDNSErrors) {
2!
5480
      extendedError = EDNSExtendedError{static_cast<uint16_t>(EDNSExtendedError::code::NoReachableAuthority), "Timeout waiting for answer(s)"};
×
5481
    }
×
5482
    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✔
5483
  }
2✔
5484

5485
  int preOutQueryRet = RCode::NoError;
11,238✔
5486
  if (d_pdl && d_pdl->preoutquery(remoteIP, d_requestor, qname, qtype, doTCP, lwr.d_records, preOutQueryRet, d_eventTrace, timeval{0, 0})) {
11,238✔
5487
    LOG(prefix << qname << ": Query handled by Lua" << endl);
4!
5488
  }
4✔
5489
  else {
11,234✔
5490
    ednsmask = getEDNSSubnetMask(qname, remoteIP);
11,234✔
5491
    if (ednsmask) {
11,234✔
5492
      LOG(prefix << qname << ": Adding EDNS Client Subnet Mask " << ednsmask->toString() << " to query" << endl);
70!
5493
      s_ecsqueries++;
70✔
5494
    }
70✔
5495
    updateQueryCounts(prefix, qname, remoteIP, doTCP, doDoT);
11,234✔
5496
    resolveret = asyncresolveWrapper(remoteIP, d_doDNSSEC, qname, auth, qtype.getCode(),
11,234✔
5497
                                     doTCP, sendRDQuery, &d_now, ednsmask, &lwr, &chained, nsName); // <- we go out on the wire!
11,234✔
5498
    ednsStats(ednsmask, qname, prefix);
11,234✔
5499
  }
11,234✔
5500

5501
  /* preoutquery killed the query by setting dq.rcode to -3 */
5502
  if (preOutQueryRet == -3) {
11,238✔
5503
    throw ImmediateServFailException("Query killed by policy");
4✔
5504
  }
4✔
5505

5506
  d_totUsec += lwr.d_usec;
11,234✔
5507

5508
  if (resolveret == LWResult::Result::Spoofed) {
11,234!
5509
    spoofed = true;
×
5510
    return false;
×
5511
  }
×
5512

5513
  accountAuthLatency(lwr.d_usec, remoteIP.sin4.sin_family);
11,234✔
5514
  ++t_Counters.at(rec::RCode::auth).rcodeCounters.at(static_cast<uint8_t>(lwr.d_rcode));
11,234✔
5515

5516
  if (!dontThrottle) {
11,234✔
5517
    dontThrottle = shouldNotThrottle(&nsName, &remoteIP);
11,226✔
5518
  }
11,226✔
5519

5520
  if (resolveret != LWResult::Result::Success) {
11,234✔
5521
    /* Error while resolving */
5522
    if (resolveret == LWResult::Result::Timeout) {
383✔
5523
      LOG(prefix << qname << ": Timeout resolving after " << lwr.d_usec / 1000.0 << " ms " << (doTCP ? "over TCP" : "") << endl);
333!
5524
      incTimeoutStats(remoteIP);
333✔
5525
    }
333✔
5526
    else if (resolveret == LWResult::Result::OSLimitError) {
50✔
5527
      /* OS resource limit reached */
5528
      LOG(prefix << qname << ": Hit a local resource limit resolving" << (doTCP ? " over TCP" : "") << ", probable error: " << stringerror() << endl);
6!
5529
      t_Counters.at(rec::Counter::resourceLimits)++;
6✔
5530
    }
6✔
5531
    else if (resolveret == LWResult::Result::ChainLimitError) {
44!
5532
      /* Chain resource limit reached */
5533
      LOG(prefix << qname << ": Hit a chain limit resolving" << (doTCP ? " over TCP" : ""));
×
5534
      t_Counters.at(rec::Counter::chainLimits)++;
×
5535
    }
×
5536
    else {
44✔
5537
      /* LWResult::Result::PermanentError */
5538
      t_Counters.at(rec::Counter::unreachables)++;
44✔
5539
      d_unreachables++;
44✔
5540
      // XXX questionable use of errno
5541
      LOG(prefix << qname << ": Error resolving from " << remoteIP.toString() << (doTCP ? " over TCP" : "") << ", possible error: " << stringerror() << endl);
44!
5542
    }
44✔
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) {
383!
5547
      uint32_t responseUsec = 1000000; // 1 sec for non-timeout cases
377✔
5548
      // Use the actual time if we saw a timeout
5549
      if (resolveret == LWResult::Result::Timeout) {
377✔
5550
        responseUsec = lwr.d_usec;
333✔
5551
      }
333✔
5552

5553
      s_nsSpeeds.lock()->find_or_enter(nsName.empty() ? DNSName(remoteIP.toStringWithPort()) : nsName, d_now).submit(remoteIP, static_cast<int>(responseUsec), d_now);
377✔
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) {
377!
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) {
377✔
5562
        // unreachable, 1 minute or 100 queries
5563
        doThrottle(d_now.tv_sec, remoteIP, qname, qtype, 60, 100, Throttle::Reason::PermanentError);
44✔
5564
      }
44✔
5565
      else {
333✔
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) {
333✔
5570
          // timeout, 10 seconds or 5 queries
5571
          doThrottle(d_now.tv_sec, remoteIP, qname, qtype, 10, 5, Throttle::Reason::Timeout);
30✔
5572
        }
30✔
5573
      }
333✔
5574
    }
377✔
5575

5576
    return false;
383✔
5577
  }
383✔
5578

5579
  if (!lwr.d_validpacket) {
10,851✔
5580
    LOG(prefix << qname << ": " << nsName << " (" << remoteIP.toString() << ") returned a packet we could not parse over " << (doTCP ? "TCP" : "UDP") << ", trying sibling IP or NS" << endl);
8!
5581
    if (!chained && !dontThrottle) {
8!
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
8!
5585

5586
      if (doTCP) {
8✔
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 {
4✔
5591
        doThrottle(d_now.tv_sec, remoteIP, qname, qtype, 10, 2, Throttle::Reason::ParseError);
4✔
5592
      }
4✔
5593
    }
8✔
5594
    return false;
8✔
5595
  }
8✔
5596
  /* we got an answer */
5597
  if (lwr.d_rcode != RCode::NoError && lwr.d_rcode != RCode::NXDomain) {
10,843✔
5598
    LOG(prefix << qname << ": " << nsName << " (" << remoteIP.toString() << ") returned a " << RCode::to_s(lwr.d_rcode) << ", trying sibling IP or NS" << endl);
64!
5599
    if (!chained && !dontThrottle) {
64!
5600
      if (wasForwarded && lwr.d_rcode == RCode::ServFail) {
64✔
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 {
60✔
5607
        Throttle::Reason reason{};
60✔
5608
        switch (lwr.d_rcode) {
60✔
5609
        case RCode::ServFail:
×
5610
          reason = Throttle::Reason::RCodeServFail;
×
5611
          break;
×
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
        }
60✔
5619
        doThrottle(d_now.tv_sec, remoteIP, qname, qtype, 60, 3, reason);
60✔
5620
      }
60✔
5621
    }
64✔
5622
    return false;
64✔
5623
  }
64✔
5624

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

5632
  if (lwr.d_tcbit) {
10,779✔
5633
    truncated = true;
16✔
5634

5635
    if (doTCP) {
16✔
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);
14!
5644

5645
    return true;
14✔
5646
  }
16✔
5647

5648
  return true;
10,763✔
5649
}
10,779✔
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,298✔
5653
  if (newtarget == qname) {
1,298✔
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,295✔
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);
25!
5662
    setQNameMinimization(false);
25✔
5663
  }
25✔
5664

5665
  if (!d_followCNAME) {
1,295✔
5666
    rcode = RCode::NoError;
119✔
5667
    return;
119✔
5668
  }
119✔
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,176✔
5672
  if (CNAMELoop) {
1,176✔
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,174✔
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,172✔
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,168!
5696

5697
  set<GetBestNSAnswer> beenthere;
1,168✔
5698
  Context cnameContext;
1,168✔
5699
  rcode = doResolve(newtarget, qtype, ret, depth + 1, beenthere, cnameContext);
1,168✔
5700
  LOG(prefix << qname << ": Updating validation state for response to " << qname << " from " << state << " with the state from the CNAME quest: " << cnameContext.state << endl);
1,168!
5701
  updateValidationState(qname, state, cnameContext.state, prefix);
1,168✔
5702
}
1,168✔
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)
5705
{
10,896✔
5706
  if (s_minimumTTL != 0) {
10,896✔
5707
    for (auto& rec : lwr.d_records) {
49,799✔
5708
      rec.d_ttl = max(rec.d_ttl, s_minimumTTL);
49,799✔
5709
    }
49,799✔
5710
  }
8,720✔
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)) {
10,896!
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;
10,896✔
5723
  bool gatherWildcardProof = false;
10,896✔
5724
  unsigned int wildcardLabelsCount = 0;
10,896✔
5725
  *rcode = updateCacheFromRecords(depth, prefix, lwr, qname, qtype, auth, wasForwarded, ednsmask, state, needWildcardProof, gatherWildcardProof, wildcardLabelsCount, sendRDQuery, remoteIP);
10,896✔
5726
  if (*rcode != RCode::NoError) {
10,896!
5727
    return true;
×
5728
  }
×
5729

5730
  LOG(prefix << qname << ": Determining status after receiving this packet" << endl);
10,896!
5731

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

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

5741
  if (done) {
10,896✔
5742
    LOG(prefix << qname << ": Status=got results, this level of recursion done" << endl);
5,130!
5743
    LOG(prefix << qname << ": Validation status is " << state << endl);
5,130!
5744
    return true;
5,130✔
5745
  }
5,130✔
5746

5747
  if (!newtarget.empty()) {
5,766✔
5748
    handleNewTarget(prefix, qname, newtarget, qtype.getCode(), ret, *rcode, depth, lwr.d_records, state);
1,291✔
5749
    return true;
1,291✔
5750
  }
1,291✔
5751

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

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

5766
    if (d_doDNSSEC) {
130✔
5767
      addNXNSECS(ret, lwr.d_records);
100✔
5768
    }
100✔
5769

5770
    *rcode = RCode::NXDomain;
130✔
5771
    return true;
130✔
5772
  }
130✔
5773

5774
  if (nsset.empty() && lwr.d_rcode == 0 && (negindic || lwr.d_aabit || sendRDQuery)) {
4,345!
5775
    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,153!
5776

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

5788
    if (d_doDNSSEC) {
1,153✔
5789
      addNXNSECS(ret, lwr.d_records);
1,083✔
5790
    }
1,083✔
5791

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

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

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

5823
    return false;
3,117✔
5824
  }
3,119✔
5825

5826
  return false;
73✔
5827
}
3,192✔
5828

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

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

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

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

5859
  LOG(endl);
9,596✔
5860

5861
  unsigned int addressQueriesForNS = 0;
9,596✔
5862
  for (;;) { // we may get more specific nameservers
11,168✔
5863
    auto rnameservers = shuffleInSpeedOrder(qname, nameservers, prefix);
11,168✔
5864

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

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

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

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

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

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

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

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

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

5983
          bool truncated = false;
11,226✔
5984
          bool spoofed = false;
11,226✔
5985
          bool gotAnswer = false;
11,226✔
5986
          bool doDoT = false;
11,226✔
5987

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

5997
          if (!doDoT && s_max_busy_dot_probes > 0) {
11,226!
5998
            submitTryDotTask(*remoteIP, auth, tns->first, d_now.tv_sec);
×
5999
          }
×
6000
          if (!forceTCP) {
11,226✔
6001
            gotAnswer = doResolveAtThisIP(prefix, qname, qtype, lwr, ednsmask, auth, sendRDQuery, wasForwarded,
11,217✔
6002
                                          tns->first, *remoteIP, false, false, truncated, spoofed, context.extendedError);
11,217✔
6003
          }
11,217✔
6004
          if (forceTCP || (spoofed || (gotAnswer && truncated))) {
11,226!
6005
            /* retry, over TCP this time */
6006
            gotAnswer = doResolveAtThisIP(prefix, qname, qtype, lwr, ednsmask, auth, sendRDQuery, wasForwarded,
23✔
6007
                                          tns->first, *remoteIP, true, doDoT, truncated, spoofed, context.extendedError);
23✔
6008
          }
23✔
6009

6010
          if (!gotAnswer) {
11,226✔
6011
            if (doDoT && s_max_busy_dot_probes > 0) {
457!
6012
              // This is quite pessimistic...
6013
              updateDoTStatus(*remoteIP, DoTStatus::Bad, d_now.tv_sec + dotFailWait);
×
6014
            }
×
6015
            continue;
457✔
6016
          }
457✔
6017

6018
          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);
10,769!
6019

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

6029
          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);
10,769✔
6030

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

6049
        if (gotNewServers) {
2,020✔
6050
          break;
1,569✔
6051
        }
1,569✔
6052

6053
        if (remoteIP == remoteIPs.cend()) { // we tried all IP addresses, none worked
451✔
6054
          continue;
321✔
6055
        }
321✔
6056
      }
451✔
6057
    }
11,407✔
6058
  }
11,168✔
6059
  return -1;
130✔
6060
}
9,596✔
6061

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

6072
void SyncRes::setQuerySource(const ComboAddress& requestor, const boost::optional<const EDNSSubnetOpts&>& incomingECS)
6073
{
2,392✔
6074
  d_requestor = requestor;
2,392✔
6075

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

6117
boost::optional<Netmask> SyncRes::getEDNSSubnetMask(const DNSName& name, const ComboAddress& rem)
6118
{
11,234✔
6119
  if (d_outgoingECSNetwork && (s_ednsdomains.check(name) || s_ednsremotesubnets.match(rem))) {
11,234✔
6120
    return d_outgoingECSNetwork;
70✔
6121
  }
70✔
6122
  return boost::none;
11,164✔
6123
}
11,234✔
6124

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

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

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

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

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

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

6169
  int res = -1;
24✔
6170
  const std::string msg = "Exception while resolving";
24✔
6171
  try {
24✔
6172
    res = resolver.beginResolve(qname, qtype, qclass, ret, 0);
24✔
6173
  }
24✔
6174
  catch (const PDNSException& e) {
24✔
6175
    SLOG(g_log << Logger::Warning << "Failed to resolve " << qname << ", got pdns exception: " << e.reason << endl,
×
6176
         log->error(Logr::Warning, e.reason, msg, "exception", Logging::Loggable("PDNSException")));
×
6177
    ret.clear();
×
6178
  }
×
6179
  catch (const ImmediateServFailException& e) {
24✔
6180
    SLOG(g_log << Logger::Warning << "Failed to resolve " << qname << ", got ImmediateServFailException: " << e.reason << endl,
×
6181
         log->error(Logr::Warning, e.reason, msg, "exception", Logging::Loggable("ImmediateServFailException")));
×
6182
    ret.clear();
×
6183
  }
×
6184
  catch (const PolicyHitException& e) {
24✔
6185
    SLOG(g_log << Logger::Warning << "Failed to resolve " << qname << ", got a policy hit" << endl,
×
6186
         log->info(Logr::Warning, msg, "exception", Logging::Loggable("PolicyHitException")));
×
6187
    ret.clear();
×
6188
  }
×
6189
  catch (const std::exception& e) {
24✔
6190
    SLOG(g_log << Logger::Warning << "Failed to resolve " << qname << ", got STL error: " << e.what() << endl,
×
6191
         log->error(Logr::Warning, e.what(), msg, "exception", Logging::Loggable("std::exception")));
×
6192
    ret.clear();
×
6193
  }
×
6194
  catch (...) {
24✔
6195
    SLOG(g_log << Logger::Warning << "Failed to resolve " << qname << ", got an exception" << endl,
×
6196
         log->info(Logr::Warning, msg));
×
6197
    ret.clear();
×
6198
  }
×
6199

6200
  return res;
24✔
6201
}
24✔
6202

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

6217
  const string msg = "Failed to update . records";
222✔
6218
  vector<DNSRecord> ret;
222✔
6219
  int res = -1;
222✔
6220
  try {
222✔
6221
    res = resolver.beginResolve(g_rootdnsname, QType::NS, 1, ret, depth + 1);
222✔
6222
    if (g_dnssecmode != DNSSECMode::Off && g_dnssecmode != DNSSECMode::ProcessNoValidate) {
222✔
6223
      auto state = resolver.getValidationState();
175✔
6224
      if (vStateIsBogus(state)) {
175✔
6225
        throw PDNSException("Got Bogus validation result for .|NS");
3✔
6226
      }
3✔
6227
    }
175✔
6228
  }
222✔
6229
  catch (const PDNSException& e) {
222✔
6230
    SLOG(g_log << Logger::Error << "Failed to update . records, got an exception: " << e.reason << endl,
3✔
6231
         log->error(Logr::Error, e.reason, msg, "exception", Logging::Loggable("PDNSException")));
3✔
6232
  }
3✔
6233
  catch (const ImmediateServFailException& e) {
222✔
6234
    SLOG(g_log << Logger::Error << "Failed to update . records, got an exception: " << e.reason << endl,
×
6235
         log->error(Logr::Error, e.reason, msg, "exception", Logging::Loggable("ImmediateServFailException")));
×
6236
  }
×
6237
  catch (const PolicyHitException& policyHit) {
222✔
6238
    SLOG(g_log << Logger::Error << "Failed to update . records, got a policy hit" << endl,
×
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) {
222✔
6244
    SLOG(g_log << Logger::Error << "Failed to update . records, got an exception: " << e.what() << endl,
×
6245
         log->error(Logr::Error, e.what(), msg, "exception", Logging::Loggable("std::exception")));
×
6246
  }
×
6247
  catch (...) {
222✔
6248
    SLOG(g_log << Logger::Error << "Failed to update . records, got an exception" << endl,
×
6249
         log->info(Logr::Error, msg));
×
6250
  }
×
6251

6252
  if (res == 0) {
222✔
6253
    SLOG(g_log << Logger::Debug << "Refreshed . records" << endl,
194✔
6254
         log->info(Logr::Debug, "Refreshed . records"));
194✔
6255
  }
194✔
6256
  else {
28✔
6257
    SLOG(g_log << Logger::Warning << "Failed to update root NS records, RCODE=" << res << endl,
28✔
6258
         log->info(Logr::Warning, msg, "rcode", Logging::Loggable(res)));
28✔
6259
  }
28✔
6260
  return res;
222✔
6261
}
222✔
6262

6263
bool SyncRes::answerIsNOData(uint16_t requestedType, int rcode, const std::vector<DNSRecord>& records)
6264
{
345✔
6265
  if (rcode != RCode::NoError) {
345✔
6266
    return false;
43✔
6267
  }
43✔
6268

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