1 #define BOOST_TEST_DYN_LINK
2 #define BOOST_TEST_NO_MAIN
8 #include <boost/test/unit_test.hpp>
10 #include "dnsparser.hh"
12 BOOST_AUTO_TEST_SUITE(test_dnsparser_cc
)
14 BOOST_AUTO_TEST_CASE(test_editDNSPacketTTL
) {
16 auto generatePacket
= [](uint32_t ttl
) {
17 DNSName
name("powerdns.com.");
18 ComboAddress
v4("1.2.3.4");
19 ComboAddress
v6("2001:db8::1");
21 vector
<uint8_t> packet
;
22 DNSPacketWriter
pwR(packet
, name
, QType::A
, QClass::IN
, 0);
23 pwR
.getHeader()->qr
= 1;
25 /* record we want to see altered */
26 pwR
.startRecord(name
, QType::A
, ttl
, QClass::IN
, DNSResourceRecord::ANSWER
);
27 pwR
.xfrIP(v4
.sin4
.sin_addr
.s_addr
);
30 /* same record but different TTL (yeah, don't do that but it's just a test) */
31 pwR
.startRecord(name
, QType::A
, 100, QClass::IN
, DNSResourceRecord::ANSWER
);
32 pwR
.xfrIP(v4
.sin4
.sin_addr
.s_addr
);
36 pwR
.startRecord(name
, QType::AAAA
, 42, QClass::IN
, DNSResourceRecord::ANSWER
);
37 pwR
.xfrIP6(std::string(reinterpret_cast<const char*>(v6
.sin6
.sin6_addr
.s6_addr
), 16));
41 pwR
.startRecord(name
, QType::A
, 42, QClass::CHAOS
, DNSResourceRecord::ANSWER
);
44 /* different section */
45 pwR
.startRecord(name
, QType::A
, 42, QClass::IN
, DNSResourceRecord::AUTHORITY
);
46 pwR
.xfrIP(v4
.sin4
.sin_addr
.s_addr
);
52 auto firstPacket
= generatePacket(42);
53 auto expectedAlteredPacket
= generatePacket(84);
56 editDNSPacketTTL(reinterpret_cast<char*>(firstPacket
.data()), firstPacket
.size(), [&called
](uint8_t section
, uint16_t class_
, uint16_t type
, uint32_t ttl
) {
60 /* only updates the TTL of IN/A, in answer, with an existing ttl of 42 */
61 if (section
== 1 && class_
== QClass::IN
&& type
== QType::A
&& ttl
== 42) {
67 /* check that we have been for all records */
68 BOOST_CHECK_EQUAL(called
, 5);
70 BOOST_REQUIRE_EQUAL(firstPacket
.size(), expectedAlteredPacket
.size());
71 for (size_t idx
= 0; idx
< firstPacket
.size(); idx
++) {
72 BOOST_CHECK_EQUAL(firstPacket
.at(idx
), expectedAlteredPacket
.at(idx
));
74 BOOST_CHECK(firstPacket
== expectedAlteredPacket
);
76 /* now call it with a truncated packet, missing the last TTL and rdata,
77 we should only be called 4 times but everything else should be fine. */
79 editDNSPacketTTL(reinterpret_cast<char*>(firstPacket
.data()), firstPacket
.size() - sizeof(uint32_t) - /* rdata length */ sizeof (uint16_t) - /* IPv4 payload in rdata */ 4, [&called
](uint8_t section
, uint16_t class_
, uint16_t type
, uint32_t ttl
) {
83 /* only updates the TTL of IN/A, in answer, with an existing ttl of 42 */
84 if (section
== 1 && class_
== QClass::IN
&& type
== QType::A
&& ttl
== 42) {
90 /* check that we have been for all records */
91 BOOST_CHECK_EQUAL(called
, 4);
92 BOOST_CHECK(firstPacket
== expectedAlteredPacket
);
95 BOOST_AUTO_TEST_CASE(test_ageDNSPacket
) {
97 auto generatePacket
= [](uint32_t ttl
) {
98 DNSName
name("powerdns.com.");
99 ComboAddress
v4("1.2.3.4");
100 ComboAddress
v6("2001:db8::1");
102 vector
<uint8_t> packet
;
103 DNSPacketWriter
pwR(packet
, name
, QType::A
, QClass::IN
, 0);
104 pwR
.getHeader()->qr
= 1;
106 /* record we want to see altered */
107 pwR
.startRecord(name
, QType::A
, ttl
, QClass::IN
, DNSResourceRecord::ANSWER
);
108 pwR
.xfrIP(v4
.sin4
.sin_addr
.s_addr
);
111 pwR
.addOpt(4096, 0, 0);
117 auto firstPacket
= generatePacket(3600);
118 auto expectedAlteredPacket
= generatePacket(1800);
120 ageDNSPacket(reinterpret_cast<char*>(firstPacket
.data()), firstPacket
.size(), 1800);
122 BOOST_REQUIRE_EQUAL(firstPacket
.size(), expectedAlteredPacket
.size());
123 for (size_t idx
= 0; idx
< firstPacket
.size(); idx
++) {
124 BOOST_CHECK_EQUAL(firstPacket
.at(idx
), expectedAlteredPacket
.at(idx
));
126 BOOST_CHECK(firstPacket
== expectedAlteredPacket
);
128 /* now call it with a truncated packet, missing the last TTL and rdata,
129 the packet should not be altered. */
130 ageDNSPacket(reinterpret_cast<char*>(firstPacket
.data()), firstPacket
.size() - sizeof(uint32_t) - /* rdata length */ sizeof (uint16_t) - /* IPv4 payload in rdata */ 4 - /* size of OPT record */ 11, 900);
132 BOOST_CHECK(firstPacket
== expectedAlteredPacket
);
134 /* now remove more than the remaining TTL, not that while TTL are,
135 per rfc1035 errata, "a 32 bit unsigned integer" so we should be
136 able to expect unsigned overflow to apply, but rfc2181 specifies
137 a maximum of "2^31 - 1". */
138 ageDNSPacket(reinterpret_cast<char*>(firstPacket
.data()), firstPacket
.size(), 1801);
140 uint32_t ttl
= std::numeric_limits
<uint32_t>::max();
142 expectedAlteredPacket
= generatePacket(ttl
);
143 BOOST_REQUIRE_EQUAL(firstPacket
.size(), expectedAlteredPacket
.size());
144 for (size_t idx
= 0; idx
< firstPacket
.size(); idx
++) {
145 BOOST_CHECK_EQUAL(firstPacket
.at(idx
), expectedAlteredPacket
.at(idx
));
147 BOOST_CHECK(firstPacket
== expectedAlteredPacket
);
150 BOOST_AUTO_TEST_CASE(test_getDNSPacketMinTTL
) {
152 const DNSName
name("powerdns.com.");
153 const ComboAddress
v4("1.2.3.4");
154 const ComboAddress
v6("2001:db8::1");
158 vector
<uint8_t> packet
;
159 DNSPacketWriter
pwR(packet
, name
, QType::A
, QClass::IN
, 0);
160 pwR
.getHeader()->qr
= 1;
163 auto result
= getDNSPacketMinTTL(reinterpret_cast<char*>(packet
.data()), packet
.size(), nullptr);
164 BOOST_CHECK_EQUAL(result
, std::numeric_limits
<uint32_t>::max());
168 /* only one record, not an OPT one */
170 vector
<uint8_t> packet
;
171 DNSPacketWriter
pwR(packet
, name
, QType::A
, QClass::IN
, 0);
172 pwR
.getHeader()->qr
= 1;
175 pwR
.startRecord(name
, QType::A
, ttl
, QClass::IN
, DNSResourceRecord::ANSWER
);
176 pwR
.xfrIP(v4
.sin4
.sin_addr
.s_addr
);
179 auto result
= getDNSPacketMinTTL(reinterpret_cast<char*>(packet
.data()), packet
.size(), nullptr);
180 BOOST_CHECK_EQUAL(result
, ttl
);
184 /* only one record, an OPT one */
185 vector
<uint8_t> packet
;
186 DNSPacketWriter
pwR(packet
, name
, QType::A
, QClass::IN
, 0);
187 pwR
.getHeader()->qr
= 1;
190 pwR
.addOpt(4096, 0, 0);
193 auto result
= getDNSPacketMinTTL(reinterpret_cast<char*>(packet
.data()), packet
.size(), nullptr);
194 BOOST_CHECK_EQUAL(result
, std::numeric_limits
<uint32_t>::max());
198 /* records with different TTLs, should return the lower */
199 vector
<uint8_t> packet
;
200 DNSPacketWriter
pwR(packet
, name
, QType::A
, QClass::IN
, 0);
201 pwR
.getHeader()->qr
= 1;
204 pwR
.startRecord(name
, QType::A
, 257, QClass::IN
, DNSResourceRecord::ANSWER
);
205 pwR
.xfrIP(v4
.sin4
.sin_addr
.s_addr
);
208 pwR
.startRecord(name
, QType::A
, 255, QClass::IN
, DNSResourceRecord::AUTHORITY
);
209 pwR
.xfrIP(v4
.sin4
.sin_addr
.s_addr
);
212 pwR
.startRecord(name
, QType::A
, 256, QClass::IN
, DNSResourceRecord::ADDITIONAL
);
213 pwR
.xfrIP(v4
.sin4
.sin_addr
.s_addr
);
216 auto result
= getDNSPacketMinTTL(reinterpret_cast<char*>(packet
.data()), packet
.size(), nullptr);
217 BOOST_CHECK_EQUAL(result
, 255);
221 /* SOA record in answer, seenAuthSOA should not be set */
222 vector
<uint8_t> packet
;
223 DNSPacketWriter
pwR(packet
, name
, QType::A
, QClass::IN
, 0);
224 pwR
.getHeader()->qr
= 1;
227 pwR
.startRecord(name
, QType::SOA
, 257, QClass::IN
, DNSResourceRecord::ANSWER
);
230 pwR
.startRecord(name
, QType::A
, 255, QClass::IN
, DNSResourceRecord::AUTHORITY
);
231 pwR
.xfrIP(v4
.sin4
.sin_addr
.s_addr
);
234 pwR
.startRecord(name
, QType::A
, 256, QClass::IN
, DNSResourceRecord::ADDITIONAL
);
235 pwR
.xfrIP(v4
.sin4
.sin_addr
.s_addr
);
238 bool seenAuthSOA
= false;
239 auto result
= getDNSPacketMinTTL(reinterpret_cast<char*>(packet
.data()), packet
.size(), &seenAuthSOA
);
240 BOOST_CHECK_EQUAL(result
, 255);
241 BOOST_CHECK_EQUAL(seenAuthSOA
, false);
245 /* one SOA record in auth, seenAuthSOA should be set */
246 vector
<uint8_t> packet
;
247 DNSPacketWriter
pwR(packet
, name
, QType::A
, QClass::IN
, 0);
248 pwR
.getHeader()->qr
= 1;
251 pwR
.startRecord(name
, QType::A
, 255, QClass::IN
, DNSResourceRecord::ANSWER
);
252 pwR
.xfrIP(v4
.sin4
.sin_addr
.s_addr
);
255 pwR
.startRecord(name
, QType::SOA
, 257, QClass::IN
, DNSResourceRecord::AUTHORITY
);
258 pwR
.startRecord(name
, QType::A
, 256, QClass::IN
, DNSResourceRecord::ADDITIONAL
);
259 pwR
.xfrIP(v4
.sin4
.sin_addr
.s_addr
);
262 bool seenAuthSOA
= false;
263 auto result
= getDNSPacketMinTTL(reinterpret_cast<char*>(packet
.data()), packet
.size(), &seenAuthSOA
);
264 BOOST_CHECK_EQUAL(result
, 255);
265 BOOST_CHECK_EQUAL(seenAuthSOA
, true);
269 /* one SOA record of the wrong qclass in auth, seenAuthSOA should not be set */
270 vector
<uint8_t> packet
;
271 DNSPacketWriter
pwR(packet
, name
, QType::A
, QClass::IN
, 0);
272 pwR
.getHeader()->qr
= 1;
275 pwR
.startRecord(name
, QType::A
, 257, QClass::IN
, DNSResourceRecord::ANSWER
);
276 pwR
.xfrIP(v4
.sin4
.sin_addr
.s_addr
);
279 pwR
.startRecord(name
, QType::SOA
, 255, QClass::CHAOS
, DNSResourceRecord::AUTHORITY
);
282 pwR
.startRecord(name
, QType::A
, 256, QClass::IN
, DNSResourceRecord::ADDITIONAL
);
283 pwR
.xfrIP(v4
.sin4
.sin_addr
.s_addr
);
286 bool seenAuthSOA
= false;
287 auto result
= getDNSPacketMinTTL(reinterpret_cast<char*>(packet
.data()), packet
.size(), &seenAuthSOA
);
288 BOOST_CHECK_EQUAL(result
, 255);
289 BOOST_CHECK_EQUAL(seenAuthSOA
, false);
293 /* one A record in auth, seenAuthSOA should not be set */
294 vector
<uint8_t> packet
;
295 DNSPacketWriter
pwR(packet
, name
, QType::A
, QClass::IN
, 0);
296 pwR
.getHeader()->qr
= 1;
299 pwR
.startRecord(name
, QType::A
, 257, QClass::IN
, DNSResourceRecord::AUTHORITY
);
300 pwR
.xfrIP(v4
.sin4
.sin_addr
.s_addr
);
303 bool seenAuthSOA
= false;
304 auto result
= getDNSPacketMinTTL(reinterpret_cast<char*>(packet
.data()), packet
.size(), &seenAuthSOA
);
305 BOOST_CHECK_EQUAL(result
, 257);
306 BOOST_CHECK_EQUAL(seenAuthSOA
, false);
310 /* one SOA record in additional, seenAuthSOA should not be set */
311 vector
<uint8_t> packet
;
312 DNSPacketWriter
pwR(packet
, name
, QType::A
, QClass::IN
, 0);
313 pwR
.getHeader()->qr
= 1;
316 pwR
.startRecord(name
, QType::SOA
, 255, QClass::IN
, DNSResourceRecord::ADDITIONAL
);
319 bool seenAuthSOA
= false;
320 auto result
= getDNSPacketMinTTL(reinterpret_cast<char*>(packet
.data()), packet
.size(), &seenAuthSOA
);
321 BOOST_CHECK_EQUAL(result
, 255);
322 BOOST_CHECK_EQUAL(seenAuthSOA
, false);
326 /* truncated packet, no exception should be raised */
327 /* one SOA record in auth, seenAuthSOA should be set */
328 vector
<uint8_t> packet
;
329 DNSPacketWriter
pwR(packet
, name
, QType::A
, QClass::IN
, 0);
330 pwR
.getHeader()->qr
= 1;
333 pwR
.startRecord(name
, QType::A
, 255, QClass::IN
, DNSResourceRecord::ANSWER
);
334 pwR
.xfrIP(v4
.sin4
.sin_addr
.s_addr
);
337 pwR
.startRecord(name
, QType::SOA
, 257, QClass::IN
, DNSResourceRecord::AUTHORITY
);
340 pwR
.startRecord(name
, QType::A
, 254, QClass::IN
, DNSResourceRecord::ADDITIONAL
);
341 pwR
.xfrIP(v4
.sin4
.sin_addr
.s_addr
);
344 bool seenAuthSOA
= false;
345 auto result
= getDNSPacketMinTTL(reinterpret_cast<char*>(packet
.data()), packet
.size() - sizeof(uint32_t) - /* rdata length */ sizeof (uint16_t) - /* IPv4 payload in rdata */ 4, &seenAuthSOA
);
346 BOOST_CHECK_EQUAL(result
, 255);
347 BOOST_CHECK_EQUAL(seenAuthSOA
, true);
351 BOOST_AUTO_TEST_CASE(test_getDNSPacketLength
) {
353 const DNSName
name("powerdns.com.");
354 const ComboAddress
v4("1.2.3.4");
355 const ComboAddress
v6("2001:db8::1");
359 vector
<uint8_t> packet
;
360 DNSPacketWriter
pwR(packet
, name
, QType::A
, QClass::IN
, 0);
361 pwR
.getHeader()->qr
= 1;
364 auto result
= getDNSPacketLength(reinterpret_cast<char*>(packet
.data()), packet
.size());
365 BOOST_CHECK_EQUAL(result
, packet
.size());
369 /* several records */
370 vector
<uint8_t> packet
;
371 DNSPacketWriter
pwR(packet
, name
, QType::A
, QClass::IN
, 0);
372 pwR
.getHeader()->qr
= 1;
375 pwR
.startRecord(name
, QType::A
, 255, QClass::IN
, DNSResourceRecord::ANSWER
);
376 pwR
.xfrIP(v4
.sin4
.sin_addr
.s_addr
);
379 pwR
.startRecord(name
, QType::SOA
, 257, QClass::IN
, DNSResourceRecord::AUTHORITY
);
382 pwR
.startRecord(name
, QType::A
, 256, QClass::IN
, DNSResourceRecord::ADDITIONAL
);
383 pwR
.xfrIP(v4
.sin4
.sin_addr
.s_addr
);
386 pwR
.addOpt(4096, 0, 0);
389 auto result
= getDNSPacketLength(reinterpret_cast<char*>(packet
.data()), packet
.size());
390 BOOST_CHECK_EQUAL(result
, packet
.size());
395 vector
<uint8_t> packet
;
396 DNSPacketWriter
pwR(packet
, name
, QType::A
, QClass::IN
, 0);
397 pwR
.getHeader()->qr
= 1;
400 pwR
.startRecord(name
, QType::A
, 255, QClass::IN
, DNSResourceRecord::ANSWER
);
401 pwR
.xfrIP(v4
.sin4
.sin_addr
.s_addr
);
404 pwR
.startRecord(name
, QType::SOA
, 257, QClass::IN
, DNSResourceRecord::AUTHORITY
);
407 pwR
.startRecord(name
, QType::A
, 256, QClass::IN
, DNSResourceRecord::ADDITIONAL
);
408 pwR
.xfrIP(v4
.sin4
.sin_addr
.s_addr
);
411 pwR
.addOpt(4096, 0, 0);
414 auto realSize
= packet
.size();
415 packet
.resize(realSize
+ 512);
416 auto result
= getDNSPacketLength(reinterpret_cast<char*>(packet
.data()), packet
.size());
417 BOOST_CHECK_EQUAL(result
, realSize
);
421 /* truncated packet, should return the full size */
422 vector
<uint8_t> packet
;
423 DNSPacketWriter
pwR(packet
, name
, QType::A
, QClass::IN
, 0);
424 pwR
.getHeader()->qr
= 1;
427 pwR
.startRecord(name
, QType::A
, 255, QClass::IN
, DNSResourceRecord::ANSWER
);
428 pwR
.xfrIP(v4
.sin4
.sin_addr
.s_addr
);
431 pwR
.startRecord(name
, QType::SOA
, 257, QClass::IN
, DNSResourceRecord::AUTHORITY
);
434 pwR
.startRecord(name
, QType::A
, 256, QClass::IN
, DNSResourceRecord::ADDITIONAL
);
435 pwR
.xfrIP(v4
.sin4
.sin_addr
.s_addr
);
438 pwR
.addOpt(4096, 0, 0);
441 size_t fakeSize
= packet
.size()-1;
442 auto result
= getDNSPacketLength(reinterpret_cast<char*>(packet
.data()), fakeSize
);
443 BOOST_CHECK_EQUAL(result
, fakeSize
);
448 BOOST_AUTO_TEST_CASE(test_getRecordsOfTypeCount
) {
449 const DNSName
name("powerdns.com.");
450 const ComboAddress
v4("1.2.3.4");
451 const ComboAddress
v6("2001:db8::1");
454 vector
<uint8_t> packet
;
455 DNSPacketWriter
pwR(packet
, name
, QType::A
, QClass::IN
, 0);
456 pwR
.getHeader()->qr
= 1;
459 pwR
.startRecord(name
, QType::A
, 255, QClass::IN
, DNSResourceRecord::ANSWER
);
460 pwR
.xfrIP(v4
.sin4
.sin_addr
.s_addr
);
463 pwR
.startRecord(name
, QType::SOA
, 257, QClass::IN
, DNSResourceRecord::AUTHORITY
);
466 pwR
.startRecord(name
, QType::A
, 256, QClass::IN
, DNSResourceRecord::ADDITIONAL
);
467 pwR
.xfrIP(v4
.sin4
.sin_addr
.s_addr
);
470 pwR
.addOpt(4096, 0, 0);
473 BOOST_CHECK_EQUAL(getRecordsOfTypeCount(reinterpret_cast<char*>(packet
.data()), packet
.size(), 0, QType::A
), 1);
474 BOOST_CHECK_EQUAL(getRecordsOfTypeCount(reinterpret_cast<char*>(packet
.data()), packet
.size(), 0, QType::SOA
), 0);
475 BOOST_CHECK_EQUAL(getRecordsOfTypeCount(reinterpret_cast<char*>(packet
.data()), packet
.size(), 1, QType::A
), 1);
476 BOOST_CHECK_EQUAL(getRecordsOfTypeCount(reinterpret_cast<char*>(packet
.data()), packet
.size(), 1, QType::SOA
), 0);
477 BOOST_CHECK_EQUAL(getRecordsOfTypeCount(reinterpret_cast<char*>(packet
.data()), packet
.size(), 2, QType::A
), 0);
478 BOOST_CHECK_EQUAL(getRecordsOfTypeCount(reinterpret_cast<char*>(packet
.data()), packet
.size(), 2, QType::SOA
), 1);
479 BOOST_CHECK_EQUAL(getRecordsOfTypeCount(reinterpret_cast<char*>(packet
.data()), packet
.size(), 3, QType::A
), 1);
480 BOOST_CHECK_EQUAL(getRecordsOfTypeCount(reinterpret_cast<char*>(packet
.data()), packet
.size(), 3, QType::SOA
), 0);
482 BOOST_CHECK_EQUAL(getRecordsOfTypeCount(reinterpret_cast<char*>(packet
.data()), packet
.size(), 4, QType::SOA
), 0);
487 BOOST_AUTO_TEST_SUITE_END()