]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/test-dnsdist_cc.cc
Merge pull request #14139 from omoerbeek/rec-openbsd-iputils-followup
[thirdparty/pdns.git] / pdns / test-dnsdist_cc.cc
1 /*
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
22 #define BOOST_TEST_DYN_LINK
23 #define BOOST_TEST_NO_MAIN
24
25 #include <boost/test/unit_test.hpp>
26 #include <unistd.h>
27
28 #include "dnsdist.hh"
29 #include "dnsdist-ecs.hh"
30 #include "dnsdist-xpf.hh"
31
32 #include "dolog.hh"
33 #include "dnsname.hh"
34 #include "dnsparser.hh"
35 #include "dnswriter.hh"
36 #include "ednsoptions.hh"
37 #include "ednscookies.hh"
38 #include "ednssubnet.hh"
39
40 BOOST_AUTO_TEST_SUITE(test_dnsdist_cc)
41
42 static const uint16_t ECSSourcePrefixV4 = 24;
43 static const uint16_t ECSSourcePrefixV6 = 56;
44
45 static void validateQuery(const char * packet, size_t packetSize, bool hasEdns=true, bool hasXPF=false)
46 {
47 MOADNSParser mdp(true, packet, packetSize);
48
49 BOOST_CHECK_EQUAL(mdp.d_qname.toString(), "www.powerdns.com.");
50
51 BOOST_CHECK_EQUAL(mdp.d_header.qdcount, 1);
52 BOOST_CHECK_EQUAL(mdp.d_header.ancount, 0);
53 BOOST_CHECK_EQUAL(mdp.d_header.nscount, 0);
54 uint16_t expectedARCount = 0 + (hasEdns ? 1 : 0) + (hasXPF ? 1 : 0);
55 BOOST_CHECK_EQUAL(mdp.d_header.arcount, expectedARCount);
56 }
57
58 static void validateECS(const char* packet, size_t packetSize, const ComboAddress& expected)
59 {
60 ComboAddress rem("::1");
61 unsigned int consumed = 0;
62 uint16_t qtype;
63 uint16_t qclass;
64 DNSName qname(packet, packetSize, sizeof(dnsheader), false, &qtype, &qclass, &consumed);
65 DNSQuestion dq(&qname, qtype, qclass, consumed, nullptr, &rem, const_cast<dnsheader*>(reinterpret_cast<const dnsheader*>(packet)), packetSize, packetSize, false, nullptr);
66 BOOST_CHECK(parseEDNSOptions(dq));
67 BOOST_REQUIRE(dq.ednsOptions != nullptr);
68 BOOST_CHECK_EQUAL(dq.ednsOptions->size(), 1);
69 const auto& ecsOption = dq.ednsOptions->find(EDNSOptionCode::ECS);
70 BOOST_REQUIRE(ecsOption != dq.ednsOptions->cend());
71
72 string expectedOption;
73 generateECSOption(expected, expectedOption, expected.sin4.sin_family == AF_INET ? ECSSourcePrefixV4 : ECSSourcePrefixV6);
74 /* we need to skip the option code and length, which are not included */
75 BOOST_REQUIRE_EQUAL(ecsOption->second.values.size(), 1);
76 BOOST_CHECK_EQUAL(expectedOption.substr(EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE), std::string(ecsOption->second.values.at(0).content, ecsOption->second.values.at(0).size));
77 }
78
79 static void validateResponse(const char * packet, size_t packetSize, bool hasEdns, uint8_t additionalCount=0)
80 {
81 MOADNSParser mdp(false, packet, packetSize);
82
83 BOOST_CHECK_EQUAL(mdp.d_qname.toString(), "www.powerdns.com.");
84
85 BOOST_CHECK_EQUAL(mdp.d_header.qr, 1);
86 BOOST_CHECK_EQUAL(mdp.d_header.qdcount, 1);
87 BOOST_CHECK_EQUAL(mdp.d_header.ancount, 1);
88 BOOST_CHECK_EQUAL(mdp.d_header.nscount, 0);
89 BOOST_CHECK_EQUAL(mdp.d_header.arcount, (hasEdns ? 1 : 0) + additionalCount);
90 }
91
92 BOOST_AUTO_TEST_CASE(test_addXPF)
93 {
94 static const uint16_t xpfOptionCode = 65422;
95
96 struct timespec queryTime;
97 gettime(&queryTime); // does not have to be accurate ("realTime") in tests
98 ComboAddress remote;
99 DNSName name("www.powerdns.com.");
100
101 vector<uint8_t> query;
102 DNSPacketWriter pw(query, name, QType::A, QClass::IN, 0);
103 pw.getHeader()->rd = 1;
104 const uint16_t len = query.size();
105 vector<uint8_t> queryWithXPF;
106
107 {
108 char packet[1500];
109 memcpy(packet, query.data(), query.size());
110
111 /* large enough packet */
112 unsigned int consumed = 0;
113 uint16_t qtype;
114 DNSName qname(packet, len, sizeof(dnsheader), false, &qtype, nullptr, &consumed);
115 BOOST_CHECK_EQUAL(qname, name);
116 BOOST_CHECK(qtype == QType::A);
117
118 auto dh = reinterpret_cast<dnsheader*>(packet);
119 DNSQuestion dq(&qname, qtype, QClass::IN, qname.wirelength(), &remote, &remote, dh, sizeof(packet), query.size(), false, &queryTime);
120
121 BOOST_CHECK(addXPF(dq, xpfOptionCode, false));
122 BOOST_CHECK(static_cast<size_t>(dq.len) > query.size());
123 validateQuery(packet, dq.len, false, true);
124 queryWithXPF.resize(dq.len);
125 memcpy(queryWithXPF.data(), packet, dq.len);
126 }
127
128 {
129 char packet[1500];
130 memcpy(packet, query.data(), query.size());
131
132 /* not large enough packet */
133 unsigned int consumed = 0;
134 uint16_t qtype;
135 DNSName qname(packet, len, sizeof(dnsheader), false, &qtype, nullptr, &consumed);
136 BOOST_CHECK_EQUAL(qname, name);
137 BOOST_CHECK(qtype == QType::A);
138
139 auto dh = reinterpret_cast<dnsheader*>(packet);
140 DNSQuestion dq(&qname, qtype, QClass::IN, qname.wirelength(), &remote, &remote, dh, sizeof(packet), query.size(), false, &queryTime);
141 dq.size = dq.len;
142
143 BOOST_CHECK(!addXPF(dq, xpfOptionCode, false));
144 BOOST_CHECK_EQUAL(static_cast<size_t>(dq.len), query.size());
145 validateQuery(packet, dq.len, false, false);
146 }
147
148 {
149 char packet[1500];
150 memcpy(packet, query.data(), query.size());
151
152 /* packet with trailing data (overriding it) */
153 unsigned int consumed = 0;
154 uint16_t qtype;
155 DNSName qname(packet, len, sizeof(dnsheader), false, &qtype, nullptr, &consumed);
156 BOOST_CHECK_EQUAL(qname, name);
157 BOOST_CHECK(qtype == QType::A);
158
159 auto dh = reinterpret_cast<dnsheader*>(packet);
160 DNSQuestion dq(&qname, qtype, QClass::IN, qname.wirelength(), &remote, &remote, dh, sizeof(packet), query.size(), false, &queryTime);
161
162 /* add trailing data */
163 const size_t trailingDataSize = 10;
164 /* Making sure we have enough room to allow for fake trailing data */
165 BOOST_REQUIRE(sizeof(packet) > dq.len && (sizeof(packet) - dq.len) > trailingDataSize);
166 for (size_t idx = 0; idx < trailingDataSize; idx++) {
167 packet[dq.len + idx] = 'A';
168 }
169 dq.len += trailingDataSize;
170
171 BOOST_CHECK(addXPF(dq, xpfOptionCode, false));
172 BOOST_CHECK_EQUAL(static_cast<size_t>(dq.len), queryWithXPF.size());
173 BOOST_CHECK_EQUAL(memcmp(queryWithXPF.data(), packet, queryWithXPF.size()), 0);
174 validateQuery(packet, dq.len, false, true);
175 }
176
177 {
178 char packet[1500];
179 memcpy(packet, query.data(), query.size());
180
181 /* packet with trailing data (preserving trailing data) */
182 unsigned int consumed = 0;
183 uint16_t qtype;
184 DNSName qname(packet, len, sizeof(dnsheader), false, &qtype, nullptr, &consumed);
185 BOOST_CHECK_EQUAL(qname, name);
186 BOOST_CHECK(qtype == QType::A);
187
188 auto dh = reinterpret_cast<dnsheader*>(packet);
189 DNSQuestion dq(&qname, qtype, QClass::IN, qname.wirelength(), &remote, &remote, dh, sizeof(packet), query.size(), false, &queryTime);
190
191 /* add trailing data */
192 const size_t trailingDataSize = 10;
193 /* Making sure we have enough room to allow for fake trailing data */
194 BOOST_REQUIRE(sizeof(packet) > dq.len && (sizeof(packet) - dq.len) > trailingDataSize);
195 for (size_t idx = 0; idx < trailingDataSize; idx++) {
196 packet[dq.len + idx] = 'A';
197 }
198 dq.len += trailingDataSize;
199
200 BOOST_CHECK(addXPF(dq, xpfOptionCode, true));
201 BOOST_CHECK(static_cast<size_t>(dq.len) > queryWithXPF.size());
202 BOOST_CHECK_EQUAL(memcmp(queryWithXPF.data(), packet, queryWithXPF.size()), 0);
203 for (size_t idx = 0; idx < trailingDataSize; idx++) {
204 BOOST_CHECK_EQUAL(packet[queryWithXPF.size() + idx], 'A');
205 }
206 validateQuery(packet, dq.len, false, true);
207 }
208 }
209
210 BOOST_AUTO_TEST_CASE(addECSWithoutEDNS)
211 {
212 bool ednsAdded = false;
213 bool ecsAdded = false;
214 ComboAddress remote("192.0.2.1");
215 DNSName name("www.powerdns.com.");
216 string newECSOption;
217 generateECSOption(remote, newECSOption, remote.sin4.sin_family == AF_INET ? ECSSourcePrefixV4 : ECSSourcePrefixV6);
218
219 vector<uint8_t> query;
220 DNSPacketWriter pw(query, name, QType::A, QClass::IN, 0);
221 pw.getHeader()->rd = 1;
222 uint16_t len = query.size();
223
224 /* large enough packet */
225 char packet[1500];
226 memcpy(packet, query.data(), query.size());
227
228 unsigned int consumed = 0;
229 uint16_t qtype;
230 DNSName qname(packet, len, sizeof(dnsheader), false, &qtype, nullptr, &consumed);
231 BOOST_CHECK_EQUAL(qname, name);
232 BOOST_CHECK(qtype == QType::A);
233
234 BOOST_CHECK(handleEDNSClientSubnet(packet, sizeof packet, consumed, &len, &ednsAdded, &ecsAdded, false, newECSOption, false));
235 BOOST_CHECK(static_cast<size_t>(len) > query.size());
236 BOOST_CHECK_EQUAL(ednsAdded, true);
237 BOOST_CHECK_EQUAL(ecsAdded, false);
238 validateQuery(packet, len);
239 validateECS(packet, len, remote);
240 vector<uint8_t> queryWithEDNS;
241 queryWithEDNS.resize(len);
242 memcpy(queryWithEDNS.data(), packet, len);
243
244 /* not large enough packet */
245 ednsAdded = false;
246 ecsAdded = false;
247 consumed = 0;
248 len = query.size();
249 qname = DNSName(reinterpret_cast<char*>(query.data()), len, sizeof(dnsheader), false, &qtype, nullptr, &consumed);
250 BOOST_CHECK_EQUAL(qname, name);
251 BOOST_CHECK(qtype == QType::A);
252
253 BOOST_CHECK(!handleEDNSClientSubnet(reinterpret_cast<char*>(query.data()), query.size(), consumed, &len, &ednsAdded, &ecsAdded, false, newECSOption, false));
254 BOOST_CHECK_EQUAL(static_cast<size_t>(len), query.size());
255 BOOST_CHECK_EQUAL(ednsAdded, false);
256 BOOST_CHECK_EQUAL(ecsAdded, false);
257 validateQuery(reinterpret_cast<char*>(query.data()), len, false);
258
259 /* packet with trailing data (overriding it) */
260 memcpy(packet, query.data(), query.size());
261 ednsAdded = false;
262 ecsAdded = false;
263 consumed = 0;
264 len = query.size();
265 qname = DNSName(packet, len, sizeof(dnsheader), false, &qtype, nullptr, &consumed);
266 BOOST_CHECK_EQUAL(qname, name);
267 BOOST_CHECK(qtype == QType::A);
268 /* add trailing data */
269 const size_t trailingDataSize = 10;
270 /* Making sure we have enough room to allow for fake trailing data */
271 BOOST_REQUIRE(sizeof(packet) > len && (sizeof(packet) - len) > trailingDataSize);
272 for (size_t idx = 0; idx < trailingDataSize; idx++) {
273 packet[len + idx] = 'A';
274 }
275 len += trailingDataSize;
276 BOOST_CHECK(handleEDNSClientSubnet(packet, sizeof packet, consumed, &len, &ednsAdded, &ecsAdded, false, newECSOption, false));
277 BOOST_REQUIRE_EQUAL(static_cast<size_t>(len), queryWithEDNS.size());
278 BOOST_CHECK_EQUAL(memcmp(queryWithEDNS.data(), packet, queryWithEDNS.size()), 0);
279 BOOST_CHECK_EQUAL(ednsAdded, true);
280 BOOST_CHECK_EQUAL(ecsAdded, false);
281 validateQuery(packet, len);
282
283 /* packet with trailing data (preserving trailing data) */
284 memcpy(packet, query.data(), query.size());
285 ednsAdded = false;
286 ecsAdded = false;
287 consumed = 0;
288 len = query.size();
289 qname = DNSName(packet, len, sizeof(dnsheader), false, &qtype, nullptr, &consumed);
290 BOOST_CHECK_EQUAL(qname, name);
291 BOOST_CHECK(qtype == QType::A);
292 /* add trailing data */
293 /* Making sure we have enough room to allow for fake trailing data */
294 BOOST_REQUIRE(sizeof(packet) > len && (sizeof(packet) - len) > trailingDataSize);
295 for (size_t idx = 0; idx < trailingDataSize; idx++) {
296 packet[len + idx] = 'A';
297 }
298 len += trailingDataSize;
299 BOOST_CHECK(handleEDNSClientSubnet(packet, sizeof packet, consumed, &len, &ednsAdded, &ecsAdded, false, newECSOption, true));
300 BOOST_REQUIRE_EQUAL(static_cast<size_t>(len), queryWithEDNS.size() + trailingDataSize);
301 BOOST_CHECK_EQUAL(memcmp(queryWithEDNS.data(), packet, queryWithEDNS.size()), 0);
302 for (size_t idx = 0; idx < trailingDataSize; idx++) {
303 BOOST_CHECK_EQUAL(packet[queryWithEDNS.size() + idx], 'A');
304 }
305 BOOST_CHECK_EQUAL(ednsAdded, true);
306 BOOST_CHECK_EQUAL(ecsAdded, false);
307 validateQuery(packet, len);
308 }
309
310 BOOST_AUTO_TEST_CASE(addECSWithoutEDNSAlreadyParsed)
311 {
312 bool ednsAdded = false;
313 bool ecsAdded = false;
314 ComboAddress remote("192.0.2.1");
315 DNSName name("www.powerdns.com.");
316
317 vector<uint8_t> query;
318 DNSPacketWriter pw(query, name, QType::A, QClass::IN, 0);
319 pw.getHeader()->rd = 1;
320
321 /* large enough packet */
322 char packet[1500];
323 memcpy(packet, query.data(), query.size());
324
325 unsigned int consumed = 0;
326 uint16_t qtype;
327 uint16_t qclass;
328 DNSName qname(packet, query.size(), sizeof(dnsheader), false, &qtype, &qclass, &consumed);
329 BOOST_CHECK_EQUAL(qname, name);
330 BOOST_CHECK(qtype == QType::A);
331 BOOST_CHECK(qclass == QClass::IN);
332
333 DNSQuestion dq(&qname, qtype, qclass, consumed, nullptr, &remote, reinterpret_cast<dnsheader*>(packet), sizeof(packet), query.size(), false, nullptr);
334 /* Parse the options before handling ECS, simulating a Lua rule asking for EDNS Options */
335 BOOST_CHECK(!parseEDNSOptions(dq));
336
337 /* And now we add our own ECS */
338 BOOST_CHECK(handleEDNSClientSubnet(dq, &ednsAdded, &ecsAdded, false));
339 BOOST_CHECK_GT(static_cast<size_t>(dq.len), query.size());
340 BOOST_CHECK_EQUAL(ednsAdded, true);
341 BOOST_CHECK_EQUAL(ecsAdded, false);
342 validateQuery(packet, dq.len);
343 validateECS(packet, dq.len, remote);
344
345 /* not large enough packet */
346 ednsAdded = false;
347 ecsAdded = false;
348 consumed = 0;
349 qname = DNSName(reinterpret_cast<char*>(query.data()), query.size(), sizeof(dnsheader), false, &qtype, &qclass, &consumed);
350 BOOST_CHECK_EQUAL(qname, name);
351 BOOST_CHECK(qtype == QType::A);
352 BOOST_CHECK(qclass == QClass::IN);
353 DNSQuestion dq2(&qname, qtype, qclass, consumed, nullptr, &remote, reinterpret_cast<dnsheader*>(query.data()), query.size(), query.size(), false, nullptr);
354
355 BOOST_CHECK(!handleEDNSClientSubnet(dq2, &ednsAdded, &ecsAdded, false));
356 BOOST_CHECK_EQUAL(static_cast<size_t>(dq2.len), query.size());
357 BOOST_CHECK_EQUAL(ednsAdded, false);
358 BOOST_CHECK_EQUAL(ecsAdded, false);
359 validateQuery(reinterpret_cast<char*>(query.data()), dq2.len, false);
360 }
361
362 BOOST_AUTO_TEST_CASE(addECSWithEDNSNoECS) {
363 bool ednsAdded = false;
364 bool ecsAdded = false;
365 ComboAddress remote;
366 DNSName name("www.powerdns.com.");
367 string newECSOption;
368 generateECSOption(remote, newECSOption, remote.sin4.sin_family == AF_INET ? ECSSourcePrefixV4 : ECSSourcePrefixV6);
369
370 vector<uint8_t> query;
371 DNSPacketWriter pw(query, name, QType::A, QClass::IN, 0);
372 pw.getHeader()->rd = 1;
373 pw.addOpt(512, 0, 0);
374 pw.commit();
375 uint16_t len = query.size();
376
377 /* large enough packet */
378 char packet[1500];
379 memcpy(packet, query.data(), query.size());
380
381 unsigned int consumed = 0;
382 uint16_t qtype;
383 DNSName qname(packet, len, sizeof(dnsheader), false, &qtype, NULL, &consumed);
384 BOOST_CHECK_EQUAL(qname, name);
385 BOOST_CHECK(qtype == QType::A);
386
387 BOOST_CHECK(handleEDNSClientSubnet(packet, sizeof packet, consumed, &len, &ednsAdded, &ecsAdded, false, newECSOption, false));
388 BOOST_CHECK((size_t) len > query.size());
389 BOOST_CHECK_EQUAL(ednsAdded, false);
390 BOOST_CHECK_EQUAL(ecsAdded, true);
391 validateQuery(packet, len);
392 validateECS(packet, len, remote);
393
394 /* not large enough packet */
395 consumed = 0;
396 ednsAdded = false;
397 ecsAdded = false;
398 len = query.size();
399 qname = DNSName(reinterpret_cast<char*>(query.data()), len, sizeof(dnsheader), false, &qtype, NULL, &consumed);
400 BOOST_CHECK_EQUAL(qname, name);
401 BOOST_CHECK(qtype == QType::A);
402
403 BOOST_CHECK(!handleEDNSClientSubnet(reinterpret_cast<char*>(query.data()), query.size(), consumed, &len, &ednsAdded, &ecsAdded, false, newECSOption, false));
404 BOOST_CHECK_EQUAL((size_t) len, query.size());
405 BOOST_CHECK_EQUAL(ednsAdded, false);
406 BOOST_CHECK_EQUAL(ecsAdded, false);
407 validateQuery(reinterpret_cast<char*>(query.data()), len);
408 }
409
410 BOOST_AUTO_TEST_CASE(addECSWithEDNSNoECSAlreadyParsed) {
411 bool ednsAdded = false;
412 bool ecsAdded = false;
413 ComboAddress remote("2001:DB8::1");
414 DNSName name("www.powerdns.com.");
415
416 vector<uint8_t> query;
417 DNSPacketWriter pw(query, name, QType::A, QClass::IN, 0);
418 pw.getHeader()->rd = 1;
419 pw.addOpt(512, 0, 0);
420 pw.commit();
421
422 /* large enough packet */
423 char packet[1500];
424 memcpy(packet, query.data(), query.size());
425
426 unsigned int consumed = 0;
427 uint16_t qtype;
428 uint16_t qclass;
429 DNSName qname(packet, query.size(), sizeof(dnsheader), false, &qtype, &qclass, &consumed);
430 BOOST_CHECK_EQUAL(qname, name);
431 BOOST_CHECK(qtype == QType::A);
432 BOOST_CHECK(qclass == QClass::IN);
433
434 DNSQuestion dq(&qname, qtype, qclass, consumed, nullptr, &remote, reinterpret_cast<dnsheader*>(packet), sizeof(packet), query.size(), false, nullptr);
435 /* Parse the options before handling ECS, simulating a Lua rule asking for EDNS Options */
436 BOOST_CHECK(parseEDNSOptions(dq));
437
438 /* And now we add our own ECS */
439 BOOST_CHECK(handleEDNSClientSubnet(dq, &ednsAdded, &ecsAdded, false));
440 BOOST_CHECK_GT(static_cast<size_t>(dq.len), query.size());
441 BOOST_CHECK_EQUAL(ednsAdded, false);
442 BOOST_CHECK_EQUAL(ecsAdded, true);
443 validateQuery(packet, dq.len);
444 validateECS(packet, dq.len, remote);
445
446 /* not large enough packet */
447 consumed = 0;
448 ednsAdded = false;
449 ecsAdded = false;
450 qname = DNSName(reinterpret_cast<char*>(query.data()), query.size(), sizeof(dnsheader), false, &qtype, NULL, &consumed);
451 BOOST_CHECK_EQUAL(qname, name);
452 BOOST_CHECK(qtype == QType::A);
453 BOOST_CHECK(qclass == QClass::IN);
454 DNSQuestion dq2(&qname, qtype, qclass, consumed, nullptr, &remote, reinterpret_cast<dnsheader*>(query.data()), query.size(), query.size(), false, nullptr);
455
456 BOOST_CHECK(!handleEDNSClientSubnet(dq2, &ednsAdded, &ecsAdded, false));
457 BOOST_CHECK_EQUAL(static_cast<size_t>(dq2.len), query.size());
458 BOOST_CHECK_EQUAL(ednsAdded, false);
459 BOOST_CHECK_EQUAL(ecsAdded, false);
460 validateQuery(reinterpret_cast<char*>(query.data()), dq2.len);
461 }
462
463 BOOST_AUTO_TEST_CASE(replaceECSWithSameSize) {
464 bool ednsAdded = false;
465 bool ecsAdded = false;
466 ComboAddress remote("192.168.1.25");
467 DNSName name("www.powerdns.com.");
468 ComboAddress origRemote("127.0.0.1");
469 string newECSOption;
470 generateECSOption(remote, newECSOption, remote.sin4.sin_family == AF_INET ? ECSSourcePrefixV4 : ECSSourcePrefixV6);
471
472 vector<uint8_t> query;
473 DNSPacketWriter pw(query, name, QType::A, QClass::IN, 0);
474 pw.getHeader()->rd = 1;
475 EDNSSubnetOpts ecsOpts;
476 ecsOpts.source = Netmask(origRemote, ECSSourcePrefixV4);
477 string origECSOption = makeEDNSSubnetOptsString(ecsOpts);
478 DNSPacketWriter::optvect_t opts;
479 opts.push_back(make_pair(EDNSOptionCode::ECS, origECSOption));
480 pw.addOpt(512, 0, 0, opts);
481 pw.commit();
482 uint16_t len = query.size();
483
484 /* large enough packet */
485 char packet[1500];
486 memcpy(packet, query.data(), query.size());
487
488 unsigned int consumed = 0;
489 uint16_t qtype;
490 DNSName qname(packet, len, sizeof(dnsheader), false, &qtype, NULL, &consumed);
491 BOOST_CHECK_EQUAL(qname, name);
492 BOOST_CHECK(qtype == QType::A);
493
494 BOOST_CHECK(handleEDNSClientSubnet(packet, sizeof packet, consumed, &len, &ednsAdded, &ecsAdded, true, newECSOption, false));
495 BOOST_CHECK_EQUAL((size_t) len, query.size());
496 BOOST_CHECK_EQUAL(ednsAdded, false);
497 BOOST_CHECK_EQUAL(ecsAdded, false);
498 validateQuery(packet, len);
499 validateECS(packet, len, remote);
500 }
501
502 BOOST_AUTO_TEST_CASE(replaceECSWithSameSizeAlreadyParsed) {
503 bool ednsAdded = false;
504 bool ecsAdded = false;
505 ComboAddress remote("192.168.1.25");
506 DNSName name("www.powerdns.com.");
507 ComboAddress origRemote("127.0.0.1");
508
509 vector<uint8_t> query;
510 DNSPacketWriter pw(query, name, QType::A, QClass::IN, 0);
511 pw.getHeader()->rd = 1;
512 EDNSSubnetOpts ecsOpts;
513 ecsOpts.source = Netmask(origRemote, ECSSourcePrefixV4);
514 string origECSOption = makeEDNSSubnetOptsString(ecsOpts);
515 DNSPacketWriter::optvect_t opts;
516 opts.push_back(make_pair(EDNSOptionCode::ECS, origECSOption));
517 pw.addOpt(512, 0, 0, opts);
518 pw.commit();
519
520 /* large enough packet */
521 char packet[1500];
522 memcpy(packet, query.data(), query.size());
523
524 unsigned int consumed = 0;
525 uint16_t qtype;
526 uint16_t qclass;
527 DNSName qname(packet, query.size(), sizeof(dnsheader), false, &qtype, &qclass, &consumed);
528 BOOST_CHECK_EQUAL(qname, name);
529 BOOST_CHECK(qtype == QType::A);
530 BOOST_CHECK(qclass == QClass::IN);
531
532 DNSQuestion dq(&qname, qtype, qclass, consumed, nullptr, &remote, reinterpret_cast<dnsheader*>(packet), sizeof(packet), query.size(), false, nullptr);
533 dq.ecsOverride = true;
534
535 /* Parse the options before handling ECS, simulating a Lua rule asking for EDNS Options */
536 BOOST_CHECK(parseEDNSOptions(dq));
537
538 /* And now we add our own ECS */
539 BOOST_CHECK(handleEDNSClientSubnet(dq, &ednsAdded, &ecsAdded, false));
540 BOOST_CHECK_EQUAL(static_cast<size_t>(dq.len), query.size());
541 BOOST_CHECK_EQUAL(ednsAdded, false);
542 BOOST_CHECK_EQUAL(ecsAdded, false);
543 validateQuery(packet, dq.len);
544 validateECS(packet, dq.len, remote);
545 }
546
547 BOOST_AUTO_TEST_CASE(replaceECSWithSmaller) {
548 bool ednsAdded = false;
549 bool ecsAdded = false;
550 ComboAddress remote("192.168.1.25");
551 DNSName name("www.powerdns.com.");
552 ComboAddress origRemote("127.0.0.1");
553 string newECSOption;
554 generateECSOption(remote, newECSOption, remote.sin4.sin_family == AF_INET ? ECSSourcePrefixV4 : ECSSourcePrefixV6);
555
556 vector<uint8_t> query;
557 DNSPacketWriter pw(query, name, QType::A, QClass::IN, 0);
558 pw.getHeader()->rd = 1;
559 EDNSSubnetOpts ecsOpts;
560 ecsOpts.source = Netmask(origRemote, 32);
561 string origECSOption = makeEDNSSubnetOptsString(ecsOpts);
562 DNSPacketWriter::optvect_t opts;
563 opts.push_back(make_pair(EDNSOptionCode::ECS, origECSOption));
564 pw.addOpt(512, 0, 0, opts);
565 pw.commit();
566 uint16_t len = query.size();
567
568 /* large enough packet */
569 char packet[1500];
570 memcpy(packet, query.data(), query.size());
571
572 unsigned int consumed = 0;
573 uint16_t qtype;
574 DNSName qname(packet, len, sizeof(dnsheader), false, &qtype, NULL, &consumed);
575 BOOST_CHECK_EQUAL(qname, name);
576 BOOST_CHECK(qtype == QType::A);
577
578 BOOST_CHECK(handleEDNSClientSubnet(packet, sizeof packet, consumed, &len, &ednsAdded, &ecsAdded, true, newECSOption, false));
579 BOOST_CHECK((size_t) len < query.size());
580 BOOST_CHECK_EQUAL(ednsAdded, false);
581 BOOST_CHECK_EQUAL(ecsAdded, false);
582 validateQuery(packet, len);
583 validateECS(packet, len, remote);
584 }
585
586 BOOST_AUTO_TEST_CASE(replaceECSWithLarger) {
587 bool ednsAdded = false;
588 bool ecsAdded = false;
589 ComboAddress remote("192.168.1.25");
590 DNSName name("www.powerdns.com.");
591 ComboAddress origRemote("127.0.0.1");
592 string newECSOption;
593 generateECSOption(remote, newECSOption, remote.sin4.sin_family == AF_INET ? ECSSourcePrefixV4 : ECSSourcePrefixV6);
594
595 vector<uint8_t> query;
596 DNSPacketWriter pw(query, name, QType::A, QClass::IN, 0);
597 pw.getHeader()->rd = 1;
598 EDNSSubnetOpts ecsOpts;
599 ecsOpts.source = Netmask(origRemote, 8);
600 string origECSOption = makeEDNSSubnetOptsString(ecsOpts);
601 DNSPacketWriter::optvect_t opts;
602 opts.push_back(make_pair(EDNSOptionCode::ECS, origECSOption));
603 pw.addOpt(512, 0, 0, opts);
604 pw.commit();
605 uint16_t len = query.size();
606
607 /* large enough packet */
608 char packet[1500];
609 memcpy(packet, query.data(), query.size());
610
611 unsigned int consumed = 0;
612 uint16_t qtype;
613 DNSName qname(packet, len, sizeof(dnsheader), false, &qtype, NULL, &consumed);
614 BOOST_CHECK_EQUAL(qname, name);
615 BOOST_CHECK(qtype == QType::A);
616
617 BOOST_CHECK(handleEDNSClientSubnet(packet, sizeof packet, consumed, &len, &ednsAdded, &ecsAdded, true, newECSOption, false));
618 BOOST_CHECK((size_t) len > query.size());
619 BOOST_CHECK_EQUAL(ednsAdded, false);
620 BOOST_CHECK_EQUAL(ecsAdded, false);
621 validateQuery(packet, len);
622 validateECS(packet, len, remote);
623
624 /* not large enough packet */
625 ednsAdded = false;
626 ecsAdded = false;
627 consumed = 0;
628 len = query.size();
629 qname = DNSName(reinterpret_cast<char*>(query.data()), len, sizeof(dnsheader), false, &qtype, NULL, &consumed);
630 BOOST_CHECK_EQUAL(qname, name);
631 BOOST_CHECK(qtype == QType::A);
632
633 BOOST_CHECK(!handleEDNSClientSubnet(reinterpret_cast<char*>(query.data()), query.size(), consumed, &len, &ednsAdded, &ecsAdded, true, newECSOption, false));
634 BOOST_CHECK_EQUAL((size_t) len, query.size());
635 BOOST_CHECK_EQUAL(ednsAdded, false);
636 BOOST_CHECK_EQUAL(ecsAdded, false);
637 validateQuery(reinterpret_cast<char*>(query.data()), len);
638 }
639
640 BOOST_AUTO_TEST_CASE(removeEDNSWhenFirst) {
641 DNSName name("www.powerdns.com.");
642
643 vector<uint8_t> response;
644 DNSPacketWriter pw(response, name, QType::A, QClass::IN, 0);
645 pw.getHeader()->qr = 1;
646 pw.startRecord(name, QType::A, 3600, QClass::IN, DNSResourceRecord::ANSWER, true);
647 pw.xfr32BitInt(0x01020304);
648 pw.addOpt(512, 0, 0);
649 pw.commit();
650 pw.startRecord(name, QType::A, 3600, QClass::IN, DNSResourceRecord::ADDITIONAL, true);
651 pw.xfr32BitInt(0x01020304);
652 pw.commit();
653
654 vector<uint8_t> newResponse;
655 int res = rewriteResponseWithoutEDNS(std::string((const char *) response.data(), response.size()), newResponse);
656 BOOST_CHECK_EQUAL(res, 0);
657
658 unsigned int consumed = 0;
659 uint16_t qtype;
660 DNSName qname((const char*) newResponse.data(), newResponse.size(), sizeof(dnsheader), false, &qtype, NULL, &consumed);
661 BOOST_CHECK_EQUAL(qname, name);
662 BOOST_CHECK(qtype == QType::A);
663 size_t const ednsOptRRSize = sizeof(struct dnsrecordheader) + 1 /* root in OPT RR */;
664 BOOST_CHECK_EQUAL(newResponse.size(), response.size() - ednsOptRRSize);
665
666 validateResponse((const char *) newResponse.data(), newResponse.size(), false, 1);
667 }
668
669 BOOST_AUTO_TEST_CASE(removeEDNSWhenIntermediary) {
670 DNSName name("www.powerdns.com.");
671
672 vector<uint8_t> response;
673 DNSPacketWriter pw(response, name, QType::A, QClass::IN, 0);
674 pw.getHeader()->qr = 1;
675 pw.startRecord(name, QType::A, 3600, QClass::IN, DNSResourceRecord::ANSWER, true);
676 pw.xfr32BitInt(0x01020304);
677 pw.startRecord(DNSName("other.powerdns.com."), QType::A, 3600, QClass::IN, DNSResourceRecord::ADDITIONAL, true);
678 pw.xfr32BitInt(0x01020304);
679 pw.commit();
680 pw.addOpt(512, 0, 0);
681 pw.commit();
682 pw.startRecord(DNSName("yetanother.powerdns.com."), QType::A, 3600, QClass::IN, DNSResourceRecord::ADDITIONAL, true);
683 pw.xfr32BitInt(0x01020304);
684 pw.commit();
685
686 vector<uint8_t> newResponse;
687 int res = rewriteResponseWithoutEDNS(std::string((const char *) response.data(), response.size()), newResponse);
688 BOOST_CHECK_EQUAL(res, 0);
689
690 unsigned int consumed = 0;
691 uint16_t qtype;
692 DNSName qname((const char*) newResponse.data(), newResponse.size(), sizeof(dnsheader), false, &qtype, NULL, &consumed);
693 BOOST_CHECK_EQUAL(qname, name);
694 BOOST_CHECK(qtype == QType::A);
695 size_t const ednsOptRRSize = sizeof(struct dnsrecordheader) + 1 /* root in OPT RR */;
696 BOOST_CHECK_EQUAL(newResponse.size(), response.size() - ednsOptRRSize);
697
698 validateResponse((const char *) newResponse.data(), newResponse.size(), false, 2);
699 }
700
701 BOOST_AUTO_TEST_CASE(removeEDNSWhenLast) {
702 DNSName name("www.powerdns.com.");
703
704 vector<uint8_t> response;
705 DNSPacketWriter pw(response, name, QType::A, QClass::IN, 0);
706 pw.getHeader()->qr = 1;
707 pw.startRecord(name, QType::A, 3600, QClass::IN, DNSResourceRecord::ANSWER, true);
708 pw.xfr32BitInt(0x01020304);
709 pw.commit();
710 pw.startRecord(DNSName("other.powerdns.com."), QType::A, 3600, QClass::IN, DNSResourceRecord::ADDITIONAL, true);
711 pw.xfr32BitInt(0x01020304);
712 pw.commit();
713 pw.addOpt(512, 0, 0);
714 pw.commit();
715
716 vector<uint8_t> newResponse;
717 int res = rewriteResponseWithoutEDNS(std::string((const char *) response.data(), response.size()), newResponse);
718
719 BOOST_CHECK_EQUAL(res, 0);
720
721 unsigned int consumed = 0;
722 uint16_t qtype;
723 DNSName qname((const char*) newResponse.data(), newResponse.size(), sizeof(dnsheader), false, &qtype, NULL, &consumed);
724 BOOST_CHECK_EQUAL(qname, name);
725 BOOST_CHECK(qtype == QType::A);
726 size_t const ednsOptRRSize = sizeof(struct dnsrecordheader) + 1 /* root in OPT RR */;
727 BOOST_CHECK_EQUAL(newResponse.size(), response.size() - ednsOptRRSize);
728
729 validateResponse((const char *) newResponse.data(), newResponse.size(), false, 1);
730 }
731
732 BOOST_AUTO_TEST_CASE(removeECSWhenOnlyOption) {
733 DNSName name("www.powerdns.com.");
734 ComboAddress origRemote("127.0.0.1");
735
736 vector<uint8_t> response;
737 DNSPacketWriter pw(response, name, QType::A, QClass::IN, 0);
738 pw.getHeader()->qr = 1;
739 pw.startRecord(name, QType::A, 3600, QClass::IN, DNSResourceRecord::ANSWER, true);
740 pw.xfr32BitInt(0x01020304);
741
742 pw.startRecord(name, QType::A, 3600, QClass::IN, DNSResourceRecord::ADDITIONAL, true);
743 pw.xfr32BitInt(0x01020304);
744 pw.commit();
745
746 EDNSSubnetOpts ecsOpts;
747 ecsOpts.source = Netmask(origRemote, ECSSourcePrefixV4);
748 string origECSOptionStr = makeEDNSSubnetOptsString(ecsOpts);
749 DNSPacketWriter::optvect_t opts;
750 opts.push_back(make_pair(EDNSOptionCode::ECS, origECSOptionStr));
751 pw.addOpt(512, 0, 0, opts);
752 pw.commit();
753
754 uint16_t optStart;
755 size_t optLen = 0;
756 bool last = false;
757
758 int res = locateEDNSOptRR(std::string((char *) response.data(), response.size()), &optStart, &optLen, &last);
759 BOOST_CHECK_EQUAL(res, 0);
760 BOOST_CHECK_EQUAL(last, true);
761
762 size_t responseLen = response.size();
763 size_t existingOptLen = optLen;
764 BOOST_CHECK(existingOptLen < responseLen);
765 res = removeEDNSOptionFromOPT(reinterpret_cast<char *>(response.data()) + optStart, &optLen, EDNSOptionCode::ECS);
766 BOOST_CHECK_EQUAL(res, 0);
767 BOOST_CHECK_EQUAL(optLen, existingOptLen - (origECSOptionStr.size() + 4));
768 responseLen -= (existingOptLen - optLen);
769
770 unsigned int consumed = 0;
771 uint16_t qtype;
772 DNSName qname((const char*) response.data(), responseLen, sizeof(dnsheader), false, &qtype, NULL, &consumed);
773 BOOST_CHECK_EQUAL(qname, name);
774 BOOST_CHECK(qtype == QType::A);
775
776 validateResponse((const char *) response.data(), responseLen, true, 1);
777 }
778
779 BOOST_AUTO_TEST_CASE(removeECSWhenFirstOption) {
780 DNSName name("www.powerdns.com.");
781 ComboAddress origRemote("127.0.0.1");
782
783 vector<uint8_t> response;
784 DNSPacketWriter pw(response, name, QType::A, QClass::IN, 0);
785 pw.getHeader()->qr = 1;
786 pw.startRecord(name, QType::A, 3600, QClass::IN, DNSResourceRecord::ANSWER, true);
787 pw.xfr32BitInt(0x01020304);
788
789 pw.startRecord(name, QType::A, 3600, QClass::IN, DNSResourceRecord::ADDITIONAL, true);
790 pw.xfr32BitInt(0x01020304);
791 pw.commit();
792
793 EDNSSubnetOpts ecsOpts;
794 ecsOpts.source = Netmask(origRemote, ECSSourcePrefixV6);
795 string origECSOptionStr = makeEDNSSubnetOptsString(ecsOpts);
796 EDNSCookiesOpt cookiesOpt;
797 cookiesOpt.client = string("deadbeef");
798 cookiesOpt.server = string("deadbeef");
799 string cookiesOptionStr = makeEDNSCookiesOptString(cookiesOpt);
800 DNSPacketWriter::optvect_t opts;
801 opts.push_back(make_pair(EDNSOptionCode::ECS, origECSOptionStr));
802 opts.push_back(make_pair(EDNSOptionCode::COOKIE, cookiesOptionStr));
803 pw.addOpt(512, 0, 0, opts);
804 pw.commit();
805
806 uint16_t optStart;
807 size_t optLen = 0;
808 bool last = false;
809
810 int res = locateEDNSOptRR(std::string((char *) response.data(), response.size()), &optStart, &optLen, &last);
811 BOOST_CHECK_EQUAL(res, 0);
812 BOOST_CHECK_EQUAL(last, true);
813
814 size_t responseLen = response.size();
815 size_t existingOptLen = optLen;
816 BOOST_CHECK(existingOptLen < responseLen);
817 res = removeEDNSOptionFromOPT(reinterpret_cast<char *>(response.data()) + optStart, &optLen, EDNSOptionCode::ECS);
818 BOOST_CHECK_EQUAL(res, 0);
819 BOOST_CHECK_EQUAL(optLen, existingOptLen - (origECSOptionStr.size() + 4));
820 responseLen -= (existingOptLen - optLen);
821
822 unsigned int consumed = 0;
823 uint16_t qtype;
824 DNSName qname((const char*) response.data(), responseLen, sizeof(dnsheader), false, &qtype, NULL, &consumed);
825 BOOST_CHECK_EQUAL(qname, name);
826 BOOST_CHECK(qtype == QType::A);
827
828 validateResponse((const char *) response.data(), responseLen, true, 1);
829 }
830
831 BOOST_AUTO_TEST_CASE(removeECSWhenIntermediaryOption) {
832 DNSName name("www.powerdns.com.");
833 ComboAddress origRemote("127.0.0.1");
834
835 vector<uint8_t> response;
836 DNSPacketWriter pw(response, name, QType::A, QClass::IN, 0);
837 pw.getHeader()->qr = 1;
838 pw.startRecord(name, QType::A, 3600, QClass::IN, DNSResourceRecord::ANSWER, true);
839 pw.xfr32BitInt(0x01020304);
840
841 pw.startRecord(name, QType::A, 3600, QClass::IN, DNSResourceRecord::ADDITIONAL, true);
842 pw.xfr32BitInt(0x01020304);
843 pw.commit();
844
845 EDNSSubnetOpts ecsOpts;
846 ecsOpts.source = Netmask(origRemote, ECSSourcePrefixV4);
847 string origECSOptionStr = makeEDNSSubnetOptsString(ecsOpts);
848
849 EDNSCookiesOpt cookiesOpt;
850 cookiesOpt.client = string("deadbeef");
851 cookiesOpt.server = string("deadbeef");
852 string cookiesOptionStr1 = makeEDNSCookiesOptString(cookiesOpt);
853 string cookiesOptionStr2 = makeEDNSCookiesOptString(cookiesOpt);
854
855 DNSPacketWriter::optvect_t opts;
856 opts.push_back(make_pair(EDNSOptionCode::COOKIE, cookiesOptionStr1));
857 opts.push_back(make_pair(EDNSOptionCode::ECS, origECSOptionStr));
858 opts.push_back(make_pair(EDNSOptionCode::COOKIE, cookiesOptionStr2));
859 pw.addOpt(512, 0, 0, opts);
860 pw.commit();
861
862 uint16_t optStart;
863 size_t optLen = 0;
864 bool last = false;
865
866 int res = locateEDNSOptRR(std::string((char *) response.data(), response.size()), &optStart, &optLen, &last);
867 BOOST_CHECK_EQUAL(res, 0);
868 BOOST_CHECK_EQUAL(last, true);
869
870 size_t responseLen = response.size();
871 size_t existingOptLen = optLen;
872 BOOST_CHECK(existingOptLen < responseLen);
873 res = removeEDNSOptionFromOPT(reinterpret_cast<char *>(response.data()) + optStart, &optLen, EDNSOptionCode::ECS);
874 BOOST_CHECK_EQUAL(res, 0);
875 BOOST_CHECK_EQUAL(optLen, existingOptLen - (origECSOptionStr.size() + 4));
876 responseLen -= (existingOptLen - optLen);
877
878 unsigned int consumed = 0;
879 uint16_t qtype;
880 DNSName qname((const char*) response.data(), responseLen, sizeof(dnsheader), false, &qtype, NULL, &consumed);
881 BOOST_CHECK_EQUAL(qname, name);
882 BOOST_CHECK(qtype == QType::A);
883
884 validateResponse((const char *) response.data(), responseLen, true, 1);
885 }
886
887 BOOST_AUTO_TEST_CASE(removeECSWhenLastOption) {
888 DNSName name("www.powerdns.com.");
889 ComboAddress origRemote("127.0.0.1");
890
891 vector<uint8_t> response;
892 DNSPacketWriter pw(response, name, QType::A, QClass::IN, 0);
893 pw.getHeader()->qr = 1;
894 pw.startRecord(name, QType::A, 3600, QClass::IN, DNSResourceRecord::ANSWER, true);
895 pw.xfr32BitInt(0x01020304);
896
897 pw.startRecord(name, QType::A, 3600, QClass::IN, DNSResourceRecord::ADDITIONAL, true);
898 pw.xfr32BitInt(0x01020304);
899 pw.commit();
900
901 EDNSCookiesOpt cookiesOpt;
902 cookiesOpt.client = string("deadbeef");
903 cookiesOpt.server = string("deadbeef");
904 string cookiesOptionStr = makeEDNSCookiesOptString(cookiesOpt);
905 EDNSSubnetOpts ecsOpts;
906 ecsOpts.source = Netmask(origRemote, ECSSourcePrefixV4);
907 string origECSOptionStr = makeEDNSSubnetOptsString(ecsOpts);
908 DNSPacketWriter::optvect_t opts;
909 opts.push_back(make_pair(EDNSOptionCode::COOKIE, cookiesOptionStr));
910 opts.push_back(make_pair(EDNSOptionCode::ECS, origECSOptionStr));
911 pw.addOpt(512, 0, 0, opts);
912 pw.commit();
913
914 uint16_t optStart;
915 size_t optLen = 0;
916 bool last = false;
917
918 int res = locateEDNSOptRR(std::string((char *) response.data(), response.size()), &optStart, &optLen, &last);
919 BOOST_CHECK_EQUAL(res, 0);
920 BOOST_CHECK_EQUAL(last, true);
921
922 size_t responseLen = response.size();
923 size_t existingOptLen = optLen;
924 BOOST_CHECK(existingOptLen < responseLen);
925 res = removeEDNSOptionFromOPT(reinterpret_cast<char *>(response.data()) + optStart, &optLen, EDNSOptionCode::ECS);
926 BOOST_CHECK_EQUAL(res, 0);
927 BOOST_CHECK_EQUAL(optLen, existingOptLen - (origECSOptionStr.size() + 4));
928 responseLen -= (existingOptLen - optLen);
929
930 unsigned int consumed = 0;
931 uint16_t qtype;
932 DNSName qname((const char*) response.data(), responseLen, sizeof(dnsheader), false, &qtype, NULL, &consumed);
933 BOOST_CHECK_EQUAL(qname, name);
934 BOOST_CHECK(qtype == QType::A);
935
936 validateResponse((const char *) response.data(), responseLen, true, 1);
937 }
938
939 BOOST_AUTO_TEST_CASE(rewritingWithoutECSWhenOnlyOption) {
940 DNSName name("www.powerdns.com.");
941 ComboAddress origRemote("127.0.0.1");
942
943 vector<uint8_t> response;
944 DNSPacketWriter pw(response, name, QType::A, QClass::IN, 0);
945 pw.getHeader()->qr = 1;
946 pw.startRecord(name, QType::A, 3600, QClass::IN, DNSResourceRecord::ANSWER, true);
947 pw.xfr32BitInt(0x01020304);
948
949 EDNSSubnetOpts ecsOpts;
950 ecsOpts.source = Netmask(origRemote, ECSSourcePrefixV4);
951 string origECSOptionStr = makeEDNSSubnetOptsString(ecsOpts);
952 DNSPacketWriter::optvect_t opts;
953 opts.push_back(make_pair(EDNSOptionCode::ECS, origECSOptionStr));
954 pw.addOpt(512, 0, 0, opts);
955 pw.commit();
956
957 pw.startRecord(name, QType::A, 3600, QClass::IN, DNSResourceRecord::ADDITIONAL, true);
958 pw.xfr32BitInt(0x01020304);
959 pw.commit();
960
961 vector<uint8_t> newResponse;
962 int res = rewriteResponseWithoutEDNSOption(std::string((const char *) response.data(), response.size()), EDNSOptionCode::ECS, newResponse);
963 BOOST_CHECK_EQUAL(res, 0);
964
965 BOOST_CHECK_EQUAL(newResponse.size(), response.size() - (origECSOptionStr.size() + 4));
966
967 unsigned int consumed = 0;
968 uint16_t qtype;
969 DNSName qname((const char*) newResponse.data(), newResponse.size(), sizeof(dnsheader), false, &qtype, NULL, &consumed);
970 BOOST_CHECK_EQUAL(qname, name);
971 BOOST_CHECK(qtype == QType::A);
972
973 validateResponse((const char *) newResponse.data(), newResponse.size(), true, 1);
974 }
975
976 BOOST_AUTO_TEST_CASE(rewritingWithoutECSWhenFirstOption) {
977 DNSName name("www.powerdns.com.");
978 ComboAddress origRemote("127.0.0.1");
979
980 vector<uint8_t> response;
981 DNSPacketWriter pw(response, name, QType::A, QClass::IN, 0);
982 pw.getHeader()->qr = 1;
983 pw.startRecord(name, QType::A, 3600, QClass::IN, DNSResourceRecord::ANSWER, true);
984 pw.xfr32BitInt(0x01020304);
985
986 EDNSSubnetOpts ecsOpts;
987 ecsOpts.source = Netmask(origRemote, ECSSourcePrefixV4);
988 string origECSOptionStr = makeEDNSSubnetOptsString(ecsOpts);
989 EDNSCookiesOpt cookiesOpt;
990 cookiesOpt.client = string("deadbeef");
991 cookiesOpt.server = string("deadbeef");
992 string cookiesOptionStr = makeEDNSCookiesOptString(cookiesOpt);
993 DNSPacketWriter::optvect_t opts;
994 opts.push_back(make_pair(EDNSOptionCode::ECS, origECSOptionStr));
995 opts.push_back(make_pair(EDNSOptionCode::COOKIE, cookiesOptionStr));
996 pw.addOpt(512, 0, 0, opts);
997 pw.commit();
998
999 pw.startRecord(name, QType::A, 3600, QClass::IN, DNSResourceRecord::ADDITIONAL, true);
1000 pw.xfr32BitInt(0x01020304);
1001 pw.commit();
1002
1003 vector<uint8_t> newResponse;
1004 int res = rewriteResponseWithoutEDNSOption(std::string((const char *) response.data(), response.size()), EDNSOptionCode::ECS, newResponse);
1005 BOOST_CHECK_EQUAL(res, 0);
1006
1007 BOOST_CHECK_EQUAL(newResponse.size(), response.size() - (origECSOptionStr.size() + 4));
1008
1009 unsigned int consumed = 0;
1010 uint16_t qtype;
1011 DNSName qname((const char*) newResponse.data(), newResponse.size(), sizeof(dnsheader), false, &qtype, NULL, &consumed);
1012 BOOST_CHECK_EQUAL(qname, name);
1013 BOOST_CHECK(qtype == QType::A);
1014
1015 validateResponse((const char *) newResponse.data(), newResponse.size(), true, 1);
1016 }
1017
1018 BOOST_AUTO_TEST_CASE(rewritingWithoutECSWhenIntermediaryOption) {
1019 DNSName name("www.powerdns.com.");
1020 ComboAddress origRemote("127.0.0.1");
1021
1022 vector<uint8_t> response;
1023 DNSPacketWriter pw(response, name, QType::A, QClass::IN, 0);
1024 pw.getHeader()->qr = 1;
1025 pw.startRecord(name, QType::A, 3600, QClass::IN, DNSResourceRecord::ANSWER, true);
1026 pw.xfr32BitInt(0x01020304);
1027
1028 EDNSSubnetOpts ecsOpts;
1029 ecsOpts.source = Netmask(origRemote, ECSSourcePrefixV4);
1030 string origECSOptionStr = makeEDNSSubnetOptsString(ecsOpts);
1031 EDNSCookiesOpt cookiesOpt;
1032 cookiesOpt.client = string("deadbeef");
1033 cookiesOpt.server = string("deadbeef");
1034 string cookiesOptionStr1 = makeEDNSCookiesOptString(cookiesOpt);
1035 string cookiesOptionStr2 = makeEDNSCookiesOptString(cookiesOpt);
1036 DNSPacketWriter::optvect_t opts;
1037 opts.push_back(make_pair(EDNSOptionCode::COOKIE, cookiesOptionStr1));
1038 opts.push_back(make_pair(EDNSOptionCode::ECS, origECSOptionStr));
1039 opts.push_back(make_pair(EDNSOptionCode::COOKIE, cookiesOptionStr2));
1040 pw.addOpt(512, 0, 0, opts);
1041 pw.commit();
1042
1043 pw.startRecord(name, QType::A, 3600, QClass::IN, DNSResourceRecord::ADDITIONAL, true);
1044 pw.xfr32BitInt(0x01020304);
1045 pw.commit();
1046
1047 vector<uint8_t> newResponse;
1048 int res = rewriteResponseWithoutEDNSOption(std::string((const char *) response.data(), response.size()), EDNSOptionCode::ECS, newResponse);
1049 BOOST_CHECK_EQUAL(res, 0);
1050
1051 BOOST_CHECK_EQUAL(newResponse.size(), response.size() - (origECSOptionStr.size() + 4));
1052
1053 unsigned int consumed = 0;
1054 uint16_t qtype;
1055 DNSName qname((const char*) newResponse.data(), newResponse.size(), sizeof(dnsheader), false, &qtype, NULL, &consumed);
1056 BOOST_CHECK_EQUAL(qname, name);
1057 BOOST_CHECK(qtype == QType::A);
1058
1059 validateResponse((const char *) newResponse.data(), newResponse.size(), true, 1);
1060 }
1061
1062 BOOST_AUTO_TEST_CASE(rewritingWithoutECSWhenLastOption) {
1063 DNSName name("www.powerdns.com.");
1064 ComboAddress origRemote("127.0.0.1");
1065
1066 vector<uint8_t> response;
1067 DNSPacketWriter pw(response, name, QType::A, QClass::IN, 0);
1068 pw.getHeader()->qr = 1;
1069 pw.startRecord(name, QType::A, 3600, QClass::IN, DNSResourceRecord::ANSWER, true);
1070 pw.xfr32BitInt(0x01020304);
1071
1072 EDNSSubnetOpts ecsOpts;
1073 ecsOpts.source = Netmask(origRemote, ECSSourcePrefixV4);
1074 string origECSOptionStr = makeEDNSSubnetOptsString(ecsOpts);
1075 EDNSCookiesOpt cookiesOpt;
1076 cookiesOpt.client = string("deadbeef");
1077 cookiesOpt.server = string("deadbeef");
1078 string cookiesOptionStr = makeEDNSCookiesOptString(cookiesOpt);
1079 DNSPacketWriter::optvect_t opts;
1080 opts.push_back(make_pair(EDNSOptionCode::COOKIE, cookiesOptionStr));
1081 opts.push_back(make_pair(EDNSOptionCode::ECS, origECSOptionStr));
1082 pw.addOpt(512, 0, 0, opts);
1083 pw.commit();
1084
1085 pw.startRecord(name, QType::A, 3600, QClass::IN, DNSResourceRecord::ADDITIONAL, true);
1086 pw.xfr32BitInt(0x01020304);
1087 pw.commit();
1088
1089 vector<uint8_t> newResponse;
1090 int res = rewriteResponseWithoutEDNSOption(std::string((const char *) response.data(), response.size()), EDNSOptionCode::ECS, newResponse);
1091 BOOST_CHECK_EQUAL(res, 0);
1092
1093 BOOST_CHECK_EQUAL(newResponse.size(), response.size() - (origECSOptionStr.size() + 4));
1094
1095 unsigned int consumed = 0;
1096 uint16_t qtype;
1097 DNSName qname((const char*) newResponse.data(), newResponse.size(), sizeof(dnsheader), false, &qtype, NULL, &consumed);
1098 BOOST_CHECK_EQUAL(qname, name);
1099 BOOST_CHECK(qtype == QType::A);
1100
1101 validateResponse((const char *) newResponse.data(), newResponse.size(), true, 1);
1102 }
1103
1104 static DNSQuestion getDNSQuestion(const DNSName& qname, const uint16_t qtype, const uint16_t qclass, const ComboAddress& lc, const ComboAddress& rem, const struct timespec& realTime, vector<uint8_t>& query, size_t len)
1105 {
1106 dnsheader* dh = reinterpret_cast<dnsheader*>(query.data());
1107
1108 return DNSQuestion(&qname, qtype, qclass, qname.wirelength(), &lc, &rem, dh, query.size(), len, false, &realTime);
1109 }
1110
1111 static DNSQuestion turnIntoResponse(const DNSName& qname, const uint16_t qtype, const uint16_t qclass, const ComboAddress& lc, const ComboAddress& rem, const struct timespec& queryRealTime, vector<uint8_t>& query, bool resizeBuffer=true)
1112 {
1113 size_t length = query.size();
1114 if (resizeBuffer) {
1115 query.resize(4096);
1116 }
1117
1118 auto dq = getDNSQuestion(qname, qtype, qclass, lc, rem, queryRealTime, query, length);
1119
1120 BOOST_CHECK(addEDNSToQueryTurnedResponse(dq));
1121
1122 return dq;
1123 }
1124
1125 static int getZ(const DNSName& qname, const uint16_t qtype, const uint16_t qclass, vector<uint8_t>& query)
1126 {
1127 ComboAddress lc("127.0.0.1");
1128 ComboAddress rem("127.0.0.1");
1129 struct timespec queryRealTime;
1130 gettime(&queryRealTime, true);
1131 size_t length = query.size();
1132 DNSQuestion dq = getDNSQuestion(qname, qtype, qclass, lc, rem, queryRealTime, query, length);
1133
1134 return getEDNSZ(dq);
1135 }
1136
1137 BOOST_AUTO_TEST_CASE(test_getEDNSZ) {
1138
1139 uint16_t z;
1140 uint16_t udpPayloadSize;
1141 DNSName qname("www.powerdns.com.");
1142 uint16_t qtype = QType::A;
1143 uint16_t qclass = QClass::IN;
1144 EDNSSubnetOpts ecsOpts;
1145 ecsOpts.source = Netmask(ComboAddress("127.0.0.1"), ECSSourcePrefixV4);
1146 string origECSOptionStr = makeEDNSSubnetOptsString(ecsOpts);
1147 EDNSCookiesOpt cookiesOpt;
1148 cookiesOpt.client = string("deadbeef");
1149 cookiesOpt.server = string("deadbeef");
1150 string cookiesOptionStr = makeEDNSCookiesOptString(cookiesOpt);
1151 DNSPacketWriter::optvect_t opts;
1152 opts.push_back(make_pair(EDNSOptionCode::COOKIE, cookiesOptionStr));
1153 opts.push_back(make_pair(EDNSOptionCode::ECS, origECSOptionStr));
1154
1155 {
1156 /* no EDNS */
1157 vector<uint8_t> query;
1158 DNSPacketWriter pw(query, qname, qtype, qclass, 0);
1159 pw.commit();
1160
1161 BOOST_CHECK_EQUAL(getZ(qname, qtype, qclass, query), 0);
1162 BOOST_CHECK_EQUAL(getEDNSUDPPayloadSizeAndZ(reinterpret_cast<const char*>(query.data()), query.size(), &udpPayloadSize, &z), false);
1163 BOOST_CHECK_EQUAL(z, 0);
1164 BOOST_CHECK_EQUAL(udpPayloadSize, 0);
1165 }
1166
1167 {
1168 /* truncated EDNS */
1169 vector<uint8_t> query;
1170 DNSPacketWriter pw(query, qname, qtype, qclass, 0);
1171 pw.addOpt(512, 0, EDNS_HEADER_FLAG_DO);
1172 pw.commit();
1173
1174 query.resize(query.size() - (/* RDLEN */ sizeof(uint16_t) + /* last byte of TTL / Z */ 1));
1175 BOOST_CHECK_EQUAL(getZ(qname, qtype, qclass, query), 0);
1176 BOOST_CHECK_EQUAL(getEDNSUDPPayloadSizeAndZ(reinterpret_cast<const char*>(query.data()), query.size(), &udpPayloadSize, &z), false);
1177 BOOST_CHECK_EQUAL(z, 0);
1178 BOOST_CHECK_EQUAL(udpPayloadSize, 0);
1179 }
1180
1181 {
1182 /* valid EDNS, no options, DO not set */
1183 vector<uint8_t> query;
1184 DNSPacketWriter pw(query, qname, qtype, qclass, 0);
1185 pw.addOpt(512, 0, 0);
1186 pw.commit();
1187
1188 BOOST_CHECK_EQUAL(getZ(qname, qtype, qclass, query), 0);
1189 BOOST_CHECK_EQUAL(getEDNSUDPPayloadSizeAndZ(reinterpret_cast<const char*>(query.data()), query.size(), &udpPayloadSize, &z), true);
1190 BOOST_CHECK_EQUAL(z, 0);
1191 BOOST_CHECK_EQUAL(udpPayloadSize, 512);
1192 }
1193
1194 {
1195 /* valid EDNS, no options, DO set */
1196 vector<uint8_t> query;
1197 DNSPacketWriter pw(query, qname, qtype, qclass, 0);
1198 pw.addOpt(512, 0, EDNS_HEADER_FLAG_DO);
1199 pw.commit();
1200
1201 BOOST_CHECK_EQUAL(getZ(qname, qtype, qclass, query), EDNS_HEADER_FLAG_DO);
1202 BOOST_CHECK_EQUAL(getEDNSUDPPayloadSizeAndZ(reinterpret_cast<const char*>(query.data()), query.size(), &udpPayloadSize, &z), true);
1203 BOOST_CHECK_EQUAL(z, EDNS_HEADER_FLAG_DO);
1204 BOOST_CHECK_EQUAL(udpPayloadSize, 512);
1205 }
1206
1207 {
1208 /* valid EDNS, options, DO not set */
1209 vector<uint8_t> query;
1210 DNSPacketWriter pw(query, qname, qtype, qclass, 0);
1211 pw.addOpt(512, 0, 0, opts);
1212 pw.commit();
1213
1214 BOOST_CHECK_EQUAL(getZ(qname, qtype, qclass, query), 0);
1215 BOOST_CHECK_EQUAL(getEDNSUDPPayloadSizeAndZ(reinterpret_cast<const char*>(query.data()), query.size(), &udpPayloadSize, &z), true);
1216 BOOST_CHECK_EQUAL(z, 0);
1217 BOOST_CHECK_EQUAL(udpPayloadSize, 512);
1218 }
1219
1220 {
1221 /* valid EDNS, options, DO set */
1222 vector<uint8_t> query;
1223 DNSPacketWriter pw(query, qname, qtype, qclass, 0);
1224 pw.addOpt(512, 0, EDNS_HEADER_FLAG_DO, opts);
1225 pw.commit();
1226
1227 BOOST_CHECK_EQUAL(getZ(qname, qtype, qclass, query), EDNS_HEADER_FLAG_DO);
1228 BOOST_CHECK_EQUAL(getEDNSUDPPayloadSizeAndZ(reinterpret_cast<const char*>(query.data()), query.size(), &udpPayloadSize, &z), true);
1229 BOOST_CHECK_EQUAL(z, EDNS_HEADER_FLAG_DO);
1230 BOOST_CHECK_EQUAL(udpPayloadSize, 512);
1231 }
1232
1233 }
1234
1235 BOOST_AUTO_TEST_CASE(test_addEDNSToQueryTurnedResponse) {
1236
1237 uint16_t z;
1238 uint16_t udpPayloadSize;
1239 DNSName qname("www.powerdns.com.");
1240 uint16_t qtype = QType::A;
1241 uint16_t qclass = QClass::IN;
1242 EDNSSubnetOpts ecsOpts;
1243 ecsOpts.source = Netmask(ComboAddress("127.0.0.1"), ECSSourcePrefixV4);
1244 string origECSOptionStr = makeEDNSSubnetOptsString(ecsOpts);
1245 EDNSCookiesOpt cookiesOpt;
1246 cookiesOpt.client = string("deadbeef");
1247 cookiesOpt.server = string("deadbeef");
1248 string cookiesOptionStr = makeEDNSCookiesOptString(cookiesOpt);
1249 DNSPacketWriter::optvect_t opts;
1250 opts.push_back(make_pair(EDNSOptionCode::COOKIE, cookiesOptionStr));
1251 opts.push_back(make_pair(EDNSOptionCode::ECS, origECSOptionStr));
1252 ComboAddress lc("127.0.0.1");
1253 ComboAddress rem("127.0.0.1");
1254 struct timespec queryRealTime;
1255 gettime(&queryRealTime, true);
1256
1257 {
1258 /* no EDNS */
1259 vector<uint8_t> query;
1260 DNSPacketWriter pw(query, qname, qtype, qclass, 0);
1261 pw.getHeader()->qr = 1;
1262 pw.getHeader()->rcode = RCode::NXDomain;
1263 pw.commit();
1264
1265 auto dq = turnIntoResponse(qname, qtype, qclass, lc, rem, queryRealTime, query);
1266 BOOST_CHECK_EQUAL(getEDNSZ(dq), 0);
1267 BOOST_CHECK_EQUAL(getEDNSUDPPayloadSizeAndZ(reinterpret_cast<const char*>(dq.dh), dq.len, &udpPayloadSize, &z), false);
1268 BOOST_CHECK_EQUAL(z, 0);
1269 BOOST_CHECK_EQUAL(udpPayloadSize, 0);
1270 }
1271
1272 {
1273 /* truncated EDNS */
1274 vector<uint8_t> query;
1275 DNSPacketWriter pw(query, qname, qtype, qclass, 0);
1276 pw.addOpt(512, 0, EDNS_HEADER_FLAG_DO);
1277 pw.commit();
1278
1279 query.resize(query.size() - (/* RDLEN */ sizeof(uint16_t) + /* last byte of TTL / Z */ 1));
1280 auto dq = turnIntoResponse(qname, qtype, qclass, lc, rem, queryRealTime, query);
1281 BOOST_CHECK_EQUAL(getEDNSZ(dq), 0);
1282 BOOST_CHECK_EQUAL(getEDNSUDPPayloadSizeAndZ(reinterpret_cast<const char*>(dq.dh), dq.len, &udpPayloadSize, &z), false);
1283 BOOST_CHECK_EQUAL(z, 0);
1284 BOOST_CHECK_EQUAL(udpPayloadSize, 0);
1285 }
1286
1287 {
1288 /* valid EDNS, no options, DO not set */
1289 vector<uint8_t> query;
1290 DNSPacketWriter pw(query, qname, qtype, qclass, 0);
1291 pw.addOpt(512, 0, 0);
1292 pw.commit();
1293
1294 auto dq = turnIntoResponse(qname, qtype, qclass, lc, rem, queryRealTime, query);
1295 BOOST_CHECK_EQUAL(getEDNSZ(dq), 0);
1296 BOOST_CHECK_EQUAL(getEDNSUDPPayloadSizeAndZ(reinterpret_cast<const char*>(dq.dh), dq.len, &udpPayloadSize, &z), true);
1297 BOOST_CHECK_EQUAL(z, 0);
1298 BOOST_CHECK_EQUAL(udpPayloadSize, g_PayloadSizeSelfGenAnswers);
1299 }
1300
1301 {
1302 /* valid EDNS, no options, DO set */
1303 vector<uint8_t> query;
1304 DNSPacketWriter pw(query, qname, qtype, qclass, 0);
1305 pw.addOpt(512, 0, EDNS_HEADER_FLAG_DO);
1306 pw.commit();
1307
1308 auto dq = turnIntoResponse(qname, qtype, qclass, lc, rem, queryRealTime, query);
1309 BOOST_CHECK_EQUAL(getEDNSZ(dq), EDNS_HEADER_FLAG_DO);
1310 BOOST_CHECK_EQUAL(getEDNSUDPPayloadSizeAndZ(reinterpret_cast<const char*>(dq.dh), dq.len, &udpPayloadSize, &z), true);
1311 BOOST_CHECK_EQUAL(z, EDNS_HEADER_FLAG_DO);
1312 BOOST_CHECK_EQUAL(udpPayloadSize, g_PayloadSizeSelfGenAnswers);
1313 }
1314
1315 {
1316 /* valid EDNS, options, DO not set */
1317 vector<uint8_t> query;
1318 DNSPacketWriter pw(query, qname, qtype, qclass, 0);
1319 pw.addOpt(512, 0, 0, opts);
1320 pw.commit();
1321
1322 auto dq = turnIntoResponse(qname, qtype, qclass, lc, rem, queryRealTime, query);
1323 BOOST_CHECK_EQUAL(getEDNSZ(dq), 0);
1324 BOOST_CHECK_EQUAL(getEDNSUDPPayloadSizeAndZ(reinterpret_cast<const char*>(dq.dh), dq.len, &udpPayloadSize, &z), true);
1325 BOOST_CHECK_EQUAL(z, 0);
1326 BOOST_CHECK_EQUAL(udpPayloadSize, g_PayloadSizeSelfGenAnswers);
1327 }
1328
1329 {
1330 /* valid EDNS, options, DO set */
1331 vector<uint8_t> query;
1332 DNSPacketWriter pw(query, qname, qtype, qclass, 0);
1333 pw.addOpt(512, 0, EDNS_HEADER_FLAG_DO, opts);
1334 pw.commit();
1335
1336 auto dq = turnIntoResponse(qname, qtype, qclass, lc, rem, queryRealTime, query);
1337 BOOST_CHECK_EQUAL(getEDNSZ(dq), EDNS_HEADER_FLAG_DO);
1338 BOOST_CHECK_EQUAL(getEDNSUDPPayloadSizeAndZ(reinterpret_cast<const char*>(dq.dh), dq.len, &udpPayloadSize, &z), true);
1339 BOOST_CHECK_EQUAL(z, EDNS_HEADER_FLAG_DO);
1340 BOOST_CHECK_EQUAL(udpPayloadSize, g_PayloadSizeSelfGenAnswers);
1341 }
1342 }
1343
1344 BOOST_AUTO_TEST_CASE(test_getEDNSOptionsStart) {
1345 const DNSName qname("www.powerdns.com.");
1346 const uint16_t qtype = QType::A;
1347 const uint16_t qclass = QClass::IN;
1348 EDNSSubnetOpts ecsOpts;
1349 ecsOpts.source = Netmask(ComboAddress("127.0.0.1"), ECSSourcePrefixV4);
1350 const string ecsOptionStr = makeEDNSSubnetOptsString(ecsOpts);
1351 DNSPacketWriter::optvect_t opts;
1352 opts.push_back(make_pair(EDNSOptionCode::ECS, ecsOptionStr));
1353 const ComboAddress lc("127.0.0.1");
1354 const ComboAddress rem("127.0.0.1");
1355 uint16_t optRDPosition;
1356 size_t remaining;
1357
1358 const size_t optRDExpectedOffset = sizeof(dnsheader) + qname.wirelength() + DNS_TYPE_SIZE + DNS_CLASS_SIZE + /* root */ 1 + DNS_TYPE_SIZE + DNS_CLASS_SIZE + DNS_TTL_SIZE;
1359
1360 {
1361 /* no EDNS */
1362 vector<uint8_t> query;
1363 DNSPacketWriter pw(query, qname, qtype, qclass, 0);
1364 pw.getHeader()->qr = 1;
1365 pw.getHeader()->rcode = RCode::NXDomain;
1366 pw.commit();
1367
1368 int res = getEDNSOptionsStart(reinterpret_cast<const char*>(query.data()), qname.wirelength(), query.size(), &optRDPosition, &remaining);
1369
1370 BOOST_CHECK_EQUAL(res, ENOENT);
1371
1372 /* truncated packet (should not matter) */
1373 query.resize(query.size() - 1);
1374 res = getEDNSOptionsStart(reinterpret_cast<const char*>(query.data()), qname.wirelength(), query.size(), &optRDPosition, &remaining);
1375
1376 BOOST_CHECK_EQUAL(res, ENOENT);
1377 }
1378
1379 {
1380 /* valid EDNS, no options */
1381 vector<uint8_t> query;
1382 DNSPacketWriter pw(query, qname, qtype, qclass, 0);
1383 pw.addOpt(512, 0, 0);
1384 pw.commit();
1385
1386 int res = getEDNSOptionsStart(reinterpret_cast<const char*>(query.data()), qname.wirelength(), query.size(), &optRDPosition, &remaining);
1387
1388 BOOST_CHECK_EQUAL(res, 0);
1389 BOOST_CHECK_EQUAL(optRDPosition, optRDExpectedOffset);
1390 BOOST_CHECK_EQUAL(remaining, query.size() - optRDExpectedOffset);
1391
1392 /* truncated packet */
1393 query.resize(query.size() - 1);
1394
1395 res = getEDNSOptionsStart(reinterpret_cast<const char*>(query.data()), qname.wirelength(), query.size(), &optRDPosition, &remaining);
1396 BOOST_CHECK_EQUAL(res, ENOENT);
1397 }
1398
1399 {
1400 /* valid EDNS, options */
1401 vector<uint8_t> query;
1402 DNSPacketWriter pw(query, qname, qtype, qclass, 0);
1403 pw.addOpt(512, 0, 0, opts);
1404 pw.commit();
1405
1406 int res = getEDNSOptionsStart(reinterpret_cast<const char*>(query.data()), qname.wirelength(), query.size(), &optRDPosition, &remaining);
1407
1408 BOOST_CHECK_EQUAL(res, 0);
1409 BOOST_CHECK_EQUAL(optRDPosition, optRDExpectedOffset);
1410 BOOST_CHECK_EQUAL(remaining, query.size() - optRDExpectedOffset);
1411
1412 /* truncated options (should not matter for this test) */
1413 query.resize(query.size() - 1);
1414 res = getEDNSOptionsStart(reinterpret_cast<const char*>(query.data()), qname.wirelength(), query.size(), &optRDPosition, &remaining);
1415 BOOST_CHECK_EQUAL(res, 0);
1416 BOOST_CHECK_EQUAL(optRDPosition, optRDExpectedOffset);
1417 BOOST_CHECK_EQUAL(remaining, query.size() - optRDExpectedOffset);
1418 }
1419
1420 }
1421
1422 BOOST_AUTO_TEST_CASE(test_isEDNSOptionInOpt) {
1423
1424 auto locateEDNSOption = [](const vector<uint8_t>& query, uint16_t code, size_t* optContentStart, uint16_t* optContentLen) {
1425 uint16_t optStart;
1426 size_t optLen;
1427 bool last = false;
1428 std::string packetStr(reinterpret_cast<const char*>(query.data()), query.size());
1429 int res = locateEDNSOptRR(packetStr, &optStart, &optLen, &last);
1430 if (res != 0) {
1431 // no EDNS OPT RR
1432 return false;
1433 }
1434
1435 if (optLen < optRecordMinimumSize) {
1436 return false;
1437 }
1438
1439 if (optStart < query.size() && packetStr.at(optStart) != 0) {
1440 // OPT RR Name != '.'
1441 return false;
1442 }
1443
1444 return isEDNSOptionInOpt(packetStr, optStart, optLen, code, optContentStart, optContentLen);
1445 };
1446
1447 const DNSName qname("www.powerdns.com.");
1448 const uint16_t qtype = QType::A;
1449 const uint16_t qclass = QClass::IN;
1450 EDNSSubnetOpts ecsOpts;
1451 ecsOpts.source = Netmask(ComboAddress("127.0.0.1"), ECSSourcePrefixV4);
1452 const string ecsOptionStr = makeEDNSSubnetOptsString(ecsOpts);
1453 const size_t sizeOfECSContent = ecsOptionStr.size();
1454 EDNSCookiesOpt cookiesOpt;
1455 cookiesOpt.client = string("deadbeef");
1456 cookiesOpt.server = string("deadbeef");
1457 const string cookiesOptionStr = makeEDNSCookiesOptString(cookiesOpt);
1458 const size_t sizeOfCookieOption = /* option code */ 2 + /* option length */ 2 + cookiesOpt.client.size() + cookiesOpt.server.size();
1459 /*
1460 DNSPacketWriter::optvect_t opts;
1461 opts.push_back(make_pair(EDNSOptionCode::COOKIE, cookiesOptionStr));
1462 opts.push_back(make_pair(EDNSOptionCode::ECS, ecsOptionStr));
1463 opts.push_back(make_pair(EDNSOptionCode::COOKIE, cookiesOptionStr));
1464 */
1465 const ComboAddress lc("127.0.0.1");
1466 const ComboAddress rem("127.0.0.1");
1467 size_t optContentStart;
1468 uint16_t optContentLen;
1469
1470 const size_t optRDExpectedOffset = sizeof(dnsheader) + qname.wirelength() + DNS_TYPE_SIZE + DNS_CLASS_SIZE + /* root */ 1 + DNS_TYPE_SIZE + DNS_CLASS_SIZE + DNS_TTL_SIZE;
1471
1472 {
1473 /* no EDNS */
1474 vector<uint8_t> query;
1475 DNSPacketWriter pw(query, qname, qtype, qclass, 0);
1476 pw.getHeader()->qr = 1;
1477 pw.getHeader()->rcode = RCode::NXDomain;
1478 pw.commit();
1479
1480 bool found = locateEDNSOption(query, EDNSOptionCode::ECS, &optContentStart, &optContentLen);
1481 BOOST_CHECK_EQUAL(found, false);
1482
1483 /* truncated packet (should not matter here) */
1484 query.resize(query.size() - 1);
1485 found = locateEDNSOption(query, EDNSOptionCode::ECS, &optContentStart, &optContentLen);
1486 BOOST_CHECK_EQUAL(found, false);
1487 }
1488
1489 {
1490 /* valid EDNS, no options */
1491 vector<uint8_t> query;
1492 DNSPacketWriter pw(query, qname, qtype, qclass, 0);
1493 pw.addOpt(512, 0, 0);
1494 pw.commit();
1495
1496 bool found = locateEDNSOption(query, EDNSOptionCode::ECS, &optContentStart, &optContentLen);
1497 BOOST_CHECK_EQUAL(found, false);
1498
1499 /* truncated packet */
1500 query.resize(query.size() - 1);
1501 BOOST_CHECK_THROW(locateEDNSOption(query, EDNSOptionCode::ECS, &optContentStart, &optContentLen), std::out_of_range);
1502 }
1503
1504 {
1505 /* valid EDNS, two cookie options but no ECS */
1506 vector<uint8_t> query;
1507 DNSPacketWriter pw(query, qname, qtype, qclass, 0);
1508 DNSPacketWriter::optvect_t opts;
1509 opts.push_back(make_pair(EDNSOptionCode::COOKIE, cookiesOptionStr));
1510 opts.push_back(make_pair(EDNSOptionCode::COOKIE, cookiesOptionStr));
1511 pw.addOpt(512, 0, 0, opts);
1512 pw.commit();
1513
1514 bool found = locateEDNSOption(query, EDNSOptionCode::ECS, &optContentStart, &optContentLen);
1515 BOOST_CHECK_EQUAL(found, false);
1516
1517 /* truncated packet */
1518 query.resize(query.size() - 1);
1519 BOOST_CHECK_THROW(locateEDNSOption(query, EDNSOptionCode::ECS, &optContentStart, &optContentLen), std::range_error);
1520 }
1521
1522 {
1523 /* valid EDNS, two ECS */
1524 vector<uint8_t> query;
1525 DNSPacketWriter pw(query, qname, qtype, qclass, 0);
1526 DNSPacketWriter::optvect_t opts;
1527 opts.push_back(make_pair(EDNSOptionCode::ECS, ecsOptionStr));
1528 opts.push_back(make_pair(EDNSOptionCode::ECS, ecsOptionStr));
1529 pw.addOpt(512, 0, 0, opts);
1530 pw.commit();
1531
1532 bool found = locateEDNSOption(query, EDNSOptionCode::ECS, &optContentStart, &optContentLen);
1533 BOOST_CHECK_EQUAL(found, true);
1534 if (found == true) {
1535 BOOST_CHECK_EQUAL(optContentStart, optRDExpectedOffset + sizeof(uint16_t) /* RD len */ + /* option code */ 2 + /* option length */ 2);
1536 BOOST_CHECK_EQUAL(optContentLen, sizeOfECSContent);
1537 }
1538
1539 /* truncated packet */
1540 query.resize(query.size() - 1);
1541 BOOST_CHECK_THROW(locateEDNSOption(query, EDNSOptionCode::ECS, &optContentStart, &optContentLen), std::range_error);
1542 }
1543
1544 {
1545 /* valid EDNS, one ECS between two cookies */
1546 vector<uint8_t> query;
1547 DNSPacketWriter pw(query, qname, qtype, qclass, 0);
1548 DNSPacketWriter::optvect_t opts;
1549 opts.push_back(make_pair(EDNSOptionCode::COOKIE, cookiesOptionStr));
1550 opts.push_back(make_pair(EDNSOptionCode::ECS, ecsOptionStr));
1551 opts.push_back(make_pair(EDNSOptionCode::COOKIE, cookiesOptionStr));
1552 pw.addOpt(512, 0, 0, opts);
1553 pw.commit();
1554
1555 bool found = locateEDNSOption(query, EDNSOptionCode::ECS, &optContentStart, &optContentLen);
1556 BOOST_CHECK_EQUAL(found, true);
1557 if (found == true) {
1558 BOOST_CHECK_EQUAL(optContentStart, optRDExpectedOffset + sizeof(uint16_t) /* RD len */ + sizeOfCookieOption + /* option code */ 2 + /* option length */ 2);
1559 BOOST_CHECK_EQUAL(optContentLen, sizeOfECSContent);
1560 }
1561
1562 /* truncated packet */
1563 query.resize(query.size() - 1);
1564 BOOST_CHECK_THROW(locateEDNSOption(query, EDNSOptionCode::ECS, &optContentStart, &optContentLen), std::range_error);
1565 }
1566 }
1567
1568 BOOST_AUTO_TEST_SUITE_END();