• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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