]>
Commit | Line | Data |
---|---|---|
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" |
fa980c59 | 14 | #include "packetcache.hh" |
886e2cf2 | 15 | |
c7f29d3e | 16 | BOOST_AUTO_TEST_SUITE(test_dnsdistpacketcache_cc) |
886e2cf2 | 17 | |
d84ea5be RG |
18 | static bool receivedOverUDP = true; |
19 | ||
886e2cf2 RG |
20 | BOOST_AUTO_TEST_CASE(test_PacketCacheSimple) { |
21 | const size_t maxEntries = 150000; | |
22 | DNSDistPacketCache PC(maxEntries, 86400, 1); | |
690b86b7 | 23 | BOOST_CHECK_EQUAL(PC.getSize(), 0U); |
5ffb2f83 CH |
24 | struct timespec queryTime; |
25 | gettime(&queryTime); // does not have to be accurate ("realTime") in tests | |
886e2cf2 RG |
26 | |
27 | size_t counter=0; | |
28 | size_t skipped=0; | |
1ea747c0 | 29 | ComboAddress remote; |
d7728daf | 30 | bool dnssecOK = false; |
f6e76e12 | 31 | const time_t now = time(nullptr); |
886e2cf2 | 32 | try { |
f6e76e12 | 33 | for (counter = 0; counter < 100000; ++counter) { |
490dc586 | 34 | DNSName a=DNSName(std::to_string(counter))+DNSName(" hello"); |
886e2cf2 RG |
35 | BOOST_CHECK_EQUAL(DNSName(a.toString()), a); |
36 | ||
32fbb2ab RG |
37 | PacketBuffer query; |
38 | GenericDNSPacketWriter<PacketBuffer> pwQ(query, a, QType::A, QClass::IN, 0); | |
886e2cf2 RG |
39 | pwQ.getHeader()->rd = 1; |
40 | ||
32fbb2ab RG |
41 | PacketBuffer response; |
42 | GenericDNSPacketWriter<PacketBuffer> pwR(response, a, QType::A, QClass::IN, 0); | |
886e2cf2 RG |
43 | pwR.getHeader()->rd = 1; |
44 | pwR.getHeader()->ra = 1; | |
45 | pwR.getHeader()->qr = 1; | |
46 | pwR.getHeader()->id = pwQ.getHeader()->id; | |
f627611d | 47 | pwR.startRecord(a, QType::A, 7200, QClass::IN, DNSResourceRecord::ANSWER); |
886e2cf2 RG |
48 | pwR.xfr32BitInt(0x01020304); |
49 | pwR.commit(); | |
886e2cf2 | 50 | |
886e2cf2 | 51 | uint32_t key = 0; |
78e3ac9e | 52 | boost::optional<Netmask> subnet; |
2c0e81bb | 53 | DNSQuestion dq(&a, QType::A, QClass::IN, &remote, &remote, query, dnsdist::Protocol::DoUDP, &queryTime); |
d84ea5be | 54 | bool found = PC.get(dq, 0, &key, subnet, dnssecOK, receivedOverUDP); |
886e2cf2 | 55 | BOOST_CHECK_EQUAL(found, false); |
78e3ac9e | 56 | BOOST_CHECK(!subnet); |
886e2cf2 | 57 | |
d84ea5be | 58 | PC.insert(key, subnet, *(getFlagsFromDNSHeader(dq.getHeader())), dnssecOK, a, QType::A, QClass::IN, response, receivedOverUDP, 0, boost::none); |
886e2cf2 | 59 | |
d84ea5be | 60 | found = PC.get(dq, pwR.getHeader()->id, &key, subnet, dnssecOK, receivedOverUDP, 0, true); |
886e2cf2 | 61 | if (found == true) { |
341d2553 RG |
62 | BOOST_CHECK_EQUAL(dq.getData().size(), response.size()); |
63 | int match = memcmp(dq.getData().data(), response.data(), dq.getData().size()); | |
886e2cf2 | 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; | |
f6e76e12 | 77 | for (delcounter=0; delcounter < counter/1000; ++delcounter) { |
490dc586 | 78 | DNSName a=DNSName(std::to_string(delcounter))+DNSName(" hello"); |
32fbb2ab RG |
79 | PacketBuffer query; |
80 | GenericDNSPacketWriter<PacketBuffer> pwQ(query, a, QType::A, QClass::IN, 0); | |
886e2cf2 | 81 | pwQ.getHeader()->rd = 1; |
886e2cf2 | 82 | uint32_t key = 0; |
78e3ac9e | 83 | boost::optional<Netmask> subnet; |
2c0e81bb | 84 | DNSQuestion dq(&a, QType::A, QClass::IN, &remote, &remote, query, dnsdist::Protocol::DoUDP, &queryTime); |
d84ea5be | 85 | bool found = PC.get(dq, 0, &key, subnet, dnssecOK, receivedOverUDP); |
886e2cf2 | 86 | if (found == true) { |
f627611d | 87 | auto removed = PC.expungeByName(a); |
690b86b7 | 88 | BOOST_CHECK_EQUAL(removed, 1U); |
f627611d | 89 | deleted += removed; |
886e2cf2 RG |
90 | } |
91 | } | |
92 | BOOST_CHECK_EQUAL(PC.getSize(), counter - skipped - deleted); | |
93 | ||
94 | size_t matches=0; | |
886e2cf2 | 95 | size_t expected=counter-skipped-deleted; |
f6e76e12 | 96 | for (; delcounter < counter; ++delcounter) { |
490dc586 | 97 | DNSName a(DNSName(std::to_string(delcounter))+DNSName(" hello")); |
32fbb2ab RG |
98 | PacketBuffer query; |
99 | GenericDNSPacketWriter<PacketBuffer> pwQ(query, a, QType::A, QClass::IN, 0); | |
886e2cf2 | 100 | pwQ.getHeader()->rd = 1; |
886e2cf2 | 101 | uint32_t key = 0; |
78e3ac9e | 102 | boost::optional<Netmask> subnet; |
2c0e81bb | 103 | DNSQuestion dq(&a, QType::A, QClass::IN, &remote, &remote, query, dnsdist::Protocol::DoUDP, &queryTime); |
d84ea5be | 104 | if (PC.get(dq, pwQ.getHeader()->id, &key, subnet, dnssecOK, receivedOverUDP)) { |
490dc586 | 105 | matches++; |
886e2cf2 RG |
106 | } |
107 | } | |
490dc586 | 108 | |
6d1a9248 | 109 | /* in the unlikely event that the test took so long that the entries did expire.. */ |
f6e76e12 | 110 | auto expired = PC.purgeExpired(0, now); |
f627611d RG |
111 | BOOST_CHECK_EQUAL(matches + expired, expected); |
112 | ||
113 | auto remaining = PC.getSize(); | |
114 | auto removed = PC.expungeByName(DNSName(" hello"), QType::ANY, true); | |
690b86b7 | 115 | BOOST_CHECK_EQUAL(PC.getSize(), 0U); |
f627611d | 116 | BOOST_CHECK_EQUAL(removed, remaining); |
af9f7f64 RG |
117 | |
118 | /* nothing to remove */ | |
119 | BOOST_CHECK_EQUAL(PC.purgeExpired(0, now), 0U); | |
886e2cf2 | 120 | } |
f6e76e12 RG |
121 | catch (const PDNSException& e) { |
122 | cerr<<"Had error: "<<e.reason<<endl; | |
123 | throw; | |
124 | } | |
125 | } | |
126 | ||
30014e5f | 127 | |
f6e76e12 RG |
128 | BOOST_AUTO_TEST_CASE(test_PacketCacheSharded) { |
129 | const size_t maxEntries = 150000; | |
130 | const size_t numberOfShards = 10; | |
131 | DNSDistPacketCache PC(maxEntries, 86400, 1, 60, 3600, 60, false, numberOfShards); | |
132 | BOOST_CHECK_EQUAL(PC.getSize(), 0U); | |
133 | struct timespec queryTime; | |
134 | gettime(&queryTime); // does not have to be accurate ("realTime") in tests | |
135 | ||
136 | size_t counter = 0; | |
137 | size_t skipped = 0; | |
138 | ComboAddress remote; | |
139 | bool dnssecOK = false; | |
140 | const time_t now = time(nullptr); | |
141 | ||
142 | try { | |
143 | for (counter = 0; counter < 100000; ++counter) { | |
144 | DNSName a(std::to_string(counter) + ".powerdns.com."); | |
145 | ||
146 | PacketBuffer query; | |
147 | GenericDNSPacketWriter<PacketBuffer> pwQ(query, a, QType::AAAA, QClass::IN, 0); | |
148 | pwQ.getHeader()->rd = 1; | |
149 | ||
150 | PacketBuffer response; | |
151 | GenericDNSPacketWriter<PacketBuffer> pwR(response, a, QType::AAAA, QClass::IN, 0); | |
152 | pwR.getHeader()->rd = 1; | |
153 | pwR.getHeader()->ra = 1; | |
154 | pwR.getHeader()->qr = 1; | |
155 | pwR.getHeader()->id = pwQ.getHeader()->id; | |
156 | pwR.startRecord(a, QType::AAAA, 7200, QClass::IN, DNSResourceRecord::ANSWER); | |
157 | ComboAddress v6("2001:db8::1"); | |
158 | pwR.xfrIP6(std::string(reinterpret_cast<const char*>(v6.sin6.sin6_addr.s6_addr), 16)); | |
159 | pwR.xfr32BitInt(0x01020304); | |
160 | pwR.commit(); | |
161 | ||
162 | uint32_t key = 0; | |
163 | boost::optional<Netmask> subnet; | |
2c0e81bb | 164 | DNSQuestion dq(&a, QType::AAAA, QClass::IN, &remote, &remote, query, dnsdist::Protocol::DoUDP, &queryTime); |
d84ea5be | 165 | bool found = PC.get(dq, 0, &key, subnet, dnssecOK, receivedOverUDP); |
f6e76e12 RG |
166 | BOOST_CHECK_EQUAL(found, false); |
167 | BOOST_CHECK(!subnet); | |
168 | ||
d84ea5be | 169 | PC.insert(key, subnet, *(getFlagsFromDNSHeader(dq.getHeader())), dnssecOK, a, QType::AAAA, QClass::IN, response, receivedOverUDP, 0, boost::none); |
f6e76e12 | 170 | |
d84ea5be | 171 | found = PC.get(dq, pwR.getHeader()->id, &key, subnet, dnssecOK, receivedOverUDP, 0, true); |
f6e76e12 RG |
172 | if (found == true) { |
173 | BOOST_CHECK_EQUAL(dq.getData().size(), response.size()); | |
174 | int match = memcmp(dq.getData().data(), response.data(), dq.getData().size()); | |
175 | BOOST_CHECK_EQUAL(match, 0); | |
176 | BOOST_CHECK(!subnet); | |
177 | } | |
178 | else { | |
179 | skipped++; | |
180 | } | |
181 | } | |
182 | ||
183 | BOOST_CHECK_EQUAL(skipped, PC.getInsertCollisions()); | |
184 | BOOST_CHECK_EQUAL(PC.getSize(), counter - skipped); | |
185 | ||
186 | size_t matches = 0; | |
187 | for (counter = 0; counter < 100000; ++counter) { | |
188 | DNSName a(std::to_string(counter) + ".powerdns.com."); | |
189 | ||
190 | PacketBuffer query; | |
191 | GenericDNSPacketWriter<PacketBuffer> pwQ(query, a, QType::AAAA, QClass::IN, 0); | |
192 | pwQ.getHeader()->rd = 1; | |
193 | uint32_t key = 0; | |
194 | boost::optional<Netmask> subnet; | |
2c0e81bb | 195 | DNSQuestion dq(&a, QType::AAAA, QClass::IN, &remote, &remote, query, dnsdist::Protocol::DoUDP, &queryTime); |
d84ea5be | 196 | if (PC.get(dq, pwQ.getHeader()->id, &key, subnet, dnssecOK, receivedOverUDP)) { |
f6e76e12 RG |
197 | matches++; |
198 | } | |
199 | } | |
200 | ||
201 | BOOST_CHECK_EQUAL(matches, counter - skipped); | |
202 | ||
203 | auto remaining = PC.getSize(); | |
204 | ||
205 | /* no entry should have expired */ | |
206 | auto expired = PC.purgeExpired(0, now); | |
207 | BOOST_CHECK_EQUAL(expired, 0U); | |
208 | ||
209 | /* but after the TTL .. let's ask for at most 1k entries */ | |
b8a0a4a8 | 210 | auto removed = PC.purgeExpired(1000, now + 7200 + 3600); |
f6e76e12 RG |
211 | BOOST_CHECK_EQUAL(removed, remaining - 1000U); |
212 | BOOST_CHECK_EQUAL(PC.getSize(), 1000U); | |
213 | ||
214 | /* now remove everything */ | |
b8a0a4a8 | 215 | removed = PC.purgeExpired(0, now + 7200 + 3600); |
f6e76e12 RG |
216 | BOOST_CHECK_EQUAL(removed, 1000U); |
217 | BOOST_CHECK_EQUAL(PC.getSize(), 0U); | |
af9f7f64 RG |
218 | |
219 | /* nothing to remove */ | |
220 | BOOST_CHECK_EQUAL(PC.purgeExpired(0, now), 0U); | |
f6e76e12 RG |
221 | } |
222 | catch (const PDNSException& e) { | |
886e2cf2 RG |
223 | cerr<<"Had error: "<<e.reason<<endl; |
224 | throw; | |
225 | } | |
226 | } | |
227 | ||
30014e5f RG |
228 | BOOST_AUTO_TEST_CASE(test_PacketCacheTCP) { |
229 | const size_t maxEntries = 150000; | |
230 | DNSDistPacketCache PC(maxEntries, 86400, 1); | |
231 | struct timespec queryTime; | |
232 | gettime(&queryTime); // does not have to be accurate ("realTime") in tests | |
233 | ||
234 | ComboAddress remote; | |
235 | bool dnssecOK = false; | |
236 | try { | |
237 | DNSName a = DNSName("tcp"); | |
238 | BOOST_CHECK_EQUAL(DNSName(a.toString()), a); | |
239 | ||
240 | PacketBuffer query; | |
241 | GenericDNSPacketWriter<PacketBuffer> pwQ(query, a, QType::AAAA, QClass::IN, 0); | |
242 | pwQ.getHeader()->rd = 1; | |
243 | ||
244 | PacketBuffer response; | |
245 | GenericDNSPacketWriter<PacketBuffer> pwR(response, a, QType::AAAA, QClass::IN, 0); | |
246 | pwR.getHeader()->rd = 1; | |
247 | pwR.getHeader()->ra = 1; | |
248 | pwR.getHeader()->qr = 1; | |
249 | pwR.getHeader()->id = pwQ.getHeader()->id; | |
250 | pwR.startRecord(a, QType::AAAA, 7200, QClass::IN, DNSResourceRecord::ANSWER); | |
251 | ComboAddress v6("2001:db8::1"); | |
252 | pwR.xfrIP6(std::string(reinterpret_cast<const char*>(v6.sin6.sin6_addr.s6_addr), 16)); | |
253 | pwR.xfr32BitInt(0x01020304); | |
254 | pwR.commit(); | |
255 | ||
256 | { | |
257 | /* UDP */ | |
258 | uint32_t key = 0; | |
259 | boost::optional<Netmask> subnet; | |
2c0e81bb | 260 | DNSQuestion dq(&a, QType::A, QClass::IN, &remote, &remote, query, dnsdist::Protocol::DoUDP, &queryTime); |
30014e5f RG |
261 | bool found = PC.get(dq, 0, &key, subnet, dnssecOK, receivedOverUDP); |
262 | BOOST_CHECK_EQUAL(found, false); | |
263 | BOOST_CHECK(!subnet); | |
264 | ||
265 | PC.insert(key, subnet, *(getFlagsFromDNSHeader(dq.getHeader())), dnssecOK, a, QType::A, QClass::IN, response, receivedOverUDP, RCode::NoError, boost::none); | |
266 | found = PC.get(dq, pwR.getHeader()->id, &key, subnet, dnssecOK, receivedOverUDP, 0, true); | |
267 | BOOST_CHECK_EQUAL(found, true); | |
268 | BOOST_CHECK(!subnet); | |
269 | } | |
270 | ||
271 | { | |
272 | /* same but over TCP */ | |
273 | uint32_t key = 0; | |
274 | boost::optional<Netmask> subnet; | |
2c0e81bb | 275 | DNSQuestion dq(&a, QType::A, QClass::IN, &remote, &remote, query, dnsdist::Protocol::DoTCP, &queryTime); |
30014e5f RG |
276 | bool found = PC.get(dq, 0, &key, subnet, dnssecOK, !receivedOverUDP); |
277 | BOOST_CHECK_EQUAL(found, false); | |
278 | BOOST_CHECK(!subnet); | |
279 | ||
280 | PC.insert(key, subnet, *(getFlagsFromDNSHeader(dq.getHeader())), dnssecOK, a, QType::A, QClass::IN, response, !receivedOverUDP, RCode::NoError, boost::none); | |
281 | found = PC.get(dq, pwR.getHeader()->id, &key, subnet, dnssecOK, !receivedOverUDP, 0, true); | |
282 | BOOST_CHECK_EQUAL(found, true); | |
283 | BOOST_CHECK(!subnet); | |
284 | } | |
285 | } | |
286 | catch(PDNSException& e) { | |
287 | cerr<<"Had error: "<<e.reason<<endl; | |
288 | throw; | |
289 | } | |
290 | } | |
291 | ||
acb8f5d5 CH |
292 | BOOST_AUTO_TEST_CASE(test_PacketCacheServFailTTL) { |
293 | const size_t maxEntries = 150000; | |
294 | DNSDistPacketCache PC(maxEntries, 86400, 1); | |
5ffb2f83 CH |
295 | struct timespec queryTime; |
296 | gettime(&queryTime); // does not have to be accurate ("realTime") in tests | |
acb8f5d5 CH |
297 | |
298 | ComboAddress remote; | |
d7728daf | 299 | bool dnssecOK = false; |
acb8f5d5 CH |
300 | try { |
301 | DNSName a = DNSName("servfail"); | |
302 | BOOST_CHECK_EQUAL(DNSName(a.toString()), a); | |
303 | ||
32fbb2ab RG |
304 | PacketBuffer query; |
305 | GenericDNSPacketWriter<PacketBuffer> pwQ(query, a, QType::A, QClass::IN, 0); | |
acb8f5d5 CH |
306 | pwQ.getHeader()->rd = 1; |
307 | ||
32fbb2ab RG |
308 | PacketBuffer response; |
309 | GenericDNSPacketWriter<PacketBuffer> pwR(response, a, QType::A, QClass::IN, 0); | |
acb8f5d5 CH |
310 | pwR.getHeader()->rd = 1; |
311 | pwR.getHeader()->ra = 0; | |
312 | pwR.getHeader()->qr = 1; | |
313 | pwR.getHeader()->rcode = RCode::ServFail; | |
314 | pwR.getHeader()->id = pwQ.getHeader()->id; | |
315 | pwR.commit(); | |
acb8f5d5 | 316 | |
acb8f5d5 | 317 | uint32_t key = 0; |
78e3ac9e | 318 | boost::optional<Netmask> subnet; |
2c0e81bb | 319 | DNSQuestion dq(&a, QType::A, QClass::IN, &remote, &remote, query, dnsdist::Protocol::DoUDP, &queryTime); |
d84ea5be | 320 | bool found = PC.get(dq, 0, &key, subnet, dnssecOK, receivedOverUDP); |
acb8f5d5 | 321 | BOOST_CHECK_EQUAL(found, false); |
78e3ac9e | 322 | BOOST_CHECK(!subnet); |
acb8f5d5 CH |
323 | |
324 | // Insert with failure-TTL of 0 (-> should not enter cache). | |
d84ea5be RG |
325 | PC.insert(key, subnet, *(getFlagsFromDNSHeader(dq.getHeader())), dnssecOK, a, QType::A, QClass::IN, response, receivedOverUDP, RCode::ServFail, boost::optional<uint32_t>(0)); |
326 | found = PC.get(dq, pwR.getHeader()->id, &key, subnet, dnssecOK, receivedOverUDP, 0, true); | |
acb8f5d5 | 327 | BOOST_CHECK_EQUAL(found, false); |
78e3ac9e | 328 | BOOST_CHECK(!subnet); |
acb8f5d5 | 329 | |
56459632 | 330 | // Insert with failure-TTL non-zero (-> should enter cache). |
d84ea5be RG |
331 | PC.insert(key, subnet, *(getFlagsFromDNSHeader(dq.getHeader())), dnssecOK, a, QType::A, QClass::IN, response, receivedOverUDP, RCode::ServFail, boost::optional<uint32_t>(300)); |
332 | found = PC.get(dq, pwR.getHeader()->id, &key, subnet, dnssecOK, receivedOverUDP, 0, true); | |
acb8f5d5 | 333 | BOOST_CHECK_EQUAL(found, true); |
78e3ac9e | 334 | BOOST_CHECK(!subnet); |
acb8f5d5 CH |
335 | } |
336 | catch(PDNSException& e) { | |
337 | cerr<<"Had error: "<<e.reason<<endl; | |
338 | throw; | |
339 | } | |
340 | } | |
341 | ||
47698274 RG |
342 | BOOST_AUTO_TEST_CASE(test_PacketCacheNoDataTTL) { |
343 | const size_t maxEntries = 150000; | |
344 | DNSDistPacketCache PC(maxEntries, /* maxTTL */ 86400, /* minTTL */ 1, /* tempFailureTTL */ 60, /* maxNegativeTTL */ 1); | |
345 | ||
346 | struct timespec queryTime; | |
347 | gettime(&queryTime); // does not have to be accurate ("realTime") in tests | |
348 | ||
349 | ComboAddress remote; | |
d7728daf | 350 | bool dnssecOK = false; |
47698274 RG |
351 | try { |
352 | DNSName name("nodata"); | |
32fbb2ab RG |
353 | PacketBuffer query; |
354 | GenericDNSPacketWriter<PacketBuffer> pwQ(query, name, QType::A, QClass::IN, 0); | |
47698274 RG |
355 | pwQ.getHeader()->rd = 1; |
356 | ||
32fbb2ab RG |
357 | PacketBuffer response; |
358 | GenericDNSPacketWriter<PacketBuffer> pwR(response, name, QType::A, QClass::IN, 0); | |
47698274 RG |
359 | pwR.getHeader()->rd = 1; |
360 | pwR.getHeader()->ra = 0; | |
361 | pwR.getHeader()->qr = 1; | |
362 | pwR.getHeader()->rcode = RCode::NoError; | |
363 | pwR.getHeader()->id = pwQ.getHeader()->id; | |
364 | pwR.commit(); | |
365 | pwR.startRecord(name, QType::SOA, 86400, QClass::IN, DNSResourceRecord::AUTHORITY); | |
366 | pwR.commit(); | |
367 | pwR.addOpt(4096, 0, 0); | |
368 | pwR.commit(); | |
369 | ||
47698274 | 370 | uint32_t key = 0; |
78e3ac9e | 371 | boost::optional<Netmask> subnet; |
2c0e81bb | 372 | DNSQuestion dq(&name, QType::A, QClass::IN, &remote, &remote, query, dnsdist::Protocol::DoUDP, &queryTime); |
d84ea5be | 373 | bool found = PC.get(dq, 0, &key, subnet, dnssecOK, receivedOverUDP); |
47698274 | 374 | BOOST_CHECK_EQUAL(found, false); |
78e3ac9e | 375 | BOOST_CHECK(!subnet); |
47698274 | 376 | |
d84ea5be RG |
377 | PC.insert(key, subnet, *(getFlagsFromDNSHeader(dq.getHeader())), dnssecOK, name, QType::A, QClass::IN, response, receivedOverUDP, RCode::NoError, boost::none); |
378 | found = PC.get(dq, pwR.getHeader()->id, &key, subnet, dnssecOK, receivedOverUDP, 0, true); | |
47698274 | 379 | BOOST_CHECK_EQUAL(found, true); |
78e3ac9e RG |
380 | BOOST_CHECK(!subnet); |
381 | ||
47698274 RG |
382 | sleep(2); |
383 | /* it should have expired by now */ | |
d84ea5be | 384 | found = PC.get(dq, pwR.getHeader()->id, &key, subnet, dnssecOK, receivedOverUDP, 0, true); |
47698274 | 385 | BOOST_CHECK_EQUAL(found, false); |
78e3ac9e | 386 | BOOST_CHECK(!subnet); |
47698274 RG |
387 | } |
388 | catch(const PDNSException& e) { | |
389 | cerr<<"Had error: "<<e.reason<<endl; | |
390 | throw; | |
391 | } | |
392 | } | |
393 | ||
394 | BOOST_AUTO_TEST_CASE(test_PacketCacheNXDomainTTL) { | |
395 | const size_t maxEntries = 150000; | |
396 | DNSDistPacketCache PC(maxEntries, /* maxTTL */ 86400, /* minTTL */ 1, /* tempFailureTTL */ 60, /* maxNegativeTTL */ 1); | |
397 | ||
398 | struct timespec queryTime; | |
399 | gettime(&queryTime); // does not have to be accurate ("realTime") in tests | |
400 | ||
401 | ComboAddress remote; | |
d7728daf | 402 | bool dnssecOK = false; |
47698274 RG |
403 | try { |
404 | DNSName name("nxdomain"); | |
32fbb2ab RG |
405 | PacketBuffer query; |
406 | GenericDNSPacketWriter<PacketBuffer> pwQ(query, name, QType::A, QClass::IN, 0); | |
47698274 RG |
407 | pwQ.getHeader()->rd = 1; |
408 | ||
32fbb2ab RG |
409 | PacketBuffer response; |
410 | GenericDNSPacketWriter<PacketBuffer> pwR(response, name, QType::A, QClass::IN, 0); | |
47698274 RG |
411 | pwR.getHeader()->rd = 1; |
412 | pwR.getHeader()->ra = 0; | |
413 | pwR.getHeader()->qr = 1; | |
414 | pwR.getHeader()->rcode = RCode::NXDomain; | |
415 | pwR.getHeader()->id = pwQ.getHeader()->id; | |
416 | pwR.commit(); | |
417 | pwR.startRecord(name, QType::SOA, 86400, QClass::IN, DNSResourceRecord::AUTHORITY); | |
418 | pwR.commit(); | |
419 | pwR.addOpt(4096, 0, 0); | |
420 | pwR.commit(); | |
421 | ||
47698274 | 422 | uint32_t key = 0; |
78e3ac9e | 423 | boost::optional<Netmask> subnet; |
2c0e81bb | 424 | DNSQuestion dq(&name, QType::A, QClass::IN, &remote, &remote, query, dnsdist::Protocol::DoUDP, &queryTime); |
d84ea5be | 425 | bool found = PC.get(dq, 0, &key, subnet, dnssecOK, receivedOverUDP); |
47698274 | 426 | BOOST_CHECK_EQUAL(found, false); |
78e3ac9e | 427 | BOOST_CHECK(!subnet); |
47698274 | 428 | |
d84ea5be RG |
429 | PC.insert(key, subnet, *(getFlagsFromDNSHeader(dq.getHeader())), dnssecOK, name, QType::A, QClass::IN, response, receivedOverUDP, RCode::NXDomain, boost::none); |
430 | found = PC.get(dq, pwR.getHeader()->id, &key, subnet, dnssecOK, receivedOverUDP, 0, true); | |
47698274 | 431 | BOOST_CHECK_EQUAL(found, true); |
78e3ac9e RG |
432 | BOOST_CHECK(!subnet); |
433 | ||
47698274 RG |
434 | sleep(2); |
435 | /* it should have expired by now */ | |
d84ea5be | 436 | found = PC.get(dq, pwR.getHeader()->id, &key, subnet, dnssecOK, receivedOverUDP, 0, true); |
47698274 | 437 | BOOST_CHECK_EQUAL(found, false); |
78e3ac9e | 438 | BOOST_CHECK(!subnet); |
47698274 RG |
439 | } |
440 | catch(const PDNSException& e) { | |
441 | cerr<<"Had error: "<<e.reason<<endl; | |
442 | throw; | |
443 | } | |
444 | } | |
445 | ||
dd026b9c | 446 | static DNSDistPacketCache g_PC(500000); |
886e2cf2 | 447 | |
f0941861 | 448 | static void threadMangler(unsigned int offset) |
886e2cf2 | 449 | { |
5ffb2f83 CH |
450 | struct timespec queryTime; |
451 | gettime(&queryTime); // does not have to be accurate ("realTime") in tests | |
886e2cf2 | 452 | try { |
1ea747c0 | 453 | ComboAddress remote; |
d7728daf | 454 | bool dnssecOK = false; |
886e2cf2 RG |
455 | for(unsigned int counter=0; counter < 100000; ++counter) { |
456 | DNSName a=DNSName("hello ")+DNSName(std::to_string(counter+offset)); | |
32fbb2ab RG |
457 | PacketBuffer query; |
458 | GenericDNSPacketWriter<PacketBuffer> pwQ(query, a, QType::A, QClass::IN, 0); | |
886e2cf2 RG |
459 | pwQ.getHeader()->rd = 1; |
460 | ||
32fbb2ab RG |
461 | PacketBuffer response; |
462 | GenericDNSPacketWriter<PacketBuffer> pwR(response, a, QType::A, QClass::IN, 0); | |
886e2cf2 RG |
463 | pwR.getHeader()->rd = 1; |
464 | pwR.getHeader()->ra = 1; | |
465 | pwR.getHeader()->qr = 1; | |
466 | pwR.getHeader()->id = pwQ.getHeader()->id; | |
467 | pwR.startRecord(a, QType::A, 3600, QClass::IN, DNSResourceRecord::ANSWER); | |
468 | pwR.xfr32BitInt(0x01020304); | |
469 | pwR.commit(); | |
886e2cf2 | 470 | |
886e2cf2 | 471 | uint32_t key = 0; |
78e3ac9e | 472 | boost::optional<Netmask> subnet; |
2c0e81bb | 473 | DNSQuestion dq(&a, QType::A, QClass::IN, &remote, &remote, query, dnsdist::Protocol::DoUDP, &queryTime); |
d84ea5be | 474 | g_PC.get(dq, 0, &key, subnet, dnssecOK, receivedOverUDP); |
886e2cf2 | 475 | |
d84ea5be | 476 | g_PC.insert(key, subnet, *(getFlagsFromDNSHeader(dq.getHeader())), dnssecOK, a, QType::A, QClass::IN, response, receivedOverUDP, 0, boost::none); |
886e2cf2 RG |
477 | } |
478 | } | |
479 | catch(PDNSException& e) { | |
480 | cerr<<"Had error: "<<e.reason<<endl; | |
481 | throw; | |
482 | } | |
886e2cf2 RG |
483 | } |
484 | ||
485 | AtomicCounter g_missing; | |
486 | ||
f0941861 | 487 | static void threadReader(unsigned int offset) |
886e2cf2 | 488 | { |
d7728daf | 489 | bool dnssecOK = false; |
5ffb2f83 CH |
490 | struct timespec queryTime; |
491 | gettime(&queryTime); // does not have to be accurate ("realTime") in tests | |
886e2cf2 RG |
492 | try |
493 | { | |
1ea747c0 | 494 | ComboAddress remote; |
886e2cf2 RG |
495 | for(unsigned int counter=0; counter < 100000; ++counter) { |
496 | DNSName a=DNSName("hello ")+DNSName(std::to_string(counter+offset)); | |
32fbb2ab RG |
497 | PacketBuffer query; |
498 | GenericDNSPacketWriter<PacketBuffer> pwQ(query, a, QType::A, QClass::IN, 0); | |
886e2cf2 RG |
499 | pwQ.getHeader()->rd = 1; |
500 | ||
886e2cf2 | 501 | uint32_t key = 0; |
78e3ac9e | 502 | boost::optional<Netmask> subnet; |
2c0e81bb | 503 | DNSQuestion dq(&a, QType::A, QClass::IN, &remote, &remote, query, dnsdist::Protocol::DoUDP, &queryTime); |
d84ea5be | 504 | bool found = g_PC.get(dq, 0, &key, subnet, dnssecOK, receivedOverUDP); |
886e2cf2 RG |
505 | if (!found) { |
506 | g_missing++; | |
507 | } | |
508 | } | |
509 | } | |
510 | catch(PDNSException& e) { | |
511 | cerr<<"Had error in threadReader: "<<e.reason<<endl; | |
512 | throw; | |
513 | } | |
886e2cf2 RG |
514 | } |
515 | ||
516 | BOOST_AUTO_TEST_CASE(test_PacketCacheThreaded) { | |
517 | try { | |
f0941861 RG |
518 | std::vector<std::thread> threads; |
519 | for (int i = 0; i < 4; ++i) { | |
520 | threads.push_back(std::thread(threadMangler, i*1000000UL)); | |
521 | } | |
522 | ||
523 | for (auto& t : threads) { | |
524 | t.join(); | |
525 | } | |
526 | ||
527 | threads.clear(); | |
886e2cf2 | 528 | |
690b86b7 | 529 | BOOST_CHECK_EQUAL(g_PC.getSize() + g_PC.getDeferredInserts() + g_PC.getInsertCollisions(), 400000U); |
dd026b9c | 530 | BOOST_CHECK_SMALL(1.0*g_PC.getInsertCollisions(), 10000.0); |
886e2cf2 | 531 | |
f0941861 RG |
532 | for (int i = 0; i < 4; ++i) { |
533 | threads.push_back(std::thread(threadReader, i*1000000UL)); | |
534 | } | |
535 | ||
536 | for (auto& t : threads) { | |
537 | t.join(); | |
538 | } | |
886e2cf2 | 539 | |
dd026b9c | 540 | BOOST_CHECK((g_PC.getDeferredInserts() + g_PC.getDeferredLookups() + g_PC.getInsertCollisions()) >= g_missing); |
886e2cf2 RG |
541 | } |
542 | catch(PDNSException& e) { | |
543 | cerr<<"Had error: "<<e.reason<<endl; | |
544 | throw; | |
545 | } | |
546 | ||
547 | } | |
548 | ||
78e3ac9e RG |
549 | BOOST_AUTO_TEST_CASE(test_PCCollision) { |
550 | const size_t maxEntries = 150000; | |
551 | DNSDistPacketCache PC(maxEntries, 86400, 1, 60, 3600, 60, false, 1, true, true); | |
690b86b7 | 552 | BOOST_CHECK_EQUAL(PC.getSize(), 0U); |
78e3ac9e RG |
553 | |
554 | DNSName qname("www.powerdns.com."); | |
555 | uint16_t qtype = QType::AAAA; | |
556 | uint16_t qid = 0x42; | |
557 | uint32_t key; | |
558 | uint32_t secondKey; | |
559 | boost::optional<Netmask> subnetOut; | |
d7728daf | 560 | bool dnssecOK = false; |
78e3ac9e | 561 | |
fa980c59 | 562 | /* lookup for a query with a first ECS value, |
78e3ac9e RG |
563 | insert a corresponding response */ |
564 | { | |
32fbb2ab RG |
565 | PacketBuffer query; |
566 | GenericDNSPacketWriter<PacketBuffer> pwQ(query, qname, qtype, QClass::IN, 0); | |
78e3ac9e RG |
567 | pwQ.getHeader()->rd = 1; |
568 | pwQ.getHeader()->id = qid; | |
32fbb2ab | 569 | GenericDNSPacketWriter<PacketBuffer>::optvect_t ednsOptions; |
78e3ac9e | 570 | EDNSSubnetOpts opt; |
fa980c59 | 571 | opt.source = Netmask("10.0.59.220/32"); |
78e3ac9e RG |
572 | ednsOptions.push_back(std::make_pair(EDNSOptionCode::ECS, makeEDNSSubnetOptsString(opt))); |
573 | pwQ.addOpt(512, 0, 0, ednsOptions); | |
574 | pwQ.commit(); | |
575 | ||
78e3ac9e RG |
576 | ComboAddress remote("192.0.2.1"); |
577 | struct timespec queryTime; | |
578 | gettime(&queryTime); | |
2c0e81bb | 579 | DNSQuestion dq(&qname, QType::AAAA, QClass::IN, &remote, &remote, query, dnsdist::Protocol::DoUDP, &queryTime); |
d84ea5be | 580 | bool found = PC.get(dq, 0, &key, subnetOut, dnssecOK, receivedOverUDP); |
78e3ac9e RG |
581 | BOOST_CHECK_EQUAL(found, false); |
582 | BOOST_REQUIRE(subnetOut); | |
583 | BOOST_CHECK_EQUAL(subnetOut->toString(), opt.source.toString()); | |
584 | ||
32fbb2ab RG |
585 | PacketBuffer response; |
586 | GenericDNSPacketWriter<PacketBuffer> pwR(response, qname, qtype, QClass::IN, 0); | |
78e3ac9e RG |
587 | pwR.getHeader()->rd = 1; |
588 | pwR.getHeader()->id = qid; | |
589 | pwR.startRecord(qname, qtype, 100, QClass::IN, DNSResourceRecord::ANSWER); | |
590 | ComboAddress v6("::1"); | |
591 | pwR.xfrCAWithoutPort(6, v6); | |
592 | pwR.commit(); | |
593 | pwR.addOpt(512, 0, 0, ednsOptions); | |
594 | pwR.commit(); | |
595 | ||
d84ea5be | 596 | PC.insert(key, subnetOut, *(getFlagsFromDNSHeader(pwR.getHeader())), dnssecOK, qname, qtype, QClass::IN, response, receivedOverUDP, RCode::NoError, boost::none); |
690b86b7 | 597 | BOOST_CHECK_EQUAL(PC.getSize(), 1U); |
78e3ac9e | 598 | |
d84ea5be | 599 | found = PC.get(dq, 0, &key, subnetOut, dnssecOK, receivedOverUDP); |
78e3ac9e RG |
600 | BOOST_CHECK_EQUAL(found, true); |
601 | BOOST_REQUIRE(subnetOut); | |
602 | BOOST_CHECK_EQUAL(subnetOut->toString(), opt.source.toString()); | |
603 | } | |
604 | ||
fa980c59 | 605 | /* now lookup for the same query with a different ECS value, |
78e3ac9e RG |
606 | we should get the same key (collision) but no match */ |
607 | { | |
32fbb2ab RG |
608 | PacketBuffer query; |
609 | GenericDNSPacketWriter<PacketBuffer> pwQ(query, qname, qtype, QClass::IN, 0); | |
78e3ac9e RG |
610 | pwQ.getHeader()->rd = 1; |
611 | pwQ.getHeader()->id = qid; | |
32fbb2ab | 612 | GenericDNSPacketWriter<PacketBuffer>::optvect_t ednsOptions; |
78e3ac9e | 613 | EDNSSubnetOpts opt; |
fa980c59 | 614 | opt.source = Netmask("10.0.167.48/32"); |
78e3ac9e RG |
615 | ednsOptions.push_back(std::make_pair(EDNSOptionCode::ECS, makeEDNSSubnetOptsString(opt))); |
616 | pwQ.addOpt(512, 0, 0, ednsOptions); | |
617 | pwQ.commit(); | |
618 | ||
78e3ac9e RG |
619 | ComboAddress remote("192.0.2.1"); |
620 | struct timespec queryTime; | |
621 | gettime(&queryTime); | |
2c0e81bb | 622 | DNSQuestion dq(&qname, QType::AAAA, QClass::IN, &remote, &remote, query, dnsdist::Protocol::DoUDP, &queryTime); |
d84ea5be | 623 | bool found = PC.get(dq, 0, &secondKey, subnetOut, dnssecOK, receivedOverUDP); |
78e3ac9e RG |
624 | BOOST_CHECK_EQUAL(found, false); |
625 | BOOST_CHECK_EQUAL(secondKey, key); | |
626 | BOOST_REQUIRE(subnetOut); | |
627 | BOOST_CHECK_EQUAL(subnetOut->toString(), opt.source.toString()); | |
690b86b7 | 628 | BOOST_CHECK_EQUAL(PC.getLookupCollisions(), 1U); |
78e3ac9e | 629 | } |
fa980c59 RG |
630 | |
631 | #if 0 | |
632 | /* to be able to compute a new collision if the packet cache hashing code is updated */ | |
633 | { | |
634 | DNSDistPacketCache pc(10000); | |
32fbb2ab | 635 | GenericDNSPacketWriter<PacketBuffer>::optvect_t ednsOptions; |
fa980c59 RG |
636 | EDNSSubnetOpts opt; |
637 | std::map<uint32_t, Netmask> colMap; | |
638 | size_t collisions = 0; | |
639 | size_t total = 0; | |
640 | //qname = DNSName("collision-with-ecs-parsing.cache.tests.powerdns.com."); | |
641 | ||
642 | for (size_t idxA = 0; idxA < 256; idxA++) { | |
643 | for (size_t idxB = 0; idxB < 256; idxB++) { | |
644 | for (size_t idxC = 0; idxC < 256; idxC++) { | |
32fbb2ab RG |
645 | PacketBuffer secondQuery; |
646 | GenericDNSPacketWriter<PacketBuffer> pwFQ(secondQuery, qname, QType::AAAA, QClass::IN, 0); | |
fa980c59 RG |
647 | pwFQ.getHeader()->rd = 1; |
648 | pwFQ.getHeader()->qr = false; | |
649 | pwFQ.getHeader()->id = 0x42; | |
650 | opt.source = Netmask("10." + std::to_string(idxA) + "." + std::to_string(idxB) + "." + std::to_string(idxC) + "/32"); | |
651 | ednsOptions.clear(); | |
652 | ednsOptions.push_back(std::make_pair(EDNSOptionCode::ECS, makeEDNSSubnetOptsString(opt))); | |
653 | pwFQ.addOpt(512, 0, 0, ednsOptions); | |
654 | pwFQ.commit(); | |
341d2553 | 655 | secondKey = pc.getKey(qname.toDNSString(), qname.wirelength(), secondQuery, false); |
fa980c59 RG |
656 | auto pair = colMap.insert(std::make_pair(secondKey, opt.source)); |
657 | total++; | |
658 | if (!pair.second) { | |
659 | collisions++; | |
660 | cerr<<"Collision between "<<colMap[secondKey].toString()<<" and "<<opt.source.toString()<<" for key "<<secondKey<<endl; | |
661 | goto done; | |
662 | } | |
663 | } | |
664 | } | |
665 | } | |
666 | done: | |
667 | cerr<<"collisions: "<<collisions<<endl; | |
668 | cerr<<"total: "<<total<<endl; | |
669 | } | |
670 | #endif | |
78e3ac9e RG |
671 | } |
672 | ||
d7728daf RG |
673 | BOOST_AUTO_TEST_CASE(test_PCDNSSECCollision) { |
674 | const size_t maxEntries = 150000; | |
675 | DNSDistPacketCache PC(maxEntries, 86400, 1, 60, 3600, 60, false, 1, true, true); | |
690b86b7 | 676 | BOOST_CHECK_EQUAL(PC.getSize(), 0U); |
d7728daf RG |
677 | |
678 | DNSName qname("www.powerdns.com."); | |
679 | uint16_t qtype = QType::AAAA; | |
680 | uint16_t qid = 0x42; | |
681 | uint32_t key; | |
682 | boost::optional<Netmask> subnetOut; | |
683 | ||
684 | /* lookup for a query with DNSSEC OK, | |
685 | insert a corresponding response with DO set, | |
686 | check that it doesn't match without DO, but does with it */ | |
687 | { | |
32fbb2ab RG |
688 | PacketBuffer query; |
689 | GenericDNSPacketWriter<PacketBuffer> pwQ(query, qname, qtype, QClass::IN, 0); | |
d7728daf RG |
690 | pwQ.getHeader()->rd = 1; |
691 | pwQ.getHeader()->id = qid; | |
692 | pwQ.addOpt(512, 0, EDNS_HEADER_FLAG_DO); | |
693 | pwQ.commit(); | |
694 | ||
d7728daf RG |
695 | ComboAddress remote("192.0.2.1"); |
696 | struct timespec queryTime; | |
697 | gettime(&queryTime); | |
2c0e81bb | 698 | DNSQuestion dq(&qname, QType::AAAA, QClass::IN, &remote, &remote, query, dnsdist::Protocol::DoUDP, &queryTime); |
d84ea5be | 699 | bool found = PC.get(dq, 0, &key, subnetOut, true, receivedOverUDP); |
d7728daf RG |
700 | BOOST_CHECK_EQUAL(found, false); |
701 | ||
32fbb2ab RG |
702 | PacketBuffer response; |
703 | GenericDNSPacketWriter<PacketBuffer> pwR(response, qname, qtype, QClass::IN, 0); | |
d7728daf RG |
704 | pwR.getHeader()->rd = 1; |
705 | pwR.getHeader()->id = qid; | |
706 | pwR.startRecord(qname, qtype, 100, QClass::IN, DNSResourceRecord::ANSWER); | |
707 | ComboAddress v6("::1"); | |
708 | pwR.xfrCAWithoutPort(6, v6); | |
709 | pwR.commit(); | |
710 | pwR.addOpt(512, 0, EDNS_HEADER_FLAG_DO); | |
711 | pwR.commit(); | |
712 | ||
d84ea5be | 713 | PC.insert(key, subnetOut, *(getFlagsFromDNSHeader(pwR.getHeader())), /* DNSSEC OK is set */ true, qname, qtype, QClass::IN, response, receivedOverUDP, RCode::NoError, boost::none); |
690b86b7 | 714 | BOOST_CHECK_EQUAL(PC.getSize(), 1U); |
d7728daf | 715 | |
d84ea5be | 716 | found = PC.get(dq, 0, &key, subnetOut, false, receivedOverUDP); |
d7728daf RG |
717 | BOOST_CHECK_EQUAL(found, false); |
718 | ||
d84ea5be | 719 | found = PC.get(dq, 0, &key, subnetOut, true, receivedOverUDP); |
d7728daf RG |
720 | BOOST_CHECK_EQUAL(found, true); |
721 | } | |
722 | ||
723 | } | |
724 | ||
886e2cf2 | 725 | BOOST_AUTO_TEST_SUITE_END() |