Index: net/dns/dns_response.cc |
diff --git a/net/dns/dns_response.cc b/net/dns/dns_response.cc |
index 760bd5b68064dbc60c6a0f5b14b63e3e9d9b6f71..554e09d1cfce98f716f7bd57daeab09979f7ae0f 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,7 @@ DnsRecordParser::DnsRecordParser(const void* packet, |
DCHECK_LE(offset, length); |
} |
-int DnsRecordParser::ParseName(const void* const vpos, std::string* out) const { |
+int 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); |
@@ -54,7 +56,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 +107,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, |
@@ -220,4 +222,73 @@ 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. |
+ |
+ // 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; |
+ |
+ // getcanonname in eglibc returns the first owner name of an A or AAAA RR. |
+ std::string canon_name; |
+ |
+ uint32 cname_ttl_sec = kuint32max; |
+ uint32 addr_ttl_sec = kuint32max; |
+ IPAddressList ip_addresses; |
+ DnsRecordParser parser = Parser(); |
+ DnsResourceRecord record; |
+ while (parser.ReadRecord(&record)) { |
+ if (base::strcasecmp(record.name.c_str(), expected_name.c_str()) != 0) { |
+ if (record.type == expected_type) |
cbentzel
2012/02/15 19:48:35
Are there any cases where DNS servers will append
szym
2012/02/15 20:28:05
It might. I'll add a comment clarifying that for n
|
+ return DNS_NAME_MISMATCH; |
+ continue; |
cbentzel
2012/02/15 19:48:35
Why do you want to continue in this case?
szym
2012/02/15 20:28:05
Hmmm. I'll make it unconditionally reject.
|
+ } |
+ if ((record.type == dns_protocol::kTypeCNAME)) { |
cbentzel
2012/02/15 19:48:35
Nit: double parents not needed.
szym
2012/02/15 20:28:05
clang barfed on it :-/
|
+ // Following the CNAME chain, only if no addresses seen. |
+ if (!ip_addresses.empty()) { |
cbentzel
2012/02/15 19:48:35
Nit: extra braces
|
+ return DNS_CNAME_AFTER_ADDRESS; |
+ } |
+ |
+ if (!parser.ReadName(record.rdata.begin(), &expected_name)) { |
+ return DNS_MALFORMED_RESPONSE; |
+ } |
+ 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 (canon_name.empty()) |
+ canon_name = record.name; |
cbentzel
2012/02/15 19:48:35
Shouldn't the canon_name always be the same? Shoul
szym
2012/02/15 20:28:05
All the other checks will already catch that. This
|
+ } |
+ } |
+ if (!parser.AtEnd()) |
+ return DNS_MALFORMED_RESPONSE; |
+ |
+ if (ip_addresses.empty()) |
+ return DNS_NO_ADDRESSES; |
+ |
+ *addr_list = AddressList::CreateFromIPAddressList(ip_addresses, |
+ canon_name); |
+ *ttl = base::TimeDelta::FromSeconds(std::min(cname_ttl_sec, addr_ttl_sec)); |
+ return DNS_SUCCESS; |
+} |
+ |
} // namespace net |