]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/test-dnsdistpacketcache_cc.cc
Merge pull request #7677 from rgacogne/dnsdist-logging-facility
[thirdparty/pdns.git] / pdns / test-dnsdistpacketcache_cc.cc
CommitLineData
886e2cf2
RG
1#define BOOST_TEST_DYN_LINK
2#define BOOST_TEST_NO_MAIN
3
4#include <boost/test/unit_test.hpp>
5
78e3ac9e 6#include "ednscookies.hh"
8dcdbdb1 7#include "ednsoptions.hh"
78e3ac9e 8#include "ednssubnet.hh"
1ea747c0 9#include "dnsdist.hh"
886e2cf2 10#include "iputils.hh"
886e2cf2 11#include "dnswriter.hh"
1ea747c0 12#include "dnsdist-cache.hh"
5ffb2f83 13#include "gettime.hh"
886e2cf2 14
c7f29d3e 15BOOST_AUTO_TEST_SUITE(test_dnsdistpacketcache_cc)
886e2cf2
RG
16
17BOOST_AUTO_TEST_CASE(test_PacketCacheSimple) {
18 const size_t maxEntries = 150000;
19 DNSDistPacketCache PC(maxEntries, 86400, 1);
20 BOOST_CHECK_EQUAL(PC.getSize(), 0);
5ffb2f83
CH
21 struct timespec queryTime;
22 gettime(&queryTime); // does not have to be accurate ("realTime") in tests
886e2cf2
RG
23
24 size_t counter=0;
25 size_t skipped=0;
1ea747c0 26 ComboAddress remote;
d7728daf 27 bool dnssecOK = false;
886e2cf2
RG
28 try {
29 for(counter = 0; counter < 100000; ++counter) {
490dc586 30 DNSName a=DNSName(std::to_string(counter))+DNSName(" hello");
886e2cf2
RG
31 BOOST_CHECK_EQUAL(DNSName(a.toString()), a);
32
33 vector<uint8_t> query;
34 DNSPacketWriter pwQ(query, a, QType::A, QClass::IN, 0);
35 pwQ.getHeader()->rd = 1;
36
37 vector<uint8_t> response;
38 DNSPacketWriter pwR(response, a, QType::A, QClass::IN, 0);
39 pwR.getHeader()->rd = 1;
40 pwR.getHeader()->ra = 1;
41 pwR.getHeader()->qr = 1;
42 pwR.getHeader()->id = pwQ.getHeader()->id;
f627611d 43 pwR.startRecord(a, QType::A, 7200, QClass::IN, DNSResourceRecord::ANSWER);
886e2cf2
RG
44 pwR.xfr32BitInt(0x01020304);
45 pwR.commit();
46 uint16_t responseLen = response.size();
47
48 char responseBuf[4096];
49 uint16_t responseBufSize = sizeof(responseBuf);
50 uint32_t key = 0;
78e3ac9e 51 boost::optional<Netmask> subnet;
8dcdbdb1 52 auto dh = reinterpret_cast<dnsheader*>(query.data());
e7c732b8 53 DNSQuestion dq(&a, QType::A, QClass::IN, 0, &remote, &remote, dh, query.size(), query.size(), false, &queryTime);
d7728daf 54 bool found = PC.get(dq, a.wirelength(), 0, responseBuf, &responseBufSize, &key, subnet, dnssecOK);
886e2cf2 55 BOOST_CHECK_EQUAL(found, false);
78e3ac9e 56 BOOST_CHECK(!subnet);
886e2cf2 57
d7728daf 58 PC.insert(key, subnet, *(getFlagsFromDNSHeader(dh)), dnssecOK, a, QType::A, QClass::IN, (const char*) response.data(), responseLen, false, 0, boost::none);
886e2cf2 59
d7728daf 60 found = PC.get(dq, a.wirelength(), pwR.getHeader()->id, responseBuf, &responseBufSize, &key, subnet, dnssecOK, 0, true);
886e2cf2
RG
61 if (found == true) {
62 BOOST_CHECK_EQUAL(responseBufSize, responseLen);
63 int match = memcmp(responseBuf, response.data(), responseLen);
64 BOOST_CHECK_EQUAL(match, 0);
78e3ac9e 65 BOOST_CHECK(!subnet);
886e2cf2
RG
66 }
67 else {
68 skipped++;
69 }
70 }
71
72 BOOST_CHECK_EQUAL(skipped, PC.getInsertCollisions());
73 BOOST_CHECK_EQUAL(PC.getSize(), counter - skipped);
74
75 size_t deleted=0;
76 size_t delcounter=0;
77 for(delcounter=0; delcounter < counter/1000; ++delcounter) {
490dc586 78 DNSName a=DNSName(std::to_string(delcounter))+DNSName(" hello");
886e2cf2
RG
79 vector<uint8_t> query;
80 DNSPacketWriter pwQ(query, a, QType::A, QClass::IN, 0);
81 pwQ.getHeader()->rd = 1;
82 char responseBuf[4096];
83 uint16_t responseBufSize = sizeof(responseBuf);
84 uint32_t key = 0;
78e3ac9e 85 boost::optional<Netmask> subnet;
e7c732b8 86 DNSQuestion dq(&a, QType::A, QClass::IN, 0, &remote, &remote, (struct dnsheader*) query.data(), query.size(), query.size(), false, &queryTime);
d7728daf 87 bool found = PC.get(dq, a.wirelength(), 0, responseBuf, &responseBufSize, &key, subnet, dnssecOK);
886e2cf2 88 if (found == true) {
f627611d
RG
89 auto removed = PC.expungeByName(a);
90 BOOST_CHECK_EQUAL(removed, 1);
91 deleted += removed;
886e2cf2
RG
92 }
93 }
94 BOOST_CHECK_EQUAL(PC.getSize(), counter - skipped - deleted);
95
96 size_t matches=0;
97 vector<DNSResourceRecord> entry;
98 size_t expected=counter-skipped-deleted;
99 for(; delcounter < counter; ++delcounter) {
490dc586 100 DNSName a(DNSName(std::to_string(delcounter))+DNSName(" hello"));
886e2cf2
RG
101 vector<uint8_t> query;
102 DNSPacketWriter pwQ(query, a, QType::A, QClass::IN, 0);
103 pwQ.getHeader()->rd = 1;
104 uint16_t len = query.size();
105 uint32_t key = 0;
78e3ac9e 106 boost::optional<Netmask> subnet;
886e2cf2
RG
107 char response[4096];
108 uint16_t responseSize = sizeof(response);
e7c732b8 109 DNSQuestion dq(&a, QType::A, QClass::IN, 0, &remote, &remote, (struct dnsheader*) query.data(), len, query.size(), false, &queryTime);
d7728daf 110 if(PC.get(dq, a.wirelength(), pwQ.getHeader()->id, response, &responseSize, &key, subnet, dnssecOK)) {
490dc586 111 matches++;
886e2cf2
RG
112 }
113 }
490dc586 114
6d1a9248 115 /* in the unlikely event that the test took so long that the entries did expire.. */
f627611d
RG
116 auto expired = PC.purgeExpired();
117 BOOST_CHECK_EQUAL(matches + expired, expected);
118
119 auto remaining = PC.getSize();
120 auto removed = PC.expungeByName(DNSName(" hello"), QType::ANY, true);
490dc586 121 BOOST_CHECK_EQUAL(PC.getSize(), 0);
f627611d 122 BOOST_CHECK_EQUAL(removed, remaining);
886e2cf2
RG
123 }
124 catch(PDNSException& e) {
125 cerr<<"Had error: "<<e.reason<<endl;
126 throw;
127 }
128}
129
acb8f5d5
CH
130BOOST_AUTO_TEST_CASE(test_PacketCacheServFailTTL) {
131 const size_t maxEntries = 150000;
132 DNSDistPacketCache PC(maxEntries, 86400, 1);
5ffb2f83
CH
133 struct timespec queryTime;
134 gettime(&queryTime); // does not have to be accurate ("realTime") in tests
acb8f5d5
CH
135
136 ComboAddress remote;
d7728daf 137 bool dnssecOK = false;
acb8f5d5
CH
138 try {
139 DNSName a = DNSName("servfail");
140 BOOST_CHECK_EQUAL(DNSName(a.toString()), a);
141
142 vector<uint8_t> query;
143 DNSPacketWriter pwQ(query, a, QType::A, QClass::IN, 0);
144 pwQ.getHeader()->rd = 1;
145
146 vector<uint8_t> response;
147 DNSPacketWriter pwR(response, a, QType::A, QClass::IN, 0);
148 pwR.getHeader()->rd = 1;
149 pwR.getHeader()->ra = 0;
150 pwR.getHeader()->qr = 1;
151 pwR.getHeader()->rcode = RCode::ServFail;
152 pwR.getHeader()->id = pwQ.getHeader()->id;
153 pwR.commit();
154 uint16_t responseLen = response.size();
155
156 char responseBuf[4096];
157 uint16_t responseBufSize = sizeof(responseBuf);
158 uint32_t key = 0;
78e3ac9e 159 boost::optional<Netmask> subnet;
8dcdbdb1 160 auto dh = reinterpret_cast<dnsheader*>(query.data());
e7c732b8 161 DNSQuestion dq(&a, QType::A, QClass::IN, 0, &remote, &remote, dh, query.size(), query.size(), false, &queryTime);
d7728daf 162 bool found = PC.get(dq, a.wirelength(), 0, responseBuf, &responseBufSize, &key, subnet, dnssecOK);
acb8f5d5 163 BOOST_CHECK_EQUAL(found, false);
78e3ac9e 164 BOOST_CHECK(!subnet);
acb8f5d5
CH
165
166 // Insert with failure-TTL of 0 (-> should not enter cache).
d7728daf
RG
167 PC.insert(key, subnet, *(getFlagsFromDNSHeader(dh)), dnssecOK, a, QType::A, QClass::IN, (const char*) response.data(), responseLen, false, RCode::ServFail, boost::optional<uint32_t>(0));
168 found = PC.get(dq, a.wirelength(), pwR.getHeader()->id, responseBuf, &responseBufSize, &key, subnet, dnssecOK, 0, true);
acb8f5d5 169 BOOST_CHECK_EQUAL(found, false);
78e3ac9e 170 BOOST_CHECK(!subnet);
acb8f5d5 171
56459632 172 // Insert with failure-TTL non-zero (-> should enter cache).
d7728daf
RG
173 PC.insert(key, subnet, *(getFlagsFromDNSHeader(dh)), dnssecOK, a, QType::A, QClass::IN, (const char*) response.data(), responseLen, false, RCode::ServFail, boost::optional<uint32_t>(300));
174 found = PC.get(dq, a.wirelength(), pwR.getHeader()->id, responseBuf, &responseBufSize, &key, subnet, dnssecOK, 0, true);
acb8f5d5 175 BOOST_CHECK_EQUAL(found, true);
78e3ac9e 176 BOOST_CHECK(!subnet);
acb8f5d5
CH
177 }
178 catch(PDNSException& e) {
179 cerr<<"Had error: "<<e.reason<<endl;
180 throw;
181 }
182}
183
47698274
RG
184BOOST_AUTO_TEST_CASE(test_PacketCacheNoDataTTL) {
185 const size_t maxEntries = 150000;
186 DNSDistPacketCache PC(maxEntries, /* maxTTL */ 86400, /* minTTL */ 1, /* tempFailureTTL */ 60, /* maxNegativeTTL */ 1);
187
188 struct timespec queryTime;
189 gettime(&queryTime); // does not have to be accurate ("realTime") in tests
190
191 ComboAddress remote;
d7728daf 192 bool dnssecOK = false;
47698274
RG
193 try {
194 DNSName name("nodata");
195 vector<uint8_t> query;
196 DNSPacketWriter pwQ(query, name, QType::A, QClass::IN, 0);
197 pwQ.getHeader()->rd = 1;
198
199 vector<uint8_t> response;
200 DNSPacketWriter pwR(response, name, QType::A, QClass::IN, 0);
201 pwR.getHeader()->rd = 1;
202 pwR.getHeader()->ra = 0;
203 pwR.getHeader()->qr = 1;
204 pwR.getHeader()->rcode = RCode::NoError;
205 pwR.getHeader()->id = pwQ.getHeader()->id;
206 pwR.commit();
207 pwR.startRecord(name, QType::SOA, 86400, QClass::IN, DNSResourceRecord::AUTHORITY);
208 pwR.commit();
209 pwR.addOpt(4096, 0, 0);
210 pwR.commit();
211
212 uint16_t responseLen = response.size();
213
214 char responseBuf[4096];
215 uint16_t responseBufSize = sizeof(responseBuf);
216 uint32_t key = 0;
78e3ac9e 217 boost::optional<Netmask> subnet;
8dcdbdb1 218 auto dh = reinterpret_cast<dnsheader*>(query.data());
e7c732b8 219 DNSQuestion dq(&name, QType::A, QClass::IN, 0, &remote, &remote, dh, query.size(), query.size(), false, &queryTime);
d7728daf 220 bool found = PC.get(dq, name.wirelength(), 0, responseBuf, &responseBufSize, &key, subnet, dnssecOK);
47698274 221 BOOST_CHECK_EQUAL(found, false);
78e3ac9e 222 BOOST_CHECK(!subnet);
47698274 223
d7728daf
RG
224 PC.insert(key, subnet, *(getFlagsFromDNSHeader(dh)), dnssecOK, name, QType::A, QClass::IN, reinterpret_cast<const char*>(response.data()), responseLen, false, RCode::NoError, boost::none);
225 found = PC.get(dq, name.wirelength(), pwR.getHeader()->id, responseBuf, &responseBufSize, &key, subnet, dnssecOK, 0, true);
47698274 226 BOOST_CHECK_EQUAL(found, true);
78e3ac9e
RG
227 BOOST_CHECK(!subnet);
228
47698274
RG
229 sleep(2);
230 /* it should have expired by now */
d7728daf 231 found = PC.get(dq, name.wirelength(), pwR.getHeader()->id, responseBuf, &responseBufSize, &key, subnet, dnssecOK, 0, true);
47698274 232 BOOST_CHECK_EQUAL(found, false);
78e3ac9e 233 BOOST_CHECK(!subnet);
47698274
RG
234 }
235 catch(const PDNSException& e) {
236 cerr<<"Had error: "<<e.reason<<endl;
237 throw;
238 }
239}
240
241BOOST_AUTO_TEST_CASE(test_PacketCacheNXDomainTTL) {
242 const size_t maxEntries = 150000;
243 DNSDistPacketCache PC(maxEntries, /* maxTTL */ 86400, /* minTTL */ 1, /* tempFailureTTL */ 60, /* maxNegativeTTL */ 1);
244
245 struct timespec queryTime;
246 gettime(&queryTime); // does not have to be accurate ("realTime") in tests
247
248 ComboAddress remote;
d7728daf 249 bool dnssecOK = false;
47698274
RG
250 try {
251 DNSName name("nxdomain");
252 vector<uint8_t> query;
253 DNSPacketWriter pwQ(query, name, QType::A, QClass::IN, 0);
254 pwQ.getHeader()->rd = 1;
255
256 vector<uint8_t> response;
257 DNSPacketWriter pwR(response, name, QType::A, QClass::IN, 0);
258 pwR.getHeader()->rd = 1;
259 pwR.getHeader()->ra = 0;
260 pwR.getHeader()->qr = 1;
261 pwR.getHeader()->rcode = RCode::NXDomain;
262 pwR.getHeader()->id = pwQ.getHeader()->id;
263 pwR.commit();
264 pwR.startRecord(name, QType::SOA, 86400, QClass::IN, DNSResourceRecord::AUTHORITY);
265 pwR.commit();
266 pwR.addOpt(4096, 0, 0);
267 pwR.commit();
268
269 uint16_t responseLen = response.size();
270
271 char responseBuf[4096];
272 uint16_t responseBufSize = sizeof(responseBuf);
273 uint32_t key = 0;
78e3ac9e 274 boost::optional<Netmask> subnet;
8dcdbdb1 275 auto dh = reinterpret_cast<dnsheader*>(query.data());
e7c732b8 276 DNSQuestion dq(&name, QType::A, QClass::IN, 0, &remote, &remote, dh, query.size(), query.size(), false, &queryTime);
d7728daf 277 bool found = PC.get(dq, name.wirelength(), 0, responseBuf, &responseBufSize, &key, subnet, dnssecOK);
47698274 278 BOOST_CHECK_EQUAL(found, false);
78e3ac9e 279 BOOST_CHECK(!subnet);
47698274 280
d7728daf
RG
281 PC.insert(key, subnet, *(getFlagsFromDNSHeader(dh)), dnssecOK, name, QType::A, QClass::IN, reinterpret_cast<const char*>(response.data()), responseLen, false, RCode::NXDomain, boost::none);
282 found = PC.get(dq, name.wirelength(), pwR.getHeader()->id, responseBuf, &responseBufSize, &key, subnet, dnssecOK, 0, true);
47698274 283 BOOST_CHECK_EQUAL(found, true);
78e3ac9e
RG
284 BOOST_CHECK(!subnet);
285
47698274
RG
286 sleep(2);
287 /* it should have expired by now */
d7728daf 288 found = PC.get(dq, name.wirelength(), pwR.getHeader()->id, responseBuf, &responseBufSize, &key, subnet, dnssecOK, 0, true);
47698274 289 BOOST_CHECK_EQUAL(found, false);
78e3ac9e 290 BOOST_CHECK(!subnet);
47698274
RG
291 }
292 catch(const PDNSException& e) {
293 cerr<<"Had error: "<<e.reason<<endl;
294 throw;
295 }
296}
297
886e2cf2
RG
298static DNSDistPacketCache PC(500000);
299
af619119 300static void *threadMangler(void* off)
886e2cf2 301{
5ffb2f83
CH
302 struct timespec queryTime;
303 gettime(&queryTime); // does not have to be accurate ("realTime") in tests
886e2cf2 304 try {
1ea747c0 305 ComboAddress remote;
d7728daf 306 bool dnssecOK = false;
af619119 307 unsigned int offset=(unsigned int)(unsigned long)off;
886e2cf2
RG
308 for(unsigned int counter=0; counter < 100000; ++counter) {
309 DNSName a=DNSName("hello ")+DNSName(std::to_string(counter+offset));
310 vector<uint8_t> query;
311 DNSPacketWriter pwQ(query, a, QType::A, QClass::IN, 0);
312 pwQ.getHeader()->rd = 1;
313
314 vector<uint8_t> response;
315 DNSPacketWriter pwR(response, a, QType::A, QClass::IN, 0);
316 pwR.getHeader()->rd = 1;
317 pwR.getHeader()->ra = 1;
318 pwR.getHeader()->qr = 1;
319 pwR.getHeader()->id = pwQ.getHeader()->id;
320 pwR.startRecord(a, QType::A, 3600, QClass::IN, DNSResourceRecord::ANSWER);
321 pwR.xfr32BitInt(0x01020304);
322 pwR.commit();
323 uint16_t responseLen = response.size();
324
325 char responseBuf[4096];
326 uint16_t responseBufSize = sizeof(responseBuf);
327 uint32_t key = 0;
78e3ac9e 328 boost::optional<Netmask> subnet;
8dcdbdb1 329 auto dh = reinterpret_cast<dnsheader*>(query.data());
e7c732b8 330 DNSQuestion dq(&a, QType::A, QClass::IN, 0, &remote, &remote, dh, query.size(), query.size(), false, &queryTime);
d7728daf 331 PC.get(dq, a.wirelength(), 0, responseBuf, &responseBufSize, &key, subnet, dnssecOK);
886e2cf2 332
d7728daf 333 PC.insert(key, subnet, *(getFlagsFromDNSHeader(dh)), dnssecOK, a, QType::A, QClass::IN, (const char*) response.data(), responseLen, false, 0, boost::none);
886e2cf2
RG
334 }
335 }
336 catch(PDNSException& e) {
337 cerr<<"Had error: "<<e.reason<<endl;
338 throw;
339 }
340 return 0;
341}
342
343AtomicCounter g_missing;
344
af619119 345static void *threadReader(void* off)
886e2cf2 346{
d7728daf 347 bool dnssecOK = false;
5ffb2f83
CH
348 struct timespec queryTime;
349 gettime(&queryTime); // does not have to be accurate ("realTime") in tests
886e2cf2
RG
350 try
351 {
af619119 352 unsigned int offset=(unsigned int)(unsigned long)off;
886e2cf2 353 vector<DNSResourceRecord> entry;
1ea747c0 354 ComboAddress remote;
886e2cf2
RG
355 for(unsigned int counter=0; counter < 100000; ++counter) {
356 DNSName a=DNSName("hello ")+DNSName(std::to_string(counter+offset));
357 vector<uint8_t> query;
358 DNSPacketWriter pwQ(query, a, QType::A, QClass::IN, 0);
359 pwQ.getHeader()->rd = 1;
360
361 char responseBuf[4096];
362 uint16_t responseBufSize = sizeof(responseBuf);
363 uint32_t key = 0;
78e3ac9e 364 boost::optional<Netmask> subnet;
e7c732b8 365 DNSQuestion dq(&a, QType::A, QClass::IN, 0, &remote, &remote, (struct dnsheader*) query.data(), query.size(), query.size(), false, &queryTime);
d7728daf 366 bool found = PC.get(dq, a.wirelength(), 0, responseBuf, &responseBufSize, &key, subnet, dnssecOK);
886e2cf2
RG
367 if (!found) {
368 g_missing++;
369 }
370 }
371 }
372 catch(PDNSException& e) {
373 cerr<<"Had error in threadReader: "<<e.reason<<endl;
374 throw;
375 }
376 return 0;
377}
378
379BOOST_AUTO_TEST_CASE(test_PacketCacheThreaded) {
380 try {
381 pthread_t tid[4];
382 for(int i=0; i < 4; ++i)
383 pthread_create(&tid[i], 0, threadMangler, (void*)(i*1000000UL));
384 void* res;
385 for(int i=0; i < 4 ; ++i)
386 pthread_join(tid[i], &res);
387
388 BOOST_CHECK_EQUAL(PC.getSize() + PC.getDeferredInserts() + PC.getInsertCollisions(), 400000);
389 BOOST_CHECK_SMALL(1.0*PC.getInsertCollisions(), 10000.0);
390
391 for(int i=0; i < 4; ++i)
392 pthread_create(&tid[i], 0, threadReader, (void*)(i*1000000UL));
393 for(int i=0; i < 4 ; ++i)
394 pthread_join(tid[i], &res);
395
396 BOOST_CHECK((PC.getDeferredInserts() + PC.getDeferredLookups() + PC.getInsertCollisions()) >= g_missing);
397 }
398 catch(PDNSException& e) {
399 cerr<<"Had error: "<<e.reason<<endl;
400 throw;
401 }
402
403}
404
78e3ac9e
RG
405BOOST_AUTO_TEST_CASE(test_PCCollision) {
406 const size_t maxEntries = 150000;
407 DNSDistPacketCache PC(maxEntries, 86400, 1, 60, 3600, 60, false, 1, true, true);
408 BOOST_CHECK_EQUAL(PC.getSize(), 0);
409
410 DNSName qname("www.powerdns.com.");
411 uint16_t qtype = QType::AAAA;
412 uint16_t qid = 0x42;
413 uint32_t key;
414 uint32_t secondKey;
415 boost::optional<Netmask> subnetOut;
d7728daf 416 bool dnssecOK = false;
78e3ac9e
RG
417
418 /* lookup for a query with an ECS value of 10.0.118.46/32,
419 insert a corresponding response */
420 {
421 vector<uint8_t> query;
422 DNSPacketWriter pwQ(query, qname, qtype, QClass::IN, 0);
423 pwQ.getHeader()->rd = 1;
424 pwQ.getHeader()->id = qid;
425 DNSPacketWriter::optvect_t ednsOptions;
426 EDNSSubnetOpts opt;
427 opt.source = Netmask("10.0.118.46/32");
428 ednsOptions.push_back(std::make_pair(EDNSOptionCode::ECS, makeEDNSSubnetOptsString(opt)));
429 pwQ.addOpt(512, 0, 0, ednsOptions);
430 pwQ.commit();
431
432 char responseBuf[4096];
433 uint16_t responseBufSize = sizeof(responseBuf);
434 ComboAddress remote("192.0.2.1");
435 struct timespec queryTime;
436 gettime(&queryTime);
e7c732b8 437 DNSQuestion dq(&qname, QType::AAAA, QClass::IN, 0, &remote, &remote, pwQ.getHeader(), query.size(), query.size(), false, &queryTime);
d7728daf 438 bool found = PC.get(dq, qname.wirelength(), 0, responseBuf, &responseBufSize, &key, subnetOut, dnssecOK);
78e3ac9e
RG
439 BOOST_CHECK_EQUAL(found, false);
440 BOOST_REQUIRE(subnetOut);
441 BOOST_CHECK_EQUAL(subnetOut->toString(), opt.source.toString());
442
443 vector<uint8_t> response;
444 DNSPacketWriter pwR(response, qname, qtype, QClass::IN, 0);
445 pwR.getHeader()->rd = 1;
446 pwR.getHeader()->id = qid;
447 pwR.startRecord(qname, qtype, 100, QClass::IN, DNSResourceRecord::ANSWER);
448 ComboAddress v6("::1");
449 pwR.xfrCAWithoutPort(6, v6);
450 pwR.commit();
451 pwR.addOpt(512, 0, 0, ednsOptions);
452 pwR.commit();
453
d7728daf 454 PC.insert(key, subnetOut, *(getFlagsFromDNSHeader(pwR.getHeader())), dnssecOK, qname, qtype, QClass::IN, reinterpret_cast<const char*>(response.data()), response.size(), false, RCode::NoError, boost::none);
78e3ac9e
RG
455 BOOST_CHECK_EQUAL(PC.getSize(), 1);
456
d7728daf 457 found = PC.get(dq, qname.wirelength(), 0, responseBuf, &responseBufSize, &key, subnetOut, dnssecOK);
78e3ac9e
RG
458 BOOST_CHECK_EQUAL(found, true);
459 BOOST_REQUIRE(subnetOut);
460 BOOST_CHECK_EQUAL(subnetOut->toString(), opt.source.toString());
461 }
462
463 /* now lookup for the same query with an ECS value of 10.0.123.193/32
464 we should get the same key (collision) but no match */
465 {
466 vector<uint8_t> query;
467 DNSPacketWriter pwQ(query, qname, qtype, QClass::IN, 0);
468 pwQ.getHeader()->rd = 1;
469 pwQ.getHeader()->id = qid;
470 DNSPacketWriter::optvect_t ednsOptions;
471 EDNSSubnetOpts opt;
472 opt.source = Netmask("10.0.123.193/32");
473 ednsOptions.push_back(std::make_pair(EDNSOptionCode::ECS, makeEDNSSubnetOptsString(opt)));
474 pwQ.addOpt(512, 0, 0, ednsOptions);
475 pwQ.commit();
476
477 char responseBuf[4096];
478 uint16_t responseBufSize = sizeof(responseBuf);
479 ComboAddress remote("192.0.2.1");
480 struct timespec queryTime;
481 gettime(&queryTime);
e7c732b8 482 DNSQuestion dq(&qname, QType::AAAA, QClass::IN, 0, &remote, &remote, pwQ.getHeader(), query.size(), query.size(), false, &queryTime);
d7728daf 483 bool found = PC.get(dq, qname.wirelength(), 0, responseBuf, &responseBufSize, &secondKey, subnetOut, dnssecOK);
78e3ac9e
RG
484 BOOST_CHECK_EQUAL(found, false);
485 BOOST_CHECK_EQUAL(secondKey, key);
486 BOOST_REQUIRE(subnetOut);
487 BOOST_CHECK_EQUAL(subnetOut->toString(), opt.source.toString());
488 BOOST_CHECK_EQUAL(PC.getLookupCollisions(), 1);
489 }
490}
491
d7728daf
RG
492BOOST_AUTO_TEST_CASE(test_PCDNSSECCollision) {
493 const size_t maxEntries = 150000;
494 DNSDistPacketCache PC(maxEntries, 86400, 1, 60, 3600, 60, false, 1, true, true);
495 BOOST_CHECK_EQUAL(PC.getSize(), 0);
496
497 DNSName qname("www.powerdns.com.");
498 uint16_t qtype = QType::AAAA;
499 uint16_t qid = 0x42;
500 uint32_t key;
501 boost::optional<Netmask> subnetOut;
502
503 /* lookup for a query with DNSSEC OK,
504 insert a corresponding response with DO set,
505 check that it doesn't match without DO, but does with it */
506 {
507 vector<uint8_t> query;
508 DNSPacketWriter pwQ(query, qname, qtype, QClass::IN, 0);
509 pwQ.getHeader()->rd = 1;
510 pwQ.getHeader()->id = qid;
511 pwQ.addOpt(512, 0, EDNS_HEADER_FLAG_DO);
512 pwQ.commit();
513
514 char responseBuf[4096];
515 uint16_t responseBufSize = sizeof(responseBuf);
516 ComboAddress remote("192.0.2.1");
517 struct timespec queryTime;
518 gettime(&queryTime);
519 DNSQuestion dq(&qname, QType::AAAA, QClass::IN, 0, &remote, &remote, pwQ.getHeader(), query.size(), query.size(), false, &queryTime);
520 bool found = PC.get(dq, qname.wirelength(), 0, responseBuf, &responseBufSize, &key, subnetOut, true);
521 BOOST_CHECK_EQUAL(found, false);
522
523 vector<uint8_t> response;
524 DNSPacketWriter pwR(response, qname, qtype, QClass::IN, 0);
525 pwR.getHeader()->rd = 1;
526 pwR.getHeader()->id = qid;
527 pwR.startRecord(qname, qtype, 100, QClass::IN, DNSResourceRecord::ANSWER);
528 ComboAddress v6("::1");
529 pwR.xfrCAWithoutPort(6, v6);
530 pwR.commit();
531 pwR.addOpt(512, 0, EDNS_HEADER_FLAG_DO);
532 pwR.commit();
533
534 PC.insert(key, subnetOut, *(getFlagsFromDNSHeader(pwR.getHeader())), /* DNSSEC OK is set */ true, qname, qtype, QClass::IN, reinterpret_cast<const char*>(response.data()), response.size(), false, RCode::NoError, boost::none);
535 BOOST_CHECK_EQUAL(PC.getSize(), 1);
536
537 found = PC.get(dq, qname.wirelength(), 0, responseBuf, &responseBufSize, &key, subnetOut, false);
538 BOOST_CHECK_EQUAL(found, false);
539
540 found = PC.get(dq, qname.wirelength(), 0, responseBuf, &responseBufSize, &key, subnetOut, true);
541 BOOST_CHECK_EQUAL(found, true);
542 }
543
544}
545
886e2cf2 546BOOST_AUTO_TEST_SUITE_END()