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);
128 int locateEDNSOptRR(const std::string
& packet
, uint16_t * optStart
, size_t * optLen
, bool * last
)
130 assert(optStart
!= NULL
);
131 assert(optLen
!= NULL
);
132 assert(last
!= NULL
);
133 const struct dnsheader
* dh
= reinterpret_cast<const struct dnsheader
*>(packet
.data());
135 if (ntohs(dh
->arcount
) == 0)
138 PacketReader
pr(packet
);
141 uint16_t qdcount
= ntohs(dh
->qdcount
);
142 uint16_t ancount
= ntohs(dh
->ancount
);
143 uint16_t nscount
= ntohs(dh
->nscount
);
144 uint16_t arcount
= ntohs(dh
->arcount
);
147 struct dnsrecordheader ah
;
150 for(idx
= 0; idx
< qdcount
; idx
++) {
151 rrname
= pr
.getName();
152 rrtype
= pr
.get16BitInt();
153 rrclass
= pr
.get16BitInt();
158 /* consume AN and NS */
159 for (idx
= 0; idx
< ancount
+ nscount
; idx
++) {
160 rrname
= pr
.getName();
161 pr
.getDnsrecordheader(ah
);
165 /* consume AR, looking for OPT */
166 for (idx
= 0; idx
< arcount
; idx
++) {
167 uint16_t start
= pr
.getPosition();
168 rrname
= pr
.getName();
169 pr
.getDnsrecordheader(ah
);
171 if (ah
.d_type
== QType::OPT
) {
173 *optLen
= (pr
.getPosition() - start
) + ah
.d_clen
;
175 if (packet
.size() < (*optStart
+ *optLen
)) {
176 throw std::range_error("Opt record overflow");
179 if (idx
== ((size_t) arcount
- 1)) {
193 /* extract the start of the OPT RR in a QUERY packet if any */
194 int getEDNSOptionsStart(const char* packet
, const size_t offset
, const size_t len
, uint16_t* optRDPosition
, size_t * remaining
)
196 assert(packet
!= nullptr);
197 assert(optRDPosition
!= nullptr);
198 assert(remaining
!= nullptr);
199 const struct dnsheader
* dh
= reinterpret_cast<const struct dnsheader
*>(packet
);
205 if (ntohs(dh
->qdcount
) != 1 || ntohs(dh
->ancount
) != 0 || ntohs(dh
->arcount
) != 1 || ntohs(dh
->nscount
) != 0)
208 size_t pos
= sizeof(dnsheader
) + offset
;
209 pos
+= DNS_TYPE_SIZE
+ DNS_CLASS_SIZE
;
214 if ((pos
+ /* root */ 1 + DNS_TYPE_SIZE
+ DNS_CLASS_SIZE
) >= len
) {
218 if (packet
[pos
] != 0) {
219 /* not the root so not an OPT record */
224 uint16_t qtype
= (reinterpret_cast<const unsigned char*>(packet
)[pos
])*256 + reinterpret_cast<const unsigned char*>(packet
)[pos
+1];
225 pos
+= DNS_TYPE_SIZE
;
226 pos
+= DNS_CLASS_SIZE
;
228 if(qtype
!= QType::OPT
|| (len
- pos
) < (DNS_TTL_SIZE
+ DNS_RDLENGTH_SIZE
))
232 *optRDPosition
= pos
;
233 *remaining
= len
- pos
;
238 void generateECSOption(const ComboAddress
& source
, string
& res
, uint16_t ECSPrefixLength
)
240 Netmask
sourceNetmask(source
, ECSPrefixLength
);
241 EDNSSubnetOpts ecsOpts
;
242 ecsOpts
.source
= sourceNetmask
;
243 string payload
= makeEDNSSubnetOptsString(ecsOpts
);
244 generateEDNSOption(EDNSOptionCode::ECS
, payload
, res
);
247 void generateOptRR(const std::string
& optRData
, string
& res
, uint16_t udpPayloadSize
, uint8_t ednsrcode
, bool dnssecOK
)
249 const uint8_t name
= 0;
252 edns0
.extRCode
= ednsrcode
;
254 edns0
.extFlags
= dnssecOK
? htons(EDNS_HEADER_FLAG_DO
) : 0;
256 dh
.d_type
= htons(QType::OPT
);
257 dh
.d_class
= htons(udpPayloadSize
);
258 static_assert(sizeof(EDNS0Record
) == sizeof(dh
.d_ttl
), "sizeof(EDNS0Record) must match sizeof(dnsrecordheader.d_ttl)");
259 memcpy(&dh
.d_ttl
, &edns0
, sizeof edns0
);
260 dh
.d_clen
= htons(static_cast<uint16_t>(optRData
.length()));
261 res
.reserve(sizeof(name
) + sizeof(dh
) + optRData
.length());
262 res
.assign(reinterpret_cast<const char *>(&name
), sizeof name
);
263 res
.append(reinterpret_cast<const char *>(&dh
), sizeof(dh
));
264 res
.append(optRData
.c_str(), optRData
.length());
267 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
)
269 assert(packet
!= NULL
);
271 assert(oldEcsOptionStart
!= NULL
);
272 assert(optRDLen
!= NULL
);
274 if (newECSOption
.size() == oldEcsOptionSize
) {
275 /* same size as the existing option */
276 memcpy(oldEcsOptionStart
, newECSOption
.c_str(), oldEcsOptionSize
);
279 /* different size than the existing option */
280 const unsigned int newPacketLen
= *len
+ (newECSOption
.length() - oldEcsOptionSize
);
281 const size_t beforeOptionLen
= oldEcsOptionStart
- packet
;
282 const size_t dataBehindSize
= *len
- beforeOptionLen
- oldEcsOptionSize
;
284 /* check that it fits in the existing buffer */
285 if (newPacketLen
> packetSize
) {
289 /* fix the size of ECS Option RDLen */
290 uint16_t newRDLen
= (optRDLen
[0] * 256) + optRDLen
[1];
291 newRDLen
+= (newECSOption
.size() - oldEcsOptionSize
);
292 optRDLen
[0] = newRDLen
/ 256;
293 optRDLen
[1] = newRDLen
% 256;
295 if (dataBehindSize
> 0) {
296 memmove(oldEcsOptionStart
, oldEcsOptionStart
+ oldEcsOptionSize
, dataBehindSize
);
298 memcpy(oldEcsOptionStart
+ dataBehindSize
, newECSOption
.c_str(), newECSOption
.size());
305 /* This function looks for an OPT RR, return true if a valid one was found (even if there was no options)
306 and false otherwise. */
307 bool parseEDNSOptions(DNSQuestion
& dq
)
309 assert(dq
.dh
!= nullptr);
310 assert(dq
.consumed
<= dq
.len
);
311 assert(dq
.len
<= dq
.size
);
313 if (dq
.ednsOptions
!= nullptr) {
317 dq
.ednsOptions
= std::make_shared
<std::map
<uint16_t, EDNSOptionView
> >();
318 const char* packet
= reinterpret_cast<const char*>(dq
.dh
);
320 size_t remaining
= 0;
321 uint16_t optRDPosition
;
322 int res
= getEDNSOptionsStart(packet
, dq
.consumed
, dq
.len
, &optRDPosition
, &remaining
);
325 res
= getEDNSOptions(packet
+ optRDPosition
, remaining
, *dq
.ednsOptions
);
332 static bool addECSToExistingOPT(char* const packet
, size_t const packetSize
, uint16_t* const len
, const string
& newECSOption
, unsigned char* optRDLen
, bool* const ecsAdded
)
334 /* we need to add one EDNS0 ECS option, fixing the size of EDNS0 RDLENGTH */
335 /* getEDNSOptionsStart has already checked that there is exactly one AR,
338 /* check if the existing buffer is large enough */
339 const size_t newECSOptionSize
= newECSOption
.size();
340 if (packetSize
- *len
<= newECSOptionSize
) {
344 uint16_t newRDLen
= (optRDLen
[0] * 256) + optRDLen
[1];
345 newRDLen
+= newECSOptionSize
;
346 optRDLen
[0] = newRDLen
/ 256;
347 optRDLen
[1] = newRDLen
% 256;
349 memcpy(packet
+ *len
, newECSOption
.c_str(), newECSOptionSize
);
350 *len
+= newECSOptionSize
;
356 static bool addEDNSWithECS(char* const packet
, size_t const packetSize
, uint16_t* const len
, const string
& newECSOption
, bool* const ednsAdded
, bool preserveTrailingData
)
358 /* we need to add a EDNS0 RR with one EDNS0 ECS option, fixing the AR count */
360 struct dnsheader
* dh
= reinterpret_cast<struct dnsheader
*>(packet
);
361 generateOptRR(newECSOption
, EDNSRR
, g_EdnsUDPPayloadSize
, 0, false);
363 /* does it fit in the existing buffer? */
364 if (packetSize
- *len
<= EDNSRR
.size()) {
368 uint32_t realPacketLen
= getDNSPacketLength(packet
, *len
);
369 if (realPacketLen
< *len
&& preserveTrailingData
) {
370 size_t toMove
= *len
- realPacketLen
;
371 memmove(packet
+ realPacketLen
+ EDNSRR
.size(), packet
+ realPacketLen
, toMove
);
372 *len
+= EDNSRR
.size();
375 *len
= realPacketLen
+ EDNSRR
.size();
378 uint16_t arcount
= ntohs(dh
->arcount
);
380 dh
->arcount
= htons(arcount
);
383 memcpy(packet
+ realPacketLen
, EDNSRR
.c_str(), EDNSRR
.size());
388 bool handleEDNSClientSubnet(char* const packet
, const size_t packetSize
, const unsigned int consumed
, uint16_t* const len
, bool* const ednsAdded
, bool* const ecsAdded
, bool overrideExisting
, const string
& newECSOption
, bool preserveTrailingData
)
390 assert(packet
!= nullptr);
391 assert(len
!= nullptr);
392 assert(consumed
<= (size_t) *len
);
393 assert(ednsAdded
!= nullptr);
394 assert(ecsAdded
!= nullptr);
395 uint16_t optRDPosition
= 0;
396 size_t remaining
= 0;
398 int res
= getEDNSOptionsStart(packet
, consumed
, *len
, &optRDPosition
, &remaining
);
401 return addEDNSWithECS(packet
, packetSize
, len
, newECSOption
, ednsAdded
, preserveTrailingData
);
404 unsigned char* optRDLen
= reinterpret_cast<unsigned char*>(packet
) + optRDPosition
;
405 char * ecsOptionStart
= nullptr;
406 size_t ecsOptionSize
= 0;
408 res
= getEDNSOption(reinterpret_cast<char*>(optRDLen
), remaining
, EDNSOptionCode::ECS
, &ecsOptionStart
, &ecsOptionSize
);
411 /* there is already an ECS value */
412 if (!overrideExisting
) {
416 return replaceEDNSClientSubnetOption(packet
, packetSize
, len
, ecsOptionStart
, ecsOptionSize
, optRDLen
, newECSOption
);
418 /* we have an EDNS OPT RR but no existing ECS option */
419 return addECSToExistingOPT(packet
, packetSize
, len
, newECSOption
, optRDLen
, ecsAdded
);
425 bool handleEDNSClientSubnet(DNSQuestion
& dq
, bool* ednsAdded
, bool* ecsAdded
, bool preserveTrailingData
)
427 assert(dq
.remote
!= nullptr);
429 generateECSOption(dq
.ecsSet
? dq
.ecs
.getNetwork() : *dq
.remote
, newECSOption
, dq
.ecsSet
? dq
.ecs
.getBits() : dq
.ecsPrefixLength
);
430 char* packet
= reinterpret_cast<char*>(dq
.dh
);
432 return handleEDNSClientSubnet(packet
, dq
.size
, dq
.consumed
, &dq
.len
, ednsAdded
, ecsAdded
, dq
.ecsOverride
, newECSOption
, preserveTrailingData
);
435 static int removeEDNSOptionFromOptions(unsigned char* optionsStart
, const uint16_t optionsLen
, const uint16_t optionCodeToRemove
, uint16_t* newOptionsLen
)
437 unsigned char* p
= optionsStart
;
439 while ((pos
+ 4) <= optionsLen
) {
440 unsigned char* optionBegin
= p
;
441 const uint16_t optionCode
= 0x100*p
[0] + p
[1];
442 p
+= sizeof(optionCode
);
443 pos
+= sizeof(optionCode
);
444 const uint16_t optionLen
= 0x100*p
[0] + p
[1];
445 p
+= sizeof(optionLen
);
446 pos
+= sizeof(optionLen
);
447 if ((pos
+ optionLen
) > optionsLen
) {
450 if (optionCode
== optionCodeToRemove
) {
451 if (pos
+ optionLen
< optionsLen
) {
452 /* move remaining options over the removed one,
454 memmove(optionBegin
, p
+ optionLen
, optionsLen
- (pos
+ optionLen
));
456 *newOptionsLen
= optionsLen
- (sizeof(optionCode
) + sizeof(optionLen
) + optionLen
);
465 int removeEDNSOptionFromOPT(char* optStart
, size_t* optLen
, const uint16_t optionCodeToRemove
)
467 if (*optLen
< optRecordMinimumSize
) {
470 const unsigned char* end
= (const unsigned char*) optStart
+ *optLen
;
471 unsigned char* p
= (unsigned char*) optStart
+ 9;
472 unsigned char* rdLenPtr
= p
;
473 uint16_t rdLen
= (0x100*p
[0] + p
[1]);
475 if (p
+ rdLen
!= end
) {
478 uint16_t newRdLen
= 0;
479 int res
= removeEDNSOptionFromOptions(p
, rdLen
, optionCodeToRemove
, &newRdLen
);
483 *optLen
-= (rdLen
- newRdLen
);
484 rdLenPtr
[0] = newRdLen
/ 0x100;
485 rdLenPtr
[1] = newRdLen
% 0x100;
489 bool isEDNSOptionInOpt(const std::string
& packet
, const size_t optStart
, const size_t optLen
, const uint16_t optionCodeToFind
, size_t* optContentStart
, uint16_t* optContentLen
)
491 if (optLen
< optRecordMinimumSize
) {
494 size_t p
= optStart
+ 9;
495 uint16_t rdLen
= (0x100*static_cast<unsigned char>(packet
.at(p
)) + static_cast<unsigned char>(packet
.at(p
+1)));
497 if (rdLen
> (optLen
- optRecordMinimumSize
)) {
501 size_t rdEnd
= p
+ rdLen
;
502 while ((p
+ 4) <= rdEnd
) {
503 const uint16_t optionCode
= 0x100*static_cast<unsigned char>(packet
.at(p
)) + static_cast<unsigned char>(packet
.at(p
+1));
504 p
+= sizeof(optionCode
);
505 const uint16_t optionLen
= 0x100*static_cast<unsigned char>(packet
.at(p
)) + static_cast<unsigned char>(packet
.at(p
+1));
506 p
+= sizeof(optionLen
);
508 if ((p
+ optionLen
) > rdEnd
) {
512 if (optionCode
== optionCodeToFind
) {
513 if (optContentStart
!= nullptr) {
514 *optContentStart
= p
;
517 if (optContentLen
!= nullptr) {
518 *optContentLen
= optionLen
;
528 int rewriteResponseWithoutEDNSOption(const std::string
& initialPacket
, const uint16_t optionCodeToSkip
, vector
<uint8_t>& newContent
)
530 assert(initialPacket
.size() >= sizeof(dnsheader
));
531 const struct dnsheader
* dh
= reinterpret_cast<const struct dnsheader
*>(initialPacket
.data());
533 if (ntohs(dh
->arcount
) == 0)
536 if (ntohs(dh
->qdcount
) == 0)
539 PacketReader
pr(initialPacket
);
543 uint16_t qdcount
= ntohs(dh
->qdcount
);
544 uint16_t ancount
= ntohs(dh
->ancount
);
545 uint16_t nscount
= ntohs(dh
->nscount
);
546 uint16_t arcount
= ntohs(dh
->arcount
);
550 struct dnsrecordheader ah
;
552 rrname
= pr
.getName();
553 rrtype
= pr
.get16BitInt();
554 rrclass
= pr
.get16BitInt();
556 DNSPacketWriter
pw(newContent
, rrname
, rrtype
, rrclass
, dh
->opcode
);
557 pw
.getHeader()->id
=dh
->id
;
558 pw
.getHeader()->qr
=dh
->qr
;
559 pw
.getHeader()->aa
=dh
->aa
;
560 pw
.getHeader()->tc
=dh
->tc
;
561 pw
.getHeader()->rd
=dh
->rd
;
562 pw
.getHeader()->ra
=dh
->ra
;
563 pw
.getHeader()->ad
=dh
->ad
;
564 pw
.getHeader()->cd
=dh
->cd
;
565 pw
.getHeader()->rcode
=dh
->rcode
;
567 /* consume remaining qd if any */
569 for(idx
= 1; idx
< qdcount
; idx
++) {
570 rrname
= pr
.getName();
571 rrtype
= pr
.get16BitInt();
572 rrclass
= pr
.get16BitInt();
579 for (idx
= 0; idx
< ancount
; idx
++) {
580 rrname
= pr
.getName();
581 pr
.getDnsrecordheader(ah
);
583 pw
.startRecord(rrname
, ah
.d_type
, ah
.d_ttl
, ah
.d_class
, DNSResourceRecord::ANSWER
, true);
588 for (idx
= 0; idx
< nscount
; idx
++) {
589 rrname
= pr
.getName();
590 pr
.getDnsrecordheader(ah
);
592 pw
.startRecord(rrname
, ah
.d_type
, ah
.d_ttl
, ah
.d_class
, DNSResourceRecord::AUTHORITY
, true);
597 /* consume AR, looking for OPT */
598 for (idx
= 0; idx
< arcount
; idx
++) {
599 rrname
= pr
.getName();
600 pr
.getDnsrecordheader(ah
);
602 if (ah
.d_type
!= QType::OPT
) {
603 pw
.startRecord(rrname
, ah
.d_type
, ah
.d_ttl
, ah
.d_class
, DNSResourceRecord::ADDITIONAL
, true);
607 pw
.startRecord(rrname
, ah
.d_type
, ah
.d_ttl
, ah
.d_class
, DNSResourceRecord::ADDITIONAL
, false);
609 uint16_t rdLen
= blob
.length();
610 removeEDNSOptionFromOptions((unsigned char*)blob
.c_str(), rdLen
, optionCodeToSkip
, &rdLen
);
611 /* xfrBlob(string, size) completely ignores size.. */
613 blob
.resize((size_t)rdLen
);
625 bool addEDNS(dnsheader
* dh
, uint16_t& len
, const size_t size
, bool dnssecOK
, uint16_t payloadSize
, uint8_t ednsrcode
)
627 if (dh
->arcount
!= 0) {
631 std::string optRecord
;
632 generateOptRR(std::string(), optRecord
, payloadSize
, ednsrcode
, dnssecOK
);
634 if (optRecord
.size() >= size
|| (size
- optRecord
.size()) < len
) {
638 char * optPtr
= reinterpret_cast<char*>(dh
) + len
;
639 memcpy(optPtr
, optRecord
.data(), optRecord
.size());
640 len
+= optRecord
.size();
641 dh
->arcount
= htons(1);
646 bool addEDNSToQueryTurnedResponse(DNSQuestion
& dq
)
648 uint16_t optRDPosition
;
649 /* remaining is at least the size of the rdlen + the options if any + the following records if any */
650 size_t remaining
= 0;
652 int res
= getEDNSOptionsStart(reinterpret_cast<char*>(dq
.dh
), dq
.consumed
, dq
.len
, &optRDPosition
, &remaining
);
655 /* if the initial query did not have EDNS0, we are done */
659 const size_t existingOptLen
= /* root */ 1 + DNS_TYPE_SIZE
+ DNS_CLASS_SIZE
+ EDNS_EXTENDED_RCODE_SIZE
+ EDNS_VERSION_SIZE
+ /* Z */ 2 + remaining
;
660 if (existingOptLen
>= dq
.len
) {
661 /* something is wrong, bail out */
665 char* optRDLen
= reinterpret_cast<char*>(dq
.dh
) + optRDPosition
;
666 char * optPtr
= (optRDLen
- (/* root */ 1 + DNS_TYPE_SIZE
+ DNS_CLASS_SIZE
+ EDNS_EXTENDED_RCODE_SIZE
+ EDNS_VERSION_SIZE
+ /* Z */ 2));
668 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
;
669 uint16_t z
= 0x100 * (*zPtr
) + *(zPtr
+ 1);
670 bool dnssecOK
= z
& EDNS_HEADER_FLAG_DO
;
672 /* remove the existing OPT record, and everything else that follows (any SIG or TSIG would be useless anyway) */
673 dq
.len
-= existingOptLen
;
676 if (g_addEDNSToSelfGeneratedResponses
) {
677 /* now we need to add a new OPT record */
678 return addEDNS(dq
.dh
, dq
.len
, dq
.size
, dnssecOK
, g_PayloadSizeSelfGenAnswers
, dq
.ednsRCode
);
681 /* otherwise we are just fine */
685 // goal in life - if you send us a reasonably normal packet, we'll get Z for you, otherwise 0
686 int getEDNSZ(const DNSQuestion
& dq
)
689 if (ntohs(dq
.dh
->qdcount
) != 1 || dq
.dh
->ancount
!= 0 || ntohs(dq
.dh
->arcount
) != 1 || dq
.dh
->nscount
!= 0) {
693 if (dq
.len
<= sizeof(dnsheader
)) {
697 size_t pos
= sizeof(dnsheader
) + dq
.consumed
+ DNS_TYPE_SIZE
+ DNS_CLASS_SIZE
;
699 if (dq
.len
<= (pos
+ /* root */ 1 + DNS_TYPE_SIZE
+ DNS_CLASS_SIZE
)) {
703 const char* packet
= reinterpret_cast<const char*>(dq
.dh
);
705 if (packet
[pos
] != 0) {
706 /* not root, so not a valid OPT record */
712 uint16_t qtype
= (reinterpret_cast<const unsigned char*>(packet
)[pos
])*256 + reinterpret_cast<const unsigned char*>(packet
)[pos
+1];
713 pos
+= DNS_TYPE_SIZE
;
714 pos
+= DNS_CLASS_SIZE
;
716 if (qtype
!= QType::OPT
|| (pos
+ EDNS_EXTENDED_RCODE_SIZE
+ EDNS_VERSION_SIZE
+ 1) >= dq
.len
) {
720 const uint8_t* z
= reinterpret_cast<const uint8_t*>(packet
) + pos
+ EDNS_EXTENDED_RCODE_SIZE
+ EDNS_VERSION_SIZE
;
721 return 0x100 * (*z
) + *(z
+1);
728 bool queryHasEDNS(const DNSQuestion
& dq
)
730 uint16_t optRDPosition
;
731 size_t ecsRemaining
= 0;
733 int res
= getEDNSOptionsStart(reinterpret_cast<char*>(dq
.dh
), dq
.consumed
, dq
.len
, &optRDPosition
, &ecsRemaining
);
741 bool getEDNS0Record(const DNSQuestion
& dq
, EDNS0Record
& edns0
)
746 const char * packet
= reinterpret_cast<const char*>(dq
.dh
);
747 std::string
packetStr(packet
, dq
.len
);
748 int res
= locateEDNSOptRR(packetStr
, &optStart
, &optLen
, &last
);
754 if (optLen
< optRecordMinimumSize
) {
758 if (optStart
< dq
.len
&& packetStr
.at(optStart
) != 0) {
759 // OPT RR Name != '.'
763 static_assert(sizeof(EDNS0Record
) == sizeof(uint32_t), "sizeof(EDNS0Record) must match sizeof(uint32_t) AKA RR TTL size");
764 // copy out 4-byte "ttl" (really the EDNS0 record), after root label (1) + type (2) + class (2).
765 memcpy(&edns0
, packet
+ optStart
+ 5, sizeof edns0
);