xref: /aosp_15_r20/external/cronet/net/dns/dns_response.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2012 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 #ifndef NET_DNS_DNS_RESPONSE_H_
6 #define NET_DNS_DNS_RESPONSE_H_
7 
8 #include <stddef.h>
9 #include <stdint.h>
10 
11 #include <optional>
12 #include <string>
13 #include <string_view>
14 #include <vector>
15 
16 #include "base/compiler_specific.h"
17 #include "base/containers/span.h"
18 #include "base/containers/span_writer.h"
19 #include "base/memory/scoped_refptr.h"
20 #include "net/base/net_export.h"
21 #include "net/dns/dns_response_result_extractor.h"
22 #include "net/dns/public/dns_protocol.h"
23 
24 namespace net {
25 
26 class DnsQuery;
27 class IOBuffer;
28 
29 namespace dns_protocol {
30 struct Header;
31 }  // namespace dns_protocol
32 
33 // Structure representing a Resource Record as specified in RFC 1035, Section
34 // 4.1.3.
35 struct NET_EXPORT_PRIVATE DnsResourceRecord {
36   DnsResourceRecord();
37   DnsResourceRecord(const DnsResourceRecord& other);
38   DnsResourceRecord(DnsResourceRecord&& other);
39   ~DnsResourceRecord();
40 
41   DnsResourceRecord& operator=(const DnsResourceRecord& other);
42   DnsResourceRecord& operator=(DnsResourceRecord&& other);
43 
44   // A helper to set |owned_rdata| that also sets |rdata| to point to it. The
45   // |value| must be non-empty. See the definition of |owned_rdata| below.
46   void SetOwnedRdata(std::string value);
47 
48   // NAME (variable length) + TYPE (2 bytes) + CLASS (2 bytes) + TTL (4 bytes) +
49   // RDLENGTH (2 bytes) + RDATA (variable length)
50   //
51   // Uses |owned_rdata| for RDATA if non-empty.
52   size_t CalculateRecordSize() const;
53 
54   std::string name;  // in dotted form
55   uint16_t type = 0;
56   uint16_t klass = 0;
57   uint32_t ttl = 0;
58   // Points to the original response buffer or otherwise to |owned_rdata|.
59   std::string_view rdata;
60   // Used to construct a DnsResponse from data. This field is empty if |rdata|
61   // points to the response buffer.
62   std::string owned_rdata;
63 };
64 
65 // Iterator to walk over resource records of the DNS response packet.
66 class NET_EXPORT_PRIVATE DnsRecordParser {
67  public:
68   // Construct an uninitialized iterator.
69   DnsRecordParser();
70 
71   // Construct an iterator to process the `packet`.
72   // `offset` points to the beginning of the answer section. `ReadRecord()` will
73   // fail if called more than `num_records` times, no matter whether or not
74   // there is additional data at the end of the buffer that may appear to be a
75   // valid record.
76   DnsRecordParser(base::span<const uint8_t> packet,
77                   size_t offset,
78                   size_t num_records);
79 
80   // TODO(crbug.com/40284755): Deprecated, use the span-based constructor.
81   UNSAFE_BUFFER_USAGE DnsRecordParser(const void* packet,
82                                       size_t length,
83                                       size_t offset,
84                                       size_t num_records);
85 
86   // Returns |true| if initialized.
IsValid()87   bool IsValid() const { return !packet_.empty(); }
88 
89   // Returns |true| if no more bytes remain in the packet.
AtEnd()90   bool AtEnd() const { return cur_ == packet_.size(); }
91 
92   // Returns current offset into the packet.
GetOffset()93   size_t GetOffset() const { return cur_; }
94 
95   // Parses a (possibly compressed) DNS name from the packet starting at
96   // |pos|. Stores output (even partial) in |out| unless |out| is NULL. |out|
97   // is stored in the dotted form, e.g., "example.com". Returns number of bytes
98   // consumed or 0 on failure.
99   // This is exposed to allow parsing compressed names within RRDATA for TYPEs
100   // such as NS, CNAME, PTR, MX, SOA.
101   // See RFC 1035 section 4.1.4.
102   unsigned ReadName(const void* pos, std::string* out) const;
103 
104   // Parses the next resource record into |record|. Returns true if succeeded.
105   bool ReadRecord(DnsResourceRecord* record);
106 
107   // Read a question section, returns true if succeeded. In `DnsResponse`,
108   // expected to be called during parse, after which the current offset will be
109   // after all questions.
110   bool ReadQuestion(std::string& out_dotted_qname, uint16_t& out_qtype);
111 
112  private:
113   base::span<const uint8_t> packet_;
114   size_t num_records_ = 0u;
115   size_t num_records_parsed_ = 0u;
116   // Current offset within the packet.
117   size_t cur_ = 0u;
118 };
119 
120 // Buffer-holder for the DNS response allowing easy access to the header fields
121 // and resource records. After reading into |io_buffer| must call InitParse to
122 // position the RR parser.
123 class NET_EXPORT_PRIVATE DnsResponse {
124  public:
125   // Constructs a response buffer large enough to store one byte more than
126   // largest possible response, to detect malformed responses.
127   DnsResponse();
128 
129   // Constructs a response message from `answers` and the originating `query`.
130   // After the successful construction, and the parser is also initialized.
131   //
132   // If `validate_records` is false, DCHECKs validating the correctness of
133   // records will be skipped. Intended for tests to allow creation of malformed
134   // responses.
135   DnsResponse(uint16_t id,
136               bool is_authoritative,
137               const std::vector<DnsResourceRecord>& answers,
138               const std::vector<DnsResourceRecord>& authority_records,
139               const std::vector<DnsResourceRecord>& additional_records,
140               const std::optional<DnsQuery>& query,
141               uint8_t rcode = dns_protocol::kRcodeNOERROR,
142               bool validate_records = true,
143               bool validate_names_as_internet_hostnames = true);
144 
145   // Constructs a response buffer of given length. Used for TCP transactions.
146   explicit DnsResponse(size_t length);
147 
148   // Constructs a response from the passed buffer.
149   DnsResponse(scoped_refptr<IOBuffer> buffer, size_t size);
150 
151   // Constructs a response from |data|. Used for testing purposes only!
152   DnsResponse(const void* data, size_t length, size_t answer_offset);
153 
154   static DnsResponse CreateEmptyNoDataResponse(uint16_t id,
155                                                bool is_authoritative,
156                                                base::span<const uint8_t> qname,
157                                                uint16_t qtype);
158 
159   // Move-only.
160   DnsResponse(DnsResponse&& other);
161   DnsResponse& operator=(DnsResponse&& other);
162 
163   ~DnsResponse();
164 
165   // Internal buffer accessor into which actual bytes of response will be
166   // read.
io_buffer()167   IOBuffer* io_buffer() { return io_buffer_.get(); }
io_buffer()168   const IOBuffer* io_buffer() const { return io_buffer_.get(); }
169 
170   // Size of the internal buffer.
io_buffer_size()171   size_t io_buffer_size() const { return io_buffer_size_; }
172 
173   // Assuming the internal buffer holds |nbytes| bytes, returns true iff the
174   // packet matches the |query| id and question. This should only be called if
175   // the response is constructed from a raw buffer.
176   bool InitParse(size_t nbytes, const DnsQuery& query);
177 
178   // Assuming the internal buffer holds |nbytes| bytes, initialize the parser
179   // without matching it against an existing query. This should only be called
180   // if the response is constructed from a raw buffer.
181   bool InitParseWithoutQuery(size_t nbytes);
182 
183   // Does not require the response to be fully parsed and valid, but will return
184   // nullopt if the ID is unknown. The ID will only be known if the response is
185   // successfully constructed from data or if InitParse...() has been able to
186   // parse at least as far as the ID (not necessarily a fully successful parse).
187   std::optional<uint16_t> id() const;
188 
189   // Returns true if response is valid, that is, after successful InitParse, or
190   // after successful construction of a new response from data.
191   bool IsValid() const;
192 
193   // All of the methods below are valid only if the response is valid.
194 
195   // Accessors for the header.
196   uint16_t flags() const;  // excluding rcode
197   uint8_t rcode() const;
198 
199   unsigned question_count() const;
200   unsigned answer_count() const;
201   unsigned authority_count() const;
202   unsigned additional_answer_count() const;
203 
qtypes()204   const std::vector<uint16_t>& qtypes() const {
205     DCHECK(parser_.IsValid());
206     DCHECK_EQ(question_count(), qtypes_.size());
207     return qtypes_;
208   }
dotted_qnames()209   const std::vector<std::string>& dotted_qnames() const {
210     DCHECK(parser_.IsValid());
211     DCHECK_EQ(question_count(), dotted_qnames_.size());
212     return dotted_qnames_;
213   }
214 
215   // Shortcuts to get qtype or qname for single-query responses. Should only be
216   // used in cases where there is known to be exactly one question (e.g. because
217   // that has been validated by `InitParse()`).
218   uint16_t GetSingleQType() const;
219   std::string_view GetSingleDottedName() const;
220 
221   // Returns an iterator to the resource records in the answer section.
222   // The iterator is valid only in the scope of the DnsResponse.
223   // This operation is idempotent.
224   DnsRecordParser Parser() const;
225 
226  private:
227   bool WriteHeader(base::SpanWriter<uint8_t>* writer,
228                    const dns_protocol::Header& header);
229   bool WriteQuestion(base::SpanWriter<uint8_t>* writer, const DnsQuery& query);
230   bool WriteRecord(base::SpanWriter<uint8_t>* writer,
231                    const DnsResourceRecord& record,
232                    bool validate_record,
233                    bool validate_name_as_internet_hostname);
234   bool WriteAnswer(base::SpanWriter<uint8_t>* writer,
235                    const DnsResourceRecord& answer,
236                    const std::optional<DnsQuery>& query,
237                    bool validate_record,
238                    bool validate_name_as_internet_hostname);
239 
240   // Convenience for header access.
241   const dns_protocol::Header* header() const;
242 
243   // Buffer into which response bytes are read.
244   scoped_refptr<IOBuffer> io_buffer_;
245 
246   // Size of the buffer.
247   size_t io_buffer_size_;
248 
249   // Iterator constructed after InitParse positioned at the answer section.
250   // It is never updated afterwards, so can be used in accessors.
251   DnsRecordParser parser_;
252   bool id_available_ = false;
253   std::vector<std::string> dotted_qnames_;
254   std::vector<uint16_t> qtypes_;
255 };
256 
257 }  // namespace net
258 
259 #endif  // NET_DNS_DNS_RESPONSE_H_
260