]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/dnsdist-ecs.cc
Merge pull request #4692 from cmouse/ssql-unique-ptr
[thirdparty/pdns.git] / pdns / dnsdist-ecs.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 #include "dolog.hh"
23 #include "dnsdist.hh"
24 #include "dnsdist-ecs.hh"
25 #include "dnsparser.hh"
26 #include "dnswriter.hh"
27 #include "ednsoptions.hh"
28 #include "ednssubnet.hh"
29
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;
36
37 bool g_ECSOverride{false};
38
39 int rewriteResponseWithoutEDNS(const char * packet, const size_t len, vector<uint8_t>& newContent)
40 {
41 assert(packet != NULL);
42 assert(len >= sizeof(dnsheader));
43 const struct dnsheader* dh = (const struct dnsheader*) packet;
44
45 if (ntohs(dh->arcount) == 0)
46 return ENOENT;
47
48 if (ntohs(dh->qdcount) == 0)
49 return ENOENT;
50
51 vector<uint8_t> content(len - sizeof(dnsheader));
52 copy(packet + sizeof(dnsheader), packet + len, content.begin());
53 PacketReader pr(content);
54
55 size_t idx = 0;
56 DNSName rrname;
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);
61 uint16_t rrtype;
62 uint16_t rrclass;
63 string blob;
64 struct dnsrecordheader ah;
65
66 rrname = pr.getName();
67 rrtype = pr.get16BitInt();
68 rrclass = pr.get16BitInt();
69
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;
80
81 /* consume remaining qd if any */
82 if (qdcount > 1) {
83 for(idx = 1; idx < qdcount; idx++) {
84 rrname = pr.getName();
85 rrtype = pr.get16BitInt();
86 rrclass = pr.get16BitInt();
87 (void) rrtype;
88 (void) rrclass;
89 }
90 }
91
92 /* copy AN and NS */
93 for (idx = 0; idx < ancount; idx++) {
94 rrname = pr.getName();
95 pr.getDnsrecordheader(ah);
96
97 pw.startRecord(rrname, ah.d_type, ah.d_ttl, ah.d_class, DNSResourceRecord::ANSWER, true);
98 pr.xfrBlob(blob);
99 pw.xfrBlob(blob);
100 }
101
102 for (idx = 0; idx < nscount; idx++) {
103 rrname = pr.getName();
104 pr.getDnsrecordheader(ah);
105
106 pw.startRecord(rrname, ah.d_type, ah.d_ttl, ah.d_class, DNSResourceRecord::AUTHORITY, true);
107 pr.xfrBlob(blob);
108 pw.xfrBlob(blob);
109 }
110 /* consume AR, looking for OPT */
111 for (idx = 0; idx < arcount; idx++) {
112 rrname = pr.getName();
113 pr.getDnsrecordheader(ah);
114
115 if (ah.d_type != QType::OPT) {
116 pw.startRecord(rrname, ah.d_type, ah.d_ttl, ah.d_class, DNSResourceRecord::ADDITIONAL, true);
117 pr.xfrBlob(blob);
118 pw.xfrBlob(blob);
119 } else {
120 pr.d_pos += ah.d_clen;
121 }
122 }
123 pw.commit();
124
125 return 0;
126 }
127
128 int locateEDNSOptRR(char * packet, const size_t len, char ** optStart, size_t * optLen, bool * last)
129 {
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;
135
136 if (ntohs(dh->arcount) == 0)
137 return ENOENT;
138
139 vector<uint8_t> content(len - sizeof(dnsheader));
140 copy(packet + sizeof(dnsheader), packet + len, content.begin());
141 PacketReader pr(content);
142 size_t idx = 0;
143 DNSName rrname;
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);
148 uint16_t rrtype;
149 uint16_t rrclass;
150 struct dnsrecordheader ah;
151
152 /* consume qd */
153 for(idx = 0; idx < qdcount; idx++) {
154 rrname = pr.getName();
155 rrtype = pr.get16BitInt();
156 rrclass = pr.get16BitInt();
157 (void) rrtype;
158 (void) rrclass;
159 }
160
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;
166 }
167
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);
173
174 if (ah.d_type == QType::OPT) {
175 *optStart = packet + sizeof(dnsheader) + start;
176 *optLen = (pr.d_pos - start) + ah.d_clen;
177
178 if ((packet + len) < (*optStart + *optLen)) {
179 throw std::range_error("Opt record overflow");
180 }
181
182 if (idx == ((size_t) arcount - 1)) {
183 *last = true;
184 }
185 else {
186 *last = false;
187 }
188 return 0;
189 }
190 pr.d_pos += ah.d_clen;
191 }
192
193 return ENOENT;
194 }
195
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)
198 {
199 assert(packet != NULL);
200 assert(optRDLen != NULL);
201 assert(remaining != NULL);
202 const struct dnsheader* dh = (const struct dnsheader*) packet;
203
204 if (offset >= len)
205 return ENOENT;
206
207 if (ntohs(dh->qdcount) != 1 || dh->ancount != 0 || ntohs(dh->arcount) != 1 || dh->nscount != 0)
208 return ENOENT;
209
210 size_t pos = sizeof(dnsheader) + offset;
211 pos += DNS_TYPE_SIZE + DNS_CLASS_SIZE;
212
213 if (pos >= len)
214 return ENOENT;
215
216 uint16_t qtype, qclass;
217 unsigned int consumed;
218 DNSName aname(packet, len, pos, true, &qtype, &qclass, &consumed);
219
220 if ((len - pos) < (consumed + DNS_TYPE_SIZE + DNS_CLASS_SIZE))
221 return ENOENT;
222
223 pos += consumed + DNS_TYPE_SIZE + DNS_CLASS_SIZE;
224 if(qtype != QType::OPT || (len - pos) < (DNS_TTL_SIZE + DNS_RDLENGTH_SIZE))
225 return ENOENT;
226
227 pos += DNS_TTL_SIZE;
228 *optRDLen = packet + pos;
229 *remaining = len - pos;
230
231 return 0;
232 }
233
234 static void generateECSOption(const ComboAddress& source, string& res, uint16_t ECSPrefixLength)
235 {
236 Netmask sourceNetmask(source, ECSPrefixLength);
237 EDNSSubnetOpts ecsOpts;
238 ecsOpts.source = sourceNetmask;
239 string payload = makeEDNSSubnetOptsString(ecsOpts);
240 generateEDNSOption(EDNSOptionCode::ECS, payload, res);
241 }
242
243 void generateOptRR(const std::string& optRData, string& res)
244 {
245 const uint8_t name = 0;
246 dnsrecordheader dh;
247 EDNS0Record edns0;
248 edns0.extRCode = 0;
249 edns0.version = 0;
250 edns0.Z = 0;
251
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());
260 }
261
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)
263 {
264 assert(packet != NULL);
265 assert(len != NULL);
266 assert(oldEcsOptionStart != NULL);
267 assert(optRDLen != NULL);
268 string ECSOption;
269 generateECSOption(remote, ECSOption, ECSPrefixLength);
270
271 if (ECSOption.size() == oldEcsOptionSize) {
272 /* same size as the existing option */
273 memcpy(oldEcsOptionStart, ECSOption.c_str(), oldEcsOptionSize);
274 }
275 else {
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;
280
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;
286
287 if (newPacketLen <= packetSize) {
288 /* it fits in the existing buffer */
289 if (dataBehindSize > 0) {
290 memmove(oldEcsOptionStart, oldEcsOptionStart + oldEcsOptionSize, dataBehindSize);
291 }
292 memcpy(oldEcsOptionStart + dataBehindSize, ECSOption.c_str(), ECSOption.size());
293 *len = newPacketLen;
294 }
295 else {
296 /* We need a larger packet */
297 if (newPacketLen > largerPacket.capacity()) {
298 largerPacket.reserve(newPacketLen);
299 }
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);
307 }
308 }
309 }
310 }
311
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)
313 {
314 assert(packet != NULL);
315 assert(len != 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;
321
322 int res = getEDNSOptionsStart(packet, consumed, *len, (char**) &optRDLen, &remaining);
323
324 if (res == 0) {
325 char * ecsOptionStart = NULL;
326 size_t ecsOptionSize = 0;
327
328 res = getEDNSOption((char*)optRDLen, remaining, EDNSOptionCode::ECS, &ecsOptionStart, &ecsOptionSize);
329
330 if (res == 0) {
331 /* there is already an ECS value */
332 if (overrideExisting) {
333 replaceEDNSClientSubnetOption(packet, packetSize, len, largerPacket, remote, ecsOptionStart, ecsOptionSize, optRDLen, ecsPrefixLength);
334 }
335 } else {
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,
338 no NS and no AN */
339 string ECSOption;
340 generateECSOption(remote, ECSOption, ecsPrefixLength);
341 const size_t ECSOptionSize = ECSOption.size();
342
343 uint16_t newRDLen = (optRDLen[0] * 256) + optRDLen[1];
344 newRDLen += ECSOptionSize;
345 optRDLen[0] = newRDLen / 256;
346 optRDLen[1] = newRDLen % 256;
347
348 if (packetSize - *len > ECSOptionSize) {
349 /* if the existing buffer is large enough */
350 memcpy(packet + *len, ECSOption.c_str(), ECSOptionSize);
351 *len += ECSOptionSize;
352 }
353 else {
354 if (*len + ECSOptionSize > largerPacket.capacity()) {
355 largerPacket.reserve(*len + ECSOptionSize);
356 }
357
358 largerPacket.append(packet, *len);
359 largerPacket.append(ECSOption);
360 }
361 *ecsAdded = true;
362 }
363 }
364 else {
365 /* we need to add a EDNS0 RR with one EDNS0 ECS option, fixing the AR count */
366 string EDNSRR;
367 struct dnsheader* dh = (struct dnsheader*) packet;
368 string optRData;
369 generateECSOption(remote, optRData, ecsPrefixLength);
370 generateOptRR(optRData, EDNSRR);
371 uint16_t arcount = ntohs(dh->arcount);
372 arcount++;
373 dh->arcount = htons(arcount);
374 *ednsAdded = true;
375
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();
380 }
381 else {
382 if (*len + EDNSRR.size() > largerPacket.capacity()) {
383 largerPacket.reserve(*len + EDNSRR.size());
384 }
385
386 largerPacket.append(packet, *len);
387 largerPacket.append(EDNSRR);
388 }
389 }
390 }
391
392 static int removeEDNSOptionFromOptions(unsigned char* optionsStart, const uint16_t optionsLen, const uint16_t optionCodeToRemove, uint16_t* newOptionsLen)
393 {
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) {
403 return EINVAL;
404 }
405 if (optionCode == optionCodeToRemove) {
406 if (p + optionLen < end) {
407 /* move remaining options over the removed one,
408 if any */
409 memmove(optionBegin, p + optionLen, end - (p + optionLen));
410 }
411 *newOptionsLen = optionsLen - (sizeof(optionCode) + sizeof(optionLen) + optionLen);
412 return 0;
413 }
414 p += optionLen;
415 }
416 return ENOENT;
417 }
418
419 int removeEDNSOptionFromOPT(char* optStart, size_t* optLen, const uint16_t optionCodeToRemove)
420 {
421 /* we need at least:
422 root label (1), type (2), class (2), ttl (4) + rdlen (2)*/
423 if (*optLen < 11) {
424 return EINVAL;
425 }
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]);
430 p += sizeof(rdLen);
431 if (p + rdLen != end) {
432 return EINVAL;
433 }
434 uint16_t newRdLen = 0;
435 int res = removeEDNSOptionFromOptions(p, rdLen, optionCodeToRemove, &newRdLen);
436 if (res != 0) {
437 return res;
438 }
439 *optLen -= (rdLen - newRdLen);
440 rdLenPtr[0] = newRdLen / 0x100;
441 rdLenPtr[1] = newRdLen % 0x100;
442 return 0;
443 }
444
445 int rewriteResponseWithoutEDNSOption(const char * packet, const size_t len, const uint16_t optionCodeToSkip, vector<uint8_t>& newContent)
446 {
447 assert(packet != NULL);
448 assert(len >= sizeof(dnsheader));
449 const struct dnsheader* dh = (const struct dnsheader*) packet;
450
451 if (ntohs(dh->arcount) == 0)
452 return ENOENT;
453
454 if (ntohs(dh->qdcount) == 0)
455 return ENOENT;
456
457 vector<uint8_t> content(len - sizeof(dnsheader));
458 copy(packet + sizeof(dnsheader), packet + len, content.begin());
459 PacketReader pr(content);
460
461 size_t idx = 0;
462 DNSName rrname;
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);
467 uint16_t rrtype;
468 uint16_t rrclass;
469 string blob;
470 struct dnsrecordheader ah;
471
472 rrname = pr.getName();
473 rrtype = pr.get16BitInt();
474 rrclass = pr.get16BitInt();
475
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;
486
487 /* consume remaining qd if any */
488 if (qdcount > 1) {
489 for(idx = 1; idx < qdcount; idx++) {
490 rrname = pr.getName();
491 rrtype = pr.get16BitInt();
492 rrclass = pr.get16BitInt();
493 (void) rrtype;
494 (void) rrclass;
495 }
496 }
497
498 /* copy AN and NS */
499 for (idx = 0; idx < ancount; idx++) {
500 rrname = pr.getName();
501 pr.getDnsrecordheader(ah);
502
503 pw.startRecord(rrname, ah.d_type, ah.d_ttl, ah.d_class, DNSResourceRecord::ANSWER, true);
504 pr.xfrBlob(blob);
505 pw.xfrBlob(blob);
506 }
507
508 for (idx = 0; idx < nscount; idx++) {
509 rrname = pr.getName();
510 pr.getDnsrecordheader(ah);
511
512 pw.startRecord(rrname, ah.d_type, ah.d_ttl, ah.d_class, DNSResourceRecord::AUTHORITY, true);
513 pr.xfrBlob(blob);
514 pw.xfrBlob(blob);
515 }
516
517 /* consume AR, looking for OPT */
518 for (idx = 0; idx < arcount; idx++) {
519 rrname = pr.getName();
520 pr.getDnsrecordheader(ah);
521
522 if (ah.d_type != QType::OPT) {
523 pw.startRecord(rrname, ah.d_type, ah.d_ttl, ah.d_class, DNSResourceRecord::ADDITIONAL, true);
524 pr.xfrBlob(blob);
525 pw.xfrBlob(blob);
526 } else {
527 pw.startRecord(rrname, ah.d_type, ah.d_ttl, ah.d_class, DNSResourceRecord::ADDITIONAL, false);
528 pr.xfrBlob(blob);
529 uint16_t rdLen = blob.length();
530 removeEDNSOptionFromOptions((unsigned char*)blob.c_str(), rdLen, optionCodeToSkip, &rdLen);
531 /* xfrBlob(string, size) completely ignores size.. */
532 if (rdLen > 0) {
533 blob.resize((size_t)rdLen);
534 pw.xfrBlob(blob);
535 } else {
536 pw.commit();
537 }
538 }
539 }
540 pw.commit();
541
542 return 0;
543 }