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

PowerDNS / pdns / 14103313974

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

Pull #15362

github

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

41494 of 100080 branches covered (41.46%)

Branch coverage included in aggregate %.

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

56 existing lines in 11 files now uncovered.

128265 of 167624 relevant lines covered (76.52%)

3990104.75 hits per line

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

88.85
/pdns/recursordist/test-syncres_cc2.cc
1
#ifndef BOOST_TEST_DYN_LINK
2
#define BOOST_TEST_DYN_LINK
3
#endif
4

5
#include <boost/test/unit_test.hpp>
6

7
#include "test-syncres_cc.hh"
8
#include "taskqueue.hh"
9
#include "rec-taskqueue.hh"
10

11
BOOST_AUTO_TEST_SUITE(syncres_cc2)
12

13
static void do_test_referral_depth(bool limited)
14
{
4✔
15
  std::unique_ptr<SyncRes> sr;
4✔
16
  initSR(sr);
4✔
17

18
  primeHints();
4✔
19

20
  size_t queries = 0;
4✔
21
  const DNSName target("www.powerdns.com.");
4✔
22

23
  sr->setAsyncCallback([&](const ComboAddress& address, const DNSName& domain, int /* type */, bool /* doTCP */, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval* /* now */, boost::optional<Netmask>& /* srcmask */, const ResolveContext& /* context */, LWResult* res, bool* /* chained */) {
28✔
24
    queries++;
28✔
25

26
    if (isRootServer(address)) {
28✔
27
      setLWResult(res, 0, false, false, true);
18✔
28

29
      if (domain == DNSName("www.powerdns.com.")) {
18✔
30
        addRecordToLW(res, domain, QType::NS, "ns.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
4✔
31
      }
4✔
32
      else if (domain == DNSName("ns.powerdns.com.")) {
14✔
33
        addRecordToLW(res, domain, QType::NS, "ns1.powerdns.org.", DNSResourceRecord::AUTHORITY, 172800);
4✔
34
      }
4✔
35
      else if (domain == DNSName("ns1.powerdns.org.")) {
10✔
36
        addRecordToLW(res, domain, QType::NS, "ns2.powerdns.org.", DNSResourceRecord::AUTHORITY, 172800);
4✔
37
      }
4✔
38
      else if (domain == DNSName("ns2.powerdns.org.")) {
6✔
39
        addRecordToLW(res, domain, QType::NS, "ns3.powerdns.org.", DNSResourceRecord::AUTHORITY, 172800);
4✔
40
      }
4✔
41
      else if (domain == DNSName("ns3.powerdns.org.")) {
2!
42
        addRecordToLW(res, domain, QType::NS, "ns4.powerdns.org.", DNSResourceRecord::AUTHORITY, 172800);
2✔
43
        addRecordToLW(res, "ns4.powerdns.org.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
2✔
44
      }
2✔
45

46
      return LWResult::Result::Success;
18✔
47
    }
18✔
48
    if (address == ComboAddress("192.0.2.1:53")) {
10!
49
      setLWResult(res, 0, true, false, false);
10✔
50
      if (domain == DNSName("www.powerdns.com.")) {
10✔
51
        addRecordToLW(res, domain, QType::A, "192.0.2.2");
2✔
52
      }
2✔
53
      else {
8✔
54
        addRecordToLW(res, domain, QType::A, "192.0.2.1");
8✔
55
      }
8✔
56
      return LWResult::Result::Success;
10✔
57
    }
10✔
58

59
    return LWResult::Result::Timeout;
×
60
  });
10✔
61

62
  if (limited) {
4✔
63
    /* Set the maximum depth low */
64
    SyncRes::s_maxdepth = 3;
2✔
65
    try {
2✔
66
      vector<DNSRecord> ret;
2✔
67
      sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
68
      BOOST_CHECK(false);
2✔
69
    }
2✔
70
    catch (const ImmediateServFailException& e) {
2✔
71
      BOOST_CHECK(e.reason.find("max-recursion-depth") != string::npos);
2✔
72
    }
2✔
73
  }
2✔
74
  else {
2✔
75
    // Check if the setup with high limit is OK.
76
    SyncRes::s_maxdepth = 50;
2✔
77
    try {
2✔
78
      vector<DNSRecord> ret;
2✔
79
      int rcode = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
80
      BOOST_CHECK_EQUAL(rcode, RCode::NoError);
2✔
81
      BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2✔
82
      BOOST_CHECK_EQUAL(ret[0].d_name, target);
2✔
83
      BOOST_REQUIRE(ret[0].d_type == QType::A);
2✔
84
      BOOST_CHECK(getRR<ARecordContent>(ret[0])->getCA() == ComboAddress("192.0.2.2"));
2✔
85
    }
2✔
86
    catch (const ImmediateServFailException& e) {
2✔
87
      BOOST_CHECK(false);
×
88
    }
×
89
  }
2✔
90
}
4✔
91

92
BOOST_AUTO_TEST_CASE(test_referral_depth)
93
{
2✔
94
  // Test with limit
95
  do_test_referral_depth(true);
2✔
96
}
2✔
97

98
BOOST_AUTO_TEST_CASE(test_referral_depth_ok)
99
{
2✔
100
  // Test with default limit
101
  do_test_referral_depth(false);
2✔
102
}
2✔
103

104
BOOST_AUTO_TEST_CASE(test_glueless_referral_loop)
105
{
2✔
106
  std::unique_ptr<SyncRes> sr;
2✔
107
  initSR(sr);
2✔
108

109
  // We only do v4, this avoids "beenthere" non-deterministic behaviour. If we do both v4 and v6, there are multiple IPs
110
  // per (root) nameserver, and the "beenthere" loop detection is influenced by the particular address family selected.
111
  // To see the non-deterministic behaviour, uncomment the line below (you'll be seeing around 21-24 queries).
112
  // See #9565
113
  SyncRes::s_doIPv6 = false;
2✔
114

115
  primeHints();
2✔
116

117
  const DNSName target1("powerdns.com.");
2✔
118
  const DNSName target2("powerdns.org.");
2✔
119
  size_t queriesToNS = 0;
2✔
120

121
  sr->setAsyncCallback([&](const ComboAddress& address, const DNSName& domain, int /* type */, bool /* doTCP */, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval* /* now */, boost::optional<Netmask>& /* srcmask */, const ResolveContext& /* context */, LWResult* res, bool* /* chained */) {
32✔
122
    queriesToNS++;
32✔
123

124
    if (isRootServer(address)) {
32✔
125
      setLWResult(res, 0, false, false, true);
12✔
126

127
      if (domain.isPartOf(DNSName("com."))) {
12✔
128
        addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
6✔
129
      }
6✔
130
      else if (domain.isPartOf(DNSName("org."))) {
6!
131
        addRecordToLW(res, "org.", QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
6✔
132
      }
6✔
133
      else {
×
134
        setLWResult(res, RCode::NXDomain, false, false, true);
×
135
        return LWResult::Result::Success;
×
136
      }
×
137

138
      addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
12✔
139
      addRecordToLW(res, "a.gtld-servers.net.", QType::AAAA, "2001:DB8::1", DNSResourceRecord::ADDITIONAL, 3600);
12✔
140
      return LWResult::Result::Success;
12✔
141
    }
12✔
142
    if (address == ComboAddress("192.0.2.1:53") || address == ComboAddress("[2001:DB8::1]:53")) {
20!
143
      if (domain.isPartOf(target1)) {
20✔
144
        setLWResult(res, 0, false, false, true);
10✔
145
        addRecordToLW(res, "powerdns.com.", QType::NS, "ns1.powerdns.org.", DNSResourceRecord::AUTHORITY, 172800);
10✔
146
        addRecordToLW(res, "powerdns.com.", QType::NS, "ns2.powerdns.org.", DNSResourceRecord::AUTHORITY, 172800);
10✔
147
        return LWResult::Result::Success;
10✔
148
      }
10✔
149
      if (domain.isPartOf(target2)) {
10!
150
        setLWResult(res, 0, false, false, true);
10✔
151
        addRecordToLW(res, "powerdns.org.", QType::NS, "ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
10✔
152
        addRecordToLW(res, "powerdns.org.", QType::NS, "ns2.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
10✔
153
        return LWResult::Result::Success;
10✔
154
      }
10✔
155
      setLWResult(res, RCode::NXDomain, false, false, true);
×
156
      return LWResult::Result::Success;
×
157
    }
10✔
158
    else {
×
159
      return LWResult::Result::Timeout;
×
160
    }
×
161
  });
20✔
162

163
  vector<DNSRecord> ret;
2✔
164
  int res = sr->beginResolve(target1, QType(QType::A), QClass::IN, ret);
2✔
165
  BOOST_CHECK_EQUAL(res, RCode::ServFail);
2✔
166
  BOOST_REQUIRE_EQUAL(ret.size(), 0U);
2✔
167
  BOOST_CHECK_EQUAL(queriesToNS, 16U);
2✔
168
}
2✔
169

170
BOOST_AUTO_TEST_CASE(test_glueless_referral_loop_with_nonresolving)
171
{
2✔
172
  std::unique_ptr<SyncRes> sr;
2✔
173
  initSR(sr);
2✔
174

175
  // We only do v4, this avoids "beenthere" non-deterministic behaviour. If we do both v4 and v6, there are multiple IPs
176
  // per (root) nameserver, and the "beenthere" loop detection is influenced by the particular address family selected.
177
  // To see the non-deterministic behaviour, uncomment the line below (you'll be seeing around 21-24 queries).
178
  // See #9565
179
  SyncRes::s_doIPv6 = false;
2✔
180

181
  primeHints();
2✔
182

183
  const DNSName target1("powerdns.com.");
2✔
184
  const DNSName target2("powerdns.org.");
2✔
185
  size_t queriesToNS = 0;
2✔
186

187
  sr->setAsyncCallback([&](const ComboAddress& address, const DNSName& domain, int /* type */, bool /* doTCP */, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval* /* now */, boost::optional<Netmask>& /* srcmask */, const ResolveContext& /* context */, LWResult* res, bool* /* chained */) {
30✔
188
    queriesToNS++;
30✔
189

190
    if (isRootServer(address)) {
30✔
191
      setLWResult(res, 0, false, false, true);
11✔
192

193
      if (domain.isPartOf(DNSName("com."))) {
11✔
194
        addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
6✔
195
      }
6✔
196
      else if (domain.isPartOf(DNSName("org."))) {
5!
197
        addRecordToLW(res, "org.", QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
5✔
198
      }
5✔
199
      else {
×
200
        setLWResult(res, RCode::NXDomain, false, false, true);
×
201
        return LWResult::Result::Success;
×
202
      }
×
203

204
      addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
11✔
205
      addRecordToLW(res, "a.gtld-servers.net.", QType::AAAA, "2001:DB8::1", DNSResourceRecord::ADDITIONAL, 3600);
11✔
206
      return LWResult::Result::Success;
11✔
207
    }
11✔
208
    if (address == ComboAddress("192.0.2.1:53") || address == ComboAddress("[2001:DB8::1]:53")) {
19!
209
      if (domain.isPartOf(target1)) {
19✔
210
        setLWResult(res, 0, false, false, true);
10✔
211
        addRecordToLW(res, "powerdns.com.", QType::NS, "ns1.powerdns.org.", DNSResourceRecord::AUTHORITY, 172800);
10✔
212
        addRecordToLW(res, "powerdns.com.", QType::NS, "ns2.powerdns.org.", DNSResourceRecord::AUTHORITY, 172800);
10✔
213
        return LWResult::Result::Success;
10✔
214
      }
10✔
215
      if (domain.isPartOf(target2)) {
9!
216
        setLWResult(res, 0, false, false, true);
9✔
217
        addRecordToLW(res, "powerdns.org.", QType::NS, "ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
9✔
218
        addRecordToLW(res, "powerdns.org.", QType::NS, "ns2.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
9✔
219
        return LWResult::Result::Success;
9✔
220
      }
9✔
221
      setLWResult(res, RCode::NXDomain, false, false, true);
×
222
      return LWResult::Result::Success;
×
223
    }
9✔
224
    else {
×
225
      return LWResult::Result::Timeout;
×
226
    }
×
227
  });
19✔
228

229
  SyncRes::s_nonresolvingnsmaxfails = 1;
2✔
230
  SyncRes::s_nonresolvingnsthrottletime = 60;
2✔
231

232
  vector<DNSRecord> ret;
2✔
233
  int res = sr->beginResolve(target1, QType(QType::A), QClass::IN, ret);
2✔
234
  BOOST_CHECK_EQUAL(res, RCode::ServFail);
2✔
235
  BOOST_REQUIRE_EQUAL(ret.size(), 0U);
2✔
236
  // queriesToNS count varies due to shuffling
237
  // But all NS from above should be recorded as failing
238
  BOOST_CHECK_EQUAL(SyncRes::getNonResolvingNSSize(), 4U);
2✔
239
}
2✔
240

241
BOOST_AUTO_TEST_CASE(test_glueless_referral_with_non_resolving)
242
{
2✔
243
  std::unique_ptr<SyncRes> sr;
2✔
244
  initSR(sr);
2✔
245

246
  primeHints();
2✔
247

248
  const DNSName target("powerdns.com.");
2✔
249

250
  size_t queryCount = 0;
2✔
251

252
  sr->setAsyncCallback([&](const ComboAddress& address, const DNSName& domain, int /* type */, bool /* doTCP */, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval* /* now */, boost::optional<Netmask>& /* srcmask */, const ResolveContext& /* context */, LWResult* res, bool* /* chained */) {
26✔
253
    if (isRootServer(address)) {
26✔
254
      setLWResult(res, 0, false, false, true);
4✔
255

256
      if (domain.isPartOf(DNSName("com."))) {
4✔
257
        addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
2✔
258
      }
2✔
259
      else if (domain.isPartOf(DNSName("org."))) {
2!
260
        addRecordToLW(res, "org.", QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
2✔
261
      }
2✔
262
      else {
×
263
        setLWResult(res, RCode::NXDomain, false, false, true);
×
264
        return LWResult::Result::Success;
×
265
      }
×
266

267
      addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
4✔
268
      addRecordToLW(res, "a.gtld-servers.net.", QType::AAAA, "2001:DB8::1", DNSResourceRecord::ADDITIONAL, 3600);
4✔
269
      return LWResult::Result::Success;
4✔
270
    }
4✔
271
    if (address == ComboAddress("192.0.2.1:53") || address == ComboAddress("[2001:DB8::1]:53")) {
22✔
272
      if (domain == target) {
20✔
273
        setLWResult(res, 0, false, false, true);
2✔
274
        addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns1.powerdns.org.", DNSResourceRecord::AUTHORITY, 172800);
2✔
275
        addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns2.powerdns.org.", DNSResourceRecord::AUTHORITY, 172800);
2✔
276
        return LWResult::Result::Success;
2✔
277
      }
2✔
278
      if (domain == DNSName("pdns-public-ns1.powerdns.org.")) {
18✔
279
        queryCount++;
10✔
280
        setLWResult(res, 0, true, false, true);
10✔
281
        if (queryCount > 8) {
10✔
282
          addRecordToLW(res, "pdns-public-ns1.powerdns.org.", QType::A, "192.0.2.2");
2✔
283
          addRecordToLW(res, "pdns-public-ns1.powerdns.org.", QType::AAAA, "2001:DB8::2");
2✔
284
        }
2✔
285
        return LWResult::Result::Success;
10✔
286
      }
10✔
287
      else if (domain == DNSName("pdns-public-ns2.powerdns.org.")) {
8!
288
        queryCount++;
8✔
289
        setLWResult(res, 0, true, false, true);
8✔
290
        if (queryCount > 8) {
8!
UNCOV
291
          addRecordToLW(res, "pdns-public-ns2.powerdns.org.", QType::A, "192.0.2.3");
×
UNCOV
292
          addRecordToLW(res, "pdns-public-ns2.powerdns.org.", QType::AAAA, "2001:DB8::3");
×
UNCOV
293
        }
×
294
        return LWResult::Result::Success;
8✔
295
      }
8✔
296

297
      setLWResult(res, RCode::NXDomain, false, false, true);
×
298
      return LWResult::Result::Success;
×
299
    }
18✔
300
    if (address == ComboAddress("192.0.2.2:53") || address == ComboAddress("192.0.2.3:53") || address == ComboAddress("[2001:DB8::2]:53") || address == ComboAddress("[2001:DB8::3]:53")) {
2!
301
      setLWResult(res, 0, true, false, true);
2✔
302
      addRecordToLW(res, target, QType::A, "192.0.2.4");
2✔
303
      return LWResult::Result::Success;
2✔
304
    }
2✔
305
    return LWResult::Result::Timeout;
×
306
  });
2✔
307

308
  SyncRes::s_nonresolvingnsmaxfails = 10;
2✔
309
  SyncRes::s_nonresolvingnsthrottletime = 60;
2✔
310

311
  vector<DNSRecord> ret;
2✔
312
  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
313
  BOOST_CHECK_EQUAL(res, RCode::ServFail);
2✔
314
  BOOST_REQUIRE_EQUAL(ret.size(), 0U);
2✔
315
  BOOST_CHECK_EQUAL(SyncRes::getNonResolvingNSSize(), 2U);
2✔
316

317
  // Again, should not change anything
318
  res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
319
  BOOST_CHECK_EQUAL(res, RCode::ServFail);
2✔
320
  BOOST_REQUIRE_EQUAL(ret.size(), 0U);
2✔
321
  //BOOST_CHECK(ret[0].d_type == QType::A);
322
  //BOOST_CHECK_EQUAL(ret[0].d_name, target);
323

324
  BOOST_CHECK_EQUAL(SyncRes::getNonResolvingNSSize(), 2U);
2✔
325

326
  // Again, but now things should start working because of the queryCounter getting high enough
327
  // and one entry remains in the non-resolving cache
328
  res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
329
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
330
  BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2✔
331
  BOOST_CHECK(ret[0].d_type == QType::A);
2✔
332
  BOOST_CHECK_EQUAL(ret[0].d_name, target);
2✔
333
  BOOST_CHECK_EQUAL(SyncRes::getNonResolvingNSSize(), 1U);
2✔
334
}
2✔
335

336
BOOST_AUTO_TEST_CASE(test_cname_qperq)
337
{
2✔
338
  std::unique_ptr<SyncRes> sr;
2✔
339
  initSR(sr);
2✔
340

341
  primeHints();
2✔
342

343
  size_t queries = 0;
2✔
344
  const DNSName target("cname.powerdns.com.");
2✔
345

346
  sr->setAsyncCallback([&](const ComboAddress& address, const DNSName& domain, int /* type */, bool /* doTCP */, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval* /* now */, boost::optional<Netmask>& /* srcmask */, const ResolveContext& /* context */, LWResult* res, bool* /* chained */) {
10✔
347
    queries++;
10✔
348

349
    if (isRootServer(address)) {
10✔
350

351
      setLWResult(res, 0, false, false, true);
6✔
352
      addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
6✔
353
      addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
6✔
354
      return LWResult::Result::Success;
6✔
355
    }
6✔
356
    if (address == ComboAddress("192.0.2.1:53")) {
4!
357

358
      setLWResult(res, 0, true, false, false);
4✔
359
      addRecordToLW(res, domain, QType::CNAME, std::to_string(queries) + "-cname.powerdns.com");
4✔
360
      return LWResult::Result::Success;
4✔
361
    }
4✔
362

363
    return LWResult::Result::Timeout;
×
364
  });
4✔
365

366
  /* Set the maximum number of questions very low */
367
  SyncRes::s_maxqperq = 5;
2✔
368

369
  try {
2✔
370
    vector<DNSRecord> ret;
2✔
371
    sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
372
    BOOST_CHECK(false);
2✔
373
  }
2✔
374
  catch (const ImmediateServFailException& e) {
2✔
375
    BOOST_CHECK_EQUAL(queries, SyncRes::s_maxqperq);
2✔
376
  }
2✔
377
}
2✔
378

379
BOOST_AUTO_TEST_CASE(test_throttled_server)
380
{
2✔
381
  std::unique_ptr<SyncRes> sr;
2✔
382
  initSR(sr);
2✔
383

384
  primeHints();
2✔
385

386
  const DNSName target("throttled.powerdns.com.");
2✔
387
  const ComboAddress ns("192.0.2.1:53");
2✔
388
  size_t queriesToNS = 0;
2✔
389

390
  sr->setAsyncCallback([&](const ComboAddress& address, const DNSName& domain, int /* type */, bool /* doTCP */, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval* /* now */, boost::optional<Netmask>& /* srcmask */, const ResolveContext& /* context */, LWResult* res, bool* /* chained */) {
2✔
391
    if (isRootServer(address)) {
2!
392

393
      setLWResult(res, 0, false, false, true);
2✔
394
      addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
2✔
395
      addRecordToLW(res, "a.gtld-servers.net.", QType::A, ns.toString(), DNSResourceRecord::ADDITIONAL, 3600);
2✔
396
      return LWResult::Result::Success;
2✔
397
    }
2✔
398
    if (address == ns) {
×
399

400
      queriesToNS++;
×
401

402
      setLWResult(res, 0, true, false, false);
×
403
      addRecordToLW(res, domain, QType::A, "192.0.2.2");
×
404

405
      return LWResult::Result::Success;
×
406
    }
×
407

408
    return LWResult::Result::Timeout;
×
409
  });
×
410

411
  /* mark ns as down */
412
  time_t now = sr->getNow().tv_sec;
2✔
413
  SyncRes::doThrottle(now, ns, SyncRes::s_serverdownthrottletime, 10000, SyncRes::ThrottleReason::Timeout);
2✔
414

415
  vector<DNSRecord> ret;
2✔
416
  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
417
  BOOST_CHECK_EQUAL(res, RCode::ServFail);
2✔
418
  BOOST_CHECK_EQUAL(ret.size(), 0U);
2✔
419
  /* we should not have sent any queries to ns */
420
  BOOST_CHECK_EQUAL(queriesToNS, 0U);
2✔
421
}
2✔
422

423
BOOST_AUTO_TEST_CASE(test_throttled_server_count)
424
{
2✔
425
  std::unique_ptr<SyncRes> sr;
2✔
426
  initSR(sr);
2✔
427

428
  primeHints();
2✔
429

430
  const ComboAddress ns("192.0.2.1:53");
2✔
431

432
  const size_t blocks = 10;
2✔
433
  /* mark ns as down for 'blocks' queries */
434
  time_t now = sr->getNow().tv_sec;
2✔
435
  SyncRes::doThrottle(now, ns, SyncRes::s_serverdownthrottletime, blocks, SyncRes::ThrottleReason::Timeout);
2✔
436

437
  for (size_t idx = 0; idx < blocks; idx++) {
22✔
438
    BOOST_CHECK(SyncRes::isThrottled(now, ns));
20✔
439
  }
20✔
440

441
  /* we have been throttled 'blocks' times, we should not be throttled anymore */
442
  BOOST_CHECK(!SyncRes::isThrottled(now, ns));
2✔
443
}
2✔
444

445
BOOST_AUTO_TEST_CASE(test_throttled_server_time)
446
{
2✔
447
  std::unique_ptr<SyncRes> sr;
2✔
448
  initSR(sr);
2✔
449

450
  primeHints();
2✔
451

452
  const ComboAddress ns("192.0.2.1:53");
2✔
453

454
  const size_t seconds = 1;
2✔
455
  /* mark ns as down for 'seconds' seconds */
456
  time_t now = sr->getNow().tv_sec;
2✔
457
  SyncRes::doThrottle(now, ns, seconds, 10000, SyncRes::ThrottleReason::Timeout);
2✔
458

459
  BOOST_CHECK(SyncRes::isThrottled(now, ns));
2✔
460

461
  /* we should not be throttled anymore */
462
  BOOST_CHECK(!SyncRes::isThrottled(now + 2, ns));
2✔
463
}
2✔
464

465
BOOST_AUTO_TEST_CASE(test_dont_query_server)
466
{
2✔
467
  std::unique_ptr<SyncRes> sr;
2✔
468
  initSR(sr);
2✔
469

470
  primeHints();
2✔
471

472
  const DNSName target("throttled.powerdns.com.");
2✔
473
  const ComboAddress ns("192.0.2.1:53");
2✔
474
  size_t queriesToNS = 0;
2✔
475

476
  sr->setAsyncCallback([&](const ComboAddress& address, const DNSName& domain, int /* type */, bool /* doTCP */, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval* /* now */, boost::optional<Netmask>& /* srcmask */, const ResolveContext& /* context */, LWResult* res, bool* /* chained */) {
2✔
477
    if (isRootServer(address)) {
2!
478

479
      setLWResult(res, 0, false, false, true);
2✔
480
      addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
2✔
481
      addRecordToLW(res, "a.gtld-servers.net.", QType::A, ns.toString(), DNSResourceRecord::ADDITIONAL, 3600);
2✔
482
      return LWResult::Result::Success;
2✔
483
    }
2✔
484
    if (address == ns) {
×
485

486
      queriesToNS++;
×
487

488
      setLWResult(res, 0, true, false, false);
×
489
      addRecordToLW(res, domain, QType::A, "192.0.2.2");
×
490

491
      return LWResult::Result::Success;
×
492
    }
×
493

494
    return LWResult::Result::Timeout;
×
495
  });
×
496

497
  /* prevent querying this NS */
498
  SyncRes::addDontQuery(Netmask(ns));
2✔
499

500
  vector<DNSRecord> ret;
2✔
501
  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
502
  BOOST_CHECK_EQUAL(res, RCode::ServFail);
2✔
503
  BOOST_CHECK_EQUAL(ret.size(), 0U);
2✔
504
  /* we should not have sent any queries to ns */
505
  BOOST_CHECK_EQUAL(queriesToNS, 0U);
2✔
506
}
2✔
507

508
BOOST_AUTO_TEST_CASE(test_root_nx_trust)
509
{
2✔
510
  std::unique_ptr<SyncRes> sr;
2✔
511
  initSR(sr);
2✔
512

513
  primeHints();
2✔
514

515
  const DNSName target1("powerdns.com.");
2✔
516
  const DNSName target2("notpowerdns.com.");
2✔
517
  const ComboAddress ns("192.0.2.1:53");
2✔
518
  size_t queriesCount = 0;
2✔
519

520
  sr->setAsyncCallback([&](const ComboAddress& address, const DNSName& domain, int /* type */, bool /* doTCP */, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval* /* now */, boost::optional<Netmask>& /* srcmask */, const ResolveContext& /* context */, LWResult* res, bool* /* chained */) {
2✔
521
    queriesCount++;
2✔
522

523
    if (isRootServer(address)) {
2!
524

525
      if (domain == target1) {
2!
526
        setLWResult(res, RCode::NXDomain, true, false, true);
2✔
527
        addRecordToLW(res, ".", QType::SOA, "a.root-servers.net. nstld.verisign-grs.com. 2017032800 1800 900 604800 86400", DNSResourceRecord::AUTHORITY, 86400);
2✔
528
      }
2✔
529
      else {
×
530
        setLWResult(res, 0, true, false, true);
×
531
        addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
×
532
        addRecordToLW(res, "a.gtld-servers.net.", QType::A, ns.toString(), DNSResourceRecord::ADDITIONAL, 3600);
×
533
      }
×
534

535
      return LWResult::Result::Success;
2✔
536
    }
2✔
537
    if (address == ns) {
×
538

539
      setLWResult(res, 0, true, false, false);
×
540
      addRecordToLW(res, domain, QType::A, "192.0.2.2");
×
541

542
      return LWResult::Result::Success;
×
543
    }
×
544

545
    return LWResult::Result::Timeout;
×
546
  });
×
547

548
  SyncRes::s_maxnegttl = 3600;
2✔
549

550
  vector<DNSRecord> ret;
2✔
551
  int res = sr->beginResolve(target1, QType(QType::A), QClass::IN, ret);
2✔
552
  BOOST_CHECK_EQUAL(res, RCode::NXDomain);
2✔
553
  BOOST_CHECK_EQUAL(ret.size(), 1U);
2✔
554
  /* one for target1 and one for the entire TLD */
555
  BOOST_CHECK_EQUAL(g_negCache->size(), 2U);
2✔
556

557
  ret.clear();
2✔
558
  res = sr->beginResolve(target2, QType(QType::A), QClass::IN, ret);
2✔
559
  BOOST_CHECK_EQUAL(res, RCode::NXDomain);
2✔
560
  BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2✔
561
  BOOST_CHECK_LE(ret[0].d_ttl, SyncRes::s_maxnegttl);
2✔
562
  /* one for target1 and one for the entire TLD */
563
  BOOST_CHECK_EQUAL(g_negCache->size(), 2U);
2✔
564

565
  /* we should have sent only one query */
566
  BOOST_CHECK_EQUAL(queriesCount, 1U);
2✔
567
}
2✔
568

569
BOOST_AUTO_TEST_CASE(test_root_nx_trust_specific)
570
{
2✔
571
  std::unique_ptr<SyncRes> sr;
2✔
572
  initSR();
2✔
573
  initSR(sr, true, false);
2✔
574

575
  primeHints();
2✔
576

577
  const DNSName target1("powerdns.com.");
2✔
578
  const DNSName target2("notpowerdns.com.");
2✔
579
  const ComboAddress ns("192.0.2.1:53");
2✔
580
  size_t queriesCount = 0;
2✔
581

582
  /* This time the root denies target1 with a "com." SOA instead of a "." one.
583
     We should add target1 to the negcache, but not "com.". */
584

585
  sr->setAsyncCallback([&](const ComboAddress& address, const DNSName& domain, int /* type */, bool /* doTCP */, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval* /* now */, boost::optional<Netmask>& /* srcmask */, const ResolveContext& /* context */, LWResult* res, bool* /* chained */) {
6✔
586
    queriesCount++;
6✔
587

588
    if (isRootServer(address)) {
6✔
589

590
      if (domain == target1) {
4✔
591
        setLWResult(res, RCode::NXDomain, true, false, true);
2✔
592
        addRecordToLW(res, "com.", QType::SOA, "a.root-servers.net. nstld.verisign-grs.com. 2017032800 1800 900 604800 86400", DNSResourceRecord::AUTHORITY, 86400);
2✔
593
      }
2✔
594
      else {
2✔
595
        setLWResult(res, 0, true, false, true);
2✔
596
        addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
2✔
597
        addRecordToLW(res, "a.gtld-servers.net.", QType::A, ns.toString(), DNSResourceRecord::ADDITIONAL, 3600);
2✔
598
      }
2✔
599

600
      return LWResult::Result::Success;
4✔
601
    }
4✔
602
    if (address == ns) {
2!
603

604
      setLWResult(res, 0, true, false, false);
2✔
605
      addRecordToLW(res, domain, QType::A, "192.0.2.2");
2✔
606

607
      return LWResult::Result::Success;
2✔
608
    }
2✔
609

610
    return LWResult::Result::Timeout;
×
611
  });
2✔
612

613
  vector<DNSRecord> ret;
2✔
614
  int res = sr->beginResolve(target1, QType(QType::A), QClass::IN, ret);
2✔
615
  BOOST_CHECK_EQUAL(res, RCode::NXDomain);
2✔
616
  BOOST_CHECK_EQUAL(ret.size(), 1U);
2✔
617

618
  /* even with root-nx-trust on and a NX answer from the root,
619
     we should not have cached the entire TLD this time. */
620
  BOOST_CHECK_EQUAL(g_negCache->size(), 1U);
2✔
621

622
  ret.clear();
2✔
623
  res = sr->beginResolve(target2, QType(QType::A), QClass::IN, ret);
2✔
624
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
625
  BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2✔
626
  BOOST_CHECK_EQUAL(ret[0].d_name, target2);
2✔
627
  BOOST_REQUIRE(ret[0].d_type == QType::A);
2✔
628
  BOOST_CHECK(getRR<ARecordContent>(ret[0])->getCA() == ComboAddress("192.0.2.2"));
2✔
629

630
  BOOST_CHECK_EQUAL(g_negCache->size(), 1U);
2✔
631

632
  BOOST_CHECK_EQUAL(queriesCount, 3U);
2✔
633
}
2✔
634

635
BOOST_AUTO_TEST_CASE(test_root_nx_dont_trust)
636
{
2✔
637
  std::unique_ptr<SyncRes> sr;
2✔
638
  initSR(sr);
2✔
639

640
  primeHints();
2✔
641

642
  const DNSName target1("powerdns.com.");
2✔
643
  const DNSName target2("notpowerdns.com.");
2✔
644
  const ComboAddress ns("192.0.2.1:53");
2✔
645
  size_t queriesCount = 0;
2✔
646

647
  sr->setAsyncCallback([&](const ComboAddress& address, const DNSName& domain, int /* type */, bool /* doTCP */, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval* /* now */, boost::optional<Netmask>& /* srcmask */, const ResolveContext& /* context */, LWResult* res, bool* /* chained */) {
6✔
648
    queriesCount++;
6✔
649

650
    if (isRootServer(address)) {
6✔
651

652
      if (domain == target1) {
4✔
653
        setLWResult(res, RCode::NXDomain, true, false, true);
2✔
654
        addRecordToLW(res, ".", QType::SOA, "a.root-servers.net. nstld.verisign-grs.com. 2017032800 1800 900 604800 86400", DNSResourceRecord::AUTHORITY, 86400);
2✔
655
      }
2✔
656
      else {
2✔
657
        setLWResult(res, 0, true, false, true);
2✔
658
        addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
2✔
659
        addRecordToLW(res, "a.gtld-servers.net.", QType::A, ns.toString(), DNSResourceRecord::ADDITIONAL, 3600);
2✔
660
      }
2✔
661

662
      return LWResult::Result::Success;
4✔
663
    }
4✔
664
    if (address == ns) {
2!
665

666
      setLWResult(res, 0, true, false, false);
2✔
667
      addRecordToLW(res, domain, QType::A, "192.0.2.2");
2✔
668

669
      return LWResult::Result::Success;
2✔
670
    }
2✔
671

672
    return LWResult::Result::Timeout;
×
673
  });
2✔
674

675
  SyncRes::s_rootNXTrust = false;
2✔
676

677
  vector<DNSRecord> ret;
2✔
678
  int res = sr->beginResolve(target1, QType(QType::A), QClass::IN, ret);
2✔
679
  BOOST_CHECK_EQUAL(res, RCode::NXDomain);
2✔
680
  BOOST_CHECK_EQUAL(ret.size(), 1U);
2✔
681
  /* one for target1 */
682
  BOOST_CHECK_EQUAL(g_negCache->size(), 1U);
2✔
683

684
  ret.clear();
2✔
685
  res = sr->beginResolve(target2, QType(QType::A), QClass::IN, ret);
2✔
686
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
687
  BOOST_CHECK_EQUAL(ret.size(), 1U);
2✔
688
  /* one for target1 */
689
  BOOST_CHECK_EQUAL(g_negCache->size(), 1U);
2✔
690

691
  /* we should have sent three queries */
692
  BOOST_CHECK_EQUAL(queriesCount, 3U);
2✔
693
}
2✔
694

695
BOOST_AUTO_TEST_CASE(test_rfc8020_nothing_underneath)
696
{
2✔
697
  std::unique_ptr<SyncRes> sr;
2✔
698
  initSR(sr);
2✔
699
  SyncRes::s_hardenNXD = SyncRes::HardenNXD::Yes;
2✔
700

701
  primeHints();
2✔
702

703
  const DNSName target1("www.powerdns.com."); // will be denied
2✔
704
  const DNSName target2("foo.www.powerdns.com.");
2✔
705
  const DNSName target3("bar.www.powerdns.com.");
2✔
706
  const DNSName target4("quux.bar.www.powerdns.com.");
2✔
707
  const ComboAddress ns("192.0.2.1:53");
2✔
708
  size_t queriesCount = 0;
2✔
709

710
  sr->setAsyncCallback([&](const ComboAddress& address, const DNSName& /* domain */, int /* type */, bool /* doTCP */, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval* /* now */, boost::optional<Netmask>& /* srcmask */, const ResolveContext& /* context */, LWResult* res, bool* /* chained */) {
10✔
711
    queriesCount++;
10✔
712

713
    if (isRootServer(address)) {
10✔
714
      setLWResult(res, 0, false, false, true);
2✔
715
      addRecordToLW(res, "powerdns.com.", QType::NS, "ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
2✔
716
      addRecordToLW(res, "ns1.powerdns.com.", QType::A, ns.toString(), DNSResourceRecord::ADDITIONAL, 3600);
2✔
717
      return LWResult::Result::Success;
2✔
718
    }
2✔
719
    if (address == ns) {
8!
720
      setLWResult(res, RCode::NXDomain, true, false, false);
8✔
721
      addRecordToLW(res, "powerdns.com.", QType::SOA, "ns1.powerdns.com. hostmaster.powerdns.com. 2017032800 1800 900 604800 86400", DNSResourceRecord::AUTHORITY, 86400);
8✔
722
      return LWResult::Result::Success;
8✔
723
    }
8✔
724
    return LWResult::Result::Timeout;
×
725
  });
8✔
726

727
  vector<DNSRecord> ret;
2✔
728
  int res = sr->beginResolve(target1, QType(QType::A), QClass::IN, ret);
2✔
729
  BOOST_CHECK_EQUAL(res, RCode::NXDomain);
2✔
730
  BOOST_CHECK_EQUAL(ret.size(), 1U);
2✔
731
  BOOST_CHECK_EQUAL(queriesCount, 2U);
2✔
732
  BOOST_CHECK_EQUAL(g_negCache->size(), 1U);
2✔
733

734
  ret.clear();
2✔
735
  res = sr->beginResolve(target2, QType(QType::A), QClass::IN, ret);
2✔
736
  BOOST_CHECK_EQUAL(res, RCode::NXDomain);
2✔
737
  BOOST_CHECK_EQUAL(ret.size(), 1U);
2✔
738
  BOOST_CHECK_EQUAL(queriesCount, 2U);
2✔
739
  BOOST_CHECK_EQUAL(g_negCache->size(), 1U);
2✔
740

741
  ret.clear();
2✔
742
  res = sr->beginResolve(target3, QType(QType::A), QClass::IN, ret);
2✔
743
  BOOST_CHECK_EQUAL(res, RCode::NXDomain);
2✔
744
  BOOST_CHECK_EQUAL(ret.size(), 1U);
2✔
745
  BOOST_CHECK_EQUAL(queriesCount, 2U);
2✔
746
  BOOST_CHECK_EQUAL(g_negCache->size(), 1U);
2✔
747

748
  ret.clear();
2✔
749
  res = sr->beginResolve(target4, QType(QType::A), QClass::IN, ret);
2✔
750
  BOOST_CHECK_EQUAL(res, RCode::NXDomain);
2✔
751
  BOOST_CHECK_EQUAL(ret.size(), 1U);
2✔
752
  BOOST_CHECK_EQUAL(queriesCount, 2U);
2✔
753
  BOOST_CHECK_EQUAL(g_negCache->size(), 1U);
2✔
754

755
  // Now test without RFC 8020 to see the cache and query count grow
756
  SyncRes::s_hardenNXD = SyncRes::HardenNXD::No;
2✔
757

758
  // Already cached
759
  ret.clear();
2✔
760
  res = sr->beginResolve(target1, QType(QType::A), QClass::IN, ret);
2✔
761
  BOOST_CHECK_EQUAL(res, RCode::NXDomain);
2✔
762
  BOOST_CHECK_EQUAL(ret.size(), 1U);
2✔
763
  BOOST_CHECK_EQUAL(queriesCount, 2U);
2✔
764
  BOOST_CHECK_EQUAL(g_negCache->size(), 1U);
2✔
765

766
  // New query
767
  ret.clear();
2✔
768
  res = sr->beginResolve(target2, QType(QType::A), QClass::IN, ret);
2✔
769
  BOOST_CHECK_EQUAL(res, RCode::NXDomain);
2✔
770
  BOOST_CHECK_EQUAL(ret.size(), 1U);
2✔
771
  BOOST_CHECK_EQUAL(queriesCount, 3U);
2✔
772
  BOOST_CHECK_EQUAL(g_negCache->size(), 2U);
2✔
773

774
  ret.clear();
2✔
775
  res = sr->beginResolve(target3, QType(QType::A), QClass::IN, ret);
2✔
776
  BOOST_CHECK_EQUAL(res, RCode::NXDomain);
2✔
777
  BOOST_CHECK_EQUAL(ret.size(), 1U);
2✔
778
  BOOST_CHECK_EQUAL(queriesCount, 4U);
2✔
779
  BOOST_CHECK_EQUAL(g_negCache->size(), 3U);
2✔
780

781
  ret.clear();
2✔
782
  res = sr->beginResolve(target4, QType(QType::A), QClass::IN, ret);
2✔
783
  BOOST_CHECK_EQUAL(res, RCode::NXDomain);
2✔
784
  BOOST_CHECK_EQUAL(ret.size(), 1U);
2✔
785
  BOOST_CHECK_EQUAL(queriesCount, 5U);
2✔
786
  BOOST_CHECK_EQUAL(g_negCache->size(), 4U);
2✔
787

788
  // reset
789
  SyncRes::s_hardenNXD = SyncRes::HardenNXD::DNSSEC;
2✔
790
}
2✔
791

792
BOOST_AUTO_TEST_CASE(test_rfc8020_nothing_underneath_dnssec)
793
{
2✔
794
  std::unique_ptr<SyncRes> sr;
2✔
795
  initSR(sr, true);
2✔
796
  setDNSSECValidation(sr, DNSSECMode::ValidateAll);
2✔
797

798
  primeHints();
2✔
799

800
  const DNSName parent1("com.");
2✔
801
  const DNSName parent2("powerdns.com.");
2✔
802
  const DNSName target1("www.powerdns.com."); // will be denied
2✔
803
  const DNSName target2("foo.www.powerdns.com.");
2✔
804
  const DNSName target3("bar.www.powerdns.com.");
2✔
805
  const DNSName target4("quux.bar.www.powerdns.com.");
2✔
806
  const ComboAddress ns("192.0.2.1:53");
2✔
807

808
  testkeysset_t keys;
2✔
809

810
  auto luaconfsCopy = g_luaconfs.getCopy();
2✔
811
  luaconfsCopy.dsAnchors.clear();
2✔
812
  generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys, luaconfsCopy.dsAnchors);
2✔
813
  generateKeyMaterial(parent1, DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
2✔
814
  generateKeyMaterial(parent2, DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
2✔
815
  g_luaconfs.setState(luaconfsCopy);
2✔
816

817
  size_t queriesCount = 0;
2✔
818

819
  sr->setAsyncCallback([&](const ComboAddress& address, const DNSName& domain, int type, bool /* doTCP */, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval* /* now */, boost::optional<Netmask>& /* srcmask */, const ResolveContext& /* context */, LWResult* res, bool* /* chained */) {
18✔
820
    queriesCount++;
18✔
821

822
    DNSName auth = domain;
18✔
823
    if (domain == target1 || domain == target2 || domain == target3 || domain == target4) {
18✔
824
      auth = DNSName("powerdns.com.");
12✔
825
    }
12✔
826
    if (type == QType::DS || type == QType::DNSKEY) {
18!
827
      if (type == QType::DS && (domain == target1 || domain == target2 || domain == target3 || domain == target4)) {
6!
828
        setLWResult(res, RCode::NXDomain, true, false, true);
×
829
        addRecordToLW(res, DNSName("powerdns.com."), QType::SOA, "pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 3600);
×
830
        addRRSIG(keys, res->d_records, auth, 300);
×
831
        addNSECRecordToLW(DNSName("wwa.powerdns.com."), DNSName("wwz.powerdns.com."), {QType::RRSIG, QType::NSEC}, 600, res->d_records);
×
832
        addRRSIG(keys, res->d_records, auth, 300);
×
833
        return LWResult::Result::Success;
×
834
      }
×
835
      return genericDSAndDNSKEYHandler(res, domain, auth, type, keys);
6✔
836
    }
6✔
837
    {
12✔
838
      if (isRootServer(address)) {
12✔
839
        setLWResult(res, 0, false, false, true);
2✔
840
        addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResourceRecord::AUTHORITY, 3600);
2✔
841
        addDS(DNSName("com."), 300, res->d_records, keys);
2✔
842
        addRRSIG(keys, res->d_records, DNSName("."), 300);
2✔
843
        addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
2✔
844
        return LWResult::Result::Success;
2✔
845
      }
2✔
846
      if (address == ComboAddress("192.0.2.1:53")) {
10✔
847
        if (domain == DNSName("com.")) {
2!
848
          setLWResult(res, 0, true, false, true);
×
849
          addRecordToLW(res, domain, QType::NS, "a.gtld-servers.com.");
×
850
          addRRSIG(keys, res->d_records, domain, 300);
×
851
          addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
×
852
          addRRSIG(keys, res->d_records, domain, 300);
×
853
        }
×
854
        else {
2✔
855
          setLWResult(res, 0, false, false, true);
2✔
856
          addRecordToLW(res, auth, QType::NS, "ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 3600);
2✔
857
          addDS(auth, 300, res->d_records, keys);
2✔
858
          addRRSIG(keys, res->d_records, DNSName("com."), 300);
2✔
859
          addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
2✔
860
        }
2✔
861
        return LWResult::Result::Success;
2✔
862
      }
2✔
863
      if (address == ComboAddress("192.0.2.2:53")) {
8!
864
        if (type == QType::NS) {
8!
865
          setLWResult(res, 0, true, false, true);
×
866
          if (domain == DNSName("powerdns.com.")) {
×
867
            addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.");
×
868
            addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
×
869
            addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
×
870
            addRRSIG(keys, res->d_records, DNSName("powerdns.com"), 300);
×
871
          }
×
872
        }
×
873
        else {
8✔
874
          setLWResult(res, RCode::NXDomain, true, false, true);
8✔
875
          addRecordToLW(res, DNSName("powerdns.com."), QType::SOA, "pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 3600);
8✔
876
          addRRSIG(keys, res->d_records, auth, 300);
8✔
877
          addNSECRecordToLW(DNSName("wwa.powerdns.com."), DNSName("wwz.powerdns.com."), {QType::RRSIG, QType::NSEC}, 600, res->d_records);
8✔
878
          addRRSIG(keys, res->d_records, auth, 300);
8✔
879
          /* add wildcard denial */
880
          addNSECRecordToLW(DNSName("powerdns.com."), DNSName("a.powerdns.com."), {QType::RRSIG, QType::NSEC}, 600, res->d_records);
8✔
881
          addRRSIG(keys, res->d_records, auth, 300);
8✔
882
        }
8✔
883
        return LWResult::Result::Success;
8✔
884
      }
8✔
885
    }
8✔
886

887
    return LWResult::Result::Timeout;
×
888
  });
8✔
889

890
  vector<DNSRecord> ret;
2✔
891
  int res = sr->beginResolve(target1, QType(QType::A), QClass::IN, ret);
2✔
892
  BOOST_CHECK_EQUAL(res, RCode::NXDomain);
2✔
893
  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
2✔
894
  BOOST_CHECK_EQUAL(ret.size(), 6U);
2✔
895
  BOOST_CHECK_EQUAL(queriesCount, 6U);
2✔
896
  BOOST_CHECK_EQUAL(g_negCache->size(), 1U);
2✔
897

898
  ret.clear();
2✔
899
  res = sr->beginResolve(target2, QType(QType::A), QClass::IN, ret);
2✔
900
  BOOST_CHECK_EQUAL(res, RCode::NXDomain);
2✔
901
  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
2✔
902
  BOOST_CHECK_EQUAL(ret.size(), 6U);
2✔
903
  BOOST_CHECK_EQUAL(queriesCount, 6U);
2✔
904
  BOOST_CHECK_EQUAL(g_negCache->size(), 1U);
2✔
905

906
  ret.clear();
2✔
907
  res = sr->beginResolve(target3, QType(QType::A), QClass::IN, ret);
2✔
908
  BOOST_CHECK_EQUAL(res, RCode::NXDomain);
2✔
909
  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
2✔
910
  BOOST_CHECK_EQUAL(ret.size(), 6U);
2✔
911
  BOOST_CHECK_EQUAL(queriesCount, 6U);
2✔
912
  BOOST_CHECK_EQUAL(g_negCache->size(), 1U);
2✔
913

914
  ret.clear();
2✔
915
  res = sr->beginResolve(target4, QType(QType::A), QClass::IN, ret);
2✔
916
  BOOST_CHECK_EQUAL(res, RCode::NXDomain);
2✔
917
  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
2✔
918
  BOOST_CHECK_EQUAL(ret.size(), 6U);
2✔
919
  BOOST_CHECK_EQUAL(queriesCount, 6U);
2✔
920
  BOOST_CHECK_EQUAL(g_negCache->size(), 1U);
2✔
921

922
  // Now test without RFC 8020 to see the cache and query count grow
923
  SyncRes::s_hardenNXD = SyncRes::HardenNXD::No;
2✔
924

925
  // Already cached
926
  ret.clear();
2✔
927
  res = sr->beginResolve(target1, QType(QType::A), QClass::IN, ret);
2✔
928
  BOOST_CHECK_EQUAL(res, RCode::NXDomain);
2✔
929
  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
2✔
930
  BOOST_CHECK_EQUAL(ret.size(), 6U);
2✔
931
  BOOST_CHECK_EQUAL(queriesCount, 6U);
2✔
932
  BOOST_CHECK_EQUAL(g_negCache->size(), 1U);
2✔
933

934
  // New query
935
  ret.clear();
2✔
936
  res = sr->beginResolve(target2, QType(QType::A), QClass::IN, ret);
2✔
937
  BOOST_CHECK_EQUAL(res, RCode::NXDomain);
2✔
938
  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
2✔
939
  BOOST_CHECK_EQUAL(ret.size(), 6U);
2✔
940
  BOOST_CHECK_EQUAL(queriesCount, 7U);
2✔
941
  BOOST_CHECK_EQUAL(g_negCache->size(), 2U);
2✔
942

943
  ret.clear();
2✔
944
  res = sr->beginResolve(target3, QType(QType::A), QClass::IN, ret);
2✔
945
  BOOST_CHECK_EQUAL(res, RCode::NXDomain);
2✔
946
  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
2✔
947
  BOOST_CHECK_EQUAL(ret.size(), 6U);
2✔
948
  BOOST_CHECK_EQUAL(queriesCount, 8U);
2✔
949
  BOOST_CHECK_EQUAL(g_negCache->size(), 3U);
2✔
950

951
  ret.clear();
2✔
952
  res = sr->beginResolve(target4, QType(QType::A), QClass::IN, ret);
2✔
953
  BOOST_CHECK_EQUAL(res, RCode::NXDomain);
2✔
954
  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
2✔
955
  BOOST_CHECK_EQUAL(ret.size(), 6U);
2✔
956
  BOOST_CHECK_EQUAL(queriesCount, 9U);
2✔
957
  BOOST_CHECK_EQUAL(g_negCache->size(), 4U);
2✔
958

959
  // reset
960
  SyncRes::s_hardenNXD = SyncRes::HardenNXD::DNSSEC;
2✔
961
}
2✔
962

963
BOOST_AUTO_TEST_CASE(test_rfc8020_nodata)
964
{
2✔
965
  std::unique_ptr<SyncRes> sr;
2✔
966
  initSR(sr);
2✔
967
  SyncRes::s_hardenNXD = SyncRes::HardenNXD::Yes;
2✔
968

969
  primeHints();
2✔
970

971
  const DNSName target1("www.powerdns.com."); // TXT record will be denied
2✔
972
  const DNSName target2("bar.www.powerdns.com."); // will be NXD, but the www. NODATA should not interfere with 8020 processing
2✔
973
  const DNSName target3("quux.bar.www.powerdns.com."); // will be NXD, but will not yield a query
2✔
974
  const ComboAddress ns("192.0.2.1:53");
2✔
975
  size_t queriesCount = 0;
2✔
976

977
  sr->setAsyncCallback([&](const ComboAddress& address, const DNSName& domain, int type, bool /* doTCP */, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval* /* now */, boost::optional<Netmask>& /* srcmask */, const ResolveContext& /* context */, LWResult* res, bool* /* chained */) {
8✔
978
    queriesCount++;
8✔
979

980
    if (isRootServer(address)) {
8✔
981
      setLWResult(res, 0, false, false, true);
2✔
982
      addRecordToLW(res, "powerdns.com.", QType::NS, "ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
2✔
983
      addRecordToLW(res, "ns1.powerdns.com.", QType::A, ns.toString(), DNSResourceRecord::ADDITIONAL, 3600);
2✔
984
      return LWResult::Result::Success;
2✔
985
    }
2✔
986
    if (address == ns) {
6!
987
      if (domain == target1) { // NODATA for TXT, NOERROR for A
6✔
988
        if (type == QType::TXT) {
4✔
989
          setLWResult(res, RCode::NoError, true);
2✔
990
          addRecordToLW(res, "powerdns.com.", QType::SOA, "ns1.powerdns.com. hostmaster.powerdns.com. 2017032800 1800 900 604800 86400", DNSResourceRecord::AUTHORITY, 86400);
2✔
991
          return LWResult::Result::Success;
2✔
992
        }
2✔
993
        if (type == QType::A) {
2!
994
          setLWResult(res, RCode::NoError, true);
2✔
995
          addRecordToLW(res, domain, QType::A, "192.0.2.1", DNSResourceRecord::ANSWER, 86400);
2✔
996
          return LWResult::Result::Success;
2✔
997
        }
2✔
998
      }
2✔
999
      if (domain == target2 || domain == target3) {
2!
1000
        setLWResult(res, RCode::NXDomain, true);
2✔
1001
        addRecordToLW(res, "powerdns.com.", QType::SOA, "ns1.powerdns.com. hostmaster.powerdns.com. 2017032800 1800 900 604800 86400", DNSResourceRecord::AUTHORITY, 86400);
2✔
1002
        return LWResult::Result::Success;
2✔
1003
      }
2✔
1004
    }
2✔
1005
    return LWResult::Result::Timeout;
×
1006
  });
6✔
1007

1008
  vector<DNSRecord> ret;
2✔
1009
  int res = sr->beginResolve(target1, QType(QType::TXT), QClass::IN, ret);
2✔
1010
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1011
  BOOST_CHECK_EQUAL(ret.size(), 1U);
2✔
1012
  BOOST_CHECK_EQUAL(queriesCount, 2U);
2✔
1013
  BOOST_CHECK_EQUAL(g_negCache->size(), 1U);
2✔
1014

1015
  ret.clear();
2✔
1016
  res = sr->beginResolve(target1, QType(QType::A), QClass::IN, ret);
2✔
1017
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1018
  BOOST_CHECK_EQUAL(ret.size(), 1U);
2✔
1019
  BOOST_CHECK_EQUAL(queriesCount, 3U);
2✔
1020
  BOOST_CHECK_EQUAL(g_negCache->size(), 1U);
2✔
1021

1022
  ret.clear();
2✔
1023
  res = sr->beginResolve(target2, QType(QType::A), QClass::IN, ret);
2✔
1024
  BOOST_CHECK_EQUAL(res, RCode::NXDomain);
2✔
1025
  BOOST_CHECK_EQUAL(ret.size(), 1U);
2✔
1026
  BOOST_CHECK_EQUAL(queriesCount, 4U);
2✔
1027
  BOOST_CHECK_EQUAL(g_negCache->size(), 2U);
2✔
1028

1029
  ret.clear();
2✔
1030
  res = sr->beginResolve(target3, QType(QType::A), QClass::IN, ret);
2✔
1031
  BOOST_CHECK_EQUAL(res, RCode::NXDomain);
2✔
1032
  BOOST_CHECK_EQUAL(ret.size(), 1U);
2✔
1033
  BOOST_CHECK_EQUAL(queriesCount, 4U);
2✔
1034
  BOOST_CHECK_EQUAL(g_negCache->size(), 2U);
2✔
1035
}
2✔
1036

1037
BOOST_AUTO_TEST_CASE(test_rfc8020_nodata_bis)
1038
{
2✔
1039
  std::unique_ptr<SyncRes> sr;
2✔
1040
  initSR(sr);
2✔
1041
  SyncRes::s_hardenNXD = SyncRes::HardenNXD::Yes;
2✔
1042

1043
  primeHints();
2✔
1044

1045
  const DNSName target1("www.powerdns.com."); // TXT record will be denied
2✔
1046
  const DNSName target2("bar.www.powerdns.com."); // will be NXD, but the www. NODATA should not interfere with 8020 processing
2✔
1047
  const DNSName target3("quux.bar.www.powerdns.com."); // will be NXD, but will not yield a query
2✔
1048
  const ComboAddress ns("192.0.2.1:53");
2✔
1049
  size_t queriesCount = 0;
2✔
1050

1051
  sr->setAsyncCallback([&](const ComboAddress& address, const DNSName& domain, int type, bool /* doTCP */, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval* /* now */, boost::optional<Netmask>& /* srcmask */, const ResolveContext& /* context */, LWResult* res, bool* /* chained */) {
8✔
1052
    queriesCount++;
8✔
1053

1054
    if (isRootServer(address)) {
8✔
1055
      setLWResult(res, 0, false, false, true);
2✔
1056
      addRecordToLW(res, "powerdns.com.", QType::NS, "ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
2✔
1057
      addRecordToLW(res, "ns1.powerdns.com.", QType::A, ns.toString(), DNSResourceRecord::ADDITIONAL, 3600);
2✔
1058
      return LWResult::Result::Success;
2✔
1059
    }
2✔
1060
    if (address == ns) {
6!
1061
      if (domain == target1) { // NODATA for TXT, NOERROR for A
6✔
1062
        if (type == QType::TXT) {
4✔
1063
          setLWResult(res, RCode::NoError, true);
2✔
1064
          addRecordToLW(res, "powerdns.com.", QType::SOA, "ns1.powerdns.com. hostmaster.powerdns.com. 2017032800 1800 900 604800 86400", DNSResourceRecord::AUTHORITY, 86400);
2✔
1065
          return LWResult::Result::Success;
2✔
1066
        }
2✔
1067
        if (type == QType::A) {
2!
1068
          setLWResult(res, RCode::NoError, true);
2✔
1069
          addRecordToLW(res, domain, QType::A, "192.0.2.1", DNSResourceRecord::ANSWER, 86400);
2✔
1070
          return LWResult::Result::Success;
2✔
1071
        }
2✔
1072
      }
2✔
1073
      if (domain == target2 || domain == target3) {
2!
1074
        setLWResult(res, RCode::NXDomain, true);
2✔
1075
        addRecordToLW(res, "powerdns.com.", QType::SOA, "ns1.powerdns.com. hostmaster.powerdns.com. 2017032800 1800 900 604800 86400", DNSResourceRecord::AUTHORITY, 86400);
2✔
1076
        return LWResult::Result::Success;
2✔
1077
      }
2✔
1078
    }
2✔
1079
    return LWResult::Result::Timeout;
×
1080
  });
6✔
1081

1082
  vector<DNSRecord> ret;
2✔
1083
  int res = sr->beginResolve(target1, QType(QType::TXT), QClass::IN, ret);
2✔
1084
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1085
  BOOST_CHECK_EQUAL(ret.size(), 1U);
2✔
1086
  BOOST_CHECK_EQUAL(queriesCount, 2U);
2✔
1087
  BOOST_CHECK_EQUAL(g_negCache->size(), 1U);
2✔
1088

1089
  ret.clear();
2✔
1090
  res = sr->beginResolve(target1, QType(QType::A), QClass::IN, ret);
2✔
1091
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1092
  BOOST_CHECK_EQUAL(ret.size(), 1U);
2✔
1093
  BOOST_CHECK_EQUAL(queriesCount, 3U);
2✔
1094
  BOOST_CHECK_EQUAL(g_negCache->size(), 1U);
2✔
1095

1096
  ret.clear();
2✔
1097
  res = sr->beginResolve(target2, QType(QType::TXT), QClass::IN, ret);
2✔
1098
  BOOST_CHECK_EQUAL(res, RCode::NXDomain);
2✔
1099
  BOOST_CHECK_EQUAL(ret.size(), 1U);
2✔
1100
  BOOST_CHECK_EQUAL(queriesCount, 4U);
2✔
1101
  BOOST_CHECK_EQUAL(g_negCache->size(), 2U);
2✔
1102

1103
  ret.clear();
2✔
1104
  res = sr->beginResolve(target3, QType(QType::TXT), QClass::IN, ret);
2✔
1105
  BOOST_CHECK_EQUAL(res, RCode::NXDomain);
2✔
1106
  BOOST_CHECK_EQUAL(ret.size(), 1U);
2✔
1107
  BOOST_CHECK_EQUAL(queriesCount, 4U);
2✔
1108
  BOOST_CHECK_EQUAL(g_negCache->size(), 2U);
2✔
1109
}
2✔
1110

1111
BOOST_AUTO_TEST_CASE(test_dont_skip_negcache_for_variable_response)
1112
{
2✔
1113
  std::unique_ptr<SyncRes> sr;
2✔
1114
  initSR(sr);
2✔
1115

1116
  primeHints();
2✔
1117

1118
  const DNSName target("www.powerdns.com.");
2✔
1119
  const DNSName cnameTarget("cname.powerdns.com.");
2✔
1120

1121
  SyncRes::addEDNSDomain(DNSName("powerdns.com."));
2✔
1122

1123
  EDNSSubnetOpts incomingECS;
2✔
1124
  incomingECS.setSource(Netmask("192.0.2.128/32"));
2✔
1125
  sr->setQuerySource(ComboAddress(), boost::optional<const EDNSSubnetOpts&>(incomingECS));
2✔
1126

1127
  sr->setAsyncCallback([&](const ComboAddress& address, const DNSName& domain, int /* type */, bool /* doTCP */, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval* /* now */, boost::optional<Netmask>& srcmask, const ResolveContext& /* context */, LWResult* res, bool* /* chained */) {
6✔
1128
    BOOST_REQUIRE(srcmask);
6✔
1129
    BOOST_CHECK_EQUAL(srcmask->toString(), "192.0.2.0/24");
6✔
1130

1131
    if (isRootServer(address)) {
6✔
1132
      setLWResult(res, 0, false, false, true);
2✔
1133
      addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
2✔
1134
      addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
2✔
1135

1136
      srcmask = boost::none;
2✔
1137

1138
      return LWResult::Result::Success;
2✔
1139
    }
2✔
1140
    if (address == ComboAddress("192.0.2.1:53")) {
4!
1141
      if (domain == target) {
4✔
1142
        /* Type 2 NXDOMAIN (rfc2308 section-2.1) */
1143
        setLWResult(res, RCode::NXDomain, true, false, true);
2✔
1144
        addRecordToLW(res, domain, QType::CNAME, cnameTarget.toString());
2✔
1145
        addRecordToLW(res, "powerdns.com", QType::SOA, "pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 3600);
2✔
1146
      }
2✔
1147
      else if (domain == cnameTarget) {
2!
1148
        /* we shouldn't get there since the Type NXDOMAIN should have been enough,
1149
             but we might if we still chase the CNAME. */
1150
        setLWResult(res, RCode::NXDomain, true, false, true);
2✔
1151
        addRecordToLW(res, "powerdns.com", QType::SOA, "pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 3600);
2✔
1152
      }
2✔
1153

1154
      return LWResult::Result::Success;
4✔
1155
    }
4✔
1156

1157
    return LWResult::Result::Timeout;
×
1158
  });
4✔
1159

1160
  vector<DNSRecord> ret;
2✔
1161
  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1162
  BOOST_CHECK_EQUAL(res, RCode::NXDomain);
2✔
1163
  BOOST_CHECK_EQUAL(ret.size(), 2U);
2✔
1164
  BOOST_CHECK_EQUAL(g_negCache->size(), 1U);
2✔
1165
}
2✔
1166

1167
BOOST_AUTO_TEST_CASE(test_ecs_cache_limit_allowed)
1168
{
2✔
1169
  std::unique_ptr<SyncRes> sr;
2✔
1170
  initSR(sr);
2✔
1171

1172
  primeHints();
2✔
1173

1174
  const DNSName target("www.powerdns.com.");
2✔
1175

1176
  SyncRes::addEDNSDomain(DNSName("powerdns.com."));
2✔
1177

1178
  EDNSSubnetOpts incomingECS;
2✔
1179
  incomingECS.setSource(Netmask("192.0.2.128/32"));
2✔
1180
  sr->setQuerySource(ComboAddress(), boost::optional<const EDNSSubnetOpts&>(incomingECS));
2✔
1181
  SyncRes::s_ecsipv4cachelimit = 24;
2✔
1182

1183
  sr->setAsyncCallback([&](const ComboAddress& /* ip */, const DNSName& /* domain */, int /* type */, bool /* doTCP */, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval* /* now */, boost::optional<Netmask>& srcmask, const ResolveContext& /* context */, LWResult* res, bool* /* chained */) {
2✔
1184
    BOOST_REQUIRE(srcmask);
2✔
1185
    BOOST_CHECK_EQUAL(srcmask->toString(), "192.0.2.0/24");
2✔
1186

1187
    setLWResult(res, 0, true, false, true);
2✔
1188
    addRecordToLW(res, target, QType::A, "192.0.2.1");
2✔
1189

1190
    return LWResult::Result::Success;
2✔
1191
  });
2✔
1192

1193
  const time_t now = sr->getNow().tv_sec;
2✔
1194
  vector<DNSRecord> ret;
2✔
1195
  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1196
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1197
  BOOST_CHECK_EQUAL(ret.size(), 1U);
2✔
1198

1199
  /* should have been cached */
1200
  const ComboAddress who("192.0.2.128");
2✔
1201
  vector<DNSRecord> cached;
2✔
1202
  BOOST_REQUIRE_GT(g_recCache->get(now, target, QType(QType::A), MemRecursorCache::RequireAuth, &cached, who), 0);
2✔
1203
  BOOST_REQUIRE_EQUAL(cached.size(), 1U);
2✔
1204
}
2✔
1205

1206
BOOST_AUTO_TEST_CASE(test_ecs_cache_limit_no_ttl_limit_allowed)
1207
{
2✔
1208
  std::unique_ptr<SyncRes> sr;
2✔
1209
  initSR(sr);
2✔
1210

1211
  primeHints();
2✔
1212

1213
  const DNSName target("www.powerdns.com.");
2✔
1214

1215
  SyncRes::addEDNSDomain(DNSName("powerdns.com."));
2✔
1216

1217
  EDNSSubnetOpts incomingECS;
2✔
1218
  incomingECS.setSource(Netmask("192.0.2.128/32"));
2✔
1219
  sr->setQuerySource(ComboAddress(), boost::optional<const EDNSSubnetOpts&>(incomingECS));
2✔
1220
  SyncRes::s_ecsipv4cachelimit = 16;
2✔
1221

1222
  sr->setAsyncCallback([&](const ComboAddress& /* ip */, const DNSName& /* domain */, int /* type */, bool /* doTCP */, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval* /* now */, boost::optional<Netmask>& srcmask, const ResolveContext& /* context */, LWResult* res, bool* /* chained */) {
2✔
1223
    BOOST_REQUIRE(srcmask);
2✔
1224
    BOOST_CHECK_EQUAL(srcmask->toString(), "192.0.2.0/24");
2✔
1225

1226
    setLWResult(res, 0, true, false, true);
2✔
1227
    addRecordToLW(res, target, QType::A, "192.0.2.1");
2✔
1228

1229
    return LWResult::Result::Success;
2✔
1230
  });
2✔
1231

1232
  const time_t now = sr->getNow().tv_sec;
2✔
1233
  vector<DNSRecord> ret;
2✔
1234
  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1235
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1236
  BOOST_CHECK_EQUAL(ret.size(), 1U);
2✔
1237

1238
  /* should have been cached because /24 is more specific than /16 but TTL limit is nof effective */
1239
  const ComboAddress who("192.0.2.128");
2✔
1240
  vector<DNSRecord> cached;
2✔
1241
  BOOST_REQUIRE_GT(g_recCache->get(now, target, QType(QType::A), MemRecursorCache::RequireAuth, &cached, who), 0);
2✔
1242
  BOOST_REQUIRE_EQUAL(cached.size(), 1U);
2✔
1243
}
2✔
1244

1245
BOOST_AUTO_TEST_CASE(test_ecs_cache_ttllimit_allowed)
1246
{
2✔
1247
  std::unique_ptr<SyncRes> sr;
2✔
1248
  initSR(sr);
2✔
1249

1250
  primeHints();
2✔
1251

1252
  const DNSName target("www.powerdns.com.");
2✔
1253

1254
  SyncRes::addEDNSDomain(DNSName("powerdns.com."));
2✔
1255

1256
  EDNSSubnetOpts incomingECS;
2✔
1257
  incomingECS.setSource(Netmask("192.0.2.128/32"));
2✔
1258
  sr->setQuerySource(ComboAddress(), boost::optional<const EDNSSubnetOpts&>(incomingECS));
2✔
1259
  SyncRes::s_ecscachelimitttl = 30;
2✔
1260

1261
  sr->setAsyncCallback([&](const ComboAddress& /* ip */, const DNSName& /* domain */, int /* type */, bool /* doTCP */, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval* /* now */, boost::optional<Netmask>& srcmask, const ResolveContext& /* context */, LWResult* res, bool* /* chained */) {
2✔
1262
    BOOST_REQUIRE(srcmask);
2✔
1263
    BOOST_CHECK_EQUAL(srcmask->toString(), "192.0.2.0/24");
2✔
1264

1265
    setLWResult(res, 0, true, false, true);
2✔
1266
    addRecordToLW(res, target, QType::A, "192.0.2.1");
2✔
1267

1268
    return LWResult::Result::Success;
2✔
1269
  });
2✔
1270

1271
  const time_t now = sr->getNow().tv_sec;
2✔
1272
  vector<DNSRecord> ret;
2✔
1273
  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1274
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1275
  BOOST_CHECK_EQUAL(ret.size(), 1U);
2✔
1276

1277
  /* should have been cached */
1278
  const ComboAddress who("192.0.2.128");
2✔
1279
  vector<DNSRecord> cached;
2✔
1280
  BOOST_REQUIRE_GT(g_recCache->get(now, target, QType(QType::A), MemRecursorCache::RequireAuth, &cached, who), 0);
2✔
1281
  BOOST_REQUIRE_EQUAL(cached.size(), 1U);
2✔
1282
}
2✔
1283

1284
BOOST_AUTO_TEST_CASE(test_ecs_cache_ttllimit_and_scope_allowed)
1285
{
2✔
1286
  std::unique_ptr<SyncRes> sr;
2✔
1287
  initSR(sr);
2✔
1288

1289
  primeHints();
2✔
1290

1291
  const DNSName target("www.powerdns.com.");
2✔
1292

1293
  SyncRes::addEDNSDomain(DNSName("powerdns.com."));
2✔
1294

1295
  EDNSSubnetOpts incomingECS;
2✔
1296
  incomingECS.setSource(Netmask("192.0.2.128/32"));
2✔
1297
  sr->setQuerySource(ComboAddress(), boost::optional<const EDNSSubnetOpts&>(incomingECS));
2✔
1298
  SyncRes::s_ecscachelimitttl = 100;
2✔
1299
  SyncRes::s_ecsipv4cachelimit = 24;
2✔
1300

1301
  sr->setAsyncCallback([&](const ComboAddress& /* ip */, const DNSName& /* domain */, int /* type */, bool /* doTCP */, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval* /* now */, boost::optional<Netmask>& srcmask, const ResolveContext& /* context */, LWResult* res, bool* /* chained */) {
2✔
1302
    BOOST_REQUIRE(srcmask);
2✔
1303
    BOOST_CHECK_EQUAL(srcmask->toString(), "192.0.2.0/24");
2✔
1304

1305
    setLWResult(res, 0, true, false, true);
2✔
1306
    addRecordToLW(res, target, QType::A, "192.0.2.1");
2✔
1307

1308
    return LWResult::Result::Success;
2✔
1309
  });
2✔
1310

1311
  const time_t now = sr->getNow().tv_sec;
2✔
1312
  vector<DNSRecord> ret;
2✔
1313
  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1314
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1315
  BOOST_CHECK_EQUAL(ret.size(), 1U);
2✔
1316

1317
  /* should have been cached */
1318
  const ComboAddress who("192.0.2.128");
2✔
1319
  vector<DNSRecord> cached;
2✔
1320
  BOOST_REQUIRE_GT(g_recCache->get(now, target, QType(QType::A), MemRecursorCache::RequireAuth, &cached, who), 0);
2✔
1321
  BOOST_REQUIRE_EQUAL(cached.size(), 1U);
2✔
1322
}
2✔
1323

1324
BOOST_AUTO_TEST_CASE(test_ecs_cache_ttllimit_notallowed)
1325
{
2✔
1326
  std::unique_ptr<SyncRes> sr;
2✔
1327
  initSR(sr);
2✔
1328

1329
  primeHints();
2✔
1330

1331
  const DNSName target("www.powerdns.com.");
2✔
1332

1333
  SyncRes::addEDNSDomain(DNSName("powerdns.com."));
2✔
1334

1335
  EDNSSubnetOpts incomingECS;
2✔
1336
  incomingECS.setSource(Netmask("192.0.2.128/32"));
2✔
1337
  sr->setQuerySource(ComboAddress(), boost::optional<const EDNSSubnetOpts&>(incomingECS));
2✔
1338
  SyncRes::s_ecscachelimitttl = 100;
2✔
1339
  SyncRes::s_ecsipv4cachelimit = 16;
2✔
1340

1341
  sr->setAsyncCallback([&](const ComboAddress& /* ip */, const DNSName& /* domain */, int /* type */, bool /* doTCP */, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval* /* now */, boost::optional<Netmask>& srcmask, const ResolveContext& /* context */, LWResult* res, bool* /* chained */) {
2✔
1342
    BOOST_REQUIRE(srcmask);
2✔
1343
    BOOST_CHECK_EQUAL(srcmask->toString(), "192.0.2.0/24");
2✔
1344

1345
    setLWResult(res, 0, true, false, true);
2✔
1346
    addRecordToLW(res, target, QType::A, "192.0.2.1");
2✔
1347

1348
    return LWResult::Result::Success;
2✔
1349
  });
2✔
1350

1351
  const time_t now = sr->getNow().tv_sec;
2✔
1352
  vector<DNSRecord> ret;
2✔
1353
  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1354
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1355
  BOOST_CHECK_EQUAL(ret.size(), 1U);
2✔
1356

1357
  /* should have NOT been cached because TTL of 60 is too small and /24 is more specific than /16 */
1358
  const ComboAddress who("192.0.2.128");
2✔
1359
  vector<DNSRecord> cached;
2✔
1360
  BOOST_REQUIRE_LT(g_recCache->get(now, target, QType(QType::A), MemRecursorCache::RequireAuth, &cached, who), 0);
2✔
1361
  BOOST_REQUIRE_EQUAL(cached.size(), 0U);
2✔
1362
}
2✔
1363

1364
BOOST_AUTO_TEST_CASE(test_ns_speed)
1365
{
2✔
1366
  std::unique_ptr<SyncRes> sr;
2✔
1367
  initSR(sr);
2✔
1368

1369
  primeHints();
2✔
1370

1371
  const DNSName target("powerdns.com.");
2✔
1372

1373
  std::map<ComboAddress, uint64_t> nsCounts;
2✔
1374

1375
  sr->setAsyncCallback([&](const ComboAddress& address, const DNSName& domain, int /* type */, bool /* doTCP */, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval* /* now */, boost::optional<Netmask>& /* srcmask */, const ResolveContext& /* context */, LWResult* res, bool* /* chained */) {
8✔
1376
    if (isRootServer(address)) {
8✔
1377
      setLWResult(res, 0, false, false, true);
2✔
1378
      addRecordToLW(res, domain, QType::NS, "pdns-public-ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
2✔
1379
      addRecordToLW(res, domain, QType::NS, "pdns-public-ns2.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
2✔
1380
      addRecordToLW(res, domain, QType::NS, "pdns-public-ns3.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
2✔
1381

1382
      addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
2✔
1383
      addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::AAAA, "2001:DB8::1", DNSResourceRecord::ADDITIONAL, 3600);
2✔
1384
      addRecordToLW(res, "pdns-public-ns2.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
2✔
1385
      addRecordToLW(res, "pdns-public-ns2.powerdns.com.", QType::AAAA, "2001:DB8::2", DNSResourceRecord::ADDITIONAL, 3600);
2✔
1386
      addRecordToLW(res, "pdns-public-ns3.powerdns.com.", QType::A, "192.0.2.3", DNSResourceRecord::ADDITIONAL, 3600);
2✔
1387
      addRecordToLW(res, "pdns-public-ns3.powerdns.com.", QType::AAAA, "2001:DB8::3", DNSResourceRecord::ADDITIONAL, 3600);
2✔
1388

1389
      return LWResult::Result::Success;
2✔
1390
    }
2✔
1391
    {
6✔
1392
      nsCounts[address]++;
6✔
1393

1394
      if (address == ComboAddress("[2001:DB8::2]:53") || address == ComboAddress("192.0.2.2:53")) {
6✔
1395
        BOOST_CHECK_LT(nsCounts.size(), 3U);
4✔
1396

1397
        /* let's time out on pdns-public-ns2.powerdns.com. */
1398
        return LWResult::Result::Timeout;
4✔
1399
      }
4✔
1400
      if (address == ComboAddress("192.0.2.1:53")) {
2!
1401
        BOOST_CHECK_EQUAL(nsCounts.size(), 3U);
2✔
1402

1403
        setLWResult(res, 0, true, false, true);
2✔
1404
        addRecordToLW(res, domain, QType::A, "192.0.2.254");
2✔
1405
        return LWResult::Result::Success;
2✔
1406
      }
2✔
1407

1408
      return LWResult::Result::Timeout;
×
1409
    }
2✔
1410

1411
    return LWResult::Result::Timeout;
×
1412
  });
2✔
1413

1414
  struct timeval now = sr->getNow();
2✔
1415

1416
  /* make pdns-public-ns2.powerdns.com. the fastest NS, with its IPv6 address faster than the IPV4 one,
1417
     then pdns-public-ns1.powerdns.com. on IPv4 */
1418
  SyncRes::submitNSSpeed(DNSName("pdns-public-ns1.powerdns.com."), ComboAddress("192.0.2.1:53"), 100, now);
2✔
1419
  SyncRes::submitNSSpeed(DNSName("pdns-public-ns1.powerdns.com."), ComboAddress("[2001:DB8::1]:53"), 10000, now);
2✔
1420
  SyncRes::submitNSSpeed(DNSName("pdns-public-ns2.powerdns.com."), ComboAddress("192.0.2.2:53"), 10, now);
2✔
1421
  SyncRes::submitNSSpeed(DNSName("pdns-public-ns2.powerdns.com."), ComboAddress("[2001:DB8::2]:53"), 1, now);
2✔
1422
  SyncRes::submitNSSpeed(DNSName("pdns-public-ns3.powerdns.com."), ComboAddress("192.0.2.3:53"), 10000, now);
2✔
1423
  SyncRes::submitNSSpeed(DNSName("pdns-public-ns3.powerdns.com."), ComboAddress("[2001:DB8::3]:53"), 10000, now);
2✔
1424

1425
  vector<DNSRecord> ret;
2✔
1426
  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1427
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1428
  BOOST_CHECK_EQUAL(ret.size(), 1U);
2✔
1429
  BOOST_CHECK_EQUAL(nsCounts.size(), 3U);
2✔
1430
  BOOST_CHECK_EQUAL(nsCounts[ComboAddress("192.0.2.1:53")], 1U);
2✔
1431
  BOOST_CHECK_EQUAL(nsCounts[ComboAddress("192.0.2.2:53")], 1U);
2✔
1432
  BOOST_CHECK_EQUAL(nsCounts[ComboAddress("[2001:DB8::2]:53")], 1U);
2✔
1433
}
2✔
1434

1435
BOOST_AUTO_TEST_CASE(test_flawed_nsset)
1436
{
2✔
1437
  std::unique_ptr<SyncRes> sr;
2✔
1438
  initSR(sr);
2✔
1439

1440
  primeHints();
2✔
1441

1442
  const DNSName target("powerdns.com.");
2✔
1443

1444
  sr->setAsyncCallback([&](const ComboAddress& address, const DNSName& domain, int /* type */, bool /* doTCP */, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval* /* now */, boost::optional<Netmask>& /* srcmask */, const ResolveContext& /* context */, LWResult* res, bool* /* chained */) {
4✔
1445
    if (isRootServer(address)) {
4✔
1446
      setLWResult(res, 0, false, false, true);
2✔
1447
      addRecordToLW(res, domain, QType::NS, "pdns-public-ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
2✔
1448

1449
      addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
2✔
1450

1451
      return LWResult::Result::Success;
2✔
1452
    }
2✔
1453
    if (address == ComboAddress("192.0.2.1:53")) {
2!
1454
      setLWResult(res, 0, true, false, true);
2✔
1455
      addRecordToLW(res, domain, QType::A, "192.0.2.254");
2✔
1456
      return LWResult::Result::Success;
2✔
1457
    }
2✔
1458

1459
    return LWResult::Result::Timeout;
×
1460
  });
2✔
1461

1462
  /* we populate the cache with a flawed NSset, i.e. there is a NS entry but no corresponding glue */
1463
  time_t now = sr->getNow().tv_sec;
2✔
1464
  std::vector<DNSRecord> records;
2✔
1465
  std::vector<shared_ptr<const RRSIGRecordContent>> sigs;
2✔
1466
  addRecordToList(records, target, QType::NS, "pdns-public-ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, now + 3600);
2✔
1467

1468
  g_recCache->replace(now, target, QType(QType::NS), records, sigs, {}, true, g_rootdnsname, boost::optional<Netmask>());
2✔
1469

1470
  vector<DNSRecord> ret;
2✔
1471
  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1472
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1473
  BOOST_CHECK_EQUAL(ret.size(), 1U);
2✔
1474
}
2✔
1475

1476
BOOST_AUTO_TEST_CASE(test_completely_flawed_nsset)
1477
{
2✔
1478
  std::unique_ptr<SyncRes> sr;
2✔
1479
  initSR(sr);
2✔
1480

1481
  primeHints();
2✔
1482

1483
  const DNSName target("powerdns.com.");
2✔
1484
  size_t queriesCount = 0;
2✔
1485

1486
  sr->setAsyncCallback([&](const ComboAddress& address, const DNSName& domain, int /* type */, bool /* doTCP */, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval* /* now */, boost::optional<Netmask>& /* srcmask */, const ResolveContext& /* context */, LWResult* res, bool* /* chained */) {
10✔
1487
    queriesCount++;
10✔
1488

1489
    if (isRootServer(address) && domain == target) {
10!
1490
      setLWResult(res, 0, false, false, true);
2✔
1491
      addRecordToLW(res, domain, QType::NS, "pdns-public-ns2.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
2✔
1492
      addRecordToLW(res, domain, QType::NS, "pdns-public-ns3.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
2✔
1493
      return LWResult::Result::Success;
2✔
1494
    }
2✔
1495
    if (domain == DNSName("pdns-public-ns2.powerdns.com.") || domain == DNSName("pdns-public-ns3.powerdns.com.")) {
8!
1496
      setLWResult(res, 0, true, false, true);
8✔
1497
      addRecordToLW(res, ".", QType::SOA, "a.root-servers.net. nstld.verisign-grs.com. 2017032800 1800 900 604800 86400", DNSResourceRecord::AUTHORITY, 86400);
8✔
1498
      return LWResult::Result::Success;
8✔
1499
    }
8✔
1500

1501
    return LWResult::Result::Timeout;
×
1502
  });
8✔
1503

1504
  vector<DNSRecord> ret;
2✔
1505
  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1506
  BOOST_CHECK_EQUAL(res, RCode::ServFail);
2✔
1507
  BOOST_CHECK_EQUAL(ret.size(), 0U);
2✔
1508
  /* one query to get NSs, then A and AAAA for each NS */
1509
  BOOST_CHECK_EQUAL(queriesCount, 5U);
2✔
1510
}
2✔
1511

1512
BOOST_AUTO_TEST_CASE(test_completely_flawed_big_nsset)
1513
{
2✔
1514
  std::unique_ptr<SyncRes> sr;
2✔
1515
  initSR(sr);
2✔
1516

1517
  primeHints();
2✔
1518

1519
  const DNSName target("powerdns.com.");
2✔
1520
  size_t queriesCount = 0;
2✔
1521

1522
  sr->setAsyncCallback([&](const ComboAddress& address, const DNSName& domain, int /* type */, bool /* doTCP */, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval* /* now */, boost::optional<Netmask>& /* srcmask */, const ResolveContext& /* context */, LWResult* res, bool* /* chained */) {
22✔
1523
    queriesCount++;
22✔
1524

1525
    if (isRootServer(address) && domain == target) {
22!
1526
      setLWResult(res, 0, false, false, true);
2✔
1527
      // 20 NS records
1528
      for (int i = 0; i < 20; i++) {
42✔
1529
        string n = string("pdns-public-ns") + std::to_string(i) + string(".powerdns.com.");
40✔
1530
        addRecordToLW(res, domain, QType::NS, n, DNSResourceRecord::AUTHORITY, 172800);
40✔
1531
      }
40✔
1532
      return LWResult::Result::Success;
2✔
1533
    }
2✔
1534
    if (domain.toString().length() > 14 && domain.toString().substr(0, 14) == "pdns-public-ns") {
20!
1535
      setLWResult(res, 0, true, false, true);
20✔
1536
      addRecordToLW(res, ".", QType::SOA, "a.root-servers.net. nstld.verisign-grs.com. 2017032800 1800 900 604800 86400", DNSResourceRecord::AUTHORITY, 86400);
20✔
1537
      return LWResult::Result::Success;
20✔
1538
    }
20✔
1539
    return LWResult::Result::Timeout;
×
1540
  });
20✔
1541

1542
  vector<DNSRecord> ret;
2✔
1543
  try {
2✔
1544
    sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1545
    BOOST_CHECK(0);
2✔
1546
  }
2✔
1547
  catch (const ImmediateServFailException& ex) {
2✔
1548
    BOOST_CHECK_EQUAL(ret.size(), 0U);
2✔
1549
    // one query to get NSs, then A and AAAA for each NS, 5th NS hits the limit
1550
    // limit is reduced to 5, because zone publishes many (20) NS
1551
    BOOST_CHECK_EQUAL(queriesCount, 11U);
2✔
1552
  }
2✔
1553
}
2✔
1554

1555
BOOST_AUTO_TEST_CASE(test_cache_hit)
1556
{
2✔
1557
  std::unique_ptr<SyncRes> sr;
2✔
1558
  initSR(sr);
2✔
1559

1560
  primeHints();
2✔
1561

1562
  const DNSName target("powerdns.com.");
2✔
1563

1564
  sr->setAsyncCallback([&](const ComboAddress& /* ip */, const DNSName& /* domain */, int /* type */, bool /* doTCP */, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval* /* now */, boost::optional<Netmask>& /* srcmask */, const ResolveContext& /* context */, LWResult* /* res */, bool* /* chained */) {
2✔
1565
    return LWResult::Result::Timeout;
×
1566
  });
×
1567

1568
  /* we populate the cache with everything we need */
1569
  time_t now = sr->getNow().tv_sec;
2✔
1570
  std::vector<DNSRecord> records;
2✔
1571
  std::vector<shared_ptr<const RRSIGRecordContent>> sigs;
2✔
1572

1573
  addRecordToList(records, target, QType::A, "192.0.2.1", DNSResourceRecord::ANSWER, now + 3600);
2✔
1574
  g_recCache->replace(now, target, QType(QType::A), records, sigs, {}, true, g_rootdnsname, boost::optional<Netmask>());
2✔
1575

1576
  vector<DNSRecord> ret;
2✔
1577
  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1578
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1579
  BOOST_CHECK_EQUAL(ret.size(), 1U);
2✔
1580
}
2✔
1581

1582
BOOST_AUTO_TEST_CASE(test_no_rd)
1583
{
2✔
1584
  std::unique_ptr<SyncRes> sr;
2✔
1585
  initSR(sr);
2✔
1586

1587
  primeHints();
2✔
1588

1589
  const DNSName target("powerdns.com.");
2✔
1590
  size_t queriesCount = 0;
2✔
1591

1592
  sr->setCacheOnly();
2✔
1593

1594
  sr->setAsyncCallback([&](const ComboAddress& /* ip */, const DNSName& /* domain */, int /* type */, bool /* doTCP */, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval* /* now */, boost::optional<Netmask>& /* srcmask */, const ResolveContext& /* context */, LWResult* /* res */, bool* /* chained */) {
2✔
1595
    queriesCount++;
×
1596
    return LWResult::Result::Timeout;
×
1597
  });
×
1598

1599
  vector<DNSRecord> ret;
2✔
1600
  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1601
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1602
  BOOST_CHECK_EQUAL(ret.size(), 0U);
2✔
1603
  BOOST_CHECK_EQUAL(queriesCount, 0U);
2✔
1604
}
2✔
1605

1606
BOOST_AUTO_TEST_CASE(test_cache_min_max_ttl)
1607
{
2✔
1608
  std::unique_ptr<SyncRes> sr;
2✔
1609
  initSR(sr);
2✔
1610

1611
  primeHints();
2✔
1612

1613
  const DNSName target("cachettl.powerdns.com.");
2✔
1614
  const ComboAddress ns("192.0.2.1:53");
2✔
1615

1616
  sr->setAsyncCallback([&](const ComboAddress& address, const DNSName& domain, int /* type */, bool /* doTCP */, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval* /* now */, boost::optional<Netmask>& /* srcmask */, const ResolveContext& /* context */, LWResult* res, bool* /* chained */) {
4✔
1617
    if (isRootServer(address)) {
4✔
1618

1619
      setLWResult(res, 0, false, false, true);
2✔
1620
      addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
2✔
1621
      addRecordToLW(res, "a.gtld-servers.net.", QType::A, ns.toString(), DNSResourceRecord::ADDITIONAL, 7200);
2✔
1622
      return LWResult::Result::Success;
2✔
1623
    }
2✔
1624
    if (address == ns) {
2!
1625

1626
      setLWResult(res, 0, true, false, false);
2✔
1627
      addRecordToLW(res, domain, QType::A, "192.0.2.2", DNSResourceRecord::ANSWER, 10);
2✔
1628

1629
      return LWResult::Result::Success;
2✔
1630
    }
2✔
1631

1632
    return LWResult::Result::Timeout;
×
1633
  });
2✔
1634

1635
  const time_t now = sr->getNow().tv_sec;
2✔
1636
  SyncRes::s_minimumTTL = 60;
2✔
1637
  SyncRes::s_maxcachettl = 3600;
2✔
1638

1639
  vector<DNSRecord> ret;
2✔
1640
  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1641
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1642
  BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2✔
1643
  BOOST_CHECK_EQUAL(ret[0].d_ttl, SyncRes::s_minimumTTL);
2✔
1644

1645
  const ComboAddress who;
2✔
1646
  vector<DNSRecord> cached;
2✔
1647
  BOOST_REQUIRE_GT(g_recCache->get(now, target, QType(QType::A), MemRecursorCache::RequireAuth, &cached, who), 0);
2✔
1648
  BOOST_REQUIRE_EQUAL(cached.size(), 1U);
2✔
1649
  BOOST_REQUIRE_GT(cached[0].d_ttl, now);
2✔
1650
  BOOST_CHECK_EQUAL((cached[0].d_ttl - now), SyncRes::s_minimumTTL);
2✔
1651

1652
  cached.clear();
2✔
1653
  BOOST_REQUIRE_GT(g_recCache->get(now, target, QType(QType::NS), MemRecursorCache::None, &cached, who), 0);
2✔
1654
  BOOST_REQUIRE_EQUAL(cached.size(), 1U);
2✔
1655
  BOOST_REQUIRE_GT(cached[0].d_ttl, now);
2✔
1656
  BOOST_CHECK_LE((cached[0].d_ttl - now), SyncRes::s_maxcachettl);
2✔
1657
}
2✔
1658

1659
BOOST_AUTO_TEST_CASE(test_cache_min_max_ecs_ttl)
1660
{
2✔
1661
  std::unique_ptr<SyncRes> sr;
2✔
1662
  initSR(sr);
2✔
1663

1664
  primeHints();
2✔
1665

1666
  const DNSName target("cacheecsttl.powerdns.com.");
2✔
1667
  const ComboAddress ns("192.0.2.1:53");
2✔
1668

1669
  EDNSSubnetOpts incomingECS;
2✔
1670
  incomingECS.setSource(Netmask("192.0.2.128/32"));
2✔
1671
  sr->setQuerySource(ComboAddress(), boost::optional<const EDNSSubnetOpts&>(incomingECS));
2✔
1672
  SyncRes::addEDNSDomain(target);
2✔
1673

1674
  sr->setAsyncCallback([&](const ComboAddress& address, const DNSName& domain, int /* type */, bool /* doTCP */, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval* /* now */, boost::optional<Netmask>& srcmask, const ResolveContext& /* context */, LWResult* res, bool* /* chained */) {
4✔
1675
    BOOST_REQUIRE(srcmask);
4✔
1676
    BOOST_CHECK_EQUAL(srcmask->toString(), "192.0.2.0/24");
4✔
1677

1678
    if (isRootServer(address)) {
4✔
1679

1680
      setLWResult(res, 0, false, false, true);
2✔
1681
      addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
2✔
1682
      addRecordToLW(res, "a.gtld-servers.net.", QType::A, ns.toString(), DNSResourceRecord::ADDITIONAL, 20);
2✔
1683
      srcmask = boost::none;
2✔
1684

1685
      return LWResult::Result::Success;
2✔
1686
    }
2✔
1687
    if (address == ns) {
2!
1688

1689
      setLWResult(res, 0, true, false, false);
2✔
1690
      addRecordToLW(res, domain, QType::A, "192.0.2.2", DNSResourceRecord::ANSWER, 10);
2✔
1691

1692
      return LWResult::Result::Success;
2✔
1693
    }
2✔
1694

1695
    return LWResult::Result::Timeout;
×
1696
  });
2✔
1697

1698
  const time_t now = sr->getNow().tv_sec;
2✔
1699
  SyncRes::s_minimumTTL = 60;
2✔
1700
  SyncRes::s_minimumECSTTL = 120;
2✔
1701
  SyncRes::s_maxcachettl = 3600;
2✔
1702

1703
  vector<DNSRecord> ret;
2✔
1704
  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1705
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1706
  BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2✔
1707
  BOOST_CHECK_EQUAL(ret[0].d_ttl, SyncRes::s_minimumECSTTL);
2✔
1708

1709
  const ComboAddress who("192.0.2.128");
2✔
1710
  vector<DNSRecord> cached;
2✔
1711
  BOOST_REQUIRE_GT(g_recCache->get(now, target, QType(QType::A), MemRecursorCache::RequireAuth, &cached, who), 0);
2✔
1712
  BOOST_REQUIRE_EQUAL(cached.size(), 1U);
2✔
1713
  BOOST_REQUIRE_GT(cached[0].d_ttl, now);
2✔
1714
  BOOST_CHECK_EQUAL((cached[0].d_ttl - now), SyncRes::s_minimumECSTTL);
2✔
1715

1716
  cached.clear();
2✔
1717
  BOOST_REQUIRE_GT(g_recCache->get(now, target, QType(QType::NS), MemRecursorCache::None, &cached, who), 0);
2✔
1718
  BOOST_REQUIRE_EQUAL(cached.size(), 1U);
2✔
1719
  BOOST_REQUIRE_GT(cached[0].d_ttl, now);
2✔
1720
  BOOST_CHECK_LE((cached[0].d_ttl - now), SyncRes::s_maxcachettl);
2✔
1721

1722
  cached.clear();
2✔
1723
  BOOST_REQUIRE_GT(g_recCache->get(now, DNSName("a.gtld-servers.net."), QType(QType::A), MemRecursorCache::None, &cached, who), 0);
2✔
1724
  BOOST_REQUIRE_EQUAL(cached.size(), 1U);
2✔
1725
  BOOST_REQUIRE_GT(cached[0].d_ttl, now);
2✔
1726
  BOOST_CHECK_LE((cached[0].d_ttl - now), SyncRes::s_minimumTTL);
2✔
1727
}
2✔
1728

1729
BOOST_AUTO_TEST_CASE(test_cache_expired_ttl)
1730
{
2✔
1731
  std::unique_ptr<SyncRes> sr;
2✔
1732
  initSR(sr);
2✔
1733

1734
  primeHints();
2✔
1735

1736
  const DNSName target("powerdns.com.");
2✔
1737

1738
  sr->setAsyncCallback([&](const ComboAddress& address, const DNSName& domain, int /* type */, bool /* doTCP */, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval* /* now */, boost::optional<Netmask>& /* srcmask */, const ResolveContext& /* context */, LWResult* res, bool* /* chained */) {
4✔
1739
    if (isRootServer(address)) {
4✔
1740
      setLWResult(res, 0, false, false, true);
2✔
1741
      addRecordToLW(res, domain, QType::NS, "pdns-public-ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
2✔
1742

1743
      addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
2✔
1744

1745
      return LWResult::Result::Success;
2✔
1746
    }
2✔
1747
    if (address == ComboAddress("192.0.2.1:53")) {
2!
1748
      setLWResult(res, 0, true, false, true);
2✔
1749
      addRecordToLW(res, domain, QType::A, "192.0.2.2");
2✔
1750
      return LWResult::Result::Success;
2✔
1751
    }
2✔
1752

1753
    return LWResult::Result::Timeout;
×
1754
  });
2✔
1755

1756
  /* we populate the cache with entries that expired 60s ago*/
1757
  const time_t now = sr->getNow().tv_sec;
2✔
1758

1759
  std::vector<DNSRecord> records;
2✔
1760
  std::vector<shared_ptr<const RRSIGRecordContent>> sigs;
2✔
1761
  addRecordToList(records, target, QType::A, "192.0.2.42", DNSResourceRecord::ANSWER, now - 60);
2✔
1762

1763
  g_recCache->replace(now - 3600, target, QType(QType::A), records, sigs, {}, true, g_rootdnsname, boost::optional<Netmask>());
2✔
1764

1765
  vector<DNSRecord> ret;
2✔
1766
  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1767
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1768
  BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2✔
1769
  BOOST_REQUIRE(ret[0].d_type == QType::A);
2✔
1770
  BOOST_CHECK_EQUAL(getRR<ARecordContent>(ret[0])->getCA().toStringWithPort(), ComboAddress("192.0.2.2").toStringWithPort());
2✔
1771
}
2✔
1772

1773
BOOST_AUTO_TEST_CASE(test_cache_almost_expired_ttl)
1774
{
2✔
1775

1776
  std::unique_ptr<SyncRes> sr;
2✔
1777
  initSR(sr);
2✔
1778
  SyncRes::s_refresh_ttlperc = 50;
2✔
1779
  primeHints();
2✔
1780

1781
  const DNSName target("powerdns.com.");
2✔
1782

1783
  auto callback = [&](const ComboAddress& address, const DNSName& domain, int /* type */, bool /* doTCP */, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval* /* now */, boost::optional<Netmask>& /* srcmask */, const ResolveContext& /* context */, LWResult* res, bool* /* chained */) {
4✔
1784
    if (isRootServer(address)) {
4✔
1785
      setLWResult(res, 0, false, false, true);
2✔
1786
      addRecordToLW(res, domain, QType::NS, "pdns-public-ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
2✔
1787

1788
      addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
2✔
1789

1790
      return LWResult::Result::Success;
2✔
1791
    }
2✔
1792
    if (address == ComboAddress("192.0.2.1:53")) {
2!
1793
      setLWResult(res, 0, true, false, true);
2✔
1794
      addRecordToLW(res, domain, QType::A, "192.0.2.2");
2✔
1795
      return LWResult::Result::Success;
2✔
1796
    }
2✔
1797

1798
    return LWResult::Result::Timeout;
×
1799
  };
2✔
1800
  sr->setAsyncCallback(callback);
2✔
1801

1802
  /* we populate the cache with an 60s TTL entry that is 31s old*/
1803
  const time_t now = sr->getNow().tv_sec;
2✔
1804

1805
  std::vector<DNSRecord> records;
2✔
1806
  std::vector<shared_ptr<const RRSIGRecordContent>> sigs;
2✔
1807
  addRecordToList(records, target, QType::A, "192.0.2.2", DNSResourceRecord::ANSWER, now + 29);
2✔
1808

1809
  g_recCache->replace(now - 30, target, QType(QType::A), records, sigs, {}, true, g_rootdnsname, boost::optional<Netmask>(), boost::none, vState::Indeterminate, boost::none, false, now - 31);
2✔
1810

1811
  /* Same for the NS record */
1812
  std::vector<DNSRecord> ns;
2✔
1813
  addRecordToList(ns, target, QType::NS, "pdns-public-ns1.powerdns.com", DNSResourceRecord::ANSWER, now + 29);
2✔
1814
  g_recCache->replace(now - 30, target, QType::NS, ns, sigs, {}, false, target, boost::optional<Netmask>(), boost::none, vState::Indeterminate, boost::none, false, now - 31);
2✔
1815

1816
  vector<DNSRecord> ret;
2✔
1817
  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1818
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1819
  BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2✔
1820
  BOOST_REQUIRE(ret[0].d_type == QType::A);
2✔
1821
  BOOST_CHECK_EQUAL(getRR<ARecordContent>(ret[0])->getCA().toStringWithPort(), ComboAddress("192.0.2.2").toStringWithPort());
2✔
1822
  auto ttl = ret[0].d_ttl;
2✔
1823
  BOOST_CHECK_EQUAL(ttl, 29U);
2✔
1824

1825
  // One task should be submitted
1826
  BOOST_REQUIRE_EQUAL(getTaskSize(), 1U);
2✔
1827

1828
  auto task = taskQueuePop();
2✔
1829

1830
  // Refresh the almost expired record, its NS records also gets updated
1831
  sr->setRefreshAlmostExpired(task.d_refreshMode);
2✔
1832
  ret.clear();
2✔
1833
  res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1834
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1835
  BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2✔
1836
  BOOST_REQUIRE(ret[0].d_type == QType::A);
2✔
1837
  BOOST_CHECK_EQUAL(getRR<ARecordContent>(ret[0])->getCA().toStringWithPort(), ComboAddress("192.0.2.2").toStringWithPort());
2✔
1838
  ttl = ret[0].d_ttl;
2✔
1839
  BOOST_CHECK_EQUAL(ttl, 60U);
2✔
1840

1841
  // Also check if NS record was updated
1842
  ret.clear();
2✔
1843
  BOOST_REQUIRE_GT(g_recCache->get(now, target, QType(QType::NS), MemRecursorCache::None, &ret, ComboAddress()), 0);
2✔
1844
  BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2✔
1845
  BOOST_REQUIRE(ret[0].d_type == QType::NS);
2✔
1846
  BOOST_CHECK_EQUAL(getRR<NSRecordContent>(ret[0])->getNS(), DNSName("pdns-public-ns1.powerdns.com."));
2✔
1847
  ttl = ret[0].d_ttl - now;
2✔
1848
  BOOST_CHECK_EQUAL(ttl, std::min(SyncRes::s_maxcachettl, 172800U));
2✔
1849

1850
  // ATM we are not testing the almost expiry of root infra records, it would require quite some cache massage...
1851
}
2✔
1852

1853
BOOST_AUTO_TEST_SUITE_END()
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