summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoragl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-08-04 15:54:40 +0000
committeragl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-08-04 15:54:40 +0000
commitb3ccac85bee8a3d89c7b5ca3a64ee311f3f565c3 (patch)
tree6a156892643928ceb04c3c6fe33243c2c9946bee
parent973ce28247510567d996b91ad5aa3f1da590fbcc (diff)
downloadchromium_src-b3ccac85bee8a3d89c7b5ca3a64ee311f3f565c3.zip
chromium_src-b3ccac85bee8a3d89c7b5ca3a64ee311f3f565c3.tar.gz
chromium_src-b3ccac85bee8a3d89c7b5ca3a64ee311f3f565c3.tar.bz2
net: add DnsRRResovler to fetch arbitary DNS resource types.
(Linux/Mac only for now.) TEST=net_unittests BUG=none http://codereview.chromium.org/3029035 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@54907 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--build/linux/system.gyp9
-rw-r--r--net/base/dns_util.h10
-rw-r--r--net/base/dnsrr_resolver.cc345
-rw-r--r--net/base/dnsrr_resolver.h66
-rw-r--r--net/base/dnsrr_resolver_unittest.cc154
-rw-r--r--net/net.gyp4
6 files changed, 588 insertions, 0 deletions
diff --git a/build/linux/system.gyp b/build/linux/system.gyp
index 876579a..f746ebf 100644
--- a/build/linux/system.gyp
+++ b/build/linux/system.gyp
@@ -308,6 +308,15 @@
],
},
},
+ {
+ 'target_name': 'libresolv',
+ 'type': 'settings',
+ 'link_settings': {
+ 'libraries': [
+ '-lresolv',
+ ],
+ },
+ },
],
}
diff --git a/net/base/dns_util.h b/net/base/dns_util.h
index cd81420..88c48a8 100644
--- a/net/base/dns_util.h
+++ b/net/base/dns_util.h
@@ -8,6 +8,8 @@
#include <string>
+#include "base/basictypes.h"
+
namespace net {
// DNSDomainFromDot - convert a domain string to DNS format. From DJB's
@@ -24,6 +26,14 @@ bool IsSTD3ASCIIValidCharacter(char c);
// Returns the hostname by trimming the ending dot, if one exists.
std::string TrimEndingDot(const std::string& host);
+// DNS resource record types. See
+// http://www.iana.org/assignments/dns-parameters
+
+static const uint16 kDNS_TXT = 16;
+static const uint16 kDNS_RRSIG = 46;
+static const uint16 kDNS_CERT = 37;
+static const uint16 kDNS_ANY = 0xff;
+
} // 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..7a872dd
--- /dev/null
+++ b/net/base/dnsrr_resolver.cc
@@ -0,0 +1,345 @@
+// Copyright (c) 2010 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 <resolv.h>
+#endif
+
+#include "base/string_piece.h"
+#include "base/task.h"
+#include "base/worker_pool.h"
+#include "net/base/dns_reload_timer.h"
+#include "net/base/dns_util.h"
+#include "net/base/net_errors.h"
+
+namespace net {
+
+static const uint16 kClassIN = 1;
+
+#if defined(OS_POSIX)
+
+// A Buffer is used for walking over a DNS packet.
+class Buffer {
+ public:
+ Buffer(const uint8* p, unsigned len)
+ : p_(p),
+ packet_(p),
+ len_(len),
+ packet_len_(len) {
+ }
+
+ bool U8(uint8* v) {
+ if (len_ < 1)
+ return false;
+ *v = *p_;
+ p_++;
+ len_--;
+ return true;
+ }
+
+ bool 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 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 Skip(unsigned n) {
+ if (len_ < n)
+ return false;
+ p_ += n;
+ len_ -= n;
+ return true;
+ }
+
+ bool 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 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];
+ } 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;
+ }
+
+ private:
+ const uint8* p_;
+ const uint8* const packet_;
+ unsigned len_;
+ const unsigned packet_len_;
+};
+
+bool DnsRRResolver::Response::ParseFromResponse(const uint8* p, unsigned len,
+ uint16 rrtype_requested) {
+ name.clear();
+ ttl = 0;
+ dnssec = false;
+ rrdatas.clear();
+ signatures.clear();
+
+ // RFC 1035 section 4.4.1
+ uint8 flags2;
+ Buffer 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;
+}
+
+class ResolveTask : public Task {
+ public:
+ ResolveTask(const std::string& name, uint16 rrtype,
+ uint16 flags, CompletionCallback* callback,
+ DnsRRResolver::Response* response)
+ : name_(name),
+ rrtype_(rrtype),
+ flags_(flags),
+ callback_(callback),
+ response_(response) {
+ }
+
+ virtual void Run() {
+ // Runs on a worker thread.
+
+ if ((_res.options & RES_INIT) == 0) {
+ if (res_ninit(&_res) != 0)
+ return Failure();
+ }
+
+ unsigned long saved_options = _res.options;
+ bool r = Do();
+
+#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_OPENBSD)
+ if (!r && DnsReloadTimerHasExpired()) {
+ res_nclose(&_res);
+ if (res_ninit(&_res) == 0)
+ r = Do();
+ }
+#endif
+ _res.options = saved_options;
+ int error = r ? OK : ERR_NAME_NOT_RESOLVED;
+ callback_->Run(error);
+ }
+
+ 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
+
+ // 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_);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ResolveTask);
+
+ void Failure() {
+ callback_->Run(ERR_NAME_NOT_RESOLVED);
+ }
+
+ const std::string name_;
+ const uint16 rrtype_;
+ const uint16 flags_;
+ CompletionCallback* const callback_;
+ DnsRRResolver::Response* const response_;
+};
+#else // OS_POSIX
+// On non-Linux platforms we fail everything for now.
+class ResolveTask : public Task {
+ public:
+ ResolveTask(const std::string& name, uint16 rrtype,
+ uint16 flags, CompletionCallback* callback,
+ DnsRRResolver::Response* response)
+ : callback_(callback) {
+ }
+
+ virtual void Run() {
+ callback_->Run(ERR_NAME_NOT_RESOLVED);
+ }
+
+ private:
+ CompletionCallback* const callback_;
+ DISALLOW_COPY_AND_ASSIGN(ResolveTask);
+};
+#endif
+
+// static
+bool DnsRRResolver::Resolve(const std::string& name, uint16 rrtype,
+ uint16 flags, CompletionCallback* callback,
+ Response* response) {
+ if (!callback || !response || name.empty())
+ return false;
+
+ // Don't allow queries of type ANY
+ if (rrtype == kDNS_ANY)
+ return false;
+
+ ResolveTask* task = new ResolveTask(name, rrtype, flags, callback, response);
+
+ return WorkerPool::PostTask(FROM_HERE, task, true /* task is slow */);
+}
+
+} // namespace net
diff --git a/net/base/dnsrr_resolver.h b/net/base/dnsrr_resolver.h
new file mode 100644
index 0000000..6dfea2f
--- /dev/null
+++ b/net/base/dnsrr_resolver.h
@@ -0,0 +1,66 @@
+// Copyright (c) 2010 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 <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "build/build_config.h"
+#include "net/base/completion_callback.h"
+
+namespace net {
+
+// 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.
+class DnsRRResolver {
+ public:
+ // Response contains the details of a successful request.
+ struct Response {
+ // 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;
+
+ // For testing only
+ bool ParseFromResponse(const uint8* data, unsigned len,
+ uint16 rrtype_requested);
+ };
+
+ enum {
+ // Try harder to get a DNSSEC signed response. This doesn't mean that the
+ // Response will always have the dnssec bit set.
+ FLAG_WANT_DNSSEC = 1,
+ };
+
+ // 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 the |callback| is called from a random
+ // worker thread.
+ static bool Resolve(const std::string& name, uint16 rrtype,
+ uint16 flags, CompletionCallback* callback,
+ Response* response);
+
+ private:
+ 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..6e36126
--- /dev/null
+++ b/net/base/dnsrr_resolver_unittest.cc
@@ -0,0 +1,154 @@
+// Copyright (c) 2010 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/callback.h"
+#include "base/condition_variable.h"
+#include "base/lock.h"
+#include "net/base/dns_util.h"
+#include "net/base/net_errors.h"
+#include "net/base/test_completion_callback.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+class DnsRRResolverTest : public testing::Test {
+};
+
+#if defined(OS_LINUX)
+
+class Rendezvous : public CallbackRunner<Tuple1<int> > {
+ public:
+ Rendezvous()
+ : have_result_(false),
+ cv_(&lock_) {
+ }
+
+ int WaitForResult() {
+ lock_.Acquire();
+ while (!have_result_)
+ cv_.Wait();
+ lock_.Release();
+ return result_;
+ }
+
+ virtual void RunWithParams(const Tuple1<int>& params) {
+ lock_.Acquire();
+ result_ = params.a;
+ have_result_ = true;
+ lock_.Release();
+ cv_.Broadcast();
+ }
+
+ private:
+ bool have_result_;
+ int result_;
+ Lock lock_;
+ ConditionVariable cv_;
+};
+
+// This test is disabled because it depends on the external network to pass.
+// However, it may be useful when chaging the code.
+TEST_F(DnsRRResolverTest, DISABLED_NetworkResolve) {
+ DnsRRResolver::Response response;
+ Rendezvous callback;
+ ASSERT_TRUE(DnsRRResolver::Resolve(
+ "agl._pka.imperialviolet.org", kDNS_TXT, 0, &callback, &response));
+ ASSERT_EQ(OK, callback.WaitForResult());
+ ASSERT_EQ(1u, response.rrdatas.size());
+ ASSERT_EQ(1u, response.signatures.size());
+ ASSERT_STREQ("]v=pka1;fpr=2AF0032B48E856CE06157A1AD43C670DE04AAA74;"
+ "uri=http://www.imperialviolet.org/key.asc",
+ response.rrdatas[0].c_str());
+}
+
+// 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_F(DnsRRResolverTest, ParseExample) {
+ DnsRRResolver::Response 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_F(DnsRRResolverTest, FuzzTruncation) {
+ DnsRRResolver::Response response;
+
+ for (unsigned len = sizeof(kExamplePacket); len <= sizeof(kExamplePacket);
+ len--) {
+ response.ParseFromResponse(kExamplePacket, len, kDNS_TXT);
+ }
+}
+
+TEST_F(DnsRRResolverTest, FuzzCorruption) {
+ DnsRRResolver::Response 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 // OS_LINUX
+
+} // namespace net
diff --git a/net/net.gyp b/net/net.gyp
index 9ef2145..b7f6245 100644
--- a/net/net.gyp
+++ b/net/net.gyp
@@ -55,6 +55,8 @@
'base/dns_reload_timer.h',
'base/dns_util.cc',
'base/dns_util.h',
+ 'base/dnsrr_resolver.cc',
+ 'base/dnsrr_resolver.h',
'base/escape.cc',
'base/escape.h',
'base/ev_root_ca_metadata.cc',
@@ -188,6 +190,7 @@
'../build/linux/system.gyp:gconf',
'../build/linux/system.gyp:gdk',
'../build/linux/system.gyp:nss',
+ '../build/linux/system.gyp:libresolv',
],
},
{ # else: OS is not in the above list
@@ -666,6 +669,7 @@
'base/data_url_unittest.cc',
'base/directory_lister_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',