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

PowerDNS / pdns / 14103313974

27 Mar 2025 09:35AM UTC coverage: 63.413% (+2.0%) from 61.378%
14103313974

Pull #15362

github

web-flow
Merge bbd1a19e0 into 72002bd86
Pull Request #15362: dnsdist: Do not register Xsk sockets on configuration check or client mode

41494 of 100080 branches covered (41.46%)

Branch coverage included in aggregate %.

6 of 7 new or added lines in 2 files covered. (85.71%)

56 existing lines in 11 files now uncovered.

128265 of 167624 relevant lines covered (76.52%)

3990104.75 hits per line

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

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

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

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

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

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

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

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

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

75
    if (iter == d_cont.end()) {
9,713✔
76
      return 0;
9,674✔
77
    }
9,674✔
78
    return iter->value;
39✔
79
  }
9,713✔
80

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

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

94
  void clear(const T& arg)
95
  {
11,760✔
96
    d_cont.erase(arg);
11,760✔
97
  }
11,760✔
98

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

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

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

115
private:
116
  cont_t d_cont;
117
};
118

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

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

146
    float get(float factor)
147
    {
52,992✔
148
      return d_val *= factor;
52,992✔
149
    }
52,992✔
150

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

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

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

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

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

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

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

187
  void purge(const std::map<ComboAddress, float>& keep) const
188
  {
12,021✔
189
    for (auto iter = d_collection.begin(); iter != d_collection.end();) {
25,438✔
190
      if (keep.find(iter->first) != keep.end()) {
13,417✔
191
        ++iter;
13,416✔
192
      }
13,416✔
193
      else {
1✔
194
        iter = d_collection.erase(iter);
1✔
195
      }
1✔
196
    }
13,417✔
197
  }
12,021✔
198

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

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

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

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

235
    float ret = std::numeric_limits<float>::max();
38,447✔
236
    const float factor = iter->getFactor(now);
38,447✔
237
    for (auto& entry : iter->d_collection) {
39,576✔
238
      if (float tmp = entry.second.get(factor); tmp < ret) {
39,576✔
239
        ret = tmp;
38,512✔
240
      }
38,512✔
241
    }
39,576✔
242
    ind.modify(iter, [&](DecayingEwmaCollection& dec) { dec.d_lastget = now; });
38,447✔
243
    return ret;
38,447✔
244
  }
68,561✔
245
};
246

247
static LockGuarded<nsspeeds_t> s_nsSpeeds;
248

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

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

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

278
  bool shouldThrottle(time_t now, const Key& arg)
279
  {
24,462✔
280
    auto iter = d_cont.find(arg);
24,462✔
281
    if (iter == d_cont.end()) {
24,462✔
282
      return false;
24,399✔
283
    }
24,399✔
284
    if (now > iter->ttd || iter->count == 0) {
63✔
285
      d_cont.erase(iter);
4✔
286
      return false;
4✔
287
    }
4✔
288
    iter->count--;
59✔
289

290
    return true; // still listed, still blocked
59✔
291
  }
63✔
292

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

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

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

322
  void clear()
323
  {
602✔
324
    d_cont.clear();
602✔
325
  }
602✔
326

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

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

357
private:
358
  cont_t d_cont;
359
};
360

361
static LockGuarded<Throttle> s_throttle;
362

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

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

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

401
static LockGuarded<SavedParentNSSet> s_savedParentNSSet;
402

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

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

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

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

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

458
static LockGuarded<DoTMap> s_dotMap;
459

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

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

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

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

514
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
515
#define LOG(x)                       \
516
  if (d_lm == Log) {                 \
950,107✔
517
    g_log << Logger::Warning << x;   \
105,531✔
518
  }                                  \
105,531✔
519
  else if (d_lm == Store) {          \
950,107✔
520
    addTraceTS(d_fixednow, d_trace); \
1,450✔
521
    d_trace << x;                    \
1,450✔
522
  }
1,450✔
523

524
OptLog SyncRes::LogObject(const string& prefix)
525
{
62,418✔
526
  OptLog ret;
62,418✔
527
  if (d_lm == Log) {
62,418✔
528
    ret = {prefix, d_fixednow, g_log};
10,870✔
529
  }
10,870✔
530
  else if (d_lm == Store) {
51,548✔
531
    ret = {prefix, d_fixednow, d_trace};
98✔
532
  }
98✔
533
  return ret;
62,418✔
534
}
62,418✔
535

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

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

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

576
SyncRes::SyncRes(const struct timeval& now) :
577
  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)
578
{
4,362✔
579
  d_validationContext.d_nsec3IterationsRemainingQuota = s_maxnsec3iterationsperq > 0 ? s_maxnsec3iterationsperq : std::numeric_limits<decltype(d_validationContext.d_nsec3IterationsRemainingQuota)>::max();
2,147,486,206✔
580
}
4,362✔
581

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

935
  return handled;
3,970✔
936
}
3,970✔
937

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

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

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

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

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

989
  SyncRes::AuthDomain::records_t::const_iterator ziter;
131✔
990
  bool somedata = false;
131✔
991

992
  for (ziter = range.first; ziter != range.second; ++ziter) {
278✔
993
    somedata = true;
147✔
994

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

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

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

1016
    return result;
9✔
1017
  }
9✔
1018

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

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

1039
    return result;
6✔
1040
  }
22✔
1041

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

1056
  if (records.empty()) {
14✔
1057
    addSOA(records);
12✔
1058
    result = RCode::NXDomain;
12✔
1059
  }
12✔
1060

1061
  return result;
14✔
1062
}
20✔
1063

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

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

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

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

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

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

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

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

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

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

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

1152
  void prune(time_t now)
1153
  {
95✔
1154
    auto& ind = get<time_t>();
95✔
1155
    ind.erase(ind.begin(), ind.upper_bound(now));
95✔
1156
  }
95✔
1157

1158
  static const time_t Expire = 7200;
1159
};
1160

1161
static LockGuarded<ednsstatus_t> s_ednsstatus;
1162

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

1173
uint64_t SyncRes::getEDNSStatusesSize()
1174
{
651✔
1175
  return s_ednsstatus.lock()->size();
651✔
1176
}
651✔
1177

1178
void SyncRes::clearEDNSStatuses()
1179
{
602✔
1180
  s_ednsstatus.lock()->clear();
602✔
1181
}
602✔
1182

1183
void SyncRes::pruneEDNSStatuses(time_t cutoff)
1184
{
95✔
1185
  s_ednsstatus.lock()->prune(cutoff);
95✔
1186
}
95✔
1187

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

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

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

1218
uint64_t SyncRes::getNSSpeedsSize()
1219
{
777✔
1220
  return s_nsSpeeds.lock()->size();
777✔
1221
}
777✔
1222

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

1229
void SyncRes::clearNSSpeeds()
1230
{
602✔
1231
  s_nsSpeeds.lock()->clear();
602✔
1232
}
602✔
1233

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

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

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

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

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

1272
uint64_t SyncRes::getThrottledServersSize()
1273
{
777✔
1274
  return s_throttle.lock()->size();
777✔
1275
}
777✔
1276

1277
void SyncRes::pruneThrottledServers(time_t now)
1278
{
122✔
1279
  s_throttle.lock()->prune(now);
122✔
1280
}
122✔
1281

1282
void SyncRes::clearThrottle()
1283
{
602✔
1284
  s_throttle.lock()->clear();
602✔
1285
}
602✔
1286

1287
bool SyncRes::isThrottled(time_t now, const ComboAddress& server, const DNSName& target, QType qtype)
1288
{
12,238✔
1289
  return s_throttle.lock()->shouldThrottle(now, std::tuple(server, target, qtype));
12,238✔
1290
}
12,238✔
1291

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

1305
void SyncRes::unThrottle(const ComboAddress& server, const DNSName& name, QType qtype)
1306
{
11,758✔
1307
  s_throttle.lock()->clear(std::tuple(server, g_rootdnsname, 0));
11,758✔
1308
  s_throttle.lock()->clear(std::tuple(server, name, qtype));
11,758✔
1309
}
11,758✔
1310

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

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

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

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

1345
  return count;
×
1346
}
×
1347

1348
uint64_t SyncRes::getFailedServersSize()
1349
{
777✔
1350
  return s_fails.lock()->size();
777✔
1351
}
777✔
1352

1353
void SyncRes::clearFailedServers()
1354
{
602✔
1355
  s_fails.lock()->clear();
602✔
1356
}
602✔
1357

1358
void SyncRes::pruneFailedServers(time_t cutoff)
1359
{
122✔
1360
  s_fails.lock()->prune(cutoff);
122✔
1361
}
122✔
1362

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

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

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

1390
  return count;
×
1391
}
×
1392

1393
uint64_t SyncRes::getNonResolvingNSSize()
1394
{
785✔
1395
  return s_nonresolving.lock()->size();
785✔
1396
}
785✔
1397

1398
void SyncRes::clearNonResolvingNS()
1399
{
602✔
1400
  s_nonresolving.lock()->clear();
602✔
1401
}
602✔
1402

1403
void SyncRes::pruneNonResolving(time_t cutoff)
1404
{
122✔
1405
  s_nonresolving.lock()->prune(cutoff);
122✔
1406
}
122✔
1407

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

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

1430
  return count;
×
1431
}
×
1432

1433
void SyncRes::clearSaveParentsNSSets()
1434
{
602✔
1435
  s_savedParentNSSet.lock()->clear();
602✔
1436
}
602✔
1437

1438
size_t SyncRes::getSaveParentsNSSetsSize()
1439
{
649✔
1440
  return s_savedParentNSSet.lock()->size();
649✔
1441
}
649✔
1442

1443
void SyncRes::pruneSaveParentsNSSets(time_t now)
1444
{
95✔
1445
  s_savedParentNSSet.lock()->prune(now);
95✔
1446
}
95✔
1447

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

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

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

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

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

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

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

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

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

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

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

1543
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
1544
{
12,215✔
1545
  /* what is your QUEST?
1546
     the goal is to get as many remotes as possible on the best level of EDNS support
1547
     The levels are:
1548

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

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

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

1577
  int EDNSLevel = 0;
12,215✔
1578
  auto luaconfsLocal = g_luaconfs.getLocal();
12,215✔
1579
  ResolveContext ctx(d_initialRequestId, nsName);
12,215✔
1580
#ifdef HAVE_FSTRM
10,917✔
1581
  ctx.d_auth = auth;
10,917✔
1582
#endif
10,917✔
1583

1584
  LWResult::Result ret{};
12,215✔
1585

1586
  for (int tries = 0; tries < 2; ++tries) {
12,218!
1587

1588
    if (mode == EDNSStatus::NOEDNS) {
12,218✔
1589
      t_Counters.at(rec::Counter::noEdnsOutQueries)++;
3✔
1590
      EDNSLevel = 0; // level != mode
3✔
1591
    }
3✔
1592
    else if (ednsMANDATORY || mode != EDNSStatus::NOEDNS) {
12,215!
1593
      EDNSLevel = 1;
12,215✔
1594
    }
12,215✔
1595

1596
    DNSName sendQname(domain);
12,218✔
1597
    if (g_lowercaseOutgoing) {
12,218✔
1598
      sendQname.makeUsLowerCase();
8✔
1599
    }
8✔
1600

1601
    if (d_asyncResolve) {
12,218✔
1602
      ret = d_asyncResolve(address, sendQname, type, doTCP, sendRDQuery, EDNSLevel, now, srcmask, ctx, res, chained);
2,501✔
1603
    }
2,501✔
1604
    else {
9,717✔
1605
      ret = asyncresolve(address, sendQname, type, doTCP, sendRDQuery, EDNSLevel, now, srcmask, ctx, d_outgoingProtobufServers, d_frameStreamServers, luaconfsLocal->outgoingProtobufExportConfig.exportTypes, res, chained);
9,717✔
1606
    }
9,717✔
1607

1608
    if (ret == LWResult::Result::PermanentError || LWResult::isLimitError(ret) || ret == LWResult::Result::Spoofed) {
12,218!
1609
      break; // transport error, nothing to learn here
58✔
1610
    }
58✔
1611

1612
    if (ret == LWResult::Result::Timeout) { // timeout, not doing anything with it now
12,160✔
1613
      break;
325✔
1614
    }
325✔
1615

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

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

1642
    break;
11,832✔
1643
  }
11,835✔
1644
  return ret;
12,215✔
1645
}
12,215✔
1646

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

1653
static unsigned int qmStepLen(unsigned int labels, unsigned int qnamelen, unsigned int qmIteration)
1654
{
9,875✔
1655
  unsigned int step{};
9,875✔
1656

1657
  if (qmIteration < SyncRes::s_minimize_one_label) {
9,875✔
1658
    step = 1;
9,772✔
1659
  }
9,772✔
1660
  else if (qmIteration < SyncRes::s_max_minimize_count) {
103!
1661
    step = std::max(1U, (qnamelen - labels) / (SyncRes::s_max_minimize_count - qmIteration));
103✔
1662
  }
103✔
UNCOV
1663
  else {
×
UNCOV
1664
    step = qnamelen - labels;
×
UNCOV
1665
  }
×
1666
  unsigned int targetlen = std::min(labels + step, qnamelen);
9,875✔
1667
  return targetlen;
9,875✔
1668
}
9,875✔
1669

1670
static string resToString(int res)
1671
{
1,103✔
1672
  return res >= 0 ? RCode::to_s(res) : std::to_string(res);
1,103!
1673
}
1,103✔
1674

1675
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)
1676
{
17,594✔
1677
  auto prefix = getPrefix(depth);
17,594✔
1678
  auto luaconfsLocal = g_luaconfs.getLocal();
17,594✔
1679

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

1693
  initZoneCutsFromTA(qname, prefix);
17,536✔
1694

1695
  // In the auth or recursive forward case, it does not make sense to do qname-minimization
1696
  if (!getQNameMinimization() || isRecursiveForwardOrAuth(qname)) {
17,536✔
1697
    return doResolveNoQNameMinimization(qname, qtype, ret, depth, beenthere, context);
2,514✔
1698
  }
2,514✔
1699

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

1715
  DNSName child;
15,022✔
1716
  prefix.append(string("QM "));
15,022✔
1717

1718
  LOG(prefix << qname << ": doResolve" << endl);
15,022✔
1719

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

1739
  const unsigned int qnamelen = qname.countLabels();
6,425✔
1740

1741
  DNSName fwdomain(qname);
6,425✔
1742
  const bool forwarded = getBestAuthZone(&fwdomain) != t_sstorage.domainmap->end();
6,425✔
1743
  if (forwarded) {
6,425✔
1744
    LOG(prefix << qname << ": Step0 qname is in a forwarded domain " << fwdomain << endl);
64!
1745
  }
64✔
1746

1747
  for (unsigned int i = 0; i <= qnamelen; i++) {
7,911✔
1748

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

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

1789
      while (labels < targetlen) {
18,405✔
1790
        child.prependRawLabel(qname.getRawLabel(qnamelen - labels - 1));
8,529✔
1791
        labels++;
8,529✔
1792
      }
8,529✔
1793
      // rfc9156 section-2.3, append labels if they start with an underscore
1794
      while (labels < qnamelen) {
9,876✔
1795
        auto prependLabel = qname.getRawLabel(qnamelen - labels - 1);
3,499✔
1796
        if (prependLabel.at(0) != '_') {
3,499!
1797
          break;
3,499✔
1798
        }
3,499✔
1799
        child.prependRawLabel(prependLabel);
×
1800
        labels++;
×
1801
      }
×
1802

1803
      LOG(prefix << qname << ": Step2 New child " << child << endl);
9,876✔
1804

1805
      // Step 3 resolve
1806
      if (child == qname) {
9,876✔
1807
        LOG(prefix << qname << ": Step3 Going to do final resolve" << endl);
6,378✔
1808
        res = doResolveNoQNameMinimization(qname, qtype, ret, depth, beenthere, context);
6,378✔
1809
        LOG(prefix << qname << ": Step3 Final resolve: " << resToString(res) << "/" << ret.size() << endl);
6,378✔
1810
        return res;
6,378✔
1811
      }
6,378✔
1812

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

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

1843
      if (res != RCode::NoError) {
2,014✔
1844
        // Case 5: unexpected answer
1845
        LOG(prefix << qname << ": Step5: other rcode, last effort final resolve" << endl);
19!
1846
        setQNameMinimization(false);
19✔
1847
        setQMFallbackMode(true);
19✔
1848

1849
        auto oldEDE = context.extendedError;
19✔
1850
        res = doResolveNoQNameMinimization(qname, qtype, ret, depth + 1, beenthere, context);
19✔
1851

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

1862
        LOG(prefix << qname << ": Step5 End resolve: " << resToString(res) << "/" << ret.size() << endl);
19!
1863
        return res;
19✔
1864
      }
19✔
1865
    }
2,014✔
1866
  }
7,880✔
1867

1868
  // Should not be reached
1869
  LOG(prefix << qname << ": Max iterations reached, return ServFail" << endl);
2,147,483,678!
1870
  return RCode::ServFail;
2,147,483,678✔
1871
}
6,425✔
1872

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

1885
static bool haveFinalAnswer(const DNSName& qname, QType qtype, int res, const vector<DNSRecord>& ret)
1886
{
1,047✔
1887
  if (res != RCode::NoError) {
1,047!
1888
    return false;
×
1889
  }
×
1890

1891
  // This loop assumes the CNAME's records are in-order
1892
  DNSName target(qname);
1,047✔
1893
  for (const auto& record : ret) {
3,569✔
1894
    if (record.d_place == DNSResourceRecord::ANSWER && record.d_name == target) {
3,569✔
1895
      if (record.d_type == qtype) {
1,794✔
1896
        return true;
73✔
1897
      }
73✔
1898
      if (record.d_type == QType::CNAME) {
1,721!
1899
        if (auto ptr = getRR<CNAMERecordContent>(record)) {
1,721!
1900
          target = ptr->getTarget();
1,721✔
1901
        }
1,721✔
1902
        else {
×
1903
          return false;
×
1904
        }
×
1905
      }
1,721✔
1906
    }
1,721✔
1907
  }
3,569✔
1908
  return false;
974✔
1909
}
1,047✔
1910

1911
/*! This function will check the cache and go out to the internet if the answer is not in cache
1912
 *
1913
 * \param qname The name we need an answer for
1914
 * \param qtype
1915
 * \param ret The vector of DNSRecords we need to fill with the answers
1916
 * \param depth The recursion depth we are in
1917
 * \param beenthere
1918
 * \param fromCache tells the caller the result came from the cache, may be nullptr
1919
 * \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
1920
 * \return DNS RCODE or -1 (Error)
1921
 */
1922
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)
1923
{
29,043✔
1924
  context.extendedError.reset();
29,043✔
1925
  auto prefix = getPrefix(depth);
29,043✔
1926

1927
  LOG(prefix << qname << ": Wants " << (d_doDNSSEC ? "" : "NO ") << "DNSSEC processing, " << (d_requireAuthData ? "" : "NO ") << "auth data required by query for " << qtype << endl);
29,043!
1928

1929
  d_maxdepth = std::max(d_maxdepth, depth);
29,043✔
1930
  if (s_maxdepth > 0) {
29,049✔
1931
    auto bound = getAdjustedRecursionBound();
29,047✔
1932
    // Use a stricter bound if throttling
1933
    if (depth > bound || (d_outqueries > 10 && d_throttledqueries > 5 && depth > bound * 2 / 3)) {
29,048!
1934
      string msg = "More than " + std::to_string(bound) + " (adjusted max-recursion-depth) levels of recursion needed while resolving " + qname.toLogString();
4✔
1935
      LOG(prefix << qname << ": " << msg << endl);
4!
1936
      throw ImmediateServFailException(std::move(msg));
4✔
1937
    }
4✔
1938
  }
29,047✔
1939

1940
  int res = 0;
29,039✔
1941

1942
  const int iterations = !d_refresh && MemRecursorCache::s_maxServedStaleExtensions > 0 ? 2 : 1;
29,039✔
1943
  for (int loop = 0; loop < iterations; loop++) {
2,147,492,227✔
1944

1945
    d_serveStale = loop == 1;
29,062✔
1946
    if (d_serveStale) {
29,062✔
1947
      LOG(prefix << qname << ": Restart, with serve-stale enabled" << endl);
20!
1948
    }
20✔
1949
    // This is a difficult way of expressing "this is a normal query", i.e. not getRootNS.
1950
    if (!d_updatingRootNS || qtype.getCode() != QType::NS || !qname.isRoot()) {
29,062!
1951
      DNSName authname(qname);
28,601✔
1952
      const auto iter = getBestAuthZone(&authname);
28,601✔
1953

1954
      if (d_cacheonly) {
28,601✔
1955
        if (iter != t_sstorage.domainmap->end()) {
16,623✔
1956
          if (iter->second.isAuth()) {
156✔
1957
            LOG(prefix << qname << ": Cache only lookup for '" << qname << "|" << qtype << "', in auth zone" << endl);
2!
1958
            ret.clear();
2✔
1959
            d_wasOutOfBand = doOOBResolve(qname, qtype, ret, depth, prefix, res);
2✔
1960
            if (fromCache != nullptr) {
2!
1961
              *fromCache = d_wasOutOfBand;
×
1962
            }
×
1963
            return res;
2✔
1964
          }
2✔
1965
        }
156✔
1966
      }
16,623✔
1967

1968
      bool wasForwardedOrAuthZone = false;
28,599✔
1969
      bool wasAuthZone = false;
28,599✔
1970
      bool wasForwardRecurse = false;
28,599✔
1971

1972
      if (iter != t_sstorage.domainmap->end()) {
28,599✔
1973
        wasForwardedOrAuthZone = true;
631✔
1974

1975
        if (iter->second.isAuth()) {
631✔
1976
          wasAuthZone = true;
294✔
1977
        }
294✔
1978
        else if (iter->second.shouldRecurse()) {
337✔
1979
          wasForwardRecurse = true;
99✔
1980
        }
99✔
1981
      }
631✔
1982

1983
      /* When we are looking for a DS, we want to the non-CNAME cache check first
1984
         because we can actually have a DS (from the parent zone) AND a CNAME (from
1985
         the child zone), and what we really want is the DS */
1986
      if (qtype != QType::DS && doCNAMECacheCheck(qname, qtype, ret, depth, prefix, res, context, wasAuthZone, wasForwardRecurse, loop == 1)) { // will reroute us if needed
28,599✔
1987
        d_wasOutOfBand = wasAuthZone;
2,341✔
1988
        // Here we have an issue. If we were prevented from going out to the network (cache-only was set, possibly because we
1989
        // are in QM Step0) we might have a CNAME but not the corresponding target.
1990
        // It means that we will sometimes go to the next steps when we are in fact done, but that's fine since
1991
        // we will get the records from the cache, resulting in a small overhead.
1992
        // This might be a real problem if we had a RPZ hit, though, because we do not want the processing to continue, since
1993
        // RPZ rules will not be evaluated anymore (we already matched).
1994
        const bool stoppedByPolicyHit = d_appliedPolicy.wasHit();
2,341✔
1995

1996
        if (fromCache != nullptr && (!d_cacheonly || stoppedByPolicyHit)) {
2,341✔
1997
          *fromCache = true;
3✔
1998
        }
3✔
1999
        /* Apply Post filtering policies */
2000

2001
        if (d_wantsRPZ && !stoppedByPolicyHit) {
2,341✔
2002
          auto luaLocal = g_luaconfs.getLocal();
2,336✔
2003
          if (luaLocal->dfe.getPostPolicy(ret, d_discardedPolicies, d_appliedPolicy)) {
2,336!
2004
            mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());
×
2005
            bool done = false;
×
2006
            handlePolicyHit(prefix, qname, qtype, ret, done, res, depth);
×
2007
            if (done && fromCache != nullptr) {
×
2008
              *fromCache = true;
×
2009
            }
×
2010
          }
×
2011
        }
2,336✔
2012
        // This handles the case mentioned above: if the full CNAME chain leading to the answer was
2013
        // constructed from the cache, indicate that.
2014
        if (fromCache != nullptr && !*fromCache && haveFinalAnswer(qname, qtype, res, ret)) {
2,341✔
2015
          *fromCache = true;
73✔
2016
        }
73✔
2017
        return res;
2,341✔
2018
      }
2,341✔
2019

2020
      if (doCacheCheck(qname, authname, wasForwardedOrAuthZone, wasAuthZone, wasForwardRecurse, qtype, ret, depth, prefix, res, context)) {
26,258✔
2021
        // we done
2022
        d_wasOutOfBand = wasAuthZone;
9,944✔
2023
        if (fromCache != nullptr) {
9,944✔
2024
          *fromCache = true;
8,521✔
2025
        }
8,521✔
2026

2027
        if (d_wantsRPZ && !d_appliedPolicy.wasHit()) {
9,944!
2028
          auto luaLocal = g_luaconfs.getLocal();
9,940✔
2029
          if (luaLocal->dfe.getPostPolicy(ret, d_discardedPolicies, d_appliedPolicy)) {
9,940✔
2030
            mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());
4✔
2031
            bool done = false;
4✔
2032
            handlePolicyHit(prefix, qname, qtype, ret, done, res, depth);
4✔
2033
          }
4✔
2034
        }
9,940✔
2035

2036
        return res;
9,944✔
2037
      }
9,944✔
2038

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

2050
        if (fromCache != nullptr && (!d_cacheonly || stoppedByPolicyHit)) {
18!
2051
          *fromCache = true;
×
2052
        }
×
2053
        /* Apply Post filtering policies */
2054

2055
        if (d_wantsRPZ && !stoppedByPolicyHit) {
18!
2056
          auto luaLocal = g_luaconfs.getLocal();
18✔
2057
          if (luaLocal->dfe.getPostPolicy(ret, d_discardedPolicies, d_appliedPolicy)) {
18!
2058
            mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());
×
2059
            bool done = false;
×
2060
            handlePolicyHit(prefix, qname, qtype, ret, done, res, depth);
×
2061
            if (done && fromCache != nullptr) {
×
2062
              *fromCache = true;
×
2063
            }
×
2064
          }
×
2065
        }
18✔
2066
        if (fromCache != nullptr && !*fromCache && haveFinalAnswer(qname, qtype, res, ret)) {
18!
2067
          *fromCache = true;
×
2068
        }
×
2069
        return res;
18✔
2070
      }
18✔
2071
    }
16,314✔
2072

2073
    if (d_cacheonly) {
16,757✔
2074
      return 0;
6,479✔
2075
    }
6,479✔
2076

2077
    // When trying to serve-stale, we also only look at the cache. Don't look at d_serveStale, it
2078
    // might be changed by recursive calls (this should be fixed in a better way!).
2079
    if (loop == 1) {
10,278✔
2080
      return res;
4✔
2081
    }
4✔
2082

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

2085
    DNSName subdomain(qname);
10,274✔
2086
    if (qtype == QType::DS) {
10,274✔
2087
      subdomain.chopOff();
194✔
2088
    }
194✔
2089

2090
    NsSet nsset;
10,274✔
2091
    bool flawedNSSet = false;
10,274✔
2092

2093
    // the two retries allow getBestNSNamesFromCache&co to reprime the root
2094
    // hints, in case they ever go missing
2095
    for (int tries = 0; tries < 2 && nsset.empty(); ++tries) {
20,550✔
2096
      subdomain = getBestNSNamesFromCache(subdomain, qtype, nsset, &flawedNSSet, depth, prefix, beenthere); //  pass beenthere to both occasions
10,272✔
2097
    }
10,272✔
2098

2099
    res = doResolveAt(nsset, subdomain, flawedNSSet, qname, qtype, ret, depth, prefix, beenthere, context, stopAtDelegation, nullptr);
10,274✔
2100

2101
    if (res == -1 && s_save_parent_ns_set) {
10,274✔
2102
      // It did not work out, lets check if we have a saved parent NS set
2103
      map<DNSName, vector<ComboAddress>> fallBack;
223✔
2104
      {
223✔
2105
        auto lock = s_savedParentNSSet.lock();
223✔
2106
        auto domainData = lock->find(subdomain);
223✔
2107
        if (domainData != lock->end() && !domainData->d_nsAddresses.empty()) {
223!
2108
          nsset.clear();
2✔
2109
          // Build the nsset arg and fallBack data for the fallback doResolveAt() attempt
2110
          // Take a copy to be able to release the lock, NsSet is actually a map, go figure
2111
          for (const auto& nsAddress : domainData->d_nsAddresses) {
4✔
2112
            nsset.emplace(nsAddress.first, pair(std::vector<ComboAddress>(), false));
4✔
2113
            fallBack.emplace(nsAddress.first, nsAddress.second);
4✔
2114
          }
4✔
2115
        }
2✔
2116
      }
223✔
2117
      if (!fallBack.empty()) {
223✔
2118
        LOG(prefix << qname << ": Failure, but we have a saved parent NS set, trying that one" << endl);
2!
2119
        res = doResolveAt(nsset, subdomain, flawedNSSet, qname, qtype, ret, depth, prefix, beenthere, context, stopAtDelegation, &fallBack);
2✔
2120
        if (res == 0) {
2!
2121
          // It did work out
2122
          s_savedParentNSSet.lock()->inc(subdomain);
2✔
2123
        }
2✔
2124
      }
2✔
2125
    }
223✔
2126
    /* Apply Post filtering policies */
2127
    if (d_wantsRPZ && !d_appliedPolicy.wasHit()) {
10,278✔
2128
      auto luaLocal = g_luaconfs.getLocal();
10,066✔
2129
      if (luaLocal->dfe.getPostPolicy(ret, d_discardedPolicies, d_appliedPolicy)) {
10,066✔
2130
        mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());
5✔
2131
        bool done = false;
5✔
2132
        handlePolicyHit(prefix, qname, qtype, ret, done, res, depth);
5✔
2133
      }
5✔
2134
    }
10,066✔
2135

2136
    if (res == 0) {
10,275✔
2137
      return 0;
9,672✔
2138
    }
9,672✔
2139

2140
    LOG(prefix << qname << ": Failed (res=" << res << ")" << endl);
2,147,484,237✔
2141
    if (res >= 0) {
2,147,484,237✔
2142
      break;
182✔
2143
    }
182✔
2144
  }
2,147,484,237✔
2145
  return res < 0 ? RCode::ServFail : res;
2,147,484,217✔
2146
}
29,039✔
2147

2148
#if 0
2149
// for testing purposes
2150
static bool ipv6First(const ComboAddress& a, const ComboAddress& b)
2151
{
2152
  return !(a.sin4.sin_family < a.sin4.sin_family);
2153
}
2154
#endif
2155

2156
struct speedOrderCA
2157
{
2158
  speedOrderCA(std::map<ComboAddress, float>& speeds) :
2159
    d_speeds(speeds) {}
1,593✔
2160
  bool operator()(const ComboAddress& lhs, const ComboAddress& rhs) const
2161
  {
1,611✔
2162
    return d_speeds[lhs] < d_speeds[rhs];
1,611✔
2163
  }
1,611✔
2164
  std::map<ComboAddress, float>& d_speeds; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members): nothing wrong afaiks
2165
};
2166

2167
void SyncRes::selectNSOnSpeed(const DNSName& qname, const string& prefix, vector<ComboAddress>& ret)
2168
{
12,021✔
2169
  /* we need to remove from the nsSpeeds collection the existing IPs
2170
     for this nameserver that are no longer in the set, even if there
2171
     is only one or none at all in the current set.
2172
  */
2173
  map<ComboAddress, float> speeds;
12,021✔
2174
  {
12,021✔
2175
    auto lock = s_nsSpeeds.lock();
12,021✔
2176
    const auto& collection = lock->find_or_enter(qname, d_now);
12,021✔
2177
    float factor = collection.getFactor(d_now);
12,021✔
2178
    for (const auto& val : ret) {
13,416✔
2179
      speeds[val] = collection.d_collection[val].get(factor);
13,416✔
2180
    }
13,416✔
2181
    collection.purge(speeds);
12,021✔
2182
  }
12,021✔
2183

2184
  if (ret.size() > 1) {
12,021✔
2185
    shuffle(ret.begin(), ret.end(), pdns::dns_random_engine());
1,459✔
2186
    speedOrderCA speedOrder(speeds);
1,459✔
2187
    stable_sort(ret.begin(), ret.end(), speedOrder);
1,459✔
2188
  }
1,459✔
2189

2190
  if (doLog()) {
12,021✔
2191
    LOG(prefix << qname << ": Nameserver " << qname << " IPs: ");
1,554!
2192
    bool first = true;
1,554✔
2193
    for (const auto& addr : ret) {
1,562✔
2194
      if (first) {
1,562✔
2195
        first = false;
1,554✔
2196
      }
1,554✔
2197
      else {
8✔
2198
        LOG(", ");
8!
2199
      }
8✔
2200
      LOG((addr.toString()) << "(" << fmtfloat(speeds[addr] / 1000.0) << "ms)");
1,562!
2201
    }
1,562✔
2202
    LOG(endl);
1,554!
2203
  }
1,554✔
2204
}
12,021✔
2205

2206
template <typename T>
2207
static bool collectAddresses(const vector<DNSRecord>& cset, vector<ComboAddress>& ret)
2208
{
13,279✔
2209
  bool pushed = false;
13,279✔
2210
  for (const auto& record : cset) {
13,564✔
2211
    if (auto rec = getRR<T>(record)) {
13,562✔
2212
      ret.push_back(rec->getCA(53));
13,416✔
2213
      pushed = true;
13,416✔
2214
    }
13,416✔
2215
  }
13,562✔
2216
  return pushed;
13,279✔
2217
}
13,279✔
2218

2219
/** This function explicitly goes out for A or AAAA addresses
2220
 */
2221
vector<ComboAddress> SyncRes::getAddrs(const DNSName& qname, unsigned int depth, const string& prefix, set<GetBestNSAnswer>& beenthere, bool cacheOnly, unsigned int& addressQueriesForNS)
2222
{
12,111✔
2223
  typedef vector<DNSRecord> res_t;
12,111✔
2224
  typedef vector<ComboAddress> ret_t;
12,111✔
2225
  ret_t ret;
12,111✔
2226

2227
  bool oldCacheOnly = setCacheOnly(cacheOnly);
12,111✔
2228
  bool oldRequireAuthData = d_requireAuthData;
12,111✔
2229
  bool oldValidationRequested = d_DNSSECValidationRequested;
12,111✔
2230
  bool oldFollowCNAME = d_followCNAME;
12,111✔
2231
  bool seenV6 = false;
12,111✔
2232
  const unsigned int startqueries = d_outqueries;
12,111✔
2233
  d_requireAuthData = false;
12,111✔
2234
  d_DNSSECValidationRequested = false;
12,111✔
2235
  d_followCNAME = false;
12,111✔
2236

2237
  MemRecursorCache::Flags flags = MemRecursorCache::None;
12,111✔
2238
  if (d_serveStale) {
12,111!
2239
    flags |= MemRecursorCache::ServeStale;
×
2240
  }
×
2241
  try {
12,111✔
2242
    // First look for both A and AAAA in the cache
2243
    res_t cset;
12,111✔
2244
    if (s_doIPv4 && g_recCache->get(d_now.tv_sec, qname, QType::A, flags, &cset, d_cacheRemote, d_routingTag) > 0) {
12,111✔
2245
      collectAddresses<ARecordContent>(cset, ret);
10,483✔
2246
    }
10,483✔
2247
    if (s_doIPv6 && g_recCache->get(d_now.tv_sec, qname, QType::AAAA, flags, &cset, d_cacheRemote, d_routingTag) > 0) {
12,111✔
2248
      if (collectAddresses<AAAARecordContent>(cset, ret)) {
1,362!
2249
        seenV6 = true;
1,362✔
2250
      }
1,362✔
2251
    }
1,362✔
2252
    if (ret.empty()) {
12,111✔
2253
      // Neither A nor AAAA in the cache...
2254
      Context newContext1;
1,597✔
2255
      cset.clear();
1,597✔
2256
      // Go out to get A's
2257
      if (s_doIPv4 && doResolveNoQNameMinimization(qname, QType::A, cset, depth + 1, beenthere, newContext1) == 0) { // this consults cache, OR goes out
1,597✔
2258
        collectAddresses<ARecordContent>(cset, ret);
1,410✔
2259
      }
1,410✔
2260
      if (s_doIPv6) { // s_doIPv6 **IMPLIES** pdns::isQueryLocalAddressFamilyEnabled(AF_INET6) returned true
1,597✔
2261
        if (ret.empty()) {
51✔
2262
          // We only go out immediately to find IPv6 records if we did not find any IPv4 ones.
2263
          Context newContext2;
29✔
2264
          if (doResolveNoQNameMinimization(qname, QType::AAAA, cset, depth + 1, beenthere, newContext2) == 0) { // this consults cache, OR goes out
29✔
2265
            if (collectAddresses<AAAARecordContent>(cset, ret)) {
22!
2266
              seenV6 = true;
×
2267
            }
×
2268
          }
22✔
2269
        }
29✔
2270
        else {
22✔
2271
          // We have some IPv4 records, consult the cache, we might have encountered some IPv6 glue
2272
          cset.clear();
22✔
2273
          if (g_recCache->get(d_now.tv_sec, qname, QType::AAAA, flags, &cset, d_cacheRemote, d_routingTag) > 0) {
22✔
2274
            if (collectAddresses<AAAARecordContent>(cset, ret)) {
2!
2275
              seenV6 = true;
2✔
2276
            }
2✔
2277
          }
2✔
2278
        }
22✔
2279
      }
51✔
2280
    }
1,597✔
2281
    if (s_doIPv6 && !seenV6 && !cacheOnly) {
12,111!
2282
      // No IPv6 records in cache, check negcache and submit async task if negache does not have the data
2283
      // so that the next time the cache or the negcache will have data
2284
      pushResolveIfNotInNegCache(qname, QType::AAAA, d_now);
948✔
2285
    }
948✔
2286
  }
12,111✔
2287
  catch (const PolicyHitException&) {
12,111✔
2288
    // We ignore a policy hit while trying to retrieve the addresses
2289
    // of a NS and keep processing the current query
2290
  }
×
2291

2292
  if (ret.empty() && d_outqueries > startqueries) {
12,111✔
2293
    // We did 1 or more outgoing queries to resolve this NS name but returned empty handed
2294
    addressQueriesForNS++;
77✔
2295
  }
77✔
2296
  d_requireAuthData = oldRequireAuthData;
12,021✔
2297
  d_DNSSECValidationRequested = oldValidationRequested;
12,021✔
2298
  setCacheOnly(oldCacheOnly);
12,021✔
2299
  d_followCNAME = oldFollowCNAME;
12,021✔
2300

2301
  if (s_max_busy_dot_probes > 0 && s_dot_to_port_853) {
12,021!
2302
    for (auto& add : ret) {
×
2303
      if (shouldDoDoT(add, d_now.tv_sec)) {
×
2304
        add.setPort(853);
×
2305
      }
×
2306
    }
×
2307
  }
×
2308
  selectNSOnSpeed(qname, prefix, ret);
12,021✔
2309
  return ret;
12,021✔
2310
}
12,111✔
2311

2312
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)
2313
{
18,109✔
2314
  DNSName subdomain(qname);
18,109✔
2315
  bestns.clear();
18,109✔
2316
  bool brokeloop = false;
18,109✔
2317
  MemRecursorCache::Flags flags = MemRecursorCache::None;
18,109✔
2318
  if (d_serveStale) {
18,109!
2319
    flags |= MemRecursorCache::ServeStale;
×
2320
  }
×
2321
  do {
41,934✔
2322
    if (cutOffDomain && (subdomain == *cutOffDomain || !subdomain.isPartOf(*cutOffDomain))) {
41,934✔
2323
      break;
64✔
2324
    }
64✔
2325
    brokeloop = false;
41,870✔
2326
    LOG(prefix << qname << ": Checking if we have NS in cache for '" << subdomain << "'" << endl);
41,870✔
2327
    vector<DNSRecord> nsVector;
41,870✔
2328
    *flawedNSSet = false;
41,870✔
2329

2330
    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) {
41,870✔
2331
      if (s_maxnsperresolve > 0 && nsVector.size() > s_maxnsperresolve) {
18,084✔
2332
        vector<DNSRecord> selected;
20✔
2333
        selected.reserve(s_maxnsperresolve);
20✔
2334
        std::sample(nsVector.cbegin(), nsVector.cend(), std::back_inserter(selected), s_maxnsperresolve, pdns::dns_random_engine());
20✔
2335
        nsVector = std::move(selected);
20✔
2336
      }
20✔
2337
      bestns.reserve(nsVector.size());
18,083✔
2338

2339
      vector<DNSName> missing;
18,083✔
2340
      for (const auto& nsRecord : nsVector) {
104,152✔
2341
        if (nsRecord.d_ttl > (unsigned int)d_now.tv_sec) {
104,155✔
2342
          vector<DNSRecord> aset;
104,154✔
2343
          QType nsqt{QType::ADDR};
104,154✔
2344
          if (s_doIPv4 && !s_doIPv6) {
104,156✔
2345
            nsqt = QType::A;
89,129✔
2346
          }
89,129✔
2347
          else if (!s_doIPv4 && s_doIPv6) {
2,147,498,671!
2348
            nsqt = QType::AAAA;
59✔
2349
          }
59✔
2350

2351
          auto nrr = getRR<NSRecordContent>(nsRecord);
104,154✔
2352
          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)) {
104,157✔
2353
            bestns.push_back(nsRecord);
103,847✔
2354
            LOG(prefix << qname << ": NS (with ip, or non-glue) in cache for '" << subdomain << "' -> '" << nrr->getNS() << "'");
103,847✔
2355
            LOG(", within bailiwick: " << nrr->getNS().isPartOf(subdomain));
103,847✔
2356
            if (!aset.empty()) {
103,847✔
2357
              LOG(", in cache, ttl=" << (unsigned int)(((time_t)aset.begin()->d_ttl - d_now.tv_sec)) << endl);
4,434!
2358
            }
4,434✔
2359
            else {
99,413✔
2360
              LOG(", not in cache / did not look at cache" << endl);
99,413✔
2361
            }
99,413✔
2362
          }
103,847✔
2363
          else if (nrr != nullptr) {
2,147,483,954✔
2364
            *flawedNSSet = true;
307✔
2365
            LOG(prefix << qname << ": NS in cache for '" << subdomain << "', but needs glue (" << nrr->getNS() << ") which we miss or is expired" << endl);
307!
2366
            missing.emplace_back(nrr->getNS());
307✔
2367
          }
307✔
2368
        }
104,154✔
2369
      }
104,152✔
2370
      if (*flawedNSSet && bestns.empty() && isAuth) {
18,083✔
2371
        // The authoritative (child) NS records did not produce any usable addresses, wipe them, so
2372
        // these useless records do not prevent parent records to be inserted into the cache
2373
        LOG(prefix << qname << ": Wiping flawed authoritative NS records for " << subdomain << endl);
2!
2374
        g_recCache->doWipeCache(subdomain, false, QType::NS);
2✔
2375
      }
2✔
2376
      if (!missing.empty() && missing.size() < nsVector.size()) {
18,083✔
2377
        // We miss glue, but we have a chance to resolve it
2378
        // Pick a few and push async tasks to resolve them
2379
        const unsigned int max = 2;
23✔
2380
        unsigned int counter = 0;
23✔
2381
        shuffle(missing.begin(), missing.end(), pdns::dns_random_engine());
23✔
2382
        for (const auto& name : missing) {
23!
2383
          if (s_doIPv4 && pushResolveIfNotInNegCache(name, QType::A, d_now)) {
23!
2384
            LOG(prefix << qname << ": A glue for " << subdomain << " NS " << name << " missing, pushed task to resolve" << endl);
23!
2385
            counter++;
23✔
2386
          }
23✔
2387
          if (s_doIPv6 && pushResolveIfNotInNegCache(name, QType::AAAA, d_now)) {
23!
2388
            LOG(prefix << qname << ": AAAA glue for " << subdomain << " NS " << name << " missing, pushed task to resolve" << endl);
23!
2389
            counter++;
23✔
2390
          }
23✔
2391
          if (counter >= max) {
23!
2392
            break;
23✔
2393
          }
23✔
2394
        }
23✔
2395
      }
23✔
2396

2397
      if (!bestns.empty()) {
18,085✔
2398
        GetBestNSAnswer answer;
18,048✔
2399
        answer.qname = qname;
18,048✔
2400
        answer.qtype = qtype.getCode();
18,048✔
2401
        for (const auto& bestNSRecord : bestns) {
103,848✔
2402
          if (auto nsContent = getRR<NSRecordContent>(bestNSRecord)) {
103,851✔
2403
            answer.bestns.emplace(bestNSRecord.d_name, nsContent->getNS());
103,849✔
2404
          }
103,849✔
2405
        }
103,848✔
2406

2407
        auto insertionPair = beenthere.insert(std::move(answer));
18,048✔
2408
        if (!insertionPair.second) {
18,048✔
2409
          brokeloop = true;
254✔
2410
          LOG(prefix << qname << ": We have NS in cache for '" << subdomain << "' but part of LOOP (already seen " << insertionPair.first->qname << ")! Trying less specific NS" << endl);
254!
2411
          ;
254✔
2412
          if (doLog()) {
254!
2413
            for (auto j = beenthere.begin(); j != beenthere.end(); ++j) {
×
2414
              bool neo = (j == insertionPair.first);
×
2415
              LOG(prefix << qname << ": Beenthere" << (neo ? "*" : "") << ": " << j->qname << "|" << DNSRecordContent::NumberToType(j->qtype) << " (" << (unsigned int)j->bestns.size() << ")" << endl);
×
2416
            }
×
2417
          }
×
2418
          bestns.clear();
254✔
2419
        }
254✔
2420
        else {
17,794✔
2421
          LOG(prefix << qname << ": We have NS in cache for '" << subdomain << "' (flawedNSSet=" << *flawedNSSet << ")" << endl);
17,794✔
2422
          return;
17,794✔
2423
        }
17,794✔
2424
      }
18,048✔
2425
    }
18,083✔
2426
    LOG(prefix << qname << ": No valid/useful NS in cache for '" << subdomain << "'" << endl);
24,076✔
2427

2428
    if (subdomain.isRoot() && !brokeloop) {
24,076✔
2429
      // We lost the root NS records
2430
      primeHints();
150✔
2431
      LOG(prefix << qname << ": Reprimed the root" << endl);
150!
2432
      /* let's prevent an infinite loop */
2433
      if (!d_updatingRootNS) {
150✔
2434
        auto log = g_slog->withName("housekeeping");
143✔
2435
        getRootNS(d_now, d_asyncResolve, depth, log);
143✔
2436
      }
143✔
2437
    }
150✔
2438
  } while (subdomain.chopOff());
24,076✔
2439
}
18,109✔
2440

2441
SyncRes::domainmap_t::const_iterator SyncRes::getBestAuthZone(DNSName* qname)
2442
{
105,440✔
2443
  if (t_sstorage.domainmap->empty()) {
105,440✔
2444
    return t_sstorage.domainmap->end();
5,637✔
2445
  }
5,637✔
2446

2447
  SyncRes::domainmap_t::const_iterator ret;
99,803✔
2448
  do {
360,206✔
2449
    ret = t_sstorage.domainmap->find(*qname);
360,206✔
2450
    if (ret != t_sstorage.domainmap->end()) {
360,206✔
2451
      break;
1,859✔
2452
    }
1,859✔
2453
  } while (qname->chopOff());
360,206✔
2454
  return ret;
×
2455
}
105,440✔
2456

2457
/** doesn't actually do the work, leaves that to getBestNSFromCache */
2458
DNSName SyncRes::getBestNSNamesFromCache(const DNSName& qname, const QType qtype, NsSet& nsset, bool* flawedNSSet, unsigned int depth, const string& prefix, set<GetBestNSAnswer>& beenthere)
2459
{
10,272✔
2460
  DNSName authOrForwDomain(qname);
10,272✔
2461

2462
  auto iter = getBestAuthZone(&authOrForwDomain);
10,272✔
2463
  // We have an auth, forwarder of forwarder-recurse
2464
  if (iter != t_sstorage.domainmap->end()) {
10,272✔
2465
    if (iter->second.isAuth()) {
266✔
2466
      // this gets picked up in doResolveAt, the empty DNSName, combined with the
2467
      // empty vector means 'we are auth for this zone'
2468
      nsset.insert({DNSName(), {{}, false}});
130✔
2469
      return authOrForwDomain;
130✔
2470
    }
130✔
2471
    if (iter->second.shouldRecurse()) {
136✔
2472
      // Again, picked up in doResolveAt. An empty DNSName, combined with a
2473
      // non-empty vector of ComboAddresses means 'this is a forwarded domain'
2474
      // This is actually picked up in retrieveAddressesForNS called from doResolveAt.
2475
      nsset.insert({DNSName(), {iter->second.d_servers, true}});
60✔
2476
      return authOrForwDomain;
60✔
2477
    }
60✔
2478
  }
136✔
2479

2480
  // We might have a (non-recursive) forwarder, but maybe the cache already contains
2481
  // a better NS
2482
  vector<DNSRecord> bestns;
10,082✔
2483
  DNSName nsFromCacheDomain(g_rootdnsname);
10,082✔
2484
  getBestNSFromCache(qname, qtype, bestns, flawedNSSet, depth, prefix, beenthere);
10,082✔
2485

2486
  // Pick up the auth domain
2487
  for (const auto& nsRecord : bestns) {
10,082✔
2488
    const auto nsContent = getRR<NSRecordContent>(nsRecord);
9,978✔
2489
    if (nsContent) {
9,978!
2490
      nsFromCacheDomain = nsRecord.d_name;
9,978✔
2491
      break;
9,978✔
2492
    }
9,978✔
2493
  }
9,978✔
2494

2495
  if (iter != t_sstorage.domainmap->end()) {
10,082✔
2496
    if (doLog()) {
76✔
2497
      LOG(prefix << qname << " authOrForwDomain: " << authOrForwDomain << " nsFromCacheDomain: " << nsFromCacheDomain << " isPartof: " << authOrForwDomain.isPartOf(nsFromCacheDomain) << endl);
64!
2498
    }
64✔
2499

2500
    // If the forwarder is better or equal to what's found in the cache, use forwarder. Note that name.isPartOf(name).
2501
    // So queries that get NS for authOrForwDomain itself go to the forwarder
2502
    if (authOrForwDomain.isPartOf(nsFromCacheDomain)) {
76!
2503
      if (doLog()) {
76✔
2504
        LOG(prefix << qname << ": Using forwarder as NS" << endl);
64!
2505
      }
64✔
2506
      nsset.insert({DNSName(), {iter->second.d_servers, false}});
76✔
2507
      return authOrForwDomain;
76✔
2508
    }
76✔
2509
    if (doLog()) {
×
2510
      LOG(prefix << qname << ": Using NS from cache" << endl);
×
2511
    }
×
2512
  }
×
2513
  for (const auto& bestn : bestns) {
61,571✔
2514
    // The actual resolver code will not even look at the ComboAddress or bool
2515
    const auto nsContent = getRR<NSRecordContent>(bestn);
61,571✔
2516
    if (nsContent) {
61,573✔
2517
      nsset.insert({nsContent->getNS(), {{}, false}});
61,572✔
2518
    }
61,572✔
2519
  }
61,571✔
2520
  return nsFromCacheDomain;
10,006✔
2521
}
10,082✔
2522

2523
void SyncRes::updateValidationStatusInCache(const DNSName& qname, const QType qtype, bool aaFlag, vState newState) const
2524
{
67✔
2525
  if (qtype == QType::ANY || qtype == QType::ADDR) {
67!
2526
    // not doing that
2527
    return;
×
2528
  }
×
2529

2530
  if (vStateIsBogus(newState)) {
67✔
2531
    g_recCache->updateValidationStatus(d_now.tv_sec, qname, qtype, d_cacheRemote, d_routingTag, aaFlag, newState, s_maxbogusttl + d_now.tv_sec);
20✔
2532
  }
20✔
2533
  else {
47✔
2534
    g_recCache->updateValidationStatus(d_now.tv_sec, qname, qtype, d_cacheRemote, d_routingTag, aaFlag, newState, boost::none);
47✔
2535
  }
47✔
2536
}
67✔
2537

2538
static pair<bool, unsigned int> scanForCNAMELoop(const DNSName& name, const vector<DNSRecord>& records)
2539
{
3,628✔
2540
  unsigned int numCNames = 0;
3,628✔
2541
  for (const auto& record : records) {
9,020✔
2542
    if (record.d_type == QType::CNAME && record.d_place == DNSResourceRecord::ANSWER) {
9,020✔
2543
      ++numCNames;
8,824✔
2544
      if (name == record.d_name) {
8,824✔
2545
        return {true, numCNames};
6✔
2546
      }
6✔
2547
    }
8,824✔
2548
  }
9,020✔
2549
  return {false, numCNames};
3,622✔
2550
}
3,628✔
2551

2552
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)
2553
{
23,277✔
2554
  vector<DNSRecord> cset;
23,277✔
2555
  MemRecursorCache::SigRecs signatures = MemRecursorCache::s_emptySigRecs;
23,277✔
2556
  MemRecursorCache::AuthRecs authorityRecs = MemRecursorCache::s_emptyAuthRecs;
23,277✔
2557
  bool wasAuth = false;
23,277✔
2558
  uint32_t capTTL = std::numeric_limits<uint32_t>::max();
23,277✔
2559
  DNSName foundName;
23,277✔
2560
  DNSName authZone;
23,277✔
2561
  QType foundQT = QType::ENT;
23,277✔
2562

2563
  /* we don't require auth data for forward-recurse lookups */
2564
  MemRecursorCache::Flags flags = MemRecursorCache::None;
23,277✔
2565
  if (!wasForwardRecurse && d_requireAuthData) {
23,277✔
2566
    flags |= MemRecursorCache::RequireAuth;
21,556✔
2567
  }
21,556✔
2568
  if (d_refresh) {
23,277✔
2569
    flags |= MemRecursorCache::Refresh;
310✔
2570
  }
310✔
2571
  if (d_serveStale) {
23,277✔
2572
    flags |= MemRecursorCache::ServeStale;
20✔
2573
  }
20✔
2574
  if (g_recCache->get(d_now.tv_sec, qname, QType::CNAME, flags, &cset, d_cacheRemote, d_routingTag, d_doDNSSEC ? &signatures : nullptr, d_doDNSSEC ? &authorityRecs : nullptr, &d_wasVariable, &context.state, &wasAuth, &authZone, &d_fromAuthIP) > 0) {
2,147,490,490✔
2575
    foundName = qname;
2,405✔
2576
    foundQT = QType::CNAME;
2,405✔
2577
  }
2,405✔
2578

2579
  if (foundName.empty() && qname != g_rootdnsname) {
23,277✔
2580
    // look for a DNAME cache hit
2581
    auto labels = qname.getRawLabels();
19,809✔
2582
    DNSName dnameName(g_rootdnsname);
19,809✔
2583

2584
    do {
56,660✔
2585
      dnameName.prependRawLabel(labels.back());
56,660✔
2586
      labels.pop_back();
56,660✔
2587
      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
56,660✔
2588
        break;
19,791✔
2589
      }
19,791✔
2590
      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) {
36,869✔
2591
        foundName = std::move(dnameName);
16✔
2592
        foundQT = QType::DNAME;
16✔
2593
        break;
16✔
2594
      }
16✔
2595
    } while (!labels.empty());
36,869✔
2596
  }
19,809✔
2597

2598
  if (foundName.empty()) {
23,277✔
2599
    return false;
20,856✔
2600
  }
20,856✔
2601

2602
  if (qtype == QType::DS && authZone == qname) {
2,421✔
2603
    /* CNAME at APEX of the child zone, we can't use that to prove that
2604
       there is no DS */
2605
    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!
2606
    return false;
4✔
2607
  }
4✔
2608

2609
  for (auto const& record : cset) {
2,419✔
2610
    if (record.d_class != QClass::IN) {
2,417!
2611
      continue;
×
2612
    }
×
2613

2614
    if (record.d_ttl > (unsigned int)d_now.tv_sec) {
2,417✔
2615

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

2619
        vState recordState = getValidationStatus(foundName, !signatures->empty(), qtype == QType::DS, depth, prefix);
8✔
2620
        if (recordState == vState::Secure) {
8✔
2621
          LOG(prefix << qname << ": Got vState::Indeterminate state from the " << foundQT.toString() << " cache, validating.." << endl);
6!
2622
          context.state = SyncRes::validateRecordsWithSigs(depth, prefix, qname, qtype, foundName, foundQT, cset, *signatures);
6✔
2623
          if (context.state != vState::Indeterminate) {
6!
2624
            LOG(prefix << qname << ": Got vState::Indeterminate state from the " << foundQT.toString() << " cache, new validation result is " << context.state << endl);
6!
2625
            if (vStateIsBogus(context.state)) {
6✔
2626
              capTTL = s_maxbogusttl;
4✔
2627
            }
4✔
2628
            updateValidationStatusInCache(foundName, foundQT, wasAuth, context.state);
6✔
2629
          }
6✔
2630
        }
6✔
2631
      }
8✔
2632

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

2635
      DNSRecord dnsRecord = record;
2,416✔
2636
      auto alreadyPresent = false;
2,416✔
2637

2638
      if (checkForDups) {
2,416✔
2639
        // This can happen on the 2nd iteration of the servestale loop, where the first iteration
2640
        // added a C/DNAME record, but the target resolve failed
2641
        for (const auto& dnsrec : ret) {
6✔
2642
          if (dnsrec.d_type == foundQT && dnsrec.d_name == record.d_name) {
2!
2643
            alreadyPresent = true;
2✔
2644
            break;
2✔
2645
          }
2✔
2646
        }
2✔
2647
      }
6✔
2648
      dnsRecord.d_ttl -= d_now.tv_sec;
2,416✔
2649
      dnsRecord.d_ttl = std::min(dnsRecord.d_ttl, capTTL);
2,416✔
2650
      const uint32_t ttl = dnsRecord.d_ttl;
2,416✔
2651
      if (!alreadyPresent) {
2,417✔
2652
        ret.reserve(ret.size() + 2 + signatures->size() + authorityRecs->size());
2,414✔
2653
        ret.push_back(dnsRecord);
2,414✔
2654

2655
        for (const auto& signature : *signatures) {
2,414✔
2656
          DNSRecord sigdr;
45✔
2657
          sigdr.d_type = QType::RRSIG;
45✔
2658
          sigdr.d_name = foundName;
45✔
2659
          sigdr.d_ttl = ttl;
45✔
2660
          sigdr.setContent(signature);
45✔
2661
          sigdr.d_place = DNSResourceRecord::ANSWER;
45✔
2662
          sigdr.d_class = QClass::IN;
45✔
2663
          ret.push_back(std::move(sigdr));
45✔
2664
        }
45✔
2665

2666
        for (const auto& rec : *authorityRecs) {
2,414!
2667
          DNSRecord authDR(rec);
×
2668
          authDR.d_ttl = ttl;
×
2669
          ret.push_back(std::move(authDR));
×
2670
        }
×
2671
      }
2,414✔
2672

2673
      DNSName newTarget;
2,416✔
2674
      if (foundQT == QType::DNAME) {
2,416✔
2675
        if (qtype == QType::DNAME && qname == foundName) { // client wanted the DNAME, no need to synthesize a CNAME
16!
2676
          res = RCode::NoError;
2✔
2677
          return true;
2✔
2678
        }
2✔
2679
        // Synthesize a CNAME
2680
        auto dnameRR = getRR<DNAMERecordContent>(record);
14✔
2681
        if (dnameRR == nullptr) {
14!
2682
          throw ImmediateServFailException("Unable to get record content for " + foundName.toLogString() + "|DNAME cache entry");
×
2683
        }
×
2684
        const auto& dnameSuffix = dnameRR->getTarget();
14✔
2685
        DNSName targetPrefix = qname.makeRelative(foundName);
14✔
2686
        try {
14✔
2687
          dnsRecord.d_type = QType::CNAME;
14✔
2688
          dnsRecord.d_name = targetPrefix + foundName;
14✔
2689
          newTarget = targetPrefix + dnameSuffix;
14✔
2690
          dnsRecord.setContent(std::make_shared<CNAMERecordContent>(CNAMERecordContent(newTarget)));
14✔
2691
          ret.push_back(dnsRecord);
14✔
2692
        }
14✔
2693
        catch (const std::exception& e) {
14✔
2694
          // We should probably catch an std::range_error here and set the rcode to YXDOMAIN (RFC 6672, section 2.2)
2695
          // But this is consistent with processRecords
2696
          throw ImmediateServFailException("Unable to perform DNAME substitution(DNAME owner: '" + foundName.toLogString() + "', DNAME target: '" + dnameSuffix.toLogString() + "', substituted name: '" + targetPrefix.toLogString() + "." + dnameSuffix.toLogString() + "' : " + e.what());
×
2697
        }
×
2698

2699
        LOG(prefix << qname << ": Synthesized " << dnsRecord.d_name << "|CNAME " << newTarget << endl);
14!
2700
      }
14✔
2701

2702
      if (qtype == QType::CNAME) { // perhaps they really wanted a CNAME!
2,414✔
2703
        res = RCode::NoError;
2✔
2704
        return true;
2✔
2705
      }
2✔
2706

2707
      if (qtype == QType::DS || qtype == QType::DNSKEY) {
2,413!
2708
        res = RCode::NoError;
18✔
2709
        return true;
18✔
2710
      }
18✔
2711

2712
      // We have a DNAME _or_ CNAME cache hit and the client wants something else than those two.
2713
      // Let's find the answer!
2714
      if (foundQT == QType::CNAME) {
2,395✔
2715
        const auto cnameContent = getRR<CNAMERecordContent>(record);
2,382✔
2716
        if (cnameContent == nullptr) {
2,382!
2717
          throw ImmediateServFailException("Unable to get record content for " + foundName.toLogString() + "|CNAME cache entry");
×
2718
        }
×
2719
        newTarget = cnameContent->getTarget();
2,382✔
2720
      }
2,382✔
2721

2722
      if (qname == newTarget) {
2,394✔
2723
        string msg = "Got a CNAME referral (from cache) to self";
2✔
2724
        LOG(prefix << qname << ": " << msg << endl);
2!
2725
        throw ImmediateServFailException(std::move(msg));
2✔
2726
      }
2✔
2727

2728
      if (newTarget.isPartOf(qname)) {
2,392✔
2729
        // a.b.c. CNAME x.a.b.c will go to great depths with QM on
2730
        string msg = "Got a CNAME referral (from cache) to child, disabling QM";
40✔
2731
        LOG(prefix << qname << ": " << msg << endl);
40!
2732
        setQNameMinimization(false);
40✔
2733
      }
40✔
2734

2735
      if (!d_followCNAME) {
2,392✔
2736
        res = RCode::NoError;
167✔
2737
        return true;
167✔
2738
      }
167✔
2739

2740
      // Check to see if we already have seen the new target as a previous target or that we have a very long CNAME chain
2741
      const auto [CNAMELoop, numCNAMEs] = scanForCNAMELoop(newTarget, ret);
2,225✔
2742
      if (CNAMELoop) {
2,225✔
2743
        string msg = "got a CNAME referral (from cache) that causes a loop";
4✔
2744
        LOG(prefix << qname << ": Status=" << msg << endl);
4!
2745
        throw ImmediateServFailException(std::move(msg));
4✔
2746
      }
4✔
2747
      if (numCNAMEs > s_max_CNAMES_followed) {
2,221✔
2748
        string msg = "max number of CNAMEs exceeded";
4✔
2749
        LOG(prefix << qname << ": Status=" << msg << endl);
4!
2750
        throw ImmediateServFailException(std::move(msg));
4✔
2751
      }
4✔
2752

2753
      set<GetBestNSAnswer> beenthere;
2,217✔
2754
      Context cnameContext;
2,217✔
2755
      // Be aware that going out on the network might be disabled (cache-only), for example because we are in QM Step0,
2756
      // so you can't trust that a real lookup will have been made.
2757
      res = doResolve(newTarget, qtype, ret, depth + 1, beenthere, cnameContext);
2,217✔
2758
      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,217✔
2759
      pdns::dedupRecords(ret); // multiple NSECS could have been added, #14120
2,217✔
2760
      updateValidationState(qname, context.state, cnameContext.state, prefix);
2,217✔
2761

2762
      return true;
2,217✔
2763
    }
2,221✔
2764
  }
2,417✔
2765
  throw ImmediateServFailException("Could not determine whether or not there was a CNAME or DNAME in cache for '" + qname.toLogString() + "'");
2,147,483,647✔
2766
}
2,417✔
2767

2768
namespace
2769
{
2770
struct CacheEntry
2771
{
2772
  vector<DNSRecord> records;
2773
  MemRecursorCache::SigRecsVec signatures;
2774
  time_t d_ttl_time{0};
2775
  uint32_t signaturesTTL{std::numeric_limits<uint32_t>::max()};
2776
};
2777
struct CacheKey
2778
{
2779
  DNSName name;
2780
  QType type;
2781
  DNSResourceRecord::Place place;
2782
  bool operator<(const CacheKey& rhs) const
2783
  {
386,537✔
2784
    return std::tie(type, place, name) < std::tie(rhs.type, rhs.place, rhs.name);
386,537✔
2785
  }
386,537✔
2786
};
2787
using tcache_t = map<CacheKey, CacheEntry>;
2788
}
2789

2790
static void reapRecordsFromNegCacheEntryForValidation(tcache_t& tcache, const vector<DNSRecord>& records)
2791
{
32✔
2792
  for (const auto& rec : records) {
32✔
2793
    if (rec.d_type == QType::RRSIG) {
22✔
2794
      auto rrsig = getRR<RRSIGRecordContent>(rec);
10✔
2795
      if (rrsig) {
10!
2796
        tcache[{rec.d_name, rrsig->d_type, rec.d_place}].signatures.push_back(rrsig);
10✔
2797
      }
10✔
2798
    }
10✔
2799
    else {
12✔
2800
      tcache[{rec.d_name, rec.d_type, rec.d_place}].records.push_back(rec);
12✔
2801
    }
12✔
2802
  }
22✔
2803
}
32✔
2804

2805
static bool negativeCacheEntryHasSOA(const NegCache::NegCacheEntry& negEntry)
2806
{
16✔
2807
  return !negEntry.authoritySOA.records.empty();
16✔
2808
}
16✔
2809

2810
static void reapRecordsForValidation(std::map<QType, CacheEntry>& entries, const vector<DNSRecord>& records)
2811
{
3✔
2812
  for (const auto& rec : records) {
5✔
2813
    entries[rec.d_type].records.push_back(rec);
5✔
2814
  }
5✔
2815
}
3✔
2816

2817
static void reapSignaturesForValidation(std::map<QType, CacheEntry>& entries, const MemRecursorCache::SigRecs& signatures)
2818
{
3✔
2819
  for (const auto& sig : *signatures) {
5✔
2820
    entries[sig->d_type].signatures.push_back(sig);
5✔
2821
  }
5✔
2822
}
3✔
2823

2824
/*!
2825
 * Convenience function to push the records from records into ret with a new TTL
2826
 *
2827
 * \param records DNSRecords that need to go into ret
2828
 * \param ttl     The new TTL for these records
2829
 * \param ret     The vector of DNSRecords that should contain the records with the modified TTL
2830
 */
2831
static void addTTLModifiedRecords(vector<DNSRecord>& records, const uint32_t ttl, vector<DNSRecord>& ret)
2832
{
13,111✔
2833
  for (auto& rec : records) {
13,111✔
2834
    rec.d_ttl = ttl;
10,904✔
2835
    ret.push_back(std::move(rec));
10,904✔
2836
  }
10,904✔
2837
}
13,111✔
2838

2839
void SyncRes::computeNegCacheValidationStatus(const NegCache::NegCacheEntry& negEntry, const DNSName& qname, const QType qtype, const int res, vState& state, unsigned int depth, const string& prefix)
2840
{
8✔
2841
  tcache_t tcache;
8✔
2842
  reapRecordsFromNegCacheEntryForValidation(tcache, negEntry.authoritySOA.records);
8✔
2843
  reapRecordsFromNegCacheEntryForValidation(tcache, negEntry.authoritySOA.signatures);
8✔
2844
  reapRecordsFromNegCacheEntryForValidation(tcache, negEntry.DNSSECRecords.records);
8✔
2845
  reapRecordsFromNegCacheEntryForValidation(tcache, negEntry.DNSSECRecords.signatures);
8✔
2846

2847
  for (const auto& entry : tcache) {
12✔
2848
    // this happens when we did store signatures, but passed on the records themselves
2849
    if (entry.second.records.empty()) {
12!
2850
      continue;
×
2851
    }
×
2852

2853
    const DNSName& owner = entry.first.name;
12✔
2854

2855
    vState recordState = getValidationStatus(owner, !entry.second.signatures.empty(), qtype == QType::DS, depth, prefix);
12✔
2856
    if (state == vState::Indeterminate) {
12✔
2857
      state = recordState;
8✔
2858
    }
8✔
2859

2860
    if (recordState == vState::Secure) {
12✔
2861
      recordState = SyncRes::validateRecordsWithSigs(depth, prefix, qname, qtype, owner, QType(entry.first.type), entry.second.records, entry.second.signatures);
10✔
2862
    }
10✔
2863

2864
    if (recordState != vState::Indeterminate && recordState != state) {
12!
2865
      updateValidationState(qname, state, recordState, prefix);
×
2866
      if (state != vState::Secure) {
×
2867
        break;
×
2868
      }
×
2869
    }
×
2870
  }
12✔
2871

2872
  if (state == vState::Secure) {
8✔
2873
    vState neValidationState = negEntry.d_validationState;
6✔
2874
    dState expectedState = res == RCode::NXDomain ? dState::NXDOMAIN : dState::NXQTYPE;
6!
2875
    dState denialState = getDenialValidationState(negEntry, expectedState, false, prefix);
6✔
2876
    updateDenialValidationState(qname, neValidationState, negEntry.d_name, state, denialState, expectedState, qtype == QType::DS, depth, prefix);
6✔
2877
  }
6✔
2878
  if (state != vState::Indeterminate) {
8!
2879
    /* validation succeeded, let's update the cache entry so we don't have to validate again */
2880
    boost::optional<time_t> capTTD = boost::none;
8✔
2881
    if (vStateIsBogus(state)) {
8✔
2882
      capTTD = d_now.tv_sec + s_maxbogusttl;
2✔
2883
    }
2✔
2884
    g_negCache->updateValidationStatus(negEntry.d_name, negEntry.d_qtype, state, capTTD);
8✔
2885
  }
8✔
2886
}
8✔
2887

2888
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)
2889
{
26,205✔
2890
  bool giveNegative = false;
26,205✔
2891

2892
  // 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)
2893
  DNSName sqname(qname);
26,205✔
2894
  QType sqt(qtype);
26,205✔
2895
  uint32_t sttl = 0;
26,205✔
2896
  //  cout<<"Lookup for '"<<qname<<"|"<<qtype.toString()<<"' -> "<<getLastLabel(qname)<<endl;
2897
  vState cachedState{};
26,205✔
2898
  NegCache::NegCacheEntry negEntry;
26,205✔
2899

2900
  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.
26,205!
2901
    sttl = negEntry.d_ttd - d_now.tv_sec;
4✔
2902
    LOG(prefix << qname << ": Entire name '" << qname << "', is negatively cached via '" << negEntry.d_auth << "' & '" << negEntry.d_name << "' for another " << sttl << " seconds" << endl);
4!
2903
    res = RCode::NXDomain;
4✔
2904
    giveNegative = true;
4✔
2905
    cachedState = negEntry.d_validationState;
4✔
2906
    if (s_addExtendedResolutionDNSErrors) {
4✔
2907
      context.extendedError = EDNSExtendedError{static_cast<uint16_t>(EDNSExtendedError::code::Synthesized), "Result synthesized by root-nx-trust"};
2✔
2908
    }
2✔
2909
  }
4✔
2910
  else if (g_negCache->get(qname, qtype, d_now, negEntry, false, d_serveStale, d_refresh)) {
26,201✔
2911
    /* If we are looking for a DS, discard NXD if auth == qname
2912
       and ask for a specific denial instead */
2913
    if (qtype != QType::DS || negEntry.d_qtype.getCode() != 0 || negEntry.d_auth != qname || g_negCache->get(qname, qtype, d_now, negEntry, true, d_serveStale, d_refresh)) {
3,278!
2914
      /* 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
2915
         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
2916
         query. */
2917
      if (qtype == QType::DS && negEntry.d_qtype.getCode() != 0 && !d_externalDSQuery.empty() && qname == d_externalDSQuery && !negativeCacheEntryHasSOA(negEntry)) {
3,278✔
2918
        giveNegative = false;
6✔
2919
      }
6✔
2920
      else {
3,272✔
2921
        res = RCode::NXDomain;
3,272✔
2922
        sttl = negEntry.d_ttd - d_now.tv_sec;
3,272✔
2923
        giveNegative = true;
3,272✔
2924
        cachedState = negEntry.d_validationState;
3,272✔
2925
        if (negEntry.d_qtype.getCode() != 0) {
3,272✔
2926
          LOG(prefix << qname << "|" << qtype << ": Is negatively cached via '" << negEntry.d_auth << "' for another " << sttl << " seconds" << endl);
3,232!
2927
          res = RCode::NoError;
3,232✔
2928
          if (s_addExtendedResolutionDNSErrors) {
3,232✔
2929
            context.extendedError = EDNSExtendedError{static_cast<uint16_t>(EDNSExtendedError::code::Synthesized), "Result from negative cache"};
3,104✔
2930
          }
3,104✔
2931
        }
3,232✔
2932
        else {
40✔
2933
          LOG(prefix << qname << ": Entire name '" << qname << "' is negatively cached via '" << negEntry.d_auth << "' for another " << sttl << " seconds" << endl);
40!
2934
          if (s_addExtendedResolutionDNSErrors) {
40✔
2935
            context.extendedError = EDNSExtendedError{static_cast<uint16_t>(EDNSExtendedError::code::Synthesized), "Result from negative cache for entire name"};
28✔
2936
          }
28✔
2937
        }
40✔
2938
      }
3,272✔
2939
    }
3,278✔
2940
  }
3,278✔
2941
  else if (s_hardenNXD != HardenNXD::No && !qname.isRoot() && !wasForwardedOrAuthZone) {
22,923✔
2942
    auto labels = qname.getRawLabels();
21,280✔
2943
    DNSName negCacheName(g_rootdnsname);
21,280✔
2944
    negCacheName.prependRawLabel(labels.back());
21,280✔
2945
    labels.pop_back();
21,280✔
2946
    while (!labels.empty()) {
56,163✔
2947
      if (g_negCache->get(negCacheName, QType::ENT, d_now, negEntry, true, d_serveStale, d_refresh)) {
34,914✔
2948
        if (negEntry.d_validationState == vState::Indeterminate && validationEnabled()) {
35!
2949
          // LOG(prefix << negCacheName <<  " negatively cached and vState::Indeterminate, trying to validate NXDOMAIN" << endl);
2950
          // ...
2951
          // And get the updated ne struct
2952
          // t_sstorage.negcache.get(negCacheName, QType(0), d_now, ne, true);
2953
        }
×
2954
        if ((s_hardenNXD == HardenNXD::Yes && !vStateIsBogus(negEntry.d_validationState)) || negEntry.d_validationState == vState::Secure) {
35!
2955
          res = RCode::NXDomain;
31✔
2956
          sttl = negEntry.d_ttd - d_now.tv_sec;
31✔
2957
          giveNegative = true;
31✔
2958
          cachedState = negEntry.d_validationState;
31✔
2959
          LOG(prefix << qname << ": Name '" << negCacheName << "' and below, is negatively cached via '" << negEntry.d_auth << "' for another " << sttl << " seconds" << endl);
31!
2960
          if (s_addExtendedResolutionDNSErrors) {
31✔
2961
            context.extendedError = EDNSExtendedError{static_cast<uint16_t>(EDNSExtendedError::code::Synthesized), "Result synthesized by nothing-below-nxdomain (RFC8020)"};
15✔
2962
          }
15✔
2963
          break;
31✔
2964
        }
31✔
2965
      }
35✔
2966
      negCacheName.prependRawLabel(labels.back());
34,883✔
2967
      labels.pop_back();
34,883✔
2968
    }
34,883✔
2969
  }
21,280✔
2970

2971
  if (giveNegative) {
26,205✔
2972

2973
    context.state = cachedState;
3,307✔
2974

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

2979
      if (context.state != cachedState && vStateIsBogus(context.state)) {
8!
2980
        sttl = std::min(sttl, s_maxbogusttl);
2✔
2981
      }
2✔
2982
    }
8✔
2983

2984
    // Transplant SOA to the returned packet
2985
    addTTLModifiedRecords(negEntry.authoritySOA.records, sttl, ret);
3,307✔
2986
    if (d_doDNSSEC) {
3,307✔
2987
      addTTLModifiedRecords(negEntry.authoritySOA.signatures, sttl, ret);
3,268✔
2988
      addTTLModifiedRecords(negEntry.DNSSECRecords.records, sttl, ret);
3,268✔
2989
      addTTLModifiedRecords(negEntry.DNSSECRecords.signatures, sttl, ret);
3,268✔
2990
    }
3,268✔
2991

2992
    LOG(prefix << qname << ": Updating validation state with negative cache content for " << qname << " to " << context.state << endl);
3,307!
2993
    return true;
3,307✔
2994
  }
3,307✔
2995

2996
  vector<DNSRecord> cset;
22,898✔
2997
  bool found = false;
22,898✔
2998
  bool expired = false;
22,898✔
2999
  MemRecursorCache::SigRecs signatures = MemRecursorCache::s_emptySigRecs;
22,898✔
3000
  MemRecursorCache::AuthRecs authorityRecs = MemRecursorCache::s_emptyAuthRecs;
22,898✔
3001
  uint32_t ttl = 0;
22,898✔
3002
  uint32_t capTTL = std::numeric_limits<uint32_t>::max();
22,898✔
3003
  bool wasCachedAuth{};
22,898✔
3004
  MemRecursorCache::Flags flags = MemRecursorCache::None;
22,898✔
3005
  if (!wasForwardRecurse && d_requireAuthData) {
22,898✔
3006
    flags |= MemRecursorCache::RequireAuth;
21,188✔
3007
  }
21,188✔
3008
  if (d_serveStale) {
22,898✔
3009
    flags |= MemRecursorCache::ServeStale;
10✔
3010
  }
10✔
3011
  if (d_refresh) {
22,898✔
3012
    flags |= MemRecursorCache::Refresh;
310✔
3013
  }
310✔
3014
  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) {
22,898✔
3015

3016
    LOG(prefix << sqname << ": Found cache hit for " << sqt.toString() << ": ");
6,593✔
3017

3018
    if (!wasAuthZone && shouldValidate() && (wasCachedAuth || wasForwardRecurse) && cachedState == vState::Indeterminate && d_requireAuthData) {
6,593!
3019

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

3023
      if (recordState == vState::Secure) {
56✔
3024
        LOG(prefix << sqname << ": Got vState::Indeterminate state from the cache, validating.." << endl);
48!
3025
        if (sqt == QType::DNSKEY && sqname == getSigner(*signatures)) {
48✔
3026
          cachedState = validateDNSKeys(sqname, cset, *signatures, depth, prefix);
2✔
3027
        }
2✔
3028
        else {
46✔
3029
          if (sqt == QType::ANY) {
46✔
3030
            std::map<QType, CacheEntry> types;
3✔
3031
            reapRecordsForValidation(types, cset);
3✔
3032
            reapSignaturesForValidation(types, signatures);
3✔
3033

3034
            for (const auto& type : types) {
5✔
3035
              vState cachedRecordState{};
5✔
3036
              if (type.first == QType::DNSKEY && sqname == getSigner(type.second.signatures)) {
5!
3037
                cachedRecordState = validateDNSKeys(sqname, type.second.records, type.second.signatures, depth, prefix);
×
3038
              }
×
3039
              else {
5✔
3040
                cachedRecordState = SyncRes::validateRecordsWithSigs(depth, prefix, qname, qtype, sqname, type.first, type.second.records, type.second.signatures);
5✔
3041
              }
5✔
3042
              updateDNSSECValidationState(cachedState, cachedRecordState);
5✔
3043
            }
5✔
3044
          }
3✔
3045
          else {
43✔
3046
            cachedState = SyncRes::validateRecordsWithSigs(depth, prefix, qname, qtype, sqname, sqt, cset, *signatures);
43✔
3047
          }
43✔
3048
        }
46✔
3049
      }
48✔
3050
      else {
8✔
3051
        cachedState = recordState;
8✔
3052
      }
8✔
3053

3054
      if (cachedState != vState::Indeterminate) {
56!
3055
        LOG(prefix << qname << ": Got vState::Indeterminate state from the cache, validation result is " << cachedState << endl);
56!
3056
        if (vStateIsBogus(cachedState)) {
56✔
3057
          capTTL = s_maxbogusttl;
10✔
3058
        }
10✔
3059
        if (sqt != QType::ANY && sqt != QType::ADDR) {
56!
3060
          updateValidationStatusInCache(sqname, sqt, wasCachedAuth, cachedState);
53✔
3061
        }
53✔
3062
      }
56✔
3063
    }
56✔
3064

3065
    for (auto j = cset.cbegin(); j != cset.cend(); ++j) {
16,428✔
3066

3067
      LOG(j->getContent()->getZoneRepresentation());
9,835✔
3068

3069
      if (j->d_class != QClass::IN) {
9,835!
3070
        continue;
×
3071
      }
×
3072

3073
      if (j->d_ttl > (unsigned int)d_now.tv_sec) {
9,835!
3074
        DNSRecord dnsRecord = *j;
9,835✔
3075
        dnsRecord.d_ttl -= d_now.tv_sec;
9,835✔
3076
        dnsRecord.d_ttl = std::min(dnsRecord.d_ttl, capTTL);
9,835✔
3077
        ttl = dnsRecord.d_ttl;
9,835✔
3078
        ret.push_back(dnsRecord);
9,835✔
3079
        LOG("[ttl=" << dnsRecord.d_ttl << "] ");
9,835✔
3080
        found = true;
9,835✔
3081
      }
9,835✔
UNCOV
3082
      else {
×
UNCOV
3083
        LOG("[expired] ");
×
UNCOV
3084
        expired = true;
×
UNCOV
3085
      }
×
3086
    }
9,835✔
3087

3088
    ret.reserve(ret.size() + signatures->size() + authorityRecs->size());
6,593✔
3089

3090
    for (const auto& signature : *signatures) {
6,593✔
3091
      DNSRecord dnsRecord;
6,140✔
3092
      dnsRecord.d_type = QType::RRSIG;
6,140✔
3093
      dnsRecord.d_name = sqname;
6,140✔
3094
      dnsRecord.d_ttl = ttl;
6,140✔
3095
      dnsRecord.setContent(signature);
6,140✔
3096
      dnsRecord.d_place = DNSResourceRecord::ANSWER;
6,140✔
3097
      dnsRecord.d_class = QClass::IN;
6,140✔
3098
      ret.push_back(std::move(dnsRecord));
6,140✔
3099
    }
6,140✔
3100

3101
    for (const auto& rec : *authorityRecs) {
6,593✔
3102
      DNSRecord dnsRecord(rec);
32✔
3103
      dnsRecord.d_ttl = ttl;
32✔
3104
      ret.push_back(std::move(dnsRecord));
32✔
3105
    }
32✔
3106

3107
    LOG(endl);
6,593✔
3108
    if (found && !expired) {
6,593✔
3109
      if (!giveNegative) {
6,593!
3110
        res = 0;
6,593✔
3111
      }
6,593✔
3112
      LOG(prefix << qname << ": Updating validation state with cache content for " << qname << " to " << cachedState << endl);
6,593✔
3113
      context.state = cachedState;
6,593✔
3114
      return true;
6,593✔
3115
    }
6,593✔
3116
    LOG(prefix << qname << ": Cache had only stale entries" << endl);
×
3117
  }
×
3118

3119
  /* let's check if we have a NSEC covering that record */
3120
  if (g_aggressiveNSECCache && !wasForwardedOrAuthZone) {
16,305✔
3121
    if (g_aggressiveNSECCache->getDenial(d_now.tv_sec, qname, qtype, ret, res, d_cacheRemote, d_routingTag, d_doDNSSEC, d_validationContext, LogObject(prefix))) {
13,995✔
3122
      context.state = vState::Secure;
44✔
3123
      if (s_addExtendedResolutionDNSErrors) {
44✔
3124
        context.extendedError = EDNSExtendedError{static_cast<uint16_t>(EDNSExtendedError::code::Synthesized), "Result synthesized from aggressive NSEC cache (RFC8198)"};
28✔
3125
      }
28✔
3126
      return true;
44✔
3127
    }
44✔
3128
  }
13,995✔
3129

3130
  return false;
16,261✔
3131
}
16,305✔
3132

3133
bool SyncRes::moreSpecificThan(const DNSName& lhs, const DNSName& rhs)
3134
{
13,989✔
3135
  return (lhs.isPartOf(rhs) && lhs.countLabels() > rhs.countLabels());
13,989!
3136
}
13,989✔
3137

3138
struct speedOrder
3139
{
3140
  bool operator()(const std::pair<DNSName, float>& lhs, const std::pair<DNSName, float>& rhs) const
3141
  {
145,810✔
3142
    return lhs.second < rhs.second;
145,810✔
3143
  }
145,810✔
3144
};
3145

3146
std::vector<std::pair<DNSName, float>> SyncRes::shuffleInSpeedOrder(const DNSName& qname, NsSet& tnameservers, const string& prefix)
3147
{
12,131✔
3148
  std::vector<std::pair<DNSName, float>> rnameservers;
12,131✔
3149
  rnameservers.reserve(tnameservers.size());
12,131✔
3150
  for (const auto& tns : tnameservers) {
68,398✔
3151
    float speed = s_nsSpeeds.lock()->fastest(tns.first, d_now);
68,398✔
3152
    rnameservers.emplace_back(tns.first, speed);
68,398✔
3153
    if (tns.first.empty()) { // this was an authoritative OOB zone, don't pollute the nsSpeeds with that
68,398✔
3154
      return rnameservers;
264✔
3155
    }
264✔
3156
  }
68,398✔
3157

3158
  shuffle(rnameservers.begin(), rnameservers.end(), pdns::dns_random_engine());
11,867✔
3159
  speedOrder speedCompare;
11,867✔
3160
  stable_sort(rnameservers.begin(), rnameservers.end(), speedCompare);
11,867✔
3161

3162
  if (doLog()) {
11,867✔
3163
    LOG(prefix << qname << ": Nameservers: ");
1,543!
3164
    for (auto i = rnameservers.begin(); i != rnameservers.end(); ++i) {
4,144✔
3165
      if (i != rnameservers.begin()) {
2,601✔
3166
        LOG(", ");
1,058!
3167
        if (((i - rnameservers.begin()) % 3) == 0) {
1,058✔
3168
          LOG(endl
187!
3169
              << prefix << "             ");
187✔
3170
        }
187✔
3171
      }
1,058✔
3172
      LOG(i->first.toLogString() << "(" << fmtfloat(i->second / 1000.0) << "ms)");
2,601!
3173
    }
2,601✔
3174
    LOG(endl);
1,543!
3175
  }
1,543✔
3176
  return rnameservers;
11,867✔
3177
}
12,131✔
3178

3179
vector<ComboAddress> SyncRes::shuffleForwardSpeed(const DNSName& qname, const vector<ComboAddress>& rnameservers, const string& prefix, const bool wasRd)
3180
{
134✔
3181
  vector<ComboAddress> nameservers = rnameservers;
134✔
3182
  map<ComboAddress, float> speeds;
134✔
3183

3184
  for (const auto& val : nameservers) {
162✔
3185
    DNSName nsName = DNSName(val.toStringWithPort());
162✔
3186
    float speed = s_nsSpeeds.lock()->fastest(nsName, d_now);
162✔
3187
    speeds[val] = speed;
162✔
3188
  }
162✔
3189
  shuffle(nameservers.begin(), nameservers.end(), pdns::dns_random_engine());
134✔
3190
  speedOrderCA speedCompare(speeds);
134✔
3191
  stable_sort(nameservers.begin(), nameservers.end(), speedCompare);
134✔
3192

3193
  if (doLog()) {
134✔
3194
    LOG(prefix << qname << ": Nameservers: ");
76!
3195
    for (auto i = nameservers.cbegin(); i != nameservers.cend(); ++i) {
176✔
3196
      if (i != nameservers.cbegin()) {
100✔
3197
        LOG(", ");
24!
3198
        if (((i - nameservers.cbegin()) % 3) == 0) {
24!
3199
          LOG(endl
×
3200
              << prefix << "             ");
×
3201
        }
×
3202
      }
24✔
3203
      LOG((wasRd ? string("+") : string("-")) << i->toStringWithPort() << "(" << fmtfloat(speeds[*i] / 1000.0) << "ms)");
100!
3204
    }
100✔
3205
    LOG(endl);
76!
3206
  }
76✔
3207
  return nameservers;
134✔
3208
}
134✔
3209

3210
static uint32_t getRRSIGTTL(const time_t now, const std::shared_ptr<const RRSIGRecordContent>& rrsig)
3211
{
7,374✔
3212
  uint32_t res = 0;
7,374✔
3213
  if (now < rrsig->d_sigexpire) {
7,374✔
3214
    // coverity[store_truncates_time_t]
3215
    res = static_cast<uint32_t>(rrsig->d_sigexpire) - now;
7,373✔
3216
  }
7,373✔
3217
  return res;
7,374✔
3218
}
7,374✔
3219

3220
static const set<QType> nsecTypes = {QType::NSEC, QType::NSEC3};
3221

3222
/* Fills the authoritySOA and DNSSECRecords fields from ne with those found in the records
3223
 *
3224
 * \param records The records to parse for the authority SOA and NSEC(3) records
3225
 * \param ne      The NegCacheEntry to be filled out (will not be cleared, only appended to
3226
 */
3227
static void harvestNXRecords(const vector<DNSRecord>& records, NegCache::NegCacheEntry& negEntry, const time_t now, uint32_t* lowestTTL)
3228
{
6,407✔
3229
  for (const auto& rec : records) {
56,870✔
3230
    if (rec.d_place != DNSResourceRecord::AUTHORITY) {
56,870✔
3231
      // RFC 4035 section 3.1.3. indicates that NSEC records MUST be placed in
3232
      // the AUTHORITY section. Section 3.1.1 indicates that that RRSIGs for
3233
      // records MUST be in the same section as the records they cover.
3234
      // Hence, we ignore all records outside of the AUTHORITY section.
3235
      continue;
23,717✔
3236
    }
23,717✔
3237

3238
    if (rec.d_type == QType::RRSIG) {
33,153✔
3239
      auto rrsig = getRR<RRSIGRecordContent>(rec);
8,022✔
3240
      if (rrsig) {
8,023✔
3241
        if (rrsig->d_type == QType::SOA) {
8,023✔
3242
          negEntry.authoritySOA.signatures.push_back(rec);
553✔
3243
          if (lowestTTL != nullptr && isRRSIGNotExpired(now, *rrsig)) {
553!
3244
            *lowestTTL = min(*lowestTTL, rec.d_ttl);
283✔
3245
            *lowestTTL = min(*lowestTTL, getRRSIGTTL(now, rrsig));
283✔
3246
          }
283✔
3247
        }
553✔
3248
        if (nsecTypes.count(rrsig->d_type) != 0) {
8,023✔
3249
          negEntry.DNSSECRecords.signatures.push_back(rec);
7,471✔
3250
          if (lowestTTL != nullptr && isRRSIGNotExpired(now, *rrsig)) {
7,471✔
3251
            *lowestTTL = min(*lowestTTL, rec.d_ttl);
7,090✔
3252
            *lowestTTL = min(*lowestTTL, getRRSIGTTL(now, rrsig));
7,090✔
3253
          }
7,090✔
3254
        }
7,471✔
3255
      }
8,023✔
3256
      continue;
8,022✔
3257
    }
8,022✔
3258
    if (rec.d_type == QType::SOA) {
25,131✔
3259
      negEntry.authoritySOA.records.push_back(rec);
2,850✔
3260
      if (lowestTTL != nullptr) {
2,850✔
3261
        *lowestTTL = min(*lowestTTL, rec.d_ttl);
1,470✔
3262
      }
1,470✔
3263
      continue;
2,850✔
3264
    }
2,850✔
3265
    if (nsecTypes.count(rec.d_type) != 0) {
22,281✔
3266
      negEntry.DNSSECRecords.records.push_back(rec);
7,483✔
3267
      if (lowestTTL != nullptr) {
7,483✔
3268
        *lowestTTL = min(*lowestTTL, rec.d_ttl);
7,097✔
3269
      }
7,097✔
3270
      continue;
7,483✔
3271
    }
7,483✔
3272
  }
22,281✔
3273
}
6,407✔
3274

3275
static cspmap_t harvestCSPFromNE(const NegCache::NegCacheEntry& negEntry)
3276
{
2,643✔
3277
  cspmap_t cspmap;
2,643✔
3278
  for (const auto& rec : negEntry.DNSSECRecords.signatures) {
4,833✔
3279
    if (rec.d_type == QType::RRSIG) {
4,833✔
3280
      auto rrc = getRR<RRSIGRecordContent>(rec);
4,832✔
3281
      if (rrc) {
4,834✔
3282
        cspmap[{rec.d_name, rrc->d_type}].signatures.push_back(rrc);
4,833✔
3283
      }
4,833✔
3284
    }
4,832✔
3285
  }
4,833✔
3286
  for (const auto& rec : negEntry.DNSSECRecords.records) {
4,835✔
3287
    cspmap[{rec.d_name, rec.d_type}].records.insert(rec.getContent());
4,835✔
3288
  }
4,835✔
3289
  return cspmap;
2,643✔
3290
}
2,643✔
3291

3292
// TODO remove after processRecords is fixed!
3293
// Adds the RRSIG for the SOA and the NSEC(3) + RRSIGs to ret
3294
static void addNXNSECS(vector<DNSRecord>& ret, const vector<DNSRecord>& records)
3295
{
1,423✔
3296
  NegCache::NegCacheEntry negEntry;
1,423✔
3297
  harvestNXRecords(records, negEntry, 0, nullptr);
1,423✔
3298
  ret.insert(ret.end(), negEntry.authoritySOA.signatures.begin(), negEntry.authoritySOA.signatures.end());
1,423✔
3299
  ret.insert(ret.end(), negEntry.DNSSECRecords.records.begin(), negEntry.DNSSECRecords.records.end());
1,423✔
3300
  ret.insert(ret.end(), negEntry.DNSSECRecords.signatures.begin(), negEntry.DNSSECRecords.signatures.end());
1,423✔
3301
}
1,423✔
3302

3303
static bool rpzHitShouldReplaceContent(const DNSName& qname, const QType qtype, const std::vector<DNSRecord>& records)
3304
{
59✔
3305
  if (qtype == QType::CNAME) {
59!
3306
    return true;
×
3307
  }
×
3308

3309
  for (const auto& record : records) { // NOLINT(readability-use-anyofallof): don't agree
59✔
3310
    if (record.d_type == QType::CNAME) {
7✔
3311
      if (auto content = getRR<CNAMERecordContent>(record)) {
4!
3312
        if (qname == content->getTarget()) {
4!
3313
          /* we have a CNAME whose target matches the entry we are about to
3314
             generate, so it will complete the current records, not replace
3315
             them
3316
          */
3317
          return false;
4✔
3318
        }
4✔
3319
      }
4✔
3320
    }
4✔
3321
  }
7✔
3322

3323
  return true;
55✔
3324
}
59✔
3325

3326
static void removeConflictingRecord(std::vector<DNSRecord>& records, const DNSName& name, const QType dtype)
3327
{
63✔
3328
  for (auto it = records.begin(); it != records.end();) {
73✔
3329
    bool remove = false;
10✔
3330

3331
    if (it->d_class == QClass::IN && (it->d_type == QType::CNAME || dtype == QType::CNAME || it->d_type == dtype) && it->d_name == name) {
10!
3332
      remove = true;
2✔
3333
    }
2✔
3334
    else if (it->d_class == QClass::IN && it->d_type == QType::RRSIG && it->d_name == name) {
8!
3335
      if (auto rrc = getRR<RRSIGRecordContent>(*it)) {
2!
3336
        if (rrc->d_type == QType::CNAME || rrc->d_type == dtype) {
2!
3337
          /* also remove any RRSIG that could conflict */
3338
          remove = true;
2✔
3339
        }
2✔
3340
      }
2✔
3341
    }
2✔
3342

3343
    if (remove) {
10✔
3344
      it = records.erase(it);
4✔
3345
    }
4✔
3346
    else {
6✔
3347
      ++it;
6✔
3348
    }
6✔
3349
  }
10✔
3350
}
63✔
3351

3352
void SyncRes::handlePolicyHit(const std::string& prefix, const DNSName& qname, const QType qtype, std::vector<DNSRecord>& ret, bool& done, int& rcode, unsigned int depth)
3353
{
103✔
3354
  if (d_pdl && d_pdl->policyHitEventFilter(d_requestor, qname, qtype, d_queryReceivedOverTCP, d_appliedPolicy, d_policyTags, d_discardedPolicies)) {
103✔
3355
    /* reset to no match */
3356
    d_appliedPolicy = DNSFilterEngine::Policy();
2✔
3357
    return;
2✔
3358
  }
2✔
3359

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

3366
  if (d_appliedPolicy.d_type != DNSFilterEngine::PolicyType::None) {
101!
3367
    LOG(prefix << qname << "|" << qtype << ':' << d_appliedPolicy.getLogString() << endl);
101!
3368
  }
101✔
3369

3370
  switch (d_appliedPolicy.d_kind) {
101!
3371

3372
  case DNSFilterEngine::PolicyKind::NoAction:
22✔
3373
    return;
22✔
3374

3375
  case DNSFilterEngine::PolicyKind::Drop:
6✔
3376
    ++t_Counters.at(rec::Counter::policyDrops);
6✔
3377
    throw ImmediateQueryDropException();
6✔
3378

3379
  case DNSFilterEngine::PolicyKind::NXDOMAIN:
4✔
3380
    ret.clear();
4✔
3381
    d_appliedPolicy.addSOAtoRPZResult(ret);
4✔
3382
    rcode = RCode::NXDomain;
4✔
3383
    done = true;
4✔
3384
    return;
4✔
3385

3386
  case DNSFilterEngine::PolicyKind::NODATA:
4✔
3387
    ret.clear();
4✔
3388
    d_appliedPolicy.addSOAtoRPZResult(ret);
4✔
3389
    rcode = RCode::NoError;
4✔
3390
    done = true;
4✔
3391
    return;
4✔
3392

3393
  case DNSFilterEngine::PolicyKind::Truncate:
6✔
3394
    if (!d_queryReceivedOverTCP) {
6✔
3395
      ret.clear();
3✔
3396
      rcode = RCode::NoError;
3✔
3397
      // Exception handling code in pdns_recursor clears ret as well, so no use to
3398
      // fill it here.
3399
      throw SendTruncatedAnswerException();
3✔
3400
    }
3✔
3401
    return;
3✔
3402

3403
  case DNSFilterEngine::PolicyKind::Custom: {
59✔
3404
    if (rpzHitShouldReplaceContent(qname, qtype, ret)) {
59✔
3405
      ret.clear();
55✔
3406
    }
55✔
3407

3408
    rcode = RCode::NoError;
59✔
3409
    done = true;
59✔
3410
    auto spoofed = d_appliedPolicy.getCustomRecords(qname, qtype.getCode());
59✔
3411
    for (auto& dnsRecord : spoofed) {
63✔
3412
      removeConflictingRecord(ret, dnsRecord.d_name, dnsRecord.d_type);
63✔
3413
    }
63✔
3414

3415
    for (auto& dnsRecord : spoofed) {
63✔
3416
      ret.push_back(dnsRecord);
63✔
3417

3418
      if (dnsRecord.d_name == qname && dnsRecord.d_type == QType::CNAME && qtype != QType::CNAME) {
63!
3419
        if (auto content = getRR<CNAMERecordContent>(dnsRecord)) {
6!
3420
          vState newTargetState = vState::Indeterminate;
6✔
3421
          handleNewTarget(prefix, qname, content->getTarget(), qtype.getCode(), ret, rcode, depth, {}, newTargetState);
6✔
3422
        }
6✔
3423
      }
6✔
3424
    }
63✔
3425
    d_appliedPolicy.addSOAtoRPZResult(ret);
59✔
3426
  }
59✔
3427
  }
101✔
3428
}
101✔
3429

3430
bool SyncRes::nameserversBlockedByRPZ(const DNSFilterEngine& dfe, const NsSet& nameservers)
3431
{
10,217✔
3432
  /* we skip RPZ processing if:
3433
     - it was disabled (d_wantsRPZ is false) ;
3434
     - we already got a RPZ hit (d_appliedPolicy.d_type != DNSFilterEngine::PolicyType::None) since
3435
     the only way we can get back here is that it was a 'pass-thru' (NoAction) meaning that we should not
3436
     process any further RPZ rules. Except that we need to process rules of higher priority..
3437
  */
3438
  if (d_wantsRPZ && !d_appliedPolicy.wasHit()) {
10,217✔
3439
    for (auto const& nameserver : nameservers) {
61,807✔
3440
      bool match = dfe.getProcessingPolicy(nameserver.first, d_discardedPolicies, d_appliedPolicy);
61,807✔
3441
      if (match) {
61,807!
3442
        mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());
×
3443
        if (d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) { // client query needs an RPZ response
×
3444
          LOG(", however nameserver " << nameserver.first << " was blocked by RPZ policy '" << d_appliedPolicy.getName() << "'" << endl);
×
3445
          return true;
×
3446
        }
×
3447
      }
×
3448

3449
      // Traverse all IP addresses for this NS to see if they have an RPN NSIP policy
3450
      for (auto const& address : nameserver.second.first) {
61,807✔
3451
        match = dfe.getProcessingPolicy(address, d_discardedPolicies, d_appliedPolicy);
164✔
3452
        if (match) {
164✔
3453
          mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());
3✔
3454
          if (d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) { // client query needs an RPZ response
3✔
3455
            LOG(", however nameserver " << nameserver.first << " IP address " << address.toString() << " was blocked by RPZ policy '" << d_appliedPolicy.getName() << "'" << endl);
2!
3456
            return true;
2✔
3457
          }
2✔
3458
        }
3✔
3459
      }
164✔
3460
    }
61,807✔
3461
  }
10,210✔
3462
  return false;
10,215✔
3463
}
10,217✔
3464

3465
bool SyncRes::nameserverIPBlockedByRPZ(const DNSFilterEngine& dfe, const ComboAddress& remoteIP)
3466
{
13,522✔
3467
  /* we skip RPZ processing if:
3468
     - it was disabled (d_wantsRPZ is false) ;
3469
     - we already got a RPZ hit (d_appliedPolicy.d_type != DNSFilterEngine::PolicyType::None) since
3470
     the only way we can get back here is that it was a 'pass-thru' (NoAction) meaning that we should not
3471
     process any further RPZ rules. Except that we need to process rules of higher priority..
3472
  */
3473
  if (d_wantsRPZ && !d_appliedPolicy.wasHit()) {
13,522!
3474
    bool match = dfe.getProcessingPolicy(remoteIP, d_discardedPolicies, d_appliedPolicy);
13,516✔
3475
    if (match) {
13,516✔
3476
      mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());
4✔
3477
      if (d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) {
4!
3478
        LOG(" (blocked by RPZ policy '" + d_appliedPolicy.getName() + "')");
4!
3479
        return true;
4✔
3480
      }
4✔
3481
    }
4✔
3482
  }
13,516✔
3483
  return false;
13,518✔
3484
}
13,522✔
3485

3486
static bool shouldNotThrottle(const DNSName* name, const ComboAddress* address)
3487
{
12,298✔
3488
  if (name != nullptr) {
12,298!
3489
    auto dontThrottleNames = g_dontThrottleNames.getLocal();
12,298✔
3490
    if (dontThrottleNames->check(*name)) {
12,298!
3491
      return true;
×
3492
    }
×
3493
  }
12,298✔
3494
  if (address != nullptr) {
12,298✔
3495
    auto dontThrottleNetmasks = g_dontThrottleNetmasks.getLocal();
12,265✔
3496
    if (dontThrottleNetmasks->match(*address)) {
12,265!
3497
      return true;
×
3498
    }
×
3499
  }
12,265✔
3500
  return false;
12,298✔
3501
}
12,298✔
3502

3503
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)
3504
{
12,232✔
3505
  vector<ComboAddress> result;
12,232✔
3506

3507
  size_t nonresolvingfails = 0;
12,232✔
3508
  if (!tns->first.empty()) {
12,232✔
3509
    if (s_nonresolvingnsmaxfails > 0) {
12,098✔
3510
      nonresolvingfails = s_nonresolving.lock()->value(tns->first);
9,639✔
3511
      if (nonresolvingfails >= s_nonresolvingnsmaxfails) {
9,639✔
3512
        LOG(prefix << qname << ": NS " << tns->first << " in non-resolving map, skipping" << endl);
17!
3513
        return result;
17✔
3514
      }
17✔
3515
    }
9,639✔
3516

3517
    LOG(prefix << qname << ": Trying to resolve NS '" << tns->first << "' (" << 1 + tns - rnameservers.begin() << "/" << (unsigned int)rnameservers.size() << ")" << endl);
12,081✔
3518
    const unsigned int oldOutQueries = d_outqueries;
12,081✔
3519
    try {
12,081✔
3520
      result = getAddrs(tns->first, depth, prefix, beenthere, cacheOnly, nretrieveAddressesForNS);
12,081✔
3521
    }
12,081✔
3522
    // Other exceptions should likely not throttle...
3523
    catch (const ImmediateServFailException& ex) {
12,081✔
3524
      if (s_nonresolvingnsmaxfails > 0 && d_outqueries > oldOutQueries) {
90!
3525
        if (!shouldNotThrottle(&tns->first, nullptr)) {
×
3526
          s_nonresolving.lock()->incr(tns->first, d_now);
×
3527
        }
×
3528
      }
×
3529
      throw ex;
90✔
3530
    }
90✔
3531
    if (s_nonresolvingnsmaxfails > 0 && d_outqueries > oldOutQueries) {
11,991✔
3532
      if (result.empty()) {
1,398✔
3533
        if (!shouldNotThrottle(&tns->first, nullptr)) {
33!
3534
          s_nonresolving.lock()->incr(tns->first, d_now);
33✔
3535
        }
33✔
3536
      }
33✔
3537
      else if (nonresolvingfails > 0) {
1,365✔
3538
        // Succeeding resolve, clear memory of recent failures
3539
        s_nonresolving.lock()->clear(tns->first);
2✔
3540
      }
2✔
3541
    }
1,398✔
3542
    pierceDontQuery = false;
11,991✔
3543
  }
11,991✔
3544
  else {
134✔
3545
    LOG(prefix << qname << ": Domain has hardcoded nameserver");
134✔
3546

3547
    if (nameservers[tns->first].first.size() > 1) {
134✔
3548
      LOG("s");
16!
3549
    }
16✔
3550
    LOG(endl);
134✔
3551

3552
    sendRDQuery = nameservers[tns->first].second;
134✔
3553
    result = shuffleForwardSpeed(qname, nameservers[tns->first].first, prefix, sendRDQuery);
134✔
3554
    pierceDontQuery = true;
134✔
3555
  }
134✔
3556
  return result;
12,125✔
3557
}
12,232✔
3558

3559
void SyncRes::checkMaxQperQ(const DNSName& qname) const
3560
{
12,226✔
3561
  if (d_outqueries + d_throttledqueries > s_maxqperq) {
12,226✔
3562
    throw ImmediateServFailException("more than " + std::to_string(s_maxqperq) + " (max-qperq) queries sent or throttled while resolving " + qname.toLogString());
4✔
3563
  }
4✔
3564
}
12,226✔
3565

3566
bool SyncRes::throttledOrBlocked(const std::string& prefix, const ComboAddress& remoteIP, const DNSName& qname, const QType qtype, bool pierceDontQuery)
3567
{
12,198✔
3568
  if (isThrottled(d_now.tv_sec, remoteIP)) {
12,198✔
3569
    LOG(prefix << qname << ": Server throttled " << endl);
2!
3570
    t_Counters.at(rec::Counter::throttledqueries)++;
2✔
3571
    d_throttledqueries++;
2✔
3572
    return true;
2✔
3573
  }
2✔
3574
  if (isThrottled(d_now.tv_sec, remoteIP, qname, qtype)) {
12,196✔
3575
    LOG(prefix << qname << ": Query throttled " << remoteIP.toString() << ", " << qname << "; " << qtype << endl);
3!
3576
    t_Counters.at(rec::Counter::throttledqueries)++;
3✔
3577
    d_throttledqueries++;
3✔
3578
    return true;
3✔
3579
  }
3✔
3580
  if (!pierceDontQuery && s_dontQuery && s_dontQuery->match(&remoteIP)) {
12,193✔
3581
    // We could have retrieved an NS from the cache in a forwarding domain
3582
    // Even in the case of !pierceDontQuery we still want to allow that NS
3583
    DNSName forwardCandidate(qname);
2✔
3584
    auto iter = getBestAuthZone(&forwardCandidate);
2✔
3585
    if (iter == t_sstorage.domainmap->end()) {
2!
3586
      LOG(prefix << qname << ": Not sending query to " << remoteIP.toString() << ", blocked by 'dont-query' setting" << endl);
2!
3587
      t_Counters.at(rec::Counter::dontqueries)++;
2✔
3588
      return true;
2✔
3589
    }
2✔
3590
    // The name (from the cache) is forwarded, but is it forwarded to an IP in known forwarders?
3591
    const auto& ips = iter->second.d_servers;
×
3592
    if (std::find(ips.cbegin(), ips.cend(), remoteIP) == ips.cend()) {
×
3593
      LOG(prefix << qname << ": Not sending query to " << remoteIP.toString() << ", blocked by 'dont-query' setting" << endl);
×
3594
      t_Counters.at(rec::Counter::dontqueries)++;
×
3595
      return true;
×
3596
    }
×
3597
    LOG(prefix << qname << ": Sending query to " << remoteIP.toString() << ", blocked by 'dont-query' but a forwarding/auth case" << endl);
×
3598
  }
×
3599
  return false;
12,191✔
3600
}
12,193✔
3601

3602
bool SyncRes::validationEnabled()
3603
{
12,131✔
3604
  return g_dnssecmode != DNSSECMode::Off && g_dnssecmode != DNSSECMode::ProcessNoValidate;
12,131✔
3605
}
12,131✔
3606

3607
uint32_t SyncRes::computeLowestTTD(const std::vector<DNSRecord>& records, const MemRecursorCache::SigRecsVec& signatures, uint32_t signaturesTTL, const MemRecursorCache::AuthRecsVec& authorityRecs) const
3608
{
11,597✔
3609
  uint32_t lowestTTD = std::numeric_limits<uint32_t>::max();
11,597✔
3610
  for (const auto& record : records) {
26,352✔
3611
    lowestTTD = min(lowestTTD, record.d_ttl);
26,352✔
3612
  }
26,352✔
3613

3614
  /* even if it was not requested for that request (Process, and neither AD nor DO set),
3615
     it might be requested at a later time so we need to be careful with the TTL. */
3616
  if (validationEnabled() && !signatures.empty()) {
11,597✔
3617
    /* if we are validating, we don't want to cache records after their signatures expire. */
3618
    /* records TTL are now TTD, let's add 'now' to the signatures lowest TTL */
3619
    lowestTTD = min(lowestTTD, static_cast<uint32_t>(signaturesTTL + d_now.tv_sec));
7,653✔
3620

3621
    for (const auto& sig : signatures) {
7,674✔
3622
      if (isRRSIGNotExpired(d_now.tv_sec, *sig)) {
7,674✔
3623
        // we don't decrement d_sigexpire by 'now' because we actually want a TTD, not a TTL */
3624
        lowestTTD = min(lowestTTD, static_cast<uint32_t>(sig->d_sigexpire));
7,662✔
3625
      }
7,662✔
3626
    }
7,674✔
3627
  }
7,653✔
3628

3629
  for (const auto& entry : authorityRecs) {
11,597✔
3630
    /* be careful, this is still a TTL here */
3631
    lowestTTD = min(lowestTTD, static_cast<uint32_t>(entry.d_ttl + d_now.tv_sec));
1,048✔
3632

3633
    if (entry.d_type == QType::RRSIG && validationEnabled()) {
1,048!
3634
      auto rrsig = getRR<RRSIGRecordContent>(entry);
524✔
3635
      if (rrsig) {
524!
3636
        if (isRRSIGNotExpired(d_now.tv_sec, *rrsig)) {
524!
3637
          // we don't decrement d_sigexpire by 'now' because we actually want a TTD, not a TTL */
3638
          lowestTTD = min(lowestTTD, static_cast<uint32_t>(rrsig->d_sigexpire));
524✔
3639
        }
524✔
3640
      }
524✔
3641
    }
524✔
3642
  }
1,048✔
3643

3644
  return lowestTTD;
11,597✔
3645
}
11,597✔
3646

3647
void SyncRes::updateValidationState(const DNSName& qname, vState& state, const vState stateUpdate, const string& prefix)
3648
{
12,694✔
3649
  LOG(prefix << qname << ": Validation state was " << state << ", state update is " << stateUpdate);
12,694✔
3650
  updateDNSSECValidationState(state, stateUpdate);
12,694✔
3651
  LOG(", validation state is now " << state << endl);
12,694✔
3652
}
12,694✔
3653

3654
vState SyncRes::getTA(const DNSName& zone, dsset_t& dsSet, const string& prefix)
3655
{
62,169✔
3656
  auto luaLocal = g_luaconfs.getLocal();
62,169✔
3657

3658
  if (luaLocal->dsAnchors.empty()) {
62,169✔
3659
    LOG(prefix << zone << ": No trust anchors configured, everything is Insecure" << endl);
44!
3660
    /* We have no TA, everything is insecure */
3661
    return vState::Insecure;
44✔
3662
  }
44✔
3663

3664
  std::string reason;
62,125✔
3665
  if (haveNegativeTrustAnchor(luaLocal->negAnchors, zone, reason)) {
62,125✔
3666
    LOG(prefix << zone << ": Got NTA" << endl);
15!
3667
    return vState::NTA;
15✔
3668
  }
15✔
3669

3670
  if (getTrustAnchor(luaLocal->dsAnchors, zone, dsSet)) {
62,110✔
3671
    if (!zone.isRoot()) {
18,078✔
3672
      LOG(prefix << zone << ": Got TA" << endl);
144!
3673
    }
144✔
3674
    return vState::TA;
18,078✔
3675
  }
18,078✔
3676

3677
  if (zone.isRoot()) {
44,032!
3678
    /* No TA for the root */
3679
    return vState::Insecure;
×
3680
  }
×
3681

3682
  return vState::Indeterminate;
44,032✔
3683
}
44,032✔
3684

3685
size_t SyncRes::countSupportedDS(const dsset_t& dsset, const string& prefix)
3686
{
18,080✔
3687
  size_t count = 0;
18,080✔
3688

3689
  for (const auto& dsRecordContent : dsset) {
37,022✔
3690
    if (isSupportedDS(dsRecordContent, LogObject(prefix))) {
37,028✔
3691
      count++;
37,017✔
3692
    }
37,017✔
3693
  }
37,022✔
3694

3695
  return count;
18,080✔
3696
}
18,080✔
3697

3698
void SyncRes::initZoneCutsFromTA(const DNSName& from, const string& prefix)
3699
{
17,530✔
3700
  DNSName zone(from);
17,530✔
3701
  do {
56,228✔
3702
    dsset_t dsSet;
56,228✔
3703
    vState result = getTA(zone, dsSet, prefix);
56,228✔
3704
    if (result != vState::Indeterminate) {
56,228✔
3705
      if (result == vState::TA) {
17,685✔
3706
        if (countSupportedDS(dsSet, prefix) == 0) {
17,626✔
3707
          dsSet.clear();
8✔
3708
          result = vState::Insecure;
8✔
3709
        }
8✔
3710
        else {
17,618✔
3711
          result = vState::Secure;
17,618✔
3712
        }
17,618✔
3713
      }
17,626✔
3714
      else if (result == vState::NTA) {
59✔
3715
        result = vState::Insecure;
15✔
3716
      }
15✔
3717

3718
      d_cutStates[zone] = result;
17,685✔
3719
    }
17,685✔
3720
  } while (zone.chopOff());
56,228✔
3721
}
17,530✔
3722

3723
vState SyncRes::getDSRecords(const DNSName& zone, dsset_t& dsSet, bool onlyTA, unsigned int depth, const string& prefix, bool bogusOnNXD, bool* foundCut)
3724
{
5,944✔
3725
  vState result = getTA(zone, dsSet, prefix);
5,944✔
3726

3727
  if (result != vState::Indeterminate || onlyTA) {
5,944!
3728
    if (foundCut != nullptr) {
454!
3729
      *foundCut = (result != vState::Indeterminate);
×
3730
    }
×
3731

3732
    if (result == vState::TA) {
454!
3733
      if (countSupportedDS(dsSet, prefix) == 0) {
454!
3734
        dsSet.clear();
×
3735
        result = vState::Insecure;
×
3736
      }
×
3737
      else {
454✔
3738
        result = vState::Secure;
454✔
3739
      }
454✔
3740
    }
454✔
3741
    else if (result == vState::NTA) {
×
3742
      result = vState::Insecure;
×
3743
    }
×
3744

3745
    return result;
454✔
3746
  }
454✔
3747

3748
  std::set<GetBestNSAnswer> beenthere;
5,490✔
3749
  std::vector<DNSRecord> dsrecords;
5,490✔
3750

3751
  Context context;
5,490✔
3752

3753
  const bool oldCacheOnly = setCacheOnly(false);
5,490✔
3754
  const bool oldQM = setQNameMinimization(!getQMFallbackMode());
5,490✔
3755
  int rcode = doResolve(zone, QType::DS, dsrecords, depth + 1, beenthere, context);
5,490✔
3756
  setCacheOnly(oldCacheOnly);
5,490✔
3757
  setQNameMinimization(oldQM);
5,490✔
3758

3759
  if (rcode == RCode::ServFail) {
5,490✔
3760
    throw ImmediateServFailException("Server Failure while retrieving DS records for " + zone.toLogString());
4✔
3761
  }
4✔
3762

3763
  if (rcode != RCode::NoError && (rcode != RCode::NXDomain || bogusOnNXD)) {
5,486!
3764
    LOG(prefix << zone << ": Returning Bogus state from " << static_cast<const char*>(__func__) << "(" << zone << ")" << endl);
2!
3765
    return vState::BogusUnableToGetDSs;
2✔
3766
  }
2✔
3767

3768
  uint8_t bestDigestType = 0;
5,484✔
3769

3770
  bool gotCNAME = false;
5,484✔
3771
  for (const auto& record : dsrecords) {
15,948✔
3772
    if (record.d_type == QType::DS) {
15,948✔
3773
      const auto dscontent = getRR<DSRecordContent>(record);
2,923✔
3774
      if (dscontent && isSupportedDS(*dscontent, LogObject(prefix))) {
2,923!
3775
        // Make GOST a lower prio than SHA256
3776
        if (dscontent->d_digesttype == DNSSECKeeper::DIGEST_GOST && bestDigestType == DNSSECKeeper::DIGEST_SHA256) {
2,923!
3777
          continue;
×
3778
        }
×
3779
        if (dscontent->d_digesttype > bestDigestType || (bestDigestType == DNSSECKeeper::DIGEST_GOST && dscontent->d_digesttype == DNSSECKeeper::DIGEST_SHA256)) {
2,923!
3780
          bestDigestType = dscontent->d_digesttype;
2,911✔
3781
        }
2,911✔
3782
        dsSet.insert(*dscontent);
2,923✔
3783
      }
2,923✔
3784
    }
2,923✔
3785
    else if (record.d_type == QType::CNAME && record.d_name == zone) {
13,025!
3786
      gotCNAME = true;
13✔
3787
    }
13✔
3788
  }
15,948✔
3789

3790
  /* RFC 4509 section 3: "Validator implementations SHOULD ignore DS RRs containing SHA-1
3791
   * digests if DS RRs with SHA-256 digests are present in the DS RRset."
3792
   * We interpret that as: do not use SHA-1 if SHA-256 or SHA-384 is available
3793
   */
3794
  for (auto dsrec = dsSet.begin(); dsrec != dsSet.end();) {
8,407✔
3795
    if (dsrec->d_digesttype == DNSSECKeeper::DIGEST_SHA1 && dsrec->d_digesttype != bestDigestType) {
2,923✔
3796
      dsrec = dsSet.erase(dsrec);
7✔
3797
    }
7✔
3798
    else {
2,916✔
3799
      ++dsrec;
2,916✔
3800
    }
2,916✔
3801
  }
2,923✔
3802

3803
  if (rcode == RCode::NoError) {
5,484✔
3804
    if (dsSet.empty()) {
5,478✔
3805
      /* we have no DS, it's either:
3806
         - a delegation to a non-DNSSEC signed zone
3807
         - no delegation, we stay in the same zone
3808
      */
3809
      if (gotCNAME || denialProvesNoDelegation(zone, dsrecords, d_validationContext)) {
2,581✔
3810
        /* we are still inside the same zone */
3811

3812
        if (foundCut != nullptr) {
31✔
3813
          *foundCut = false;
25✔
3814
        }
25✔
3815
        return context.state;
31✔
3816
      }
31✔
3817

3818
      d_cutStates[zone] = context.state == vState::Secure ? vState::Insecure : context.state;
2,550✔
3819
      /* delegation with no DS, might be Secure -> Insecure */
3820
      if (foundCut != nullptr) {
2,550✔
3821
        *foundCut = true;
2,529✔
3822
      }
2,529✔
3823

3824
      /* a delegation with no DS is either:
3825
         - a signed zone (Secure) to an unsigned one (Insecure)
3826
         - an unsigned zone to another unsigned one (Insecure stays Insecure, Bogus stays Bogus)
3827
      */
3828
      return context.state == vState::Secure ? vState::Insecure : context.state;
2,550✔
3829
    }
2,581✔
3830
    /* we have a DS */
3831
    d_cutStates[zone] = context.state;
2,897✔
3832
    if (foundCut != nullptr) {
2,897✔
3833
      *foundCut = true;
1,959✔
3834
    }
1,959✔
3835
  }
2,897✔
3836

3837
  return context.state;
2,903✔
3838
}
5,484✔
3839

3840
vState SyncRes::getValidationStatus(const DNSName& name, bool wouldBeValid, bool typeIsDS, unsigned int depth, const string& prefix)
3841
{
16,625✔
3842
  vState result = vState::Indeterminate;
16,625✔
3843

3844
  if (!shouldValidate()) {
16,625✔
3845
    return result;
1,381✔
3846
  }
1,381✔
3847

3848
  DNSName subdomain(name);
15,244✔
3849
  if (typeIsDS) {
15,244✔
3850
    subdomain.chopOff();
3,164✔
3851
  }
3,164✔
3852

3853
  {
15,244✔
3854
    const auto& iter = d_cutStates.find(subdomain);
15,244✔
3855
    if (iter != d_cutStates.cend()) {
15,244✔
3856
      LOG(prefix << name << ": Got status " << iter->second << " for name " << subdomain << endl);
2,984!
3857
      return iter->second;
2,984✔
3858
    }
2,984✔
3859
  }
15,244✔
3860

3861
  /* look for the best match we have */
3862
  DNSName best(subdomain);
12,260✔
3863
  while (best.chopOff()) {
22,757✔
3864
    const auto& iter = d_cutStates.find(best);
22,757✔
3865
    if (iter != d_cutStates.cend()) {
22,757✔
3866
      result = iter->second;
12,262✔
3867
      if (vStateIsBogus(result) || result == vState::Insecure) {
12,262✔
3868
        LOG(prefix << name << ": Got status " << result << " for name " << best << endl);
2,897!
3869
        return result;
2,897✔
3870
      }
2,897✔
3871
      break;
9,365✔
3872
    }
12,262✔
3873
  }
22,757✔
3874

3875
  /* by now we have the best match, it's likely Secure (otherwise we would not be there)
3876
     but we don't know if we missed a cut (or several).
3877
     We could see if we have DS (or denial of) in cache but let's not worry for now,
3878
     we will if we don't have a signature, or if the signer doesn't match what we expect */
3879
  if (!wouldBeValid && best != subdomain) {
9,363!
3880
    /* no signatures or Bogus, we likely missed a cut, let's try to find it */
3881
    LOG(prefix << name << ": No or invalid signature/proof for " << name << ", we likely missed a cut between " << best << " and " << subdomain << ", looking for it" << endl);
2,590!
3882
    DNSName dsName(best);
2,590✔
3883
    std::vector<string> labelsToAdd = subdomain.makeRelative(dsName).getRawLabels();
2,590✔
3884

3885
    while (!labelsToAdd.empty()) {
4,580✔
3886

3887
      dsName.prependRawLabel(labelsToAdd.back());
4,519✔
3888
      labelsToAdd.pop_back();
4,519✔
3889
      LOG(prefix << name << ": - Looking for a DS at " << dsName << endl);
4,519!
3890

3891
      bool foundCut = false;
4,519✔
3892
      dsset_t results;
4,519✔
3893
      vState dsState = getDSRecords(dsName, results, false, depth, prefix, false, &foundCut);
4,519✔
3894

3895
      if (foundCut) {
4,519✔
3896
        LOG(prefix << name << ": - Found cut at " << dsName << endl);
4,488!
3897
        LOG(prefix << name << ": New state for " << dsName << " is " << dsState << endl);
4,488!
3898
        d_cutStates[dsName] = dsState;
4,488✔
3899

3900
        if (dsState != vState::Secure) {
4,488✔
3901
          return dsState;
2,529✔
3902
        }
2,529✔
3903
      }
4,488✔
3904
    }
4,519✔
3905

3906
    /* we did not miss a cut, good luck */
3907
    return result;
61✔
3908
  }
2,590✔
3909

3910
#if 0
3911
  /* we don't need this, we actually do the right thing later */
3912
  DNSName signer = getSigner(signatures);
3913

3914
  if (!signer.empty() && name.isPartOf(signer)) {
3915
    if (signer == best) {
3916
      return result;
3917
    }
3918
    /* the zone cut is not the one we expected,
3919
       this is fine because we will retrieve the needed DNSKEYs and DSs
3920
       later, and even go Insecure if we missed a cut to Insecure (no DS)
3921
       and the signatures do not validate (we should not go Bogus in that
3922
       case) */
3923
  }
3924
  /* something is not right, but let's not worry about that for now.. */
3925
#endif
3926

3927
  return result;
6,773✔
3928
}
9,363✔
3929

3930
vState SyncRes::validateDNSKeys(const DNSName& zone, const std::vector<DNSRecord>& dnskeys, const MemRecursorCache::SigRecsVec& signatures, unsigned int depth, const string& prefix)
3931
{
1,406✔
3932
  dsset_t dsSet;
1,406✔
3933
  if (signatures.empty()) {
1,406!
3934
    LOG(prefix << zone << ": We have " << std::to_string(dnskeys.size()) << " DNSKEYs but no signature, going Bogus!" << endl);
×
3935
    return vState::BogusNoRRSIG;
×
3936
  }
×
3937

3938
  DNSName signer = getSigner(signatures);
1,406✔
3939

3940
  if (!signer.empty() && zone.isPartOf(signer)) {
1,406!
3941
    vState state = getDSRecords(signer, dsSet, false, depth, prefix);
1,406✔
3942

3943
    if (state != vState::Secure) {
1,406✔
3944
      return state;
29✔
3945
    }
29✔
3946
  }
1,406✔
3947
  else {
×
3948
    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);
×
3949
    /* try again to get the missed cuts, harder this time */
3950
    auto zState = getValidationStatus(zone, false, false, depth, prefix);
×
3951
    if (zState == vState::Secure) {
×
3952
      /* too bad */
3953
      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);
×
3954
      return vState::BogusNoValidRRSIG;
×
3955
    }
×
3956
    return zState;
×
3957
  }
×
3958

3959
  skeyset_t tentativeKeys;
1,377✔
3960
  sortedRecords_t toSign;
1,377✔
3961

3962
  for (const auto& dnskey : dnskeys) {
1,683✔
3963
    if (dnskey.d_type == QType::DNSKEY) {
1,683!
3964
      auto content = getRR<DNSKEYRecordContent>(dnskey);
1,683✔
3965
      if (content) {
1,683!
3966
        tentativeKeys.insert(content);
1,683✔
3967
        toSign.insert(content);
1,683✔
3968
      }
1,683✔
3969
    }
1,683✔
3970
  }
1,683✔
3971

3972
  LOG(prefix << zone << ": Trying to validate " << std::to_string(tentativeKeys.size()) << " DNSKEYs with " << std::to_string(dsSet.size()) << " DS" << endl);
1,377!
3973
  skeyset_t validatedKeys;
1,377✔
3974
  auto state = validateDNSKeysAgainstDS(d_now.tv_sec, zone, dsSet, tentativeKeys, toSign, signatures, validatedKeys, LogObject(prefix), d_validationContext);
1,377✔
3975

3976
  if (s_maxvalidationsperq != 0 && d_validationContext.d_validationsCounter > s_maxvalidationsperq) {
1,377!
3977
    throw ImmediateServFailException("Server Failure while validating DNSKEYs, too many signature validations for this query");
×
3978
  }
×
3979

3980
  LOG(prefix << zone << ": We now have " << std::to_string(validatedKeys.size()) << " DNSKEYs" << endl);
1,377!
3981

3982
  /* if we found at least one valid RRSIG covering the set,
3983
     all tentative keys are validated keys. Otherwise it means
3984
     we haven't found at least one DNSKEY and a matching RRSIG
3985
     covering this set, this looks Bogus. */
3986
  if (validatedKeys.size() != tentativeKeys.size()) {
1,377✔
3987
    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);
41!
3988
    /* try again to get the missed cuts, harder this time */
3989
    auto zState = getValidationStatus(zone, false, false, depth, prefix);
41✔
3990
    if (zState == vState::Secure) {
41!
3991
      /* too bad */
3992
      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);
41!
3993
      return state;
41✔
3994
    }
41✔
3995
    return zState;
×
3996
  }
41✔
3997

3998
  return state;
1,336✔
3999
}
1,377✔
4000

4001
vState SyncRes::getDNSKeys(const DNSName& signer, skeyset_t& keys, bool& servFailOccurred, unsigned int depth, const string& prefix)
4002
{
4,582✔
4003
  std::vector<DNSRecord> records;
4,582✔
4004
  std::set<GetBestNSAnswer> beenthere;
4,582✔
4005
  LOG(prefix << signer << ": Retrieving DNSKEYs" << endl);
4,582!
4006

4007
  Context context;
4,582✔
4008

4009
  const bool oldCacheOnly = setCacheOnly(false);
4,582✔
4010
  int rcode = doResolve(signer, QType::DNSKEY, records, depth + 1, beenthere, context);
4,582✔
4011
  setCacheOnly(oldCacheOnly);
4,582✔
4012

4013
  if (rcode == RCode::ServFail) {
4,582✔
4014
    servFailOccurred = true;
12✔
4015
    return vState::BogusUnableToGetDNSKEYs;
12✔
4016
  }
12✔
4017

4018
  if (rcode == RCode::NoError) {
4,570✔
4019
    if (context.state == vState::Secure) {
4,564✔
4020
      for (const auto& key : records) {
11,687✔
4021
        if (key.d_type == QType::DNSKEY) {
11,687✔
4022
          auto content = getRR<DNSKEYRecordContent>(key);
7,233✔
4023
          if (content) {
7,233!
4024
            keys.insert(std::move(content));
7,233✔
4025
          }
7,233✔
4026
        }
7,233✔
4027
      }
11,687✔
4028
    }
4,456✔
4029
    LOG(prefix << signer << ": Retrieved " << keys.size() << " DNSKeys, state is " << context.state << endl);
4,564!
4030
    return context.state;
4,564✔
4031
  }
4,564✔
4032

4033
  if (context.state == vState::Insecure) {
6✔
4034
    return context.state;
2✔
4035
  }
2✔
4036

4037
  LOG(prefix << signer << ": Returning Bogus state from " << static_cast<const char*>(__func__) << "(" << signer << ")" << endl);
4!
4038
  return vState::BogusUnableToGetDNSKEYs;
4✔
4039
}
6✔
4040

4041
vState SyncRes::validateRecordsWithSigs(unsigned int depth, const string& prefix, const DNSName& qname, const QType qtype, const DNSName& name, const QType type, const std::vector<DNSRecord>& records, const MemRecursorCache::SigRecsVec& signatures)
4042
{
4,642✔
4043
  skeyset_t keys;
4,642✔
4044
  if (signatures.empty()) {
4,642✔
4045
    LOG(prefix << qname << ": Bogus!" << endl);
36!
4046
    return vState::BogusNoRRSIG;
36✔
4047
  }
36✔
4048

4049
  const DNSName signer = getSigner(signatures);
4,606✔
4050
  bool dsFailed = false;
4,606✔
4051
  if (!signer.empty() && name.isPartOf(signer)) {
4,606!
4052
    vState state = vState::Secure;
4,604✔
4053

4054
    if ((qtype == QType::DNSKEY || qtype == QType::DS) && signer == qname) {
4,604✔
4055
      /* we are already retrieving those keys, sorry */
4056
      if (type == QType::DS && signer == name && !signer.isRoot()) {
26!
4057
        /* Unless we are getting the DS of the root zone, we should never see a
4058
           DS (or a denial of a DS) signed by the DS itself, since we should be
4059
           requesting it from the parent zone. Something is very wrong */
4060
        LOG(prefix << qname << ": The DS for " << qname << " is signed by itself" << endl);
2!
4061
        state = vState::BogusSelfSignedDS;
2✔
4062
        dsFailed = true;
2✔
4063
      }
2✔
4064
      else if (qtype == QType::DS && signer == qname && !signer.isRoot()) {
24!
4065
        if (type == QType::SOA || type == QType::NSEC || type == QType::NSEC3) {
6!
4066
          /* if we are trying to validate the DS or more likely NSEC(3)s proving that it does not exist, we have a problem.
4067
             In that case let's go Bogus (we will check later if we missed a cut)
4068
          */
4069
          state = vState::BogusSelfSignedDS;
2✔
4070
          dsFailed = true;
2✔
4071
        }
2✔
4072
        else if (type == QType::CNAME) {
4!
4073
          state = vState::BogusUnableToGetDSs;
4✔
4074
          dsFailed = true;
4✔
4075
        }
4✔
4076
      }
6✔
4077
      else if (qtype == QType::DNSKEY && signer == qname) {
18!
4078
        /* that actually does happen when a server returns NS records in authority
4079
           along with the DNSKEY, leading us to trying to validate the RRSIGs for
4080
           the NS with the DNSKEY that we are about to process. */
4081
        if ((name == signer && type == QType::NSEC) || type == QType::NSEC3) {
14✔
4082
          /* if we are trying to validate the DNSKEY (should not happen here),
4083
             or more likely NSEC(3)s proving that it does not exist, we have a problem.
4084
             In that case let's see if the DS does exist, and if it does let's go Bogus
4085
          */
4086
          dsset_t results;
6✔
4087
          vState dsState = getDSRecords(signer, results, false, depth, prefix, true);
6✔
4088
          if (vStateIsBogus(dsState) || dsState == vState::Insecure) {
6!
4089
            state = dsState;
6✔
4090
            if (vStateIsBogus(dsState)) {
6!
4091
              dsFailed = true;
×
4092
            }
×
4093
          }
6✔
4094
          else {
×
4095
            LOG(prefix << qname << ": Unable to get the DS for " << signer << endl);
×
4096
            state = vState::BogusUnableToGetDNSKEYs;
×
4097
            dsFailed = true;
×
4098
          }
×
4099
        }
6✔
4100
        else {
8✔
4101
          /* return immediately since looking at the cuts is not going to change the
4102
             fact that we are looking at a signature done with the key we are trying to
4103
             obtain */
4104
          LOG(prefix << qname << ": We are looking at a signature done with the key we are trying to obtain " << signer << endl);
8!
4105
          return vState::Indeterminate;
8✔
4106
        }
8✔
4107
      }
14✔
4108
    }
26✔
4109
    bool servFailOccurred = false;
4,596✔
4110
    if (state == vState::Secure) {
4,596✔
4111
      state = getDNSKeys(signer, keys, servFailOccurred, depth, prefix);
4,581✔
4112
    }
4,581✔
4113

4114
    if (state != vState::Secure) {
4,596✔
4115
      if (!vStateIsBogus(state)) {
136✔
4116
        return state;
45✔
4117
      }
45✔
4118
      /* try again to get the missed cuts, harder this time */
4119
      LOG(prefix << signer << ": Checking whether we missed a zone cut for " << signer << " before returning a Bogus state for " << name << "|" << type.toString() << endl);
91!
4120
      auto zState = getValidationStatus(signer, false, dsFailed, depth, prefix);
91✔
4121
      if (zState == vState::Secure) {
91✔
4122
        if (state == vState::BogusUnableToGetDNSKEYs && servFailOccurred) {
73!
4123
          throw ImmediateServFailException("Server Failure while retrieving DNSKEY records for " + signer.toLogString());
8✔
4124
        }
8✔
4125
        /* too bad */
4126
        LOG(prefix << signer << ": We are still in a Secure zone, returning " << vStateToString(state) << endl);
65!
4127
        return state;
65✔
4128
      }
73✔
4129
      return zState;
18✔
4130
    }
91✔
4131
  }
4,596✔
4132

4133
  sortedRecords_t recordcontents;
4,462✔
4134
  for (const auto& record : records) {
4,942✔
4135
    recordcontents.insert(record.getContent());
4,942✔
4136
  }
4,942✔
4137

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

4144
  if (state == vState::Secure) {
4,460✔
4145
    LOG(prefix << name << ": Secure!" << endl);
4,427!
4146
    return vState::Secure;
4,427✔
4147
  }
4,427✔
4148

4149
  LOG(prefix << vStateToString(state) << "!" << endl);
33!
4150

4151
  bool skipThisLevelWhenLookingForMissedCuts = false;
33✔
4152
  if (name == qname && qtype == QType::DS && (type == QType::NSEC || type == QType::NSEC3)) {
33!
4153
    /* so we have a NSEC(3) record likely proving that the DS we were looking for does not exist,
4154
       but we cannot validate it:
4155
       - if there actually is a cut at this level, we will not be able to validate it anyway
4156
       - if there is no cut at this level, the only thing that can save us is a cut above
4157
    */
4158
    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!
4159
    skipThisLevelWhenLookingForMissedCuts = true;
2✔
4160
  }
2✔
4161

4162
  /* try again to get the missed cuts, harder this time */
4163
  auto zState = getValidationStatus(name, false, type == QType::DS || skipThisLevelWhenLookingForMissedCuts, depth, prefix);
33✔
4164
  LOG(prefix << name << ": Checking whether we missed a zone cut before returning a Bogus state" << endl);
33!
4165
  if (zState == vState::Secure) {
33✔
4166
    /* too bad */
4167
    LOG(prefix << name << ": We are still in a Secure zone, returning " << vStateToString(state) << endl);
25!
4168
    return state;
25✔
4169
  }
25✔
4170
  return zState;
8✔
4171
}
33✔
4172

4173
/* This function will check whether the answer should have the AA bit set, and will set if it should be set and isn't.
4174
   This is unfortunately needed to deal with very crappy so-called DNS servers */
4175
void SyncRes::fixupAnswer(const std::string& prefix, LWResult& lwr, const DNSName& qname, const QType qtype, const DNSName& auth, bool wasForwarded, bool rdQuery)
4176
{
11,852✔
4177
  const bool wasForwardRecurse = wasForwarded && rdQuery;
11,852✔
4178

4179
  if (wasForwardRecurse || lwr.d_aabit) {
11,852✔
4180
    /* easy */
4181
    return;
8,394✔
4182
  }
8,394✔
4183

4184
  for (const auto& rec : lwr.d_records) {
3,458✔
4185

4186
    if (rec.d_type == QType::OPT) {
3,406!
4187
      continue;
×
4188
    }
×
4189

4190
    if (rec.d_class != QClass::IN) {
3,406!
4191
      continue;
×
4192
    }
×
4193

4194
    if (rec.d_type == QType::ANY) {
3,406!
4195
      continue;
×
4196
    }
×
4197

4198
    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,406!
4199
      /* This is clearly an answer to the question we were asking, from an authoritative server that is allowed to send it.
4200
         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 */
4201
      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!
4202
      lwr.d_aabit = true;
10✔
4203
      return;
10✔
4204
    }
10✔
4205

4206
    if (rec.d_place != DNSResourceRecord::ANSWER) {
3,396!
4207
      /* we have scanned all the records in the answer section, if any, we are done */
4208
      return;
3,396✔
4209
    }
3,396✔
4210
  }
3,396✔
4211
}
3,458✔
4212

4213
static void allowAdditionalEntry(std::unordered_set<DNSName>& allowedAdditionals, const DNSRecord& rec)
4214
{
27,789✔
4215
  // As we only use a limited amount of NS names for resolving, limit number of additional names as
4216
  // well.  s_maxnsperresolve is a proper limit for the NS case and is also reasonable for other
4217
  // qtypes.  Allow one extra for qname itself, which is always in allowedAdditionals.
4218
  if (SyncRes::s_maxnsperresolve > 0 && allowedAdditionals.size() > SyncRes::s_maxnsperresolve + 1) {
27,789!
4219
    return;
12✔
4220
  }
12✔
4221
  switch (rec.d_type) {
27,777✔
4222
  case QType::MX:
25✔
4223
    if (auto mxContent = getRR<MXRecordContent>(rec)) {
25!
4224
      allowedAdditionals.insert(mxContent->d_mxname);
25✔
4225
    }
25✔
4226
    break;
25✔
4227
  case QType::NS:
15,131✔
4228
    if (auto nsContent = getRR<NSRecordContent>(rec)) {
15,131!
4229
      allowedAdditionals.insert(nsContent->getNS());
15,131✔
4230
    }
15,131✔
4231
    break;
15,131✔
4232
  case QType::SRV:
5✔
4233
    if (auto srvContent = getRR<SRVRecordContent>(rec)) {
5!
4234
      allowedAdditionals.insert(srvContent->d_target);
5✔
4235
    }
5✔
4236
    break;
5✔
4237
  case QType::SVCB: /* fall-through */
×
4238
  case QType::HTTPS:
×
4239
    if (auto svcbContent = getRR<SVCBBaseRecordContent>(rec)) {
×
4240
      if (svcbContent->getPriority() > 0) {
×
4241
        DNSName target = svcbContent->getTarget();
×
4242
        if (target.isRoot()) {
×
4243
          target = rec.d_name;
×
4244
        }
×
4245
        allowedAdditionals.insert(std::move(target));
×
4246
      }
×
4247
      else {
×
4248
        // FIXME: Alias mode not implemented yet
4249
      }
×
4250
    }
×
4251
    break;
×
4252
  case QType::NAPTR:
6✔
4253
    if (auto naptrContent = getRR<NAPTRRecordContent>(rec)) {
6!
4254
      auto flags = naptrContent->getFlags();
6✔
4255
      toLowerInPlace(flags);
6✔
4256
      if (flags.find('a') != string::npos || flags.find('s') != string::npos) {
6!
4257
        allowedAdditionals.insert(naptrContent->getReplacement());
6✔
4258
      }
6✔
4259
    }
6✔
4260
    break;
6✔
4261
  default:
12,610✔
4262
    break;
12,610✔
4263
  }
27,777✔
4264
}
27,777✔
4265

4266
static bool isRedirection(QType qtype)
4267
{
19,284✔
4268
  return qtype == QType::CNAME || qtype == QType::DNAME;
19,284✔
4269
}
19,284✔
4270

4271
void SyncRes::sanitizeRecords(const std::string& prefix, LWResult& lwr, const DNSName& qname, const QType qtype, const DNSName& auth, bool wasForwarded, bool rdQuery)
4272
{
11,852✔
4273
  const bool wasForwardRecurse = wasForwarded && rdQuery;
11,852✔
4274
  /* list of names for which we will allow A and AAAA records in the additional section
4275
     to remain */
4276
  std::unordered_set<DNSName> allowedAdditionals = {qname};
11,852✔
4277
  std::unordered_set<DNSName> allowedAnswerNames = {qname};
11,852✔
4278
  bool cnameSeen = false;
11,852✔
4279
  bool haveAnswers = false;
11,852✔
4280
  bool isNXDomain = false;
11,852✔
4281
  bool isNXQType = false;
11,852✔
4282

4283
  std::vector<bool> skipvec(lwr.d_records.size(), false);
11,852✔
4284
  unsigned int counter = 0;
11,852✔
4285
  unsigned int skipCount = 0;
11,852✔
4286

4287
  for (auto rec = lwr.d_records.cbegin(); rec != lwr.d_records.cend(); ++rec, ++counter) {
78,934✔
4288

4289
    // Allow OPT record containing EDNS(0) data
4290
    if (rec->d_type == QType::OPT) {
67,082✔
4291
      continue;
9,537✔
4292
    }
9,537✔
4293

4294
    // Disallow QClass != IN
4295
    if (rec->d_class != QClass::IN) {
57,545!
4296
      LOG(prefix << qname << ": Removing non internet-classed data received from " << auth << endl);
×
4297
      skipvec[counter] = true;
×
4298
      ++skipCount;
×
4299
      continue;
×
4300
    }
×
4301

4302
    // Disallow QType ANY in responses
4303
    if (rec->d_type == QType::ANY) {
57,545✔
4304
      LOG(prefix << qname << ": Removing 'ANY'-typed data received from " << auth << endl);
2!
4305
      skipvec[counter] = true;
2✔
4306
      ++skipCount;
2✔
4307
      continue;
2✔
4308
    }
2✔
4309

4310
    // Disallow any name not part of auth requested (i.e. disallow x.y.z if asking a NS authoritative for x.w.z)
4311
    if (!rec->d_name.isPartOf(auth)) {
57,543✔
4312
      LOG(prefix << qname << ": Removing record '" << rec->toString() << "' in the " << DNSResourceRecord::placeString(rec->d_place) << " section received from " << auth << endl);
567!
4313
      skipvec[counter] = true;
567✔
4314
      ++skipCount;
567✔
4315
      continue;
567✔
4316
    }
567✔
4317

4318
    // Disallow QType DNAME in non-answer section or containing an answer that is not a parent of or equal to the question name
4319
    // i.e. disallowed bar.example.com. DNAME bar.example.net. when asking foo.example.com
4320
    // But allow it when asking for foo.bar.example.com.
4321
    if (rec->d_type == QType::DNAME && (rec->d_place != DNSResourceRecord::ANSWER || !qname.isPartOf(rec->d_name))) {
56,976!
4322
      LOG(prefix << qname << ": Removing invalid DNAME record '" << rec->toString() << "' in the " << DNSResourceRecord::placeString(rec->d_place) << " section received from " << auth << endl);
2!
4323
      skipvec[counter] = true;
2✔
4324
      ++skipCount;
2✔
4325
      continue;
2✔
4326
    }
2✔
4327

4328
    /* dealing with the records in answer */
4329
    if (rec->d_place == DNSResourceRecord::ANSWER) {
56,974✔
4330
      // Special case for Amazon CNAME records
4331
      if (!(lwr.d_aabit || wasForwardRecurse)) {
13,694!
4332
        /* for now we allow a CNAME for the exact qname in ANSWER with AA=0, because Amazon DNS servers
4333
           are sending such responses */
4334
        if (rec->d_type != QType::CNAME || qname != rec->d_name) {
×
4335
          LOG(prefix << qname << ": Removing record '" << rec->toString() << "' in the ANSWER section without the AA bit set received from " << auth << endl);
×
4336
          skipvec[counter] = true;
×
4337
          ++skipCount;
×
4338
          continue;
×
4339
        }
×
4340
      }
×
4341
      // Disallow answer records not answering the QType requested. ANY, CNAME, DNAME, RRSIG complicate matters here
4342
      if (qtype != QType::ANY && rec->d_type != qtype.getCode() && !isRedirection(rec->d_type) && rec->d_type != QType::RRSIG) {
13,694✔
4343
        LOG(prefix << qname << ": Removing irrelevant record '" << rec->toString() << "' in the ANSWER section received from " << auth << endl);
6!
4344
        skipvec[counter] = true;
6✔
4345
        ++skipCount;
6✔
4346
        continue;
6✔
4347
      }
6✔
4348

4349
      haveAnswers = true;
13,688✔
4350
      if (rec->d_type == QType::CNAME) {
13,688✔
4351
        if (auto cnametarget = getRR<CNAMERecordContent>(*rec); cnametarget != nullptr) {
1,569!
4352
          allowedAnswerNames.insert(cnametarget->getTarget());
1,569✔
4353
        }
1,569✔
4354
        cnameSeen = cnameSeen || qname == rec->d_name;
1,569✔
4355
      }
1,569✔
4356
      else if (rec->d_type == QType::DNAME) {
12,119✔
4357
        // We have checked the DNAME rec->d_name above, the actual answer will be synthesized in a later step
4358
        allowedAnswerNames.insert(rec->d_name);
32✔
4359
      }
32✔
4360
      allowAdditionalEntry(allowedAdditionals, *rec);
13,688✔
4361
    }
13,688✔
4362

4363
    /* dealing with the records in authority */
4364
    // Only allow NS, DS, SOA, RRSIG, NSEC, NSEC3 in AUTHORITY section
4365
    else if (rec->d_place == DNSResourceRecord::AUTHORITY) {
43,280✔
4366
      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) {
25,605✔
4367
        LOG(prefix << qname << ": Removing irrelevant record '" << rec->toString() << "' in the AUTHORITY section received from " << auth << endl);
2!
4368
        skipvec[counter] = true;
2✔
4369
        ++skipCount;
2✔
4370
        continue;
2✔
4371
      }
2✔
4372
      if (rec->d_type == QType::NS && !d_updatingRootNS && rec->d_name == g_rootdnsname) {
25,603!
4373
        /*
4374
         * We don't want to pick up root NS records in AUTHORITY and their associated ADDITIONAL sections of random queries.
4375
         * So remove them and don't add them to allowedAdditionals.
4376
         */
4377
        LOG(prefix << qname << ": Removing NS record '" << rec->toString() << "' in the AUTHORITY section of a response received from " << auth << endl);
2!
4378
        skipvec[counter] = true;
2✔
4379
        ++skipCount;
2✔
4380
        continue;
2✔
4381
      }
2✔
4382

4383
      if (rec->d_type == QType::SOA) {
25,601✔
4384
        // Disallow a SOA record with a name that is not a parent of or equal to the name we asked
4385
        if (!qname.isPartOf(rec->d_name)) {
1,477✔
4386
          LOG(prefix << qname << ": Removing irrelevant SOA record '" << rec->toString() << "' in the AUTHORITY section received from " << auth << endl);
2!
4387
          skipvec[counter] = true;
2✔
4388
          ++skipCount;
2✔
4389
          continue;
2✔
4390
        }
2✔
4391
        // Disallow SOA without AA bit (except for forward with RD=1)
4392
        if (!(lwr.d_aabit || wasForwardRecurse)) {
1,475!
4393
          LOG(prefix << qname << ": Removing irrelevant record (AA not set) '" << rec->toString() << "' in the AUTHORITY section received from " << auth << endl);
×
4394
          skipvec[counter] = true;
×
4395
          ++skipCount;
×
4396
          continue;
×
4397
        }
×
4398

4399
        if (!haveAnswers) {
1,475✔
4400
          switch (lwr.d_rcode) {
1,460!
4401
          case RCode::NXDomain:
119✔
4402
            isNXDomain = true;
119✔
4403
            break;
119✔
4404
          case RCode::NoError:
1,341✔
4405
            isNXQType = true;
1,341✔
4406
            break;
1,341✔
4407
          }
1,460✔
4408
        }
1,460✔
4409
      }
1,475✔
4410
    }
25,601✔
4411
    /* dealing with records in additional */
4412
    else if (rec->d_place == DNSResourceRecord::ADDITIONAL) {
17,675✔
4413
      if (rec->d_type != QType::A && rec->d_type != QType::AAAA && rec->d_type != QType::RRSIG) {
17,674✔
4414
        LOG(prefix << qname << ": Removing irrelevant record '" << rec->toString() << "' in the ADDITIONAL section received from " << auth << endl);
2!
4415
        skipvec[counter] = true;
2✔
4416
        ++skipCount;
2✔
4417
        continue;
2✔
4418
      }
2✔
4419
    }
17,674✔
4420
  } // end of first loop, handled answer and most of authority section
56,974✔
4421

4422
  sanitizeRecordsPass2(prefix, lwr, qname, qtype, auth, allowedAnswerNames, allowedAdditionals, cnameSeen, isNXDomain, isNXQType, skipvec, skipCount);
11,852✔
4423
}
11,852✔
4424

4425
void SyncRes::sanitizeRecordsPass2(const std::string& prefix, LWResult& lwr, const DNSName& qname, const QType qtype, const DNSName& auth, std::unordered_set<DNSName>& allowedAnswerNames, std::unordered_set<DNSName>& allowedAdditionals, bool cnameSeen, bool isNXDomain, bool isNXQType, std::vector<bool>& skipvec, unsigned int& skipCount)
4426
{
11,852✔
4427
  // Second loop, we know now if the answer was NxDomain or NoData
4428
  unsigned int counter = 0;
11,852✔
4429
  for (auto rec = lwr.d_records.cbegin(); rec != lwr.d_records.cend(); ++rec, ++counter) {
78,930✔
4430

4431
    if (skipvec[counter]) {
67,078✔
4432
      continue;
585✔
4433
    }
585✔
4434
    // Allow OPT record containing EDNS(0) data
4435
    if (rec->d_type == QType::OPT) {
66,493✔
4436
      continue;
9,537✔
4437
    }
9,537✔
4438

4439
    if (rec->d_place == DNSResourceRecord::ANSWER) {
56,956✔
4440
      if (allowedAnswerNames.count(rec->d_name) == 0) {
13,688✔
4441
        LOG(prefix << qname << ": Removing irrelevent record '" << rec->toString() << "' in the ANSWER section received from " << auth << endl);
10!
4442
        skipvec[counter] = true;
10✔
4443
        ++skipCount;
10✔
4444
      }
10✔
4445
      // If we have a CNAME, skip answer records for the requested type
4446
      if (cnameSeen && rec->d_type == qtype && rec->d_name == qname && qtype != QType::CNAME) {
13,688!
4447
        LOG(prefix << qname << ": Removing answer record in presence of CNAME record '" << rec->toString() << "' in the ANSWER section received from " << auth << endl);
6!
4448
        skipvec[counter] = true;
6✔
4449
        ++skipCount;
6✔
4450
        continue;
6✔
4451
      }
6✔
4452
    }
13,688✔
4453
    if (rec->d_place == DNSResourceRecord::AUTHORITY && rec->d_type == QType::NS) {
56,950✔
4454
      if (isNXDomain || isNXQType) {
14,083!
4455
        /*
4456
         * We don't want to pick up NS records in AUTHORITY and their ADDITIONAL sections of NXDomain answers
4457
         * because they are somewhat easy to insert into a large, fragmented UDP response
4458
         * for an off-path attacker by injecting spoofed UDP fragments. So do not add these to allowedAdditionals.
4459
         */
4460
        LOG(prefix << qname << ": Removing NS record '" << rec->toString() << "' in the AUTHORITY section of a " << (isNXDomain ? "NXD" : "NXQTYPE") << " response received from " << auth << endl);
2!
4461
        skipvec[counter] = true;
2✔
4462
        ++skipCount;
2✔
4463
        continue;
2✔
4464
      }
2✔
4465
      allowAdditionalEntry(allowedAdditionals, *rec);
14,081✔
4466
    }
14,081✔
4467
    /* dealing with the records in additional */
4468
    else if (rec->d_place == DNSResourceRecord::ADDITIONAL) {
42,867✔
4469
      if (allowedAdditionals.count(rec->d_name) == 0) {
17,672✔
4470
        LOG(prefix << qname << ": Removing irrelevant record '" << rec->toString() << "' in the ADDITIONAL section received from " << auth << endl);
10!
4471
        skipvec[counter] = true;
10✔
4472
        ++skipCount;
10✔
4473
        continue;
10✔
4474
      }
10✔
4475
    }
17,672✔
4476
  }
56,950✔
4477
  if (skipCount > 0) {
11,852✔
4478
    std::vector<DNSRecord> vec;
99✔
4479
    vec.reserve(lwr.d_records.size() - skipCount);
99✔
4480
    for (counter = 0; counter < lwr.d_records.size(); ++counter) {
1,065✔
4481
      if (!skipvec[counter]) {
966✔
4482
        vec.emplace_back(std::move(lwr.d_records[counter]));
353✔
4483
      }
353✔
4484
    }
966✔
4485
    lwr.d_records = std::move(vec);
99✔
4486
  }
99✔
4487
#ifdef notyet
4488
  // As dedupping is relatively expensive and having dup records not really hurts as far as we have seen, do not dedup.
4489
  if (auto count = pdns::dedupRecords(lwr.d_records); count > 0) {
4490
    LOG(prefix << qname << ": Removed " << count << " duplicate records from response received from " << auth << endl);
4491
  }
4492
#endif
4493
}
11,852✔
4494

4495
void SyncRes::rememberParentSetIfNeeded(const DNSName& domain, const vector<DNSRecord>& newRecords, unsigned int depth, const string& prefix)
4496
{
478✔
4497
  vector<DNSRecord> existing;
478✔
4498
  bool wasAuth = false;
478✔
4499
  auto ttl = g_recCache->get(d_now.tv_sec, domain, QType::NS, MemRecursorCache::None, &existing, d_cacheRemote, d_routingTag, nullptr, nullptr, nullptr, nullptr, &wasAuth);
478✔
4500

4501
  if (ttl <= 0 || wasAuth) {
478✔
4502
    return;
41✔
4503
  }
41✔
4504
  {
437✔
4505
    auto lock = s_savedParentNSSet.lock();
437✔
4506
    if (lock->find(domain) != lock->end()) {
437!
4507
      // no relevant data, or we already stored the parent data
4508
      return;
×
4509
    }
×
4510
  }
437✔
4511

4512
  set<DNSName> authSet;
437✔
4513
  for (const auto& dnsRecord : newRecords) {
1,849✔
4514
    auto content = getRR<NSRecordContent>(dnsRecord);
1,849✔
4515
    authSet.insert(content->getNS());
1,849✔
4516
  }
1,849✔
4517
  // The glue IPs could also differ, but we're not checking that yet, we're only looking for parent NS records not
4518
  // in the child set
4519
  bool shouldSave = false;
437✔
4520
  for (const auto& dnsRecord : existing) {
1,849✔
4521
    auto content = getRR<NSRecordContent>(dnsRecord);
1,849✔
4522
    if (authSet.count(content->getNS()) == 0) {
1,849✔
4523
      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!
4524
      shouldSave = true;
4✔
4525
      break;
4✔
4526
    }
4✔
4527
  }
1,849✔
4528

4529
  if (shouldSave) {
437✔
4530
    map<DNSName, vector<ComboAddress>> entries;
4✔
4531
    for (const auto& dnsRecord : existing) {
30✔
4532
      auto content = getRR<NSRecordContent>(dnsRecord);
30✔
4533
      const DNSName& name = content->getNS();
30✔
4534
      set<GetBestNSAnswer> beenthereIgnored;
30✔
4535
      unsigned int nretrieveAddressesForNSIgnored{};
30✔
4536
      auto addresses = getAddrs(name, depth, prefix, beenthereIgnored, true, nretrieveAddressesForNSIgnored);
30✔
4537
      entries.emplace(name, addresses);
30✔
4538
    }
30✔
4539
    s_savedParentNSSet.lock()->emplace(domain, std::move(entries), d_now.tv_sec + ttl);
4✔
4540
  }
4✔
4541
}
437✔
4542

4543
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)
4544
{
11,852✔
4545
  bool wasForwardRecurse = wasForwarded && rdQuery;
11,852✔
4546
  tcache_t tcache;
11,852✔
4547

4548
  fixupAnswer(prefix, lwr, qname, qtype, auth, wasForwarded, rdQuery);
11,852✔
4549
  sanitizeRecords(prefix, lwr, qname, qtype, auth, wasForwarded, rdQuery);
11,852✔
4550

4551
  MemRecursorCache::AuthRecsVec authorityRecs;
11,852✔
4552
  bool isCNAMEAnswer = false;
11,852✔
4553
  bool isDNAMEAnswer = false;
11,852✔
4554
  DNSName seenAuth;
11,852✔
4555

4556
  // names that might be expanded from a wildcard, and thus require denial of existence proof
4557
  // this is the queried name and any part of the CNAME chain from the queried name
4558
  // the key is the name itself, the value is initially false and is set to true once we have
4559
  // confirmed it was actually expanded from a wildcard
4560
  std::map<DNSName, bool> wildcardCandidates{{qname, false}};
11,852✔
4561

4562
  if (rdQuery) {
11,852✔
4563
    std::unordered_map<DNSName, DNSName> cnames;
58✔
4564
    for (const auto& rec : lwr.d_records) {
146✔
4565
      if (rec.d_type != QType::CNAME || rec.d_class != QClass::IN) {
146!
4566
        continue;
134✔
4567
      }
134✔
4568
      if (auto content = getRR<CNAMERecordContent>(rec)) {
12!
4569
        cnames[rec.d_name] = DNSName(content->getTarget());
12✔
4570
      }
12✔
4571
    }
12✔
4572
    auto initial = qname;
58✔
4573
    while (true) {
68✔
4574
      auto cnameIt = cnames.find(initial);
68✔
4575
      if (cnameIt == cnames.end()) {
68✔
4576
        break;
56✔
4577
      }
56✔
4578
      initial = cnameIt->second;
12✔
4579
      if (!wildcardCandidates.emplace(initial, false).second) {
12✔
4580
        // CNAME Loop
4581
        break;
2✔
4582
      }
2✔
4583
    }
12✔
4584
  }
58✔
4585

4586
  for (auto& rec : lwr.d_records) {
66,466✔
4587
    if (rec.d_type == QType::OPT || rec.d_class != QClass::IN) {
66,466✔
4588
      continue;
9,537✔
4589
    }
9,537✔
4590

4591
    rec.d_ttl = min(s_maxcachettl, rec.d_ttl);
56,929✔
4592

4593
    if (!isCNAMEAnswer && rec.d_place == DNSResourceRecord::ANSWER && rec.d_type == QType::CNAME && (!(qtype == QType::CNAME)) && rec.d_name == qname && !isDNAMEAnswer) {
56,929!
4594
      isCNAMEAnswer = true;
1,550✔
4595
    }
1,550✔
4596
    if (!isDNAMEAnswer && rec.d_place == DNSResourceRecord::ANSWER && rec.d_type == QType::DNAME && qtype != QType::DNAME && qname.isPartOf(rec.d_name)) {
56,930!
4597
      isDNAMEAnswer = true;
32✔
4598
      isCNAMEAnswer = false;
32✔
4599
    }
32✔
4600

4601
    if (rec.d_type == QType::SOA && rec.d_place == DNSResourceRecord::AUTHORITY && qname.isPartOf(rec.d_name)) {
56,929!
4602
      seenAuth = rec.d_name;
1,475✔
4603
    }
1,475✔
4604

4605
    const auto labelCount = rec.d_name.countLabels();
56,929✔
4606
    if (rec.d_type == QType::RRSIG) {
56,929✔
4607
      auto rrsig = getRR<RRSIGRecordContent>(rec);
7,809✔
4608
      if (rrsig) {
7,809!
4609
        /* As illustrated in rfc4035's Appendix B.6, the RRSIG label
4610
           count can be lower than the name's label count if it was
4611
           synthesized from the wildcard. Note that the difference might
4612
           be > 1. */
4613
        if (auto wcIt = wildcardCandidates.find(rec.d_name); wcIt != wildcardCandidates.end() && isWildcardExpanded(labelCount, *rrsig)) {
7,809✔
4614
          wcIt->second = true;
245✔
4615
          gatherWildcardProof = true;
245✔
4616
          if (!isWildcardExpandedOntoItself(rec.d_name, labelCount, *rrsig)) {
245✔
4617
            /* if we have a wildcard expanded onto itself, we don't need to prove
4618
               that the exact name doesn't exist because it actually does.
4619
               We still want to gather the corresponding NSEC/NSEC3 records
4620
               to pass them to our client in case it wants to validate by itself.
4621
            */
4622
            LOG(prefix << qname << ": RRSIG indicates the name was synthesized from a wildcard, we need a wildcard proof" << endl);
237!
4623
            needWildcardProof = true;
237✔
4624
          }
237✔
4625
          else {
8✔
4626
            LOG(prefix << qname << ": RRSIG indicates the name was synthesized from a wildcard expanded onto itself, we need to gather wildcard proof" << endl);
8!
4627
          }
8✔
4628
          wildcardLabelsCount = rrsig->d_labels;
245✔
4629
        }
245✔
4630

4631
        // cerr<<"Got an RRSIG for "<<DNSRecordContent::NumberToType(rrsig->d_type)<<" with name '"<<rec.d_name<<"' and place "<<rec.d_place<<endl;
4632
        tcache[{rec.d_name, rrsig->d_type, rec.d_place}].signatures.push_back(rrsig);
7,809✔
4633
        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,809✔
4634
      }
7,809✔
4635
    }
7,809✔
4636
  }
56,929✔
4637

4638
  /* if we have a positive answer synthesized from a wildcard,
4639
     we need to store the corresponding NSEC/NSEC3 records proving
4640
     that the exact name did not exist in the negative cache */
4641
  if (gatherWildcardProof) {
11,852✔
4642
    for (const auto& rec : lwr.d_records) {
1,195✔
4643
      if (rec.d_type == QType::OPT || rec.d_class != QClass::IN) {
1,195!
4644
        continue;
213✔
4645
      }
213✔
4646

4647
      if (nsecTypes.count(rec.d_type) != 0) {
982✔
4648
        authorityRecs.emplace_back(rec);
243✔
4649
      }
243✔
4650
      else if (rec.d_type == QType::RRSIG) {
739✔
4651
        auto rrsig = getRR<RRSIGRecordContent>(rec);
490✔
4652
        if (rrsig && nsecTypes.count(rrsig->d_type) != 0) {
490!
4653
          authorityRecs.emplace_back(rec);
243✔
4654
        }
243✔
4655
      }
490✔
4656
    }
982✔
4657
  }
241✔
4658

4659
  // reap all answers from this packet that are acceptable
4660
  for (auto& rec : lwr.d_records) {
66,467✔
4661
    if (rec.d_type == QType::OPT) {
66,467✔
4662
      LOG(prefix << qname << ": OPT answer '" << rec.d_name << "' from '" << auth << "' nameservers" << endl);
9,537!
4663
      continue;
9,537✔
4664
    }
9,537✔
4665

4666
    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 << " ");
56,930✔
4667

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

4670
    if (rec.d_name.isPartOf(auth)) {
56,932✔
4671
      if (rec.d_type == QType::RRSIG) {
56,929✔
4672
        LOG("RRSIG - separate" << endl);
7,809!
4673
      }
7,809✔
4674
      else if (rec.d_type == QType::DS && rec.d_name == auth) {
49,120✔
4675
        LOG("NO - DS provided by child zone" << endl);
2!
4676
      }
2✔
4677
      else {
49,118✔
4678
        bool haveLogged = false;
49,118✔
4679
        if (isDNAMEAnswer && rec.d_type == QType::CNAME) {
49,118✔
4680
          LOG("NO - we already have a DNAME answer for this domain" << endl);
30!
4681
          continue;
30✔
4682
        }
30✔
4683
        if (!t_sstorage.domainmap->empty()) {
49,090✔
4684
          // Check if we are authoritative for a zone in this answer
4685
          DNSName tmp_qname(rec.d_name);
44,727✔
4686
          // We may be auth for domain example.com, but the DS record needs to come from the parent (.com) nameserver
4687
          if (rec.d_type == QType::DS) {
44,727✔
4688
            tmp_qname.chopOff();
788✔
4689
          }
788✔
4690
          auto auth_domain_iter = getBestAuthZone(&tmp_qname);
44,727✔
4691
          if (auth_domain_iter != t_sstorage.domainmap->end() && auth.countLabels() <= auth_domain_iter->first.countLabels()) {
44,727✔
4692
            if (auth_domain_iter->first != auth) {
313✔
4693
              LOG("NO! - we are authoritative for the zone " << auth_domain_iter->first << endl);
2!
4694
              continue;
2✔
4695
            }
2✔
4696
            LOG("YES! - This answer was ");
311!
4697
            if (!wasForwarded) {
311✔
4698
              LOG("retrieved from the local auth store.");
134!
4699
            }
134✔
4700
            else {
177✔
4701
              LOG("received from a server we forward to.");
177!
4702
            }
177✔
4703
            haveLogged = true;
311✔
4704
            LOG(endl);
311!
4705
          }
311✔
4706
        }
44,727✔
4707
        if (!haveLogged) {
49,089✔
4708
          LOG("YES!" << endl);
48,776✔
4709
        }
48,776✔
4710

4711
        rec.d_ttl = min(s_maxcachettl, rec.d_ttl);
49,086✔
4712

4713
        DNSRecord dnsRecord(rec);
49,086✔
4714
        tcache[{rec.d_name, rec.d_type, rec.d_place}].d_ttl_time = d_now.tv_sec;
49,086✔
4715
        dnsRecord.d_ttl += d_now.tv_sec;
49,086✔
4716
        dnsRecord.d_place = DNSResourceRecord::ANSWER;
49,086✔
4717
        tcache[{rec.d_name, rec.d_type, rec.d_place}].records.push_back(std::move(dnsRecord));
49,086✔
4718
      }
49,086✔
4719
    }
56,929✔
4720
    else
2,147,483,647✔
4721
      LOG("NO!" << endl);
2,147,496,583!
4722
  }
56,898✔
4723

4724
  // supplant
4725
  for (auto& entry : tcache) {
34,331✔
4726
    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)
34,331✔
4727
      uint32_t lowestTTD = computeLowestTTD(entry.second.records, entry.second.signatures, entry.second.signaturesTTL, authorityRecs);
11,597✔
4728

4729
      for (auto& record : entry.second.records) {
26,354✔
4730
        record.d_ttl = lowestTTD; // boom
26,354✔
4731
      }
26,354✔
4732
    }
11,597✔
4733
  }
34,331✔
4734

4735
  for (auto tCacheEntry = tcache.begin(); tCacheEntry != tcache.end(); ++tCacheEntry) {
46,181✔
4736

4737
    if (tCacheEntry->second.records.empty()) { // this happens when we did store signatures, but passed on the records themselves
34,329!
4738
      continue;
×
4739
    }
×
4740

4741
    /* Even if the AA bit is set, additional data cannot be considered
4742
       as authoritative. This is especially important during validation
4743
       because keeping records in the additional section is allowed even
4744
       if the corresponding RRSIGs are not included, without setting the TC
4745
       bit, as stated in rfc4035's section 3.1.1.  Including RRSIG RRs in a Response:
4746
       "When placing a signed RRset in the Additional section, the name
4747
       server MUST also place its RRSIG RRs in the Additional section.
4748
       If space does not permit inclusion of both the RRset and its
4749
       associated RRSIG RRs, the name server MAY retain the RRset while
4750
       dropping the RRSIG RRs.  If this happens, the name server MUST NOT
4751
       set the TC bit solely because these RRSIG RRs didn't fit."
4752
    */
4753
    bool isAA = lwr.d_aabit && tCacheEntry->first.place != DNSResourceRecord::ADDITIONAL;
34,329✔
4754
    /* if we forwarded the query to a recursor, we can expect the answer to be signed,
4755
       even if the answer is not AA. Of course that's not only true inside a Secure
4756
       zone, but we check that below. */
4757
    bool expectSignature = tCacheEntry->first.place == DNSResourceRecord::ANSWER || ((lwr.d_aabit || wasForwardRecurse) && tCacheEntry->first.place != DNSResourceRecord::ADDITIONAL);
34,329✔
4758
    /* in a non authoritative answer, we only care about the DS record (or lack of)  */
4759
    if (!isAA && (tCacheEntry->first.type == QType::DS || tCacheEntry->first.type == QType::NSEC || tCacheEntry->first.type == QType::NSEC3) && tCacheEntry->first.place == DNSResourceRecord::AUTHORITY) {
34,329✔
4760
      expectSignature = true;
4,247✔
4761
    }
4,247✔
4762

4763
    if (isCNAMEAnswer && (tCacheEntry->first.place != DNSResourceRecord::ANSWER || tCacheEntry->first.type != QType::CNAME || tCacheEntry->first.name != qname)) {
34,329✔
4764
      /*
4765
        rfc2181 states:
4766
        Note that the answer section of an authoritative answer normally
4767
        contains only authoritative data.  However when the name sought is an
4768
        alias (see section 10.1.1) only the record describing that alias is
4769
        necessarily authoritative.  Clients should assume that other records
4770
        may have come from the server's cache.  Where authoritative answers
4771
        are required, the client should query again, using the canonical name
4772
        associated with the alias.
4773
      */
4774
      isAA = false;
282✔
4775
      expectSignature = false;
282✔
4776
    }
282✔
4777
    if (isDNAMEAnswer && (tCacheEntry->first.place != DNSResourceRecord::ANSWER || tCacheEntry->first.type != QType::DNAME || !qname.isPartOf(tCacheEntry->first.name))) {
34,329!
4778
      /* see above */
4779
      isAA = false;
6✔
4780
      expectSignature = false;
6✔
4781
    }
6✔
4782

4783
    if ((isCNAMEAnswer || isDNAMEAnswer) && tCacheEntry->first.place == DNSResourceRecord::AUTHORITY && tCacheEntry->first.type == QType::NS && auth == tCacheEntry->first.name) {
34,329✔
4784
      /* These NS can't be authoritative since we have a CNAME/DNAME answer for which (see above) only the
4785
         record describing that alias is necessarily authoritative.
4786
         But if we allow the current auth, which might be serving the child zone, to raise the TTL
4787
         of non-authoritative NS in the cache, they might be able to keep a "ghost" zone alive forever,
4788
         even after the delegation is gone from the parent.
4789
         So let's just do nothing with them, we can fetch them directly if we need them.
4790
      */
4791
      LOG(prefix << qname << ": Skipping authority NS from '" << auth << "' nameservers in CNAME/DNAME answer " << tCacheEntry->first.name << "|" << DNSRecordContent::NumberToType(tCacheEntry->first.type) << endl);
2!
4792
      continue;
2✔
4793
    }
2✔
4794

4795
    /*
4796
     * RFC 6672 section 5.3.1
4797
     *  In any response, a signed DNAME RR indicates a non-terminal
4798
     *  redirection of the query.  There might or might not be a server-
4799
     *  synthesized CNAME in the answer section; if there is, the CNAME will
4800
     *  never be signed.  For a DNSSEC validator, verification of the DNAME
4801
     *  RR and then that the CNAME was properly synthesized is sufficient
4802
     *  proof.
4803
     *
4804
     * We do the synthesis check in processRecords, here we make sure we
4805
     * don't validate the CNAME.
4806
     */
4807
    if (isDNAMEAnswer && tCacheEntry->first.type == QType::CNAME) {
34,327!
4808
      expectSignature = false;
×
4809
    }
×
4810

4811
    vState recordState = vState::Indeterminate;
34,327✔
4812

4813
    if (expectSignature && shouldValidate()) {
34,327✔
4814
      vState initialState = getValidationStatus(tCacheEntry->first.name, !tCacheEntry->second.signatures.empty(), tCacheEntry->first.type == QType::DS, depth, prefix);
9,885✔
4815
      LOG(prefix << qname << ": Got initial zone status " << initialState << " for record " << tCacheEntry->first.name << "|" << DNSRecordContent::NumberToType(tCacheEntry->first.type) << endl);
9,885!
4816

4817
      if (initialState == vState::Secure) {
9,885✔
4818
        if (tCacheEntry->first.type == QType::DNSKEY && tCacheEntry->first.place == DNSResourceRecord::ANSWER && tCacheEntry->first.name == getSigner(tCacheEntry->second.signatures)) {
5,982!
4819
          LOG(prefix << qname << ": Validating DNSKEY for " << tCacheEntry->first.name << endl);
1,404!
4820
          recordState = validateDNSKeys(tCacheEntry->first.name, tCacheEntry->second.records, tCacheEntry->second.signatures, depth, prefix);
1,404✔
4821
        }
1,404✔
4822
        else {
4,578✔
4823
          LOG(prefix << qname << ": Validating non-additional " << QType(tCacheEntry->first.type).toString() << " record for " << tCacheEntry->first.name << endl);
4,578!
4824
          recordState = validateRecordsWithSigs(depth, prefix, qname, qtype, tCacheEntry->first.name, QType(tCacheEntry->first.type), tCacheEntry->second.records, tCacheEntry->second.signatures);
4,578✔
4825
        }
4,578✔
4826
      }
5,982✔
4827
      else {
3,903✔
4828
        recordState = initialState;
3,903✔
4829
        LOG(prefix << qname << ": Skipping validation because the current state is " << recordState << endl);
3,903!
4830
      }
3,903✔
4831

4832
      LOG(prefix << qname << ": Validation result is " << recordState << ", current state is " << state << endl);
9,885!
4833
      if (state != recordState) {
9,885✔
4834
        updateValidationState(qname, state, recordState, prefix);
6,149✔
4835
      }
6,149✔
4836
    }
9,885✔
4837

4838
    if (vStateIsBogus(recordState)) {
34,327✔
4839
      /* this is a TTD by now, be careful */
4840
      for (auto& record : tCacheEntry->second.records) {
509✔
4841
        auto newval = std::min(record.d_ttl, static_cast<uint32_t>(s_maxbogusttl + d_now.tv_sec));
509✔
4842
        record.d_ttl = newval;
509✔
4843
      }
509✔
4844
      tCacheEntry->second.d_ttl_time = d_now.tv_sec;
180✔
4845
    }
180✔
4846

4847
    /* We don't need to store NSEC3 records in the positive cache because:
4848
       - we don't allow direct NSEC3 queries
4849
       - denial of existence proofs in wildcard expanded positive responses are stored in authorityRecs
4850
       - denial of existence proofs for negative responses are stored in the negative cache
4851
       We also don't want to cache non-authoritative data except for:
4852
       - records coming from non forward-recurse servers (those will never be AA)
4853
       - DS (special case)
4854
       - NS, A and AAAA (used for infra queries)
4855
    */
4856
    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)) {
34,327✔
4857

4858
      bool doCache = true;
30,849✔
4859
      if (tCacheEntry->first.place == DNSResourceRecord::ANSWER && ednsmask) {
30,849✔
4860
        const bool isv4 = ednsmask->isIPv4();
56✔
4861
        if ((isv4 && s_ecsipv4nevercache) || (!isv4 && s_ecsipv6nevercache)) {
56!
4862
          doCache = false;
×
4863
        }
×
4864
        // If ednsmask is relevant, we do not want to cache if the scope prefix length is large and TTL is small
4865
        if (doCache && s_ecscachelimitttl > 0) {
56!
4866
          bool manyMaskBits = (isv4 && ednsmask->getBits() > s_ecsipv4cachelimit) || (!isv4 && ednsmask->getBits() > s_ecsipv6cachelimit);
6!
4867

4868
          if (manyMaskBits) {
6✔
4869
            uint32_t minttl = UINT32_MAX;
2✔
4870
            for (const auto& iter : tCacheEntry->second.records) {
2✔
4871
              if (iter.d_ttl < minttl) {
2!
4872
                minttl = iter.d_ttl;
2✔
4873
              }
2✔
4874
            }
2✔
4875
            bool ttlIsSmall = minttl < s_ecscachelimitttl + d_now.tv_sec;
2✔
4876
            if (ttlIsSmall) {
2!
4877
              // Case: many bits and ttlIsSmall
4878
              doCache = false;
2✔
4879
            }
2✔
4880
          }
2✔
4881
        }
6✔
4882
      }
56✔
4883

4884
      d_fromAuthIP = remoteIP;
30,849✔
4885

4886
      if (doCache) {
30,850✔
4887
        // Check if we are going to replace a non-auth (parent) NS recordset
4888
        if (isAA && tCacheEntry->first.type == QType::NS && s_save_parent_ns_set) {
30,848!
4889
          rememberParentSetIfNeeded(tCacheEntry->first.name, tCacheEntry->second.records, depth, prefix);
478✔
4890
        }
478✔
4891
        bool thisRRNeedsWildcardProof = false;
30,848✔
4892
        if (gatherWildcardProof) {
30,848✔
4893
          if (auto wcIt = wildcardCandidates.find(tCacheEntry->first.name); wcIt != wildcardCandidates.end() && wcIt->second) {
464✔
4894
            thisRRNeedsWildcardProof = true;
245✔
4895
          }
245✔
4896
        }
464✔
4897
        g_recCache->replace(d_now.tv_sec, tCacheEntry->first.name, tCacheEntry->first.type, tCacheEntry->second.records, tCacheEntry->second.signatures, thisRRNeedsWildcardProof ? authorityRecs : *MemRecursorCache::s_emptyAuthRecs, tCacheEntry->first.type == QType::DS ? true : isAA, auth, tCacheEntry->first.place == DNSResourceRecord::ANSWER ? ednsmask : boost::none, d_routingTag, recordState, remoteIP, d_refresh, tCacheEntry->second.d_ttl_time);
30,848✔
4898

4899
        // Delete potential negcache entry. When a record recovers with serve-stale the negcache entry can cause the wrong entry to
4900
        // be served, as negcache entries are checked before record cache entries
4901
        if (NegCache::s_maxServedStaleExtensions > 0) {
30,848✔
4902
          g_negCache->wipeTyped(tCacheEntry->first.name, tCacheEntry->first.type);
192✔
4903
        }
192✔
4904

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

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

4914
            std::vector<DNSRecord> content;
217✔
4915
            content.reserve(tCacheEntry->second.records.size());
217✔
4916
            for (const auto& record : tCacheEntry->second.records) {
217✔
4917
              DNSRecord nonExpandedRecord(record);
217✔
4918
              nonExpandedRecord.d_name = realOwner;
217✔
4919
              content.push_back(std::move(nonExpandedRecord));
217✔
4920
            }
217✔
4921

4922
            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!
4923
          }
217✔
4924
        }
217✔
4925
      }
30,848✔
4926
    }
30,849✔
4927

4928
    if (seenAuth.empty() && !tCacheEntry->second.signatures.empty()) {
34,327✔
4929
      seenAuth = getSigner(tCacheEntry->second.signatures);
5,050✔
4930
    }
5,050✔
4931

4932
    if (g_aggressiveNSECCache && (tCacheEntry->first.type == QType::NSEC || tCacheEntry->first.type == QType::NSEC3) && recordState == vState::Secure && !seenAuth.empty()) {
34,327✔
4933
      // Good candidate for NSEC{,3} caching
4934
      g_aggressiveNSECCache->insertNSEC(seenAuth, tCacheEntry->first.name, tCacheEntry->second.records.at(0), tCacheEntry->second.signatures, tCacheEntry->first.type == QType::NSEC3, qname, qtype);
2,540✔
4935
    }
2,540✔
4936

4937
    if (tCacheEntry->first.place == DNSResourceRecord::ANSWER && ednsmask) {
34,327✔
4938
      d_wasVariable = true;
56✔
4939
    }
56✔
4940
  }
34,327✔
4941

4942
  if (gatherWildcardProof) {
11,852✔
4943
    if (auto wcIt = wildcardCandidates.find(qname); wcIt != wildcardCandidates.end() && !wcIt->second) {
241!
4944
      // 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
4945
      gatherWildcardProof = false;
2✔
4946
    }
2✔
4947
  }
241✔
4948

4949
  return RCode::NoError;
11,852✔
4950
}
11,852✔
4951

4952
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)
4953
{
240✔
4954
  if (denialState == expectedState) {
240✔
4955
    neValidationState = vState::Secure;
213✔
4956
  }
213✔
4957
  else {
27✔
4958
    if (denialState == dState::OPTOUT) {
27✔
4959
      LOG(prefix << qname << ": OPT-out denial found for " << neName << endl);
13!
4960
      /* rfc5155 states:
4961
         "The AD bit, as defined by [RFC4035], MUST NOT be set when returning a
4962
         response containing a closest (provable) encloser proof in which the
4963
         NSEC3 RR that covers the "next closer" name has the Opt-Out bit set.
4964

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

4971
         At best the Opt-Out NSEC3 RR proves that there is no signed DS (so no
4972
         secure delegation).
4973
      */
4974
      neValidationState = vState::Insecure;
13✔
4975
    }
13✔
4976
    else if (denialState == dState::INSECURE) {
14✔
4977
      LOG(prefix << qname << ": Insecure denial found for " << neName << ", returning Insecure" << endl);
2!
4978
      neValidationState = vState::Insecure;
2✔
4979
    }
2✔
4980
    else {
12✔
4981
      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!
4982
      /* try again to get the missed cuts, harder this time */
4983
      auto zState = getValidationStatus(neName, false, isDS, depth, prefix);
12✔
4984
      if (zState != vState::Secure) {
12✔
4985
        neValidationState = zState;
4✔
4986
      }
4✔
4987
      else {
8✔
4988
        LOG(prefix << qname << ": Still in a secure zone with an invalid denial for " << neName << ", returning " << vStateToString(vState::BogusInvalidDenial) << endl);
8!
4989
        neValidationState = vState::BogusInvalidDenial;
8✔
4990
      }
8✔
4991
    }
12✔
4992
  }
27✔
4993
  updateValidationState(qname, state, neValidationState, prefix);
240✔
4994
}
240✔
4995

4996
dState SyncRes::getDenialValidationState(const NegCache::NegCacheEntry& negEntry, const dState expectedState, bool referralToUnsigned, const string& prefix)
4997
{
2,410✔
4998
  cspmap_t csp = harvestCSPFromNE(negEntry);
2,410✔
4999
  return getDenial(csp, negEntry.d_name, negEntry.d_qtype.getCode(), referralToUnsigned, expectedState == dState::NXQTYPE, d_validationContext, LogObject(prefix));
2,410✔
5000
}
2,410✔
5001

5002
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)
5003
{
237✔
5004
  /* positive answer synthesized from a wildcard */
5005
  NegCache::NegCacheEntry negEntry;
237✔
5006
  negEntry.d_name = qname;
237✔
5007
  negEntry.d_qtype = QType::ENT; // this encodes 'whole record'
237✔
5008
  uint32_t lowestTTL = rec.d_ttl;
237✔
5009
  harvestNXRecords(lwr.d_records, negEntry, d_now.tv_sec, &lowestTTL);
237✔
5010

5011
  if (vStateIsBogus(state)) {
237!
5012
    negEntry.d_validationState = state;
×
5013
  }
×
5014
  else {
237✔
5015
    auto recordState = getValidationStatus(qname, !negEntry.authoritySOA.signatures.empty() || !negEntry.DNSSECRecords.signatures.empty(), false, depth, prefix);
237✔
5016

5017
    if (recordState == vState::Secure) {
237✔
5018
      /* We have a positive answer synthesized from a wildcard, we need to check that we have
5019
         proof that the exact name doesn't exist so the wildcard can be used,
5020
         as described in section 5.3.4 of RFC 4035 and 5.3 of RFC 7129.
5021
      */
5022
      cspmap_t csp = harvestCSPFromNE(negEntry);
233✔
5023
      dState res = getDenial(csp, qname, negEntry.d_qtype.getCode(), false, false, d_validationContext, LogObject(prefix), false, wildcardLabelsCount);
233✔
5024
      if (res != dState::NXDOMAIN) {
233✔
5025
        vState tmpState = vState::BogusInvalidDenial;
8✔
5026
        if (res == dState::INSECURE || res == dState::OPTOUT) {
8!
5027
          /* Some part could not be validated, for example a NSEC3 record with a too large number of iterations,
5028
             this is not enough to warrant a Bogus, but go Insecure. */
5029
          tmpState = vState::Insecure;
2✔
5030
          LOG(prefix << qname << ": Unable to validate denial in wildcard expanded positive response found for " << qname << ", returning Insecure, res=" << res << endl);
2!
5031
        }
2✔
5032
        else {
6✔
5033
          LOG(prefix << qname << ": Invalid denial in wildcard expanded positive response found for " << qname << ", returning Bogus, res=" << res << endl);
6!
5034
          rec.d_ttl = std::min(rec.d_ttl, s_maxbogusttl);
6✔
5035
        }
6✔
5036

5037
        updateValidationState(qname, state, tmpState, prefix);
8✔
5038
        /* we already stored the record with a different validation status, let's fix it */
5039
        updateValidationStatusInCache(qname, qtype, lwr.d_aabit, tmpState);
8✔
5040
      }
8✔
5041
    }
233✔
5042
  }
237✔
5043
}
237✔
5044

5045
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)
5046
{
11,834✔
5047
  bool done = false;
11,834✔
5048
  DNSName dnameTarget;
11,834✔
5049
  DNSName dnameOwner;
11,834✔
5050
  uint32_t dnameTTL = 0;
11,834✔
5051
  bool referralOnDS = false;
11,834✔
5052

5053
  for (auto& rec : lwr.d_records) {
66,388✔
5054
    if (rec.d_type == QType::OPT || rec.d_class != QClass::IN) {
66,388✔
5055
      continue;
9,535✔
5056
    }
9,535✔
5057

5058
    if (rec.d_place == DNSResourceRecord::ANSWER && !(lwr.d_aabit || sendRDQuery)) {
56,853!
5059
      /* for now we allow a CNAME for the exact qname in ANSWER with AA=0, because Amazon DNS servers
5060
         are sending such responses */
5061
      if (rec.d_type != QType::CNAME || rec.d_name != qname) {
×
5062
        continue;
×
5063
      }
×
5064
    }
×
5065
    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);
56,853!
5066

5067
    bool putInNegCache = true;
56,853✔
5068
    if (negCacheIndication && qtype == QType::DS && isForwardOrAuth(qname)) {
56,853✔
5069
      // #10189, a NXDOMAIN to a DS query for a forwarded or auth domain should not NXDOMAIN the whole domain
5070
      putInNegCache = false;
7✔
5071
    }
7✔
5072

5073
    if (negCacheIndication) {
56,853✔
5074
      LOG(prefix << qname << ": Got negative caching indication for name '" << qname << "' (accept=" << rec.d_name.isPartOf(auth) << "), newtarget='" << newtarget << "'" << endl);
125!
5075

5076
      rec.d_ttl = min(rec.d_ttl, s_maxnegttl);
125✔
5077
      // only add a SOA if we're not going anywhere after this
5078
      if (newtarget.empty()) {
125✔
5079
        ret.push_back(rec);
119✔
5080
      }
119✔
5081

5082
      NegCache::NegCacheEntry negEntry;
125✔
5083

5084
      uint32_t lowestTTL = rec.d_ttl;
125✔
5085
      /* if we get an NXDomain answer with a CNAME, the name
5086
         does exist but the target does not */
5087
      negEntry.d_name = newtarget.empty() ? qname : newtarget;
125✔
5088
      negEntry.d_qtype = QType::ENT; // this encodes 'whole record'
125✔
5089
      negEntry.d_auth = rec.d_name;
125✔
5090
      harvestNXRecords(lwr.d_records, negEntry, d_now.tv_sec, &lowestTTL);
125✔
5091

5092
      if (vStateIsBogus(state)) {
125✔
5093
        negEntry.d_validationState = state;
7✔
5094
      }
7✔
5095
      else {
118✔
5096
        /* here we need to get the validation status of the zone telling us that the domain does not
5097
           exist, ie the owner of the SOA */
5098
        auto recordState = getValidationStatus(rec.d_name, !negEntry.authoritySOA.signatures.empty() || !negEntry.DNSSECRecords.signatures.empty(), false, depth, prefix);
118!
5099
        if (recordState == vState::Secure) {
118✔
5100
          dState denialState = getDenialValidationState(negEntry, dState::NXDOMAIN, false, prefix);
58✔
5101
          updateDenialValidationState(qname, negEntry.d_validationState, negEntry.d_name, state, denialState, dState::NXDOMAIN, false, depth, prefix);
58✔
5102
        }
58✔
5103
        else {
60✔
5104
          negEntry.d_validationState = recordState;
60✔
5105
          updateValidationState(qname, state, negEntry.d_validationState, prefix);
60✔
5106
        }
60✔
5107
      }
118✔
5108

5109
      if (vStateIsBogus(negEntry.d_validationState)) {
125✔
5110
        lowestTTL = min(lowestTTL, s_maxbogusttl);
7✔
5111
      }
7✔
5112

5113
      negEntry.d_ttd = d_now.tv_sec + lowestTTL;
125✔
5114
      negEntry.d_orig_ttl = lowestTTL;
125✔
5115
      /* if we get an NXDomain answer with a CNAME, let's not cache the
5116
         target, even the server was authoritative for it,
5117
         and do an additional query for the CNAME target.
5118
         We have a regression test making sure we do exactly that.
5119
      */
5120
      if (newtarget.empty() && putInNegCache) {
125✔
5121
        g_negCache->add(negEntry);
112✔
5122
        // doCNAMECacheCheck() checks record cache and does not look into negcache. That means that an old record might be found if
5123
        // serve-stale is active. Avoid that by explicitly zapping that CNAME record.
5124
        if (qtype == QType::CNAME && MemRecursorCache::s_maxServedStaleExtensions > 0) {
112!
5125
          g_recCache->doWipeCache(qname, false, qtype);
2✔
5126
        }
2✔
5127
        if (s_rootNXTrust && negEntry.d_auth.isRoot() && auth.isRoot() && lwr.d_aabit) {
112!
5128
          negEntry.d_name = negEntry.d_name.getLastLabel();
3✔
5129
          g_negCache->add(negEntry);
3✔
5130
        }
3✔
5131
      }
112✔
5132

5133
      negIndicHasSignatures = !negEntry.authoritySOA.signatures.empty() || !negEntry.DNSSECRecords.signatures.empty();
125!
5134
      negindic = true;
125✔
5135
    }
125✔
5136
    else if (rec.d_place == DNSResourceRecord::ANSWER && isRedirection(rec.d_type) && // CNAME or DNAME answer
56,728✔
5137
             !isRedirection(qtype.getCode())) { // But not in response to a CNAME or DNAME query
56,728!
5138
      if (rec.d_type == QType::CNAME && rec.d_name == qname) {
1,597✔
5139
        if (!dnameOwner.empty()) { // We synthesize ourselves
1,559✔
5140
          continue;
9✔
5141
        }
9✔
5142
        ret.push_back(rec);
1,550✔
5143
        if (auto content = getRR<CNAMERecordContent>(rec)) {
1,550!
5144
          newtarget = DNSName(content->getTarget());
1,550✔
5145
        }
1,550✔
5146
        if (needWildcardProof) {
1,550✔
5147
          checkWildcardProof(qname, QType::CNAME, rec, lwr, state, depth, prefix, wildcardLabelsCount);
12✔
5148
        }
12✔
5149
      }
1,550✔
5150
      else if (rec.d_type == QType::DNAME && qname.isPartOf(rec.d_name)) { // DNAME
38!
5151
        ret.push_back(rec);
32✔
5152
        if (auto content = getRR<DNAMERecordContent>(rec)) {
32!
5153
          dnameOwner = rec.d_name;
32✔
5154
          dnameTarget = content->getTarget();
32✔
5155
          dnameTTL = rec.d_ttl;
32✔
5156
          if (!newtarget.empty()) { // We had a CNAME before, remove it from ret so we don't cache it
32✔
5157
            ret.erase(std::remove_if(
21✔
5158
                        ret.begin(),
21✔
5159
                        ret.end(),
21✔
5160
                        [&qname](DNSRecord& dnsrecord) {
56✔
5161
                          return (dnsrecord.d_place == DNSResourceRecord::ANSWER && dnsrecord.d_type == QType::CNAME && dnsrecord.d_name == qname);
56!
5162
                        }),
56✔
5163
                      ret.end());
21✔
5164
          }
21✔
5165
          try {
32✔
5166
            newtarget = qname.makeRelative(dnameOwner) + dnameTarget;
32✔
5167
            if (needWildcardProof) {
32!
5168
              checkWildcardProof(qname, QType::DNAME, rec, lwr, state, depth, prefix, wildcardLabelsCount);
×
5169
            }
×
5170
          }
32✔
5171
          catch (const std::exception& e) {
32✔
5172
            // We should probably catch an std::range_error here and set the rcode to YXDOMAIN (RFC 6672, section 2.2)
5173
            // But there is no way to set the RCODE from this function
5174
            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());
×
5175
          }
×
5176
        }
32✔
5177
      }
32✔
5178
    }
1,597✔
5179
    /* if we have a positive answer synthesized from a wildcard, we need to
5180
       return the corresponding NSEC/NSEC3 records from the AUTHORITY section
5181
       proving that the exact name did not exist.
5182
       Except if this is a NODATA answer because then we will gather the NXNSEC records later */
5183
    else if (gatherWildcardProof && !negindic && (rec.d_type == QType::RRSIG || rec.d_type == QType::NSEC || rec.d_type == QType::NSEC3) && rec.d_place == DNSResourceRecord::AUTHORITY) {
55,131✔
5184
      ret.push_back(rec); // enjoy your DNSSEC
468✔
5185
    }
468✔
5186
    // for ANY answers we *must* have an authoritative answer, unless we are forwarding recursively
5187
    else if (rec.d_place == DNSResourceRecord::ANSWER && rec.d_name == qname && (rec.d_type == qtype.getCode() || ((lwr.d_aabit || sendRDQuery) && qtype == QType::ANY))) {
54,663!
5188
      LOG(prefix << qname << ": Answer is in: resolved to '" << rec.getContent()->getZoneRepresentation() << "|" << DNSRecordContent::NumberToType(rec.d_type) << "'" << endl);
9,081✔
5189

5190
      done = true;
9,081✔
5191
      rcode = RCode::NoError;
9,081✔
5192

5193
      if (needWildcardProof) {
9,081✔
5194
        checkWildcardProof(qname, qtype, rec, lwr, state, depth, prefix, wildcardLabelsCount);
225✔
5195
      }
225✔
5196

5197
      ret.push_back(rec);
9,081✔
5198
    }
9,081✔
5199
    else if ((rec.d_type == QType::RRSIG || rec.d_type == QType::NSEC || rec.d_type == QType::NSEC3) && rec.d_place == DNSResourceRecord::ANSWER) {
45,582✔
5200
      if (rec.d_type != QType::RRSIG || rec.d_name == qname) {
2,449!
5201
        ret.push_back(rec); // enjoy your DNSSEC
2,416✔
5202
      }
2,416✔
5203
      else if (rec.d_type == QType::RRSIG && qname.isPartOf(rec.d_name)) {
33!
5204
        auto rrsig = getRR<RRSIGRecordContent>(rec);
24✔
5205
        if (rrsig != nullptr && rrsig->d_type == QType::DNAME) {
24!
5206
          ret.push_back(rec);
24✔
5207
        }
24✔
5208
      }
24✔
5209
    }
2,449✔
5210
    else if (rec.d_place == DNSResourceRecord::AUTHORITY && rec.d_type == QType::NS && qname.isPartOf(rec.d_name)) {
43,133✔
5211
      if (moreSpecificThan(rec.d_name, auth)) {
13,989✔
5212
        newauth = rec.d_name;
13,132✔
5213
        LOG(prefix << qname << ": Got NS record '" << rec.d_name << "' -> '" << rec.getContent()->getZoneRepresentation() << "'" << endl);
13,132✔
5214

5215
        /* check if we have a referral from the parent zone to a child zone for a DS query, which is not right */
5216
        if (qtype == QType::DS && (newauth.isPartOf(qname) || qname == newauth)) {
13,132!
5217
          /* just got a referral from the parent zone when asking for a DS, looks like this server did not get the DNSSEC memo.. */
5218
          referralOnDS = true;
4✔
5219
        }
4✔
5220
        else {
13,128✔
5221
          realreferral = true;
13,128✔
5222
          if (auto content = getRR<NSRecordContent>(rec)) {
13,128✔
5223
            nsset.insert(content->getNS());
13,127✔
5224
          }
13,127✔
5225
        }
13,128✔
5226
      }
13,132✔
5227
      else {
857✔
5228
        LOG(prefix << qname << ": Got upwards/level NS record '" << rec.d_name << "' -> '" << rec.getContent()->getZoneRepresentation() << "', had '" << auth << "'" << endl);
857!
5229
        if (auto content = getRR<NSRecordContent>(rec)) {
857!
5230
          nsset.insert(content->getNS());
857✔
5231
        }
857✔
5232
      }
857✔
5233
    }
13,989✔
5234
    else if (rec.d_place == DNSResourceRecord::AUTHORITY && rec.d_type == QType::DS && qname.isPartOf(rec.d_name)) {
29,144✔
5235
      LOG(prefix << qname << ": Got DS record '" << rec.d_name << "' -> '" << rec.getContent()->getZoneRepresentation() << "'" << endl);
968!
5236
    }
968✔
5237
    else if (realreferral && rec.d_place == DNSResourceRecord::AUTHORITY && (rec.d_type == QType::NSEC || rec.d_type == QType::NSEC3) && newauth.isPartOf(auth)) {
28,176!
5238
      /* we might have received a denial of the DS, let's check */
5239
      NegCache::NegCacheEntry negEntry;
3,281✔
5240
      uint32_t lowestTTL = rec.d_ttl;
3,281✔
5241
      harvestNXRecords(lwr.d_records, negEntry, d_now.tv_sec, &lowestTTL);
3,281✔
5242

5243
      if (!vStateIsBogus(state)) {
3,281!
5244
        auto recordState = getValidationStatus(newauth, !negEntry.authoritySOA.signatures.empty() || !negEntry.DNSSECRecords.signatures.empty(), true, depth, prefix);
3,281!
5245

5246
        if (recordState == vState::Secure) {
3,281✔
5247
          negEntry.d_auth = auth;
2,168✔
5248
          negEntry.d_name = newauth;
2,168✔
5249
          negEntry.d_qtype = QType::DS;
2,168✔
5250
          rec.d_ttl = min(s_maxnegttl, rec.d_ttl);
2,168✔
5251

5252
          dState denialState = getDenialValidationState(negEntry, dState::NXQTYPE, true, prefix);
2,168✔
5253

5254
          if (denialState == dState::NXQTYPE || denialState == dState::OPTOUT || denialState == dState::INSECURE) {
2,168!
5255
            negEntry.d_ttd = lowestTTL + d_now.tv_sec;
2,164✔
5256
            negEntry.d_orig_ttl = lowestTTL;
2,164✔
5257
            negEntry.d_validationState = vState::Secure;
2,164✔
5258
            if (denialState == dState::OPTOUT) {
2,164✔
5259
              negEntry.d_validationState = vState::Insecure;
2,081✔
5260
            }
2,081✔
5261
            LOG(prefix << qname << ": Got negative indication of DS record for '" << newauth << "'" << endl);
2,164!
5262

5263
            g_negCache->add(negEntry);
2,164✔
5264

5265
            /* 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
5266
               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
5267
               query. */
5268
            if (qtype == QType::DS && qname == newauth && (d_externalDSQuery.empty() || qname != d_externalDSQuery)) {
2,164!
5269
              /* we are actually done! */
5270
              negindic = true;
×
5271
              negIndicHasSignatures = !negEntry.authoritySOA.signatures.empty() || !negEntry.DNSSECRecords.signatures.empty();
×
5272
              nsset.clear();
×
5273
            }
×
5274
          }
2,164✔
5275
        }
2,168✔
5276
      }
3,281✔
5277
    }
3,281✔
5278
    else if (!done && rec.d_place == DNSResourceRecord::AUTHORITY && rec.d_type == QType::SOA && lwr.d_rcode == RCode::NoError && qname.isPartOf(rec.d_name)) {
24,895!
5279
      LOG(prefix << qname << ": Got negative caching indication for '" << qname << "|" << qtype << "'" << endl);
1,346!
5280

5281
      if (!newtarget.empty()) {
1,346✔
5282
        LOG(prefix << qname << ": Hang on! Got a redirect to '" << newtarget << "' already" << endl);
5!
5283
      }
5✔
5284
      else {
1,341✔
5285
        rec.d_ttl = min(s_maxnegttl, rec.d_ttl);
1,341✔
5286

5287
        NegCache::NegCacheEntry negEntry;
1,341✔
5288
        negEntry.d_auth = rec.d_name;
1,341✔
5289
        uint32_t lowestTTL = rec.d_ttl;
1,341✔
5290
        negEntry.d_name = qname;
1,341✔
5291
        negEntry.d_qtype = qtype;
1,341✔
5292
        harvestNXRecords(lwr.d_records, negEntry, d_now.tv_sec, &lowestTTL);
1,341✔
5293

5294
        if (vStateIsBogus(state)) {
1,341✔
5295
          negEntry.d_validationState = state;
7✔
5296
        }
7✔
5297
        else {
1,334✔
5298
          auto recordState = getValidationStatus(qname, !negEntry.authoritySOA.signatures.empty() || !negEntry.DNSSECRecords.signatures.empty(), qtype == QType::DS, depth, prefix);
1,334!
5299
          if (recordState == vState::Secure) {
1,334✔
5300
            dState denialState = getDenialValidationState(negEntry, dState::NXQTYPE, false, prefix);
178✔
5301
            updateDenialValidationState(qname, negEntry.d_validationState, negEntry.d_name, state, denialState, dState::NXQTYPE, qtype == QType::DS, depth, prefix);
178✔
5302
          }
178✔
5303
          else {
1,156✔
5304
            negEntry.d_validationState = recordState;
1,156✔
5305
            updateValidationState(qname, state, negEntry.d_validationState, prefix);
1,156✔
5306
          }
1,156✔
5307
        }
1,334✔
5308

5309
        if (vStateIsBogus(negEntry.d_validationState)) {
1,341✔
5310
          lowestTTL = min(lowestTTL, s_maxbogusttl);
13✔
5311
          rec.d_ttl = min(rec.d_ttl, s_maxbogusttl);
13✔
5312
        }
13✔
5313
        negEntry.d_ttd = d_now.tv_sec + lowestTTL;
1,341✔
5314
        negEntry.d_orig_ttl = lowestTTL;
1,341✔
5315
        if (qtype.getCode() != 0) { // prevents us from NXDOMAIN'ing a whole domain
1,341✔
5316
          // doCNAMECacheCheck() checks record cache and does not look into negcache. That means that an old record might be found if
5317
          // serve-stale is active. Avoid that by explicitly zapping that CNAME record.
5318
          if (qtype == QType::CNAME && MemRecursorCache::s_maxServedStaleExtensions > 0) {
1,339!
5319
            g_recCache->doWipeCache(qname, false, qtype);
2✔
5320
          }
2✔
5321
          g_negCache->add(negEntry);
1,339✔
5322
        }
1,339✔
5323

5324
        ret.push_back(rec);
1,341✔
5325
        negindic = true;
1,341✔
5326
        negIndicHasSignatures = !negEntry.authoritySOA.signatures.empty() || !negEntry.DNSSECRecords.signatures.empty();
1,341!
5327
      }
1,341✔
5328
    }
1,346✔
5329
  }
56,853✔
5330

5331
  if (!dnameTarget.empty()) {
11,834✔
5332
    // Synthesize a CNAME
5333
    auto cnamerec = DNSRecord();
32✔
5334
    cnamerec.d_name = qname;
32✔
5335
    cnamerec.d_type = QType::CNAME;
32✔
5336
    cnamerec.d_ttl = dnameTTL;
32✔
5337
    cnamerec.setContent(std::make_shared<CNAMERecordContent>(CNAMERecordContent(newtarget)));
32✔
5338
    ret.push_back(std::move(cnamerec));
32✔
5339
  }
32✔
5340

5341
  /* If we have seen a proper denial, let's forget that we also had a referral for a DS query.
5342
     Otherwise we need to deal with it. */
5343
  if (referralOnDS && !negindic) {
11,834!
5344
    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!
5345
    if (!vStateIsBogus(state)) {
4!
5346
      auto recordState = getValidationStatus(qname, false, true, depth, prefix);
4✔
5347
      if (recordState == vState::Secure) {
4✔
5348
        /* we are in a secure zone, got a referral to the child zone on a DS query, no denial, that's wrong */
5349
        LOG(prefix << qname << ": NODATA without a negative indication (missing SOA in authority) in a DNSSEC secure zone, going Bogus" << endl);
2!
5350
        updateValidationState(qname, state, vState::BogusMissingNegativeIndication, prefix);
2✔
5351
      }
2✔
5352
    }
4✔
5353
    negindic = true;
4✔
5354
    negIndicHasSignatures = false;
4✔
5355
  }
4✔
5356

5357
  return done;
11,834✔
5358
}
11,834✔
5359

5360
static void submitTryDotTask(ComboAddress address, const DNSName& auth, const DNSName& nsname, time_t now)
5361
{
×
5362
  if (address.getPort() == 853) {
×
5363
    return;
×
5364
  }
×
5365
  address.setPort(853);
×
5366
  auto lock = s_dotMap.lock();
×
5367
  if (lock->d_numBusy >= SyncRes::s_max_busy_dot_probes) {
×
5368
    return;
×
5369
  }
×
5370
  auto iter = lock->d_map.emplace(DoTStatus{address, auth, now + dotFailWait}).first;
×
5371
  if (iter->d_status == DoTStatus::Busy) {
×
5372
    return;
×
5373
  }
×
5374
  if (iter->d_ttd > now) {
×
5375
    if (iter->d_status == DoTStatus::Bad) {
×
5376
      return;
×
5377
    }
×
5378
    if (iter->d_status == DoTStatus::Good) {
×
5379
      return;
×
5380
    }
×
5381
    // We only want to probe auths that we have seen before, auth that only come around once are not interesting
5382
    if (iter->d_status == DoTStatus::Unknown && iter->d_count == 0) {
×
5383
      return;
×
5384
    }
×
5385
  }
×
5386
  lock->d_map.modify(iter, [=](DoTStatus& status) { status.d_ttd = now + dotFailWait; });
×
5387
  bool pushed = pushTryDoTTask(auth, QType::SOA, address, std::numeric_limits<time_t>::max(), nsname);
×
5388
  if (pushed) {
×
5389
    iter->d_status = DoTStatus::Busy;
×
5390
    ++lock->d_numBusy;
×
5391
  }
×
5392
}
×
5393

5394
static bool shouldDoDoT(ComboAddress address, time_t now)
5395
{
×
5396
  address.setPort(853);
×
5397
  auto lock = s_dotMap.lock();
×
5398
  auto iter = lock->d_map.find(address);
×
5399
  if (iter == lock->d_map.end()) {
×
5400
    return false;
×
5401
  }
×
5402
  iter->d_count++;
×
5403
  return iter->d_status == DoTStatus::Good && iter->d_ttd > now;
×
5404
}
×
5405

5406
static void updateDoTStatus(ComboAddress address, DoTStatus::Status status, time_t time, bool updateBusy = false)
5407
{
×
5408
  address.setPort(853);
×
5409
  auto lock = s_dotMap.lock();
×
5410
  auto iter = lock->d_map.find(address);
×
5411
  if (iter != lock->d_map.end()) {
×
5412
    iter->d_status = status;
×
5413
    lock->d_map.modify(iter, [=](DoTStatus& statusToModify) { statusToModify.d_ttd = time; });
×
5414
    if (updateBusy) {
×
5415
      --lock->d_numBusy;
×
5416
    }
×
5417
  }
×
5418
}
×
5419

5420
bool SyncRes::tryDoT(const DNSName& qname, const QType qtype, const DNSName& nsName, ComboAddress address, time_t now)
5421
{
×
5422
  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));
×
5423

5424
  auto logHelper1 = [&log](const string& ename) {
×
5425
    log->info(Logr::Debug, "Failed to probe DoT records, got an exception", "exception", Logging::Loggable(ename));
×
5426
  };
×
5427
  auto logHelper2 = [&log](const string& msg, const string& ename) {
×
5428
    log->error(Logr::Debug, msg, "Failed to probe DoT records, got an exception", "exception", Logging::Loggable(ename));
×
5429
  };
×
5430
  LWResult lwr;
×
5431
  bool truncated{};
×
5432
  bool spoofed{};
×
5433
  boost::optional<Netmask> netmask;
×
5434
  address.setPort(853);
×
5435
  // We use the fact that qname equals auth
5436
  bool isOK = false;
×
5437
  try {
×
5438
    boost::optional<EDNSExtendedError> extendedError;
×
5439
    isOK = doResolveAtThisIP("", qname, qtype, lwr, netmask, qname, false, false, nsName, address, true, true, truncated, spoofed, extendedError, true);
×
5440
    isOK = isOK && lwr.d_rcode == RCode::NoError && !lwr.d_records.empty();
×
5441
  }
×
5442
  catch (const PDNSException& e) {
×
5443
    logHelper2(e.reason, "PDNSException");
×
5444
  }
×
5445
  catch (const ImmediateServFailException& e) {
×
5446
    logHelper2(e.reason, "ImmediateServFailException");
×
5447
  }
×
5448
  catch (const PolicyHitException& e) {
×
5449
    logHelper1("PolicyHitException");
×
5450
  }
×
5451
  catch (const std::exception& e) {
×
5452
    logHelper2(e.what(), "std::exception");
×
5453
  }
×
5454
  catch (...) {
×
5455
    logHelper1("other");
×
5456
  }
×
5457
  updateDoTStatus(address, isOK ? DoTStatus::Good : DoTStatus::Bad, now + (isOK ? dotSuccessWait : dotFailWait), true);
×
5458
  return isOK;
×
5459
}
×
5460

5461
void SyncRes::ednsStats(boost::optional<Netmask>& ednsmask, const DNSName& qname, const string& prefix)
5462
{
12,213✔
5463
  if (!ednsmask) {
12,213✔
5464
    return;
12,155✔
5465
  }
12,155✔
5466
  s_ecsresponses++;
58✔
5467
  LOG(prefix << qname << ": Received EDNS Client Subnet Mask " << ednsmask->toString() << " on response" << endl);
58!
5468

5469
  if (ednsmask->getBits() > 0) {
58!
5470
    if (ednsmask->isIPv4()) {
58✔
5471
      ++SyncRes::s_ecsResponsesBySubnetSize4.at(ednsmask->getBits() - 1);
51✔
5472
    }
51✔
5473
    else {
7✔
5474
      ++SyncRes::s_ecsResponsesBySubnetSize6.at(ednsmask->getBits() - 1);
7✔
5475
    }
7✔
5476
  }
58✔
5477
}
58✔
5478

5479
void SyncRes::updateQueryCounts(const string& prefix, const DNSName& qname, const ComboAddress& address, bool doTCP, bool doDoT)
5480
{
12,219✔
5481
  t_Counters.at(rec::Counter::outqueries)++;
12,219✔
5482
  d_outqueries++;
12,219✔
5483
  checkMaxQperQ(qname);
12,219✔
5484
  if (address.sin4.sin_family == AF_INET6) {
12,219✔
5485
    t_Counters.at(rec::Counter::ipv6queries)++;
810✔
5486
  }
810✔
5487
  if (doTCP) {
12,219✔
5488
    if (doDoT) {
43✔
5489
      LOG(prefix << qname << ": Using DoT with " << address.toStringWithPort() << endl);
9!
5490
      t_Counters.at(rec::Counter::dotoutqueries)++;
9✔
5491
      d_dotoutqueries++;
9✔
5492
    }
9✔
5493
    else {
34✔
5494
      LOG(prefix << qname << ": Using TCP with " << address.toStringWithPort() << endl);
34!
5495
      t_Counters.at(rec::Counter::tcpoutqueries)++;
34✔
5496
      d_tcpoutqueries++;
34✔
5497
    }
34✔
5498
  }
43✔
5499
}
12,219✔
5500

5501
void SyncRes::incTimeoutStats(const ComboAddress& remoteIP)
5502
{
325✔
5503
  d_timeouts++;
325✔
5504
  t_Counters.at(rec::Counter::outgoingtimeouts)++;
325✔
5505

5506
  if (remoteIP.sin4.sin_family == AF_INET) {
325✔
5507
    t_Counters.at(rec::Counter::outgoing4timeouts)++;
173✔
5508
  }
173✔
5509
  else {
152✔
5510
    t_Counters.at(rec::Counter::outgoing6timeouts)++;
152✔
5511
  }
152✔
5512

5513
  if (t_timeouts) {
325✔
5514
    t_timeouts->push_back(remoteIP);
8✔
5515
  }
8✔
5516
}
325✔
5517

5518
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)
5519
{
12,225✔
5520
  bool chained = false;
12,225✔
5521
  LWResult::Result resolveret = LWResult::Result::Success;
12,225✔
5522

5523
  if (s_maxtotusec != 0 && d_totUsec > s_maxtotusec) {
12,225✔
5524
    if (s_addExtendedResolutionDNSErrors) {
2!
5525
      extendedError = EDNSExtendedError{static_cast<uint16_t>(EDNSExtendedError::code::NoReachableAuthority), "Timeout waiting for answer(s)"};
×
5526
    }
×
5527
    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✔
5528
  }
2✔
5529

5530
  int preOutQueryRet = RCode::NoError;
12,223✔
5531
  if (d_pdl && d_pdl->preoutquery(remoteIP, d_requestor, qname, qtype, doTCP, lwr.d_records, preOutQueryRet, d_eventTrace, timeval{0, 0})) {
12,223✔
5532
    LOG(prefix << qname << ": Query handled by Lua" << endl);
4!
5533
  }
4✔
5534
  else {
12,219✔
5535
    ednsmask = getEDNSSubnetMask(qname, remoteIP);
12,219✔
5536
    if (ednsmask) {
12,219✔
5537
      LOG(prefix << qname << ": Adding EDNS Client Subnet Mask " << ednsmask->toString() << " to query" << endl);
70!
5538
      s_ecsqueries++;
70✔
5539
    }
70✔
5540
    updateQueryCounts(prefix, qname, remoteIP, doTCP, doDoT);
12,219✔
5541
    resolveret = asyncresolveWrapper(remoteIP, d_doDNSSEC, qname, auth, qtype.getCode(),
12,219✔
5542
                                     doTCP, sendRDQuery, &d_now, ednsmask, &lwr, &chained, nsName); // <- we go out on the wire!
12,219✔
5543
    ednsStats(ednsmask, qname, prefix);
12,219✔
5544
  }
12,219✔
5545

5546
  /* preoutquery killed the query by setting dq.rcode to -3 */
5547
  if (preOutQueryRet == -3) {
12,223✔
5548
    throw ImmediateServFailException("Query killed by policy");
4✔
5549
  }
4✔
5550

5551
  d_totUsec += lwr.d_usec;
12,219✔
5552

5553
  if (resolveret == LWResult::Result::Spoofed) {
12,219!
5554
    spoofed = true;
×
5555
    return false;
×
5556
  }
×
5557

5558
  accountAuthLatency(lwr.d_usec, remoteIP.sin4.sin_family);
12,219✔
5559
  if (lwr.d_rcode >= 0 && lwr.d_rcode < static_cast<decltype(lwr.d_rcode)>(t_Counters.at(rec::RCode::auth).rcodeCounters.size())) {
12,219✔
5560
    ++t_Counters.at(rec::RCode::auth).rcodeCounters.at(static_cast<uint8_t>(lwr.d_rcode));
12,212✔
5561
  }
12,212✔
5562

5563
  if (!dontThrottle) {
12,219✔
5564
    dontThrottle = shouldNotThrottle(&nsName, &remoteIP);
12,213✔
5565
  }
12,213✔
5566

5567
  if (resolveret != LWResult::Result::Success) {
12,219✔
5568
    /* Error while resolving */
5569
    if (resolveret == LWResult::Result::Timeout) {
383✔
5570
      LOG(prefix << qname << ": Timeout resolving after " << lwr.d_usec / 1000.0 << " ms " << (doTCP ? "over TCP" : "") << endl);
325!
5571
      incTimeoutStats(remoteIP);
325✔
5572
    }
325✔
5573
    else if (resolveret == LWResult::Result::OSLimitError) {
58✔
5574
      /* OS resource limit reached */
5575
      LOG(prefix << qname << ": Hit a local resource limit resolving" << (doTCP ? " over TCP" : "") << ", probable error: " << stringerror() << endl);
6!
5576
      t_Counters.at(rec::Counter::resourceLimits)++;
6✔
5577
    }
6✔
5578
    else if (resolveret == LWResult::Result::ChainLimitError) {
52!
5579
      /* Chain resource limit reached */
5580
      LOG(prefix << qname << ": Hit a chain limit resolving" << (doTCP ? " over TCP" : ""));
×
5581
      t_Counters.at(rec::Counter::chainLimits)++;
×
5582
    }
×
5583
    else {
52✔
5584
      /* LWResult::Result::PermanentError */
5585
      t_Counters.at(rec::Counter::unreachables)++;
52✔
5586
      d_unreachables++;
52✔
5587
      // XXX questionable use of errno
5588
      LOG(prefix << qname << ": Error resolving from " << remoteIP.toString() << (doTCP ? " over TCP" : "") << ", possible error: " << stringerror() << endl);
52!
5589
    }
52✔
5590

5591
    // don't account for resource limits, they are our own fault
5592
    // And don't throttle when the IP address is on the dontThrottleNetmasks list or the name is part of dontThrottleNames
5593
    if (!LWResult::isLimitError(resolveret) && !chained && !dontThrottle) {
383!
5594
      uint32_t responseUsec = 1000000; // 1 sec for non-timeout cases
377✔
5595
      // Use the actual time if we saw a timeout
5596
      if (resolveret == LWResult::Result::Timeout) {
377✔
5597
        responseUsec = lwr.d_usec;
325✔
5598
      }
325✔
5599

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

5602
      // make sure we don't throttle the root
5603
      if (s_serverdownmaxfails > 0 && auth != g_rootdnsname && s_fails.lock()->incr(remoteIP, d_now) >= s_serverdownmaxfails) {
377!
5604
        LOG(prefix << qname << ": Max fails reached resolving on " << remoteIP.toString() << ". Going full throttle for " << s_serverdownthrottletime << " seconds" << endl);
×
5605
        // mark server as down
5606
        doThrottle(d_now.tv_sec, remoteIP, s_serverdownthrottletime, 10000, Throttle::Reason::ServerDown);
×
5607
      }
×
5608
      else if (resolveret == LWResult::Result::PermanentError) {
377✔
5609
        // unreachable, 1 minute or 100 queries
5610
        doThrottle(d_now.tv_sec, remoteIP, qname, qtype, 60, 100, Throttle::Reason::PermanentError);
52✔
5611
      }
52✔
5612
      else {
325✔
5613
        // If the actual response time was more than 80% of the default timeout, we throttle. On a
5614
        // busy rec we reduce the time we are willing to wait for an auth, it is unfair to throttle on
5615
        // such a shortened timeout.
5616
        if (responseUsec > g_networkTimeoutMsec * 800) {
325✔
5617
          // timeout, 10 seconds or 5 queries
5618
          doThrottle(d_now.tv_sec, remoteIP, qname, qtype, 10, 5, Throttle::Reason::Timeout);
24✔
5619
        }
24✔
5620
      }
325✔
5621
    }
377✔
5622

5623
    return false;
383✔
5624
  }
383✔
5625

5626
  if (!lwr.d_validpacket) {
11,836✔
5627
    LOG(prefix << qname << ": " << nsName << " (" << remoteIP.toString() << ") returned a packet we could not parse over " << (doTCP ? "TCP" : "UDP") << ", trying sibling IP or NS" << endl);
8!
5628
    if (!chained && !dontThrottle) {
8!
5629

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

5633
      if (doTCP) {
8✔
5634
        // we can be more heavy-handed over TCP
5635
        doThrottle(d_now.tv_sec, remoteIP, qname, qtype, 60, 10, Throttle::Reason::ParseError);
4✔
5636
      }
4✔
5637
      else {
4✔
5638
        doThrottle(d_now.tv_sec, remoteIP, qname, qtype, 10, 2, Throttle::Reason::ParseError);
4✔
5639
      }
4✔
5640
    }
8✔
5641
    return false;
8✔
5642
  }
8✔
5643
  /* we got an answer */
5644
  if (lwr.d_rcode != RCode::NoError && lwr.d_rcode != RCode::NXDomain) {
11,828✔
5645
    LOG(prefix << qname << ": " << nsName << " (" << remoteIP.toString() << ") returned a " << RCode::to_s(lwr.d_rcode) << ", trying sibling IP or NS" << endl);
64!
5646
    if (!chained && !dontThrottle) {
64!
5647
      if (wasForwarded && lwr.d_rcode == RCode::ServFail) {
64✔
5648
        // 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
5649
        // 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
5650
        // at the very least we will detect that if our packets stop being answered
5651
        s_nsSpeeds.lock()->find_or_enter(nsName.empty() ? DNSName(remoteIP.toStringWithPort()) : nsName, d_now).submit(remoteIP, 1000000, d_now); // 1 sec
4!
5652
      }
4✔
5653
      else {
60✔
5654
        Throttle::Reason reason{};
60✔
5655
        switch (lwr.d_rcode) {
60✔
5656
        case RCode::ServFail:
×
5657
          reason = Throttle::Reason::RCodeServFail;
×
5658
          break;
×
5659
        case RCode::Refused:
8✔
5660
          reason = Throttle::Reason::RCodeRefused;
8✔
5661
          break;
8✔
5662
        default:
52✔
5663
          reason = Throttle::Reason::RCodeOther;
52✔
5664
          break;
52✔
5665
        }
60✔
5666
        doThrottle(d_now.tv_sec, remoteIP, qname, qtype, 60, 3, reason);
60✔
5667
      }
60✔
5668
    }
64✔
5669
    return false;
64✔
5670
  }
64✔
5671

5672
  /* this server sent a valid answer, mark it backup up if it was down */
5673
  if (s_serverdownmaxfails > 0) {
11,764✔
5674
    s_fails.lock()->clear(remoteIP);
11,754✔
5675
  }
11,754✔
5676
  // Clear all throttles for this IP, both general and specific throttles for qname-qtype
5677
  unThrottle(remoteIP, qname, qtype);
11,764✔
5678

5679
  if (lwr.d_tcbit) {
11,764✔
5680
    truncated = true;
36✔
5681

5682
    if (doTCP) {
36✔
5683
      LOG(prefix << qname << ": Truncated bit set, over TCP?" << endl);
2!
5684
      if (!dontThrottle) {
2!
5685
        /* let's treat that as a ServFail answer from this server */
5686
        doThrottle(d_now.tv_sec, remoteIP, qname, qtype, 60, 3, Throttle::Reason::TCPTruncate);
2✔
5687
      }
2✔
5688
      return false;
2✔
5689
    }
2✔
5690
    LOG(prefix << qname << ": Truncated bit set, over UDP" << endl);
34!
5691

5692
    return true;
34✔
5693
  }
36✔
5694

5695
  return true;
11,728✔
5696
}
11,764✔
5697

5698
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)
5699
{
1,567✔
5700
  if (newtarget == qname) {
1,567✔
5701
    LOG(prefix << qname << ": Status=got a CNAME referral to self, returning SERVFAIL" << endl);
3!
5702
    ret.clear();
3✔
5703
    rcode = RCode::ServFail;
3✔
5704
    return;
3✔
5705
  }
3✔
5706
  if (newtarget.isPartOf(qname)) {
1,564✔
5707
    // a.b.c. CNAME x.a.b.c will go to great depths with QM on
5708
    LOG(prefix << qname << ": Status=got a CNAME referral to child, disabling QM" << endl);
29!
5709
    setQNameMinimization(false);
29✔
5710
  }
29✔
5711

5712
  if (!d_followCNAME) {
1,564✔
5713
    rcode = RCode::NoError;
161✔
5714
    return;
161✔
5715
  }
161✔
5716

5717
  // Check to see if we already have seen the new target as a previous target or that the chain is too long
5718
  const auto [CNAMELoop, numCNAMEs] = scanForCNAMELoop(newtarget, ret);
1,403✔
5719
  if (CNAMELoop) {
1,403✔
5720
    LOG(prefix << qname << ": Status=got a CNAME referral that causes a loop, returning SERVFAIL" << endl);
2!
5721
    ret.clear();
2✔
5722
    rcode = RCode::ServFail;
2✔
5723
    return;
2✔
5724
  }
2✔
5725
  if (numCNAMEs > s_max_CNAMES_followed) {
1,401✔
5726
    LOG(prefix << qname << ": Status=got a CNAME referral, but chain too long, returning SERVFAIL" << endl);
2!
5727
    rcode = RCode::ServFail;
2✔
5728
    return;
2✔
5729
  }
2✔
5730

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

5734
    if (d_doDNSSEC) {
4!
5735
      addNXNSECS(ret, recordsFromAnswer);
4✔
5736
    }
4✔
5737

5738
    rcode = RCode::NoError;
4✔
5739
    return;
4✔
5740
  }
4✔
5741

5742
  LOG(prefix << qname << ": Status=got a CNAME referral, starting over with " << newtarget << endl);
1,395✔
5743

5744
  set<GetBestNSAnswer> beenthere;
1,395✔
5745
  Context cnameContext;
1,395✔
5746
  rcode = doResolve(newtarget, qtype, ret, depth + 1, beenthere, cnameContext);
1,395✔
5747
  LOG(prefix << qname << ": Updating validation state for response to " << qname << " from " << state << " with the state from the CNAME quest: " << cnameContext.state << endl);
1,395✔
5748
  updateValidationState(qname, state, cnameContext.state, prefix);
1,395✔
5749
}
1,395✔
5750

5751
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)
5752
{
11,852✔
5753
  if (s_minimumTTL != 0) {
11,852✔
5754
    for (auto& rec : lwr.d_records) {
61,061✔
5755
      rec.d_ttl = max(rec.d_ttl, s_minimumTTL);
61,061✔
5756
    }
61,061✔
5757
  }
9,642✔
5758

5759
  /* if the answer is ECS-specific, a minimum TTL is set for this kind of answers
5760
     and it's higher than the global minimum TTL */
5761
  if (ednsmask && s_minimumECSTTL > 0 && (s_minimumTTL == 0 || s_minimumECSTTL > s_minimumTTL)) {
11,852!
5762
    for (auto& rec : lwr.d_records) {
2✔
5763
      if (rec.d_place == DNSResourceRecord::ANSWER) {
2!
5764
        rec.d_ttl = max(rec.d_ttl, s_minimumECSTTL);
2✔
5765
      }
2✔
5766
    }
2✔
5767
  }
2✔
5768

5769
  bool needWildcardProof = false;
11,852✔
5770
  bool gatherWildcardProof = false;
11,852✔
5771
  unsigned int wildcardLabelsCount = 0;
11,852✔
5772
  *rcode = updateCacheFromRecords(depth, prefix, lwr, qname, qtype, auth, wasForwarded, ednsmask, state, needWildcardProof, gatherWildcardProof, wildcardLabelsCount, sendRDQuery, remoteIP);
11,852✔
5773
  if (*rcode != RCode::NoError) {
11,852!
5774
    return true;
×
5775
  }
×
5776

5777
  LOG(prefix << qname << ": Determining status after receiving this packet" << endl);
11,852✔
5778

5779
  set<DNSName> nsset;
11,852✔
5780
  bool realreferral = false;
11,852✔
5781
  bool negindic = false;
11,852✔
5782
  bool negIndicHasSignatures = false;
11,852✔
5783
  DNSName newauth;
11,852✔
5784
  DNSName newtarget;
11,852✔
5785

5786
  bool done = processRecords(prefix, qname, qtype, auth, lwr, sendRDQuery, ret, nsset, newtarget, newauth, realreferral, negindic, state, needWildcardProof, gatherWildcardProof, wildcardLabelsCount, *rcode, negIndicHasSignatures, depth);
11,852✔
5787

5788
  // If we both have a CNAME and an answer, let the CNAME take precedence. This *should* not happen
5789
  // (because CNAMEs cannot co-exist with other records), but reality says otherwise. Other
5790
  // resolvers choose to follow the CNAME in this case as well. We removed the answer record from
5791
  // the records received from the auth when sanitizing, so `done' should not be set when a CNAME is
5792
  // present.
5793
  if (done) {
11,852✔
5794
    LOG(prefix << qname << ": Status=got results, this level of recursion done" << endl);
5,298✔
5795
    LOG(prefix << qname << ": Validation status is " << state << endl);
5,298✔
5796
    return true;
5,298✔
5797
  }
5,298✔
5798

5799
  if (!newtarget.empty()) {
6,554✔
5800
    handleNewTarget(prefix, qname, newtarget, qtype.getCode(), ret, *rcode, depth, lwr.d_records, state);
1,561✔
5801
    return true;
1,561✔
5802
  }
1,561✔
5803

5804
  if (lwr.d_rcode == RCode::NXDomain) {
4,993✔
5805
    LOG(prefix << qname << ": Status=NXDOMAIN, we are done " << (negindic ? "(have negative SOA)" : "") << endl);
131!
5806

5807
    auto tempState = getValidationStatus(qname, negIndicHasSignatures, qtype == QType::DS, depth, prefix);
131✔
5808
    if (tempState == vState::Secure && (lwr.d_aabit || sendRDQuery) && !negindic) {
131!
5809
      LOG(prefix << qname << ": NXDOMAIN without a negative indication (missing SOA in authority) in a DNSSEC secure zone, going Bogus" << endl);
4!
5810
      updateValidationState(qname, state, vState::BogusMissingNegativeIndication, prefix);
4✔
5811
    }
4✔
5812
    else {
127✔
5813
      /* we might not have validated any record, because we did get a NXDOMAIN without any SOA
5814
         from an insecure zone, for example */
5815
      updateValidationState(qname, state, tempState, prefix);
127✔
5816
    }
127✔
5817

5818
    if (d_doDNSSEC) {
131✔
5819
      addNXNSECS(ret, lwr.d_records);
101✔
5820
    }
101✔
5821

5822
    *rcode = RCode::NXDomain;
131✔
5823
    return true;
131✔
5824
  }
131✔
5825

5826
  if (nsset.empty() && lwr.d_rcode == 0 && (negindic || lwr.d_aabit || sendRDQuery)) {
4,862!
5827
    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,388!
5828

5829
    auto tempState = getValidationStatus(qname, negIndicHasSignatures, qtype == QType::DS, depth, prefix);
1,388✔
5830
    if (tempState == vState::Secure && (lwr.d_aabit || sendRDQuery) && !negindic) {
1,388✔
5831
      LOG(prefix << qname << ": NODATA without a negative indication (missing SOA in authority) in a DNSSEC secure zone, going Bogus" << endl);
11!
5832
      updateValidationState(qname, state, vState::BogusMissingNegativeIndication, prefix);
11✔
5833
    }
11✔
5834
    else {
1,377✔
5835
      /* we might not have validated any record, because we did get a NODATA without any SOA
5836
         from an insecure zone, for example */
5837
      updateValidationState(qname, state, tempState, prefix);
1,377✔
5838
    }
1,377✔
5839

5840
    if (d_doDNSSEC) {
1,388✔
5841
      addNXNSECS(ret, lwr.d_records);
1,318✔
5842
    }
1,318✔
5843

5844
    *rcode = RCode::NoError;
1,388✔
5845
    return true;
1,388✔
5846
  }
1,388✔
5847

5848
  if (realreferral) {
3,474✔
5849
    LOG(prefix << qname << ": Status=did not resolve, got " << (unsigned int)nsset.size() << " NS, ");
3,402✔
5850

5851
    nameservers.clear();
3,402✔
5852
    for (auto const& nameserver : nsset) {
13,047✔
5853
      if (d_wantsRPZ && !d_appliedPolicy.wasHit()) {
13,048✔
5854
        bool match = dfe.getProcessingPolicy(nameserver, d_discardedPolicies, d_appliedPolicy);
13,046✔
5855
        if (match) {
13,046✔
5856
          mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());
2✔
5857
          if (d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) { // client query needs an RPZ response
2!
5858
            if (d_pdl && d_pdl->policyHitEventFilter(d_requestor, qname, qtype, d_queryReceivedOverTCP, d_appliedPolicy, d_policyTags, d_discardedPolicies)) {
2!
5859
              /* reset to no match */
5860
              d_appliedPolicy = DNSFilterEngine::Policy();
×
5861
            }
×
5862
            else {
2✔
5863
              LOG("however " << nameserver << " was blocked by RPZ policy '" << d_appliedPolicy.getName() << "'" << endl);
2!
5864
              throw PolicyHitException();
2✔
5865
            }
2✔
5866
          }
2✔
5867
        }
2✔
5868
      }
13,046✔
5869
      nameservers.insert({nameserver, {{}, false}});
13,045✔
5870
    }
13,045✔
5871
    LOG("looping to them" << endl);
3,400✔
5872
    *gotNewServers = true;
3,400✔
5873
    auth = std::move(newauth);
3,400✔
5874

5875
    return false;
3,400✔
5876
  }
3,402✔
5877

5878
  return false;
72✔
5879
}
3,474✔
5880

5881
bool SyncRes::doDoTtoAuth(const DNSName& nameServer)
5882
{
12,191✔
5883
  return g_DoTToAuthNames.getLocal()->check(nameServer);
12,191✔
5884
}
12,191✔
5885

5886
/** returns:
5887
 *  -1 in case of no results
5888
 *  rcode otherwise
5889
 */
5890
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
5891
int SyncRes::doResolveAt(NsSet& nameservers, DNSName auth, bool flawedNSSet, const DNSName& qname, const QType qtype,
5892
                         vector<DNSRecord>& ret,
5893
                         unsigned int depth, const string& prefix, set<GetBestNSAnswer>& beenthere, Context& context, StopAtDelegation* stopAtDelegation,
5894
                         map<DNSName, vector<ComboAddress>>* fallBack)
5895
{
10,217✔
5896
  auto luaconfsLocal = g_luaconfs.getLocal();
10,217✔
5897

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

5900
  if (nameserversBlockedByRPZ(luaconfsLocal->dfe, nameservers)) {
10,217✔
5901
    /* RPZ hit */
5902
    if (d_pdl && d_pdl->policyHitEventFilter(d_requestor, qname, qtype, d_queryReceivedOverTCP, d_appliedPolicy, d_policyTags, d_discardedPolicies)) {
2!
5903
      /* reset to no match */
5904
      d_appliedPolicy = DNSFilterEngine::Policy();
×
5905
    }
×
5906
    else {
2✔
5907
      throw PolicyHitException();
2✔
5908
    }
2✔
5909
  }
2✔
5910

5911
  LOG(endl);
10,215✔
5912

5913
  unsigned int addressQueriesForNS = 0;
10,215✔
5914
  for (;;) { // we may get more specific nameservers
12,131✔
5915
    auto rnameservers = shuffleInSpeedOrder(qname, nameservers, prefix);
12,131✔
5916

5917
    // We allow s_maxnsaddressqperq (default 10) queries with empty responses when resolving NS names.
5918
    // If a zone publishes many (more than s_maxnsaddressqperq) NS records, we allow less.
5919
    // This is to "punish" zones that publish many non-resolving NS names.
5920
    // We always allow 5 NS name resolving attempts with empty results.
5921
    unsigned int nsLimit = s_maxnsaddressqperq;
12,131✔
5922
    if (rnameservers.size() > nsLimit) {
12,131✔
5923
      int newLimit = static_cast<int>(nsLimit - (rnameservers.size() - nsLimit));
2,699✔
5924
      nsLimit = std::max(5, newLimit);
2,699✔
5925
    }
2,699✔
5926

5927
    for (auto tns = rnameservers.cbegin();; ++tns) {
12,589✔
5928
      if (addressQueriesForNS >= nsLimit) {
12,589✔
5929
        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✔
5930
      }
2✔
5931
      if (tns == rnameservers.cend()) {
12,587✔
5932
        LOG(prefix << qname << ": Failed to resolve via any of the " << (unsigned int)rnameservers.size() << " offered NS at level '" << auth << "'" << endl);
223✔
5933
        if (s_addExtendedResolutionDNSErrors) {
223✔
5934
          context.extendedError = EDNSExtendedError{static_cast<uint16_t>(EDNSExtendedError::code::NoReachableAuthority), "delegation " + auth.toLogString()};
41✔
5935
        }
41✔
5936
        if (!auth.isRoot() && flawedNSSet) {
223✔
5937
          LOG(prefix << qname << ": Ageing nameservers for level '" << auth << "', next query might succeed" << endl);
65!
5938
          if (g_recCache->doAgeCache(d_now.tv_sec, auth, QType::NS, 10)) {
65✔
5939
            t_Counters.at(rec::Counter::nsSetInvalidations)++;
16✔
5940
          }
16✔
5941
        }
65✔
5942
        return -1;
223✔
5943
      }
223✔
5944

5945
      bool cacheOnly = false;
12,364✔
5946
      // this line needs to identify the 'self-resolving' behaviour
5947
      if (qname == tns->first && (qtype.getCode() == QType::A || qtype.getCode() == QType::AAAA)) {
12,364!
5948
        /* we might have a glue entry in cache so let's try this NS
5949
           but only if we have enough in the cache to know how to reach it */
5950
        LOG(prefix << qname << ": Using NS to resolve itself, but only using what we have in cache (" << (1 + tns - rnameservers.cbegin()) << "/" << rnameservers.size() << ")" << endl);
51!
5951
        cacheOnly = true;
51✔
5952
      }
51✔
5953

5954
      typedef vector<ComboAddress> remoteIPs_t;
12,364✔
5955
      remoteIPs_t remoteIPs;
12,364✔
5956
      remoteIPs_t::iterator remoteIP;
12,364✔
5957
      bool pierceDontQuery = false;
12,364✔
5958
      bool sendRDQuery = false;
12,364✔
5959
      boost::optional<Netmask> ednsmask;
12,364✔
5960
      LWResult lwr;
12,364✔
5961
      const bool wasForwarded = tns->first.empty() && (!nameservers[tns->first].first.empty());
12,364✔
5962
      int rcode = RCode::NoError;
12,364✔
5963
      bool gotNewServers = false;
12,364✔
5964

5965
      if (tns->first.empty() && !wasForwarded) {
12,364✔
5966
        static ComboAddress const s_oobRemote("255.255.255.255");
130✔
5967
        LOG(prefix << qname << ": Domain is out-of-band" << endl);
130!
5968
        /* setting state to indeterminate since validation is disabled for local auth zone,
5969
           and Insecure would be misleading. */
5970
        context.state = vState::Indeterminate;
130✔
5971
        d_wasOutOfBand = doOOBResolve(qname, qtype, lwr.d_records, depth, prefix, lwr.d_rcode);
130✔
5972
        lwr.d_tcbit = false;
130✔
5973
        lwr.d_aabit = true;
130✔
5974

5975
        /* we have received an answer, are we done ? */
5976
        bool done = processAnswer(depth, prefix, lwr, qname, qtype, auth, false, ednsmask, sendRDQuery, nameservers, ret, luaconfsLocal->dfe, &gotNewServers, &rcode, context.state, s_oobRemote);
130✔
5977
        if (done) {
130✔
5978
          return rcode;
126✔
5979
        }
126✔
5980
        if (gotNewServers) {
4!
5981
          if (stopAtDelegation != nullptr && *stopAtDelegation == Stop) {
4!
5982
            *stopAtDelegation = Stopped;
×
5983
            return rcode;
×
5984
          }
×
5985
          break;
4✔
5986
        }
4✔
5987
      }
4✔
5988
      else {
12,234✔
5989
        if (fallBack != nullptr) {
12,234✔
5990
          if (auto iter = fallBack->find(tns->first); iter != fallBack->end()) {
2!
5991
            remoteIPs = iter->second;
2✔
5992
          }
2✔
5993
        }
2✔
5994
        if (remoteIPs.empty()) {
12,234✔
5995
          remoteIPs = retrieveAddressesForNS(prefix, qname, tns, depth, beenthere, rnameservers, nameservers, sendRDQuery, pierceDontQuery, flawedNSSet, cacheOnly, addressQueriesForNS);
12,232✔
5996
        }
12,232✔
5997

5998
        if (remoteIPs.empty()) {
12,234✔
5999
          LOG(prefix << qname << ": Failed to get IP for NS " << tns->first << ", trying next if available" << endl);
136!
6000
          flawedNSSet = true;
136✔
6001
          continue;
136✔
6002
        }
136✔
6003
        bool hitPolicy{false};
12,098✔
6004
        LOG(prefix << qname << ": Resolved '" << auth << "' NS " << tns->first << " to: ");
12,098✔
6005
        for (remoteIP = remoteIPs.begin(); remoteIP != remoteIPs.end(); ++remoteIP) {
25,620✔
6006
          if (remoteIP != remoteIPs.begin()) {
13,522✔
6007
            LOG(", ");
1,514✔
6008
          }
1,514✔
6009
          LOG(remoteIP->toString());
13,522✔
6010
          if (nameserverIPBlockedByRPZ(luaconfsLocal->dfe, *remoteIP)) {
13,522✔
6011
            hitPolicy = true;
4✔
6012
          }
4✔
6013
        }
13,522✔
6014
        LOG(endl);
12,098✔
6015
        if (hitPolicy) { // implies d_wantsRPZ
12,098✔
6016
          /* RPZ hit */
6017
          if (d_pdl && d_pdl->policyHitEventFilter(d_requestor, qname, qtype, d_queryReceivedOverTCP, d_appliedPolicy, d_policyTags, d_discardedPolicies)) {
4!
6018
            /* reset to no match */
6019
            d_appliedPolicy = DNSFilterEngine::Policy();
×
6020
          }
×
6021
          else {
4✔
6022
            throw PolicyHitException();
4✔
6023
          }
4✔
6024
        }
4✔
6025

6026
        for (remoteIP = remoteIPs.begin(); remoteIP != remoteIPs.end(); ++remoteIP) {
12,650✔
6027
          LOG(prefix << qname << ": Trying IP " << remoteIP->toStringWithPort() << ", asking '" << qname << "|" << qtype << "'" << endl);
12,198✔
6028

6029
          if (throttledOrBlocked(prefix, *remoteIP, qname, qtype, pierceDontQuery)) {
12,198✔
6030
            // As d_throttledqueries might be increased, check the max-qperq condition
6031
            checkMaxQperQ(qname);
7✔
6032
            continue;
7✔
6033
          }
7✔
6034

6035
          bool truncated = false;
12,191✔
6036
          bool spoofed = false;
12,191✔
6037
          bool gotAnswer = false;
12,191✔
6038
          bool doDoT = false;
12,191✔
6039

6040
          if (doDoTtoAuth(tns->first)) {
12,191✔
6041
            remoteIP->setPort(853);
3✔
6042
            doDoT = true;
3✔
6043
          }
3✔
6044
          if (SyncRes::s_dot_to_port_853 && remoteIP->getPort() == 853) {
12,191✔
6045
            doDoT = true;
9✔
6046
          }
9✔
6047
          bool forceTCP = doDoT;
12,191✔
6048

6049
          if (!doDoT && s_max_busy_dot_probes > 0) {
12,191!
6050
            submitTryDotTask(*remoteIP, auth, tns->first, d_now.tv_sec);
×
6051
          }
×
6052
          if (!forceTCP) {
12,191✔
6053
            gotAnswer = doResolveAtThisIP(prefix, qname, qtype, lwr, ednsmask, auth, sendRDQuery, wasForwarded,
12,182✔
6054
                                          tns->first, *remoteIP, false, false, truncated, spoofed, context.extendedError);
12,182✔
6055
          }
12,182✔
6056
          if (forceTCP || (spoofed || (gotAnswer && truncated))) {
12,191!
6057
            /* retry, over TCP this time */
6058
            gotAnswer = doResolveAtThisIP(prefix, qname, qtype, lwr, ednsmask, auth, sendRDQuery, wasForwarded,
43✔
6059
                                          tns->first, *remoteIP, true, doDoT, truncated, spoofed, context.extendedError);
43✔
6060
          }
43✔
6061

6062
          if (!gotAnswer) {
12,191✔
6063
            if (doDoT && s_max_busy_dot_probes > 0) {
457!
6064
              // This is quite pessimistic...
6065
              updateDoTStatus(*remoteIP, DoTStatus::Bad, d_now.tv_sec + dotFailWait);
×
6066
            }
×
6067
            continue;
457✔
6068
          }
457✔
6069

6070
          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);
11,734✔
6071

6072
          if (doDoT && s_max_busy_dot_probes > 0) {
11,734!
6073
            updateDoTStatus(*remoteIP, DoTStatus::Good, d_now.tv_sec + dotSuccessWait);
×
6074
          }
×
6075
          /*  // for you IPv6 fanatics :-)
6076
              if(remoteIP->sin4.sin_family==AF_INET6)
6077
              lwr.d_usec/=3;
6078
          */
6079
          //        cout<<"ms: "<<lwr.d_usec/1000.0<<", "<<g_avgLatency/1000.0<<'\n';
6080

6081
          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);
11,734✔
6082

6083
          /* we have received an answer, are we done ? */
6084
          bool done = processAnswer(depth, prefix, lwr, qname, qtype, auth, wasForwarded, ednsmask, sendRDQuery, nameservers, ret, luaconfsLocal->dfe, &gotNewServers, &rcode, context.state, *remoteIP);
11,734✔
6085
          if (done) {
11,734✔
6086
            return rcode;
8,246✔
6087
          }
8,246✔
6088
          if (gotNewServers) {
3,488✔
6089
            if (stopAtDelegation != nullptr && *stopAtDelegation == Stop) {
3,396!
6090
              *stopAtDelegation = Stopped;
1,484✔
6091
              return rcode;
1,484✔
6092
            }
1,484✔
6093
            break;
1,912✔
6094
          }
3,396✔
6095
          /* was lame */
6096
          if (!shouldNotThrottle(&tns->first, &*remoteIP)) {
92✔
6097
            doThrottle(d_now.tv_sec, *remoteIP, qname, qtype, 60, 100, Throttle::Reason::Lame);
52✔
6098
          }
52✔
6099
        }
92✔
6100

6101
        if (gotNewServers) {
2,364✔
6102
          break;
1,912✔
6103
        }
1,912✔
6104

6105
        if (remoteIP == remoteIPs.cend()) { // we tried all IP addresses, none worked
452✔
6106
          continue;
322✔
6107
        }
322✔
6108
      }
452✔
6109
    }
12,364✔
6110
  }
12,131✔
6111
  return -1;
130✔
6112
}
10,215✔
6113

6114
void SyncRes::setQuerySource(const Netmask& netmask)
6115
{
×
6116
  if (!netmask.empty()) {
×
6117
    d_outgoingECSNetwork = netmask;
×
6118
  }
×
6119
  else {
×
6120
    d_outgoingECSNetwork = boost::none;
×
6121
  }
×
6122
}
×
6123

6124
void SyncRes::setQuerySource(const ComboAddress& requestor, const boost::optional<const EDNSSubnetOpts&>& incomingECS)
6125
{
2,741✔
6126
  d_requestor = requestor;
2,741✔
6127

6128
  if (incomingECS && incomingECS->getSourcePrefixLength() > 0) {
2,741✔
6129
    d_cacheRemote = incomingECS->getSource().getMaskedNetwork();
48✔
6130
    uint8_t bits = std::min(incomingECS->getSourcePrefixLength(), (incomingECS->getSource().isIPv4() ? s_ecsipv4limit : s_ecsipv6limit));
48✔
6131
    ComboAddress trunc = incomingECS->getSource().getNetwork();
48✔
6132
    trunc.truncate(bits);
48✔
6133
    d_outgoingECSNetwork = boost::optional<Netmask>(Netmask(trunc, bits));
48✔
6134
  }
48✔
6135
  else {
2,693✔
6136
    d_cacheRemote = d_requestor;
2,693✔
6137
    if (!incomingECS && s_ednslocalsubnets.match(d_requestor)) {
2,693✔
6138
      ComboAddress trunc = d_requestor;
84✔
6139
      uint8_t bits = d_requestor.isIPv4() ? 32 : 128;
84✔
6140
      bits = std::min(bits, (trunc.isIPv4() ? s_ecsipv4limit : s_ecsipv6limit));
84✔
6141
      trunc.truncate(bits);
84✔
6142
      d_outgoingECSNetwork = boost::optional<Netmask>(Netmask(trunc, bits));
84✔
6143
    }
84✔
6144
    else if (s_ecsScopeZero.getSourcePrefixLength() > 0) {
2,609✔
6145
      /* RFC7871 says we MUST NOT send any ECS if the source scope is 0.
6146
         But using an empty ECS in that case would mean inserting
6147
         a non ECS-specific entry into the cache, preventing any further
6148
         ECS-specific query to be sent.
6149
         So instead we use the trick described in section 7.1.2:
6150
         "The subsequent Recursive Resolver query to the Authoritative Nameserver
6151
         will then either not include an ECS option or MAY optionally include
6152
         its own address information, which is what the Authoritative
6153
         Nameserver will almost certainly use to generate any Tailored
6154
         Response in lieu of an option.  This allows the answer to be handled
6155
         by the same caching mechanism as other queries, with an explicit
6156
         indicator of the applicable scope.  Subsequent Stub Resolver queries
6157
         for /0 can then be answered from this cached response.
6158
      */
6159
      d_outgoingECSNetwork = boost::optional<Netmask>(s_ecsScopeZero.getSource().getMaskedNetwork());
2,605✔
6160
      d_cacheRemote = s_ecsScopeZero.getSource().getNetwork();
2,605✔
6161
    }
2,605✔
6162
    else {
4✔
6163
      // ECS disabled because no scope-zero address could be derived.
6164
      d_outgoingECSNetwork = boost::none;
4✔
6165
    }
4✔
6166
  }
2,693✔
6167
}
2,741✔
6168

6169
boost::optional<Netmask> SyncRes::getEDNSSubnetMask(const DNSName& name, const ComboAddress& rem)
6170
{
12,219✔
6171
  if (d_outgoingECSNetwork && (s_ednsdomains.check(name) || s_ednsremotesubnets.match(rem))) {
12,219✔
6172
    return d_outgoingECSNetwork;
70✔
6173
  }
70✔
6174
  return boost::none;
12,149✔
6175
}
12,219✔
6176

6177
void SyncRes::parseEDNSSubnetAllowlist(const std::string& alist)
6178
{
314✔
6179
  vector<string> parts;
314✔
6180
  stringtok(parts, alist, ",; ");
314✔
6181
  for (const auto& allow : parts) {
314✔
6182
    try {
15✔
6183
      s_ednsremotesubnets.addMask(Netmask(allow));
15✔
6184
    }
15✔
6185
    catch (...) {
15✔
6186
      s_ednsdomains.add(DNSName(allow));
12✔
6187
    }
12✔
6188
  }
15✔
6189
}
314✔
6190

6191
void SyncRes::parseEDNSSubnetAddFor(const std::string& subnetlist)
6192
{
157✔
6193
  vector<string> parts;
157✔
6194
  stringtok(parts, subnetlist, ",; ");
157✔
6195
  for (const auto& allow : parts) {
1,567✔
6196
    s_ednslocalsubnets.addMask(allow);
1,567✔
6197
  }
1,567✔
6198
}
157✔
6199

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

6206
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)
6207
{
24✔
6208
  auto log = slog->withValues("qname", Logging::Loggable(qname), "qtype", Logging::Loggable(qtype));
24✔
6209

6210
  struct timeval now{};
24✔
6211
  gettimeofday(&now, nullptr);
24✔
6212

6213
  SyncRes resolver(now);
24✔
6214
  resolver.setQNameMinimization(qnamemin);
24✔
6215
  if (pdl) {
24!
6216
    resolver.setLuaEngine(pdl);
24✔
6217
  }
24✔
6218

6219
  int res = -1;
24✔
6220
  const std::string msg = "Exception while resolving";
24✔
6221
  try {
24✔
6222
    res = resolver.beginResolve(qname, qtype, qclass, ret, 0);
24✔
6223
  }
24✔
6224
  catch (const PDNSException& e) {
24✔
6225
    SLOG(g_log << Logger::Warning << "Failed to resolve " << qname << ", got pdns exception: " << e.reason << endl,
×
6226
         log->error(Logr::Warning, e.reason, msg, "exception", Logging::Loggable("PDNSException")));
×
6227
    ret.clear();
×
6228
  }
×
6229
  catch (const ImmediateServFailException& e) {
24✔
6230
    SLOG(g_log << Logger::Warning << "Failed to resolve " << qname << ", got ImmediateServFailException: " << e.reason << endl,
×
6231
         log->error(Logr::Warning, e.reason, msg, "exception", Logging::Loggable("ImmediateServFailException")));
×
6232
    ret.clear();
×
6233
  }
×
6234
  catch (const PolicyHitException& e) {
24✔
6235
    SLOG(g_log << Logger::Warning << "Failed to resolve " << qname << ", got a policy hit" << endl,
×
6236
         log->info(Logr::Warning, msg, "exception", Logging::Loggable("PolicyHitException")));
×
6237
    ret.clear();
×
6238
  }
×
6239
  catch (const std::exception& e) {
24✔
6240
    SLOG(g_log << Logger::Warning << "Failed to resolve " << qname << ", got STL error: " << e.what() << endl,
×
6241
         log->error(Logr::Warning, e.what(), msg, "exception", Logging::Loggable("std::exception")));
×
6242
    ret.clear();
×
6243
  }
×
6244
  catch (...) {
24✔
6245
    SLOG(g_log << Logger::Warning << "Failed to resolve " << qname << ", got an exception" << endl,
×
6246
         log->info(Logr::Warning, msg));
×
6247
    ret.clear();
×
6248
  }
×
6249

6250
  return res;
24✔
6251
}
24✔
6252

6253
int SyncRes::getRootNS(struct timeval now, asyncresolve_t asyncCallback, unsigned int depth, Logr::log_t log)
6254
{
234✔
6255
  if (::arg()["hint-file"] == "no-refresh") {
234!
6256
    return 0;
×
6257
  }
×
6258
  SyncRes resolver(now);
234✔
6259
  resolver.d_prefix = "[getRootNS]";
234✔
6260
  resolver.setDoEDNS0(true);
234✔
6261
  resolver.setUpdatingRootNS();
234✔
6262
  resolver.setDoDNSSEC(g_dnssecmode != DNSSECMode::Off);
234✔
6263
  resolver.setDNSSECValidationRequested(g_dnssecmode != DNSSECMode::Off && g_dnssecmode != DNSSECMode::ProcessNoValidate);
234✔
6264
  resolver.setAsyncCallback(std::move(asyncCallback));
234✔
6265
  resolver.setRefreshAlmostExpired(true);
234✔
6266

6267
  const string msg = "Failed to update . records";
234✔
6268
  vector<DNSRecord> ret;
234✔
6269
  int res = -1;
234✔
6270
  try {
234✔
6271
    res = resolver.beginResolve(g_rootdnsname, QType::NS, 1, ret, depth + 1);
234✔
6272
    if (g_dnssecmode != DNSSECMode::Off && g_dnssecmode != DNSSECMode::ProcessNoValidate) {
234✔
6273
      auto state = resolver.getValidationState();
187✔
6274
      if (vStateIsBogus(state)) {
187!
6275
        throw PDNSException("Got Bogus validation result for .|NS");
×
6276
      }
×
6277
    }
187✔
6278
  }
234✔
6279
  catch (const PDNSException& e) {
234✔
6280
    SLOG(g_log << Logger::Error << "Failed to update . records, got an exception: " << e.reason << endl,
×
6281
         log->error(Logr::Error, e.reason, msg, "exception", Logging::Loggable("PDNSException")));
×
6282
  }
×
6283
  catch (const ImmediateServFailException& e) {
234✔
6284
    SLOG(g_log << Logger::Error << "Failed to update . records, got an exception: " << e.reason << endl,
×
6285
         log->error(Logr::Error, e.reason, msg, "exception", Logging::Loggable("ImmediateServFailException")));
×
6286
  }
×
6287
  catch (const PolicyHitException& policyHit) {
234✔
6288
    SLOG(g_log << Logger::Error << "Failed to update . records, got a policy hit" << endl,
×
6289
         log->info(Logr::Error, msg, "exception", Logging::Loggable("PolicyHitException"),
×
6290
                   "policyName", Logging::Loggable(resolver.d_appliedPolicy.getName())));
×
6291
    ret.clear();
×
6292
  }
×
6293
  catch (const std::exception& e) {
234✔
6294
    SLOG(g_log << Logger::Error << "Failed to update . records, got an exception: " << e.what() << endl,
×
6295
         log->error(Logr::Error, e.what(), msg, "exception", Logging::Loggable("std::exception")));
×
6296
  }
×
6297
  catch (...) {
234✔
6298
    SLOG(g_log << Logger::Error << "Failed to update . records, got an exception" << endl,
×
6299
         log->info(Logr::Error, msg));
×
6300
  }
×
6301

6302
  if (res == 0) {
234✔
6303
    SLOG(g_log << Logger::Debug << "Refreshed . records" << endl,
199✔
6304
         log->info(Logr::Debug, "Refreshed . records"));
199✔
6305
  }
199✔
6306
  else {
35✔
6307
    SLOG(g_log << Logger::Warning << "Failed to update root NS records, RCODE=" << res << endl,
35✔
6308
         log->info(Logr::Warning, msg, "rcode", Logging::Loggable(res)));
35✔
6309
  }
35✔
6310
  return res;
234✔
6311
}
234✔
6312

6313
bool SyncRes::answerIsNOData(uint16_t requestedType, int rcode, const std::vector<DNSRecord>& records)
6314
{
347✔
6315
  if (rcode != RCode::NoError) {
347✔
6316
    return false;
43✔
6317
  }
43✔
6318

6319
  // NOLINTNEXTLINE(readability-use-anyofallof)
6320
  for (const auto& rec : records) {
366✔
6321
    if (rec.d_place == DNSResourceRecord::ANSWER && rec.d_type == requestedType) {
366✔
6322
      /* we have a record, of the right type, in the right section */
6323
      return false;
247✔
6324
    }
247✔
6325
  }
366✔
6326
  return true;
57✔
6327
#if 0
6328
  // This code should be equivalent to the code above, clang-tidy prefers any_of()
6329
  // I have doubts if that is easier to read
6330
  return !std::any_of(records.begin(), records.end(), [=](const DNSRecord& rec) {
6331
    return rec.d_place == DNSResourceRecord::ANSWER && rec.d_type == requestedType;
6332
  });
6333
#endif
6334
}
304✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc