]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/test-dnsdistpacketcache_cc.cc
dnsdist: Delint test-dnsdistpacketcache_cc.cc a bit more
[thirdparty/pdns.git] / pdns / test-dnsdistpacketcache_cc.cc
CommitLineData
1c2d079d 1#ifndef BOOST_TEST_DYN_LINK
886e2cf2 2#define BOOST_TEST_DYN_LINK
1c2d079d
FM
3#endif
4
886e2cf2
RG
5#define BOOST_TEST_NO_MAIN
6
7#include <boost/test/unit_test.hpp>
8
78e3ac9e 9#include "ednscookies.hh"
8dcdbdb1 10#include "ednsoptions.hh"
78e3ac9e 11#include "ednssubnet.hh"
1ea747c0 12#include "dnsdist.hh"
886e2cf2 13#include "iputils.hh"
886e2cf2 14#include "dnswriter.hh"
1ea747c0 15#include "dnsdist-cache.hh"
5ffb2f83 16#include "gettime.hh"
fa980c59 17#include "packetcache.hh"
886e2cf2 18
c7f29d3e 19BOOST_AUTO_TEST_SUITE(test_dnsdistpacketcache_cc)
886e2cf2 20
d84ea5be
RG
21static bool receivedOverUDP = true;
22
886e2cf2
RG
23BOOST_AUTO_TEST_CASE(test_PacketCacheSimple) {
24 const size_t maxEntries = 150000;
25 DNSDistPacketCache PC(maxEntries, 86400, 1);
690b86b7 26 BOOST_CHECK_EQUAL(PC.getSize(), 0U);
886e2cf2 27
592b1d99
RG
28 size_t counter = 0;
29 size_t skipped = 0;
d7728daf 30 bool dnssecOK = false;
f6e76e12 31 const time_t now = time(nullptr);
592b1d99
RG
32 InternalQueryState ids;
33 ids.qtype = QType::A;
34 ids.qclass = QClass::IN;
35 ids.protocol = dnsdist::Protocol::DoUDP;
36
886e2cf2 37 try {
f6e76e12 38 for (counter = 0; counter < 100000; ++counter) {
592b1d99
RG
39 auto a = DNSName(std::to_string(counter))+DNSName(" hello");
40 ids.qname = a;
886e2cf2 41
32fbb2ab
RG
42 PacketBuffer query;
43 GenericDNSPacketWriter<PacketBuffer> pwQ(query, a, QType::A, QClass::IN, 0);
886e2cf2
RG
44 pwQ.getHeader()->rd = 1;
45
32fbb2ab
RG
46 PacketBuffer response;
47 GenericDNSPacketWriter<PacketBuffer> pwR(response, a, QType::A, QClass::IN, 0);
886e2cf2
RG
48 pwR.getHeader()->rd = 1;
49 pwR.getHeader()->ra = 1;
50 pwR.getHeader()->qr = 1;
51 pwR.getHeader()->id = pwQ.getHeader()->id;
f627611d 52 pwR.startRecord(a, QType::A, 7200, QClass::IN, DNSResourceRecord::ANSWER);
886e2cf2
RG
53 pwR.xfr32BitInt(0x01020304);
54 pwR.commit();
886e2cf2 55
886e2cf2 56 uint32_t key = 0;
78e3ac9e 57 boost::optional<Netmask> subnet;
80a210f2
RG
58 DNSQuestion dnsQuestion(ids, query);
59 bool found = PC.get(dnsQuestion, 0, &key, subnet, dnssecOK, receivedOverUDP);
886e2cf2 60 BOOST_CHECK_EQUAL(found, false);
78e3ac9e 61 BOOST_CHECK(!subnet);
886e2cf2 62
80a210f2 63 PC.insert(key, subnet, *(getFlagsFromDNSHeader(dnsQuestion.getHeader().get())), dnssecOK, a, QType::A, QClass::IN, response, receivedOverUDP, 0, boost::none);
886e2cf2 64
80a210f2 65 found = PC.get(dnsQuestion, pwR.getHeader()->id, &key, subnet, dnssecOK, receivedOverUDP, 0, true);
886e2cf2 66 if (found == true) {
80a210f2
RG
67 BOOST_CHECK_EQUAL(dnsQuestion.getData().size(), response.size());
68 int match = memcmp(dnsQuestion.getData().data(), response.data(), dnsQuestion.getData().size());
886e2cf2 69 BOOST_CHECK_EQUAL(match, 0);
78e3ac9e 70 BOOST_CHECK(!subnet);
886e2cf2
RG
71 }
72 else {
73 skipped++;
74 }
75 }
76
77 BOOST_CHECK_EQUAL(skipped, PC.getInsertCollisions());
78 BOOST_CHECK_EQUAL(PC.getSize(), counter - skipped);
79
80 size_t deleted=0;
81 size_t delcounter=0;
f6e76e12 82 for (delcounter=0; delcounter < counter/1000; ++delcounter) {
592b1d99 83 ids.qname = DNSName(std::to_string(delcounter))+DNSName(" hello");
32fbb2ab 84 PacketBuffer query;
592b1d99 85 GenericDNSPacketWriter<PacketBuffer> pwQ(query, ids.qname, QType::A, QClass::IN, 0);
886e2cf2 86 pwQ.getHeader()->rd = 1;
886e2cf2 87 uint32_t key = 0;
78e3ac9e 88 boost::optional<Netmask> subnet;
80a210f2
RG
89 DNSQuestion dnsQuestion(ids, query);
90 bool found = PC.get(dnsQuestion, 0, &key, subnet, dnssecOK, receivedOverUDP);
886e2cf2 91 if (found == true) {
592b1d99 92 auto removed = PC.expungeByName(ids.qname);
690b86b7 93 BOOST_CHECK_EQUAL(removed, 1U);
f627611d 94 deleted += removed;
886e2cf2
RG
95 }
96 }
97 BOOST_CHECK_EQUAL(PC.getSize(), counter - skipped - deleted);
98
99 size_t matches=0;
886e2cf2 100 size_t expected=counter-skipped-deleted;
f6e76e12 101 for (; delcounter < counter; ++delcounter) {
592b1d99 102 ids.qname = DNSName(std::to_string(delcounter))+DNSName(" hello");
32fbb2ab 103 PacketBuffer query;
592b1d99 104 GenericDNSPacketWriter<PacketBuffer> pwQ(query, ids.qname, QType::A, QClass::IN, 0);
886e2cf2 105 pwQ.getHeader()->rd = 1;
886e2cf2 106 uint32_t key = 0;
78e3ac9e 107 boost::optional<Netmask> subnet;
80a210f2
RG
108 DNSQuestion dnsQuestion(ids, query);
109 if (PC.get(dnsQuestion, pwQ.getHeader()->id, &key, subnet, dnssecOK, receivedOverUDP)) {
490dc586 110 matches++;
886e2cf2
RG
111 }
112 }
490dc586 113
6d1a9248 114 /* in the unlikely event that the test took so long that the entries did expire.. */
f6e76e12 115 auto expired = PC.purgeExpired(0, now);
f627611d
RG
116 BOOST_CHECK_EQUAL(matches + expired, expected);
117
118 auto remaining = PC.getSize();
119 auto removed = PC.expungeByName(DNSName(" hello"), QType::ANY, true);
690b86b7 120 BOOST_CHECK_EQUAL(PC.getSize(), 0U);
f627611d 121 BOOST_CHECK_EQUAL(removed, remaining);
af9f7f64
RG
122
123 /* nothing to remove */
124 BOOST_CHECK_EQUAL(PC.purgeExpired(0, now), 0U);
886e2cf2 125 }
f6e76e12
RG
126 catch (const PDNSException& e) {
127 cerr<<"Had error: "<<e.reason<<endl;
128 throw;
129 }
130}
131
132BOOST_AUTO_TEST_CASE(test_PacketCacheSharded) {
133 const size_t maxEntries = 150000;
134 const size_t numberOfShards = 10;
135 DNSDistPacketCache PC(maxEntries, 86400, 1, 60, 3600, 60, false, numberOfShards);
136 BOOST_CHECK_EQUAL(PC.getSize(), 0U);
f6e76e12
RG
137
138 size_t counter = 0;
139 size_t skipped = 0;
140 ComboAddress remote;
141 bool dnssecOK = false;
142 const time_t now = time(nullptr);
592b1d99
RG
143 InternalQueryState ids;
144 ids.qtype = QType::AAAA;
145 ids.qclass = QClass::IN;
146 ids.protocol = dnsdist::Protocol::DoUDP;
f6e76e12
RG
147
148 try {
149 for (counter = 0; counter < 100000; ++counter) {
592b1d99 150 ids.qname = DNSName(std::to_string(counter) + ".powerdns.com.");
f6e76e12
RG
151
152 PacketBuffer query;
592b1d99 153 GenericDNSPacketWriter<PacketBuffer> pwQ(query, ids.qname, QType::AAAA, QClass::IN, 0);
f6e76e12
RG
154 pwQ.getHeader()->rd = 1;
155
156 PacketBuffer response;
592b1d99 157 GenericDNSPacketWriter<PacketBuffer> pwR(response, ids.qname, QType::AAAA, QClass::IN, 0);
f6e76e12
RG
158 pwR.getHeader()->rd = 1;
159 pwR.getHeader()->ra = 1;
160 pwR.getHeader()->qr = 1;
161 pwR.getHeader()->id = pwQ.getHeader()->id;
592b1d99 162 pwR.startRecord(ids.qname, QType::AAAA, 7200, QClass::IN, DNSResourceRecord::ANSWER);
80a210f2 163 ComboAddress v6addr("2001:db8::1");
e1911853 164 pwR.xfrCAWithoutPort(6, v6addr);
f6e76e12
RG
165 pwR.commit();
166
167 uint32_t key = 0;
168 boost::optional<Netmask> subnet;
80a210f2
RG
169 DNSQuestion dnsQuestion(ids, query);
170 bool found = PC.get(dnsQuestion, 0, &key, subnet, dnssecOK, receivedOverUDP);
f6e76e12
RG
171 BOOST_CHECK_EQUAL(found, false);
172 BOOST_CHECK(!subnet);
173
80a210f2 174 PC.insert(key, subnet, *(getFlagsFromDNSHeader(dnsQuestion.getHeader().get())), dnssecOK, ids.qname, QType::AAAA, QClass::IN, response, receivedOverUDP, 0, boost::none);
f6e76e12 175
80a210f2 176 found = PC.get(dnsQuestion, pwR.getHeader()->id, &key, subnet, dnssecOK, receivedOverUDP, 0, true);
f6e76e12 177 if (found == true) {
80a210f2
RG
178 BOOST_CHECK_EQUAL(dnsQuestion.getData().size(), response.size());
179 int match = memcmp(dnsQuestion.getData().data(), response.data(), dnsQuestion.getData().size());
f6e76e12
RG
180 BOOST_CHECK_EQUAL(match, 0);
181 BOOST_CHECK(!subnet);
182 }
183 else {
184 skipped++;
185 }
186 }
187
188 BOOST_CHECK_EQUAL(skipped, PC.getInsertCollisions());
189 BOOST_CHECK_EQUAL(PC.getSize(), counter - skipped);
190
191 size_t matches = 0;
192 for (counter = 0; counter < 100000; ++counter) {
592b1d99 193 ids.qname = DNSName(std::to_string(counter) + ".powerdns.com.");
f6e76e12
RG
194
195 PacketBuffer query;
592b1d99 196 GenericDNSPacketWriter<PacketBuffer> pwQ(query, ids.qname, QType::AAAA, QClass::IN, 0);
f6e76e12
RG
197 pwQ.getHeader()->rd = 1;
198 uint32_t key = 0;
199 boost::optional<Netmask> subnet;
80a210f2
RG
200 DNSQuestion dnsQuestion(ids, query);
201 if (PC.get(dnsQuestion, pwQ.getHeader()->id, &key, subnet, dnssecOK, receivedOverUDP)) {
f6e76e12
RG
202 matches++;
203 }
204 }
205
206 BOOST_CHECK_EQUAL(matches, counter - skipped);
207
208 auto remaining = PC.getSize();
209
210 /* no entry should have expired */
211 auto expired = PC.purgeExpired(0, now);
212 BOOST_CHECK_EQUAL(expired, 0U);
213
214 /* but after the TTL .. let's ask for at most 1k entries */
b8a0a4a8 215 auto removed = PC.purgeExpired(1000, now + 7200 + 3600);
f6e76e12
RG
216 BOOST_CHECK_EQUAL(removed, remaining - 1000U);
217 BOOST_CHECK_EQUAL(PC.getSize(), 1000U);
218
219 /* now remove everything */
b8a0a4a8 220 removed = PC.purgeExpired(0, now + 7200 + 3600);
f6e76e12
RG
221 BOOST_CHECK_EQUAL(removed, 1000U);
222 BOOST_CHECK_EQUAL(PC.getSize(), 0U);
af9f7f64
RG
223
224 /* nothing to remove */
225 BOOST_CHECK_EQUAL(PC.purgeExpired(0, now), 0U);
f6e76e12
RG
226 }
227 catch (const PDNSException& e) {
886e2cf2
RG
228 cerr<<"Had error: "<<e.reason<<endl;
229 throw;
230 }
231}
232
30014e5f
RG
233BOOST_AUTO_TEST_CASE(test_PacketCacheTCP) {
234 const size_t maxEntries = 150000;
235 DNSDistPacketCache PC(maxEntries, 86400, 1);
592b1d99
RG
236 InternalQueryState ids;
237 ids.qtype = QType::A;
238 ids.qclass = QClass::IN;
239 ids.protocol = dnsdist::Protocol::DoUDP;
30014e5f
RG
240
241 ComboAddress remote;
242 bool dnssecOK = false;
243 try {
592b1d99
RG
244 DNSName a("tcp");
245 ids.qname = a;
30014e5f
RG
246
247 PacketBuffer query;
248 GenericDNSPacketWriter<PacketBuffer> pwQ(query, a, QType::AAAA, QClass::IN, 0);
249 pwQ.getHeader()->rd = 1;
250
251 PacketBuffer response;
252 GenericDNSPacketWriter<PacketBuffer> pwR(response, a, QType::AAAA, QClass::IN, 0);
253 pwR.getHeader()->rd = 1;
254 pwR.getHeader()->ra = 1;
255 pwR.getHeader()->qr = 1;
256 pwR.getHeader()->id = pwQ.getHeader()->id;
257 pwR.startRecord(a, QType::AAAA, 7200, QClass::IN, DNSResourceRecord::ANSWER);
80a210f2 258 ComboAddress v6addr("2001:db8::1");
e1911853 259 pwR.xfrCAWithoutPort(6, v6addr);
30014e5f
RG
260 pwR.commit();
261
262 {
263 /* UDP */
264 uint32_t key = 0;
265 boost::optional<Netmask> subnet;
80a210f2
RG
266 DNSQuestion dnsQuestion(ids, query);
267 bool found = PC.get(dnsQuestion, 0, &key, subnet, dnssecOK, receivedOverUDP);
30014e5f
RG
268 BOOST_CHECK_EQUAL(found, false);
269 BOOST_CHECK(!subnet);
270
80a210f2
RG
271 PC.insert(key, subnet, *(getFlagsFromDNSHeader(dnsQuestion.getHeader().get())), dnssecOK, a, QType::A, QClass::IN, response, receivedOverUDP, RCode::NoError, boost::none);
272 found = PC.get(dnsQuestion, pwR.getHeader()->id, &key, subnet, dnssecOK, receivedOverUDP, 0, true);
30014e5f
RG
273 BOOST_CHECK_EQUAL(found, true);
274 BOOST_CHECK(!subnet);
275 }
276
277 {
278 /* same but over TCP */
279 uint32_t key = 0;
280 boost::optional<Netmask> subnet;
592b1d99 281 ids.protocol = dnsdist::Protocol::DoTCP;
80a210f2
RG
282 DNSQuestion dnsQuestion(ids, query);
283 bool found = PC.get(dnsQuestion, 0, &key, subnet, dnssecOK, !receivedOverUDP);
30014e5f
RG
284 BOOST_CHECK_EQUAL(found, false);
285 BOOST_CHECK(!subnet);
286
80a210f2
RG
287 PC.insert(key, subnet, *(getFlagsFromDNSHeader(dnsQuestion.getHeader().get())), dnssecOK, a, QType::A, QClass::IN, response, !receivedOverUDP, RCode::NoError, boost::none);
288 found = PC.get(dnsQuestion, pwR.getHeader()->id, &key, subnet, dnssecOK, !receivedOverUDP, 0, true);
30014e5f
RG
289 BOOST_CHECK_EQUAL(found, true);
290 BOOST_CHECK(!subnet);
291 }
292 }
293 catch(PDNSException& e) {
294 cerr<<"Had error: "<<e.reason<<endl;
295 throw;
296 }
297}
298
acb8f5d5
CH
299BOOST_AUTO_TEST_CASE(test_PacketCacheServFailTTL) {
300 const size_t maxEntries = 150000;
301 DNSDistPacketCache PC(maxEntries, 86400, 1);
592b1d99
RG
302 InternalQueryState ids;
303 ids.qtype = QType::A;
304 ids.qclass = QClass::IN;
305 ids.protocol = dnsdist::Protocol::DoUDP;
acb8f5d5
CH
306
307 ComboAddress remote;
d7728daf 308 bool dnssecOK = false;
acb8f5d5
CH
309 try {
310 DNSName a = DNSName("servfail");
592b1d99 311 ids.qname = a;
acb8f5d5 312
32fbb2ab
RG
313 PacketBuffer query;
314 GenericDNSPacketWriter<PacketBuffer> pwQ(query, a, QType::A, QClass::IN, 0);
acb8f5d5
CH
315 pwQ.getHeader()->rd = 1;
316
32fbb2ab
RG
317 PacketBuffer response;
318 GenericDNSPacketWriter<PacketBuffer> pwR(response, a, QType::A, QClass::IN, 0);
acb8f5d5
CH
319 pwR.getHeader()->rd = 1;
320 pwR.getHeader()->ra = 0;
321 pwR.getHeader()->qr = 1;
322 pwR.getHeader()->rcode = RCode::ServFail;
323 pwR.getHeader()->id = pwQ.getHeader()->id;
324 pwR.commit();
acb8f5d5 325
acb8f5d5 326 uint32_t key = 0;
78e3ac9e 327 boost::optional<Netmask> subnet;
80a210f2
RG
328 DNSQuestion dnsQuestion(ids, query);
329 bool found = PC.get(dnsQuestion, 0, &key, subnet, dnssecOK, receivedOverUDP);
acb8f5d5 330 BOOST_CHECK_EQUAL(found, false);
78e3ac9e 331 BOOST_CHECK(!subnet);
acb8f5d5
CH
332
333 // Insert with failure-TTL of 0 (-> should not enter cache).
80a210f2
RG
334 PC.insert(key, subnet, *(getFlagsFromDNSHeader(dnsQuestion.getHeader().get())), dnssecOK, a, QType::A, QClass::IN, response, receivedOverUDP, RCode::ServFail, boost::optional<uint32_t>(0));
335 found = PC.get(dnsQuestion, pwR.getHeader()->id, &key, subnet, dnssecOK, receivedOverUDP, 0, true);
acb8f5d5 336 BOOST_CHECK_EQUAL(found, false);
78e3ac9e 337 BOOST_CHECK(!subnet);
acb8f5d5 338
56459632 339 // Insert with failure-TTL non-zero (-> should enter cache).
80a210f2
RG
340 PC.insert(key, subnet, *(getFlagsFromDNSHeader(dnsQuestion.getHeader().get())), dnssecOK, a, QType::A, QClass::IN, response, receivedOverUDP, RCode::ServFail, boost::optional<uint32_t>(300));
341 found = PC.get(dnsQuestion, pwR.getHeader()->id, &key, subnet, dnssecOK, receivedOverUDP, 0, true);
acb8f5d5 342 BOOST_CHECK_EQUAL(found, true);
78e3ac9e 343 BOOST_CHECK(!subnet);
acb8f5d5
CH
344 }
345 catch(PDNSException& e) {
346 cerr<<"Had error: "<<e.reason<<endl;
347 throw;
348 }
349}
350
47698274
RG
351BOOST_AUTO_TEST_CASE(test_PacketCacheNoDataTTL) {
352 const size_t maxEntries = 150000;
353 DNSDistPacketCache PC(maxEntries, /* maxTTL */ 86400, /* minTTL */ 1, /* tempFailureTTL */ 60, /* maxNegativeTTL */ 1);
354
592b1d99
RG
355 ComboAddress remote;
356 bool dnssecOK = false;
357 InternalQueryState ids;
358 ids.qtype = QType::A;
359 ids.qclass = QClass::IN;
360 ids.protocol = dnsdist::Protocol::DoUDP;
47698274 361
47698274
RG
362 try {
363 DNSName name("nodata");
592b1d99 364 ids.qname = name;
32fbb2ab
RG
365 PacketBuffer query;
366 GenericDNSPacketWriter<PacketBuffer> pwQ(query, name, QType::A, QClass::IN, 0);
47698274
RG
367 pwQ.getHeader()->rd = 1;
368
32fbb2ab
RG
369 PacketBuffer response;
370 GenericDNSPacketWriter<PacketBuffer> pwR(response, name, QType::A, QClass::IN, 0);
47698274
RG
371 pwR.getHeader()->rd = 1;
372 pwR.getHeader()->ra = 0;
373 pwR.getHeader()->qr = 1;
374 pwR.getHeader()->rcode = RCode::NoError;
375 pwR.getHeader()->id = pwQ.getHeader()->id;
376 pwR.commit();
377 pwR.startRecord(name, QType::SOA, 86400, QClass::IN, DNSResourceRecord::AUTHORITY);
378 pwR.commit();
379 pwR.addOpt(4096, 0, 0);
380 pwR.commit();
381
47698274 382 uint32_t key = 0;
78e3ac9e 383 boost::optional<Netmask> subnet;
80a210f2
RG
384 DNSQuestion dnsQuestion(ids, query);
385 bool found = PC.get(dnsQuestion, 0, &key, subnet, dnssecOK, receivedOverUDP);
47698274 386 BOOST_CHECK_EQUAL(found, false);
78e3ac9e 387 BOOST_CHECK(!subnet);
47698274 388
80a210f2
RG
389 PC.insert(key, subnet, *(getFlagsFromDNSHeader(dnsQuestion.getHeader().get())), dnssecOK, name, QType::A, QClass::IN, response, receivedOverUDP, RCode::NoError, boost::none);
390 found = PC.get(dnsQuestion, pwR.getHeader()->id, &key, subnet, dnssecOK, receivedOverUDP, 0, true);
47698274 391 BOOST_CHECK_EQUAL(found, true);
78e3ac9e
RG
392 BOOST_CHECK(!subnet);
393
47698274
RG
394 sleep(2);
395 /* it should have expired by now */
80a210f2 396 found = PC.get(dnsQuestion, pwR.getHeader()->id, &key, subnet, dnssecOK, receivedOverUDP, 0, true);
47698274 397 BOOST_CHECK_EQUAL(found, false);
78e3ac9e 398 BOOST_CHECK(!subnet);
47698274
RG
399 }
400 catch(const PDNSException& e) {
401 cerr<<"Had error: "<<e.reason<<endl;
402 throw;
403 }
404}
405
406BOOST_AUTO_TEST_CASE(test_PacketCacheNXDomainTTL) {
407 const size_t maxEntries = 150000;
408 DNSDistPacketCache PC(maxEntries, /* maxTTL */ 86400, /* minTTL */ 1, /* tempFailureTTL */ 60, /* maxNegativeTTL */ 1);
409
592b1d99
RG
410 InternalQueryState ids;
411 ids.qtype = QType::A;
412 ids.qclass = QClass::IN;
413 ids.protocol = dnsdist::Protocol::DoUDP;
47698274
RG
414
415 ComboAddress remote;
d7728daf 416 bool dnssecOK = false;
47698274
RG
417 try {
418 DNSName name("nxdomain");
592b1d99 419 ids.qname = name;
32fbb2ab
RG
420 PacketBuffer query;
421 GenericDNSPacketWriter<PacketBuffer> pwQ(query, name, QType::A, QClass::IN, 0);
47698274
RG
422 pwQ.getHeader()->rd = 1;
423
32fbb2ab
RG
424 PacketBuffer response;
425 GenericDNSPacketWriter<PacketBuffer> pwR(response, name, QType::A, QClass::IN, 0);
47698274
RG
426 pwR.getHeader()->rd = 1;
427 pwR.getHeader()->ra = 0;
428 pwR.getHeader()->qr = 1;
429 pwR.getHeader()->rcode = RCode::NXDomain;
430 pwR.getHeader()->id = pwQ.getHeader()->id;
431 pwR.commit();
432 pwR.startRecord(name, QType::SOA, 86400, QClass::IN, DNSResourceRecord::AUTHORITY);
433 pwR.commit();
434 pwR.addOpt(4096, 0, 0);
435 pwR.commit();
436
47698274 437 uint32_t key = 0;
78e3ac9e 438 boost::optional<Netmask> subnet;
80a210f2
RG
439 DNSQuestion dnsQuestion(ids, query);
440 bool found = PC.get(dnsQuestion, 0, &key, subnet, dnssecOK, receivedOverUDP);
47698274 441 BOOST_CHECK_EQUAL(found, false);
78e3ac9e 442 BOOST_CHECK(!subnet);
47698274 443
80a210f2
RG
444 PC.insert(key, subnet, *(getFlagsFromDNSHeader(dnsQuestion.getHeader().get())), dnssecOK, name, QType::A, QClass::IN, response, receivedOverUDP, RCode::NXDomain, boost::none);
445 found = PC.get(dnsQuestion, pwR.getHeader()->id, &key, subnet, dnssecOK, receivedOverUDP, 0, true);
47698274 446 BOOST_CHECK_EQUAL(found, true);
78e3ac9e
RG
447 BOOST_CHECK(!subnet);
448
47698274
RG
449 sleep(2);
450 /* it should have expired by now */
80a210f2 451 found = PC.get(dnsQuestion, pwR.getHeader()->id, &key, subnet, dnssecOK, receivedOverUDP, 0, true);
47698274 452 BOOST_CHECK_EQUAL(found, false);
78e3ac9e 453 BOOST_CHECK(!subnet);
47698274
RG
454 }
455 catch(const PDNSException& e) {
456 cerr<<"Had error: "<<e.reason<<endl;
457 throw;
458 }
459}
460
0c8759d8
RG
461BOOST_AUTO_TEST_CASE(test_PacketCacheTruncated) {
462 const size_t maxEntries = 150000;
463 DNSDistPacketCache PC(maxEntries, /* maxTTL */ 86400, /* minTTL */ 1, /* tempFailureTTL */ 60, /* maxNegativeTTL */ 1);
464
592b1d99
RG
465 InternalQueryState ids;
466 ids.qtype = QType::A;
467 ids.qclass = QClass::IN;
468 ids.protocol = dnsdist::Protocol::DoUDP;
d5d15da1 469 ids.queryRealTime.start(); // does not have to be accurate ("realTime") in tests
0c8759d8
RG
470 bool dnssecOK = false;
471
472 try {
592b1d99 473 ids.qname = DNSName("truncated");
0c8759d8 474 PacketBuffer query;
592b1d99 475 GenericDNSPacketWriter<PacketBuffer> pwQ(query, ids.qname, QType::A, QClass::IN, 0);
0c8759d8
RG
476 pwQ.getHeader()->rd = 1;
477
478 PacketBuffer response;
592b1d99 479 GenericDNSPacketWriter<PacketBuffer> pwR(response, ids.qname, QType::A, QClass::IN, 0);
0c8759d8
RG
480 pwR.getHeader()->rd = 1;
481 pwR.getHeader()->ra = 0;
482 pwR.getHeader()->qr = 1;
483 pwR.getHeader()->tc = 1;
484 pwR.getHeader()->rcode = RCode::NoError;
485 pwR.getHeader()->id = pwQ.getHeader()->id;
486 pwR.commit();
592b1d99 487 pwR.startRecord(ids.qname, QType::A, 7200, QClass::IN, DNSResourceRecord::ANSWER);
0c8759d8
RG
488 pwR.xfr32BitInt(0x01020304);
489 pwR.commit();
490
491 uint32_t key = 0;
492 boost::optional<Netmask> subnet;
80a210f2
RG
493 DNSQuestion dnsQuestion(ids, query);
494 bool found = PC.get(dnsQuestion, 0, &key, subnet, dnssecOK, receivedOverUDP);
0c8759d8
RG
495 BOOST_CHECK_EQUAL(found, false);
496 BOOST_CHECK(!subnet);
497
80a210f2 498 PC.insert(key, subnet, *(getFlagsFromDNSHeader(dnsQuestion.getHeader().get())), dnssecOK, ids.qname, QType::A, QClass::IN, response, receivedOverUDP, RCode::NXDomain, boost::none);
0c8759d8
RG
499
500 bool allowTruncated = true;
80a210f2 501 found = PC.get(dnsQuestion, pwR.getHeader()->id, &key, subnet, dnssecOK, receivedOverUDP, 0, true, allowTruncated);
0c8759d8
RG
502 BOOST_CHECK_EQUAL(found, true);
503 BOOST_CHECK(!subnet);
504
505 allowTruncated = false;
80a210f2 506 found = PC.get(dnsQuestion, pwR.getHeader()->id, &key, subnet, dnssecOK, receivedOverUDP, 0, true, allowTruncated);
0c8759d8
RG
507 BOOST_CHECK_EQUAL(found, false);
508}
509 catch(const PDNSException& e) {
510 cerr<<"Had error: "<<e.reason<<endl;
511 throw;
512 }
513}
514
7b71c89b
RG
515BOOST_AUTO_TEST_CASE(test_PacketCacheMaximumSize) {
516 const size_t maxEntries = 150000;
517 DNSDistPacketCache packetCache(maxEntries, 86400, 1);
518 InternalQueryState ids;
519 ids.qtype = QType::A;
520 ids.qclass = QClass::IN;
521 ids.protocol = dnsdist::Protocol::DoUDP;
522
523 ComboAddress remote;
524 bool dnssecOK = false;
525 ids.qname = DNSName("maximum.size");
526
527 PacketBuffer query;
528 uint16_t queryID{0};
529 {
530 GenericDNSPacketWriter<PacketBuffer> pwQ(query, ids.qname, QType::AAAA, QClass::IN, 0);
531 pwQ.getHeader()->rd = 1;
532 queryID = pwQ.getHeader()->id;
533 }
534
535 PacketBuffer response;
536 {
537 GenericDNSPacketWriter<PacketBuffer> pwR(response, ids.qname, QType::AAAA, QClass::IN, 0);
538 pwR.getHeader()->rd = 1;
539 pwR.getHeader()->ra = 1;
540 pwR.getHeader()->qr = 1;
541 pwR.getHeader()->id = queryID;
542 pwR.startRecord(ids.qname, QType::AAAA, 7200, QClass::IN, DNSResourceRecord::ANSWER);
80a210f2
RG
543 ComboAddress v6addr("2001:db8::1");
544 pwR.xfrCAWithoutPort(6, v6addr);
7b71c89b
RG
545 pwR.commit();
546 }
547
548 /* first, we set the maximum entry size to the response packet size */
549 packetCache.setMaximumEntrySize(response.size());
550
551 {
552 /* UDP */
553 uint32_t key = 0;
554 boost::optional<Netmask> subnet;
80a210f2
RG
555 DNSQuestion dnsQuestion(ids, query);
556 bool found = packetCache.get(dnsQuestion, 0, &key, subnet, dnssecOK, receivedOverUDP);
7b71c89b
RG
557 BOOST_CHECK_EQUAL(found, false);
558 BOOST_CHECK(!subnet);
559
80a210f2
RG
560 packetCache.insert(key, subnet, *(getFlagsFromDNSHeader(dnsQuestion.getHeader().get())), dnssecOK, ids.qname, QType::A, QClass::IN, response, receivedOverUDP, RCode::NoError, boost::none);
561 found = packetCache.get(dnsQuestion, queryID, &key, subnet, dnssecOK, receivedOverUDP, 0, true);
7b71c89b
RG
562 BOOST_CHECK_EQUAL(found, true);
563 BOOST_CHECK(!subnet);
564 }
565
566 {
567 /* same but over TCP */
568 uint32_t key = 0;
569 boost::optional<Netmask> subnet;
570 ids.protocol = dnsdist::Protocol::DoTCP;
80a210f2
RG
571 DNSQuestion dnsQuestion(ids, query);
572 bool found = packetCache.get(dnsQuestion, 0, &key, subnet, dnssecOK, !receivedOverUDP);
7b71c89b
RG
573 BOOST_CHECK_EQUAL(found, false);
574 BOOST_CHECK(!subnet);
575
80a210f2
RG
576 packetCache.insert(key, subnet, *(getFlagsFromDNSHeader(dnsQuestion.getHeader().get())), dnssecOK, ids.qname, QType::A, QClass::IN, response, !receivedOverUDP, RCode::NoError, boost::none);
577 found = packetCache.get(dnsQuestion, queryID, &key, subnet, dnssecOK, !receivedOverUDP, 0, true);
7b71c89b
RG
578 BOOST_CHECK_EQUAL(found, true);
579 BOOST_CHECK(!subnet);
580 }
581
582 /* then we set it slightly below response packet size */
583 packetCache.expunge(0);
584 packetCache.setMaximumEntrySize(response.size() - 1);
585 {
586 /* UDP */
587 uint32_t key = 0;
588 boost::optional<Netmask> subnet;
80a210f2
RG
589 DNSQuestion dnsQuestion(ids, query);
590 bool found = packetCache.get(dnsQuestion, 0, &key, subnet, dnssecOK, receivedOverUDP);
7b71c89b
RG
591 BOOST_CHECK_EQUAL(found, false);
592 BOOST_CHECK(!subnet);
593
80a210f2
RG
594 packetCache.insert(key, subnet, *(getFlagsFromDNSHeader(dnsQuestion.getHeader().get())), dnssecOK, ids.qname, QType::A, QClass::IN, response, receivedOverUDP, RCode::NoError, boost::none);
595 found = packetCache.get(dnsQuestion, queryID, &key, subnet, dnssecOK, receivedOverUDP, 0, true);
7b71c89b
RG
596 BOOST_CHECK_EQUAL(found, false);
597 }
598
599 {
600 /* same but over TCP */
601 uint32_t key = 0;
602 boost::optional<Netmask> subnet;
603 ids.protocol = dnsdist::Protocol::DoTCP;
80a210f2
RG
604 DNSQuestion dnsQuestion(ids, query);
605 bool found = packetCache.get(dnsQuestion, 0, &key, subnet, dnssecOK, !receivedOverUDP);
7b71c89b
RG
606 BOOST_CHECK_EQUAL(found, false);
607 BOOST_CHECK(!subnet);
608
80a210f2
RG
609 packetCache.insert(key, subnet, *(getFlagsFromDNSHeader(dnsQuestion.getHeader().get())), dnssecOK, ids.qname, QType::A, QClass::IN, response, !receivedOverUDP, RCode::NoError, boost::none);
610 found = packetCache.get(dnsQuestion, queryID, &key, subnet, dnssecOK, !receivedOverUDP, 0, true);
7b71c89b
RG
611 BOOST_CHECK_EQUAL(found, false);
612 }
613
614 /* now we generate a very big response packet, it should be cached over TCP and UDP (although in practice dnsdist will refuse to cache it for the UDP case) */
615 packetCache.expunge(0);
616 response.clear();
617 {
618 GenericDNSPacketWriter<PacketBuffer> pwR(response, ids.qname, QType::AAAA, QClass::IN, 0);
619 pwR.getHeader()->rd = 1;
620 pwR.getHeader()->ra = 1;
621 pwR.getHeader()->qr = 1;
622 pwR.getHeader()->id = queryID;
623 for (size_t idx = 0; idx < 1000; idx++) {
624 pwR.startRecord(ids.qname, QType::AAAA, 7200, QClass::IN, DNSResourceRecord::ANSWER);
80a210f2
RG
625 ComboAddress v6addr("2001:db8::1");
626 pwR.xfrCAWithoutPort(6, v6addr);
7b71c89b
RG
627 }
628 pwR.commit();
629 }
630
631 BOOST_REQUIRE_GT(response.size(), 4096U);
632 packetCache.setMaximumEntrySize(response.size());
633
634 {
635 /* UDP */
636 uint32_t key = 0;
637 boost::optional<Netmask> subnet;
80a210f2
RG
638 DNSQuestion dnsQuestion(ids, query);
639 bool found = packetCache.get(dnsQuestion, 0, &key, subnet, dnssecOK, receivedOverUDP);
7b71c89b
RG
640 BOOST_CHECK_EQUAL(found, false);
641 BOOST_CHECK(!subnet);
642
80a210f2
RG
643 packetCache.insert(key, subnet, *(getFlagsFromDNSHeader(dnsQuestion.getHeader().get())), dnssecOK, ids.qname, QType::A, QClass::IN, response, receivedOverUDP, RCode::NoError, boost::none);
644 found = packetCache.get(dnsQuestion, queryID, &key, subnet, dnssecOK, receivedOverUDP, 0, true);
7b71c89b
RG
645 BOOST_CHECK_EQUAL(found, true);
646 }
647
648 {
649 /* same but over TCP */
650 uint32_t key = 0;
651 boost::optional<Netmask> subnet;
652 ids.protocol = dnsdist::Protocol::DoTCP;
80a210f2
RG
653 DNSQuestion dnsQuestion(ids, query);
654 bool found = packetCache.get(dnsQuestion, 0, &key, subnet, dnssecOK, !receivedOverUDP);
7b71c89b
RG
655 BOOST_CHECK_EQUAL(found, false);
656 BOOST_CHECK(!subnet);
657
80a210f2
RG
658 packetCache.insert(key, subnet, *(getFlagsFromDNSHeader(dnsQuestion.getHeader().get())), dnssecOK, ids.qname, QType::A, QClass::IN, response, !receivedOverUDP, RCode::NoError, boost::none);
659 found = packetCache.get(dnsQuestion, queryID, &key, subnet, dnssecOK, !receivedOverUDP, 0, true);
7b71c89b
RG
660 BOOST_CHECK_EQUAL(found, true);
661 }
662}
663
dd026b9c 664static DNSDistPacketCache g_PC(500000);
886e2cf2 665
f0941861 666static void threadMangler(unsigned int offset)
886e2cf2 667{
592b1d99
RG
668 InternalQueryState ids;
669 ids.qtype = QType::A;
670 ids.qclass = QClass::IN;
671 ids.protocol = dnsdist::Protocol::DoUDP;
592b1d99 672
886e2cf2 673 try {
1ea747c0 674 ComboAddress remote;
d7728daf 675 bool dnssecOK = false;
886e2cf2 676 for(unsigned int counter=0; counter < 100000; ++counter) {
592b1d99 677 ids.qname = DNSName("hello ")+DNSName(std::to_string(counter+offset));
32fbb2ab 678 PacketBuffer query;
592b1d99 679 GenericDNSPacketWriter<PacketBuffer> pwQ(query, ids.qname, QType::A, QClass::IN, 0);
886e2cf2
RG
680 pwQ.getHeader()->rd = 1;
681
32fbb2ab 682 PacketBuffer response;
592b1d99 683 GenericDNSPacketWriter<PacketBuffer> pwR(response, ids.qname, QType::A, QClass::IN, 0);
886e2cf2
RG
684 pwR.getHeader()->rd = 1;
685 pwR.getHeader()->ra = 1;
686 pwR.getHeader()->qr = 1;
687 pwR.getHeader()->id = pwQ.getHeader()->id;
592b1d99 688 pwR.startRecord(ids.qname, QType::A, 3600, QClass::IN, DNSResourceRecord::ANSWER);
886e2cf2
RG
689 pwR.xfr32BitInt(0x01020304);
690 pwR.commit();
886e2cf2 691
886e2cf2 692 uint32_t key = 0;
78e3ac9e 693 boost::optional<Netmask> subnet;
80a210f2
RG
694 DNSQuestion dnsQuestion(ids, query);
695 g_PC.get(dnsQuestion, 0, &key, subnet, dnssecOK, receivedOverUDP);
886e2cf2 696
80a210f2 697 g_PC.insert(key, subnet, *(getFlagsFromDNSHeader(dnsQuestion.getHeader().get())), dnssecOK, ids.qname, QType::A, QClass::IN, response, receivedOverUDP, 0, boost::none);
886e2cf2
RG
698 }
699 }
700 catch(PDNSException& e) {
701 cerr<<"Had error: "<<e.reason<<endl;
702 throw;
703 }
886e2cf2
RG
704}
705
706AtomicCounter g_missing;
707
f0941861 708static void threadReader(unsigned int offset)
886e2cf2 709{
592b1d99
RG
710 InternalQueryState ids;
711 ids.qtype = QType::A;
712 ids.qclass = QClass::IN;
713 ids.qname = DNSName("www.powerdns.com.");
714 ids.protocol = dnsdist::Protocol::DoUDP;
592b1d99 715 bool dnssecOK = false;
886e2cf2
RG
716 try
717 {
1ea747c0 718 ComboAddress remote;
886e2cf2 719 for(unsigned int counter=0; counter < 100000; ++counter) {
592b1d99 720 ids.qname = DNSName("hello ")+DNSName(std::to_string(counter+offset));
32fbb2ab 721 PacketBuffer query;
592b1d99 722 GenericDNSPacketWriter<PacketBuffer> pwQ(query, ids.qname, QType::A, QClass::IN, 0);
886e2cf2
RG
723 pwQ.getHeader()->rd = 1;
724
886e2cf2 725 uint32_t key = 0;
78e3ac9e 726 boost::optional<Netmask> subnet;
80a210f2
RG
727 DNSQuestion dnsQuestion(ids, query);
728 bool found = g_PC.get(dnsQuestion, 0, &key, subnet, dnssecOK, receivedOverUDP);
886e2cf2
RG
729 if (!found) {
730 g_missing++;
731 }
732 }
733 }
734 catch(PDNSException& e) {
735 cerr<<"Had error in threadReader: "<<e.reason<<endl;
736 throw;
737 }
886e2cf2
RG
738}
739
740BOOST_AUTO_TEST_CASE(test_PacketCacheThreaded) {
741 try {
f0941861
RG
742 std::vector<std::thread> threads;
743 for (int i = 0; i < 4; ++i) {
744 threads.push_back(std::thread(threadMangler, i*1000000UL));
745 }
746
747 for (auto& t : threads) {
748 t.join();
749 }
750
751 threads.clear();
886e2cf2 752
690b86b7 753 BOOST_CHECK_EQUAL(g_PC.getSize() + g_PC.getDeferredInserts() + g_PC.getInsertCollisions(), 400000U);
dd026b9c 754 BOOST_CHECK_SMALL(1.0*g_PC.getInsertCollisions(), 10000.0);
886e2cf2 755
f0941861
RG
756 for (int i = 0; i < 4; ++i) {
757 threads.push_back(std::thread(threadReader, i*1000000UL));
758 }
759
760 for (auto& t : threads) {
761 t.join();
762 }
886e2cf2 763
dd026b9c 764 BOOST_CHECK((g_PC.getDeferredInserts() + g_PC.getDeferredLookups() + g_PC.getInsertCollisions()) >= g_missing);
886e2cf2
RG
765 }
766 catch(PDNSException& e) {
767 cerr<<"Had error: "<<e.reason<<endl;
768 throw;
769 }
770
771}
772
78e3ac9e
RG
773BOOST_AUTO_TEST_CASE(test_PCCollision) {
774 const size_t maxEntries = 150000;
775 DNSDistPacketCache PC(maxEntries, 86400, 1, 60, 3600, 60, false, 1, true, true);
690b86b7 776 BOOST_CHECK_EQUAL(PC.getSize(), 0U);
78e3ac9e 777
592b1d99
RG
778 InternalQueryState ids;
779 ids.qtype = QType::AAAA;
780 ids.qclass = QClass::IN;
781 ids.qname = DNSName("www.powerdns.com.");
782 ids.protocol = dnsdist::Protocol::DoUDP;
78e3ac9e
RG
783 uint16_t qid = 0x42;
784 uint32_t key;
785 uint32_t secondKey;
786 boost::optional<Netmask> subnetOut;
d7728daf 787 bool dnssecOK = false;
78e3ac9e 788
fa980c59 789 /* lookup for a query with a first ECS value,
78e3ac9e
RG
790 insert a corresponding response */
791 {
32fbb2ab 792 PacketBuffer query;
592b1d99 793 GenericDNSPacketWriter<PacketBuffer> pwQ(query, ids.qname, ids.qtype, QClass::IN, 0);
78e3ac9e
RG
794 pwQ.getHeader()->rd = 1;
795 pwQ.getHeader()->id = qid;
32fbb2ab 796 GenericDNSPacketWriter<PacketBuffer>::optvect_t ednsOptions;
78e3ac9e 797 EDNSSubnetOpts opt;
fa980c59 798 opt.source = Netmask("10.0.59.220/32");
e32a8d46 799 ednsOptions.emplace_back(EDNSOptionCode::ECS, makeEDNSSubnetOptsString(opt));
78e3ac9e
RG
800 pwQ.addOpt(512, 0, 0, ednsOptions);
801 pwQ.commit();
802
78e3ac9e 803 ComboAddress remote("192.0.2.1");
d5d15da1 804 ids.queryRealTime.start();
80a210f2
RG
805 DNSQuestion dnsQuestion(ids, query);
806 bool found = PC.get(dnsQuestion, 0, &key, subnetOut, dnssecOK, receivedOverUDP);
78e3ac9e
RG
807 BOOST_CHECK_EQUAL(found, false);
808 BOOST_REQUIRE(subnetOut);
809 BOOST_CHECK_EQUAL(subnetOut->toString(), opt.source.toString());
810
32fbb2ab 811 PacketBuffer response;
592b1d99 812 GenericDNSPacketWriter<PacketBuffer> pwR(response, ids.qname, ids.qtype, QClass::IN, 0);
78e3ac9e
RG
813 pwR.getHeader()->rd = 1;
814 pwR.getHeader()->id = qid;
592b1d99 815 pwR.startRecord(ids.qname, ids.qtype, 100, QClass::IN, DNSResourceRecord::ANSWER);
80a210f2
RG
816 ComboAddress v6addr("::1");
817 pwR.xfrCAWithoutPort(6, v6addr);
78e3ac9e
RG
818 pwR.commit();
819 pwR.addOpt(512, 0, 0, ednsOptions);
820 pwR.commit();
821
592b1d99 822 PC.insert(key, subnetOut, *(getFlagsFromDNSHeader(pwR.getHeader())), dnssecOK, ids.qname, ids.qtype, QClass::IN, response, receivedOverUDP, RCode::NoError, boost::none);
690b86b7 823 BOOST_CHECK_EQUAL(PC.getSize(), 1U);
78e3ac9e 824
80a210f2 825 found = PC.get(dnsQuestion, 0, &key, subnetOut, dnssecOK, receivedOverUDP);
78e3ac9e
RG
826 BOOST_CHECK_EQUAL(found, true);
827 BOOST_REQUIRE(subnetOut);
828 BOOST_CHECK_EQUAL(subnetOut->toString(), opt.source.toString());
829 }
830
fa980c59 831 /* now lookup for the same query with a different ECS value,
78e3ac9e
RG
832 we should get the same key (collision) but no match */
833 {
32fbb2ab 834 PacketBuffer query;
592b1d99 835 GenericDNSPacketWriter<PacketBuffer> pwQ(query, ids.qname, ids.qtype, QClass::IN, 0);
78e3ac9e
RG
836 pwQ.getHeader()->rd = 1;
837 pwQ.getHeader()->id = qid;
32fbb2ab 838 GenericDNSPacketWriter<PacketBuffer>::optvect_t ednsOptions;
78e3ac9e 839 EDNSSubnetOpts opt;
fa980c59 840 opt.source = Netmask("10.0.167.48/32");
e32a8d46 841 ednsOptions.emplace_back(EDNSOptionCode::ECS, makeEDNSSubnetOptsString(opt));
78e3ac9e
RG
842 pwQ.addOpt(512, 0, 0, ednsOptions);
843 pwQ.commit();
844
78e3ac9e 845 ComboAddress remote("192.0.2.1");
d5d15da1 846 ids.queryRealTime.start();
80a210f2
RG
847 DNSQuestion dnsQuestion(ids, query);
848 bool found = PC.get(dnsQuestion, 0, &secondKey, subnetOut, dnssecOK, receivedOverUDP);
78e3ac9e
RG
849 BOOST_CHECK_EQUAL(found, false);
850 BOOST_CHECK_EQUAL(secondKey, key);
851 BOOST_REQUIRE(subnetOut);
852 BOOST_CHECK_EQUAL(subnetOut->toString(), opt.source.toString());
690b86b7 853 BOOST_CHECK_EQUAL(PC.getLookupCollisions(), 1U);
78e3ac9e 854 }
fa980c59
RG
855
856#if 0
857 /* to be able to compute a new collision if the packet cache hashing code is updated */
858 {
859 DNSDistPacketCache pc(10000);
32fbb2ab 860 GenericDNSPacketWriter<PacketBuffer>::optvect_t ednsOptions;
fa980c59
RG
861 EDNSSubnetOpts opt;
862 std::map<uint32_t, Netmask> colMap;
863 size_t collisions = 0;
864 size_t total = 0;
865 //qname = DNSName("collision-with-ecs-parsing.cache.tests.powerdns.com.");
866
867 for (size_t idxA = 0; idxA < 256; idxA++) {
868 for (size_t idxB = 0; idxB < 256; idxB++) {
869 for (size_t idxC = 0; idxC < 256; idxC++) {
32fbb2ab 870 PacketBuffer secondQuery;
592b1d99 871 GenericDNSPacketWriter<PacketBuffer> pwFQ(secondQuery, ids.qname, QType::AAAA, QClass::IN, 0);
fa980c59
RG
872 pwFQ.getHeader()->rd = 1;
873 pwFQ.getHeader()->qr = false;
874 pwFQ.getHeader()->id = 0x42;
875 opt.source = Netmask("10." + std::to_string(idxA) + "." + std::to_string(idxB) + "." + std::to_string(idxC) + "/32");
876 ednsOptions.clear();
e32a8d46 877 ednsOptions.emplace_back(EDNSOptionCode::ECS, makeEDNSSubnetOptsString(opt));
fa980c59
RG
878 pwFQ.addOpt(512, 0, 0, ednsOptions);
879 pwFQ.commit();
592b1d99 880 secondKey = pc.getKey(ids.qname.toDNSString(), ids.qname.wirelength(), secondQuery, false);
e32a8d46 881 auto pair = colMap.emplace(secondKey, opt.source);
fa980c59
RG
882 total++;
883 if (!pair.second) {
884 collisions++;
885 cerr<<"Collision between "<<colMap[secondKey].toString()<<" and "<<opt.source.toString()<<" for key "<<secondKey<<endl;
886 goto done;
887 }
888 }
889 }
890 }
891 done:
892 cerr<<"collisions: "<<collisions<<endl;
893 cerr<<"total: "<<total<<endl;
894 }
895#endif
78e3ac9e
RG
896}
897
d7728daf
RG
898BOOST_AUTO_TEST_CASE(test_PCDNSSECCollision) {
899 const size_t maxEntries = 150000;
900 DNSDistPacketCache PC(maxEntries, 86400, 1, 60, 3600, 60, false, 1, true, true);
690b86b7 901 BOOST_CHECK_EQUAL(PC.getSize(), 0U);
d7728daf 902
592b1d99
RG
903 InternalQueryState ids;
904 ids.qtype = QType::AAAA;
905 ids.qclass = QClass::IN;
906 ids.qname = DNSName("www.powerdns.com.");
907 ids.protocol = dnsdist::Protocol::DoUDP;
d7728daf
RG
908 uint16_t qid = 0x42;
909 uint32_t key;
910 boost::optional<Netmask> subnetOut;
911
912 /* lookup for a query with DNSSEC OK,
913 insert a corresponding response with DO set,
914 check that it doesn't match without DO, but does with it */
915 {
32fbb2ab 916 PacketBuffer query;
592b1d99 917 GenericDNSPacketWriter<PacketBuffer> pwQ(query, ids.qname, ids.qtype, QClass::IN, 0);
d7728daf
RG
918 pwQ.getHeader()->rd = 1;
919 pwQ.getHeader()->id = qid;
920 pwQ.addOpt(512, 0, EDNS_HEADER_FLAG_DO);
921 pwQ.commit();
922
d7728daf 923 ComboAddress remote("192.0.2.1");
d5d15da1 924 ids.queryRealTime.start();
592b1d99 925 ids.origRemote = remote;
80a210f2
RG
926 DNSQuestion dnsQuestion(ids, query);
927 bool found = PC.get(dnsQuestion, 0, &key, subnetOut, true, receivedOverUDP);
d7728daf
RG
928 BOOST_CHECK_EQUAL(found, false);
929
32fbb2ab 930 PacketBuffer response;
592b1d99 931 GenericDNSPacketWriter<PacketBuffer> pwR(response, ids.qname, ids.qtype, QClass::IN, 0);
d7728daf
RG
932 pwR.getHeader()->rd = 1;
933 pwR.getHeader()->id = qid;
592b1d99 934 pwR.startRecord(ids.qname, ids.qtype, 100, QClass::IN, DNSResourceRecord::ANSWER);
80a210f2
RG
935 ComboAddress v6addr("::1");
936 pwR.xfrCAWithoutPort(6, v6addr);
d7728daf
RG
937 pwR.commit();
938 pwR.addOpt(512, 0, EDNS_HEADER_FLAG_DO);
939 pwR.commit();
940
592b1d99 941 PC.insert(key, subnetOut, *(getFlagsFromDNSHeader(pwR.getHeader())), /* DNSSEC OK is set */ true, ids.qname, ids.qtype, QClass::IN, response, receivedOverUDP, RCode::NoError, boost::none);
690b86b7 942 BOOST_CHECK_EQUAL(PC.getSize(), 1U);
d7728daf 943
80a210f2 944 found = PC.get(dnsQuestion, 0, &key, subnetOut, false, receivedOverUDP);
d7728daf
RG
945 BOOST_CHECK_EQUAL(found, false);
946
80a210f2 947 found = PC.get(dnsQuestion, 0, &key, subnetOut, true, receivedOverUDP);
d7728daf
RG
948 BOOST_CHECK_EQUAL(found, true);
949 }
950
951}
952
fec4382e
RG
953BOOST_AUTO_TEST_CASE(test_PacketCacheInspection) {
954 const size_t maxEntries = 100;
955 DNSDistPacketCache PC(maxEntries, 86400, 1);
956 BOOST_CHECK_EQUAL(PC.getSize(), 0U);
fec4382e
RG
957
958 ComboAddress remote;
959 bool dnssecOK = false;
960
961 uint32_t key = 0;
962
963 /* insert powerdns.com A 192.0.2.1, 192.0.2.2 */
964 {
965 DNSName qname("powerdns.com");
966 PacketBuffer query;
967 GenericDNSPacketWriter<PacketBuffer> pwQ(query, qname, QType::A, QClass::IN, 0);
968 pwQ.getHeader()->rd = 1;
969
970 PacketBuffer response;
971 GenericDNSPacketWriter<PacketBuffer> pwR(response, qname, QType::A, QClass::IN, 0);
972 pwR.getHeader()->rd = 1;
973 pwR.getHeader()->ra = 1;
974 pwR.getHeader()->qr = 1;
975 pwR.getHeader()->id = pwQ.getHeader()->id;
976 {
977 ComboAddress addr("192.0.2.1");
978 pwR.startRecord(qname, QType::A, 7200, QClass::IN, DNSResourceRecord::ANSWER);
979 pwR.xfrCAWithoutPort(4, addr);
980 pwR.commit();
981 }
982 {
983 ComboAddress addr("192.0.2.2");
984 pwR.startRecord(qname, QType::A, 7200, QClass::IN, DNSResourceRecord::ANSWER);
985 pwR.xfrCAWithoutPort(4, addr);
986 pwR.commit();
987 }
988
989 PC.insert(key++, boost::none, *getFlagsFromDNSHeader(pwQ.getHeader()), dnssecOK, qname, QType::A, QClass::IN, response, receivedOverUDP, 0, boost::none);
990 BOOST_CHECK_EQUAL(PC.getSize(), key);
991 }
992
993 /* insert powerdns1.com A 192.0.2.3, 192.0.2.4, AAAA 2001:db8::3, 2001:db8::4 */
994 {
995 DNSName qname("powerdns1.com");
996 PacketBuffer query;
997 GenericDNSPacketWriter<PacketBuffer> pwQ(query, qname, QType::A, QClass::IN, 0);
998 pwQ.getHeader()->rd = 1;
999
1000 PacketBuffer response;
1001 GenericDNSPacketWriter<PacketBuffer> pwR(response, qname, QType::A, QClass::IN, 0);
1002 pwR.getHeader()->rd = 1;
1003 pwR.getHeader()->ra = 1;
1004 pwR.getHeader()->qr = 1;
1005 pwR.getHeader()->id = pwQ.getHeader()->id;
1006 {
1007 ComboAddress addr("192.0.2.3");
1008 pwR.startRecord(qname, QType::A, 7200, QClass::IN, DNSResourceRecord::ANSWER);
1009 pwR.xfrCAWithoutPort(4, addr);
1010 pwR.commit();
1011 }
1012 {
1013 ComboAddress addr("192.0.2.4");
1014 pwR.startRecord(qname, QType::A, 7200, QClass::IN, DNSResourceRecord::ANSWER);
1015 pwR.xfrCAWithoutPort(4, addr);
1016 pwR.commit();
1017 }
1018 {
1019 ComboAddress addr("2001:db8::3");
1020 pwR.startRecord(qname, QType::AAAA, 7200, QClass::IN, DNSResourceRecord::ADDITIONAL);
1021 pwR.xfrCAWithoutPort(6, addr);
1022 pwR.commit();
1023 }
1024 {
1025 ComboAddress addr("2001:db8::4");
1026 pwR.startRecord(qname, QType::AAAA, 7200, QClass::IN, DNSResourceRecord::ADDITIONAL);
1027 pwR.xfrCAWithoutPort(6, addr);
1028 pwR.commit();
1029 }
1030
1031 PC.insert(key++, boost::none, *getFlagsFromDNSHeader(pwQ.getHeader()), dnssecOK, qname, QType::A, QClass::IN, response, receivedOverUDP, 0, boost::none);
1032 BOOST_CHECK_EQUAL(PC.getSize(), key);
1033 }
1034
1035 /* insert powerdns2.com NODATA */
1036 {
1037 DNSName qname("powerdns2.com");
1038 PacketBuffer query;
1039 GenericDNSPacketWriter<PacketBuffer> pwQ(query, qname, QType::A, QClass::IN, 0);
1040 pwQ.getHeader()->rd = 1;
1041
1042 PacketBuffer response;
1043 GenericDNSPacketWriter<PacketBuffer> pwR(response, qname, QType::A, QClass::IN, 0);
1044 pwR.getHeader()->rd = 1;
1045 pwR.getHeader()->ra = 1;
1046 pwR.getHeader()->qr = 1;
1047 pwR.getHeader()->id = pwQ.getHeader()->id;
1048 pwR.commit();
1049 pwR.startRecord(qname, QType::SOA, 86400, QClass::IN, DNSResourceRecord::AUTHORITY);
1050 pwR.commit();
1051 pwR.addOpt(4096, 0, 0);
1052 pwR.commit();
1053
1054 PC.insert(key++, boost::none, *getFlagsFromDNSHeader(pwQ.getHeader()), dnssecOK, qname, QType::A, QClass::IN, response, receivedOverUDP, 0, boost::none);
1055 BOOST_CHECK_EQUAL(PC.getSize(), key);
1056 }
1057
1058 /* insert powerdns3.com AAAA 2001:db8::4, 2001:db8::5 */
1059 {
1060 DNSName qname("powerdns3.com");
1061 PacketBuffer query;
1062 GenericDNSPacketWriter<PacketBuffer> pwQ(query, qname, QType::A, QClass::IN, 0);
1063 pwQ.getHeader()->rd = 1;
1064
1065 PacketBuffer response;
1066 GenericDNSPacketWriter<PacketBuffer> pwR(response, qname, QType::A, QClass::IN, 0);
1067 pwR.getHeader()->rd = 1;
1068 pwR.getHeader()->ra = 1;
1069 pwR.getHeader()->qr = 1;
1070 pwR.getHeader()->id = pwQ.getHeader()->id;
1071 {
1072 ComboAddress addr("2001:db8::4");
1073 pwR.startRecord(qname, QType::AAAA, 7200, QClass::IN, DNSResourceRecord::ADDITIONAL);
1074 pwR.xfrCAWithoutPort(6, addr);
1075 pwR.commit();
1076 }
1077 {
1078 ComboAddress addr("2001:db8::5");
1079 pwR.startRecord(qname, QType::AAAA, 7200, QClass::IN, DNSResourceRecord::ADDITIONAL);
1080 pwR.xfrCAWithoutPort(6, addr);
1081 pwR.commit();
1082 }
1083
1084 PC.insert(key++, boost::none, *getFlagsFromDNSHeader(pwQ.getHeader()), dnssecOK, qname, QType::A, QClass::IN, response, receivedOverUDP, 0, boost::none);
1085 BOOST_CHECK_EQUAL(PC.getSize(), key);
1086 }
1087
1088 /* insert powerdns4.com A 192.0.2.1 */
1089 {
1090 DNSName qname("powerdns4.com");
1091 PacketBuffer query;
1092 GenericDNSPacketWriter<PacketBuffer> pwQ(query, qname, QType::A, QClass::IN, 0);
1093 pwQ.getHeader()->rd = 1;
1094
1095 PacketBuffer response;
1096 GenericDNSPacketWriter<PacketBuffer> pwR(response, qname, QType::A, QClass::IN, 0);
1097 pwR.getHeader()->rd = 1;
1098 pwR.getHeader()->ra = 1;
1099 pwR.getHeader()->qr = 1;
1100 pwR.getHeader()->id = pwQ.getHeader()->id;
1101 {
1102 ComboAddress addr("192.0.2.1");
1103 pwR.startRecord(qname, QType::A, 7200, QClass::IN, DNSResourceRecord::ADDITIONAL);
1104 pwR.xfrCAWithoutPort(4, addr);
1105 pwR.commit();
1106 }
1107
1108 PC.insert(key++, boost::none, *getFlagsFromDNSHeader(pwQ.getHeader()), dnssecOK, qname, QType::A, QClass::IN, response, receivedOverUDP, 0, boost::none);
1109 BOOST_CHECK_EQUAL(PC.getSize(), key);
1110 }
1111
1112 {
1113 auto domains = PC.getDomainsContainingRecords(ComboAddress("192.0.2.1"));
1114 BOOST_CHECK_EQUAL(domains.size(), 2U);
1115 BOOST_CHECK_EQUAL(domains.count(DNSName("powerdns.com")), 1U);
1116 BOOST_CHECK_EQUAL(domains.count(DNSName("powerdns4.com")), 1U);
1117 }
1118 {
1119 auto domains = PC.getDomainsContainingRecords(ComboAddress("192.0.2.2"));
1120 BOOST_CHECK_EQUAL(domains.size(), 1U);
1121 BOOST_CHECK_EQUAL(domains.count(DNSName("powerdns.com")), 1U);
1122 }
1123 {
1124 auto domains = PC.getDomainsContainingRecords(ComboAddress("192.0.2.3"));
1125 BOOST_CHECK_EQUAL(domains.size(), 1U);
1126 BOOST_CHECK_EQUAL(domains.count(DNSName("powerdns1.com")), 1U);
1127 }
1128 {
1129 auto domains = PC.getDomainsContainingRecords(ComboAddress("192.0.2.4"));
1130 BOOST_CHECK_EQUAL(domains.size(), 1U);
1131 BOOST_CHECK_EQUAL(domains.count(DNSName("powerdns1.com")), 1U);
1132 }
1133 {
1134 auto domains = PC.getDomainsContainingRecords(ComboAddress("192.0.2.5"));
1135 BOOST_CHECK_EQUAL(domains.size(), 0U);
1136 }
1137 {
1138 auto domains = PC.getDomainsContainingRecords(ComboAddress("2001:db8::3"));
1139 BOOST_CHECK_EQUAL(domains.size(), 1U);
1140 BOOST_CHECK_EQUAL(domains.count(DNSName("powerdns1.com")), 1U);
1141 }
1142 {
1143 auto domains = PC.getDomainsContainingRecords(ComboAddress("2001:db8::4"));
1144 BOOST_CHECK_EQUAL(domains.size(), 2U);
1145 BOOST_CHECK_EQUAL(domains.count(DNSName("powerdns1.com")), 1U);
1146 BOOST_CHECK_EQUAL(domains.count(DNSName("powerdns3.com")), 1U);
1147 }
1148 {
1149 auto domains = PC.getDomainsContainingRecords(ComboAddress("2001:db8::5"));
1150 BOOST_CHECK_EQUAL(domains.size(), 1U);
1151 BOOST_CHECK_EQUAL(domains.count(DNSName("powerdns3.com")), 1U);
1152 }
1153
1154 {
1155 auto records = PC.getRecordsForDomain(DNSName("powerdns.com"));
1156 BOOST_CHECK_EQUAL(records.size(), 2U);
1157 BOOST_CHECK_EQUAL(records.count(ComboAddress("192.0.2.1")), 1U);
1158 BOOST_CHECK_EQUAL(records.count(ComboAddress("192.0.2.2")), 1U);
1159 }
1160
1161 {
1162 auto records = PC.getRecordsForDomain(DNSName("powerdns1.com"));
1163 BOOST_CHECK_EQUAL(records.size(), 4U);
1164 BOOST_CHECK_EQUAL(records.count(ComboAddress("192.0.2.3")), 1U);
1165 BOOST_CHECK_EQUAL(records.count(ComboAddress("192.0.2.4")), 1U);
1166 BOOST_CHECK_EQUAL(records.count(ComboAddress("2001:db8::3")), 1U);
1167 BOOST_CHECK_EQUAL(records.count(ComboAddress("2001:db8::4")), 1U);
1168 }
1169
1170 {
1171 auto records = PC.getRecordsForDomain(DNSName("powerdns2.com"));
1172 BOOST_CHECK_EQUAL(records.size(), 0U);
1173 }
1174
1175 {
1176 auto records = PC.getRecordsForDomain(DNSName("powerdns3.com"));
1177 BOOST_CHECK_EQUAL(records.size(), 2U);
1178 BOOST_CHECK_EQUAL(records.count(ComboAddress("2001:db8::4")), 1U);
1179 BOOST_CHECK_EQUAL(records.count(ComboAddress("2001:db8::4")), 1U);
1180 }
1181
1182 {
1183 auto records = PC.getRecordsForDomain(DNSName("powerdns4.com"));
1184 BOOST_CHECK_EQUAL(records.size(), 1U);
1185 BOOST_CHECK_EQUAL(records.count(ComboAddress("192.0.2.1")), 1U);
1186 }
1187
1188 {
1189 auto records = PC.getRecordsForDomain(DNSName("powerdns5.com"));
1190 BOOST_CHECK_EQUAL(records.size(), 0U);
1191 }
1192}
1193
8eff5c8d
RG
1194BOOST_AUTO_TEST_CASE(test_PacketCacheXFR) {
1195 const size_t maxEntries = 150000;
1196 DNSDistPacketCache PC(maxEntries, 86400, 1);
1197 BOOST_CHECK_EQUAL(PC.getSize(), 0U);
1198
1199 const std::set<QType> xfrTypes = { QType::AXFR, QType::IXFR };
1200 for (const auto& type : xfrTypes) {
1201 bool dnssecOK = false;
8eff5c8d
RG
1202 InternalQueryState ids;
1203 ids.qtype = type;
1204 ids.qclass = QClass::IN;
1205 ids.protocol = dnsdist::Protocol::DoUDP;
1206 ids.qname = DNSName("powerdns.com.");
1207
1208 PacketBuffer query;
1209 GenericDNSPacketWriter<PacketBuffer> pwQ(query, ids.qname, ids.qtype, ids.qclass, 0);
1210 pwQ.getHeader()->rd = 1;
1211
1212 PacketBuffer response;
1213 GenericDNSPacketWriter<PacketBuffer> pwR(response, ids.qname, ids.qtype, ids.qclass, 0);
1214 pwR.getHeader()->rd = 1;
1215 pwR.getHeader()->ra = 1;
1216 pwR.getHeader()->qr = 1;
1217 pwR.getHeader()->id = pwQ.getHeader()->id;
1218 pwR.startRecord(ids.qname, QType::A, 7200, QClass::IN, DNSResourceRecord::ANSWER);
1219 pwR.xfr32BitInt(0x01020304);
1220 pwR.commit();
1221
1222 uint32_t key = 0;
1223 boost::optional<Netmask> subnet;
80a210f2
RG
1224 DNSQuestion dnsQuestion(ids, query);
1225 bool found = PC.get(dnsQuestion, 0, &key, subnet, dnssecOK, receivedOverUDP);
8eff5c8d
RG
1226 BOOST_CHECK_EQUAL(found, false);
1227 BOOST_CHECK(!subnet);
1228
80a210f2
RG
1229 PC.insert(key, subnet, *(getFlagsFromDNSHeader(dnsQuestion.getHeader().get())), dnssecOK, ids.qname, ids.qtype, ids.qclass, response, receivedOverUDP, 0, boost::none);
1230 found = PC.get(dnsQuestion, pwR.getHeader()->id, &key, subnet, dnssecOK, receivedOverUDP, 0, true);
8eff5c8d
RG
1231 BOOST_CHECK_EQUAL(found, false);
1232 }
1233}
1234
886e2cf2 1235BOOST_AUTO_TEST_SUITE_END()