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

PowerDNS / pdns / 17765900968

16 Sep 2025 12:33PM UTC coverage: 65.987% (-0.04%) from 66.029%
17765900968

Pull #16108

github

web-flow
Merge 4059c5fe8 into 2e297650d
Pull Request #16108: dnsdist: implement simple packet shuffle in cache

42424 of 93030 branches covered (45.6%)

Branch coverage included in aggregate %.

9 of 135 new or added lines in 6 files covered. (6.67%)

34 existing lines in 8 files now uncovered.

128910 of 166619 relevant lines covered (77.37%)

5500581.66 hits per line

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

90.23
/pdns/recursordist/test-syncres_cc1.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_cc1)
12

13
BOOST_AUTO_TEST_CASE(test_root_primed)
14
{
2✔
15
  std::unique_ptr<SyncRes> sr;
2✔
16
  initSR(sr);
2✔
17

18
  primeHints();
2✔
19

20
  const DNSName target("a.root-servers.net.");
2✔
21
  try {
2✔
22
    /* we are primed, but only with non-auth data so we cannot resolve A a.root-servers.net. without any query */
23
    vector<DNSRecord> ret;
2✔
24
    int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
25
    BOOST_CHECK_EQUAL(res, RCode::ServFail);
2✔
26
    BOOST_REQUIRE_EQUAL(ret.size(), 0U);
2✔
27

28
    ret.clear();
2✔
29
    res = sr->beginResolve(target, QType(QType::AAAA), QClass::IN, ret);
2✔
30
    BOOST_CHECK_EQUAL(res, RCode::ServFail);
2✔
31
    BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Indeterminate);
2✔
32
    BOOST_REQUIRE_EQUAL(ret.size(), 0U);
2✔
33
    BOOST_CHECK(false);
2✔
34
  }
2✔
35
  catch (const ImmediateServFailException) {
2✔
36
    // Expected
37
  }
2✔
38
}
2✔
39

40
BOOST_AUTO_TEST_CASE(test_root_primed_ns)
41
{
2✔
42
  std::unique_ptr<SyncRes> sr;
2✔
43
  initSR(sr);
2✔
44

45
  primeHints();
2✔
46
  const DNSName target(".");
2✔
47

48
  /* we are primed, but we should not be able to NS . without any query
49
   because the . NS entry is not stored as authoritative */
50

51
  size_t queriesCount = 0;
2✔
52

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

56
    if (domain == target && type == QType::NS) {
2!
57

58
      setLWResult(res, 0, true, false, true);
2✔
59
      char addr[] = "a.root-servers.net.";
2✔
60
      for (char idx = 'a'; idx <= 'm'; idx++) {
28✔
61
        addr[0] = idx;
26✔
62
        addRecordToLW(res, g_rootdnsname, QType::NS, std::string(addr), DNSResourceRecord::ANSWER, 3600);
26✔
63
      }
26✔
64

65
      addRecordToLW(res, "a.root-servers.net.", QType::A, "198.41.0.4", DNSResourceRecord::ADDITIONAL, 3600);
2✔
66
      addRecordToLW(res, "a.root-servers.net.", QType::AAAA, "2001:503:ba3e::2:30", DNSResourceRecord::ADDITIONAL, 3600);
2✔
67

68
      return LWResult::Result::Success;
2✔
69
    }
2✔
70

71
    return LWResult::Result::Timeout;
×
72
  });
2✔
73

74
  vector<DNSRecord> ret;
2✔
75
  int res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
2✔
76
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
77
  BOOST_REQUIRE_EQUAL(ret.size(), 13U);
2✔
78
  BOOST_CHECK_EQUAL(queriesCount, 1U);
2✔
79
}
2✔
80

81
BOOST_AUTO_TEST_CASE(test_root_not_primed)
82
{
2✔
83
  std::unique_ptr<SyncRes> sr;
2✔
84
  initSR(sr);
2✔
85

86
  size_t queriesCount = 0;
2✔
87

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

91
    if (domain == g_rootdnsname && type == QType::NS) {
4!
92
      setLWResult(res, 0, true, false, true);
4✔
93
      addRecordToLW(res, g_rootdnsname, QType::NS, "a.root-servers.net.", DNSResourceRecord::ANSWER, 3600);
4✔
94
      addRecordToLW(res, "a.root-servers.net.", QType::A, "198.41.0.4", DNSResourceRecord::ADDITIONAL, 3600);
4✔
95
      addRecordToLW(res, "a.root-servers.net.", QType::AAAA, "2001:503:ba3e::2:30", DNSResourceRecord::ADDITIONAL, 3600);
4✔
96

97
      return LWResult::Result::Success;
4✔
98
    }
4✔
99

100
    return LWResult::Result::Timeout;
×
101
  });
4✔
102

103
  /* we are not primed yet, so SyncRes will have to call primeHints()
104
     then call getRootNS(), for which at least one of the root servers needs to answer */
105
  vector<DNSRecord> ret;
2✔
106
  int res = sr->beginResolve(DNSName("."), QType(QType::NS), QClass::IN, ret);
2✔
107
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
108
  BOOST_CHECK_EQUAL(ret.size(), 1U);
2✔
109
  BOOST_CHECK_EQUAL(queriesCount, 2U);
2✔
110
}
2✔
111

112
BOOST_AUTO_TEST_CASE(test_root_not_primed_and_no_response)
113
{
2✔
114
  std::unique_ptr<SyncRes> sr;
2✔
115
  initSR(sr);
2✔
116
  // We expect an error, do not log it
117
  g_log.toConsole(Logger::Critical);
2✔
118
  std::set<ComboAddress> downServers;
2✔
119

120
  /* we are not primed yet, so SyncRes will have to call primeHints()
121
     then call getRootNS(), for which at least one of the root servers needs to answer.
122
     None will, so it should ServFail.
123
  */
124
  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 */) {
104✔
125
    downServers.insert(address);
104✔
126
    return LWResult::Result::Timeout;
104✔
127
  });
104✔
128

129
  vector<DNSRecord> ret;
2✔
130
  int res = sr->beginResolve(DNSName("."), QType(QType::NS), QClass::IN, ret);
2✔
131
  BOOST_CHECK_EQUAL(res, RCode::ServFail);
2✔
132
  BOOST_CHECK_EQUAL(ret.size(), 0U);
2✔
133
  BOOST_CHECK(downServers.size() > 0);
2✔
134
  /* we explicitly refuse to mark the root servers down */
135
  for (const auto& server : downServers) {
52✔
136
    BOOST_CHECK_EQUAL(SyncRes::getServerFailsCount(server), 0U);
52✔
137
  }
52✔
138
}
2✔
139

140
BOOST_AUTO_TEST_CASE(test_root_ns_poison_resistance)
141
{
2✔
142
  std::unique_ptr<SyncRes> sr;
2✔
143
  initSR(sr);
2✔
144

145
  primeHints();
2✔
146
  const DNSName target("www.example.com.");
2✔
147

148
  sr->setAsyncCallback([&](const ComboAddress& /* ip */, const DNSName& domain, int type, bool /* doTCP */, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval* /* now */, boost::optional<Netmask>& /* srcmask */, const ResolveContext& /* context */, LWResult* res, bool* /* chained */) {
4✔
149
    if (domain == g_rootdnsname && type == QType::NS) {
4!
150

151
      setLWResult(res, 0, true, false, true);
2✔
152
      char addr[] = "a.root-servers.net.";
2✔
153
      for (char idx = 'a'; idx <= 'm'; idx++) {
28✔
154
        addr[0] = idx;
26✔
155
        addRecordToLW(res, g_rootdnsname, QType::NS, std::string(addr), DNSResourceRecord::ANSWER, 3600);
26✔
156
      }
26✔
157

158
      addRecordToLW(res, "a.root-servers.net.", QType::A, "198.41.0.4", DNSResourceRecord::ADDITIONAL, 3600);
2✔
159
      addRecordToLW(res, "a.root-servers.net.", QType::AAAA, "2001:503:ba3e::2:30", DNSResourceRecord::ADDITIONAL, 3600);
2✔
160

161
      return LWResult::Result::Success;
2✔
162
    }
2✔
163

164
    if (domain == target && type == QType::A) {
2!
165

166
      setLWResult(res, 0, true, false, true);
2✔
167
      addRecordToLW(res, target, QType::A, "1.2.3.4", DNSResourceRecord::ANSWER, 3600);
2✔
168

169
      addRecordToLW(res, ".", QType::NS, "poison.name.", DNSResourceRecord::AUTHORITY, 3600);
2✔
170
      addRecordToLW(res, "poison.name", QType::A, "4.5.6.7", DNSResourceRecord::ADDITIONAL, 3600);
2✔
171

172
      return LWResult::Result::Success;
2✔
173
    }
2✔
174

175
    return LWResult::Result::Timeout;
×
176
  });
2✔
177

178
  vector<DNSRecord> ret;
2✔
179
  // Check we have 13 root servers
180
  int res = sr->beginResolve(g_rootdnsname, QType(QType::NS), QClass::IN, ret);
2✔
181
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
182
  BOOST_REQUIRE_EQUAL(ret.size(), 13U);
2✔
183

184
  // Try to poison
185
  ret.clear();
2✔
186
  res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
187
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
188
  BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2✔
189

190
  // Still should have 13
191
  ret.clear();
2✔
192
  res = sr->beginResolve(g_rootdnsname, QType(QType::NS), QClass::IN, ret);
2✔
193
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
194
  BOOST_REQUIRE_EQUAL(ret.size(), 13U);
2✔
195
}
2✔
196

197
BOOST_AUTO_TEST_CASE(test_root_primed_ns_update)
198
{
2✔
199
  std::unique_ptr<SyncRes> sr;
2✔
200
  initSR(sr);
2✔
201

202
  primeHints();
2✔
203
  const DNSName target(".");
2✔
204
  const DNSName aroot("a.root-servers.net.");
2✔
205
  const string newA = "1.2.3.4";
2✔
206
  const string newAAAA = "1::2";
2✔
207

208
  /* we are primed, but we should not be able to NS . without any query
209
   because the . NS entry is not stored as authoritative */
210

211
  size_t queriesCount = 0;
2✔
212

213
  auto asynccb = [&](const ComboAddress& /* ip */, const DNSName& domain, int type, bool /* doTCP */, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval* /* now */, boost::optional<Netmask>& /* srcmask */, const ResolveContext& /* context */, LWResult* res, bool* /* chained */) {
2✔
214
    queriesCount++;
2✔
215

216
    if (domain == target && type == QType::NS) {
2!
217

218
      setLWResult(res, 0, true, false, true);
2✔
219
      char addr[] = "a.root-servers.net.";
2✔
220
      for (char idx = 'a'; idx <= 'm'; idx++) {
28✔
221
        addr[0] = idx;
26✔
222
        addRecordToLW(res, g_rootdnsname, QType::NS, std::string(addr), DNSResourceRecord::ANSWER, 3600);
26✔
223
      }
26✔
224

225
      addRecordToLW(res, aroot.toString(), QType::A, newA, DNSResourceRecord::ADDITIONAL, 3600);
2✔
226
      addRecordToLW(res, aroot.toString(), QType::AAAA, newAAAA, DNSResourceRecord::ADDITIONAL, 3600);
2✔
227

228
      return LWResult::Result::Success;
2✔
229
    }
2✔
230
    return LWResult::Result::Timeout;
×
231
  };
2✔
232

233
  sr->setAsyncCallback(asynccb);
2✔
234

235
  struct timeval now;
2✔
236
  Utility::gettimeofday(&now, nullptr);
2✔
237

238
  vector<DNSRecord> ret;
2✔
239
  int res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
2✔
240
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
241
  BOOST_REQUIRE_EQUAL(ret.size(), 13U);
2✔
242
  BOOST_CHECK_EQUAL(queriesCount, 1U);
2✔
243

244
  ret.clear();
2✔
245
  time_t cached = g_recCache->get(now.tv_sec, aroot, QType::A, MemRecursorCache::None, &ret, ComboAddress());
2✔
246
  BOOST_CHECK(cached > 0);
2✔
247
  BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2✔
248
  BOOST_CHECK(getRR<ARecordContent>(ret[0])->getCA() == ComboAddress(newA));
2✔
249

250
  ret.clear();
2✔
251
  cached = g_recCache->get(now.tv_sec, aroot, QType::AAAA, MemRecursorCache::None, &ret, ComboAddress());
2✔
252
  BOOST_CHECK(cached > 0);
2✔
253
  BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2✔
254
  BOOST_CHECK(getRR<AAAARecordContent>(ret[0])->getCA() == ComboAddress(newAAAA));
2✔
255
}
2✔
256

257
static void test_edns_formerr_fallback_f(bool sample)
258
{
2✔
259
  std::unique_ptr<SyncRes> sr;
2✔
260
  initSR(sr);
2✔
261
  if (sample) {
2!
262
    sr->setQNameMinimization();
×
263
  }
×
264
  ComboAddress noEDNSServer;
2✔
265
  size_t queriesWithEDNS = 0;
2✔
266
  size_t queriesWithoutEDNS = 0;
2✔
267

268
  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✔
269
    if (EDNS0Level != 0) {
4✔
270
      queriesWithEDNS++;
2✔
271
      noEDNSServer = address;
2✔
272

273
      setLWResult(res, RCode::FormErr);
2✔
274
      return LWResult::Result::Success;
2✔
275
    }
2✔
276

277
    queriesWithoutEDNS++;
2✔
278

279
    if (domain == DNSName("powerdns.com") && type == QType::A && !doTCP) {
2!
280
      setLWResult(res, 0, true, false, false);
2✔
281
      addRecordToLW(res, domain, QType::A, "192.0.2.1");
2✔
282
      return LWResult::Result::Success;
2✔
283
    }
2✔
284

285
    return sample ? basicRecordsForQnameMinimization(res, domain, type) : LWResult::Result::Timeout;
×
286
  });
2✔
287

288
  primeHints();
2✔
289

290
  /* fake that the root NS doesn't handle EDNS, check that we fallback */
291
  vector<DNSRecord> ret;
2✔
292
  int res = sr->beginResolve(DNSName("powerdns.com."), QType(QType::A), QClass::IN, ret);
2✔
293
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
294
  BOOST_CHECK_EQUAL(ret.size(), 1U);
2✔
295
  BOOST_CHECK_EQUAL(queriesWithEDNS, sample ? 3U : 1U);
2✔
296
  BOOST_CHECK_EQUAL(queriesWithoutEDNS, sample ? 4U : 1U);
2✔
297
  BOOST_CHECK_EQUAL(SyncRes::getEDNSStatusesSize(), sample ? 3U : 1U);
2✔
298
  BOOST_CHECK_EQUAL(SyncRes::getEDNSStatus(noEDNSServer), SyncRes::EDNSStatus::NOEDNS);
2✔
299
}
2✔
300

301
BOOST_AUTO_TEST_CASE(test_edns_formerr_fallback)
302
{
2✔
303
  test_edns_formerr_fallback_f(false);
2✔
304
}
2✔
305

306
BOOST_AUTO_TEST_CASE(test_edns_formerr_fallback_qmin)
307
{
2✔
308
  // DISABLED UNTIL QNAME MINIMIZATION IS THERE
309
  return;
2✔
310
  test_edns_formerr_fallback_f(true);
×
311
}
×
312

313
BOOST_AUTO_TEST_CASE(test_edns_formerr_but_edns_enabled)
314
{
2✔
315
  std::unique_ptr<SyncRes> sr;
2✔
316
  initSR(sr);
2✔
317

318
  /* in this test, the auth answers with FormErr to an EDNS-enabled
319
     query, but the response does contain EDNS so we should not mark
320
     it as EDNS ignorant or intolerant.
321
  */
322
  size_t queriesWithEDNS = 0;
2✔
323
  size_t queriesWithoutEDNS = 0;
2✔
324
  std::set<ComboAddress> usedServers;
2✔
325

326
  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 */) {
52✔
327
    if (EDNS0Level > 0) {
52!
328
      queriesWithEDNS++;
52✔
329
    }
52✔
330
    else {
×
331
      queriesWithoutEDNS++;
×
332
    }
×
333
    usedServers.insert(address);
52✔
334

335
    if (type == QType::DNAME) {
52!
336
      setLWResult(res, RCode::FormErr);
52✔
337
      if (EDNS0Level > 0) {
52!
338
        res->d_haveEDNS = true;
52✔
339
      }
52✔
340
      return LWResult::Result::Success;
52✔
341
    }
52✔
342

343
    return LWResult::Result::Timeout;
×
344
  });
52✔
345

346
  primeHints();
2✔
347

348
  vector<DNSRecord> ret;
2✔
349
  int res = sr->beginResolve(DNSName("powerdns.com."), QType(QType::DNAME), QClass::IN, ret);
2✔
350
  BOOST_CHECK_EQUAL(res, RCode::ServFail);
2✔
351
  BOOST_CHECK_EQUAL(ret.size(), 0U);
2✔
352
  BOOST_CHECK_EQUAL(queriesWithEDNS, 26U);
2✔
353
  BOOST_CHECK_EQUAL(queriesWithoutEDNS, 0U);
2✔
354
  BOOST_CHECK_EQUAL(SyncRes::getEDNSStatusesSize(), 0U);
2✔
355
  BOOST_CHECK_EQUAL(usedServers.size(), 26U);
2✔
356
  for (const auto& server : usedServers) {
52✔
357
    BOOST_CHECK_EQUAL(SyncRes::getEDNSStatus(server), SyncRes::EDNSStatus::EDNSOK);
52✔
358
  }
52✔
359
}
2✔
360

361
BOOST_AUTO_TEST_CASE(test_meta_types)
362
{
2✔
363
  std::unique_ptr<SyncRes> sr;
2✔
364
  initSR(sr);
2✔
365

366
  static const std::set<uint16_t> invalidTypes = {128, QType::AXFR, QType::IXFR, QType::RRSIG, QType::NSEC3, QType::OPT, QType::TSIG, QType::TKEY, QType::MAILA, QType::MAILB, 65535};
2✔
367

368
  for (const auto qtype : invalidTypes) {
22✔
369
    size_t queriesCount = 0;
22✔
370

371
    sr->setAsyncCallback([&](const ComboAddress& /* ip */, const DNSName& /* domain */, int /* type */, bool /* doTCP */, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval* /* now */, boost::optional<Netmask>& /* srcmask */, const ResolveContext& /* context */, LWResult* /* res */, bool* /* chained */) {
22✔
372
      queriesCount++;
×
373
      return LWResult::Result::Timeout;
×
374
    });
×
375

376
    primeHints();
22✔
377

378
    vector<DNSRecord> ret;
22✔
379
    int res = sr->beginResolve(DNSName("powerdns.com."), QType(qtype), QClass::IN, ret);
22✔
380
    BOOST_CHECK_EQUAL(res, -1);
22✔
381
    BOOST_CHECK_EQUAL(ret.size(), 0U);
22✔
382
    BOOST_CHECK_EQUAL(queriesCount, 0U);
22✔
383
  }
22✔
384
}
2✔
385

386
BOOST_AUTO_TEST_CASE(test_tc_fallback_to_tcp)
387
{
2✔
388
  std::unique_ptr<SyncRes> sr;
2✔
389
  initSR(sr);
2✔
390

391
  sr->setAsyncCallback([&](const ComboAddress& /* ip */, const DNSName& domain, int type, bool doTCP, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval* /* now */, boost::optional<Netmask>& /* srcmask */, const ResolveContext& /* context */, LWResult* res, bool* /* chained */) {
4✔
392
    if (!doTCP) {
4✔
393
      setLWResult(res, 0, false, true, false);
2✔
394
      return LWResult::Result::Success;
2✔
395
    }
2✔
396
    if (domain == DNSName("powerdns.com") && type == QType::A && doTCP) {
2!
397
      setLWResult(res, 0, true, false, false);
2✔
398
      addRecordToLW(res, domain, QType::A, "192.0.2.1");
2✔
399
      return LWResult::Result::Success;
2✔
400
    }
2✔
401

402
    return LWResult::Result::Timeout;
×
403
  });
2✔
404

405
  primeHints();
2✔
406

407
  /* fake that the NS truncates every request over UDP, we should fallback to TCP */
408
  vector<DNSRecord> ret;
2✔
409
  int res = sr->beginResolve(DNSName("powerdns.com."), QType(QType::A), QClass::IN, ret);
2✔
410
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
411
}
2✔
412

413
BOOST_AUTO_TEST_CASE(test_tc_over_tcp)
414
{
2✔
415
  std::unique_ptr<SyncRes> sr;
2✔
416
  initSR(sr);
2✔
417

418
  size_t tcpQueriesCount = 0;
2✔
419

420
  sr->setAsyncCallback([&](const ComboAddress& /* ip */, const DNSName& domain, int /* type */, bool doTCP, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval* /* now */, boost::optional<Netmask>& /* srcmask */, const ResolveContext& /* context */, LWResult* res, bool* /* chained */) {
8✔
421
    if (!doTCP) {
8✔
422
      setLWResult(res, 0, true, true, false);
4✔
423
      return LWResult::Result::Success;
4✔
424
    }
4✔
425

426
    /* first TCP query is answered with a TC response */
427
    tcpQueriesCount++;
4✔
428
    if (tcpQueriesCount == 1) {
4✔
429
      setLWResult(res, 0, true, true, false);
2✔
430
    }
2✔
431
    else {
2✔
432
      setLWResult(res, 0, true, false, false);
2✔
433
    }
2✔
434

435
    addRecordToLW(res, domain, QType::A, "192.0.2.1");
4✔
436
    return LWResult::Result::Success;
4✔
437
  });
8✔
438

439
  primeHints();
2✔
440

441
  vector<DNSRecord> ret;
2✔
442
  int res = sr->beginResolve(DNSName("powerdns.com."), QType(QType::A), QClass::IN, ret);
2✔
443
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
444
  BOOST_CHECK_EQUAL(tcpQueriesCount, 2U);
2✔
445
}
2✔
446

447
BOOST_AUTO_TEST_CASE(test_all_nss_down)
448
{
2✔
449
  std::unique_ptr<SyncRes> sr;
2✔
450
  initSR(sr);
2✔
451
  std::set<ComboAddress> downServers;
2✔
452

453
  primeHints();
2✔
454

455
  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✔
456
    if (isRootServer(address)) {
12✔
457
      setLWResult(res, 0, false, false, true);
2✔
458
      addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
2✔
459
      addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
2✔
460
      addRecordToLW(res, "a.gtld-servers.net.", QType::AAAA, "2001:DB8::1", DNSResourceRecord::ADDITIONAL, 3600);
2✔
461
      return LWResult::Result::Success;
2✔
462
    }
2✔
463
    if (address == ComboAddress("192.0.2.1:53") || address == ComboAddress("[2001:DB8::1]:53")) {
10!
464
      setLWResult(res, 0, false, false, true);
2✔
465
      addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
2✔
466
      addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns2.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
2✔
467
      addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 172800);
2✔
468
      addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::AAAA, "2001:DB8::2", DNSResourceRecord::ADDITIONAL, 172800);
2✔
469
      addRecordToLW(res, "pdns-public-ns2.powerdns.com.", QType::A, "192.0.2.3", DNSResourceRecord::ADDITIONAL, 172800);
2✔
470
      addRecordToLW(res, "pdns-public-ns2.powerdns.com.", QType::AAAA, "2001:DB8::3", DNSResourceRecord::ADDITIONAL, 172800);
2✔
471
      return LWResult::Result::Success;
2✔
472
    }
2✔
473
    downServers.insert(address);
8✔
474
    res->d_usec = g_networkTimeoutMsec * 1000;
8✔
475
    return LWResult::Result::Timeout;
8✔
476
  });
10✔
477

478
  DNSName target("powerdns.com.");
2✔
479

480
  vector<DNSRecord> ret;
2✔
481
  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
482
  BOOST_CHECK_EQUAL(res, RCode::ServFail);
2✔
483
  BOOST_CHECK_EQUAL(ret.size(), 0U);
2✔
484
  BOOST_CHECK_EQUAL(downServers.size(), 4U);
2✔
485

486
  time_t now = sr->getNow().tv_sec;
2✔
487
  for (const auto& server : downServers) {
8✔
488
    BOOST_CHECK_EQUAL(SyncRes::getServerFailsCount(server), 1U);
8✔
489
    BOOST_CHECK(SyncRes::isThrottled(now, server, target, QType::A));
8✔
490
  }
8✔
491
}
2✔
492

493
BOOST_AUTO_TEST_CASE(test_all_nss_network_error)
494
{
2✔
495
  std::unique_ptr<SyncRes> sr;
2✔
496
  initSR(sr);
2✔
497
  std::set<ComboAddress> downServers;
2✔
498

499
  primeHints();
2✔
500

501
  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✔
502
    if (isRootServer(address)) {
12✔
503
      setLWResult(res, 0, false, false, true);
2✔
504
      addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
2✔
505
      addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
2✔
506
      addRecordToLW(res, "a.gtld-servers.net.", QType::AAAA, "2001:DB8::1", DNSResourceRecord::ADDITIONAL, 3600);
2✔
507
      return LWResult::Result::Success;
2✔
508
    }
2✔
509
    if (address == ComboAddress("192.0.2.1:53") || address == ComboAddress("[2001:DB8::1]:53")) {
10!
510
      setLWResult(res, 0, false, false, true);
2✔
511
      addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
2✔
512
      addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns2.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
2✔
513
      addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 172800);
2✔
514
      addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::AAAA, "2001:DB8::2", DNSResourceRecord::ADDITIONAL, 172800);
2✔
515
      addRecordToLW(res, "pdns-public-ns2.powerdns.com.", QType::A, "192.0.2.3", DNSResourceRecord::ADDITIONAL, 172800);
2✔
516
      addRecordToLW(res, "pdns-public-ns2.powerdns.com.", QType::AAAA, "2001:DB8::3", DNSResourceRecord::ADDITIONAL, 172800);
2✔
517
      return LWResult::Result::Success;
2✔
518
    }
2✔
519
    downServers.insert(address);
8✔
520
    res->d_usec = g_networkTimeoutMsec * 1000;
8✔
521
    return LWResult::Result::Timeout;
8✔
522
  });
10✔
523

524
  /* exact same test than the previous one, except instead of a time out we fake a network error */
525
  DNSName target("powerdns.com.");
2✔
526

527
  vector<DNSRecord> ret;
2✔
528
  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
529
  BOOST_CHECK_EQUAL(res, RCode::ServFail);
2✔
530
  BOOST_CHECK_EQUAL(ret.size(), 0U);
2✔
531
  BOOST_CHECK_EQUAL(downServers.size(), 4U);
2✔
532

533
  time_t now = sr->getNow().tv_sec;
2✔
534
  for (const auto& server : downServers) {
8✔
535
    BOOST_CHECK_EQUAL(SyncRes::getServerFailsCount(server), 1U);
8✔
536
    BOOST_CHECK(SyncRes::isThrottled(now, server, target, QType::A));
8✔
537
  }
8✔
538
}
2✔
539

540
BOOST_AUTO_TEST_CASE(test_all_nss_send_tc_then_garbage_over_tcp)
541
{
2✔
542
  std::unique_ptr<SyncRes> sr;
2✔
543
  initSR(sr);
2✔
544

545
  primeHints();
2✔
546

547
  std::set<ComboAddress> downServers;
2✔
548

549
  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✔
550
    if (isRootServer(address)) {
10✔
551
      setLWResult(res, 0, false, false, true);
2✔
552
      addRecordToLW(res, "lock-up.", QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
2✔
553
      addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
2✔
554
      addRecordToLW(res, "a.gtld-servers.net.", QType::AAAA, "2001:DB8::1", DNSResourceRecord::ADDITIONAL, 3600);
2✔
555
      return LWResult::Result::Success;
2✔
556
    }
2✔
557

558
    if (!doTCP) {
8✔
559
      setLWResult(res, 0, false, true, false);
4✔
560
      return LWResult::Result::Success;
4✔
561
    }
4✔
562
    downServers.insert(address);
4✔
563

564
    setLWResult(res, RCode::FormErr, false, false, false);
4✔
565
    res->d_validpacket = false;
4✔
566
    return LWResult::Result::Success;
4✔
567
  });
8✔
568

569
  DNSName target("www.lock-up.");
2✔
570

571
  vector<DNSRecord> ret;
2✔
572
  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
573
  BOOST_CHECK_EQUAL(res, RCode::ServFail);
2✔
574
  BOOST_CHECK_EQUAL(ret.size(), 0U);
2✔
575
  BOOST_CHECK_EQUAL(downServers.size(), 2U);
2✔
576

577
  for (const auto& server : downServers) {
4✔
578
    BOOST_CHECK(SyncRes::isThrottled(time(nullptr), server, target, QType::A));
4✔
579
    BOOST_CHECK_EQUAL(SyncRes::getNSSpeed(DNSName("a.gtld-servers.net."), server), 1000000U);
4✔
580
  }
4✔
581
}
2✔
582

583
BOOST_AUTO_TEST_CASE(test_all_nss_send_garbage_over_udp)
584
{
2✔
585
  std::unique_ptr<SyncRes> sr;
2✔
586
  initSR(sr);
2✔
587

588
  primeHints();
2✔
589

590
  std::set<ComboAddress> downServers;
2✔
591
  size_t queriesCount = 0;
2✔
592

593
  sr->setAsyncCallback([&](const ComboAddress& address, const DNSName& /* domain */, int /* type */, bool /* doTCP */, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval* /* now */, boost::optional<Netmask>& /* srcmask */, const ResolveContext& /* context */, LWResult* res, bool* /* chained */) {
6✔
594
    if (isRootServer(address)) {
6✔
595
      setLWResult(res, 0, false, false, true);
2✔
596
      addRecordToLW(res, "lock-up.", QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
2✔
597
      addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
2✔
598
      addRecordToLW(res, "a.gtld-servers.net.", QType::AAAA, "2001:DB8::1", DNSResourceRecord::ADDITIONAL, 3600);
2✔
599
      return LWResult::Result::Success;
2✔
600
    }
2✔
601

602
    ++queriesCount;
4✔
603
    downServers.insert(address);
4✔
604

605
    setLWResult(res, RCode::FormErr, false, false, false);
4✔
606
    res->d_validpacket = false;
4✔
607
    return LWResult::Result::Success;
4✔
608
  });
6✔
609

610
  DNSName target("www.lock-up.");
2✔
611

612
  vector<DNSRecord> ret;
2✔
613
  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
614
  BOOST_CHECK_EQUAL(res, RCode::ServFail);
2✔
615
  BOOST_CHECK_EQUAL(ret.size(), 0U);
2✔
616
  BOOST_CHECK_EQUAL(downServers.size(), 2U);
2✔
617
  /* two queries with EDNS, that's it */
618
  BOOST_CHECK_EQUAL(queriesCount, 2U);
2✔
619

620
  for (const auto& server : downServers) {
4✔
621
    BOOST_CHECK(SyncRes::isThrottled(time(nullptr), server, target, QType::A));
4✔
622
    BOOST_CHECK_EQUAL(SyncRes::getNSSpeed(DNSName("a.gtld-servers.net."), server), 1000000U);
4✔
623
    BOOST_CHECK_EQUAL(SyncRes::getEDNSStatus(server), SyncRes::EDNSStatus::EDNSIGNORANT);
4✔
624
  }
4✔
625
}
2✔
626

627
BOOST_AUTO_TEST_CASE(test_regular_ns_send_refused)
628
{
2✔
629
  std::unique_ptr<SyncRes> sr;
2✔
630
  initSR(sr);
2✔
631

632
  primeHints();
2✔
633

634
  std::set<ComboAddress> downServers;
2✔
635
  size_t queriesCount = 0;
2✔
636

637
  sr->setAsyncCallback([&](const ComboAddress& address, const DNSName& /* domain */, int /* type */, bool /* doTCP */, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval* /* now */, boost::optional<Netmask>& /* srcmask */, const ResolveContext& /* context */, LWResult* res, bool* /* chained */) {
6✔
638
    if (isRootServer(address)) {
6✔
639
      setLWResult(res, 0, false, false, true);
2✔
640
      addRecordToLW(res, "refused.", QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
2✔
641
      addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
2✔
642
      addRecordToLW(res, "a.gtld-servers.net.", QType::AAAA, "2001:DB8::1", DNSResourceRecord::ADDITIONAL, 3600);
2✔
643
      return LWResult::Result::Success;
2✔
644
    }
2✔
645

646
    ++queriesCount;
4✔
647
    downServers.insert(address);
4✔
648

649
    setLWResult(res, RCode::Refused, false, false, true);
4✔
650

651
    return LWResult::Result::Success;
4✔
652
  });
6✔
653

654
  DNSName target("www.refused.");
2✔
655

656
  vector<DNSRecord> ret;
2✔
657
  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
658
  BOOST_CHECK_EQUAL(res, RCode::ServFail);
2✔
659
  BOOST_CHECK_EQUAL(ret.size(), 0U);
2✔
660
  BOOST_CHECK_EQUAL(downServers.size(), 2U);
2✔
661
  BOOST_CHECK_EQUAL(queriesCount, 2U);
2✔
662

663
  for (const auto& server : downServers) {
4✔
664
    /* same as any other server */
665
    BOOST_CHECK(SyncRes::isThrottled(time(nullptr), server, target, QType::A));
4✔
666
    BOOST_CHECK_EQUAL(SyncRes::getNSSpeed(DNSName("a.gtld-servers.net."), server), 0U);
4✔
667
    BOOST_CHECK_EQUAL(SyncRes::getEDNSStatus(server), SyncRes::EDNSStatus::EDNSOK);
4✔
668
  }
4✔
669
}
2✔
670

671
BOOST_AUTO_TEST_CASE(test_forward_ns_send_refused)
672
{
2✔
673
  std::unique_ptr<SyncRes> sr;
2✔
674
  initSR(sr);
2✔
675

676
  primeHints();
2✔
677

678
  std::set<ComboAddress> downServers;
2✔
679
  size_t queriesCount = 0;
2✔
680

681
  const DNSName target("www.refused.");
2✔
682

683
  SyncRes::AuthDomain ad;
2✔
684
  const std::vector<ComboAddress> forwardedNSs{ComboAddress("192.0.2.42:53"), ComboAddress("192.0.2.43:53")};
2✔
685
  ad.d_rdForward = false;
2✔
686
  ad.d_servers = forwardedNSs;
2✔
687
  (*SyncRes::t_sstorage.domainmap)[target] = ad;
2✔
688

689
  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✔
690
    if (isRootServer(address)) {
4!
691
      setLWResult(res, 0, false, false, true);
×
692
      addRecordToLW(res, "refused.", QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
×
693
      addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
×
694
      addRecordToLW(res, "a.gtld-servers.net.", QType::AAAA, "2001:DB8::1", DNSResourceRecord::ADDITIONAL, 3600);
×
695
      return LWResult::Result::Success;
×
696
    }
×
697

698
    ++queriesCount;
4✔
699
    downServers.insert(address);
4✔
700

701
    setLWResult(res, RCode::Refused, false, false, true);
4✔
702

703
    return LWResult::Result::Success;
4✔
704
  });
4✔
705

706
  vector<DNSRecord> ret;
2✔
707
  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
708
  BOOST_CHECK_EQUAL(res, RCode::ServFail);
2✔
709
  BOOST_CHECK_EQUAL(ret.size(), 0U);
2✔
710
  BOOST_CHECK_EQUAL(downServers.size(), 2U);
2✔
711
  BOOST_CHECK_EQUAL(queriesCount, 2U);
2✔
712

713
  for (const auto& server : forwardedNSs) {
4✔
714
    BOOST_CHECK_EQUAL(downServers.count(server), 1U);
4✔
715
    /* same as any other server */
716
    BOOST_CHECK(SyncRes::isThrottled(time(nullptr), server, target, QType::A));
4✔
717
    BOOST_CHECK_EQUAL(SyncRes::getNSSpeed(DNSName("a.gtld-servers.net."), server), 0U);
4✔
718
    BOOST_CHECK_EQUAL(SyncRes::getEDNSStatus(server), SyncRes::EDNSStatus::EDNSOK);
4✔
719
  }
4✔
720
}
2✔
721

722
BOOST_AUTO_TEST_CASE(test_forward_ns_send_servfail)
723
{
2✔
724
  std::unique_ptr<SyncRes> sr;
2✔
725
  initSR(sr);
2✔
726

727
  primeHints();
2✔
728

729
  std::set<ComboAddress> downServers;
2✔
730
  size_t queriesCount = 0;
2✔
731

732
  const DNSName target("www.refused.");
2✔
733

734
  SyncRes::AuthDomain ad;
2✔
735
  const std::vector<ComboAddress> forwardedNSs{ComboAddress("192.0.2.42:53"), ComboAddress("192.0.2.43:53")};
2✔
736
  ad.d_rdForward = false;
2✔
737
  ad.d_servers = forwardedNSs;
2✔
738
  (*SyncRes::t_sstorage.domainmap)[DNSName("refused.")] = ad;
2✔
739

740
  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✔
741
    if (isRootServer(address)) {
4!
742
      setLWResult(res, 0, false, false, true);
×
743
      addRecordToLW(res, "refused.", QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
×
744
      addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
×
745
      addRecordToLW(res, "a.gtld-servers.net.", QType::AAAA, "2001:DB8::1", DNSResourceRecord::ADDITIONAL, 3600);
×
746
      return LWResult::Result::Success;
×
747
    }
×
748

749
    ++queriesCount;
4✔
750
    downServers.insert(address);
4✔
751

752
    setLWResult(res, RCode::ServFail, false, false, true);
4✔
753

754
    return LWResult::Result::Success;
4✔
755
  });
4✔
756

757
  vector<DNSRecord> ret;
2✔
758
  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
759
  BOOST_CHECK_EQUAL(res, RCode::ServFail);
2✔
760
  BOOST_CHECK_EQUAL(ret.size(), 0U);
2✔
761
  BOOST_CHECK_EQUAL(downServers.size(), 2U);
2✔
762
  BOOST_CHECK_EQUAL(queriesCount, 2U);
2✔
763

764
  for (const auto& server : forwardedNSs) {
4✔
765
    BOOST_CHECK_EQUAL(downServers.count(server), 1U);
4✔
766
    /* on servfail from a server we forward to we only increase the NS speed so
767
       that a different server might be tried instead, but we don't throttle */
768
    BOOST_CHECK(!SyncRes::isThrottled(time(nullptr), server, target, QType::A));
4✔
769
    BOOST_CHECK_EQUAL(SyncRes::getNSSpeed(DNSName(server.toStringWithPort()), server), 1000000U);
4✔
770
    BOOST_CHECK_EQUAL(SyncRes::getEDNSStatus(server), SyncRes::EDNSStatus::EDNSOK);
4✔
771
  }
4✔
772
}
2✔
773

774
BOOST_AUTO_TEST_CASE(test_only_one_ns_up_resolving_itself_with_glue)
775
{
2✔
776
  std::unique_ptr<SyncRes> sr;
2✔
777
  initSR(sr);
2✔
778

779
  primeHints();
2✔
780

781
  DNSName target("www.powerdns.com.");
2✔
782

783
  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✔
784
    if (isRootServer(address)) {
12✔
785
      setLWResult(res, 0, false, false, true);
4✔
786
      if (domain == target) {
4✔
787
        addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
2✔
788
        addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns2.powerdns.net.", DNSResourceRecord::AUTHORITY, 172800);
2✔
789
        addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 172800);
2✔
790
        addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::AAAA, "2001:DB8::2", DNSResourceRecord::ADDITIONAL, 172800);
2✔
791
      }
2✔
792
      else if (domain == DNSName("pdns-public-ns2.powerdns.net.")) {
2!
793
        addRecordToLW(res, "powerdns.net.", QType::NS, "pdns-public-ns2.powerdns.net.", DNSResourceRecord::AUTHORITY, 172800);
2✔
794
        addRecordToLW(res, "powerdns.net.", QType::NS, "pdns-public-ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
2✔
795
        addRecordToLW(res, "pdns-public-ns2.powerdns.net.", QType::A, "192.0.2.3", DNSResourceRecord::ADDITIONAL, 172800);
2✔
796
        addRecordToLW(res, "pdns-public-ns2.powerdns.net.", QType::AAAA, "2001:DB8::3", DNSResourceRecord::ADDITIONAL, 172800);
2✔
797
      }
2✔
798
      return LWResult::Result::Success;
4✔
799
    }
4✔
800
    if (address == ComboAddress("192.0.2.3:53")) {
8✔
801
      setLWResult(res, 0, true, false, true);
4✔
802
      if (domain == DNSName("pdns-public-ns2.powerdns.net.")) {
4✔
803
        if (type == QType::A) {
2!
804
          addRecordToLW(res, "pdns-public-ns2.powerdns.net.", QType::A, "192.0.2.3");
2✔
805
        }
2✔
806
        else if (type == QType::AAAA) {
×
807
          addRecordToLW(res, "pdns-public-ns2.powerdns.net.", QType::AAAA, "2001:DB8::3");
×
808
        }
×
809
      }
2✔
810
      else if (domain == target) {
2!
811
        if (type == QType::A) {
2!
812
          addRecordToLW(res, domain, QType::A, "192.0.2.1");
2✔
813
        }
2✔
814
        else if (type == QType::AAAA) {
×
815
          addRecordToLW(res, domain, QType::AAAA, "2001:DB8::1");
×
816
        }
×
817
      }
2✔
818
      return LWResult::Result::Success;
4✔
819
    }
4✔
820
    return LWResult::Result::Timeout;
4✔
821
  });
8✔
822

823
  vector<DNSRecord> ret;
2✔
824
  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
825
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
826
  BOOST_CHECK_EQUAL(ret.size(), 1U);
2✔
827
}
2✔
828

829
BOOST_AUTO_TEST_CASE(test_os_limit_errors)
830
{
2✔
831
  std::unique_ptr<SyncRes> sr;
2✔
832
  initSR(sr);
2✔
833
  std::set<ComboAddress> downServers;
2✔
834

835
  primeHints();
2✔
836

837
  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✔
838
    if (isRootServer(address)) {
12✔
839
      setLWResult(res, 0, false, false, true);
2✔
840
      addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
2✔
841
      addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
2✔
842
      addRecordToLW(res, "a.gtld-servers.net.", QType::AAAA, "2001:DB8::1", DNSResourceRecord::ADDITIONAL, 3600);
2✔
843
      return LWResult::Result::Success;
2✔
844
    }
2✔
845
    if (address == ComboAddress("192.0.2.1:53") || address == ComboAddress("[2001:DB8::1]:53")) {
10✔
846
      setLWResult(res, 0, false, false, true);
2✔
847
      addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
2✔
848
      addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns2.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
2✔
849
      addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 172800);
2✔
850
      addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::AAAA, "2001:DB8::2", DNSResourceRecord::ADDITIONAL, 172800);
2✔
851
      addRecordToLW(res, "pdns-public-ns2.powerdns.com.", QType::A, "192.0.2.3", DNSResourceRecord::ADDITIONAL, 172800);
2✔
852
      addRecordToLW(res, "pdns-public-ns2.powerdns.com.", QType::AAAA, "2001:DB8::3", DNSResourceRecord::ADDITIONAL, 172800);
2✔
853
      return LWResult::Result::Success;
2✔
854
    }
2✔
855
    {
8✔
856
      if (downServers.size() < 3) {
8✔
857
        /* only the last one will answer */
858
        downServers.insert(address);
6✔
859
        res->d_usec = g_networkTimeoutMsec * 1000;
6✔
860
        return LWResult::Result::OSLimitError;
6✔
861
      }
6✔
862
      setLWResult(res, 0, true, false, true);
2✔
863
      addRecordToLW(res, "powerdns.com.", QType::A, "192.0.2.42");
2✔
864
      return LWResult::Result::Success;
2✔
865
    }
8✔
866
  });
8✔
867

868
  DNSName target("powerdns.com.");
2✔
869

870
  vector<DNSRecord> ret;
2✔
871
  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
872
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
873
  BOOST_CHECK_EQUAL(ret.size(), 1U);
2✔
874
  BOOST_CHECK_EQUAL(downServers.size(), 3U);
2✔
875

876
  /* Error is reported as "OS limit error" (-2) so the servers should _NOT_ be marked down */
877
  time_t now = sr->getNow().tv_sec;
2✔
878
  for (const auto& server : downServers) {
6✔
879
    BOOST_CHECK_EQUAL(SyncRes::getServerFailsCount(server), 0U);
6✔
880
    BOOST_CHECK(!SyncRes::isThrottled(now, server, target, QType::A));
6✔
881
  }
6✔
882
}
2✔
883

884
BOOST_AUTO_TEST_CASE(test_glued_referral)
885
{
2✔
886
  std::unique_ptr<SyncRes> sr;
2✔
887
  initSR(sr);
2✔
888

889
  primeHints();
2✔
890

891
  const DNSName target("powerdns.com.");
2✔
892

893
  sr->setAsyncCallback([&](const ComboAddress& address, const DNSName& domain, int /* type */, bool /* doTCP */, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval* /* now */, boost::optional<Netmask>& /* srcmask */, const ResolveContext& /* context */, LWResult* res, bool* /* chained */) {
6✔
894
    /* this will cause issue with qname minimization if we ever implement it */
895
    if (domain != target) {
6!
896
      return LWResult::Result::Timeout;
×
897
    }
×
898

899
    if (isRootServer(address)) {
6✔
900
      setLWResult(res, 0, false, false, true);
2✔
901
      addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
2✔
902
      addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
2✔
903
      addRecordToLW(res, "a.gtld-servers.net.", QType::AAAA, "2001:DB8::1", DNSResourceRecord::ADDITIONAL, 3600);
2✔
904
      return LWResult::Result::Success;
2✔
905
    }
2✔
906
    if (address == ComboAddress("192.0.2.1:53") || address == ComboAddress("[2001:DB8::1]:53")) {
4✔
907
      setLWResult(res, 0, false, false, true);
2✔
908
      addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
2✔
909
      addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns2.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
2✔
910
      addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 172800);
2✔
911
      addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::AAAA, "2001:DB8::2", DNSResourceRecord::ADDITIONAL, 172800);
2✔
912
      addRecordToLW(res, "pdns-public-ns2.powerdns.com.", QType::A, "192.0.2.3", DNSResourceRecord::ADDITIONAL, 172800);
2✔
913
      addRecordToLW(res, "pdns-public-ns2.powerdns.com.", QType::AAAA, "2001:DB8::3", DNSResourceRecord::ADDITIONAL, 172800);
2✔
914
      return LWResult::Result::Success;
2✔
915
    }
2✔
916
    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!
917
      setLWResult(res, 0, true, false, true);
2✔
918
      addRecordToLW(res, target, QType::A, "192.0.2.4");
2✔
919
      return LWResult::Result::Success;
2✔
920
    }
2✔
921
    return LWResult::Result::Timeout;
×
922
  });
2✔
923

924
  vector<DNSRecord> ret;
2✔
925
  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
926
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
927
  BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2✔
928
  BOOST_CHECK(ret[0].d_type == QType::A);
2✔
929
  BOOST_CHECK_EQUAL(ret[0].d_name, target);
2✔
930
}
2✔
931

932
BOOST_AUTO_TEST_CASE(test_glueless_referral)
933
{
2✔
934
  std::unique_ptr<SyncRes> sr;
2✔
935
  initSR(sr);
2✔
936

937
  primeHints();
2✔
938

939
  const DNSName target("powerdns.com.");
2✔
940

941
  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✔
942
    if (isRootServer(address)) {
10✔
943
      setLWResult(res, 0, false, false, true);
4✔
944

945
      if (domain.isPartOf(DNSName("com."))) {
4✔
946
        addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
2✔
947
      }
2✔
948
      else if (domain.isPartOf(DNSName("org."))) {
2!
949
        addRecordToLW(res, "org.", QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
2✔
950
      }
2✔
951
      else {
×
952
        setLWResult(res, RCode::NXDomain, false, false, true);
×
953
        return LWResult::Result::Success;
×
954
      }
×
955

956
      addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
4✔
957
      addRecordToLW(res, "a.gtld-servers.net.", QType::AAAA, "2001:DB8::1", DNSResourceRecord::ADDITIONAL, 3600);
4✔
958
      return LWResult::Result::Success;
4✔
959
    }
4✔
960
    if (address == ComboAddress("192.0.2.1:53") || address == ComboAddress("[2001:DB8::1]:53")) {
6✔
961
      if (domain == target) {
4✔
962
        setLWResult(res, 0, false, false, true);
2✔
963
        addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns1.powerdns.org.", DNSResourceRecord::AUTHORITY, 172800);
2✔
964
        addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns2.powerdns.org.", DNSResourceRecord::AUTHORITY, 172800);
2✔
965
        return LWResult::Result::Success;
2✔
966
      }
2✔
967
      if (domain == DNSName("pdns-public-ns1.powerdns.org.")) {
2✔
968
        setLWResult(res, 0, true, false, true);
1✔
969
        addRecordToLW(res, "pdns-public-ns1.powerdns.org.", QType::A, "192.0.2.2");
1✔
970
        addRecordToLW(res, "pdns-public-ns1.powerdns.org.", QType::AAAA, "2001:DB8::2");
1✔
971
        return LWResult::Result::Success;
1✔
972
      }
1✔
973
      if (domain == DNSName("pdns-public-ns2.powerdns.org.")) {
1!
974
        setLWResult(res, 0, true, false, true);
1✔
975
        addRecordToLW(res, "pdns-public-ns2.powerdns.org.", QType::A, "192.0.2.3");
1✔
976
        addRecordToLW(res, "pdns-public-ns2.powerdns.org.", QType::AAAA, "2001:DB8::3");
1✔
977
        return LWResult::Result::Success;
1✔
978
      }
1✔
979

980
      setLWResult(res, RCode::NXDomain, false, false, true);
×
981
      return LWResult::Result::Success;
×
982
    }
1✔
983
    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!
984
      setLWResult(res, 0, true, false, true);
2✔
985
      addRecordToLW(res, target, QType::A, "192.0.2.4");
2✔
986
      return LWResult::Result::Success;
2✔
987
    }
2✔
988
    return LWResult::Result::Timeout;
×
989
  });
2✔
990

991
  vector<DNSRecord> ret;
2✔
992
  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
993
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
994
  BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2✔
995
  BOOST_CHECK(ret[0].d_type == QType::A);
2✔
996
  BOOST_CHECK_EQUAL(ret[0].d_name, target);
2✔
997
}
2✔
998

999
BOOST_AUTO_TEST_CASE(test_endless_glueless_referral)
1000
{
2✔
1001
  std::unique_ptr<SyncRes> sr;
2✔
1002
  initSR(sr);
2✔
1003

1004
  primeHints();
2✔
1005

1006
  const DNSName target("powerdns.com.");
2✔
1007

1008
  size_t count = 0;
2✔
1009
  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 */) {
51✔
1010
    if (isRootServer(address)) {
51✔
1011
      setLWResult(res, 0, false, false, true);
10✔
1012

1013
      if (domain.isPartOf(DNSName("com."))) {
10✔
1014
        addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
2✔
1015
      }
2✔
1016
      else if (domain.isPartOf(DNSName("org."))) {
8!
1017
        addRecordToLW(res, "org.", QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
8✔
1018
      }
8✔
1019
      else {
×
1020
        setLWResult(res, RCode::NXDomain, false, false, true);
×
1021
        return LWResult::Result::Success;
×
1022
      }
×
1023

1024
      addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
10✔
1025
      addRecordToLW(res, "a.gtld-servers.net.", QType::AAAA, "2001:DB8::1", DNSResourceRecord::ADDITIONAL, 3600);
10✔
1026
      return LWResult::Result::Success;
10✔
1027
    }
10✔
1028
    if (domain == target) {
41✔
1029
      setLWResult(res, 0, false, false, true);
2✔
1030
      addRecordToLW(res, "powerdns.com.", QType::NS, "ns1.powerdns.org.", DNSResourceRecord::AUTHORITY, 172800);
2✔
1031
      addRecordToLW(res, "powerdns.com.", QType::NS, "ns2.powerdns.org.", DNSResourceRecord::AUTHORITY, 172800);
2✔
1032
      return LWResult::Result::Success;
2✔
1033
    }
2✔
1034
    setLWResult(res, 0, false, false, true);
39✔
1035
    addRecordToLW(res, domain, QType::NS, std::to_string(count) + ".ns1.powerdns.org", DNSResourceRecord::AUTHORITY, 172800);
39✔
1036
    addRecordToLW(res, domain, QType::NS, std::to_string(count) + ".ns2.powerdns.org", DNSResourceRecord::AUTHORITY, 172800);
39✔
1037
    count++;
39✔
1038
    return LWResult::Result::Success;
39✔
1039
  });
41✔
1040

1041
  vector<DNSRecord> ret;
2✔
1042
  BOOST_CHECK_EXCEPTION(sr->beginResolve(target, QType(QType::A), QClass::IN, ret),
2✔
1043
                        ImmediateServFailException,
2✔
1044
                        [&](const ImmediateServFailException& isfe) {
2✔
1045
                          return isfe.reason.substr(0, 9) == "More than";
2✔
1046
                        });
2✔
1047
}
2✔
1048

1049
BOOST_AUTO_TEST_CASE(test_glueless_referral_aaaa_task)
1050
{
2✔
1051
  std::unique_ptr<SyncRes> sr;
2✔
1052
  initSR(sr);
2✔
1053

1054
  primeHints();
2✔
1055

1056
  const DNSName target("powerdns.com.");
2✔
1057

1058
  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✔
1059
    if (isRootServer(address)) {
10✔
1060
      setLWResult(res, 0, false, false, true);
4✔
1061

1062
      if (domain.isPartOf(DNSName("com."))) {
4✔
1063
        addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
2✔
1064
      }
2✔
1065
      else if (domain.isPartOf(DNSName("org."))) {
2!
1066
        addRecordToLW(res, "org.", QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
2✔
1067
      }
2✔
1068
      else {
×
1069
        setLWResult(res, RCode::NXDomain, false, false, true);
×
1070
        return LWResult::Result::Success;
×
1071
      }
×
1072

1073
      addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
4✔
1074
      addRecordToLW(res, "a.gtld-servers.net.", QType::AAAA, "2001:DB8::1", DNSResourceRecord::ADDITIONAL, 3600);
4✔
1075
      return LWResult::Result::Success;
4✔
1076
    }
4✔
1077
    if (address == ComboAddress("192.0.2.1:53") || address == ComboAddress("[2001:DB8::1]:53")) {
6✔
1078
      if (domain == target) {
4✔
1079
        setLWResult(res, 0, false, false, true);
2✔
1080
        addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns1.powerdns.org.", DNSResourceRecord::AUTHORITY, 172800);
2✔
1081
        addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns2.powerdns.org.", DNSResourceRecord::AUTHORITY, 172800);
2✔
1082
        return LWResult::Result::Success;
2✔
1083
      }
2✔
1084
      if (domain == DNSName("pdns-public-ns1.powerdns.org.")) {
2!
UNCOV
1085
        setLWResult(res, 0, true, false, true);
×
UNCOV
1086
        if (type == QType::A) {
×
UNCOV
1087
          addRecordToLW(res, "pdns-public-ns1.powerdns.org.", QType::A, "192.0.2.2");
×
UNCOV
1088
        }
×
1089
        else {
×
1090
          addRecordToLW(res, "pdns-public-ns1.powerdns.org.", QType::AAAA, "2001:DB8::2");
×
1091
        }
×
UNCOV
1092
        return LWResult::Result::Success;
×
UNCOV
1093
      }
×
1094
      if (domain == DNSName("pdns-public-ns2.powerdns.org.")) {
2!
1095
        setLWResult(res, 0, true, false, true);
2✔
1096
        if (type == QType::A) {
2!
1097
          addRecordToLW(res, "pdns-public-ns2.powerdns.org.", QType::A, "192.0.2.3");
2✔
1098
        }
2✔
1099
        else {
×
1100
          addRecordToLW(res, "pdns-public-ns2.powerdns.org.", QType::AAAA, "2001:DB8::3");
×
1101
        }
×
1102
        return LWResult::Result::Success;
2✔
1103
      }
2✔
1104

1105
      setLWResult(res, RCode::NXDomain, false, false, true);
×
1106
      return LWResult::Result::Success;
×
1107
    }
2✔
1108
    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!
1109
      setLWResult(res, 0, true, false, true);
2✔
1110
      addRecordToLW(res, target, QType::A, "192.0.2.4");
2✔
1111
      return LWResult::Result::Success;
2✔
1112
    }
2✔
1113
    return LWResult::Result::Timeout;
×
1114
  });
2✔
1115

1116
  vector<DNSRecord> ret;
2✔
1117
  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1118
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1119
  BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2✔
1120
  BOOST_CHECK(ret[0].d_type == QType::A);
2✔
1121
  BOOST_CHECK_EQUAL(ret[0].d_name, target);
2✔
1122

1123
  // One task should be submitted
1124
  BOOST_REQUIRE_EQUAL(getTaskSize(), 1U);
2✔
1125
  auto task = taskQueuePop();
2✔
1126
  BOOST_CHECK(task.d_qname == DNSName("pdns-public-ns1.powerdns.org") || task.d_qname == DNSName("pdns-public-ns2.powerdns.org"));
2✔
1127
  BOOST_CHECK_EQUAL(task.d_qtype, QType::AAAA);
2✔
1128
}
2✔
1129

1130
BOOST_AUTO_TEST_CASE(test_edns_subnet_by_domain)
1131
{
2✔
1132
  std::unique_ptr<SyncRes> sr;
2✔
1133
  initSR(sr);
2✔
1134

1135
  primeHints();
2✔
1136

1137
  const DNSName target("powerdns.com.");
2✔
1138
  SyncRes::addEDNSDomain(target);
2✔
1139

1140
  EDNSSubnetOpts incomingECS;
2✔
1141
  incomingECS.setSource(Netmask("192.0.2.128/32"));
2✔
1142
  sr->setQuerySource(ComboAddress(), boost::optional<const EDNSSubnetOpts&>(incomingECS));
2✔
1143

1144
  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✔
1145
    BOOST_REQUIRE(srcmask);
4✔
1146
    BOOST_CHECK_EQUAL(srcmask->toString(), "192.0.2.0/24");
4✔
1147

1148
    if (isRootServer(address)) {
4✔
1149
      setLWResult(res, 0, false, false, true);
2✔
1150
      addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
2✔
1151
      addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
2✔
1152

1153
      /* this one did not use the ECS info */
1154
      srcmask = boost::none;
2✔
1155

1156
      return LWResult::Result::Success;
2✔
1157
    }
2✔
1158
    if (address == ComboAddress("192.0.2.1:53")) {
2!
1159

1160
      setLWResult(res, 0, true, false, false);
2✔
1161
      addRecordToLW(res, domain, QType::A, "192.0.2.2");
2✔
1162

1163
      /* this one did, but only up to a precision of /16, not the full /24 */
1164
      srcmask = Netmask("192.0.0.0/16");
2✔
1165

1166
      return LWResult::Result::Success;
2✔
1167
    }
2✔
1168

1169
    return LWResult::Result::Timeout;
×
1170
  });
2✔
1171

1172
  SyncRes::s_ecsqueries = 0;
2✔
1173
  SyncRes::s_ecsresponses = 0;
2✔
1174
  vector<DNSRecord> ret;
2✔
1175
  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1176
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1177
  BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2✔
1178
  BOOST_CHECK(ret[0].d_type == QType::A);
2✔
1179
  BOOST_CHECK_EQUAL(ret[0].d_name, target);
2✔
1180
  BOOST_CHECK_EQUAL(SyncRes::s_ecsqueries, 2U);
2✔
1181
  BOOST_CHECK_EQUAL(SyncRes::s_ecsresponses, 1U);
2✔
1182
  for (const auto& entry : SyncRes::s_ecsResponsesBySubnetSize4) {
64✔
1183
    BOOST_CHECK_EQUAL(entry.second, entry.first == 15 ? 1U : 0U);
64✔
1184
  }
64✔
1185
  for (const auto& entry : SyncRes::s_ecsResponsesBySubnetSize6) {
256✔
1186
    BOOST_CHECK_EQUAL(entry.second, 0U);
256✔
1187
  }
256✔
1188
}
2✔
1189

1190
BOOST_AUTO_TEST_CASE(test_edns_subnet_by_addr)
1191
{
2✔
1192
  std::unique_ptr<SyncRes> sr;
2✔
1193
  initSR(sr);
2✔
1194

1195
  primeHints();
2✔
1196

1197
  const DNSName target("powerdns.com.");
2✔
1198
  SyncRes::addEDNSRemoteSubnet("192.0.2.1/32");
2✔
1199

1200
  EDNSSubnetOpts incomingECS;
2✔
1201
  incomingECS.setSource(Netmask("2001:DB8::FF/128"));
2✔
1202
  sr->setQuerySource(ComboAddress(), boost::optional<const EDNSSubnetOpts&>(incomingECS));
2✔
1203

1204
  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✔
1205
    if (isRootServer(address)) {
4✔
1206
      BOOST_REQUIRE(!srcmask);
2✔
1207

1208
      setLWResult(res, 0, false, false, true);
2✔
1209
      addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
2✔
1210
      addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
2✔
1211
      return LWResult::Result::Success;
2✔
1212
    }
2✔
1213
    if (address == ComboAddress("192.0.2.1:53")) {
2!
1214

1215
      BOOST_REQUIRE(srcmask);
2✔
1216
      BOOST_CHECK_EQUAL(srcmask->toString(), "2001:db8::/56");
2✔
1217

1218
      setLWResult(res, 0, true, false, false);
2✔
1219
      addRecordToLW(res, domain, QType::A, "192.0.2.2");
2✔
1220
      return LWResult::Result::Success;
2✔
1221
    }
2✔
1222

1223
    return LWResult::Result::Timeout;
×
1224
  });
2✔
1225

1226
  SyncRes::s_ecsqueries = 0;
2✔
1227
  SyncRes::s_ecsresponses = 0;
2✔
1228
  vector<DNSRecord> ret;
2✔
1229
  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1230
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1231
  BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2✔
1232
  BOOST_CHECK(ret[0].d_type == QType::A);
2✔
1233
  BOOST_CHECK_EQUAL(ret[0].d_name, target);
2✔
1234
  BOOST_CHECK_EQUAL(SyncRes::s_ecsqueries, 1U);
2✔
1235
  BOOST_CHECK_EQUAL(SyncRes::s_ecsresponses, 1U);
2✔
1236
  for (const auto& entry : SyncRes::s_ecsResponsesBySubnetSize4) {
64✔
1237
    BOOST_CHECK_EQUAL(entry.second, 0u);
64✔
1238
  }
64✔
1239
  for (const auto& entry : SyncRes::s_ecsResponsesBySubnetSize6) {
256✔
1240
    BOOST_CHECK_EQUAL(entry.second, entry.first == 55 ? 1U : 0U);
256✔
1241
  }
256✔
1242
}
2✔
1243

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

1249
  primeHints();
2✔
1250

1251
  const DNSName target("powerdns.com.");
2✔
1252
  SyncRes::addEDNSRemoteSubnet("192.0.2.1/32");
2✔
1253
  // No incoming ECS data
1254
  sr->setQuerySource(ComboAddress("192.0.2.127"), boost::none);
2✔
1255

1256
  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✔
1257
    if (isRootServer(address)) {
4✔
1258
      BOOST_REQUIRE(!srcmask);
2✔
1259

1260
      setLWResult(res, 0, false, false, true);
2✔
1261
      addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
2✔
1262
      addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
2✔
1263
      return LWResult::Result::Success;
2✔
1264
    }
2✔
1265
    if (address == ComboAddress("192.0.2.1:53")) {
2!
1266

1267
      BOOST_REQUIRE(srcmask);
2✔
1268
      BOOST_CHECK_EQUAL(srcmask->toString(), "192.0.2.0/24");
2✔
1269

1270
      setLWResult(res, 0, true, false, false);
2✔
1271
      addRecordToLW(res, domain, QType::A, "192.0.2.2");
2✔
1272
      return LWResult::Result::Success;
2✔
1273
    }
2✔
1274

1275
    return LWResult::Result::Timeout;
×
1276
  });
2✔
1277

1278
  vector<DNSRecord> ret;
2✔
1279
  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1280
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1281
  BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2✔
1282
  BOOST_CHECK(ret[0].d_type == QType::A);
2✔
1283
  BOOST_CHECK_EQUAL(ret[0].d_name, target);
2✔
1284
}
2✔
1285

1286
BOOST_AUTO_TEST_CASE(test_ecs_use_scope_zero)
1287
{
2✔
1288
  std::unique_ptr<SyncRes> sr;
2✔
1289
  initSR(sr);
2✔
1290

1291
  primeHints();
2✔
1292

1293
  const DNSName target("powerdns.com.");
2✔
1294
  SyncRes::addEDNSRemoteSubnet("192.0.2.1/32");
2✔
1295
  SyncRes::clearEDNSLocalSubnets();
2✔
1296
  SyncRes::addEDNSLocalSubnet("192.0.2.254/32");
2✔
1297
  // No incoming ECS data, Requestor IP not in ecs-add-for
1298
  sr->setQuerySource(ComboAddress("192.0.2.127"), boost::none);
2✔
1299

1300
  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✔
1301
    if (isRootServer(address)) {
4✔
1302
      BOOST_REQUIRE(!srcmask);
2✔
1303

1304
      setLWResult(res, 0, false, false, true);
2✔
1305
      addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
2✔
1306
      addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
2✔
1307
      return LWResult::Result::Success;
2✔
1308
    }
2✔
1309
    if (address == ComboAddress("192.0.2.1:53")) {
2!
1310

1311
      BOOST_REQUIRE(srcmask);
2✔
1312
      BOOST_CHECK_EQUAL(srcmask->toString(), "127.0.0.1/32");
2✔
1313

1314
      setLWResult(res, 0, true, false, false);
2✔
1315
      addRecordToLW(res, domain, QType::A, "192.0.2.2");
2✔
1316
      return LWResult::Result::Success;
2✔
1317
    }
2✔
1318

1319
    return LWResult::Result::Timeout;
×
1320
  });
2✔
1321

1322
  vector<DNSRecord> ret;
2✔
1323
  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1324
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1325
  BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2✔
1326
  BOOST_CHECK(ret[0].d_type == QType::A);
2✔
1327
  BOOST_CHECK_EQUAL(ret[0].d_name, target);
2✔
1328
}
2✔
1329

1330
BOOST_AUTO_TEST_CASE(test_ecs_honor_incoming_mask)
1331
{
2✔
1332
  std::unique_ptr<SyncRes> sr;
2✔
1333
  initSR(sr);
2✔
1334

1335
  primeHints();
2✔
1336

1337
  const DNSName target("powerdns.com.");
2✔
1338
  SyncRes::addEDNSRemoteSubnet("192.0.2.1/32");
2✔
1339
  SyncRes::clearEDNSLocalSubnets();
2✔
1340
  SyncRes::addEDNSLocalSubnet("192.0.2.254/32");
2✔
1341
  EDNSSubnetOpts incomingECS;
2✔
1342
  incomingECS.setSource(Netmask("192.0.0.0/16"));
2✔
1343
  sr->setQuerySource(ComboAddress("192.0.2.127"), boost::optional<const EDNSSubnetOpts&>(incomingECS));
2✔
1344

1345
  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✔
1346
    if (isRootServer(address)) {
4✔
1347
      BOOST_REQUIRE(!srcmask);
2✔
1348

1349
      setLWResult(res, 0, false, false, true);
2✔
1350
      addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
2✔
1351
      addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
2✔
1352
      return LWResult::Result::Success;
2✔
1353
    }
2✔
1354
    if (address == ComboAddress("192.0.2.1:53")) {
2!
1355

1356
      BOOST_REQUIRE(srcmask);
2✔
1357
      BOOST_CHECK_EQUAL(srcmask->toString(), "192.0.0.0/16");
2✔
1358

1359
      setLWResult(res, 0, true, false, false);
2✔
1360
      addRecordToLW(res, domain, QType::A, "192.0.2.2");
2✔
1361
      return LWResult::Result::Success;
2✔
1362
    }
2✔
1363

1364
    return LWResult::Result::Timeout;
×
1365
  });
2✔
1366

1367
  vector<DNSRecord> ret;
2✔
1368
  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1369
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1370
  BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2✔
1371
  BOOST_CHECK(ret[0].d_type == QType::A);
2✔
1372
  BOOST_CHECK_EQUAL(ret[0].d_name, target);
2✔
1373
}
2✔
1374

1375
BOOST_AUTO_TEST_CASE(test_ecs_honor_incoming_mask_zero)
1376
{
2✔
1377
  std::unique_ptr<SyncRes> sr;
2✔
1378
  initSR(sr);
2✔
1379

1380
  primeHints();
2✔
1381

1382
  const DNSName target("powerdns.com.");
2✔
1383
  SyncRes::addEDNSRemoteSubnet("192.0.2.1/32");
2✔
1384
  SyncRes::clearEDNSLocalSubnets();
2✔
1385
  SyncRes::addEDNSLocalSubnet("192.0.2.254/32");
2✔
1386
  EDNSSubnetOpts incomingECS;
2✔
1387
  incomingECS.setSource(Netmask("0.0.0.0/0"));
2✔
1388
  sr->setQuerySource(ComboAddress("192.0.2.127"), boost::optional<const EDNSSubnetOpts&>(incomingECS));
2✔
1389

1390
  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✔
1391
    if (isRootServer(address)) {
4✔
1392
      BOOST_REQUIRE(!srcmask);
2✔
1393

1394
      setLWResult(res, 0, false, false, true);
2✔
1395
      addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
2✔
1396
      addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
2✔
1397
      return LWResult::Result::Success;
2✔
1398
    }
2✔
1399
    if (address == ComboAddress("192.0.2.1:53")) {
2!
1400

1401
      BOOST_REQUIRE(srcmask);
2✔
1402
      BOOST_CHECK_EQUAL(srcmask->toString(), "127.0.0.1/32");
2✔
1403

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

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

1412
  vector<DNSRecord> ret;
2✔
1413
  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1414
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1415
  BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2✔
1416
  BOOST_CHECK(ret[0].d_type == QType::A);
2✔
1417
  BOOST_CHECK_EQUAL(ret[0].d_name, target);
2✔
1418
}
2✔
1419

1420
BOOST_AUTO_TEST_CASE(test_following_cname)
1421
{
2✔
1422
  std::unique_ptr<SyncRes> sr;
2✔
1423
  initSR(sr);
2✔
1424

1425
  primeHints();
2✔
1426

1427
  const DNSName target("cname.powerdns.com.");
2✔
1428
  const DNSName cnameTarget("cname-target.powerdns.com");
2✔
1429

1430
  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✔
1431
    if (isRootServer(address)) {
8✔
1432
      setLWResult(res, 0, false, false, true);
4✔
1433
      addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
4✔
1434
      addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
4✔
1435
      return LWResult::Result::Success;
4✔
1436
    }
4✔
1437
    if (address == ComboAddress("192.0.2.1:53")) {
4!
1438

1439
      if (domain == target) {
4✔
1440
        setLWResult(res, 0, true, false, false);
2✔
1441
        addRecordToLW(res, domain, QType::CNAME, cnameTarget.toString());
2✔
1442
        return LWResult::Result::Success;
2✔
1443
      }
2✔
1444
      if (domain == cnameTarget) {
2!
1445
        setLWResult(res, 0, true, false, false);
2✔
1446
        addRecordToLW(res, domain, QType::A, "192.0.2.2");
2✔
1447
      }
2✔
1448

1449
      return LWResult::Result::Success;
2✔
1450
    }
4✔
1451

1452
    return LWResult::Result::Timeout;
×
1453
  });
4✔
1454

1455
  vector<DNSRecord> ret;
2✔
1456
  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1457
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1458
  BOOST_REQUIRE_EQUAL(ret.size(), 2U);
2✔
1459
  BOOST_CHECK(ret[0].d_type == QType::CNAME);
2✔
1460
  BOOST_CHECK_EQUAL(ret[0].d_name, target);
2✔
1461
  BOOST_CHECK(ret[1].d_type == QType::A);
2✔
1462
  BOOST_CHECK_EQUAL(ret[1].d_name, cnameTarget);
2✔
1463
}
2✔
1464

1465
BOOST_AUTO_TEST_CASE(test_following_cname_with_a)
1466
{
2✔
1467
  std::unique_ptr<SyncRes> resolver;
2✔
1468
  initSR(resolver);
2✔
1469

1470
  primeHints();
2✔
1471

1472
  const DNSName target("cname.powerdns.com.");
2✔
1473
  const DNSName cnameTarget("cname-target.powerdns.com");
2✔
1474

1475
  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 */) {
8✔
1476
    if (isRootServer(address)) {
8✔
1477
      setLWResult(res, 0, false, false, true);
4✔
1478
      addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
4✔
1479
      addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
4✔
1480
      return LWResult::Result::Success;
4✔
1481
    }
4✔
1482
    if (address == ComboAddress("192.0.2.1:53")) {
4!
1483

1484
      if (domain == target) {
4✔
1485
        setLWResult(res, 0, true, false, false);
2✔
1486
        addRecordToLW(res, domain, QType::A, "192.1.1.1");
2✔
1487
        addRecordToLW(res, domain, QType::CNAME, cnameTarget.toString());
2✔
1488
        return LWResult::Result::Success;
2✔
1489
      }
2✔
1490
      if (domain == cnameTarget) {
2!
1491
        setLWResult(res, 0, true, false, false);
2✔
1492
        addRecordToLW(res, domain, QType::A, "192.0.2.2");
2✔
1493
      }
2✔
1494

1495
      return LWResult::Result::Success;
2✔
1496
    }
4✔
1497

1498
    return LWResult::Result::Timeout;
×
1499
  });
4✔
1500

1501
  vector<DNSRecord> ret;
2✔
1502
  int res = resolver->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1503
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1504
  BOOST_REQUIRE_EQUAL(ret.size(), 2U);
2✔
1505
  BOOST_CHECK(ret[0].d_type == QType::CNAME);
2✔
1506
  BOOST_CHECK_EQUAL(ret[0].d_name, target);
2✔
1507
  BOOST_CHECK(ret[1].d_type == QType::A);
2✔
1508
  BOOST_CHECK_EQUAL(ret[1].d_name, cnameTarget);
2✔
1509
  BOOST_CHECK_EQUAL(ret[1].getContent()->getZoneRepresentation(), "192.0.2.2");
2✔
1510
}
2✔
1511

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

1517
  primeHints();
2✔
1518

1519
  const DNSName target("cname.powerdns.com.");
2✔
1520
  const DNSName cnameTarget1("cname-target1.powerdns.com");
2✔
1521
  const DNSName cnameTarget("cname-target.powerdns.com");
2✔
1522

1523
  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✔
1524
    if (isRootServer(address)) {
12✔
1525
      setLWResult(res, 0, false, false, true);
6✔
1526
      addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
6✔
1527
      addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
6✔
1528
      return LWResult::Result::Success;
6✔
1529
    }
6✔
1530
    if (address == ComboAddress("192.0.2.1:53")) {
6!
1531

1532
      if (domain == target) {
6✔
1533
        setLWResult(res, 0, true, false, false);
2✔
1534
        addRecordToLW(res, domain, QType::A, "192.1.1.1");
2✔
1535
        addRecordToLW(res, domain, QType::CNAME, cnameTarget1.toString());
2✔
1536
        addRecordToLW(res, cnameTarget1, QType::A, "192.1.1.2");
2✔
1537
        addRecordToLW(res, cnameTarget1, QType::CNAME, cnameTarget.toString());
2✔
1538
      }
2✔
1539
      if (domain == cnameTarget1) {
6✔
1540
        setLWResult(res, 0, true, false, false);
2✔
1541
        addRecordToLW(res, cnameTarget1, QType::A, "192.1.1.2");
2✔
1542
        addRecordToLW(res, cnameTarget1, QType::CNAME, cnameTarget.toString());
2✔
1543
        return LWResult::Result::Success;
2✔
1544
      }
2✔
1545
      if (domain == cnameTarget) {
4✔
1546
        setLWResult(res, 0, true, false, false);
2✔
1547
        addRecordToLW(res, domain, QType::A, "192.0.2.2");
2✔
1548
      }
2✔
1549

1550
      return LWResult::Result::Success;
4✔
1551
    }
6✔
1552

1553
    return LWResult::Result::Timeout;
×
1554
  });
6✔
1555

1556
  vector<DNSRecord> ret;
2✔
1557
  int res = resolver->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1558
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1559
  BOOST_REQUIRE_EQUAL(ret.size(), 3U);
2✔
1560
  BOOST_CHECK(ret[0].d_type == QType::CNAME);
2✔
1561
  BOOST_CHECK_EQUAL(ret[0].d_name, target);
2✔
1562
  BOOST_CHECK_EQUAL(ret[0].getContent()->getZoneRepresentation(), cnameTarget1.toString());
2✔
1563
  BOOST_CHECK(ret[1].d_type == QType::CNAME);
2✔
1564
  BOOST_CHECK_EQUAL(ret[1].d_name, cnameTarget1);
2✔
1565
  BOOST_CHECK_EQUAL(ret[1].getContent()->getZoneRepresentation(), cnameTarget.toString());
2✔
1566
  BOOST_CHECK(ret[2].d_type == QType::A);
2✔
1567
  BOOST_CHECK_EQUAL(ret[2].d_name, cnameTarget);
2✔
1568
  BOOST_CHECK_EQUAL(ret[2].getContent()->getZoneRepresentation(), "192.0.2.2");
2✔
1569
}
2✔
1570

1571
BOOST_AUTO_TEST_CASE(test_following_cname_chain_with_rpz)
1572
{
2✔
1573
  std::unique_ptr<SyncRes> resolver;
2✔
1574
  initSR(resolver);
2✔
1575
  resolver->setQNameMinimization(true);
2✔
1576

1577
  primeHints();
2✔
1578

1579
  const DNSName target("rpzhit.powerdns.com.");
2✔
1580
  const DNSName cnameTargeta("cname-targeta.powerdns.com");
2✔
1581
  const DNSName cnameTargetb("cname-targetb.powerdns.com");
2✔
1582

1583
  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✔
1584
    if (isRootServer(address)) {
12✔
1585
      setLWResult(res, 0, false, false, true);
2✔
1586
      addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
2✔
1587
      addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
2✔
1588
      return LWResult::Result::Success;
2✔
1589
    }
2✔
1590
    if (address == ComboAddress("192.0.2.1:53")) {
10!
1591

1592
      if (domain == cnameTargeta) {
10✔
1593
        setLWResult(res, 0, true, false, false);
2✔
1594
        addRecordToLW(res, cnameTargeta, QType::CNAME, cnameTargetb.toString());
2✔
1595
        return LWResult::Result::Success;
2✔
1596
      }
2✔
1597
      if (domain == cnameTargetb) {
8✔
1598
        setLWResult(res, 0, true, false, false);
4✔
1599
        addRecordToLW(res, domain, QType::A, "192.0.2.2", DNSResourceRecord::ANSWER, 10);
4✔
1600
      }
4✔
1601

1602
      return LWResult::Result::Success;
8✔
1603
    }
10✔
1604

1605
    return LWResult::Result::Timeout;
×
1606
  });
10✔
1607

1608
  DNSFilterEngine::Policy pol;
2✔
1609
  pol.d_ttl = 600;
2✔
1610
  pol.d_kind = DNSFilterEngine::PolicyKind::Custom;
2✔
1611
  auto customRecord = DNSRecordContent::make(QType::CNAME, QClass::IN, cnameTargeta.toString());
2✔
1612
  std::vector<std::shared_ptr<const DNSRecordContent>> custom = {customRecord};
2✔
1613
  pol.setCustom(custom);
2✔
1614
  std::shared_ptr<DNSFilterEngine::Zone> zone = std::make_shared<DNSFilterEngine::Zone>();
2✔
1615
  zone->setName("Unit test policy 0");
2✔
1616
  zone->addQNameTrigger(target, std::move(pol));
2✔
1617
  auto luaconfsCopy = g_luaconfs.getCopy();
2✔
1618
  luaconfsCopy.dfe.clearZones();
2✔
1619
  luaconfsCopy.dfe.addZone(zone);
2✔
1620
  g_luaconfs.setState(luaconfsCopy);
2✔
1621

1622
  time_t now = time(nullptr);
2✔
1623

1624
  vector<DNSRecord> ret;
2✔
1625
  int res = resolver->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1626
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1627
  BOOST_REQUIRE_EQUAL(ret.size(), 3U);
2✔
1628
  BOOST_CHECK(ret[0].d_type == QType::CNAME);
2✔
1629
  BOOST_CHECK_EQUAL(ret[0].d_name, target);
2✔
1630
  BOOST_CHECK_EQUAL(ret[0].getContent()->getZoneRepresentation(), cnameTargeta.toString());
2✔
1631
  BOOST_CHECK(ret[1].d_type == QType::CNAME);
2✔
1632
  BOOST_CHECK_EQUAL(ret[1].d_name, cnameTargeta);
2✔
1633
  BOOST_CHECK_EQUAL(ret[1].getContent()->getZoneRepresentation(), cnameTargetb.toString());
2✔
1634
  BOOST_CHECK(ret[2].d_type == QType::A);
2✔
1635
  BOOST_CHECK_EQUAL(ret[2].d_name, cnameTargetb);
2✔
1636
  BOOST_CHECK_EQUAL(ret[2].getContent()->getZoneRepresentation(), "192.0.2.2");
2✔
1637

1638
  // Let the final record expire. If an RPZ producing a custom CNAME was hit, we used to not follow
1639
  // the CNAME as aggressively as needed.  The symptom being the final record missing from the
1640
  // result.
1641
  resolver->setNow(timeval{now + 20, 0});
2✔
1642
  resolver->setQNameMinimization(true); // XXX find out why this is needed
2✔
1643

1644
  ret.clear();
2✔
1645
  resolver->d_appliedPolicy = DNSFilterEngine::Policy();
2✔
1646
  res = resolver->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1647
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1648
  BOOST_REQUIRE_EQUAL(ret.size(), 3U);
2✔
1649
  BOOST_CHECK(ret[0].d_type == QType::CNAME);
2✔
1650
  BOOST_CHECK_EQUAL(ret[0].d_name, target);
2✔
1651
  BOOST_CHECK_EQUAL(ret[0].getContent()->getZoneRepresentation(), cnameTargeta.toString());
2✔
1652
  BOOST_CHECK(ret[1].d_type == QType::CNAME);
2✔
1653
  BOOST_CHECK_EQUAL(ret[1].d_name, cnameTargeta);
2✔
1654
  BOOST_CHECK_EQUAL(ret[1].getContent()->getZoneRepresentation(), cnameTargetb.toString());
2✔
1655
  BOOST_CHECK(ret[2].d_type == QType::A);
2✔
1656
  BOOST_CHECK_EQUAL(ret[2].d_name, cnameTargetb);
2✔
1657
  BOOST_CHECK_EQUAL(ret[2].getContent()->getZoneRepresentation(), "192.0.2.2");
2✔
1658
}
2✔
1659

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

1665
  primeHints();
2✔
1666

1667
  const DNSName target("cname.powerdns.com.");
2✔
1668
  const DNSName cnameTarget("cname-target.powerdns.com");
2✔
1669

1670
  sr->setAsyncCallback([&](const ComboAddress& address, const DNSName& domain, int /* type */, bool /* doTCP */, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval* /* now */, boost::optional<Netmask>& /* srcmask */, const ResolveContext& /* context */, LWResult* res, bool* /* chained */) {
6✔
1671
    if (isRootServer(address)) {
6✔
1672
      setLWResult(res, 0, false, false, true);
2✔
1673
      addRecordToLW(res, "powerdns.com.", QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
2✔
1674
      addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
2✔
1675
      return LWResult::Result::Success;
2✔
1676
    }
2✔
1677
    if (address == ComboAddress("192.0.2.1:53")) {
4!
1678

1679
      if (domain == target) {
4✔
1680
        setLWResult(res, RCode::NXDomain, true, false, false);
2✔
1681
        addRecordToLW(res, domain, QType::CNAME, cnameTarget.toString());
2✔
1682
        addRecordToLW(res, "powerdns.com.", QType::SOA, "a.powerdns.com. nstld.verisign-grs.com. 2017032800 1800 900 604800 86400", DNSResourceRecord::AUTHORITY, 86400);
2✔
1683
      }
2✔
1684
      else if (domain == cnameTarget) {
2!
1685
        setLWResult(res, RCode::NXDomain, true, false, false);
2✔
1686
        addRecordToLW(res, "powerdns.com.", QType::SOA, "a.powerdns.com. nstld.verisign-grs.com. 2017032800 1800 900 604800 86400", DNSResourceRecord::AUTHORITY, 86400);
2✔
1687
        return LWResult::Result::Success;
2✔
1688
      }
2✔
1689

1690
      return LWResult::Result::Success;
2✔
1691
    }
4✔
1692

1693
    return LWResult::Result::Timeout;
×
1694
  });
4✔
1695

1696
  vector<DNSRecord> ret;
2✔
1697
  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1698
  BOOST_CHECK_EQUAL(res, RCode::NXDomain);
2✔
1699
  BOOST_REQUIRE_EQUAL(ret.size(), 2U);
2✔
1700
  BOOST_CHECK(ret[0].d_type == QType::CNAME);
2✔
1701
  BOOST_CHECK_EQUAL(ret[0].d_name, target);
2✔
1702
  BOOST_CHECK(ret[1].d_type == QType::SOA);
2✔
1703

1704
  /* a second time, to check the cache */
1705
  ret.clear();
2✔
1706
  res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1707
  BOOST_CHECK_EQUAL(res, RCode::NXDomain);
2✔
1708
  BOOST_REQUIRE_EQUAL(ret.size(), 2U);
2✔
1709
  BOOST_CHECK(ret[0].d_type == QType::CNAME);
2✔
1710
  BOOST_CHECK_EQUAL(ret[0].d_name, target);
2✔
1711
  BOOST_CHECK(ret[1].d_type == QType::SOA);
2✔
1712
}
2✔
1713

1714
BOOST_AUTO_TEST_CASE(test_included_poisonous_cname)
1715
{
2✔
1716
  std::unique_ptr<SyncRes> sr;
2✔
1717
  initSR(sr);
2✔
1718

1719
  primeHints();
2✔
1720

1721
  /* In this test we directly get the NS server for cname.powerdns.com.,
1722
     and we don't know whether it's also authoritative for
1723
     cname-target.powerdns.com or powerdns.com, so we shouldn't accept
1724
     the additional A record for cname-target.powerdns.com. */
1725
  const DNSName target("cname.powerdns.com.");
2✔
1726
  const DNSName cnameTarget("cname-target.powerdns.com");
2✔
1727

1728
  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✔
1729
    if (isRootServer(address)) {
8✔
1730

1731
      setLWResult(res, 0, false, false, true);
4✔
1732

1733
      addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
4✔
1734
      addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
4✔
1735
      return LWResult::Result::Success;
4✔
1736
    }
4✔
1737
    if (address == ComboAddress("192.0.2.1:53")) {
4!
1738

1739
      if (domain == target) {
4✔
1740
        setLWResult(res, 0, true, false, false);
2✔
1741
        addRecordToLW(res, domain, QType::CNAME, cnameTarget.toString());
2✔
1742
        addRecordToLW(res, cnameTarget, QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL);
2✔
1743
        return LWResult::Result::Success;
2✔
1744
      }
2✔
1745
      if (domain == cnameTarget) {
2!
1746
        setLWResult(res, 0, true, false, false);
2✔
1747
        addRecordToLW(res, cnameTarget, QType::A, "192.0.2.3");
2✔
1748
        return LWResult::Result::Success;
2✔
1749
      }
2✔
1750

1751
      return LWResult::Result::Success;
×
1752
    }
2✔
1753

1754
    return LWResult::Result::Timeout;
×
1755
  });
4✔
1756

1757
  vector<DNSRecord> ret;
2✔
1758
  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1759
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
1760
  BOOST_REQUIRE_EQUAL(ret.size(), 2U);
2✔
1761
  BOOST_REQUIRE(ret[0].d_type == QType::CNAME);
2✔
1762
  BOOST_CHECK_EQUAL(ret[0].d_name, target);
2✔
1763
  BOOST_CHECK_EQUAL(getRR<CNAMERecordContent>(ret[0])->getTarget(), cnameTarget);
2✔
1764
  BOOST_REQUIRE(ret[1].d_type == QType::A);
2✔
1765
  BOOST_CHECK_EQUAL(ret[1].d_name, cnameTarget);
2✔
1766
  BOOST_CHECK(getRR<ARecordContent>(ret[1])->getCA() == ComboAddress("192.0.2.3"));
2✔
1767
}
2✔
1768

1769
BOOST_AUTO_TEST_CASE(test_cname_loop)
1770
{
2✔
1771
  std::unique_ptr<SyncRes> sr;
2✔
1772
  initSR(sr);
2✔
1773

1774
  primeHints();
2✔
1775

1776
  size_t count = 0;
2✔
1777
  const DNSName target("cname.powerdns.com.");
2✔
1778

1779
  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✔
1780
    count++;
4✔
1781

1782
    if (isRootServer(address)) {
4✔
1783

1784
      setLWResult(res, 0, false, false, true);
2✔
1785
      addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
2✔
1786
      addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
2✔
1787
      return LWResult::Result::Success;
2✔
1788
    }
2✔
1789
    if (address == ComboAddress("192.0.2.1:53")) {
2!
1790

1791
      if (domain == target) {
2!
1792
        setLWResult(res, 0, true, false, false);
2✔
1793
        addRecordToLW(res, domain, QType::CNAME, domain.toString());
2✔
1794
        return LWResult::Result::Success;
2✔
1795
      }
2✔
1796

1797
      return LWResult::Result::Success;
×
1798
    }
2✔
1799

1800
    return LWResult::Result::Timeout;
×
1801
  });
2✔
1802

1803
  vector<DNSRecord> ret;
2✔
1804
  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1805
  BOOST_CHECK_EQUAL(res, RCode::ServFail);
2✔
1806
  BOOST_CHECK_EQUAL(ret.size(), 0U);
2✔
1807
  BOOST_CHECK_EQUAL(count, 2U);
2✔
1808

1809
  // Again to check cache
1810
  try {
2✔
1811
    sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
1812
    BOOST_CHECK(false);
2✔
1813
  }
2✔
1814
  catch (const ImmediateServFailException& ex) {
2✔
1815
    BOOST_CHECK(true);
2✔
1816
  }
2✔
1817
}
2✔
1818

1819
BOOST_AUTO_TEST_CASE(test_cname_loop_forwarder)
1820
{
2✔
1821
  std::unique_ptr<SyncRes> resolver;
2✔
1822
  initSR(resolver);
2✔
1823

1824
  primeHints();
2✔
1825

1826
  size_t count = 0;
2✔
1827
  const DNSName target("cname.powerdns.com.");
2✔
1828
  const DNSName cname1("cname1.cname.powerdns.com.");
2✔
1829
  const DNSName cname2("cname2.cname.powerdns.com.");
2✔
1830

1831
  SyncRes::AuthDomain ad;
2✔
1832
  const std::vector<ComboAddress> forwardedNSs{ComboAddress("192.0.2.42:53")};
2✔
1833
  ad.d_rdForward = true;
2✔
1834
  ad.d_servers = forwardedNSs;
2✔
1835
  (*SyncRes::t_sstorage.domainmap)[target] = ad;
2✔
1836

1837
  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 */) {
2✔
1838
    count++;
2✔
1839

1840
    if (isRootServer(address)) {
2!
1841

1842
      setLWResult(res, 0, false, false, true);
×
1843
      addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
×
1844
      addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
×
1845
      return LWResult::Result::Success;
×
1846
    }
×
1847
    if (address == ComboAddress("192.0.2.42:53")) {
2!
1848

1849
      if (domain == target) {
2!
1850
        setLWResult(res, 0, true, false, false);
2✔
1851
        addRecordToLW(res, domain, QType::CNAME, cname1.toString());
2✔
1852
        addRecordToLW(res, cname1, QType::CNAME, cname2.toString());
2✔
1853
        addRecordToLW(res, cname2, QType::CNAME, domain.toString());
2✔
1854
        return LWResult::Result::Success;
2✔
1855
      }
2✔
1856

1857
      return LWResult::Result::Success;
×
1858
    }
2✔
1859

1860
    return LWResult::Result::Timeout;
×
1861
  });
2✔
1862

1863
  vector<DNSRecord> ret;
2✔
1864
  BOOST_REQUIRE_THROW(resolver->beginResolve(target, QType(QType::A), QClass::IN, ret), ImmediateServFailException);
2✔
1865
}
2✔
1866

1867
BOOST_AUTO_TEST_CASE(test_cname_long_loop)
1868
{
2✔
1869
  std::unique_ptr<SyncRes> sr;
2✔
1870
  initSR(sr);
2✔
1871

1872
  primeHints();
2✔
1873

1874
  size_t count = 0;
2✔
1875
  const DNSName target1("cname1.powerdns.com.");
2✔
1876
  const DNSName target2("cname2.powerdns.com.");
2✔
1877
  const DNSName target3("cname3.powerdns.com.");
2✔
1878
  const DNSName target4("cname4.powerdns.com.");
2✔
1879

1880
  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✔
1881
    count++;
16✔
1882

1883
    if (isRootServer(address)) {
16✔
1884

1885
      setLWResult(res, 0, false, false, true);
8✔
1886
      addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
8✔
1887
      addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
8✔
1888
      return LWResult::Result::Success;
8✔
1889
    }
8✔
1890
    if (address == ComboAddress("192.0.2.1:53")) {
8!
1891

1892
      if (domain == target1) {
8✔
1893
        setLWResult(res, 0, true, false, false);
2✔
1894
        addRecordToLW(res, domain, QType::CNAME, target2.toString());
2✔
1895
        return LWResult::Result::Success;
2✔
1896
      }
2✔
1897
      if (domain == target2) {
6✔
1898
        setLWResult(res, 0, true, false, false);
2✔
1899
        addRecordToLW(res, domain, QType::CNAME, target3.toString());
2✔
1900
        return LWResult::Result::Success;
2✔
1901
      }
2✔
1902
      if (domain == target3) {
4✔
1903
        setLWResult(res, 0, true, false, false);
2✔
1904
        addRecordToLW(res, domain, QType::CNAME, target4.toString());
2✔
1905
        return LWResult::Result::Success;
2✔
1906
      }
2✔
1907
      if (domain == target4) {
2!
1908
        setLWResult(res, 0, true, false, false);
2✔
1909
        addRecordToLW(res, domain, QType::CNAME, target1.toString());
2✔
1910
        return LWResult::Result::Success;
2✔
1911
      }
2✔
1912

1913
      return LWResult::Result::Success;
×
1914
    }
2✔
1915

1916
    return LWResult::Result::Timeout;
×
1917
  });
8✔
1918

1919
  vector<DNSRecord> ret;
2✔
1920
  int res = sr->beginResolve(target1, QType(QType::A), QClass::IN, ret);
2✔
1921
  BOOST_CHECK_EQUAL(res, RCode::ServFail);
2✔
1922
  BOOST_CHECK_EQUAL(ret.size(), 0U);
2✔
1923
  BOOST_CHECK_EQUAL(count, 8U);
2✔
1924

1925
  // And again to check cache
1926
  try {
2✔
1927
    sr->beginResolve(target1, QType(QType::A), QClass::IN, ret);
2✔
1928
    BOOST_CHECK(false);
2✔
1929
  }
2✔
1930
  catch (const ImmediateServFailException& ex) {
2✔
1931
    BOOST_CHECK(true);
2✔
1932
  }
2✔
1933
}
2✔
1934

1935
BOOST_AUTO_TEST_CASE(test_cname_long_step0_shortcut)
1936
{
2✔
1937
  // This tets the case fixed /optimizaed in #14973
1938
  std::unique_ptr<SyncRes> resolver;
2✔
1939
  initSR(resolver);
2✔
1940
  resolver->setQNameMinimization();
2✔
1941
  primeHints();
2✔
1942
  resolver->setLogMode(SyncRes::Store);
2✔
1943

1944
  size_t count = 0;
2✔
1945
  const DNSName target1("cname1.powerdns.com.");
2✔
1946
  const DNSName target2("cname2.powerdns.com.");
2✔
1947
  const DNSName target3("cname3.powerdns.com.");
2✔
1948
  const DNSName target4("cname4.powerdns.com.");
2✔
1949

1950
  resolver->setAsyncCallback([&](const ComboAddress& address, const DNSName& domain, int qtype, bool /* doTCP */, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval* /* now */, boost::optional<Netmask>& /* srcmask */, const ResolveContext& /* context */, LWResult* res, bool* /* chained */) {
14✔
1951
    count++;
14✔
1952

1953
    if (domain == DNSName("com.")) {
14✔
1954
      setLWResult(res, 0, false, false, true);
2✔
1955
      addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
2✔
1956
      addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
2✔
1957
      return LWResult::Result::Success;
2✔
1958
    }
2✔
1959
    if (domain == DNSName("powerdns.com.")) {
12✔
1960
      setLWResult(res, 0, false, false, true);
2✔
1961
      addRecordToLW(res, domain, QType::NS, "ns.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
2✔
1962
      addRecordToLW(res, "ns.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
2✔
1963
      return LWResult::Result::Success;
2✔
1964
    }
2✔
1965
    if (address == ComboAddress("192.0.2.2:53")) {
10!
1966

1967
      if (domain == target1) {
10✔
1968
        setLWResult(res, 0, true, false, false);
2✔
1969
        addRecordToLW(res, domain, QType::CNAME, target2.toString());
2✔
1970
        return LWResult::Result::Success;
2✔
1971
      }
2✔
1972
      if (domain == target2) {
8✔
1973
        setLWResult(res, 0, true, false, false);
2✔
1974
        addRecordToLW(res, domain, QType::CNAME, target3.toString());
2✔
1975
        return LWResult::Result::Success;
2✔
1976
      }
2✔
1977
      if (domain == target3) {
6✔
1978
        setLWResult(res, 0, true, false, false);
2✔
1979
        addRecordToLW(res, domain, QType::CNAME, target4.toString());
2✔
1980
        return LWResult::Result::Success;
2✔
1981
      }
2✔
1982
      if (domain == target4) {
4!
1983
        setLWResult(res, 0, true, false, false);
4✔
1984
        if (qtype == QType::A) {
4!
1985
          addRecordToLW(res, domain, QType::A, "1.2.3.4");
×
1986
        }
×
1987
        else if (qtype == QType::AAAA) {
4!
1988
          addRecordToLW(res, domain, QType::AAAA, "::1234");
4✔
1989
        }
4✔
1990
        return LWResult::Result::Success;
4✔
1991
      }
4✔
1992

1993
      return LWResult::Result::Success;
×
1994
    }
4✔
1995

1996
    return LWResult::Result::Timeout;
×
1997
  });
10✔
1998

1999
  // We analyze the trace to see how many cases of a failed Step0 occurs. This is a bit dirty, but
2000
  // we have no other way of telling how the resolving took place, as the number of cache lookups is
2001
  // not recorded by the record cache. Also, we like to know if something in Syncres changed that
2002
  // influences the number of failing Step0 lookups.
2003
  auto counter = [](const string& str) {
6✔
2004
    const std::string key = "Step0 Not cached";
6✔
2005
    size_t occurences = 0;
6✔
2006
    auto pos = str.find(key);
6✔
2007
    while (pos != std::string::npos) {
60✔
2008
      occurences++;
54✔
2009
      pos = str.find(key, pos + 1);
54✔
2010
    }
54✔
2011
    return occurences;
6✔
2012
  };
6✔
2013
  vector<DNSRecord> ret;
2✔
2014
  int res = resolver->beginResolve(target1, QType(QType::AAAA), QClass::IN, ret);
2✔
2015
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
2016
  BOOST_CHECK_EQUAL(ret.size(), 4U);
2✔
2017
  BOOST_CHECK_EQUAL(count, 6U);
2✔
2018
  BOOST_CHECK_EQUAL(resolver->d_maxdepth, 3U);
2✔
2019
  auto trace = resolver->getTrace();
2✔
2020
  trace = resolver->getTrace();
2✔
2021
  BOOST_CHECK_EQUAL(counter(trace), 4U);
2✔
2022

2023
  // And again to check cache, all Step0 cases should succeed
2024
  ret.clear();
2✔
2025
  res = resolver->beginResolve(target1, QType(QType::AAAA), QClass::IN, ret);
2✔
2026
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
2027
  BOOST_CHECK_EQUAL(ret.size(), 4U);
2✔
2028
  BOOST_CHECK_EQUAL(count, 6U);
2✔
2029
  BOOST_CHECK_EQUAL(resolver->d_maxdepth, 3U);
2✔
2030
  trace = resolver->getTrace();
2✔
2031
  BOOST_CHECK_EQUAL(counter(trace), 4U);
2✔
2032

2033
  // And again to check a name that does not fully resolve, we expect Step0 failures to increase
2034
  g_recCache->doWipeCache(target4, false, QType::AAAA);
2✔
2035
  ret.clear();
2✔
2036
  res = resolver->beginResolve(target1, QType(QType::AAAA), QClass::IN, ret);
2✔
2037
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
2038
  BOOST_CHECK_EQUAL(ret.size(), 4U);
2✔
2039
  BOOST_CHECK_EQUAL(count, 7U);
2✔
2040
  BOOST_CHECK_EQUAL(resolver->d_maxdepth, 3U);
2✔
2041
  trace = resolver->getTrace();
2✔
2042
  // XXX This number feels pretty high (15 extra, same as before #14973, there seems to be room for more improvement).
2043
  BOOST_CHECK_EQUAL(counter(trace), 19U);
2✔
2044
}
2✔
2045

2046
BOOST_AUTO_TEST_CASE(test_cname_length)
2047
{
2✔
2048
  std::unique_ptr<SyncRes> sr;
2✔
2049
  initSR(sr);
2✔
2050

2051
  primeHints();
2✔
2052

2053
  size_t length = 0;
2✔
2054
  const DNSName target("cname.powerdns.com.");
2✔
2055

2056
  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 */) {
44✔
2057
    if (isRootServer(address)) {
44✔
2058

2059
      setLWResult(res, 0, false, false, true);
22✔
2060
      addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
22✔
2061
      addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
22✔
2062
      return LWResult::Result::Success;
22✔
2063
    }
22✔
2064
    if (address == ComboAddress("192.0.2.1:53")) {
22!
2065

2066
      setLWResult(res, 0, true, false, false);
22✔
2067
      addRecordToLW(res, domain, QType::CNAME, std::to_string(length) + "-cname.powerdns.com");
22✔
2068
      length++;
22✔
2069
      return LWResult::Result::Success;
22✔
2070
    }
22✔
2071

2072
    return LWResult::Result::Timeout;
×
2073
  });
22✔
2074

2075
  vector<DNSRecord> ret;
2✔
2076
  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
2077
  BOOST_CHECK_EQUAL(res, RCode::ServFail);
2✔
2078
  BOOST_CHECK_EQUAL(ret.size(), length);
2✔
2079
  BOOST_CHECK_EQUAL(length, SyncRes::s_max_CNAMES_followed + 1);
2✔
2080

2081
  // The CNAME bounds check originating from the record cache causes an ImmediateServFail
2082
  // exception. This is different from the non-cached case, tested above.
2083
  ret.clear();
2✔
2084
  BOOST_CHECK_EXCEPTION(sr->beginResolve(target, QType(QType::A), QClass::IN, ret),
2✔
2085
                        ImmediateServFailException,
2✔
2086
                        [&](const ImmediateServFailException& isfe) {
2✔
2087
                          return isfe.reason == "max number of CNAMEs exceeded";
2✔
2088
                        });
2✔
2089
  BOOST_CHECK_EQUAL(ret.size(), SyncRes::s_max_CNAMES_followed + 1);
2✔
2090

2091
  // Again, now with QName Minimization on, originally this would fail as the result was collected
2092
  // in a temp vector and the throw would skip the copy of the temp vector into the end result
2093
  sr->setQNameMinimization();
2✔
2094
  ret.clear();
2✔
2095
  BOOST_CHECK_EXCEPTION(sr->beginResolve(target, QType(QType::A), QClass::IN, ret),
2✔
2096
                        ImmediateServFailException,
2✔
2097
                        [&](const ImmediateServFailException& isfe) {
2✔
2098
                          return isfe.reason == "max number of CNAMEs exceeded";
2✔
2099
                        });
2✔
2100
  BOOST_CHECK_EQUAL(ret.size(), SyncRes::s_max_CNAMES_followed + 1);
2✔
2101
}
2✔
2102

2103
BOOST_AUTO_TEST_CASE(test_cname_target_servfail)
2104
{
2✔
2105
  std::unique_ptr<SyncRes> resolver;
2✔
2106
  initSR(resolver);
2✔
2107

2108
  primeHints();
2✔
2109

2110
  const DNSName target("cname.powerdns.com.");
2✔
2111
  const DNSName cnameTarget("cname-target.powerdns.com");
2✔
2112

2113
  resolver->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 */) {
8✔
2114
    if (isRootServer(ipAddress)) {
8✔
2115
      setLWResult(res, 0, false, false, true);
4✔
2116
      addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
4✔
2117
      addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
4✔
2118
      return LWResult::Result::Success;
4✔
2119
    }
4✔
2120
    if (ipAddress == ComboAddress("192.0.2.1:53")) {
4!
2121

2122
      if (domain == target) {
4✔
2123
        setLWResult(res, 0, true, false, false);
2✔
2124
        addRecordToLW(res, domain, QType::CNAME, cnameTarget.toString());
2✔
2125
        return LWResult::Result::Success;
2✔
2126
      }
2✔
2127
      if (domain == cnameTarget) {
2!
2128
        return LWResult::Result::PermanentError;
2✔
2129
      }
2✔
2130

2131
      return LWResult::Result::Success;
×
2132
    }
2✔
2133

2134
    return LWResult::Result::Timeout;
×
2135
  });
4✔
2136

2137
  vector<DNSRecord> ret;
2✔
2138
  int res = resolver->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
2139
  BOOST_CHECK_EQUAL(res, RCode::ServFail);
2✔
2140
  BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2✔
2141
  BOOST_CHECK(ret[0].d_type == QType::CNAME);
2✔
2142
  BOOST_CHECK_EQUAL(ret[0].d_name, target);
2✔
2143
}
2✔
2144

2145
BOOST_AUTO_TEST_CASE(test_cname_target_servfail_servestale)
2146
{
2✔
2147
  std::unique_ptr<SyncRes> resolver;
2✔
2148
  initSR(resolver);
2✔
2149
  MemRecursorCache::s_maxServedStaleExtensions = 1440;
2✔
2150

2151
  primeHints();
2✔
2152

2153
  const DNSName target("cname.powerdns.com.");
2✔
2154
  const DNSName cnameTarget("cname-target.powerdns.com");
2✔
2155

2156
  resolver->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 */) {
8✔
2157
    if (isRootServer(ipAddress)) {
8✔
2158
      setLWResult(res, 0, false, false, true);
4✔
2159
      addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
4✔
2160
      addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
4✔
2161
      return LWResult::Result::Success;
4✔
2162
    }
4✔
2163
    if (ipAddress == ComboAddress("192.0.2.1:53")) {
4!
2164

2165
      if (domain == target) {
4✔
2166
        setLWResult(res, 0, true, false, false);
2✔
2167
        addRecordToLW(res, domain, QType::CNAME, cnameTarget.toString());
2✔
2168
        return LWResult::Result::Success;
2✔
2169
      }
2✔
2170
      if (domain == cnameTarget) {
2!
2171
        return LWResult::Result::PermanentError;
2✔
2172
      }
2✔
2173

2174
      return LWResult::Result::Success;
×
2175
    }
2✔
2176

2177
    return LWResult::Result::Timeout;
×
2178
  });
4✔
2179

2180
  vector<DNSRecord> ret;
2✔
2181
  int res = resolver->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
2182
  // different compared no non-servestale case (returns ServFail), handled by pdns_recursor
2183
  BOOST_CHECK_EQUAL(res, -1);
2✔
2184
  BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2✔
2185
  BOOST_CHECK(ret[0].d_type == QType::CNAME);
2✔
2186
  BOOST_CHECK_EQUAL(ret[0].d_name, target);
2✔
2187
}
2✔
2188

2189
BOOST_AUTO_TEST_CASE(test_time_limit)
2190
{
2✔
2191
  std::unique_ptr<SyncRes> sr;
2✔
2192
  initSR(sr);
2✔
2193

2194
  primeHints();
2✔
2195

2196
  size_t queries = 0;
2✔
2197
  const DNSName target("cname.powerdns.com.");
2✔
2198

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

2202
    if (isRootServer(address)) {
2!
2203
      setLWResult(res, 0, false, false, true);
2✔
2204
      /* Pretend that this query took 2000 ms */
2205
      res->d_usec = 2000;
2✔
2206

2207
      addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
2✔
2208
      addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
2✔
2209
      return LWResult::Result::Success;
2✔
2210
    }
2✔
2211
    if (address == ComboAddress("192.0.2.1:53")) {
×
2212

2213
      setLWResult(res, 0, true, false, false);
×
2214
      addRecordToLW(res, domain, QType::A, "192.0.2.2");
×
2215
      return LWResult::Result::Success;
×
2216
    }
×
2217

2218
    return LWResult::Result::Timeout;
×
2219
  });
×
2220

2221
  /* Set the maximum time to 1 ms */
2222
  SyncRes::s_maxtotusec = 1000;
2✔
2223

2224
  try {
2✔
2225
    vector<DNSRecord> ret;
2✔
2226
    sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
2227
    BOOST_CHECK(false);
2✔
2228
  }
2✔
2229
  catch (const ImmediateServFailException& e) {
2✔
2230
  }
2✔
2231
  BOOST_CHECK_EQUAL(queries, 1U);
2✔
2232
}
2✔
2233

2234
BOOST_AUTO_TEST_CASE(test_dname_processing)
2235
{
2✔
2236
  std::unique_ptr<SyncRes> sr;
2✔
2237
  initSR(sr);
2✔
2238

2239
  primeHints();
2✔
2240

2241
  const DNSName dnameOwner("powerdns.com");
2✔
2242
  const DNSName dnameTarget("powerdns.net");
2✔
2243

2244
  const DNSName target("dname.powerdns.com.");
2✔
2245
  const DNSName cnameTarget("dname.powerdns.net");
2✔
2246

2247
  const DNSName uncachedTarget("dname-uncached.powerdns.com.");
2✔
2248
  const DNSName uncachedCNAMETarget("dname-uncached.powerdns.net.");
2✔
2249

2250
  const DNSName synthCNAME("cname-uncached.powerdns.com.");
2✔
2251
  const DNSName synthCNAMETarget("cname-uncached.powerdns.net.");
2✔
2252

2253
  size_t queries = 0;
2✔
2254

2255
  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✔
2256
    queries++;
10✔
2257

2258
    if (isRootServer(address)) {
10✔
2259
      if (domain.isPartOf(dnameOwner)) {
4✔
2260
        setLWResult(res, 0, false, false, true);
2✔
2261
        addRecordToLW(res, dnameOwner, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
2✔
2262
        addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
2✔
2263
        return LWResult::Result::Success;
2✔
2264
      }
2✔
2265
      if (domain.isPartOf(dnameTarget)) {
2!
2266
        setLWResult(res, 0, false, false, true);
2✔
2267
        addRecordToLW(res, dnameTarget, QType::NS, "b.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
2✔
2268
        addRecordToLW(res, "b.gtld-servers.net.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
2✔
2269
        return LWResult::Result::Success;
2✔
2270
      }
2✔
2271
    }
2✔
2272
    else if (address == ComboAddress("192.0.2.1:53")) {
6✔
2273
      if (domain == target) {
2!
2274
        setLWResult(res, 0, true, false, false);
2✔
2275
        addRecordToLW(res, dnameOwner, QType::DNAME, dnameTarget.toString());
2✔
2276
        addRecordToLW(res, domain, QType::CNAME, cnameTarget.toString());
2✔
2277
        return LWResult::Result::Success;
2✔
2278
      }
2✔
2279
    }
2✔
2280
    else if (address == ComboAddress("192.0.2.2:53")) {
4!
2281
      if (domain == cnameTarget) {
4✔
2282
        setLWResult(res, 0, true, false, false);
2✔
2283
        addRecordToLW(res, domain, QType::A, "192.0.2.2");
2✔
2284
      }
2✔
2285
      if (domain == uncachedCNAMETarget) {
4✔
2286
        setLWResult(res, 0, true, false, false);
2✔
2287
        addRecordToLW(res, domain, QType::A, "192.0.2.3");
2✔
2288
      }
2✔
2289
      return LWResult::Result::Success;
4✔
2290
    }
4✔
2291
    return LWResult::Result::Timeout;
×
2292
  });
10✔
2293

2294
  vector<DNSRecord> ret;
2✔
2295
  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
2296

2297
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
2298
  BOOST_REQUIRE_EQUAL(ret.size(), 3U);
2✔
2299

2300
  BOOST_CHECK_EQUAL(queries, 4u);
2✔
2301

2302
  BOOST_REQUIRE(ret[0].d_type == QType::DNAME);
2✔
2303
  BOOST_CHECK(ret[0].d_name == dnameOwner);
2✔
2304
  BOOST_CHECK_EQUAL(getRR<DNAMERecordContent>(ret[0])->getTarget(), dnameTarget);
2✔
2305

2306
  BOOST_CHECK(ret[1].d_type == QType::CNAME);
2✔
2307
  BOOST_CHECK_EQUAL(ret[1].d_name, target);
2✔
2308

2309
  BOOST_CHECK(ret[2].d_type == QType::A);
2✔
2310
  BOOST_CHECK_EQUAL(ret[2].d_name, cnameTarget);
2✔
2311

2312
  // Now check the cache
2313
  ret.clear();
2✔
2314
  res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
2315

2316
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
2317
  BOOST_REQUIRE_EQUAL(ret.size(), 3U);
2✔
2318

2319
  BOOST_CHECK_EQUAL(queries, 4U);
2✔
2320

2321
  BOOST_REQUIRE(ret[0].d_type == QType::DNAME);
2✔
2322
  BOOST_CHECK(ret[0].d_name == dnameOwner);
2✔
2323
  BOOST_CHECK_EQUAL(getRR<DNAMERecordContent>(ret[0])->getTarget(), dnameTarget);
2✔
2324

2325
  BOOST_CHECK(ret[1].d_type == QType::CNAME);
2✔
2326
  BOOST_CHECK_EQUAL(ret[1].d_name, target);
2✔
2327

2328
  BOOST_CHECK(ret[2].d_type == QType::A);
2✔
2329
  BOOST_CHECK_EQUAL(ret[2].d_name, cnameTarget);
2✔
2330

2331
  // Check if we correctly return a synthesized CNAME, should send out just 1 more query
2332
  ret.clear();
2✔
2333
  res = sr->beginResolve(uncachedTarget, QType(QType::A), QClass::IN, ret);
2✔
2334

2335
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
2336
  BOOST_CHECK_EQUAL(queries, 5U);
2✔
2337

2338
  BOOST_REQUIRE(ret[0].d_type == QType::DNAME);
2✔
2339
  BOOST_CHECK(ret[0].d_name == dnameOwner);
2✔
2340
  BOOST_CHECK_EQUAL(getRR<DNAMERecordContent>(ret[0])->getTarget(), dnameTarget);
2✔
2341

2342
  BOOST_REQUIRE(ret[1].d_type == QType::CNAME);
2✔
2343
  BOOST_CHECK_EQUAL(ret[1].d_name, uncachedTarget);
2✔
2344
  BOOST_CHECK_EQUAL(getRR<CNAMERecordContent>(ret[1])->getTarget(), uncachedCNAMETarget);
2✔
2345

2346
  BOOST_CHECK(ret[2].d_type == QType::A);
2✔
2347
  BOOST_CHECK_EQUAL(ret[2].d_name, uncachedCNAMETarget);
2✔
2348

2349
  // Check if we correctly return the DNAME from cache when asked
2350
  ret.clear();
2✔
2351
  res = sr->beginResolve(dnameOwner, QType(QType::DNAME), QClass::IN, ret);
2✔
2352
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
2353
  BOOST_CHECK_EQUAL(queries, 5U);
2✔
2354

2355
  BOOST_REQUIRE(ret[0].d_type == QType::DNAME);
2✔
2356
  BOOST_CHECK(ret[0].d_name == dnameOwner);
2✔
2357
  BOOST_CHECK_EQUAL(getRR<DNAMERecordContent>(ret[0])->getTarget(), dnameTarget);
2✔
2358

2359
  // Check if we correctly return the synthesized CNAME from cache when asked
2360
  ret.clear();
2✔
2361
  res = sr->beginResolve(synthCNAME, QType(QType::CNAME), QClass::IN, ret);
2✔
2362
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
2363
  BOOST_CHECK_EQUAL(queries, 5U);
2✔
2364

2365
  BOOST_REQUIRE(ret[0].d_type == QType::DNAME);
2✔
2366
  BOOST_CHECK(ret[0].d_name == dnameOwner);
2✔
2367
  BOOST_CHECK_EQUAL(getRR<DNAMERecordContent>(ret[0])->getTarget(), dnameTarget);
2✔
2368

2369
  BOOST_REQUIRE(ret[1].d_type == QType::CNAME);
2✔
2370
  BOOST_CHECK(ret[1].d_name == synthCNAME);
2✔
2371
  BOOST_CHECK_EQUAL(getRR<CNAMERecordContent>(ret[1])->getTarget(), synthCNAMETarget);
2✔
2372
}
2✔
2373

2374
BOOST_AUTO_TEST_CASE(test_dname_dnssec_secure)
2375
{
2✔
2376
  std::unique_ptr<SyncRes> sr;
2✔
2377
  initSR(sr, true);
2✔
2378
  setDNSSECValidation(sr, DNSSECMode::ValidateAll);
2✔
2379

2380
  primeHints();
2✔
2381

2382
  const DNSName dnameOwner("powerdns");
2✔
2383
  const DNSName dnameTarget("example");
2✔
2384

2385
  const DNSName target("dname.powerdns");
2✔
2386
  const DNSName cnameTarget("dname.example");
2✔
2387

2388
  testkeysset_t keys;
2✔
2389

2390
  auto luaconfsCopy = g_luaconfs.getCopy();
2✔
2391
  luaconfsCopy.dsAnchors.clear();
2✔
2392
  generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys, luaconfsCopy.dsAnchors);
2✔
2393
  generateKeyMaterial(dnameOwner, DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
2✔
2394
  generateKeyMaterial(dnameTarget, DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
2✔
2395
  g_luaconfs.setState(luaconfsCopy);
2✔
2396

2397
  size_t queries = 0;
2✔
2398

2399
  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✔
2400
    queries++;
14✔
2401
    /* We don't use the genericDSAndDNSKEYHandler here, as it would deny names existing at the wrong level of the tree, due to the way computeZoneCuts works
2402
     * As such, we need to do some more work to make the answers correct.
2403
     */
2404

2405
    if (isRootServer(address)) {
14✔
2406
      if (domain.countLabels() == 0 && type == QType::DNSKEY) { // .|DNSKEY
6!
2407
        setLWResult(res, 0, true, false, true);
2✔
2408
        addDNSKEY(keys, domain, 300, res->d_records);
2✔
2409
        addRRSIG(keys, res->d_records, DNSName("."), 300);
2✔
2410
        return LWResult::Result::Success;
2✔
2411
      }
2✔
2412
      if (domain.countLabels() == 1 && type == QType::DS) { // powerdns|DS or example|DS
4!
2413
        setLWResult(res, 0, true, false, true);
×
2414
        addDS(domain, 300, res->d_records, keys, DNSResourceRecord::ANSWER);
×
2415
        addRRSIG(keys, res->d_records, DNSName("."), 300);
×
2416
        return LWResult::Result::Success;
×
2417
      }
×
2418
      // For the rest, delegate!
2419
      if (domain.isPartOf(dnameOwner)) {
4✔
2420
        setLWResult(res, 0, false, false, true);
2✔
2421
        addRecordToLW(res, dnameOwner, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
2✔
2422
        addDS(dnameOwner, 300, res->d_records, keys);
2✔
2423
        addRRSIG(keys, res->d_records, DNSName("."), 300);
2✔
2424
        addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
2✔
2425
        return LWResult::Result::Success;
2✔
2426
      }
2✔
2427
      if (domain.isPartOf(dnameTarget)) {
2!
2428
        setLWResult(res, 0, false, false, true);
2✔
2429
        addRecordToLW(res, dnameTarget, QType::NS, "b.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
2✔
2430
        addDS(dnameTarget, 300, res->d_records, keys);
2✔
2431
        addRRSIG(keys, res->d_records, DNSName("."), 300);
2✔
2432
        addRecordToLW(res, "b.gtld-servers.net.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
2✔
2433
        return LWResult::Result::Success;
2✔
2434
      }
2✔
2435
    }
2✔
2436
    else if (address == ComboAddress("192.0.2.1:53")) {
8✔
2437
      if (domain.countLabels() == 1 && type == QType::DNSKEY) { // powerdns|DNSKEY
4!
2438
        setLWResult(res, 0, true, false, true);
2✔
2439
        addDNSKEY(keys, domain, 300, res->d_records);
2✔
2440
        addRRSIG(keys, res->d_records, domain, 300);
2✔
2441
        return LWResult::Result::Success;
2✔
2442
      }
2✔
2443
      if (domain == target && type == QType::DS) { // dname.powerdns|DS
2!
2444
        return genericDSAndDNSKEYHandler(res, domain, domain, type, keys, false);
×
2445
      }
×
2446
      if (domain == target) {
2!
2447
        setLWResult(res, 0, true, false, false);
2✔
2448
        addRecordToLW(res, dnameOwner, QType::DNAME, dnameTarget.toString());
2✔
2449
        addRRSIG(keys, res->d_records, dnameOwner, 300);
2✔
2450
        addRecordToLW(res, domain, QType::CNAME, cnameTarget.toString()); // CNAME from a DNAME is not signed
2✔
2451
        return LWResult::Result::Success;
2✔
2452
      }
2✔
2453
    }
2✔
2454
    else if (address == ComboAddress("192.0.2.2:53")) {
4!
2455
      if (domain.countLabels() == 1 && type == QType::DNSKEY) { // example|DNSKEY
4!
2456
        setLWResult(res, 0, true, false, true);
2✔
2457
        addDNSKEY(keys, domain, 300, res->d_records);
2✔
2458
        addRRSIG(keys, res->d_records, domain, 300);
2✔
2459
        return LWResult::Result::Success;
2✔
2460
      }
2✔
2461
      if (domain == cnameTarget && type == QType::DS) { // dname.example|DS
2!
2462
        return genericDSAndDNSKEYHandler(res, domain, domain, type, keys, false);
×
2463
      }
×
2464
      if (domain == cnameTarget) {
2!
2465
        setLWResult(res, 0, true, false, false);
2✔
2466
        addRecordToLW(res, domain, QType::A, "192.0.2.2");
2✔
2467
        addRRSIG(keys, res->d_records, dnameTarget, 300);
2✔
2468
      }
2✔
2469
      return LWResult::Result::Success;
2✔
2470
    }
2✔
2471
    return LWResult::Result::Timeout;
×
2472
  });
14✔
2473

2474
  vector<DNSRecord> ret;
2✔
2475
  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
2476

2477
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
2478
  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
2✔
2479
  BOOST_REQUIRE_EQUAL(ret.size(), 5U); /* DNAME + RRSIG(DNAME) + CNAME + A + RRSIG(A) */
2✔
2480

2481
  BOOST_CHECK_EQUAL(queries, 7U);
2✔
2482

2483
  BOOST_REQUIRE(ret[0].d_type == QType::DNAME);
2✔
2484
  BOOST_CHECK(ret[0].d_name == dnameOwner);
2✔
2485
  BOOST_CHECK_EQUAL(getRR<DNAMERecordContent>(ret[0])->getTarget(), dnameTarget);
2✔
2486

2487
  BOOST_REQUIRE(ret[1].d_type == QType::RRSIG);
2✔
2488
  BOOST_CHECK_EQUAL(ret[1].d_name, dnameOwner);
2✔
2489

2490
  BOOST_CHECK(ret[2].d_type == QType::CNAME);
2✔
2491
  BOOST_CHECK_EQUAL(ret[2].d_name, target);
2✔
2492

2493
  BOOST_CHECK(ret[3].d_type == QType::A);
2✔
2494
  BOOST_CHECK_EQUAL(ret[3].d_name, cnameTarget);
2✔
2495

2496
  BOOST_CHECK(ret[4].d_type == QType::RRSIG);
2✔
2497
  BOOST_CHECK_EQUAL(ret[4].d_name, cnameTarget);
2✔
2498

2499
  // And the cache
2500
  ret.clear();
2✔
2501
  res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
2502

2503
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
2504
  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
2✔
2505
  BOOST_REQUIRE_EQUAL(ret.size(), 5U); /* DNAME + RRSIG(DNAME) + CNAME + A + RRSIG(A) */
2✔
2506

2507
  BOOST_CHECK_EQUAL(queries, 7U);
2✔
2508

2509
  BOOST_REQUIRE(ret[0].d_type == QType::DNAME);
2✔
2510
  BOOST_CHECK(ret[0].d_name == dnameOwner);
2✔
2511
  BOOST_CHECK_EQUAL(getRR<DNAMERecordContent>(ret[0])->getTarget(), dnameTarget);
2✔
2512

2513
  BOOST_CHECK(ret[1].d_type == QType::RRSIG);
2✔
2514
  BOOST_CHECK_EQUAL(ret[1].d_name, dnameOwner);
2✔
2515

2516
  BOOST_CHECK(ret[2].d_type == QType::CNAME);
2✔
2517
  BOOST_CHECK_EQUAL(ret[2].d_name, target);
2✔
2518

2519
  BOOST_CHECK(ret[3].d_type == QType::A);
2✔
2520
  BOOST_CHECK_EQUAL(ret[3].d_name, cnameTarget);
2✔
2521

2522
  BOOST_CHECK(ret[4].d_type == QType::RRSIG);
2✔
2523
  BOOST_CHECK_EQUAL(ret[4].d_name, cnameTarget);
2✔
2524
}
2✔
2525

2526
BOOST_AUTO_TEST_CASE(test_dname_plus_ns_dnssec_secure)
2527
{
2✔
2528
  std::unique_ptr<SyncRes> sr;
2✔
2529
  initSR(sr, true);
2✔
2530
  setDNSSECValidation(sr, DNSSECMode::ValidateAll);
2✔
2531

2532
  primeHints();
2✔
2533

2534
  const DNSName dnameOwner("powerdns");
2✔
2535
  const DNSName dnameTarget("example");
2✔
2536

2537
  const DNSName target("dname.powerdns");
2✔
2538
  const DNSName cnameTarget("dname.example");
2✔
2539

2540
  testkeysset_t keys;
2✔
2541

2542
  auto luaconfsCopy = g_luaconfs.getCopy();
2✔
2543
  luaconfsCopy.dsAnchors.clear();
2✔
2544
  generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys, luaconfsCopy.dsAnchors);
2✔
2545
  generateKeyMaterial(dnameTarget, DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
2✔
2546
  g_luaconfs.setState(luaconfsCopy);
2✔
2547

2548
  size_t queries = 0;
2✔
2549

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

2553
    if (type == QType::DS || type == QType::DNSKEY) {
8!
2554
      return genericDSAndDNSKEYHandler(res, domain, domain, type, keys, false);
4✔
2555
    }
4✔
2556

2557
    if (domain.isPartOf(dnameOwner)) {
4✔
2558
      setLWResult(res, 0, true, false, true);
2✔
2559
      addRecordToLW(res, dnameOwner, QType::DNAME, dnameTarget.toString());
2✔
2560
      addRRSIG(keys, res->d_records, DNSName("."), 300);
2✔
2561
      addRecordToLW(res, domain, QType::CNAME, cnameTarget.toString()); // CNAME from a DNAME is not signed
2✔
2562

2563
      addRecordToLW(res, dnameTarget, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
2✔
2564
      addDS(dnameTarget, 300, res->d_records, keys);
2✔
2565
      addRRSIG(keys, res->d_records, DNSName("."), 300);
2✔
2566
      addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
2✔
2567
      return LWResult::Result::Success;
2✔
2568
    }
2✔
2569
    if (domain == cnameTarget) {
2!
2570
      setLWResult(res, 0, true, false, true);
2✔
2571
      addRecordToLW(res, domain, QType::A, "192.0.2.42");
2✔
2572
      addRRSIG(keys, res->d_records, dnameTarget, 300);
2✔
2573
      return LWResult::Result::Success;
2✔
2574
    }
2✔
2575
    return LWResult::Result::Timeout;
×
2576
  });
2✔
2577

2578
  vector<DNSRecord> ret;
2✔
2579
  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
2580

2581
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
2582
  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
2✔
2583
  BOOST_REQUIRE_EQUAL(ret.size(), 5U); /* DNAME + RRSIG(DNAME) + CNAME + A + RRSIG(A) */
2✔
2584

2585
  BOOST_CHECK_EQUAL(queries, 4U);
2✔
2586

2587
  BOOST_REQUIRE(ret[0].d_type == QType::DNAME);
2✔
2588
  BOOST_CHECK(ret[0].d_name == dnameOwner);
2✔
2589
  BOOST_CHECK_EQUAL(getRR<DNAMERecordContent>(ret[0])->getTarget(), dnameTarget);
2✔
2590

2591
  BOOST_REQUIRE(ret[1].d_type == QType::RRSIG);
2✔
2592
  BOOST_CHECK_EQUAL(ret[1].d_name, dnameOwner);
2✔
2593

2594
  BOOST_CHECK(ret[2].d_type == QType::CNAME);
2✔
2595
  BOOST_CHECK_EQUAL(ret[2].d_name, target);
2✔
2596

2597
  BOOST_CHECK(ret[3].d_type == QType::A);
2✔
2598
  BOOST_CHECK_EQUAL(ret[3].d_name, cnameTarget);
2✔
2599

2600
  BOOST_CHECK(ret[4].d_type == QType::RRSIG);
2✔
2601
  BOOST_CHECK_EQUAL(ret[4].d_name, cnameTarget);
2✔
2602

2603
  // And the cache
2604
  ret.clear();
2✔
2605
  res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
2606

2607
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
2608
  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
2✔
2609
  BOOST_REQUIRE_EQUAL(ret.size(), 5U); /* DNAME + RRSIG(DNAME) + CNAME + A + RRSIG(A) */
2✔
2610

2611
  BOOST_CHECK_EQUAL(queries, 4U);
2✔
2612

2613
  BOOST_REQUIRE(ret[0].d_type == QType::DNAME);
2✔
2614
  BOOST_CHECK(ret[0].d_name == dnameOwner);
2✔
2615
  BOOST_CHECK_EQUAL(getRR<DNAMERecordContent>(ret[0])->getTarget(), dnameTarget);
2✔
2616

2617
  BOOST_CHECK(ret[1].d_type == QType::RRSIG);
2✔
2618
  BOOST_CHECK_EQUAL(ret[1].d_name, dnameOwner);
2✔
2619

2620
  BOOST_CHECK(ret[2].d_type == QType::CNAME);
2✔
2621
  BOOST_CHECK_EQUAL(ret[2].d_name, target);
2✔
2622

2623
  BOOST_CHECK(ret[3].d_type == QType::A);
2✔
2624
  BOOST_CHECK_EQUAL(ret[3].d_name, cnameTarget);
2✔
2625

2626
  BOOST_CHECK(ret[4].d_type == QType::RRSIG);
2✔
2627
  BOOST_CHECK_EQUAL(ret[4].d_name, cnameTarget);
2✔
2628
}
2✔
2629

2630
BOOST_AUTO_TEST_CASE(test_dname_dnssec_insecure)
2631
{
2✔
2632
  /*
2633
   * The DNAME itself is signed, but the final A record is not
2634
   */
2635
  std::unique_ptr<SyncRes> sr;
2✔
2636
  initSR(sr, true);
2✔
2637
  setDNSSECValidation(sr, DNSSECMode::ValidateAll);
2✔
2638

2639
  primeHints();
2✔
2640

2641
  const DNSName dnameOwner("powerdns");
2✔
2642
  const DNSName dnameTarget("example");
2✔
2643

2644
  const DNSName target("dname.powerdns");
2✔
2645
  const DNSName cnameTarget("dname.example");
2✔
2646

2647
  testkeysset_t keys;
2✔
2648

2649
  auto luaconfsCopy = g_luaconfs.getCopy();
2✔
2650
  luaconfsCopy.dsAnchors.clear();
2✔
2651
  generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys, luaconfsCopy.dsAnchors);
2✔
2652
  generateKeyMaterial(dnameOwner, DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
2✔
2653
  g_luaconfs.setState(luaconfsCopy);
2✔
2654

2655
  size_t queries = 0;
2✔
2656

2657
  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✔
2658
    queries++;
14✔
2659

2660
    if (isRootServer(address)) {
14✔
2661
      if (domain.countLabels() == 0 && type == QType::DNSKEY) { // .|DNSKEY
8!
2662
        setLWResult(res, 0, true, false, true);
2✔
2663
        addDNSKEY(keys, domain, 300, res->d_records);
2✔
2664
        addRRSIG(keys, res->d_records, DNSName("."), 300);
2✔
2665
        return LWResult::Result::Success;
2✔
2666
      }
2✔
2667
      if (domain == dnameOwner && type == QType::DS) { // powerdns|DS
6!
2668
        setLWResult(res, 0, true, false, true);
×
2669
        addDS(domain, 300, res->d_records, keys, DNSResourceRecord::ANSWER);
×
2670
        addRRSIG(keys, res->d_records, DNSName("."), 300);
×
2671
        return LWResult::Result::Success;
×
2672
      }
×
2673
      if (domain == dnameTarget && type == QType::DS) { // example|DS
6!
2674
        return genericDSAndDNSKEYHandler(res, domain, DNSName("."), type, keys);
2✔
2675
      }
2✔
2676
      // For the rest, delegate!
2677
      if (domain.isPartOf(dnameOwner)) {
4✔
2678
        setLWResult(res, 0, false, false, true);
2✔
2679
        addRecordToLW(res, dnameOwner, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
2✔
2680
        addDS(dnameOwner, 300, res->d_records, keys);
2✔
2681
        addRRSIG(keys, res->d_records, DNSName("."), 300);
2✔
2682
        addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
2✔
2683
        return LWResult::Result::Success;
2✔
2684
      }
2✔
2685
      if (domain.isPartOf(dnameTarget)) {
2!
2686
        setLWResult(res, 0, false, false, true);
2✔
2687
        addRecordToLW(res, dnameTarget, QType::NS, "b.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
2✔
2688
        addDS(dnameTarget, 300, res->d_records, keys);
2✔
2689
        addRRSIG(keys, res->d_records, DNSName("."), 300);
2✔
2690
        addRecordToLW(res, "b.gtld-servers.net.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
2✔
2691
        return LWResult::Result::Success;
2✔
2692
      }
2✔
2693
    }
2✔
2694
    else if (address == ComboAddress("192.0.2.1:53")) {
6✔
2695
      if (domain.countLabels() == 1 && type == QType::DNSKEY) { // powerdns|DNSKEY
4!
2696
        setLWResult(res, 0, true, false, true);
2✔
2697
        addDNSKEY(keys, domain, 300, res->d_records);
2✔
2698
        addRRSIG(keys, res->d_records, domain, 300);
2✔
2699
        return LWResult::Result::Success;
2✔
2700
      }
2✔
2701
      if (domain == target && type == QType::DS) { // dname.powerdns|DS
2!
2702
        return genericDSAndDNSKEYHandler(res, domain, dnameOwner, type, keys, false);
×
2703
      }
×
2704
      if (domain == target) {
2!
2705
        setLWResult(res, 0, true, false, false);
2✔
2706
        addRecordToLW(res, dnameOwner, QType::DNAME, dnameTarget.toString());
2✔
2707
        addRRSIG(keys, res->d_records, dnameOwner, 300);
2✔
2708
        addRecordToLW(res, domain, QType::CNAME, cnameTarget.toString()); // CNAME from a DNAME is not signed
2✔
2709
        return LWResult::Result::Success;
2✔
2710
      }
2✔
2711
    }
2✔
2712
    else if (address == ComboAddress("192.0.2.2:53")) {
2!
2713
      if (domain == target && type == QType::DS) { // dname.example|DS
2!
2714
        return genericDSAndDNSKEYHandler(res, domain, dnameTarget, type, keys, false);
×
2715
      }
×
2716
      if (domain == cnameTarget) {
2!
2717
        setLWResult(res, 0, true, false, false);
2✔
2718
        addRecordToLW(res, domain, QType::A, "192.0.2.2");
2✔
2719
      }
2✔
2720
      return LWResult::Result::Success;
2✔
2721
    }
2✔
2722
    return LWResult::Result::Timeout;
×
2723
  });
14✔
2724

2725
  vector<DNSRecord> ret;
2✔
2726
  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
2727

2728
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
2729
  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
2✔
2730
  BOOST_REQUIRE_EQUAL(ret.size(), 4U); /* DNAME + RRSIG(DNAME) + CNAME + A */
2✔
2731

2732
  BOOST_CHECK_EQUAL(queries, 7U);
2✔
2733

2734
  BOOST_REQUIRE(ret[0].d_type == QType::DNAME);
2✔
2735
  BOOST_CHECK(ret[0].d_name == dnameOwner);
2✔
2736
  BOOST_CHECK_EQUAL(getRR<DNAMERecordContent>(ret[0])->getTarget(), dnameTarget);
2✔
2737

2738
  BOOST_CHECK(ret[1].d_type == QType::RRSIG);
2✔
2739
  BOOST_CHECK_EQUAL(ret[1].d_name, dnameOwner);
2✔
2740

2741
  BOOST_CHECK(ret[2].d_type == QType::CNAME);
2✔
2742
  BOOST_CHECK_EQUAL(ret[2].d_name, target);
2✔
2743

2744
  BOOST_CHECK(ret[3].d_type == QType::A);
2✔
2745
  BOOST_CHECK_EQUAL(ret[3].d_name, cnameTarget);
2✔
2746

2747
  // And the cache
2748
  ret.clear();
2✔
2749
  res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
2750

2751
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
2752
  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
2✔
2753
  BOOST_REQUIRE_EQUAL(ret.size(), 4U); /* DNAME + RRSIG(DNAME) + CNAME + A */
2✔
2754

2755
  BOOST_CHECK_EQUAL(queries, 7U);
2✔
2756

2757
  BOOST_REQUIRE(ret[0].d_type == QType::DNAME);
2✔
2758
  BOOST_CHECK(ret[0].d_name == dnameOwner);
2✔
2759
  BOOST_CHECK_EQUAL(getRR<DNAMERecordContent>(ret[0])->getTarget(), dnameTarget);
2✔
2760

2761
  BOOST_CHECK(ret[1].d_type == QType::RRSIG);
2✔
2762
  BOOST_CHECK_EQUAL(ret[1].d_name, dnameOwner);
2✔
2763

2764
  BOOST_CHECK(ret[2].d_type == QType::CNAME);
2✔
2765
  BOOST_CHECK_EQUAL(ret[2].d_name, target);
2✔
2766

2767
  BOOST_CHECK(ret[3].d_type == QType::A);
2✔
2768
  BOOST_CHECK_EQUAL(ret[3].d_name, cnameTarget);
2✔
2769
}
2✔
2770

2771
BOOST_AUTO_TEST_CASE(test_dname_processing_no_CNAME)
2772
{
2✔
2773
  std::unique_ptr<SyncRes> sr;
2✔
2774
  initSR(sr);
2✔
2775

2776
  primeHints();
2✔
2777

2778
  const DNSName dnameOwner("powerdns.com");
2✔
2779
  const DNSName dnameTarget("powerdns.net");
2✔
2780

2781
  const DNSName target("dname.powerdns.com.");
2✔
2782
  const DNSName cnameTarget("dname.powerdns.net");
2✔
2783

2784
  size_t queries = 0;
2✔
2785

2786
  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✔
2787
    queries++;
8✔
2788

2789
    if (isRootServer(address)) {
8✔
2790
      if (domain.isPartOf(dnameOwner)) {
4✔
2791
        setLWResult(res, 0, false, false, true);
2✔
2792
        addRecordToLW(res, dnameOwner, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
2✔
2793
        addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
2✔
2794
        return LWResult::Result::Success;
2✔
2795
      }
2✔
2796
      if (domain.isPartOf(dnameTarget)) {
2!
2797
        setLWResult(res, 0, false, false, true);
2✔
2798
        addRecordToLW(res, dnameTarget, QType::NS, "b.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
2✔
2799
        addRecordToLW(res, "b.gtld-servers.net.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
2✔
2800
        return LWResult::Result::Success;
2✔
2801
      }
2✔
2802
    }
2✔
2803
    else if (address == ComboAddress("192.0.2.1:53")) {
4✔
2804
      if (domain == target) {
2!
2805
        setLWResult(res, 0, true, false, false);
2✔
2806
        addRecordToLW(res, dnameOwner, QType::DNAME, dnameTarget.toString());
2✔
2807
        // No CNAME, recursor should synth
2808
        return LWResult::Result::Success;
2✔
2809
      }
2✔
2810
    }
2✔
2811
    else if (address == ComboAddress("192.0.2.2:53")) {
2!
2812
      if (domain == cnameTarget) {
2!
2813
        setLWResult(res, 0, true, false, false);
2✔
2814
        addRecordToLW(res, domain, QType::A, "192.0.2.2");
2✔
2815
      }
2✔
2816
      return LWResult::Result::Success;
2✔
2817
    }
2✔
2818
    return LWResult::Result::Timeout;
×
2819
  });
8✔
2820

2821
  vector<DNSRecord> ret;
2✔
2822
  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
2823

2824
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
2825
  BOOST_REQUIRE_EQUAL(ret.size(), 3U);
2✔
2826

2827
  BOOST_CHECK_EQUAL(queries, 4U);
2✔
2828

2829
  BOOST_REQUIRE(ret[0].d_type == QType::DNAME);
2✔
2830
  BOOST_CHECK(ret[0].d_name == dnameOwner);
2✔
2831
  BOOST_CHECK_EQUAL(getRR<DNAMERecordContent>(ret[0])->getTarget(), dnameTarget);
2✔
2832

2833
  BOOST_CHECK(ret[1].d_type == QType::CNAME);
2✔
2834
  BOOST_CHECK_EQUAL(ret[1].d_name, target);
2✔
2835

2836
  BOOST_CHECK(ret[2].d_type == QType::A);
2✔
2837
  BOOST_CHECK_EQUAL(ret[2].d_name, cnameTarget);
2✔
2838

2839
  // Now check the cache
2840
  ret.clear();
2✔
2841
  res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
2842

2843
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
2844
  BOOST_REQUIRE_EQUAL(ret.size(), 3U);
2✔
2845

2846
  BOOST_CHECK_EQUAL(queries, 4U);
2✔
2847

2848
  BOOST_REQUIRE(ret[0].d_type == QType::DNAME);
2✔
2849
  BOOST_CHECK(ret[0].d_name == dnameOwner);
2✔
2850
  BOOST_CHECK_EQUAL(getRR<DNAMERecordContent>(ret[0])->getTarget(), dnameTarget);
2✔
2851

2852
  BOOST_CHECK(ret[1].d_type == QType::CNAME);
2✔
2853
  BOOST_CHECK_EQUAL(ret[1].d_name, target);
2✔
2854

2855
  BOOST_CHECK(ret[2].d_type == QType::A);
2✔
2856
  BOOST_CHECK_EQUAL(ret[2].d_name, cnameTarget);
2✔
2857
}
2✔
2858

2859
/*
2860
// cerr<<"asyncresolve called to ask "<<ip.toStringWithPort()<<" about "<<domain.toString()<<" / "<<QType(type).getName()<<" over "<<(doTCP ? "TCP" : "UDP")<<" (rd: "<<sendRDQuery<<", EDNS0 level: "<<EDNS0Level<<")"<<endl;
2861

2862
- check out of band support
2863

2864
- check preoutquery
2865

2866
*/
2867

2868
BOOST_AUTO_TEST_CASE(test_glued_referral_child_ns_set_wrong)
2869
{
2✔
2870
  std::unique_ptr<SyncRes> sr;
2✔
2871
  initSR(sr);
2✔
2872

2873
  primeHints();
2✔
2874

2875
  const DNSName target("powerdns.com.");
2✔
2876

2877
  sr->setAsyncCallback([&](const ComboAddress& address, const DNSName& domain, int type, bool /* doTCP */, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval* /* now */, boost::optional<Netmask>& /* srcmask */, const ResolveContext& /* context */, LWResult* res, bool* /* chained */) {
26✔
2878
    /* this will cause issue with qname minimization if we ever implement it */
2879
    if (domain != target) {
26!
2880
      return LWResult::Result::Timeout;
×
2881
    }
×
2882

2883
    if (isRootServer(address)) {
26✔
2884
      setLWResult(res, 0, false, false, true);
2✔
2885
      addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
2✔
2886
      addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
2✔
2887
      addRecordToLW(res, "a.gtld-servers.net.", QType::AAAA, "2001:DB8::1", DNSResourceRecord::ADDITIONAL, 3600);
2✔
2888
      return LWResult::Result::Success;
2✔
2889
    }
2✔
2890
    if (address == ComboAddress("192.0.2.1:53") || address == ComboAddress("[2001:DB8::1]:53")) {
24✔
2891
      setLWResult(res, 0, false, false, true);
2✔
2892
      addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
2✔
2893
      addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns2.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
2✔
2894
      addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 172800);
2✔
2895
      addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::AAAA, "2001:DB8::2", DNSResourceRecord::ADDITIONAL, 172800);
2✔
2896
      addRecordToLW(res, "pdns-public-ns2.powerdns.com.", QType::A, "192.0.2.3", DNSResourceRecord::ADDITIONAL, 172800);
2✔
2897
      addRecordToLW(res, "pdns-public-ns2.powerdns.com.", QType::AAAA, "2001:DB8::3", DNSResourceRecord::ADDITIONAL, 172800);
2✔
2898
      return LWResult::Result::Success;
2✔
2899
    }
2✔
2900
    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")) {
22✔
2901

2902
      if (type == QType::A) {
6✔
2903
        setLWResult(res, 0, true, false, true);
4✔
2904
        addRecordToLW(res, target, QType::A, "192.0.2.4");
4✔
2905
        return LWResult::Result::Success;
4✔
2906
      }
4✔
2907
      if (type == QType::NS) {
2!
2908
        setLWResult(res, 0, true, false, true);
2✔
2909
        addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-nsX1.powerdns.com.", DNSResourceRecord::ANSWER, 172800);
2✔
2910
        addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-nsX2.powerdns.com.", DNSResourceRecord::ANSWER, 172800);
2✔
2911
        addRecordToLW(res, "pdns-public-nsX1.powerdns.com.", QType::A, "192.0.2.11", DNSResourceRecord::ADDITIONAL, 172800);
2✔
2912
        addRecordToLW(res, "pdns-public-nsX1.powerdns.com.", QType::AAAA, "2001:DB8::11", DNSResourceRecord::ADDITIONAL, 172800);
2✔
2913
        addRecordToLW(res, "pdns-public-nsX2.powerdns.com.", QType::A, "192.0.2.12", DNSResourceRecord::ADDITIONAL, 172800);
2✔
2914
        addRecordToLW(res, "pdns-public-nsX2.powerdns.com.", QType::AAAA, "2001:DB8::12", DNSResourceRecord::ADDITIONAL, 172800);
2✔
2915
        return LWResult::Result::Success;
2✔
2916
      }
2✔
2917
      return LWResult::Result::Timeout;
×
2918
    }
2✔
2919
    return LWResult::Result::Timeout;
16✔
2920
  });
22✔
2921

2922
  vector<DNSRecord> ret;
2✔
2923
  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
2924
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
2925
  BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2✔
2926
  BOOST_CHECK(ret[0].d_type == QType::A);
2✔
2927
  BOOST_CHECK_EQUAL(ret[0].d_name, target);
2✔
2928

2929
  // Now resolve NS to get auth NS set in cache and save the parent NS set
2930
  ret.clear();
2✔
2931
  res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
2✔
2932
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
2933
  BOOST_REQUIRE_EQUAL(ret.size(), 2U);
2✔
2934
  BOOST_CHECK(ret[0].d_type == QType::NS);
2✔
2935
  BOOST_CHECK_EQUAL(ret[0].d_name, target);
2✔
2936
  BOOST_CHECK_EQUAL(SyncRes::getSaveParentsNSSetsSize(), 1U);
2✔
2937

2938
  g_recCache->doWipeCache(target, false, QType::A);
2✔
2939
  SyncRes::s_save_parent_ns_set = false;
2✔
2940

2941
  // Try to resolve now via the broken child NS set... should not work
2942
  ret.clear();
2✔
2943
  res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
2944
  BOOST_CHECK_EQUAL(res, RCode::ServFail);
2✔
2945
  BOOST_REQUIRE_EQUAL(ret.size(), 0U);
2✔
2946

2947
  SyncRes::s_save_parent_ns_set = true;
2✔
2948

2949
  // Try to resolve now via the broken child... should work now via fallback to parent NS set
2950
  ret.clear();
2✔
2951
  res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
2✔
2952
  BOOST_CHECK_EQUAL(res, RCode::NoError);
2✔
2953
  BOOST_REQUIRE_EQUAL(ret.size(), 1U);
2✔
2954
  BOOST_CHECK(ret[0].d_type == QType::A);
2✔
2955
  BOOST_CHECK_EQUAL(ret[0].d_name, target);
2✔
2956
}
2✔
2957

2958
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