1 #define BOOST_TEST_DYN_LINK
2 #define BOOST_TEST_NO_MAIN
4 #include <boost/test/unit_test.hpp>
6 #include "ednscookies.hh"
7 #include "ednsoptions.hh"
8 #include "ednssubnet.hh"
11 #include "dnswriter.hh"
12 #include "dnsdist-cache.hh"
15 BOOST_AUTO_TEST_SUITE(dnsdistpacketcache_cc
)
17 BOOST_AUTO_TEST_CASE(test_PacketCacheSimple
) {
18 const size_t maxEntries
= 150000;
19 DNSDistPacketCache
PC(maxEntries
, 86400, 1);
20 BOOST_CHECK_EQUAL(PC
.getSize(), 0);
21 struct timespec queryTime
;
22 gettime(&queryTime
); // does not have to be accurate ("realTime") in tests
28 for(counter
= 0; counter
< 100000; ++counter
) {
29 DNSName a
=DNSName(std::to_string(counter
))+DNSName(" hello");
30 BOOST_CHECK_EQUAL(DNSName(a
.toString()), a
);
32 vector
<uint8_t> query
;
33 DNSPacketWriter
pwQ(query
, a
, QType::A
, QClass::IN
, 0);
34 pwQ
.getHeader()->rd
= 1;
36 vector
<uint8_t> response
;
37 DNSPacketWriter
pwR(response
, a
, QType::A
, QClass::IN
, 0);
38 pwR
.getHeader()->rd
= 1;
39 pwR
.getHeader()->ra
= 1;
40 pwR
.getHeader()->qr
= 1;
41 pwR
.getHeader()->id
= pwQ
.getHeader()->id
;
42 pwR
.startRecord(a
, QType::A
, 100, QClass::IN
, DNSResourceRecord::ANSWER
);
43 pwR
.xfr32BitInt(0x01020304);
45 uint16_t responseLen
= response
.size();
47 char responseBuf
[4096];
48 uint16_t responseBufSize
= sizeof(responseBuf
);
50 boost::optional
<Netmask
> subnet
;
51 auto dh
= reinterpret_cast<dnsheader
*>(query
.data());
52 DNSQuestion
dq(&a
, QType::A
, QClass::IN
, 0, &remote
, &remote
, dh
, query
.size(), query
.size(), false, &queryTime
);
53 bool found
= PC
.get(dq
, a
.wirelength(), 0, responseBuf
, &responseBufSize
, &key
, subnet
);
54 BOOST_CHECK_EQUAL(found
, false);
57 PC
.insert(key
, subnet
, *(getFlagsFromDNSHeader(dh
)), a
, QType::A
, QClass::IN
, (const char*) response
.data(), responseLen
, false, 0, boost::none
);
59 found
= PC
.get(dq
, a
.wirelength(), pwR
.getHeader()->id
, responseBuf
, &responseBufSize
, &key
, subnet
, 0, true);
61 BOOST_CHECK_EQUAL(responseBufSize
, responseLen
);
62 int match
= memcmp(responseBuf
, response
.data(), responseLen
);
63 BOOST_CHECK_EQUAL(match
, 0);
71 BOOST_CHECK_EQUAL(skipped
, PC
.getInsertCollisions());
72 BOOST_CHECK_EQUAL(PC
.getSize(), counter
- skipped
);
76 for(delcounter
=0; delcounter
< counter
/1000; ++delcounter
) {
77 DNSName a
=DNSName(std::to_string(delcounter
))+DNSName(" hello");
78 vector
<uint8_t> query
;
79 DNSPacketWriter
pwQ(query
, a
, QType::A
, QClass::IN
, 0);
80 pwQ
.getHeader()->rd
= 1;
81 char responseBuf
[4096];
82 uint16_t responseBufSize
= sizeof(responseBuf
);
84 boost::optional
<Netmask
> subnet
;
85 DNSQuestion
dq(&a
, QType::A
, QClass::IN
, 0, &remote
, &remote
, (struct dnsheader
*) query
.data(), query
.size(), query
.size(), false, &queryTime
);
86 bool found
= PC
.get(dq
, a
.wirelength(), 0, responseBuf
, &responseBufSize
, &key
, subnet
);
92 BOOST_CHECK_EQUAL(PC
.getSize(), counter
- skipped
- deleted
);
96 vector
<DNSResourceRecord
> entry
;
97 size_t expected
=counter
-skipped
-deleted
;
98 for(; delcounter
< counter
; ++delcounter
) {
99 DNSName
a(DNSName(std::to_string(delcounter
))+DNSName(" hello"));
100 vector
<uint8_t> query
;
101 DNSPacketWriter
pwQ(query
, a
, QType::A
, QClass::IN
, 0);
102 pwQ
.getHeader()->rd
= 1;
103 uint16_t len
= query
.size();
105 boost::optional
<Netmask
> subnet
;
107 uint16_t responseSize
= sizeof(response
);
108 DNSQuestion
dq(&a
, QType::A
, QClass::IN
, 0, &remote
, &remote
, (struct dnsheader
*) query
.data(), len
, query
.size(), false, &queryTime
);
109 if(PC
.get(dq
, a
.wirelength(), pwQ
.getHeader()->id
, response
, &responseSize
, &key
, subnet
)) {
113 BOOST_CHECK_EQUAL(matches
, expected
);
115 PC
.expungeByName(DNSName(" hello"), QType::ANY
, true);
116 BOOST_CHECK_EQUAL(PC
.getSize(), 0);
118 catch(PDNSException
& e
) {
119 cerr
<<"Had error: "<<e
.reason
<<endl
;
124 BOOST_AUTO_TEST_CASE(test_PacketCacheServFailTTL
) {
125 const size_t maxEntries
= 150000;
126 DNSDistPacketCache
PC(maxEntries
, 86400, 1);
127 struct timespec queryTime
;
128 gettime(&queryTime
); // does not have to be accurate ("realTime") in tests
132 DNSName a
= DNSName("servfail");
133 BOOST_CHECK_EQUAL(DNSName(a
.toString()), a
);
135 vector
<uint8_t> query
;
136 DNSPacketWriter
pwQ(query
, a
, QType::A
, QClass::IN
, 0);
137 pwQ
.getHeader()->rd
= 1;
139 vector
<uint8_t> response
;
140 DNSPacketWriter
pwR(response
, a
, QType::A
, QClass::IN
, 0);
141 pwR
.getHeader()->rd
= 1;
142 pwR
.getHeader()->ra
= 0;
143 pwR
.getHeader()->qr
= 1;
144 pwR
.getHeader()->rcode
= RCode::ServFail
;
145 pwR
.getHeader()->id
= pwQ
.getHeader()->id
;
147 uint16_t responseLen
= response
.size();
149 char responseBuf
[4096];
150 uint16_t responseBufSize
= sizeof(responseBuf
);
152 boost::optional
<Netmask
> subnet
;
153 auto dh
= reinterpret_cast<dnsheader
*>(query
.data());
154 DNSQuestion
dq(&a
, QType::A
, QClass::IN
, 0, &remote
, &remote
, dh
, query
.size(), query
.size(), false, &queryTime
);
155 bool found
= PC
.get(dq
, a
.wirelength(), 0, responseBuf
, &responseBufSize
, &key
, subnet
);
156 BOOST_CHECK_EQUAL(found
, false);
157 BOOST_CHECK(!subnet
);
159 // Insert with failure-TTL of 0 (-> should not enter cache).
160 PC
.insert(key
, subnet
, *(getFlagsFromDNSHeader(dh
)), a
, QType::A
, QClass::IN
, (const char*) response
.data(), responseLen
, false, RCode::ServFail
, boost::optional
<uint32_t>(0));
161 found
= PC
.get(dq
, a
.wirelength(), pwR
.getHeader()->id
, responseBuf
, &responseBufSize
, &key
, subnet
, 0, true);
162 BOOST_CHECK_EQUAL(found
, false);
163 BOOST_CHECK(!subnet
);
165 // Insert with failure-TTL non-zero (-> should enter cache).
166 PC
.insert(key
, subnet
, *(getFlagsFromDNSHeader(dh
)), a
, QType::A
, QClass::IN
, (const char*) response
.data(), responseLen
, false, RCode::ServFail
, boost::optional
<uint32_t>(300));
167 found
= PC
.get(dq
, a
.wirelength(), pwR
.getHeader()->id
, responseBuf
, &responseBufSize
, &key
, subnet
, 0, true);
168 BOOST_CHECK_EQUAL(found
, true);
169 BOOST_CHECK(!subnet
);
171 catch(PDNSException
& e
) {
172 cerr
<<"Had error: "<<e
.reason
<<endl
;
177 BOOST_AUTO_TEST_CASE(test_PacketCacheNoDataTTL
) {
178 const size_t maxEntries
= 150000;
179 DNSDistPacketCache
PC(maxEntries
, /* maxTTL */ 86400, /* minTTL */ 1, /* tempFailureTTL */ 60, /* maxNegativeTTL */ 1);
181 struct timespec queryTime
;
182 gettime(&queryTime
); // does not have to be accurate ("realTime") in tests
186 DNSName
name("nodata");
187 vector
<uint8_t> query
;
188 DNSPacketWriter
pwQ(query
, name
, QType::A
, QClass::IN
, 0);
189 pwQ
.getHeader()->rd
= 1;
191 vector
<uint8_t> response
;
192 DNSPacketWriter
pwR(response
, name
, QType::A
, QClass::IN
, 0);
193 pwR
.getHeader()->rd
= 1;
194 pwR
.getHeader()->ra
= 0;
195 pwR
.getHeader()->qr
= 1;
196 pwR
.getHeader()->rcode
= RCode::NoError
;
197 pwR
.getHeader()->id
= pwQ
.getHeader()->id
;
199 pwR
.startRecord(name
, QType::SOA
, 86400, QClass::IN
, DNSResourceRecord::AUTHORITY
);
201 pwR
.addOpt(4096, 0, 0);
204 uint16_t responseLen
= response
.size();
206 char responseBuf
[4096];
207 uint16_t responseBufSize
= sizeof(responseBuf
);
209 boost::optional
<Netmask
> subnet
;
210 auto dh
= reinterpret_cast<dnsheader
*>(query
.data());
211 DNSQuestion
dq(&name
, QType::A
, QClass::IN
, 0, &remote
, &remote
, dh
, query
.size(), query
.size(), false, &queryTime
);
212 bool found
= PC
.get(dq
, name
.wirelength(), 0, responseBuf
, &responseBufSize
, &key
, subnet
);
213 BOOST_CHECK_EQUAL(found
, false);
214 BOOST_CHECK(!subnet
);
216 PC
.insert(key
, subnet
, *(getFlagsFromDNSHeader(dh
)), name
, QType::A
, QClass::IN
, reinterpret_cast<const char*>(response
.data()), responseLen
, false, RCode::NoError
, boost::none
);
217 found
= PC
.get(dq
, name
.wirelength(), pwR
.getHeader()->id
, responseBuf
, &responseBufSize
, &key
, subnet
, 0, true);
218 BOOST_CHECK_EQUAL(found
, true);
219 BOOST_CHECK(!subnet
);
222 /* it should have expired by now */
223 found
= PC
.get(dq
, name
.wirelength(), pwR
.getHeader()->id
, responseBuf
, &responseBufSize
, &key
, subnet
, 0, true);
224 BOOST_CHECK_EQUAL(found
, false);
225 BOOST_CHECK(!subnet
);
227 catch(const PDNSException
& e
) {
228 cerr
<<"Had error: "<<e
.reason
<<endl
;
233 BOOST_AUTO_TEST_CASE(test_PacketCacheNXDomainTTL
) {
234 const size_t maxEntries
= 150000;
235 DNSDistPacketCache
PC(maxEntries
, /* maxTTL */ 86400, /* minTTL */ 1, /* tempFailureTTL */ 60, /* maxNegativeTTL */ 1);
237 struct timespec queryTime
;
238 gettime(&queryTime
); // does not have to be accurate ("realTime") in tests
242 DNSName
name("nxdomain");
243 vector
<uint8_t> query
;
244 DNSPacketWriter
pwQ(query
, name
, QType::A
, QClass::IN
, 0);
245 pwQ
.getHeader()->rd
= 1;
247 vector
<uint8_t> response
;
248 DNSPacketWriter
pwR(response
, name
, QType::A
, QClass::IN
, 0);
249 pwR
.getHeader()->rd
= 1;
250 pwR
.getHeader()->ra
= 0;
251 pwR
.getHeader()->qr
= 1;
252 pwR
.getHeader()->rcode
= RCode::NXDomain
;
253 pwR
.getHeader()->id
= pwQ
.getHeader()->id
;
255 pwR
.startRecord(name
, QType::SOA
, 86400, QClass::IN
, DNSResourceRecord::AUTHORITY
);
257 pwR
.addOpt(4096, 0, 0);
260 uint16_t responseLen
= response
.size();
262 char responseBuf
[4096];
263 uint16_t responseBufSize
= sizeof(responseBuf
);
265 boost::optional
<Netmask
> subnet
;
266 auto dh
= reinterpret_cast<dnsheader
*>(query
.data());
267 DNSQuestion
dq(&name
, QType::A
, QClass::IN
, 0, &remote
, &remote
, dh
, query
.size(), query
.size(), false, &queryTime
);
268 bool found
= PC
.get(dq
, name
.wirelength(), 0, responseBuf
, &responseBufSize
, &key
, subnet
);
269 BOOST_CHECK_EQUAL(found
, false);
270 BOOST_CHECK(!subnet
);
272 PC
.insert(key
, subnet
, *(getFlagsFromDNSHeader(dh
)), name
, QType::A
, QClass::IN
, reinterpret_cast<const char*>(response
.data()), responseLen
, false, RCode::NXDomain
, boost::none
);
273 found
= PC
.get(dq
, name
.wirelength(), pwR
.getHeader()->id
, responseBuf
, &responseBufSize
, &key
, subnet
, 0, true);
274 BOOST_CHECK_EQUAL(found
, true);
275 BOOST_CHECK(!subnet
);
278 /* it should have expired by now */
279 found
= PC
.get(dq
, name
.wirelength(), pwR
.getHeader()->id
, responseBuf
, &responseBufSize
, &key
, subnet
, 0, true);
280 BOOST_CHECK_EQUAL(found
, false);
281 BOOST_CHECK(!subnet
);
283 catch(const PDNSException
& e
) {
284 cerr
<<"Had error: "<<e
.reason
<<endl
;
289 static DNSDistPacketCache
PC(500000);
291 static void *threadMangler(void* off
)
293 struct timespec queryTime
;
294 gettime(&queryTime
); // does not have to be accurate ("realTime") in tests
297 unsigned int offset
=(unsigned int)(unsigned long)off
;
298 for(unsigned int counter
=0; counter
< 100000; ++counter
) {
299 DNSName a
=DNSName("hello ")+DNSName(std::to_string(counter
+offset
));
300 vector
<uint8_t> query
;
301 DNSPacketWriter
pwQ(query
, a
, QType::A
, QClass::IN
, 0);
302 pwQ
.getHeader()->rd
= 1;
304 vector
<uint8_t> response
;
305 DNSPacketWriter
pwR(response
, a
, QType::A
, QClass::IN
, 0);
306 pwR
.getHeader()->rd
= 1;
307 pwR
.getHeader()->ra
= 1;
308 pwR
.getHeader()->qr
= 1;
309 pwR
.getHeader()->id
= pwQ
.getHeader()->id
;
310 pwR
.startRecord(a
, QType::A
, 3600, QClass::IN
, DNSResourceRecord::ANSWER
);
311 pwR
.xfr32BitInt(0x01020304);
313 uint16_t responseLen
= response
.size();
315 char responseBuf
[4096];
316 uint16_t responseBufSize
= sizeof(responseBuf
);
318 boost::optional
<Netmask
> subnet
;
319 auto dh
= reinterpret_cast<dnsheader
*>(query
.data());
320 DNSQuestion
dq(&a
, QType::A
, QClass::IN
, 0, &remote
, &remote
, dh
, query
.size(), query
.size(), false, &queryTime
);
321 PC
.get(dq
, a
.wirelength(), 0, responseBuf
, &responseBufSize
, &key
, subnet
);
323 PC
.insert(key
, subnet
, *(getFlagsFromDNSHeader(dh
)), a
, QType::A
, QClass::IN
, (const char*) response
.data(), responseLen
, false, 0, boost::none
);
326 catch(PDNSException
& e
) {
327 cerr
<<"Had error: "<<e
.reason
<<endl
;
333 AtomicCounter g_missing
;
335 static void *threadReader(void* off
)
337 struct timespec queryTime
;
338 gettime(&queryTime
); // does not have to be accurate ("realTime") in tests
341 unsigned int offset
=(unsigned int)(unsigned long)off
;
342 vector
<DNSResourceRecord
> entry
;
344 for(unsigned int counter
=0; counter
< 100000; ++counter
) {
345 DNSName a
=DNSName("hello ")+DNSName(std::to_string(counter
+offset
));
346 vector
<uint8_t> query
;
347 DNSPacketWriter
pwQ(query
, a
, QType::A
, QClass::IN
, 0);
348 pwQ
.getHeader()->rd
= 1;
350 char responseBuf
[4096];
351 uint16_t responseBufSize
= sizeof(responseBuf
);
353 boost::optional
<Netmask
> subnet
;
354 DNSQuestion
dq(&a
, QType::A
, QClass::IN
, 0, &remote
, &remote
, (struct dnsheader
*) query
.data(), query
.size(), query
.size(), false, &queryTime
);
355 bool found
= PC
.get(dq
, a
.wirelength(), 0, responseBuf
, &responseBufSize
, &key
, subnet
);
361 catch(PDNSException
& e
) {
362 cerr
<<"Had error in threadReader: "<<e
.reason
<<endl
;
368 BOOST_AUTO_TEST_CASE(test_PacketCacheThreaded
) {
371 for(int i
=0; i
< 4; ++i
)
372 pthread_create(&tid
[i
], 0, threadMangler
, (void*)(i
*1000000UL));
374 for(int i
=0; i
< 4 ; ++i
)
375 pthread_join(tid
[i
], &res
);
377 BOOST_CHECK_EQUAL(PC
.getSize() + PC
.getDeferredInserts() + PC
.getInsertCollisions(), 400000);
378 BOOST_CHECK_SMALL(1.0*PC
.getInsertCollisions(), 10000.0);
380 for(int i
=0; i
< 4; ++i
)
381 pthread_create(&tid
[i
], 0, threadReader
, (void*)(i
*1000000UL));
382 for(int i
=0; i
< 4 ; ++i
)
383 pthread_join(tid
[i
], &res
);
385 BOOST_CHECK((PC
.getDeferredInserts() + PC
.getDeferredLookups() + PC
.getInsertCollisions()) >= g_missing
);
387 catch(PDNSException
& e
) {
388 cerr
<<"Had error: "<<e
.reason
<<endl
;
394 BOOST_AUTO_TEST_CASE(test_PCCollision
) {
395 const size_t maxEntries
= 150000;
396 DNSDistPacketCache
PC(maxEntries
, 86400, 1, 60, 3600, 60, false, 1, true, true);
397 BOOST_CHECK_EQUAL(PC
.getSize(), 0);
399 DNSName
qname("www.powerdns.com.");
400 uint16_t qtype
= QType::AAAA
;
404 boost::optional
<Netmask
> subnetOut
;
406 /* lookup for a query with an ECS value of 10.0.118.46/32,
407 insert a corresponding response */
409 vector
<uint8_t> query
;
410 DNSPacketWriter
pwQ(query
, qname
, qtype
, QClass::IN
, 0);
411 pwQ
.getHeader()->rd
= 1;
412 pwQ
.getHeader()->id
= qid
;
413 DNSPacketWriter::optvect_t ednsOptions
;
415 opt
.source
= Netmask("10.0.118.46/32");
416 ednsOptions
.push_back(std::make_pair(EDNSOptionCode::ECS
, makeEDNSSubnetOptsString(opt
)));
417 pwQ
.addOpt(512, 0, 0, ednsOptions
);
420 char responseBuf
[4096];
421 uint16_t responseBufSize
= sizeof(responseBuf
);
422 ComboAddress
remote("192.0.2.1");
423 struct timespec queryTime
;
425 DNSQuestion
dq(&qname
, QType::AAAA
, QClass::IN
, 0, &remote
, &remote
, pwQ
.getHeader(), query
.size(), query
.size(), false, &queryTime
);
426 bool found
= PC
.get(dq
, qname
.wirelength(), 0, responseBuf
, &responseBufSize
, &key
, subnetOut
);
427 BOOST_CHECK_EQUAL(found
, false);
428 BOOST_REQUIRE(subnetOut
);
429 BOOST_CHECK_EQUAL(subnetOut
->toString(), opt
.source
.toString());
431 vector
<uint8_t> response
;
432 DNSPacketWriter
pwR(response
, qname
, qtype
, QClass::IN
, 0);
433 pwR
.getHeader()->rd
= 1;
434 pwR
.getHeader()->id
= qid
;
435 pwR
.startRecord(qname
, qtype
, 100, QClass::IN
, DNSResourceRecord::ANSWER
);
436 ComboAddress
v6("::1");
437 pwR
.xfrCAWithoutPort(6, v6
);
439 pwR
.addOpt(512, 0, 0, ednsOptions
);
442 PC
.insert(key
, subnetOut
, *(getFlagsFromDNSHeader(pwR
.getHeader())), qname
, qtype
, QClass::IN
, reinterpret_cast<const char*>(response
.data()), response
.size(), false, RCode::NoError
, boost::none
);
443 BOOST_CHECK_EQUAL(PC
.getSize(), 1);
445 found
= PC
.get(dq
, qname
.wirelength(), 0, responseBuf
, &responseBufSize
, &key
, subnetOut
);
446 BOOST_CHECK_EQUAL(found
, true);
447 BOOST_REQUIRE(subnetOut
);
448 BOOST_CHECK_EQUAL(subnetOut
->toString(), opt
.source
.toString());
451 /* now lookup for the same query with an ECS value of 10.0.123.193/32
452 we should get the same key (collision) but no match */
454 vector
<uint8_t> query
;
455 DNSPacketWriter
pwQ(query
, qname
, qtype
, QClass::IN
, 0);
456 pwQ
.getHeader()->rd
= 1;
457 pwQ
.getHeader()->id
= qid
;
458 DNSPacketWriter::optvect_t ednsOptions
;
460 opt
.source
= Netmask("10.0.123.193/32");
461 ednsOptions
.push_back(std::make_pair(EDNSOptionCode::ECS
, makeEDNSSubnetOptsString(opt
)));
462 pwQ
.addOpt(512, 0, 0, ednsOptions
);
465 char responseBuf
[4096];
466 uint16_t responseBufSize
= sizeof(responseBuf
);
467 ComboAddress
remote("192.0.2.1");
468 struct timespec queryTime
;
470 DNSQuestion
dq(&qname
, QType::AAAA
, QClass::IN
, 0, &remote
, &remote
, pwQ
.getHeader(), query
.size(), query
.size(), false, &queryTime
);
471 bool found
= PC
.get(dq
, qname
.wirelength(), 0, responseBuf
, &responseBufSize
, &secondKey
, subnetOut
);
472 BOOST_CHECK_EQUAL(found
, false);
473 BOOST_CHECK_EQUAL(secondKey
, key
);
474 BOOST_REQUIRE(subnetOut
);
475 BOOST_CHECK_EQUAL(subnetOut
->toString(), opt
.source
.toString());
476 BOOST_CHECK_EQUAL(PC
.getLookupCollisions(), 1);
480 BOOST_AUTO_TEST_SUITE_END()