2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
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.
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.
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.
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.
24 #include "dnsdist-ecs.hh"
25 #include "dnsparser.hh"
26 #include "dnswriter.hh"
27 #include "ednsoptions.hh"
28 #include "ednssubnet.hh"
30 /* when we add EDNS to a query, we don't want to advertise
31 a large buffer size */
32 size_t g_EdnsUDPPayloadSize
= 512;
33 uint16_t g_PayloadSizeSelfGenAnswers
{s_udpIncomingBufferSize
};
35 /* draft-ietf-dnsop-edns-client-subnet-04 "11.1. Privacy" */
36 uint16_t g_ECSSourcePrefixV4
= 24;
37 uint16_t g_ECSSourcePrefixV6
= 56;
39 bool g_ECSOverride
{false};
40 bool g_addEDNSToSelfGeneratedResponses
{true};
42 int rewriteResponseWithoutEDNS(const std::string
& initialPacket
, vector
<uint8_t>& newContent
)
44 assert(initialPacket
.size() >= sizeof(dnsheader
));
45 const struct dnsheader
* dh
= reinterpret_cast<const struct dnsheader
*>(initialPacket
.data());
47 if (ntohs(dh
->arcount
) == 0)
50 if (ntohs(dh
->qdcount
) == 0)
53 PacketReader
pr(initialPacket
);
57 uint16_t qdcount
= ntohs(dh
->qdcount
);
58 uint16_t ancount
= ntohs(dh
->ancount
);
59 uint16_t nscount
= ntohs(dh
->nscount
);
60 uint16_t arcount
= ntohs(dh
->arcount
);
64 struct dnsrecordheader ah
;
66 rrname
= pr
.getName();
67 rrtype
= pr
.get16BitInt();
68 rrclass
= pr
.get16BitInt();
70 DNSPacketWriter
pw(newContent
, rrname
, rrtype
, rrclass
, dh
->opcode
);
71 pw
.getHeader()->id
=dh
->id
;
72 pw
.getHeader()->qr
=dh
->qr
;
73 pw
.getHeader()->aa
=dh
->aa
;
74 pw
.getHeader()->tc
=dh
->tc
;
75 pw
.getHeader()->rd
=dh
->rd
;
76 pw
.getHeader()->ra
=dh
->ra
;
77 pw
.getHeader()->ad
=dh
->ad
;
78 pw
.getHeader()->cd
=dh
->cd
;
79 pw
.getHeader()->rcode
=dh
->rcode
;
81 /* consume remaining qd if any */
83 for(idx
= 1; idx
< qdcount
; idx
++) {
84 rrname
= pr
.getName();
85 rrtype
= pr
.get16BitInt();
86 rrclass
= pr
.get16BitInt();
93 for (idx
= 0; idx
< ancount
; idx
++) {
94 rrname
= pr
.getName();
95 pr
.getDnsrecordheader(ah
);
97 pw
.startRecord(rrname
, ah
.d_type
, ah
.d_ttl
, ah
.d_class
, DNSResourceRecord::ANSWER
, true);
102 for (idx
= 0; idx
< nscount
; idx
++) {
103 rrname
= pr
.getName();
104 pr
.getDnsrecordheader(ah
);
106 pw
.startRecord(rrname
, ah
.d_type
, ah
.d_ttl
, ah
.d_class
, DNSResourceRecord::AUTHORITY
, true);
110 /* consume AR, looking for OPT */
111 for (idx
= 0; idx
< arcount
; idx
++) {
112 rrname
= pr
.getName();
113 pr
.getDnsrecordheader(ah
);
115 if (ah
.d_type
!= QType::OPT
) {
116 pw
.startRecord(rrname
, ah
.d_type
, ah
.d_ttl
, ah
.d_class
, DNSResourceRecord::ADDITIONAL
, true);
129 static bool addOrReplaceECSOption(std::vector
<std::pair
<uint16_t, std::string
>>& options
, bool& ecsAdded
, bool overrideExisting
, const string
& newECSOption
)
131 for (auto it
= options
.begin(); it
!= options
.end(); ) {
132 if (it
->first
== EDNSOptionCode::ECS
) {
135 if (!overrideExisting
) {
139 it
= options
.erase(it
);
146 options
.emplace_back(EDNSOptionCode::ECS
, std::string(&newECSOption
.at(EDNS_OPTION_CODE_SIZE
+ EDNS_OPTION_LENGTH_SIZE
), newECSOption
.size() - (EDNS_OPTION_CODE_SIZE
+ EDNS_OPTION_LENGTH_SIZE
)));
150 static bool slowRewriteQueryWithExistingEDNS(const std::string
& initialPacket
, vector
<uint8_t>& newContent
, bool& ednsAdded
, bool& ecsAdded
, bool overrideExisting
, const string
& newECSOption
)
152 assert(initialPacket
.size() >= sizeof(dnsheader
));
153 const struct dnsheader
* dh
= reinterpret_cast<const struct dnsheader
*>(initialPacket
.data());
158 if (ntohs(dh
->qdcount
) == 0) {
162 if (ntohs(dh
->arcount
) == 0) {
163 throw std::runtime_error("slowRewriteQueryWithExistingEDNS() should not be called for queries that have no EDNS");
166 PacketReader
pr(initialPacket
);
170 uint16_t qdcount
= ntohs(dh
->qdcount
);
171 uint16_t ancount
= ntohs(dh
->ancount
);
172 uint16_t nscount
= ntohs(dh
->nscount
);
173 uint16_t arcount
= ntohs(dh
->arcount
);
177 struct dnsrecordheader ah
;
179 rrname
= pr
.getName();
180 rrtype
= pr
.get16BitInt();
181 rrclass
= pr
.get16BitInt();
183 DNSPacketWriter
pw(newContent
, rrname
, rrtype
, rrclass
, dh
->opcode
);
184 pw
.getHeader()->id
=dh
->id
;
185 pw
.getHeader()->qr
=dh
->qr
;
186 pw
.getHeader()->aa
=dh
->aa
;
187 pw
.getHeader()->tc
=dh
->tc
;
188 pw
.getHeader()->rd
=dh
->rd
;
189 pw
.getHeader()->ra
=dh
->ra
;
190 pw
.getHeader()->ad
=dh
->ad
;
191 pw
.getHeader()->cd
=dh
->cd
;
192 pw
.getHeader()->rcode
=dh
->rcode
;
194 /* consume remaining qd if any */
196 for(idx
= 1; idx
< qdcount
; idx
++) {
197 rrname
= pr
.getName();
198 rrtype
= pr
.get16BitInt();
199 rrclass
= pr
.get16BitInt();
206 for (idx
= 0; idx
< ancount
; idx
++) {
207 rrname
= pr
.getName();
208 pr
.getDnsrecordheader(ah
);
210 pw
.startRecord(rrname
, ah
.d_type
, ah
.d_ttl
, ah
.d_class
, DNSResourceRecord::ANSWER
, true);
215 for (idx
= 0; idx
< nscount
; idx
++) {
216 rrname
= pr
.getName();
217 pr
.getDnsrecordheader(ah
);
219 pw
.startRecord(rrname
, ah
.d_type
, ah
.d_ttl
, ah
.d_class
, DNSResourceRecord::AUTHORITY
, true);
224 /* consume AR, looking for OPT */
225 for (idx
= 0; idx
< arcount
; idx
++) {
226 rrname
= pr
.getName();
227 pr
.getDnsrecordheader(ah
);
229 if (ah
.d_type
!= QType::OPT
) {
230 pw
.startRecord(rrname
, ah
.d_type
, ah
.d_ttl
, ah
.d_class
, DNSResourceRecord::ADDITIONAL
, true);
238 std::vector
<std::pair
<uint16_t, std::string
>> options
;
239 getEDNSOptionsFromContent(blob
, options
);
242 static_assert(sizeof(edns0
) == sizeof(ah
.d_ttl
), "sizeof(EDNS0Record) must match sizeof(uint32_t) AKA RR TTL size");
243 memcpy(&edns0
, &ah
.d_ttl
, sizeof(edns0
));
245 /* addOrReplaceECSOption will set it to false if there is already an existing option */
247 addOrReplaceECSOption(options
, ecsAdded
, overrideExisting
, newECSOption
);
248 pw
.addOpt(ah
.d_class
, edns0
.extRCode
, edns0
.extFlags
, options
, edns0
.version
);
253 pw
.addOpt(g_EdnsUDPPayloadSize
, 0, 0, {{EDNSOptionCode::ECS
, std::string(&newECSOption
.at(EDNS_OPTION_CODE_SIZE
+ EDNS_OPTION_LENGTH_SIZE
), newECSOption
.size() - (EDNS_OPTION_CODE_SIZE
+ EDNS_OPTION_LENGTH_SIZE
))}}, 0);
262 static bool slowParseEDNSOptions(const char* packet
, uint16_t const len
, std::shared_ptr
<std::map
<uint16_t, EDNSOptionView
> >& options
)
264 const struct dnsheader
* dh
= reinterpret_cast<const struct dnsheader
*>(packet
);
266 if (len
< sizeof(dnsheader
) || ntohs(dh
->qdcount
) == 0)
271 if (ntohs(dh
->arcount
) == 0) {
272 throw std::runtime_error("slowParseEDNSOptions() should not be called for queries that have no EDNS");
276 uint64_t numrecords
= ntohs(dh
->ancount
) + ntohs(dh
->nscount
) + ntohs(dh
->arcount
);
277 DNSPacketMangler
dpm(const_cast<char*>(packet
), len
);
279 for(n
=0; n
< ntohs(dh
->qdcount
) ; ++n
) {
280 dpm
.skipDomainName();
285 for(n
=0; n
< numrecords
; ++n
) {
286 dpm
.skipDomainName();
288 uint8_t section
= n
< ntohs(dh
->ancount
) ? 1 : (n
< (ntohs(dh
->ancount
) + ntohs(dh
->nscount
)) ? 2 : 3);
289 uint16_t dnstype
= dpm
.get16BitInt();
291 dpm
.skipBytes(4); /* TTL */
293 if(section
== 3 && dnstype
== QType::OPT
) {
294 uint32_t offset
= dpm
.getOffset();
298 /* if we survive this call, we can parse it safely */
300 return getEDNSOptions(packet
+ offset
, len
- offset
, *options
) == 0;
315 int locateEDNSOptRR(const std::string
& packet
, uint16_t * optStart
, size_t * optLen
, bool * last
)
317 assert(optStart
!= NULL
);
318 assert(optLen
!= NULL
);
319 assert(last
!= NULL
);
320 const struct dnsheader
* dh
= reinterpret_cast<const struct dnsheader
*>(packet
.data());
322 if (ntohs(dh
->arcount
) == 0)
325 PacketReader
pr(packet
);
328 uint16_t qdcount
= ntohs(dh
->qdcount
);
329 uint16_t ancount
= ntohs(dh
->ancount
);
330 uint16_t nscount
= ntohs(dh
->nscount
);
331 uint16_t arcount
= ntohs(dh
->arcount
);
334 struct dnsrecordheader ah
;
337 for(idx
= 0; idx
< qdcount
; idx
++) {
338 rrname
= pr
.getName();
339 rrtype
= pr
.get16BitInt();
340 rrclass
= pr
.get16BitInt();
345 /* consume AN and NS */
346 for (idx
= 0; idx
< ancount
+ nscount
; idx
++) {
347 rrname
= pr
.getName();
348 pr
.getDnsrecordheader(ah
);
352 /* consume AR, looking for OPT */
353 for (idx
= 0; idx
< arcount
; idx
++) {
354 uint16_t start
= pr
.getPosition();
355 rrname
= pr
.getName();
356 pr
.getDnsrecordheader(ah
);
358 if (ah
.d_type
== QType::OPT
) {
360 *optLen
= (pr
.getPosition() - start
) + ah
.d_clen
;
362 if (packet
.size() < (*optStart
+ *optLen
)) {
363 throw std::range_error("Opt record overflow");
366 if (idx
== ((size_t) arcount
- 1)) {
380 /* extract the start of the OPT RR in a QUERY packet if any */
381 int getEDNSOptionsStart(const char* packet
, const size_t offset
, const size_t len
, uint16_t* optRDPosition
, size_t * remaining
)
383 assert(packet
!= nullptr);
384 assert(optRDPosition
!= nullptr);
385 assert(remaining
!= nullptr);
386 const struct dnsheader
* dh
= reinterpret_cast<const struct dnsheader
*>(packet
);
392 if (ntohs(dh
->qdcount
) != 1 || ntohs(dh
->ancount
) != 0 || ntohs(dh
->arcount
) != 1 || ntohs(dh
->nscount
) != 0)
395 size_t pos
= sizeof(dnsheader
) + offset
;
396 pos
+= DNS_TYPE_SIZE
+ DNS_CLASS_SIZE
;
401 if ((pos
+ /* root */ 1 + DNS_TYPE_SIZE
+ DNS_CLASS_SIZE
) >= len
) {
405 if (packet
[pos
] != 0) {
406 /* not the root so not an OPT record */
411 uint16_t qtype
= (reinterpret_cast<const unsigned char*>(packet
)[pos
])*256 + reinterpret_cast<const unsigned char*>(packet
)[pos
+1];
412 pos
+= DNS_TYPE_SIZE
;
413 pos
+= DNS_CLASS_SIZE
;
415 if(qtype
!= QType::OPT
|| (len
- pos
) < (DNS_TTL_SIZE
+ DNS_RDLENGTH_SIZE
))
419 *optRDPosition
= pos
;
420 *remaining
= len
- pos
;
425 void generateECSOption(const ComboAddress
& source
, string
& res
, uint16_t ECSPrefixLength
)
427 Netmask
sourceNetmask(source
, ECSPrefixLength
);
428 EDNSSubnetOpts ecsOpts
;
429 ecsOpts
.source
= sourceNetmask
;
430 string payload
= makeEDNSSubnetOptsString(ecsOpts
);
431 generateEDNSOption(EDNSOptionCode::ECS
, payload
, res
);
434 void generateOptRR(const std::string
& optRData
, string
& res
, uint16_t udpPayloadSize
, uint8_t ednsrcode
, bool dnssecOK
)
436 const uint8_t name
= 0;
439 edns0
.extRCode
= ednsrcode
;
441 edns0
.extFlags
= dnssecOK
? htons(EDNS_HEADER_FLAG_DO
) : 0;
443 dh
.d_type
= htons(QType::OPT
);
444 dh
.d_class
= htons(udpPayloadSize
);
445 static_assert(sizeof(EDNS0Record
) == sizeof(dh
.d_ttl
), "sizeof(EDNS0Record) must match sizeof(dnsrecordheader.d_ttl)");
446 memcpy(&dh
.d_ttl
, &edns0
, sizeof edns0
);
447 dh
.d_clen
= htons(static_cast<uint16_t>(optRData
.length()));
448 res
.reserve(sizeof(name
) + sizeof(dh
) + optRData
.length());
449 res
.assign(reinterpret_cast<const char *>(&name
), sizeof name
);
450 res
.append(reinterpret_cast<const char *>(&dh
), sizeof(dh
));
451 res
.append(optRData
.c_str(), optRData
.length());
454 static bool replaceEDNSClientSubnetOption(char * const packet
, const size_t packetSize
, uint16_t * const len
, char * const oldEcsOptionStart
, size_t const oldEcsOptionSize
, unsigned char * const optRDLen
, const string
& newECSOption
)
456 assert(packet
!= NULL
);
458 assert(oldEcsOptionStart
!= NULL
);
459 assert(optRDLen
!= NULL
);
461 if (newECSOption
.size() == oldEcsOptionSize
) {
462 /* same size as the existing option */
463 memcpy(oldEcsOptionStart
, newECSOption
.c_str(), oldEcsOptionSize
);
466 /* different size than the existing option */
467 const unsigned int newPacketLen
= *len
+ (newECSOption
.length() - oldEcsOptionSize
);
468 const size_t beforeOptionLen
= oldEcsOptionStart
- packet
;
469 const size_t dataBehindSize
= *len
- beforeOptionLen
- oldEcsOptionSize
;
471 /* check that it fits in the existing buffer */
472 if (newPacketLen
> packetSize
) {
476 /* fix the size of ECS Option RDLen */
477 uint16_t newRDLen
= (optRDLen
[0] * 256) + optRDLen
[1];
478 newRDLen
+= (newECSOption
.size() - oldEcsOptionSize
);
479 optRDLen
[0] = newRDLen
/ 256;
480 optRDLen
[1] = newRDLen
% 256;
482 if (dataBehindSize
> 0) {
483 memmove(oldEcsOptionStart
, oldEcsOptionStart
+ oldEcsOptionSize
, dataBehindSize
);
485 memcpy(oldEcsOptionStart
+ dataBehindSize
, newECSOption
.c_str(), newECSOption
.size());
492 /* This function looks for an OPT RR, return true if a valid one was found (even if there was no options)
493 and false otherwise. */
494 bool parseEDNSOptions(DNSQuestion
& dq
)
496 assert(dq
.dh
!= nullptr);
497 assert(dq
.consumed
<= dq
.len
);
498 assert(dq
.len
<= dq
.size
);
500 if (dq
.ednsOptions
!= nullptr) {
504 dq
.ednsOptions
= std::make_shared
<std::map
<uint16_t, EDNSOptionView
> >();
506 if (ntohs(dq
.dh
->ancount
) != 0 || ntohs(dq
.dh
->nscount
) != 0 || (ntohs(dq
.dh
->arcount
) != 0 && ntohs(dq
.dh
->arcount
) != 1)) {
507 return slowParseEDNSOptions(reinterpret_cast<const char*>(dq
.dh
), dq
.len
, dq
.ednsOptions
);
510 const char* packet
= reinterpret_cast<const char*>(dq
.dh
);
512 size_t remaining
= 0;
513 uint16_t optRDPosition
;
514 int res
= getEDNSOptionsStart(packet
, dq
.consumed
, dq
.len
, &optRDPosition
, &remaining
);
517 res
= getEDNSOptions(packet
+ optRDPosition
, remaining
, *dq
.ednsOptions
);
524 static bool addECSToExistingOPT(char* const packet
, size_t const packetSize
, uint16_t* const len
, const string
& newECSOption
, unsigned char* optRDLen
, bool& ecsAdded
)
526 /* we need to add one EDNS0 ECS option, fixing the size of EDNS0 RDLENGTH */
527 /* getEDNSOptionsStart has already checked that there is exactly one AR,
530 /* check if the existing buffer is large enough */
531 const size_t newECSOptionSize
= newECSOption
.size();
532 if (packetSize
- *len
<= newECSOptionSize
) {
536 uint16_t newRDLen
= (optRDLen
[0] * 256) + optRDLen
[1];
537 newRDLen
+= newECSOptionSize
;
538 optRDLen
[0] = newRDLen
/ 256;
539 optRDLen
[1] = newRDLen
% 256;
541 memcpy(packet
+ *len
, newECSOption
.c_str(), newECSOptionSize
);
542 *len
+= newECSOptionSize
;
548 static bool addEDNSWithECS(char* const packet
, size_t const packetSize
, uint16_t* const len
, const string
& newECSOption
, bool& ednsAdded
, bool& ecsAdded
, bool preserveTrailingData
)
550 /* we need to add a EDNS0 RR with one EDNS0 ECS option, fixing the AR count */
552 struct dnsheader
* dh
= reinterpret_cast<struct dnsheader
*>(packet
);
553 generateOptRR(newECSOption
, EDNSRR
, g_EdnsUDPPayloadSize
, 0, false);
555 /* does it fit in the existing buffer? */
556 if (packetSize
- *len
<= EDNSRR
.size()) {
560 uint32_t realPacketLen
= getDNSPacketLength(packet
, *len
);
561 if (realPacketLen
< *len
&& preserveTrailingData
) {
562 size_t toMove
= *len
- realPacketLen
;
563 memmove(packet
+ realPacketLen
+ EDNSRR
.size(), packet
+ realPacketLen
, toMove
);
564 *len
+= EDNSRR
.size();
567 *len
= realPacketLen
+ EDNSRR
.size();
570 uint16_t arcount
= ntohs(dh
->arcount
);
572 dh
->arcount
= htons(arcount
);
576 memcpy(packet
+ realPacketLen
, EDNSRR
.c_str(), EDNSRR
.size());
581 bool handleEDNSClientSubnet(char* const packet
, const size_t packetSize
, const unsigned int consumed
, uint16_t* const len
, bool& ednsAdded
, bool& ecsAdded
, bool overrideExisting
, const string
& newECSOption
, bool preserveTrailingData
)
583 assert(packet
!= nullptr);
584 assert(len
!= nullptr);
585 assert(consumed
<= (size_t) *len
);
587 const struct dnsheader
* dh
= reinterpret_cast<const struct dnsheader
*>(packet
);
589 if (ntohs(dh
->ancount
) != 0 || ntohs(dh
->nscount
) != 0 || (ntohs(dh
->arcount
) != 0 && ntohs(dh
->arcount
) != 1)) {
590 vector
<uint8_t> newContent
;
591 newContent
.reserve(packetSize
);
593 if (!slowRewriteQueryWithExistingEDNS(std::string(packet
, *len
), newContent
, ednsAdded
, ecsAdded
, overrideExisting
, newECSOption
)) {
599 if (newContent
.size() > packetSize
) {
605 memcpy(packet
, &newContent
.at(0), newContent
.size());
606 *len
= newContent
.size();
610 uint16_t optRDPosition
= 0;
611 size_t remaining
= 0;
613 int res
= getEDNSOptionsStart(packet
, consumed
, *len
, &optRDPosition
, &remaining
);
616 return addEDNSWithECS(packet
, packetSize
, len
, newECSOption
, ednsAdded
, ecsAdded
, preserveTrailingData
);
619 unsigned char* optRDLen
= reinterpret_cast<unsigned char*>(packet
) + optRDPosition
;
620 char * ecsOptionStart
= nullptr;
621 size_t ecsOptionSize
= 0;
623 res
= getEDNSOption(reinterpret_cast<char*>(optRDLen
), remaining
, EDNSOptionCode::ECS
, &ecsOptionStart
, &ecsOptionSize
);
626 /* there is already an ECS value */
627 if (!overrideExisting
) {
631 return replaceEDNSClientSubnetOption(packet
, packetSize
, len
, ecsOptionStart
, ecsOptionSize
, optRDLen
, newECSOption
);
633 /* we have an EDNS OPT RR but no existing ECS option */
634 return addECSToExistingOPT(packet
, packetSize
, len
, newECSOption
, optRDLen
, ecsAdded
);
640 bool handleEDNSClientSubnet(DNSQuestion
& dq
, bool& ednsAdded
, bool& ecsAdded
, bool preserveTrailingData
)
642 assert(dq
.remote
!= nullptr);
644 generateECSOption(dq
.ecsSet
? dq
.ecs
.getNetwork() : *dq
.remote
, newECSOption
, dq
.ecsSet
? dq
.ecs
.getBits() : dq
.ecsPrefixLength
);
645 char* packet
= reinterpret_cast<char*>(dq
.dh
);
647 return handleEDNSClientSubnet(packet
, dq
.size
, dq
.consumed
, &dq
.len
, ednsAdded
, ecsAdded
, dq
.ecsOverride
, newECSOption
, preserveTrailingData
);
650 static int removeEDNSOptionFromOptions(unsigned char* optionsStart
, const uint16_t optionsLen
, const uint16_t optionCodeToRemove
, uint16_t* newOptionsLen
)
652 unsigned char* p
= optionsStart
;
654 while ((pos
+ 4) <= optionsLen
) {
655 unsigned char* optionBegin
= p
;
656 const uint16_t optionCode
= 0x100*p
[0] + p
[1];
657 p
+= sizeof(optionCode
);
658 pos
+= sizeof(optionCode
);
659 const uint16_t optionLen
= 0x100*p
[0] + p
[1];
660 p
+= sizeof(optionLen
);
661 pos
+= sizeof(optionLen
);
662 if ((pos
+ optionLen
) > optionsLen
) {
665 if (optionCode
== optionCodeToRemove
) {
666 if (pos
+ optionLen
< optionsLen
) {
667 /* move remaining options over the removed one,
669 memmove(optionBegin
, p
+ optionLen
, optionsLen
- (pos
+ optionLen
));
671 *newOptionsLen
= optionsLen
- (sizeof(optionCode
) + sizeof(optionLen
) + optionLen
);
680 int removeEDNSOptionFromOPT(char* optStart
, size_t* optLen
, const uint16_t optionCodeToRemove
)
682 if (*optLen
< optRecordMinimumSize
) {
685 const unsigned char* end
= (const unsigned char*) optStart
+ *optLen
;
686 unsigned char* p
= (unsigned char*) optStart
+ 9;
687 unsigned char* rdLenPtr
= p
;
688 uint16_t rdLen
= (0x100*p
[0] + p
[1]);
690 if (p
+ rdLen
!= end
) {
693 uint16_t newRdLen
= 0;
694 int res
= removeEDNSOptionFromOptions(p
, rdLen
, optionCodeToRemove
, &newRdLen
);
698 *optLen
-= (rdLen
- newRdLen
);
699 rdLenPtr
[0] = newRdLen
/ 0x100;
700 rdLenPtr
[1] = newRdLen
% 0x100;
704 bool isEDNSOptionInOpt(const std::string
& packet
, const size_t optStart
, const size_t optLen
, const uint16_t optionCodeToFind
, size_t* optContentStart
, uint16_t* optContentLen
)
706 if (optLen
< optRecordMinimumSize
) {
709 size_t p
= optStart
+ 9;
710 uint16_t rdLen
= (0x100*static_cast<unsigned char>(packet
.at(p
)) + static_cast<unsigned char>(packet
.at(p
+1)));
712 if (rdLen
> (optLen
- optRecordMinimumSize
)) {
716 size_t rdEnd
= p
+ rdLen
;
717 while ((p
+ 4) <= rdEnd
) {
718 const uint16_t optionCode
= 0x100*static_cast<unsigned char>(packet
.at(p
)) + static_cast<unsigned char>(packet
.at(p
+1));
719 p
+= sizeof(optionCode
);
720 const uint16_t optionLen
= 0x100*static_cast<unsigned char>(packet
.at(p
)) + static_cast<unsigned char>(packet
.at(p
+1));
721 p
+= sizeof(optionLen
);
723 if ((p
+ optionLen
) > rdEnd
) {
727 if (optionCode
== optionCodeToFind
) {
728 if (optContentStart
!= nullptr) {
729 *optContentStart
= p
;
732 if (optContentLen
!= nullptr) {
733 *optContentLen
= optionLen
;
743 int rewriteResponseWithoutEDNSOption(const std::string
& initialPacket
, const uint16_t optionCodeToSkip
, vector
<uint8_t>& newContent
)
745 assert(initialPacket
.size() >= sizeof(dnsheader
));
746 const struct dnsheader
* dh
= reinterpret_cast<const struct dnsheader
*>(initialPacket
.data());
748 if (ntohs(dh
->arcount
) == 0)
751 if (ntohs(dh
->qdcount
) == 0)
754 PacketReader
pr(initialPacket
);
758 uint16_t qdcount
= ntohs(dh
->qdcount
);
759 uint16_t ancount
= ntohs(dh
->ancount
);
760 uint16_t nscount
= ntohs(dh
->nscount
);
761 uint16_t arcount
= ntohs(dh
->arcount
);
765 struct dnsrecordheader ah
;
767 rrname
= pr
.getName();
768 rrtype
= pr
.get16BitInt();
769 rrclass
= pr
.get16BitInt();
771 DNSPacketWriter
pw(newContent
, rrname
, rrtype
, rrclass
, dh
->opcode
);
772 pw
.getHeader()->id
=dh
->id
;
773 pw
.getHeader()->qr
=dh
->qr
;
774 pw
.getHeader()->aa
=dh
->aa
;
775 pw
.getHeader()->tc
=dh
->tc
;
776 pw
.getHeader()->rd
=dh
->rd
;
777 pw
.getHeader()->ra
=dh
->ra
;
778 pw
.getHeader()->ad
=dh
->ad
;
779 pw
.getHeader()->cd
=dh
->cd
;
780 pw
.getHeader()->rcode
=dh
->rcode
;
782 /* consume remaining qd if any */
784 for(idx
= 1; idx
< qdcount
; idx
++) {
785 rrname
= pr
.getName();
786 rrtype
= pr
.get16BitInt();
787 rrclass
= pr
.get16BitInt();
794 for (idx
= 0; idx
< ancount
; idx
++) {
795 rrname
= pr
.getName();
796 pr
.getDnsrecordheader(ah
);
798 pw
.startRecord(rrname
, ah
.d_type
, ah
.d_ttl
, ah
.d_class
, DNSResourceRecord::ANSWER
, true);
803 for (idx
= 0; idx
< nscount
; idx
++) {
804 rrname
= pr
.getName();
805 pr
.getDnsrecordheader(ah
);
807 pw
.startRecord(rrname
, ah
.d_type
, ah
.d_ttl
, ah
.d_class
, DNSResourceRecord::AUTHORITY
, true);
812 /* consume AR, looking for OPT */
813 for (idx
= 0; idx
< arcount
; idx
++) {
814 rrname
= pr
.getName();
815 pr
.getDnsrecordheader(ah
);
817 if (ah
.d_type
!= QType::OPT
) {
818 pw
.startRecord(rrname
, ah
.d_type
, ah
.d_ttl
, ah
.d_class
, DNSResourceRecord::ADDITIONAL
, true);
822 pw
.startRecord(rrname
, ah
.d_type
, ah
.d_ttl
, ah
.d_class
, DNSResourceRecord::ADDITIONAL
, false);
824 uint16_t rdLen
= blob
.length();
825 removeEDNSOptionFromOptions((unsigned char*)blob
.c_str(), rdLen
, optionCodeToSkip
, &rdLen
);
826 /* xfrBlob(string, size) completely ignores size.. */
828 blob
.resize((size_t)rdLen
);
840 bool addEDNS(dnsheader
* dh
, uint16_t& len
, const size_t size
, bool dnssecOK
, uint16_t payloadSize
, uint8_t ednsrcode
)
842 if (dh
->arcount
!= 0) {
846 std::string optRecord
;
847 generateOptRR(std::string(), optRecord
, payloadSize
, ednsrcode
, dnssecOK
);
849 if (optRecord
.size() >= size
|| (size
- optRecord
.size()) < len
) {
853 char * optPtr
= reinterpret_cast<char*>(dh
) + len
;
854 memcpy(optPtr
, optRecord
.data(), optRecord
.size());
855 len
+= optRecord
.size();
856 dh
->arcount
= htons(1);
861 bool addEDNSToQueryTurnedResponse(DNSQuestion
& dq
)
863 uint16_t optRDPosition
;
864 /* remaining is at least the size of the rdlen + the options if any + the following records if any */
865 size_t remaining
= 0;
867 int res
= getEDNSOptionsStart(reinterpret_cast<char*>(dq
.dh
), dq
.consumed
, dq
.len
, &optRDPosition
, &remaining
);
870 /* if the initial query did not have EDNS0, we are done */
874 const size_t existingOptLen
= /* root */ 1 + DNS_TYPE_SIZE
+ DNS_CLASS_SIZE
+ EDNS_EXTENDED_RCODE_SIZE
+ EDNS_VERSION_SIZE
+ /* Z */ 2 + remaining
;
875 if (existingOptLen
>= dq
.len
) {
876 /* something is wrong, bail out */
880 char* optRDLen
= reinterpret_cast<char*>(dq
.dh
) + optRDPosition
;
881 char * optPtr
= (optRDLen
- (/* root */ 1 + DNS_TYPE_SIZE
+ DNS_CLASS_SIZE
+ EDNS_EXTENDED_RCODE_SIZE
+ EDNS_VERSION_SIZE
+ /* Z */ 2));
883 const uint8_t* zPtr
= reinterpret_cast<const uint8_t*>(optPtr
) + /* root */ 1 + DNS_TYPE_SIZE
+ DNS_CLASS_SIZE
+ EDNS_EXTENDED_RCODE_SIZE
+ EDNS_VERSION_SIZE
;
884 uint16_t z
= 0x100 * (*zPtr
) + *(zPtr
+ 1);
885 bool dnssecOK
= z
& EDNS_HEADER_FLAG_DO
;
887 /* remove the existing OPT record, and everything else that follows (any SIG or TSIG would be useless anyway) */
888 dq
.len
-= existingOptLen
;
891 if (g_addEDNSToSelfGeneratedResponses
) {
892 /* now we need to add a new OPT record */
893 return addEDNS(dq
.dh
, dq
.len
, dq
.size
, dnssecOK
, g_PayloadSizeSelfGenAnswers
, dq
.ednsRCode
);
896 /* otherwise we are just fine */
900 // goal in life - if you send us a reasonably normal packet, we'll get Z for you, otherwise 0
901 int getEDNSZ(const DNSQuestion
& dq
)
904 if (ntohs(dq
.dh
->qdcount
) != 1 || dq
.dh
->ancount
!= 0 || ntohs(dq
.dh
->arcount
) != 1 || dq
.dh
->nscount
!= 0) {
908 if (dq
.len
<= sizeof(dnsheader
)) {
912 size_t pos
= sizeof(dnsheader
) + dq
.consumed
+ DNS_TYPE_SIZE
+ DNS_CLASS_SIZE
;
914 if (dq
.len
<= (pos
+ /* root */ 1 + DNS_TYPE_SIZE
+ DNS_CLASS_SIZE
)) {
918 const char* packet
= reinterpret_cast<const char*>(dq
.dh
);
920 if (packet
[pos
] != 0) {
921 /* not root, so not a valid OPT record */
927 uint16_t qtype
= (reinterpret_cast<const unsigned char*>(packet
)[pos
])*256 + reinterpret_cast<const unsigned char*>(packet
)[pos
+1];
928 pos
+= DNS_TYPE_SIZE
;
929 pos
+= DNS_CLASS_SIZE
;
931 if (qtype
!= QType::OPT
|| (pos
+ EDNS_EXTENDED_RCODE_SIZE
+ EDNS_VERSION_SIZE
+ 1) >= dq
.len
) {
935 const uint8_t* z
= reinterpret_cast<const uint8_t*>(packet
) + pos
+ EDNS_EXTENDED_RCODE_SIZE
+ EDNS_VERSION_SIZE
;
936 return 0x100 * (*z
) + *(z
+1);
943 bool queryHasEDNS(const DNSQuestion
& dq
)
945 uint16_t optRDPosition
;
946 size_t ecsRemaining
= 0;
948 int res
= getEDNSOptionsStart(reinterpret_cast<char*>(dq
.dh
), dq
.consumed
, dq
.len
, &optRDPosition
, &ecsRemaining
);
956 bool getEDNS0Record(const DNSQuestion
& dq
, EDNS0Record
& edns0
)
961 const char * packet
= reinterpret_cast<const char*>(dq
.dh
);
962 std::string
packetStr(packet
, dq
.len
);
963 int res
= locateEDNSOptRR(packetStr
, &optStart
, &optLen
, &last
);
969 if (optLen
< optRecordMinimumSize
) {
973 if (optStart
< dq
.len
&& packetStr
.at(optStart
) != 0) {
974 // OPT RR Name != '.'
978 static_assert(sizeof(EDNS0Record
) == sizeof(uint32_t), "sizeof(EDNS0Record) must match sizeof(uint32_t) AKA RR TTL size");
979 // copy out 4-byte "ttl" (really the EDNS0 record), after root label (1) + type (2) + class (2).
980 memcpy(&edns0
, packet
+ optStart
+ 5, sizeof edns0
);