1 #define BOOST_TEST_DYN_LINK
2 #define BOOST_TEST_NO_MAIN
7 #include <boost/test/unit_test.hpp>
9 #include "dnswriter.hh"
10 #include "dnsrecords.hh"
11 #include "ednscookies.hh"
12 #include "ednssubnet.hh"
13 #include "packetcache.hh"
15 BOOST_AUTO_TEST_SUITE(packetcache_hh
)
17 BOOST_AUTO_TEST_CASE(test_PacketCacheAuthCollision
) {
19 /* auth version (ECS is not processed, we just hash the whole query except for the ID, while lowercasing the qname) */
20 const DNSName
qname("www.powerdns.com.");
21 uint16_t qtype
= QType::AAAA
;
23 DNSPacketWriter::optvect_t ednsOptions
;
26 /* same query, different IDs */
27 vector
<uint8_t> packet
;
28 DNSPacketWriter
pw1(packet
, qname
, qtype
);
29 pw1
.getHeader()->rd
= true;
30 pw1
.getHeader()->qr
= false;
31 pw1
.getHeader()->id
= 0x42;
32 string
spacket1((const char*)&packet
[0], packet
.size());
33 auto hash1
= PacketCache::canHashPacket(spacket1
);
36 DNSPacketWriter
pw2(packet
, qname
, qtype
);
37 pw2
.getHeader()->rd
= true;
38 pw2
.getHeader()->qr
= false;
39 pw2
.getHeader()->id
= 0x84;
40 string
spacket2((const char*)&packet
[0], packet
.size());
41 auto hash2
= PacketCache::canHashPacket(spacket2
);
43 BOOST_CHECK_EQUAL(hash1
, hash2
);
44 BOOST_CHECK(PacketCache::queryMatches(spacket1
, spacket2
, qname
));
48 /* same query, different IDs, different ECS, still hashes to the same value */
49 vector
<uint8_t> packet
;
50 DNSPacketWriter
pw1(packet
, qname
, qtype
);
51 pw1
.getHeader()->rd
= true;
52 pw1
.getHeader()->qr
= false;
53 pw1
.getHeader()->id
= 0x42;
54 opt
.source
= Netmask("10.0.18.199/32");
56 ednsOptions
.push_back(std::make_pair(EDNSOptionCode::ECS
, makeEDNSSubnetOptsString(opt
)));
57 pw1
.addOpt(512, 0, 0, ednsOptions
);
60 string
spacket1((const char*)&packet
[0], packet
.size());
61 auto hash1
= PacketCache::canHashPacket(spacket1
);
64 DNSPacketWriter
pw2(packet
, qname
, qtype
);
65 pw2
.getHeader()->rd
= true;
66 pw2
.getHeader()->qr
= false;
67 pw2
.getHeader()->id
= 0x84;
68 opt
.source
= Netmask("10.0.131.66/32");
70 ednsOptions
.push_back(std::make_pair(EDNSOptionCode::ECS
, makeEDNSSubnetOptsString(opt
)));
71 pw2
.addOpt(512, 0, 0, ednsOptions
);
74 string
spacket2((const char*)&packet
[0], packet
.size());
75 auto hash2
= PacketCache::canHashPacket(spacket2
);
77 BOOST_CHECK_EQUAL(hash1
, hash2
);
78 /* the hash is the same but we should _not_ match */
79 BOOST_CHECK(!PacketCache::queryMatches(spacket1
, spacket2
, qname
));
83 /* same query but one has DNSSECOK, not the other, different IDs, different ECS, still hashes to the same value */
84 vector
<uint8_t> packet
;
85 DNSPacketWriter
pw1(packet
, qname
, qtype
);
86 pw1
.getHeader()->rd
= true;
87 pw1
.getHeader()->qr
= false;
88 pw1
.getHeader()->id
= 0x42;
89 opt
.source
= Netmask("47.8.0.0/32");
91 ednsOptions
.push_back(std::make_pair(EDNSOptionCode::ECS
, makeEDNSSubnetOptsString(opt
)));
92 pw1
.addOpt(512, 0, EDNSOpts::DNSSECOK
, ednsOptions
);
95 string
spacket1((const char*)&packet
[0], packet
.size());
96 auto hash1
= PacketCache::canHashPacket(spacket1
);
99 DNSPacketWriter
pw2(packet
, qname
, qtype
);
100 pw2
.getHeader()->rd
= true;
101 pw2
.getHeader()->qr
= false;
102 pw2
.getHeader()->id
= 0x84;
103 opt
.source
= Netmask("18.43.1.0/32");
105 ednsOptions
.push_back(std::make_pair(EDNSOptionCode::ECS
, makeEDNSSubnetOptsString(opt
)));
106 /* no EDNSOpts::DNSSECOK !! */
107 pw2
.addOpt(512, 0, 0, ednsOptions
);
110 string
spacket2((const char*)&packet
[0], packet
.size());
111 auto hash2
= PacketCache::canHashPacket(spacket2
);
113 BOOST_CHECK_EQUAL(hash1
, hash2
);
114 /* the hash is the same but we should _not_ match */
115 BOOST_CHECK(!PacketCache::queryMatches(spacket1
, spacket2
, qname
));
119 /* same query but different cookies, still hashes to the same value */
120 vector
<uint8_t> packet
;
121 DNSPacketWriter
pw1(packet
, qname
, qtype
);
122 pw1
.getHeader()->rd
= true;
123 pw1
.getHeader()->qr
= false;
124 pw1
.getHeader()->id
= 0x42;
125 opt
.source
= Netmask("192.0.2.1/32");
127 ednsOptions
.push_back(std::make_pair(EDNSOptionCode::ECS
, makeEDNSSubnetOptsString(opt
)));
128 EDNSCookiesOpt cookiesOpt
;
129 cookiesOpt
.client
= string("deadbeef");
130 cookiesOpt
.server
= string("deadbeef");
131 cookiesOpt
.server
[4] = -42;
132 cookiesOpt
.server
[5] = -6;
133 cookiesOpt
.server
[6] = 1;
134 cookiesOpt
.server
[7] = 0;
135 ednsOptions
.push_back(std::make_pair(EDNSOptionCode::COOKIE
, makeEDNSCookiesOptString(cookiesOpt
)));
136 pw1
.addOpt(512, 0, EDNSOpts::DNSSECOK
, ednsOptions
);
139 string
spacket1((const char*)&packet
[0], packet
.size());
140 auto hash1
= PacketCache::canHashPacket(spacket1
);
143 DNSPacketWriter
pw2(packet
, qname
, qtype
);
144 pw2
.getHeader()->rd
= true;
145 pw2
.getHeader()->qr
= false;
146 pw2
.getHeader()->id
= 0x84;
147 opt
.source
= Netmask("192.0.2.1/32");
149 ednsOptions
.push_back(std::make_pair(EDNSOptionCode::ECS
, makeEDNSSubnetOptsString(opt
)));
150 cookiesOpt
.client
= string("deadbeef");
151 cookiesOpt
.server
= string("deadbeef");
152 cookiesOpt
.server
[4] = 29;
153 cookiesOpt
.server
[5] = -79;
154 cookiesOpt
.server
[6] = 1;
155 cookiesOpt
.server
[7] = 0;
156 ednsOptions
.push_back(std::make_pair(EDNSOptionCode::COOKIE
, makeEDNSCookiesOptString(cookiesOpt
)));
157 pw2
.addOpt(512, 0, EDNSOpts::DNSSECOK
, ednsOptions
);
160 string
spacket2((const char*)&packet
[0], packet
.size());
161 auto hash2
= PacketCache::canHashPacket(spacket2
);
163 BOOST_CHECK_EQUAL(hash1
, hash2
);
164 /* the hash is the same but we should _not_ match */
165 BOOST_CHECK(!PacketCache::queryMatches(spacket1
, spacket2
, qname
));
169 BOOST_AUTO_TEST_CASE(test_PacketCacheRecSimple
) {
171 const DNSName
qname("www.powerdns.com.");
172 uint16_t qtype
= QType::AAAA
;
174 DNSPacketWriter::optvect_t ednsOptions
;
179 vector
<uint8_t> packet
;
180 DNSPacketWriter
pw1(packet
, qname
, qtype
);
181 pw1
.getHeader()->rd
= true;
182 pw1
.getHeader()->qr
= false;
183 pw1
.getHeader()->id
= 0x42;
184 pw1
.addOpt(512, 0, 0);
187 string
spacket1((const char*)&packet
[0], packet
.size());
188 /* set the RD length to a large value */
189 unsigned char* ptr
= reinterpret_cast<unsigned char*>(&spacket1
.at(sizeof(dnsheader
) + qname
.wirelength() + /* qtype and qclass */ 4 + /* OPT root label (1), type (2), class (2) and ttl (4) */ 9));
192 /* truncate the end of the OPT header to try to trigger an out of bounds read */
193 spacket1
.resize(spacket1
.size() - 6);
194 PacketCache::canHashPacket(spacket1
, &ecsBegin
, &ecsEnd
);
196 BOOST_CHECK_EQUAL(ecsBegin
, 0);
197 BOOST_CHECK_EQUAL(ecsEnd
, 0);
201 BOOST_AUTO_TEST_CASE(test_PacketCacheRecCollision
) {
203 /* rec version (ECS is processed, we hash the whole query except for the ID and the ECS value, while lowercasing the qname) */
204 const DNSName
qname("www.powerdns.com.");
205 uint16_t qtype
= QType::AAAA
;
207 DNSPacketWriter::optvect_t ednsOptions
;
212 /* same query, different IDs */
213 vector
<uint8_t> packet
;
214 DNSPacketWriter
pw1(packet
, qname
, qtype
);
215 pw1
.getHeader()->rd
= true;
216 pw1
.getHeader()->qr
= false;
217 pw1
.getHeader()->id
= 0x42;
218 string
spacket1((const char*)&packet
[0], packet
.size());
219 auto hash1
= PacketCache::canHashPacket(spacket1
, &ecsBegin
, &ecsEnd
);
221 BOOST_CHECK_EQUAL(ecsBegin
, 0);
222 BOOST_CHECK_EQUAL(ecsEnd
, 0);
225 DNSPacketWriter
pw2(packet
, qname
, qtype
);
226 pw2
.getHeader()->rd
= true;
227 pw2
.getHeader()->qr
= false;
228 pw2
.getHeader()->id
= 0x84;
229 string
spacket2((const char*)&packet
[0], packet
.size());
230 auto hash2
= PacketCache::canHashPacket(spacket2
, &ecsBegin
, &ecsEnd
);
232 BOOST_CHECK_EQUAL(ecsBegin
, 0);
233 BOOST_CHECK_EQUAL(ecsEnd
, 0);
235 BOOST_CHECK_EQUAL(hash1
, hash2
);
236 BOOST_CHECK(PacketCache::queryMatches(spacket1
, spacket2
, qname
, ecsBegin
, ecsEnd
));
240 /* same query, different IDs, different ECS, still hashes to the same value */
241 vector
<uint8_t> packet
;
242 DNSPacketWriter
pw1(packet
, qname
, qtype
);
243 pw1
.getHeader()->rd
= true;
244 pw1
.getHeader()->qr
= false;
245 pw1
.getHeader()->id
= 0x42;
246 opt
.source
= Netmask("10.0.18.199/32");
248 ednsOptions
.push_back(std::make_pair(EDNSOptionCode::ECS
, makeEDNSSubnetOptsString(opt
)));
249 pw1
.addOpt(512, 0, 0, ednsOptions
);
252 string
spacket1((const char*)&packet
[0], packet
.size());
253 auto hash1
= PacketCache::canHashPacket(spacket1
, &ecsBegin
, &ecsEnd
);
255 BOOST_CHECK_EQUAL(ecsBegin
, sizeof(dnsheader
) + qname
.wirelength() + ( 2 * sizeof(uint16_t)) /* qtype */ + (2 * sizeof(uint16_t)) /* qclass */ + /* OPT root label */ 1 + sizeof(uint32_t) /* TTL */ + DNS_RDLENGTH_SIZE
);
256 BOOST_CHECK_EQUAL(ecsEnd
, ecsBegin
+ EDNS_OPTION_CODE_SIZE
+ EDNS_OPTION_LENGTH_SIZE
+ 2 /* family */ + 1 /* scope length */ + 1 /* source length */ + 4 /* IPv4 */);
259 DNSPacketWriter
pw2(packet
, qname
, qtype
);
260 pw2
.getHeader()->rd
= true;
261 pw2
.getHeader()->qr
= false;
262 pw2
.getHeader()->id
= 0x84;
263 opt
.source
= Netmask("10.0.131.66/32");
265 ednsOptions
.push_back(std::make_pair(EDNSOptionCode::ECS
, makeEDNSSubnetOptsString(opt
)));
266 pw2
.addOpt(512, 0, 0, ednsOptions
);
269 string
spacket2((const char*)&packet
[0], packet
.size());
270 auto hash2
= PacketCache::canHashPacket(spacket2
, &ecsBegin
, &ecsEnd
);
272 BOOST_CHECK_EQUAL(ecsBegin
, sizeof(dnsheader
) + qname
.wirelength() + ( 2 * sizeof(uint16_t)) /* qtype */ + (2 * sizeof(uint16_t)) /* qclass */ + /* OPT root label */ 1 + sizeof(uint32_t) /* TTL */ + DNS_RDLENGTH_SIZE
);
273 BOOST_CHECK_EQUAL(ecsEnd
, ecsBegin
+ EDNS_OPTION_CODE_SIZE
+ EDNS_OPTION_LENGTH_SIZE
+ 2 /* family */ + 1 /* scope length */ + 1 /* source length */ + 4 /* IPv4 */);
275 BOOST_CHECK_EQUAL(hash1
, hash2
);
276 /* the hash is the same and we don't hash the ECS so we should match */
277 BOOST_CHECK(PacketCache::queryMatches(spacket1
, spacket2
, qname
, ecsBegin
, ecsEnd
));
281 /* same query but different cookies, still hashes to the same value */
282 vector
<uint8_t> packet
;
283 DNSPacketWriter
pw1(packet
, qname
, qtype
);
284 pw1
.getHeader()->rd
= true;
285 pw1
.getHeader()->qr
= false;
286 pw1
.getHeader()->id
= 0x42;
287 opt
.source
= Netmask("192.0.2.1/32");
289 ednsOptions
.push_back(std::make_pair(EDNSOptionCode::ECS
, makeEDNSSubnetOptsString(opt
)));
290 EDNSCookiesOpt cookiesOpt
;
291 cookiesOpt
.client
= string("deadbeef");
292 cookiesOpt
.server
= string("deadbeef");
293 cookiesOpt
.server
[4] = -20;
294 cookiesOpt
.server
[5] = -114;
295 cookiesOpt
.server
[6] = 0;
296 cookiesOpt
.server
[7] = 0;
297 ednsOptions
.push_back(std::make_pair(EDNSOptionCode::COOKIE
, makeEDNSCookiesOptString(cookiesOpt
)));
298 pw1
.addOpt(512, 0, EDNSOpts::DNSSECOK
, ednsOptions
);
301 string
spacket1((const char*)&packet
[0], packet
.size());
302 auto hash1
= PacketCache::canHashPacket(spacket1
, &ecsBegin
, &ecsEnd
);
304 BOOST_CHECK_EQUAL(ecsBegin
, sizeof(dnsheader
) + qname
.wirelength() + ( 2 * sizeof(uint16_t)) /* qtype */ + (2 * sizeof(uint16_t)) /* qclass */ + /* OPT root label */ 1 + sizeof(uint32_t) /* TTL */ + DNS_RDLENGTH_SIZE
);
305 BOOST_CHECK_EQUAL(ecsEnd
, ecsBegin
+ EDNS_OPTION_CODE_SIZE
+ EDNS_OPTION_LENGTH_SIZE
+ 2 /* family */ + 1 /* scope length */ + 1 /* source length */ + 4 /* IPv4 */);
308 DNSPacketWriter
pw2(packet
, qname
, qtype
);
309 pw2
.getHeader()->rd
= true;
310 pw2
.getHeader()->qr
= false;
311 pw2
.getHeader()->id
= 0x84;
312 opt
.source
= Netmask("192.0.2.1/32");
314 ednsOptions
.push_back(std::make_pair(EDNSOptionCode::ECS
, makeEDNSSubnetOptsString(opt
)));
315 cookiesOpt
.client
= string("deadbeef");
316 cookiesOpt
.server
= string("deadbeef");
317 cookiesOpt
.server
[4] = 103;
318 cookiesOpt
.server
[5] = 68;
319 cookiesOpt
.server
[6] = 0;
320 cookiesOpt
.server
[7] = 0;
321 ednsOptions
.push_back(std::make_pair(EDNSOptionCode::COOKIE
, makeEDNSCookiesOptString(cookiesOpt
)));
322 pw2
.addOpt(512, 0, EDNSOpts::DNSSECOK
, ednsOptions
);
325 string
spacket2((const char*)&packet
[0], packet
.size());
326 auto hash2
= PacketCache::canHashPacket(spacket2
, &ecsBegin
, &ecsEnd
);
328 BOOST_CHECK_EQUAL(ecsBegin
, sizeof(dnsheader
) + qname
.wirelength() + ( 2 * sizeof(uint16_t)) /* qtype */ + (2 * sizeof(uint16_t)) /* qclass */ + /* OPT root label */ 1 + sizeof(uint32_t) /* TTL */ + DNS_RDLENGTH_SIZE
);
329 BOOST_CHECK_EQUAL(ecsEnd
, ecsBegin
+ EDNS_OPTION_CODE_SIZE
+ EDNS_OPTION_LENGTH_SIZE
+ 2 /* family */ + 1 /* scope length */ + 1 /* source length */ + 4 /* IPv4 */);
331 BOOST_CHECK_EQUAL(hash1
, hash2
);
332 /* the hash is the same but we should _not_ match, even though we skip the ECS part, because the cookies are different */
333 BOOST_CHECK(!PacketCache::queryMatches(spacket1
, spacket2
, qname
, ecsBegin
, ecsEnd
));
337 BOOST_AUTO_TEST_SUITE_END()