summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authoragl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-02-23 15:51:34 +0000
committeragl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-02-23 15:51:34 +0000
commitc284be3f28d9c9eea24b844c7aeb9c004ce2de9f (patch)
tree901986c10f09beb41d1a8a2454ff6a452d0a6bb6 /net
parentdeb40835d8e2f88901af6934b659f0eb89fa2177 (diff)
downloadchromium_src-c284be3f28d9c9eea24b844c7aeb9c004ce2de9f.zip
chromium_src-c284be3f28d9c9eea24b844c7aeb9c004ce2de9f.tar.gz
chromium_src-c284be3f28d9c9eea24b844c7aeb9c004ce2de9f.tar.bz2
Revert "net: remove DnsRRResolver"
This reverts commit r114845. git-svn-id: svn://svn.chromium.org/chrome/trunk/src@123247 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r--net/base/dns_util.cc116
-rw-r--r--net/base/dns_util.h37
-rw-r--r--net/base/dnsrr_resolver.cc686
-rw-r--r--net/base/dnsrr_resolver.h140
-rw-r--r--net/base/dnsrr_resolver_unittest.cc211
-rw-r--r--net/net.gyp3
6 files changed, 1193 insertions, 0 deletions
diff --git a/net/base/dns_util.cc b/net/base/dns_util.cc
index 816095b..a49ada8 100644
--- a/net/base/dns_util.cc
+++ b/net/base/dns_util.cc
@@ -101,4 +101,120 @@ std::string TrimEndingDot(const base::StringPiece& host) {
return host_trimmed.as_string();
}
+bool DnsResponseBuffer::U8(uint8* v) {
+ if (len_ < 1)
+ return false;
+ *v = *p_;
+ p_++;
+ len_--;
+ return true;
+}
+
+bool DnsResponseBuffer::U16(uint16* v) {
+ if (len_ < 2)
+ return false;
+ *v = static_cast<uint16>(p_[0]) << 8 |
+ static_cast<uint16>(p_[1]);
+ p_ += 2;
+ len_ -= 2;
+ return true;
+}
+
+bool DnsResponseBuffer::U32(uint32* v) {
+ if (len_ < 4)
+ return false;
+ *v = static_cast<uint32>(p_[0]) << 24 |
+ static_cast<uint32>(p_[1]) << 16 |
+ static_cast<uint32>(p_[2]) << 8 |
+ static_cast<uint32>(p_[3]);
+ p_ += 4;
+ len_ -= 4;
+ return true;
+}
+
+bool DnsResponseBuffer::Skip(unsigned n) {
+ if (len_ < n)
+ return false;
+ p_ += n;
+ len_ -= n;
+ return true;
+}
+
+bool DnsResponseBuffer::Block(base::StringPiece* out, unsigned len) {
+ if (len_ < len)
+ return false;
+ *out = base::StringPiece(reinterpret_cast<const char*>(p_), len);
+ p_ += len;
+ len_ -= len;
+ return true;
+}
+
+// DNSName parses a (possibly compressed) DNS name from the packet. If |name|
+// is not NULL, then the name is written into it. See RFC 1035 section 4.1.4.
+bool DnsResponseBuffer::DNSName(std::string* name) {
+ unsigned jumps = 0;
+ const uint8* p = p_;
+ unsigned len = len_;
+
+ if (name)
+ name->clear();
+
+ for (;;) {
+ if (len < 1)
+ return false;
+ uint8 d = *p;
+ p++;
+ len--;
+
+ // The two couple of 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.
+ if ((d & 0xc0) == 0xc0) {
+ // This limit matches the depth limit in djbdns.
+ if (jumps > 100)
+ return false;
+ if (len < 1)
+ return false;
+ uint16 offset = static_cast<uint16>(d) << 8 |
+ static_cast<uint16>(p[0]);
+ offset &= 0x3ff;
+ p++;
+ len--;
+
+ if (jumps == 0) {
+ p_ = p;
+ len_ = len;
+ }
+ jumps++;
+
+ if (offset >= packet_len_)
+ return false;
+ p = &packet_[offset];
+ len = packet_len_ - offset;
+ } else if ((d & 0xc0) == 0) {
+ uint8 label_len = d;
+ if (len < label_len)
+ return false;
+ if (name && label_len) {
+ if (!name->empty())
+ name->append(".");
+ name->append(reinterpret_cast<const char*>(p), label_len);
+ }
+ p += label_len;
+ len -= label_len;
+
+ if (jumps == 0) {
+ p_ = p;
+ len_ = len;
+ }
+
+ if (label_len == 0)
+ break;
+ } else {
+ return false;
+ }
+ }
+
+ return true;
+}
+
} // namespace net
diff --git a/net/base/dns_util.h b/net/base/dns_util.h
index edfe559..2bd3eda 100644
--- a/net/base/dns_util.h
+++ b/net/base/dns_util.h
@@ -42,13 +42,19 @@ static const uint16 kClassIN = 1;
// DNS resource record types. See
// http://www.iana.org/assignments/dns-parameters
+// WARNING: if you're adding any new values here you may need to add them to
+// dnsrr_resolver.cc:DnsRRIsParsedByWindows.
+static const uint16 kDNS_A = 1;
static const uint16 kDNS_CNAME = 5;
static const uint16 kDNS_TXT = 16;
+static const uint16 kDNS_AAAA = 28;
static const uint16 kDNS_CERT = 37;
static const uint16 kDNS_DS = 43;
static const uint16 kDNS_RRSIG = 46;
static const uint16 kDNS_DNSKEY = 48;
+static const uint16 kDNS_ANY = 0xff;
static const uint16 kDNS_CAA = 257;
+static const uint16 kDNS_TESTING = 0xfffe; // in private use area.
// http://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml
static const uint8 kDNSSEC_RSA_SHA1 = 5;
@@ -59,6 +65,37 @@ static const uint8 kDNSSEC_RSA_SHA256 = 8;
static const uint8 kDNSSEC_SHA1 = 1;
static const uint8 kDNSSEC_SHA256 = 2;
+// A Buffer is used for walking over a DNS response packet.
+class DnsResponseBuffer {
+ public:
+ DnsResponseBuffer(const uint8* p, unsigned len)
+ : p_(p),
+ packet_(p),
+ len_(len),
+ packet_len_(len) {
+ }
+
+ bool U8(uint8* v);
+ bool U16(uint16* v);
+ bool U32(uint32* v);
+ bool Skip(unsigned n);
+
+ bool Block(base::StringPiece* out, unsigned len);
+
+ // DNSName parses a (possibly compressed) DNS name from the packet. If |name|
+ // is not NULL, then the name is written into it. See RFC 1035 section 4.1.4.
+ bool DNSName(std::string* name);
+
+ private:
+ const uint8* p_;
+ const uint8* const packet_;
+ unsigned len_;
+ const unsigned packet_len_;
+
+ DISALLOW_COPY_AND_ASSIGN(DnsResponseBuffer);
+};
+
+
} // namespace net
#endif // NET_BASE_DNS_UTIL_H_
diff --git a/net/base/dnsrr_resolver.cc b/net/base/dnsrr_resolver.cc
new file mode 100644
index 0000000..65ea8b6
--- /dev/null
+++ b/net/base/dnsrr_resolver.cc
@@ -0,0 +1,686 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/base/dnsrr_resolver.h"
+
+#if defined(OS_POSIX)
+#include <netinet/in.h>
+#include <resolv.h>
+#endif
+
+#if defined(OS_WIN)
+#include <windns.h>
+#endif
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/location.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/singleton.h"
+#include "base/message_loop.h"
+#include "base/stl_util.h"
+#include "base/string_piece.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/worker_pool.h"
+#include "net/base/dns_reloader.h"
+#include "net/base/dns_util.h"
+#include "net/base/net_errors.h"
+
+// Life of a query:
+//
+// DnsRRResolver RRResolverJob RRResolverWorker ... Handle
+// | (origin loop) (worker loop)
+// |
+// Resolve()
+// |---->-------------------<creates>
+// |
+// |---->----<creates>
+// |
+// |---->---------------------------------------------------<creates>
+// |
+// |---->--------------------Start
+// | |
+// | PostTask
+// |
+// | <starts resolving>
+// |---->-----AddHandle |
+// |
+// |
+// |
+// Finish
+// |
+// PostTask
+//
+// |
+// DoReply
+// |----<-----------------------|
+// HandleResult
+// |
+// |---->-----HandleResult
+// |
+// |------>-----------------------------------Post
+//
+//
+//
+// A cache hit:
+//
+// DnsRRResolver Handle
+// |
+// Resolve()
+// |---->------------------------<creates>
+// |
+// |
+// PostTask
+//
+// (MessageLoop cycles)
+//
+// Post
+
+namespace net {
+
+#if defined(OS_WIN)
+// DnsRRIsParsedByWindows returns true if Windows knows how to parse the given
+// RR type. RR data is returned in a DNS_RECORD structure which may be raw (if
+// Windows doesn't parse it) or may be a parse result. It's unclear how this
+// API is intended to evolve in the future. If Windows adds support for new RR
+// types in a future version a client which expected raw data will break.
+// See http://msdn.microsoft.com/en-us/library/ms682082(v=vs.85).aspx
+static bool DnsRRIsParsedByWindows(uint16 rrtype) {
+ // We only cover the types which are defined in dns_util.h
+ switch (rrtype) {
+ case kDNS_CNAME:
+ case kDNS_TXT:
+ case kDNS_DS:
+ case kDNS_RRSIG:
+ case kDNS_DNSKEY:
+ return true;
+ default:
+ return false;
+ }
+}
+#endif
+
+// kMaxCacheEntries is the number of RRResponse objects that we'll cache.
+static const unsigned kMaxCacheEntries = 32;
+// kNegativeTTLSecs is the number of seconds for which we'll cache a negative
+// cache entry.
+static const unsigned kNegativeTTLSecs = 60;
+
+RRResponse::RRResponse()
+ : ttl(0), dnssec(false), negative(false) {
+}
+
+RRResponse::~RRResponse() {}
+
+class RRResolverHandle {
+ public:
+ RRResolverHandle(const CompletionCallback& callback, RRResponse* response)
+ : callback_(callback),
+ response_(response) {
+ }
+
+ // Cancel ensures that the result callback will never be made.
+ void Cancel() {
+ callback_.Reset();
+ response_ = NULL;
+ }
+
+ // Post copies the contents of |response| to the caller's RRResponse and
+ // calls the callback.
+ void Post(int rv, const RRResponse* response) {
+ if (!callback_.is_null()) {
+ if (response_ && response)
+ *response_ = *response;
+ callback_.Run(rv);
+ }
+ delete this;
+ }
+
+ private:
+ CompletionCallback callback_;
+ RRResponse* response_;
+};
+
+
+// RRResolverWorker runs on a worker thread and takes care of the blocking
+// process of performing the DNS resolution.
+class RRResolverWorker {
+ public:
+ RRResolverWorker(const std::string& name, uint16 rrtype, uint16 flags,
+ DnsRRResolver* dnsrr_resolver)
+ : name_(name),
+ rrtype_(rrtype),
+ flags_(flags),
+ origin_loop_(MessageLoop::current()),
+ dnsrr_resolver_(dnsrr_resolver),
+ canceled_(false),
+ result_(ERR_UNEXPECTED) {
+ }
+
+ bool Start() {
+ DCHECK_EQ(MessageLoop::current(), origin_loop_);
+
+ return base::WorkerPool::PostTask(
+ FROM_HERE, base::Bind(&RRResolverWorker::Run, base::Unretained(this)),
+ true /* task is slow */);
+ }
+
+ // Cancel is called from the origin loop when the DnsRRResolver is getting
+ // deleted.
+ void Cancel() {
+ DCHECK_EQ(MessageLoop::current(), origin_loop_);
+ base::AutoLock locked(lock_);
+ canceled_ = true;
+ }
+
+ private:
+
+#if defined(OS_ANDROID)
+
+ void Run() {
+ NOTIMPLEMENTED();
+ }
+
+#elif defined(OS_POSIX)
+
+ void Run() {
+ // Runs on a worker thread.
+
+ if (HandleTestCases()) {
+ Finish();
+ return;
+ }
+
+ bool r = true;
+#if defined(OS_MACOSX) || defined(OS_OPENBSD)
+ if ((_res.options & RES_INIT) == 0) {
+#if defined(OS_OPENBSD)
+ if (res_init() != 0)
+#else
+ if (res_ninit(&_res) != 0)
+#endif
+ r = false;
+ }
+#else
+ DnsReloaderMaybeReload();
+#endif
+
+ if (r) {
+ unsigned long saved_options = _res.options;
+ r = Do();
+ _res.options = saved_options;
+ }
+
+ response_.fetch_time = base::Time::Now();
+
+ if (r) {
+ result_ = OK;
+ } else {
+ result_ = ERR_NAME_NOT_RESOLVED;
+ response_.negative = true;
+ response_.ttl = kNegativeTTLSecs;
+ }
+
+ Finish();
+ }
+
+ bool Do() {
+ // For DNSSEC, a 4K buffer is suggested
+ static const unsigned kMaxDNSPayload = 4096;
+
+#ifndef RES_USE_DNSSEC
+ // Some versions of libresolv don't have support for the DO bit. In this
+ // case, we proceed without it.
+ static const int RES_USE_DNSSEC = 0;
+#endif
+
+#ifndef RES_USE_EDNS0
+ // Some versions of glibc are so old that they don't support EDNS0 either.
+ // http://code.google.com/p/chromium/issues/detail?id=51676
+ static const int RES_USE_EDNS0 = 0;
+#endif
+
+ // We set the options explicitly. Note that this removes several default
+ // options: RES_DEFNAMES and RES_DNSRCH (see res_init(3)).
+ _res.options = RES_INIT | RES_RECURSE | RES_USE_EDNS0 | RES_USE_DNSSEC;
+ uint8 answer[kMaxDNSPayload];
+ int len = res_search(name_.c_str(), kClassIN, rrtype_, answer,
+ sizeof(answer));
+ if (len == -1)
+ return false;
+
+ return response_.ParseFromResponse(answer, len, rrtype_);
+ }
+
+#else // OS_WIN
+
+ void Run() {
+ if (HandleTestCases()) {
+ Finish();
+ return;
+ }
+
+ // See http://msdn.microsoft.com/en-us/library/ms682016(v=vs.85).aspx
+ PDNS_RECORD record = NULL;
+ DNS_STATUS status =
+ DnsQuery_A(name_.c_str(), rrtype_, DNS_QUERY_STANDARD,
+ NULL /* pExtra (reserved) */, &record, NULL /* pReserved */);
+ response_.fetch_time = base::Time::Now();
+ response_.name = name_;
+ response_.dnssec = false;
+ response_.ttl = 0;
+
+ if (status != 0) {
+ response_.negative = true;
+ result_ = ERR_NAME_NOT_RESOLVED;
+ } else {
+ response_.negative = false;
+ result_ = OK;
+ for (DNS_RECORD* cur = record; cur; cur = cur->pNext) {
+ if (cur->wType == rrtype_) {
+ response_.ttl = record->dwTtl;
+ // Windows will parse some types of resource records. If we want one
+ // of these types then we have to reserialise the record.
+ switch (rrtype_) {
+ case kDNS_TXT: {
+ // http://msdn.microsoft.com/en-us/library/ms682109(v=vs.85).aspx
+ const DNS_TXT_DATA* txt = &cur->Data.TXT;
+ std::string rrdata;
+
+ for (DWORD i = 0; i < txt->dwStringCount; i++) {
+ // Although the string is typed as a PWSTR, it's actually just
+ // an ASCII byte-string. Also, the string must be < 256
+ // elements because the length in the DNS packet is a single
+ // byte.
+ const char* s = reinterpret_cast<char*>(txt->pStringArray[i]);
+ size_t len = strlen(s);
+ DCHECK_LT(len, 256u);
+ char len8 = static_cast<char>(len);
+ rrdata.push_back(len8);
+ rrdata += s;
+ }
+ response_.rrdatas.push_back(rrdata);
+ break;
+ }
+ default:
+ if (DnsRRIsParsedByWindows(rrtype_)) {
+ // Windows parses this type, but we don't have code to unparse
+ // it.
+ NOTREACHED() << "you need to add code for the RR type here";
+ response_.negative = true;
+ result_ = ERR_INVALID_ARGUMENT;
+ } else {
+ // This type is given to us raw.
+ response_.rrdatas.push_back(
+ std::string(reinterpret_cast<char*>(&cur->Data),
+ cur->wDataLength));
+ }
+ }
+ }
+ }
+ }
+
+ DnsRecordListFree(record, DnsFreeRecordList);
+ Finish();
+ }
+
+#endif // OS_WIN
+
+ // HandleTestCases stuffs in magic test values in the event that the query is
+ // from a unittest.
+ bool HandleTestCases() {
+ if (rrtype_ == kDNS_TESTING) {
+ response_.fetch_time = base::Time::Now();
+
+ if (name_ == "www.testing.notatld") {
+ response_.ttl = 86400;
+ response_.negative = false;
+ response_.rrdatas.push_back("goats!");
+ result_ = OK;
+ return true;
+ } else if (name_ == "nx.testing.notatld") {
+ response_.negative = true;
+ result_ = ERR_NAME_NOT_RESOLVED;
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ // DoReply runs on the origin thread.
+ void DoReply() {
+ DCHECK_EQ(MessageLoop::current(), origin_loop_);
+ {
+ // We lock here because the worker thread could still be in Finished,
+ // after the PostTask, but before unlocking |lock_|. If we do not lock in
+ // this case, we will end up deleting a locked Lock, which can lead to
+ // memory leaks or worse errors.
+ base::AutoLock locked(lock_);
+ if (!canceled_)
+ dnsrr_resolver_->HandleResult(name_, rrtype_, result_, response_);
+ }
+ delete this;
+ }
+
+ void Finish() {
+ // Runs on the worker thread.
+ // We assume that the origin loop outlives the DnsRRResolver. If the
+ // DnsRRResolver is deleted, it will call Cancel on us. If it does so
+ // before the Acquire, we'll delete ourselves and return. If it's trying to
+ // do so concurrently, then it'll block on the lock and we'll call PostTask
+ // while the DnsRRResolver (and therefore the MessageLoop) is still alive.
+ // If it does so after this function, we assume that the MessageLoop will
+ // process pending tasks. In which case we'll notice the |canceled_| flag
+ // in DoReply.
+
+ bool canceled;
+ {
+ base::AutoLock locked(lock_);
+ canceled = canceled_;
+ if (!canceled) {
+ origin_loop_->PostTask(FROM_HERE, base::Bind(
+ &RRResolverWorker::DoReply, base::Unretained(this)));
+ }
+ }
+
+ if (canceled)
+ delete this;
+ }
+
+ const std::string name_;
+ const uint16 rrtype_;
+ const uint16 flags_;
+ MessageLoop* const origin_loop_;
+ DnsRRResolver* const dnsrr_resolver_;
+
+ base::Lock lock_;
+ bool canceled_;
+
+ int result_;
+ RRResponse response_;
+
+ DISALLOW_COPY_AND_ASSIGN(RRResolverWorker);
+};
+
+bool RRResponse::HasExpired(const base::Time current_time) const {
+ const base::TimeDelta delta(base::TimeDelta::FromSeconds(ttl));
+ const base::Time expiry = fetch_time + delta;
+ return current_time >= expiry;
+}
+
+#if defined(OS_POSIX) && !defined(OS_ANDROID)
+bool RRResponse::ParseFromResponse(const uint8* p, unsigned len,
+ uint16 rrtype_requested) {
+ name.clear();
+ ttl = 0;
+ dnssec = false;
+ negative = false;
+ rrdatas.clear();
+ signatures.clear();
+
+ // RFC 1035 section 4.4.1
+ uint8 flags2;
+ DnsResponseBuffer buf(p, len);
+ if (!buf.Skip(2) || // skip id
+ !buf.Skip(1) || // skip first flags byte
+ !buf.U8(&flags2)) {
+ return false;
+ }
+
+ // Bit 5 is the Authenticated Data (AD) bit. See
+ // http://tools.ietf.org/html/rfc2535#section-6.1
+ if (flags2 & 32) {
+ // AD flag is set. We'll trust it if it came from a local nameserver.
+ // Currently the resolv structure is IPv4 only, so we can't test for IPv6
+ // loopback addresses.
+ if (_res.nscount == 1 &&
+ memcmp(&_res.nsaddr_list[0].sin_addr,
+ "\x7f\x00\x00\x01" /* 127.0.0.1 */, 4) == 0) {
+ dnssec = true;
+ }
+ }
+
+ uint16 query_count, answer_count, authority_count, additional_count;
+ if (!buf.U16(&query_count) ||
+ !buf.U16(&answer_count) ||
+ !buf.U16(&authority_count) ||
+ !buf.U16(&additional_count)) {
+ return false;
+ }
+
+ if (query_count != 1)
+ return false;
+
+ uint16 type, klass;
+ if (!buf.DNSName(NULL) ||
+ !buf.U16(&type) ||
+ !buf.U16(&klass) ||
+ type != rrtype_requested ||
+ klass != kClassIN) {
+ return false;
+ }
+
+ if (answer_count < 1)
+ return false;
+
+ for (uint32 i = 0; i < answer_count; i++) {
+ std::string* name = NULL;
+ if (i == 0)
+ name = &this->name;
+ uint32 ttl;
+ uint16 rrdata_len;
+ if (!buf.DNSName(name) ||
+ !buf.U16(&type) ||
+ !buf.U16(&klass) ||
+ !buf.U32(&ttl) ||
+ !buf.U16(&rrdata_len)) {
+ return false;
+ }
+
+ base::StringPiece rrdata;
+ if (!buf.Block(&rrdata, rrdata_len))
+ return false;
+
+ if (klass == kClassIN && type == rrtype_requested) {
+ if (i == 0)
+ this->ttl = ttl;
+ rrdatas.push_back(std::string(rrdata.data(), rrdata.size()));
+ } else if (klass == kClassIN && type == kDNS_RRSIG) {
+ signatures.push_back(std::string(rrdata.data(), rrdata.size()));
+ }
+ }
+
+ return true;
+}
+#endif // defined(OS_POSIX) && !defined(OS_ANDROID)
+
+
+// An RRResolverJob is a one-to-one counterpart of an RRResolverWorker. It
+// lives only on the DnsRRResolver's origin message loop.
+class RRResolverJob {
+ public:
+ explicit RRResolverJob(RRResolverWorker* worker)
+ : worker_(worker) {
+ }
+
+ ~RRResolverJob() {
+ if (worker_) {
+ worker_->Cancel();
+ worker_ = NULL;
+ PostAll(ERR_ABORTED, NULL);
+ }
+ }
+
+ void AddHandle(RRResolverHandle* handle) {
+ handles_.push_back(handle);
+ }
+
+ void HandleResult(int result, const RRResponse& response) {
+ worker_ = NULL;
+ PostAll(result, &response);
+ }
+
+ private:
+ void PostAll(int result, const RRResponse* response) {
+ std::vector<RRResolverHandle*> handles;
+ handles_.swap(handles);
+
+ for (std::vector<RRResolverHandle*>::iterator
+ i = handles.begin(); i != handles.end(); i++) {
+ (*i)->Post(result, response);
+ // Post() causes the RRResolverHandle to delete itself.
+ }
+ }
+
+ std::vector<RRResolverHandle*> handles_;
+ RRResolverWorker* worker_;
+};
+
+
+DnsRRResolver::DnsRRResolver()
+ : requests_(0),
+ cache_hits_(0),
+ inflight_joins_(0),
+ in_destructor_(false) {
+}
+
+DnsRRResolver::~DnsRRResolver() {
+ DCHECK(!in_destructor_);
+ in_destructor_ = true;
+ STLDeleteValues(&inflight_);
+}
+
+intptr_t DnsRRResolver::Resolve(const std::string& name, uint16 rrtype,
+ uint16 flags,
+ const CompletionCallback& callback,
+ RRResponse* response,
+ int priority /* ignored */,
+ const BoundNetLog& netlog /* ignored */) {
+ DCHECK(CalledOnValidThread());
+ DCHECK(!in_destructor_);
+
+ if (callback.is_null() || !response || name.empty())
+ return kInvalidHandle;
+
+ // Don't allow queries of type ANY
+ if (rrtype == kDNS_ANY)
+ return kInvalidHandle;
+
+ requests_++;
+
+ const std::pair<std::string, uint16> key(make_pair(name, rrtype));
+ // First check the cache.
+ std::map<std::pair<std::string, uint16>, RRResponse>::iterator i;
+ i = cache_.find(key);
+ if (i != cache_.end()) {
+ if (!i->second.HasExpired(base::Time::Now())) {
+ int error;
+ if (i->second.negative) {
+ error = ERR_NAME_NOT_RESOLVED;
+ } else {
+ error = OK;
+ *response = i->second;
+ }
+ RRResolverHandle* handle = new RRResolverHandle(
+ callback, NULL /* no response pointer because we've already filled */
+ /* it in */);
+ cache_hits_++;
+ // We need a typed NULL pointer in order to make the templates work out.
+ static const RRResponse* kNoResponse = NULL;
+ MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(
+ &RRResolverHandle::Post, base::Unretained(handle), error,
+ kNoResponse));
+ return reinterpret_cast<intptr_t>(handle);
+ } else {
+ // entry has expired.
+ cache_.erase(i);
+ }
+ }
+
+ // No cache hit. See if a request is currently in flight.
+ RRResolverJob* job;
+ std::map<std::pair<std::string, uint16>, RRResolverJob*>::const_iterator j;
+ j = inflight_.find(key);
+ if (j != inflight_.end()) {
+ // The request is in flight already. We'll just attach our callback.
+ inflight_joins_++;
+ job = j->second;
+ } else {
+ // Need to make a new request.
+ RRResolverWorker* worker = new RRResolverWorker(name, rrtype, flags, this);
+ job = new RRResolverJob(worker);
+ inflight_.insert(make_pair(key, job));
+ if (!worker->Start()) {
+ inflight_.erase(key);
+ delete job;
+ delete worker;
+ return kInvalidHandle;
+ }
+ }
+
+ RRResolverHandle* handle = new RRResolverHandle(callback, response);
+ job->AddHandle(handle);
+ return reinterpret_cast<intptr_t>(handle);
+}
+
+void DnsRRResolver::CancelResolve(intptr_t h) {
+ DCHECK(CalledOnValidThread());
+ RRResolverHandle* handle = reinterpret_cast<RRResolverHandle*>(h);
+ handle->Cancel();
+}
+
+void DnsRRResolver::OnIPAddressChanged() {
+ DCHECK(CalledOnValidThread());
+ DCHECK(!in_destructor_);
+
+ std::map<std::pair<std::string, uint16>, RRResolverJob*> inflight;
+ inflight.swap(inflight_);
+ cache_.clear();
+
+ STLDeleteValues(&inflight);
+}
+
+// HandleResult is called on the origin message loop.
+void DnsRRResolver::HandleResult(const std::string& name, uint16 rrtype,
+ int result, const RRResponse& response) {
+ DCHECK(CalledOnValidThread());
+
+ const std::pair<std::string, uint16> key(std::make_pair(name, rrtype));
+
+ DCHECK_GE(kMaxCacheEntries, 1u);
+ DCHECK_LE(cache_.size(), kMaxCacheEntries);
+ if (cache_.size() == kMaxCacheEntries) {
+ // need to remove an element of the cache.
+ const base::Time current_time(base::Time::Now());
+ std::map<std::pair<std::string, uint16>, RRResponse>::iterator i, cur;
+ for (i = cache_.begin(); i != cache_.end(); ) {
+ cur = i++;
+ if (cur->second.HasExpired(current_time))
+ cache_.erase(cur);
+ }
+ }
+ if (cache_.size() == kMaxCacheEntries) {
+ // if we didn't clear out any expired entries, we just remove the first
+ // element. Crummy but simple.
+ cache_.erase(cache_.begin());
+ }
+
+ cache_.insert(std::make_pair(key, response));
+
+ std::map<std::pair<std::string, uint16>, RRResolverJob*>::iterator j;
+ j = inflight_.find(key);
+ if (j == inflight_.end()) {
+ NOTREACHED();
+ return;
+ }
+ RRResolverJob* job = j->second;
+ inflight_.erase(j);
+
+ job->HandleResult(result, response);
+ delete job;
+}
+
+} // namespace net
diff --git a/net/base/dnsrr_resolver.h b/net/base/dnsrr_resolver.h
new file mode 100644
index 0000000..297fc43
--- /dev/null
+++ b/net/base/dnsrr_resolver.h
@@ -0,0 +1,140 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_BASE_DNSRR_RESOLVER_H_
+#define NET_BASE_DNSRR_RESOLVER_H_
+#pragma once
+
+#include <map>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
+#include "base/threading/non_thread_safe.h"
+#include "base/time.h"
+#include "build/build_config.h"
+#include "net/base/completion_callback.h"
+#include "net/base/net_export.h"
+#include "net/base/network_change_notifier.h"
+
+namespace net {
+
+// RRResponse contains the result of a successful request for a resource record.
+struct NET_EXPORT_PRIVATE RRResponse {
+ RRResponse();
+ ~RRResponse();
+
+ // HasExpired returns true if |fetch_time| + |ttl| is less than
+ // |current_time|.
+ bool HasExpired(base::Time current_time) const;
+
+#if defined(OS_POSIX) && !defined(OS_ANDROID)
+ // For testing only
+ bool ParseFromResponse(const uint8* data, unsigned len,
+ uint16 rrtype_requested);
+#endif
+
+ // name contains the canonical name of the resulting domain. If the queried
+ // name was a CNAME then this can differ.
+ std::string name;
+ // ttl contains the TTL of the resource records.
+ uint32 ttl;
+ // dnssec is true if the response was DNSSEC validated.
+ bool dnssec;
+ std::vector<std::string> rrdatas;
+ // sigs contains the RRSIG records returned.
+ std::vector<std::string> signatures;
+ // fetch_time is the time at which the response was received from the
+ // network.
+ base::Time fetch_time;
+ // negative is true if this is a negative cache entry, i.e. is a placeholder
+ // to remember that a given RR doesn't exist.
+ bool negative;
+};
+
+class BoundNetLog;
+class RRResolverWorker;
+class RRResolverJob;
+
+// DnsRRResolver resolves arbitary DNS resource record types. It should not be
+// confused with HostResolver and should not be used to resolve A/AAAA records.
+//
+// HostResolver exists to lookup addresses and there are many details about
+// address resolution over and above DNS (i.e. Bonjour, VPNs etc).
+//
+// DnsRRResolver should only be used when the data is specifically DNS data and
+// the name is a fully qualified DNS domain.
+//
+// A DnsRRResolver must be used from the MessageLoop which created it.
+class NET_EXPORT DnsRRResolver
+ : NON_EXPORTED_BASE(public base::NonThreadSafe),
+ public NetworkChangeNotifier::IPAddressObserver {
+ public:
+ typedef intptr_t Handle;
+
+ enum {
+ kInvalidHandle = 0,
+ };
+
+ enum {
+ // Try harder to get a DNSSEC signed response. This doesn't mean that the
+ // RRResponse will always have the dnssec bit set.
+ FLAG_WANT_DNSSEC = 1,
+ };
+
+ DnsRRResolver();
+ virtual ~DnsRRResolver();
+
+ uint64 requests() const { return requests_; }
+ uint64 cache_hits() const { return cache_hits_; }
+ uint64 inflight_joins() const { return inflight_joins_; }
+
+ // Resolve starts the resolution process. When complete, |callback| is called
+ // with a result. If the result is |OK| then |response| is filled with the
+ // result of the resolution. Note that |callback| is called via the current
+ // MessageLoop.
+ //
+ // This returns a handle value which can be passed to |CancelResolve|. If
+ // this function returns kInvalidHandle then the resolution failed
+ // immediately because it was improperly formed.
+ Handle Resolve(const std::string& name, uint16 rrtype,
+ uint16 flags, const CompletionCallback& callback,
+ RRResponse* response, int priority,
+ const BoundNetLog& netlog);
+
+ // CancelResolve cancels an inflight lookup. The callback for this lookup
+ // must not have already been called.
+ void CancelResolve(Handle handle);
+
+ // Implementation of NetworkChangeNotifier::IPAddressObserver
+ virtual void OnIPAddressChanged() OVERRIDE;
+
+ private:
+ friend class RRResolverWorker;
+
+ void HandleResult(const std::string& name, uint16 rrtype, int result,
+ const RRResponse& response);
+
+ // cache_ maps from a request to a cached response. The cached answer may
+ // have expired and the size of |cache_| must be <= kMaxCacheEntries.
+ // < name , rrtype>
+ std::map<std::pair<std::string, uint16>, RRResponse> cache_;
+ // inflight_ maps from a request to an active resolution which is taking
+ // place.
+ std::map<std::pair<std::string, uint16>, RRResolverJob*> inflight_;
+
+ uint64 requests_;
+ uint64 cache_hits_;
+ uint64 inflight_joins_;
+
+ bool in_destructor_;
+
+ DISALLOW_COPY_AND_ASSIGN(DnsRRResolver);
+};
+
+} // namespace net
+
+#endif // NET_BASE_DNSRR_RESOLVER_H_
diff --git a/net/base/dnsrr_resolver_unittest.cc b/net/base/dnsrr_resolver_unittest.cc
new file mode 100644
index 0000000..cc6e80b
--- /dev/null
+++ b/net/base/dnsrr_resolver_unittest.cc
@@ -0,0 +1,211 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/base/dnsrr_resolver.h"
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/synchronization/lock.h"
+#include "net/base/dns_util.h"
+#include "net/base/net_errors.h"
+#include "net/base/net_log.h"
+#include "net/base/test_completion_callback.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace {
+
+void FailTest(int /* result */) {
+ FAIL();
+}
+
+// These tests are disabled because they depend on the external network to
+// pass. However, they may be useful when chaging the code.
+TEST(DnsRRResolverTest, DISABLED_ResolveReal) {
+ RRResponse response;
+ TestCompletionCallback callback;
+ DnsRRResolver resolver;
+ DnsRRResolver::Handle handle;
+
+ handle = resolver.Resolve("test.imperialviolet.org", 13172, 0,
+ callback.callback(), &response, 0, BoundNetLog());
+ ASSERT_TRUE(handle != DnsRRResolver::kInvalidHandle);
+ ASSERT_EQ(OK, callback.WaitForResult());
+ ASSERT_EQ(1u, response.rrdatas.size());
+ LOG(ERROR) << "result length " << response.rrdatas[0].size();
+ LOG(ERROR) << "result is " << response.rrdatas[0];
+}
+
+TEST(DnsRRResolverTest, DISABLED_ResolveReal2) {
+ RRResponse response;
+ TestCompletionCallback callback;
+ DnsRRResolver resolver;
+ DnsRRResolver::Handle handle;
+
+ handle = resolver.Resolve("google.com", kDNS_TXT, 0,
+ callback.callback(), &response, 0, BoundNetLog());
+ ASSERT_TRUE(handle != DnsRRResolver::kInvalidHandle);
+ ASSERT_EQ(OK, callback.WaitForResult());
+ ASSERT_EQ(1u, response.rrdatas.size());
+ LOG(ERROR) << "result length " << response.rrdatas[0].size();
+ LOG(ERROR) << "result is " << response.rrdatas[0];
+}
+
+
+TEST(DnsRRResolverTest, Resolve) {
+ RRResponse response;
+ TestCompletionCallback callback;
+ DnsRRResolver resolver;
+ DnsRRResolver::Handle handle;
+
+ handle = resolver.Resolve("www.testing.notatld", kDNS_TESTING, 0,
+ callback.callback(), &response, 0, BoundNetLog());
+ ASSERT_TRUE(handle != DnsRRResolver::kInvalidHandle);
+ ASSERT_EQ(OK, callback.WaitForResult());
+ ASSERT_EQ(1u, response.rrdatas.size());
+ ASSERT_STREQ("goats!", response.rrdatas[0].c_str());
+ ASSERT_EQ(1u, resolver.requests());
+ ASSERT_EQ(0u, resolver.cache_hits());
+ ASSERT_EQ(0u, resolver.inflight_joins());
+
+ // Test a cache hit.
+ handle = resolver.Resolve("www.testing.notatld", kDNS_TESTING, 0,
+ callback.callback(), &response, 0, BoundNetLog());
+ ASSERT_TRUE(handle != DnsRRResolver::kInvalidHandle);
+ ASSERT_EQ(OK, callback.WaitForResult());
+ ASSERT_EQ(1u, response.rrdatas.size());
+ ASSERT_STREQ("goats!", response.rrdatas[0].c_str());
+ ASSERT_EQ(2u, resolver.requests());
+ ASSERT_EQ(1u, resolver.cache_hits());
+ ASSERT_EQ(0u, resolver.inflight_joins());
+
+ // Test that a callback is never made. This depends on there being another
+ // test after this one which will pump the MessageLoop.
+ handle = resolver.Resolve("www.testing.notatld", kDNS_TESTING, 0,
+ base::Bind(&FailTest), &response, 0, BoundNetLog());
+ ASSERT_TRUE(handle != DnsRRResolver::kInvalidHandle);
+ resolver.CancelResolve(handle);
+ ASSERT_EQ(3u, resolver.requests());
+ ASSERT_EQ(2u, resolver.cache_hits());
+ ASSERT_EQ(0u, resolver.inflight_joins());
+
+ // Test what happens in the event of a network config change.
+ handle = resolver.Resolve("nx.testing.notatld", kDNS_TESTING, 0,
+ callback.callback(), &response, 0, BoundNetLog());
+ ASSERT_TRUE(handle != DnsRRResolver::kInvalidHandle);
+ resolver.OnIPAddressChanged();
+ ASSERT_TRUE(callback.have_result());
+ ASSERT_EQ(ERR_ABORTED, callback.WaitForResult());
+ ASSERT_EQ(4u, resolver.requests());
+ ASSERT_EQ(2u, resolver.cache_hits());
+ ASSERT_EQ(0u, resolver.inflight_joins());
+
+ // Test an inflight join. (Note that this depends on the cache being flushed
+ // by OnIPAddressChanged.)
+ TestCompletionCallback callback2;
+ DnsRRResolver::Handle handle2;
+ handle = resolver.Resolve("nx.testing.notatld", kDNS_TESTING, 0,
+ callback.callback(), &response, 0, BoundNetLog());
+ ASSERT_TRUE(handle != DnsRRResolver::kInvalidHandle);
+ handle2 = resolver.Resolve("nx.testing.notatld", kDNS_TESTING, 0,
+ callback2.callback(), &response, 0, BoundNetLog());
+ ASSERT_TRUE(handle2 != DnsRRResolver::kInvalidHandle);
+ ASSERT_EQ(ERR_NAME_NOT_RESOLVED, callback.WaitForResult());
+ ASSERT_EQ(ERR_NAME_NOT_RESOLVED, callback2.WaitForResult());
+ ASSERT_EQ(6u, resolver.requests());
+ ASSERT_EQ(2u, resolver.cache_hits());
+ ASSERT_EQ(1u, resolver.inflight_joins());
+}
+
+#if defined(OS_POSIX) && !defined(OS_ANDROID)
+// This is a DNS packet resulting from querying a recursive resolver for a TXT
+// record for agl._pka.imperialviolet.org. You should be able to get a
+// replacement from a packet capture should it ever be needed.
+static const uint8 kExamplePacket[] = {
+ 0xce, 0xfe, 0x81, 0x80, 0x00, 0x01, 0x00, 0x02, 0x00, 0x06, 0x00, 0x01, 0x03,
+ 0x61, 0x67, 0x6c, 0x04, 0x5f, 0x70, 0x6b, 0x61, 0x0e, 0x69, 0x6d, 0x70, 0x65,
+ 0x72, 0x69, 0x61, 0x6c, 0x76, 0x69, 0x6f, 0x6c, 0x65, 0x74, 0x03, 0x6f, 0x72,
+ 0x67, 0x00, 0x00, 0x10, 0x00, 0x01, 0xc0, 0x0c, 0x00, 0x10, 0x00, 0x01, 0x00,
+ 0x00, 0x01, 0x2c, 0x00, 0x5e, 0x5d, 0x76, 0x3d, 0x70, 0x6b, 0x61, 0x31, 0x3b,
+ 0x66, 0x70, 0x72, 0x3d, 0x32, 0x41, 0x46, 0x30, 0x30, 0x33, 0x32, 0x42, 0x34,
+ 0x38, 0x45, 0x38, 0x35, 0x36, 0x43, 0x45, 0x30, 0x36, 0x31, 0x35, 0x37, 0x41,
+ 0x31, 0x41, 0x44, 0x34, 0x33, 0x43, 0x36, 0x37, 0x30, 0x44, 0x45, 0x30, 0x34,
+ 0x41, 0x41, 0x41, 0x37, 0x34, 0x3b, 0x75, 0x72, 0x69, 0x3d, 0x68, 0x74, 0x74,
+ 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x6d, 0x70, 0x65, 0x72,
+ 0x69, 0x61, 0x6c, 0x76, 0x69, 0x6f, 0x6c, 0x65, 0x74, 0x2e, 0x6f, 0x72, 0x67,
+ 0x2f, 0x6b, 0x65, 0x79, 0x2e, 0x61, 0x73, 0x63, 0xc0, 0x0c, 0x00, 0x2e, 0x00,
+ 0x01, 0x00, 0x00, 0x01, 0x2c, 0x00, 0xc6, 0x00, 0x10, 0x05, 0x04, 0x00, 0x01,
+ 0x51, 0x80, 0x4c, 0x74, 0x2f, 0x1a, 0x4c, 0x4c, 0x9c, 0xeb, 0x45, 0xc9, 0x0e,
+ 0x69, 0x6d, 0x70, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x76, 0x69, 0x6f, 0x6c, 0x65,
+ 0x74, 0x03, 0x6f, 0x72, 0x67, 0x00, 0x3b, 0x6d, 0x3d, 0xbb, 0xae, 0x1b, 0x07,
+ 0x8d, 0xa9, 0xb0, 0xa7, 0xa5, 0x7a, 0x84, 0x24, 0x34, 0x29, 0x43, 0x36, 0x3f,
+ 0x5a, 0x48, 0x3b, 0x79, 0xa3, 0x16, 0xa4, 0x28, 0x5b, 0xd7, 0x03, 0xc6, 0x93,
+ 0xba, 0x4e, 0x93, 0x4d, 0x18, 0x5c, 0x98, 0xc2, 0x0d, 0x57, 0xd2, 0x6b, 0x9a,
+ 0x72, 0xbd, 0xe5, 0x8d, 0x10, 0x7b, 0x03, 0xe7, 0x19, 0x1e, 0x51, 0xe5, 0x7e,
+ 0x49, 0x6b, 0xa3, 0xa8, 0xf1, 0xd3, 0x1b, 0xff, 0x40, 0x26, 0x82, 0x65, 0xd0,
+ 0x74, 0x8e, 0xcf, 0xc9, 0x71, 0xea, 0x91, 0x57, 0x7e, 0x50, 0x61, 0x4d, 0x4b,
+ 0x77, 0x05, 0x6a, 0xd8, 0x3f, 0x12, 0x87, 0x50, 0xc2, 0x35, 0x13, 0xab, 0x01,
+ 0x78, 0xd2, 0x3a, 0x55, 0xa2, 0x89, 0xc8, 0x87, 0xe2, 0x7b, 0xec, 0x51, 0x7c,
+ 0xc0, 0x24, 0xb5, 0xa3, 0x33, 0x78, 0x98, 0x28, 0x8e, 0x9b, 0x6b, 0x88, 0x13,
+ 0x25, 0xfa, 0x1d, 0xdc, 0xf1, 0xf0, 0xa6, 0x8d, 0x2a, 0xbb, 0xbc, 0xb0, 0xc7,
+ 0x97, 0x98, 0x8e, 0xef, 0xd9, 0x12, 0x24, 0xee, 0x38, 0x50, 0xdb, 0xd3, 0x59,
+ 0xcc, 0x30, 0x54, 0x4c, 0x38, 0x94, 0x24, 0xbc, 0x75, 0xa5, 0xc0, 0xc4, 0x00,
+ 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x15, 0x02, 0x62, 0x30, 0x03,
+ 0x6f, 0x72, 0x67, 0x0b, 0x61, 0x66, 0x69, 0x6c, 0x69, 0x61, 0x73, 0x2d, 0x6e,
+ 0x73, 0x74, 0xc0, 0xc4, 0xc0, 0xc4, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x3a, 0x00, 0x19, 0x02, 0x63, 0x30, 0x03, 0x6f, 0x72, 0x67, 0x0b, 0x61, 0x66,
+ 0x69, 0x6c, 0x69, 0x61, 0x73, 0x2d, 0x6e, 0x73, 0x74, 0x04, 0x69, 0x6e, 0x66,
+ 0x6f, 0x00, 0xc0, 0xc4, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3a, 0x00,
+ 0x05, 0x02, 0x61, 0x30, 0xc1, 0x99, 0xc0, 0xc4, 0x00, 0x02, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x3a, 0x00, 0x05, 0x02, 0x62, 0x32, 0xc1, 0x78, 0xc0, 0xc4, 0x00,
+ 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x05, 0x02, 0x64, 0x30, 0xc1,
+ 0x78, 0xc0, 0xc4, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x05,
+ 0x02, 0x61, 0x32, 0xc1, 0x99, 0x00, 0x00, 0x29, 0x10, 0x00, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x00,
+};
+
+TEST(DnsRRResolverTest, ParseExample) {
+ RRResponse response;
+ ASSERT_TRUE(response.ParseFromResponse(kExamplePacket,
+ sizeof(kExamplePacket), kDNS_TXT));
+ ASSERT_EQ(1u, response.rrdatas.size());
+ ASSERT_EQ(1u, response.signatures.size());
+ ASSERT_STREQ("agl._pka.imperialviolet.org", response.name.c_str());
+ ASSERT_STREQ("]v=pka1;fpr=2AF0032B48E856CE06157A1AD43C670DE04AAA74;"
+ "uri=http://www.imperialviolet.org/key.asc",
+ response.rrdatas[0].c_str());
+ ASSERT_FALSE(response.dnssec);
+}
+
+TEST(DnsRRResolverTest, FuzzTruncation) {
+ RRResponse response;
+
+ for (unsigned len = sizeof(kExamplePacket); len <= sizeof(kExamplePacket);
+ len--) {
+ response.ParseFromResponse(kExamplePacket, len, kDNS_TXT);
+ }
+}
+
+TEST(DnsRRResolverTest, FuzzCorruption) {
+ RRResponse response;
+ uint8 copy[sizeof(kExamplePacket)];
+
+
+ for (unsigned bit_to_corrupt = 0; bit_to_corrupt < sizeof(kExamplePacket) * 8;
+ bit_to_corrupt++) {
+ unsigned byte = bit_to_corrupt >> 3;
+ unsigned bit = bit_to_corrupt & 7;
+
+ memcpy(copy, kExamplePacket, sizeof(copy));
+ copy[byte] ^= (1 << bit);
+
+ response.ParseFromResponse(copy, sizeof(copy), kDNS_TXT);
+ }
+}
+#endif
+
+} // namespace
+
+} // namespace net
diff --git a/net/net.gyp b/net/net.gyp
index e3bdb7a..b7b36be 100644
--- a/net/net.gyp
+++ b/net/net.gyp
@@ -97,6 +97,8 @@
'base/dns_reloader.h',
'base/dns_util.cc',
'base/dns_util.h',
+ 'base/dnsrr_resolver.cc',
+ 'base/dnsrr_resolver.h',
'base/dnssec_chain_verifier.cc',
'base/dnssec_chain_verifier.h',
'base/dnssec_keyset.cc',
@@ -1016,6 +1018,7 @@
'base/directory_lister_unittest.cc',
'base/dnssec_unittest.cc',
'base/dns_util_unittest.cc',
+ 'base/dnsrr_resolver_unittest.cc',
'base/escape_unittest.cc',
'base/file_stream_unittest.cc',
'base/filter_unittest.cc',