| Index: net/dns/dns_response.cc
|
| diff --git a/net/dns/dns_response.cc b/net/dns/dns_response.cc
|
| index 760bd5b68064dbc60c6a0f5b14b63e3e9d9b6f71..95180953eb17b3bedab442d170947f2fe16df9e9 100644
|
| --- a/net/dns/dns_response.cc
|
| +++ b/net/dns/dns_response.cc
|
| @@ -4,7 +4,9 @@
|
|
|
| #include "net/dns/dns_response.h"
|
|
|
| +#include "base/string_util.h"
|
| #include "base/sys_byteorder.h"
|
| +#include "net/base/address_list.h"
|
| #include "net/base/big_endian.h"
|
| #include "net/base/dns_util.h"
|
| #include "net/base/io_buffer.h"
|
| @@ -32,7 +34,8 @@ DnsRecordParser::DnsRecordParser(const void* packet,
|
| DCHECK_LE(offset, length);
|
| }
|
|
|
| -int DnsRecordParser::ParseName(const void* const vpos, std::string* out) const {
|
| +unsigned DnsRecordParser::ReadName(const void* const vpos,
|
| + std::string* out) const {
|
| const char* const pos = reinterpret_cast<const char*>(vpos);
|
| DCHECK(packet_);
|
| DCHECK_LE(packet_, pos);
|
| @@ -41,9 +44,9 @@ int DnsRecordParser::ParseName(const void* const vpos, std::string* out) const {
|
| const char* p = pos;
|
| const char* end = packet_ + length_;
|
| // Count number of seen bytes to detect loops.
|
| - size_t seen = 0;
|
| + unsigned seen = 0;
|
| // Remember how many bytes were consumed before first jump.
|
| - size_t consumed = 0;
|
| + unsigned consumed = 0;
|
|
|
| if (pos >= end)
|
| return 0;
|
| @@ -54,7 +57,7 @@ int DnsRecordParser::ParseName(const void* const vpos, std::string* out) const {
|
| }
|
|
|
| for (;;) {
|
| - // The two couple of bits of the length give the type of the length. It's
|
| + // The first two bits of the length give the type of the length. It's
|
| // either a direct length or a pointer to the remainder of the name.
|
| switch (*p & dns_protocol::kLabelMask) {
|
| case dns_protocol::kLabelPointer: {
|
| @@ -105,9 +108,9 @@ int DnsRecordParser::ParseName(const void* const vpos, std::string* out) const {
|
| }
|
| }
|
|
|
| -bool DnsRecordParser::ParseRecord(DnsResourceRecord* out) {
|
| +bool DnsRecordParser::ReadRecord(DnsResourceRecord* out) {
|
| DCHECK(packet_);
|
| - size_t consumed = ParseName(cur_, &out->name);
|
| + size_t consumed = ReadName(cur_, &out->name);
|
| if (!consumed)
|
| return false;
|
| BigEndianReader reader(cur_ + consumed,
|
| @@ -182,7 +185,7 @@ uint8 DnsResponse::rcode() const {
|
| return ntohs(header()->flags) & dns_protocol::kRcodeMask;
|
| }
|
|
|
| -int DnsResponse::answer_count() const {
|
| +unsigned DnsResponse::answer_count() const {
|
| DCHECK(parser_.IsValid());
|
| return ntohs(header()->ancount);
|
| }
|
| @@ -220,4 +223,71 @@ const dns_protocol::Header* DnsResponse::header() const {
|
| return reinterpret_cast<const dns_protocol::Header*>(io_buffer_->data());
|
| }
|
|
|
| +DnsResponse::Result DnsResponse::ParseToAddressList(
|
| + AddressList* addr_list,
|
| + base::TimeDelta* ttl) const {
|
| + DCHECK(IsValid());
|
| + // DnsTransaction already verified that |response| matches the issued query.
|
| + // We still need to determine if there is a valid chain of CNAMEs from the
|
| + // query name to the RR owner name.
|
| + // We err on the side of caution with the assumption that if we are too picky,
|
| + // we can always fall back to the system getaddrinfo.
|
| +
|
| + // Expected owner of record. No trailing dot.
|
| + std::string expected_name = GetDottedName();
|
| +
|
| + uint16 expected_type = qtype();
|
| + DCHECK(expected_type == dns_protocol::kTypeA ||
|
| + expected_type == dns_protocol::kTypeAAAA);
|
| +
|
| + size_t expected_size = (expected_type == dns_protocol::kTypeAAAA)
|
| + ? kIPv6AddressSize : kIPv4AddressSize;
|
| +
|
| + uint32 cname_ttl_sec = kuint32max;
|
| + uint32 addr_ttl_sec = kuint32max;
|
| + IPAddressList ip_addresses;
|
| + DnsRecordParser parser = Parser();
|
| + DnsResourceRecord record;
|
| + unsigned ancount = answer_count();
|
| + for (unsigned i = 0; i < ancount; ++i) {
|
| + if (!parser.ReadRecord(&record))
|
| + return DNS_MALFORMED_RESPONSE;
|
| +
|
| + if (base::strcasecmp(record.name.c_str(), expected_name.c_str()) != 0)
|
| + return DNS_NAME_MISMATCH;
|
| + if (record.type == dns_protocol::kTypeCNAME) {
|
| + // Following the CNAME chain, only if no addresses seen.
|
| + if (!ip_addresses.empty())
|
| + return DNS_CNAME_AFTER_ADDRESS;
|
| +
|
| + if (record.rdata.size() !=
|
| + parser.ReadName(record.rdata.begin(), &expected_name))
|
| + return DNS_MALFORMED_CNAME;
|
| +
|
| + cname_ttl_sec = std::min(cname_ttl_sec, record.ttl);
|
| + } else if (record.type == expected_type) {
|
| + if (record.rdata.size() != expected_size)
|
| + return DNS_SIZE_MISMATCH;
|
| + if (ip_addresses.empty()) {
|
| + addr_ttl_sec = record.ttl;
|
| + } else {
|
| + if (addr_ttl_sec != record.ttl)
|
| + return DNS_ADDRESS_TTL_MISMATCH;
|
| + }
|
| + ip_addresses.push_back(IPAddressNumber(record.rdata.begin(),
|
| + record.rdata.end()));
|
| + }
|
| + }
|
| +
|
| + if (ip_addresses.empty())
|
| + return DNS_NO_ADDRESSES;
|
| +
|
| + // getcanonname in eglibc returns the first owner name of an A or AAAA RR.
|
| + // If the response passed all the checks so far, then |expected_name| is it.
|
| + *addr_list = AddressList::CreateFromIPAddressList(ip_addresses,
|
| + expected_name);
|
| + *ttl = base::TimeDelta::FromSeconds(std::min(cname_ttl_sec, addr_ttl_sec));
|
| + return DNS_SUCCESS;
|
| +}
|
| +
|
| } // namespace net
|
|
|