/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 | | } |