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

PowerDNS / pdns / 12595591960

03 Jan 2025 09:27AM UTC coverage: 62.774% (+2.5%) from 60.245%
12595591960

Pull #15008

github

web-flow
Merge c2a2749d3 into 788f396a7
Pull Request #15008: Do not follow CNAME records for ANY or CNAME queries

30393 of 78644 branches covered (38.65%)

Branch coverage included in aggregate %.

105822 of 138350 relevant lines covered (76.49%)

4613078.44 hits per line

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

89.77
/pdns/recursordist/test-syncres_cc10.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_cc10)
12
BOOST_AUTO_TEST_CASE(test_outgoing_v4_only)
13
{
2✔
14
  std::unique_ptr<SyncRes> sr;
2✔
15
  initSR(sr);
2✔
16
  SyncRes::s_doIPv6 = false;
2✔
17
  primeHints();
2✔
18
  bool v6Hit = false;
2✔
19
  bool v4Hit = false;
2✔
20
  int queries = 0;
2✔
21

22
  const DNSName target("powerdns.com.");
2✔
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 */) {
4✔
24
    queries++;
4✔
25
    if (isRootServer(address)) {
4✔
26
      setLWResult(res, 0, false, false, true);
2✔
27
      v4Hit |= address.isIPv4();
2✔
28
      v6Hit |= address.isIPv6();
2✔
29

30
      if (domain == DNSName("powerdns.com.")) {
2!
31
        addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
2✔
32
        addRecordToLW(res, "ns1.powerdns.com.", QType::AAAA, "2001:DB8:1::53", DNSResourceRecord::ADDITIONAL, 3600);
2✔
33
        addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
2✔
34
      }
2✔
35
      return LWResult::Result::Success;
2✔
36
    }
2✔
37
    if (address == ComboAddress("192.0.2.1:53")) {
2!
38
      setLWResult(res, 0, true, false, false);
2✔
39
      v4Hit |= true;
2✔
40
      if (domain == DNSName("powerdns.com.")) {
2!
41
        addRecordToLW(res, domain, QType::A, "192.0.2.2");
2✔
42
      }
2✔
43
      return LWResult::Result::Success;
2✔
44
    }
2✔
45
    if (address == ComboAddress("[2001:DB8:1::53]:53")) {
×
46
      setLWResult(res, 0, true, false, false);
×
47
      v6Hit |= true;
×
48
      if (domain == DNSName("powerdns.com.")) {
×
49
        addRecordToLW(res, domain, QType::A, "192.0.2.2");
×
50
      }
×
51
      return LWResult::Result::Success;
×
52
    }
×
53
    return LWResult::Result::Timeout;
×
54
  });
×
55

56
  vector<DNSRecord> ret;
2✔
57
  int rcode;
2✔
58
  rcode = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
59
  BOOST_REQUIRE_EQUAL(queries, 2);
2✔
60
  BOOST_REQUIRE_EQUAL(v4Hit, true);
2✔
61
  BOOST_REQUIRE_EQUAL(v6Hit, false);
2✔
62
  BOOST_CHECK_EQUAL(rcode, RCode::NoError);
2✔
63
  BOOST_CHECK_EQUAL(ret.size(), 1U);
2✔
64
}
2✔
65

66
BOOST_AUTO_TEST_CASE(test_outgoing_v4_only_no_A_in_delegation)
67
{
2✔
68
  // The name is not resolvable, as there's no A glue for an in-bailiwick NS
69
  std::unique_ptr<SyncRes> sr;
2✔
70
  initSR(sr);
2✔
71
  SyncRes::s_doIPv6 = false;
2✔
72
  primeHints();
2✔
73
  int queries = 0;
2✔
74

75
  const DNSName target("powerdns.com.");
2✔
76
  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✔
77
    queries++;
28✔
78
    if (isRootServer(address)) {
28!
79
      setLWResult(res, 0, false, false, true);
28✔
80

81
      if (domain == DNSName("powerdns.com.")) {
28✔
82
        addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
2✔
83
        addRecordToLW(res, "ns1.powerdns.com.", QType::AAAA, "2001:DB8:1::53", DNSResourceRecord::ADDITIONAL, 3600);
2✔
84
      }
2✔
85
      return LWResult::Result::Success;
28✔
86
    }
28✔
87
    if (address == ComboAddress("[2001:DB8:1::53]:53")) {
×
88
      setLWResult(res, 0, true, false, false);
×
89
      if (domain == DNSName("powerdns.com.")) {
×
90
        addRecordToLW(res, domain, QType::A, "192.0.2.2");
×
91
      }
×
92
      return LWResult::Result::Success;
×
93
    }
×
94
    return LWResult::Result::Timeout;
×
95
  });
×
96

97
  vector<DNSRecord> ret;
2✔
98
  int rcode;
2✔
99
  rcode = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
100
  BOOST_REQUIRE_EQUAL(queries, 14); // We keep trying all parent nameservers, this is wrong!
2✔
101
  BOOST_CHECK_EQUAL(rcode, RCode::ServFail);
2✔
102
  BOOST_CHECK_EQUAL(ret.size(), 0U);
2✔
103
}
2✔
104

105
BOOST_AUTO_TEST_CASE(test_outgoing_v6_only_no_AAAA_in_delegation)
106
{
2✔
107
  std::unique_ptr<SyncRes> sr;
2✔
108
  initSR(sr);
2✔
109
  SyncRes::s_doIPv4 = false;
2✔
110
  SyncRes::s_doIPv6 = true;
2✔
111
  primeHints();
2✔
112
  int queries = 0;
2✔
113

114
  const DNSName target("powerdns.com.");
2✔
115
  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✔
116
    queries++;
28✔
117
    if (isRootServer(address)) {
28!
118
      setLWResult(res, 0, false, false, true);
28✔
119
      if (domain == DNSName("powerdns.com.")) {
28✔
120
        addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
2✔
121
        addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
2✔
122
      }
2✔
123
      return LWResult::Result::Success;
28✔
124
    }
28✔
125
    if (address == ComboAddress("192.0.2.1:53")) {
×
126
      setLWResult(res, 0, true, false, false);
×
127
      if (domain == DNSName("powerdns.com.")) {
×
128
        addRecordToLW(res, domain, QType::A, "192.0.2.2");
×
129
      }
×
130
      return LWResult::Result::Success;
×
131
    }
×
132
    return LWResult::Result::Timeout;
×
133
  });
×
134

135
  vector<DNSRecord> ret;
2✔
136
  int rcode;
2✔
137
  rcode = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
138
  BOOST_REQUIRE_EQUAL(queries, 14); // The recursor tries all parent nameservers... this needs to be fixed
2✔
139
  BOOST_CHECK_EQUAL(rcode, RCode::ServFail);
2✔
140
  BOOST_CHECK_EQUAL(ret.size(), 0U);
2✔
141
}
2✔
142

143
BOOST_AUTO_TEST_CASE(test_dnssec_secure_to_insecure_skipped_cut_invalid_ds_denial)
144
{
2✔
145
  std::unique_ptr<SyncRes> resolver;
2✔
146
  initSR(resolver, true);
2✔
147

148
  setDNSSECValidation(resolver, DNSSECMode::ValidateAll);
2✔
149

150
  primeHints();
2✔
151
  const DNSName target("www.sub.powerdns.com.");
2✔
152
  const ComboAddress targetAddr("192.0.2.42");
2✔
153
  testkeysset_t keys;
2✔
154

155
  auto luaconfsCopy = g_luaconfs.getCopy();
2✔
156
  luaconfsCopy.dsAnchors.clear();
2✔
157
  generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys, luaconfsCopy.dsAnchors);
2✔
158
  generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
2✔
159
  generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
2✔
160
  generateKeyMaterial(DNSName("sub.powerdns.com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
2✔
161

162
  g_luaconfs.setState(luaconfsCopy);
2✔
163

164
  size_t queriesCount = 0;
2✔
165

166
  resolver->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✔
167
    queriesCount++;
18✔
168

169
    if (type == QType::DS) {
18✔
170
      /* powerdns.com and sub.powerdns.com are signed but not secure (no DS in the parent) */
171
      if (domain == DNSName("powerdns.com.")) {
2!
172
        /* sends a NODATA for the DS */
173
        setLWResult(res, 0, true, false, true);
×
174
        addRecordToLW(res, domain, QType::SOA, "pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 3600);
×
175
        addRRSIG(keys, res->d_records, DNSName("com."), 300);
×
176
        addNSECRecordToLW(domain, DNSName("powerdnsz.com."), {QType::NS}, 600, res->d_records);
×
177
        addRRSIG(keys, res->d_records, DNSName("com."), 300);
×
178
        return LWResult::Result::Success;
×
179
      }
×
180
      if (domain == DNSName("sub.powerdns.com.")) {
2!
181
        /* sends a NODATA for the DS, but it is in fact a NXD proof, which would be bogus if the zone was actually secure */
182
        setLWResult(res, 0, true, false, true);
2✔
183
        addRecordToLW(res, domain, QType::SOA, "pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 3600);
2✔
184
        addRRSIG(keys, res->d_records, DNSName("powerdns.com."), 300);
2✔
185
        addNSECRecordToLW(DNSName("nw.powerdns.com."), DNSName("tz.powerdns.com."), {QType::NS}, 600, res->d_records);
2✔
186
        addRRSIG(keys, res->d_records, DNSName("powerdns.com."), 300);
2✔
187
        return LWResult::Result::Success;
2✔
188
      }
2✔
189
      return genericDSAndDNSKEYHandler(res, domain, domain, type, keys);
×
190
    }
2✔
191
    if (type == QType::DNSKEY) {
16✔
192
      return genericDSAndDNSKEYHandler(res, domain, domain, type, keys);
8✔
193
    }
8✔
194
    {
8✔
195
      if (isRootServer(address)) {
8✔
196
        setLWResult(res, 0, false, false, true);
2✔
197
        addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResourceRecord::AUTHORITY, 3600);
2✔
198
        addDS(DNSName("com."), 300, res->d_records, keys);
2✔
199
        addRRSIG(keys, res->d_records, DNSName("."), 300);
2✔
200
        addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
2✔
201
        return LWResult::Result::Success;
2✔
202
      }
2✔
203
      if (address == ComboAddress("192.0.2.1:53")) {
6✔
204
        if (domain.isPartOf(DNSName("powerdns.com."))) {
2!
205
          setLWResult(res, 0, false, false, true);
2✔
206
          addRecordToLW(res, DNSName("powerdns.com."), QType::NS, "ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 3600);
2✔
207
          /* no DS */
208
          addNSECRecordToLW(DNSName("powerdns.com."), DNSName("powerdnsz.com."), {QType::NS}, 600, res->d_records);
2✔
209
          addRRSIG(keys, res->d_records, DNSName("com."), 300);
2✔
210
          addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
2✔
211
          return LWResult::Result::Success;
2✔
212
        }
2✔
213
      }
2✔
214
      else if (address == ComboAddress("192.0.2.2:53")) {
4!
215
        if (domain.isPartOf(DNSName("sub.powerdns.com."))) {
4✔
216
          setLWResult(res, 0, true, false, true);
2✔
217
          addRecordToLW(res, domain, QType::A, targetAddr.toString(), DNSResourceRecord::ANSWER, 3600);
2✔
218
          addRRSIG(keys, res->d_records, DNSName("sub.powerdns.com."), 300);
2✔
219
          return LWResult::Result::Success;
2✔
220
        }
2✔
221
        if (domain == DNSName("nx.powerdns.com.")) {
2!
222
          setLWResult(res, 0, true, false, true);
2✔
223
          addRecordToLW(res, DNSName("powerdns.com."), QType::SOA, "pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 3600);
2✔
224
          addRRSIG(keys, res->d_records, DNSName("powerdns.com."), 300);
2✔
225
          /* sends a NODATA for the DS, but it is in fact a NXD proof, which would be bogus if the zone was actually secure */
226
          addNSECRecordToLW(DNSName("nw.powerdns.com."), DNSName("tz.powerdns.com."), {QType::A}, 600, res->d_records);
2✔
227
          addRRSIG(keys, res->d_records, DNSName("com."), 300);
2✔
228
          return LWResult::Result::Success;
2✔
229
        }
2✔
230
      }
2✔
231
    }
6✔
232

233
    return LWResult::Result::Timeout;
×
234
  });
6✔
235

236
  /* first we do a query in the parent zone (powerdns.com), insecure,
237
     to get the NS in cache so we don't learn the zone cut before
238
     validating */
239
  vector<DNSRecord> ret;
2✔
240
  int res = resolver->beginResolve(DNSName("nx.powerdns.com."), QType(QType::A), QClass::IN, ret);
2✔
241
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
242
  BOOST_CHECK_EQUAL(resolver->getValidationState(), vState::Insecure);
2✔
243
  BOOST_REQUIRE_EQUAL(ret.size(), 4U);
2✔
244
  BOOST_CHECK(ret.at(0).d_type == QType::SOA);
2✔
245
  BOOST_CHECK(ret.at(1).d_type == QType::RRSIG);
2✔
246
  BOOST_CHECK(ret.at(2).d_type == QType::NSEC);
2✔
247
  BOOST_CHECK(ret.at(3).d_type == QType::RRSIG);
2✔
248
  BOOST_CHECK_EQUAL(queriesCount, 6U);
2✔
249

250
  /* now we query the sub zone */
251
  ret.clear();
2✔
252
  res = resolver->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
253
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
254
  BOOST_CHECK_EQUAL(resolver->getValidationState(), vState::Insecure);
2✔
255
  BOOST_REQUIRE_EQUAL(ret.size(), 2U);
2✔
256
  BOOST_CHECK(ret[0].d_type == QType::A);
2✔
257
  BOOST_CHECK_EQUAL(queriesCount, 9U);
2✔
258

259
  /* again, to test the cache */
260
  ret.clear();
2✔
261
  res = resolver->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
262
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
263
  BOOST_CHECK_EQUAL(resolver->getValidationState(), vState::Insecure);
2✔
264
  BOOST_REQUIRE_EQUAL(ret.size(), 2U);
2✔
265
  BOOST_CHECK(ret[0].d_type == QType::A);
2✔
266
  BOOST_CHECK_EQUAL(queriesCount, 9U);
2✔
267
}
2✔
268

269
BOOST_AUTO_TEST_CASE(test_dnssec_secure_to_insecure_wrong_rrsig_fake_signer)
270
{
2✔
271
  /* We have an insecure (no DS at the parent) but signed zone, albeit
272
     badly broken (RRSIG do not validate, the signer is clearly not right).
273
     Check that we correctly detect the zone as Insecure.
274
  */
275
  std::unique_ptr<SyncRes> sr;
2✔
276
  initSR(sr, true);
2✔
277

278
  setDNSSECValidation(sr, DNSSECMode::ValidateAll);
2✔
279

280
  primeHints();
2✔
281
  const DNSName target("www.sub.powerdns.com.");
2✔
282
  const ComboAddress targetAddr("192.0.2.42");
2✔
283
  testkeysset_t keys;
2✔
284

285
  auto luaconfsCopy = g_luaconfs.getCopy();
2✔
286
  luaconfsCopy.dsAnchors.clear();
2✔
287
  generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys, luaconfsCopy.dsAnchors);
2✔
288
  generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
2✔
289
  generateKeyMaterial(DNSName("sub.powerdns.com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
2✔
290

291
  g_luaconfs.setState(luaconfsCopy);
2✔
292

293
  size_t queriesCount = 0;
2✔
294

295
  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 */) {
12✔
296
    queriesCount++;
12✔
297

298
    if (type == QType::DS) {
12!
299
      /* powerdns.com is not signed.
300
         sub.powerdns.com is signed but not secure, and actually badly broken */
301
      if (domain == DNSName("powerdns.com.")) {
×
302
        /* sends a NODATA for the DS */
303
        setLWResult(res, 0, true, false, true);
×
304
        addRecordToLW(res, domain, QType::SOA, "pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 3600);
×
305
        addRRSIG(keys, res->d_records, DNSName("com."), 300);
×
306
        addNSECRecordToLW(domain, DNSName("powerdnsz.com."), {QType::NS}, 600, res->d_records);
×
307
        addRRSIG(keys, res->d_records, DNSName("com."), 300);
×
308
        return LWResult::Result::Success;
×
309
      }
×
310
      return genericDSAndDNSKEYHandler(res, domain, domain, type, keys);
×
311
    }
×
312
    if (type == QType::DNSKEY) {
12✔
313
      return genericDSAndDNSKEYHandler(res, domain, domain, type, keys);
4✔
314
    }
4✔
315
    {
8✔
316
      if (isRootServer(address)) {
8✔
317
        setLWResult(res, 0, false, false, true);
2✔
318
        addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResourceRecord::AUTHORITY, 3600);
2✔
319
        addDS(DNSName("com."), 300, res->d_records, keys);
2✔
320
        addRRSIG(keys, res->d_records, DNSName("."), 300);
2✔
321
        addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
2✔
322
        return LWResult::Result::Success;
2✔
323
      }
2✔
324
      if (address == ComboAddress("192.0.2.1:53")) {
6✔
325
        if (domain.isPartOf(DNSName("powerdns.com."))) {
2!
326
          setLWResult(res, 0, false, false, true);
2✔
327
          addRecordToLW(res, DNSName("powerdns.com."), QType::NS, "ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 3600);
2✔
328
          /* no DS */
329
          addNSECRecordToLW(DNSName("powerdns.com."), DNSName("powerdnsz.com."), {QType::NS}, 600, res->d_records);
2✔
330
          addRRSIG(keys, res->d_records, DNSName("com."), 300);
2✔
331
          addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
2✔
332
          return LWResult::Result::Success;
2✔
333
        }
2✔
334
      }
2✔
335
      else if (address == ComboAddress("192.0.2.2:53")) {
4!
336
        if (domain.isPartOf(DNSName("sub.powerdns.com."))) {
4✔
337
          setLWResult(res, 0, true, false, true);
2✔
338
          addRecordToLW(res, domain, QType::A, targetAddr.toString(), DNSResourceRecord::ANSWER, 3600);
2✔
339
          addRRSIG(keys, res->d_records, DNSName("com.") /* wrong signer !! */, 300, /* broken !!!*/ true);
2✔
340
          return LWResult::Result::Success;
2✔
341
        }
2✔
342
        if (domain == DNSName("www.powerdns.com.")) {
2!
343
          setLWResult(res, 0, true, false, true);
2✔
344
          addRecordToLW(res, domain, QType::A, targetAddr.toString(), DNSResourceRecord::ANSWER, 3600);
2✔
345
          return LWResult::Result::Success;
2✔
346
        }
2✔
347
      }
2✔
348
    }
6✔
349

350
    return LWResult::Result::Timeout;
×
351
  });
6✔
352

353
  /* first we do a query in the parent zone (powerdns.com), insecure,
354
     to get the NS in cache so we don't learn the zone cut before
355
     validating */
356
  vector<DNSRecord> ret;
2✔
357
  int res = sr->beginResolve(DNSName("www.powerdns.com."), QType(QType::A), QClass::IN, ret);
2✔
358
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
359
  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
2✔
360
  BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2✔
361
  BOOST_CHECK(ret.at(0).d_type == QType::A);
2✔
362
  BOOST_CHECK_EQUAL(queriesCount, 5U);
2✔
363

364
  /* now we query the sub zone */
365
  ret.clear();
2✔
366
  res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
367
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
368
  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
2✔
369
  BOOST_REQUIRE_EQUAL(ret.size(), 2U);
2✔
370
  BOOST_CHECK(ret[0].d_type == QType::A);
2✔
371
  BOOST_CHECK_EQUAL(queriesCount, 6U);
2✔
372

373
  /* again, to test the cache */
374
  ret.clear();
2✔
375
  res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
376
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
377
  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
2✔
378
  BOOST_REQUIRE_EQUAL(ret.size(), 2U);
2✔
379
  BOOST_CHECK(ret[0].d_type == QType::A);
2✔
380
  BOOST_CHECK_EQUAL(queriesCount, 6U);
2✔
381
}
2✔
382

383
BOOST_AUTO_TEST_CASE(test_dnssec_secure_to_insecure_missing_soa)
384
{
2✔
385
  /* We have an insecure (no DS at the parent) but signed zone, albeit
386
     slightly broken (no SOA in NXD/NODATA answers).
387
     Check that we correctly detect the zone as Insecure.
388
  */
389
  std::unique_ptr<SyncRes> sr;
2✔
390
  initSR(sr, true);
2✔
391

392
  setDNSSECValidation(sr, DNSSECMode::ValidateAll);
2✔
393

394
  primeHints();
2✔
395
  const ComboAddress targetAddr("192.0.2.42");
2✔
396
  testkeysset_t keys;
2✔
397

398
  auto luaconfsCopy = g_luaconfs.getCopy();
2✔
399
  luaconfsCopy.dsAnchors.clear();
2✔
400
  generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys, luaconfsCopy.dsAnchors);
2✔
401
  generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
2✔
402
  generateKeyMaterial(DNSName("sub.powerdns.com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
2✔
403

404
  g_luaconfs.setState(luaconfsCopy);
2✔
405

406
  size_t queriesCount = 0;
2✔
407

408
  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✔
409
    queriesCount++;
22✔
410

411
    if (type == QType::DS) {
22✔
412
      /* powerdns.com is not signed.
413
         sub.powerdns.com is signed but not secure */
414
      if (domain == DNSName("powerdns.com.")) {
2!
415
        /* sends a NODATA for the DS */
416
        setLWResult(res, 0, true, false, true);
×
417
        addRecordToLW(res, domain, QType::SOA, "pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 3600);
×
418
        addRRSIG(keys, res->d_records, DNSName("com."), 300);
×
419
        addNSECRecordToLW(domain, DNSName("powerdnsz.com."), {QType::NS}, 600, res->d_records);
×
420
        addRRSIG(keys, res->d_records, DNSName("com."), 300);
×
421
        return LWResult::Result::Success;
×
422
      }
×
423
      if (domain == DNSName("sub.powerdns.com.")) {
2!
424
        /* sends a NODATA for the DS */
425
        setLWResult(res, 0, true, false, true);
2✔
426
        addRecordToLW(res, domain, QType::SOA, "pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 3600);
2✔
427
        return LWResult::Result::Success;
2✔
428
      }
2✔
429
      return genericDSAndDNSKEYHandler(res, domain, domain, type, keys);
×
430
    }
2✔
431
    if (type == QType::DNSKEY) {
20✔
432
      return genericDSAndDNSKEYHandler(res, domain, domain, type, keys);
6✔
433
    }
6✔
434
    {
14✔
435
      if (isRootServer(address)) {
14✔
436
        setLWResult(res, 0, false, false, true);
2✔
437
        addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResourceRecord::AUTHORITY, 3600);
2✔
438
        addDS(DNSName("com."), 300, res->d_records, keys);
2✔
439
        addRRSIG(keys, res->d_records, DNSName("."), 300);
2✔
440
        addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
2✔
441
        return LWResult::Result::Success;
2✔
442
      }
2✔
443
      if (address == ComboAddress("192.0.2.1:53")) {
12✔
444
        if (domain.isPartOf(DNSName("powerdns.com."))) {
2!
445
          setLWResult(res, 0, false, false, true);
2✔
446
          addRecordToLW(res, DNSName("powerdns.com."), QType::NS, "ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 3600);
2✔
447
          /* no DS */
448
          addNSECRecordToLW(DNSName("powerdns.com."), DNSName("powerdnsz.com."), {QType::NS}, 600, res->d_records);
2✔
449
          addRRSIG(keys, res->d_records, DNSName("com."), 300);
2✔
450
          addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
2✔
451
          return LWResult::Result::Success;
2✔
452
        }
2✔
453
      }
2✔
454
      else if (address == ComboAddress("192.0.2.2:53")) {
10!
455
        if (domain == DNSName("nxd.sub.powerdns.com.")) {
10✔
456
          setLWResult(res, RCode::NXDomain, true, false, true);
4✔
457
          addNSECRecordToLW(DNSName("nxc.sub.powerdns.com."), DNSName("nxe.sub.powerdnsz.com."), {QType::AAAA}, 600, res->d_records);
4✔
458
          addRRSIG(keys, res->d_records, DNSName("sub.powerdns.com."), 300);
4✔
459
          return LWResult::Result::Success;
4✔
460
        }
4✔
461
        if (domain == DNSName("nodata.sub.powerdns.com.")) {
6✔
462
          setLWResult(res, 0, true, false, true);
4✔
463
          /* NSEC but no SOA */
464
          addNSECRecordToLW(DNSName("nodata.sub.powerdns.com."), DNSName("nodata2.sub.powerdnsz.com."), {QType::AAAA}, 600, res->d_records);
4✔
465
          addRRSIG(keys, res->d_records, DNSName("sub.powerdns.com."), 300);
4✔
466
          return LWResult::Result::Success;
4✔
467
        }
4✔
468
        if (domain == DNSName("www.powerdns.com.")) {
2!
469
          setLWResult(res, 0, true, false, true);
2✔
470
          addRecordToLW(res, domain, QType::A, targetAddr.toString(), DNSResourceRecord::ANSWER, 3600);
2✔
471
          return LWResult::Result::Success;
2✔
472
        }
2✔
473
      }
2✔
474
    }
12✔
475

476
    return LWResult::Result::Timeout;
×
477
  });
12✔
478

479
  /* first we do a query in the parent zone (powerdns.com), insecure,
480
     to get the NS in cache so we don't learn the zone cut before
481
     validating */
482
  vector<DNSRecord> ret;
2✔
483
  int res = sr->beginResolve(DNSName("www.powerdns.com."), QType(QType::A), QClass::IN, ret);
2✔
484
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
485
  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
2✔
486
  BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2✔
487
  BOOST_CHECK(ret.at(0).d_type == QType::A);
2✔
488
  BOOST_CHECK_EQUAL(queriesCount, 5U);
2✔
489

490
  /* now we query the sub zone for the NXD */
491
  ret.clear();
2✔
492
  res = sr->beginResolve(DNSName("nxd.sub.powerdns.com."), QType(QType::A), QClass::IN, ret);
2✔
493
  BOOST_CHECK_EQUAL(res, RCode::NXDomain);
2✔
494
  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
2✔
495
  BOOST_REQUIRE_EQUAL(ret.size(), 2U);
2✔
496
  BOOST_CHECK(ret[0].d_type == QType::NSEC);
2✔
497
  BOOST_CHECK_EQUAL(queriesCount, 8U);
2✔
498

499
  /* again, to test the cache (we need to do the query again because there was no SOA -> no neg caching) */
500
  ret.clear();
2✔
501
  res = sr->beginResolve(DNSName("nxd.sub.powerdns.com."), QType(QType::A), QClass::IN, ret);
2✔
502
  BOOST_CHECK_EQUAL(res, RCode::NXDomain);
2✔
503
  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
2✔
504
  BOOST_REQUIRE_EQUAL(ret.size(), 2U);
2✔
505
  BOOST_CHECK(ret[0].d_type == QType::NSEC);
2✔
506
  BOOST_CHECK_EQUAL(queriesCount, 9U);
2✔
507

508
  /* now we query the sub zone for the NODATA */
509
  ret.clear();
2✔
510
  res = sr->beginResolve(DNSName("nodata.sub.powerdns.com."), QType(QType::A), QClass::IN, ret);
2✔
511
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
512
  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
2✔
513
  BOOST_REQUIRE_EQUAL(ret.size(), 2U);
2✔
514
  BOOST_CHECK(ret[0].d_type == QType::NSEC);
2✔
515
  BOOST_CHECK_EQUAL(queriesCount, 10U);
2✔
516

517
  /* again, to test the cache */
518
  ret.clear();
2✔
519
  res = sr->beginResolve(DNSName("nodata.sub.powerdns.com."), QType(QType::A), QClass::IN, ret);
2✔
520
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
521
  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
2✔
522
  BOOST_REQUIRE_EQUAL(ret.size(), 2U);
2✔
523
  BOOST_CHECK(ret[0].d_type == QType::NSEC);
2✔
524
  BOOST_CHECK_EQUAL(queriesCount, 11U);
2✔
525
}
2✔
526

527
BOOST_AUTO_TEST_CASE(test_dnssec_secure_to_insecure_missing_dnskey)
528
{
2✔
529
  /* We have an insecure (no DS at the parent) but signed zone, albeit
530
     slightly broken (no DNSKEY).
531
     Check that we correctly detect the zone as Insecure.
532
  */
533
  std::unique_ptr<SyncRes> sr;
2✔
534
  initSR(sr, true);
2✔
535

536
  setDNSSECValidation(sr, DNSSECMode::ValidateAll);
2✔
537

538
  primeHints();
2✔
539
  const ComboAddress targetAddr("192.0.2.42");
2✔
540
  testkeysset_t keys;
2✔
541

542
  auto luaconfsCopy = g_luaconfs.getCopy();
2✔
543
  luaconfsCopy.dsAnchors.clear();
2✔
544
  generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys, luaconfsCopy.dsAnchors);
2✔
545
  generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
2✔
546
  generateKeyMaterial(DNSName("sub.powerdns.com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
2✔
547

548
  g_luaconfs.setState(luaconfsCopy);
2✔
549

550
  size_t queriesCount = 0;
2✔
551

552
  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 */) {
16✔
553
    queriesCount++;
16✔
554

555
    if (type == QType::DS) {
16✔
556
      /* powerdns.com is not signed.
557
         sub.powerdns.com is signed but not secure */
558
      if (domain == DNSName("powerdns.com.")) {
2!
559
        /* sends a NODATA for the DS */
560
        setLWResult(res, 0, true, false, true);
×
561
        addRecordToLW(res, domain, QType::SOA, "pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 3600);
×
562
        addRRSIG(keys, res->d_records, DNSName("com."), 300);
×
563
        addNSECRecordToLW(domain, DNSName("powerdnsz.com."), {QType::NS}, 600, res->d_records);
×
564
        addRRSIG(keys, res->d_records, DNSName("com."), 300);
×
565
        return LWResult::Result::Success;
×
566
      }
×
567
      if (domain == DNSName("sub.powerdns.com.")) {
2!
568
        /* sends a NODATA for the DS */
569
        setLWResult(res, 0, true, false, true);
2✔
570
        addRecordToLW(res, domain, QType::SOA, "pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 3600);
2✔
571
        return LWResult::Result::Success;
2✔
572
      }
2✔
573
      return genericDSAndDNSKEYHandler(res, domain, domain, type, keys);
×
574
    }
2✔
575
    if (type == QType::DNSKEY) {
14✔
576
      if (domain == DNSName("sub.powerdns.com.")) {
6✔
577
        /* sends a NODATA for the DNSKEY */
578
        setLWResult(res, 0, true, false, true);
2✔
579
        addRecordToLW(res, domain, QType::SOA, "pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 3600);
2✔
580
        addRRSIG(keys, res->d_records, DNSName("sub.powerdns.com."), 300);
2✔
581
        addNSECRecordToLW(domain, DNSName("sub2.powerdns.com."), {QType::SOA}, 600, res->d_records);
2✔
582
        addRRSIG(keys, res->d_records, DNSName("sub.powerdns.com."), 300);
2✔
583
        return LWResult::Result::Success;
2✔
584
      }
2✔
585
      return genericDSAndDNSKEYHandler(res, domain, domain, type, keys);
4✔
586
    }
6✔
587
    {
8✔
588
      if (isRootServer(address)) {
8✔
589
        setLWResult(res, 0, false, false, true);
2✔
590
        addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResourceRecord::AUTHORITY, 3600);
2✔
591
        addDS(DNSName("com."), 300, res->d_records, keys);
2✔
592
        addRRSIG(keys, res->d_records, DNSName("."), 300);
2✔
593
        addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
2✔
594
        return LWResult::Result::Success;
2✔
595
      }
2✔
596
      if (address == ComboAddress("192.0.2.1:53")) {
6✔
597
        if (domain.isPartOf(DNSName("powerdns.com."))) {
2!
598
          setLWResult(res, 0, false, false, true);
2✔
599
          addRecordToLW(res, DNSName("powerdns.com."), QType::NS, "ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 3600);
2✔
600
          /* no DS */
601
          addNSECRecordToLW(DNSName("powerdns.com."), DNSName("powerdnsz.com."), {QType::NS}, 600, res->d_records);
2✔
602
          addRRSIG(keys, res->d_records, DNSName("com."), 300);
2✔
603
          addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
2✔
604
          return LWResult::Result::Success;
2✔
605
        }
2✔
606
      }
2✔
607
      else if (address == ComboAddress("192.0.2.2:53")) {
4!
608
        if (domain == DNSName("www.sub.powerdns.com.")) {
4✔
609
          setLWResult(res, 0, true, false, true);
2✔
610
          addRecordToLW(res, domain, QType::A, targetAddr.toString(), DNSResourceRecord::ANSWER, 3600);
2✔
611
          addRRSIG(keys, res->d_records, DNSName("sub.powerdns.com."), 300);
2✔
612
          return LWResult::Result::Success;
2✔
613
        }
2✔
614
        if (domain == DNSName("www.powerdns.com.")) {
2!
615
          setLWResult(res, 0, true, false, true);
2✔
616
          addRecordToLW(res, domain, QType::A, targetAddr.toString(), DNSResourceRecord::ANSWER, 3600);
2✔
617
          return LWResult::Result::Success;
2✔
618
        }
2✔
619
      }
2✔
620
    }
6✔
621

622
    return LWResult::Result::Timeout;
×
623
  });
6✔
624

625
  /* first we do a query in the parent zone (powerdns.com), insecure,
626
     to get the NS in cache so we don't learn the zone cut before
627
     validating */
628
  vector<DNSRecord> ret;
2✔
629
  int res = sr->beginResolve(DNSName("www.powerdns.com."), QType(QType::A), QClass::IN, ret);
2✔
630
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
631
  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
2✔
632
  BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2✔
633
  BOOST_CHECK(ret.at(0).d_type == QType::A);
2✔
634
  BOOST_CHECK_EQUAL(queriesCount, 5U);
2✔
635

636
  /* now we query the sub zone */
637
  ret.clear();
2✔
638
  res = sr->beginResolve(DNSName("www.sub.powerdns.com."), QType(QType::A), QClass::IN, ret);
2✔
639
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
640
  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
2✔
641
  BOOST_REQUIRE_EQUAL(ret.size(), 2U);
2✔
642
  BOOST_CHECK(ret[0].d_type == QType::A);
2✔
643
  BOOST_CHECK_EQUAL(queriesCount, 8U);
2✔
644

645
  /* again, to test the cache */
646
  ret.clear();
2✔
647
  res = sr->beginResolve(DNSName("www.sub.powerdns.com."), QType(QType::A), QClass::IN, ret);
2✔
648
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
649
  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
2✔
650
  BOOST_REQUIRE_EQUAL(ret.size(), 2U);
2✔
651
  BOOST_CHECK(ret[0].d_type == QType::A);
2✔
652
  BOOST_CHECK_EQUAL(queriesCount, 8U);
2✔
653
}
2✔
654

655
BOOST_AUTO_TEST_CASE(test_dnssec_secure_to_insecure_nxd_dnskey)
656
{
2✔
657
  /* We have an insecure (no DS at the parent) but signed zone, albeit
658
     slightly broken (no DNSKEY, returning NXD while there is data there).
659
     Check that we correctly detect the zone as Insecure.
660
  */
661
  std::unique_ptr<SyncRes> sr;
2✔
662
  initSR(sr, true);
2✔
663

664
  setDNSSECValidation(sr, DNSSECMode::ValidateAll);
2✔
665

666
  primeHints();
2✔
667
  const ComboAddress targetAddr("192.0.2.42");
2✔
668
  testkeysset_t keys;
2✔
669

670
  auto luaconfsCopy = g_luaconfs.getCopy();
2✔
671
  luaconfsCopy.dsAnchors.clear();
2✔
672
  generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys, luaconfsCopy.dsAnchors);
2✔
673
  generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
2✔
674
  generateKeyMaterial(DNSName("sub.powerdns.com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
2✔
675

676
  g_luaconfs.setState(luaconfsCopy);
2✔
677

678
  size_t queriesCount = 0;
2✔
679

680
  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 */) {
14✔
681
    queriesCount++;
14✔
682

683
    if (type == QType::DS) {
14!
684
      /* powerdns.com is not signed.
685
         sub.powerdns.com is signed but not secure */
686
      if (domain == DNSName("powerdns.com.")) {
×
687
        /* sends a NODATA for the DS */
688
        setLWResult(res, 0, true, false, true);
×
689
        addRecordToLW(res, domain, QType::SOA, "pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 3600);
×
690
        addRRSIG(keys, res->d_records, DNSName("com."), 300);
×
691
        addNSECRecordToLW(domain, DNSName("powerdnsz.com."), {QType::NS}, 600, res->d_records);
×
692
        addRRSIG(keys, res->d_records, DNSName("com."), 300);
×
693
        return LWResult::Result::Success;
×
694
      }
×
695
      if (domain == DNSName("sub.powerdns.com.")) {
×
696
        /* sends a NODATA for the DS */
697
        setLWResult(res, 0, true, false, true);
×
698
        addRecordToLW(res, domain, QType::SOA, "pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 3600);
×
699
        return LWResult::Result::Success;
×
700
      }
×
701
      return genericDSAndDNSKEYHandler(res, domain, domain, type, keys);
×
702
    }
×
703
    if (type == QType::DNSKEY) {
14✔
704
      if (domain == DNSName("sub.powerdns.com.")) {
6✔
705
        /* sends a NXD for the DNSKEY */
706
        setLWResult(res, RCode::NXDomain, true, false, true);
2✔
707
        addRecordToLW(res, domain, QType::SOA, "pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 3600);
2✔
708
        addRRSIG(keys, res->d_records, DNSName("sub.powerdns.com."), 300);
2✔
709
        addNSECRecordToLW(DNSName("sua.powerdnsz.com."), DNSName("suc.powerdns.com."), {QType::SOA}, 600, res->d_records);
2✔
710
        addRRSIG(keys, res->d_records, DNSName("sub.powerdns.com."), 300);
2✔
711
        return LWResult::Result::Success;
2✔
712
      }
2✔
713
      return genericDSAndDNSKEYHandler(res, domain, domain, type, keys);
4✔
714
    }
6✔
715
    {
8✔
716
      if (isRootServer(address)) {
8✔
717
        setLWResult(res, 0, false, false, true);
2✔
718
        addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResourceRecord::AUTHORITY, 3600);
2✔
719
        addDS(DNSName("com."), 300, res->d_records, keys);
2✔
720
        addRRSIG(keys, res->d_records, DNSName("."), 300);
2✔
721
        addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
2✔
722
        return LWResult::Result::Success;
2✔
723
      }
2✔
724
      if (address == ComboAddress("192.0.2.1:53")) {
6✔
725
        if (domain.isPartOf(DNSName("powerdns.com."))) {
2!
726
          setLWResult(res, 0, false, false, true);
2✔
727
          addRecordToLW(res, DNSName("powerdns.com."), QType::NS, "ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 3600);
2✔
728
          /* no DS */
729
          addNSECRecordToLW(DNSName("powerdns.com."), DNSName("powerdnsz.com."), {QType::NS}, 600, res->d_records);
2✔
730
          addRRSIG(keys, res->d_records, DNSName("com."), 300);
2✔
731
          addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
2✔
732
          return LWResult::Result::Success;
2✔
733
        }
2✔
734
      }
2✔
735
      else if (address == ComboAddress("192.0.2.2:53")) {
4!
736
        if (domain == DNSName("www.sub.powerdns.com.")) {
4✔
737
          setLWResult(res, 0, true, false, true);
2✔
738
          addRecordToLW(res, domain, QType::A, targetAddr.toString(), DNSResourceRecord::ANSWER, 3600);
2✔
739
          addRRSIG(keys, res->d_records, DNSName("sub.powerdns.com."), 300);
2✔
740
          return LWResult::Result::Success;
2✔
741
        }
2✔
742
        if (domain == DNSName("www.powerdns.com.")) {
2!
743
          setLWResult(res, 0, true, false, true);
2✔
744
          addRecordToLW(res, domain, QType::A, targetAddr.toString(), DNSResourceRecord::ANSWER, 3600);
2✔
745
          return LWResult::Result::Success;
2✔
746
        }
2✔
747
      }
2✔
748
    }
6✔
749

750
    return LWResult::Result::Timeout;
×
751
  });
6✔
752

753
  /* first we do a query in the parent zone (powerdns.com), insecure,
754
     to get the NS in cache so we don't learn the zone cut before
755
     validating */
756
  vector<DNSRecord> ret;
2✔
757
  int res = sr->beginResolve(DNSName("www.powerdns.com."), QType(QType::A), QClass::IN, ret);
2✔
758
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
759
  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
2✔
760
  BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2✔
761
  BOOST_CHECK(ret.at(0).d_type == QType::A);
2✔
762
  BOOST_CHECK_EQUAL(queriesCount, 5U);
2✔
763

764
  /* now we query the sub zone */
765
  ret.clear();
2✔
766
  res = sr->beginResolve(DNSName("www.sub.powerdns.com."), QType(QType::A), QClass::IN, ret);
2✔
767
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
768
  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
2✔
769
  BOOST_REQUIRE_EQUAL(ret.size(), 2U);
2✔
770
  BOOST_CHECK(ret[0].d_type == QType::A);
2✔
771
  BOOST_CHECK_EQUAL(queriesCount, 7U);
2✔
772

773
  /* again, to test the cache */
774
  ret.clear();
2✔
775
  res = sr->beginResolve(DNSName("www.sub.powerdns.com."), QType(QType::A), QClass::IN, ret);
2✔
776
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
777
  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
2✔
778
  BOOST_REQUIRE_EQUAL(ret.size(), 2U);
2✔
779
  BOOST_CHECK(ret[0].d_type == QType::A);
2✔
780
  BOOST_CHECK_EQUAL(queriesCount, 7U);
2✔
781
}
2✔
782

783
BOOST_AUTO_TEST_CASE(test_dnssec_secure_to_insecure_nxd_ds)
784
{
2✔
785
  /* We have an insecure (no DS at the parent) but signed zone, albeit
786
     slightly broken (returning NXD when asking for the DS while there is data there).
787
     Check that we correctly detect the zone as Insecure.
788
  */
789
  std::unique_ptr<SyncRes> sr;
2✔
790
  initSR(sr, true);
2✔
791

792
  setDNSSECValidation(sr, DNSSECMode::ValidateAll);
2✔
793

794
  primeHints();
2✔
795
  const ComboAddress targetAddr("192.0.2.42");
2✔
796
  testkeysset_t keys;
2✔
797

798
  auto luaconfsCopy = g_luaconfs.getCopy();
2✔
799
  luaconfsCopy.dsAnchors.clear();
2✔
800
  generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys, luaconfsCopy.dsAnchors);
2✔
801
  generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
2✔
802
  generateKeyMaterial(DNSName("sub.powerdns.com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
2✔
803

804
  g_luaconfs.setState(luaconfsCopy);
2✔
805

806
  size_t queriesCount = 0;
2✔
807

808
  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 */) {
16✔
809
    queriesCount++;
16✔
810

811
    if (type == QType::DS) {
16✔
812
      /* powerdns.com is not signed.
813
         sub.powerdns.com is signed but not secure */
814
      if (domain == DNSName("powerdns.com.")) {
2!
815
        /* sends a NODATA for the DS */
816
        setLWResult(res, 0, true, false, true);
×
817
        addRecordToLW(res, domain, QType::SOA, "pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 3600);
×
818
        addRRSIG(keys, res->d_records, DNSName("com."), 300);
×
819
        addNSECRecordToLW(domain, DNSName("powerdnsz.com."), {QType::NS}, 600, res->d_records);
×
820
        addRRSIG(keys, res->d_records, DNSName("com."), 300);
×
821
        return LWResult::Result::Success;
×
822
      }
×
823
      if (domain == DNSName("sub.powerdns.com.")) {
2!
824
        /* sends a NXD!! for the DS */
825
        setLWResult(res, RCode::NXDomain, true, false, true);
2✔
826
        addRecordToLW(res, domain, QType::SOA, "pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 3600);
2✔
827
        return LWResult::Result::Success;
2✔
828
      }
2✔
829
      return genericDSAndDNSKEYHandler(res, domain, domain, type, keys);
×
830
    }
2✔
831
    if (type == QType::DNSKEY) {
14✔
832
      return genericDSAndDNSKEYHandler(res, domain, domain, type, keys);
6✔
833
    }
6✔
834
    {
8✔
835
      if (isRootServer(address)) {
8✔
836
        setLWResult(res, 0, false, false, true);
2✔
837
        addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResourceRecord::AUTHORITY, 3600);
2✔
838
        addDS(DNSName("com."), 300, res->d_records, keys);
2✔
839
        addRRSIG(keys, res->d_records, DNSName("."), 300);
2✔
840
        addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
2✔
841
        return LWResult::Result::Success;
2✔
842
      }
2✔
843
      if (address == ComboAddress("192.0.2.1:53")) {
6✔
844
        if (domain.isPartOf(DNSName("powerdns.com."))) {
2!
845
          setLWResult(res, 0, false, false, true);
2✔
846
          addRecordToLW(res, DNSName("powerdns.com."), QType::NS, "ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 3600);
2✔
847
          /* no DS */
848
          addNSECRecordToLW(DNSName("powerdns.com."), DNSName("powerdnsz.com."), {QType::NS}, 600, res->d_records);
2✔
849
          addRRSIG(keys, res->d_records, DNSName("com."), 300);
2✔
850
          addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
2✔
851
          return LWResult::Result::Success;
2✔
852
        }
2✔
853
      }
2✔
854
      else if (address == ComboAddress("192.0.2.2:53")) {
4!
855
        if (domain == DNSName("www.sub.powerdns.com.")) {
4✔
856
          setLWResult(res, 0, true, false, true);
2✔
857
          addRecordToLW(res, domain, QType::A, targetAddr.toString(), DNSResourceRecord::ANSWER, 3600);
2✔
858
          addRRSIG(keys, res->d_records, DNSName("sub.powerdns.com."), 300);
2✔
859
          return LWResult::Result::Success;
2✔
860
        }
2✔
861
        if (domain == DNSName("www.powerdns.com.")) {
2!
862
          setLWResult(res, 0, true, false, true);
2✔
863
          addRecordToLW(res, domain, QType::A, targetAddr.toString(), DNSResourceRecord::ANSWER, 3600);
2✔
864
          return LWResult::Result::Success;
2✔
865
        }
2✔
866
      }
2✔
867
    }
6✔
868

869
    return LWResult::Result::Timeout;
×
870
  });
6✔
871

872
  /* first we do a query in the parent zone (powerdns.com), insecure,
873
     to get the NS in cache so we don't learn the zone cut before
874
     validating */
875
  vector<DNSRecord> ret;
2✔
876
  int res = sr->beginResolve(DNSName("www.powerdns.com."), QType(QType::A), QClass::IN, ret);
2✔
877
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
878
  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
2✔
879
  BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2✔
880
  BOOST_CHECK(ret.at(0).d_type == QType::A);
2✔
881
  BOOST_CHECK_EQUAL(queriesCount, 5U);
2✔
882

883
  /* now we query the sub zone */
884
  ret.clear();
2✔
885
  res = sr->beginResolve(DNSName("www.sub.powerdns.com."), QType(QType::A), QClass::IN, ret);
2✔
886
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
887
  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
2✔
888
  BOOST_REQUIRE_EQUAL(ret.size(), 2U);
2✔
889
  BOOST_CHECK(ret[0].d_type == QType::A);
2✔
890
  BOOST_CHECK_EQUAL(queriesCount, 8U);
2✔
891

892
  /* again, to test the cache */
893
  ret.clear();
2✔
894
  res = sr->beginResolve(DNSName("www.sub.powerdns.com."), QType(QType::A), QClass::IN, ret);
2✔
895
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
896
  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
2✔
897
  BOOST_REQUIRE_EQUAL(ret.size(), 2U);
2✔
898
  BOOST_CHECK(ret[0].d_type == QType::A);
2✔
899
  BOOST_CHECK_EQUAL(queriesCount, 8U);
2✔
900
}
2✔
901

902
BOOST_AUTO_TEST_CASE(test_dnssec_bogus_dnskey_loop)
903
{
2✔
904
  std::unique_ptr<SyncRes> sr;
2✔
905
  initSR(sr, true);
2✔
906

907
  setDNSSECValidation(sr, DNSSECMode::ValidateAll);
2✔
908

909
  primeHints();
2✔
910
  const ComboAddress targetAddr("192.0.2.42");
2✔
911
  testkeysset_t keys;
2✔
912

913
  auto luaconfsCopy = g_luaconfs.getCopy();
2✔
914
  luaconfsCopy.dsAnchors.clear();
2✔
915
  generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys, luaconfsCopy.dsAnchors);
2✔
916
  generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
2✔
917

918
  /* Generate key material for "powerdns.com." */
919
  auto dcke = DNSCryptoKeyEngine::make(DNSSECKeeper::ECDSA256);
2✔
920
  dcke->create(dcke->getBits());
2✔
921
  DNSSECPrivateKey key;
2✔
922
  key.setKey(std::move(dcke), 257);
2✔
923
  DSRecordContent drc = makeDSFromDNSKey(DNSName("powerdns.com."), key.getDNSKEY(), DNSSECKeeper::DIGEST_SHA256);
2✔
924

925
  testkeysset_t wrongKeys;
2✔
926
  auto wrongDcke = DNSCryptoKeyEngine::make(DNSSECKeeper::ECDSA256);
2✔
927
  wrongDcke->create(wrongDcke->getBits());
2✔
928
  DNSSECPrivateKey wrongKey;
2✔
929
  wrongKey.setKey(std::move(wrongDcke), 256);
2✔
930
  DSRecordContent uselessdrc = makeDSFromDNSKey(DNSName("powerdns.com."), wrongKey.getDNSKEY(), DNSSECKeeper::DIGEST_SHA256);
2✔
931

932
  wrongKeys[DNSName("powerdns.com.")] = std::pair<DNSSECPrivateKey, DSRecordContent>(wrongKey, uselessdrc);
2✔
933
  keys[DNSName("powerdns.com.")] = std::pair<DNSSECPrivateKey, DSRecordContent>(key, drc);
2✔
934

935
  g_luaconfs.setState(luaconfsCopy);
2✔
936

937
  size_t queriesCount = 0;
2✔
938

939
  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✔
940
    queriesCount++;
10✔
941

942
    if (type == QType::DS) {
10!
943
      return genericDSAndDNSKEYHandler(res, domain, domain, type, keys);
×
944
    }
×
945
    if (type == QType::DNSKEY) {
10✔
946
      if (domain == DNSName("powerdns.com.")) {
4✔
947
        /* wrong DNSKEY */
948
        return genericDSAndDNSKEYHandler(res, domain, domain, type, wrongKeys);
2✔
949
      }
2✔
950
      return genericDSAndDNSKEYHandler(res, domain, domain, type, keys);
2✔
951
    }
4✔
952
    {
6✔
953
      if (isRootServer(address)) {
6✔
954
        setLWResult(res, 0, false, false, true);
2✔
955
        addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResourceRecord::AUTHORITY, 3600);
2✔
956
        addDS(DNSName("com."), 300, res->d_records, keys);
2✔
957
        addRRSIG(keys, res->d_records, DNSName("."), 300);
2✔
958
        addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
2✔
959
        return LWResult::Result::Success;
2✔
960
      }
2✔
961
      if (address == ComboAddress("192.0.2.1:53")) {
4✔
962
        if (domain.isPartOf(DNSName("powerdns.com."))) {
2!
963
          setLWResult(res, 0, false, false, true);
2✔
964
          addRecordToLW(res, DNSName("powerdns.com."), QType::NS, "ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 3600);
2✔
965
          addDS(DNSName("powerdns.com."), 300, res->d_records, keys);
2✔
966
          addRRSIG(keys, res->d_records, DNSName("."), 300);
2✔
967
          addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
2✔
968
          return LWResult::Result::Success;
2✔
969
        }
2✔
970
      }
2✔
971
      else if (address == ComboAddress("192.0.2.2:53")) {
2!
972
        if (domain == DNSName("www.powerdns.com.")) {
2!
973
          setLWResult(res, 0, true, false, true);
2✔
974
          addRecordToLW(res, domain, QType::A, targetAddr.toString(), DNSResourceRecord::ANSWER, 3600);
2✔
975
          addRRSIG(keys, res->d_records, DNSName("powerdns.com."), 300);
2✔
976
          return LWResult::Result::Success;
2✔
977
        }
2✔
978
      }
2✔
979
    }
4✔
980

981
    return LWResult::Result::Timeout;
×
982
  });
4✔
983

984
  /* first we do a query in the parent zone (powerdns.com), insecure,
985
     to get the NS in cache so we don't learn the zone cut before
986
     validating */
987
  vector<DNSRecord> ret;
2✔
988
  int res = sr->beginResolve(DNSName("www.powerdns.com."), QType(QType::A), QClass::IN, ret);
2✔
989
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
990
  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::BogusNoValidDNSKEY);
2✔
991
  BOOST_REQUIRE_EQUAL(ret.size(), 2U);
2✔
992
  BOOST_CHECK(ret.at(0).d_type == QType::A);
2✔
993
  BOOST_CHECK_EQUAL(queriesCount, 5U);
2✔
994

995
  /* again, to test the cache */
996
  ret.clear();
2✔
997
  res = sr->beginResolve(DNSName("www.powerdns.com."), QType(QType::A), QClass::IN, ret);
2✔
998
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
999
  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::BogusNoValidDNSKEY);
2✔
1000
  BOOST_REQUIRE_EQUAL(ret.size(), 2U);
2✔
1001
  BOOST_CHECK(ret[0].d_type == QType::A);
2✔
1002
  BOOST_CHECK_EQUAL(queriesCount, 5U);
2✔
1003
}
2✔
1004

1005
BOOST_AUTO_TEST_CASE(test_dnssec_bogus_ds_loop)
1006
{
2✔
1007
  // Test the case where the RRSIG on the name *and* the RRSIG of the NSEC denying the DS is broken.
1008
  // This sends te zone cut code trying extra hard to find a zone cut into an endless recursion.
1009
  std::unique_ptr<SyncRes> resolver;
2✔
1010
  initSR(resolver, true, false);
2✔
1011

1012
  setDNSSECValidation(resolver, DNSSECMode::ValidateAll);
2✔
1013
  resolver->setQNameMinimization();
2✔
1014

1015
  primeHints();
2✔
1016
  const ComboAddress targetAddr("192.0.2.42");
2✔
1017
  testkeysset_t keys;
2✔
1018

1019
  auto luaconfsCopy = g_luaconfs.getCopy();
2✔
1020
  luaconfsCopy.dsAnchors.clear();
2✔
1021
  generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys, luaconfsCopy.dsAnchors);
2✔
1022
  generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
2✔
1023

1024
  /* Generate key material for "powerdns.com." */
1025
  auto dcke = DNSCryptoKeyEngine::make(DNSSECKeeper::ECDSA256);
2✔
1026
  dcke->create(dcke->getBits());
2✔
1027
  DNSSECPrivateKey key;
2✔
1028
  key.setKey(std::move(dcke), 257);
2✔
1029
  DSRecordContent drc = makeDSFromDNSKey(DNSName("powerdns.com."), key.getDNSKEY(), DNSSECKeeper::DIGEST_SHA256);
2✔
1030

1031
  keys[DNSName("powerdns.com.")] = std::pair<DNSSECPrivateKey, DSRecordContent>(key, drc);
2✔
1032
  g_luaconfs.setState(luaconfsCopy);
2✔
1033

1034
  size_t queriesCount = 0;
2✔
1035

1036
  resolver->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 */) {
12✔
1037
    queriesCount++;
12✔
1038

1039
    if (type == QType::DNSKEY) {
12✔
1040
      return genericDSAndDNSKEYHandler(res, domain, domain, type, keys);
4✔
1041
    }
4✔
1042
    if (type == QType::DS) {
8✔
1043
      if (domain == DNSName("www.powerdns.com.")) {
2!
1044
        auto ret = genericDSAndDNSKEYHandler(res, domain, domain, type, keys, true, boost::none, false, false);
2✔
1045
        for (auto& rec : res->d_records) {
8✔
1046
          // We know the NSEC RRSIG for the DS is the only one
1047
          if (rec.d_name == DNSName("www.powerdns.com") && rec.d_type == QType::RRSIG) {
8✔
1048
            auto ptr = getRR<RRSIGRecordContent>(rec);
2✔
1049
            ((char*)(void*)(ptr->d_signature.data()))[0] ^= 0x42; // NOLINT
2✔
1050
          }
2✔
1051
        }
8✔
1052
        return ret;
2✔
1053
      }
2✔
1054
      return genericDSAndDNSKEYHandler(res, domain, domain, type, keys);
×
1055
    }
2✔
1056
    {
6✔
1057
      if (isRootServer(address)) {
6✔
1058
        setLWResult(res, 0, false, false, true);
2✔
1059
        addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResourceRecord::AUTHORITY, 3600);
2✔
1060
        addDS(DNSName("com."), 300, res->d_records, keys);
2✔
1061
        addRRSIG(keys, res->d_records, DNSName("."), 300);
2✔
1062
        addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
2✔
1063
        return LWResult::Result::Success;
2✔
1064
      }
2✔
1065
      if (address == ComboAddress("192.0.2.1:53")) {
4✔
1066
        if (domain.isPartOf(DNSName("powerdns.com."))) {
2!
1067
          setLWResult(res, 0, false, false, true);
2✔
1068
          addRecordToLW(res, DNSName("powerdns.com."), QType::NS, "ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 3600);
2✔
1069
          addDS(DNSName("powerdns.com."), 300, res->d_records, keys);
2✔
1070
          addRRSIG(keys, res->d_records, DNSName("."), 300);
2✔
1071
          addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
2✔
1072
          return LWResult::Result::Success;
2✔
1073
        }
2✔
1074
      }
2✔
1075
      else if (address == ComboAddress("192.0.2.2:53")) {
2!
1076
        if (domain == DNSName("www.powerdns.com.")) {
2!
1077
          setLWResult(res, 0, true, false, true);
2✔
1078
          addRecordToLW(res, domain, QType::A, targetAddr.toString(), DNSResourceRecord::ANSWER, 3600);
2✔
1079
          addRRSIG(keys, res->d_records, DNSName("powerdns.com."), 300, true);
2✔
1080
          return LWResult::Result::Success;
2✔
1081
        }
2✔
1082
      }
2✔
1083
    }
4✔
1084

1085
    return LWResult::Result::Timeout;
×
1086
  });
4✔
1087

1088
  vector<DNSRecord> ret;
2✔
1089
  int res = resolver->beginResolve(DNSName("www.powerdns.com."), QType(QType::A), QClass::IN, ret);
2✔
1090
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1091
  BOOST_CHECK_EQUAL(resolver->getValidationState(), vState::BogusNoValidRRSIG);
2✔
1092
  BOOST_REQUIRE_EQUAL(ret.size(), 2U);
2✔
1093
  BOOST_CHECK(ret.at(0).d_type == QType::A);
2✔
1094
  BOOST_CHECK_EQUAL(queriesCount, 6U);
2✔
1095

1096
  /* again, to test the cache */
1097
  ret.clear();
2✔
1098
  res = resolver->beginResolve(DNSName("www.powerdns.com."), QType(QType::A), QClass::IN, ret);
2✔
1099
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1100
  BOOST_CHECK_EQUAL(resolver->getValidationState(), vState::BogusNoValidRRSIG);
2✔
1101
  BOOST_REQUIRE_EQUAL(ret.size(), 2U);
2✔
1102
  BOOST_CHECK(ret[0].d_type == QType::A);
2✔
1103
  BOOST_CHECK_EQUAL(queriesCount, 6U);
2✔
1104
}
2✔
1105

1106
static auto createPID(std::string rem, int tcpsock, uint16_t type, std::string domain, int fd, uint16_t id)
1107
{
8✔
1108
  PacketID pid;
8✔
1109
  pid.remote = ComboAddress(rem);
8✔
1110
  pid.tcpsock = tcpsock;
8✔
1111
  pid.type = type;
8✔
1112
  pid.domain = DNSName(domain);
8✔
1113
  pid.fd = fd;
8✔
1114
  pid.id = id;
8✔
1115
  return std::make_shared<PacketID>(pid);
8✔
1116
}
8✔
1117

1118
BOOST_AUTO_TEST_CASE(test_PacketIDCompare)
1119
{
2✔
1120
  // Ordered by domain, but not by id
1121
  auto a = createPID("1.2.3.4", -1, 1, "powerdns.com", -1, 1000);
2✔
1122
  auto b = createPID("1.2.3.4", -1, 1, "powerdns.net", -1, 999);
2✔
1123

1124
  auto cmp = PacketIDCompare();
2✔
1125
  auto bcmp = PacketIDBirthdayCompare();
2✔
1126

1127
  bool r1 = cmp.operator()(a, b);
2✔
1128
  bool br1 = bcmp.operator()(a, b);
2✔
1129
  bool r2 = cmp.operator()(b, a);
2✔
1130
  bool br2 = bcmp.operator()(b, a);
2✔
1131

1132
  BOOST_CHECK(r1);
2✔
1133
  BOOST_CHECK(br1);
2✔
1134
  BOOST_CHECK(!r2);
2✔
1135
  BOOST_CHECK(!br2);
2✔
1136

1137
  // Ordered by domain, but not by fd
1138
  a = createPID("1.2.3.4", -1, 1, "powerdns.com", 1, 1000);
2✔
1139
  b = createPID("1.2.3.4", -1, 1, "powerdns.net", -1, 1000);
2✔
1140

1141
  r1 = cmp.operator()(a, b);
2✔
1142
  br1 = bcmp.operator()(a, b);
2✔
1143
  r2 = cmp.operator()(b, a);
2✔
1144
  br2 = bcmp.operator()(b, a);
2✔
1145

1146
  BOOST_CHECK(r1);
2✔
1147
  BOOST_CHECK(br1);
2✔
1148
  BOOST_CHECK(!r2);
2✔
1149
  BOOST_CHECK(!br2);
2✔
1150
}
2✔
1151

1152
BOOST_AUTO_TEST_CASE(test_servestale)
1153
{
2✔
1154
  std::unique_ptr<SyncRes> sr;
2✔
1155
  initSR(sr);
2✔
1156
  MemRecursorCache::s_maxServedStaleExtensions = 1440;
2✔
1157

1158
  primeHints();
2✔
1159

1160
  const DNSName target("powerdns.com.");
2✔
1161

1162
  std::set<ComboAddress> downServers;
2✔
1163
  size_t downCount = 0;
2✔
1164
  size_t lookupCount = 0;
2✔
1165

1166
  const int theTTL = 5;
2✔
1167

1168
  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 */) {
20✔
1169
    /* this will cause issue with qname minimization if we ever implement it */
1170
    if (downServers.find(address) != downServers.end()) {
20✔
1171
      downCount++;
8✔
1172
      return LWResult::Result::Timeout;
8✔
1173
    }
8✔
1174

1175
    if (isRootServer(address)) {
12✔
1176
      setLWResult(res, 0, false, false, true);
2✔
1177
      addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY);
2✔
1178
      addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL);
2✔
1179
      addRecordToLW(res, "a.gtld-servers.net.", QType::AAAA, "2001:DB8::1", DNSResourceRecord::ADDITIONAL);
2✔
1180
      return LWResult::Result::Success;
2✔
1181
    }
2✔
1182
    if (address == ComboAddress("192.0.2.1:53") || address == ComboAddress("[2001:DB8::1]:53")) {
10✔
1183
      setLWResult(res, 0, false, false, true);
6✔
1184
      addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, theTTL);
6✔
1185
      addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns2.powerdns.com.", DNSResourceRecord::AUTHORITY, theTTL);
6✔
1186
      addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 5);
6✔
1187
      addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::AAAA, "2001:DB8::2", DNSResourceRecord::ADDITIONAL, theTTL);
6✔
1188
      addRecordToLW(res, "pdns-public-ns2.powerdns.com.", QType::A, "192.0.2.3", DNSResourceRecord::ADDITIONAL, 5);
6✔
1189
      addRecordToLW(res, "pdns-public-ns2.powerdns.com.", QType::AAAA, "2001:DB8::3", DNSResourceRecord::ADDITIONAL, theTTL);
6✔
1190
      return LWResult::Result::Success;
6✔
1191
    }
6✔
1192
    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")) {
4!
1193
      setLWResult(res, 0, true, false, true);
4✔
1194
      addRecordToLW(res, target, QType::A, "192.0.2.4", DNSResourceRecord::ANSWER, 5);
4✔
1195
      lookupCount++;
4✔
1196
      return LWResult::Result::Success;
4✔
1197
    }
4✔
1198
    return LWResult::Result::Timeout;
×
1199
  });
4✔
1200

1201
  time_t now = time(nullptr);
2✔
1202

1203
  vector<DNSRecord> ret;
2✔
1204
  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1205
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1206
  BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2✔
1207
  BOOST_CHECK(ret[0].d_type == QType::A);
2✔
1208
  BOOST_CHECK_EQUAL(ret[0].d_name, target);
2✔
1209
  BOOST_CHECK_EQUAL(downCount, 0U);
2✔
1210
  BOOST_CHECK_EQUAL(lookupCount, 1U);
2✔
1211

1212
  downServers.insert(ComboAddress("192.0.2.2:53"));
2✔
1213
  downServers.insert(ComboAddress("192.0.2.3:53"));
2✔
1214
  downServers.insert(ComboAddress("[2001:DB8::2]:53"));
2✔
1215
  downServers.insert(ComboAddress("[2001:DB8::3]:53"));
2✔
1216

1217
  sr->setNow(timeval{now + theTTL + 1, 0});
2✔
1218

1219
  BOOST_REQUIRE_EQUAL(getTaskSize(), 0U);
2✔
1220

1221
  // record is expired, so serve stale should kick in
1222
  ret.clear();
2✔
1223
  res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1224
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1225
  BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2✔
1226
  BOOST_CHECK(ret[0].d_type == QType::A);
2✔
1227
  BOOST_CHECK_EQUAL(ret[0].d_name, target);
2✔
1228
  BOOST_CHECK_EQUAL(downCount, 4U);
2✔
1229
  BOOST_CHECK_EQUAL(lookupCount, 1U);
2✔
1230

1231
  BOOST_REQUIRE_EQUAL(getTaskSize(), 1U);
2✔
1232
  auto task = taskQueuePop();
2✔
1233
  BOOST_CHECK(task.d_qname == target);
2✔
1234
  BOOST_CHECK_EQUAL(task.d_qtype, QType::A);
2✔
1235

1236
  // Again, no lookup as the record is marked stale
1237
  ret.clear();
2✔
1238
  res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1239
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1240
  BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2✔
1241
  BOOST_CHECK(ret[0].d_type == QType::A);
2✔
1242
  BOOST_CHECK_EQUAL(ret[0].d_name, target);
2✔
1243
  BOOST_CHECK_EQUAL(downCount, 4U);
2✔
1244
  BOOST_CHECK_EQUAL(lookupCount, 1U);
2✔
1245

1246
  // Again, no lookup as the record is marked stale but as the TTL has passed a task should have been pushed
1247
  sr->setNow(timeval{now + 2 * (theTTL + 1), 0});
2✔
1248
  ret.clear();
2✔
1249
  res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1250
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1251
  BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2✔
1252
  BOOST_CHECK(ret[0].d_type == QType::A);
2✔
1253
  BOOST_CHECK_EQUAL(ret[0].d_name, target);
2✔
1254
  BOOST_CHECK_EQUAL(downCount, 4U);
2✔
1255
  BOOST_CHECK_EQUAL(lookupCount, 1U);
2✔
1256

1257
  BOOST_REQUIRE_EQUAL(getTaskSize(), 1U);
2✔
1258
  task = taskQueuePop();
2✔
1259
  BOOST_CHECK(task.d_qname == target);
2✔
1260
  BOOST_CHECK_EQUAL(task.d_qtype, QType::A);
2✔
1261

1262
  // Now simulate a succeeding task execution
1263
  sr->setNow(timeval{now + 3 * (theTTL + 1), 0});
2✔
1264
  downServers.clear();
2✔
1265
  sr->setRefreshAlmostExpired(true);
2✔
1266
  ret.clear();
2✔
1267
  res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1268
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1269
  BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2✔
1270
  BOOST_CHECK(ret[0].d_type == QType::A);
2✔
1271
  BOOST_CHECK_EQUAL(ret[0].d_name, target);
2✔
1272
  BOOST_CHECK_EQUAL(downCount, 4U);
2✔
1273
  BOOST_CHECK_EQUAL(lookupCount, 2U);
2✔
1274

1275
  // And again, result should come from cache
1276
  sr->setRefreshAlmostExpired(false);
2✔
1277
  ret.clear();
2✔
1278
  res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1279
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1280
  BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2✔
1281
  BOOST_CHECK(ret[0].d_type == QType::A);
2✔
1282
  BOOST_CHECK_EQUAL(ret[0].d_name, target);
2✔
1283
  BOOST_CHECK_EQUAL(downCount, 4U);
2✔
1284
  BOOST_CHECK_EQUAL(lookupCount, 2U);
2✔
1285
}
2✔
1286

1287
BOOST_AUTO_TEST_CASE(test_servestale_neg)
1288
{
2✔
1289
  std::unique_ptr<SyncRes> sr;
2✔
1290
  initSR(sr);
2✔
1291
  MemRecursorCache::s_maxServedStaleExtensions = 1440;
2✔
1292
  NegCache::s_maxServedStaleExtensions = 1440;
2✔
1293

1294
  primeHints();
2✔
1295

1296
  const DNSName target("powerdns.com.");
2✔
1297

1298
  std::set<ComboAddress> downServers;
2✔
1299
  size_t downCount = 0;
2✔
1300
  size_t lookupCount = 0;
2✔
1301

1302
  const int theTTL = 5;
2✔
1303

1304
  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 */) {
24✔
1305
    /* this will cause issue with qname minimization if we ever implement it */
1306
    if (downServers.find(address) != downServers.end()) {
24✔
1307
      downCount++;
8✔
1308
      return LWResult::Result::Timeout;
8✔
1309
    }
8✔
1310

1311
    if (isRootServer(address)) {
16✔
1312
      setLWResult(res, 0, false, false, true);
6✔
1313
      addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY);
6✔
1314
      addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL);
6✔
1315
      addRecordToLW(res, "a.gtld-servers.net.", QType::AAAA, "2001:DB8::1", DNSResourceRecord::ADDITIONAL);
6✔
1316
      return LWResult::Result::Success;
6✔
1317
    }
6✔
1318
    if (address == ComboAddress("192.0.2.1:53") || address == ComboAddress("[2001:DB8::1]:53")) {
10✔
1319
      setLWResult(res, 0, false, false, true);
6✔
1320
      addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, theTTL);
6✔
1321
      addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns2.powerdns.com.", DNSResourceRecord::AUTHORITY, theTTL);
6✔
1322
      addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, theTTL);
6✔
1323
      addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::AAAA, "2001:DB8::2", DNSResourceRecord::ADDITIONAL, theTTL);
6✔
1324
      addRecordToLW(res, "pdns-public-ns2.powerdns.com.", QType::A, "192.0.2.3", DNSResourceRecord::ADDITIONAL, theTTL);
6✔
1325
      addRecordToLW(res, "pdns-public-ns2.powerdns.com.", QType::AAAA, "2001:DB8::3", DNSResourceRecord::ADDITIONAL, theTTL);
6✔
1326
      return LWResult::Result::Success;
6✔
1327
    }
6✔
1328
    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")) {
4!
1329
      setLWResult(res, 0, true, false, true);
4✔
1330
      addRecordToLW(res, "powerdns.com.", QType::SOA, "pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 60", DNSResourceRecord::AUTHORITY);
4✔
1331
      lookupCount++;
4✔
1332
      return LWResult::Result::Success;
4✔
1333
    }
4✔
1334
    else {
×
1335
      return LWResult::Result::Timeout;
×
1336
    }
×
1337
  });
4✔
1338

1339
  time_t now = time(nullptr);
2✔
1340

1341
  vector<DNSRecord> ret;
2✔
1342
  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1343
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1344
  BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2✔
1345
  BOOST_CHECK_EQUAL(ret[0].d_name, target);
2✔
1346
  BOOST_CHECK(ret[0].d_type == QType::SOA);
2✔
1347
  BOOST_CHECK_EQUAL(downCount, 0U);
2✔
1348
  BOOST_CHECK_EQUAL(lookupCount, 1U);
2✔
1349

1350
  downServers.insert(ComboAddress("192.0.2.2:53"));
2✔
1351
  downServers.insert(ComboAddress("192.0.2.3:53"));
2✔
1352
  downServers.insert(ComboAddress("[2001:DB8::2]:53"));
2✔
1353
  downServers.insert(ComboAddress("[2001:DB8::3]:53"));
2✔
1354

1355
  const int negTTL = 60;
2✔
1356

1357
  sr->setNow(timeval{now + negTTL + 1, 0});
2✔
1358

1359
  // record is expired, so serve stale should kick in
1360
  ret.clear();
2✔
1361
  res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1362
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1363
  BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2✔
1364
  BOOST_CHECK(ret[0].d_type == QType::SOA);
2✔
1365
  BOOST_CHECK_EQUAL(ret[0].d_name, target);
2✔
1366
  BOOST_CHECK_EQUAL(downCount, 4U);
2✔
1367
  BOOST_CHECK_EQUAL(lookupCount, 1U);
2✔
1368

1369
  // Again, no lookup as the record is marked stale
1370
  ret.clear();
2✔
1371
  res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1372
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1373
  BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2✔
1374
  BOOST_CHECK(ret[0].d_type == QType::SOA);
2✔
1375
  BOOST_CHECK_EQUAL(ret[0].d_name, target);
2✔
1376
  BOOST_CHECK_EQUAL(downCount, 4U);
2✔
1377
  BOOST_CHECK_EQUAL(lookupCount, 1U);
2✔
1378

1379
  // Again, no lookup as the record is marked stale but as the TTL has passed a task should have been pushed
1380
  sr->setNow(timeval{now + 2 * (negTTL + 1), 0});
2✔
1381
  ret.clear();
2✔
1382
  res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1383
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1384
  BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2✔
1385
  BOOST_CHECK(ret[0].d_type == QType::SOA);
2✔
1386
  BOOST_CHECK_EQUAL(ret[0].d_name, target);
2✔
1387
  BOOST_CHECK_EQUAL(downCount, 4U);
2✔
1388
  BOOST_CHECK_EQUAL(lookupCount, 1U);
2✔
1389

1390
  BOOST_REQUIRE_EQUAL(getTaskSize(), 1U);
2✔
1391
  auto task = taskQueuePop();
2✔
1392
  BOOST_CHECK(task.d_qname == target);
2✔
1393
  BOOST_CHECK_EQUAL(task.d_qtype, QType::A);
2✔
1394

1395
  // Now simulate a succeeding task execution
1396
  sr->setNow(timeval{now + 3 * (negTTL + 1), 0});
2✔
1397
  downServers.clear();
2✔
1398
  sr->setRefreshAlmostExpired(true);
2✔
1399
  ret.clear();
2✔
1400
  res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1401
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1402
  BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2✔
1403
  BOOST_CHECK(ret[0].d_type == QType::SOA);
2✔
1404
  BOOST_CHECK_EQUAL(ret[0].d_name, target);
2✔
1405
  BOOST_CHECK_EQUAL(downCount, 4U);
2✔
1406
  BOOST_CHECK_EQUAL(lookupCount, 2U);
2✔
1407

1408
  // And again, result should come from cache
1409
  sr->setRefreshAlmostExpired(false);
2✔
1410
  ret.clear();
2✔
1411
  res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1412
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1413
  BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2✔
1414
  BOOST_CHECK(ret[0].d_type == QType::SOA);
2✔
1415
  BOOST_CHECK_EQUAL(ret[0].d_name, target);
2✔
1416
  BOOST_CHECK_EQUAL(downCount, 4U);
2✔
1417
  BOOST_CHECK_EQUAL(lookupCount, 2U);
2✔
1418
}
2✔
1419

1420
BOOST_AUTO_TEST_CASE(test_servestale_neg_to_available)
1421
{
2✔
1422
  std::unique_ptr<SyncRes> sr;
2✔
1423
  initSR(sr);
2✔
1424
  MemRecursorCache::s_maxServedStaleExtensions = 1440;
2✔
1425
  NegCache::s_maxServedStaleExtensions = 1440;
2✔
1426

1427
  primeHints();
2✔
1428

1429
  const DNSName target("powerdns.com.");
2✔
1430

1431
  std::set<ComboAddress> downServers;
2✔
1432
  size_t downCount = 0;
2✔
1433
  size_t lookupCount = 0;
2✔
1434
  bool negLookup = true;
2✔
1435

1436
  const int theTTL = 5;
2✔
1437
  const int negTTL = 60;
2✔
1438

1439
  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 */) {
24✔
1440
    /* this will cause issue with qname minimization if we ever implement it */
1441
    if (downServers.find(address) != downServers.end()) {
24✔
1442
      downCount++;
8✔
1443
      return LWResult::Result::Timeout;
8✔
1444
    }
8✔
1445

1446
    if (isRootServer(address)) {
16✔
1447
      setLWResult(res, 0, false, false, true);
6✔
1448
      addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY);
6✔
1449
      addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL);
6✔
1450
      addRecordToLW(res, "a.gtld-servers.net.", QType::AAAA, "2001:DB8::1", DNSResourceRecord::ADDITIONAL);
6✔
1451
      return LWResult::Result::Success;
6✔
1452
    }
6✔
1453
    if (address == ComboAddress("192.0.2.1:53") || address == ComboAddress("[2001:DB8::1]:53")) {
10✔
1454
      setLWResult(res, 0, false, false, true);
6✔
1455
      addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, theTTL);
6✔
1456
      addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns2.powerdns.com.", DNSResourceRecord::AUTHORITY, theTTL);
6✔
1457
      addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, theTTL);
6✔
1458
      addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::AAAA, "2001:DB8::2", DNSResourceRecord::ADDITIONAL, theTTL);
6✔
1459
      addRecordToLW(res, "pdns-public-ns2.powerdns.com.", QType::A, "192.0.2.3", DNSResourceRecord::ADDITIONAL, theTTL);
6✔
1460
      addRecordToLW(res, "pdns-public-ns2.powerdns.com.", QType::AAAA, "2001:DB8::3", DNSResourceRecord::ADDITIONAL, theTTL);
6✔
1461
      return LWResult::Result::Success;
6✔
1462
    }
6✔
1463
    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")) {
4!
1464
      if (negLookup) {
4✔
1465
        setLWResult(res, 0, true, false, true);
2✔
1466
        addRecordToLW(res, "powerdns.com.", QType::SOA, "pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 60", DNSResourceRecord::AUTHORITY, negTTL);
2✔
1467
        lookupCount++;
2✔
1468
        return LWResult::Result::Success;
2✔
1469
      }
2✔
1470
      {
2✔
1471
        setLWResult(res, 0, true, false, true);
2✔
1472
        addRecordToLW(res, target, QType::A, "192.0.2.4", DNSResourceRecord::ANSWER, theTTL);
2✔
1473
        lookupCount++;
2✔
1474
        return LWResult::Result::Success;
2✔
1475
      }
4✔
1476
    }
4✔
1477
    else {
×
1478
      return LWResult::Result::Timeout;
×
1479
    }
×
1480
  });
4✔
1481

1482
  time_t now = time(nullptr);
2✔
1483

1484
  vector<DNSRecord> ret;
2✔
1485
  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1486
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1487
  BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2✔
1488
  BOOST_CHECK_EQUAL(ret[0].d_name, target);
2✔
1489
  BOOST_CHECK(ret[0].d_type == QType::SOA);
2✔
1490
  BOOST_CHECK_EQUAL(downCount, 0U);
2✔
1491
  BOOST_CHECK_EQUAL(lookupCount, 1U);
2✔
1492

1493
  downServers.insert(ComboAddress("192.0.2.2:53"));
2✔
1494
  downServers.insert(ComboAddress("192.0.2.3:53"));
2✔
1495
  downServers.insert(ComboAddress("[2001:DB8::2]:53"));
2✔
1496
  downServers.insert(ComboAddress("[2001:DB8::3]:53"));
2✔
1497

1498
  sr->setNow(timeval{now + negTTL + 1, 0});
2✔
1499

1500
  // record is expired, so serve stale should kick in
1501
  ret.clear();
2✔
1502
  res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1503
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1504
  BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2✔
1505
  BOOST_CHECK(ret[0].d_type == QType::SOA);
2✔
1506
  BOOST_CHECK_EQUAL(ret[0].d_name, target);
2✔
1507
  BOOST_CHECK_EQUAL(downCount, 4U);
2✔
1508
  BOOST_CHECK_EQUAL(lookupCount, 1U);
2✔
1509

1510
  // Again, no lookup as the record is marked stale
1511
  ret.clear();
2✔
1512
  res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1513
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1514
  BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2✔
1515
  BOOST_CHECK(ret[0].d_type == QType::SOA);
2✔
1516
  BOOST_CHECK_EQUAL(ret[0].d_name, target);
2✔
1517
  BOOST_CHECK_EQUAL(downCount, 4U);
2✔
1518
  BOOST_CHECK_EQUAL(lookupCount, 1U);
2✔
1519

1520
  // Again, no lookup as the record is marked stale but as the TTL has passed a task should have been pushed
1521
  sr->setNow(timeval{now + 2 * (negTTL + 1), 0});
2✔
1522
  ret.clear();
2✔
1523
  res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1524
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1525
  BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2✔
1526
  BOOST_CHECK(ret[0].d_type == QType::SOA);
2✔
1527
  BOOST_CHECK_EQUAL(ret[0].d_name, target);
2✔
1528
  BOOST_CHECK_EQUAL(downCount, 4U);
2✔
1529
  BOOST_CHECK_EQUAL(lookupCount, 1U);
2✔
1530

1531
  BOOST_REQUIRE_EQUAL(getTaskSize(), 1U);
2✔
1532
  auto task = taskQueuePop();
2✔
1533
  BOOST_CHECK(task.d_qname == target);
2✔
1534
  BOOST_CHECK_EQUAL(task.d_qtype, QType::A);
2✔
1535

1536
  // Now simulate a succeeding task execution an record has become available
1537
  negLookup = false;
2✔
1538
  sr->setNow(timeval{now + 3 * (negTTL + 1), 0});
2✔
1539
  downServers.clear();
2✔
1540
  sr->setRefreshAlmostExpired(true);
2✔
1541
  ret.clear();
2✔
1542
  res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1543
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1544
  BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2✔
1545
  BOOST_CHECK(ret[0].d_type == QType::A);
2✔
1546
  BOOST_CHECK_EQUAL(ret[0].d_name, target);
2✔
1547
  BOOST_CHECK_EQUAL(downCount, 4U);
2✔
1548
  BOOST_CHECK_EQUAL(lookupCount, 2U);
2✔
1549

1550
  // And again, result should come from cache
1551
  sr->setRefreshAlmostExpired(false);
2✔
1552
  ret.clear();
2✔
1553
  res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1554
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1555
  BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2✔
1556
  BOOST_CHECK(ret[0].d_type == QType::A);
2✔
1557
  BOOST_CHECK_EQUAL(ret[0].d_name, target);
2✔
1558
  BOOST_CHECK_EQUAL(downCount, 4U);
2✔
1559
  BOOST_CHECK_EQUAL(lookupCount, 2U);
2✔
1560
}
2✔
1561

1562
BOOST_AUTO_TEST_CASE(test_servestale_cname_to_nxdomain)
1563
{
2✔
1564
  std::unique_ptr<SyncRes> sr;
2✔
1565
  initSR(sr);
2✔
1566
  MemRecursorCache::s_maxServedStaleExtensions = 1440;
2✔
1567
  NegCache::s_maxServedStaleExtensions = 1440;
2✔
1568

1569
  primeHints();
2✔
1570

1571
  const DNSName target("www.powerdns.com.");
2✔
1572
  const DNSName auth("powerdns.com.");
2✔
1573

1574
  std::set<ComboAddress> downServers;
2✔
1575
  size_t downCount = 0;
2✔
1576
  size_t lookupCount = 0;
2✔
1577
  bool cnameOK = true;
2✔
1578

1579
  const int theTTL = 5;
2✔
1580
  const int negTTL = 60;
2✔
1581

1582
  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✔
1583
    /* this will cause issue with qname minimization if we ever implement it */
1584
    if (downServers.find(address) != downServers.end()) {
30✔
1585
      downCount++;
16✔
1586
      return LWResult::Result::Timeout;
16✔
1587
    }
16✔
1588

1589
    if (isRootServer(address)) {
14✔
1590
      setLWResult(res, 0, false, false, true);
2✔
1591
      addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY);
2✔
1592
      addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL);
2✔
1593
      addRecordToLW(res, "a.gtld-servers.net.", QType::AAAA, "2001:DB8::1", DNSResourceRecord::ADDITIONAL);
2✔
1594
      return LWResult::Result::Success;
2✔
1595
    }
2✔
1596
    if (address == ComboAddress("192.0.2.1:53") || address == ComboAddress("[2001:DB8::1]:53")) {
12✔
1597
      setLWResult(res, 0, false, false, true);
6✔
1598
      addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, theTTL);
6✔
1599
      addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns2.powerdns.com.", DNSResourceRecord::AUTHORITY, theTTL);
6✔
1600
      addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, theTTL);
6✔
1601
      addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::AAAA, "2001:DB8::2", DNSResourceRecord::ADDITIONAL, theTTL);
6✔
1602
      addRecordToLW(res, "pdns-public-ns2.powerdns.com.", QType::A, "192.0.2.3", DNSResourceRecord::ADDITIONAL, theTTL);
6✔
1603
      addRecordToLW(res, "pdns-public-ns2.powerdns.com.", QType::AAAA, "2001:DB8::3", DNSResourceRecord::ADDITIONAL, theTTL);
6✔
1604
      return LWResult::Result::Success;
6✔
1605
    }
6✔
1606
    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")) {
6!
1607
      if (cnameOK) {
6✔
1608
        setLWResult(res, 0, true, false, true);
4✔
1609
        addRecordToLW(res, target, QType::CNAME, "cname.powerdns.com.", DNSResourceRecord::ANSWER, 5);
4✔
1610
        addRecordToLW(res, DNSName("cname.powerdns.com"), QType::A, "192.0.2.4", DNSResourceRecord::ANSWER, theTTL);
4✔
1611
        lookupCount++;
4✔
1612
        return LWResult::Result::Success;
4✔
1613
      }
4✔
1614
      setLWResult(res, RCode::NXDomain, true, false, true);
2✔
1615
      addRecordToLW(res, auth, QType::SOA, "pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 60", DNSResourceRecord::AUTHORITY, negTTL);
2✔
1616
      lookupCount++;
2✔
1617
      return LWResult::Result::Success;
2✔
1618
    }
6✔
1619
    else {
×
1620
      return LWResult::Result::Timeout;
×
1621
    }
×
1622
  });
6✔
1623

1624
  time_t now = time(nullptr);
2✔
1625

1626
  vector<DNSRecord> ret;
2✔
1627
  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1628
  BOOST_REQUIRE_EQUAL(ret.size(), 2U);
2✔
1629
  BOOST_CHECK(ret[0].d_type == QType::CNAME);
2✔
1630
  BOOST_CHECK(ret[1].d_type == QType::A);
2✔
1631
  BOOST_CHECK_EQUAL(ret[0].d_name, target);
2✔
1632
  BOOST_CHECK_EQUAL(ret[1].d_name, DNSName("cname.powerdns.com"));
2✔
1633
  BOOST_CHECK_EQUAL(downCount, 0U);
2✔
1634
  BOOST_CHECK_EQUAL(lookupCount, 2U);
2✔
1635

1636
  downServers.insert(ComboAddress("192.0.2.2:53"));
2✔
1637
  downServers.insert(ComboAddress("192.0.2.3:53"));
2✔
1638
  downServers.insert(ComboAddress("[2001:DB8::2]:53"));
2✔
1639
  downServers.insert(ComboAddress("[2001:DB8::3]:53"));
2✔
1640

1641
  sr->setNow(timeval{now + theTTL + 1, 0});
2✔
1642

1643
  // record is expired, so serve stale should kick in
1644
  ret.clear();
2✔
1645
  res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1646
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1647
  BOOST_REQUIRE_EQUAL(ret.size(), 2U);
2✔
1648
  BOOST_CHECK(ret[0].d_type == QType::CNAME);
2✔
1649
  BOOST_CHECK(ret[1].d_type == QType::A);
2✔
1650
  BOOST_CHECK_EQUAL(ret[0].d_name, target);
2✔
1651
  BOOST_CHECK_EQUAL(ret[1].d_name, DNSName("cname.powerdns.com"));
2✔
1652
  BOOST_CHECK_EQUAL(downCount, 8U);
2✔
1653
  BOOST_CHECK_EQUAL(lookupCount, 2U);
2✔
1654

1655
  // Again, no lookup as the record is marked stale
1656
  ret.clear();
2✔
1657
  res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1658
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1659
  BOOST_REQUIRE_EQUAL(ret.size(), 2U);
2✔
1660
  BOOST_CHECK(ret[0].d_type == QType::CNAME);
2✔
1661
  BOOST_CHECK(ret[1].d_type == QType::A);
2✔
1662
  BOOST_CHECK_EQUAL(ret[0].d_name, target);
2✔
1663
  BOOST_CHECK_EQUAL(ret[1].d_name, DNSName("cname.powerdns.com"));
2✔
1664
  BOOST_CHECK_EQUAL(downCount, 8U);
2✔
1665
  BOOST_CHECK_EQUAL(lookupCount, 2U);
2✔
1666

1667
  // Again, no lookup as the record is marked stale but as the TTL has passed a task should have been pushed
1668
  sr->setNow(timeval{now + 2 * (theTTL + 1), 0});
2✔
1669
  ret.clear();
2✔
1670
  res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1671
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1672
  BOOST_REQUIRE_EQUAL(ret.size(), 2U);
2✔
1673
  BOOST_CHECK(ret[0].d_type == QType::CNAME);
2✔
1674
  BOOST_CHECK(ret[1].d_type == QType::A);
2✔
1675
  BOOST_CHECK_EQUAL(ret[0].d_name, target);
2✔
1676
  BOOST_CHECK_EQUAL(ret[1].d_name, DNSName("cname.powerdns.com"));
2✔
1677
  BOOST_CHECK_EQUAL(downCount, 8U);
2✔
1678
  BOOST_CHECK_EQUAL(lookupCount, 2U);
2✔
1679

1680
  BOOST_REQUIRE_EQUAL(getTaskSize(), 2U);
2✔
1681
  auto task = taskQueuePop();
2✔
1682
  BOOST_CHECK(task.d_qname == target);
2✔
1683
  BOOST_CHECK_EQUAL(task.d_qtype, QType::CNAME);
2✔
1684
  task = taskQueuePop();
2✔
1685
  BOOST_CHECK(task.d_qname == DNSName("cname.powerdns.com"));
2✔
1686
  BOOST_CHECK_EQUAL(task.d_qtype, QType::A);
2✔
1687

1688
  // Now simulate a succeeding task execution and NxDomain on explicit CNAME result becomes available
1689
  cnameOK = false;
2✔
1690
  sr->setNow(timeval{now + 3 * (theTTL + 1), 0});
2✔
1691
  downServers.clear();
2✔
1692
  sr->setRefreshAlmostExpired(true);
2✔
1693

1694
  ret.clear();
2✔
1695
  res = sr->beginResolve(target, QType(QType::CNAME), QClass::IN, ret);
2✔
1696
  BOOST_CHECK_EQUAL(res, RCode::NXDomain);
2✔
1697
  BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2✔
1698
  BOOST_CHECK(ret[0].d_type == QType::SOA);
2✔
1699
  BOOST_CHECK_EQUAL(ret[0].d_name, auth);
2✔
1700
  BOOST_CHECK_EQUAL(downCount, 8U);
2✔
1701
  BOOST_CHECK_EQUAL(lookupCount, 3U);
2✔
1702

1703
  // And again, result should come from cache
1704
  sr->setRefreshAlmostExpired(false);
2✔
1705
  ret.clear();
2✔
1706
  res = sr->beginResolve(target, QType(QType::CNAME), QClass::IN, ret);
2✔
1707
  BOOST_CHECK_EQUAL(res, RCode::NXDomain);
2✔
1708
  BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2✔
1709
  BOOST_CHECK(ret[0].d_type == QType::SOA);
2✔
1710
  BOOST_CHECK_EQUAL(ret[0].d_name, auth);
2✔
1711
  BOOST_CHECK_EQUAL(downCount, 8U);
2✔
1712
  BOOST_CHECK_EQUAL(lookupCount, 3U);
2✔
1713
}
2✔
1714

1715
BOOST_AUTO_TEST_CASE(test_servestale_cname_to_nodata)
1716
{
2✔
1717
  std::unique_ptr<SyncRes> sr;
2✔
1718
  initSR(sr);
2✔
1719
  MemRecursorCache::s_maxServedStaleExtensions = 1440;
2✔
1720
  NegCache::s_maxServedStaleExtensions = 1440;
2✔
1721

1722
  primeHints();
2✔
1723

1724
  const DNSName target("www.powerdns.com.");
2✔
1725
  const DNSName auth("powerdns.com.");
2✔
1726

1727
  std::set<ComboAddress> downServers;
2✔
1728
  size_t downCount = 0;
2✔
1729
  size_t lookupCount = 0;
2✔
1730
  bool cnameOK = true;
2✔
1731

1732
  const time_t theTTL = 5;
2✔
1733
  const time_t negTTL = 60;
2✔
1734

1735
  sr->setAsyncCallback([&](const ComboAddress& ipAddress, 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✔
1736
    /* this will cause issue with qname minimization if we ever implement it */
1737
    if (downServers.find(ipAddress) != downServers.end()) {
30✔
1738
      downCount++;
16✔
1739
      return LWResult::Result::Timeout;
16✔
1740
    }
16✔
1741

1742
    if (isRootServer(ipAddress)) {
14✔
1743
      setLWResult(res, 0, false, false, true);
2✔
1744
      addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY);
2✔
1745
      addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL);
2✔
1746
      addRecordToLW(res, "a.gtld-servers.net.", QType::AAAA, "2001:DB8::1", DNSResourceRecord::ADDITIONAL);
2✔
1747
      return LWResult::Result::Success;
2✔
1748
    }
2✔
1749
    if (ipAddress == ComboAddress("192.0.2.1:53") || ipAddress == ComboAddress("[2001:DB8::1]:53")) {
12✔
1750
      setLWResult(res, 0, false, false, true);
6✔
1751
      addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, theTTL);
6✔
1752
      addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns2.powerdns.com.", DNSResourceRecord::AUTHORITY, theTTL);
6✔
1753
      addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, theTTL);
6✔
1754
      addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::AAAA, "2001:DB8::2", DNSResourceRecord::ADDITIONAL, theTTL);
6✔
1755
      addRecordToLW(res, "pdns-public-ns2.powerdns.com.", QType::A, "192.0.2.3", DNSResourceRecord::ADDITIONAL, theTTL);
6✔
1756
      addRecordToLW(res, "pdns-public-ns2.powerdns.com.", QType::AAAA, "2001:DB8::3", DNSResourceRecord::ADDITIONAL, theTTL);
6✔
1757
      return LWResult::Result::Success;
6✔
1758
    }
6✔
1759
    if (ipAddress == ComboAddress("192.0.2.2:53") || ipAddress == ComboAddress("192.0.2.3:53") || ipAddress == ComboAddress("[2001:DB8::2]:53") || ipAddress == ComboAddress("[2001:DB8::3]:53")) {
6!
1760
      if (cnameOK) {
6✔
1761
        setLWResult(res, 0, true, false, true);
4✔
1762
        addRecordToLW(res, target, QType::CNAME, "cname.powerdns.com.", DNSResourceRecord::ANSWER, 5);
4✔
1763
        addRecordToLW(res, DNSName("cname.powerdns.com"), QType::A, "192.0.2.4", DNSResourceRecord::ANSWER, theTTL);
4✔
1764
        lookupCount++;
4✔
1765
        return LWResult::Result::Success;
4✔
1766
      }
4✔
1767
      setLWResult(res, RCode::NoError, true, false, true);
2✔
1768
      addRecordToLW(res, auth, QType::SOA, "pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 60", DNSResourceRecord::AUTHORITY, negTTL);
2✔
1769
      lookupCount++;
2✔
1770
      return LWResult::Result::Success;
2✔
1771
    }
6✔
1772
    return LWResult::Result::Timeout;
×
1773
  });
6✔
1774

1775
  time_t now = time(nullptr);
2✔
1776

1777
  vector<DNSRecord> ret;
2✔
1778
  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1779
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1780
  BOOST_REQUIRE_EQUAL(ret.size(), 2U);
2✔
1781
  BOOST_CHECK(ret[0].d_type == QType::CNAME);
2✔
1782
  BOOST_CHECK(ret[1].d_type == QType::A);
2✔
1783
  BOOST_CHECK_EQUAL(ret[0].d_name, target);
2✔
1784
  BOOST_CHECK_EQUAL(ret[1].d_name, DNSName("cname.powerdns.com"));
2✔
1785
  BOOST_CHECK_EQUAL(downCount, 0U);
2✔
1786
  BOOST_CHECK_EQUAL(lookupCount, 2U);
2✔
1787

1788
  downServers.insert(ComboAddress("192.0.2.2:53"));
2✔
1789
  downServers.insert(ComboAddress("192.0.2.3:53"));
2✔
1790
  downServers.insert(ComboAddress("[2001:DB8::2]:53"));
2✔
1791
  downServers.insert(ComboAddress("[2001:DB8::3]:53"));
2✔
1792

1793
  sr->setNow(timeval{now + theTTL + 1, 0});
2✔
1794

1795
  // record is expired, so serve stale should kick in
1796
  ret.clear();
2✔
1797
  res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1798
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1799
  BOOST_REQUIRE_EQUAL(ret.size(), 2U);
2✔
1800
  BOOST_CHECK(ret[0].d_type == QType::CNAME);
2✔
1801
  BOOST_CHECK(ret[1].d_type == QType::A);
2✔
1802
  BOOST_CHECK_EQUAL(ret[0].d_name, target);
2✔
1803
  BOOST_CHECK_EQUAL(ret[1].d_name, DNSName("cname.powerdns.com"));
2✔
1804
  BOOST_CHECK_EQUAL(downCount, 8U);
2✔
1805
  BOOST_CHECK_EQUAL(lookupCount, 2U);
2✔
1806

1807
  // Again, no lookup as the record is marked stale
1808
  ret.clear();
2✔
1809
  res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1810
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1811
  BOOST_REQUIRE_EQUAL(ret.size(), 2U);
2✔
1812
  BOOST_CHECK(ret[0].d_type == QType::CNAME);
2✔
1813
  BOOST_CHECK(ret[1].d_type == QType::A);
2✔
1814
  BOOST_CHECK_EQUAL(ret[0].d_name, target);
2✔
1815
  BOOST_CHECK_EQUAL(ret[1].d_name, DNSName("cname.powerdns.com"));
2✔
1816
  BOOST_CHECK_EQUAL(downCount, 8U);
2✔
1817
  BOOST_CHECK_EQUAL(lookupCount, 2U);
2✔
1818

1819
  // Again, no lookup as the record is marked stale but as the TTL has passed a task should have been pushed
1820
  sr->setNow(timeval{now + 2 * (theTTL + 1), 0});
2✔
1821
  ret.clear();
2✔
1822
  res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1823
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1824
  BOOST_REQUIRE_EQUAL(ret.size(), 2U);
2✔
1825
  BOOST_CHECK(ret[0].d_type == QType::CNAME);
2✔
1826
  BOOST_CHECK(ret[1].d_type == QType::A);
2✔
1827
  BOOST_CHECK_EQUAL(ret[0].d_name, target);
2✔
1828
  BOOST_CHECK_EQUAL(ret[1].d_name, DNSName("cname.powerdns.com"));
2✔
1829
  BOOST_CHECK_EQUAL(downCount, 8U);
2✔
1830
  BOOST_CHECK_EQUAL(lookupCount, 2U);
2✔
1831

1832
  BOOST_REQUIRE_EQUAL(getTaskSize(), 2U);
2✔
1833
  auto task = taskQueuePop();
2✔
1834
  BOOST_CHECK(task.d_qname == target);
2✔
1835
  BOOST_CHECK_EQUAL(task.d_qtype, QType::CNAME);
2✔
1836
  task = taskQueuePop();
2✔
1837
  BOOST_CHECK(task.d_qname == DNSName("cname.powerdns.com"));
2✔
1838
  BOOST_CHECK_EQUAL(task.d_qtype, QType::A);
2✔
1839

1840
  // Now simulate a succeeding task execution and NoDATA on explicit CNAME result becomes available
1841
  cnameOK = false;
2✔
1842
  sr->setNow(timeval{now + 3 * (theTTL + 1), 0});
2✔
1843
  downServers.clear();
2✔
1844
  sr->setRefreshAlmostExpired(true);
2✔
1845

1846
  ret.clear();
2✔
1847
  res = sr->beginResolve(target, QType(QType::CNAME), QClass::IN, ret);
2✔
1848
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1849
  BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2✔
1850
  BOOST_CHECK(ret[0].d_type == QType::SOA);
2✔
1851
  BOOST_CHECK_EQUAL(ret[0].d_name, auth);
2✔
1852
  BOOST_CHECK_EQUAL(downCount, 8U);
2✔
1853
  BOOST_CHECK_EQUAL(lookupCount, 3U);
2✔
1854

1855
  // And again, result should come from cache
1856
  sr->setRefreshAlmostExpired(false);
2✔
1857
  ret.clear();
2✔
1858
  res = sr->beginResolve(target, QType(QType::CNAME), QClass::IN, ret);
2✔
1859
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1860
  BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2✔
1861
  BOOST_CHECK(ret[0].d_type == QType::SOA);
2✔
1862
  BOOST_CHECK_EQUAL(ret[0].d_name, auth);
2✔
1863
  BOOST_CHECK_EQUAL(downCount, 8U);
2✔
1864
  BOOST_CHECK_EQUAL(lookupCount, 3U);
2✔
1865
}
2✔
1866

1867
BOOST_AUTO_TEST_CASE(test_servestale_immediateservfail)
1868
{
2✔
1869
  std::unique_ptr<SyncRes> sr;
2✔
1870
  initSR(sr);
2✔
1871
  MemRecursorCache::s_maxServedStaleExtensions = 1440;
2✔
1872

1873
  primeHints();
2✔
1874

1875
  const DNSName target("powerdns.com.");
2✔
1876

1877
  std::set<ComboAddress> downServers;
2✔
1878
  size_t downCount = 0;
2✔
1879
  size_t lookupCount = 0;
2✔
1880

1881
  const int theTTL = 5;
2✔
1882

1883
  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✔
1884
    /* this will cause issue with qname minimization if we ever implement it */
1885

1886
    if (downServers.find(address) != downServers.end()) {
10✔
1887
      downCount++;
2✔
1888
      throw ImmediateServFailException("FAIL!");
2✔
1889
    }
2✔
1890

1891
    if (isRootServer(address)) {
8✔
1892
      setLWResult(res, 0, false, false, true);
2✔
1893
      addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY);
2✔
1894
      addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL);
2✔
1895
      addRecordToLW(res, "a.gtld-servers.net.", QType::AAAA, "2001:DB8::1", DNSResourceRecord::ADDITIONAL);
2✔
1896
      return LWResult::Result::Success;
2✔
1897
    }
2✔
1898
    if (address == ComboAddress("192.0.2.1:53") || address == ComboAddress("[2001:DB8::1]:53")) {
6✔
1899
      setLWResult(res, 0, false, false, true);
4✔
1900
      addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, theTTL);
4✔
1901
      addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns2.powerdns.com.", DNSResourceRecord::AUTHORITY, theTTL);
4✔
1902
      addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 5);
4✔
1903
      addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::AAAA, "2001:DB8::2", DNSResourceRecord::ADDITIONAL, theTTL);
4✔
1904
      addRecordToLW(res, "pdns-public-ns2.powerdns.com.", QType::A, "192.0.2.3", DNSResourceRecord::ADDITIONAL, 5);
4✔
1905
      addRecordToLW(res, "pdns-public-ns2.powerdns.com.", QType::AAAA, "2001:DB8::3", DNSResourceRecord::ADDITIONAL, theTTL);
4✔
1906
      return LWResult::Result::Success;
4✔
1907
    }
4✔
1908
    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!
1909
      setLWResult(res, 0, true, false, true);
2✔
1910
      addRecordToLW(res, target, QType::A, "192.0.2.4", DNSResourceRecord::ANSWER, 5);
2✔
1911
      lookupCount++;
2✔
1912
      return LWResult::Result::Success;
2✔
1913
    }
2✔
1914
    else {
×
1915
      return LWResult::Result::Timeout;
×
1916
    }
×
1917
  });
2✔
1918

1919
  time_t now = time(nullptr);
2✔
1920

1921
  vector<DNSRecord> ret;
2✔
1922
  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1923

1924
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1925
  BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2✔
1926
  BOOST_CHECK(ret[0].d_type == QType::A);
2✔
1927
  BOOST_CHECK_EQUAL(ret[0].d_name, target);
2✔
1928
  BOOST_CHECK_EQUAL(downCount, 0U);
2✔
1929
  BOOST_CHECK_EQUAL(lookupCount, 1U);
2✔
1930

1931
  downServers.insert(ComboAddress("192.0.2.2:53"));
2✔
1932
  downServers.insert(ComboAddress("192.0.2.3:53"));
2✔
1933
  downServers.insert(ComboAddress("[2001:DB8::2]:53"));
2✔
1934
  downServers.insert(ComboAddress("[2001:DB8::3]:53"));
2✔
1935

1936
  sr->setNow(timeval{now + theTTL + 1, 0});
2✔
1937

1938
  BOOST_REQUIRE_EQUAL(getTaskSize(), 0U);
2✔
1939

1940
  // record is expired, so serve stale should kick in
1941
  ret.clear();
2✔
1942
  BOOST_REQUIRE_THROW(sr->beginResolve(target, QType(QType::A), QClass::IN, ret), ImmediateServFailException);
2✔
1943
  BOOST_CHECK_EQUAL(downCount, 1U);
2✔
1944
}
2✔
1945

1946
BOOST_AUTO_TEST_CASE(test_glued_referral_additional_update)
1947
{
2✔
1948
  // Test that additional records update the cache
1949
  // We use two zones that share NS and their addresses
1950
  std::unique_ptr<SyncRes> sr;
2✔
1951
  initSR(sr);
2✔
1952
  primeHints();
2✔
1953

1954
  const DNSName target1("powerdns.com.");
2✔
1955
  const DNSName target2("pdns.com.");
2✔
1956

1957
  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✔
1958
    /* this will cause issue with qname minimization if we ever implement it */
1959
    if (domain != target1 && domain != target2) {
10!
1960
      return LWResult::Result::Timeout;
×
1961
    }
×
1962

1963
    if (isRootServer(address)) {
10✔
1964
      setLWResult(res, 0, false, false, true);
2✔
1965
      addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
2✔
1966
      addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
2✔
1967
      addRecordToLW(res, "a.gtld-servers.net.", QType::AAAA, "2001:DB8::1", DNSResourceRecord::ADDITIONAL, 3600);
2✔
1968
      return LWResult::Result::Success;
2✔
1969
    }
2✔
1970
    if (address == ComboAddress("192.0.2.1:53") || address == ComboAddress("[2001:DB8::1]:53")) {
8✔
1971
      if (domain == target1) {
4✔
1972
        setLWResult(res, 0, false, false, true);
2✔
1973
        addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
2✔
1974
        addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns2.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
2✔
1975
        addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 172800);
2✔
1976
        addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::AAAA, "2001:DB8::2", DNSResourceRecord::ADDITIONAL, 172800);
2✔
1977
        addRecordToLW(res, "pdns-public-ns2.powerdns.com.", QType::A, "192.0.2.3", DNSResourceRecord::ADDITIONAL, 172800);
2✔
1978
        addRecordToLW(res, "pdns-public-ns2.powerdns.com.", QType::AAAA, "2001:DB8::3", DNSResourceRecord::ADDITIONAL, 172800);
2✔
1979
        return LWResult::Result::Success;
2✔
1980
      }
2✔
1981
      if (domain == target2) {
2!
1982
        setLWResult(res, 0, false, false, true);
2✔
1983
        addRecordToLW(res, "pdns.com.", QType::NS, "pdns-public-ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
2✔
1984
        addRecordToLW(res, "pdns.com.", QType::NS, "pdns-public-ns2.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
2✔
1985
        addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 172800);
2✔
1986
        addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::AAAA, "2001:DB8::2", DNSResourceRecord::ADDITIONAL, 172800);
2✔
1987
        addRecordToLW(res, "pdns-public-ns2.powerdns.com.", QType::A, "192.0.2.3", DNSResourceRecord::ADDITIONAL, 172800);
2✔
1988
        addRecordToLW(res, "pdns-public-ns2.powerdns.com.", QType::AAAA, "2001:DB8::3", DNSResourceRecord::ADDITIONAL, 172800);
2✔
1989
        return LWResult::Result::Success;
2✔
1990
      }
2✔
1991
      return LWResult::Result::Timeout;
×
1992
    }
2✔
1993
    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")) {
4!
1994
      setLWResult(res, 0, true, false, true);
4✔
1995
      if (domain == target1) {
4✔
1996
        addRecordToLW(res, target1, QType::A, "192.0.2.4");
2✔
1997
      }
2✔
1998
      else if (domain == target2) {
2!
1999
        addRecordToLW(res, target2, QType::A, "192.0.2.5");
2✔
2000
      }
2✔
2001
      return LWResult::Result::Success;
4✔
2002
    }
4✔
2003
    else {
×
2004
      return LWResult::Result::Timeout;
×
2005
    }
×
2006
  });
4✔
2007

2008
  // Lookup first name. We should see the address of an nameserver in the cache
2009
  vector<DNSRecord> ret;
2✔
2010
  int res = sr->beginResolve(target1, QType(QType::A), QClass::IN, ret);
2✔
2011
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
2012
  BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2✔
2013
  BOOST_CHECK(ret[0].d_type == QType::A);
2✔
2014
  BOOST_CHECK_EQUAL(ret[0].d_name, target1);
2✔
2015

2016
  auto firstTTL = g_recCache->get(sr->getNow().tv_sec, DNSName("pdns-public-ns1.powerdns.com"), QType::A, MemRecursorCache::None, nullptr, ComboAddress());
2✔
2017

2018
  // Move the time
2019
  sr->setNow({sr->getNow().tv_sec + 2, sr->getNow().tv_usec});
2✔
2020

2021
  // Lookup second name. We should see the address rec of a nameserver in the cache being updated
2022
  ret.clear();
2✔
2023
  res = sr->beginResolve(target2, QType(QType::A), QClass::IN, ret);
2✔
2024
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
2025
  BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2✔
2026
  BOOST_CHECK(ret[0].d_type == QType::A);
2✔
2027
  BOOST_CHECK_EQUAL(ret[0].d_name, target2);
2✔
2028

2029
  auto secondTTL = g_recCache->get(sr->getNow().tv_sec, DNSName("pdns-public-ns1.powerdns.com"), QType::A, MemRecursorCache::None, nullptr, ComboAddress());
2✔
2030
  // TTL shoud be back to original value
2031
  BOOST_CHECK_EQUAL(firstTTL, secondTTL);
2✔
2032
}
2✔
2033

2034
BOOST_AUTO_TEST_CASE(test_glued_referral_additional_no_update_because_locked)
2035
{
2✔
2036
  // Test that additional records do not update the cache
2037
  // We use two zones that share NS and their addresses
2038
  std::unique_ptr<SyncRes> sr;
2✔
2039
  initSR(sr);
2✔
2040
  // Set the lock
2041
  SyncRes::s_locked_ttlperc = 50;
2✔
2042

2043
  primeHints();
2✔
2044

2045
  const DNSName target1("powerdns.com.");
2✔
2046
  const DNSName target2("pdns.com.");
2✔
2047

2048
  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✔
2049
    /* this will cause issue with qname minimization if we ever implement it */
2050
    if (domain != target1 && domain != target2) {
10!
2051
      return LWResult::Result::Timeout;
×
2052
    }
×
2053

2054
    if (isRootServer(address)) {
10✔
2055
      setLWResult(res, 0, false, false, true);
2✔
2056
      addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
2✔
2057
      addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
2✔
2058
      addRecordToLW(res, "a.gtld-servers.net.", QType::AAAA, "2001:DB8::1", DNSResourceRecord::ADDITIONAL, 3600);
2✔
2059
      return LWResult::Result::Success;
2✔
2060
    }
2✔
2061
    if (address == ComboAddress("192.0.2.1:53") || address == ComboAddress("[2001:DB8::1]:53")) {
8✔
2062
      if (domain == target1) {
4✔
2063
        setLWResult(res, 0, false, false, true);
2✔
2064
        addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
2✔
2065
        addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns2.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
2✔
2066
        addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 172800);
2✔
2067
        addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::AAAA, "2001:DB8::2", DNSResourceRecord::ADDITIONAL, 172800);
2✔
2068
        addRecordToLW(res, "pdns-public-ns2.powerdns.com.", QType::A, "192.0.2.3", DNSResourceRecord::ADDITIONAL, 172800);
2✔
2069
        addRecordToLW(res, "pdns-public-ns2.powerdns.com.", QType::AAAA, "2001:DB8::3", DNSResourceRecord::ADDITIONAL, 172800);
2✔
2070
        return LWResult::Result::Success;
2✔
2071
      }
2✔
2072
      if (domain == target2) {
2!
2073
        setLWResult(res, 0, false, false, true);
2✔
2074
        addRecordToLW(res, "pdns.com.", QType::NS, "pdns-public-ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
2✔
2075
        addRecordToLW(res, "pdns.com.", QType::NS, "pdns-public-ns2.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
2✔
2076
        addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 172800);
2✔
2077
        addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::AAAA, "2001:DB8::2", DNSResourceRecord::ADDITIONAL, 172800);
2✔
2078
        addRecordToLW(res, "pdns-public-ns2.powerdns.com.", QType::A, "192.0.2.3", DNSResourceRecord::ADDITIONAL, 172800);
2✔
2079
        addRecordToLW(res, "pdns-public-ns2.powerdns.com.", QType::AAAA, "2001:DB8::3", DNSResourceRecord::ADDITIONAL, 172800);
2✔
2080
        return LWResult::Result::Success;
2✔
2081
      }
2✔
2082
      return LWResult::Result::Timeout;
×
2083
    }
2✔
2084
    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")) {
4!
2085
      setLWResult(res, 0, true, false, true);
4✔
2086
      if (domain == target1) {
4✔
2087
        addRecordToLW(res, target1, QType::A, "192.0.2.4");
2✔
2088
      }
2✔
2089
      else if (domain == target2) {
2!
2090
        addRecordToLW(res, target2, QType::A, "192.0.2.5");
2✔
2091
      }
2✔
2092
      return LWResult::Result::Success;
4✔
2093
    }
4✔
2094
    else {
×
2095
      return LWResult::Result::Timeout;
×
2096
    }
×
2097
  });
4✔
2098

2099
  // Lookup first name. We should see the address of an nameserver in the cache
2100
  vector<DNSRecord> ret;
2✔
2101
  int res = sr->beginResolve(target1, QType(QType::A), QClass::IN, ret);
2✔
2102
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
2103
  BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2✔
2104
  BOOST_CHECK(ret[0].d_type == QType::A);
2✔
2105
  BOOST_CHECK_EQUAL(ret[0].d_name, target1);
2✔
2106

2107
  auto firstTTL = g_recCache->get(sr->getNow().tv_sec, DNSName("pdns-public-ns1.powerdns.com"), QType::A, MemRecursorCache::None, nullptr, ComboAddress());
2✔
2108

2109
  // Move the time
2110
  sr->setNow({sr->getNow().tv_sec + 2, sr->getNow().tv_usec});
2✔
2111

2112
  // Lookup second name. We should see the address of an nameserver in the cache *not* being updated
2113
  ret.clear();
2✔
2114
  res = sr->beginResolve(target2, QType(QType::A), QClass::IN, ret);
2✔
2115
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
2116
  BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2✔
2117
  BOOST_CHECK(ret[0].d_type == QType::A);
2✔
2118
  BOOST_CHECK_EQUAL(ret[0].d_name, target2);
2✔
2119

2120
  auto secondTTL = g_recCache->get(sr->getNow().tv_sec, DNSName("pdns-public-ns1.powerdns.com"), QType::A, MemRecursorCache::None, nullptr, ComboAddress());
2✔
2121
  // Time has passed, so ttl1 != ttl2
2122
  BOOST_CHECK_NE(firstTTL, secondTTL);
2✔
2123
}
2✔
2124

2125
BOOST_AUTO_TEST_CASE(test_locked_nonauth_update_to_auth)
2126
{
2✔
2127
  // Test that a non-bogus authoritative record replaces a non-authoritative one
2128
  // even if the cache is locked
2129
  std::unique_ptr<SyncRes> sr;
2✔
2130
  initSR(sr);
2✔
2131
  // Set the lock
2132
  SyncRes::s_locked_ttlperc = 50;
2✔
2133

2134
  primeHints();
2✔
2135

2136
  const DNSName target("powerdns.com.");
2✔
2137

2138
  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✔
2139
    /* this will cause issue with qname minimization if we ever implement it */
2140
    if (domain != target) {
8!
2141
      return LWResult::Result::Timeout;
×
2142
    }
×
2143

2144
    if (isRootServer(address)) {
8✔
2145
      setLWResult(res, 0, false, false, true);
2✔
2146
      addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
2✔
2147
      addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
2✔
2148
      addRecordToLW(res, "a.gtld-servers.net.", QType::AAAA, "2001:DB8::1", DNSResourceRecord::ADDITIONAL, 3600);
2✔
2149
      return LWResult::Result::Success;
2✔
2150
    }
2✔
2151
    if (address == ComboAddress("192.0.2.1:53") || address == ComboAddress("[2001:DB8::1]:53")) {
6!
2152
      setLWResult(res, 0, false, false, true);
2✔
2153
      addRecordToLW(res, target, QType::NS, "pdns-public-ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
2✔
2154
      addRecordToLW(res, target, QType::NS, "pdns-public-ns2.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
2✔
2155
      addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 172800);
2✔
2156
      addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::AAAA, "2001:DB8::2", DNSResourceRecord::ADDITIONAL, 172800);
2✔
2157
      addRecordToLW(res, "pdns-public-ns2.powerdns.com.", QType::A, "192.0.2.3", DNSResourceRecord::ADDITIONAL, 172800);
2✔
2158
      addRecordToLW(res, "pdns-public-ns2.powerdns.com.", QType::AAAA, "2001:DB8::3", DNSResourceRecord::ADDITIONAL, 172800);
2✔
2159
      return LWResult::Result::Success;
2✔
2160
    }
2✔
2161
    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")) {
4!
2162
      if (type == QType::A) {
4✔
2163
        setLWResult(res, 0, true, false, true);
2✔
2164
        addRecordToLW(res, target, QType::A, "192.0.2.4");
2✔
2165
        return LWResult::Result::Success;
2✔
2166
      }
2✔
2167
      if (type == QType::NS) {
2!
2168
        setLWResult(res, 0, true, false, true);
2✔
2169
        addRecordToLW(res, target, QType::NS, "pdns-public-ns1.powerdns.com.", DNSResourceRecord::ANSWER, 172800);
2✔
2170
        addRecordToLW(res, target, QType::NS, "pdns-public-ns2.powerdns.com.", DNSResourceRecord::ANSWER, 172800);
2✔
2171
        return LWResult::Result::Success;
2✔
2172
      }
2✔
2173
    }
2✔
2174
    return LWResult::Result::Timeout;
×
2175
  });
4✔
2176

2177
  // Lookup first name. We should see the (unauth) nameserver in the cache
2178
  vector<DNSRecord> ret;
2✔
2179
  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
2180
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
2181
  BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2✔
2182
  BOOST_CHECK(ret[0].d_type == QType::A);
2✔
2183
  BOOST_CHECK_EQUAL(ret[0].d_name, target);
2✔
2184

2185
  auto firstTTL = g_recCache->get(sr->getNow().tv_sec, target, QType::NS, MemRecursorCache::None, nullptr, ComboAddress());
2✔
2186
  BOOST_CHECK_GT(firstTTL, 0);
2✔
2187

2188
  // Lookup NS records. We should see the nameserver in the cache being updated to auth
2189
  ret.clear();
2✔
2190
  res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
2✔
2191
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
2192
  BOOST_REQUIRE_EQUAL(ret.size(), 2U);
2✔
2193
  BOOST_CHECK(ret[0].d_type == QType::NS);
2✔
2194

2195
  auto secondTTL = g_recCache->get(sr->getNow().tv_sec, target, QType::NS, MemRecursorCache::RequireAuth, nullptr, ComboAddress());
2✔
2196
  BOOST_CHECK_GT(secondTTL, 0);
2✔
2197
}
2✔
2198

2199
BOOST_AUTO_TEST_CASE(test_nodata_ok)
2200
{
2✔
2201
  vector<DNSRecord> vec;
2✔
2202
  vec.emplace_back("nz.compass.com", nullptr, QType::CNAME, QClass::IN, 60, 0, DNSResourceRecord::ANSWER);
2✔
2203
  vec.emplace_back("nz.compass.com", nullptr, QType::RRSIG, QClass::IN, 60, 0, DNSResourceRecord::ANSWER);
2✔
2204
  vec.emplace_back("kslicmitv6qe1behk70g8q7e572vabp0.kompass.com", nullptr, QType::NSEC3, QClass::IN, 60, 0, DNSResourceRecord::AUTHORITY);
2✔
2205
  vec.emplace_back("kslicmitv6qe1behk70g8q7e572vabp0.kompass.com", nullptr, QType::RRSIG, QClass::IN, 60, 0, DNSResourceRecord::AUTHORITY);
2✔
2206

2207
  BOOST_CHECK(SyncRes::answerIsNOData(QType::A, RCode::NoError, vec));
2✔
2208
}
2✔
2209

2210
BOOST_AUTO_TEST_CASE(test_nodata_not)
2211
{
2✔
2212
  vector<DNSRecord> vec;
2✔
2213
  vec.emplace_back("kc-pro.westeurope.cloudapp.azure.com", nullptr, QType::A, QClass::IN, 60, 0, DNSResourceRecord::ANSWER);
2✔
2214
  vec.emplace_back("nz.compass.com", nullptr, QType::CNAME, QClass::IN, 60, 0, DNSResourceRecord::ANSWER);
2✔
2215
  vec.emplace_back("nz.compass.com", nullptr, QType::RRSIG, QClass::IN, 60, 0, DNSResourceRecord::ANSWER);
2✔
2216
  vec.emplace_back("kslicmitv6qe1behk70g8q7e572vabp0.kompass.com", nullptr, QType::NSEC3, QClass::IN, 60, 0, DNSResourceRecord::AUTHORITY);
2✔
2217
  vec.emplace_back("kslicmitv6qe1behk70g8q7e572vabp0.kompass.com", nullptr, QType::RRSIG, QClass::IN, 60, 0, DNSResourceRecord::AUTHORITY);
2✔
2218

2219
  BOOST_CHECK(!SyncRes::answerIsNOData(QType::A, RCode::NoError, vec));
2✔
2220
}
2✔
2221

2222
BOOST_AUTO_TEST_CASE(test_nodata_out_of_order)
2223
{
2✔
2224
  vector<DNSRecord> vec;
2✔
2225
  vec.emplace_back("nz.compass.com", nullptr, QType::CNAME, QClass::IN, 60, 0, DNSResourceRecord::ANSWER);
2✔
2226
  vec.emplace_back("nz.compass.com", nullptr, QType::RRSIG, QClass::IN, 60, 0, DNSResourceRecord::ANSWER);
2✔
2227
  vec.emplace_back("kslicmitv6qe1behk70g8q7e572vabp0.kompass.com", nullptr, QType::NSEC3, QClass::IN, 60, 0, DNSResourceRecord::AUTHORITY);
2✔
2228
  vec.emplace_back("kslicmitv6qe1behk70g8q7e572vabp0.kompass.com", nullptr, QType::RRSIG, QClass::IN, 60, 0, DNSResourceRecord::AUTHORITY);
2✔
2229
  vec.emplace_back("kc-pro.westeurope.cloudapp.azure.com", nullptr, QType::A, QClass::IN, 60, 0, DNSResourceRecord::ANSWER);
2✔
2230

2231
  BOOST_CHECK(!SyncRes::answerIsNOData(QType::A, RCode::NoError, vec));
2✔
2232
}
2✔
2233

2234
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