1 // Copyright 2022 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "net/dns/opt_record_rdata.h"
6
7 #include <algorithm>
8 #include <memory>
9 #include <numeric>
10 #include <utility>
11
12 #include "base/big_endian.h"
13 #include "base/check_is_test.h"
14 #include "base/containers/contains.h"
15 #include "base/memory/ptr_util.h"
16 #include "base/numerics/safe_conversions.h"
17 #include "base/strings/string_piece.h"
18 #include "base/strings/string_util.h"
19 #include "net/dns/public/dns_protocol.h"
20
21 namespace net {
22
23 namespace {
SerializeEdeOpt(uint16_t info_code,base::StringPiece extra_text)24 std::string SerializeEdeOpt(uint16_t info_code, base::StringPiece extra_text) {
25 std::string buf(2 + extra_text.size(), '\0');
26
27 base::BigEndianWriter writer(buf.data(), buf.size());
28 CHECK(writer.WriteU16(info_code));
29 CHECK(writer.WriteBytes(extra_text.data(), extra_text.size()));
30 return buf;
31 }
32 } // namespace
33
Opt(std::string data)34 OptRecordRdata::Opt::Opt(std::string data) : data_(std::move(data)) {}
35
operator ==(const OptRecordRdata::Opt & other) const36 bool OptRecordRdata::Opt::operator==(const OptRecordRdata::Opt& other) const {
37 return IsEqual(other);
38 }
39
operator !=(const OptRecordRdata::Opt & other) const40 bool OptRecordRdata::Opt::operator!=(const OptRecordRdata::Opt& other) const {
41 return !IsEqual(other);
42 }
43
IsEqual(const OptRecordRdata::Opt & other) const44 bool OptRecordRdata::Opt::IsEqual(const OptRecordRdata::Opt& other) const {
45 return GetCode() == other.GetCode() && data() == other.data();
46 }
47
EdeOpt(uint16_t info_code,std::string extra_text)48 OptRecordRdata::EdeOpt::EdeOpt(uint16_t info_code, std::string extra_text)
49 : Opt(SerializeEdeOpt(info_code, extra_text)),
50 info_code_(info_code),
51 extra_text_(std::move(extra_text)) {
52 CHECK(base::IsStringUTF8(extra_text_));
53 }
54
55 OptRecordRdata::EdeOpt::~EdeOpt() = default;
56
Create(std::string data)57 std::unique_ptr<OptRecordRdata::EdeOpt> OptRecordRdata::EdeOpt::Create(
58 std::string data) {
59 uint16_t info_code;
60 base::StringPiece extra_text;
61 auto edeReader = base::BigEndianReader::FromStringPiece(data);
62
63 // size must be at least 2: info_code + optional extra_text
64 if (!(edeReader.ReadU16(&info_code) &&
65 edeReader.ReadPiece(&extra_text, edeReader.remaining()))) {
66 return nullptr;
67 }
68
69 if (!base::IsStringUTF8(extra_text)) {
70 return nullptr;
71 }
72
73 std::string extra_text_str(extra_text.data(), extra_text.size());
74 return std::make_unique<EdeOpt>(info_code, std::move(extra_text_str));
75 }
76
GetCode() const77 uint16_t OptRecordRdata::EdeOpt::GetCode() const {
78 return EdeOpt::kOptCode;
79 }
80
81 OptRecordRdata::EdeOpt::EdeInfoCode
GetEnumFromInfoCode() const82 OptRecordRdata::EdeOpt::GetEnumFromInfoCode() const {
83 return GetEnumFromInfoCode(info_code_);
84 }
85
GetEnumFromInfoCode(uint16_t info_code)86 OptRecordRdata::EdeOpt::EdeInfoCode OptRecordRdata::EdeOpt::GetEnumFromInfoCode(
87 uint16_t info_code) {
88 switch (info_code) {
89 case 0:
90 return EdeInfoCode::kOtherError;
91 case 1:
92 return EdeInfoCode::kUnsupportedDnskeyAlgorithm;
93 case 2:
94 return EdeInfoCode::kUnsupportedDsDigestType;
95 case 3:
96 return EdeInfoCode::kStaleAnswer;
97 case 4:
98 return EdeInfoCode::kForgedAnswer;
99 case 5:
100 return EdeInfoCode::kDnssecIndeterminate;
101 case 6:
102 return EdeInfoCode::kDnssecBogus;
103 case 7:
104 return EdeInfoCode::kSignatureExpired;
105 case 8:
106 return EdeInfoCode::kSignatureNotYetValid;
107 case 9:
108 return EdeInfoCode::kDnskeyMissing;
109 case 10:
110 return EdeInfoCode::kRrsigsMissing;
111 case 11:
112 return EdeInfoCode::kNoZoneKeyBitSet;
113 case 12:
114 return EdeInfoCode::kNsecMissing;
115 case 13:
116 return EdeInfoCode::kCachedError;
117 case 14:
118 return EdeInfoCode::kNotReady;
119 case 15:
120 return EdeInfoCode::kBlocked;
121 case 16:
122 return EdeInfoCode::kCensored;
123 case 17:
124 return EdeInfoCode::kFiltered;
125 case 18:
126 return EdeInfoCode::kProhibited;
127 case 19:
128 return EdeInfoCode::kStaleNxdomainAnswer;
129 case 20:
130 return EdeInfoCode::kNotAuthoritative;
131 case 21:
132 return EdeInfoCode::kNotSupported;
133 case 22:
134 return EdeInfoCode::kNoReachableAuthority;
135 case 23:
136 return EdeInfoCode::kNetworkError;
137 case 24:
138 return EdeInfoCode::kInvalidData;
139 case 25:
140 return EdeInfoCode::kSignatureExpiredBeforeValid;
141 case 26:
142 return EdeInfoCode::kTooEarly;
143 case 27:
144 return EdeInfoCode::kUnsupportedNsec3IterationsValue;
145 default:
146 return EdeInfoCode::kUnrecognizedErrorCode;
147 }
148 }
149
PaddingOpt(std::string padding)150 OptRecordRdata::PaddingOpt::PaddingOpt(std::string padding)
151 : Opt(std::move(padding)) {}
152
PaddingOpt(uint16_t padding_len)153 OptRecordRdata::PaddingOpt::PaddingOpt(uint16_t padding_len)
154 : Opt(std::string(base::checked_cast<size_t>(padding_len), '\0')) {}
155
156 OptRecordRdata::PaddingOpt::~PaddingOpt() = default;
157
GetCode() const158 uint16_t OptRecordRdata::PaddingOpt::GetCode() const {
159 return PaddingOpt::kOptCode;
160 }
161
162 OptRecordRdata::UnknownOpt::~UnknownOpt() = default;
163
164 std::unique_ptr<OptRecordRdata::UnknownOpt>
CreateForTesting(uint16_t code,std::string data)165 OptRecordRdata::UnknownOpt::CreateForTesting(uint16_t code, std::string data) {
166 CHECK_IS_TEST();
167 return base::WrapUnique(
168 new OptRecordRdata::UnknownOpt(code, std::move(data)));
169 }
170
UnknownOpt(uint16_t code,std::string data)171 OptRecordRdata::UnknownOpt::UnknownOpt(uint16_t code, std::string data)
172 : Opt(std::move(data)), code_(code) {
173 CHECK(!base::Contains(kOptsWithDedicatedClasses, code));
174 }
175
GetCode() const176 uint16_t OptRecordRdata::UnknownOpt::GetCode() const {
177 return code_;
178 }
179
180 OptRecordRdata::OptRecordRdata() = default;
181
182 OptRecordRdata::~OptRecordRdata() = default;
183
operator ==(const OptRecordRdata & other) const184 bool OptRecordRdata::operator==(const OptRecordRdata& other) const {
185 return IsEqual(&other);
186 }
187
operator !=(const OptRecordRdata & other) const188 bool OptRecordRdata::operator!=(const OptRecordRdata& other) const {
189 return !IsEqual(&other);
190 }
191
192 // static
Create(base::StringPiece data)193 std::unique_ptr<OptRecordRdata> OptRecordRdata::Create(base::StringPiece data) {
194 auto rdata = std::make_unique<OptRecordRdata>();
195 rdata->buf_.assign(data.begin(), data.end());
196
197 auto reader = base::BigEndianReader::FromStringPiece(data);
198 while (reader.remaining() > 0) {
199 uint16_t opt_code, opt_data_size;
200 base::StringPiece opt_data_sp;
201
202 if (!(reader.ReadU16(&opt_code) && reader.ReadU16(&opt_data_size) &&
203 reader.ReadPiece(&opt_data_sp, opt_data_size))) {
204 return nullptr;
205 }
206
207 std::string opt_data{opt_data_sp.data(), opt_data_sp.size()};
208
209 // After the Opt object has been parsed, parse the contents (the data)
210 // depending on the opt_code. The specific Opt subclasses all inherit from
211 // Opt. If an opt code does not have a matching Opt subclass, a simple Opt
212 // object will be created, and data won't be parsed.
213
214 std::unique_ptr<Opt> opt;
215
216 switch (opt_code) {
217 case dns_protocol::kEdnsPadding:
218 opt = std::make_unique<OptRecordRdata::PaddingOpt>(std::move(opt_data));
219 break;
220 case dns_protocol::kEdnsExtendedDnsError:
221 opt = OptRecordRdata::EdeOpt::Create(std::move(opt_data));
222 break;
223 default:
224 opt = base::WrapUnique(
225 new OptRecordRdata::UnknownOpt(opt_code, std::move(opt_data)));
226 break;
227 }
228
229 // Confirm that opt is not null, which would be the result of a failed
230 // parse.
231 if (!opt) {
232 return nullptr;
233 }
234
235 rdata->opts_.emplace(opt_code, std::move(opt));
236 }
237
238 return rdata;
239 }
240
Type() const241 uint16_t OptRecordRdata::Type() const {
242 return OptRecordRdata::kType;
243 }
244
IsEqual(const RecordRdata * other) const245 bool OptRecordRdata::IsEqual(const RecordRdata* other) const {
246 if (other->Type() != Type()) {
247 return false;
248 }
249 const OptRecordRdata* opt_other = static_cast<const OptRecordRdata*>(other);
250 return opt_other->buf_ == buf_;
251 }
252
AddOpt(std::unique_ptr<Opt> opt)253 void OptRecordRdata::AddOpt(std::unique_ptr<Opt> opt) {
254 base::StringPiece opt_data = opt->data();
255
256 // Resize buffer to accommodate new OPT.
257 const size_t orig_rdata_size = buf_.size();
258 buf_.resize(orig_rdata_size + Opt::kHeaderSize + opt_data.size());
259
260 // Start writing from the end of the existing rdata.
261 base::BigEndianWriter writer(buf_.data(), buf_.size());
262 CHECK(writer.Skip(orig_rdata_size));
263 bool success = writer.WriteU16(opt->GetCode()) &&
264 writer.WriteU16(opt_data.size()) &&
265 writer.WriteBytes(opt_data.data(), opt_data.size());
266 DCHECK(success);
267
268 opts_.emplace(opt->GetCode(), std::move(opt));
269 }
270
ContainsOptCode(uint16_t opt_code) const271 bool OptRecordRdata::ContainsOptCode(uint16_t opt_code) const {
272 return opts_.find(opt_code) != opts_.end();
273 }
274
GetOpts() const275 std::vector<const OptRecordRdata::Opt*> OptRecordRdata::GetOpts() const {
276 std::vector<const OptRecordRdata::Opt*> opts;
277 opts.reserve(OptCount());
278 for (const auto& elem : opts_) {
279 opts.push_back(elem.second.get());
280 }
281 return opts;
282 }
283
GetPaddingOpts() const284 std::vector<const OptRecordRdata::PaddingOpt*> OptRecordRdata::GetPaddingOpts()
285 const {
286 std::vector<const OptRecordRdata::PaddingOpt*> opts;
287 auto range = opts_.equal_range(dns_protocol::kEdnsPadding);
288 for (auto it = range.first; it != range.second; ++it) {
289 opts.push_back(static_cast<const PaddingOpt*>(it->second.get()));
290 }
291 return opts;
292 }
293
GetEdeOpts() const294 std::vector<const OptRecordRdata::EdeOpt*> OptRecordRdata::GetEdeOpts() const {
295 std::vector<const OptRecordRdata::EdeOpt*> opts;
296 auto range = opts_.equal_range(dns_protocol::kEdnsExtendedDnsError);
297 for (auto it = range.first; it != range.second; ++it) {
298 opts.push_back(static_cast<const EdeOpt*>(it->second.get()));
299 }
300 return opts;
301 }
302
303 } // namespace net
304