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

PowerDNS / pdns / 13012068652

28 Jan 2025 01:59PM UTC coverage: 64.71% (+0.01%) from 64.699%
13012068652

Pull #14724

github

web-flow
Merge b15562560 into db18c3a17
Pull Request #14724: dnsdist: Add meson support

38328 of 90334 branches covered (42.43%)

Branch coverage included in aggregate %.

361 of 513 new or added lines in 35 files covered. (70.37%)

42 existing lines in 13 files now uncovered.

128150 of 166934 relevant lines covered (76.77%)

4540890.91 hits per line

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

79.99
/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)) {}
165✔
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
  {
8,803✔
73
    auto iter = d_cont.find(arg);
8,803✔
74

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

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

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

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

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

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

109
  void prune(time_t cutoff)
110
  {
232✔
111
    auto& ind = d_cont.template get<time_t>();
232✔
112
    ind.erase(ind.begin(), ind.upper_bound(cutoff));
232✔
113
  }
232✔
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
    {
11,183✔
134
      d_last = arg;
11,183✔
135
      auto val = static_cast<float>(arg);
11,183✔
136
      if (d_val == 0) {
11,183✔
137
        d_val = val;
5,385✔
138
      }
5,385✔
139
      else {
5,798✔
140
        auto diff = makeFloat(last - now);
5,798✔
141
        auto factor = expf(diff) / 2.0F; // might be '0.5', or 0.0001
5,798✔
142
        d_val = (1.0F - factor) * val + factor * d_val;
5,798✔
143
      }
5,798✔
144
    }
11,183✔
145

146
    float get(float factor)
147
    {
44,313✔
148
      return d_val *= factor;
44,313✔
149
    }
44,313✔
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
  {
80,024✔
169
  }
80,024✔
170

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

176
  float getFactor(const struct timeval& now) const
177
  {
41,879✔
178
    float diff = makeFloat(d_lastget - now);
41,879✔
179
    return expf(diff / 60.0F); // is 1.0 or less
41,879✔
180
  }
41,879✔
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
  {
11,083✔
189
    for (auto iter = d_collection.begin(); iter != d_collection.end();) {
23,504✔
190
      if (keep.find(iter->first) != keep.end()) {
12,421✔
191
        ++iter;
12,419✔
192
      }
12,419✔
193
      else {
2✔
194
        iter = d_collection.erase(iter);
2✔
195
      }
2✔
196
    }
12,421✔
197
  }
11,083✔
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
  {
22,266✔
213
    const auto iter = insert(DecayingEwmaCollection{name, now}).first;
22,266✔
214
    return *iter;
22,266✔
215
  }
22,266✔
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
  {
57,738✔
225
    auto& ind = get<DNSName>();
57,738✔
226
    auto iter = insert(DecayingEwmaCollection{name, now}).first;
57,738✔
227
    if (iter->d_collection.empty()) {
57,738✔
228
      return 0;
26,942✔
229
    }
26,942✔
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) {
30,796!
232
      ind.modify(iter, [&](DecayingEwmaCollection& dec) { dec.d_lastget = now; });
×
233
    }
×
234

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

247
static LockGuarded<nsspeeds_t> s_nsSpeeds;
248

249
class Throttle
250
{
251
public:
252
  Throttle() = default;
159✔
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
    {
199✔
267
    }
199✔
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
  {
22,582✔
280
    auto iter = d_cont.find(arg);
22,582✔
281
    if (iter == d_cont.end()) {
22,582✔
282
      return false;
22,520✔
283
    }
22,520✔
284
    if (now > iter->ttd || iter->count == 0) {
62✔
285
      d_cont.erase(iter);
4✔
286
      return false;
4✔
287
    }
4✔
288
    iter->count--;
58✔
289

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

293
  void throttle(time_t now, const Key& arg, time_t ttl, unsigned int count, Reason reason)
294
  {
199✔
295
    auto iter = d_cont.find(arg);
199✔
296
    time_t ttd = now + ttl;
199✔
297
    if (iter == d_cont.end()) {
199!
298
      d_cont.emplace(arg, ttd, count, reason);
199✔
299
    }
199✔
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
  }
199✔
311

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

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

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

327
  void clear(const Key& thing)
328
  {
21,614✔
329
    d_cont.erase(thing);
21,614✔
330
  }
21,614✔
331
  void prune(time_t now)
332
  {
116✔
333
    auto& ind = d_cont.template get<time_t>();
116✔
334
    ind.erase(ind.begin(), ind.upper_bound(now));
116✔
335
  }
116✔
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
  {
88✔
385
    auto& ind = get<time_t>();
88✔
386
    ind.erase(ind.begin(), ind.upper_bound(now));
88✔
387
  }
88✔
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) {                 \
828,735✔
517
    g_log << Logger::Warning << x;   \
105,089✔
518
  }                                  \
105,089✔
519
  else if (d_lm == Store) {          \
828,735✔
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
{
57,420✔
526
  OptLog ret;
57,420✔
527
  if (d_lm == Log) {
57,420✔
528
    ret = {prefix, d_fixednow, g_log};
10,856✔
529
  }
10,856✔
530
  else if (d_lm == Store) {
46,564✔
531
    ret = {prefix, d_fixednow, d_trace};
98✔
532
  }
98✔
533
  return ret;
57,420✔
534
}
57,420✔
535

536
static bool pushResolveIfNotInNegCache(const DNSName& qname, QType qtype, const struct timeval& now)
537
{
996✔
538
  NegCache::NegCacheEntry negEntry;
996✔
539
  bool inNegCache = g_negCache->get(qname, qtype, now, negEntry, false);
996✔
540
  if (!inNegCache) {
996✔
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);
982✔
545
  }
982✔
546
  return !inNegCache;
996✔
547
}
996✔
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,216✔
556
  std::array<char, 20> buf{};
4,216✔
557
  int ret = snprintf(buf.data(), buf.size(), "%0.2f", value);
4,216✔
558
  if (ret < 0 || ret >= static_cast<int>(buf.size())) {
4,216!
559
    return "?";
×
560
  }
×
561
  return {buf.data(), static_cast<size_t>(ret)};
4,216✔
562
}
4,216✔
563

564
static inline void accountAuthLatency(uint64_t usec, int family)
565
{
11,256✔
566
  if (family == AF_INET) {
11,256✔
567
    t_Counters.at(rec::Histogram::auth4Answers)(usec);
10,475✔
568
    t_Counters.at(rec::Histogram::cumulativeAuth4Answers)(usec);
10,475✔
569
  }
10,475✔
570
  else {
781✔
571
    t_Counters.at(rec::Histogram::auth6Answers)(usec);
781✔
572
    t_Counters.at(rec::Histogram::cumulativeAuth6Answers)(usec);
781✔
573
  }
781✔
574
}
11,256✔
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,008✔
579
  d_validationContext.d_nsec3IterationsRemainingQuota = s_maxnsec3iterationsperq > 0 ? s_maxnsec3iterationsperq : std::numeric_limits<decltype(d_validationContext.d_nsec3IterationsRemainingQuota)>::max();
2,147,486,195✔
580
}
4,008✔
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,633✔
767
  d_eventTrace.add(RecEventTrace::SyncRes);
3,633✔
768
  d_wasVariable = false;
3,633✔
769
  d_wasOutOfBand = false;
3,633✔
770
  d_cutStates.clear();
3,633✔
771

772
  if (doSpecialNamesResolve(qname, qtype, qclass, ret)) {
3,633✔
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,587✔
778
    return -1;
30✔
779
  }
30✔
780

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

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

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

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

814
  // Avoid calling addAdditionals() if we know we won't find anything
815
  auto luaLocal = g_luaconfs.getLocal();
3,553✔
816
  if (res == 0 && qclass == QClass::IN && luaLocal->allowAdditionalQTypes.find(qtype) != luaLocal->allowAdditionalQTypes.end()) {
3,553!
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,553✔
823
  return res;
3,553✔
824
}
3,557✔
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,635✔
843
  static const DNSName arpa("1.0.0.127.in-addr.arpa.");
3,635✔
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,635✔
845
  static const DNSName localhost("localhost.");
3,635✔
846
  static const DNSName versionbind("version.bind.");
3,635✔
847
  static const DNSName idserver("id.server.");
3,635✔
848
  static const DNSName versionpdns("version.pdns.");
3,635✔
849
  static const DNSName trustanchorserver("trustanchor.server.");
3,635✔
850
  static const DNSName negativetrustanchorserver("negativetrustanchor.server.");
3,635✔
851

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

855
  if ((qname == arpa || qname == ip6_arpa) && qclass == QClass::IN) {
3,635!
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,635!
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,635!
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,635!
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,635!
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,635!
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,635✔
936
}
3,635✔
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
{
25✔
941
  SyncRes::AuthDomain::records_t::const_iterator ziter = d_records.find(std::tuple(getName(), QType::SOA));
25✔
942
  if (ziter != d_records.end()) {
25!
943
    DNSRecord dnsRecord = *ziter;
25✔
944
    dnsRecord.d_place = DNSResourceRecord::AUTHORITY;
25✔
945
    records.push_back(std::move(dnsRecord));
25✔
946
  }
25✔
947
}
25✔
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
{
140✔
983
  int result = RCode::NoError;
140✔
984
  records.clear();
140✔
985

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

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

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

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

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

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

1016
    return result;
10✔
1017
  }
10✔
1018

1019
  DNSName wcarddomain(qname);
21✔
1020
  while (wcarddomain != getName() && wcarddomain.chopOff()) {
38!
1021
    range = d_records.equal_range(std::tuple(g_wildcarddnsname + wcarddomain));
23✔
1022
    if (range.first == range.second) {
23✔
1023
      continue;
17✔
1024
    }
17✔
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
  }
23✔
1041

1042
  /* Nothing for this name, no wildcard, let's see if there is some NS */
1043
  DNSName nsdomain(qname);
15✔
1044
  while (nsdomain.chopOff() && nsdomain != getName()) {
17!
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()) {
15✔
1057
    addSOA(records);
13✔
1058
    result = RCode::NXDomain;
13✔
1059
  }
13✔
1060

1061
  return result;
15✔
1062
}
21✔
1063

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

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

1073
bool SyncRes::doOOBResolve(const DNSName& qname, const QType qtype, vector<DNSRecord>& ret, unsigned int /* depth */, const string& prefix, int& res)
1074
{
141✔
1075
  DNSName authdomain(qname);
141✔
1076
  const auto iter = getBestAuthZone(&authdomain);
141✔
1077
  if (iter == t_sstorage.domainmap->end() || !iter->second.isAuth()) {
141!
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);
140!
1083
  return doOOBResolve(iter->second, qname, qtype, ret, res);
140✔
1084
}
141✔
1085

1086
bool SyncRes::isRecursiveForwardOrAuth(const DNSName& qname)
1087
{
13,265✔
1088
  DNSName authname(qname);
13,265✔
1089
  const auto iter = getBestAuthZone(&authname);
13,265✔
1090
  return iter != t_sstorage.domainmap->end() && (iter->second.isAuth() || iter->second.shouldRecurse());
13,265✔
1091
}
13,265✔
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
  {
×
1105
  };
×
1106
  size_t len = strftime(buf.data(), buf.size(), s_timestampFormat.c_str(), localtime_r(&tval.tv_sec, &tmval));
×
1107
  if (len == 0) {
×
1108
    int ret = snprintf(buf.data(), buf.size(), "%lld", static_cast<long long>(tval.tv_sec));
×
1109
    if (ret < 0 || static_cast<size_t>(ret) >= buf.size()) {
×
1110
      buf[0] = '\0';
×
1111
      return buf.data();
×
1112
    }
×
1113
    len = ret;
×
1114
  }
×
1115

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

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

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

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

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

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

1165
static LockGuarded<ednsstatus_t> s_ednsstatus;
1166

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1349
  return count;
×
1350
}
×
1351

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

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

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

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

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

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

1394
  return count;
×
1395
}
×
1396

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

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

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

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

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

1434
  return count;
×
1435
}
×
1436

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1547
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
1548
{
11,258✔
1549
  /* what is your QUEST?
1550
     the goal is to get as many remotes as possible on the best level of EDNS support
1551
     The levels are:
1552

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

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

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

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

1588
  LWResult::Result ret{};
11,258✔
1589

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

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

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

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

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

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

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

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

1646
    break;
10,882✔
1647
  }
10,885✔
1648
  return ret;
11,258✔
1649
}
11,258✔
1650

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

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

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

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

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

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

1697
  initZoneCutsFromTA(qname, prefix);
15,449✔
1698

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

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

1719
  DNSName child;
12,975✔
1720
  prefix.append(string("QM "));
12,975✔
1721

1722
  LOG(prefix << qname << ": doResolve" << endl);
12,975✔
1723

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

1743
  const unsigned int qnamelen = qname.countLabels();
5,776✔
1744

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

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

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

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

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

1807
      LOG(prefix << qname << ": Step2 New child " << child << endl);
8,841✔
1808

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

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

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

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

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

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

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

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

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

1889
static bool haveFinalAnswer(const DNSName& qname, QType qtype, int res, const vector<DNSRecord>& ret)
1890
{
792✔
1891
  if (res != RCode::NoError) {
792!
1892
    return false;
×
1893
  }
×
1894

1895
  // This loop assumes the CNAME's records are in-order
1896
  DNSName target(qname);
792✔
1897
  for (const auto& record : ret) {
2,628✔
1898
    if (record.d_place == DNSResourceRecord::ANSWER && record.d_name == target) {
2,628✔
1899
      if (record.d_type == qtype) {
1,393✔
1900
        return true;
79✔
1901
      }
79✔
1902
      if (record.d_type == QType::CNAME) {
1,314!
1903
        if (auto ptr = getRR<CNAMERecordContent>(record)) {
1,314!
1904
          target = ptr->getTarget();
1,314✔
1905
        }
1,314✔
1906
        else {
×
1907
          return false;
×
1908
        }
×
1909
      }
1,314✔
1910
    }
1,314✔
1911
  }
2,628✔
1912
  return false;
713✔
1913
}
792✔
1914

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

1931
  LOG(prefix << qname << ": Wants " << (d_doDNSSEC ? "" : "NO ") << "DNSSEC processing, " << (d_requireAuthData ? "" : "NO ") << "auth data required by query for " << qtype << endl);
25,655!
1932

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

1944
  int res = 0;
25,651✔
1945

1946
  const int iterations = !d_refresh && MemRecursorCache::s_maxServedStaleExtensions > 0 ? 2 : 1;
25,651✔
1947
  for (int loop = 0; loop < iterations; loop++) {
26,076✔
1948

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

1958
      if (d_cacheonly) {
25,228✔
1959
        if (iter != t_sstorage.domainmap->end()) {
14,105✔
1960
          if (iter->second.isAuth()) {
156✔
1961
            LOG(prefix << qname << ": Cache only lookup for '" << qname << "|" << qtype << "', in auth zone" << endl);
2!
1962
            ret.clear();
2✔
1963
            d_wasOutOfBand = doOOBResolve(qname, qtype, ret, depth, prefix, res);
2✔
1964
            if (fromCache != nullptr) {
2!
1965
              *fromCache = d_wasOutOfBand;
×
1966
            }
×
1967
            return res;
2✔
1968
          }
2✔
1969
        }
156✔
1970
      }
14,105✔
1971

1972
      bool wasForwardedOrAuthZone = false;
25,226✔
1973
      bool wasAuthZone = false;
25,226✔
1974
      bool wasForwardRecurse = false;
25,226✔
1975

1976
      if (iter != t_sstorage.domainmap->end()) {
25,226✔
1977
        wasForwardedOrAuthZone = true;
635✔
1978

1979
        if (iter->second.isAuth()) {
635✔
1980
          wasAuthZone = true;
298✔
1981
        }
298✔
1982
        else if (iter->second.shouldRecurse()) {
337✔
1983
          wasForwardRecurse = true;
99✔
1984
        }
99✔
1985
      }
635✔
1986

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

2000
        if (fromCache != nullptr && (!d_cacheonly || stoppedByPolicyHit)) {
1,801!
2001
          *fromCache = true;
3✔
2002
        }
3✔
2003
        /* Apply Post filtering policies */
2004

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

2024
      if (doCacheCheck(qname, authname, wasForwardedOrAuthZone, wasAuthZone, wasForwardRecurse, qtype, ret, depth, prefix, res, context)) {
23,425✔
2025
        // we done
2026
        d_wasOutOfBand = wasAuthZone;
8,357✔
2027
        if (fromCache != nullptr) {
8,357✔
2028
          *fromCache = true;
7,115✔
2029
        }
7,115✔
2030

2031
        if (d_wantsRPZ && !d_appliedPolicy.wasHit()) {
8,357!
2032
          auto luaLocal = g_luaconfs.getLocal();
8,352✔
2033
          if (luaLocal->dfe.getPostPolicy(ret, d_discardedPolicies, d_appliedPolicy)) {
8,352✔
2034
            mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());
4✔
2035
            bool done = false;
4✔
2036
            handlePolicyHit(prefix, qname, qtype, ret, done, res, depth);
4✔
2037
          }
4✔
2038
        }
8,352✔
2039

2040
        return res;
8,357✔
2041
      }
8,357✔
2042

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

2054
        if (fromCache != nullptr && (!d_cacheonly || stoppedByPolicyHit)) {
18!
2055
          *fromCache = true;
×
2056
        }
×
2057
        /* Apply Post filtering policies */
2058

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

2077
    if (d_cacheonly) {
15,495✔
2078
      return 0;
5,822✔
2079
    }
5,822✔
2080

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

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

2089
    DNSName subdomain(qname);
9,669✔
2090
    if (qtype == QType::DS) {
9,669✔
2091
      subdomain.chopOff();
194✔
2092
    }
194✔
2093

2094
    NsSet nsset;
9,669✔
2095
    bool flawedNSSet = false;
9,669✔
2096

2097
    // the two retries allow getBestNSNamesFromCache&co to reprime the root
2098
    // hints, in case they ever go missing
2099
    for (int tries = 0; tries < 2 && nsset.empty(); ++tries) {
19,338✔
2100
      subdomain = getBestNSNamesFromCache(subdomain, qtype, nsset, &flawedNSSet, depth, prefix, beenthere); //  pass beenthere to both occasions
9,669✔
2101
    }
9,669✔
2102

2103
    res = doResolveAt(nsset, subdomain, flawedNSSet, qname, qtype, ret, depth, prefix, beenthere, context, stopAtDelegation, nullptr);
9,669✔
2104

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

2140
    if (res == 0) {
9,669✔
2141
      return 0;
9,066✔
2142
    }
9,066✔
2143

2144
    LOG(prefix << qname << ": Failed (res=" << res << ")" << endl);
603✔
2145
    if (res >= 0) {
603✔
2146
      break;
181✔
2147
    }
181✔
2148
  }
603✔
2149
  return res < 0 ? RCode::ServFail : res;
581✔
2150
}
25,651✔
2151

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

2160
struct speedOrderCA
2161
{
2162
  speedOrderCA(std::map<ComboAddress, float>& speeds) :
2163
    d_speeds(speeds) {}
1,544✔
2164
  bool operator()(const ComboAddress& lhs, const ComboAddress& rhs) const
2165
  {
1,547✔
2166
    return d_speeds[lhs] < d_speeds[rhs];
1,547✔
2167
  }
1,547✔
2168
  std::map<ComboAddress, float>& d_speeds; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members): nothing wrong afaiks
2169
};
2170

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

2188
  if (ret.size() > 1) {
11,083✔
2189
    shuffle(ret.begin(), ret.end(), pdns::dns_random_engine());
1,410✔
2190
    speedOrderCA speedOrder(speeds);
1,410✔
2191
    stable_sort(ret.begin(), ret.end(), speedOrder);
1,410✔
2192
  }
1,410✔
2193

2194
  if (doLog()) {
11,083✔
2195
    LOG(prefix << qname << ": Nameserver " << qname << " IPs: ");
1,546!
2196
    bool first = true;
1,546✔
2197
    for (const auto& addr : ret) {
1,554✔
2198
      if (first) {
1,554✔
2199
        first = false;
1,546✔
2200
      }
1,546✔
2201
      else {
8✔
2202
        LOG(", ");
8!
2203
      }
8✔
2204
      LOG((addr.toString()) << "(" << fmtfloat(speeds[addr] / 1000.0) << "ms)");
1,554!
2205
    }
1,554✔
2206
    LOG(endl);
1,546!
2207
  }
1,546✔
2208
}
11,083✔
2209

2210
template <typename T>
2211
static bool collectAddresses(const vector<DNSRecord>& cset, vector<ComboAddress>& ret)
2212
{
12,321✔
2213
  bool pushed = false;
12,321✔
2214
  for (const auto& record : cset) {
12,531✔
2215
    if (auto rec = getRR<T>(record)) {
12,529✔
2216
      ret.push_back(rec->getCA(53));
12,419✔
2217
      pushed = true;
12,419✔
2218
    }
12,419✔
2219
  }
12,529✔
2220
  return pushed;
12,321✔
2221
}
12,321✔
2222

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

2231
  bool oldCacheOnly = setCacheOnly(cacheOnly);
11,173✔
2232
  bool oldRequireAuthData = d_requireAuthData;
11,173✔
2233
  bool oldValidationRequested = d_DNSSECValidationRequested;
11,173✔
2234
  bool oldFollowCNAME = d_followCNAME;
11,173✔
2235
  bool seenV6 = false;
11,173✔
2236
  const unsigned int startqueries = d_outqueries;
11,173✔
2237
  d_requireAuthData = false;
11,173✔
2238
  d_DNSSECValidationRequested = false;
11,173✔
2239
  d_followCNAME = false;
11,173✔
2240

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

2296
  if (ret.empty() && d_outqueries > startqueries) {
11,173✔
2297
    // We did 1 or more outgoing queries to resolve this NS name but returned empty handed
2298
    addressQueriesForNS++;
77✔
2299
  }
77✔
2300
  d_requireAuthData = oldRequireAuthData;
11,083✔
2301
  d_DNSSECValidationRequested = oldValidationRequested;
11,083✔
2302
  setCacheOnly(oldCacheOnly);
11,083✔
2303
  d_followCNAME = oldFollowCNAME;
11,083✔
2304

2305
  if (s_max_busy_dot_probes > 0 && s_dot_to_port_853) {
11,083!
2306
    for (auto& add : ret) {
×
2307
      if (shouldDoDoT(add, d_now.tv_sec)) {
×
2308
        add.setPort(853);
×
2309
      }
×
2310
    }
×
2311
  }
×
2312
  selectNSOnSpeed(qname, prefix, ret);
11,083✔
2313
  return ret;
11,083✔
2314
}
11,173✔
2315

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

2334
    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) {
37,468✔
2335
      if (s_maxnsperresolve > 0 && nsVector.size() > s_maxnsperresolve) {
16,889!
2336
        vector<DNSRecord> selected;
20✔
2337
        selected.reserve(s_maxnsperresolve);
20✔
2338
        std::sample(nsVector.cbegin(), nsVector.cend(), std::back_inserter(selected), s_maxnsperresolve, pdns::dns_random_engine());
20✔
2339
        nsVector = std::move(selected);
20✔
2340
      }
20✔
2341
      bestns.reserve(nsVector.size());
16,889✔
2342

2343
      vector<DNSName> missing;
16,889✔
2344
      for (const auto& nsRecord : nsVector) {
86,958✔
2345
        if (nsRecord.d_ttl > (unsigned int)d_now.tv_sec) {
86,958!
2346
          vector<DNSRecord> aset;
86,958✔
2347
          QType nsqt{QType::ADDR};
86,958✔
2348
          if (s_doIPv4 && !s_doIPv6) {
86,958✔
2349
            nsqt = QType::A;
71,886✔
2350
          }
71,886✔
2351
          else if (!s_doIPv4 && s_doIPv6) {
15,072!
2352
            nsqt = QType::AAAA;
59✔
2353
          }
59✔
2354

2355
          auto nrr = getRR<NSRecordContent>(nsRecord);
86,958✔
2356
          if (nrr && (!nrr->getNS().isPartOf(subdomain) || g_recCache->get(d_now.tv_sec, nrr->getNS(), nsqt, flags, doLog() ? &aset : nullptr, d_cacheRemote, d_routingTag) > 0)) {
86,958!
2357
            bestns.push_back(nsRecord);
86,644✔
2358
            LOG(prefix << qname << ": NS (with ip, or non-glue) in cache for '" << subdomain << "' -> '" << nrr->getNS() << "'");
86,644✔
2359
            LOG(", within bailiwick: " << nrr->getNS().isPartOf(subdomain));
86,644✔
2360
            if (!aset.empty()) {
86,644✔
2361
              LOG(", in cache, ttl=" << (unsigned int)(((time_t)aset.begin()->d_ttl - d_now.tv_sec)) << endl);
4,375!
2362
            }
4,375✔
2363
            else {
82,269✔
2364
              LOG(", not in cache / did not look at cache" << endl);
82,269✔
2365
            }
82,269✔
2366
          }
86,644✔
2367
          else if (nrr != nullptr) {
314✔
2368
            *flawedNSSet = true;
313✔
2369
            LOG(prefix << qname << ": NS in cache for '" << subdomain << "', but needs glue (" << nrr->getNS() << ") which we miss or is expired" << endl);
313!
2370
            missing.emplace_back(nrr->getNS());
313✔
2371
          }
313✔
2372
        }
86,958✔
2373
      }
86,958✔
2374
      if (*flawedNSSet && bestns.empty() && isAuth) {
16,889✔
2375
        // The authoritative (child) NS records did not produce any usable addresses, wipe them, so
2376
        // these useless records do not prevent parent records to be inserted into the cache
2377
        LOG(prefix << qname << ": Wiping flawed authoritative NS records for " << subdomain << endl);
2!
2378
        g_recCache->doWipeCache(subdomain, false, QType::NS);
2✔
2379
      }
2✔
2380
      if (!missing.empty() && missing.size() < nsVector.size()) {
16,889✔
2381
        // We miss glue, but we have a chance to resolve it
2382
        // Pick a few and push async tasks to resolve them
2383
        const unsigned int max = 2;
29✔
2384
        unsigned int counter = 0;
29✔
2385
        shuffle(missing.begin(), missing.end(), pdns::dns_random_engine());
29✔
2386
        for (const auto& name : missing) {
29!
2387
          if (s_doIPv4 && pushResolveIfNotInNegCache(name, QType::A, d_now)) {
29!
2388
            LOG(prefix << qname << ": A glue for " << subdomain << " NS " << name << " missing, pushed task to resolve" << endl);
29!
2389
            counter++;
29✔
2390
          }
29✔
2391
          if (s_doIPv6 && pushResolveIfNotInNegCache(name, QType::AAAA, d_now)) {
29!
2392
            LOG(prefix << qname << ": AAAA glue for " << subdomain << " NS " << name << " missing, pushed task to resolve" << endl);
29!
2393
            counter++;
29✔
2394
          }
29✔
2395
          if (counter >= max) {
29!
2396
            break;
29✔
2397
          }
29✔
2398
        }
29✔
2399
      }
29✔
2400

2401
      if (!bestns.empty()) {
16,890✔
2402
        GetBestNSAnswer answer;
16,854✔
2403
        answer.qname = qname;
16,854✔
2404
        answer.qtype = qtype.getCode();
16,854✔
2405
        for (const auto& bestNSRecord : bestns) {
86,641✔
2406
          if (auto nsContent = getRR<NSRecordContent>(bestNSRecord)) {
86,645✔
2407
            answer.bestns.emplace(bestNSRecord.d_name, nsContent->getNS());
86,642✔
2408
          }
86,642✔
2409
        }
86,641✔
2410

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

2432
    if (subdomain.isRoot() && !brokeloop) {
20,895✔
2433
      // We lost the root NS records
2434
      primeHints();
152✔
2435
      LOG(prefix << qname << ": Reprimed the root" << endl);
152!
2436
      /* let's prevent an infinite loop */
2437
      if (!d_updatingRootNS) {
152✔
2438
        auto log = g_slog->withName("housekeeping");
143✔
2439
        getRootNS(d_now, d_asyncResolve, depth, log);
143✔
2440
      }
143✔
2441
    }
152✔
2442
  } while (subdomain.chopOff());
20,895✔
2443
}
16,897✔
2444

2445
SyncRes::domainmap_t::const_iterator SyncRes::getBestAuthZone(DNSName* qname)
2446
{
90,791✔
2447
  if (t_sstorage.domainmap->empty()) {
90,791✔
2448
    return t_sstorage.domainmap->end();
5,632✔
2449
  }
5,632✔
2450

2451
  SyncRes::domainmap_t::const_iterator ret;
85,159✔
2452
  do {
295,762✔
2453
    ret = t_sstorage.domainmap->find(*qname);
295,762✔
2454
    if (ret != t_sstorage.domainmap->end()) {
295,762✔
2455
      break;
1,892✔
2456
    }
1,892✔
2457
  } while (qname->chopOff());
295,762✔
2458
  return ret;
×
2459
}
90,791✔
2460

2461
/** doesn't actually do the work, leaves that to getBestNSFromCache */
2462
DNSName SyncRes::getBestNSNamesFromCache(const DNSName& qname, const QType qtype, NsSet& nsset, bool* flawedNSSet, unsigned int depth, const string& prefix, set<GetBestNSAnswer>& beenthere)
2463
{
9,669✔
2464
  DNSName authOrForwDomain(qname);
9,669✔
2465

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

2484
  // We might have a (non-recursive) forwarder, but maybe the cache already contains
2485
  // a better NS
2486
  vector<DNSRecord> bestns;
9,470✔
2487
  DNSName nsFromCacheDomain(g_rootdnsname);
9,470✔
2488
  getBestNSFromCache(qname, qtype, bestns, flawedNSSet, depth, prefix, beenthere);
9,470✔
2489

2490
  // Pick up the auth domain
2491
  for (const auto& nsRecord : bestns) {
9,470✔
2492
    const auto nsContent = getRR<NSRecordContent>(nsRecord);
9,356✔
2493
    if (nsContent) {
9,356!
2494
      nsFromCacheDomain = nsRecord.d_name;
9,356✔
2495
      break;
9,356✔
2496
    }
9,356✔
2497
  }
9,356✔
2498

2499
  if (iter != t_sstorage.domainmap->end()) {
9,470✔
2500
    if (doLog()) {
76✔
2501
      LOG(prefix << qname << " authOrForwDomain: " << authOrForwDomain << " nsFromCacheDomain: " << nsFromCacheDomain << " isPartof: " << authOrForwDomain.isPartOf(nsFromCacheDomain) << endl);
64!
2502
    }
64✔
2503

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

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

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

2542
static pair<bool, unsigned int> scanForCNAMELoop(const DNSName& name, const vector<DNSRecord>& records)
2543
{
2,850✔
2544
  unsigned int numCNames = 0;
2,850✔
2545
  for (const auto& record : records) {
6,853✔
2546
    if (record.d_type == QType::CNAME && record.d_place == DNSResourceRecord::ANSWER) {
6,853!
2547
      ++numCNames;
6,662✔
2548
      if (name == record.d_name) {
6,662✔
2549
        return {true, numCNames};
6✔
2550
      }
6✔
2551
    }
6,662✔
2552
  }
6,853✔
2553
  return {false, numCNames};
2,844✔
2554
}
2,850✔
2555

2556
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)
2557
{
20,564✔
2558
  vector<DNSRecord> cset;
20,564✔
2559
  MemRecursorCache::SigRecs signatures = MemRecursorCache::s_emptySigRecs;
20,564✔
2560
  MemRecursorCache::AuthRecs authorityRecs = MemRecursorCache::s_emptyAuthRecs;
20,564✔
2561
  bool wasAuth = false;
20,564✔
2562
  uint32_t capTTL = std::numeric_limits<uint32_t>::max();
20,564✔
2563
  DNSName foundName;
20,564✔
2564
  DNSName authZone;
20,564✔
2565
  QType foundQT = QType::ENT;
20,564✔
2566

2567
  /* we don't require auth data for forward-recurse lookups */
2568
  MemRecursorCache::Flags flags = MemRecursorCache::None;
20,564✔
2569
  if (!wasForwardRecurse && d_requireAuthData) {
20,565✔
2570
    flags |= MemRecursorCache::RequireAuth;
19,108✔
2571
  }
19,108✔
2572
  if (d_refresh) {
20,564✔
2573
    flags |= MemRecursorCache::Refresh;
303✔
2574
  }
303✔
2575
  if (d_serveStale) {
20,564✔
2576
    flags |= MemRecursorCache::ServeStale;
20✔
2577
  }
20✔
2578
  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,491,696✔
2579
    foundName = qname;
1,865✔
2580
    foundQT = QType::CNAME;
1,865✔
2581
  }
1,865✔
2582

2583
  if (foundName.empty() && qname != g_rootdnsname) {
20,564✔
2584
    // look for a DNAME cache hit
2585
    auto labels = qname.getRawLabels();
17,262✔
2586
    DNSName dnameName(g_rootdnsname);
17,262✔
2587

2588
    do {
47,616✔
2589
      dnameName.prependRawLabel(labels.back());
47,616✔
2590
      labels.pop_back();
47,616✔
2591
      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
47,616✔
2592
        break;
17,243✔
2593
      }
17,243✔
2594
      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) {
30,373✔
2595
        foundName = std::move(dnameName);
16✔
2596
        foundQT = QType::DNAME;
16✔
2597
        break;
16✔
2598
      }
16✔
2599
    } while (!labels.empty());
30,373✔
2600
  }
17,262✔
2601

2602
  if (foundName.empty()) {
20,564✔
2603
    return false;
18,684✔
2604
  }
18,684✔
2605

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

2613
  for (auto const& record : cset) {
1,877✔
2614
    if (record.d_class != QClass::IN) {
1,877!
2615
      continue;
×
2616
    }
×
2617

2618
    if (record.d_ttl > (unsigned int)d_now.tv_sec) {
1,877!
2619

2620
      if (!wasAuthZone && shouldValidate() && (wasAuth || wasForwardRecurse) && context.state == vState::Indeterminate && d_requireAuthData) {
1,877!
2621
        /* This means we couldn't figure out the state when this entry was cached */
2622

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

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

2639
      DNSRecord dnsRecord = record;
1,877✔
2640
      auto alreadyPresent = false;
1,877✔
2641

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

2659
        for (const auto& signature : *signatures) {
1,875✔
2660
          DNSRecord sigdr;
45✔
2661
          sigdr.d_type = QType::RRSIG;
45✔
2662
          sigdr.d_name = foundName;
45✔
2663
          sigdr.d_ttl = ttl;
45✔
2664
          sigdr.setContent(signature);
45✔
2665
          sigdr.d_place = DNSResourceRecord::ANSWER;
45✔
2666
          sigdr.d_class = QClass::IN;
45✔
2667
          ret.push_back(std::move(sigdr));
45✔
2668
        }
45✔
2669

2670
        for (const auto& rec : *authorityRecs) {
1,875!
2671
          DNSRecord authDR(rec);
×
2672
          authDR.d_ttl = ttl;
×
2673
          ret.push_back(std::move(authDR));
×
2674
        }
×
2675
      }
1,875✔
2676

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

2703
        LOG(prefix << qname << ": Synthesized " << dnsRecord.d_name << "|CNAME " << newTarget << endl);
14!
2704
      }
14✔
2705

2706
      if (qtype == QType::CNAME) { // perhaps they really wanted a CNAME!
1,875✔
2707
        res = RCode::NoError;
2✔
2708
        return true;
2✔
2709
      }
2✔
2710

2711
      if (qtype == QType::DS || qtype == QType::DNSKEY) {
1,873!
2712
        res = RCode::NoError;
18✔
2713
        return true;
18✔
2714
      }
18✔
2715

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

2726
      if (qname == newTarget) {
1,855✔
2727
        string msg = "Got a CNAME referral (from cache) to self";
2✔
2728
        LOG(prefix << qname << ": " << msg << endl);
2!
2729
        throw ImmediateServFailException(std::move(msg));
2✔
2730
      }
2✔
2731

2732
      if (newTarget.isPartOf(qname)) {
1,853✔
2733
        // a.b.c. CNAME x.a.b.c will go to great depths with QM on
2734
        string msg = "Got a CNAME referral (from cache) to child, disabling QM";
34✔
2735
        LOG(prefix << qname << ": " << msg << endl);
34!
2736
        setQNameMinimization(false);
34✔
2737
      }
34✔
2738

2739
      if (!d_followCNAME) {
1,853✔
2740
        res = RCode::NoError;
150✔
2741
        return true;
150✔
2742
      }
150✔
2743

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

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

2766
      return true;
1,695✔
2767
    }
1,699✔
2768
  }
1,877✔
2769
  throw ImmediateServFailException("Could not determine whether or not there was a CNAME or DNAME in cache for '" + qname.toLogString() + "'");
2,147,483,647✔
2770
}
1,876✔
2771

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

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

2809
static bool negativeCacheEntryHasSOA(const NegCache::NegCacheEntry& negEntry)
2810
{
16✔
2811
  return !negEntry.authoritySOA.records.empty();
16✔
2812
}
16✔
2813

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

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

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

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

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

2857
    const DNSName& owner = entry.first.name;
12✔
2858

2859
    vState recordState = getValidationStatus(owner, !entry.second.signatures.empty(), qtype == QType::DS, depth, prefix);
12✔
2860
    if (state == vState::Indeterminate) {
12✔
2861
      state = recordState;
8✔
2862
    }
8✔
2863

2864
    if (recordState == vState::Secure) {
12✔
2865
      recordState = SyncRes::validateRecordsWithSigs(depth, prefix, qname, qtype, owner, QType(entry.first.type), entry.second.records, entry.second.signatures);
10✔
2866
    }
10✔
2867

2868
    if (recordState != vState::Indeterminate && recordState != state) {
12!
2869
      updateValidationState(qname, state, recordState, prefix);
×
2870
      if (state != vState::Secure) {
×
2871
        break;
×
2872
      }
×
2873
    }
×
2874
  }
12✔
2875

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

2892
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)
2893
{
23,366✔
2894
  bool giveNegative = false;
23,366✔
2895

2896
  // 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)
2897
  DNSName sqname(qname);
23,366✔
2898
  QType sqt(qtype);
23,366✔
2899
  uint32_t sttl = 0;
23,366✔
2900
  //  cout<<"Lookup for '"<<qname<<"|"<<qtype.toString()<<"' -> "<<getLastLabel(qname)<<endl;
2901
  vState cachedState{};
23,366✔
2902
  NegCache::NegCacheEntry negEntry;
23,366✔
2903

2904
  if (s_rootNXTrust && g_negCache->getRootNXTrust(qname, d_now, negEntry, d_serveStale, d_refresh) && negEntry.d_auth.isRoot() && (!wasForwardedOrAuthZone || authname.isRoot())) { // when forwarding, the root may only neg-cache if it was forwarded to.
23,367!
2905
    sttl = negEntry.d_ttd - d_now.tv_sec;
4✔
2906
    LOG(prefix << qname << ": Entire name '" << qname << "', is negatively cached via '" << negEntry.d_auth << "' & '" << negEntry.d_name << "' for another " << sttl << " seconds" << endl);
4!
2907
    res = RCode::NXDomain;
4✔
2908
    giveNegative = true;
4✔
2909
    cachedState = negEntry.d_validationState;
4✔
2910
    if (s_addExtendedResolutionDNSErrors) {
4✔
2911
      context.extendedError = EDNSExtendedError{static_cast<uint16_t>(EDNSExtendedError::code::Synthesized), "Result synthesized by root-nx-trust"};
2✔
2912
    }
2✔
2913
  }
4✔
2914
  else if (g_negCache->get(qname, qtype, d_now, negEntry, false, d_serveStale, d_refresh)) {
23,362✔
2915
    /* If we are looking for a DS, discard NXD if auth == qname
2916
       and ask for a specific denial instead */
2917
    if (qtype != QType::DS || negEntry.d_qtype.getCode() != 0 || negEntry.d_auth != qname || g_negCache->get(qname, qtype, d_now, negEntry, true, d_serveStale, d_refresh)) {
2,635!
2918
      /* 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
2919
         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
2920
         query. */
2921
      if (qtype == QType::DS && negEntry.d_qtype.getCode() != 0 && !d_externalDSQuery.empty() && qname == d_externalDSQuery && !negativeCacheEntryHasSOA(negEntry)) {
2,635✔
2922
        giveNegative = false;
6✔
2923
      }
6✔
2924
      else {
2,629✔
2925
        res = RCode::NXDomain;
2,629✔
2926
        sttl = negEntry.d_ttd - d_now.tv_sec;
2,629✔
2927
        giveNegative = true;
2,629✔
2928
        cachedState = negEntry.d_validationState;
2,629✔
2929
        if (negEntry.d_qtype.getCode() != 0) {
2,629✔
2930
          LOG(prefix << qname << "|" << qtype << ": Is negatively cached via '" << negEntry.d_auth << "' for another " << sttl << " seconds" << endl);
2,590!
2931
          res = RCode::NoError;
2,590✔
2932
          if (s_addExtendedResolutionDNSErrors) {
2,590✔
2933
            context.extendedError = EDNSExtendedError{static_cast<uint16_t>(EDNSExtendedError::code::Synthesized), "Result from negative cache"};
2,462✔
2934
          }
2,462✔
2935
        }
2,590✔
2936
        else {
39✔
2937
          LOG(prefix << qname << ": Entire name '" << qname << "' is negatively cached via '" << negEntry.d_auth << "' for another " << sttl << " seconds" << endl);
39!
2938
          if (s_addExtendedResolutionDNSErrors) {
39✔
2939
            context.extendedError = EDNSExtendedError{static_cast<uint16_t>(EDNSExtendedError::code::Synthesized), "Result from negative cache for entire name"};
27✔
2940
          }
27✔
2941
        }
39✔
2942
      }
2,629✔
2943
    }
2,635✔
2944
  }
2,635✔
2945
  else if (s_hardenNXD != HardenNXD::No && !qname.isRoot() && !wasForwardedOrAuthZone) {
20,728✔
2946
    auto labels = qname.getRawLabels();
18,706✔
2947
    DNSName negCacheName(g_rootdnsname);
18,706✔
2948
    negCacheName.prependRawLabel(labels.back());
18,706✔
2949
    labels.pop_back();
18,706✔
2950
    while (!labels.empty()) {
47,522✔
2951
      if (g_negCache->get(negCacheName, QType::ENT, d_now, negEntry, true, d_serveStale, d_refresh)) {
28,845✔
2952
        if (negEntry.d_validationState == vState::Indeterminate && validationEnabled()) {
33!
2953
          // LOG(prefix << negCacheName <<  " negatively cached and vState::Indeterminate, trying to validate NXDOMAIN" << endl);
2954
          // ...
2955
          // And get the updated ne struct
2956
          // t_sstorage.negcache.get(negCacheName, QType(0), d_now, ne, true);
2957
        }
×
2958
        if ((s_hardenNXD == HardenNXD::Yes && !vStateIsBogus(negEntry.d_validationState)) || negEntry.d_validationState == vState::Secure) {
33!
2959
          res = RCode::NXDomain;
29✔
2960
          sttl = negEntry.d_ttd - d_now.tv_sec;
29✔
2961
          giveNegative = true;
29✔
2962
          cachedState = negEntry.d_validationState;
29✔
2963
          LOG(prefix << qname << ": Name '" << negCacheName << "' and below, is negatively cached via '" << negEntry.d_auth << "' for another " << sttl << " seconds" << endl);
29!
2964
          if (s_addExtendedResolutionDNSErrors) {
29✔
2965
            context.extendedError = EDNSExtendedError{static_cast<uint16_t>(EDNSExtendedError::code::Synthesized), "Result synthesized by nothing-below-nxdomain (RFC8020)"};
13✔
2966
          }
13✔
2967
          break;
29✔
2968
        }
29✔
2969
      }
33✔
2970
      negCacheName.prependRawLabel(labels.back());
28,816✔
2971
      labels.pop_back();
28,816✔
2972
    }
28,816✔
2973
  }
18,706✔
2974

2975
  if (giveNegative) {
23,366✔
2976

2977
    context.state = cachedState;
2,662✔
2978

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

2983
      if (context.state != cachedState && vStateIsBogus(context.state)) {
8!
2984
        sttl = std::min(sttl, s_maxbogusttl);
2✔
2985
      }
2✔
2986
    }
8✔
2987

2988
    // Transplant SOA to the returned packet
2989
    addTTLModifiedRecords(negEntry.authoritySOA.records, sttl, ret);
2,662✔
2990
    if (d_doDNSSEC) {
2,662✔
2991
      addTTLModifiedRecords(negEntry.authoritySOA.signatures, sttl, ret);
2,623✔
2992
      addTTLModifiedRecords(negEntry.DNSSECRecords.records, sttl, ret);
2,623✔
2993
      addTTLModifiedRecords(negEntry.DNSSECRecords.signatures, sttl, ret);
2,623✔
2994
    }
2,623✔
2995

2996
    LOG(prefix << qname << ": Updating validation state with negative cache content for " << qname << " to " << context.state << endl);
2,662!
2997
    return true;
2,662✔
2998
  }
2,662✔
2999

3000
  vector<DNSRecord> cset;
20,704✔
3001
  bool found = false;
20,704✔
3002
  bool expired = false;
20,704✔
3003
  MemRecursorCache::SigRecs signatures = MemRecursorCache::s_emptySigRecs;
20,704✔
3004
  MemRecursorCache::AuthRecs authorityRecs = MemRecursorCache::s_emptyAuthRecs;
20,704✔
3005
  uint32_t ttl = 0;
20,704✔
3006
  uint32_t capTTL = std::numeric_limits<uint32_t>::max();
20,704✔
3007
  bool wasCachedAuth{};
20,704✔
3008
  MemRecursorCache::Flags flags = MemRecursorCache::None;
20,704✔
3009
  if (!wasForwardRecurse && d_requireAuthData) {
20,705✔
3010
    flags |= MemRecursorCache::RequireAuth;
19,261✔
3011
  }
19,261✔
3012
  if (d_serveStale) {
20,704✔
3013
    flags |= MemRecursorCache::ServeStale;
10✔
3014
  }
10✔
3015
  if (d_refresh) {
20,704✔
3016
    flags |= MemRecursorCache::Refresh;
303✔
3017
  }
303✔
3018
  if (g_recCache->get(d_now.tv_sec, sqname, sqt, flags, &cset, d_cacheRemote, d_routingTag, d_doDNSSEC ? &signatures : nullptr, d_doDNSSEC ? &authorityRecs : nullptr, &d_wasVariable, &cachedState, &wasCachedAuth, nullptr, &d_fromAuthIP) > 0) {
2,147,492,359✔
3019

3020
    LOG(prefix << sqname << ": Found cache hit for " << sqt.toString() << ": ");
5,653✔
3021

3022
    if (!wasAuthZone && shouldValidate() && (wasCachedAuth || wasForwardRecurse) && cachedState == vState::Indeterminate && d_requireAuthData) {
5,653!
3023

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

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

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

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

3069
    for (auto j = cset.cbegin(); j != cset.cend(); ++j) {
13,624✔
3070

3071
      LOG(j->getContent()->getZoneRepresentation());
7,971✔
3072

3073
      if (j->d_class != QClass::IN) {
7,971!
3074
        continue;
×
3075
      }
×
3076

3077
      if (j->d_ttl > (unsigned int)d_now.tv_sec) {
7,971!
3078
        DNSRecord dnsRecord = *j;
7,971✔
3079
        dnsRecord.d_ttl -= d_now.tv_sec;
7,971✔
3080
        dnsRecord.d_ttl = std::min(dnsRecord.d_ttl, capTTL);
7,971✔
3081
        ttl = dnsRecord.d_ttl;
7,971✔
3082
        ret.push_back(dnsRecord);
7,971✔
3083
        LOG("[ttl=" << dnsRecord.d_ttl << "] ");
7,971✔
3084
        found = true;
7,971✔
3085
      }
7,971✔
3086
      else {
×
3087
        LOG("[expired] ");
×
3088
        expired = true;
×
3089
      }
×
3090
    }
7,971✔
3091

3092
    ret.reserve(ret.size() + signatures->size() + authorityRecs->size());
5,653✔
3093

3094
    for (const auto& signature : *signatures) {
5,653✔
3095
      DNSRecord dnsRecord;
5,252✔
3096
      dnsRecord.d_type = QType::RRSIG;
5,252✔
3097
      dnsRecord.d_name = sqname;
5,252✔
3098
      dnsRecord.d_ttl = ttl;
5,252✔
3099
      dnsRecord.setContent(signature);
5,252✔
3100
      dnsRecord.d_place = DNSResourceRecord::ANSWER;
5,252✔
3101
      dnsRecord.d_class = QClass::IN;
5,252✔
3102
      ret.push_back(std::move(dnsRecord));
5,252✔
3103
    }
5,252✔
3104

3105
    for (const auto& rec : *authorityRecs) {
5,653✔
3106
      DNSRecord dnsRecord(rec);
32✔
3107
      dnsRecord.d_ttl = ttl;
32✔
3108
      ret.push_back(std::move(dnsRecord));
32✔
3109
    }
32✔
3110

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

3123
  /* let's check if we have a NSEC covering that record */
3124
  if (g_aggressiveNSECCache && !wasForwardedOrAuthZone) {
15,052✔
3125
    if (g_aggressiveNSECCache->getDenial(d_now.tv_sec, qname, qtype, ret, res, d_cacheRemote, d_routingTag, d_doDNSSEC, d_validationContext, LogObject(prefix))) {
12,738✔
3126
      context.state = vState::Secure;
42✔
3127
      if (s_addExtendedResolutionDNSErrors) {
42✔
3128
        context.extendedError = EDNSExtendedError{static_cast<uint16_t>(EDNSExtendedError::code::Synthesized), "Result synthesized from aggressive NSEC cache (RFC8198)"};
26✔
3129
      }
26✔
3130
      return true;
42✔
3131
    }
42✔
3132
  }
12,738✔
3133

3134
  return false;
15,009✔
3135
}
15,051✔
3136

3137
bool SyncRes::moreSpecificThan(const DNSName& lhs, const DNSName& rhs)
3138
{
11,334✔
3139
  return (lhs.isPartOf(rhs) && lhs.countLabels() > rhs.countLabels());
11,334!
3140
}
11,334✔
3141

3142
struct speedOrder
3143
{
3144
  bool operator()(const std::pair<DNSName, float>& lhs, const std::pair<DNSName, float>& rhs) const
3145
  {
118,761✔
3146
    return lhs.second < rhs.second;
118,761✔
3147
  }
118,761✔
3148
};
3149

3150
std::vector<std::pair<DNSName, float>> SyncRes::shuffleInSpeedOrder(const DNSName& qname, NsSet& tnameservers, const string& prefix)
3151
{
11,206✔
3152
  std::vector<std::pair<DNSName, float>> rnameservers;
11,206✔
3153
  rnameservers.reserve(tnameservers.size());
11,206✔
3154
  for (const auto& tns : tnameservers) {
57,576✔
3155
    float speed = s_nsSpeeds.lock()->fastest(tns.first, d_now);
57,576✔
3156
    rnameservers.emplace_back(tns.first, speed);
57,576✔
3157
    if (tns.first.empty()) { // this was an authoritative OOB zone, don't pollute the nsSpeeds with that
57,576✔
3158
      return rnameservers;
273✔
3159
    }
273✔
3160
  }
57,576✔
3161

3162
  shuffle(rnameservers.begin(), rnameservers.end(), pdns::dns_random_engine());
10,933✔
3163
  speedOrder speedCompare;
10,933✔
3164
  stable_sort(rnameservers.begin(), rnameservers.end(), speedCompare);
10,933✔
3165

3166
  if (doLog()) {
10,933✔
3167
    LOG(prefix << qname << ": Nameservers: ");
1,535!
3168
    for (auto i = rnameservers.begin(); i != rnameservers.end(); ++i) {
4,097✔
3169
      if (i != rnameservers.begin()) {
2,562✔
3170
        LOG(", ");
1,027!
3171
        if (((i - rnameservers.begin()) % 3) == 0) {
1,027✔
3172
          LOG(endl
180!
3173
              << prefix << "             ");
180✔
3174
        }
180✔
3175
      }
1,027✔
3176
      LOG(i->first.toLogString() << "(" << fmtfloat(i->second / 1000.0) << "ms)");
2,562!
3177
    }
2,562✔
3178
    LOG(endl);
1,535!
3179
  }
1,535✔
3180
  return rnameservers;
10,933✔
3181
}
11,206✔
3182

3183
vector<ComboAddress> SyncRes::shuffleForwardSpeed(const DNSName& qname, const vector<ComboAddress>& rnameservers, const string& prefix, const bool wasRd)
3184
{
134✔
3185
  vector<ComboAddress> nameservers = rnameservers;
134✔
3186
  map<ComboAddress, float> speeds;
134✔
3187

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

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

3214
static uint32_t getRRSIGTTL(const time_t now, const std::shared_ptr<const RRSIGRecordContent>& rrsig)
3215
{
5,526✔
3216
  uint32_t res = 0;
5,526✔
3217
  if (now < rrsig->d_sigexpire) {
5,526!
3218
    // coverity[store_truncates_time_t]
3219
    res = static_cast<uint32_t>(rrsig->d_sigexpire) - now;
5,526✔
3220
  }
5,526✔
3221
  return res;
5,526✔
3222
}
5,526✔
3223

3224
static const set<QType> nsecTypes = {QType::NSEC, QType::NSEC3};
3225

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

3242
    if (rec.d_type == QType::RRSIG) {
24,822✔
3243
      auto rrsig = getRR<RRSIGRecordContent>(rec);
6,172✔
3244
      if (rrsig) {
6,172!
3245
        if (rrsig->d_type == QType::SOA) {
6,172✔
3246
          negEntry.authoritySOA.signatures.push_back(rec);
549✔
3247
          if (lowestTTL != nullptr && isRRSIGNotExpired(now, *rrsig)) {
549!
3248
            *lowestTTL = min(*lowestTTL, rec.d_ttl);
281✔
3249
            *lowestTTL = min(*lowestTTL, getRRSIGTTL(now, rrsig));
281✔
3250
          }
281✔
3251
        }
549✔
3252
        if (nsecTypes.count(rrsig->d_type) != 0) {
6,172✔
3253
          negEntry.DNSSECRecords.signatures.push_back(rec);
5,623✔
3254
          if (lowestTTL != nullptr && isRRSIGNotExpired(now, *rrsig)) {
5,623!
3255
            *lowestTTL = min(*lowestTTL, rec.d_ttl);
5,245✔
3256
            *lowestTTL = min(*lowestTTL, getRRSIGTTL(now, rrsig));
5,245✔
3257
          }
5,245✔
3258
        }
5,623✔
3259
      }
6,172✔
3260
      continue;
6,172✔
3261
    }
6,172✔
3262
    if (rec.d_type == QType::SOA) {
18,650✔
3263
      negEntry.authoritySOA.records.push_back(rec);
2,388✔
3264
      if (lowestTTL != nullptr) {
2,388✔
3265
        *lowestTTL = min(*lowestTTL, rec.d_ttl);
1,239✔
3266
      }
1,239✔
3267
      continue;
2,388✔
3268
    }
2,388✔
3269
    if (nsecTypes.count(rec.d_type) != 0) {
16,262✔
3270
      negEntry.DNSSECRecords.records.push_back(rec);
5,635✔
3271
      if (lowestTTL != nullptr) {
5,635✔
3272
        *lowestTTL = min(*lowestTTL, rec.d_ttl);
5,251✔
3273
      }
5,251✔
3274
      continue;
5,635✔
3275
    }
5,635✔
3276
  }
16,262✔
3277
}
5,023✔
3278

3279
static cspmap_t harvestCSPFromNE(const NegCache::NegCacheEntry& negEntry)
3280
{
2,001✔
3281
  cspmap_t cspmap;
2,001✔
3282
  for (const auto& rec : negEntry.DNSSECRecords.signatures) {
3,552✔
3283
    if (rec.d_type == QType::RRSIG) {
3,552!
3284
      auto rrc = getRR<RRSIGRecordContent>(rec);
3,552✔
3285
      if (rrc) {
3,552!
3286
        cspmap[{rec.d_name, rrc->d_type}].signatures.push_back(rrc);
3,552✔
3287
      }
3,552✔
3288
    }
3,552✔
3289
  }
3,552✔
3290
  for (const auto& rec : negEntry.DNSSECRecords.records) {
3,554✔
3291
    cspmap[{rec.d_name, rec.d_type}].records.insert(rec.getContent());
3,554✔
3292
  }
3,554✔
3293
  return cspmap;
2,001✔
3294
}
2,001✔
3295

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

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

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

3327
  return true;
55✔
3328
}
59✔
3329

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

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

3347
    if (remove) {
10✔
3348
      it = records.erase(it);
4✔
3349
    }
4✔
3350
    else {
6✔
3351
      ++it;
6✔
3352
    }
6✔
3353
  }
10✔
3354
}
63✔
3355

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

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

3370
  if (d_appliedPolicy.d_type != DNSFilterEngine::PolicyType::None) {
101!
3371
    LOG(prefix << qname << "|" << qtype << ':' << d_appliedPolicy.getLogString() << endl);
101!
3372
  }
101✔
3373

3374
  switch (d_appliedPolicy.d_kind) {
101!
3375

3376
  case DNSFilterEngine::PolicyKind::NoAction:
22✔
3377
    return;
22✔
3378

3379
  case DNSFilterEngine::PolicyKind::Drop:
6✔
3380
    ++t_Counters.at(rec::Counter::policyDrops);
6✔
3381
    throw ImmediateQueryDropException();
6✔
3382

3383
  case DNSFilterEngine::PolicyKind::NXDOMAIN:
4✔
3384
    ret.clear();
4✔
3385
    d_appliedPolicy.addSOAtoRPZResult(ret);
4✔
3386
    rcode = RCode::NXDomain;
4✔
3387
    done = true;
4✔
3388
    return;
4✔
3389

3390
  case DNSFilterEngine::PolicyKind::NODATA:
4✔
3391
    ret.clear();
4✔
3392
    d_appliedPolicy.addSOAtoRPZResult(ret);
4✔
3393
    rcode = RCode::NoError;
4✔
3394
    done = true;
4✔
3395
    return;
4✔
3396

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

3407
  case DNSFilterEngine::PolicyKind::Custom: {
59✔
3408
    if (rpzHitShouldReplaceContent(qname, qtype, ret)) {
59✔
3409
      ret.clear();
55✔
3410
    }
55✔
3411

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

3419
    for (auto& dnsRecord : spoofed) {
63✔
3420
      ret.push_back(dnsRecord);
63✔
3421

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

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

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

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

3490
static bool shouldNotThrottle(const DNSName* name, const ComboAddress* address)
3491
{
11,341✔
3492
  if (name != nullptr) {
11,341!
3493
    auto dontThrottleNames = g_dontThrottleNames.getLocal();
11,341✔
3494
    if (dontThrottleNames->check(*name)) {
11,341!
3495
      return true;
×
3496
    }
×
3497
  }
11,341✔
3498
  if (address != nullptr) {
11,341✔
3499
    auto dontThrottleNetmasks = g_dontThrottleNetmasks.getLocal();
11,308✔
3500
    if (dontThrottleNetmasks->match(*address)) {
11,308!
3501
      return true;
×
3502
    }
×
3503
  }
11,308✔
3504
  return false;
11,341✔
3505
}
11,341✔
3506

3507
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)
3508
{
11,293✔
3509
  vector<ComboAddress> result;
11,293✔
3510

3511
  size_t nonresolvingfails = 0;
11,293✔
3512
  if (!tns->first.empty()) {
11,293✔
3513
    if (s_nonresolvingnsmaxfails > 0) {
11,159✔
3514
      nonresolvingfails = s_nonresolving.lock()->value(tns->first);
8,729✔
3515
      if (nonresolvingfails >= s_nonresolvingnsmaxfails) {
8,729✔
3516
        LOG(prefix << qname << ": NS " << tns->first << " in non-resolving map, skipping" << endl);
16!
3517
        return result;
16✔
3518
      }
16✔
3519
    }
8,729✔
3520

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

3551
    if (nameservers[tns->first].first.size() > 1) {
134✔
3552
      LOG("s");
16!
3553
    }
16✔
3554
    LOG(endl);
134✔
3555

3556
    sendRDQuery = nameservers[tns->first].second;
134✔
3557
    result = shuffleForwardSpeed(qname, nameservers[tns->first].first, prefix, sendRDQuery);
134✔
3558
    pierceDontQuery = true;
134✔
3559
  }
134✔
3560
  return result;
11,187✔
3561
}
11,293✔
3562

3563
void SyncRes::checkMaxQperQ(const DNSName& qname) const
3564
{
11,268✔
3565
  if (d_outqueries + d_throttledqueries > s_maxqperq) {
11,268✔
3566
    throw ImmediateServFailException("more than " + std::to_string(s_maxqperq) + " (max-qperq) queries sent or throttled while resolving " + qname.toLogString());
4✔
3567
  }
4✔
3568
}
11,268✔
3569

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

3606
bool SyncRes::validationEnabled()
3607
{
11,200✔
3608
  return g_dnssecmode != DNSSECMode::Off && g_dnssecmode != DNSSECMode::ProcessNoValidate;
11,200✔
3609
}
11,200✔
3610

3611
uint32_t SyncRes::computeLowestTTD(const std::vector<DNSRecord>& records, const MemRecursorCache::SigRecsVec& signatures, uint32_t signaturesTTL, const MemRecursorCache::AuthRecsVec& authorityRecs) const
3612
{
10,666✔
3613
  uint32_t lowestTTD = std::numeric_limits<uint32_t>::max();
10,666✔
3614
  for (const auto& record : records) {
22,358✔
3615
    lowestTTD = min(lowestTTD, record.d_ttl);
22,358✔
3616
  }
22,358✔
3617

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

3625
    for (const auto& sig : signatures) {
7,394✔
3626
      if (isRRSIGNotExpired(d_now.tv_sec, *sig)) {
7,394✔
3627
        // we don't decrement d_sigexpire by 'now' because we actually want a TTD, not a TTL */
3628
        lowestTTD = min(lowestTTD, static_cast<uint32_t>(sig->d_sigexpire));
7,382✔
3629
      }
7,382✔
3630
    }
7,394✔
3631
  }
7,373✔
3632

3633
  for (const auto& entry : authorityRecs) {
10,666✔
3634
    /* be careful, this is still a TTL here */
3635
    lowestTTD = min(lowestTTD, static_cast<uint32_t>(entry.d_ttl + d_now.tv_sec));
1,048✔
3636

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

3648
  return lowestTTD;
10,666✔
3649
}
10,666✔
3650

3651
void SyncRes::updateValidationState(const DNSName& qname, vState& state, const vState stateUpdate, const string& prefix)
3652
{
11,042✔
3653
  LOG(prefix << qname << ": Validation state was " << state << ", state update is " << stateUpdate);
11,042✔
3654
  updateDNSSECValidationState(state, stateUpdate);
11,042✔
3655
  LOG(", validation state is now " << state << endl);
11,042✔
3656
}
11,042✔
3657

3658
vState SyncRes::getTA(const DNSName& zone, dsset_t& dsSet, const string& prefix)
3659
{
53,656✔
3660
  auto luaLocal = g_luaconfs.getLocal();
53,656✔
3661

3662
  if (luaLocal->dsAnchors.empty()) {
53,656✔
3663
    LOG(prefix << zone << ": No trust anchors configured, everything is Insecure" << endl);
44!
3664
    /* We have no TA, everything is insecure */
3665
    return vState::Insecure;
44✔
3666
  }
44✔
3667

3668
  std::string reason;
53,612✔
3669
  if (haveNegativeTrustAnchor(luaLocal->negAnchors, zone, reason)) {
53,612✔
3670
    LOG(prefix << zone << ": Got NTA" << endl);
15!
3671
    return vState::NTA;
15✔
3672
  }
15✔
3673

3674
  if (getTrustAnchor(luaLocal->dsAnchors, zone, dsSet)) {
53,597✔
3675
    if (!zone.isRoot()) {
16,205✔
3676
      LOG(prefix << zone << ": Got TA" << endl);
144!
3677
    }
144✔
3678
    return vState::TA;
16,205✔
3679
  }
16,205✔
3680

3681
  if (zone.isRoot()) {
37,392!
3682
    /* No TA for the root */
3683
    return vState::Insecure;
×
3684
  }
×
3685

3686
  return vState::Indeterminate;
37,392✔
3687
}
37,392✔
3688

3689
size_t SyncRes::countSupportedDS(const dsset_t& dsset, const string& prefix)
3690
{
16,205✔
3691
  size_t count = 0;
16,205✔
3692

3693
  for (const auto& dsRecordContent : dsset) {
34,056✔
3694
    if (isSupportedDS(dsRecordContent, LogObject(prefix))) {
34,059✔
3695
      count++;
34,048✔
3696
    }
34,048✔
3697
  }
34,056✔
3698

3699
  return count;
16,205✔
3700
}
16,205✔
3701

3702
void SyncRes::initZoneCutsFromTA(const DNSName& from, const string& prefix)
3703
{
15,441✔
3704
  DNSName zone(from);
15,441✔
3705
  do {
48,163✔
3706
    dsset_t dsSet;
48,163✔
3707
    vState result = getTA(zone, dsSet, prefix);
48,163✔
3708
    if (result != vState::Indeterminate) {
48,163✔
3709
      if (result == vState::TA) {
15,596✔
3710
        if (countSupportedDS(dsSet, prefix) == 0) {
15,537✔
3711
          dsSet.clear();
8✔
3712
          result = vState::Insecure;
8✔
3713
        }
8✔
3714
        else {
15,529✔
3715
          result = vState::Secure;
15,529✔
3716
        }
15,529✔
3717
      }
15,537✔
3718
      else if (result == vState::NTA) {
59✔
3719
        result = vState::Insecure;
15✔
3720
      }
15✔
3721

3722
      d_cutStates[zone] = result;
15,596✔
3723
    }
15,596✔
3724
  } while (zone.chopOff());
48,163✔
3725
}
15,441✔
3726

3727
vState SyncRes::getDSRecords(const DNSName& zone, dsset_t& dsSet, bool onlyTA, unsigned int depth, const string& prefix, bool bogusOnNXD, bool* foundCut)
3728
{
5,493✔
3729
  vState result = getTA(zone, dsSet, prefix);
5,493✔
3730

3731
  if (result != vState::Indeterminate || onlyTA) {
5,493!
3732
    if (foundCut != nullptr) {
668!
3733
      *foundCut = (result != vState::Indeterminate);
×
3734
    }
×
3735

3736
    if (result == vState::TA) {
668!
3737
      if (countSupportedDS(dsSet, prefix) == 0) {
668!
3738
        dsSet.clear();
×
3739
        result = vState::Insecure;
×
3740
      }
×
3741
      else {
668✔
3742
        result = vState::Secure;
668✔
3743
      }
668✔
3744
    }
668✔
3745
    else if (result == vState::NTA) {
×
3746
      result = vState::Insecure;
×
3747
    }
×
3748

3749
    return result;
668✔
3750
  }
668✔
3751

3752
  std::set<GetBestNSAnswer> beenthere;
4,825✔
3753
  std::vector<DNSRecord> dsrecords;
4,825✔
3754

3755
  Context context;
4,825✔
3756

3757
  const bool oldCacheOnly = setCacheOnly(false);
4,825✔
3758
  const bool oldQM = setQNameMinimization(!getQMFallbackMode());
4,825✔
3759
  int rcode = doResolve(zone, QType::DS, dsrecords, depth + 1, beenthere, context);
4,825✔
3760
  setCacheOnly(oldCacheOnly);
4,825✔
3761
  setQNameMinimization(oldQM);
4,825✔
3762

3763
  if (rcode == RCode::ServFail) {
4,825✔
3764
    throw ImmediateServFailException("Server Failure while retrieving DS records for " + zone.toLogString());
4✔
3765
  }
4✔
3766

3767
  if (rcode != RCode::NoError && (rcode != RCode::NXDomain || bogusOnNXD)) {
4,821!
3768
    LOG(prefix << zone << ": Returning Bogus state from " << static_cast<const char*>(__func__) << "(" << zone << ")" << endl);
2!
3769
    return vState::BogusUnableToGetDSs;
2✔
3770
  }
2✔
3771

3772
  uint8_t bestDigestType = 0;
4,819✔
3773

3774
  bool gotCNAME = false;
4,819✔
3775
  for (const auto& record : dsrecords) {
13,678✔
3776
    if (record.d_type == QType::DS) {
13,678✔
3777
      const auto dscontent = getRR<DSRecordContent>(record);
2,721✔
3778
      if (dscontent && isSupportedDS(*dscontent, LogObject(prefix))) {
2,721!
3779
        // Make GOST a lower prio than SHA256
3780
        if (dscontent->d_digesttype == DNSSECKeeper::DIGEST_GOST && bestDigestType == DNSSECKeeper::DIGEST_SHA256) {
2,721!
3781
          continue;
×
3782
        }
×
3783
        if (dscontent->d_digesttype > bestDigestType || (bestDigestType == DNSSECKeeper::DIGEST_GOST && dscontent->d_digesttype == DNSSECKeeper::DIGEST_SHA256)) {
2,721!
3784
          bestDigestType = dscontent->d_digesttype;
2,708✔
3785
        }
2,708✔
3786
        dsSet.insert(*dscontent);
2,721✔
3787
      }
2,721✔
3788
    }
2,721✔
3789
    else if (record.d_type == QType::CNAME && record.d_name == zone) {
10,957!
3790
      gotCNAME = true;
13✔
3791
    }
13✔
3792
  }
13,678✔
3793

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

3807
  if (rcode == RCode::NoError) {
4,819✔
3808
    if (dsSet.empty()) {
4,813✔
3809
      /* we have no DS, it's either:
3810
         - a delegation to a non-DNSSEC signed zone
3811
         - no delegation, we stay in the same zone
3812
      */
3813
      if (gotCNAME || denialProvesNoDelegation(zone, dsrecords, d_validationContext)) {
2,116✔
3814
        /* we are still inside the same zone */
3815

3816
        if (foundCut != nullptr) {
31✔
3817
          *foundCut = false;
25✔
3818
        }
25✔
3819
        return context.state;
31✔
3820
      }
31✔
3821

3822
      d_cutStates[zone] = context.state == vState::Secure ? vState::Insecure : context.state;
2,085✔
3823
      /* delegation with no DS, might be Secure -> Insecure */
3824
      if (foundCut != nullptr) {
2,085✔
3825
        *foundCut = true;
2,064✔
3826
      }
2,064✔
3827

3828
      /* a delegation with no DS is either:
3829
         - a signed zone (Secure) to an unsigned one (Insecure)
3830
         - an unsigned zone to another unsigned one (Insecure stays Insecure, Bogus stays Bogus)
3831
      */
3832
      return context.state == vState::Secure ? vState::Insecure : context.state;
2,085✔
3833
    }
2,116✔
3834
    /* we have a DS */
3835
    d_cutStates[zone] = context.state;
2,697✔
3836
    if (foundCut != nullptr) {
2,697✔
3837
      *foundCut = true;
1,585✔
3838
    }
1,585✔
3839
  }
2,697✔
3840

3841
  return context.state;
2,703✔
3842
}
4,819✔
3843

3844
vState SyncRes::getValidationStatus(const DNSName& name, bool wouldBeValid, bool typeIsDS, unsigned int depth, const string& prefix)
3845
{
14,561✔
3846
  vState result = vState::Indeterminate;
14,561✔
3847

3848
  if (!shouldValidate()) {
14,561✔
3849
    return result;
1,103✔
3850
  }
1,103✔
3851

3852
  DNSName subdomain(name);
13,458✔
3853
  if (typeIsDS) {
13,458✔
3854
    subdomain.chopOff();
2,878✔
3855
  }
2,878✔
3856

3857
  {
13,458✔
3858
    const auto& iter = d_cutStates.find(subdomain);
13,458✔
3859
    if (iter != d_cutStates.cend()) {
13,458✔
3860
      LOG(prefix << name << ": Got status " << iter->second << " for name " << subdomain << endl);
3,168!
3861
      return iter->second;
3,168✔
3862
    }
3,168✔
3863
  }
13,458✔
3864

3865
  /* look for the best match we have */
3866
  DNSName best(subdomain);
10,290✔
3867
  while (best.chopOff()) {
18,397✔
3868
    const auto& iter = d_cutStates.find(best);
18,397✔
3869
    if (iter != d_cutStates.cend()) {
18,397✔
3870
      result = iter->second;
10,291✔
3871
      if (vStateIsBogus(result) || result == vState::Insecure) {
10,291✔
3872
        LOG(prefix << name << ": Got status " << result << " for name " << best << endl);
2,250!
3873
        return result;
2,250✔
3874
      }
2,250✔
3875
      break;
8,041✔
3876
    }
10,291✔
3877
  }
18,397✔
3878

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

3889
    while (!labelsToAdd.empty()) {
3,741✔
3890

3891
      dsName.prependRawLabel(labelsToAdd.back());
3,680✔
3892
      labelsToAdd.pop_back();
3,680✔
3893
      LOG(prefix << name << ": - Looking for a DS at " << dsName << endl);
3,680!
3894

3895
      bool foundCut = false;
3,680✔
3896
      dsset_t results;
3,680✔
3897
      vState dsState = getDSRecords(dsName, results, false, depth, prefix, false, &foundCut);
3,680✔
3898

3899
      if (foundCut) {
3,680✔
3900
        LOG(prefix << name << ": - Found cut at " << dsName << endl);
3,649!
3901
        LOG(prefix << name << ": New state for " << dsName << " is " << dsState << endl);
3,649!
3902
        d_cutStates[dsName] = dsState;
3,649✔
3903

3904
        if (dsState != vState::Secure) {
3,649✔
3905
          return dsState;
2,064✔
3906
        }
2,064✔
3907
      }
3,649✔
3908
    }
3,680✔
3909

3910
    /* we did not miss a cut, good luck */
3911
    return result;
61✔
3912
  }
2,125✔
3913

3914
#if 0
3915
  /* we don't need this, we actually do the right thing later */
3916
  DNSName signer = getSigner(signatures);
3917

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

3931
  return result;
5,915✔
3932
}
8,040✔
3933

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

3942
  DNSName signer = getSigner(signatures);
1,794✔
3943

3944
  if (!signer.empty() && zone.isPartOf(signer)) {
1,794!
3945
    vState state = getDSRecords(signer, dsSet, false, depth, prefix);
1,794✔
3946

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

3963
  skeyset_t tentativeKeys;
1,764✔
3964
  sortedRecords_t toSign;
1,764✔
3965

3966
  for (const auto& dnskey : dnskeys) {
2,049✔
3967
    if (dnskey.d_type == QType::DNSKEY) {
2,049!
3968
      auto content = getRR<DNSKEYRecordContent>(dnskey);
2,049✔
3969
      if (content) {
2,049!
3970
        tentativeKeys.insert(content);
2,049✔
3971
        toSign.insert(content);
2,049✔
3972
      }
2,049✔
3973
    }
2,049✔
3974
  }
2,049✔
3975

3976
  LOG(prefix << zone << ": Trying to validate " << std::to_string(tentativeKeys.size()) << " DNSKEYs with " << std::to_string(dsSet.size()) << " DS" << endl);
1,764!
3977
  skeyset_t validatedKeys;
1,764✔
3978
  auto state = validateDNSKeysAgainstDS(d_now.tv_sec, zone, dsSet, tentativeKeys, toSign, signatures, validatedKeys, LogObject(prefix), d_validationContext);
1,764✔
3979

3980
  if (s_maxvalidationsperq != 0 && d_validationContext.d_validationsCounter > s_maxvalidationsperq) {
1,764!
3981
    throw ImmediateServFailException("Server Failure while validating DNSKEYs, too many signature validations for this query");
×
3982
  }
×
3983

3984
  LOG(prefix << zone << ": We now have " << std::to_string(validatedKeys.size()) << " DNSKEYs" << endl);
1,764!
3985

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

4002
  return state;
1,719✔
4003
}
1,764✔
4004

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

4011
  Context context;
4,273✔
4012

4013
  const bool oldCacheOnly = setCacheOnly(false);
4,273✔
4014
  int rcode = doResolve(signer, QType::DNSKEY, records, depth + 1, beenthere, context);
4,273✔
4015
  setCacheOnly(oldCacheOnly);
4,273✔
4016

4017
  if (rcode == RCode::ServFail) {
4,273✔
4018
    servFailOccurred = true;
12✔
4019
    return vState::BogusUnableToGetDNSKEYs;
12✔
4020
  }
12✔
4021

4022
  if (rcode == RCode::NoError) {
4,261✔
4023
    if (context.state == vState::Secure) {
4,255✔
4024
      for (const auto& key : records) {
10,177✔
4025
        if (key.d_type == QType::DNSKEY) {
10,177✔
4026
          auto content = getRR<DNSKEYRecordContent>(key);
6,035✔
4027
          if (content) {
6,036✔
4028
            keys.insert(std::move(content));
6,036✔
4029
          }
6,036✔
4030
        }
6,035✔
4031
      }
10,177✔
4032
    }
4,143✔
4033
    LOG(prefix << signer << ": Retrieved " << keys.size() << " DNSKeys, state is " << context.state << endl);
4,255!
4034
    return context.state;
4,255✔
4035
  }
4,255✔
4036

4037
  if (context.state == vState::Insecure) {
6✔
4038
    return context.state;
2✔
4039
  }
2✔
4040

4041
  LOG(prefix << signer << ": Returning Bogus state from " << static_cast<const char*>(__func__) << "(" << signer << ")" << endl);
4!
4042
  return vState::BogusUnableToGetDNSKEYs;
4✔
4043
}
6✔
4044

4045
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)
4046
{
4,333✔
4047
  skeyset_t keys;
4,333✔
4048
  if (signatures.empty()) {
4,333✔
4049
    LOG(prefix << qname << ": Bogus!" << endl);
36!
4050
    return vState::BogusNoRRSIG;
36✔
4051
  }
36✔
4052

4053
  const DNSName signer = getSigner(signatures);
4,297✔
4054
  bool dsFailed = false;
4,297✔
4055
  if (!signer.empty() && name.isPartOf(signer)) {
4,297!
4056
    vState state = vState::Secure;
4,295✔
4057

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

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

4137
  sortedRecords_t recordcontents;
4,149✔
4138
  for (const auto& record : records) {
4,566✔
4139
    recordcontents.insert(record.getContent());
4,566✔
4140
  }
4,566✔
4141

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

4148
  if (state == vState::Secure) {
4,147✔
4149
    LOG(prefix << name << ": Secure!" << endl);
4,116!
4150
    return vState::Secure;
4,116✔
4151
  }
4,116✔
4152

4153
  LOG(prefix << vStateToString(state) << "!" << endl);
31!
4154

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

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

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

4183
  if (wasForwardRecurse || lwr.d_aabit) {
10,928✔
4184
    /* easy */
4185
    return;
7,736✔
4186
  }
7,736✔
4187

4188
  for (const auto& rec : lwr.d_records) {
3,192✔
4189

4190
    if (rec.d_type == QType::OPT) {
3,140!
4191
      continue;
×
4192
    }
×
4193

4194
    if (rec.d_class != QClass::IN) {
3,140!
4195
      continue;
×
4196
    }
×
4197

4198
    if (rec.d_type == QType::ANY) {
3,140!
4199
      continue;
×
4200
    }
×
4201

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

4210
    if (rec.d_place != DNSResourceRecord::ANSWER) {
3,130!
4211
      /* we have scanned all the records in the answer section, if any, we are done */
4212
      return;
3,130✔
4213
    }
3,130✔
4214
  }
3,130✔
4215
}
3,192✔
4216

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

4270
static bool isRedirection(QType qtype)
4271
{
18,230✔
4272
  return qtype == QType::CNAME || qtype == QType::DNAME;
18,230✔
4273
}
18,230✔
4274

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

4286
  std::vector<bool> skipvec(lwr.d_records.size(), false);
10,928✔
4287
  unsigned int counter = 0;
10,928✔
4288
  unsigned int skipCount = 0;
10,928✔
4289

4290
  for (auto rec = lwr.d_records.cbegin(); rec != lwr.d_records.cend(); ++rec, ++counter) {
68,575✔
4291

4292
    // Allow OPT record containing EDNS(0) data
4293
    if (rec->d_type == QType::OPT) {
57,647✔
4294
      continue;
8,632✔
4295
    }
8,632✔
4296

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

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

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

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

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

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

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

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

4401
        if (!haveAnswers) {
1,244✔
4402
          switch (lwr.d_rcode) {
1,229!
4403
          case RCode::NXDomain:
118✔
4404
            isNXDomain = true;
118✔
4405
            break;
118✔
4406
          case RCode::NoError:
1,111✔
4407
            isNXQType = true;
1,111✔
4408
            break;
1,111✔
4409
          }
1,229✔
4410
        }
1,229✔
4411
      }
1,244✔
4412
    }
21,433✔
4413
    /* dealing with records in additional */
4414
    else if (rec->d_place == DNSResourceRecord::ADDITIONAL) {
14,282✔
4415
      if (rec->d_type != QType::A && rec->d_type != QType::AAAA && rec->d_type != QType::RRSIG) {
14,280✔
4416
        LOG(prefix << qname << ": Removing irrelevant record '" << rec->toString() << "' in the ADDITIONAL section received from " << auth << endl);
2!
4417
        skipvec[counter] = true;
2✔
4418
        ++skipCount;
2✔
4419
        continue;
2✔
4420
      }
2✔
4421
    }
14,280✔
4422
  } // end of first loop, handled answer and most of authority section
48,653✔
4423

4424
  sanitizeRecordsPass2(prefix, lwr, qname, auth, allowedAnswerNames, allowedAdditionals, isNXDomain, isNXQType, skipvec, skipCount);
10,928✔
4425
}
10,928✔
4426

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

4433
    if (skipvec[counter]) {
57,648✔
4434
      continue;
376✔
4435
    }
376✔
4436
    // Allow OPT record containing EDNS(0) data
4437
    if (rec->d_type == QType::OPT) {
57,272✔
4438
      continue;
8,633✔
4439
    }
8,633✔
4440

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

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

4497
  if (ttl <= 0 || wasAuth) {
469✔
4498
    return;
46✔
4499
  }
46✔
4500
  {
423✔
4501
    auto lock = s_savedParentNSSet.lock();
423✔
4502
    if (lock->find(domain) != lock->end()) {
423!
4503
      // no relevant data, or we already stored the parent data
4504
      return;
×
4505
    }
×
4506
  }
423✔
4507

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

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

4539
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)
4540
{
10,928✔
4541
  bool wasForwardRecurse = wasForwarded && rdQuery;
10,928✔
4542
  tcache_t tcache;
10,928✔
4543

4544
  fixupAnswer(prefix, lwr, qname, qtype, auth, wasForwarded, rdQuery);
10,928✔
4545
  sanitizeRecords(prefix, lwr, qname, qtype, auth, wasForwarded, rdQuery);
10,928✔
4546

4547
  MemRecursorCache::AuthRecsVec authorityRecs;
10,928✔
4548
  bool isCNAMEAnswer = false;
10,928✔
4549
  bool isDNAMEAnswer = false;
10,928✔
4550
  DNSName seenAuth;
10,928✔
4551

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

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

4582
  for (auto& rec : lwr.d_records) {
57,247✔
4583
    if (rec.d_type == QType::OPT || rec.d_class != QClass::IN) {
57,247!
4584
      continue;
8,633✔
4585
    }
8,633✔
4586

4587
    rec.d_ttl = min(s_maxcachettl, rec.d_ttl);
48,614✔
4588

4589
    if (!isCNAMEAnswer && rec.d_place == DNSResourceRecord::ANSWER && rec.d_type == QType::CNAME && (!(qtype == QType::CNAME)) && rec.d_name == qname && !isDNAMEAnswer) {
48,614!
4590
      isCNAMEAnswer = true;
1,230✔
4591
    }
1,230✔
4592
    if (!isDNAMEAnswer && rec.d_place == DNSResourceRecord::ANSWER && rec.d_type == QType::DNAME && qtype != QType::DNAME && qname.isPartOf(rec.d_name)) {
48,615!
4593
      isDNAMEAnswer = true;
32✔
4594
      isCNAMEAnswer = false;
32✔
4595
    }
32✔
4596

4597
    if (rec.d_type == QType::SOA && rec.d_place == DNSResourceRecord::AUTHORITY && qname.isPartOf(rec.d_name)) {
48,614!
4598
      seenAuth = rec.d_name;
1,244✔
4599
    }
1,244✔
4600

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

4627
        // cerr<<"Got an RRSIG for "<<DNSRecordContent::NumberToType(rrsig->d_type)<<" with name '"<<rec.d_name<<"' and place "<<rec.d_place<<endl;
4628
        tcache[{rec.d_name, rrsig->d_type, rec.d_place}].signatures.push_back(rrsig);
7,529✔
4629
        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,529✔
4630
      }
7,529✔
4631
    }
7,529✔
4632
  }
48,614✔
4633

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

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

4655
  // reap all answers from this packet that are acceptable
4656
  for (auto& rec : lwr.d_records) {
57,248✔
4657
    if (rec.d_type == QType::OPT) {
57,248✔
4658
      LOG(prefix << qname << ": OPT answer '" << rec.d_name << "' from '" << auth << "' nameservers" << endl);
8,633!
4659
      continue;
8,633✔
4660
    }
8,633✔
4661

4662
    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 << " ");
48,615✔
4663

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

4666
    if (rec.d_name.isPartOf(auth)) {
48,617✔
4667
      if (rec.d_type == QType::RRSIG) {
48,616✔
4668
        LOG("RRSIG - separate" << endl);
7,529!
4669
      }
7,529✔
4670
      else if (rec.d_type == QType::DS && rec.d_name == auth) {
41,087✔
4671
        LOG("NO - DS provided by child zone" << endl);
2!
4672
      }
2✔
4673
      else {
41,085✔
4674
        bool haveLogged = false;
41,085✔
4675
        if (isDNAMEAnswer && rec.d_type == QType::CNAME) {
41,085✔
4676
          LOG("NO - we already have a DNAME answer for this domain" << endl);
30!
4677
          continue;
30✔
4678
        }
30✔
4679
        if (!t_sstorage.domainmap->empty()) {
41,056✔
4680
          // Check if we are authoritative for a zone in this answer
4681
          DNSName tmp_qname(rec.d_name);
36,737✔
4682
          // We may be auth for domain example.com, but the DS record needs to come from the parent (.com) nameserver
4683
          if (rec.d_type == QType::DS) {
36,737✔
4684
            tmp_qname.chopOff();
1,077✔
4685
          }
1,077✔
4686
          auto auth_domain_iter = getBestAuthZone(&tmp_qname);
36,737✔
4687
          if (auth_domain_iter != t_sstorage.domainmap->end() && auth.countLabels() <= auth_domain_iter->first.countLabels()) {
36,737✔
4688
            if (auth_domain_iter->first != auth) {
320✔
4689
              LOG("NO! - we are authoritative for the zone " << auth_domain_iter->first << endl);
2!
4690
              continue;
2✔
4691
            }
2✔
4692
            LOG("YES! - This answer was ");
318!
4693
            if (!wasForwarded) {
318✔
4694
              LOG("retrieved from the local auth store.");
143!
4695
            }
143✔
4696
            else {
175✔
4697
              LOG("received from a server we forward to.");
175!
4698
            }
175✔
4699
            haveLogged = true;
318✔
4700
            LOG(endl);
318!
4701
          }
318✔
4702
        }
36,737✔
4703
        if (!haveLogged) {
41,054✔
4704
          LOG("YES!" << endl);
40,736✔
4705
        }
40,736✔
4706

4707
        rec.d_ttl = min(s_maxcachettl, rec.d_ttl);
41,053✔
4708

4709
        DNSRecord dnsRecord(rec);
41,053✔
4710
        tcache[{rec.d_name, rec.d_type, rec.d_place}].d_ttl_time = d_now.tv_sec;
41,053✔
4711
        dnsRecord.d_ttl += d_now.tv_sec;
41,053✔
4712
        dnsRecord.d_place = DNSResourceRecord::ANSWER;
41,053✔
4713
        tcache[{rec.d_name, rec.d_type, rec.d_place}].records.push_back(std::move(dnsRecord));
41,053✔
4714
      }
41,053✔
4715
    }
48,616✔
4716
    else
2,147,483,647✔
4717
      LOG("NO!" << endl);
2,147,499,335!
4718
  }
48,583✔
4719

4720
  // supplant
4721
  for (auto& entry : tcache) {
29,362✔
4722
    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)
29,362✔
4723
      uint32_t lowestTTD = computeLowestTTD(entry.second.records, entry.second.signatures, entry.second.signaturesTTL, authorityRecs);
10,666✔
4724

4725
      for (auto& record : entry.second.records) {
22,358✔
4726
        record.d_ttl = lowestTTD; // boom
22,358✔
4727
      }
22,358✔
4728
    }
10,666✔
4729
  }
29,362✔
4730

4731
  for (auto tCacheEntry = tcache.begin(); tCacheEntry != tcache.end(); ++tCacheEntry) {
40,287✔
4732

4733
    if (tCacheEntry->second.records.empty()) { // this happens when we did store signatures, but passed on the records themselves
29,359!
4734
      continue;
×
4735
    }
×
4736

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

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

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

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

4807
    vState recordState = vState::Indeterminate;
29,357✔
4808

4809
    if (expectSignature && shouldValidate()) {
29,357✔
4810
      vState initialState = getValidationStatus(tCacheEntry->first.name, !tCacheEntry->second.signatures.empty(), tCacheEntry->first.type == QType::DS, depth, prefix);
9,198✔
4811
      LOG(prefix << qname << ": Got initial zone status " << initialState << " for record " << tCacheEntry->first.name << "|" << DNSRecordContent::NumberToType(tCacheEntry->first.type) << endl);
9,198!
4812

4813
      if (initialState == vState::Secure) {
9,198✔
4814
        if (tCacheEntry->first.type == QType::DNSKEY && tCacheEntry->first.place == DNSResourceRecord::ANSWER && tCacheEntry->first.name == getSigner(tCacheEntry->second.signatures)) {
6,061!
4815
          LOG(prefix << qname << ": Validating DNSKEY for " << tCacheEntry->first.name << endl);
1,792!
4816
          recordState = validateDNSKeys(tCacheEntry->first.name, tCacheEntry->second.records, tCacheEntry->second.signatures, depth, prefix);
1,792✔
4817
        }
1,792✔
4818
        else {
4,269✔
4819
          LOG(prefix << qname << ": Validating non-additional " << QType(tCacheEntry->first.type).toString() << " record for " << tCacheEntry->first.name << endl);
4,269!
4820
          recordState = validateRecordsWithSigs(depth, prefix, qname, qtype, tCacheEntry->first.name, QType(tCacheEntry->first.type), tCacheEntry->second.records, tCacheEntry->second.signatures);
4,269✔
4821
        }
4,269✔
4822
      }
6,061✔
4823
      else {
3,137✔
4824
        recordState = initialState;
3,137✔
4825
        LOG(prefix << qname << ": Skipping validation because the current state is " << recordState << endl);
3,137!
4826
      }
3,137✔
4827

4828
      LOG(prefix << qname << ": Validation result is " << recordState << ", current state is " << state << endl);
9,198!
4829
      if (state != recordState) {
9,198✔
4830
        updateValidationState(qname, state, recordState, prefix);
5,740✔
4831
      }
5,740✔
4832
    }
9,198✔
4833

4834
    if (vStateIsBogus(recordState)) {
29,357✔
4835
      /* this is a TTD by now, be careful */
4836
      for (auto& record : tCacheEntry->second.records) {
504✔
4837
        auto newval = std::min(record.d_ttl, static_cast<uint32_t>(s_maxbogusttl + d_now.tv_sec));
504✔
4838
        record.d_ttl = newval;
504✔
4839
      }
504✔
4840
      tCacheEntry->second.d_ttl_time = d_now.tv_sec;
187✔
4841
    }
187✔
4842

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

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

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

4880
      d_fromAuthIP = remoteIP;
26,798✔
4881

4882
      if (doCache) {
26,798✔
4883
        // Check if we are going to replace a non-auth (parent) NS recordset
4884
        if (isAA && tCacheEntry->first.type == QType::NS && s_save_parent_ns_set) {
26,796!
4885
          rememberParentSetIfNeeded(tCacheEntry->first.name, tCacheEntry->second.records, depth, prefix);
469✔
4886
        }
469✔
4887
        bool thisRRNeedsWildcardProof = false;
26,796✔
4888
        if (gatherWildcardProof) {
26,796✔
4889
          if (auto wcIt = wildcardCandidates.find(tCacheEntry->first.name); wcIt != wildcardCandidates.end() && wcIt->second) {
464✔
4890
            thisRRNeedsWildcardProof = true;
245✔
4891
          }
245✔
4892
        }
464✔
4893
        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);
26,796✔
4894

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

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

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

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

4918
            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!
4919
          }
217✔
4920
        }
217✔
4921
      }
26,796✔
4922
    }
26,798✔
4923

4924
    if (seenAuth.empty() && !tCacheEntry->second.signatures.empty()) {
29,357✔
4925
      seenAuth = getSigner(tCacheEntry->second.signatures);
5,222✔
4926
    }
5,222✔
4927

4928
    if (g_aggressiveNSECCache && (tCacheEntry->first.type == QType::NSEC || tCacheEntry->first.type == QType::NSEC3) && recordState == vState::Secure && !seenAuth.empty()) {
29,358!
4929
      // Good candidate for NSEC{,3} caching
4930
      g_aggressiveNSECCache->insertNSEC(seenAuth, tCacheEntry->first.name, tCacheEntry->second.records.at(0), tCacheEntry->second.signatures, tCacheEntry->first.type == QType::NSEC3, qname, qtype);
1,900✔
4931
    }
1,900✔
4932

4933
    if (tCacheEntry->first.place == DNSResourceRecord::ANSWER && ednsmask) {
29,357✔
4934
      d_wasVariable = true;
56✔
4935
    }
56✔
4936
  }
29,357✔
4937

4938
  if (gatherWildcardProof) {
10,928✔
4939
    if (auto wcIt = wildcardCandidates.find(qname); wcIt != wildcardCandidates.end() && !wcIt->second) {
241!
4940
      // 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
4941
      gatherWildcardProof = false;
2✔
4942
    }
2✔
4943
  }
241✔
4944

4945
  return RCode::NoError;
10,928✔
4946
}
10,928✔
4947

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

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

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

4992
dState SyncRes::getDenialValidationState(const NegCache::NegCacheEntry& negEntry, const dState expectedState, bool referralToUnsigned, const string& prefix)
4993
{
1,768✔
4994
  cspmap_t csp = harvestCSPFromNE(negEntry);
1,768✔
4995
  return getDenial(csp, negEntry.d_name, negEntry.d_qtype.getCode(), referralToUnsigned, expectedState == dState::NXQTYPE, d_validationContext, LogObject(prefix));
1,768✔
4996
}
1,768✔
4997

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

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

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

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

5041
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)
5042
{
10,910✔
5043
  bool done = false;
10,910✔
5044
  DNSName dnameTarget;
10,910✔
5045
  DNSName dnameOwner;
10,910✔
5046
  uint32_t dnameTTL = 0;
10,910✔
5047
  bool referralOnDS = false;
10,910✔
5048

5049
  for (auto& rec : lwr.d_records) {
57,170✔
5050
    if (rec.d_type == QType::OPT || rec.d_class != QClass::IN) {
57,170✔
5051
      continue;
8,633✔
5052
    }
8,633✔
5053

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

5063
    bool putInNegCache = true;
48,537✔
5064
    if (negCacheIndication && qtype == QType::DS && isForwardOrAuth(qname)) {
48,537✔
5065
      // #10189, a NXDOMAIN to a DS query for a forwarded or auth domain should not NXDOMAIN the whole domain
5066
      putInNegCache = false;
7✔
5067
    }
7✔
5068

5069
    if (negCacheIndication) {
48,537✔
5070
      LOG(prefix << qname << ": Got negative caching indication for name '" << qname << "' (accept=" << rec.d_name.isPartOf(auth) << "), newtarget='" << newtarget << "'" << endl);
124!
5071

5072
      rec.d_ttl = min(rec.d_ttl, s_maxnegttl);
124✔
5073
      // only add a SOA if we're not going anywhere after this
5074
      if (newtarget.empty()) {
124✔
5075
        ret.push_back(rec);
118✔
5076
      }
118✔
5077

5078
      NegCache::NegCacheEntry negEntry;
124✔
5079

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

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

5105
      if (vStateIsBogus(negEntry.d_validationState)) {
124✔
5106
        lowestTTL = min(lowestTTL, s_maxbogusttl);
7✔
5107
      }
7✔
5108

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

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

5186
      done = true;
8,457✔
5187
      rcode = RCode::NoError;
8,457✔
5188

5189
      if (needWildcardProof) {
8,457✔
5190
        checkWildcardProof(qname, qtype, rec, lwr, state, depth, prefix, wildcardLabelsCount);
225✔
5191
      }
225✔
5192

5193
      ret.push_back(rec);
8,457✔
5194
    }
8,457✔
5195
    else if ((rec.d_type == QType::RRSIG || rec.d_type == QType::NSEC || rec.d_type == QType::NSEC3) && rec.d_place == DNSResourceRecord::ANSWER) {
38,211✔
5196
      if (rec.d_type != QType::RRSIG || rec.d_name == qname) {
2,788!
5197
        ret.push_back(rec); // enjoy your DNSSEC
2,755✔
5198
      }
2,755✔
5199
      else if (rec.d_type == QType::RRSIG && qname.isPartOf(rec.d_name)) {
33!
5200
        auto rrsig = getRR<RRSIGRecordContent>(rec);
24✔
5201
        if (rrsig != nullptr && rrsig->d_type == QType::DNAME) {
24!
5202
          ret.push_back(rec);
24✔
5203
        }
24✔
5204
      }
24✔
5205
    }
2,788✔
5206
    else if (rec.d_place == DNSResourceRecord::AUTHORITY && rec.d_type == QType::NS && qname.isPartOf(rec.d_name)) {
35,423✔
5207
      if (moreSpecificThan(rec.d_name, auth)) {
11,334✔
5208
        newauth = rec.d_name;
10,510✔
5209
        LOG(prefix << qname << ": Got NS record '" << rec.d_name << "' -> '" << rec.getContent()->getZoneRepresentation() << "'" << endl);
10,510✔
5210

5211
        /* check if we have a referral from the parent zone to a child zone for a DS query, which is not right */
5212
        if (qtype == QType::DS && (newauth.isPartOf(qname) || qname == newauth)) {
10,510!
5213
          /* just got a referral from the parent zone when asking for a DS, looks like this server did not get the DNSSEC memo.. */
5214
          referralOnDS = true;
4✔
5215
        }
4✔
5216
        else {
10,506✔
5217
          realreferral = true;
10,506✔
5218
          if (auto content = getRR<NSRecordContent>(rec)) {
10,506!
5219
            nsset.insert(content->getNS());
10,506✔
5220
          }
10,506✔
5221
        }
10,506✔
5222
      }
10,510✔
5223
      else {
824✔
5224
        LOG(prefix << qname << ": Got upwards/level NS record '" << rec.d_name << "' -> '" << rec.getContent()->getZoneRepresentation() << "', had '" << auth << "'" << endl);
824!
5225
        if (auto content = getRR<NSRecordContent>(rec)) {
824!
5226
          nsset.insert(content->getNS());
824✔
5227
        }
824✔
5228
      }
824✔
5229
    }
11,334✔
5230
    else if (rec.d_place == DNSResourceRecord::AUTHORITY && rec.d_type == QType::DS && qname.isPartOf(rec.d_name)) {
24,089✔
5231
      LOG(prefix << qname << ": Got DS record '" << rec.d_name << "' -> '" << rec.getContent()->getZoneRepresentation() << "'" << endl);
1,261!
5232
    }
1,261✔
5233
    else if (realreferral && rec.d_place == DNSResourceRecord::AUTHORITY && (rec.d_type == QType::NSEC || rec.d_type == QType::NSEC3) && newauth.isPartOf(auth)) {
22,828!
5234
      /* we might have received a denial of the DS, let's check */
5235
      NegCache::NegCacheEntry negEntry;
2,359✔
5236
      uint32_t lowestTTL = rec.d_ttl;
2,359✔
5237
      harvestNXRecords(lwr.d_records, negEntry, d_now.tv_sec, &lowestTTL);
2,359✔
5238

5239
      if (!vStateIsBogus(state)) {
2,359!
5240
        auto recordState = getValidationStatus(newauth, !negEntry.authoritySOA.signatures.empty() || !negEntry.DNSSECRecords.signatures.empty(), true, depth, prefix);
2,359!
5241

5242
        if (recordState == vState::Secure) {
2,359✔
5243
          negEntry.d_auth = auth;
1,528✔
5244
          negEntry.d_name = newauth;
1,528✔
5245
          negEntry.d_qtype = QType::DS;
1,528✔
5246
          rec.d_ttl = min(s_maxnegttl, rec.d_ttl);
1,528✔
5247

5248
          dState denialState = getDenialValidationState(negEntry, dState::NXQTYPE, true, prefix);
1,528✔
5249

5250
          if (denialState == dState::NXQTYPE || denialState == dState::OPTOUT || denialState == dState::INSECURE) {
1,528!
5251
            negEntry.d_ttd = lowestTTL + d_now.tv_sec;
1,524✔
5252
            negEntry.d_orig_ttl = lowestTTL;
1,524✔
5253
            negEntry.d_validationState = vState::Secure;
1,524✔
5254
            if (denialState == dState::OPTOUT) {
1,524✔
5255
              negEntry.d_validationState = vState::Insecure;
1,442✔
5256
            }
1,442✔
5257
            LOG(prefix << qname << ": Got negative indication of DS record for '" << newauth << "'" << endl);
1,524!
5258

5259
            g_negCache->add(negEntry);
1,524✔
5260

5261
            /* 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
5262
               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
5263
               query. */
5264
            if (qtype == QType::DS && qname == newauth && (d_externalDSQuery.empty() || qname != d_externalDSQuery)) {
1,524!
5265
              /* we are actually done! */
5266
              negindic = true;
×
5267
              negIndicHasSignatures = !negEntry.authoritySOA.signatures.empty() || !negEntry.DNSSECRecords.signatures.empty();
×
5268
              nsset.clear();
×
5269
            }
×
5270
          }
1,524✔
5271
        }
1,528✔
5272
      }
2,359✔
5273
    }
2,359✔
5274
    else if (!done && rec.d_place == DNSResourceRecord::AUTHORITY && rec.d_type == QType::SOA && lwr.d_rcode == RCode::NoError && qname.isPartOf(rec.d_name)) {
20,469!
5275
      LOG(prefix << qname << ": Got negative caching indication for '" << qname << "|" << qtype << "'" << endl);
1,116!
5276

5277
      if (!newtarget.empty()) {
1,116✔
5278
        LOG(prefix << qname << ": Hang on! Got a redirect to '" << newtarget << "' already" << endl);
5!
5279
      }
5✔
5280
      else {
1,111✔
5281
        rec.d_ttl = min(s_maxnegttl, rec.d_ttl);
1,111✔
5282

5283
        NegCache::NegCacheEntry negEntry;
1,111✔
5284
        negEntry.d_auth = rec.d_name;
1,111✔
5285
        uint32_t lowestTTL = rec.d_ttl;
1,111✔
5286
        negEntry.d_name = qname;
1,111✔
5287
        negEntry.d_qtype = qtype;
1,111✔
5288
        harvestNXRecords(lwr.d_records, negEntry, d_now.tv_sec, &lowestTTL);
1,111✔
5289

5290
        if (vStateIsBogus(state)) {
1,111✔
5291
          negEntry.d_validationState = state;
7✔
5292
        }
7✔
5293
        else {
1,104✔
5294
          auto recordState = getValidationStatus(qname, !negEntry.authoritySOA.signatures.empty() || !negEntry.DNSSECRecords.signatures.empty(), qtype == QType::DS, depth, prefix);
1,104!
5295
          if (recordState == vState::Secure) {
1,104✔
5296
            dState denialState = getDenialValidationState(negEntry, dState::NXQTYPE, false, prefix);
178✔
5297
            updateDenialValidationState(qname, negEntry.d_validationState, negEntry.d_name, state, denialState, dState::NXQTYPE, qtype == QType::DS, depth, prefix);
178✔
5298
          }
178✔
5299
          else {
926✔
5300
            negEntry.d_validationState = recordState;
926✔
5301
            updateValidationState(qname, state, negEntry.d_validationState, prefix);
926✔
5302
          }
926✔
5303
        }
1,104✔
5304

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

5320
        ret.push_back(rec);
1,111✔
5321
        negindic = true;
1,111✔
5322
        negIndicHasSignatures = !negEntry.authoritySOA.signatures.empty() || !negEntry.DNSSECRecords.signatures.empty();
1,111!
5323
      }
1,111✔
5324
    }
1,116✔
5325
  }
48,537✔
5326

5327
  if (!dnameTarget.empty()) {
10,910✔
5328
    // Synthesize a CNAME
5329
    auto cnamerec = DNSRecord();
32✔
5330
    cnamerec.d_name = qname;
32✔
5331
    cnamerec.d_type = QType::CNAME;
32✔
5332
    cnamerec.d_ttl = dnameTTL;
32✔
5333
    cnamerec.setContent(std::make_shared<CNAMERecordContent>(CNAMERecordContent(newtarget)));
32✔
5334
    ret.push_back(std::move(cnamerec));
32✔
5335
  }
32✔
5336

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

5353
  return done;
10,910✔
5354
}
10,910✔
5355

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

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

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

5416
bool SyncRes::tryDoT(const DNSName& qname, const QType qtype, const DNSName& nsName, ComboAddress address, time_t now)
5417
{
×
5418
  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));
×
5419

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

5457
void SyncRes::ednsStats(boost::optional<Netmask>& ednsmask, const DNSName& qname, const string& prefix)
5458
{
11,256✔
5459
  if (!ednsmask) {
11,256✔
5460
    return;
11,198✔
5461
  }
11,198✔
5462
  s_ecsresponses++;
58✔
5463
  LOG(prefix << qname << ": Received EDNS Client Subnet Mask " << ednsmask->toString() << " on response" << endl);
58!
5464

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

5475
void SyncRes::updateQueryCounts(const string& prefix, const DNSName& qname, const ComboAddress& address, bool doTCP, bool doDoT)
5476
{
11,262✔
5477
  t_Counters.at(rec::Counter::outqueries)++;
11,262✔
5478
  d_outqueries++;
11,262✔
5479
  checkMaxQperQ(qname);
11,262✔
5480
  if (address.sin4.sin_family == AF_INET6) {
11,262✔
5481
    t_Counters.at(rec::Counter::ipv6queries)++;
781✔
5482
  }
781✔
5483
  if (doTCP) {
11,262✔
5484
    if (doDoT) {
25✔
5485
      LOG(prefix << qname << ": Using DoT with " << address.toStringWithPort() << endl);
9!
5486
      t_Counters.at(rec::Counter::dotoutqueries)++;
9✔
5487
      d_dotoutqueries++;
9✔
5488
    }
9✔
5489
    else {
16✔
5490
      LOG(prefix << qname << ": Using TCP with " << address.toStringWithPort() << endl);
16!
5491
      t_Counters.at(rec::Counter::tcpoutqueries)++;
16✔
5492
      d_tcpoutqueries++;
16✔
5493
    }
16✔
5494
  }
25✔
5495
}
11,262✔
5496

5497
void SyncRes::incTimeoutStats(const ComboAddress& remoteIP)
5498
{
323✔
5499
  d_timeouts++;
323✔
5500
  t_Counters.at(rec::Counter::outgoingtimeouts)++;
323✔
5501

5502
  if (remoteIP.sin4.sin_family == AF_INET) {
323✔
5503
    t_Counters.at(rec::Counter::outgoing4timeouts)++;
172✔
5504
  }
172✔
5505
  else {
151✔
5506
    t_Counters.at(rec::Counter::outgoing6timeouts)++;
151✔
5507
  }
151✔
5508

5509
  if (t_timeouts) {
323✔
5510
    t_timeouts->push_back(remoteIP);
7✔
5511
  }
7✔
5512
}
323✔
5513

5514
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)
5515
{
11,267✔
5516
  bool chained = false;
11,267✔
5517
  LWResult::Result resolveret = LWResult::Result::Success;
11,267✔
5518

5519
  if (s_maxtotusec != 0 && d_totUsec > s_maxtotusec) {
11,267!
5520
    if (s_addExtendedResolutionDNSErrors) {
2!
5521
      extendedError = EDNSExtendedError{static_cast<uint16_t>(EDNSExtendedError::code::NoReachableAuthority), "Timeout waiting for answer(s)"};
×
5522
    }
×
5523
    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✔
5524
  }
2✔
5525

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

5542
  /* preoutquery killed the query by setting dq.rcode to -3 */
5543
  if (preOutQueryRet == -3) {
11,265✔
5544
    throw ImmediateServFailException("Query killed by policy");
4✔
5545
  }
4✔
5546

5547
  d_totUsec += lwr.d_usec;
11,261✔
5548

5549
  if (resolveret == LWResult::Result::Spoofed) {
11,261!
5550
    spoofed = true;
×
5551
    return false;
×
5552
  }
×
5553

5554
  accountAuthLatency(lwr.d_usec, remoteIP.sin4.sin_family);
11,261✔
5555
  ++t_Counters.at(rec::RCode::auth).rcodeCounters.at(static_cast<uint8_t>(lwr.d_rcode));
11,261✔
5556

5557
  if (!dontThrottle) {
11,262✔
5558
    dontThrottle = shouldNotThrottle(&nsName, &remoteIP);
11,255✔
5559
  }
11,255✔
5560

5561
  if (resolveret != LWResult::Result::Success) {
11,261✔
5562
    /* Error while resolving */
5563
    if (resolveret == LWResult::Result::Timeout) {
376✔
5564
      LOG(prefix << qname << ": Timeout resolving after " << lwr.d_usec / 1000.0 << " ms " << (doTCP ? "over TCP" : "") << endl);
323!
5565
      incTimeoutStats(remoteIP);
323✔
5566
    }
323✔
5567
    else if (resolveret == LWResult::Result::OSLimitError) {
53✔
5568
      /* OS resource limit reached */
5569
      LOG(prefix << qname << ": Hit a local resource limit resolving" << (doTCP ? " over TCP" : "") << ", probable error: " << stringerror() << endl);
6!
5570
      t_Counters.at(rec::Counter::resourceLimits)++;
6✔
5571
    }
6✔
5572
    else if (resolveret == LWResult::Result::ChainLimitError) {
47!
5573
      /* Chain resource limit reached */
5574
      LOG(prefix << qname << ": Hit a chain limit resolving" << (doTCP ? " over TCP" : ""));
×
5575
      t_Counters.at(rec::Counter::chainLimits)++;
×
5576
    }
×
5577
    else {
47✔
5578
      /* LWResult::Result::PermanentError */
5579
      t_Counters.at(rec::Counter::unreachables)++;
47✔
5580
      d_unreachables++;
47✔
5581
      // XXX questionable use of errno
5582
      LOG(prefix << qname << ": Error resolving from " << remoteIP.toString() << (doTCP ? " over TCP" : "") << ", possible error: " << stringerror() << endl);
47!
5583
    }
47✔
5584

5585
    // don't account for resource limits, they are our own fault
5586
    // And don't throttle when the IP address is on the dontThrottleNetmasks list or the name is part of dontThrottleNames
5587
    if (!LWResult::isLimitError(resolveret) && !chained && !dontThrottle) {
376!
5588
      uint32_t responseUsec = 1000000; // 1 sec for non-timeout cases
370✔
5589
      // Use the actual time if we saw a timeout
5590
      if (resolveret == LWResult::Result::Timeout) {
370✔
5591
        responseUsec = lwr.d_usec;
323✔
5592
      }
323✔
5593

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

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

5617
    return false;
376✔
5618
  }
376✔
5619

5620
  if (!lwr.d_validpacket) {
10,885✔
5621
    LOG(prefix << qname << ": " << nsName << " (" << remoteIP.toString() << ") returned a packet we could not parse over " << (doTCP ? "TCP" : "UDP") << ", trying sibling IP or NS" << endl);
8!
5622
    if (!chained && !dontThrottle) {
8!
5623

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

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

5666
  /* this server sent a valid answer, mark it backup up if it was down */
5667
  if (s_serverdownmaxfails > 0) {
10,812✔
5668
    s_fails.lock()->clear(remoteIP);
10,804✔
5669
  }
10,804✔
5670
  // Clear all throttles for this IP, both general and specific throttles for qname-qtype
5671
  unThrottle(remoteIP, qname, qtype);
10,812✔
5672

5673
  if (lwr.d_tcbit) {
10,812✔
5674
    truncated = true;
18✔
5675

5676
    if (doTCP) {
18✔
5677
      LOG(prefix << qname << ": Truncated bit set, over TCP?" << endl);
2!
5678
      if (!dontThrottle) {
2!
5679
        /* let's treat that as a ServFail answer from this server */
5680
        doThrottle(d_now.tv_sec, remoteIP, qname, qtype, 60, 3, Throttle::Reason::TCPTruncate);
2✔
5681
      }
2✔
5682
      return false;
2✔
5683
    }
2✔
5684
    LOG(prefix << qname << ": Truncated bit set, over UDP" << endl);
16!
5685

5686
    return true;
16✔
5687
  }
18✔
5688

5689
  return true;
10,794✔
5690
}
10,812✔
5691

5692
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)
5693
{
1,247✔
5694
  if (newtarget == qname) {
1,247✔
5695
    LOG(prefix << qname << ": Status=got a CNAME referral to self, returning SERVFAIL" << endl);
3!
5696
    ret.clear();
3✔
5697
    rcode = RCode::ServFail;
3✔
5698
    return;
3✔
5699
  }
3✔
5700
  if (newtarget.isPartOf(qname)) {
1,244✔
5701
    // a.b.c. CNAME x.a.b.c will go to great depths with QM on
5702
    LOG(prefix << qname << ": Status=got a CNAME referral to child, disabling QM" << endl);
25!
5703
    setQNameMinimization(false);
25✔
5704
  }
25✔
5705

5706
  if (!d_followCNAME) {
1,244✔
5707
    rcode = RCode::NoError;
97✔
5708
    return;
97✔
5709
  }
97✔
5710

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

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

5728
    if (d_doDNSSEC) {
4!
5729
      addNXNSECS(ret, recordsFromAnswer);
4✔
5730
    }
4✔
5731

5732
    rcode = RCode::NoError;
4✔
5733
    return;
4✔
5734
  }
4✔
5735

5736
  LOG(prefix << qname << ": Status=got a CNAME referral, starting over with " << newtarget << endl);
1,139✔
5737

5738
  set<GetBestNSAnswer> beenthere;
1,139✔
5739
  Context cnameContext;
1,139✔
5740
  rcode = doResolve(newtarget, qtype, ret, depth + 1, beenthere, cnameContext);
1,139✔
5741
  LOG(prefix << qname << ": Updating validation state for response to " << qname << " from " << state << " with the state from the CNAME quest: " << cnameContext.state << endl);
1,139✔
5742
  updateValidationState(qname, state, cnameContext.state, prefix);
1,139✔
5743
}
1,139✔
5744

5745
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)
5746
{
10,928✔
5747
  if (s_minimumTTL != 0) {
10,928✔
5748
    for (auto& rec : lwr.d_records) {
51,682✔
5749
      rec.d_ttl = max(rec.d_ttl, s_minimumTTL);
51,682✔
5750
    }
51,682✔
5751
  }
8,747✔
5752

5753
  /* if the answer is ECS-specific, a minimum TTL is set for this kind of answers
5754
     and it's higher than the global minimum TTL */
5755
  if (ednsmask && s_minimumECSTTL > 0 && (s_minimumTTL == 0 || s_minimumECSTTL > s_minimumTTL)) {
10,928!
5756
    for (auto& rec : lwr.d_records) {
2✔
5757
      if (rec.d_place == DNSResourceRecord::ANSWER) {
2!
5758
        rec.d_ttl = max(rec.d_ttl, s_minimumECSTTL);
2✔
5759
      }
2✔
5760
    }
2✔
5761
  }
2✔
5762

5763
  bool needWildcardProof = false;
10,928✔
5764
  bool gatherWildcardProof = false;
10,928✔
5765
  unsigned int wildcardLabelsCount = 0;
10,928✔
5766
  *rcode = updateCacheFromRecords(depth, prefix, lwr, qname, qtype, auth, wasForwarded, ednsmask, state, needWildcardProof, gatherWildcardProof, wildcardLabelsCount, sendRDQuery, remoteIP);
10,928✔
5767
  if (*rcode != RCode::NoError) {
10,928!
5768
    return true;
×
5769
  }
×
5770

5771
  LOG(prefix << qname << ": Determining status after receiving this packet" << endl);
10,928✔
5772

5773
  set<DNSName> nsset;
10,928✔
5774
  bool realreferral = false;
10,928✔
5775
  bool negindic = false;
10,928✔
5776
  bool negIndicHasSignatures = false;
10,928✔
5777
  DNSName newauth;
10,928✔
5778
  DNSName newtarget;
10,928✔
5779

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

5782
  if (done) {
10,928✔
5783
    LOG(prefix << qname << ": Status=got results, this level of recursion done" << endl);
5,191✔
5784
    LOG(prefix << qname << ": Validation status is " << state << endl);
5,191✔
5785
    return true;
5,191✔
5786
  }
5,191✔
5787

5788
  if (!newtarget.empty()) {
5,737✔
5789
    handleNewTarget(prefix, qname, newtarget, qtype.getCode(), ret, *rcode, depth, lwr.d_records, state);
1,241✔
5790
    return true;
1,241✔
5791
  }
1,241✔
5792

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

5796
    auto tempState = getValidationStatus(qname, negIndicHasSignatures, qtype == QType::DS, depth, prefix);
130✔
5797
    if (tempState == vState::Secure && (lwr.d_aabit || sendRDQuery) && !negindic) {
130!
5798
      LOG(prefix << qname << ": NXDOMAIN without a negative indication (missing SOA in authority) in a DNSSEC secure zone, going Bogus" << endl);
4!
5799
      updateValidationState(qname, state, vState::BogusMissingNegativeIndication, prefix);
4✔
5800
    }
4✔
5801
    else {
126✔
5802
      /* we might not have validated any record, because we did get a NXDOMAIN without any SOA
5803
         from an insecure zone, for example */
5804
      updateValidationState(qname, state, tempState, prefix);
126✔
5805
    }
126✔
5806

5807
    if (d_doDNSSEC) {
130✔
5808
      addNXNSECS(ret, lwr.d_records);
100✔
5809
    }
100✔
5810

5811
    *rcode = RCode::NXDomain;
130✔
5812
    return true;
130✔
5813
  }
130✔
5814

5815
  if (nsset.empty() && lwr.d_rcode == 0 && (negindic || lwr.d_aabit || sendRDQuery)) {
4,366!
5816
    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,158!
5817

5818
    auto tempState = getValidationStatus(qname, negIndicHasSignatures, qtype == QType::DS, depth, prefix);
1,158✔
5819
    if (tempState == vState::Secure && (lwr.d_aabit || sendRDQuery) && !negindic) {
1,158✔
5820
      LOG(prefix << qname << ": NODATA without a negative indication (missing SOA in authority) in a DNSSEC secure zone, going Bogus" << endl);
11!
5821
      updateValidationState(qname, state, vState::BogusMissingNegativeIndication, prefix);
11✔
5822
    }
11✔
5823
    else {
1,147✔
5824
      /* we might not have validated any record, because we did get a NODATA without any SOA
5825
         from an insecure zone, for example */
5826
      updateValidationState(qname, state, tempState, prefix);
1,147✔
5827
    }
1,147✔
5828

5829
    if (d_doDNSSEC) {
1,158✔
5830
      addNXNSECS(ret, lwr.d_records);
1,088✔
5831
    }
1,088✔
5832

5833
    *rcode = RCode::NoError;
1,158✔
5834
    return true;
1,158✔
5835
  }
1,158✔
5836

5837
  if (realreferral) {
3,208✔
5838
    LOG(prefix << qname << ": Status=did not resolve, got " << (unsigned int)nsset.size() << " NS, ");
3,136✔
5839

5840
    nameservers.clear();
3,136✔
5841
    for (auto const& nameserver : nsset) {
10,442✔
5842
      if (d_wantsRPZ && !d_appliedPolicy.wasHit()) {
10,442!
5843
        bool match = dfe.getProcessingPolicy(nameserver, d_discardedPolicies, d_appliedPolicy);
10,440✔
5844
        if (match) {
10,440✔
5845
          mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());
2✔
5846
          if (d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) { // client query needs an RPZ response
2!
5847
            if (d_pdl && d_pdl->policyHitEventFilter(d_requestor, qname, qtype, d_queryReceivedOverTCP, d_appliedPolicy, d_policyTags, d_discardedPolicies)) {
2!
5848
              /* reset to no match */
5849
              d_appliedPolicy = DNSFilterEngine::Policy();
×
5850
            }
×
5851
            else {
2✔
5852
              LOG("however " << nameserver << " was blocked by RPZ policy '" << d_appliedPolicy.getName() << "'" << endl);
2!
5853
              throw PolicyHitException();
2✔
5854
            }
2✔
5855
          }
2✔
5856
        }
2✔
5857
      }
10,440✔
5858
      nameservers.insert({nameserver, {{}, false}});
10,440✔
5859
    }
10,440✔
5860
    LOG("looping to them" << endl);
3,134✔
5861
    *gotNewServers = true;
3,134✔
5862
    auth = std::move(newauth);
3,134✔
5863

5864
    return false;
3,134✔
5865
  }
3,136✔
5866

5867
  return false;
72✔
5868
}
3,208✔
5869

5870
bool SyncRes::doDoTtoAuth(const DNSName& nameServer)
5871
{
11,252✔
5872
  return g_DoTToAuthNames.getLocal()->check(nameServer);
11,252✔
5873
}
11,252✔
5874

5875
/** returns:
5876
 *  -1 in case of no results
5877
 *  rcode otherwise
5878
 */
5879
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
5880
int SyncRes::doResolveAt(NsSet& nameservers, DNSName auth, bool flawedNSSet, const DNSName& qname, const QType qtype,
5881
                         vector<DNSRecord>& ret,
5882
                         unsigned int depth, const string& prefix, set<GetBestNSAnswer>& beenthere, Context& context, StopAtDelegation* stopAtDelegation,
5883
                         map<DNSName, vector<ComboAddress>>* fallBack)
5884
{
9,609✔
5885
  auto luaconfsLocal = g_luaconfs.getLocal();
9,609✔
5886

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

5889
  if (nameserversBlockedByRPZ(luaconfsLocal->dfe, nameservers)) {
9,609✔
5890
    /* RPZ hit */
5891
    if (d_pdl && d_pdl->policyHitEventFilter(d_requestor, qname, qtype, d_queryReceivedOverTCP, d_appliedPolicy, d_policyTags, d_discardedPolicies)) {
2!
5892
      /* reset to no match */
5893
      d_appliedPolicy = DNSFilterEngine::Policy();
×
5894
    }
×
5895
    else {
2✔
5896
      throw PolicyHitException();
2✔
5897
    }
2✔
5898
  }
2✔
5899

5900
  LOG(endl);
9,607✔
5901

5902
  unsigned int addressQueriesForNS = 0;
9,607✔
5903
  for (;;) { // we may get more specific nameservers
11,206✔
5904
    auto rnameservers = shuffleInSpeedOrder(qname, nameservers, prefix);
11,206✔
5905

5906
    // We allow s_maxnsaddressqperq (default 10) queries with empty responses when resolving NS names.
5907
    // If a zone publishes many (more than s_maxnsaddressqperq) NS records, we allow less.
5908
    // This is to "punish" zones that publish many non-resolving NS names.
5909
    // We always allow 5 NS name resolving attempts with empty results.
5910
    unsigned int nsLimit = s_maxnsaddressqperq;
11,206✔
5911
    if (rnameservers.size() > nsLimit) {
11,206✔
5912
      int newLimit = static_cast<int>(nsLimit - (rnameservers.size() - nsLimit));
2,275✔
5913
      nsLimit = std::max(5, newLimit);
2,275✔
5914
    }
2,275✔
5915

5916
    for (auto tns = rnameservers.cbegin();; ++tns) {
11,658✔
5917
      if (addressQueriesForNS >= nsLimit) {
11,658✔
5918
        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✔
5919
      }
2✔
5920
      if (tns == rnameservers.cend()) {
11,656✔
5921
        LOG(prefix << qname << ": Failed to resolve via any of the " << (unsigned int)rnameservers.size() << " offered NS at level '" << auth << "'" << endl);
222✔
5922
        if (s_addExtendedResolutionDNSErrors) {
222✔
5923
          context.extendedError = EDNSExtendedError{static_cast<uint16_t>(EDNSExtendedError::code::NoReachableAuthority), "delegation " + auth.toLogString()};
35✔
5924
        }
35✔
5925
        if (!auth.isRoot() && flawedNSSet) {
222✔
5926
          LOG(prefix << qname << ": Ageing nameservers for level '" << auth << "', next query might succeed" << endl);
65!
5927
          if (g_recCache->doAgeCache(d_now.tv_sec, auth, QType::NS, 10)) {
65✔
5928
            t_Counters.at(rec::Counter::nsSetInvalidations)++;
18✔
5929
          }
18✔
5930
        }
65✔
5931
        return -1;
222✔
5932
      }
222✔
5933

5934
      bool cacheOnly = false;
11,434✔
5935
      // this line needs to identify the 'self-resolving' behaviour
5936
      if (qname == tns->first && (qtype.getCode() == QType::A || qtype.getCode() == QType::AAAA)) {
11,434!
5937
        /* we might have a glue entry in cache so let's try this NS
5938
           but only if we have enough in the cache to know how to reach it */
5939
        LOG(prefix << qname << ": Using NS to resolve itself, but only using what we have in cache (" << (1 + tns - rnameservers.cbegin()) << "/" << rnameservers.size() << ")" << endl);
73!
5940
        cacheOnly = true;
73✔
5941
      }
73✔
5942

5943
      typedef vector<ComboAddress> remoteIPs_t;
11,434✔
5944
      remoteIPs_t remoteIPs;
11,434✔
5945
      remoteIPs_t::iterator remoteIP;
11,434✔
5946
      bool pierceDontQuery = false;
11,434✔
5947
      bool sendRDQuery = false;
11,434✔
5948
      boost::optional<Netmask> ednsmask;
11,434✔
5949
      LWResult lwr;
11,434✔
5950
      const bool wasForwarded = tns->first.empty() && (!nameservers[tns->first].first.empty());
11,434✔
5951
      int rcode = RCode::NoError;
11,434✔
5952
      bool gotNewServers = false;
11,434✔
5953

5954
      if (tns->first.empty() && !wasForwarded) {
11,434✔
5955
        static ComboAddress const s_oobRemote("255.255.255.255");
139✔
5956
        LOG(prefix << qname << ": Domain is out-of-band" << endl);
139!
5957
        /* setting state to indeterminate since validation is disabled for local auth zone,
5958
           and Insecure would be misleading. */
5959
        context.state = vState::Indeterminate;
139✔
5960
        d_wasOutOfBand = doOOBResolve(qname, qtype, lwr.d_records, depth, prefix, lwr.d_rcode);
139✔
5961
        lwr.d_tcbit = false;
139✔
5962
        lwr.d_aabit = true;
139✔
5963

5964
        /* we have received an answer, are we done ? */
5965
        bool done = processAnswer(depth, prefix, lwr, qname, qtype, auth, false, ednsmask, sendRDQuery, nameservers, ret, luaconfsLocal->dfe, &gotNewServers, &rcode, context.state, s_oobRemote);
139✔
5966
        if (done) {
139✔
5967
          return rcode;
135✔
5968
        }
135✔
5969
        if (gotNewServers) {
4!
5970
          if (stopAtDelegation != nullptr && *stopAtDelegation == Stop) {
4!
5971
            *stopAtDelegation = Stopped;
×
5972
            return rcode;
×
5973
          }
×
5974
          break;
4✔
5975
        }
4✔
5976
      }
4✔
5977
      else {
11,295✔
5978
        if (fallBack != nullptr) {
11,295✔
5979
          if (auto iter = fallBack->find(tns->first); iter != fallBack->end()) {
2!
5980
            remoteIPs = iter->second;
2✔
5981
          }
2✔
5982
        }
2✔
5983
        if (remoteIPs.empty()) {
11,295✔
5984
          remoteIPs = retrieveAddressesForNS(prefix, qname, tns, depth, beenthere, rnameservers, nameservers, sendRDQuery, pierceDontQuery, flawedNSSet, cacheOnly, addressQueriesForNS);
11,293✔
5985
        }
11,293✔
5986

5987
        if (remoteIPs.empty()) {
11,295✔
5988
          LOG(prefix << qname << ": Failed to get IP for NS " << tns->first << ", trying next if available" << endl);
136!
5989
          flawedNSSet = true;
136✔
5990
          continue;
136✔
5991
        }
136✔
5992
        bool hitPolicy{false};
11,159✔
5993
        LOG(prefix << qname << ": Resolved '" << auth << "' NS " << tns->first << " to: ");
11,159✔
5994
        for (remoteIP = remoteIPs.begin(); remoteIP != remoteIPs.end(); ++remoteIP) {
23,684✔
5995
          if (remoteIP != remoteIPs.begin()) {
12,525✔
5996
            LOG(", ");
1,456✔
5997
          }
1,456✔
5998
          LOG(remoteIP->toString());
12,525✔
5999
          if (nameserverIPBlockedByRPZ(luaconfsLocal->dfe, *remoteIP)) {
12,525✔
6000
            hitPolicy = true;
4✔
6001
          }
4✔
6002
        }
12,525✔
6003
        LOG(endl);
11,159✔
6004
        if (hitPolicy) { // implies d_wantsRPZ
11,159✔
6005
          /* RPZ hit */
6006
          if (d_pdl && d_pdl->policyHitEventFilter(d_requestor, qname, qtype, d_queryReceivedOverTCP, d_appliedPolicy, d_policyTags, d_discardedPolicies)) {
4!
6007
            /* reset to no match */
6008
            d_appliedPolicy = DNSFilterEngine::Policy();
×
6009
          }
×
6010
          else {
4✔
6011
            throw PolicyHitException();
4✔
6012
          }
4✔
6013
        }
4✔
6014

6015
        for (remoteIP = remoteIPs.begin(); remoteIP != remoteIPs.end(); ++remoteIP) {
11,704✔
6016
          LOG(prefix << qname << ": Trying IP " << remoteIP->toStringWithPort() << ", asking '" << qname << "|" << qtype << "'" << endl);
11,258✔
6017

6018
          if (throttledOrBlocked(prefix, *remoteIP, qname, qtype, pierceDontQuery)) {
11,258✔
6019
            // As d_throttledqueries might be increased, check the max-qperq condition
6020
            checkMaxQperQ(qname);
6✔
6021
            continue;
6✔
6022
          }
6✔
6023

6024
          bool truncated = false;
11,252✔
6025
          bool spoofed = false;
11,252✔
6026
          bool gotAnswer = false;
11,252✔
6027
          bool doDoT = false;
11,252✔
6028

6029
          if (doDoTtoAuth(tns->first)) {
11,252✔
6030
            remoteIP->setPort(853);
3✔
6031
            doDoT = true;
3✔
6032
          }
3✔
6033
          if (SyncRes::s_dot_to_port_853 && remoteIP->getPort() == 853) {
11,252✔
6034
            doDoT = true;
9✔
6035
          }
9✔
6036
          bool forceTCP = doDoT;
11,252✔
6037

6038
          if (!doDoT && s_max_busy_dot_probes > 0) {
11,252!
6039
            submitTryDotTask(*remoteIP, auth, tns->first, d_now.tv_sec);
×
6040
          }
×
6041
          if (!forceTCP) {
11,252✔
6042
            gotAnswer = doResolveAtThisIP(prefix, qname, qtype, lwr, ednsmask, auth, sendRDQuery, wasForwarded,
11,242✔
6043
                                          tns->first, *remoteIP, false, false, truncated, spoofed, context.extendedError);
11,242✔
6044
          }
11,242✔
6045
          if (forceTCP || (spoofed || (gotAnswer && truncated))) {
11,252!
6046
            /* retry, over TCP this time */
6047
            gotAnswer = doResolveAtThisIP(prefix, qname, qtype, lwr, ednsmask, auth, sendRDQuery, wasForwarded,
25✔
6048
                                          tns->first, *remoteIP, true, doDoT, truncated, spoofed, context.extendedError);
25✔
6049
          }
25✔
6050

6051
          if (!gotAnswer) {
11,252✔
6052
            if (doDoT && s_max_busy_dot_probes > 0) {
451!
6053
              // This is quite pessimistic...
6054
              updateDoTStatus(*remoteIP, DoTStatus::Bad, d_now.tv_sec + dotFailWait);
×
6055
            }
×
6056
            continue;
451✔
6057
          }
451✔
6058

6059
          LOG(prefix << qname << ": Got " << (unsigned int)lwr.d_records.size() << " answers from " << tns->first << " (" << remoteIP->toString() << "), rcode=" << lwr.d_rcode << " (" << RCode::to_s(lwr.d_rcode) << "), aa=" << lwr.d_aabit << ", in " << lwr.d_usec / 1000 << "ms" << endl);
10,801✔
6060

6061
          if (doDoT && s_max_busy_dot_probes > 0) {
10,801!
6062
            updateDoTStatus(*remoteIP, DoTStatus::Good, d_now.tv_sec + dotSuccessWait);
×
6063
          }
×
6064
          /*  // for you IPv6 fanatics :-)
6065
              if(remoteIP->sin4.sin_family==AF_INET6)
6066
              lwr.d_usec/=3;
6067
          */
6068
          //        cout<<"ms: "<<lwr.d_usec/1000.0<<", "<<g_avgLatency/1000.0<<'\n';
6069

6070
          s_nsSpeeds.lock()->find_or_enter(tns->first.empty() ? DNSName(remoteIP->toStringWithPort()) : tns->first, d_now).submit(*remoteIP, static_cast<int>(lwr.d_usec), d_now);
10,801✔
6071

6072
          /* we have received an answer, are we done ? */
6073
          bool done = processAnswer(depth, prefix, lwr, qname, qtype, auth, wasForwarded, ednsmask, sendRDQuery, nameservers, ret, luaconfsLocal->dfe, &gotNewServers, &rcode, context.state, *remoteIP);
10,801✔
6074
          if (done) {
10,801✔
6075
            return rcode;
7,579✔
6076
          }
7,579✔
6077
          if (gotNewServers) {
3,222✔
6078
            if (stopAtDelegation != nullptr && *stopAtDelegation == Stop) {
3,130!
6079
              *stopAtDelegation = Stopped;
1,535✔
6080
              return rcode;
1,535✔
6081
            }
1,535✔
6082
            break;
1,595✔
6083
          }
3,130✔
6084
          /* was lame */
6085
          if (!shouldNotThrottle(&tns->first, &*remoteIP)) {
92✔
6086
            doThrottle(d_now.tv_sec, *remoteIP, qname, qtype, 60, 100, Throttle::Reason::Lame);
52✔
6087
          }
52✔
6088
        }
92✔
6089

6090
        if (gotNewServers) {
2,041✔
6091
          break;
1,595✔
6092
        }
1,595✔
6093

6094
        if (remoteIP == remoteIPs.cend()) { // we tried all IP addresses, none worked
446✔
6095
          continue;
316✔
6096
        }
316✔
6097
      }
446✔
6098
    }
11,434✔
6099
  }
11,206✔
6100
  return -1;
130✔
6101
}
9,607✔
6102

6103
void SyncRes::setQuerySource(const Netmask& netmask)
6104
{
×
6105
  if (!netmask.empty()) {
×
6106
    d_outgoingECSNetwork = netmask;
×
6107
  }
×
6108
  else {
×
6109
    d_outgoingECSNetwork = boost::none;
×
6110
  }
×
6111
}
×
6112

6113
void SyncRes::setQuerySource(const ComboAddress& requestor, const boost::optional<const EDNSSubnetOpts&>& incomingECS)
6114
{
2,416✔
6115
  d_requestor = requestor;
2,416✔
6116

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

6158
boost::optional<Netmask> SyncRes::getEDNSSubnetMask(const DNSName& name, const ComboAddress& rem)
6159
{
11,262✔
6160
  if (d_outgoingECSNetwork && (s_ednsdomains.check(name) || s_ednsremotesubnets.match(rem))) {
11,262✔
6161
    return d_outgoingECSNetwork;
70✔
6162
  }
70✔
6163
  return boost::none;
11,192✔
6164
}
11,262✔
6165

6166
void SyncRes::parseEDNSSubnetAllowlist(const std::string& alist)
6167
{
306✔
6168
  vector<string> parts;
306✔
6169
  stringtok(parts, alist, ",; ");
306✔
6170
  for (const auto& allow : parts) {
306✔
6171
    try {
15✔
6172
      s_ednsremotesubnets.addMask(Netmask(allow));
15✔
6173
    }
15✔
6174
    catch (...) {
15✔
6175
      s_ednsdomains.add(DNSName(allow));
12✔
6176
    }
12✔
6177
  }
15✔
6178
}
306✔
6179

6180
void SyncRes::parseEDNSSubnetAddFor(const std::string& subnetlist)
6181
{
153✔
6182
  vector<string> parts;
153✔
6183
  stringtok(parts, subnetlist, ",; ");
153✔
6184
  for (const auto& allow : parts) {
1,523✔
6185
    s_ednslocalsubnets.addMask(allow);
1,523✔
6186
  }
1,523✔
6187
}
153✔
6188

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

6195
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)
6196
{
24✔
6197
  auto log = slog->withValues("qname", Logging::Loggable(qname), "qtype", Logging::Loggable(qtype));
24✔
6198

6199
  struct timeval now
24✔
6200
  {
24✔
6201
  };
24✔
6202
  gettimeofday(&now, nullptr);
24✔
6203

6204
  SyncRes resolver(now);
24✔
6205
  resolver.setQNameMinimization(qnamemin);
24✔
6206
  if (pdl) {
24!
6207
    resolver.setLuaEngine(pdl);
24✔
6208
  }
24✔
6209

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

6241
  return res;
24✔
6242
}
24✔
6243

6244
int SyncRes::getRootNS(struct timeval now, asyncresolve_t asyncCallback, unsigned int depth, Logr::log_t log)
6245
{
227✔
6246
  if (::arg()["hint-file"] == "no-refresh") {
227!
6247
    return 0;
×
6248
  }
×
6249
  SyncRes resolver(now);
227✔
6250
  resolver.d_prefix = "[getRootNS]";
227✔
6251
  resolver.setDoEDNS0(true);
227✔
6252
  resolver.setUpdatingRootNS();
227✔
6253
  resolver.setDoDNSSEC(g_dnssecmode != DNSSECMode::Off);
227✔
6254
  resolver.setDNSSECValidationRequested(g_dnssecmode != DNSSECMode::Off && g_dnssecmode != DNSSECMode::ProcessNoValidate);
227✔
6255
  resolver.setAsyncCallback(std::move(asyncCallback));
227✔
6256
  resolver.setRefreshAlmostExpired(true);
227✔
6257

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

6293
  if (res == 0) {
227✔
6294
    SLOG(g_log << Logger::Debug << "Refreshed . records" << endl,
196✔
6295
         log->info(Logr::Debug, "Refreshed . records"));
196✔
6296
  }
196✔
6297
  else {
31✔
6298
    SLOG(g_log << Logger::Warning << "Failed to update root NS records, RCODE=" << res << endl,
31✔
6299
         log->info(Logr::Warning, msg, "rcode", Logging::Loggable(res)));
31✔
6300
  }
31✔
6301
  return res;
227✔
6302
}
227✔
6303

6304
bool SyncRes::answerIsNOData(uint16_t requestedType, int rcode, const std::vector<DNSRecord>& records)
6305
{
347✔
6306
  if (rcode != RCode::NoError) {
347✔
6307
    return false;
43✔
6308
  }
43✔
6309

6310
  // NOLINTNEXTLINE(readability-use-anyofallof)
6311
  for (const auto& rec : records) {
365✔
6312
    if (rec.d_place == DNSResourceRecord::ANSWER && rec.d_type == requestedType) {
365✔
6313
      /* we have a record, of the right type, in the right section */
6314
      return false;
247✔
6315
    }
247✔
6316
  }
365✔
6317
  return true;
57✔
6318
#if 0
6319
  // This code should be equivalent to the code above, clang-tidy prefers any_of()
6320
  // I have doubts if that is easier to read
6321
  return !std::any_of(records.begin(), records.end(), [=](const DNSRecord& rec) {
6322
    return rec.d_place == DNSResourceRecord::ANSWER && rec.d_type == requestedType;
6323
  });
6324
#endif
6325
}
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