]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/recursordist/test-recursorcache_cc.cc
54fa05b1f6711689d2dda5def78dda3d1ec001a2
[thirdparty/pdns.git] / pdns / recursordist / test-recursorcache_cc.cc
1 #define BOOST_TEST_DYN_LINK
2 #define BOOST_TEST_NO_MAIN
3
4 #ifdef HAVE_CONFIG_H
5 #include "config.h"
6 #endif
7 #include <boost/test/unit_test.hpp>
8
9 #include "iputils.hh"
10 #include "recursor_cache.hh"
11
12 BOOST_AUTO_TEST_SUITE(recursorcache_cc)
13
14 BOOST_AUTO_TEST_CASE(test_RecursorCacheSimple)
15 {
16 MemRecursorCache MRC;
17
18 std::vector<DNSRecord> records;
19 std::vector<std::shared_ptr<DNSRecord>> authRecords;
20 std::vector<std::shared_ptr<RRSIGRecordContent>> signatures;
21 time_t now = time(nullptr);
22
23 time_t ttd = now + 30;
24 DNSName power("powerdns.com.");
25 DNSRecord dr0;
26 ComboAddress dr0Content("2001:DB8::");
27 dr0.d_name = power;
28 dr0.d_type = QType::AAAA;
29 dr0.d_class = QClass::IN;
30 dr0.d_content = std::make_shared<AAAARecordContent>(dr0Content);
31 dr0.d_ttl = static_cast<uint32_t>(ttd);
32 dr0.d_place = DNSResourceRecord::ANSWER;
33
34 records.push_back(dr0);
35
36 BOOST_CHECK_EQUAL(MRC.size(), 0U);
37 MRC.replace(now, DNSName("hello"), QType(QType::A), records, signatures, authRecords, true, boost::none);
38 BOOST_CHECK_EQUAL(MRC.size(), 1U);
39 BOOST_CHECK_GT(MRC.bytes(), 1U);
40 BOOST_CHECK_EQUAL(MRC.doWipeCache(DNSName("hello"), false, QType::A), 1U);
41 BOOST_CHECK_EQUAL(MRC.size(), 0U);
42 BOOST_CHECK_EQUAL(MRC.bytes(), 0U);
43
44 uint64_t counter = 0;
45 try {
46 for (counter = 0; counter < 100000; ++counter) {
47 DNSName a = DNSName("hello ") + DNSName(std::to_string(counter));
48 BOOST_CHECK_EQUAL(DNSName(a.toString()), a);
49
50 MRC.replace(now, a, QType(QType::A), records, signatures, authRecords, true, boost::none);
51 if (!MRC.doWipeCache(a, false))
52 BOOST_FAIL("Could not remove entry we just added to the cache!");
53 MRC.replace(now, a, QType(QType::A), records, signatures, authRecords, true, boost::none);
54 }
55
56 BOOST_CHECK_EQUAL(MRC.size(), counter);
57
58 uint64_t delcounter = 0;
59 for (delcounter = 0; delcounter < counter / 100; ++delcounter) {
60 DNSName a = DNSName("hello ") + DNSName(std::to_string(delcounter));
61 BOOST_CHECK_EQUAL(MRC.doWipeCache(a, false, QType::A), 1U);
62 }
63
64 BOOST_CHECK_EQUAL(MRC.size(), counter - delcounter);
65
66 std::vector<DNSRecord> retrieved;
67 ComboAddress who("192.0.2.1");
68 int64_t matches = 0;
69 int64_t expected = counter - delcounter;
70
71 for (; delcounter < counter; ++delcounter) {
72 if (MRC.get(now, DNSName("hello ") + DNSName(std::to_string(delcounter)), QType(QType::A), false, &retrieved, who) > 0) {
73 matches++;
74 BOOST_REQUIRE_EQUAL(retrieved.size(), records.size());
75 BOOST_CHECK_EQUAL(getRR<AAAARecordContent>(retrieved.at(0))->getCA().toString(), dr0Content.toString());
76 }
77 }
78 BOOST_CHECK_EQUAL(matches, expected);
79
80 MRC.doWipeCache(DNSName("."), true);
81 BOOST_CHECK_EQUAL(MRC.size(), 0U);
82
83 DNSRecord dr1;
84 ComboAddress dr1Content("2001:DB8::1");
85 dr1.d_name = power;
86 dr1.d_type = QType::AAAA;
87 dr1.d_class = QClass::IN;
88 dr1.d_content = std::make_shared<AAAARecordContent>(dr1Content);
89 dr1.d_ttl = static_cast<uint32_t>(ttd);
90 dr1.d_place = DNSResourceRecord::ANSWER;
91
92 DNSRecord dr2;
93 ComboAddress dr2Content("192.0.2.42");
94 dr2.d_name = power;
95 dr2.d_type = QType::A;
96 dr2.d_class = QClass::IN;
97 dr2.d_content = std::make_shared<ARecordContent>(dr2Content);
98 dr2.d_ttl = static_cast<uint32_t>(ttd);
99 // the place should not matter to the cache
100 dr2.d_place = DNSResourceRecord::AUTHORITY;
101
102 // insert a subnet specific entry
103 records.clear();
104 records.push_back(dr1);
105 MRC.replace(now, power, QType(QType::AAAA), records, signatures, authRecords, true, boost::optional<Netmask>("192.0.2.1/25"));
106 BOOST_CHECK_EQUAL(MRC.size(), 1U);
107
108 // subnet specific should be returned for a matching subnet
109 BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::AAAA), false, &retrieved, ComboAddress("192.0.2.2")), (ttd - now));
110 BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
111 BOOST_CHECK_EQUAL(getRR<AAAARecordContent>(retrieved.at(0))->getCA().toString(), dr1Content.toString());
112
113 // subnet specific should not be returned for a different subnet
114 BOOST_CHECK_LT(MRC.get(now, power, QType(QType::AAAA), false, &retrieved, ComboAddress("127.0.0.1")), 0);
115 BOOST_CHECK_EQUAL(retrieved.size(), 0U);
116
117 // remove everything
118 MRC.doWipeCache(DNSName("."), true);
119 BOOST_CHECK_EQUAL(MRC.size(), 0U);
120
121 // insert a NON-subnet specific entry
122 records.clear();
123 records.push_back(dr2);
124 MRC.replace(now, power, QType(QType::A), records, signatures, authRecords, true, boost::none);
125 BOOST_CHECK_EQUAL(MRC.size(), 1U);
126
127 // NON-subnet specific should always be returned
128 BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("127.0.0.1")), (ttd - now));
129 BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
130 BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr2Content.toString());
131
132 // insert a subnet specific entry for the same name but a different QType
133 records.clear();
134 records.push_back(dr1);
135 MRC.replace(now, power, QType(QType::AAAA), records, signatures, authRecords, true, boost::optional<Netmask>("192.0.2.1/25"));
136 // we should not have replaced the existing entry
137 BOOST_CHECK_EQUAL(MRC.size(), 2U);
138
139 // insert a TXT one, we will use that later
140 records.clear();
141 records.push_back(dr1);
142 MRC.replace(now, power, QType(QType::TXT), records, signatures, authRecords, true, boost::none);
143 // we should not have replaced any existing entry
144 BOOST_CHECK_EQUAL(MRC.size(), 3U);
145
146 // we should still get the NON-subnet specific entry
147 BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("127.0.0.1")), (ttd - now));
148 BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
149 BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr2Content.toString());
150
151 // we should get the subnet specific entry if we are from the right subnet
152 BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::AAAA), false, &retrieved, ComboAddress("192.0.2.3")), (ttd - now));
153 BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
154 BOOST_CHECK_EQUAL(getRR<AAAARecordContent>(retrieved.at(0))->getCA().toString(), dr1Content.toString());
155
156 // but nothing from a different subnet
157 BOOST_CHECK_LT(MRC.get(now, power, QType(QType::AAAA), false, &retrieved, ComboAddress("127.0.0.1")), 0);
158 BOOST_CHECK_EQUAL(retrieved.size(), 0U);
159
160 // QType::ANY should return any qtype, so from the right subnet we should get all of them
161 BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::ANY), false, &retrieved, ComboAddress("192.0.2.3")), (ttd - now));
162 BOOST_CHECK_EQUAL(retrieved.size(), 3U);
163 for (const auto& rec : retrieved) {
164 BOOST_CHECK(rec.d_type == QType::A || rec.d_type == QType::AAAA || rec.d_type == QType::TXT);
165 }
166 // check that the place is always set to ANSWER
167 for (const auto& rec : retrieved) {
168 BOOST_CHECK(rec.d_place == DNSResourceRecord::ANSWER);
169 }
170
171 // but only the non-subnet specific from the another subnet
172 BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::ANY), false, &retrieved, ComboAddress("127.0.0.1")), (ttd - now));
173 BOOST_CHECK_EQUAL(retrieved.size(), 2U);
174 for (const auto& rec : retrieved) {
175 BOOST_CHECK(rec.d_type == QType::A || rec.d_type == QType::TXT);
176 }
177
178 // QType::ADDR should return both A and AAAA but no TXT, so two entries from the right subnet
179 BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::ADDR), false, &retrieved, ComboAddress("192.0.2.3")), (ttd - now));
180 BOOST_CHECK_EQUAL(retrieved.size(), 2U);
181 bool gotA = false;
182 bool gotAAAA = false;
183 for (const auto& rec : retrieved) {
184 BOOST_CHECK(rec.d_type == QType::A || rec.d_type == QType::AAAA);
185 if (rec.d_type == QType::A) {
186 gotA = true;
187 }
188 else if (rec.d_type == QType::AAAA) {
189 gotAAAA = true;
190 }
191 }
192 BOOST_CHECK(gotA);
193 BOOST_CHECK(gotAAAA);
194
195 // but only the non-subnet specific one from the another subnet
196 BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::ADDR), false, &retrieved, ComboAddress("127.0.0.1")), (ttd - now));
197 BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
198 BOOST_CHECK(retrieved.at(0).d_type == QType::A);
199
200 // entries are only valid until ttd, we should not get anything after that because they are expired
201 BOOST_CHECK_LT(MRC.get(ttd + 5, power, QType(QType::ADDR), false, &retrieved, ComboAddress("127.0.0.1")), 0);
202 BOOST_CHECK_EQUAL(retrieved.size(), 0U);
203
204 // let's age the records for our existing QType::TXT entry so they are now only valid for 5s
205 uint32_t newTTL = 5;
206 BOOST_CHECK_EQUAL(MRC.doAgeCache(now, power, QType::TXT, newTTL), true);
207
208 // we should still be able to retrieve it
209 BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::TXT), false, &retrieved, ComboAddress("127.0.0.1")), static_cast<int32_t>(newTTL));
210 BOOST_CHECK_EQUAL(retrieved.size(), 1U);
211 BOOST_CHECK(retrieved.at(0).d_type == QType::TXT);
212 // please note that this is still a TTD at this point
213 BOOST_CHECK_EQUAL(retrieved.at(0).d_ttl, now + newTTL);
214
215 // but 10s later it should be gone
216 BOOST_CHECK_LT(MRC.get(now + 10, power, QType(QType::TXT), false, &retrieved, ComboAddress("127.0.0.1")), 0);
217 BOOST_CHECK_EQUAL(retrieved.size(), 0U);
218
219 // wipe everything
220 MRC.doWipeCache(DNSName("."), true);
221 BOOST_CHECK_EQUAL(MRC.size(), 0U);
222 records.clear();
223
224 // insert auth record
225 records.push_back(dr2);
226 MRC.replace(now, power, QType(QType::A), records, signatures, authRecords, true, boost::none);
227 BOOST_CHECK_EQUAL(MRC.size(), 1U);
228 BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("127.0.0.1")), (ttd - now));
229 BOOST_CHECK_EQUAL(retrieved.size(), 1U);
230
231 DNSRecord dr3;
232 ComboAddress dr3Content("192.0.2.84");
233 dr3.d_name = power;
234 dr3.d_type = QType::A;
235 dr3.d_class = QClass::IN;
236 dr3.d_content = std::make_shared<ARecordContent>(dr3Content);
237 dr3.d_ttl = static_cast<uint32_t>(ttd + 100);
238 // the place should not matter to the cache
239 dr3.d_place = DNSResourceRecord::AUTHORITY;
240
241 // this is important for our tests
242 BOOST_REQUIRE_GT(dr3.d_ttl, ttd);
243
244 records.clear();
245 records.push_back(dr3);
246
247 // non-auth should not replace valid auth
248 MRC.replace(now, power, QType(QType::A), records, signatures, authRecords, false, boost::none);
249 BOOST_CHECK_EQUAL(MRC.size(), 1U);
250 BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("127.0.0.1")), (ttd - now));
251 BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
252 BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr2Content.toString());
253
254 // but non-auth _should_ replace expired auth
255 MRC.replace(ttd + 1, power, QType(QType::A), records, signatures, authRecords, false, boost::none);
256 BOOST_CHECK_EQUAL(MRC.size(), 1U);
257 BOOST_CHECK_EQUAL(MRC.get(ttd + 1, power, QType(QType::A), false, &retrieved, ComboAddress("127.0.0.1")), (dr3.d_ttl - (ttd + 1)));
258 BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
259 BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr3Content.toString());
260
261 // auth should replace non-auth
262 records.clear();
263 records.push_back(dr2);
264 MRC.replace(now, power, QType(QType::A), records, signatures, authRecords, false, boost::none);
265 BOOST_CHECK_EQUAL(MRC.size(), 1U);
266 // let's first check that non-auth is not returned when we need authoritative data
267 BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), true, &retrieved, ComboAddress("127.0.0.1")), -now);
268 BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("127.0.0.1")), (ttd - now));
269 BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
270 BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr2Content.toString());
271
272 /**** Most specific netmask tests ****/
273
274 // wipe everything
275 MRC.doWipeCache(DNSName("."), true);
276 BOOST_CHECK_EQUAL(MRC.size(), 0U);
277 records.clear();
278
279 // insert an entry for 192.0.0.1/8
280 records.clear();
281 records.push_back(dr2);
282 MRC.replace(now, power, QType(QType::A), records, signatures, authRecords, true, boost::optional<Netmask>("192.0.0.1/8"));
283 BOOST_CHECK_EQUAL(MRC.size(), 1U);
284
285 /* same as dr2 except for the actual IP */
286 DNSRecord dr4;
287 ComboAddress dr4Content("192.0.2.126");
288 dr4.d_name = power;
289 dr4.d_type = QType::A;
290 dr4.d_class = QClass::IN;
291 dr4.d_content = std::make_shared<ARecordContent>(dr4Content);
292 dr4.d_ttl = static_cast<uint32_t>(ttd);
293 dr4.d_place = DNSResourceRecord::AUTHORITY;
294
295 // insert another entry but for 192.168.0.1/31
296 records.clear();
297 records.push_back(dr4);
298 MRC.replace(now, power, QType(QType::A), records, signatures, authRecords, true, boost::optional<Netmask>("192.168.0.1/31"));
299 // we should not have replaced any existing entry
300 BOOST_CHECK_EQUAL(MRC.size(), 2U);
301
302 // insert the same than the first one but for 192.168.0.2/32
303 records.clear();
304 records.push_back(dr2);
305 MRC.replace(now, power, QType(QType::A), records, signatures, authRecords, true, boost::optional<Netmask>("192.168.0.2/32"));
306 // we should not have replaced any existing entry
307 BOOST_CHECK_EQUAL(MRC.size(), 3U);
308
309 // we should get the most specific entry for 192.168.0.1, so the second one
310 BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.168.0.1")), (ttd - now));
311 BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
312 BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr4Content.toString());
313
314 // wipe everything
315 MRC.doWipeCache(DNSName("."), true);
316 BOOST_CHECK_EQUAL(MRC.size(), 0U);
317 records.clear();
318
319 // insert an entry for 192.0.0.1/8, non auth
320 records.clear();
321 records.push_back(dr2);
322 MRC.replace(now, power, QType(QType::A), records, signatures, authRecords, false, boost::optional<Netmask>("192.0.0.1/8"));
323 BOOST_CHECK_EQUAL(MRC.size(), 1U);
324
325 // we should not get it when we need authoritative data
326 BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), true, &retrieved, ComboAddress("192.168.0.1")), -1);
327 BOOST_REQUIRE_EQUAL(retrieved.size(), 0U);
328
329 // but we should when we are OK with non-auth
330 BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.168.0.1")), (ttd - now));
331 BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
332 BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr2Content.toString());
333 }
334 catch (const PDNSException& e) {
335 cerr << "Had error: " << e.reason << endl;
336 throw;
337 }
338 }
339
340 BOOST_AUTO_TEST_CASE(test_RecursorCacheGhost)
341 {
342 MemRecursorCache MRC;
343
344 std::vector<DNSRecord> records;
345 std::vector<std::shared_ptr<DNSRecord>> authRecords;
346 std::vector<std::shared_ptr<RRSIGRecordContent>> signatures;
347 time_t now = time(nullptr);
348
349 BOOST_CHECK_EQUAL(MRC.size(), 0U);
350
351 /* insert NS coming from a delegation */
352 time_t ttd = now + 30;
353 DNSName ghost("ghost.powerdns.com.");
354 DNSRecord ns1;
355 std::string ns1Content("ns1.ghost.powerdns.com.");
356 ns1.d_name = ghost;
357 ns1.d_type = QType::NS;
358 ns1.d_class = QClass::IN;
359 ns1.d_content = std::make_shared<NSRecordContent>(ns1Content);
360 ns1.d_ttl = static_cast<uint32_t>(ttd);
361 ns1.d_place = DNSResourceRecord::ANSWER;
362 records.push_back(ns1);
363 MRC.replace(now, ns1.d_name, QType(ns1.d_type), records, signatures, authRecords, true, boost::none);
364 BOOST_CHECK_EQUAL(MRC.size(), 1U);
365
366 /* try to raise the TTL, simulating the delegated authoritative server
367 raising the TTL so the zone stays alive */
368 records.clear();
369 ns1.d_ttl = static_cast<uint32_t>(ttd + 3600);
370 records.push_back(ns1);
371 MRC.replace(now, ns1.d_name, QType(ns1.d_type), records, signatures, authRecords, true, boost::none);
372 BOOST_CHECK_EQUAL(MRC.size(), 1U);
373
374 /* the TTL should not have been raisd */
375 std::vector<DNSRecord> retrieved;
376 BOOST_CHECK_EQUAL(MRC.get(now, ghost, QType(QType::NS), false, &retrieved, ComboAddress("192.0.2.2")), (ttd - now));
377 BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
378 BOOST_CHECK_EQUAL(retrieved.at(0).d_ttl, static_cast<uint32_t>(ttd));
379 }
380
381 BOOST_AUTO_TEST_CASE(test_RecursorCache_ExpungingExpiredEntries)
382 {
383 MemRecursorCache MRC(1);
384
385 std::vector<DNSRecord> records;
386 std::vector<std::shared_ptr<RRSIGRecordContent>> signatures;
387 std::vector<std::shared_ptr<DNSRecord>> authRecs;
388 BOOST_CHECK_EQUAL(MRC.size(), 0U);
389 time_t now = time(nullptr);
390 DNSName power1("powerdns.com.");
391 DNSName power2("powerdns-1.com.");
392 time_t ttd = now - 30;
393 std::vector<DNSRecord> retrieved;
394 ComboAddress who("192.0.2.1");
395
396 /* entry for power, which expired 30s ago */
397 DNSRecord dr1;
398 ComboAddress dr1Content("2001:DB8::1");
399 dr1.d_name = power1;
400 dr1.d_type = QType::AAAA;
401 dr1.d_class = QClass::IN;
402 dr1.d_content = std::make_shared<AAAARecordContent>(dr1Content);
403 dr1.d_ttl = static_cast<uint32_t>(ttd);
404 dr1.d_place = DNSResourceRecord::ANSWER;
405
406 /* entry for power1, which expired 30 ago too */
407 DNSRecord dr2;
408 ComboAddress dr2Content("2001:DB8::2");
409 dr2.d_name = power2;
410 dr2.d_type = QType::AAAA;
411 dr2.d_class = QClass::IN;
412 dr2.d_content = std::make_shared<AAAARecordContent>(dr2Content);
413 dr2.d_ttl = static_cast<uint32_t>(ttd);
414 dr2.d_place = DNSResourceRecord::ANSWER;
415
416 /* insert both entries */
417 records.push_back(dr1);
418 MRC.replace(now, power1, QType(dr1.d_type), records, signatures, authRecs, true, boost::none);
419 records.clear();
420 records.push_back(dr2);
421 MRC.replace(now, power2, QType(dr2.d_type), records, signatures, authRecs, true, boost::none);
422 records.clear();
423 BOOST_CHECK_EQUAL(MRC.size(), 2U);
424
425 /* the one for power2 having been inserted
426 more recently should be removed last */
427 /* we ask that only entry remains in the cache */
428 MRC.doPrune(1);
429 BOOST_CHECK_EQUAL(MRC.size(), 1U);
430
431 /* the remaining entry should be power2, but to get it
432 we need to go back in the past a bit */
433 BOOST_CHECK_EQUAL(MRC.get(ttd - 1, power2, QType(dr2.d_type), false, &retrieved, who, boost::none, nullptr), 1);
434 BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
435 BOOST_CHECK_EQUAL(getRR<AAAARecordContent>(retrieved.at(0))->getCA().toString(), dr2Content.toString());
436 /* check that power1 is gone */
437 BOOST_CHECK_EQUAL(MRC.get(ttd - 1, power1, QType(dr1.d_type), false, &retrieved, who, boost::none, nullptr), -1);
438
439 /* clear everything up */
440 MRC.doWipeCache(DNSName("."), true);
441 BOOST_CHECK_EQUAL(MRC.size(), 0U);
442 records.clear();
443
444 /* insert both entries back */
445 records.push_back(dr1);
446 MRC.replace(now, power1, QType(dr1.d_type), records, signatures, authRecs, true, boost::none);
447 records.clear();
448 records.push_back(dr2);
449 MRC.replace(now, power2, QType(dr2.d_type), records, signatures, authRecs, true, boost::none);
450 records.clear();
451 BOOST_CHECK_EQUAL(MRC.size(), 2U);
452
453 /* trigger a miss (expired) for power2 */
454 BOOST_CHECK_EQUAL(MRC.get(now, power2, QType(dr2.d_type), false, &retrieved, who, boost::none, nullptr), -now);
455
456 /* power2 should have been moved to the front of the expunge
457 queue, and should this time be removed first */
458 /* we ask that only entry remains in the cache */
459 MRC.doPrune(1);
460 BOOST_CHECK_EQUAL(MRC.size(), 1U);
461
462 /* the remaining entry should be power1, but to get it
463 we need to go back in the past a bit */
464 BOOST_CHECK_EQUAL(MRC.get(ttd - 1, power1, QType(dr1.d_type), false, &retrieved, who, boost::none, nullptr), 1);
465 BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
466 BOOST_CHECK_EQUAL(getRR<AAAARecordContent>(retrieved.at(0))->getCA().toString(), dr1Content.toString());
467 /* check that power2 is gone */
468 BOOST_CHECK_EQUAL(MRC.get(ttd - 1, power2, QType(dr2.d_type), false, &retrieved, who, boost::none, nullptr), -1);
469 }
470
471 BOOST_AUTO_TEST_CASE(test_RecursorCache_ExpungingValidEntries)
472 {
473 MemRecursorCache MRC(1);
474
475 std::vector<DNSRecord> records;
476 std::vector<std::shared_ptr<RRSIGRecordContent>> signatures;
477 std::vector<std::shared_ptr<DNSRecord>> authRecs;
478 BOOST_CHECK_EQUAL(MRC.size(), 0U);
479 time_t now = time(nullptr);
480 DNSName power1("powerdns.com.");
481 DNSName power2("powerdns-1.com.");
482 time_t ttd = now + 30;
483 std::vector<DNSRecord> retrieved;
484 ComboAddress who("192.0.2.1");
485
486 /* entry for power, which will expire in 30s */
487 DNSRecord dr1;
488 ComboAddress dr1Content("2001:DB8::1");
489 dr1.d_name = power1;
490 dr1.d_type = QType::AAAA;
491 dr1.d_class = QClass::IN;
492 dr1.d_content = std::make_shared<AAAARecordContent>(dr1Content);
493 dr1.d_ttl = static_cast<uint32_t>(ttd);
494 dr1.d_place = DNSResourceRecord::ANSWER;
495
496 /* entry for power1, which will expire in 30s too */
497 DNSRecord dr2;
498 ComboAddress dr2Content("2001:DB8::2");
499 dr2.d_name = power2;
500 dr2.d_type = QType::AAAA;
501 dr2.d_class = QClass::IN;
502 dr2.d_content = std::make_shared<AAAARecordContent>(dr2Content);
503 dr2.d_ttl = static_cast<uint32_t>(ttd);
504 dr2.d_place = DNSResourceRecord::ANSWER;
505
506 /* insert both entries */
507 records.push_back(dr1);
508 MRC.replace(now, power1, QType(dr1.d_type), records, signatures, authRecs, true, boost::none);
509 records.clear();
510 records.push_back(dr2);
511 MRC.replace(now, power2, QType(dr2.d_type), records, signatures, authRecs, true, boost::none);
512 records.clear();
513 BOOST_CHECK_EQUAL(MRC.size(), 2U);
514
515 /* the one for power2 having been inserted
516 more recently should be removed last */
517 /* we ask that only entry remains in the cache */
518 MRC.doPrune(1);
519 BOOST_CHECK_EQUAL(MRC.size(), 1U);
520
521 /* the remaining entry should be power2 */
522 BOOST_CHECK_EQUAL(MRC.get(now, power2, QType(dr2.d_type), false, &retrieved, who, boost::none, nullptr), ttd - now);
523 BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
524 BOOST_CHECK_EQUAL(getRR<AAAARecordContent>(retrieved.at(0))->getCA().toString(), dr2Content.toString());
525 /* check that power1 is gone */
526 BOOST_CHECK_EQUAL(MRC.get(now, power1, QType(dr1.d_type), false, &retrieved, who, boost::none, nullptr), -1);
527
528 /* clear everything up */
529 MRC.doWipeCache(DNSName("."), true);
530 BOOST_CHECK_EQUAL(MRC.size(), 0U);
531 records.clear();
532
533 /* insert both entries back */
534 records.push_back(dr1);
535 MRC.replace(now, power1, QType(dr1.d_type), records, signatures, authRecs, true, boost::none);
536 records.clear();
537 records.push_back(dr2);
538 MRC.replace(now, power2, QType(dr2.d_type), records, signatures, authRecs, true, boost::none);
539 records.clear();
540 BOOST_CHECK_EQUAL(MRC.size(), 2U);
541
542 /* replace the entry for power1 */
543 records.push_back(dr1);
544 MRC.replace(now, power1, QType(dr1.d_type), records, signatures, authRecs, true, boost::none);
545 records.clear();
546 BOOST_CHECK_EQUAL(MRC.size(), 2U);
547
548 /* the replaced entry for power1 should have been moved
549 to the back of the expunge queue, so power2 should be at the front
550 and should this time be removed first */
551 /* we ask that only entry remains in the cache */
552 MRC.doPrune(1);
553 BOOST_CHECK_EQUAL(MRC.size(), 1U);
554
555 /* the remaining entry should be power1 */
556 BOOST_CHECK_EQUAL(MRC.get(now, power1, QType(dr1.d_type), false, &retrieved, who, boost::none, nullptr), ttd - now);
557 BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
558 BOOST_CHECK_EQUAL(getRR<AAAARecordContent>(retrieved.at(0))->getCA().toString(), dr1Content.toString());
559 /* check that power2 is gone */
560 BOOST_CHECK_EQUAL(MRC.get(now, power2, QType(dr2.d_type), false, &retrieved, who, boost::none, nullptr), -1);
561
562 /* clear everything up */
563 MRC.doWipeCache(DNSName("."), true);
564 BOOST_CHECK_EQUAL(MRC.size(), 0U);
565 records.clear();
566
567 /* insert both entries back */
568 records.push_back(dr1);
569 MRC.replace(now, power1, QType(dr1.d_type), records, signatures, authRecs, true, boost::none);
570 records.clear();
571 records.push_back(dr2);
572 MRC.replace(now, power2, QType(dr2.d_type), records, signatures, authRecs, true, boost::none);
573 records.clear();
574 BOOST_CHECK_EQUAL(MRC.size(), 2U);
575
576 /* get a hit for power1 */
577 BOOST_CHECK_EQUAL(MRC.get(now, power1, QType(dr1.d_type), false, &retrieved, who, boost::none, nullptr), ttd - now);
578 BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
579 BOOST_CHECK_EQUAL(getRR<AAAARecordContent>(retrieved.at(0))->getCA().toString(), dr1Content.toString());
580
581 /* the entry for power1 should have been moved to the back of the expunge queue
582 due to the hit, so power2 should be at the front and should this time be removed first */
583 /* we ask that only entry remains in the cache */
584 MRC.doPrune(1);
585 BOOST_CHECK_EQUAL(MRC.size(), 1U);
586
587 /* the remaining entry should be power1 */
588 BOOST_CHECK_EQUAL(MRC.get(now, power1, QType(dr1.d_type), false, &retrieved, who, boost::none, nullptr), ttd - now);
589 BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
590 BOOST_CHECK_EQUAL(getRR<AAAARecordContent>(retrieved.at(0))->getCA().toString(), dr1Content.toString());
591 /* check that power2 is gone */
592 BOOST_CHECK_EQUAL(MRC.get(now, power2, QType(dr2.d_type), false, &retrieved, who, boost::none, nullptr), -1);
593
594 MRC.doPrune(0);
595 BOOST_CHECK_EQUAL(MRC.size(), 0U);
596
597 /* add a lot of netmask-specific entries */
598 for (size_t i = 0; i <= 255; i++) {
599 records.clear();
600
601 DNSRecord r1;
602 ComboAddress r1Content("192.0.2." + std::to_string(i));
603 r1.d_name = power1;
604 r1.d_type = QType::A;
605 r1.d_class = QClass::IN;
606 r1.d_content = std::make_shared<ARecordContent>(r1Content);
607 r1.d_ttl = static_cast<uint32_t>(ttd);
608 r1.d_place = DNSResourceRecord::ANSWER;
609 records.push_back(r1);
610
611 MRC.replace(now, power1, QType(QType::A), records, signatures, authRecs, true, Netmask(r1Content, 32));
612 }
613
614 BOOST_CHECK_EQUAL(MRC.size(), 256U);
615 BOOST_CHECK_EQUAL(MRC.ecsIndexSize(), 1U);
616
617 /* remove a bit less than half of them */
618 size_t keep = 129;
619 MRC.doPrune(keep);
620 BOOST_CHECK_EQUAL(MRC.size(), keep);
621 BOOST_CHECK_EQUAL(MRC.ecsIndexSize(), 1U);
622
623 /* check that we can still retrieve the remaining ones */
624 size_t found = 0;
625 for (size_t i = 0; i <= 255; i++) {
626 ComboAddress whoLoop("192.0.2." + std::to_string(i));
627
628 auto ret = MRC.get(now, power1, QType(QType::A), false, &retrieved, whoLoop);
629 if (ret > 0) {
630 BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
631 BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), whoLoop.toString());
632 found++;
633 }
634 else {
635 BOOST_REQUIRE_EQUAL(ret, -1);
636 BOOST_REQUIRE_EQUAL(retrieved.size(), 0U);
637 }
638 }
639
640 BOOST_CHECK_EQUAL(found, keep);
641
642 /* remove the rest */
643 MRC.doPrune(0);
644 BOOST_CHECK_EQUAL(MRC.size(), 0U);
645 BOOST_CHECK_EQUAL(MRC.ecsIndexSize(), 0U);
646 }
647
648 BOOST_AUTO_TEST_CASE(test_RecursorCacheECSIndex)
649 {
650 MemRecursorCache MRC(1);
651
652 const DNSName power("powerdns.com.");
653 std::vector<DNSRecord> records;
654 std::vector<std::shared_ptr<DNSRecord>> authRecords;
655 std::vector<std::shared_ptr<RRSIGRecordContent>> signatures;
656 time_t now = time(nullptr);
657 std::vector<DNSRecord> retrieved;
658 ComboAddress who("192.0.2.1");
659
660 time_t ttl = 10;
661 time_t ttd = now + ttl;
662 DNSRecord dr1;
663 ComboAddress dr1Content("192.0.2.255");
664 dr1.d_name = power;
665 dr1.d_type = QType::A;
666 dr1.d_class = QClass::IN;
667 dr1.d_content = std::make_shared<ARecordContent>(dr1Content);
668 dr1.d_ttl = static_cast<uint32_t>(ttd);
669 dr1.d_place = DNSResourceRecord::ANSWER;
670
671 DNSRecord dr2;
672 ComboAddress dr2Content("192.0.2.127");
673 dr2.d_name = power;
674 dr2.d_type = QType::A;
675 dr2.d_class = QClass::IN;
676 dr2.d_content = std::make_shared<ARecordContent>(dr2Content);
677 dr2.d_ttl = static_cast<uint32_t>(now + 5);
678 dr2.d_place = DNSResourceRecord::ANSWER;
679
680 BOOST_CHECK_EQUAL(MRC.size(), 0U);
681 BOOST_CHECK_EQUAL(MRC.ecsIndexSize(), 0U);
682
683 /* no entry in the ECS index, no non-specific entry either */
684 BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, who), -1);
685
686 /* insert a non-specific entry */
687 records.push_back(dr1);
688 MRC.replace(now, power, QType(QType::A), records, signatures, authRecords, true, boost::none);
689
690 BOOST_CHECK_EQUAL(MRC.size(), 1U);
691 BOOST_CHECK_EQUAL(MRC.ecsIndexSize(), 0U);
692
693 /* retrieve the non-specific entry */
694 BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, who), ttd - now);
695 BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
696 BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr1Content.toString());
697
698 /* wipe everything */
699 MRC.doPrune(0);
700 BOOST_CHECK_EQUAL(MRC.size(), 0U);
701 BOOST_CHECK_EQUAL(MRC.ecsIndexSize(), 0U);
702
703 /* insert a specific entry */
704 MRC.replace(now, power, QType(QType::A), records, signatures, authRecords, true, Netmask("192.0.2.0/31"));
705
706 BOOST_CHECK_EQUAL(MRC.size(), 1U);
707 BOOST_CHECK_EQUAL(MRC.ecsIndexSize(), 1U);
708
709 /* there is an ECS index for that entry but no match, and no non-specific entry */
710 BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.0.2.4")), -1);
711 BOOST_REQUIRE_EQUAL(retrieved.size(), 0U);
712
713 /* there is an ECS index for that entry and we get a match */
714 BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.0.2.1")), ttd - now);
715 BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
716 BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr1Content.toString());
717
718 /* there is an ECS index for that entry and we get a match,
719 but it has expired. No other match, no non-specific entry */
720 BOOST_CHECK_EQUAL(MRC.get(now + ttl + 1, power, QType(QType::A), false, &retrieved, ComboAddress("192.0.2.1")), -1);
721 BOOST_REQUIRE_EQUAL(retrieved.size(), 0U);
722
723 /* The ECS index should now be empty, but the cache entry has not been expunged yet */
724 BOOST_CHECK_EQUAL(MRC.ecsIndexSize(), 0U);
725 BOOST_CHECK_EQUAL(MRC.size(), 1U);
726
727 /* add back the entry while it still exists in the cache but has been removed from the ECS index.
728 It should be added back to the ECS index, and we should be able to retrieve it */
729 MRC.replace(now + ttl + 1, power, QType(QType::A), records, signatures, authRecords, true, Netmask("192.0.2.0/31"));
730 BOOST_CHECK_EQUAL(MRC.size(), 1U);
731 BOOST_CHECK_EQUAL(MRC.ecsIndexSize(), 1U);
732 BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.0.2.1")), ttd - now);
733 BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
734 BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr1Content.toString());
735
736 /* wipe everything */
737 MRC.doPrune(0);
738 BOOST_CHECK_EQUAL(MRC.size(), 0U);
739 BOOST_CHECK_EQUAL(MRC.ecsIndexSize(), 0U);
740
741 /* insert a specific entry */
742 MRC.replace(now, power, QType(QType::A), records, signatures, authRecords, true, Netmask("192.0.2.0/24"));
743
744 BOOST_CHECK_EQUAL(MRC.size(), 1U);
745 BOOST_CHECK_EQUAL(MRC.ecsIndexSize(), 1U);
746
747 /* insert a slightly more specific one, but expiring sooner */
748 records.clear();
749 records.push_back(dr2);
750 MRC.replace(now, power, QType(QType::A), records, signatures, authRecords, true, Netmask("192.0.2.0/26"));
751
752 BOOST_CHECK_EQUAL(MRC.size(), 2U);
753 BOOST_CHECK_EQUAL(MRC.ecsIndexSize(), 1U);
754
755 /* check that we get the most specific one as long as it's still valid */
756 BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.0.2.1")), 5);
757 BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
758 BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr2Content.toString());
759
760 /* there is an ECS index for that entry and we get a match,
761 but it has expired.
762 The second ECS is a match too, and is valid. */
763 BOOST_CHECK_EQUAL(MRC.get(now + 5 + 1, power, QType(QType::A), false, &retrieved, ComboAddress("192.0.2.1")), (ttd - (now + 5 + 1)));
764 BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
765 BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr1Content.toString());
766
767 /* The ECS index should not be empty */
768 BOOST_CHECK_EQUAL(MRC.ecsIndexSize(), 1U);
769 BOOST_CHECK_EQUAL(MRC.size(), 2U);
770
771 /* wipe everything */
772 MRC.doPrune(0);
773 BOOST_CHECK_EQUAL(MRC.size(), 0U);
774 BOOST_CHECK_EQUAL(MRC.ecsIndexSize(), 0U);
775
776 /* insert a non-specific entry */
777 records.clear();
778 records.push_back(dr1);
779 MRC.replace(now, power, QType(QType::A), records, signatures, authRecords, true, boost::none);
780
781 BOOST_CHECK_EQUAL(MRC.size(), 1U);
782 BOOST_CHECK_EQUAL(MRC.ecsIndexSize(), 0U);
783
784 /* insert a subnet-specific entry */
785 records.clear();
786 records.push_back(dr2);
787 MRC.replace(now, power, QType(QType::A), records, signatures, authRecords, true, Netmask("192.0.2.42/32"));
788
789 BOOST_CHECK_EQUAL(MRC.size(), 2U);
790 BOOST_CHECK_EQUAL(MRC.ecsIndexSize(), 1U);
791
792 /* there is an ECS index for that entry and it doesn't match. No other match, but we have a non-specific entry */
793 BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.0.2.255")), ttd - now);
794 BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
795 BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr1Content.toString());
796
797 BOOST_CHECK_EQUAL(MRC.size(), 2U);
798 BOOST_CHECK_EQUAL(MRC.ecsIndexSize(), 1U);
799
800 /* wipe everything */
801 MRC.doPrune(0);
802 BOOST_CHECK_EQUAL(MRC.size(), 0U);
803 BOOST_CHECK_EQUAL(MRC.ecsIndexSize(), 0U);
804 }
805
806 BOOST_AUTO_TEST_CASE(test_RecursorCache_Wipe)
807 {
808 MemRecursorCache MRC;
809
810 const DNSName power("powerdns.com.");
811 std::vector<DNSRecord> records;
812 std::vector<std::shared_ptr<DNSRecord>> authRecords;
813 std::vector<std::shared_ptr<RRSIGRecordContent>> signatures;
814 time_t now = time(nullptr);
815 std::vector<DNSRecord> retrieved;
816 ComboAddress who("192.0.2.1");
817
818 time_t ttl = 10;
819 time_t ttd = now + ttl;
820 DNSRecord dr1;
821 ComboAddress dr1Content("192.0.2.255");
822 dr1.d_name = power;
823 dr1.d_type = QType::A;
824 dr1.d_class = QClass::IN;
825 dr1.d_content = std::make_shared<ARecordContent>(dr1Content);
826 dr1.d_ttl = static_cast<uint32_t>(ttd);
827 dr1.d_place = DNSResourceRecord::ANSWER;
828
829 BOOST_CHECK_EQUAL(MRC.size(), 0U);
830 BOOST_CHECK_EQUAL(MRC.ecsIndexSize(), 0U);
831
832 /* no entry in the ECS index, no non-specific entry either */
833 BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, who), -1);
834
835 /* insert a specific entry */
836 records.push_back(dr1);
837 MRC.replace(now, power, QType(QType::A), records, signatures, authRecords, true, Netmask("192.0.2.0/31"));
838
839 BOOST_CHECK_EQUAL(MRC.size(), 1U);
840 BOOST_CHECK_EQUAL(MRC.ecsIndexSize(), 1U);
841
842 /* insert two sub-domains entries */
843 DNSName sub1("a.powerdns.com.");
844 dr1.d_name = sub1;
845 records.clear();
846 records.push_back(dr1);
847 MRC.replace(now, sub1, QType(QType::A), records, signatures, authRecords, true, Netmask("192.0.2.0/31"));
848
849 BOOST_CHECK_EQUAL(MRC.size(), 2U);
850 BOOST_CHECK_EQUAL(MRC.ecsIndexSize(), 2U);
851
852 DNSName sub2("z.powerdns.com.");
853 dr1.d_name = sub2;
854 records.clear();
855 records.push_back(dr1);
856 MRC.replace(now, sub2, QType(QType::A), records, signatures, authRecords, true, Netmask("192.0.2.0/31"));
857
858 BOOST_CHECK_EQUAL(MRC.size(), 3U);
859 BOOST_CHECK_EQUAL(MRC.ecsIndexSize(), 3U);
860
861 /* insert two entries for different domains */
862 DNSName other1("b\bpowerdns.com.");
863 dr1.d_name = other1;
864 records.clear();
865 records.push_back(dr1);
866 MRC.replace(now, other1, QType(QType::A), records, signatures, authRecords, true, Netmask("192.0.2.0/31"));
867
868 BOOST_CHECK_EQUAL(MRC.size(), 4U);
869 BOOST_CHECK_EQUAL(MRC.ecsIndexSize(), 4U);
870
871 DNSName other2("c\bpowerdns.com.");
872 dr1.d_name = other2;
873 records.clear();
874 records.push_back(dr1);
875 MRC.replace(now, other2, QType(QType::A), records, signatures, authRecords, true, Netmask("192.0.2.0/31"));
876
877 BOOST_CHECK_EQUAL(MRC.size(), 5U);
878 BOOST_CHECK_EQUAL(MRC.ecsIndexSize(), 5U);
879
880 /* wipe everything under the powerdns.com domain */
881 BOOST_CHECK_EQUAL(MRC.doWipeCache(power, true), 3U);
882 BOOST_CHECK_EQUAL(MRC.size(), 2U);
883 BOOST_CHECK_EQUAL(MRC.ecsIndexSize(), 2U);
884
885 /* now wipe the other domains too */
886 BOOST_CHECK_EQUAL(MRC.doWipeCache(other1, true), 1U);
887 BOOST_CHECK_EQUAL(MRC.size(), 1U);
888 BOOST_CHECK_EQUAL(MRC.ecsIndexSize(), 1U);
889
890 BOOST_CHECK_EQUAL(MRC.doWipeCache(other2, true), 1U);
891 BOOST_CHECK_EQUAL(MRC.size(), 0U);
892 BOOST_CHECK_EQUAL(MRC.ecsIndexSize(), 0U);
893 }
894
895 BOOST_AUTO_TEST_CASE(test_RecursorCacheTagged)
896 {
897 MemRecursorCache MRC;
898
899 std::vector<std::shared_ptr<DNSRecord>> authRecords;
900 std::vector<std::shared_ptr<RRSIGRecordContent>> signatures;
901 time_t now = time(nullptr);
902 time_t ttd = now + 30;
903
904 DNSName power("powerdns.com.");
905 DNSRecord dr0;
906 ComboAddress dr0Content("192.0.2.40");
907 dr0.d_name = power;
908 dr0.d_type = QType::A;
909 dr0.d_class = QClass::IN;
910 dr0.d_content = std::make_shared<ARecordContent>(dr0Content);
911 dr0.d_ttl = static_cast<uint32_t>(ttd);
912 dr0.d_place = DNSResourceRecord::ANSWER;
913 std::vector<DNSRecord> rset0;
914 rset0.push_back(dr0);
915
916 DNSRecord dr0tagged;
917 ComboAddress dr0taggedContent("192.0.2.100");
918 dr0tagged.d_name = power;
919 dr0tagged.d_type = QType::A;
920 dr0tagged.d_class = QClass::IN;
921 dr0tagged.d_content = std::make_shared<ARecordContent>(dr0taggedContent);
922 dr0tagged.d_ttl = static_cast<uint32_t>(ttd);
923 dr0tagged.d_place = DNSResourceRecord::ANSWER;
924 std::vector<DNSRecord> rset0tagged;
925 rset0tagged.push_back(dr0tagged);
926
927 BOOST_CHECK_EQUAL(MRC.size(), 0U);
928 // An entry without edns subnet gets stored without tag as well
929 MRC.replace(ttd, DNSName("hello"), QType(QType::A), rset0, signatures, authRecords, true, boost::none, boost::none);
930 MRC.replace(ttd, DNSName("hello"), QType(QType::A), rset0, signatures, authRecords, true, boost::none, string("mytag"));
931 BOOST_CHECK_EQUAL(MRC.size(), 1U);
932 BOOST_CHECK_EQUAL(MRC.doWipeCache(DNSName("hello"), false, QType::A), 1U);
933 BOOST_CHECK_EQUAL(MRC.size(), 0U);
934 BOOST_CHECK_EQUAL(MRC.bytes(), 0U);
935
936 ComboAddress nobody;
937 ComboAddress who("192.0.2.1");
938
939 uint64_t counter = 0;
940 try {
941 for (counter = 0; counter < 100; ++counter) {
942 DNSName a = DNSName("hello ") + DNSName(std::to_string(counter));
943 BOOST_CHECK_EQUAL(DNSName(a.toString()), a);
944
945 MRC.replace(now, a, QType(QType::A), rset0, signatures, authRecords, true, boost::none, string("mytagA"));
946 MRC.replace(now, a, QType(QType::A), rset0, signatures, authRecords, true, boost::none, string("mytagB"));
947 // After this, we have untagged entries, since no address was specified for both replace calls
948 }
949
950 BOOST_CHECK_EQUAL(MRC.size(), counter);
951
952 std::vector<DNSRecord> retrieved;
953 int64_t matches = 0;
954 int64_t expected = counter;
955
956 for (counter = 0; counter < 110; counter++) {
957 if (MRC.get(now, DNSName("hello ") + DNSName(std::to_string(counter)), QType(QType::A), false, &retrieved, nobody, boost::none) > 0) {
958 matches++;
959 BOOST_CHECK_EQUAL(retrieved.size(), rset0.size());
960 BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr0Content.toString());
961 }
962 }
963 BOOST_CHECK_EQUAL(matches, expected);
964
965 matches = 0;
966 for (counter = 0; counter < 110; ++counter) {
967 if (MRC.get(now, DNSName("hello ") + DNSName(std::to_string(counter)), QType(QType::A), false, &retrieved, who, string("mytagB")) > 0) {
968 matches++;
969 BOOST_CHECK_EQUAL(retrieved.size(), rset0.size());
970 BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr0Content.toString());
971 }
972 }
973 BOOST_CHECK_EQUAL(matches, expected);
974
975 matches = 0;
976 for (counter = 0; counter < 110; counter++) {
977 if (MRC.get(now, DNSName("hello ") + DNSName(std::to_string(counter)), QType(QType::A), false, &retrieved, who, string("mytagX")) > 0) {
978 matches++;
979 BOOST_CHECK_EQUAL(retrieved.size(), rset0.size());
980 BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr0Content.toString());
981 }
982 }
983 BOOST_CHECK_EQUAL(matches, expected);
984
985 // Now insert some tagged entries
986 for (counter = 0; counter < 50; ++counter) {
987 DNSName a = DNSName("hello ") + DNSName(std::to_string(counter));
988 MRC.replace(now, a, QType(QType::A), rset0tagged, signatures, authRecords, true, boost::optional<Netmask>("128.0.0.0/8"), string("mytagA"));
989 }
990 BOOST_CHECK_EQUAL(MRC.size(), 150U);
991
992 matches = 0;
993 for (counter = 0; counter < 110; counter++) {
994 if (MRC.get(now, DNSName("hello ") + DNSName(std::to_string(counter)), QType(QType::A), false, &retrieved, nobody, boost::none) > 0) {
995 matches++;
996 BOOST_CHECK_EQUAL(retrieved.size(), rset0.size());
997 BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr0Content.toString());
998 }
999 }
1000 BOOST_CHECK_EQUAL(matches, 100U);
1001
1002 matches = 0;
1003 for (counter = 0; counter < 110; ++counter) {
1004 if (MRC.get(now, DNSName("hello ") + DNSName(std::to_string(counter)), QType(QType::A), false, &retrieved, nobody, string("mytagA")) > 0) {
1005 matches++;
1006 if (counter < 50) {
1007 BOOST_CHECK_EQUAL(retrieved.size(), rset0tagged.size());
1008 BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr0taggedContent.toString());
1009 }
1010 else {
1011 BOOST_CHECK_EQUAL(retrieved.size(), rset0.size());
1012 BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr0Content.toString());
1013 }
1014 }
1015 }
1016 BOOST_CHECK_EQUAL(matches, 100U);
1017
1018 matches = 0;
1019 for (counter = 0; counter < 110; counter++) {
1020 if (MRC.get(now, DNSName("hello ") + DNSName(std::to_string(counter)), QType(QType::A), false, &retrieved, nobody, string("mytagX")) > 0) {
1021 matches++;
1022 BOOST_CHECK_EQUAL(retrieved.size(), rset0.size());
1023 BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr0Content.toString());
1024 }
1025 }
1026 BOOST_CHECK_EQUAL(matches, 100U);
1027
1028 MRC.doWipeCache(DNSName("."), true);
1029 BOOST_CHECK_EQUAL(MRC.size(), 0U);
1030
1031 DNSRecord dr1;
1032 ComboAddress dr1Content("192.0.2.41");
1033 dr1.d_name = power;
1034 dr1.d_type = QType::A;
1035 dr1.d_class = QClass::IN;
1036 dr1.d_content = std::make_shared<ARecordContent>(dr1Content);
1037 dr1.d_ttl = static_cast<uint32_t>(ttd);
1038 dr1.d_place = DNSResourceRecord::ANSWER;
1039 std::vector<DNSRecord> rset1;
1040 rset1.push_back(dr1);
1041
1042 DNSRecord dr2;
1043 ComboAddress dr2Content("192.0.2.42");
1044 dr2.d_name = power;
1045 dr2.d_type = QType::A;
1046 dr2.d_class = QClass::IN;
1047 dr2.d_content = std::make_shared<ARecordContent>(dr2Content);
1048 dr2.d_ttl = static_cast<uint32_t>(ttd);
1049 dr2.d_place = DNSResourceRecord::ANSWER;
1050 std::vector<DNSRecord> rset2;
1051 rset2.push_back(dr2);
1052
1053 DNSRecord dr3;
1054 ComboAddress dr3Content("192.0.2.43");
1055 dr3.d_name = power;
1056 dr3.d_type = QType::A;
1057 dr3.d_class = QClass::IN;
1058 dr3.d_content = std::make_shared<ARecordContent>(dr3Content);
1059 dr3.d_ttl = static_cast<uint32_t>(ttd);
1060 dr3.d_place = DNSResourceRecord::ANSWER;
1061 std::vector<DNSRecord> rset3;
1062 rset3.push_back(dr3);
1063
1064 // insert a tagged entry
1065 MRC.replace(now, power, QType(QType::A), rset1, signatures, authRecords, true, boost::optional<Netmask>("192.0.2.0/24"), string("mytag"));
1066 BOOST_CHECK_EQUAL(MRC.size(), 1U);
1067
1068 // tagged specific should be returned for a matching tag
1069 BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.0.2.2"), string("mytag")), (ttd - now));
1070 BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
1071 BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr1Content.toString());
1072
1073 // tag specific should not be returned for a different tag
1074 BOOST_CHECK_LT(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.0.2.2"), string("othertag")), 0);
1075 BOOST_CHECK_EQUAL(retrieved.size(), 0U);
1076
1077 // insert a new entry without tag
1078 MRC.replace(now, power, QType(QType::A), rset2, signatures, authRecords, true, boost::optional<Netmask>("192.0.3.0/24"), boost::none);
1079 BOOST_CHECK_EQUAL(MRC.size(), 2U);
1080
1081 // tagged specific should be returned for a matching tag
1082 BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.0.2.2"), string("mytag")), (ttd - now));
1083 BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
1084 BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr1Content.toString());
1085
1086 // if no tag given nothing should be retrieved if address doesn't match
1087 BOOST_CHECK_LT(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("127.0.0.1"), boost::none), 0);
1088 BOOST_REQUIRE_EQUAL(retrieved.size(), 0U);
1089
1090 // if no tag given and no-non-tagged entries matches nothing shoudl be returned
1091 BOOST_CHECK_LT(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.0.2.2"), boost::none), 0);
1092 BOOST_REQUIRE_EQUAL(retrieved.size(), 0U);
1093
1094 // Insert untagged entry with no netmask
1095 MRC.replace(now, power, QType(QType::A), rset3, signatures, authRecords, true, boost::none, boost::none);
1096 BOOST_CHECK_EQUAL(MRC.size(), 3U);
1097
1098 // Retrieval with no address and no tag should get that one
1099 BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress(), boost::none), (ttd - now));
1100 BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr3Content.toString());
1101
1102 // If no tag given match non-tagged entry
1103 BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.0.2.2"), boost::none), (ttd - now));
1104 BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
1105 BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr3Content.toString());
1106
1107 // If no tag given we should be able to retrieve the netmask specific record
1108 BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.0.3.1"), boost::none), (ttd - now));
1109 BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
1110 BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr2Content.toString());
1111
1112 // tagged specific should still be returned for a matching tag, address is not used
1113 BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.0.2.2"), string("mytag")), (ttd - now));
1114 BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
1115 BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr1Content.toString());
1116
1117 // remove everything
1118 MRC.doWipeCache(DNSName("."), true);
1119 BOOST_CHECK_EQUAL(MRC.size(), 0U);
1120 }
1121 catch (const PDNSException& e) {
1122 cerr << "Had error: " << e.reason << endl;
1123 throw;
1124 }
1125 }
1126
1127 BOOST_AUTO_TEST_SUITE_END()