blob: aae0b80b719010a93dc9338b164649603798d7f1 [file] [log] [blame]
Kevin Cernekeed05be172017-06-17 17:40:21 -07001// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Garrick Evans3388a032020-03-24 11:25:55 +09005#ifndef PATCHPANEL_DNS_DNS_RESPONSE_H_
6#define PATCHPANEL_DNS_DNS_RESPONSE_H_
Kevin Cernekeed05be172017-06-17 17:40:21 -07007
8#include <stddef.h>
9#include <stdint.h>
10
11#include <string>
Jason Jeremy Imana21be272020-10-21 17:53:45 +090012#include <vector>
Kevin Cernekeed05be172017-06-17 17:40:21 -070013
14#include "base/macros.h"
15#include "base/memory/ref_counted.h"
Jason Jeremy Imana21be272020-10-21 17:53:45 +090016#include "base/optional.h"
Kevin Cernekeed05be172017-06-17 17:40:21 -070017#include "base/strings/string_piece.h"
18#include "base/time/time.h"
Jason Jeremy Iman9df645a2020-11-09 04:37:09 +090019#include "brillo/brillo_export.h"
Kevin Cernekeed05be172017-06-17 17:40:21 -070020
Jason Jeremy Imana21be272020-10-21 17:53:45 +090021#include "patchpanel/dns/dns_protocol.h"
Garrick Evans3388a032020-03-24 11:25:55 +090022#include "patchpanel/dns/io_buffer.h"
Kevin Cernekeed05be172017-06-17 17:40:21 -070023
Jason Jeremy Imana21be272020-10-21 17:53:45 +090024namespace base {
25class BigEndianWriter;
26} // namespace base
Kevin Cernekeed05be172017-06-17 17:40:21 -070027
Jason Jeremy Imana21be272020-10-21 17:53:45 +090028namespace patchpanel {
29
Kevin Cernekeed05be172017-06-17 17:40:21 -070030class DnsQuery;
Kevin Cernekeed05be172017-06-17 17:40:21 -070031
32namespace dns_protocol {
33struct Header;
Jason Jeremy Imana21be272020-10-21 17:53:45 +090034} // namespace dns_protocol
Kevin Cernekeed05be172017-06-17 17:40:21 -070035
36// Structure representing a Resource Record as specified in RFC 1035, Section
37// 4.1.3.
Jason Jeremy Iman9df645a2020-11-09 04:37:09 +090038struct BRILLO_EXPORT DnsResourceRecord {
Kevin Cernekeed05be172017-06-17 17:40:21 -070039 DnsResourceRecord();
Jason Jeremy Imana21be272020-10-21 17:53:45 +090040 explicit DnsResourceRecord(const DnsResourceRecord& other);
41 DnsResourceRecord(DnsResourceRecord&& other);
Kevin Cernekeed05be172017-06-17 17:40:21 -070042 ~DnsResourceRecord();
43
Jason Jeremy Imana21be272020-10-21 17:53:45 +090044 DnsResourceRecord& operator=(const DnsResourceRecord& other);
45 DnsResourceRecord& operator=(DnsResourceRecord&& other);
46
47 // A helper to set |owned_rdata| that also sets |rdata| to point to it. The
48 // |value| must be non-empty. See the definition of |owned_rdata| below.
49 void SetOwnedRdata(std::string value);
50
51 // NAME (variable length) + TYPE (2 bytes) + CLASS (2 bytes) + TTL (4 bytes) +
52 // RDLENGTH (2 bytes) + RDATA (variable length)
53 //
54 // Uses |owned_rdata| for RDATA if non-empty.
55 size_t CalculateRecordSize() const;
56
Kevin Cernekeed05be172017-06-17 17:40:21 -070057 std::string name; // in dotted form
Jason Jeremy Imana21be272020-10-21 17:53:45 +090058 uint16_t type = 0;
59 uint16_t klass = 0;
60 uint32_t ttl = 0;
61 // Points to the original response buffer or otherwise to |owned_rdata|.
62 base::StringPiece rdata;
63 // Used to construct a DnsResponse from data. This field is empty if |rdata|
64 // points to the response buffer.
65 std::string owned_rdata;
Kevin Cernekeed05be172017-06-17 17:40:21 -070066};
67
68// Iterator to walk over resource records of the DNS response packet.
Jason Jeremy Iman9df645a2020-11-09 04:37:09 +090069class BRILLO_EXPORT DnsRecordParser {
Kevin Cernekeed05be172017-06-17 17:40:21 -070070 public:
71 // Construct an uninitialized iterator.
72 DnsRecordParser();
73
74 // Construct an iterator to process the |packet| of given |length|.
75 // |offset| points to the beginning of the answer section.
76 DnsRecordParser(const void* packet, size_t length, size_t offset);
77
78 // Returns |true| if initialized.
Ben Chanac009042019-09-20 16:19:56 -070079 bool IsValid() const { return packet_ != nullptr; }
Kevin Cernekeed05be172017-06-17 17:40:21 -070080
81 // Returns |true| if no more bytes remain in the packet.
82 bool AtEnd() const { return cur_ == packet_ + length_; }
83
84 // Returns current offset into the packet.
85 size_t GetOffset() const { return cur_ - packet_; }
86
87 // Parses a (possibly compressed) DNS name from the packet starting at
88 // |pos|. Stores output (even partial) in |out| unless |out| is NULL. |out|
89 // is stored in the dotted form, e.g., "example.com". Returns number of bytes
90 // consumed or 0 on failure.
91 // This is exposed to allow parsing compressed names within RRDATA for TYPEs
92 // such as NS, CNAME, PTR, MX, SOA.
93 // See RFC 1035 section 4.1.4.
94 unsigned ReadName(const void* pos, std::string* out) const;
95
96 // Parses the next resource record into |record|. Returns true if succeeded.
97 bool ReadRecord(DnsResourceRecord* record);
98
99 // Skip a question section, returns true if succeeded.
100 bool SkipQuestion();
101
102 private:
103 const char* packet_;
104 size_t length_;
105 // Current offset within the packet.
106 const char* cur_;
107};
108
109// Buffer-holder for the DNS response allowing easy access to the header fields
110// and resource records. After reading into |io_buffer| must call InitParse to
111// position the RR parser.
Jason Jeremy Iman9df645a2020-11-09 04:37:09 +0900112class BRILLO_EXPORT DnsResponse {
Kevin Cernekeed05be172017-06-17 17:40:21 -0700113 public:
114 // Possible results from ParseToAddressList.
115 enum Result {
116 DNS_PARSE_OK = 0,
117 DNS_MALFORMED_RESPONSE, // DnsRecordParser failed before the end of
118 // packet.
119 DNS_MALFORMED_CNAME, // Could not parse CNAME out of RRDATA.
120 DNS_NAME_MISMATCH, // Got an address but no ordered chain of CNAMEs
121 // leads there.
122 DNS_SIZE_MISMATCH, // Got an address but size does not match.
123 DNS_CNAME_AFTER_ADDRESS, // Found CNAME after an address record.
124 DNS_ADDRESS_TTL_MISMATCH, // OBSOLETE. No longer used.
125 DNS_NO_ADDRESSES, // OBSOLETE. No longer used.
126 // Only add new values here.
Jason Jeremy Imana21be272020-10-21 17:53:45 +0900127 DNS_PARSE_RESULT_MAX, // Bounding value for histograms.
Kevin Cernekeed05be172017-06-17 17:40:21 -0700128 };
129
130 // Constructs a response buffer large enough to store one byte more than
131 // largest possible response, to detect malformed responses.
132 DnsResponse();
133
Jason Jeremy Imana21be272020-10-21 17:53:45 +0900134 // Constructs a response message from |answers| and the originating |query|.
135 // After the successful construction, and the parser is also initialized.
136 DnsResponse(uint16_t id,
137 bool is_authoritative,
138 const std::vector<DnsResourceRecord>& answers,
139 const std::vector<DnsResourceRecord>& authority_records,
140 const std::vector<DnsResourceRecord>& additional_records,
141 const base::Optional<DnsQuery>& query,
142 uint8_t rcode = dns_protocol::kRcodeNOERROR);
143
Kevin Cernekeed05be172017-06-17 17:40:21 -0700144 // Constructs a response buffer of given length. Used for TCP transactions.
145 explicit DnsResponse(size_t length);
146
Jason Jeremy Imana21be272020-10-21 17:53:45 +0900147 // Constructs a response from the passed buffer.
148 DnsResponse(scoped_refptr<IOBuffer> buffer, size_t size);
149
Kevin Cernekeed05be172017-06-17 17:40:21 -0700150 // Constructs a response from |data|. Used for testing purposes only!
151 DnsResponse(const void* data, size_t length, size_t answer_offset);
152
Jason Jeremy Imana21be272020-10-21 17:53:45 +0900153 // Move-only.
154 DnsResponse(DnsResponse&& other);
155 DnsResponse& operator=(DnsResponse&& other);
156
Kevin Cernekeed05be172017-06-17 17:40:21 -0700157 ~DnsResponse();
158
159 // Internal buffer accessor into which actual bytes of response will be
160 // read.
Jason Jeremy Imana21be272020-10-21 17:53:45 +0900161 IOBuffer* io_buffer() { return io_buffer_.get(); }
162 const IOBuffer* io_buffer() const { return io_buffer_.get(); }
163
164 // Size of the internal buffer.
165 size_t io_buffer_size() const { return io_buffer_size_; }
166
167 // Assuming the internal buffer holds |nbytes| bytes, returns true iff the
168 // packet matches the |query| id and question. This should only be called if
169 // the response is constructed from a raw buffer.
170 bool InitParse(size_t nbytes, const DnsQuery& query);
Kevin Cernekeed05be172017-06-17 17:40:21 -0700171
172 // Assuming the internal buffer holds |nbytes| bytes, initialize the parser
Jason Jeremy Imana21be272020-10-21 17:53:45 +0900173 // without matching it against an existing query. This should only be called
174 // if the response is constructed from a raw buffer.
175 bool InitParseWithoutQuery(size_t nbytes);
Kevin Cernekeed05be172017-06-17 17:40:21 -0700176
Jason Jeremy Imana21be272020-10-21 17:53:45 +0900177 // Does not require the response to be fully parsed and valid, but will return
178 // nullopt if the ID is unknown. The ID will only be known if the response is
179 // successfully constructed from data or if InitParse...() has been able to
180 // parse at least as far as the ID (not necessarily a fully successful parse).
181 base::Optional<uint16_t> id() const;
182
183 // Returns true if response is valid, that is, after successful InitParse, or
184 // after successful construction of a new response from data.
Kevin Cernekeed05be172017-06-17 17:40:21 -0700185 bool IsValid() const;
186
187 // All of the methods below are valid only if the response is valid.
188
189 // Accessors for the header.
190 uint16_t flags() const; // excluding rcode
191 uint8_t rcode() const;
192
193 unsigned answer_count() const;
Jason Jeremy Imana21be272020-10-21 17:53:45 +0900194 unsigned authority_count() const;
Kevin Cernekeed05be172017-06-17 17:40:21 -0700195 unsigned additional_answer_count() const;
196
197 // Accessors to the question. The qname is unparsed.
198 base::StringPiece qname() const;
199 uint16_t qtype() const;
200
Jason Jeremy Imana21be272020-10-21 17:53:45 +0900201 // Returns qname in dotted format.
202 std::string GetDottedName() const;
203
Kevin Cernekeed05be172017-06-17 17:40:21 -0700204 // Returns an iterator to the resource records in the answer section.
205 // The iterator is valid only in the scope of the DnsResponse.
206 // This operation is idempotent.
207 DnsRecordParser Parser() const;
208
209 private:
Jason Jeremy Imana21be272020-10-21 17:53:45 +0900210 bool WriteHeader(base::BigEndianWriter* writer,
211 const dns_protocol::Header& header);
212 bool WriteQuestion(base::BigEndianWriter* writer, const DnsQuery& query);
213 bool WriteRecord(base::BigEndianWriter* writer,
214 const DnsResourceRecord& record);
215 bool WriteAnswer(base::BigEndianWriter* writer,
216 const DnsResourceRecord& answer,
217 const base::Optional<DnsQuery>& query);
218
Kevin Cernekeed05be172017-06-17 17:40:21 -0700219 // Convenience for header access.
220 const dns_protocol::Header* header() const;
221
222 // Buffer into which response bytes are read.
Jason Jeremy Imana21be272020-10-21 17:53:45 +0900223 scoped_refptr<IOBuffer> io_buffer_;
224
225 // Size of the buffer.
226 size_t io_buffer_size_;
Kevin Cernekeed05be172017-06-17 17:40:21 -0700227
228 // Iterator constructed after InitParse positioned at the answer section.
229 // It is never updated afterwards, so can be used in accessors.
230 DnsRecordParser parser_;
Jason Jeremy Imana21be272020-10-21 17:53:45 +0900231 bool id_available_ = false;
Kevin Cernekeed05be172017-06-17 17:40:21 -0700232};
233
Jason Jeremy Imana21be272020-10-21 17:53:45 +0900234} // namespace patchpanel
Kevin Cernekeed05be172017-06-17 17:40:21 -0700235
Garrick Evans3388a032020-03-24 11:25:55 +0900236#endif // PATCHPANEL_DNS_DNS_RESPONSE_H_