Coverage Report

Created: 2024-04-25 06:25

/src/pdns/pdns/dnsdistdist/dnsdist-dnsparser.cc
Line
Count
Source (jump to first uncovered line)
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 "dnsdist-dnsparser.hh"
23
#include "dnsparser.hh"
24
25
namespace dnsdist
26
{
27
DNSPacketOverlay::DNSPacketOverlay(const std::string_view& packet)
28
0
{
29
0
  if (packet.size() < sizeof(dnsheader)) {
30
0
    throw std::runtime_error("Packet is too small for a DNS packet");
31
0
  }
32
33
0
  memcpy(&d_header, packet.data(), sizeof(dnsheader));
34
0
  uint64_t numRecords = ntohs(d_header.ancount) + ntohs(d_header.nscount) + ntohs(d_header.arcount);
35
0
  d_records.reserve(numRecords);
36
37
0
  try {
38
0
    PacketReader reader(std::string_view(reinterpret_cast<const char*>(packet.data()), packet.size()));
39
40
0
    for (uint16_t n = 0; n < ntohs(d_header.qdcount); ++n) {
41
0
      reader.xfrName(d_qname);
42
0
      reader.xfrType(d_qtype);
43
0
      reader.xfrType(d_qclass);
44
0
    }
45
46
0
    for (uint64_t n = 0; n < numRecords; ++n) {
47
0
      Record rec;
48
0
      reader.xfrName(rec.d_name);
49
0
      rec.d_place = n < ntohs(d_header.ancount) ? DNSResourceRecord::ANSWER : (n < (ntohs(d_header.ancount) + ntohs(d_header.nscount)) ? DNSResourceRecord::AUTHORITY : DNSResourceRecord::ADDITIONAL);
50
0
      reader.xfrType(rec.d_type);
51
0
      reader.xfrType(rec.d_class);
52
0
      reader.xfr32BitInt(rec.d_ttl);
53
0
      reader.xfr16BitInt(rec.d_contentLength);
54
0
      rec.d_contentOffset = reader.getPosition();
55
0
      reader.skip(rec.d_contentLength);
56
0
      d_records.push_back(std::move(rec));
57
0
    }
58
0
  }
59
0
  catch (const std::exception& e) {
60
0
    throw std::runtime_error("Unable to parse DNS packet: " + std::string(e.what()));
61
0
  }
62
0
  catch (...) {
63
0
    throw std::runtime_error("Unable to parse DNS packet");
64
0
  }
65
0
}
66
67
bool changeNameInDNSPacket(PacketBuffer& initialPacket, const DNSName& from, const DNSName& to)
68
0
{
69
0
  if (initialPacket.size() < sizeof(dnsheader)) {
70
0
    return false;
71
0
  }
72
73
0
  PacketReader pr(std::string_view(reinterpret_cast<const char*>(initialPacket.data()), initialPacket.size()));
74
75
0
  dnsheader dh;
76
0
  memcpy(&dh, initialPacket.data(), sizeof(dh));
77
0
  size_t idx = 0;
78
0
  DNSName rrname;
79
0
  uint16_t qdcount = ntohs(dh.qdcount);
80
0
  uint16_t ancount = ntohs(dh.ancount);
81
0
  uint16_t nscount = ntohs(dh.nscount);
82
0
  uint16_t arcount = ntohs(dh.arcount);
83
0
  uint16_t rrtype;
84
0
  uint16_t rrclass;
85
0
  string blob;
86
87
0
  size_t recordsCount = ancount + nscount + arcount;
88
0
  struct dnsrecordheader ah;
89
90
0
  rrname = pr.getName();
91
0
  if (rrname == from) {
92
0
    rrname = to;
93
0
  }
94
95
0
  rrtype = pr.get16BitInt();
96
0
  rrclass = pr.get16BitInt();
97
98
0
  PacketBuffer newContent;
99
0
  newContent.reserve(initialPacket.size());
100
0
  GenericDNSPacketWriter<PacketBuffer> pw(newContent, rrname, rrtype, rrclass, dh.opcode);
101
  /* we want to copy the flags and ID but not the counts since we recreate the records below */
102
0
  pw.getHeader()->id = dh.id;
103
0
  pw.getHeader()->qr = dh.qr;
104
0
  pw.getHeader()->aa = dh.aa;
105
0
  pw.getHeader()->tc = dh.tc;
106
0
  pw.getHeader()->rd = dh.rd;
107
0
  pw.getHeader()->ra = dh.ra;
108
0
  pw.getHeader()->ad = dh.ad;
109
0
  pw.getHeader()->cd = dh.cd;
110
0
  pw.getHeader()->rcode = dh.rcode;
111
112
  /* consume remaining qd if any, but do not copy it */
113
0
  for (idx = 1; idx < qdcount; idx++) {
114
0
    rrname = pr.getName();
115
0
    (void)pr.get16BitInt();
116
0
    (void)pr.get16BitInt();
117
0
  }
118
119
0
  static const std::unordered_set<QType> nameOnlyTypes{QType::NS, QType::PTR, QType::CNAME, QType::DNAME};
120
0
  static const std::unordered_set<QType> noNameTypes{QType::A, QType::AAAA, QType::DHCID, QType::TXT, QType::OPT, QType::HINFO, QType::DNSKEY, QType::CDNSKEY, QType::DS, QType::CDS, QType::DLV, QType::SSHFP, QType::KEY, QType::CERT, QType::TLSA, QType::SMIMEA, QType::OPENPGPKEY, QType::NSEC, QType::NSEC3, QType::CSYNC, QType::NSEC3PARAM, QType::LOC, QType::NID, QType::L32, QType::L64, QType::EUI48, QType::EUI64, QType::URI, QType::CAA};
121
122
  /* copy AN, NS and AR */
123
0
  for (idx = 0; idx < recordsCount; idx++) {
124
0
    rrname = pr.getName();
125
0
    if (rrname == from) {
126
0
      rrname = to;
127
0
    }
128
0
    pr.getDnsrecordheader(ah);
129
130
0
    auto place = idx < ancount ? DNSResourceRecord::ANSWER : (idx < (ancount + nscount) ? DNSResourceRecord::AUTHORITY : DNSResourceRecord::ADDITIONAL);
131
0
    pw.startRecord(rrname, ah.d_type, ah.d_ttl, ah.d_class, place, true);
132
0
    if (nameOnlyTypes.count(ah.d_type)) {
133
0
      rrname = pr.getName();
134
0
      pw.xfrName(rrname);
135
0
    }
136
0
    else if (noNameTypes.count(ah.d_type)) {
137
0
      pr.xfrBlob(blob);
138
0
      pw.xfrBlob(blob);
139
0
    }
140
0
    else if (ah.d_type == QType::RRSIG) {
141
      /* good luck */
142
0
      pr.xfrBlob(blob);
143
0
      pw.xfrBlob(blob);
144
0
    }
145
0
    else if (ah.d_type == QType::MX) {
146
0
      auto prio = pr.get16BitInt();
147
0
      rrname = pr.getName();
148
0
      pw.xfr16BitInt(prio);
149
0
      pw.xfrName(rrname);
150
0
    }
151
0
    else if (ah.d_type == QType::SOA) {
152
0
      auto mname = pr.getName();
153
0
      pw.xfrName(mname);
154
0
      auto rname = pr.getName();
155
0
      pw.xfrName(rname);
156
      /* serial */
157
0
      pw.xfr32BitInt(pr.get32BitInt());
158
      /* refresh */
159
0
      pw.xfr32BitInt(pr.get32BitInt());
160
      /* retry */
161
0
      pw.xfr32BitInt(pr.get32BitInt());
162
      /* expire */
163
0
      pw.xfr32BitInt(pr.get32BitInt());
164
      /* minimal */
165
0
      pw.xfr32BitInt(pr.get32BitInt());
166
0
    }
167
0
    else if (ah.d_type == QType::SRV) {
168
      /* preference */
169
0
      pw.xfr16BitInt(pr.get16BitInt());
170
      /* weight */
171
0
      pw.xfr16BitInt(pr.get16BitInt());
172
      /* port */
173
0
      pw.xfr16BitInt(pr.get16BitInt());
174
0
      auto target = pr.getName();
175
0
      pw.xfrName(target);
176
0
    }
177
0
    else {
178
      /* sorry, unsafe type */
179
0
      return false;
180
0
    }
181
0
  }
182
183
0
  pw.commit();
184
0
  initialPacket = std::move(newContent);
185
186
0
  return true;
187
0
}
188
189
namespace PacketMangling
190
{
191
  bool editDNSHeaderFromPacket(PacketBuffer& packet, const std::function<bool(dnsheader& header)>& editFunction)
192
0
  {
193
0
    if (packet.size() < sizeof(dnsheader)) {
194
0
      throw std::runtime_error("Trying to edit the DNS header of a too small packet");
195
0
    }
196
197
0
    return editDNSHeaderFromRawPacket(packet.data(), editFunction);
198
0
  }
199
200
  bool editDNSHeaderFromRawPacket(void* packet, const std::function<bool(dnsheader& header)>& editFunction)
201
0
  {
202
0
    if (dnsheader_aligned::isMemoryAligned(packet)) {
203
      // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
204
0
      auto* header = reinterpret_cast<dnsheader*>(packet);
205
0
      return editFunction(*header);
206
0
    }
207
208
0
    dnsheader header{};
209
0
    memcpy(&header, packet, sizeof(header));
210
0
    if (!editFunction(header)) {
211
0
      return false;
212
0
    }
213
0
    memcpy(packet, &header, sizeof(header));
214
0
    return true;
215
0
  }
216
}
217
}