Coverage Report

Created: 2026-08-13 06:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pdns/pdns/dnsdistdist/dnsdist-ecs.cc
Line
Count
Source
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 "dns.hh"
23
#include "dnsname.hh"
24
#include "dolog.hh"
25
#include "dnsdist.hh"
26
#include "dnsdist-dnsparser.hh"
27
#include "dnsdist-ecs.hh"
28
#include "dnsparser.hh"
29
#include "dnswriter.hh"
30
#include "ednsoptions.hh"
31
#include "ednssubnet.hh"
32
#include "qtype.hh"
33
34
int rewriteResponseWithoutEDNS(const PacketBuffer& initialPacket, PacketBuffer& newContent)
35
0
{
36
0
  if (initialPacket.size() < sizeof(dnsheader)) {
37
0
    return ENOENT;
38
0
  }
39
40
0
  const dnsheader_aligned dnsHeader(initialPacket.data());
41
42
0
  if (ntohs(dnsHeader->arcount) == 0) {
43
0
    return ENOENT;
44
0
  }
45
46
0
  if (ntohs(dnsHeader->qdcount) == 0) {
47
0
    return ENOENT;
48
0
  }
49
50
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
51
0
  PacketReader packetReader(std::string_view(reinterpret_cast<const char*>(initialPacket.data()), initialPacket.size()));
52
53
0
  size_t idx = 0;
54
0
  uint16_t qdcount = ntohs(dnsHeader->qdcount);
55
0
  uint16_t ancount = ntohs(dnsHeader->ancount);
56
0
  uint16_t nscount = ntohs(dnsHeader->nscount);
57
0
  uint16_t arcount = ntohs(dnsHeader->arcount);
58
0
  string blob;
59
0
  dnsrecordheader recordHeader{};
60
61
0
  auto rrname = packetReader.getName();
62
0
  auto rrtype = packetReader.get16BitInt();
63
0
  auto rrclass = packetReader.get16BitInt();
64
65
0
  GenericDNSPacketWriter<PacketBuffer> packetWriter(newContent, rrname, rrtype, rrclass, dnsHeader->opcode);
66
0
  packetWriter.getHeader()->id = dnsHeader->id;
67
0
  packetWriter.getHeader()->qr = dnsHeader->qr;
68
0
  packetWriter.getHeader()->aa = dnsHeader->aa;
69
0
  packetWriter.getHeader()->tc = dnsHeader->tc;
70
0
  packetWriter.getHeader()->rd = dnsHeader->rd;
71
0
  packetWriter.getHeader()->ra = dnsHeader->ra;
72
0
  packetWriter.getHeader()->ad = dnsHeader->ad;
73
0
  packetWriter.getHeader()->cd = dnsHeader->cd;
74
0
  packetWriter.getHeader()->rcode = dnsHeader->rcode;
75
76
  /* consume remaining qd if any */
77
0
  if (qdcount > 1) {
78
0
    for (idx = 1; idx < qdcount; idx++) {
79
0
      rrname = packetReader.getName();
80
0
      rrtype = packetReader.get16BitInt();
81
0
      rrclass = packetReader.get16BitInt();
82
0
      (void)rrtype;
83
0
      (void)rrclass;
84
0
    }
85
0
  }
86
87
  /* copy AN and NS */
88
0
  for (idx = 0; idx < ancount; idx++) {
89
0
    rrname = packetReader.getName();
90
0
    packetReader.getDnsrecordheader(recordHeader);
91
92
0
    packetWriter.startRecord(rrname, recordHeader.d_type, recordHeader.d_ttl, recordHeader.d_class, DNSResourceRecord::ANSWER, true);
93
0
    packetReader.xfrBlob(blob);
94
0
    packetWriter.xfrBlob(blob);
95
0
  }
96
97
0
  for (idx = 0; idx < nscount; idx++) {
98
0
    rrname = packetReader.getName();
99
0
    packetReader.getDnsrecordheader(recordHeader);
100
101
0
    packetWriter.startRecord(rrname, recordHeader.d_type, recordHeader.d_ttl, recordHeader.d_class, DNSResourceRecord::AUTHORITY, true);
102
0
    packetReader.xfrBlob(blob);
103
0
    packetWriter.xfrBlob(blob);
104
0
  }
105
  /* consume AR, looking for OPT */
106
0
  for (idx = 0; idx < arcount; idx++) {
107
0
    rrname = packetReader.getName();
108
0
    packetReader.getDnsrecordheader(recordHeader);
109
110
0
    if (!rrname.isRoot() || recordHeader.d_type != QType::OPT) {
111
0
      packetWriter.startRecord(rrname, recordHeader.d_type, recordHeader.d_ttl, recordHeader.d_class, DNSResourceRecord::ADDITIONAL, true);
112
0
      packetReader.xfrBlob(blob);
113
0
      packetWriter.xfrBlob(blob);
114
0
    }
115
0
    else {
116
117
0
      packetReader.skip(recordHeader.d_clen);
118
0
    }
119
0
  }
120
0
  packetWriter.commit();
121
122
0
  return 0;
123
0
}
124
125
static bool addOrReplaceEDNSOption(std::vector<std::pair<uint16_t, std::string>>& options, uint16_t optionCode, bool& optionAdded, bool overrideExisting, bool allowMultiple, const string& newOptionContent)
126
0
{
127
0
  if (!allowMultiple) {
128
0
    for (auto it = options.begin(); it != options.end();) {
129
0
      if (it->first == optionCode) {
130
0
        optionAdded = false;
131
132
0
        if (!overrideExisting) {
133
0
          return false;
134
0
        }
135
136
0
        it = options.erase(it);
137
0
      }
138
0
      else {
139
0
        ++it;
140
0
      }
141
0
    }
142
0
  }
143
144
0
  if (newOptionContent.size() == EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE) {
145
0
    options.emplace_back(optionCode, "");
146
0
    return true;
147
0
  }
148
149
0
  options.emplace_back(optionCode, std::string(&newOptionContent.at(EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE), newOptionContent.size() - (EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE)));
150
0
  return true;
151
0
}
152
153
bool slowRewriteEDNSOptionInQueryWithRecords(const PacketBuffer& initialPacket, PacketBuffer& newContent, bool& ednsAdded, uint16_t optionToReplace, bool& optionAdded, bool overrideExisting, bool allowMultiple, const string& newOptionContent)
154
0
{
155
0
  if (initialPacket.size() < sizeof(dnsheader)) {
156
0
    return false;
157
0
  }
158
159
0
  const dnsheader_aligned dnsHeader(initialPacket.data());
160
161
0
  if (ntohs(dnsHeader->qdcount) == 0) {
162
0
    return false;
163
0
  }
164
165
0
  if (ntohs(dnsHeader->ancount) == 0 && ntohs(dnsHeader->nscount) == 0 && ntohs(dnsHeader->arcount) == 0) {
166
0
    throw std::runtime_error("slowRewriteEDNSOptionInQueryWithRecords should not be called for queries that have no records");
167
0
  }
168
169
0
  optionAdded = false;
170
0
  ednsAdded = true;
171
172
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
173
0
  PacketReader packetReader(std::string_view(reinterpret_cast<const char*>(initialPacket.data()), initialPacket.size()));
174
175
0
  size_t idx = 0;
176
0
  uint16_t qdcount = ntohs(dnsHeader->qdcount);
177
0
  uint16_t ancount = ntohs(dnsHeader->ancount);
178
0
  uint16_t nscount = ntohs(dnsHeader->nscount);
179
0
  uint16_t arcount = ntohs(dnsHeader->arcount);
180
0
  string blob;
181
0
  dnsrecordheader recordHeader{};
182
183
0
  auto rrname = packetReader.getName();
184
0
  auto rrtype = packetReader.get16BitInt();
185
0
  auto rrclass = packetReader.get16BitInt();
186
187
0
  GenericDNSPacketWriter<PacketBuffer> packetWriter(newContent, rrname, rrtype, rrclass, dnsHeader->opcode);
188
0
  packetWriter.getHeader()->id = dnsHeader->id;
189
0
  packetWriter.getHeader()->qr = dnsHeader->qr;
190
0
  packetWriter.getHeader()->aa = dnsHeader->aa;
191
0
  packetWriter.getHeader()->tc = dnsHeader->tc;
192
0
  packetWriter.getHeader()->rd = dnsHeader->rd;
193
0
  packetWriter.getHeader()->ra = dnsHeader->ra;
194
0
  packetWriter.getHeader()->ad = dnsHeader->ad;
195
0
  packetWriter.getHeader()->cd = dnsHeader->cd;
196
0
  packetWriter.getHeader()->rcode = dnsHeader->rcode;
197
198
  /* consume remaining qd if any */
199
0
  if (qdcount > 1) {
200
0
    for (idx = 1; idx < qdcount; idx++) {
201
0
      rrname = packetReader.getName();
202
0
      rrtype = packetReader.get16BitInt();
203
0
      rrclass = packetReader.get16BitInt();
204
0
      (void)rrtype;
205
0
      (void)rrclass;
206
0
    }
207
0
  }
208
209
  /* copy AN and NS */
210
0
  for (idx = 0; idx < ancount; idx++) {
211
0
    rrname = packetReader.getName();
212
0
    packetReader.getDnsrecordheader(recordHeader);
213
214
0
    packetWriter.startRecord(rrname, recordHeader.d_type, recordHeader.d_ttl, recordHeader.d_class, DNSResourceRecord::ANSWER, true);
215
0
    packetReader.xfrBlob(blob);
216
0
    packetWriter.xfrBlob(blob);
217
0
  }
218
219
0
  for (idx = 0; idx < nscount; idx++) {
220
0
    rrname = packetReader.getName();
221
0
    packetReader.getDnsrecordheader(recordHeader);
222
223
0
    packetWriter.startRecord(rrname, recordHeader.d_type, recordHeader.d_ttl, recordHeader.d_class, DNSResourceRecord::AUTHORITY, true);
224
0
    packetReader.xfrBlob(blob);
225
0
    packetWriter.xfrBlob(blob);
226
0
  }
227
228
  /* consume AR, looking for OPT */
229
0
  for (idx = 0; idx < arcount; idx++) {
230
0
    rrname = packetReader.getName();
231
0
    packetReader.getDnsrecordheader(recordHeader);
232
233
0
    if (!rrname.isRoot() || recordHeader.d_type != QType::OPT) {
234
0
      packetWriter.startRecord(rrname, recordHeader.d_type, recordHeader.d_ttl, recordHeader.d_class, DNSResourceRecord::ADDITIONAL, true);
235
0
      packetReader.xfrBlob(blob);
236
0
      packetWriter.xfrBlob(blob);
237
0
    }
238
0
    else {
239
240
0
      ednsAdded = false;
241
0
      packetReader.xfrBlob(blob);
242
243
0
      std::vector<std::pair<uint16_t, std::string>> options;
244
0
      getEDNSOptionsFromContent(blob, options);
245
246
      /* getDnsrecordheader() has helpfully converted the TTL for us, which we do not want in that case */
247
0
      uint32_t ttl = htonl(recordHeader.d_ttl);
248
0
      EDNS0Record edns0{};
249
0
      static_assert(sizeof(edns0) == sizeof(ttl), "sizeof(EDNS0Record) must match sizeof(uint32_t) AKA RR TTL size");
250
0
      memcpy(&edns0, &ttl, sizeof(edns0));
251
252
      /* addOrReplaceEDNSOption will set it to false if there is already an existing option */
253
0
      optionAdded = true;
254
0
      addOrReplaceEDNSOption(options, optionToReplace, optionAdded, overrideExisting, allowMultiple, newOptionContent);
255
0
      packetWriter.addOpt(recordHeader.d_class, edns0.extRCode, ntohs(edns0.extFlags), options, edns0.version);
256
0
    }
257
0
  }
258
259
0
  if (ednsAdded) {
260
0
    if (newOptionContent.size() == EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE) {
261
0
      packetWriter.addOpt(dnsdist::configuration::s_EdnsUDPPayloadSize, 0, 0, {{optionToReplace, std::string()}}, 0);
262
0
    }
263
0
    else {
264
0
      packetWriter.addOpt(dnsdist::configuration::s_EdnsUDPPayloadSize, 0, 0, {{optionToReplace, std::string(&newOptionContent.at(EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE), newOptionContent.size() - (EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE))}}, 0);
265
0
    }
266
0
    optionAdded = true;
267
0
  }
268
269
0
  packetWriter.commit();
270
271
0
  return true;
272
0
}
273
274
int locateEDNSOptRR(const PacketBuffer& packet, uint16_t* optStart, size_t* optLen, bool* last)
275
0
{
276
0
  if (optStart == nullptr || optLen == nullptr || last == nullptr) {
277
0
    throw std::runtime_error("Invalid values passed to locateEDNSOptRR");
278
0
  }
279
280
0
  if (packet.size() < sizeof(dnsheader)) {
281
0
    throw std::runtime_error("Packet passed to locateEDNSOptRR was too small");
282
0
  }
283
284
0
  const dnsheader_aligned dnsHeader(packet.data());
285
286
0
  if (ntohs(dnsHeader->arcount) == 0) {
287
0
    return ENOENT;
288
0
  }
289
290
0
  if (ntohs(dnsHeader->qdcount) != 1) {
291
0
    throw std::runtime_error("Packet passed to locateEDNSOptRR did not have QDCOUNT=1");
292
0
  }
293
294
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
295
0
  PacketReader packetReader(std::string_view(reinterpret_cast<const char*>(packet.data()), packet.size()));
296
297
0
  size_t idx = 0;
298
0
  DNSName rrname;
299
0
  uint16_t ancount = ntohs(dnsHeader->ancount);
300
0
  uint16_t nscount = ntohs(dnsHeader->nscount);
301
0
  uint16_t arcount = ntohs(dnsHeader->arcount);
302
0
  dnsrecordheader recordHeader{};
303
304
  /* consume query section */
305
0
  rrname = packetReader.getName();
306
0
  packetReader.skip(4); // Skip Type and Class
307
308
  /* consume AN and NS */
309
0
  for (idx = 0; idx < ancount + nscount; idx++) {
310
0
    rrname = packetReader.getName();
311
0
    packetReader.getDnsrecordheader(recordHeader);
312
0
    packetReader.skip(recordHeader.d_clen);
313
0
  }
314
315
  /* consume AR, looking for OPT */
316
0
  for (idx = 0; idx < arcount; idx++) {
317
0
    uint16_t start = packetReader.getPosition();
318
0
    rrname = packetReader.getName();
319
0
    packetReader.getDnsrecordheader(recordHeader);
320
321
0
    if (rrname.isRoot() && recordHeader.d_type == QType::OPT) {
322
0
      *optStart = start;
323
0
      *optLen = (packetReader.getPosition() - start) + recordHeader.d_clen;
324
325
0
      if (packet.size() < (*optStart + *optLen)) {
326
0
        throw std::range_error("Opt record overflow");
327
0
      }
328
329
0
      if (idx == ((size_t)arcount - 1)) {
330
0
        *last = true;
331
0
      }
332
0
      else {
333
0
        *last = false;
334
0
      }
335
0
      return 0;
336
0
    }
337
0
    packetReader.skip(recordHeader.d_clen);
338
0
  }
339
340
0
  return ENOENT;
341
0
}
342
343
namespace dnsdist
344
{
345
/* extract the start of the OPT RR in a QUERY packet if any
346
 * optRDPosition points to the first byte of the RDLEN field
347
 * remaining contains the number of bytes in the packet after optRDPosition (i.e. packet.size() - optRDPosition)
348
 */
349
int getEDNSOptionsStart(const PacketBuffer& packet, const size_t qnameWireLength, uint16_t* optRDPosition, size_t* remaining)
350
0
{
351
0
  if (optRDPosition == nullptr || remaining == nullptr) {
352
0
    throw std::runtime_error("Invalid values passed to getEDNSOptionsStart");
353
0
  }
354
355
0
  const dnsheader_aligned dnsHeader(packet.data());
356
357
0
  if (qnameWireLength >= packet.size()) {
358
0
    return ENOENT;
359
0
  }
360
361
0
  if (ntohs(dnsHeader->qdcount) != 1 || ntohs(dnsHeader->ancount) != 0 || ntohs(dnsHeader->arcount) != 1 || ntohs(dnsHeader->nscount) != 0) {
362
0
    return ENOENT;
363
0
  }
364
365
0
  size_t pos = sizeof(dnsheader) + qnameWireLength;
366
0
  pos += DNS_TYPE_SIZE + DNS_CLASS_SIZE;
367
368
0
  if (pos >= packet.size()) {
369
0
    return ENOENT;
370
0
  }
371
372
0
  if ((pos + /* root */ 1 + DNS_TYPE_SIZE + DNS_CLASS_SIZE) >= packet.size()) {
373
0
    return ENOENT;
374
0
  }
375
376
0
  if (packet[pos] != 0) {
377
    /* not the root so not an OPT record */
378
0
    return ENOENT;
379
0
  }
380
0
  pos += 1;
381
382
0
  uint16_t qtype = packet.at(pos) * 256 + packet.at(pos + 1);
383
0
  pos += DNS_TYPE_SIZE;
384
0
  pos += DNS_CLASS_SIZE;
385
386
0
  if (qtype != QType::OPT || (packet.size() - pos) < (DNS_TTL_SIZE + DNS_RDLENGTH_SIZE)) {
387
0
    return ENOENT;
388
0
  }
389
390
0
  pos += DNS_TTL_SIZE;
391
0
  *optRDPosition = pos;
392
0
  *remaining = packet.size() - pos;
393
394
0
  return 0;
395
0
}
396
}
397
398
void generateECSOption(const ComboAddress& source, string& res, uint16_t ECSPrefixLength)
399
0
{
400
0
  Netmask sourceNetmask(source, ECSPrefixLength);
401
0
  EDNSSubnetOpts ecsOpts;
402
0
  ecsOpts.setSource(sourceNetmask);
403
0
  string payload = ecsOpts.makeOptString();
404
0
  generateEDNSOption(EDNSOptionCode::ECS, payload, res);
405
0
}
406
407
bool generateOptRR(const std::string& optRData, PacketBuffer& res, size_t maximumSize, uint16_t udpPayloadSize, uint8_t ednsrcode, bool dnssecOK)
408
0
{
409
0
  const uint8_t name = 0;
410
0
  dnsrecordheader dnsHeader{};
411
0
  EDNS0Record edns0{};
412
0
  edns0.extRCode = ednsrcode;
413
0
  edns0.version = 0;
414
0
  edns0.extFlags = dnssecOK ? htons(EDNS_HEADER_FLAG_DO) : 0;
415
416
0
  if ((maximumSize - res.size()) < (sizeof(name) + sizeof(dnsHeader) + optRData.length())) {
417
0
    return false;
418
0
  }
419
420
0
  dnsHeader.d_type = htons(QType::OPT);
421
0
  dnsHeader.d_class = htons(udpPayloadSize);
422
0
  static_assert(sizeof(EDNS0Record) == sizeof(dnsHeader.d_ttl), "sizeof(EDNS0Record) must match sizeof(dnsrecordheader.d_ttl)");
423
0
  memcpy(&dnsHeader.d_ttl, &edns0, sizeof edns0);
424
0
  dnsHeader.d_clen = htons(static_cast<uint16_t>(optRData.length()));
425
426
0
  res.reserve(res.size() + sizeof(name) + sizeof(dnsHeader) + optRData.length());
427
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast,cppcoreguidelines-pro-bounds-pointer-arithmetic)
428
0
  res.insert(res.end(), reinterpret_cast<const uint8_t*>(&name), reinterpret_cast<const uint8_t*>(&name) + sizeof(name));
429
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast,cppcoreguidelines-pro-bounds-pointer-arithmetic)
430
0
  res.insert(res.end(), reinterpret_cast<const uint8_t*>(&dnsHeader), reinterpret_cast<const uint8_t*>(&dnsHeader) + sizeof(dnsHeader));
431
0
  res.insert(res.end(), optRData.begin(), optRData.end());
432
433
0
  return true;
434
0
}
435
436
static bool replaceEDNSClientSubnetOption(PacketBuffer& packet, size_t maximumSize, size_t const oldEcsOptionStartPosition, size_t const oldEcsOptionSize, size_t const optRDLenPosition, const string& newECSOption)
437
0
{
438
0
  if (oldEcsOptionStartPosition >= packet.size() || optRDLenPosition >= packet.size()) {
439
0
    throw std::runtime_error("Invalid values passed to replaceEDNSClientSubnetOption");
440
0
  }
441
442
0
  if (newECSOption.size() == oldEcsOptionSize) {
443
    /* same size as the existing option */
444
0
    memcpy(&packet.at(oldEcsOptionStartPosition), newECSOption.c_str(), oldEcsOptionSize);
445
0
  }
446
0
  else {
447
    /* different size than the existing option */
448
0
    const unsigned int newPacketLen = packet.size() + (newECSOption.length() - oldEcsOptionSize);
449
0
    const size_t beforeOptionLen = oldEcsOptionStartPosition;
450
0
    const size_t dataBehindSize = packet.size() - beforeOptionLen - oldEcsOptionSize;
451
452
    /* check that it fits in the existing buffer */
453
0
    if (newPacketLen > packet.size()) {
454
0
      if (newPacketLen > maximumSize) {
455
0
        return false;
456
0
      }
457
458
0
      packet.resize(newPacketLen);
459
0
    }
460
461
    /* fix the size of ECS Option RDLen */
462
0
    uint16_t newRDLen = (packet.at(optRDLenPosition) * 256) + packet.at(optRDLenPosition + 1);
463
0
    newRDLen += (newECSOption.size() - oldEcsOptionSize);
464
0
    packet.at(optRDLenPosition) = newRDLen / 256;
465
0
    packet.at(optRDLenPosition + 1) = newRDLen % 256;
466
467
0
    if (dataBehindSize > 0) {
468
0
      memmove(&packet.at(oldEcsOptionStartPosition), &packet.at(oldEcsOptionStartPosition + oldEcsOptionSize), dataBehindSize);
469
0
    }
470
0
    memcpy(&packet.at(oldEcsOptionStartPosition + dataBehindSize), newECSOption.c_str(), newECSOption.size());
471
0
    packet.resize(newPacketLen);
472
0
  }
473
474
0
  return true;
475
0
}
476
477
/* This function looks for an OPT RR, return true if a valid one was found (even if there was no options)
478
   and false otherwise. */
479
std::optional<EDNSOptionViewMap> parseEDNSOptions(const DNSQuestion& dnsQuestion)
480
0
{
481
0
  EDNSOptionViewMap ednsOptions{};
482
0
  const auto dnsHeader = dnsQuestion.getHeader();
483
0
  if (ntohs(dnsHeader->arcount) == 0) {
484
    /* nothing in additional so no EDNS */
485
0
    return std::nullopt;
486
0
  }
487
488
0
  if (ntohs(dnsHeader->ancount) != 0 || ntohs(dnsHeader->nscount) != 0 || ntohs(dnsHeader->arcount) > 1) {
489
0
    if (slowParseEDNSOptions(dnsQuestion.getData(), ednsOptions)) {
490
0
      return ednsOptions;
491
0
    }
492
0
    return std::nullopt;
493
0
  }
494
495
0
  size_t remaining = 0;
496
0
  uint16_t optRDPosition{};
497
0
  int res = dnsdist::getEDNSOptionsStart(dnsQuestion.getData(), dnsQuestion.ids.qname.wirelength(), &optRDPosition, &remaining);
498
499
0
  if (res == 0) {
500
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
501
0
    res = getEDNSOptions(reinterpret_cast<const char*>(&dnsQuestion.getData().at(optRDPosition)), remaining, ednsOptions);
502
0
    if (res != 0) {
503
0
      return std::nullopt;
504
0
    }
505
0
    return ednsOptions;
506
0
  }
507
508
0
  return std::nullopt;
509
0
}
510
511
static bool addECSToExistingOPT(PacketBuffer& packet, size_t maximumSize, const string& newECSOption, size_t optRDLenPosition, bool& ecsAdded)
512
0
{
513
  /* we need to add one EDNS0 ECS option, fixing the size of EDNS0 RDLENGTH */
514
  /* getEDNSOptionsStart has already checked that there is exactly one AR,
515
     no NS and no AN */
516
0
  uint16_t oldRDLen = (packet.at(optRDLenPosition) * 256) + packet.at(optRDLenPosition + 1);
517
0
  if (packet.size() != (optRDLenPosition + sizeof(uint16_t) + oldRDLen)) {
518
    /* we are supposed to be the last record, do we have some trailing data to remove? */
519
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
520
0
    uint32_t realPacketLen = getDNSPacketLength(reinterpret_cast<const char*>(packet.data()), packet.size());
521
0
    packet.resize(realPacketLen);
522
0
  }
523
524
0
  if ((maximumSize - packet.size()) < newECSOption.size()) {
525
0
    return false;
526
0
  }
527
528
0
  const uint32_t computedRDLen = static_cast<uint32_t>(oldRDLen) + newECSOption.size();
529
0
  if (computedRDLen > std::numeric_limits<uint16_t>::max()) {
530
0
    return false;
531
0
  }
532
0
  const auto newRDLen = static_cast<uint16_t>(computedRDLen);
533
0
  packet.at(optRDLenPosition) = newRDLen / 256;
534
0
  packet.at(optRDLenPosition + 1) = newRDLen % 256;
535
536
0
  packet.insert(packet.end(), newECSOption.begin(), newECSOption.end());
537
0
  ecsAdded = true;
538
539
0
  return true;
540
0
}
541
542
static bool addEDNSWithECS(PacketBuffer& packet, size_t maximumSize, const string& newECSOption, bool& ednsAdded, bool& ecsAdded)
543
0
{
544
0
  if (!generateOptRR(newECSOption, packet, maximumSize, dnsdist::configuration::s_EdnsUDPPayloadSize, 0, false)) {
545
0
    return false;
546
0
  }
547
548
0
  dnsdist::PacketMangling::editDNSHeaderFromPacket(packet, [](dnsheader& header) {
549
0
    uint16_t arcount = ntohs(header.arcount);
550
0
    arcount++;
551
0
    header.arcount = htons(arcount);
552
0
    return true;
553
0
  });
554
0
  ednsAdded = true;
555
0
  ecsAdded = true;
556
557
0
  return true;
558
0
}
559
560
bool handleEDNSClientSubnet(PacketBuffer& packet, const size_t maximumSize, const size_t qnameWireLength, bool& ednsAdded, bool& ecsAdded, bool overrideExisting, const string& newECSOption)
561
0
{
562
0
  if (qnameWireLength > packet.size()) {
563
0
    throw std::runtime_error("Invalid value passed to handleEDNSClientSubnet");
564
0
  }
565
566
0
  const dnsheader_aligned dnsHeader(packet.data());
567
568
0
  if (ntohs(dnsHeader->ancount) != 0 || ntohs(dnsHeader->nscount) != 0 || (ntohs(dnsHeader->arcount) != 0 && ntohs(dnsHeader->arcount) != 1)) {
569
0
    PacketBuffer newContent;
570
0
    newContent.reserve(packet.size());
571
572
0
    if (!slowRewriteEDNSOptionInQueryWithRecords(packet, newContent, ednsAdded, EDNSOptionCode::ECS, ecsAdded, overrideExisting, false, newECSOption)) {
573
0
      return false;
574
0
    }
575
576
0
    if (newContent.size() > maximumSize) {
577
0
      ednsAdded = false;
578
0
      ecsAdded = false;
579
0
      return false;
580
0
    }
581
582
0
    packet = std::move(newContent);
583
0
    return true;
584
0
  }
585
586
0
  uint16_t optRDPosition = 0;
587
0
  size_t remaining = 0;
588
589
0
  int res = dnsdist::getEDNSOptionsStart(packet, qnameWireLength, &optRDPosition, &remaining);
590
591
0
  if (res != 0) {
592
    /* no EDNS but there might be another record in additional (TSIG?) */
593
    /* Careful, this code assumes that ANCOUNT == 0 && NSCOUNT == 0 */
594
0
    size_t minimumPacketSize = sizeof(dnsheader) + qnameWireLength + sizeof(uint16_t) + sizeof(uint16_t);
595
0
    if (packet.size() > minimumPacketSize) {
596
0
      if (ntohs(dnsHeader->arcount) == 0) {
597
        /* well now.. */
598
0
        packet.resize(minimumPacketSize);
599
0
      }
600
0
      else {
601
        // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
602
0
        uint32_t realPacketLen = getDNSPacketLength(reinterpret_cast<const char*>(packet.data()), packet.size());
603
0
        packet.resize(realPacketLen);
604
0
      }
605
0
    }
606
607
0
    return addEDNSWithECS(packet, maximumSize, newECSOption, ednsAdded, ecsAdded);
608
0
  }
609
610
0
  size_t ecsOptionStartPosition = 0;
611
0
  size_t ecsOptionSize = 0;
612
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
613
0
  res = getEDNSOption(reinterpret_cast<const char*>(&packet.at(optRDPosition)), remaining, EDNSOptionCode::ECS, &ecsOptionStartPosition, &ecsOptionSize);
614
615
0
  if (res == 0) {
616
    /* there is already an ECS value */
617
0
    if (!overrideExisting) {
618
0
      return true;
619
0
    }
620
621
0
    return replaceEDNSClientSubnetOption(packet, maximumSize, optRDPosition + ecsOptionStartPosition, ecsOptionSize, optRDPosition, newECSOption);
622
0
  }
623
624
0
  if (res != ENOENT) {
625
    /* something is wrong */
626
0
    return false;
627
0
  }
628
629
  /* we have an EDNS OPT RR but no existing ECS option */
630
0
  return addECSToExistingOPT(packet, maximumSize, newECSOption, optRDPosition, ecsAdded);
631
0
}
632
633
bool handleEDNSClientSubnet(DNSQuestion& dnsQuestion, bool& ednsAdded, bool& ecsAdded)
634
0
{
635
0
  string newECSOption;
636
0
  generateECSOption(dnsQuestion.ecs ? dnsQuestion.ecs->getNetwork() : dnsQuestion.ids.origRemote, newECSOption, dnsQuestion.ecs ? dnsQuestion.ecs->getBits() : dnsQuestion.ecsPrefixLength);
637
638
0
  return handleEDNSClientSubnet(dnsQuestion.getMutableData(), dnsQuestion.getMaximumSize(), dnsQuestion.ids.qname.wirelength(), ednsAdded, ecsAdded, dnsQuestion.ecsOverride, newECSOption);
639
0
}
640
641
static int removeEDNSOptionFromOptions(unsigned char* optionsStart, const uint16_t optionsLen, const uint16_t optionCodeToRemove, uint16_t* newOptionsLen)
642
0
{
643
0
  const pdns::views::UnsignedCharView view(optionsStart, optionsLen);
644
0
  size_t pos = 0;
645
0
  while ((pos + 4) <= view.size()) {
646
0
    size_t optionBeginPos = pos;
647
0
    const uint16_t optionCode = 0x100 * view.at(pos) + view.at(pos + 1);
648
0
    pos += sizeof(optionCode);
649
0
    const uint16_t optionLen = 0x100 * view.at(pos) + view.at(pos + 1);
650
0
    pos += sizeof(optionLen);
651
0
    if ((pos + optionLen) > view.size()) {
652
0
      return EINVAL;
653
0
    }
654
0
    if (optionCode == optionCodeToRemove) {
655
0
      if (pos + optionLen < view.size()) {
656
        /* move remaining options over the removed one,
657
           if any */
658
        // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
659
0
        memmove(optionsStart + optionBeginPos, optionsStart + pos + optionLen, optionsLen - (pos + optionLen));
660
0
      }
661
0
      *newOptionsLen = optionsLen - (sizeof(optionCode) + sizeof(optionLen) + optionLen);
662
0
      return 0;
663
0
    }
664
0
    pos += optionLen;
665
0
  }
666
0
  return ENOENT;
667
0
}
668
669
int removeEDNSOptionFromOPT(char* optStart, size_t* optLen, const uint16_t optionCodeToRemove)
670
0
{
671
0
  if (*optLen < optRecordMinimumSize) {
672
0
    return EINVAL;
673
0
  }
674
0
  const pdns::views::UnsignedCharView view(optStart, *optLen);
675
  /* skip the root label, qtype, qclass and TTL */
676
0
  size_t position = 9;
677
0
  uint16_t rdLen = (0x100 * view.at(position) + view.at(position + 1));
678
0
  position += sizeof(rdLen);
679
0
  if (position + rdLen != view.size()) {
680
0
    return EINVAL;
681
0
  }
682
0
  uint16_t newRdLen = 0;
683
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast,cppcoreguidelines-pro-bounds-pointer-arithmetic)
684
0
  int res = removeEDNSOptionFromOptions(reinterpret_cast<unsigned char*>(optStart + position), rdLen, optionCodeToRemove, &newRdLen);
685
0
  if (res != 0) {
686
0
    return res;
687
0
  }
688
0
  *optLen -= (rdLen - newRdLen);
689
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast,cppcoreguidelines-pro-bounds-pointer-arithmetic)
690
0
  auto* rdLenPtr = reinterpret_cast<unsigned char*>(optStart + 9);
691
  // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
692
0
  rdLenPtr[0] = newRdLen / 0x100;
693
  // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
694
0
  rdLenPtr[1] = newRdLen % 0x100;
695
0
  return 0;
696
0
}
697
698
bool isEDNSOptionInOpt(const PacketBuffer& packet, const size_t optStart, const size_t optLen, const uint16_t optionCodeToFind, size_t* optContentStart, uint16_t* optContentLen)
699
0
{
700
0
  if (optLen < optRecordMinimumSize) {
701
0
    return false;
702
0
  }
703
0
  size_t position = optStart + 9;
704
0
  uint16_t rdLen = (0x100 * static_cast<unsigned char>(packet.at(position)) + static_cast<unsigned char>(packet.at(position + 1)));
705
0
  position += sizeof(rdLen);
706
0
  if (rdLen > (optLen - optRecordMinimumSize)) {
707
0
    return false;
708
0
  }
709
710
0
  size_t rdEnd = position + rdLen;
711
0
  while ((position + 4) <= rdEnd) {
712
0
    const uint16_t optionCode = 0x100 * static_cast<unsigned char>(packet.at(position)) + static_cast<unsigned char>(packet.at(position + 1));
713
0
    position += sizeof(optionCode);
714
0
    const uint16_t optionLen = 0x100 * static_cast<unsigned char>(packet.at(position)) + static_cast<unsigned char>(packet.at(position + 1));
715
0
    position += sizeof(optionLen);
716
717
0
    if ((position + optionLen) > rdEnd) {
718
0
      return false;
719
0
    }
720
721
0
    if (optionCode == optionCodeToFind) {
722
0
      if (optContentStart != nullptr) {
723
0
        *optContentStart = position;
724
0
      }
725
726
0
      if (optContentLen != nullptr) {
727
0
        *optContentLen = optionLen;
728
0
      }
729
730
0
      return true;
731
0
    }
732
0
    position += optionLen;
733
0
  }
734
0
  return false;
735
0
}
736
737
int rewriteResponseWithoutEDNSOption(const PacketBuffer& initialPacket, const uint16_t optionCodeToSkip, PacketBuffer& newContent)
738
0
{
739
0
  if (initialPacket.size() < sizeof(dnsheader)) {
740
0
    return ENOENT;
741
0
  }
742
743
0
  const dnsheader_aligned dnsHeader(initialPacket.data());
744
745
0
  if (ntohs(dnsHeader->arcount) == 0) {
746
0
    return ENOENT;
747
0
  }
748
749
0
  if (ntohs(dnsHeader->qdcount) == 0) {
750
0
    return ENOENT;
751
0
  }
752
753
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
754
0
  PacketReader packetReader(std::string_view(reinterpret_cast<const char*>(initialPacket.data()), initialPacket.size()));
755
756
0
  size_t idx = 0;
757
0
  DNSName rrname;
758
0
  uint16_t qdcount = ntohs(dnsHeader->qdcount);
759
0
  uint16_t ancount = ntohs(dnsHeader->ancount);
760
0
  uint16_t nscount = ntohs(dnsHeader->nscount);
761
0
  uint16_t arcount = ntohs(dnsHeader->arcount);
762
0
  uint16_t rrtype = 0;
763
0
  uint16_t rrclass = 0;
764
0
  string blob;
765
0
  dnsrecordheader recordHeader{};
766
767
0
  rrname = packetReader.getName();
768
0
  rrtype = packetReader.get16BitInt();
769
0
  rrclass = packetReader.get16BitInt();
770
771
0
  GenericDNSPacketWriter<PacketBuffer> packetWriter(newContent, rrname, rrtype, rrclass, dnsHeader->opcode);
772
0
  packetWriter.getHeader()->id = dnsHeader->id;
773
0
  packetWriter.getHeader()->qr = dnsHeader->qr;
774
0
  packetWriter.getHeader()->aa = dnsHeader->aa;
775
0
  packetWriter.getHeader()->tc = dnsHeader->tc;
776
0
  packetWriter.getHeader()->rd = dnsHeader->rd;
777
0
  packetWriter.getHeader()->ra = dnsHeader->ra;
778
0
  packetWriter.getHeader()->ad = dnsHeader->ad;
779
0
  packetWriter.getHeader()->cd = dnsHeader->cd;
780
0
  packetWriter.getHeader()->rcode = dnsHeader->rcode;
781
782
  /* consume remaining qd if any */
783
0
  if (qdcount > 1) {
784
0
    for (idx = 1; idx < qdcount; idx++) {
785
0
      rrname = packetReader.getName();
786
0
      rrtype = packetReader.get16BitInt();
787
0
      rrclass = packetReader.get16BitInt();
788
0
      (void)rrtype;
789
0
      (void)rrclass;
790
0
    }
791
0
  }
792
793
  /* copy AN and NS */
794
0
  for (idx = 0; idx < ancount; idx++) {
795
0
    rrname = packetReader.getName();
796
0
    packetReader.getDnsrecordheader(recordHeader);
797
798
0
    packetWriter.startRecord(rrname, recordHeader.d_type, recordHeader.d_ttl, recordHeader.d_class, DNSResourceRecord::ANSWER, true);
799
0
    packetReader.xfrBlob(blob);
800
0
    packetWriter.xfrBlob(blob);
801
0
  }
802
803
0
  for (idx = 0; idx < nscount; idx++) {
804
0
    rrname = packetReader.getName();
805
0
    packetReader.getDnsrecordheader(recordHeader);
806
807
0
    packetWriter.startRecord(rrname, recordHeader.d_type, recordHeader.d_ttl, recordHeader.d_class, DNSResourceRecord::AUTHORITY, true);
808
0
    packetReader.xfrBlob(blob);
809
0
    packetWriter.xfrBlob(blob);
810
0
  }
811
812
  /* consume AR, looking for OPT */
813
0
  for (idx = 0; idx < arcount; idx++) {
814
0
    rrname = packetReader.getName();
815
0
    packetReader.getDnsrecordheader(recordHeader);
816
817
0
    if (!rrname.isRoot() || recordHeader.d_type != QType::OPT) {
818
0
      packetWriter.startRecord(rrname, recordHeader.d_type, recordHeader.d_ttl, recordHeader.d_class, DNSResourceRecord::ADDITIONAL, true);
819
0
      packetReader.xfrBlob(blob);
820
0
      packetWriter.xfrBlob(blob);
821
0
    }
822
0
    else {
823
0
      packetWriter.startRecord(rrname, recordHeader.d_type, recordHeader.d_ttl, recordHeader.d_class, DNSResourceRecord::ADDITIONAL, false);
824
0
      packetReader.xfrBlob(blob);
825
0
      uint16_t rdLen = blob.length();
826
      // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
827
0
      removeEDNSOptionFromOptions(reinterpret_cast<unsigned char*>(blob.data()), rdLen, optionCodeToSkip, &rdLen);
828
      /* xfrBlob(string, size) completely ignores size.. */
829
0
      if (rdLen > 0) {
830
0
        blob.resize((size_t)rdLen);
831
0
        packetWriter.xfrBlob(blob);
832
0
      }
833
0
      else {
834
0
        packetWriter.commit();
835
0
      }
836
0
    }
837
0
  }
838
0
  packetWriter.commit();
839
840
0
  return 0;
841
0
}
842
843
bool addEDNS(PacketBuffer& packet, size_t maximumSize, bool dnssecOK, uint16_t payloadSize, uint8_t ednsrcode)
844
0
{
845
0
  if (!generateOptRR(std::string(), packet, maximumSize, payloadSize, ednsrcode, dnssecOK)) {
846
0
    return false;
847
0
  }
848
849
0
  dnsdist::PacketMangling::editDNSHeaderFromPacket(packet, [](dnsheader& header) {
850
0
    header.arcount = htons(ntohs(header.arcount) + 1);
851
0
    return true;
852
0
  });
853
854
0
  return true;
855
0
}
856
857
/*
858
  This function keeps the existing header and DNSSECOK bit (if any) but wipes anything else,
859
  generating a NXD or NODATA answer with a SOA record in the additional section (or optionally the authority section for a full cacheable NXDOMAIN/NODATA).
860
*/
861
bool setNegativeAndAdditionalSOA(DNSQuestion& dnsQuestion, bool nxd, const DNSName& zone, uint32_t ttl, const DNSName& mname, const DNSName& rname, uint32_t serial, uint32_t refresh, uint32_t retry, uint32_t expire, uint32_t minimum, bool soaInAuthoritySection)
862
0
{
863
0
  auto& packet = dnsQuestion.getMutableData();
864
0
  auto dnsHeader = dnsQuestion.getHeader();
865
0
  if (ntohs(dnsHeader->qdcount) != 1) {
866
0
    return false;
867
0
  }
868
869
0
  size_t queryPartSize = sizeof(dnsheader) + dnsQuestion.ids.qname.wirelength() + DNS_TYPE_SIZE + DNS_CLASS_SIZE;
870
0
  if (packet.size() < queryPartSize) {
871
    /* something is already wrong, don't build on flawed foundations */
872
0
    return false;
873
0
  }
874
875
0
  uint16_t qtype = htons(QType::SOA);
876
0
  uint16_t qclass = htons(QClass::IN);
877
0
  uint16_t rdLength = mname.wirelength() + rname.wirelength() + sizeof(serial) + sizeof(refresh) + sizeof(retry) + sizeof(expire) + sizeof(minimum);
878
0
  size_t soaSize = zone.wirelength() + sizeof(qtype) + sizeof(qclass) + sizeof(ttl) + sizeof(rdLength) + rdLength;
879
0
  bool hadEDNS = false;
880
0
  bool dnssecOK = false;
881
882
0
  if (dnsdist::configuration::getCurrentRuntimeConfiguration().d_addEDNSToSelfGeneratedResponses) {
883
0
    uint16_t payloadSize = 0;
884
0
    uint16_t zValue = 0;
885
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
886
0
    hadEDNS = getEDNSUDPPayloadSizeAndZ(reinterpret_cast<const char*>(packet.data()), packet.size(), &payloadSize, &zValue);
887
0
    if (hadEDNS) {
888
0
      dnssecOK = (zValue & EDNS_HEADER_FLAG_DO) != 0;
889
0
    }
890
0
  }
891
892
  /* chop off everything after the question */
893
0
  packet.resize(queryPartSize);
894
0
  dnsdist::PacketMangling::editDNSHeaderFromPacket(packet, [nxd](dnsheader& header) {
895
0
    if (nxd) {
896
0
      header.rcode = RCode::NXDomain;
897
0
    }
898
0
    else {
899
0
      header.rcode = RCode::NoError;
900
0
    }
901
0
    header.qr = true;
902
0
    header.ancount = 0;
903
0
    header.nscount = 0;
904
0
    header.arcount = 0;
905
0
    return true;
906
0
  });
907
908
0
  rdLength = htons(rdLength);
909
0
  ttl = htonl(ttl);
910
0
  serial = htonl(serial);
911
0
  refresh = htonl(refresh);
912
0
  retry = htonl(retry);
913
0
  expire = htonl(expire);
914
0
  minimum = htonl(minimum);
915
916
0
  std::string soa;
917
0
  soa.reserve(soaSize);
918
0
  soa.append(zone.toDNSString());
919
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
920
0
  soa.append(reinterpret_cast<const char*>(&qtype), sizeof(qtype));
921
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
922
0
  soa.append(reinterpret_cast<const char*>(&qclass), sizeof(qclass));
923
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
924
0
  soa.append(reinterpret_cast<const char*>(&ttl), sizeof(ttl));
925
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
926
0
  soa.append(reinterpret_cast<const char*>(&rdLength), sizeof(rdLength));
927
0
  soa.append(mname.toDNSString());
928
0
  soa.append(rname.toDNSString());
929
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
930
0
  soa.append(reinterpret_cast<const char*>(&serial), sizeof(serial));
931
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
932
0
  soa.append(reinterpret_cast<const char*>(&refresh), sizeof(refresh));
933
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
934
0
  soa.append(reinterpret_cast<const char*>(&retry), sizeof(retry));
935
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
936
0
  soa.append(reinterpret_cast<const char*>(&expire), sizeof(expire));
937
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
938
0
  soa.append(reinterpret_cast<const char*>(&minimum), sizeof(minimum));
939
940
0
  if (soa.size() != soaSize) {
941
0
    throw std::runtime_error("Unexpected SOA response size: " + std::to_string(soa.size()) + " vs " + std::to_string(soaSize));
942
0
  }
943
944
0
  packet.insert(packet.end(), soa.begin(), soa.end());
945
946
  /* We are populating a response with only the query in place, order of sections is QD,AN,NS,AR
947
     NS (authority) is before AR (additional) so we can just decide which section the SOA record is in here
948
     and have EDNS added to AR afterwards */
949
0
  dnsdist::PacketMangling::editDNSHeaderFromPacket(packet, [soaInAuthoritySection](dnsheader& header) {
950
0
    if (soaInAuthoritySection) {
951
0
      header.nscount = htons(1);
952
0
    }
953
0
    else {
954
0
      header.arcount = htons(1);
955
0
    }
956
0
    return true;
957
0
  });
958
959
0
  if (hadEDNS) {
960
    /* now we need to add a new OPT record */
961
0
    return addEDNS(packet, dnsQuestion.getMaximumSize(), dnssecOK, dnsdist::configuration::getCurrentRuntimeConfiguration().d_payloadSizeSelfGenAnswers, dnsQuestion.ednsRCode);
962
0
  }
963
964
0
  return true;
965
0
}
966
967
bool addEDNSToQueryTurnedResponse(DNSQuestion& dnsQuestion)
968
0
{
969
0
  uint16_t optRDPosition{};
970
  /* remaining is at least the size of the rdlen + the options if any + the following records if any */
971
0
  size_t remaining = 0;
972
973
0
  auto& packet = dnsQuestion.getMutableData();
974
0
  int res = dnsdist::getEDNSOptionsStart(packet, dnsQuestion.ids.qname.wirelength(), &optRDPosition, &remaining);
975
976
0
  if (res != 0) {
977
    /* if the initial query did not have EDNS0, we are done */
978
0
    return true;
979
0
  }
980
981
0
  const size_t existingOptLen = /* root */ 1 + DNS_TYPE_SIZE + DNS_CLASS_SIZE + EDNS_EXTENDED_RCODE_SIZE + EDNS_VERSION_SIZE + /* Z */ 2 + remaining;
982
0
  if (existingOptLen >= packet.size()) {
983
    /* something is wrong, bail out */
984
0
    return false;
985
0
  }
986
987
0
  const size_t optPosition = (optRDPosition - (/* root */ 1 + DNS_TYPE_SIZE + DNS_CLASS_SIZE + EDNS_EXTENDED_RCODE_SIZE + EDNS_VERSION_SIZE + /* Z */ 2));
988
989
0
  size_t zPosition = optPosition + /* root */ 1 + DNS_TYPE_SIZE + DNS_CLASS_SIZE + EDNS_EXTENDED_RCODE_SIZE + EDNS_VERSION_SIZE;
990
0
  uint16_t zValue = 0x100 * packet.at(zPosition) + packet.at(zPosition + 1);
991
0
  bool dnssecOK = (zValue & EDNS_HEADER_FLAG_DO) != 0;
992
993
  /* remove the existing OPT record, and everything else that follows (any SIG or TSIG would be useless anyway) */
994
0
  packet.resize(packet.size() - existingOptLen);
995
0
  dnsdist::PacketMangling::editDNSHeaderFromPacket(packet, [](dnsheader& header) {
996
0
    header.arcount = 0;
997
0
    return true;
998
0
  });
999
1000
0
  if (dnsdist::configuration::getCurrentRuntimeConfiguration().d_addEDNSToSelfGeneratedResponses) {
1001
    /* now we need to add a new OPT record */
1002
0
    return addEDNS(packet, dnsQuestion.getMaximumSize(), dnssecOK, dnsdist::configuration::getCurrentRuntimeConfiguration().d_payloadSizeSelfGenAnswers, dnsQuestion.ednsRCode);
1003
0
  }
1004
1005
  /* otherwise we are just fine */
1006
0
  return true;
1007
0
}
1008
1009
namespace dnsdist
1010
{
1011
static std::optional<size_t> getEDNSRecordPosition(const DNSQuestion& dnsQuestion)
1012
0
{
1013
0
  try {
1014
0
    const auto& packet = dnsQuestion.getData();
1015
0
    if (packet.size() <= sizeof(dnsheader)) {
1016
0
      return std::nullopt;
1017
0
    }
1018
1019
0
    uint16_t optRDPosition = 0;
1020
0
    size_t remaining = 0;
1021
0
    auto res = getEDNSOptionsStart(packet, dnsQuestion.ids.qname.wirelength(), &optRDPosition, &remaining);
1022
0
    if (res != 0) {
1023
0
      return std::nullopt;
1024
0
    }
1025
1026
0
    if (optRDPosition < DNS_TTL_SIZE) {
1027
0
      return std::nullopt;
1028
0
    }
1029
1030
0
    return optRDPosition - DNS_TTL_SIZE;
1031
0
  }
1032
0
  catch (...) {
1033
0
    return std::nullopt;
1034
0
  }
1035
0
}
1036
1037
// goal in life - if you send us a reasonably normal packet, we'll get Z for you, otherwise 0
1038
int getEDNSZ(const DNSQuestion& dnsQuestion)
1039
0
{
1040
0
  try {
1041
0
    auto position = getEDNSRecordPosition(dnsQuestion);
1042
1043
0
    if (!position) {
1044
0
      return 0;
1045
0
    }
1046
1047
0
    const auto& packet = dnsQuestion.getData();
1048
0
    if ((*position + EDNS_EXTENDED_RCODE_SIZE + EDNS_VERSION_SIZE + 1) >= packet.size()) {
1049
0
      return 0;
1050
0
    }
1051
1052
0
    return 0x100 * packet.at(*position + EDNS_EXTENDED_RCODE_SIZE + EDNS_VERSION_SIZE) + packet.at(*position + EDNS_EXTENDED_RCODE_SIZE + EDNS_VERSION_SIZE + 1);
1053
0
  }
1054
0
  catch (...) {
1055
0
    return 0;
1056
0
  }
1057
0
}
1058
1059
std::optional<uint8_t> getEDNSVersion(const DNSQuestion& dnsQuestion)
1060
0
{
1061
0
  try {
1062
0
    auto position = getEDNSRecordPosition(dnsQuestion);
1063
1064
0
    if (!position) {
1065
0
      return std::nullopt;
1066
0
    }
1067
1068
0
    const auto& packet = dnsQuestion.getData();
1069
0
    if ((*position + EDNS_EXTENDED_RCODE_SIZE + EDNS_VERSION_SIZE) >= packet.size()) {
1070
0
      return std::nullopt;
1071
0
    }
1072
1073
0
    return packet.at(*position + EDNS_EXTENDED_RCODE_SIZE);
1074
0
  }
1075
0
  catch (...) {
1076
0
    return std::nullopt;
1077
0
  }
1078
0
}
1079
1080
std::optional<uint8_t> getEDNSExtendedRCode(const DNSQuestion& dnsQuestion)
1081
0
{
1082
0
  try {
1083
0
    auto position = getEDNSRecordPosition(dnsQuestion);
1084
1085
0
    if (!position) {
1086
0
      return std::nullopt;
1087
0
    }
1088
1089
0
    const auto& packet = dnsQuestion.getData();
1090
0
    if ((*position + EDNS_EXTENDED_RCODE_SIZE) >= packet.size()) {
1091
0
      return std::nullopt;
1092
0
    }
1093
1094
0
    return packet.at(*position);
1095
0
  }
1096
0
  catch (...) {
1097
0
    return std::nullopt;
1098
0
  }
1099
0
}
1100
1101
}
1102
1103
bool queryHasEDNS(const DNSQuestion& dnsQuestion)
1104
0
{
1105
0
  uint16_t optRDPosition = 0;
1106
0
  size_t ecsRemaining = 0;
1107
1108
0
  int res = dnsdist::getEDNSOptionsStart(dnsQuestion.getData(), dnsQuestion.ids.qname.wirelength(), &optRDPosition, &ecsRemaining);
1109
0
  return res == 0;
1110
0
}
1111
1112
bool getEDNS0Record(const PacketBuffer& packet, EDNS0Record& edns0)
1113
0
{
1114
0
  uint16_t optStart = 0;
1115
0
  size_t optLen = 0;
1116
0
  bool last = false;
1117
0
  int res = locateEDNSOptRR(packet, &optStart, &optLen, &last);
1118
0
  if (res != 0) {
1119
    // no EDNS OPT RR
1120
0
    return false;
1121
0
  }
1122
1123
0
  if (optLen < optRecordMinimumSize) {
1124
0
    return false;
1125
0
  }
1126
1127
0
  if (optStart < packet.size() && packet.at(optStart) != 0) {
1128
    // OPT RR Name != '.'
1129
0
    return false;
1130
0
  }
1131
1132
0
  static_assert(sizeof(EDNS0Record) == sizeof(uint32_t), "sizeof(EDNS0Record) must match sizeof(uint32_t) AKA RR TTL size");
1133
  // copy out 4-byte "ttl" (really the EDNS0 record), after root label (1) + type (2) + class (2).
1134
0
  memcpy(&edns0, &packet.at(optStart + 5), sizeof edns0);
1135
0
  return true;
1136
0
}
1137
1138
bool setEDNSOption(PacketBuffer& buf, uint16_t ednsCode, const std::string& ednsData, size_t maximumSize, bool& ednsAdded, bool& optionAdded)
1139
0
{
1140
0
  if (buf.size() < sizeof(dnsheader)) {
1141
0
    return false;
1142
0
  }
1143
0
  std::string optRData;
1144
0
  generateEDNSOption(ednsCode, ednsData, optRData);
1145
1146
0
  const dnsheader_aligned dnsHeader(buf.data());
1147
0
  if (dnsHeader->arcount != 0) {
1148
0
    ednsAdded = false;
1149
0
    optionAdded = false;
1150
0
    PacketBuffer newContent;
1151
0
    newContent.reserve(buf.size());
1152
1153
0
    if (!slowRewriteEDNSOptionInQueryWithRecords(buf, newContent, ednsAdded, ednsCode, optionAdded, true, false, optRData)) {
1154
0
      return false;
1155
0
    }
1156
1157
0
    if (newContent.size() > maximumSize) {
1158
0
      return false;
1159
0
    }
1160
1161
0
    buf = std::move(newContent);
1162
0
    return true;
1163
0
  }
1164
1165
0
  if (!generateOptRR(optRData, buf, maximumSize, dnsdist::configuration::s_EdnsUDPPayloadSize, 0, false)) {
1166
0
    return false;
1167
0
  }
1168
1169
0
  dnsdist::PacketMangling::editDNSHeaderFromPacket(buf, [](dnsheader& header) {
1170
0
    header.arcount = htons(1);
1171
0
    return true;
1172
0
  });
1173
0
  ednsAdded = true;
1174
1175
0
  return true;
1176
0
}
1177
1178
bool setEDNSOption(DNSQuestion& dnsQuestion, uint16_t ednsCode, const std::string& ednsData, bool isQuery)
1179
0
{
1180
0
  bool ednsAdded = false;
1181
0
  bool optionAdded = false;
1182
0
  auto ret = setEDNSOption(dnsQuestion.getMutableData(), ednsCode, ednsData, dnsQuestion.getMaximumSize(), ednsAdded, optionAdded);
1183
0
  if (!ret) {
1184
0
    return ret;
1185
0
  }
1186
1187
0
  if (isQuery && !dnsQuestion.ids.ednsAdded && ednsAdded) {
1188
0
    dnsQuestion.ids.ednsAdded = true;
1189
0
  }
1190
1191
0
  return true;
1192
0
}
1193
1194
namespace dnsdist
1195
{
1196
bool setInternalQueryRCode(InternalQueryState& state, PacketBuffer& buffer, uint8_t rcode, bool clearAnswers)
1197
0
{
1198
0
  const auto qnameLength = state.qname.wirelength();
1199
0
  if (buffer.size() < sizeof(dnsheader) + qnameLength + sizeof(uint16_t) + sizeof(uint16_t)) {
1200
0
    return false;
1201
0
  }
1202
1203
0
  EDNS0Record edns0{};
1204
0
  bool hadEDNS = false;
1205
0
  if (clearAnswers) {
1206
0
    hadEDNS = getEDNS0Record(buffer, edns0);
1207
0
  }
1208
1209
0
  dnsdist::PacketMangling::editDNSHeaderFromPacket(buffer, [rcode, clearAnswers](dnsheader& header) {
1210
0
    header.rcode = rcode;
1211
0
    header.ad = false;
1212
0
    header.aa = false;
1213
0
    header.ra = header.rd;
1214
0
    header.qr = true;
1215
1216
0
    if (clearAnswers) {
1217
0
      header.ancount = 0;
1218
0
      header.nscount = 0;
1219
0
      header.arcount = 0;
1220
0
    }
1221
0
    return true;
1222
0
  });
1223
1224
0
  if (clearAnswers) {
1225
0
    buffer.resize(sizeof(dnsheader) + qnameLength + sizeof(uint16_t) + sizeof(uint16_t));
1226
0
    if (hadEDNS) {
1227
0
      DNSQuestion dnsQuestion(state, buffer);
1228
0
      if (!addEDNS(buffer, dnsQuestion.getMaximumSize(), (edns0.extFlags & htons(EDNS_HEADER_FLAG_DO)) != 0, dnsdist::configuration::getCurrentRuntimeConfiguration().d_payloadSizeSelfGenAnswers, 0)) {
1229
0
        return false;
1230
0
      }
1231
0
    }
1232
0
  }
1233
1234
0
  return true;
1235
0
}
1236
}