| // Copyright 2012 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef NET_DNS_DNS_RESPONSE_H_ |
| #define NET_DNS_DNS_RESPONSE_H_ |
| |
| #include <stddef.h> |
| #include <stdint.h> |
| |
| #include <optional> |
| #include <string> |
| #include <string_view> |
| #include <vector> |
| |
| #include "base/compiler_specific.h" |
| #include "base/containers/span.h" |
| #include "base/containers/span_writer.h" |
| #include "base/memory/raw_span.h" |
| #include "base/memory/scoped_refptr.h" |
| #include "net/base/net_export.h" |
| #include "net/dns/dns_response_result_extractor.h" |
| #include "net/dns/public/dns_protocol.h" |
| |
| namespace net { |
| |
| class DnsQuery; |
| class IOBuffer; |
| |
| namespace dns_protocol { |
| struct Header; |
| } // namespace dns_protocol |
| |
| // Structure representing a Resource Record as specified in RFC 1035, Section |
| // 4.1.3. |
| struct NET_EXPORT_PRIVATE DnsResourceRecord { |
| DnsResourceRecord(); |
| DnsResourceRecord(const DnsResourceRecord& other); |
| DnsResourceRecord(DnsResourceRecord&& other); |
| ~DnsResourceRecord(); |
| |
| DnsResourceRecord& operator=(const DnsResourceRecord& other); |
| DnsResourceRecord& operator=(DnsResourceRecord&& other); |
| |
| // A helper to set |owned_rdata| that also sets |rdata| to point to it. The |
| // |value| must be non-empty. See the definition of |owned_rdata| below. |
| void SetOwnedRdata(base::span<const uint8_t> value); |
| |
| // NAME (variable length) + TYPE (2 bytes) + CLASS (2 bytes) + TTL (4 bytes) + |
| // RDLENGTH (2 bytes) + RDATA (variable length) |
| // |
| // Uses |owned_rdata| for RDATA if non-empty. |
| size_t CalculateRecordSize() const; |
| |
| std::string name; // in dotted form |
| uint16_t type = 0; |
| uint16_t klass = 0; |
| uint32_t ttl = 0; |
| // Points to the original response buffer or otherwise to |owned_rdata|. |
| std::vector<uint8_t> owned_rdata; |
| // Used to construct a DnsResponse from data. This field is empty if |rdata| |
| // points to the response buffer. |
| base::raw_span<const uint8_t, DanglingUntriaged> rdata; |
| }; |
| |
| // Iterator to walk over resource records of the DNS response packet. |
| class NET_EXPORT_PRIVATE DnsRecordParser { |
| public: |
| // Construct an uninitialized iterator. |
| DnsRecordParser(); |
| |
| // Construct an iterator to process the `packet`. |
| // `offset` points to the beginning of the answer section. `ReadRecord()` will |
| // fail if called more than `num_records` times, no matter whether or not |
| // there is additional data at the end of the buffer that may appear to be a |
| // valid record. |
| DnsRecordParser(base::span<const uint8_t> packet, |
| size_t offset, |
| size_t num_records); |
| |
| DnsRecordParser(const DnsRecordParser&); |
| DnsRecordParser(DnsRecordParser&&); |
| DnsRecordParser& operator=(const DnsRecordParser&); |
| DnsRecordParser& operator=(DnsRecordParser&&); |
| |
| ~DnsRecordParser(); |
| |
| // Returns |true| if initialized. |
| bool IsValid() const { return !packet_.empty(); } |
| |
| // Returns |true| if no more bytes remain in the packet. |
| bool AtEnd() const { return cur_ == packet_.size(); } |
| |
| // Returns current offset into the packet. |
| size_t GetOffset() const { return cur_; } |
| |
| // Parses a (possibly compressed) DNS name from the packet starting at |
| // |pos|. Stores output (even partial) in |out| unless |out| is NULL. |out| |
| // is stored in the dotted form, e.g., "example.com". Returns number of bytes |
| // consumed or 0 on failure. |
| // This is exposed to allow parsing compressed names within RRDATA for TYPEs |
| // such as NS, CNAME, PTR, MX, SOA. |
| // See RFC 1035 section 4.1.4. |
| unsigned ReadName(const void* pos, std::string* out) const; |
| |
| // Parses the next resource record into |record|. Returns true if succeeded. |
| bool ReadRecord(DnsResourceRecord* record); |
| |
| // Read a question section, returns true if succeeded. In `DnsResponse`, |
| // expected to be called during parse, after which the current offset will be |
| // after all questions. |
| bool ReadQuestion(std::string& out_dotted_qname, uint16_t& out_qtype); |
| |
| private: |
| base::raw_span<const uint8_t> packet_; |
| size_t num_records_ = 0u; |
| size_t num_records_parsed_ = 0u; |
| // Current offset within the packet. |
| size_t cur_ = 0u; |
| }; |
| |
| // Buffer-holder for the DNS response allowing easy access to the header fields |
| // and resource records. After reading into |io_buffer| must call InitParse to |
| // position the RR parser. |
| class NET_EXPORT_PRIVATE DnsResponse { |
| public: |
| // Constructs a response buffer large enough to store one byte more than |
| // largest possible response, to detect malformed responses. |
| DnsResponse(); |
| |
| // Constructs a response message from `answers` and the originating `query`. |
| // After the successful construction, and the parser is also initialized. |
| // |
| // If `validate_records` is false, DCHECKs validating the correctness of |
| // records will be skipped. Intended for tests to allow creation of malformed |
| // responses. |
| DnsResponse(uint16_t id, |
| bool is_authoritative, |
| const std::vector<DnsResourceRecord>& answers, |
| const std::vector<DnsResourceRecord>& authority_records, |
| const std::vector<DnsResourceRecord>& additional_records, |
| const std::optional<DnsQuery>& query, |
| uint8_t rcode = dns_protocol::kRcodeNOERROR, |
| bool validate_records = true, |
| bool validate_names_as_internet_hostnames = true); |
| |
| // Constructs a response buffer of given length. Used for TCP transactions. |
| explicit DnsResponse(size_t length); |
| |
| // Constructs a response from the passed buffer. |
| DnsResponse(scoped_refptr<IOBuffer> buffer, size_t size); |
| |
| // Constructs a response from |data|. Used for testing purposes only! |
| DnsResponse(base::span<const uint8_t> data, size_t answer_offset); |
| |
| static DnsResponse CreateEmptyNoDataResponse(uint16_t id, |
| bool is_authoritative, |
| base::span<const uint8_t> qname, |
| uint16_t qtype); |
| |
| // Move-only. |
| DnsResponse(DnsResponse&& other); |
| DnsResponse& operator=(DnsResponse&& other); |
| |
| ~DnsResponse(); |
| |
| // Internal buffer accessor into which actual bytes of response will be |
| // read. |
| IOBuffer* io_buffer() { return io_buffer_.get(); } |
| const IOBuffer* io_buffer() const { return io_buffer_.get(); } |
| |
| // Size of the internal buffer. |
| size_t io_buffer_size() const { return io_buffer_size_; } |
| |
| // Assuming the internal buffer holds |nbytes| bytes, returns true iff the |
| // packet matches the |query| id and question. This should only be called if |
| // the response is constructed from a raw buffer. |
| bool InitParse(size_t nbytes, const DnsQuery& query); |
| |
| // Assuming the internal buffer holds |nbytes| bytes, initialize the parser |
| // without matching it against an existing query. This should only be called |
| // if the response is constructed from a raw buffer. |
| bool InitParseWithoutQuery(size_t nbytes); |
| |
| // Does not require the response to be fully parsed and valid, but will return |
| // nullopt if the ID is unknown. The ID will only be known if the response is |
| // successfully constructed from data or if InitParse...() has been able to |
| // parse at least as far as the ID (not necessarily a fully successful parse). |
| std::optional<uint16_t> id() const; |
| |
| // Returns true if response is valid, that is, after successful InitParse, or |
| // after successful construction of a new response from data. |
| bool IsValid() const; |
| |
| // All of the methods below are valid only if the response is valid. |
| |
| // Accessors for the header. |
| uint16_t flags() const; // excluding rcode |
| uint8_t rcode() const; |
| |
| unsigned question_count() const; |
| unsigned answer_count() const; |
| unsigned authority_count() const; |
| unsigned additional_answer_count() const; |
| |
| const std::vector<uint16_t>& qtypes() const { |
| DCHECK(parser_.IsValid()); |
| DCHECK_EQ(question_count(), qtypes_.size()); |
| return qtypes_; |
| } |
| const std::vector<std::string>& dotted_qnames() const { |
| DCHECK(parser_.IsValid()); |
| DCHECK_EQ(question_count(), dotted_qnames_.size()); |
| return dotted_qnames_; |
| } |
| |
| // Shortcuts to get qtype or qname for single-query responses. Should only be |
| // used in cases where there is known to be exactly one question (e.g. because |
| // that has been validated by `InitParse()`). |
| uint16_t GetSingleQType() const; |
| std::string_view GetSingleDottedName() const; |
| |
| // Returns an iterator to the resource records in the answer section. |
| // The iterator is valid only in the scope of the DnsResponse. |
| // This operation is idempotent. |
| DnsRecordParser Parser() const; |
| |
| private: |
| bool WriteHeader(base::SpanWriter<uint8_t>* writer, |
| const dns_protocol::Header& header); |
| bool WriteQuestion(base::SpanWriter<uint8_t>* writer, const DnsQuery& query); |
| bool WriteRecord(base::SpanWriter<uint8_t>* writer, |
| const DnsResourceRecord& record, |
| bool validate_record, |
| bool validate_name_as_internet_hostname); |
| bool WriteAnswer(base::SpanWriter<uint8_t>* writer, |
| const DnsResourceRecord& answer, |
| const std::optional<DnsQuery>& query, |
| bool validate_record, |
| bool validate_name_as_internet_hostname); |
| |
| // Convenience for header access. |
| const dns_protocol::Header* header() const; |
| |
| // Buffer into which response bytes are read. |
| scoped_refptr<IOBuffer> io_buffer_; |
| |
| // Size of the buffer. |
| size_t io_buffer_size_; |
| |
| // Iterator constructed after InitParse positioned at the answer section. |
| // It is never updated afterwards, so can be used in accessors. |
| DnsRecordParser parser_; |
| bool id_available_ = false; |
| std::vector<std::string> dotted_qnames_; |
| std::vector<uint16_t> qtypes_; |
| }; |
| |
| } // namespace net |
| |
| #endif // NET_DNS_DNS_RESPONSE_H_ |