]>
git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/dnsdist-ecs.cc
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 /* draft-ietf-dnsop-edns-client-subnet-04 "11.1. Privacy" */
34 uint16_t g_ECSSourcePrefixV4
= 24;
35 uint16_t g_ECSSourcePrefixV6
= 56;
37 bool g_ECSOverride
{false};
39 int rewriteResponseWithoutEDNS(const char * packet
, const size_t len
, vector
<uint8_t>& newContent
)
41 assert(packet
!= NULL
);
42 assert(len
>= sizeof(dnsheader
));
43 const struct dnsheader
* dh
= (const struct dnsheader
*) packet
;
45 if (ntohs(dh
->arcount
) == 0)
48 if (ntohs(dh
->qdcount
) == 0)
51 vector
<uint8_t> content(len
- sizeof(dnsheader
));
52 copy(packet
+ sizeof(dnsheader
), packet
+ len
, content
.begin());
53 PacketReader
pr(content
);
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);
120 pr
.d_pos
+= ah
.d_clen
;
128 int locateEDNSOptRR(char * packet
, const size_t len
, char ** optStart
, size_t * optLen
, bool * last
)
130 assert(packet
!= NULL
);
131 assert(optStart
!= NULL
);
132 assert(optLen
!= NULL
);
133 assert(last
!= NULL
);
134 const struct dnsheader
* dh
= (const struct dnsheader
*) packet
;
136 if (ntohs(dh
->arcount
) == 0)
139 vector
<uint8_t> content(len
- sizeof(dnsheader
));
140 copy(packet
+ sizeof(dnsheader
), packet
+ len
, content
.begin());
141 PacketReader
pr(content
);
144 uint16_t qdcount
= ntohs(dh
->qdcount
);
145 uint16_t ancount
= ntohs(dh
->ancount
);
146 uint16_t nscount
= ntohs(dh
->nscount
);
147 uint16_t arcount
= ntohs(dh
->arcount
);
150 struct dnsrecordheader ah
;
153 for(idx
= 0; idx
< qdcount
; idx
++) {
154 rrname
= pr
.getName();
155 rrtype
= pr
.get16BitInt();
156 rrclass
= pr
.get16BitInt();
161 /* consume AN and NS */
162 for (idx
= 0; idx
< ancount
+ nscount
; idx
++) {
163 rrname
= pr
.getName();
164 pr
.getDnsrecordheader(ah
);
165 pr
.d_pos
+= ah
.d_clen
;
168 /* consume AR, looking for OPT */
169 for (idx
= 0; idx
< arcount
; idx
++) {
170 uint16_t start
= pr
.d_pos
;
171 rrname
= pr
.getName();
172 pr
.getDnsrecordheader(ah
);
174 if (ah
.d_type
== QType::OPT
) {
175 *optStart
= packet
+ sizeof(dnsheader
) + start
;
176 *optLen
= (pr
.d_pos
- start
) + ah
.d_clen
;
178 if ((packet
+ len
) < (*optStart
+ *optLen
)) {
179 throw std::range_error("Opt record overflow");
182 if (idx
== ((size_t) arcount
- 1)) {
190 pr
.d_pos
+= ah
.d_clen
;
196 /* extract the start of the OPT RR in a QUERY packet if any */
197 static int getEDNSOptionsStart(char* packet
, const size_t offset
, const size_t len
, char ** optRDLen
, size_t * remaining
)
199 assert(packet
!= NULL
);
200 assert(optRDLen
!= NULL
);
201 assert(remaining
!= NULL
);
202 const struct dnsheader
* dh
= (const struct dnsheader
*) packet
;
207 if (ntohs(dh
->qdcount
) != 1 || dh
->ancount
!= 0 || ntohs(dh
->arcount
) != 1 || dh
->nscount
!= 0)
210 size_t pos
= sizeof(dnsheader
) + offset
;
211 pos
+= DNS_TYPE_SIZE
+ DNS_CLASS_SIZE
;
216 uint16_t qtype
, qclass
;
217 unsigned int consumed
;
218 DNSName
aname(packet
, len
, pos
, true, &qtype
, &qclass
, &consumed
);
220 if ((len
- pos
) < (consumed
+ DNS_TYPE_SIZE
+ DNS_CLASS_SIZE
))
223 pos
+= consumed
+ DNS_TYPE_SIZE
+ DNS_CLASS_SIZE
;
224 if(qtype
!= QType::OPT
|| (len
- pos
) < (DNS_TTL_SIZE
+ DNS_RDLENGTH_SIZE
))
228 *optRDLen
= packet
+ pos
;
229 *remaining
= len
- pos
;
234 static void generateECSOption(const ComboAddress
& source
, string
& res
, uint16_t ECSPrefixLength
)
236 Netmask
sourceNetmask(source
, ECSPrefixLength
);
237 EDNSSubnetOpts ecsOpts
;
238 ecsOpts
.source
= sourceNetmask
;
239 string payload
= makeEDNSSubnetOptsString(ecsOpts
);
240 generateEDNSOption(EDNSOptionCode::ECS
, payload
, res
);
243 void generateOptRR(const std::string
& optRData
, string
& res
)
245 const uint8_t name
= 0;
252 dh
.d_type
= htons(QType::OPT
);
253 dh
.d_class
= htons(g_EdnsUDPPayloadSize
);
254 static_assert(sizeof(EDNS0Record
) == sizeof(dh
.d_ttl
), "sizeof(EDNS0Record) must match sizeof(dnsrecordheader.d_ttl)");
255 memcpy(&dh
.d_ttl
, &edns0
, sizeof edns0
);
256 dh
.d_clen
= htons((uint16_t) optRData
.length());
257 res
.assign((const char *) &name
, sizeof name
);
258 res
.append((const char *) &dh
, sizeof dh
);
259 res
.append(optRData
.c_str(), optRData
.length());
262 static void replaceEDNSClientSubnetOption(char * const packet
, const size_t packetSize
, uint16_t * const len
, string
& largerPacket
, const ComboAddress
& remote
, char * const oldEcsOptionStart
, size_t const oldEcsOptionSize
, unsigned char * const optRDLen
, uint16_t ECSPrefixLength
)
264 assert(packet
!= NULL
);
266 assert(oldEcsOptionStart
!= NULL
);
267 assert(optRDLen
!= NULL
);
269 generateECSOption(remote
, ECSOption
, ECSPrefixLength
);
271 if (ECSOption
.size() == oldEcsOptionSize
) {
272 /* same size as the existing option */
273 memcpy(oldEcsOptionStart
, ECSOption
.c_str(), oldEcsOptionSize
);
276 /* different size than the existing option */
277 const unsigned int newPacketLen
= *len
+ (ECSOption
.length() - oldEcsOptionSize
);
278 const size_t beforeOptionLen
= oldEcsOptionStart
- packet
;
279 const size_t dataBehindSize
= *len
- beforeOptionLen
- oldEcsOptionSize
;
281 /* fix the size of ECS Option RDLen */
282 uint16_t newRDLen
= (optRDLen
[0] * 256) + optRDLen
[1];
283 newRDLen
+= (ECSOption
.size() - oldEcsOptionSize
);
284 optRDLen
[0] = newRDLen
/ 256;
285 optRDLen
[1] = newRDLen
% 256;
287 if (newPacketLen
<= packetSize
) {
288 /* it fits in the existing buffer */
289 if (dataBehindSize
> 0) {
290 memmove(oldEcsOptionStart
, oldEcsOptionStart
+ oldEcsOptionSize
, dataBehindSize
);
292 memcpy(oldEcsOptionStart
+ dataBehindSize
, ECSOption
.c_str(), ECSOption
.size());
296 /* We need a larger packet */
297 if (newPacketLen
> largerPacket
.capacity()) {
298 largerPacket
.reserve(newPacketLen
);
300 /* copy data before the existing option */
301 largerPacket
.append(packet
, beforeOptionLen
);
302 /* copy the new option */
303 largerPacket
.append(ECSOption
);
304 /* copy data that where behind the existing option */
305 if (dataBehindSize
> 0) {
306 largerPacket
.append(oldEcsOptionStart
+ oldEcsOptionSize
, dataBehindSize
);
312 void handleEDNSClientSubnet(char* const packet
, const size_t packetSize
, const unsigned int consumed
, uint16_t* const len
, string
& largerPacket
, bool* const ednsAdded
, bool* const ecsAdded
, const ComboAddress
& remote
, bool overrideExisting
, uint16_t ecsPrefixLength
)
314 assert(packet
!= NULL
);
316 assert(consumed
<= (size_t) *len
);
317 assert(ednsAdded
!= NULL
);
318 assert(ecsAdded
!= NULL
);
319 unsigned char * optRDLen
= NULL
;
320 size_t remaining
= 0;
322 int res
= getEDNSOptionsStart(packet
, consumed
, *len
, (char**) &optRDLen
, &remaining
);
325 char * ecsOptionStart
= NULL
;
326 size_t ecsOptionSize
= 0;
328 res
= getEDNSOption((char*)optRDLen
, remaining
, EDNSOptionCode::ECS
, &ecsOptionStart
, &ecsOptionSize
);
331 /* there is already an ECS value */
332 if (overrideExisting
) {
333 replaceEDNSClientSubnetOption(packet
, packetSize
, len
, largerPacket
, remote
, ecsOptionStart
, ecsOptionSize
, optRDLen
, ecsPrefixLength
);
336 /* we need to add one EDNS0 ECS option, fixing the size of EDNS0 RDLENGTH */
337 /* getEDNSOptionsStart has already checked that there is exactly one AR,
340 generateECSOption(remote
, ECSOption
, ecsPrefixLength
);
341 const size_t ECSOptionSize
= ECSOption
.size();
343 uint16_t newRDLen
= (optRDLen
[0] * 256) + optRDLen
[1];
344 newRDLen
+= ECSOptionSize
;
345 optRDLen
[0] = newRDLen
/ 256;
346 optRDLen
[1] = newRDLen
% 256;
348 if (packetSize
- *len
> ECSOptionSize
) {
349 /* if the existing buffer is large enough */
350 memcpy(packet
+ *len
, ECSOption
.c_str(), ECSOptionSize
);
351 *len
+= ECSOptionSize
;
354 if (*len
+ ECSOptionSize
> largerPacket
.capacity()) {
355 largerPacket
.reserve(*len
+ ECSOptionSize
);
358 largerPacket
.append(packet
, *len
);
359 largerPacket
.append(ECSOption
);
365 /* we need to add a EDNS0 RR with one EDNS0 ECS option, fixing the AR count */
367 struct dnsheader
* dh
= (struct dnsheader
*) packet
;
369 generateECSOption(remote
, optRData
, ecsPrefixLength
);
370 generateOptRR(optRData
, EDNSRR
);
371 uint16_t arcount
= ntohs(dh
->arcount
);
373 dh
->arcount
= htons(arcount
);
376 /* does it fit in the existing buffer? */
377 if (packetSize
- *len
> EDNSRR
.size()) {
378 memcpy(packet
+ *len
, EDNSRR
.c_str(), EDNSRR
.size());
379 *len
+= EDNSRR
.size();
382 if (*len
+ EDNSRR
.size() > largerPacket
.capacity()) {
383 largerPacket
.reserve(*len
+ EDNSRR
.size());
386 largerPacket
.append(packet
, *len
);
387 largerPacket
.append(EDNSRR
);
392 static int removeEDNSOptionFromOptions(unsigned char* optionsStart
, const uint16_t optionsLen
, const uint16_t optionCodeToRemove
, uint16_t* newOptionsLen
)
394 unsigned char* p
= optionsStart
;
395 const unsigned char* end
= p
+ optionsLen
;
396 while ((p
+ 4) <= end
) {
397 unsigned char* optionBegin
= p
;
398 const uint16_t optionCode
= 0x100*p
[0] + p
[1];
399 p
+= sizeof(optionCode
);
400 const uint16_t optionLen
= 0x100*p
[0] + p
[1];
401 p
+= sizeof(optionLen
);
402 if ((p
+ optionLen
) > end
) {
405 if (optionCode
== optionCodeToRemove
) {
406 if (p
+ optionLen
< end
) {
407 /* move remaining options over the removed one,
409 memmove(optionBegin
, p
+ optionLen
, end
- (p
+ optionLen
));
411 *newOptionsLen
= optionsLen
- (sizeof(optionCode
) + sizeof(optionLen
) + optionLen
);
419 int removeEDNSOptionFromOPT(char* optStart
, size_t* optLen
, const uint16_t optionCodeToRemove
)
422 root label (1), type (2), class (2), ttl (4) + rdlen (2)*/
426 const unsigned char* end
= (const unsigned char*) optStart
+ *optLen
;
427 unsigned char* p
= (unsigned char*) optStart
+ 9;
428 unsigned char* rdLenPtr
= p
;
429 uint16_t rdLen
= (0x100*p
[0] + p
[1]);
431 if (p
+ rdLen
!= end
) {
434 uint16_t newRdLen
= 0;
435 int res
= removeEDNSOptionFromOptions(p
, rdLen
, optionCodeToRemove
, &newRdLen
);
439 *optLen
-= (rdLen
- newRdLen
);
440 rdLenPtr
[0] = newRdLen
/ 0x100;
441 rdLenPtr
[1] = newRdLen
% 0x100;
445 int rewriteResponseWithoutEDNSOption(const char * packet
, const size_t len
, const uint16_t optionCodeToSkip
, vector
<uint8_t>& newContent
)
447 assert(packet
!= NULL
);
448 assert(len
>= sizeof(dnsheader
));
449 const struct dnsheader
* dh
= (const struct dnsheader
*) packet
;
451 if (ntohs(dh
->arcount
) == 0)
454 if (ntohs(dh
->qdcount
) == 0)
457 vector
<uint8_t> content(len
- sizeof(dnsheader
));
458 copy(packet
+ sizeof(dnsheader
), packet
+ len
, content
.begin());
459 PacketReader
pr(content
);
463 uint16_t qdcount
= ntohs(dh
->qdcount
);
464 uint16_t ancount
= ntohs(dh
->ancount
);
465 uint16_t nscount
= ntohs(dh
->nscount
);
466 uint16_t arcount
= ntohs(dh
->arcount
);
470 struct dnsrecordheader ah
;
472 rrname
= pr
.getName();
473 rrtype
= pr
.get16BitInt();
474 rrclass
= pr
.get16BitInt();
476 DNSPacketWriter
pw(newContent
, rrname
, rrtype
, rrclass
, dh
->opcode
);
477 pw
.getHeader()->id
=dh
->id
;
478 pw
.getHeader()->qr
=dh
->qr
;
479 pw
.getHeader()->aa
=dh
->aa
;
480 pw
.getHeader()->tc
=dh
->tc
;
481 pw
.getHeader()->rd
=dh
->rd
;
482 pw
.getHeader()->ra
=dh
->ra
;
483 pw
.getHeader()->ad
=dh
->ad
;
484 pw
.getHeader()->cd
=dh
->cd
;
485 pw
.getHeader()->rcode
=dh
->rcode
;
487 /* consume remaining qd if any */
489 for(idx
= 1; idx
< qdcount
; idx
++) {
490 rrname
= pr
.getName();
491 rrtype
= pr
.get16BitInt();
492 rrclass
= pr
.get16BitInt();
499 for (idx
= 0; idx
< ancount
; idx
++) {
500 rrname
= pr
.getName();
501 pr
.getDnsrecordheader(ah
);
503 pw
.startRecord(rrname
, ah
.d_type
, ah
.d_ttl
, ah
.d_class
, DNSResourceRecord::ANSWER
, true);
508 for (idx
= 0; idx
< nscount
; idx
++) {
509 rrname
= pr
.getName();
510 pr
.getDnsrecordheader(ah
);
512 pw
.startRecord(rrname
, ah
.d_type
, ah
.d_ttl
, ah
.d_class
, DNSResourceRecord::AUTHORITY
, true);
517 /* consume AR, looking for OPT */
518 for (idx
= 0; idx
< arcount
; idx
++) {
519 rrname
= pr
.getName();
520 pr
.getDnsrecordheader(ah
);
522 if (ah
.d_type
!= QType::OPT
) {
523 pw
.startRecord(rrname
, ah
.d_type
, ah
.d_ttl
, ah
.d_class
, DNSResourceRecord::ADDITIONAL
, true);
527 pw
.startRecord(rrname
, ah
.d_type
, ah
.d_ttl
, ah
.d_class
, DNSResourceRecord::ADDITIONAL
, false);
529 uint16_t rdLen
= blob
.length();
530 removeEDNSOptionFromOptions((unsigned char*)blob
.c_str(), rdLen
, optionCodeToSkip
, &rdLen
);
531 /* xfrBlob(string, size) completely ignores size.. */
533 blob
.resize((size_t)rdLen
);